blob: 60a7ee373b6b10598307e18f61554f55fa928327 [file] [log] [blame]
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +01001#!/usr/bin/env python3
2# Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
3# for details. All rights reserved. Use of this source code is governed by a
4# BSD-style license that can be found in the LICENSE file.
5
Christoffer Quist Adamsen21f7f352022-05-30 19:52:43 +02006import argparse
Christoffer Quist Adamsenc6b19de2022-04-21 13:07:53 +02007import os
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +01008import subprocess
Christoffer Quist Adamsenc6b19de2022-04-21 13:07:53 +02009import sys
10import threading
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010011import time
12
Christoffer Quist Adamsen21f7f352022-05-30 19:52:43 +020013from enum import Enum
14
Christoffer Quist Adamsenc6b19de2022-04-21 13:07:53 +020015sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
16
Christoffer Quist Adamsen1c07af92022-08-11 13:46:25 +020017import profile_utils
Christoffer Quist Adamsenc6b19de2022-04-21 13:07:53 +020018import utils
19
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010020DEVNULL=subprocess.DEVNULL
21
Christoffer Quist Adamsenc6b19de2022-04-21 13:07:53 +020022class ProcessReader(threading.Thread):
23
24 def __init__(self, process):
25 threading.Thread.__init__(self)
26 self.lines = []
27 self.process = process
28
29 def run(self):
30 for line in self.process.stdout:
31 line = line.decode('utf-8').strip()
32 self.lines.append(line)
33
34 def stop(self):
35 self.process.kill()
36
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010037class ScreenState(Enum):
38 OFF_LOCKED = 1,
39 OFF_UNLOCKED = 2
40 ON_LOCKED = 3
41 ON_UNLOCKED = 4
42
43 def is_off(self):
44 return self == ScreenState.OFF_LOCKED or self == ScreenState.OFF_UNLOCKED
45
46 def is_on(self):
47 return self == ScreenState.ON_LOCKED or self == ScreenState.ON_UNLOCKED
48
49 def is_on_and_locked(self):
50 return self == ScreenState.ON_LOCKED
51
52 def is_on_and_unlocked(self):
53 return self == ScreenState.ON_UNLOCKED
54
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +020055def broadcast(action, component, device_id=None):
56 print('Sending broadcast %s' % action)
57 cmd = create_adb_cmd('shell am broadcast -a %s %s' % (action, component), device_id)
58 return subprocess.check_output(cmd).decode('utf-8').strip().splitlines()
59
Christoffer Quist Adamsendab3b202022-08-15 09:36:28 +020060def build_apks_from_bundle(bundle, output, overwrite=False):
Christoffer Quist Adamsen4d9fc512022-08-11 19:59:44 +020061 print('Building %s' % bundle)
62 cmd = [
63 'java', '-jar', utils.BUNDLETOOL_JAR,
64 'build-apks',
65 '--bundle=%s' % bundle,
66 '--output=%s' % output]
Christoffer Quist Adamsendab3b202022-08-15 09:36:28 +020067 if overwrite:
68 cmd.append('--overwrite')
Christoffer Quist Adamsen4d9fc512022-08-11 19:59:44 +020069 subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL)
70
Christoffer Quist Adamsened805c52022-08-08 14:46:29 +020071def capture_screen(target, device_id=None):
72 print('Taking screenshot to %s' % target)
73 tmp = '/sdcard/screencap.png'
74 cmd = create_adb_cmd('shell screencap -p %s' % tmp, device_id)
75 subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL)
76 pull(tmp, target, device_id)
77
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010078def create_adb_cmd(arguments, device_id=None):
79 assert isinstance(arguments, list) or isinstance(arguments, str)
80 cmd = ['adb']
81 if device_id is not None:
82 cmd.append('-s')
83 cmd.append(device_id)
84 cmd.extend(arguments if isinstance(arguments, list) else arguments.split(' '))
85 return cmd
86
Christoffer Quist Adamsen2771c362022-03-14 15:36:43 +010087def capture_app_profile_data(app_id, device_id=None):
88 cmd = create_adb_cmd(
89 'shell killall -s SIGUSR1 %s' % app_id, device_id)
Christoffer Quist Adamsenc6b19de2022-04-21 13:07:53 +020090 subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL)
Christoffer Quist Adamsen2771c362022-03-14 15:36:43 +010091 time.sleep(5)
92
93def check_app_has_profile_data(app_id, device_id=None):
94 profile_path = get_profile_path(app_id)
95 cmd = create_adb_cmd(
96 'shell du /data/misc/profiles/cur/0/%s/primary.prof' % app_id,
97 device_id)
98 stdout = subprocess.check_output(cmd).decode('utf-8').strip()
99 size_str = stdout[:stdout.index('\t')]
100 assert size_str.isdigit()
101 size = int(size_str)
102 if size == 4:
103 raise ValueError('Expected size of profile at %s to be > 4K' % profile_path)
104
Christoffer Quist Adamsenc6b19de2022-04-21 13:07:53 +0200105def clear_logcat(device_id=None):
106 cmd = create_adb_cmd('logcat -c', device_id)
107 subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL)
108
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100109def clear_profile_data(app_id, device_id=None):
110 cmd = create_adb_cmd(
111 'shell cmd package compile --reset %s' % app_id, device_id)
112 subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL)
113
114def drop_caches(device_id=None):
115 cmd = create_adb_cmd(
116 ['shell', 'echo 3 > /proc/sys/vm/drop_caches'], device_id)
117 subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL)
118
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200119def ensure_screen_on(device_id=None):
120 if get_screen_state(device_id).is_off():
121 toggle_screen(device_id)
Christoffer Quist Adamsen7a0014f2022-05-19 08:43:00 +0200122 assert get_screen_state(device_id).is_on()
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200123
124def ensure_screen_off(device_id=None):
125 if get_screen_state(device_id).is_on():
126 toggle_screen(device_id)
Christoffer Quist Adamsen7a0014f2022-05-19 08:43:00 +0200127 assert get_screen_state(device_id).is_off()
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200128
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100129def force_compilation(app_id, device_id=None):
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +0200130 print('Applying AOT (full)')
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100131 cmd = create_adb_cmd(
132 'shell cmd package compile -m speed -f %s' % app_id, device_id)
133 subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL)
134
135def force_profile_compilation(app_id, device_id=None):
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +0200136 print('Applying AOT (profile)')
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100137 cmd = create_adb_cmd(
138 'shell cmd package compile -m speed-profile -f %s' % app_id, device_id)
139 subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL)
140
Christoffer Quist Adamsen2771c362022-03-14 15:36:43 +0100141def get_apk_path(app_id, device_id=None):
142 cmd = create_adb_cmd('shell pm path %s' % app_id, device_id)
143 stdout = subprocess.check_output(cmd).decode('utf-8').strip()
144 if not stdout.startswith('package:'):
145 raise ValueError(
146 'Expected stdout to start with "package:", was: %s' % stdout)
147 apk_path = stdout[len('package:'):]
148 if not apk_path.endswith('.apk'):
149 raise ValueError(
150 'Expected stdout to end with ".apk", was: %s' % stdout)
151 return apk_path
152
Christoffer Quist Adamsenc6b19de2022-04-21 13:07:53 +0200153def get_profile_data(app_id, device_id=None):
154 with utils.TempDir() as temp:
155 source = get_profile_path(app_id)
156 target = os.path.join(temp, 'primary.prof')
Christoffer Quist Adamsened805c52022-08-08 14:46:29 +0200157 pull(source, target, device_id)
Christoffer Quist Adamsenc6b19de2022-04-21 13:07:53 +0200158 with open(target, 'rb') as f:
159 return f.read()
160
Christoffer Quist Adamsen2771c362022-03-14 15:36:43 +0100161def get_profile_path(app_id):
162 return '/data/misc/profiles/cur/0/%s/primary.prof' % app_id
163
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100164def get_minor_major_page_faults(app_id, device_id=None):
165 pid = get_pid(app_id, device_id)
Christoffer Quist Adamsen2d5d9632022-04-06 14:48:30 +0200166 cmd = create_adb_cmd('shell ps -p %i -o MINFL,MAJFL' % pid, device_id)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100167 stdout = subprocess.check_output(cmd).decode('utf-8')
168 lines_it = iter(stdout.splitlines())
169 first_line = next(lines_it)
170 assert first_line == ' MINFL MAJFL'
171 second_line = next(lines_it)
172 minfl, majfl = second_line.split()
173 assert minfl.isdigit()
174 assert majfl.isdigit()
175 return (int(minfl), int(majfl))
176
177def get_pid(app_id, device_id=None):
178 cmd = create_adb_cmd('shell pidof %s' % app_id, device_id)
179 stdout = subprocess.check_output(cmd).decode('utf-8').strip()
180 assert stdout.isdigit()
181 pid = int(stdout)
182 return pid
183
184def get_screen_state(device_id=None):
185 cmd = create_adb_cmd('shell dumpsys nfc', device_id)
186 stdout = subprocess.check_output(cmd).decode('utf-8').strip()
187 screen_state_value = None
188 for line in stdout.splitlines():
189 if line.startswith('mScreenState='):
190 value_start_index = len('mScreenState=')
191 screen_state_value=line[value_start_index:]
192 if screen_state_value is None:
193 raise ValueError('Expected to find mScreenState in: adb shell dumpsys nfc')
194 if not hasattr(ScreenState, screen_state_value):
195 raise ValueError(
196 'Expected mScreenState to be a value of ScreenState, was: %s'
197 % screen_state_value)
198 return ScreenState[screen_state_value]
199
Christoffer Quist Adamsen2771c362022-03-14 15:36:43 +0100200def get_classes_and_methods_from_app_profile(app_id, device_id=None):
201 apk_path = get_apk_path(app_id, device_id)
202 profile_path = get_profile_path(app_id)
Christoffer Quist Adamsen2771c362022-03-14 15:36:43 +0100203 cmd = create_adb_cmd(
204 'shell profman --dump-classes-and-methods'
205 ' --profile-file=%s --apk=%s --dex-location=%s'
Christoffer Quist Adamsen2d5d9632022-04-06 14:48:30 +0200206 % (profile_path, apk_path, apk_path), device_id)
Christoffer Quist Adamsen2771c362022-03-14 15:36:43 +0100207 stdout = subprocess.check_output(cmd).decode('utf-8').strip()
208 lines = stdout.splitlines()
Christoffer Quist Adamsen1c07af92022-08-11 13:46:25 +0200209 return profile_utils.parse_art_profile(lines)
Christoffer Quist Adamsen2771c362022-03-14 15:36:43 +0100210
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100211def get_screen_off_timeout(device_id=None):
212 cmd = create_adb_cmd(
213 'shell settings get system screen_off_timeout', device_id)
214 stdout = subprocess.check_output(cmd).decode('utf-8').strip()
215 assert stdout.isdigit()
216 screen_off_timeout = int(stdout)
217 return screen_off_timeout
218
219def install(apk, device_id=None):
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +0200220 print('Installing %s' % apk)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100221 cmd = create_adb_cmd('install %s' % apk, device_id)
222 stdout = subprocess.check_output(cmd).decode('utf-8')
223 assert 'Success' in stdout
224
Christoffer Quist Adamsen6e965502022-08-15 10:21:07 +0200225def install_apks(apks, device_id=None, max_attempts=3):
Christoffer Quist Adamsen4d9fc512022-08-11 19:59:44 +0200226 print('Installing %s' % apks)
227 cmd = [
228 'java', '-jar', utils.BUNDLETOOL_JAR,
229 'install-apks',
230 '--apks=%s' % apks]
231 if device_id is not None:
232 cmd.append('--device-id=%s' % device_id)
Christoffer Quist Adamsen6e965502022-08-15 10:21:07 +0200233 for i in range(max_attempts):
234 process_result = subprocess.run(cmd, capture_output=True)
235 stdout = process_result.stdout.decode('utf-8')
236 stderr = process_result.stderr.decode('utf-8')
237 if process_result.returncode == 0:
238 return
239 print('Failed to install %s' % apks)
240 print('Stdout: %s' % stdout)
241 print('Stderr: %s' % stderr)
242 print('Retrying...')
243 raise Exception('Unable to install apks in %s attempts' % max_attempts)
Christoffer Quist Adamsen4d9fc512022-08-11 19:59:44 +0200244
245def install_bundle(bundle, device_id=None):
246 print('Installing %s' % bundle)
247 with utils.TempDir() as temp:
248 apks = os.path.join(temp, 'Bundle.apks')
249 build_apks_from_bundle(bundle, apks)
250 install_apks(apks, device_id)
251
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +0200252def install_profile(app_id, device_id=None):
253 # This assumes that the profileinstaller library has been added to the app,
254 # https://developer.android.com/jetpack/androidx/releases/profileinstaller.
255 action = 'androidx.profileinstaller.action.INSTALL_PROFILE'
256 component = '%s/androidx.profileinstaller.ProfileInstallReceiver' % app_id
257 stdout = broadcast(action, component, device_id)
258 assert len(stdout) == 2
259 assert stdout[0] == ('Broadcasting: Intent { act=%s flg=0x400000 cmp=%s }' % (action, component))
260 assert stdout[1] == 'Broadcast completed: result=1', stdout[1]
261 stop_app(app_id, device_id)
262 force_profile_compilation(app_id, device_id)
263
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100264def issue_key_event(key_event, device_id=None, sleep_in_seconds=1):
265 cmd = create_adb_cmd('shell input keyevent %s' % key_event, device_id)
266 stdout = subprocess.check_output(cmd).decode('utf-8').strip()
267 assert len(stdout) == 0
268 time.sleep(sleep_in_seconds)
269
Christoffer Quist Adamsenea091052022-03-16 13:28:20 +0100270def launch_activity(
271 app_id, activity, device_id=None, wait_for_activity_to_launch=False):
272 args = ['shell', 'am', 'start', '-n', '%s/%s' % (app_id, activity)]
273 if wait_for_activity_to_launch:
274 args.append('-W')
275 cmd = create_adb_cmd(args, device_id)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100276 stdout = subprocess.check_output(cmd).decode('utf-8').strip()
Christoffer Quist Adamsen13e43132022-08-05 11:31:56 +0200277 if activity.startswith(app_id):
278 expected_stdout = (
279 'Starting: Intent { cmp=%s/.%s }' % (app_id, activity[len(app_id)+1:]))
280 else:
281 expected_stdout = 'Starting: Intent { cmp=%s/%s }' % (app_id, activity)
282 assert stdout.startswith(expected_stdout), 'was %s, expected %s' % (stdout, expected_stdout)
Christoffer Quist Adamsenea091052022-03-16 13:28:20 +0100283 lines = stdout.splitlines()
284 result = {}
285 for line in lines:
286 if line.startswith('TotalTime: '):
287 total_time_str = line.removeprefix('TotalTime: ')
288 assert total_time_str.isdigit()
289 result['total_time'] = int(total_time_str)
Christoffer Quist Adamsen6e965502022-08-15 10:21:07 +0200290 assert not wait_for_activity_to_launch or 'total_time' in result, lines
Christoffer Quist Adamsenea091052022-03-16 13:28:20 +0100291 return result
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100292
Christoffer Quist Adamsen1786a592022-04-21 13:13:04 +0200293def navigate_to_home_screen(device_id=None):
294 cmd = create_adb_cmd('shell input keyevent KEYCODE_HOME', device_id)
295 subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL)
296
Christoffer Quist Adamsen2771c362022-03-14 15:36:43 +0100297def prepare_for_interaction_with_device(device_id=None, device_pin=None):
298 # Increase screen off timeout to avoid device screen turns off.
299 twenty_four_hours_in_millis = 24 * 60 * 60 * 1000
300 previous_screen_off_timeout = get_screen_off_timeout(device_id)
301 set_screen_off_timeout(twenty_four_hours_in_millis, device_id)
302
303 # Unlock device.
304 unlock(device_id, device_pin)
305
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200306 teardown_options = {
Christoffer Quist Adamsen2771c362022-03-14 15:36:43 +0100307 'previous_screen_off_timeout': previous_screen_off_timeout
308 }
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200309 return teardown_options
Christoffer Quist Adamsen2771c362022-03-14 15:36:43 +0100310
Christoffer Quist Adamsened805c52022-08-08 14:46:29 +0200311def pull(source, target, device_id=None):
312 cmd = create_adb_cmd('pull %s %s' % (source, target), device_id)
313 subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL)
314
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100315def root(device_id=None):
316 cmd = create_adb_cmd('root', device_id)
317 subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL)
318
319def set_screen_off_timeout(screen_off_timeout_in_millis, device_id=None):
320 cmd = create_adb_cmd(
321 'shell settings put system screen_off_timeout %i'
322 % screen_off_timeout_in_millis,
323 device_id)
324 stdout = subprocess.check_output(cmd).decode('utf-8').strip()
325 assert len(stdout) == 0
326
Christoffer Quist Adamsenc6b19de2022-04-21 13:07:53 +0200327def start_logcat(device_id=None, format=None, filter=None):
328 args = ['logcat']
329 if format:
330 args.extend(['--format', format])
331 if filter:
332 args.append(filter)
333 cmd = create_adb_cmd(args, device_id)
334 logcat_process = subprocess.Popen(
335 cmd, bufsize=1024*1024, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
336 reader = ProcessReader(logcat_process)
337 reader.start()
338 return reader
339
340def stop_logcat(logcat_reader):
341 logcat_reader.stop()
342 logcat_reader.join()
343 return logcat_reader.lines
344
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100345def stop_app(app_id, device_id=None):
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +0200346 print('Shutting down %s' % app_id)
Christoffer Quist Adamsen2771c362022-03-14 15:36:43 +0100347 cmd = create_adb_cmd('shell am force-stop %s' % app_id, device_id)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100348 subprocess.check_call(cmd, stdout=DEVNULL, stderr=DEVNULL)
349
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200350def teardown_after_interaction_with_device(teardown_options, device_id=None):
Christoffer Quist Adamsen2771c362022-03-14 15:36:43 +0100351 # Reset screen off timeout.
352 set_screen_off_timeout(
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200353 teardown_options['previous_screen_off_timeout'],
Christoffer Quist Adamsen2771c362022-03-14 15:36:43 +0100354 device_id)
355
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200356def toggle_screen(device_id=None):
357 issue_key_event('KEYCODE_POWER', device_id)
358
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100359def uninstall(app_id, device_id=None):
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +0200360 print('Uninstalling %s' % app_id)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100361 cmd = create_adb_cmd('uninstall %s' % app_id, device_id)
362 process_result = subprocess.run(cmd, capture_output=True)
363 stdout = process_result.stdout.decode('utf-8')
364 stderr = process_result.stderr.decode('utf-8')
365 if process_result.returncode == 0:
366 assert 'Success' in stdout
Christoffer Quist Adamsen2a87a6e2022-08-05 11:32:11 +0200367 elif stdout.startswith('cmd: Failure calling service package: Broken pipe'):
368 assert app_id == 'com.google.android.youtube'
369 print('Waiting after broken pipe')
370 time.sleep(15)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100371 else:
372 expected_error = (
373 'java.lang.IllegalArgumentException: Unknown package: %s' % app_id)
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +0200374 assert 'Failure [DELETE_FAILED_INTERNAL_ERROR]' in stdout \
Christoffer Quist Adamsen13e43132022-08-05 11:31:56 +0200375 or expected_error in stderr, \
376 'stdout: %s, stderr: %s' % (stdout, stderr)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100377
378def unlock(device_id=None, device_pin=None):
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200379 ensure_screen_on(device_id)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100380 screen_state = get_screen_state(device_id)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100381 assert screen_state.is_on(), 'was %s' % screen_state
382 if screen_state.is_on_and_locked():
383 if device_pin is not None:
384 raise NotImplementedError('Device unlocking with pin not implemented')
385 issue_key_event('KEYCODE_MENU', device_id)
386 screen_state = get_screen_state(device_id)
387 assert screen_state.is_on_and_unlocked(), 'was %s' % screen_state
Christoffer Quist Adamsen21f7f352022-05-30 19:52:43 +0200388
389def parse_options(argv):
390 result = argparse.ArgumentParser(description='Run adb utils.')
Christoffer Quist Adamsened805c52022-08-08 14:46:29 +0200391 result.add_argument('--capture-screen',
392 help='Capture screen to given file')
Christoffer Quist Adamsen21f7f352022-05-30 19:52:43 +0200393 result.add_argument('--device-id',
394 help='Device id (e.g., emulator-5554).')
395 result.add_argument('--device-pin',
396 help='Device pin code (e.g., 1234)')
397 result.add_argument('--ensure-screen-off',
398 help='Ensure screen off',
399 action='store_true',
400 default=False)
401 result.add_argument('--get-screen-state',
402 help='Get screen state',
403 action='store_true',
404 default=False)
405 result.add_argument('--unlock',
406 help='Unlock device',
407 action='store_true',
408 default=False)
409 options, args = result.parse_known_args(argv)
410 return options, args
411
412def main(argv):
413 (options, args) = parse_options(argv)
Christoffer Quist Adamsened805c52022-08-08 14:46:29 +0200414 if options.capture_screen:
415 capture_screen(options.capture_screen, options.device_id)
Christoffer Quist Adamsen21f7f352022-05-30 19:52:43 +0200416 if options.ensure_screen_off:
417 ensure_screen_off(options.device_id)
418 elif options.get_screen_state:
419 print(get_screen_state(options.device_id))
420 elif options.unlock:
421 unlock(options.device_id, options.device_pin)
422
423
424if __name__ == '__main__':
425 sys.exit(main(sys.argv[1:]))