blob: 322dfbe1104e56217d49618dd6eb86e819055d36 [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
6import argparse
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +02007import datetime
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +01008import os
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +02009import re
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +020010import statistics
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010011import sys
12import time
13
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010014sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
15
16import adb_utils
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +020017import apk_utils
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010018import perfetto_utils
19import utils
20
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020021
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010022def setup(options):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020023 # Increase screen off timeout to avoid device screen turns off.
24 twenty_four_hours_in_millis = 24 * 60 * 60 * 1000
25 previous_screen_off_timeout = adb_utils.get_screen_off_timeout(
26 options.device_id)
27 adb_utils.set_screen_off_timeout(twenty_four_hours_in_millis,
28 options.device_id)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010029
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020030 # Unlock device.
31 adb_utils.unlock(options.device_id, options.device_pin)
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010032
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020033 teardown_options = {
34 'previous_screen_off_timeout': previous_screen_off_timeout
35 }
36 return teardown_options
37
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010038
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +020039def teardown(options, teardown_options):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020040 # Reset screen off timeout.
41 adb_utils.set_screen_off_timeout(
42 teardown_options['previous_screen_off_timeout'], options.device_id)
43
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010044
Christoffer Quist Adamsendab3b202022-08-15 09:36:28 +020045def run_all(apk_or_apks, options, tmp_dir):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020046 # Launch app while collecting information.
47 data_total = {}
48 for iteration in range(1, options.iterations + 1):
49 print('Starting iteration %i' % iteration)
50 out_dir = os.path.join(options.out_dir, str(iteration))
51 teardown_options = setup_for_run(apk_or_apks, out_dir, options)
52 data = run(out_dir, options, tmp_dir)
53 teardown_for_run(out_dir, options, teardown_options)
54 add_data(data_total, data)
55 print('Result:')
56 print(data)
57 print(compute_data_summary(data_total))
58 print('Done')
59 print('Average result:')
60 data_summary = compute_data_summary(data_total)
61 print(data_summary)
62 write_data_to_dir(options.out_dir, data_summary)
63 if options.out:
64 write_data_to_file(options.out, data_summary)
65
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +010066
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +020067def compute_data_summary(data_total):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020068 data_summary = {}
69 for key, value in data_total.items():
70 if not isinstance(value, list):
71 data_summary[key] = value
72 continue
73 data_summary['%s_avg' % key] = round(statistics.mean(value), 1)
74 data_summary['%s_med' % key] = statistics.median(value)
75 data_summary['%s_min' % key] = min(value)
76 data_summary['%s_max' % key] = max(value)
77 return data_summary
78
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +020079
Christoffer Quist Adamsendab3b202022-08-15 09:36:28 +020080def setup_for_run(apk_or_apks, out_dir, options):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020081 adb_utils.root(options.device_id)
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +020082
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020083 print('Installing')
84 adb_utils.uninstall(options.app_id, options.device_id)
85 if apk_or_apks['apk']:
86 adb_utils.install(apk_or_apks['apk'], options.device_id)
Christoffer Quist Adamsen3f7e4282022-04-21 13:12:31 +020087 else:
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020088 assert apk_or_apks['apks']
89 adb_utils.install_apks(apk_or_apks['apks'], options.device_id)
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +020090
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020091 os.makedirs(out_dir, exist_ok=True)
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +020092
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +020093 # Grant notifications.
94 if options.grant_post_notification_permission:
95 adb_utils.grant(options.app_id, 'android.permission.POST_NOTIFICATIONS',
96 options.device_id)
97
98 # AOT compile.
99 if options.aot:
100 print('AOT compiling')
101 if options.baseline_profile:
102 adb_utils.clear_profile_data(options.app_id, options.device_id)
103 if options.baseline_profile_install == 'adb':
104 adb_utils.install_profile_using_adb(options.app_id,
105 options.baseline_profile,
106 options.device_id)
107 else:
108 assert options.baseline_profile_install == 'profileinstaller'
109 adb_utils.install_profile_using_profileinstaller(
110 options.app_id, options.device_id)
111 else:
112 adb_utils.force_compilation(options.app_id, options.device_id)
113
114 # Cooldown and then unlock device.
115 if options.cooldown > 0:
116 print('Cooling down for %i seconds' % options.cooldown)
117 assert adb_utils.get_screen_state(options.device_id).is_off()
118 time.sleep(options.cooldown)
119 teardown_options = adb_utils.prepare_for_interaction_with_device(
120 options.device_id, options.device_pin)
121 else:
122 teardown_options = None
123
124 # Prelaunch for hot startup.
125 if options.hot_startup:
126 print('Prelaunching')
127 adb_utils.launch_activity(options.app_id,
128 options.main_activity,
129 options.device_id,
130 wait_for_activity_to_launch=False)
131 time.sleep(options.startup_duration)
132 adb_utils.navigate_to_home_screen(options.device_id)
133 time.sleep(1)
134
135 # Drop caches before run.
136 adb_utils.drop_caches(options.device_id)
137 return teardown_options
138
139
140def teardown_for_run(out_dir, options, teardown_options):
Christoffer Adamsen456ea7f2024-07-01 15:00:29 +0200141 assert adb_utils.get_screen_state(
142 options.device_id).is_on_and_unlocked_or_unknown()
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200143
144 if options.capture_screen:
145 target = os.path.join(out_dir, 'screen.png')
146 adb_utils.capture_screen(target, options.device_id)
147
148 if options.cooldown > 0:
149 adb_utils.teardown_after_interaction_with_device(
150 teardown_options, options.device_id)
151 adb_utils.ensure_screen_off(options.device_id)
152 else:
153 assert teardown_options is None
154
155
156def run(out_dir, options, tmp_dir):
Christoffer Adamsen456ea7f2024-07-01 15:00:29 +0200157 assert adb_utils.get_screen_state(
158 options.device_id).is_on_and_unlocked_or_unknown()
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200159
160 # Start logcat for time to fully drawn.
161 logcat_process = None
162 if options.fully_drawn_logcat_message:
163 adb_utils.clear_logcat(options.device_id)
164 logcat_process = adb_utils.start_logcat(
165 options.device_id,
166 format='time',
167 filter='%s ActivityTaskManager:I' %
168 options.fully_drawn_logcat_filter,
169 silent=True)
170
171 # Start perfetto trace collector.
172 perfetto_process = None
173 perfetto_trace_path = None
174 if options.perfetto:
175 perfetto_process, perfetto_trace_path = perfetto_utils.record_android_trace(
176 out_dir, tmp_dir, options.device_id)
177
178 # Launch main activity.
179 launch_activity_result = adb_utils.launch_activity(
Christoffer Quist Adamsen1786a592022-04-21 13:13:04 +0200180 options.app_id,
181 options.main_activity,
182 options.device_id,
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200183 intent_data_uri=options.intent_data_uri,
184 wait_for_activity_to_launch=True)
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200185
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200186 # Wait for app to be fully drawn.
187 logcat = None
188 if logcat_process is not None:
189 wait_until_fully_drawn(logcat_process, options)
190 logcat = adb_utils.stop_logcat(logcat_process)
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200191
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200192 # Wait for perfetto trace collector to stop.
193 if options.perfetto:
194 perfetto_utils.stop_record_android_trace(perfetto_process, out_dir)
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200195
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200196 # Get minor and major page faults from app process.
197 data = compute_data(launch_activity_result, logcat, perfetto_trace_path,
198 options)
199 write_data_to_dir(out_dir, data)
200 return data
Christoffer Quist Adamsened805c52022-08-08 14:46:29 +0200201
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100202
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200203def wait_until_fully_drawn(logcat_process, options):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200204 print('Waiting until app is fully drawn')
205 while True:
206 is_fully_drawn = any(
207 is_app_fully_drawn_logcat_message(line, options) \
208 for line in logcat_process.lines)
209 if is_fully_drawn:
210 break
211 time.sleep(1)
212 print('Done')
213
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200214
215def compute_time_to_fully_drawn_from_time_to_first_frame(logcat, options):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200216 displayed_time = None
217 fully_drawn_time = None
218 for line in logcat:
219 if is_app_displayed_logcat_message(line, options):
220 displayed_time = get_timestamp_from_logcat_message(line)
221 elif is_app_fully_drawn_logcat_message(line, options):
222 fully_drawn_time = get_timestamp_from_logcat_message(line)
223 assert displayed_time is not None
224 assert fully_drawn_time is not None
225 assert fully_drawn_time >= displayed_time
226 return fully_drawn_time - displayed_time
227
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200228
229def get_timestamp_from_logcat_message(line):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200230 time_end_index = len('00-00 00:00:00.000')
231 time_format = '%m-%d %H:%M:%S.%f'
232 time_str = line[0:time_end_index] + '000'
233 time_seconds = datetime.datetime.strptime(time_str, time_format).timestamp()
234 return int(time_seconds * 1000)
235
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200236
237def is_app_displayed_logcat_message(line, options):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200238 substring = 'Displayed %s' % adb_utils.get_component_name(
239 options.app_id, options.main_activity)
240 return substring in line
241
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200242
243def is_app_fully_drawn_logcat_message(line, options):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200244 return re.search(options.fully_drawn_logcat_message, line)
245
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200246
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200247def add_data(data_total, data):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200248 for key, value in data.items():
249 if key == 'app_id':
250 assert data_total.get(key, value) == value
251 data_total[key] = value
252 if key == 'time':
253 continue
254 if key in data_total:
255 if key == 'app_id':
256 assert data_total[key] == value
257 else:
258 existing_value = data_total[key]
259 assert isinstance(value, int)
260 assert isinstance(existing_value, list)
261 existing_value.append(value)
262 else:
263 assert isinstance(value, int), key
264 data_total[key] = [value]
265
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100266
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200267def compute_data(launch_activity_result, logcat, perfetto_trace_path, options):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200268 minfl, majfl = adb_utils.get_minor_major_page_faults(
269 options.app_id, options.device_id)
270 meminfo = adb_utils.get_meminfo(options.app_id, options.device_id)
271 data = {
272 'app_id': options.app_id,
273 'time': time.ctime(time.time()),
274 'minfl': minfl,
275 'majfl': majfl
276 }
277 data.update(meminfo)
278 startup_data = compute_startup_data(launch_activity_result, logcat,
279 perfetto_trace_path, options)
280 return data | startup_data
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100281
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200282
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200283def compute_startup_data(launch_activity_result, logcat, perfetto_trace_path,
284 options):
285 time_to_first_frame = launch_activity_result.get('total_time')
286 startup_data = {'adb_startup': time_to_first_frame}
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200287
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200288 # Time to fully drawn.
289 if options.fully_drawn_logcat_message:
290 startup_data['time_to_fully_drawn'] = \
291 compute_time_to_fully_drawn_from_time_to_first_frame(logcat, options) \
292 + time_to_first_frame
Christoffer Quist Adamsenea091052022-03-16 13:28:20 +0100293
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200294 # Perfetto stats.
295 perfetto_startup_data = {}
296 if options.perfetto:
297 TraceProcessor = perfetto_utils.get_trace_processor()
298 trace_processor = TraceProcessor(file_path=perfetto_trace_path)
Christoffer Quist Adamsenea091052022-03-16 13:28:20 +0100299
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200300 # Compute time to first frame according to the builtin android_startup
301 # metric.
302 startup_metric = trace_processor.metric(['android_startup'])
303 time_to_first_frame_ms = \
304 startup_metric.android_startup.startup[0].to_first_frame.dur_ms
305 perfetto_startup_data['perfetto_startup'] = round(
306 time_to_first_frame_ms)
Christoffer Quist Adamsenea091052022-03-16 13:28:20 +0100307
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200308 if not options.hot_startup:
309 # Compute time to first and last doFrame event.
310 bind_application_slice = perfetto_utils.find_unique_slice_by_name(
311 'bindApplication', options, trace_processor)
312 activity_start_slice = perfetto_utils.find_unique_slice_by_name(
313 'activityStart', options, trace_processor)
314 do_frame_slices = perfetto_utils.find_slices_by_name(
315 'Choreographer#doFrame', options, trace_processor)
316 first_do_frame_slice = next(do_frame_slices)
317 *_, last_do_frame_slice = do_frame_slices
Christoffer Quist Adamsenea091052022-03-16 13:28:20 +0100318
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200319 perfetto_startup_data.update({
320 'time_to_first_choreographer_do_frame_ms':
321 round(
322 perfetto_utils.get_slice_end_since_start(
323 first_do_frame_slice, bind_application_slice)),
324 'time_to_last_choreographer_do_frame_ms':
325 round(
326 perfetto_utils.get_slice_end_since_start(
327 last_do_frame_slice, bind_application_slice))
328 })
329
330 # Return combined startup data.
331 return startup_data | perfetto_startup_data
332
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100333
Christoffer Quist Adamseneb29bfe2023-02-21 14:50:22 +0100334def write_data_to_dir(out_dir, data):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200335 data_path = os.path.join(out_dir, 'data.txt')
336 write_data_to_file(data_path, data)
337
Christoffer Quist Adamseneb29bfe2023-02-21 14:50:22 +0100338
339def write_data_to_file(out_file, data):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200340 with open(out_file, 'w') as f:
341 for key, value in data.items():
342 f.write('%s=%s\n' % (key, str(value)))
343
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100344
345def parse_options(argv):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200346 result = argparse.ArgumentParser(
347 description='Generate a perfetto trace file.')
348 result.add_argument('--app-id',
349 help='The application ID of interest',
350 required=True)
351 result.add_argument('--aot',
352 help='Enable force compilation',
353 default=False,
354 action='store_true')
355 result.add_argument('--apk', help='Path to the .apk')
356 result.add_argument('--apks', help='Path to the .apks')
357 result.add_argument('--bundle', help='Path to the .aab')
358 result.add_argument('--capture-screen',
359 help='Take a screenshot after each test',
360 default=False,
361 action='store_true')
362 result.add_argument('--cooldown',
363 help='Seconds to wait before running each iteration',
364 default=0,
365 type=int)
366 result.add_argument('--device-id', help='Device id (e.g., emulator-5554).')
367 result.add_argument('--device-pin', help='Device pin code (e.g., 1234)')
368 result.add_argument('--fully-drawn-logcat-filter',
369 help='Logcat filter for the fully drawn message '
370 '(e.g., "tag:I")')
371 result.add_argument('--fully-drawn-logcat-message',
372 help='Logcat message that indicates that the app is '
373 'fully drawn (regexp)')
374 result.add_argument('--grant-post-notification-permission',
375 help='Grants the android.permission.POST_NOTIFICATIONS '
376 'permission before launching the app',
377 default=False,
378 action='store_true')
379 result.add_argument('--hot-startup',
380 help='Measure hot startup instead of cold startup',
381 default=False,
382 action='store_true')
383 result.add_argument('--intent-data-uri',
384 help='Value to use for the -d argument to the intent '
385 'that is used to launch the app')
386 result.add_argument('--iterations',
387 help='Number of traces to generate',
388 default=1,
389 type=int)
390 result.add_argument('--main-activity',
391 help='Main activity class name',
392 required=True)
393 result.add_argument('--no-perfetto',
394 help='Disables perfetto trace generation',
395 action='store_true',
396 default=False)
397 result.add_argument('--out', help='File to store result in')
398 result.add_argument('--out-dir',
399 help='Directory to store trace files in',
400 required=True)
401 result.add_argument('--baseline-profile',
402 help='Baseline profile (.prof) in binary format')
403 result.add_argument('--baseline-profile-metadata',
404 help='Baseline profile metadata (.profm) in binary '
405 'format')
406 result.add_argument('--baseline-profile-install',
407 help='Whether to install profile using adb or '
408 'profileinstaller',
409 choices=['adb', 'profileinstaller'],
410 default='profileinstaller')
411 result.add_argument('--startup-duration',
412 help='Duration in seconds before shutting down app',
413 default=15,
414 type=int)
415 options, args = result.parse_known_args(argv)
416 setattr(options, 'perfetto', not options.no_perfetto)
Christoffer Quist Adamsendab3b202022-08-15 09:36:28 +0200417
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200418 paths = [
419 path for path in [options.apk, options.apks, options.bundle]
420 if path is not None
421 ]
422 assert len(paths) == 1, 'Expected exactly one .apk, .apks, or .aab file.'
Christoffer Quist Adamsendab3b202022-08-15 09:36:28 +0200423
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200424 # Build .apks file up front to avoid building the bundle upon each install.
425 if options.bundle:
426 os.makedirs(options.out_dir, exist_ok=True)
427 options.apks = os.path.join(options.out_dir, 'Bundle.apks')
428 adb_utils.build_apks_from_bundle(options.bundle,
429 options.apks,
430 overwrite=True)
431 del options.bundle
Christoffer Quist Adamsendab3b202022-08-15 09:36:28 +0200432
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200433 # Profile is only used with --aot.
434 assert options.aot or not options.baseline_profile
Christoffer Quist Adamsendab3b202022-08-15 09:36:28 +0200435
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200436 # Fully drawn logcat filter and message is absent or both present.
437 assert (options.fully_drawn_logcat_filter is None) == \
438 (options.fully_drawn_logcat_message is None)
Christoffer Quist Adamsen85f77482022-08-18 14:31:41 +0200439
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200440 return options, args
441
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100442
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200443def global_setup(options):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200444 # If there is no cooldown then unlock the screen once. Otherwise we turn off
445 # the screen during the cooldown and unlock the screen before each iteration.
446 teardown_options = None
447 if options.cooldown == 0:
448 teardown_options = adb_utils.prepare_for_interaction_with_device(
449 options.device_id, options.device_pin)
Christoffer Adamsen456ea7f2024-07-01 15:00:29 +0200450 assert adb_utils.get_screen_state(options.device_id).is_on_or_unknown()
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200451 else:
452 adb_utils.ensure_screen_off(options.device_id)
453 return teardown_options
454
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200455
456def global_teardown(options, teardown_options):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200457 if options.cooldown == 0:
458 adb_utils.teardown_after_interaction_with_device(
459 teardown_options, options.device_id)
460 else:
461 assert teardown_options is None
462
Christoffer Quist Adamsen1a459b72022-05-11 12:09:03 +0200463
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100464def main(argv):
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200465 (options, args) = parse_options(argv)
466 with utils.TempDir() as tmp_dir:
467 apk_or_apks = {'apk': options.apk, 'apks': options.apks}
468 if options.baseline_profile \
469 and options.baseline_profile_install == 'profileinstaller':
470 assert not options.apks, 'Unimplemented'
471 apk_or_apks['apk'] = apk_utils.add_baseline_profile_to_apk(
472 options.apk, options.baseline_profile,
473 options.baseline_profile_metadata, tmp_dir)
474 teardown_options = global_setup(options)
475 run_all(apk_or_apks, options, tmp_dir)
476 global_teardown(options, teardown_options)
477
Christoffer Quist Adamsenfad33a02022-03-14 14:45:09 +0100478
479if __name__ == '__main__':
Christoffer Quist Adamsen2434a4d2023-10-16 11:29:03 +0200480 sys.exit(main(sys.argv[1:]))