xref: /aosp_15_r20/external/angle/build/android/apk_operations.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1*8975f5c5SAndroid Build Coastguard Worker#!/usr/bin/env vpython3
2*8975f5c5SAndroid Build Coastguard Worker# Copyright 2017 The Chromium Authors
3*8975f5c5SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
4*8975f5c5SAndroid Build Coastguard Worker# found in the LICENSE file.
5*8975f5c5SAndroid Build Coastguard Worker
6*8975f5c5SAndroid Build Coastguard Worker# Using colorama.Fore/Back/Style members
7*8975f5c5SAndroid Build Coastguard Worker# pylint: disable=no-member
8*8975f5c5SAndroid Build Coastguard Worker
9*8975f5c5SAndroid Build Coastguard Worker
10*8975f5c5SAndroid Build Coastguard Workerimport argparse
11*8975f5c5SAndroid Build Coastguard Workerimport collections
12*8975f5c5SAndroid Build Coastguard Workerimport json
13*8975f5c5SAndroid Build Coastguard Workerimport logging
14*8975f5c5SAndroid Build Coastguard Workerimport os
15*8975f5c5SAndroid Build Coastguard Workerimport posixpath
16*8975f5c5SAndroid Build Coastguard Workerimport random
17*8975f5c5SAndroid Build Coastguard Workerimport re
18*8975f5c5SAndroid Build Coastguard Workerimport shlex
19*8975f5c5SAndroid Build Coastguard Workerimport shutil
20*8975f5c5SAndroid Build Coastguard Workerimport subprocess
21*8975f5c5SAndroid Build Coastguard Workerimport sys
22*8975f5c5SAndroid Build Coastguard Workerimport tempfile
23*8975f5c5SAndroid Build Coastguard Workerimport textwrap
24*8975f5c5SAndroid Build Coastguard Workerimport zipfile
25*8975f5c5SAndroid Build Coastguard Worker
26*8975f5c5SAndroid Build Coastguard Workerimport adb_command_line
27*8975f5c5SAndroid Build Coastguard Workerimport devil_chromium
28*8975f5c5SAndroid Build Coastguard Workerfrom devil import devil_env
29*8975f5c5SAndroid Build Coastguard Workerfrom devil.android import apk_helper
30*8975f5c5SAndroid Build Coastguard Workerfrom devil.android import device_errors
31*8975f5c5SAndroid Build Coastguard Workerfrom devil.android import device_utils
32*8975f5c5SAndroid Build Coastguard Workerfrom devil.android.sdk import adb_wrapper
33*8975f5c5SAndroid Build Coastguard Workerfrom devil.android.sdk import build_tools
34*8975f5c5SAndroid Build Coastguard Workerfrom devil.android.sdk import intent
35*8975f5c5SAndroid Build Coastguard Workerfrom devil.android.sdk import version_codes
36*8975f5c5SAndroid Build Coastguard Workerfrom devil.utils import run_tests_helper
37*8975f5c5SAndroid Build Coastguard Worker
38*8975f5c5SAndroid Build Coastguard Worker_DIR_SOURCE_ROOT = os.path.normpath(
39*8975f5c5SAndroid Build Coastguard Worker    os.path.join(os.path.dirname(__file__), '..', '..'))
40*8975f5c5SAndroid Build Coastguard Worker_JAVA_HOME = os.path.join(_DIR_SOURCE_ROOT, 'third_party', 'jdk', 'current')
41*8975f5c5SAndroid Build Coastguard Worker
42*8975f5c5SAndroid Build Coastguard Workerwith devil_env.SysPath(
43*8975f5c5SAndroid Build Coastguard Worker    os.path.join(_DIR_SOURCE_ROOT, 'third_party', 'colorama', 'src')):
44*8975f5c5SAndroid Build Coastguard Worker  import colorama
45*8975f5c5SAndroid Build Coastguard Worker
46*8975f5c5SAndroid Build Coastguard Workerfrom incremental_install import installer
47*8975f5c5SAndroid Build Coastguard Workerfrom pylib import constants
48*8975f5c5SAndroid Build Coastguard Workerfrom pylib.symbols import deobfuscator
49*8975f5c5SAndroid Build Coastguard Workerfrom pylib.utils import simpleperf
50*8975f5c5SAndroid Build Coastguard Workerfrom pylib.utils import app_bundle_utils
51*8975f5c5SAndroid Build Coastguard Worker
52*8975f5c5SAndroid Build Coastguard Workerwith devil_env.SysPath(
53*8975f5c5SAndroid Build Coastguard Worker    os.path.join(_DIR_SOURCE_ROOT, 'build', 'android', 'gyp')):
54*8975f5c5SAndroid Build Coastguard Worker  import bundletool
55*8975f5c5SAndroid Build Coastguard Worker
56*8975f5c5SAndroid Build Coastguard WorkerBASE_MODULE = 'base'
57*8975f5c5SAndroid Build Coastguard Worker
58*8975f5c5SAndroid Build Coastguard Worker
59*8975f5c5SAndroid Build Coastguard Workerdef _Colorize(text, style=''):
60*8975f5c5SAndroid Build Coastguard Worker  return (style
61*8975f5c5SAndroid Build Coastguard Worker      + text
62*8975f5c5SAndroid Build Coastguard Worker      + colorama.Style.RESET_ALL)
63*8975f5c5SAndroid Build Coastguard Worker
64*8975f5c5SAndroid Build Coastguard Worker
65*8975f5c5SAndroid Build Coastguard Workerdef _InstallApk(devices, apk, install_dict):
66*8975f5c5SAndroid Build Coastguard Worker  def install(device):
67*8975f5c5SAndroid Build Coastguard Worker    if install_dict:
68*8975f5c5SAndroid Build Coastguard Worker      installer.Install(device, install_dict, apk=apk, permissions=[])
69*8975f5c5SAndroid Build Coastguard Worker    else:
70*8975f5c5SAndroid Build Coastguard Worker      device.Install(apk, permissions=[], allow_downgrade=True, reinstall=True)
71*8975f5c5SAndroid Build Coastguard Worker
72*8975f5c5SAndroid Build Coastguard Worker  logging.info('Installing %sincremental apk.', '' if install_dict else 'non-')
73*8975f5c5SAndroid Build Coastguard Worker  device_utils.DeviceUtils.parallel(devices).pMap(install)
74*8975f5c5SAndroid Build Coastguard Worker
75*8975f5c5SAndroid Build Coastguard Worker
76*8975f5c5SAndroid Build Coastguard Worker# A named tuple containing the information needed to convert a bundle into
77*8975f5c5SAndroid Build Coastguard Worker# an installable .apks archive.
78*8975f5c5SAndroid Build Coastguard Worker# Fields:
79*8975f5c5SAndroid Build Coastguard Worker#   bundle_path: Path to input bundle file.
80*8975f5c5SAndroid Build Coastguard Worker#   bundle_apk_path: Path to output bundle .apks archive file.
81*8975f5c5SAndroid Build Coastguard Worker#   aapt2_path: Path to aapt2 tool.
82*8975f5c5SAndroid Build Coastguard Worker#   keystore_path: Path to keystore file.
83*8975f5c5SAndroid Build Coastguard Worker#   keystore_password: Password for the keystore file.
84*8975f5c5SAndroid Build Coastguard Worker#   keystore_alias: Signing key name alias within the keystore file.
85*8975f5c5SAndroid Build Coastguard Worker#   system_image_locales: List of Chromium locales to include in system .apks.
86*8975f5c5SAndroid Build Coastguard WorkerBundleGenerationInfo = collections.namedtuple(
87*8975f5c5SAndroid Build Coastguard Worker    'BundleGenerationInfo',
88*8975f5c5SAndroid Build Coastguard Worker    'bundle_path,bundle_apks_path,aapt2_path,keystore_path,keystore_password,'
89*8975f5c5SAndroid Build Coastguard Worker    'keystore_alias,system_image_locales')
90*8975f5c5SAndroid Build Coastguard Worker
91*8975f5c5SAndroid Build Coastguard Worker
92*8975f5c5SAndroid Build Coastguard Workerdef _GenerateBundleApks(info,
93*8975f5c5SAndroid Build Coastguard Worker                        output_path=None,
94*8975f5c5SAndroid Build Coastguard Worker                        minimal=False,
95*8975f5c5SAndroid Build Coastguard Worker                        minimal_sdk_version=None,
96*8975f5c5SAndroid Build Coastguard Worker                        mode=None,
97*8975f5c5SAndroid Build Coastguard Worker                        optimize_for=None):
98*8975f5c5SAndroid Build Coastguard Worker  """Generate an .apks archive from a bundle on demand.
99*8975f5c5SAndroid Build Coastguard Worker
100*8975f5c5SAndroid Build Coastguard Worker  Args:
101*8975f5c5SAndroid Build Coastguard Worker    info: A BundleGenerationInfo instance.
102*8975f5c5SAndroid Build Coastguard Worker    output_path: Path of output .apks archive.
103*8975f5c5SAndroid Build Coastguard Worker    minimal: Create the minimal set of apks possible (english-only).
104*8975f5c5SAndroid Build Coastguard Worker    minimal_sdk_version: When minimal=True, use this sdkVersion.
105*8975f5c5SAndroid Build Coastguard Worker    mode: Build mode, either None, or one of app_bundle_utils.BUILD_APKS_MODES.
106*8975f5c5SAndroid Build Coastguard Worker    optimize_for: Override split config, either None, or one of
107*8975f5c5SAndroid Build Coastguard Worker      app_bundle_utils.OPTIMIZE_FOR_OPTIONS.
108*8975f5c5SAndroid Build Coastguard Worker  """
109*8975f5c5SAndroid Build Coastguard Worker  logging.info('Generating .apks file')
110*8975f5c5SAndroid Build Coastguard Worker  app_bundle_utils.GenerateBundleApks(
111*8975f5c5SAndroid Build Coastguard Worker      info.bundle_path,
112*8975f5c5SAndroid Build Coastguard Worker      # Store .apks file beside the .aab file by default so that it gets cached.
113*8975f5c5SAndroid Build Coastguard Worker      output_path or info.bundle_apks_path,
114*8975f5c5SAndroid Build Coastguard Worker      info.aapt2_path,
115*8975f5c5SAndroid Build Coastguard Worker      info.keystore_path,
116*8975f5c5SAndroid Build Coastguard Worker      info.keystore_password,
117*8975f5c5SAndroid Build Coastguard Worker      info.keystore_alias,
118*8975f5c5SAndroid Build Coastguard Worker      system_image_locales=info.system_image_locales,
119*8975f5c5SAndroid Build Coastguard Worker      mode=mode,
120*8975f5c5SAndroid Build Coastguard Worker      minimal=minimal,
121*8975f5c5SAndroid Build Coastguard Worker      minimal_sdk_version=minimal_sdk_version,
122*8975f5c5SAndroid Build Coastguard Worker      optimize_for=optimize_for)
123*8975f5c5SAndroid Build Coastguard Worker
124*8975f5c5SAndroid Build Coastguard Worker
125*8975f5c5SAndroid Build Coastguard Workerdef _InstallBundle(devices,
126*8975f5c5SAndroid Build Coastguard Worker                   apk_helper_instance,
127*8975f5c5SAndroid Build Coastguard Worker                   modules,
128*8975f5c5SAndroid Build Coastguard Worker                   fake_modules,
129*8975f5c5SAndroid Build Coastguard Worker                   locales=None):
130*8975f5c5SAndroid Build Coastguard Worker
131*8975f5c5SAndroid Build Coastguard Worker  def Install(device):
132*8975f5c5SAndroid Build Coastguard Worker    device.Install(apk_helper_instance,
133*8975f5c5SAndroid Build Coastguard Worker                   permissions=[],
134*8975f5c5SAndroid Build Coastguard Worker                   modules=modules,
135*8975f5c5SAndroid Build Coastguard Worker                   fake_modules=fake_modules,
136*8975f5c5SAndroid Build Coastguard Worker                   additional_locales=locales,
137*8975f5c5SAndroid Build Coastguard Worker                   allow_downgrade=True,
138*8975f5c5SAndroid Build Coastguard Worker                   reinstall=True)
139*8975f5c5SAndroid Build Coastguard Worker
140*8975f5c5SAndroid Build Coastguard Worker  # Basic checks for |modules| and |fake_modules|.
141*8975f5c5SAndroid Build Coastguard Worker  # * |fake_modules| cannot include 'base'.
142*8975f5c5SAndroid Build Coastguard Worker  # * If |fake_modules| is given, ensure |modules| includes 'base'.
143*8975f5c5SAndroid Build Coastguard Worker  # * They must be disjoint (checked by device.Install).
144*8975f5c5SAndroid Build Coastguard Worker  modules_set = set(modules) if modules else set()
145*8975f5c5SAndroid Build Coastguard Worker  fake_modules_set = set(fake_modules) if fake_modules else set()
146*8975f5c5SAndroid Build Coastguard Worker  if BASE_MODULE in fake_modules_set:
147*8975f5c5SAndroid Build Coastguard Worker    raise Exception('\'-f {}\' is disallowed.'.format(BASE_MODULE))
148*8975f5c5SAndroid Build Coastguard Worker  if fake_modules_set and BASE_MODULE not in modules_set:
149*8975f5c5SAndroid Build Coastguard Worker    raise Exception(
150*8975f5c5SAndroid Build Coastguard Worker        '\'-f FAKE\' must be accompanied by \'-m {}\''.format(BASE_MODULE))
151*8975f5c5SAndroid Build Coastguard Worker
152*8975f5c5SAndroid Build Coastguard Worker  logging.info('Installing bundle.')
153*8975f5c5SAndroid Build Coastguard Worker  device_utils.DeviceUtils.parallel(devices).pMap(Install)
154*8975f5c5SAndroid Build Coastguard Worker
155*8975f5c5SAndroid Build Coastguard Worker
156*8975f5c5SAndroid Build Coastguard Workerdef _UninstallApk(devices, install_dict, package_name):
157*8975f5c5SAndroid Build Coastguard Worker  def uninstall(device):
158*8975f5c5SAndroid Build Coastguard Worker    if install_dict:
159*8975f5c5SAndroid Build Coastguard Worker      installer.Uninstall(device, package_name)
160*8975f5c5SAndroid Build Coastguard Worker    else:
161*8975f5c5SAndroid Build Coastguard Worker      device.Uninstall(package_name)
162*8975f5c5SAndroid Build Coastguard Worker  device_utils.DeviceUtils.parallel(devices).pMap(uninstall)
163*8975f5c5SAndroid Build Coastguard Worker
164*8975f5c5SAndroid Build Coastguard Worker
165*8975f5c5SAndroid Build Coastguard Workerdef _IsWebViewProvider(apk_helper_instance):
166*8975f5c5SAndroid Build Coastguard Worker  meta_data = apk_helper_instance.GetAllMetadata()
167*8975f5c5SAndroid Build Coastguard Worker  meta_data_keys = [pair[0] for pair in meta_data]
168*8975f5c5SAndroid Build Coastguard Worker  return 'com.android.webview.WebViewLibrary' in meta_data_keys
169*8975f5c5SAndroid Build Coastguard Worker
170*8975f5c5SAndroid Build Coastguard Worker
171*8975f5c5SAndroid Build Coastguard Workerdef _SetWebViewProvider(devices, package_name):
172*8975f5c5SAndroid Build Coastguard Worker
173*8975f5c5SAndroid Build Coastguard Worker  def switch_provider(device):
174*8975f5c5SAndroid Build Coastguard Worker    if device.build_version_sdk < version_codes.NOUGAT:
175*8975f5c5SAndroid Build Coastguard Worker      logging.error('No need to switch provider on pre-Nougat devices (%s)',
176*8975f5c5SAndroid Build Coastguard Worker                    device.serial)
177*8975f5c5SAndroid Build Coastguard Worker    else:
178*8975f5c5SAndroid Build Coastguard Worker      device.SetWebViewImplementation(package_name)
179*8975f5c5SAndroid Build Coastguard Worker
180*8975f5c5SAndroid Build Coastguard Worker  device_utils.DeviceUtils.parallel(devices).pMap(switch_provider)
181*8975f5c5SAndroid Build Coastguard Worker
182*8975f5c5SAndroid Build Coastguard Worker
183*8975f5c5SAndroid Build Coastguard Workerdef _NormalizeProcessName(debug_process_name, package_name):
184*8975f5c5SAndroid Build Coastguard Worker  if not debug_process_name:
185*8975f5c5SAndroid Build Coastguard Worker    debug_process_name = package_name
186*8975f5c5SAndroid Build Coastguard Worker  elif debug_process_name.startswith(':'):
187*8975f5c5SAndroid Build Coastguard Worker    debug_process_name = package_name + debug_process_name
188*8975f5c5SAndroid Build Coastguard Worker  elif '.' not in debug_process_name:
189*8975f5c5SAndroid Build Coastguard Worker    debug_process_name = package_name + ':' + debug_process_name
190*8975f5c5SAndroid Build Coastguard Worker  return debug_process_name
191*8975f5c5SAndroid Build Coastguard Worker
192*8975f5c5SAndroid Build Coastguard Worker
193*8975f5c5SAndroid Build Coastguard Workerdef _ResolveActivity(device, package_name, category, action):
194*8975f5c5SAndroid Build Coastguard Worker  # E.g.:
195*8975f5c5SAndroid Build Coastguard Worker  # Activity Resolver Table:
196*8975f5c5SAndroid Build Coastguard Worker  #   Schemes:
197*8975f5c5SAndroid Build Coastguard Worker  #     http:
198*8975f5c5SAndroid Build Coastguard Worker  #       67e97c0 org.chromium.pkg/.MainActivityfilter c91d43e
199*8975f5c5SAndroid Build Coastguard Worker  #         Action: "android.intent.action.VIEW"
200*8975f5c5SAndroid Build Coastguard Worker  #         Category: "android.intent.category.DEFAULT"
201*8975f5c5SAndroid Build Coastguard Worker  #         Category: "android.intent.category.BROWSABLE"
202*8975f5c5SAndroid Build Coastguard Worker  #         Scheme: "http"
203*8975f5c5SAndroid Build Coastguard Worker  #         Scheme: "https"
204*8975f5c5SAndroid Build Coastguard Worker  #
205*8975f5c5SAndroid Build Coastguard Worker  #   Non-Data Actions:
206*8975f5c5SAndroid Build Coastguard Worker  #     android.intent.action.MAIN:
207*8975f5c5SAndroid Build Coastguard Worker  #       67e97c0 org.chromium.pkg/.MainActivity filter 4a34cf9
208*8975f5c5SAndroid Build Coastguard Worker  #         Action: "android.intent.action.MAIN"
209*8975f5c5SAndroid Build Coastguard Worker  #         Category: "android.intent.category.LAUNCHER"
210*8975f5c5SAndroid Build Coastguard Worker  lines = device.RunShellCommand(['dumpsys', 'package', package_name],
211*8975f5c5SAndroid Build Coastguard Worker                                 check_return=True)
212*8975f5c5SAndroid Build Coastguard Worker
213*8975f5c5SAndroid Build Coastguard Worker  # Extract the Activity Resolver Table: section.
214*8975f5c5SAndroid Build Coastguard Worker  start_idx = next((i for i, l in enumerate(lines)
215*8975f5c5SAndroid Build Coastguard Worker                    if l.startswith('Activity Resolver Table:')), None)
216*8975f5c5SAndroid Build Coastguard Worker  if start_idx is None:
217*8975f5c5SAndroid Build Coastguard Worker    if not device.IsApplicationInstalled(package_name):
218*8975f5c5SAndroid Build Coastguard Worker      raise Exception('Package not installed: ' + package_name)
219*8975f5c5SAndroid Build Coastguard Worker    raise Exception('No Activity Resolver Table in:\n' + '\n'.join(lines))
220*8975f5c5SAndroid Build Coastguard Worker  line_count = next(i for i, l in enumerate(lines[start_idx + 1:])
221*8975f5c5SAndroid Build Coastguard Worker                    if l and not l[0].isspace())
222*8975f5c5SAndroid Build Coastguard Worker  data = '\n'.join(lines[start_idx:start_idx + line_count])
223*8975f5c5SAndroid Build Coastguard Worker
224*8975f5c5SAndroid Build Coastguard Worker  # Split on each Activity entry.
225*8975f5c5SAndroid Build Coastguard Worker  entries = re.split(r'^        [0-9a-f]+ ', data, flags=re.MULTILINE)
226*8975f5c5SAndroid Build Coastguard Worker
227*8975f5c5SAndroid Build Coastguard Worker  def activity_name_from_entry(entry):
228*8975f5c5SAndroid Build Coastguard Worker    assert entry.startswith(package_name), 'Got: ' + entry
229*8975f5c5SAndroid Build Coastguard Worker    activity_name = entry[len(package_name) + 1:].split(' ', 1)[0]
230*8975f5c5SAndroid Build Coastguard Worker    if activity_name[0] == '.':
231*8975f5c5SAndroid Build Coastguard Worker      activity_name = package_name + activity_name
232*8975f5c5SAndroid Build Coastguard Worker    return activity_name
233*8975f5c5SAndroid Build Coastguard Worker
234*8975f5c5SAndroid Build Coastguard Worker  # Find the one with the text we want.
235*8975f5c5SAndroid Build Coastguard Worker  category_text = f'Category: "{category}"'
236*8975f5c5SAndroid Build Coastguard Worker  action_text = f'Action: "{action}"'
237*8975f5c5SAndroid Build Coastguard Worker  matched_entries = [
238*8975f5c5SAndroid Build Coastguard Worker      e for e in entries[1:] if category_text in e and action_text in e
239*8975f5c5SAndroid Build Coastguard Worker  ]
240*8975f5c5SAndroid Build Coastguard Worker
241*8975f5c5SAndroid Build Coastguard Worker  if not matched_entries:
242*8975f5c5SAndroid Build Coastguard Worker    raise Exception(f'Did not find {category_text}, {action_text} in\n{data}')
243*8975f5c5SAndroid Build Coastguard Worker  if len(matched_entries) > 1:
244*8975f5c5SAndroid Build Coastguard Worker    # When there are multiple matches, look for the one marked as default.
245*8975f5c5SAndroid Build Coastguard Worker    # Necessary for Monochrome, which also has MonochromeLauncherActivity.
246*8975f5c5SAndroid Build Coastguard Worker    default_entries = [
247*8975f5c5SAndroid Build Coastguard Worker        e for e in matched_entries if 'android.intent.category.DEFAULT' in e
248*8975f5c5SAndroid Build Coastguard Worker    ]
249*8975f5c5SAndroid Build Coastguard Worker    matched_entries = default_entries or matched_entries
250*8975f5c5SAndroid Build Coastguard Worker
251*8975f5c5SAndroid Build Coastguard Worker  # See if all matches point to the same activity.
252*8975f5c5SAndroid Build Coastguard Worker  activity_names = {activity_name_from_entry(e) for e in matched_entries}
253*8975f5c5SAndroid Build Coastguard Worker
254*8975f5c5SAndroid Build Coastguard Worker  if len(activity_names) > 1:
255*8975f5c5SAndroid Build Coastguard Worker    raise Exception('Found multiple launcher activities:\n * ' +
256*8975f5c5SAndroid Build Coastguard Worker                    '\n * '.join(sorted(activity_names)))
257*8975f5c5SAndroid Build Coastguard Worker  return next(iter(activity_names))
258*8975f5c5SAndroid Build Coastguard Worker
259*8975f5c5SAndroid Build Coastguard Worker
260*8975f5c5SAndroid Build Coastguard Workerdef _ReadDeviceFlags(device, command_line_flags_file):
261*8975f5c5SAndroid Build Coastguard Worker  device_path = f'/data/local/tmp/{command_line_flags_file}'
262*8975f5c5SAndroid Build Coastguard Worker  old_flags = device.RunShellCommand(f'cat {device_path} 2>/dev/null',
263*8975f5c5SAndroid Build Coastguard Worker                                     as_root=True,
264*8975f5c5SAndroid Build Coastguard Worker                                     shell=True,
265*8975f5c5SAndroid Build Coastguard Worker                                     check_return=False,
266*8975f5c5SAndroid Build Coastguard Worker                                     raw_output=True)
267*8975f5c5SAndroid Build Coastguard Worker  if not old_flags:
268*8975f5c5SAndroid Build Coastguard Worker    return None
269*8975f5c5SAndroid Build Coastguard Worker  if old_flags.startswith('_ '):
270*8975f5c5SAndroid Build Coastguard Worker    old_flags = old_flags[2:]
271*8975f5c5SAndroid Build Coastguard Worker
272*8975f5c5SAndroid Build Coastguard Worker  return old_flags
273*8975f5c5SAndroid Build Coastguard Worker
274*8975f5c5SAndroid Build Coastguard Worker
275*8975f5c5SAndroid Build Coastguard Workerdef _UpdateDeviceFlags(device, command_line_flags_file, new_flags):
276*8975f5c5SAndroid Build Coastguard Worker  if not command_line_flags_file:
277*8975f5c5SAndroid Build Coastguard Worker    if new_flags:
278*8975f5c5SAndroid Build Coastguard Worker      logging.warning('Command-line flags are not configured for this target.')
279*8975f5c5SAndroid Build Coastguard Worker    return
280*8975f5c5SAndroid Build Coastguard Worker
281*8975f5c5SAndroid Build Coastguard Worker  old_flags = _ReadDeviceFlags(device, command_line_flags_file)
282*8975f5c5SAndroid Build Coastguard Worker
283*8975f5c5SAndroid Build Coastguard Worker  if new_flags is None:
284*8975f5c5SAndroid Build Coastguard Worker    if old_flags:
285*8975f5c5SAndroid Build Coastguard Worker      logging.warning('Using pre-existing command-line flags: %s', old_flags)
286*8975f5c5SAndroid Build Coastguard Worker    return
287*8975f5c5SAndroid Build Coastguard Worker
288*8975f5c5SAndroid Build Coastguard Worker  if new_flags != old_flags:
289*8975f5c5SAndroid Build Coastguard Worker    adb_command_line.CheckBuildTypeSupportsFlags(device,
290*8975f5c5SAndroid Build Coastguard Worker                                                 command_line_flags_file)
291*8975f5c5SAndroid Build Coastguard Worker    # This file does not need to be owned by root, but devil's flag_changer
292*8975f5c5SAndroid Build Coastguard Worker    # helper uses as_root, so existing files cannot be updated without it.
293*8975f5c5SAndroid Build Coastguard Worker    device_path = f'/data/local/tmp/{command_line_flags_file}'
294*8975f5c5SAndroid Build Coastguard Worker    if new_flags:
295*8975f5c5SAndroid Build Coastguard Worker      logging.info('Updated flags file: %s with value: %s', device_path,
296*8975f5c5SAndroid Build Coastguard Worker                   new_flags)
297*8975f5c5SAndroid Build Coastguard Worker      device.WriteFile(device_path, '_ ' + new_flags, as_root=True)
298*8975f5c5SAndroid Build Coastguard Worker    else:
299*8975f5c5SAndroid Build Coastguard Worker      logging.info('Removed flags file: %s', device_path)
300*8975f5c5SAndroid Build Coastguard Worker      device.RemovePath(device_path, force=True, as_root=True)
301*8975f5c5SAndroid Build Coastguard Worker
302*8975f5c5SAndroid Build Coastguard Worker
303*8975f5c5SAndroid Build Coastguard Workerdef _LaunchUrl(devices,
304*8975f5c5SAndroid Build Coastguard Worker               package_name,
305*8975f5c5SAndroid Build Coastguard Worker               argv=None,
306*8975f5c5SAndroid Build Coastguard Worker               command_line_flags_file=None,
307*8975f5c5SAndroid Build Coastguard Worker               url=None,
308*8975f5c5SAndroid Build Coastguard Worker               wait_for_java_debugger=False,
309*8975f5c5SAndroid Build Coastguard Worker               debug_process_name=None,
310*8975f5c5SAndroid Build Coastguard Worker               nokill=None):
311*8975f5c5SAndroid Build Coastguard Worker  if argv and command_line_flags_file is None:
312*8975f5c5SAndroid Build Coastguard Worker    raise Exception('This apk does not support any flags.')
313*8975f5c5SAndroid Build Coastguard Worker
314*8975f5c5SAndroid Build Coastguard Worker  debug_process_name = _NormalizeProcessName(debug_process_name, package_name)
315*8975f5c5SAndroid Build Coastguard Worker
316*8975f5c5SAndroid Build Coastguard Worker  if url is None:
317*8975f5c5SAndroid Build Coastguard Worker    category = 'android.intent.category.LAUNCHER'
318*8975f5c5SAndroid Build Coastguard Worker    action = 'android.intent.action.MAIN'
319*8975f5c5SAndroid Build Coastguard Worker  else:
320*8975f5c5SAndroid Build Coastguard Worker    category = 'android.intent.category.BROWSABLE'
321*8975f5c5SAndroid Build Coastguard Worker    action = 'android.intent.action.VIEW'
322*8975f5c5SAndroid Build Coastguard Worker
323*8975f5c5SAndroid Build Coastguard Worker  def launch(device):
324*8975f5c5SAndroid Build Coastguard Worker    activity = _ResolveActivity(device, package_name, category, action)
325*8975f5c5SAndroid Build Coastguard Worker    # --persistent is required to have Settings.Global.DEBUG_APP be set, which
326*8975f5c5SAndroid Build Coastguard Worker    # we currently use to allow reading of flags. https://crbug.com/784947
327*8975f5c5SAndroid Build Coastguard Worker    if not nokill:
328*8975f5c5SAndroid Build Coastguard Worker      cmd = ['am', 'set-debug-app', '--persistent', debug_process_name]
329*8975f5c5SAndroid Build Coastguard Worker      if wait_for_java_debugger:
330*8975f5c5SAndroid Build Coastguard Worker        cmd[-1:-1] = ['-w']
331*8975f5c5SAndroid Build Coastguard Worker      # Ignore error since it will fail if apk is not debuggable.
332*8975f5c5SAndroid Build Coastguard Worker      device.RunShellCommand(cmd, check_return=False)
333*8975f5c5SAndroid Build Coastguard Worker
334*8975f5c5SAndroid Build Coastguard Worker      # The flags are first updated with input args.
335*8975f5c5SAndroid Build Coastguard Worker      _UpdateDeviceFlags(device, command_line_flags_file, argv)
336*8975f5c5SAndroid Build Coastguard Worker
337*8975f5c5SAndroid Build Coastguard Worker    launch_intent = intent.Intent(action=action,
338*8975f5c5SAndroid Build Coastguard Worker                                  activity=activity,
339*8975f5c5SAndroid Build Coastguard Worker                                  data=url,
340*8975f5c5SAndroid Build Coastguard Worker                                  package=package_name)
341*8975f5c5SAndroid Build Coastguard Worker    logging.info('Sending launch intent for %s', activity)
342*8975f5c5SAndroid Build Coastguard Worker    device.StartActivity(launch_intent)
343*8975f5c5SAndroid Build Coastguard Worker
344*8975f5c5SAndroid Build Coastguard Worker  device_utils.DeviceUtils.parallel(devices).pMap(launch)
345*8975f5c5SAndroid Build Coastguard Worker  if wait_for_java_debugger:
346*8975f5c5SAndroid Build Coastguard Worker    print('Waiting for debugger to attach to process: ' +
347*8975f5c5SAndroid Build Coastguard Worker          _Colorize(debug_process_name, colorama.Fore.YELLOW))
348*8975f5c5SAndroid Build Coastguard Worker
349*8975f5c5SAndroid Build Coastguard Worker
350*8975f5c5SAndroid Build Coastguard Workerdef _TargetCpuToTargetArch(target_cpu):
351*8975f5c5SAndroid Build Coastguard Worker  if target_cpu == 'x64':
352*8975f5c5SAndroid Build Coastguard Worker    return 'x86_64'
353*8975f5c5SAndroid Build Coastguard Worker  if target_cpu == 'mipsel':
354*8975f5c5SAndroid Build Coastguard Worker    return 'mips'
355*8975f5c5SAndroid Build Coastguard Worker  return target_cpu
356*8975f5c5SAndroid Build Coastguard Worker
357*8975f5c5SAndroid Build Coastguard Worker
358*8975f5c5SAndroid Build Coastguard Workerdef _RunGdb(device, package_name, debug_process_name, pid, output_directory,
359*8975f5c5SAndroid Build Coastguard Worker            target_cpu, port, ide, verbose):
360*8975f5c5SAndroid Build Coastguard Worker  if not pid:
361*8975f5c5SAndroid Build Coastguard Worker    debug_process_name = _NormalizeProcessName(debug_process_name, package_name)
362*8975f5c5SAndroid Build Coastguard Worker    pid = device.GetApplicationPids(debug_process_name, at_most_one=True)
363*8975f5c5SAndroid Build Coastguard Worker  if not pid:
364*8975f5c5SAndroid Build Coastguard Worker    # Attaching gdb makes the app run so slow that it takes *minutes* to start
365*8975f5c5SAndroid Build Coastguard Worker    # up (as of 2018). Better to just fail than to start & attach.
366*8975f5c5SAndroid Build Coastguard Worker    raise Exception('App not running.')
367*8975f5c5SAndroid Build Coastguard Worker
368*8975f5c5SAndroid Build Coastguard Worker  gdb_script_path = os.path.dirname(__file__) + '/adb_gdb'
369*8975f5c5SAndroid Build Coastguard Worker  cmd = [
370*8975f5c5SAndroid Build Coastguard Worker      gdb_script_path,
371*8975f5c5SAndroid Build Coastguard Worker      '--package-name=%s' % package_name,
372*8975f5c5SAndroid Build Coastguard Worker      '--output-directory=%s' % output_directory,
373*8975f5c5SAndroid Build Coastguard Worker      '--adb=%s' % adb_wrapper.AdbWrapper.GetAdbPath(),
374*8975f5c5SAndroid Build Coastguard Worker      '--device=%s' % device.serial,
375*8975f5c5SAndroid Build Coastguard Worker      '--pid=%s' % pid,
376*8975f5c5SAndroid Build Coastguard Worker      '--port=%d' % port,
377*8975f5c5SAndroid Build Coastguard Worker  ]
378*8975f5c5SAndroid Build Coastguard Worker  if ide:
379*8975f5c5SAndroid Build Coastguard Worker    cmd.append('--ide')
380*8975f5c5SAndroid Build Coastguard Worker  # Enable verbose output of adb_gdb if it's set for this script.
381*8975f5c5SAndroid Build Coastguard Worker  if verbose:
382*8975f5c5SAndroid Build Coastguard Worker    cmd.append('--verbose')
383*8975f5c5SAndroid Build Coastguard Worker  if target_cpu:
384*8975f5c5SAndroid Build Coastguard Worker    cmd.append('--target-arch=%s' % _TargetCpuToTargetArch(target_cpu))
385*8975f5c5SAndroid Build Coastguard Worker  logging.warning('Running: %s', ' '.join(shlex.quote(x) for x in cmd))
386*8975f5c5SAndroid Build Coastguard Worker  print(_Colorize('All subsequent output is from adb_gdb script.',
387*8975f5c5SAndroid Build Coastguard Worker                  colorama.Fore.YELLOW))
388*8975f5c5SAndroid Build Coastguard Worker  os.execv(gdb_script_path, cmd)
389*8975f5c5SAndroid Build Coastguard Worker
390*8975f5c5SAndroid Build Coastguard Worker
391*8975f5c5SAndroid Build Coastguard Workerdef _RunLldb(device,
392*8975f5c5SAndroid Build Coastguard Worker             package_name,
393*8975f5c5SAndroid Build Coastguard Worker             debug_process_name,
394*8975f5c5SAndroid Build Coastguard Worker             pid,
395*8975f5c5SAndroid Build Coastguard Worker             output_directory,
396*8975f5c5SAndroid Build Coastguard Worker             port,
397*8975f5c5SAndroid Build Coastguard Worker             target_cpu=None,
398*8975f5c5SAndroid Build Coastguard Worker             ndk_dir=None,
399*8975f5c5SAndroid Build Coastguard Worker             lldb_server=None,
400*8975f5c5SAndroid Build Coastguard Worker             lldb=None,
401*8975f5c5SAndroid Build Coastguard Worker             verbose=None):
402*8975f5c5SAndroid Build Coastguard Worker  if not pid:
403*8975f5c5SAndroid Build Coastguard Worker    debug_process_name = _NormalizeProcessName(debug_process_name, package_name)
404*8975f5c5SAndroid Build Coastguard Worker    pid = device.GetApplicationPids(debug_process_name, at_most_one=True)
405*8975f5c5SAndroid Build Coastguard Worker  if not pid:
406*8975f5c5SAndroid Build Coastguard Worker    # Attaching lldb makes the app run so slow that it takes *minutes* to start
407*8975f5c5SAndroid Build Coastguard Worker    # up (as of 2018). Better to just fail than to start & attach.
408*8975f5c5SAndroid Build Coastguard Worker    raise Exception('App not running.')
409*8975f5c5SAndroid Build Coastguard Worker
410*8975f5c5SAndroid Build Coastguard Worker  lldb_script_path = os.path.dirname(__file__) + '/connect_lldb.sh'
411*8975f5c5SAndroid Build Coastguard Worker  cmd = [
412*8975f5c5SAndroid Build Coastguard Worker      lldb_script_path,
413*8975f5c5SAndroid Build Coastguard Worker      '--package-name=%s' % package_name,
414*8975f5c5SAndroid Build Coastguard Worker      '--output-directory=%s' % output_directory,
415*8975f5c5SAndroid Build Coastguard Worker      '--adb=%s' % adb_wrapper.AdbWrapper.GetAdbPath(),
416*8975f5c5SAndroid Build Coastguard Worker      '--device=%s' % device.serial,
417*8975f5c5SAndroid Build Coastguard Worker      '--pid=%s' % pid,
418*8975f5c5SAndroid Build Coastguard Worker      '--port=%d' % port,
419*8975f5c5SAndroid Build Coastguard Worker  ]
420*8975f5c5SAndroid Build Coastguard Worker  # Enable verbose output of connect_lldb.sh if it's set for this script.
421*8975f5c5SAndroid Build Coastguard Worker  if verbose:
422*8975f5c5SAndroid Build Coastguard Worker    cmd.append('--verbose')
423*8975f5c5SAndroid Build Coastguard Worker  if target_cpu:
424*8975f5c5SAndroid Build Coastguard Worker    cmd.append('--target-arch=%s' % _TargetCpuToTargetArch(target_cpu))
425*8975f5c5SAndroid Build Coastguard Worker  if ndk_dir:
426*8975f5c5SAndroid Build Coastguard Worker    cmd.append('--ndk-dir=%s' % ndk_dir)
427*8975f5c5SAndroid Build Coastguard Worker  if lldb_server:
428*8975f5c5SAndroid Build Coastguard Worker    cmd.append('--lldb-server=%s' % lldb_server)
429*8975f5c5SAndroid Build Coastguard Worker  if lldb:
430*8975f5c5SAndroid Build Coastguard Worker    cmd.append('--lldb=%s' % lldb)
431*8975f5c5SAndroid Build Coastguard Worker  logging.warning('Running: %s', ' '.join(shlex.quote(x) for x in cmd))
432*8975f5c5SAndroid Build Coastguard Worker  print(
433*8975f5c5SAndroid Build Coastguard Worker      _Colorize('All subsequent output is from connect_lldb.sh script.',
434*8975f5c5SAndroid Build Coastguard Worker                colorama.Fore.YELLOW))
435*8975f5c5SAndroid Build Coastguard Worker  os.execv(lldb_script_path, cmd)
436*8975f5c5SAndroid Build Coastguard Worker
437*8975f5c5SAndroid Build Coastguard Worker
438*8975f5c5SAndroid Build Coastguard Workerdef _PrintPerDeviceOutput(devices, results, single_line=False):
439*8975f5c5SAndroid Build Coastguard Worker  for d, result in zip(devices, results):
440*8975f5c5SAndroid Build Coastguard Worker    if not single_line and d is not devices[0]:
441*8975f5c5SAndroid Build Coastguard Worker      sys.stdout.write('\n')
442*8975f5c5SAndroid Build Coastguard Worker    sys.stdout.write(
443*8975f5c5SAndroid Build Coastguard Worker          _Colorize('{} ({}):'.format(d, d.build_description),
444*8975f5c5SAndroid Build Coastguard Worker                    colorama.Fore.YELLOW))
445*8975f5c5SAndroid Build Coastguard Worker    sys.stdout.write(' ' if single_line else '\n')
446*8975f5c5SAndroid Build Coastguard Worker    yield result
447*8975f5c5SAndroid Build Coastguard Worker
448*8975f5c5SAndroid Build Coastguard Worker
449*8975f5c5SAndroid Build Coastguard Workerdef _RunMemUsage(devices, package_name, query_app=False):
450*8975f5c5SAndroid Build Coastguard Worker  cmd_args = ['dumpsys', 'meminfo']
451*8975f5c5SAndroid Build Coastguard Worker  if not query_app:
452*8975f5c5SAndroid Build Coastguard Worker    cmd_args.append('--local')
453*8975f5c5SAndroid Build Coastguard Worker
454*8975f5c5SAndroid Build Coastguard Worker  def mem_usage_helper(d):
455*8975f5c5SAndroid Build Coastguard Worker    ret = []
456*8975f5c5SAndroid Build Coastguard Worker    for process in sorted(_GetPackageProcesses(d, package_name)):
457*8975f5c5SAndroid Build Coastguard Worker      meminfo = d.RunShellCommand(cmd_args + [str(process.pid)])
458*8975f5c5SAndroid Build Coastguard Worker      ret.append((process.name, '\n'.join(meminfo)))
459*8975f5c5SAndroid Build Coastguard Worker    return ret
460*8975f5c5SAndroid Build Coastguard Worker
461*8975f5c5SAndroid Build Coastguard Worker  parallel_devices = device_utils.DeviceUtils.parallel(devices)
462*8975f5c5SAndroid Build Coastguard Worker  all_results = parallel_devices.pMap(mem_usage_helper).pGet(None)
463*8975f5c5SAndroid Build Coastguard Worker  for result in _PrintPerDeviceOutput(devices, all_results):
464*8975f5c5SAndroid Build Coastguard Worker    if not result:
465*8975f5c5SAndroid Build Coastguard Worker      print('No processes found.')
466*8975f5c5SAndroid Build Coastguard Worker    else:
467*8975f5c5SAndroid Build Coastguard Worker      for name, usage in sorted(result):
468*8975f5c5SAndroid Build Coastguard Worker        print(_Colorize('==== Output of "dumpsys meminfo %s" ====' % name,
469*8975f5c5SAndroid Build Coastguard Worker                        colorama.Fore.GREEN))
470*8975f5c5SAndroid Build Coastguard Worker        print(usage)
471*8975f5c5SAndroid Build Coastguard Worker
472*8975f5c5SAndroid Build Coastguard Worker
473*8975f5c5SAndroid Build Coastguard Workerdef _DuHelper(device, path_spec, run_as=None):
474*8975f5c5SAndroid Build Coastguard Worker  """Runs "du -s -k |path_spec|" on |device| and returns parsed result.
475*8975f5c5SAndroid Build Coastguard Worker
476*8975f5c5SAndroid Build Coastguard Worker  Args:
477*8975f5c5SAndroid Build Coastguard Worker    device: A DeviceUtils instance.
478*8975f5c5SAndroid Build Coastguard Worker    path_spec: The list of paths to run du on. May contain shell expansions
479*8975f5c5SAndroid Build Coastguard Worker        (will not be escaped).
480*8975f5c5SAndroid Build Coastguard Worker    run_as: Package name to run as, or None to run as shell user. If not None
481*8975f5c5SAndroid Build Coastguard Worker        and app is not android:debuggable (run-as fails), then command will be
482*8975f5c5SAndroid Build Coastguard Worker        run as root.
483*8975f5c5SAndroid Build Coastguard Worker
484*8975f5c5SAndroid Build Coastguard Worker  Returns:
485*8975f5c5SAndroid Build Coastguard Worker    A dict of path->size in KiB containing all paths in |path_spec| that exist
486*8975f5c5SAndroid Build Coastguard Worker    on device. Paths that do not exist are silently ignored.
487*8975f5c5SAndroid Build Coastguard Worker  """
488*8975f5c5SAndroid Build Coastguard Worker  # Example output for: du -s -k /data/data/org.chromium.chrome/{*,.*}
489*8975f5c5SAndroid Build Coastguard Worker  # 144     /data/data/org.chromium.chrome/cache
490*8975f5c5SAndroid Build Coastguard Worker  # 8       /data/data/org.chromium.chrome/files
491*8975f5c5SAndroid Build Coastguard Worker  # <snip>
492*8975f5c5SAndroid Build Coastguard Worker  # du: .*: No such file or directory
493*8975f5c5SAndroid Build Coastguard Worker
494*8975f5c5SAndroid Build Coastguard Worker  # The -d flag works differently across android version, so use -s instead.
495*8975f5c5SAndroid Build Coastguard Worker  # Without the explicit 2>&1, stderr and stdout get combined at random :(.
496*8975f5c5SAndroid Build Coastguard Worker  cmd_str = 'du -s -k ' + path_spec + ' 2>&1'
497*8975f5c5SAndroid Build Coastguard Worker  lines = device.RunShellCommand(cmd_str, run_as=run_as, shell=True,
498*8975f5c5SAndroid Build Coastguard Worker                                 check_return=False)
499*8975f5c5SAndroid Build Coastguard Worker  output = '\n'.join(lines)
500*8975f5c5SAndroid Build Coastguard Worker  # run-as: Package 'com.android.chrome' is not debuggable
501*8975f5c5SAndroid Build Coastguard Worker  if output.startswith('run-as:'):
502*8975f5c5SAndroid Build Coastguard Worker    # check_return=False needed for when some paths in path_spec do not exist.
503*8975f5c5SAndroid Build Coastguard Worker    lines = device.RunShellCommand(cmd_str, as_root=True, shell=True,
504*8975f5c5SAndroid Build Coastguard Worker                                   check_return=False)
505*8975f5c5SAndroid Build Coastguard Worker  ret = {}
506*8975f5c5SAndroid Build Coastguard Worker  try:
507*8975f5c5SAndroid Build Coastguard Worker    for line in lines:
508*8975f5c5SAndroid Build Coastguard Worker      # du: .*: No such file or directory
509*8975f5c5SAndroid Build Coastguard Worker      if line.startswith('du:'):
510*8975f5c5SAndroid Build Coastguard Worker        continue
511*8975f5c5SAndroid Build Coastguard Worker      size, subpath = line.split(None, 1)
512*8975f5c5SAndroid Build Coastguard Worker      ret[subpath] = int(size)
513*8975f5c5SAndroid Build Coastguard Worker    return ret
514*8975f5c5SAndroid Build Coastguard Worker  except ValueError:
515*8975f5c5SAndroid Build Coastguard Worker    logging.error('du command was: %s', cmd_str)
516*8975f5c5SAndroid Build Coastguard Worker    logging.error('Failed to parse du output:\n%s', output)
517*8975f5c5SAndroid Build Coastguard Worker    raise
518*8975f5c5SAndroid Build Coastguard Worker
519*8975f5c5SAndroid Build Coastguard Worker
520*8975f5c5SAndroid Build Coastguard Workerdef _RunDiskUsage(devices, package_name):
521*8975f5c5SAndroid Build Coastguard Worker  # Measuring dex size is a bit complicated:
522*8975f5c5SAndroid Build Coastguard Worker  # https://source.android.com/devices/tech/dalvik/jit-compiler
523*8975f5c5SAndroid Build Coastguard Worker  #
524*8975f5c5SAndroid Build Coastguard Worker  # For KitKat and below:
525*8975f5c5SAndroid Build Coastguard Worker  #   dumpsys package contains:
526*8975f5c5SAndroid Build Coastguard Worker  #     dataDir=/data/data/org.chromium.chrome
527*8975f5c5SAndroid Build Coastguard Worker  #     codePath=/data/app/org.chromium.chrome-1.apk
528*8975f5c5SAndroid Build Coastguard Worker  #     resourcePath=/data/app/org.chromium.chrome-1.apk
529*8975f5c5SAndroid Build Coastguard Worker  #     nativeLibraryPath=/data/app-lib/org.chromium.chrome-1
530*8975f5c5SAndroid Build Coastguard Worker  #   To measure odex:
531*8975f5c5SAndroid Build Coastguard Worker  #     ls -l /data/dalvik-cache/data@[email protected]@classes.dex
532*8975f5c5SAndroid Build Coastguard Worker  #
533*8975f5c5SAndroid Build Coastguard Worker  # For Android L and M (and maybe for N+ system apps):
534*8975f5c5SAndroid Build Coastguard Worker  #   dumpsys package contains:
535*8975f5c5SAndroid Build Coastguard Worker  #     codePath=/data/app/org.chromium.chrome-1
536*8975f5c5SAndroid Build Coastguard Worker  #     resourcePath=/data/app/org.chromium.chrome-1
537*8975f5c5SAndroid Build Coastguard Worker  #     legacyNativeLibraryDir=/data/app/org.chromium.chrome-1/lib
538*8975f5c5SAndroid Build Coastguard Worker  #   To measure odex:
539*8975f5c5SAndroid Build Coastguard Worker  #     # Option 1:
540*8975f5c5SAndroid Build Coastguard Worker  #  /data/dalvik-cache/arm/data@[email protected]@[email protected]
541*8975f5c5SAndroid Build Coastguard Worker  #  /data/dalvik-cache/arm/data@[email protected]@[email protected]
542*8975f5c5SAndroid Build Coastguard Worker  #     ls -l /data/dalvik-cache/profiles/org.chromium.chrome
543*8975f5c5SAndroid Build Coastguard Worker  #         (these profiles all appear to be 0 bytes)
544*8975f5c5SAndroid Build Coastguard Worker  #     # Option 2:
545*8975f5c5SAndroid Build Coastguard Worker  #     ls -l /data/app/org.chromium.chrome-1/oat/arm/base.odex
546*8975f5c5SAndroid Build Coastguard Worker  #
547*8975f5c5SAndroid Build Coastguard Worker  # For Android N+:
548*8975f5c5SAndroid Build Coastguard Worker  #   dumpsys package contains:
549*8975f5c5SAndroid Build Coastguard Worker  #     dataDir=/data/user/0/org.chromium.chrome
550*8975f5c5SAndroid Build Coastguard Worker  #     codePath=/data/app/org.chromium.chrome-UuCZ71IE-i5sZgHAkU49_w==
551*8975f5c5SAndroid Build Coastguard Worker  #     resourcePath=/data/app/org.chromium.chrome-UuCZ71IE-i5sZgHAkU49_w==
552*8975f5c5SAndroid Build Coastguard Worker  #     legacyNativeLibraryDir=/data/app/org.chromium.chrome-GUID/lib
553*8975f5c5SAndroid Build Coastguard Worker  #     Instruction Set: arm
554*8975f5c5SAndroid Build Coastguard Worker  #       path: /data/app/org.chromium.chrome-UuCZ71IE-i5sZgHAkU49_w==/base.apk
555*8975f5c5SAndroid Build Coastguard Worker  #       status: /data/.../oat/arm/base.odex[status=kOatUpToDate, compilation_f
556*8975f5c5SAndroid Build Coastguard Worker  #       ilter=quicken]
557*8975f5c5SAndroid Build Coastguard Worker  #     Instruction Set: arm64
558*8975f5c5SAndroid Build Coastguard Worker  #       path: /data/app/org.chromium.chrome-UuCZ71IE-i5sZgHAkU49_w==/base.apk
559*8975f5c5SAndroid Build Coastguard Worker  #       status: /data/.../oat/arm64/base.odex[status=..., compilation_filter=q
560*8975f5c5SAndroid Build Coastguard Worker  #       uicken]
561*8975f5c5SAndroid Build Coastguard Worker  #   To measure odex:
562*8975f5c5SAndroid Build Coastguard Worker  #     ls -l /data/app/.../oat/arm/base.odex
563*8975f5c5SAndroid Build Coastguard Worker  #     ls -l /data/app/.../oat/arm/base.vdex (optional)
564*8975f5c5SAndroid Build Coastguard Worker  #   To measure the correct odex size:
565*8975f5c5SAndroid Build Coastguard Worker  #     cmd package compile -m speed org.chromium.chrome  # For webview
566*8975f5c5SAndroid Build Coastguard Worker  #     cmd package compile -m speed-profile org.chromium.chrome  # For others
567*8975f5c5SAndroid Build Coastguard Worker  def disk_usage_helper(d):
568*8975f5c5SAndroid Build Coastguard Worker    package_output = '\n'.join(d.RunShellCommand(
569*8975f5c5SAndroid Build Coastguard Worker        ['dumpsys', 'package', package_name], check_return=True))
570*8975f5c5SAndroid Build Coastguard Worker    # Does not return error when apk is not installed.
571*8975f5c5SAndroid Build Coastguard Worker    if not package_output or 'Unable to find package:' in package_output:
572*8975f5c5SAndroid Build Coastguard Worker      return None
573*8975f5c5SAndroid Build Coastguard Worker
574*8975f5c5SAndroid Build Coastguard Worker    # Ignore system apks that have updates installed.
575*8975f5c5SAndroid Build Coastguard Worker    package_output = re.sub(r'Hidden system packages:.*?^\b', '',
576*8975f5c5SAndroid Build Coastguard Worker                            package_output, flags=re.S | re.M)
577*8975f5c5SAndroid Build Coastguard Worker
578*8975f5c5SAndroid Build Coastguard Worker    try:
579*8975f5c5SAndroid Build Coastguard Worker      data_dir = re.search(r'dataDir=(.*)', package_output).group(1)
580*8975f5c5SAndroid Build Coastguard Worker      code_path = re.search(r'codePath=(.*)', package_output).group(1)
581*8975f5c5SAndroid Build Coastguard Worker      lib_path = re.search(r'(?:legacyN|n)ativeLibrary(?:Dir|Path)=(.*)',
582*8975f5c5SAndroid Build Coastguard Worker                           package_output).group(1)
583*8975f5c5SAndroid Build Coastguard Worker    except AttributeError as e:
584*8975f5c5SAndroid Build Coastguard Worker      raise Exception('Error parsing dumpsys output: ' + package_output) from e
585*8975f5c5SAndroid Build Coastguard Worker
586*8975f5c5SAndroid Build Coastguard Worker    if code_path.startswith('/system'):
587*8975f5c5SAndroid Build Coastguard Worker      logging.warning('Measurement of system image apks can be innacurate')
588*8975f5c5SAndroid Build Coastguard Worker
589*8975f5c5SAndroid Build Coastguard Worker    compilation_filters = set()
590*8975f5c5SAndroid Build Coastguard Worker    # Match "compilation_filter=value", where a line break can occur at any spot
591*8975f5c5SAndroid Build Coastguard Worker    # (refer to examples above).
592*8975f5c5SAndroid Build Coastguard Worker    awful_wrapping = r'\s*'.join('compilation_filter=')
593*8975f5c5SAndroid Build Coastguard Worker    for m in re.finditer(awful_wrapping + r'([\s\S]+?)[\],]', package_output):
594*8975f5c5SAndroid Build Coastguard Worker      compilation_filters.add(re.sub(r'\s+', '', m.group(1)))
595*8975f5c5SAndroid Build Coastguard Worker    # Starting Android Q, output looks like:
596*8975f5c5SAndroid Build Coastguard Worker    #  arm: [status=speed-profile] [reason=install]
597*8975f5c5SAndroid Build Coastguard Worker    for m in re.finditer(r'\[status=(.+?)\]', package_output):
598*8975f5c5SAndroid Build Coastguard Worker      compilation_filters.add(m.group(1))
599*8975f5c5SAndroid Build Coastguard Worker    compilation_filter = ','.join(sorted(compilation_filters))
600*8975f5c5SAndroid Build Coastguard Worker
601*8975f5c5SAndroid Build Coastguard Worker    data_dir_sizes = _DuHelper(d, '%s/{*,.*}' % data_dir, run_as=package_name)
602*8975f5c5SAndroid Build Coastguard Worker    # Measure code_cache separately since it can be large.
603*8975f5c5SAndroid Build Coastguard Worker    code_cache_sizes = {}
604*8975f5c5SAndroid Build Coastguard Worker    code_cache_dir = next(
605*8975f5c5SAndroid Build Coastguard Worker        (k for k in data_dir_sizes if k.endswith('/code_cache')), None)
606*8975f5c5SAndroid Build Coastguard Worker    if code_cache_dir:
607*8975f5c5SAndroid Build Coastguard Worker      data_dir_sizes.pop(code_cache_dir)
608*8975f5c5SAndroid Build Coastguard Worker      code_cache_sizes = _DuHelper(d, '%s/{*,.*}' % code_cache_dir,
609*8975f5c5SAndroid Build Coastguard Worker                                   run_as=package_name)
610*8975f5c5SAndroid Build Coastguard Worker
611*8975f5c5SAndroid Build Coastguard Worker    apk_path_spec = code_path
612*8975f5c5SAndroid Build Coastguard Worker    if not apk_path_spec.endswith('.apk'):
613*8975f5c5SAndroid Build Coastguard Worker      apk_path_spec += '/*.apk'
614*8975f5c5SAndroid Build Coastguard Worker    apk_sizes = _DuHelper(d, apk_path_spec)
615*8975f5c5SAndroid Build Coastguard Worker    if lib_path.endswith('/lib'):
616*8975f5c5SAndroid Build Coastguard Worker      # Shows architecture subdirectory.
617*8975f5c5SAndroid Build Coastguard Worker      lib_sizes = _DuHelper(d, '%s/{*,.*}' % lib_path)
618*8975f5c5SAndroid Build Coastguard Worker    else:
619*8975f5c5SAndroid Build Coastguard Worker      lib_sizes = _DuHelper(d, lib_path)
620*8975f5c5SAndroid Build Coastguard Worker
621*8975f5c5SAndroid Build Coastguard Worker    # Look at all possible locations for odex files.
622*8975f5c5SAndroid Build Coastguard Worker    odex_paths = []
623*8975f5c5SAndroid Build Coastguard Worker    for apk_path in apk_sizes:
624*8975f5c5SAndroid Build Coastguard Worker      mangled_apk_path = apk_path[1:].replace('/', '@')
625*8975f5c5SAndroid Build Coastguard Worker      apk_basename = posixpath.basename(apk_path)[:-4]
626*8975f5c5SAndroid Build Coastguard Worker      for ext in ('dex', 'odex', 'vdex', 'art'):
627*8975f5c5SAndroid Build Coastguard Worker        # Easier to check all architectures than to determine active ones.
628*8975f5c5SAndroid Build Coastguard Worker        for arch in ('arm', 'arm64', 'x86', 'x86_64', 'mips', 'mips64'):
629*8975f5c5SAndroid Build Coastguard Worker          odex_paths.append(
630*8975f5c5SAndroid Build Coastguard Worker              '%s/oat/%s/%s.%s' % (code_path, arch, apk_basename, ext))
631*8975f5c5SAndroid Build Coastguard Worker          # No app could possibly have more than 6 dex files.
632*8975f5c5SAndroid Build Coastguard Worker          for suffix in ('', '2', '3', '4', '5'):
633*8975f5c5SAndroid Build Coastguard Worker            odex_paths.append('/data/dalvik-cache/%s/%s@classes%s.%s' % (
634*8975f5c5SAndroid Build Coastguard Worker                arch, mangled_apk_path, suffix, ext))
635*8975f5c5SAndroid Build Coastguard Worker            # This path does not have |arch|, so don't repeat it for every arch.
636*8975f5c5SAndroid Build Coastguard Worker            if arch == 'arm':
637*8975f5c5SAndroid Build Coastguard Worker              odex_paths.append('/data/dalvik-cache/%s@classes%s.dex' % (
638*8975f5c5SAndroid Build Coastguard Worker                  mangled_apk_path, suffix))
639*8975f5c5SAndroid Build Coastguard Worker
640*8975f5c5SAndroid Build Coastguard Worker    odex_sizes = _DuHelper(d, ' '.join(shlex.quote(p) for p in odex_paths))
641*8975f5c5SAndroid Build Coastguard Worker
642*8975f5c5SAndroid Build Coastguard Worker    return (data_dir_sizes, code_cache_sizes, apk_sizes, lib_sizes, odex_sizes,
643*8975f5c5SAndroid Build Coastguard Worker            compilation_filter)
644*8975f5c5SAndroid Build Coastguard Worker
645*8975f5c5SAndroid Build Coastguard Worker  def print_sizes(desc, sizes):
646*8975f5c5SAndroid Build Coastguard Worker    print('%s: %d KiB' % (desc, sum(sizes.values())))
647*8975f5c5SAndroid Build Coastguard Worker    for path, size in sorted(sizes.items()):
648*8975f5c5SAndroid Build Coastguard Worker      print('    %s: %s KiB' % (path, size))
649*8975f5c5SAndroid Build Coastguard Worker
650*8975f5c5SAndroid Build Coastguard Worker  parallel_devices = device_utils.DeviceUtils.parallel(devices)
651*8975f5c5SAndroid Build Coastguard Worker  all_results = parallel_devices.pMap(disk_usage_helper).pGet(None)
652*8975f5c5SAndroid Build Coastguard Worker  for result in _PrintPerDeviceOutput(devices, all_results):
653*8975f5c5SAndroid Build Coastguard Worker    if not result:
654*8975f5c5SAndroid Build Coastguard Worker      print('APK is not installed.')
655*8975f5c5SAndroid Build Coastguard Worker      continue
656*8975f5c5SAndroid Build Coastguard Worker
657*8975f5c5SAndroid Build Coastguard Worker    (data_dir_sizes, code_cache_sizes, apk_sizes, lib_sizes, odex_sizes,
658*8975f5c5SAndroid Build Coastguard Worker     compilation_filter) = result
659*8975f5c5SAndroid Build Coastguard Worker    total = sum(sum(sizes.values()) for sizes in result[:-1])
660*8975f5c5SAndroid Build Coastguard Worker
661*8975f5c5SAndroid Build Coastguard Worker    print_sizes('Apk', apk_sizes)
662*8975f5c5SAndroid Build Coastguard Worker    print_sizes('App Data (non-code cache)', data_dir_sizes)
663*8975f5c5SAndroid Build Coastguard Worker    print_sizes('App Data (code cache)', code_cache_sizes)
664*8975f5c5SAndroid Build Coastguard Worker    print_sizes('Native Libs', lib_sizes)
665*8975f5c5SAndroid Build Coastguard Worker    show_warning = compilation_filter and 'speed' not in compilation_filter
666*8975f5c5SAndroid Build Coastguard Worker    compilation_filter = compilation_filter or 'n/a'
667*8975f5c5SAndroid Build Coastguard Worker    print_sizes('odex (compilation_filter=%s)' % compilation_filter, odex_sizes)
668*8975f5c5SAndroid Build Coastguard Worker    if show_warning:
669*8975f5c5SAndroid Build Coastguard Worker      logging.warning('For a more realistic odex size, run:')
670*8975f5c5SAndroid Build Coastguard Worker      logging.warning('    %s compile-dex [speed|speed-profile]', sys.argv[0])
671*8975f5c5SAndroid Build Coastguard Worker    print('Total: %s KiB (%.1f MiB)' % (total, total / 1024.0))
672*8975f5c5SAndroid Build Coastguard Worker
673*8975f5c5SAndroid Build Coastguard Worker
674*8975f5c5SAndroid Build Coastguard Workerclass _LogcatProcessor:
675*8975f5c5SAndroid Build Coastguard Worker  ParsedLine = collections.namedtuple(
676*8975f5c5SAndroid Build Coastguard Worker      'ParsedLine',
677*8975f5c5SAndroid Build Coastguard Worker      ['date', 'invokation_time', 'pid', 'tid', 'priority', 'tag', 'message'])
678*8975f5c5SAndroid Build Coastguard Worker
679*8975f5c5SAndroid Build Coastguard Worker  class NativeStackSymbolizer:
680*8975f5c5SAndroid Build Coastguard Worker    """Buffers lines from native stacks and symbolizes them when done."""
681*8975f5c5SAndroid Build Coastguard Worker    # E.g.: #06 pc 0x0000d519 /apex/com.android.runtime/lib/libart.so
682*8975f5c5SAndroid Build Coastguard Worker    # E.g.: #01 pc 00180c8d  /data/data/.../lib/libbase.cr.so
683*8975f5c5SAndroid Build Coastguard Worker    _STACK_PATTERN = re.compile(r'\s*#\d+\s+(?:pc )?(0x)?[0-9a-f]{8,16}\s')
684*8975f5c5SAndroid Build Coastguard Worker
685*8975f5c5SAndroid Build Coastguard Worker    def __init__(self, stack_script_context, print_func):
686*8975f5c5SAndroid Build Coastguard Worker      # To symbolize native stacks, we need to pass all lines at once.
687*8975f5c5SAndroid Build Coastguard Worker      self._stack_script_context = stack_script_context
688*8975f5c5SAndroid Build Coastguard Worker      self._print_func = print_func
689*8975f5c5SAndroid Build Coastguard Worker      self._crash_lines_buffer = None
690*8975f5c5SAndroid Build Coastguard Worker
691*8975f5c5SAndroid Build Coastguard Worker    def _FlushLines(self):
692*8975f5c5SAndroid Build Coastguard Worker      """Prints queued lines after sending them through stack.py."""
693*8975f5c5SAndroid Build Coastguard Worker      if self._crash_lines_buffer is None:
694*8975f5c5SAndroid Build Coastguard Worker        return
695*8975f5c5SAndroid Build Coastguard Worker
696*8975f5c5SAndroid Build Coastguard Worker      crash_lines = self._crash_lines_buffer
697*8975f5c5SAndroid Build Coastguard Worker      self._crash_lines_buffer = None
698*8975f5c5SAndroid Build Coastguard Worker      with tempfile.NamedTemporaryFile(mode='w') as f:
699*8975f5c5SAndroid Build Coastguard Worker        f.writelines(x[0].message + '\n' for x in crash_lines)
700*8975f5c5SAndroid Build Coastguard Worker        f.flush()
701*8975f5c5SAndroid Build Coastguard Worker        proc = self._stack_script_context.Popen(
702*8975f5c5SAndroid Build Coastguard Worker            input_file=f.name, stdout=subprocess.PIPE)
703*8975f5c5SAndroid Build Coastguard Worker        lines = proc.communicate()[0].splitlines()
704*8975f5c5SAndroid Build Coastguard Worker
705*8975f5c5SAndroid Build Coastguard Worker      for i, line in enumerate(lines):
706*8975f5c5SAndroid Build Coastguard Worker        parsed_line, dim = crash_lines[min(i, len(crash_lines) - 1)]
707*8975f5c5SAndroid Build Coastguard Worker        d = parsed_line._asdict()
708*8975f5c5SAndroid Build Coastguard Worker        d['message'] = line
709*8975f5c5SAndroid Build Coastguard Worker        parsed_line = _LogcatProcessor.ParsedLine(**d)
710*8975f5c5SAndroid Build Coastguard Worker        self._print_func(parsed_line, dim)
711*8975f5c5SAndroid Build Coastguard Worker
712*8975f5c5SAndroid Build Coastguard Worker    def AddLine(self, parsed_line, dim):
713*8975f5c5SAndroid Build Coastguard Worker      # Assume all lines from DEBUG are stacks.
714*8975f5c5SAndroid Build Coastguard Worker      # Also look for "stack-looking" lines to catch manual stack prints.
715*8975f5c5SAndroid Build Coastguard Worker      # It's important to not buffer non-stack lines because stack.py does not
716*8975f5c5SAndroid Build Coastguard Worker      # pass them through.
717*8975f5c5SAndroid Build Coastguard Worker      is_crash_line = parsed_line.tag == 'DEBUG' or (self._STACK_PATTERN.match(
718*8975f5c5SAndroid Build Coastguard Worker          parsed_line.message))
719*8975f5c5SAndroid Build Coastguard Worker
720*8975f5c5SAndroid Build Coastguard Worker      if is_crash_line:
721*8975f5c5SAndroid Build Coastguard Worker        if self._crash_lines_buffer is None:
722*8975f5c5SAndroid Build Coastguard Worker          self._crash_lines_buffer = []
723*8975f5c5SAndroid Build Coastguard Worker        self._crash_lines_buffer.append((parsed_line, dim))
724*8975f5c5SAndroid Build Coastguard Worker        return
725*8975f5c5SAndroid Build Coastguard Worker
726*8975f5c5SAndroid Build Coastguard Worker      self._FlushLines()
727*8975f5c5SAndroid Build Coastguard Worker
728*8975f5c5SAndroid Build Coastguard Worker      self._print_func(parsed_line, dim)
729*8975f5c5SAndroid Build Coastguard Worker
730*8975f5c5SAndroid Build Coastguard Worker
731*8975f5c5SAndroid Build Coastguard Worker  # Logcat tags for messages that are generally relevant but are not from PIDs
732*8975f5c5SAndroid Build Coastguard Worker  # associated with the apk.
733*8975f5c5SAndroid Build Coastguard Worker  _ALLOWLISTED_TAGS = {
734*8975f5c5SAndroid Build Coastguard Worker      'ActivityManager',  # Shows activity lifecycle messages.
735*8975f5c5SAndroid Build Coastguard Worker      'ActivityTaskManager',  # More activity lifecycle messages.
736*8975f5c5SAndroid Build Coastguard Worker      'AndroidRuntime',  # Java crash dumps
737*8975f5c5SAndroid Build Coastguard Worker      'AppZygoteInit',  # Android's native application zygote support.
738*8975f5c5SAndroid Build Coastguard Worker      'DEBUG',  # Native crash dump.
739*8975f5c5SAndroid Build Coastguard Worker  }
740*8975f5c5SAndroid Build Coastguard Worker
741*8975f5c5SAndroid Build Coastguard Worker  # Matches messages only on pre-L (Dalvik) that are spammy and unimportant.
742*8975f5c5SAndroid Build Coastguard Worker  _DALVIK_IGNORE_PATTERN = re.compile('|'.join([
743*8975f5c5SAndroid Build Coastguard Worker      r'^Added shared lib',
744*8975f5c5SAndroid Build Coastguard Worker      r'^Could not find ',
745*8975f5c5SAndroid Build Coastguard Worker      r'^DexOpt:',
746*8975f5c5SAndroid Build Coastguard Worker      r'^GC_',
747*8975f5c5SAndroid Build Coastguard Worker      r'^Late-enabling CheckJNI',
748*8975f5c5SAndroid Build Coastguard Worker      r'^Link of class',
749*8975f5c5SAndroid Build Coastguard Worker      r'^No JNI_OnLoad found in',
750*8975f5c5SAndroid Build Coastguard Worker      r'^Trying to load lib',
751*8975f5c5SAndroid Build Coastguard Worker      r'^Unable to resolve superclass',
752*8975f5c5SAndroid Build Coastguard Worker      r'^VFY:',
753*8975f5c5SAndroid Build Coastguard Worker      r'^WAIT_',
754*8975f5c5SAndroid Build Coastguard Worker  ]))
755*8975f5c5SAndroid Build Coastguard Worker
756*8975f5c5SAndroid Build Coastguard Worker  def __init__(self,
757*8975f5c5SAndroid Build Coastguard Worker               device,
758*8975f5c5SAndroid Build Coastguard Worker               package_name,
759*8975f5c5SAndroid Build Coastguard Worker               stack_script_context,
760*8975f5c5SAndroid Build Coastguard Worker               deobfuscate=None,
761*8975f5c5SAndroid Build Coastguard Worker               verbose=False,
762*8975f5c5SAndroid Build Coastguard Worker               exit_on_match=None,
763*8975f5c5SAndroid Build Coastguard Worker               extra_package_names=None):
764*8975f5c5SAndroid Build Coastguard Worker    self._device = device
765*8975f5c5SAndroid Build Coastguard Worker    self._package_name = package_name
766*8975f5c5SAndroid Build Coastguard Worker    self._extra_package_names = extra_package_names or []
767*8975f5c5SAndroid Build Coastguard Worker    self._verbose = verbose
768*8975f5c5SAndroid Build Coastguard Worker    self._deobfuscator = deobfuscate
769*8975f5c5SAndroid Build Coastguard Worker    if exit_on_match is not None:
770*8975f5c5SAndroid Build Coastguard Worker      self._exit_on_match = re.compile(exit_on_match)
771*8975f5c5SAndroid Build Coastguard Worker    else:
772*8975f5c5SAndroid Build Coastguard Worker      self._exit_on_match = None
773*8975f5c5SAndroid Build Coastguard Worker    self._found_exit_match = False
774*8975f5c5SAndroid Build Coastguard Worker    if stack_script_context:
775*8975f5c5SAndroid Build Coastguard Worker      self._print_func = _LogcatProcessor.NativeStackSymbolizer(
776*8975f5c5SAndroid Build Coastguard Worker          stack_script_context, self._PrintParsedLine).AddLine
777*8975f5c5SAndroid Build Coastguard Worker    else:
778*8975f5c5SAndroid Build Coastguard Worker      self._print_func = self._PrintParsedLine
779*8975f5c5SAndroid Build Coastguard Worker    # Process ID for the app's main process (with no :name suffix).
780*8975f5c5SAndroid Build Coastguard Worker    self._primary_pid = None
781*8975f5c5SAndroid Build Coastguard Worker    # Set of all Process IDs that belong to the app.
782*8975f5c5SAndroid Build Coastguard Worker    self._my_pids = set()
783*8975f5c5SAndroid Build Coastguard Worker    # Set of all Process IDs that we've parsed at some point.
784*8975f5c5SAndroid Build Coastguard Worker    self._seen_pids = set()
785*8975f5c5SAndroid Build Coastguard Worker    # Start proc 22953:com.google.chromeremotedesktop/
786*8975f5c5SAndroid Build Coastguard Worker    self._pid_pattern = re.compile(r'Start proc (\d+):{}/'.format(package_name))
787*8975f5c5SAndroid Build Coastguard Worker    # START u0 {act=android.intent.action.MAIN \
788*8975f5c5SAndroid Build Coastguard Worker    # cat=[android.intent.category.LAUNCHER] \
789*8975f5c5SAndroid Build Coastguard Worker    # flg=0x10000000 pkg=com.google.chromeremotedesktop} from uid 2000
790*8975f5c5SAndroid Build Coastguard Worker    self._start_pattern = re.compile(r'START .*(?:cmp|pkg)=' + package_name)
791*8975f5c5SAndroid Build Coastguard Worker
792*8975f5c5SAndroid Build Coastguard Worker    self.nonce = 'Chromium apk_operations.py nonce={}'.format(random.random())
793*8975f5c5SAndroid Build Coastguard Worker    # Holds lines buffered on start-up, before we find our nonce message.
794*8975f5c5SAndroid Build Coastguard Worker    self._initial_buffered_lines = []
795*8975f5c5SAndroid Build Coastguard Worker    self._UpdateMyPids()
796*8975f5c5SAndroid Build Coastguard Worker    # Give preference to PID reported by "ps" over those found from
797*8975f5c5SAndroid Build Coastguard Worker    # _start_pattern. There can be multiple "Start proc" messages from prior
798*8975f5c5SAndroid Build Coastguard Worker    # runs of the app.
799*8975f5c5SAndroid Build Coastguard Worker    self._found_initial_pid = self._primary_pid is not None
800*8975f5c5SAndroid Build Coastguard Worker    # Retrieve any additional patterns that are relevant for the User.
801*8975f5c5SAndroid Build Coastguard Worker    self._user_defined_highlight = None
802*8975f5c5SAndroid Build Coastguard Worker    user_regex = os.environ.get('CHROMIUM_LOGCAT_HIGHLIGHT')
803*8975f5c5SAndroid Build Coastguard Worker    if user_regex:
804*8975f5c5SAndroid Build Coastguard Worker      self._user_defined_highlight = re.compile(user_regex)
805*8975f5c5SAndroid Build Coastguard Worker      if not self._user_defined_highlight:
806*8975f5c5SAndroid Build Coastguard Worker        print(_Colorize(
807*8975f5c5SAndroid Build Coastguard Worker            'Rejecting invalid regular expression: {}'.format(user_regex),
808*8975f5c5SAndroid Build Coastguard Worker            colorama.Fore.RED + colorama.Style.BRIGHT))
809*8975f5c5SAndroid Build Coastguard Worker
810*8975f5c5SAndroid Build Coastguard Worker  def _UpdateMyPids(self):
811*8975f5c5SAndroid Build Coastguard Worker    # We intentionally do not clear self._my_pids to make sure that the
812*8975f5c5SAndroid Build Coastguard Worker    # ProcessLine method below also includes lines from processes which may
813*8975f5c5SAndroid Build Coastguard Worker    # have already exited.
814*8975f5c5SAndroid Build Coastguard Worker    self._primary_pid = None
815*8975f5c5SAndroid Build Coastguard Worker    for package_name in [self._package_name] + self._extra_package_names:
816*8975f5c5SAndroid Build Coastguard Worker      for process in _GetPackageProcesses(self._device, package_name):
817*8975f5c5SAndroid Build Coastguard Worker        # We take only the first "main" process found in order to account for
818*8975f5c5SAndroid Build Coastguard Worker        # possibly forked() processes.
819*8975f5c5SAndroid Build Coastguard Worker        if ':' not in process.name and self._primary_pid is None:
820*8975f5c5SAndroid Build Coastguard Worker          self._primary_pid = process.pid
821*8975f5c5SAndroid Build Coastguard Worker        self._my_pids.add(process.pid)
822*8975f5c5SAndroid Build Coastguard Worker
823*8975f5c5SAndroid Build Coastguard Worker  def _GetPidStyle(self, pid, dim=False):
824*8975f5c5SAndroid Build Coastguard Worker    if pid == self._primary_pid:
825*8975f5c5SAndroid Build Coastguard Worker      return colorama.Fore.WHITE
826*8975f5c5SAndroid Build Coastguard Worker    if pid in self._my_pids:
827*8975f5c5SAndroid Build Coastguard Worker      # TODO(wnwen): Use one separate persistent color per process, pop LRU
828*8975f5c5SAndroid Build Coastguard Worker      return colorama.Fore.YELLOW
829*8975f5c5SAndroid Build Coastguard Worker    if dim:
830*8975f5c5SAndroid Build Coastguard Worker      return colorama.Style.DIM
831*8975f5c5SAndroid Build Coastguard Worker    return ''
832*8975f5c5SAndroid Build Coastguard Worker
833*8975f5c5SAndroid Build Coastguard Worker  def _GetPriorityStyle(self, priority, dim=False):
834*8975f5c5SAndroid Build Coastguard Worker    # pylint:disable=no-self-use
835*8975f5c5SAndroid Build Coastguard Worker    if dim:
836*8975f5c5SAndroid Build Coastguard Worker      return ''
837*8975f5c5SAndroid Build Coastguard Worker    style = colorama.Fore.BLACK
838*8975f5c5SAndroid Build Coastguard Worker    if priority in ('E', 'F'):
839*8975f5c5SAndroid Build Coastguard Worker      style += colorama.Back.RED
840*8975f5c5SAndroid Build Coastguard Worker    elif priority == 'W':
841*8975f5c5SAndroid Build Coastguard Worker      style += colorama.Back.YELLOW
842*8975f5c5SAndroid Build Coastguard Worker    elif priority == 'I':
843*8975f5c5SAndroid Build Coastguard Worker      style += colorama.Back.GREEN
844*8975f5c5SAndroid Build Coastguard Worker    elif priority == 'D':
845*8975f5c5SAndroid Build Coastguard Worker      style += colorama.Back.BLUE
846*8975f5c5SAndroid Build Coastguard Worker    return style
847*8975f5c5SAndroid Build Coastguard Worker
848*8975f5c5SAndroid Build Coastguard Worker  def _ParseLine(self, line):
849*8975f5c5SAndroid Build Coastguard Worker    tokens = line.split(None, 6)
850*8975f5c5SAndroid Build Coastguard Worker
851*8975f5c5SAndroid Build Coastguard Worker    def consume_token_or_default(default):
852*8975f5c5SAndroid Build Coastguard Worker      return tokens.pop(0) if len(tokens) > 0 else default
853*8975f5c5SAndroid Build Coastguard Worker
854*8975f5c5SAndroid Build Coastguard Worker    def consume_integer_token_or_default(default):
855*8975f5c5SAndroid Build Coastguard Worker      if len(tokens) == 0:
856*8975f5c5SAndroid Build Coastguard Worker        return default
857*8975f5c5SAndroid Build Coastguard Worker
858*8975f5c5SAndroid Build Coastguard Worker      try:
859*8975f5c5SAndroid Build Coastguard Worker        return int(tokens.pop(0))
860*8975f5c5SAndroid Build Coastguard Worker      except ValueError:
861*8975f5c5SAndroid Build Coastguard Worker        return default
862*8975f5c5SAndroid Build Coastguard Worker
863*8975f5c5SAndroid Build Coastguard Worker    date = consume_token_or_default('')
864*8975f5c5SAndroid Build Coastguard Worker    invokation_time = consume_token_or_default('')
865*8975f5c5SAndroid Build Coastguard Worker    pid = consume_integer_token_or_default(-1)
866*8975f5c5SAndroid Build Coastguard Worker    tid = consume_integer_token_or_default(-1)
867*8975f5c5SAndroid Build Coastguard Worker    priority = consume_token_or_default('')
868*8975f5c5SAndroid Build Coastguard Worker    tag = consume_token_or_default('')
869*8975f5c5SAndroid Build Coastguard Worker    original_message = consume_token_or_default('')
870*8975f5c5SAndroid Build Coastguard Worker
871*8975f5c5SAndroid Build Coastguard Worker    # Example:
872*8975f5c5SAndroid Build Coastguard Worker    #   09-19 06:35:51.113  9060  9154 W GCoreFlp: No location...
873*8975f5c5SAndroid Build Coastguard Worker    #   09-19 06:01:26.174  9060 10617 I Auth    : [ReflectiveChannelBinder]...
874*8975f5c5SAndroid Build Coastguard Worker    # Parsing "GCoreFlp:" vs "Auth    :", we only want tag to contain the word,
875*8975f5c5SAndroid Build Coastguard Worker    # and we don't want to keep the colon for the message.
876*8975f5c5SAndroid Build Coastguard Worker    if tag and tag[-1] == ':':
877*8975f5c5SAndroid Build Coastguard Worker      tag = tag[:-1]
878*8975f5c5SAndroid Build Coastguard Worker    elif len(original_message) > 2:
879*8975f5c5SAndroid Build Coastguard Worker      original_message = original_message[2:]
880*8975f5c5SAndroid Build Coastguard Worker    return self.ParsedLine(
881*8975f5c5SAndroid Build Coastguard Worker        date, invokation_time, pid, tid, priority, tag, original_message)
882*8975f5c5SAndroid Build Coastguard Worker
883*8975f5c5SAndroid Build Coastguard Worker  def _PrintParsedLine(self, parsed_line, dim=False):
884*8975f5c5SAndroid Build Coastguard Worker    if self._exit_on_match and self._exit_on_match.search(parsed_line.message):
885*8975f5c5SAndroid Build Coastguard Worker      self._found_exit_match = True
886*8975f5c5SAndroid Build Coastguard Worker
887*8975f5c5SAndroid Build Coastguard Worker    tid_style = colorama.Style.NORMAL
888*8975f5c5SAndroid Build Coastguard Worker    user_match = self._user_defined_highlight and (
889*8975f5c5SAndroid Build Coastguard Worker        re.search(self._user_defined_highlight, parsed_line.tag)
890*8975f5c5SAndroid Build Coastguard Worker        or re.search(self._user_defined_highlight, parsed_line.message))
891*8975f5c5SAndroid Build Coastguard Worker
892*8975f5c5SAndroid Build Coastguard Worker    # Make the main thread bright.
893*8975f5c5SAndroid Build Coastguard Worker    if not dim and parsed_line.pid == parsed_line.tid:
894*8975f5c5SAndroid Build Coastguard Worker      tid_style = colorama.Style.BRIGHT
895*8975f5c5SAndroid Build Coastguard Worker    pid_style = self._GetPidStyle(parsed_line.pid, dim)
896*8975f5c5SAndroid Build Coastguard Worker    msg_style = pid_style if not user_match else (colorama.Fore.GREEN +
897*8975f5c5SAndroid Build Coastguard Worker                                                  colorama.Style.BRIGHT)
898*8975f5c5SAndroid Build Coastguard Worker    # We have to pad before adding color as that changes the width of the tag.
899*8975f5c5SAndroid Build Coastguard Worker    pid_str = _Colorize('{:5}'.format(parsed_line.pid), pid_style)
900*8975f5c5SAndroid Build Coastguard Worker    tid_str = _Colorize('{:5}'.format(parsed_line.tid), tid_style)
901*8975f5c5SAndroid Build Coastguard Worker    tag = _Colorize('{:8}'.format(parsed_line.tag),
902*8975f5c5SAndroid Build Coastguard Worker                    pid_style + ('' if dim else colorama.Style.BRIGHT))
903*8975f5c5SAndroid Build Coastguard Worker    priority = _Colorize(parsed_line.priority,
904*8975f5c5SAndroid Build Coastguard Worker                         self._GetPriorityStyle(parsed_line.priority))
905*8975f5c5SAndroid Build Coastguard Worker    messages = [parsed_line.message]
906*8975f5c5SAndroid Build Coastguard Worker    if self._deobfuscator:
907*8975f5c5SAndroid Build Coastguard Worker      messages = self._deobfuscator.TransformLines(messages)
908*8975f5c5SAndroid Build Coastguard Worker    for message in messages:
909*8975f5c5SAndroid Build Coastguard Worker      message = _Colorize(message, msg_style)
910*8975f5c5SAndroid Build Coastguard Worker      sys.stdout.write('{} {} {} {} {} {}: {}\n'.format(
911*8975f5c5SAndroid Build Coastguard Worker          parsed_line.date, parsed_line.invokation_time, pid_str, tid_str,
912*8975f5c5SAndroid Build Coastguard Worker          priority, tag, message))
913*8975f5c5SAndroid Build Coastguard Worker
914*8975f5c5SAndroid Build Coastguard Worker  def _TriggerNonceFound(self):
915*8975f5c5SAndroid Build Coastguard Worker    # Once the nonce is hit, we have confidence that we know which lines
916*8975f5c5SAndroid Build Coastguard Worker    # belong to the current run of the app. Process all of the buffered lines.
917*8975f5c5SAndroid Build Coastguard Worker    if self._primary_pid:
918*8975f5c5SAndroid Build Coastguard Worker      for args in self._initial_buffered_lines:
919*8975f5c5SAndroid Build Coastguard Worker        self._print_func(*args)
920*8975f5c5SAndroid Build Coastguard Worker    self._initial_buffered_lines = None
921*8975f5c5SAndroid Build Coastguard Worker    self.nonce = None
922*8975f5c5SAndroid Build Coastguard Worker
923*8975f5c5SAndroid Build Coastguard Worker  def FoundExitMatch(self):
924*8975f5c5SAndroid Build Coastguard Worker    return self._found_exit_match
925*8975f5c5SAndroid Build Coastguard Worker
926*8975f5c5SAndroid Build Coastguard Worker  def ProcessLine(self, line):
927*8975f5c5SAndroid Build Coastguard Worker    if not line or line.startswith('------'):
928*8975f5c5SAndroid Build Coastguard Worker      return
929*8975f5c5SAndroid Build Coastguard Worker
930*8975f5c5SAndroid Build Coastguard Worker    if self.nonce and self.nonce in line:
931*8975f5c5SAndroid Build Coastguard Worker      self._TriggerNonceFound()
932*8975f5c5SAndroid Build Coastguard Worker
933*8975f5c5SAndroid Build Coastguard Worker    nonce_found = self.nonce is None
934*8975f5c5SAndroid Build Coastguard Worker
935*8975f5c5SAndroid Build Coastguard Worker    log = self._ParseLine(line)
936*8975f5c5SAndroid Build Coastguard Worker    if log.pid not in self._seen_pids:
937*8975f5c5SAndroid Build Coastguard Worker      self._seen_pids.add(log.pid)
938*8975f5c5SAndroid Build Coastguard Worker      if nonce_found:
939*8975f5c5SAndroid Build Coastguard Worker        # Update list of owned PIDs each time a new PID is encountered.
940*8975f5c5SAndroid Build Coastguard Worker        self._UpdateMyPids()
941*8975f5c5SAndroid Build Coastguard Worker
942*8975f5c5SAndroid Build Coastguard Worker    # Search for "Start proc $pid:$package_name/" message.
943*8975f5c5SAndroid Build Coastguard Worker    if not nonce_found:
944*8975f5c5SAndroid Build Coastguard Worker      # Capture logs before the nonce. Start with the most recent "am start".
945*8975f5c5SAndroid Build Coastguard Worker      if self._start_pattern.match(log.message):
946*8975f5c5SAndroid Build Coastguard Worker        self._initial_buffered_lines = []
947*8975f5c5SAndroid Build Coastguard Worker
948*8975f5c5SAndroid Build Coastguard Worker      # If we didn't find the PID via "ps", then extract it from log messages.
949*8975f5c5SAndroid Build Coastguard Worker      # This will happen if the app crashes too quickly.
950*8975f5c5SAndroid Build Coastguard Worker      if not self._found_initial_pid:
951*8975f5c5SAndroid Build Coastguard Worker        m = self._pid_pattern.match(log.message)
952*8975f5c5SAndroid Build Coastguard Worker        if m:
953*8975f5c5SAndroid Build Coastguard Worker          # Find the most recent "Start proc" line before the nonce.
954*8975f5c5SAndroid Build Coastguard Worker          # Track only the primary pid in this mode.
955*8975f5c5SAndroid Build Coastguard Worker          # The main use-case is to find app logs when no current PIDs exist.
956*8975f5c5SAndroid Build Coastguard Worker          # E.g.: When the app crashes on launch.
957*8975f5c5SAndroid Build Coastguard Worker          self._primary_pid = m.group(1)
958*8975f5c5SAndroid Build Coastguard Worker          self._my_pids.clear()
959*8975f5c5SAndroid Build Coastguard Worker          self._my_pids.add(m.group(1))
960*8975f5c5SAndroid Build Coastguard Worker
961*8975f5c5SAndroid Build Coastguard Worker    owned_pid = log.pid in self._my_pids
962*8975f5c5SAndroid Build Coastguard Worker    if owned_pid and not self._verbose and log.tag == 'dalvikvm':
963*8975f5c5SAndroid Build Coastguard Worker      if self._DALVIK_IGNORE_PATTERN.match(log.message):
964*8975f5c5SAndroid Build Coastguard Worker        return
965*8975f5c5SAndroid Build Coastguard Worker
966*8975f5c5SAndroid Build Coastguard Worker    if owned_pid or self._verbose or (log.priority == 'F' or  # Java crash dump
967*8975f5c5SAndroid Build Coastguard Worker                                      log.tag in self._ALLOWLISTED_TAGS):
968*8975f5c5SAndroid Build Coastguard Worker      if nonce_found:
969*8975f5c5SAndroid Build Coastguard Worker        self._print_func(log, not owned_pid)
970*8975f5c5SAndroid Build Coastguard Worker      else:
971*8975f5c5SAndroid Build Coastguard Worker        self._initial_buffered_lines.append((log, not owned_pid))
972*8975f5c5SAndroid Build Coastguard Worker
973*8975f5c5SAndroid Build Coastguard Worker
974*8975f5c5SAndroid Build Coastguard Workerdef _RunLogcat(device,
975*8975f5c5SAndroid Build Coastguard Worker               package_name,
976*8975f5c5SAndroid Build Coastguard Worker               stack_script_context,
977*8975f5c5SAndroid Build Coastguard Worker               deobfuscate,
978*8975f5c5SAndroid Build Coastguard Worker               verbose,
979*8975f5c5SAndroid Build Coastguard Worker               exit_on_match=None,
980*8975f5c5SAndroid Build Coastguard Worker               extra_package_names=None):
981*8975f5c5SAndroid Build Coastguard Worker  logcat_processor = _LogcatProcessor(device,
982*8975f5c5SAndroid Build Coastguard Worker                                      package_name,
983*8975f5c5SAndroid Build Coastguard Worker                                      stack_script_context,
984*8975f5c5SAndroid Build Coastguard Worker                                      deobfuscate,
985*8975f5c5SAndroid Build Coastguard Worker                                      verbose,
986*8975f5c5SAndroid Build Coastguard Worker                                      exit_on_match=exit_on_match,
987*8975f5c5SAndroid Build Coastguard Worker                                      extra_package_names=extra_package_names)
988*8975f5c5SAndroid Build Coastguard Worker  device.RunShellCommand(['log', logcat_processor.nonce])
989*8975f5c5SAndroid Build Coastguard Worker  for line in device.adb.Logcat(logcat_format='threadtime'):
990*8975f5c5SAndroid Build Coastguard Worker    try:
991*8975f5c5SAndroid Build Coastguard Worker      logcat_processor.ProcessLine(line)
992*8975f5c5SAndroid Build Coastguard Worker      if logcat_processor.FoundExitMatch():
993*8975f5c5SAndroid Build Coastguard Worker        return
994*8975f5c5SAndroid Build Coastguard Worker    except:
995*8975f5c5SAndroid Build Coastguard Worker      sys.stderr.write('Failed to process line: ' + line + '\n')
996*8975f5c5SAndroid Build Coastguard Worker      # Skip stack trace for the common case of the adb server being
997*8975f5c5SAndroid Build Coastguard Worker      # restarted.
998*8975f5c5SAndroid Build Coastguard Worker      if 'unexpected EOF' in line:
999*8975f5c5SAndroid Build Coastguard Worker        sys.exit(1)
1000*8975f5c5SAndroid Build Coastguard Worker      raise
1001*8975f5c5SAndroid Build Coastguard Worker
1002*8975f5c5SAndroid Build Coastguard Worker
1003*8975f5c5SAndroid Build Coastguard Workerdef _GetPackageProcesses(device, package_name):
1004*8975f5c5SAndroid Build Coastguard Worker  my_names = (package_name, package_name + '_zygote')
1005*8975f5c5SAndroid Build Coastguard Worker  return [
1006*8975f5c5SAndroid Build Coastguard Worker      p for p in device.ListProcesses(package_name)
1007*8975f5c5SAndroid Build Coastguard Worker      if p.name in my_names or p.name.startswith(package_name + ':')
1008*8975f5c5SAndroid Build Coastguard Worker  ]
1009*8975f5c5SAndroid Build Coastguard Worker
1010*8975f5c5SAndroid Build Coastguard Worker
1011*8975f5c5SAndroid Build Coastguard Workerdef _RunPs(devices, package_name):
1012*8975f5c5SAndroid Build Coastguard Worker  parallel_devices = device_utils.DeviceUtils.parallel(devices)
1013*8975f5c5SAndroid Build Coastguard Worker  all_processes = parallel_devices.pMap(
1014*8975f5c5SAndroid Build Coastguard Worker      lambda d: _GetPackageProcesses(d, package_name)).pGet(None)
1015*8975f5c5SAndroid Build Coastguard Worker  for processes in _PrintPerDeviceOutput(devices, all_processes):
1016*8975f5c5SAndroid Build Coastguard Worker    if not processes:
1017*8975f5c5SAndroid Build Coastguard Worker      print('No processes found.')
1018*8975f5c5SAndroid Build Coastguard Worker    else:
1019*8975f5c5SAndroid Build Coastguard Worker      proc_map = collections.defaultdict(list)
1020*8975f5c5SAndroid Build Coastguard Worker      for p in processes:
1021*8975f5c5SAndroid Build Coastguard Worker        proc_map[p.name].append(str(p.pid))
1022*8975f5c5SAndroid Build Coastguard Worker      for name, pids in sorted(proc_map.items()):
1023*8975f5c5SAndroid Build Coastguard Worker        print(name, ','.join(pids))
1024*8975f5c5SAndroid Build Coastguard Worker
1025*8975f5c5SAndroid Build Coastguard Worker
1026*8975f5c5SAndroid Build Coastguard Workerdef _RunShell(devices, package_name, cmd):
1027*8975f5c5SAndroid Build Coastguard Worker  if cmd:
1028*8975f5c5SAndroid Build Coastguard Worker    parallel_devices = device_utils.DeviceUtils.parallel(devices)
1029*8975f5c5SAndroid Build Coastguard Worker    outputs = parallel_devices.RunShellCommand(
1030*8975f5c5SAndroid Build Coastguard Worker        cmd, run_as=package_name).pGet(None)
1031*8975f5c5SAndroid Build Coastguard Worker    for output in _PrintPerDeviceOutput(devices, outputs):
1032*8975f5c5SAndroid Build Coastguard Worker      for line in output:
1033*8975f5c5SAndroid Build Coastguard Worker        print(line)
1034*8975f5c5SAndroid Build Coastguard Worker  else:
1035*8975f5c5SAndroid Build Coastguard Worker    adb_path = adb_wrapper.AdbWrapper.GetAdbPath()
1036*8975f5c5SAndroid Build Coastguard Worker    cmd = [adb_path, '-s', devices[0].serial, 'shell']
1037*8975f5c5SAndroid Build Coastguard Worker    # Pre-N devices do not support -t flag.
1038*8975f5c5SAndroid Build Coastguard Worker    if devices[0].build_version_sdk >= version_codes.NOUGAT:
1039*8975f5c5SAndroid Build Coastguard Worker      cmd += ['-t', 'run-as', package_name]
1040*8975f5c5SAndroid Build Coastguard Worker    else:
1041*8975f5c5SAndroid Build Coastguard Worker      print('Upon entering the shell, run:')
1042*8975f5c5SAndroid Build Coastguard Worker      print('run-as', package_name)
1043*8975f5c5SAndroid Build Coastguard Worker      print()
1044*8975f5c5SAndroid Build Coastguard Worker    os.execv(adb_path, cmd)
1045*8975f5c5SAndroid Build Coastguard Worker
1046*8975f5c5SAndroid Build Coastguard Worker
1047*8975f5c5SAndroid Build Coastguard Workerdef _RunCompileDex(devices, package_name, compilation_filter):
1048*8975f5c5SAndroid Build Coastguard Worker  cmd = ['cmd', 'package', 'compile', '-f', '-m', compilation_filter,
1049*8975f5c5SAndroid Build Coastguard Worker         package_name]
1050*8975f5c5SAndroid Build Coastguard Worker  parallel_devices = device_utils.DeviceUtils.parallel(devices)
1051*8975f5c5SAndroid Build Coastguard Worker  return parallel_devices.RunShellCommand(cmd, timeout=120).pGet(None)
1052*8975f5c5SAndroid Build Coastguard Worker
1053*8975f5c5SAndroid Build Coastguard Worker
1054*8975f5c5SAndroid Build Coastguard Workerdef _RunProfile(device, package_name, host_build_directory, pprof_out_path,
1055*8975f5c5SAndroid Build Coastguard Worker                process_specifier, thread_specifier, events, extra_args):
1056*8975f5c5SAndroid Build Coastguard Worker  simpleperf.PrepareDevice(device)
1057*8975f5c5SAndroid Build Coastguard Worker  device_simpleperf_path = simpleperf.InstallSimpleperf(device, package_name)
1058*8975f5c5SAndroid Build Coastguard Worker  with tempfile.NamedTemporaryFile() as fh:
1059*8975f5c5SAndroid Build Coastguard Worker    host_simpleperf_out_path = fh.name
1060*8975f5c5SAndroid Build Coastguard Worker
1061*8975f5c5SAndroid Build Coastguard Worker    with simpleperf.RunSimpleperf(device, device_simpleperf_path, package_name,
1062*8975f5c5SAndroid Build Coastguard Worker                                  process_specifier, thread_specifier,
1063*8975f5c5SAndroid Build Coastguard Worker                                  events, extra_args, host_simpleperf_out_path):
1064*8975f5c5SAndroid Build Coastguard Worker      sys.stdout.write('Profiler is running; press Enter to stop...\n')
1065*8975f5c5SAndroid Build Coastguard Worker      sys.stdin.read(1)
1066*8975f5c5SAndroid Build Coastguard Worker      sys.stdout.write('Post-processing data...\n')
1067*8975f5c5SAndroid Build Coastguard Worker
1068*8975f5c5SAndroid Build Coastguard Worker    simpleperf.ConvertSimpleperfToPprof(host_simpleperf_out_path,
1069*8975f5c5SAndroid Build Coastguard Worker                                        host_build_directory, pprof_out_path)
1070*8975f5c5SAndroid Build Coastguard Worker    print(textwrap.dedent("""
1071*8975f5c5SAndroid Build Coastguard Worker        Profile data written to %(s)s.
1072*8975f5c5SAndroid Build Coastguard Worker
1073*8975f5c5SAndroid Build Coastguard Worker        To view profile as a call graph in browser:
1074*8975f5c5SAndroid Build Coastguard Worker          pprof -web %(s)s
1075*8975f5c5SAndroid Build Coastguard Worker
1076*8975f5c5SAndroid Build Coastguard Worker        To print the hottest methods:
1077*8975f5c5SAndroid Build Coastguard Worker          pprof -top %(s)s
1078*8975f5c5SAndroid Build Coastguard Worker
1079*8975f5c5SAndroid Build Coastguard Worker        pprof has many useful customization options; `pprof --help` for details.
1080*8975f5c5SAndroid Build Coastguard Worker        """ % {'s': pprof_out_path}))
1081*8975f5c5SAndroid Build Coastguard Worker
1082*8975f5c5SAndroid Build Coastguard Worker
1083*8975f5c5SAndroid Build Coastguard Workerclass _StackScriptContext:
1084*8975f5c5SAndroid Build Coastguard Worker  """Maintains temporary files needed by stack.py."""
1085*8975f5c5SAndroid Build Coastguard Worker
1086*8975f5c5SAndroid Build Coastguard Worker  def __init__(self,
1087*8975f5c5SAndroid Build Coastguard Worker               output_directory,
1088*8975f5c5SAndroid Build Coastguard Worker               apk_path,
1089*8975f5c5SAndroid Build Coastguard Worker               bundle_generation_info,
1090*8975f5c5SAndroid Build Coastguard Worker               quiet=False):
1091*8975f5c5SAndroid Build Coastguard Worker    self._output_directory = output_directory
1092*8975f5c5SAndroid Build Coastguard Worker    self._apk_path = apk_path
1093*8975f5c5SAndroid Build Coastguard Worker    self._bundle_generation_info = bundle_generation_info
1094*8975f5c5SAndroid Build Coastguard Worker    self._staging_dir = None
1095*8975f5c5SAndroid Build Coastguard Worker    self._quiet = quiet
1096*8975f5c5SAndroid Build Coastguard Worker
1097*8975f5c5SAndroid Build Coastguard Worker  def _CreateStaging(self):
1098*8975f5c5SAndroid Build Coastguard Worker    # In many cases, stack decoding requires APKs to map trace lines to native
1099*8975f5c5SAndroid Build Coastguard Worker    # libraries. Create a temporary directory, and either unpack a bundle's
1100*8975f5c5SAndroid Build Coastguard Worker    # APKS into it, or simply symlink the standalone APK into it. This
1101*8975f5c5SAndroid Build Coastguard Worker    # provides an unambiguous set of APK files for the stack decoding process
1102*8975f5c5SAndroid Build Coastguard Worker    # to inspect.
1103*8975f5c5SAndroid Build Coastguard Worker    logging.debug('Creating stack staging directory')
1104*8975f5c5SAndroid Build Coastguard Worker    self._staging_dir = tempfile.mkdtemp()
1105*8975f5c5SAndroid Build Coastguard Worker    bundle_generation_info = self._bundle_generation_info
1106*8975f5c5SAndroid Build Coastguard Worker
1107*8975f5c5SAndroid Build Coastguard Worker    if bundle_generation_info:
1108*8975f5c5SAndroid Build Coastguard Worker      # TODO(wnwen): Use apk_helper instead.
1109*8975f5c5SAndroid Build Coastguard Worker      _GenerateBundleApks(bundle_generation_info)
1110*8975f5c5SAndroid Build Coastguard Worker      logging.debug('Extracting .apks file')
1111*8975f5c5SAndroid Build Coastguard Worker      with zipfile.ZipFile(bundle_generation_info.bundle_apks_path, 'r') as z:
1112*8975f5c5SAndroid Build Coastguard Worker        files_to_extract = [
1113*8975f5c5SAndroid Build Coastguard Worker            f for f in z.namelist() if f.endswith('-master.apk')
1114*8975f5c5SAndroid Build Coastguard Worker        ]
1115*8975f5c5SAndroid Build Coastguard Worker        z.extractall(self._staging_dir, files_to_extract)
1116*8975f5c5SAndroid Build Coastguard Worker    elif self._apk_path:
1117*8975f5c5SAndroid Build Coastguard Worker      # Otherwise an incremental APK and an empty apks directory is correct.
1118*8975f5c5SAndroid Build Coastguard Worker      output = os.path.join(self._staging_dir, os.path.basename(self._apk_path))
1119*8975f5c5SAndroid Build Coastguard Worker      os.symlink(self._apk_path, output)
1120*8975f5c5SAndroid Build Coastguard Worker
1121*8975f5c5SAndroid Build Coastguard Worker  def Close(self):
1122*8975f5c5SAndroid Build Coastguard Worker    if self._staging_dir:
1123*8975f5c5SAndroid Build Coastguard Worker      logging.debug('Clearing stack staging directory')
1124*8975f5c5SAndroid Build Coastguard Worker      shutil.rmtree(self._staging_dir)
1125*8975f5c5SAndroid Build Coastguard Worker      self._staging_dir = None
1126*8975f5c5SAndroid Build Coastguard Worker
1127*8975f5c5SAndroid Build Coastguard Worker  def Popen(self, input_file=None, **kwargs):
1128*8975f5c5SAndroid Build Coastguard Worker    if self._staging_dir is None:
1129*8975f5c5SAndroid Build Coastguard Worker      self._CreateStaging()
1130*8975f5c5SAndroid Build Coastguard Worker    stack_script = os.path.join(
1131*8975f5c5SAndroid Build Coastguard Worker        constants.host_paths.ANDROID_PLATFORM_DEVELOPMENT_SCRIPTS_PATH,
1132*8975f5c5SAndroid Build Coastguard Worker        'stack.py')
1133*8975f5c5SAndroid Build Coastguard Worker    cmd = [
1134*8975f5c5SAndroid Build Coastguard Worker        stack_script, '--output-directory', self._output_directory,
1135*8975f5c5SAndroid Build Coastguard Worker        '--apks-directory', self._staging_dir
1136*8975f5c5SAndroid Build Coastguard Worker    ]
1137*8975f5c5SAndroid Build Coastguard Worker    if self._quiet:
1138*8975f5c5SAndroid Build Coastguard Worker      cmd.append('--quiet')
1139*8975f5c5SAndroid Build Coastguard Worker    if input_file:
1140*8975f5c5SAndroid Build Coastguard Worker      cmd.append(input_file)
1141*8975f5c5SAndroid Build Coastguard Worker    logging.info('Running: %s', shlex.join(cmd))
1142*8975f5c5SAndroid Build Coastguard Worker    return subprocess.Popen(cmd, universal_newlines=True, **kwargs)
1143*8975f5c5SAndroid Build Coastguard Worker
1144*8975f5c5SAndroid Build Coastguard Worker
1145*8975f5c5SAndroid Build Coastguard Workerdef _GenerateAvailableDevicesMessage(devices):
1146*8975f5c5SAndroid Build Coastguard Worker  devices_obj = device_utils.DeviceUtils.parallel(devices)
1147*8975f5c5SAndroid Build Coastguard Worker  descriptions = devices_obj.pMap(lambda d: d.build_description).pGet(None)
1148*8975f5c5SAndroid Build Coastguard Worker  msg = 'Available devices:\n'
1149*8975f5c5SAndroid Build Coastguard Worker  for d, desc in zip(devices, descriptions):
1150*8975f5c5SAndroid Build Coastguard Worker    msg += '  %s (%s)\n' % (d, desc)
1151*8975f5c5SAndroid Build Coastguard Worker  return msg
1152*8975f5c5SAndroid Build Coastguard Worker
1153*8975f5c5SAndroid Build Coastguard Worker
1154*8975f5c5SAndroid Build Coastguard Worker# TODO(agrieve):add "--all" in the MultipleDevicesError message and use it here.
1155*8975f5c5SAndroid Build Coastguard Workerdef _GenerateMissingAllFlagMessage(devices):
1156*8975f5c5SAndroid Build Coastguard Worker  return ('More than one device available. Use --all to select all devices, ' +
1157*8975f5c5SAndroid Build Coastguard Worker          'or use --device to select a device by serial.\n\n' +
1158*8975f5c5SAndroid Build Coastguard Worker          _GenerateAvailableDevicesMessage(devices))
1159*8975f5c5SAndroid Build Coastguard Worker
1160*8975f5c5SAndroid Build Coastguard Workerdef _SanitizeFilename(filename):
1161*8975f5c5SAndroid Build Coastguard Worker  for sep in os.path.sep, os.path.altsep:
1162*8975f5c5SAndroid Build Coastguard Worker    if sep is not None:
1163*8975f5c5SAndroid Build Coastguard Worker      filename = filename.replace(sep, '_')
1164*8975f5c5SAndroid Build Coastguard Worker  return filename
1165*8975f5c5SAndroid Build Coastguard Worker
1166*8975f5c5SAndroid Build Coastguard Worker
1167*8975f5c5SAndroid Build Coastguard Workerdef _DeviceCachePath(device, output_directory):
1168*8975f5c5SAndroid Build Coastguard Worker  file_name = 'device_cache_%s.json' % _SanitizeFilename(device.serial)
1169*8975f5c5SAndroid Build Coastguard Worker  return os.path.join(output_directory, file_name)
1170*8975f5c5SAndroid Build Coastguard Worker
1171*8975f5c5SAndroid Build Coastguard Worker
1172*8975f5c5SAndroid Build Coastguard Workerdef _LoadDeviceCaches(devices, output_directory):
1173*8975f5c5SAndroid Build Coastguard Worker  if not output_directory:
1174*8975f5c5SAndroid Build Coastguard Worker    return
1175*8975f5c5SAndroid Build Coastguard Worker  for d in devices:
1176*8975f5c5SAndroid Build Coastguard Worker    cache_path = _DeviceCachePath(d, output_directory)
1177*8975f5c5SAndroid Build Coastguard Worker    if os.path.exists(cache_path):
1178*8975f5c5SAndroid Build Coastguard Worker      logging.debug('Using device cache: %s', cache_path)
1179*8975f5c5SAndroid Build Coastguard Worker      with open(cache_path) as f:
1180*8975f5c5SAndroid Build Coastguard Worker        d.LoadCacheData(f.read())
1181*8975f5c5SAndroid Build Coastguard Worker      # Delete the cached file so that any exceptions cause it to be cleared.
1182*8975f5c5SAndroid Build Coastguard Worker      os.unlink(cache_path)
1183*8975f5c5SAndroid Build Coastguard Worker    else:
1184*8975f5c5SAndroid Build Coastguard Worker      logging.debug('No cache present for device: %s', d)
1185*8975f5c5SAndroid Build Coastguard Worker
1186*8975f5c5SAndroid Build Coastguard Worker
1187*8975f5c5SAndroid Build Coastguard Workerdef _SaveDeviceCaches(devices, output_directory):
1188*8975f5c5SAndroid Build Coastguard Worker  if not output_directory:
1189*8975f5c5SAndroid Build Coastguard Worker    return
1190*8975f5c5SAndroid Build Coastguard Worker  for d in devices:
1191*8975f5c5SAndroid Build Coastguard Worker    cache_path = _DeviceCachePath(d, output_directory)
1192*8975f5c5SAndroid Build Coastguard Worker    with open(cache_path, 'w') as f:
1193*8975f5c5SAndroid Build Coastguard Worker      f.write(d.DumpCacheData())
1194*8975f5c5SAndroid Build Coastguard Worker      logging.info('Wrote device cache: %s', cache_path)
1195*8975f5c5SAndroid Build Coastguard Worker
1196*8975f5c5SAndroid Build Coastguard Worker
1197*8975f5c5SAndroid Build Coastguard Workerclass _Command:
1198*8975f5c5SAndroid Build Coastguard Worker  name = None
1199*8975f5c5SAndroid Build Coastguard Worker  description = None
1200*8975f5c5SAndroid Build Coastguard Worker  long_description = None
1201*8975f5c5SAndroid Build Coastguard Worker  needs_package_name = False
1202*8975f5c5SAndroid Build Coastguard Worker  needs_output_directory = False
1203*8975f5c5SAndroid Build Coastguard Worker  needs_apk_helper = False
1204*8975f5c5SAndroid Build Coastguard Worker  supports_incremental = False
1205*8975f5c5SAndroid Build Coastguard Worker  accepts_command_line_flags = False
1206*8975f5c5SAndroid Build Coastguard Worker  accepts_args = False
1207*8975f5c5SAndroid Build Coastguard Worker  need_device_args = True
1208*8975f5c5SAndroid Build Coastguard Worker  all_devices_by_default = False
1209*8975f5c5SAndroid Build Coastguard Worker  calls_exec = False
1210*8975f5c5SAndroid Build Coastguard Worker  supports_multiple_devices = True
1211*8975f5c5SAndroid Build Coastguard Worker
1212*8975f5c5SAndroid Build Coastguard Worker  def __init__(self, from_wrapper_script, is_bundle, is_test_apk):
1213*8975f5c5SAndroid Build Coastguard Worker    self._parser = None
1214*8975f5c5SAndroid Build Coastguard Worker    self._from_wrapper_script = from_wrapper_script
1215*8975f5c5SAndroid Build Coastguard Worker    self.args = None
1216*8975f5c5SAndroid Build Coastguard Worker    self.incremental_apk_path = None
1217*8975f5c5SAndroid Build Coastguard Worker    self.apk_helper = None
1218*8975f5c5SAndroid Build Coastguard Worker    self.additional_apk_helpers = None
1219*8975f5c5SAndroid Build Coastguard Worker    self.install_dict = None
1220*8975f5c5SAndroid Build Coastguard Worker    self.devices = None
1221*8975f5c5SAndroid Build Coastguard Worker    self.is_bundle = is_bundle
1222*8975f5c5SAndroid Build Coastguard Worker    self.is_test_apk = is_test_apk
1223*8975f5c5SAndroid Build Coastguard Worker    self.bundle_generation_info = None
1224*8975f5c5SAndroid Build Coastguard Worker    # Only support  incremental install from APK wrapper scripts.
1225*8975f5c5SAndroid Build Coastguard Worker    if is_bundle or not from_wrapper_script:
1226*8975f5c5SAndroid Build Coastguard Worker      self.supports_incremental = False
1227*8975f5c5SAndroid Build Coastguard Worker
1228*8975f5c5SAndroid Build Coastguard Worker  def RegisterBundleGenerationInfo(self, bundle_generation_info):
1229*8975f5c5SAndroid Build Coastguard Worker    self.bundle_generation_info = bundle_generation_info
1230*8975f5c5SAndroid Build Coastguard Worker
1231*8975f5c5SAndroid Build Coastguard Worker  def _RegisterExtraArgs(self, group):
1232*8975f5c5SAndroid Build Coastguard Worker    pass
1233*8975f5c5SAndroid Build Coastguard Worker
1234*8975f5c5SAndroid Build Coastguard Worker  def RegisterArgs(self, parser):
1235*8975f5c5SAndroid Build Coastguard Worker    subp = parser.add_parser(
1236*8975f5c5SAndroid Build Coastguard Worker        self.name, help=self.description,
1237*8975f5c5SAndroid Build Coastguard Worker        description=self.long_description or self.description,
1238*8975f5c5SAndroid Build Coastguard Worker        formatter_class=argparse.RawDescriptionHelpFormatter)
1239*8975f5c5SAndroid Build Coastguard Worker    self._parser = subp
1240*8975f5c5SAndroid Build Coastguard Worker    subp.set_defaults(command=self)
1241*8975f5c5SAndroid Build Coastguard Worker    if self.need_device_args:
1242*8975f5c5SAndroid Build Coastguard Worker      subp.add_argument('--all',
1243*8975f5c5SAndroid Build Coastguard Worker                        action='store_true',
1244*8975f5c5SAndroid Build Coastguard Worker                        default=self.all_devices_by_default,
1245*8975f5c5SAndroid Build Coastguard Worker                        help='Operate on all connected devices.',)
1246*8975f5c5SAndroid Build Coastguard Worker      subp.add_argument('-d',
1247*8975f5c5SAndroid Build Coastguard Worker                        '--device',
1248*8975f5c5SAndroid Build Coastguard Worker                        action='append',
1249*8975f5c5SAndroid Build Coastguard Worker                        default=[],
1250*8975f5c5SAndroid Build Coastguard Worker                        dest='devices',
1251*8975f5c5SAndroid Build Coastguard Worker                        help='Target device for script to work on. Enter '
1252*8975f5c5SAndroid Build Coastguard Worker                            'multiple times for multiple devices.')
1253*8975f5c5SAndroid Build Coastguard Worker    subp.add_argument('-v',
1254*8975f5c5SAndroid Build Coastguard Worker                      '--verbose',
1255*8975f5c5SAndroid Build Coastguard Worker                      action='count',
1256*8975f5c5SAndroid Build Coastguard Worker                      default=0,
1257*8975f5c5SAndroid Build Coastguard Worker                      dest='verbose_count',
1258*8975f5c5SAndroid Build Coastguard Worker                      help='Verbose level (multiple times for more)')
1259*8975f5c5SAndroid Build Coastguard Worker    group = subp.add_argument_group('%s arguments' % self.name)
1260*8975f5c5SAndroid Build Coastguard Worker
1261*8975f5c5SAndroid Build Coastguard Worker    if self.needs_package_name:
1262*8975f5c5SAndroid Build Coastguard Worker      # Three cases to consider here, since later code assumes
1263*8975f5c5SAndroid Build Coastguard Worker      #  self.args.package_name always exists, even if None:
1264*8975f5c5SAndroid Build Coastguard Worker      #
1265*8975f5c5SAndroid Build Coastguard Worker      # - Called from a bundle wrapper script, the package_name is already
1266*8975f5c5SAndroid Build Coastguard Worker      #   set through parser.set_defaults(), so don't call add_argument()
1267*8975f5c5SAndroid Build Coastguard Worker      #   to avoid overriding its value.
1268*8975f5c5SAndroid Build Coastguard Worker      #
1269*8975f5c5SAndroid Build Coastguard Worker      # - Called from an apk wrapper script. The --package-name argument
1270*8975f5c5SAndroid Build Coastguard Worker      #   should not appear, but self.args.package_name will be gleaned from
1271*8975f5c5SAndroid Build Coastguard Worker      #   the --apk-path file later.
1272*8975f5c5SAndroid Build Coastguard Worker      #
1273*8975f5c5SAndroid Build Coastguard Worker      # - Called directly, then --package-name is required on the command-line.
1274*8975f5c5SAndroid Build Coastguard Worker      #
1275*8975f5c5SAndroid Build Coastguard Worker      # Similarly is_official_build is set directly by the bundle wrapper
1276*8975f5c5SAndroid Build Coastguard Worker      # script, so it also needs to be added for non-bundle builds.
1277*8975f5c5SAndroid Build Coastguard Worker      if not self.is_bundle:
1278*8975f5c5SAndroid Build Coastguard Worker        group.add_argument(
1279*8975f5c5SAndroid Build Coastguard Worker            '--package-name',
1280*8975f5c5SAndroid Build Coastguard Worker            help=argparse.SUPPRESS if self._from_wrapper_script else (
1281*8975f5c5SAndroid Build Coastguard Worker                "App's package name."))
1282*8975f5c5SAndroid Build Coastguard Worker
1283*8975f5c5SAndroid Build Coastguard Worker        group.add_argument(
1284*8975f5c5SAndroid Build Coastguard Worker            '--is-official-build',
1285*8975f5c5SAndroid Build Coastguard Worker            action='store_true',
1286*8975f5c5SAndroid Build Coastguard Worker            default=False,
1287*8975f5c5SAndroid Build Coastguard Worker            help=argparse.SUPPRESS if self._from_wrapper_script else
1288*8975f5c5SAndroid Build Coastguard Worker            ('Whether this is an official build or not (affects perf).'))
1289*8975f5c5SAndroid Build Coastguard Worker
1290*8975f5c5SAndroid Build Coastguard Worker    if self.needs_apk_helper or self.needs_package_name:
1291*8975f5c5SAndroid Build Coastguard Worker      # Adding this argument to the subparser would override the set_defaults()
1292*8975f5c5SAndroid Build Coastguard Worker      # value set by on the parent parser (even if None).
1293*8975f5c5SAndroid Build Coastguard Worker      if not self._from_wrapper_script and not self.is_bundle:
1294*8975f5c5SAndroid Build Coastguard Worker        group.add_argument(
1295*8975f5c5SAndroid Build Coastguard Worker            '--apk-path', required=self.needs_apk_helper, help='Path to .apk')
1296*8975f5c5SAndroid Build Coastguard Worker
1297*8975f5c5SAndroid Build Coastguard Worker    if self.supports_incremental:
1298*8975f5c5SAndroid Build Coastguard Worker      group.add_argument('--incremental',
1299*8975f5c5SAndroid Build Coastguard Worker                          action='store_true',
1300*8975f5c5SAndroid Build Coastguard Worker                          default=False,
1301*8975f5c5SAndroid Build Coastguard Worker                          help='Always install an incremental apk.')
1302*8975f5c5SAndroid Build Coastguard Worker      group.add_argument('--non-incremental',
1303*8975f5c5SAndroid Build Coastguard Worker                          action='store_true',
1304*8975f5c5SAndroid Build Coastguard Worker                          default=False,
1305*8975f5c5SAndroid Build Coastguard Worker                          help='Always install a non-incremental apk.')
1306*8975f5c5SAndroid Build Coastguard Worker
1307*8975f5c5SAndroid Build Coastguard Worker    # accepts_command_line_flags and accepts_args are mutually exclusive.
1308*8975f5c5SAndroid Build Coastguard Worker    # argparse will throw if they are both set.
1309*8975f5c5SAndroid Build Coastguard Worker    if self.accepts_command_line_flags:
1310*8975f5c5SAndroid Build Coastguard Worker      group.add_argument(
1311*8975f5c5SAndroid Build Coastguard Worker          '--args', help='Command-line flags. Use = to assign args.')
1312*8975f5c5SAndroid Build Coastguard Worker
1313*8975f5c5SAndroid Build Coastguard Worker    if self.accepts_args:
1314*8975f5c5SAndroid Build Coastguard Worker      group.add_argument(
1315*8975f5c5SAndroid Build Coastguard Worker          '--args', help='Extra arguments. Use = to assign args')
1316*8975f5c5SAndroid Build Coastguard Worker
1317*8975f5c5SAndroid Build Coastguard Worker    if not self._from_wrapper_script and self.accepts_command_line_flags:
1318*8975f5c5SAndroid Build Coastguard Worker      # Provided by wrapper scripts.
1319*8975f5c5SAndroid Build Coastguard Worker      group.add_argument(
1320*8975f5c5SAndroid Build Coastguard Worker          '--command-line-flags-file',
1321*8975f5c5SAndroid Build Coastguard Worker          help='Name of the command-line flags file')
1322*8975f5c5SAndroid Build Coastguard Worker
1323*8975f5c5SAndroid Build Coastguard Worker    self._RegisterExtraArgs(group)
1324*8975f5c5SAndroid Build Coastguard Worker
1325*8975f5c5SAndroid Build Coastguard Worker  def _CreateApkHelpers(self, args, incremental_apk_path, install_dict):
1326*8975f5c5SAndroid Build Coastguard Worker    """Returns true iff self.apk_helper was created and assigned."""
1327*8975f5c5SAndroid Build Coastguard Worker    if self.apk_helper is None:
1328*8975f5c5SAndroid Build Coastguard Worker      if args.apk_path:
1329*8975f5c5SAndroid Build Coastguard Worker        self.apk_helper = apk_helper.ToHelper(args.apk_path)
1330*8975f5c5SAndroid Build Coastguard Worker      elif incremental_apk_path:
1331*8975f5c5SAndroid Build Coastguard Worker        self.install_dict = install_dict
1332*8975f5c5SAndroid Build Coastguard Worker        self.apk_helper = apk_helper.ToHelper(incremental_apk_path)
1333*8975f5c5SAndroid Build Coastguard Worker      elif self.is_bundle:
1334*8975f5c5SAndroid Build Coastguard Worker        _GenerateBundleApks(self.bundle_generation_info)
1335*8975f5c5SAndroid Build Coastguard Worker        self.apk_helper = apk_helper.ToHelper(
1336*8975f5c5SAndroid Build Coastguard Worker            self.bundle_generation_info.bundle_apks_path)
1337*8975f5c5SAndroid Build Coastguard Worker    if args.additional_apk_paths and self.additional_apk_helpers is None:
1338*8975f5c5SAndroid Build Coastguard Worker      self.additional_apk_helpers = [
1339*8975f5c5SAndroid Build Coastguard Worker          apk_helper.ToHelper(apk_path)
1340*8975f5c5SAndroid Build Coastguard Worker          for apk_path in args.additional_apk_paths
1341*8975f5c5SAndroid Build Coastguard Worker      ]
1342*8975f5c5SAndroid Build Coastguard Worker    return self.apk_helper is not None
1343*8975f5c5SAndroid Build Coastguard Worker
1344*8975f5c5SAndroid Build Coastguard Worker  def _FindSupportedDevices(self, devices):
1345*8975f5c5SAndroid Build Coastguard Worker    """Returns supported devices and reasons for each not supported one."""
1346*8975f5c5SAndroid Build Coastguard Worker    app_abis = self.apk_helper.GetAbis()
1347*8975f5c5SAndroid Build Coastguard Worker    calling_script_name = os.path.basename(sys.argv[0])
1348*8975f5c5SAndroid Build Coastguard Worker    is_webview = 'webview' in calling_script_name
1349*8975f5c5SAndroid Build Coastguard Worker    requires_32_bit = self.apk_helper.Get32BitAbiOverride() == '0xffffffff'
1350*8975f5c5SAndroid Build Coastguard Worker    logging.debug('App supports (requires 32bit: %r, is webview: %r): %r',
1351*8975f5c5SAndroid Build Coastguard Worker                  requires_32_bit, is_webview, app_abis)
1352*8975f5c5SAndroid Build Coastguard Worker    # Webview 32_64 targets can work even on 64-bit only devices since only the
1353*8975f5c5SAndroid Build Coastguard Worker    # webview library in the target needs the correct bitness.
1354*8975f5c5SAndroid Build Coastguard Worker    if requires_32_bit and not is_webview:
1355*8975f5c5SAndroid Build Coastguard Worker      app_abis = [abi for abi in app_abis if '64' not in abi]
1356*8975f5c5SAndroid Build Coastguard Worker      logging.debug('App supports (filtered): %r', app_abis)
1357*8975f5c5SAndroid Build Coastguard Worker    if not app_abis:
1358*8975f5c5SAndroid Build Coastguard Worker      # The app does not have any native libs, so all devices can support it.
1359*8975f5c5SAndroid Build Coastguard Worker      return devices, {}
1360*8975f5c5SAndroid Build Coastguard Worker    fully_supported = []
1361*8975f5c5SAndroid Build Coastguard Worker    not_supported_reasons = {}
1362*8975f5c5SAndroid Build Coastguard Worker    for device in devices:
1363*8975f5c5SAndroid Build Coastguard Worker      device_abis = device.GetSupportedABIs()
1364*8975f5c5SAndroid Build Coastguard Worker      device_primary_abi = device_abis[0]
1365*8975f5c5SAndroid Build Coastguard Worker      logging.debug('Device primary: %s', device_primary_abi)
1366*8975f5c5SAndroid Build Coastguard Worker      logging.debug('Device supports: %r', device_abis)
1367*8975f5c5SAndroid Build Coastguard Worker
1368*8975f5c5SAndroid Build Coastguard Worker      # x86/x86_64 emulators sometimes advertises arm support but arm builds do
1369*8975f5c5SAndroid Build Coastguard Worker      # not work on them. Thus these non-functional ABIs need to be filtered out
1370*8975f5c5SAndroid Build Coastguard Worker      # here to avoid resulting in hard to understand runtime failures.
1371*8975f5c5SAndroid Build Coastguard Worker      if device_primary_abi in ('x86', 'x86_64'):
1372*8975f5c5SAndroid Build Coastguard Worker        device_abis = [abi for abi in device_abis if not abi.startswith('arm')]
1373*8975f5c5SAndroid Build Coastguard Worker        logging.debug('Device supports (filtered): %r', device_abis)
1374*8975f5c5SAndroid Build Coastguard Worker
1375*8975f5c5SAndroid Build Coastguard Worker      if any(abi in app_abis for abi in device_abis):
1376*8975f5c5SAndroid Build Coastguard Worker        fully_supported.append(device)
1377*8975f5c5SAndroid Build Coastguard Worker      else:  # No common supported ABIs between the device and app.
1378*8975f5c5SAndroid Build Coastguard Worker        if device_primary_abi == 'x86':
1379*8975f5c5SAndroid Build Coastguard Worker          target_cpu = 'x86'
1380*8975f5c5SAndroid Build Coastguard Worker        elif device_primary_abi == 'x86_64':
1381*8975f5c5SAndroid Build Coastguard Worker          target_cpu = 'x64'
1382*8975f5c5SAndroid Build Coastguard Worker        elif device_primary_abi.startswith('arm64'):
1383*8975f5c5SAndroid Build Coastguard Worker          target_cpu = 'arm64'
1384*8975f5c5SAndroid Build Coastguard Worker        elif device_primary_abi.startswith('armeabi'):
1385*8975f5c5SAndroid Build Coastguard Worker          target_cpu = 'arm'
1386*8975f5c5SAndroid Build Coastguard Worker        else:
1387*8975f5c5SAndroid Build Coastguard Worker          target_cpu = '<something else>'
1388*8975f5c5SAndroid Build Coastguard Worker        # pylint: disable=line-too-long
1389*8975f5c5SAndroid Build Coastguard Worker        native_lib_link = 'https://chromium.googlesource.com/chromium/src/+/main/docs/android_native_libraries.md'
1390*8975f5c5SAndroid Build Coastguard Worker        not_supported_reasons[device.serial] = (
1391*8975f5c5SAndroid Build Coastguard Worker            f"none of the app's ABIs ({','.join(app_abis)}) match this "
1392*8975f5c5SAndroid Build Coastguard Worker            f"device's ABIs ({','.join(device_abis)}), you may need to set "
1393*8975f5c5SAndroid Build Coastguard Worker            f'target_cpu="{target_cpu}" in your args.gn. If you already set '
1394*8975f5c5SAndroid Build Coastguard Worker            'the target_cpu arg, you may need to use one of the _64 or _64_32 '
1395*8975f5c5SAndroid Build Coastguard Worker            f'targets, see {native_lib_link} for more details.')
1396*8975f5c5SAndroid Build Coastguard Worker    return fully_supported, not_supported_reasons
1397*8975f5c5SAndroid Build Coastguard Worker
1398*8975f5c5SAndroid Build Coastguard Worker  def ProcessArgs(self, args):
1399*8975f5c5SAndroid Build Coastguard Worker    self.args = args
1400*8975f5c5SAndroid Build Coastguard Worker    # Ensure these keys always exist. They are set by wrapper scripts, but not
1401*8975f5c5SAndroid Build Coastguard Worker    # always added when not using wrapper scripts.
1402*8975f5c5SAndroid Build Coastguard Worker    args.__dict__.setdefault('apk_path', None)
1403*8975f5c5SAndroid Build Coastguard Worker    args.__dict__.setdefault('incremental_json', None)
1404*8975f5c5SAndroid Build Coastguard Worker
1405*8975f5c5SAndroid Build Coastguard Worker    self.incremental_apk_path = None
1406*8975f5c5SAndroid Build Coastguard Worker    install_dict = None
1407*8975f5c5SAndroid Build Coastguard Worker    if args.incremental_json and not (self.supports_incremental and
1408*8975f5c5SAndroid Build Coastguard Worker                                      args.non_incremental):
1409*8975f5c5SAndroid Build Coastguard Worker      with open(args.incremental_json) as f:
1410*8975f5c5SAndroid Build Coastguard Worker        install_dict = json.load(f)
1411*8975f5c5SAndroid Build Coastguard Worker        self.incremental_apk_path = os.path.join(args.output_directory,
1412*8975f5c5SAndroid Build Coastguard Worker                                                 install_dict['apk_path'])
1413*8975f5c5SAndroid Build Coastguard Worker        if not os.path.exists(self.incremental_apk_path):
1414*8975f5c5SAndroid Build Coastguard Worker          self.incremental_apk_path = None
1415*8975f5c5SAndroid Build Coastguard Worker
1416*8975f5c5SAndroid Build Coastguard Worker    if self.supports_incremental:
1417*8975f5c5SAndroid Build Coastguard Worker      if args.incremental and args.non_incremental:
1418*8975f5c5SAndroid Build Coastguard Worker        self._parser.error('Must use only one of --incremental and '
1419*8975f5c5SAndroid Build Coastguard Worker                           '--non-incremental')
1420*8975f5c5SAndroid Build Coastguard Worker      elif args.non_incremental:
1421*8975f5c5SAndroid Build Coastguard Worker        if not args.apk_path:
1422*8975f5c5SAndroid Build Coastguard Worker          self._parser.error('Apk has not been built.')
1423*8975f5c5SAndroid Build Coastguard Worker      elif args.incremental:
1424*8975f5c5SAndroid Build Coastguard Worker        if not self.incremental_apk_path:
1425*8975f5c5SAndroid Build Coastguard Worker          self._parser.error('Incremental apk has not been built.')
1426*8975f5c5SAndroid Build Coastguard Worker        args.apk_path = None
1427*8975f5c5SAndroid Build Coastguard Worker
1428*8975f5c5SAndroid Build Coastguard Worker      if args.apk_path and self.incremental_apk_path:
1429*8975f5c5SAndroid Build Coastguard Worker        self._parser.error('Both incremental and non-incremental apks exist. '
1430*8975f5c5SAndroid Build Coastguard Worker                           'Select using --incremental or --non-incremental')
1431*8975f5c5SAndroid Build Coastguard Worker
1432*8975f5c5SAndroid Build Coastguard Worker
1433*8975f5c5SAndroid Build Coastguard Worker    # Gate apk_helper creation with _CreateApkHelpers since for bundles it takes
1434*8975f5c5SAndroid Build Coastguard Worker    # a while to unpack the apks file from the aab file, so avoid this slowdown
1435*8975f5c5SAndroid Build Coastguard Worker    # for simple commands that don't need apk_helper.
1436*8975f5c5SAndroid Build Coastguard Worker    if self.needs_apk_helper:
1437*8975f5c5SAndroid Build Coastguard Worker      if not self._CreateApkHelpers(args, self.incremental_apk_path,
1438*8975f5c5SAndroid Build Coastguard Worker                                    install_dict):
1439*8975f5c5SAndroid Build Coastguard Worker        self._parser.error('App is not built.')
1440*8975f5c5SAndroid Build Coastguard Worker
1441*8975f5c5SAndroid Build Coastguard Worker    if self.needs_package_name and not args.package_name:
1442*8975f5c5SAndroid Build Coastguard Worker      if self._CreateApkHelpers(args, self.incremental_apk_path, install_dict):
1443*8975f5c5SAndroid Build Coastguard Worker        args.package_name = self.apk_helper.GetPackageName()
1444*8975f5c5SAndroid Build Coastguard Worker      elif self._from_wrapper_script:
1445*8975f5c5SAndroid Build Coastguard Worker        self._parser.error('App is not built.')
1446*8975f5c5SAndroid Build Coastguard Worker      else:
1447*8975f5c5SAndroid Build Coastguard Worker        self._parser.error('One of --package-name or --apk-path is required.')
1448*8975f5c5SAndroid Build Coastguard Worker
1449*8975f5c5SAndroid Build Coastguard Worker    self.devices = []
1450*8975f5c5SAndroid Build Coastguard Worker    if self.need_device_args:
1451*8975f5c5SAndroid Build Coastguard Worker      # Avoid filtering by ABIs with catapult since some x86 or x86_64 emulators
1452*8975f5c5SAndroid Build Coastguard Worker      # can still work with the right target_cpu GN arg and the right targets.
1453*8975f5c5SAndroid Build Coastguard Worker      # Doing this manually allows us to output more informative warnings to
1454*8975f5c5SAndroid Build Coastguard Worker      # help devs towards the right course, see: https://crbug.com/1335139
1455*8975f5c5SAndroid Build Coastguard Worker      available_devices = device_utils.DeviceUtils.HealthyDevices(
1456*8975f5c5SAndroid Build Coastguard Worker          device_arg=args.devices,
1457*8975f5c5SAndroid Build Coastguard Worker          enable_device_files_cache=bool(args.output_directory),
1458*8975f5c5SAndroid Build Coastguard Worker          default_retries=0)
1459*8975f5c5SAndroid Build Coastguard Worker      if not available_devices:
1460*8975f5c5SAndroid Build Coastguard Worker        raise Exception('Cannot find any available devices.')
1461*8975f5c5SAndroid Build Coastguard Worker
1462*8975f5c5SAndroid Build Coastguard Worker      if not self._CreateApkHelpers(args, self.incremental_apk_path,
1463*8975f5c5SAndroid Build Coastguard Worker                                    install_dict):
1464*8975f5c5SAndroid Build Coastguard Worker        self.devices = available_devices
1465*8975f5c5SAndroid Build Coastguard Worker      else:
1466*8975f5c5SAndroid Build Coastguard Worker        fully_supported, not_supported_reasons = self._FindSupportedDevices(
1467*8975f5c5SAndroid Build Coastguard Worker            available_devices)
1468*8975f5c5SAndroid Build Coastguard Worker        reason_string = '\n'.join(
1469*8975f5c5SAndroid Build Coastguard Worker            'The device (serial={}) is not supported because {}'.format(
1470*8975f5c5SAndroid Build Coastguard Worker                serial, reason)
1471*8975f5c5SAndroid Build Coastguard Worker            for serial, reason in not_supported_reasons.items())
1472*8975f5c5SAndroid Build Coastguard Worker        if args.devices:
1473*8975f5c5SAndroid Build Coastguard Worker          if reason_string:
1474*8975f5c5SAndroid Build Coastguard Worker            logging.warning('Devices not supported: %s', reason_string)
1475*8975f5c5SAndroid Build Coastguard Worker          self.devices = available_devices
1476*8975f5c5SAndroid Build Coastguard Worker        elif fully_supported:
1477*8975f5c5SAndroid Build Coastguard Worker          self.devices = fully_supported
1478*8975f5c5SAndroid Build Coastguard Worker        else:
1479*8975f5c5SAndroid Build Coastguard Worker          raise Exception('Cannot find any supported devices for this app.\n\n'
1480*8975f5c5SAndroid Build Coastguard Worker                          f'{reason_string}')
1481*8975f5c5SAndroid Build Coastguard Worker
1482*8975f5c5SAndroid Build Coastguard Worker      # TODO(agrieve): Device cache should not depend on output directory.
1483*8975f5c5SAndroid Build Coastguard Worker      #     Maybe put into /tmp?
1484*8975f5c5SAndroid Build Coastguard Worker      _LoadDeviceCaches(self.devices, args.output_directory)
1485*8975f5c5SAndroid Build Coastguard Worker
1486*8975f5c5SAndroid Build Coastguard Worker      try:
1487*8975f5c5SAndroid Build Coastguard Worker        if len(self.devices) > 1:
1488*8975f5c5SAndroid Build Coastguard Worker          if not self.supports_multiple_devices:
1489*8975f5c5SAndroid Build Coastguard Worker            self._parser.error(device_errors.MultipleDevicesError(self.devices))
1490*8975f5c5SAndroid Build Coastguard Worker          if not args.all and not args.devices:
1491*8975f5c5SAndroid Build Coastguard Worker            self._parser.error(_GenerateMissingAllFlagMessage(self.devices))
1492*8975f5c5SAndroid Build Coastguard Worker        # Save cache now if command will not get a chance to afterwards.
1493*8975f5c5SAndroid Build Coastguard Worker        if self.calls_exec:
1494*8975f5c5SAndroid Build Coastguard Worker          _SaveDeviceCaches(self.devices, args.output_directory)
1495*8975f5c5SAndroid Build Coastguard Worker      except:
1496*8975f5c5SAndroid Build Coastguard Worker        _SaveDeviceCaches(self.devices, args.output_directory)
1497*8975f5c5SAndroid Build Coastguard Worker        raise
1498*8975f5c5SAndroid Build Coastguard Worker
1499*8975f5c5SAndroid Build Coastguard Worker
1500*8975f5c5SAndroid Build Coastguard Workerclass _DevicesCommand(_Command):
1501*8975f5c5SAndroid Build Coastguard Worker  name = 'devices'
1502*8975f5c5SAndroid Build Coastguard Worker  description = 'Describe attached devices.'
1503*8975f5c5SAndroid Build Coastguard Worker  all_devices_by_default = True
1504*8975f5c5SAndroid Build Coastguard Worker
1505*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
1506*8975f5c5SAndroid Build Coastguard Worker    print(_GenerateAvailableDevicesMessage(self.devices))
1507*8975f5c5SAndroid Build Coastguard Worker
1508*8975f5c5SAndroid Build Coastguard Worker
1509*8975f5c5SAndroid Build Coastguard Workerclass _PackageInfoCommand(_Command):
1510*8975f5c5SAndroid Build Coastguard Worker  name = 'package-info'
1511*8975f5c5SAndroid Build Coastguard Worker  description = 'Show various attributes of this app.'
1512*8975f5c5SAndroid Build Coastguard Worker  need_device_args = False
1513*8975f5c5SAndroid Build Coastguard Worker  needs_package_name = True
1514*8975f5c5SAndroid Build Coastguard Worker  needs_apk_helper = True
1515*8975f5c5SAndroid Build Coastguard Worker
1516*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
1517*8975f5c5SAndroid Build Coastguard Worker    # Format all (even ints) as strings, to handle cases where APIs return None
1518*8975f5c5SAndroid Build Coastguard Worker    print('Package name: "%s"' % self.args.package_name)
1519*8975f5c5SAndroid Build Coastguard Worker    print('versionCode: %s' % self.apk_helper.GetVersionCode())
1520*8975f5c5SAndroid Build Coastguard Worker    print('versionName: "%s"' % self.apk_helper.GetVersionName())
1521*8975f5c5SAndroid Build Coastguard Worker    print('minSdkVersion: %s' % self.apk_helper.GetMinSdkVersion())
1522*8975f5c5SAndroid Build Coastguard Worker    print('targetSdkVersion: %s' % self.apk_helper.GetTargetSdkVersion())
1523*8975f5c5SAndroid Build Coastguard Worker    print('Supported ABIs: %r' % self.apk_helper.GetAbis())
1524*8975f5c5SAndroid Build Coastguard Worker
1525*8975f5c5SAndroid Build Coastguard Worker
1526*8975f5c5SAndroid Build Coastguard Workerclass _InstallCommand(_Command):
1527*8975f5c5SAndroid Build Coastguard Worker  name = 'install'
1528*8975f5c5SAndroid Build Coastguard Worker  description = 'Installs the APK or bundle to one or more devices.'
1529*8975f5c5SAndroid Build Coastguard Worker  needs_package_name = True
1530*8975f5c5SAndroid Build Coastguard Worker  needs_apk_helper = True
1531*8975f5c5SAndroid Build Coastguard Worker  supports_incremental = True
1532*8975f5c5SAndroid Build Coastguard Worker  default_modules = []
1533*8975f5c5SAndroid Build Coastguard Worker
1534*8975f5c5SAndroid Build Coastguard Worker  def _RegisterExtraArgs(self, group):
1535*8975f5c5SAndroid Build Coastguard Worker    if self.is_bundle:
1536*8975f5c5SAndroid Build Coastguard Worker      group.add_argument(
1537*8975f5c5SAndroid Build Coastguard Worker          '--locales',
1538*8975f5c5SAndroid Build Coastguard Worker          action='append',
1539*8975f5c5SAndroid Build Coastguard Worker          help=
1540*8975f5c5SAndroid Build Coastguard Worker          'Locale splits to install (english is in base, so always installed).')
1541*8975f5c5SAndroid Build Coastguard Worker      group.add_argument(
1542*8975f5c5SAndroid Build Coastguard Worker          '-m',
1543*8975f5c5SAndroid Build Coastguard Worker          '--module',
1544*8975f5c5SAndroid Build Coastguard Worker          action='append',
1545*8975f5c5SAndroid Build Coastguard Worker          default=self.default_modules,
1546*8975f5c5SAndroid Build Coastguard Worker          help='Module to install. Can be specified multiple times.')
1547*8975f5c5SAndroid Build Coastguard Worker      group.add_argument(
1548*8975f5c5SAndroid Build Coastguard Worker          '-f',
1549*8975f5c5SAndroid Build Coastguard Worker          '--fake',
1550*8975f5c5SAndroid Build Coastguard Worker          action='append',
1551*8975f5c5SAndroid Build Coastguard Worker          default=[],
1552*8975f5c5SAndroid Build Coastguard Worker          help='Fake bundle module install. Can be specified multiple times. '
1553*8975f5c5SAndroid Build Coastguard Worker          'Requires \'-m {0}\' to be given, and \'-f {0}\' is illegal.'.format(
1554*8975f5c5SAndroid Build Coastguard Worker              BASE_MODULE))
1555*8975f5c5SAndroid Build Coastguard Worker      # Add even if |self.default_modules| is empty, for consistency.
1556*8975f5c5SAndroid Build Coastguard Worker      group.add_argument('--no-module',
1557*8975f5c5SAndroid Build Coastguard Worker                         action='append',
1558*8975f5c5SAndroid Build Coastguard Worker                         choices=self.default_modules,
1559*8975f5c5SAndroid Build Coastguard Worker                         default=[],
1560*8975f5c5SAndroid Build Coastguard Worker                         help='Module to exclude from default install.')
1561*8975f5c5SAndroid Build Coastguard Worker
1562*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
1563*8975f5c5SAndroid Build Coastguard Worker    if self.additional_apk_helpers:
1564*8975f5c5SAndroid Build Coastguard Worker      for additional_apk_helper in self.additional_apk_helpers:
1565*8975f5c5SAndroid Build Coastguard Worker        _InstallApk(self.devices, additional_apk_helper, None)
1566*8975f5c5SAndroid Build Coastguard Worker    if self.is_bundle:
1567*8975f5c5SAndroid Build Coastguard Worker      modules = list(
1568*8975f5c5SAndroid Build Coastguard Worker          set(self.args.module) - set(self.args.no_module) -
1569*8975f5c5SAndroid Build Coastguard Worker          set(self.args.fake))
1570*8975f5c5SAndroid Build Coastguard Worker      _InstallBundle(self.devices, self.apk_helper, modules, self.args.fake,
1571*8975f5c5SAndroid Build Coastguard Worker                     self.args.locales)
1572*8975f5c5SAndroid Build Coastguard Worker    else:
1573*8975f5c5SAndroid Build Coastguard Worker      _InstallApk(self.devices, self.apk_helper, self.install_dict)
1574*8975f5c5SAndroid Build Coastguard Worker    if self.args.is_official_build:
1575*8975f5c5SAndroid Build Coastguard Worker      _RunCompileDex(self.devices, self.args.package_name, 'speed')
1576*8975f5c5SAndroid Build Coastguard Worker
1577*8975f5c5SAndroid Build Coastguard Worker
1578*8975f5c5SAndroid Build Coastguard Workerclass _UninstallCommand(_Command):
1579*8975f5c5SAndroid Build Coastguard Worker  name = 'uninstall'
1580*8975f5c5SAndroid Build Coastguard Worker  description = 'Removes the APK or bundle from one or more devices.'
1581*8975f5c5SAndroid Build Coastguard Worker  needs_package_name = True
1582*8975f5c5SAndroid Build Coastguard Worker
1583*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
1584*8975f5c5SAndroid Build Coastguard Worker    _UninstallApk(self.devices, self.install_dict, self.args.package_name)
1585*8975f5c5SAndroid Build Coastguard Worker
1586*8975f5c5SAndroid Build Coastguard Worker
1587*8975f5c5SAndroid Build Coastguard Workerclass _SetWebViewProviderCommand(_Command):
1588*8975f5c5SAndroid Build Coastguard Worker  name = 'set-webview-provider'
1589*8975f5c5SAndroid Build Coastguard Worker  description = ("Sets the device's WebView provider to this APK's "
1590*8975f5c5SAndroid Build Coastguard Worker                 "package name.")
1591*8975f5c5SAndroid Build Coastguard Worker  needs_package_name = True
1592*8975f5c5SAndroid Build Coastguard Worker  needs_apk_helper = True
1593*8975f5c5SAndroid Build Coastguard Worker
1594*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
1595*8975f5c5SAndroid Build Coastguard Worker    if not _IsWebViewProvider(self.apk_helper):
1596*8975f5c5SAndroid Build Coastguard Worker      raise Exception('This package does not have a WebViewLibrary meta-data '
1597*8975f5c5SAndroid Build Coastguard Worker                      'tag. Are you sure it contains a WebView implementation?')
1598*8975f5c5SAndroid Build Coastguard Worker    _SetWebViewProvider(self.devices, self.args.package_name)
1599*8975f5c5SAndroid Build Coastguard Worker
1600*8975f5c5SAndroid Build Coastguard Worker
1601*8975f5c5SAndroid Build Coastguard Workerclass _LaunchCommand(_Command):
1602*8975f5c5SAndroid Build Coastguard Worker  name = 'launch'
1603*8975f5c5SAndroid Build Coastguard Worker  description = ('Sends a launch intent for the APK or bundle after first '
1604*8975f5c5SAndroid Build Coastguard Worker                 'writing the command-line flags file.')
1605*8975f5c5SAndroid Build Coastguard Worker  needs_package_name = True
1606*8975f5c5SAndroid Build Coastguard Worker  accepts_command_line_flags = True
1607*8975f5c5SAndroid Build Coastguard Worker  all_devices_by_default = True
1608*8975f5c5SAndroid Build Coastguard Worker
1609*8975f5c5SAndroid Build Coastguard Worker  def _RegisterExtraArgs(self, group):
1610*8975f5c5SAndroid Build Coastguard Worker    group.add_argument('-w', '--wait-for-java-debugger', action='store_true',
1611*8975f5c5SAndroid Build Coastguard Worker                       help='Pause execution until debugger attaches. Applies '
1612*8975f5c5SAndroid Build Coastguard Worker                            'only to the main process. To have renderers wait, '
1613*8975f5c5SAndroid Build Coastguard Worker                            'use --args="--renderer-wait-for-java-debugger"')
1614*8975f5c5SAndroid Build Coastguard Worker    group.add_argument('--debug-process-name',
1615*8975f5c5SAndroid Build Coastguard Worker                       help='Name of the process to debug. '
1616*8975f5c5SAndroid Build Coastguard Worker                            'E.g. "privileged_process0", or "foo.bar:baz"')
1617*8975f5c5SAndroid Build Coastguard Worker    group.add_argument('--nokill', action='store_true',
1618*8975f5c5SAndroid Build Coastguard Worker                       help='Do not set the debug-app, nor set command-line '
1619*8975f5c5SAndroid Build Coastguard Worker                            'flags. Useful to load a URL without having the '
1620*8975f5c5SAndroid Build Coastguard Worker                             'app restart.')
1621*8975f5c5SAndroid Build Coastguard Worker    group.add_argument('url', nargs='?', help='A URL to launch with.')
1622*8975f5c5SAndroid Build Coastguard Worker
1623*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
1624*8975f5c5SAndroid Build Coastguard Worker    if self.is_test_apk:
1625*8975f5c5SAndroid Build Coastguard Worker      raise Exception('Use the bin/run_* scripts to run test apks.')
1626*8975f5c5SAndroid Build Coastguard Worker    _LaunchUrl(self.devices,
1627*8975f5c5SAndroid Build Coastguard Worker               self.args.package_name,
1628*8975f5c5SAndroid Build Coastguard Worker               argv=self.args.args,
1629*8975f5c5SAndroid Build Coastguard Worker               command_line_flags_file=self.args.command_line_flags_file,
1630*8975f5c5SAndroid Build Coastguard Worker               url=self.args.url,
1631*8975f5c5SAndroid Build Coastguard Worker               wait_for_java_debugger=self.args.wait_for_java_debugger,
1632*8975f5c5SAndroid Build Coastguard Worker               debug_process_name=self.args.debug_process_name,
1633*8975f5c5SAndroid Build Coastguard Worker               nokill=self.args.nokill)
1634*8975f5c5SAndroid Build Coastguard Worker
1635*8975f5c5SAndroid Build Coastguard Worker
1636*8975f5c5SAndroid Build Coastguard Workerclass _StopCommand(_Command):
1637*8975f5c5SAndroid Build Coastguard Worker  name = 'stop'
1638*8975f5c5SAndroid Build Coastguard Worker  description = 'Force-stops the app.'
1639*8975f5c5SAndroid Build Coastguard Worker  needs_package_name = True
1640*8975f5c5SAndroid Build Coastguard Worker  all_devices_by_default = True
1641*8975f5c5SAndroid Build Coastguard Worker
1642*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
1643*8975f5c5SAndroid Build Coastguard Worker    device_utils.DeviceUtils.parallel(self.devices).ForceStop(
1644*8975f5c5SAndroid Build Coastguard Worker        self.args.package_name)
1645*8975f5c5SAndroid Build Coastguard Worker
1646*8975f5c5SAndroid Build Coastguard Worker
1647*8975f5c5SAndroid Build Coastguard Workerclass _ClearDataCommand(_Command):
1648*8975f5c5SAndroid Build Coastguard Worker  name = 'clear-data'
1649*8975f5c5SAndroid Build Coastguard Worker  descriptions = 'Clears all app data.'
1650*8975f5c5SAndroid Build Coastguard Worker  needs_package_name = True
1651*8975f5c5SAndroid Build Coastguard Worker  all_devices_by_default = True
1652*8975f5c5SAndroid Build Coastguard Worker
1653*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
1654*8975f5c5SAndroid Build Coastguard Worker    device_utils.DeviceUtils.parallel(self.devices).ClearApplicationState(
1655*8975f5c5SAndroid Build Coastguard Worker        self.args.package_name)
1656*8975f5c5SAndroid Build Coastguard Worker
1657*8975f5c5SAndroid Build Coastguard Worker
1658*8975f5c5SAndroid Build Coastguard Workerclass _ArgvCommand(_Command):
1659*8975f5c5SAndroid Build Coastguard Worker  name = 'argv'
1660*8975f5c5SAndroid Build Coastguard Worker  description = 'Display and optionally update command-line flags file.'
1661*8975f5c5SAndroid Build Coastguard Worker  needs_package_name = True
1662*8975f5c5SAndroid Build Coastguard Worker  accepts_command_line_flags = True
1663*8975f5c5SAndroid Build Coastguard Worker  all_devices_by_default = True
1664*8975f5c5SAndroid Build Coastguard Worker
1665*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
1666*8975f5c5SAndroid Build Coastguard Worker    command_line_flags_file = self.args.command_line_flags_file
1667*8975f5c5SAndroid Build Coastguard Worker    argv = self.args.args
1668*8975f5c5SAndroid Build Coastguard Worker    devices = self.devices
1669*8975f5c5SAndroid Build Coastguard Worker    parallel_devices = device_utils.DeviceUtils.parallel(devices)
1670*8975f5c5SAndroid Build Coastguard Worker
1671*8975f5c5SAndroid Build Coastguard Worker    if argv is not None:
1672*8975f5c5SAndroid Build Coastguard Worker      parallel_devices.pMap(
1673*8975f5c5SAndroid Build Coastguard Worker          lambda d: _UpdateDeviceFlags(d, command_line_flags_file, argv))
1674*8975f5c5SAndroid Build Coastguard Worker
1675*8975f5c5SAndroid Build Coastguard Worker    outputs = parallel_devices.pMap(
1676*8975f5c5SAndroid Build Coastguard Worker        lambda d: _ReadDeviceFlags(d, command_line_flags_file) or '').pGet(None)
1677*8975f5c5SAndroid Build Coastguard Worker
1678*8975f5c5SAndroid Build Coastguard Worker    print(f'Showing flags via /data/local/tmp/{command_line_flags_file}:')
1679*8975f5c5SAndroid Build Coastguard Worker    for flags in _PrintPerDeviceOutput(devices, outputs, single_line=True):
1680*8975f5c5SAndroid Build Coastguard Worker      print(flags or 'No flags set.')
1681*8975f5c5SAndroid Build Coastguard Worker
1682*8975f5c5SAndroid Build Coastguard Worker
1683*8975f5c5SAndroid Build Coastguard Workerclass _GdbCommand(_Command):
1684*8975f5c5SAndroid Build Coastguard Worker  name = 'gdb'
1685*8975f5c5SAndroid Build Coastguard Worker  description = 'Runs //build/android/adb_gdb with apk-specific args.'
1686*8975f5c5SAndroid Build Coastguard Worker  long_description = description + """
1687*8975f5c5SAndroid Build Coastguard Worker
1688*8975f5c5SAndroid Build Coastguard WorkerTo attach to a process other than the APK's main process, use --pid=1234.
1689*8975f5c5SAndroid Build Coastguard WorkerTo list all PIDs, use the "ps" command.
1690*8975f5c5SAndroid Build Coastguard Worker
1691*8975f5c5SAndroid Build Coastguard WorkerIf no apk process is currently running, sends a launch intent.
1692*8975f5c5SAndroid Build Coastguard Worker"""
1693*8975f5c5SAndroid Build Coastguard Worker  needs_package_name = True
1694*8975f5c5SAndroid Build Coastguard Worker  needs_output_directory = True
1695*8975f5c5SAndroid Build Coastguard Worker  calls_exec = True
1696*8975f5c5SAndroid Build Coastguard Worker  supports_multiple_devices = False
1697*8975f5c5SAndroid Build Coastguard Worker
1698*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
1699*8975f5c5SAndroid Build Coastguard Worker    _RunGdb(self.devices[0], self.args.package_name,
1700*8975f5c5SAndroid Build Coastguard Worker            self.args.debug_process_name, self.args.pid,
1701*8975f5c5SAndroid Build Coastguard Worker            self.args.output_directory, self.args.target_cpu, self.args.port,
1702*8975f5c5SAndroid Build Coastguard Worker            self.args.ide, bool(self.args.verbose_count))
1703*8975f5c5SAndroid Build Coastguard Worker
1704*8975f5c5SAndroid Build Coastguard Worker  def _RegisterExtraArgs(self, group):
1705*8975f5c5SAndroid Build Coastguard Worker    pid_group = group.add_mutually_exclusive_group()
1706*8975f5c5SAndroid Build Coastguard Worker    pid_group.add_argument('--debug-process-name',
1707*8975f5c5SAndroid Build Coastguard Worker                           help='Name of the process to attach to. '
1708*8975f5c5SAndroid Build Coastguard Worker                                'E.g. "privileged_process0", or "foo.bar:baz"')
1709*8975f5c5SAndroid Build Coastguard Worker    pid_group.add_argument('--pid',
1710*8975f5c5SAndroid Build Coastguard Worker                           help='The process ID to attach to. Defaults to '
1711*8975f5c5SAndroid Build Coastguard Worker                                'the main process for the package.')
1712*8975f5c5SAndroid Build Coastguard Worker    group.add_argument('--ide', action='store_true',
1713*8975f5c5SAndroid Build Coastguard Worker                       help='Rather than enter a gdb prompt, set up the '
1714*8975f5c5SAndroid Build Coastguard Worker                            'gdb connection and wait for an IDE to '
1715*8975f5c5SAndroid Build Coastguard Worker                            'connect.')
1716*8975f5c5SAndroid Build Coastguard Worker    # Same default port that ndk-gdb.py uses.
1717*8975f5c5SAndroid Build Coastguard Worker    group.add_argument('--port', type=int, default=5039,
1718*8975f5c5SAndroid Build Coastguard Worker                       help='Use the given port for the GDB connection')
1719*8975f5c5SAndroid Build Coastguard Worker
1720*8975f5c5SAndroid Build Coastguard Worker
1721*8975f5c5SAndroid Build Coastguard Workerclass _LldbCommand(_Command):
1722*8975f5c5SAndroid Build Coastguard Worker  name = 'lldb'
1723*8975f5c5SAndroid Build Coastguard Worker  description = 'Runs //build/android/connect_lldb.sh with apk-specific args.'
1724*8975f5c5SAndroid Build Coastguard Worker  long_description = description + """
1725*8975f5c5SAndroid Build Coastguard Worker
1726*8975f5c5SAndroid Build Coastguard WorkerTo attach to a process other than the APK's main process, use --pid=1234.
1727*8975f5c5SAndroid Build Coastguard WorkerTo list all PIDs, use the "ps" command.
1728*8975f5c5SAndroid Build Coastguard Worker
1729*8975f5c5SAndroid Build Coastguard WorkerIf no apk process is currently running, sends a launch intent.
1730*8975f5c5SAndroid Build Coastguard Worker"""
1731*8975f5c5SAndroid Build Coastguard Worker  needs_package_name = True
1732*8975f5c5SAndroid Build Coastguard Worker  needs_output_directory = True
1733*8975f5c5SAndroid Build Coastguard Worker  calls_exec = True
1734*8975f5c5SAndroid Build Coastguard Worker  supports_multiple_devices = False
1735*8975f5c5SAndroid Build Coastguard Worker
1736*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
1737*8975f5c5SAndroid Build Coastguard Worker    _RunLldb(device=self.devices[0],
1738*8975f5c5SAndroid Build Coastguard Worker             package_name=self.args.package_name,
1739*8975f5c5SAndroid Build Coastguard Worker             debug_process_name=self.args.debug_process_name,
1740*8975f5c5SAndroid Build Coastguard Worker             pid=self.args.pid,
1741*8975f5c5SAndroid Build Coastguard Worker             output_directory=self.args.output_directory,
1742*8975f5c5SAndroid Build Coastguard Worker             port=self.args.port,
1743*8975f5c5SAndroid Build Coastguard Worker             target_cpu=self.args.target_cpu,
1744*8975f5c5SAndroid Build Coastguard Worker             ndk_dir=self.args.ndk_dir,
1745*8975f5c5SAndroid Build Coastguard Worker             lldb_server=self.args.lldb_server,
1746*8975f5c5SAndroid Build Coastguard Worker             lldb=self.args.lldb,
1747*8975f5c5SAndroid Build Coastguard Worker             verbose=bool(self.args.verbose_count))
1748*8975f5c5SAndroid Build Coastguard Worker
1749*8975f5c5SAndroid Build Coastguard Worker  def _RegisterExtraArgs(self, group):
1750*8975f5c5SAndroid Build Coastguard Worker    pid_group = group.add_mutually_exclusive_group()
1751*8975f5c5SAndroid Build Coastguard Worker    pid_group.add_argument('--debug-process-name',
1752*8975f5c5SAndroid Build Coastguard Worker                           help='Name of the process to attach to. '
1753*8975f5c5SAndroid Build Coastguard Worker                           'E.g. "privileged_process0", or "foo.bar:baz"')
1754*8975f5c5SAndroid Build Coastguard Worker    pid_group.add_argument('--pid',
1755*8975f5c5SAndroid Build Coastguard Worker                           help='The process ID to attach to. Defaults to '
1756*8975f5c5SAndroid Build Coastguard Worker                           'the main process for the package.')
1757*8975f5c5SAndroid Build Coastguard Worker    group.add_argument('--ndk-dir',
1758*8975f5c5SAndroid Build Coastguard Worker                       help='Select alternative NDK root directory.')
1759*8975f5c5SAndroid Build Coastguard Worker    group.add_argument('--lldb-server',
1760*8975f5c5SAndroid Build Coastguard Worker                       help='Select alternative on-device lldb-server.')
1761*8975f5c5SAndroid Build Coastguard Worker    group.add_argument('--lldb', help='Select alternative client lldb.sh.')
1762*8975f5c5SAndroid Build Coastguard Worker    # Same default port that ndk-gdb.py uses.
1763*8975f5c5SAndroid Build Coastguard Worker    group.add_argument('--port',
1764*8975f5c5SAndroid Build Coastguard Worker                       type=int,
1765*8975f5c5SAndroid Build Coastguard Worker                       default=5039,
1766*8975f5c5SAndroid Build Coastguard Worker                       help='Use the given port for the LLDB connection')
1767*8975f5c5SAndroid Build Coastguard Worker
1768*8975f5c5SAndroid Build Coastguard Worker
1769*8975f5c5SAndroid Build Coastguard Workerclass _LogcatCommand(_Command):
1770*8975f5c5SAndroid Build Coastguard Worker  name = 'logcat'
1771*8975f5c5SAndroid Build Coastguard Worker  description = 'Runs "adb logcat" with filters relevant the current APK.'
1772*8975f5c5SAndroid Build Coastguard Worker  long_description = description + """
1773*8975f5c5SAndroid Build Coastguard Worker
1774*8975f5c5SAndroid Build Coastguard Worker"Relevant filters" means:
1775*8975f5c5SAndroid Build Coastguard Worker  * Log messages from processes belonging to the apk,
1776*8975f5c5SAndroid Build Coastguard Worker  * Plus log messages from log tags: ActivityManager|DEBUG,
1777*8975f5c5SAndroid Build Coastguard Worker  * Plus fatal logs from any process,
1778*8975f5c5SAndroid Build Coastguard Worker  * Minus spamy dalvikvm logs (for pre-L devices).
1779*8975f5c5SAndroid Build Coastguard Worker
1780*8975f5c5SAndroid Build Coastguard WorkerColors:
1781*8975f5c5SAndroid Build Coastguard Worker  * Primary process is white
1782*8975f5c5SAndroid Build Coastguard Worker  * Other processes (gpu, renderer) are yellow
1783*8975f5c5SAndroid Build Coastguard Worker  * Non-apk processes are grey
1784*8975f5c5SAndroid Build Coastguard Worker  * UI thread has a bolded Thread-ID
1785*8975f5c5SAndroid Build Coastguard Worker
1786*8975f5c5SAndroid Build Coastguard WorkerJava stack traces are detected and deobfuscated (for release builds).
1787*8975f5c5SAndroid Build Coastguard Worker
1788*8975f5c5SAndroid Build Coastguard WorkerTo disable filtering, (but keep coloring), use --verbose.
1789*8975f5c5SAndroid Build Coastguard Worker"""
1790*8975f5c5SAndroid Build Coastguard Worker  needs_package_name = True
1791*8975f5c5SAndroid Build Coastguard Worker  supports_multiple_devices = False
1792*8975f5c5SAndroid Build Coastguard Worker
1793*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
1794*8975f5c5SAndroid Build Coastguard Worker    deobfuscate = None
1795*8975f5c5SAndroid Build Coastguard Worker    if self.args.proguard_mapping_path and not self.args.no_deobfuscate:
1796*8975f5c5SAndroid Build Coastguard Worker      deobfuscate = deobfuscator.Deobfuscator(self.args.proguard_mapping_path)
1797*8975f5c5SAndroid Build Coastguard Worker    apk_path = self.args.apk_path or self.incremental_apk_path
1798*8975f5c5SAndroid Build Coastguard Worker    if apk_path or self.bundle_generation_info:
1799*8975f5c5SAndroid Build Coastguard Worker      stack_script_context = _StackScriptContext(self.args.output_directory,
1800*8975f5c5SAndroid Build Coastguard Worker                                                 apk_path,
1801*8975f5c5SAndroid Build Coastguard Worker                                                 self.bundle_generation_info,
1802*8975f5c5SAndroid Build Coastguard Worker                                                 quiet=True)
1803*8975f5c5SAndroid Build Coastguard Worker    else:
1804*8975f5c5SAndroid Build Coastguard Worker      stack_script_context = None
1805*8975f5c5SAndroid Build Coastguard Worker
1806*8975f5c5SAndroid Build Coastguard Worker    extra_package_names = []
1807*8975f5c5SAndroid Build Coastguard Worker    if self.is_test_apk and self.additional_apk_helpers:
1808*8975f5c5SAndroid Build Coastguard Worker      for additional_apk_helper in self.additional_apk_helpers:
1809*8975f5c5SAndroid Build Coastguard Worker        extra_package_names.append(additional_apk_helper.GetPackageName())
1810*8975f5c5SAndroid Build Coastguard Worker
1811*8975f5c5SAndroid Build Coastguard Worker    try:
1812*8975f5c5SAndroid Build Coastguard Worker      _RunLogcat(self.devices[0],
1813*8975f5c5SAndroid Build Coastguard Worker                 self.args.package_name,
1814*8975f5c5SAndroid Build Coastguard Worker                 stack_script_context,
1815*8975f5c5SAndroid Build Coastguard Worker                 deobfuscate,
1816*8975f5c5SAndroid Build Coastguard Worker                 bool(self.args.verbose_count),
1817*8975f5c5SAndroid Build Coastguard Worker                 self.args.exit_on_match,
1818*8975f5c5SAndroid Build Coastguard Worker                 extra_package_names=extra_package_names)
1819*8975f5c5SAndroid Build Coastguard Worker    except KeyboardInterrupt:
1820*8975f5c5SAndroid Build Coastguard Worker      pass  # Don't show stack trace upon Ctrl-C
1821*8975f5c5SAndroid Build Coastguard Worker    finally:
1822*8975f5c5SAndroid Build Coastguard Worker      if stack_script_context:
1823*8975f5c5SAndroid Build Coastguard Worker        stack_script_context.Close()
1824*8975f5c5SAndroid Build Coastguard Worker      if deobfuscate:
1825*8975f5c5SAndroid Build Coastguard Worker        deobfuscate.Close()
1826*8975f5c5SAndroid Build Coastguard Worker
1827*8975f5c5SAndroid Build Coastguard Worker  def _RegisterExtraArgs(self, group):
1828*8975f5c5SAndroid Build Coastguard Worker    if self._from_wrapper_script:
1829*8975f5c5SAndroid Build Coastguard Worker      group.add_argument('--no-deobfuscate', action='store_true',
1830*8975f5c5SAndroid Build Coastguard Worker          help='Disables ProGuard deobfuscation of logcat.')
1831*8975f5c5SAndroid Build Coastguard Worker    else:
1832*8975f5c5SAndroid Build Coastguard Worker      group.set_defaults(no_deobfuscate=False)
1833*8975f5c5SAndroid Build Coastguard Worker      group.add_argument('--proguard-mapping-path',
1834*8975f5c5SAndroid Build Coastguard Worker          help='Path to ProGuard map (enables deobfuscation)')
1835*8975f5c5SAndroid Build Coastguard Worker    group.add_argument('--exit-on-match',
1836*8975f5c5SAndroid Build Coastguard Worker                       help='Exits logcat when a message matches this regex.')
1837*8975f5c5SAndroid Build Coastguard Worker
1838*8975f5c5SAndroid Build Coastguard Worker
1839*8975f5c5SAndroid Build Coastguard Workerclass _PsCommand(_Command):
1840*8975f5c5SAndroid Build Coastguard Worker  name = 'ps'
1841*8975f5c5SAndroid Build Coastguard Worker  description = 'Show PIDs of any APK processes currently running.'
1842*8975f5c5SAndroid Build Coastguard Worker  needs_package_name = True
1843*8975f5c5SAndroid Build Coastguard Worker  all_devices_by_default = True
1844*8975f5c5SAndroid Build Coastguard Worker
1845*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
1846*8975f5c5SAndroid Build Coastguard Worker    _RunPs(self.devices, self.args.package_name)
1847*8975f5c5SAndroid Build Coastguard Worker
1848*8975f5c5SAndroid Build Coastguard Worker
1849*8975f5c5SAndroid Build Coastguard Workerclass _DiskUsageCommand(_Command):
1850*8975f5c5SAndroid Build Coastguard Worker  name = 'disk-usage'
1851*8975f5c5SAndroid Build Coastguard Worker  description = 'Show how much device storage is being consumed by the app.'
1852*8975f5c5SAndroid Build Coastguard Worker  needs_package_name = True
1853*8975f5c5SAndroid Build Coastguard Worker  all_devices_by_default = True
1854*8975f5c5SAndroid Build Coastguard Worker
1855*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
1856*8975f5c5SAndroid Build Coastguard Worker    _RunDiskUsage(self.devices, self.args.package_name)
1857*8975f5c5SAndroid Build Coastguard Worker
1858*8975f5c5SAndroid Build Coastguard Worker
1859*8975f5c5SAndroid Build Coastguard Workerclass _MemUsageCommand(_Command):
1860*8975f5c5SAndroid Build Coastguard Worker  name = 'mem-usage'
1861*8975f5c5SAndroid Build Coastguard Worker  description = 'Show memory usage of currently running APK processes.'
1862*8975f5c5SAndroid Build Coastguard Worker  needs_package_name = True
1863*8975f5c5SAndroid Build Coastguard Worker  all_devices_by_default = True
1864*8975f5c5SAndroid Build Coastguard Worker
1865*8975f5c5SAndroid Build Coastguard Worker  def _RegisterExtraArgs(self, group):
1866*8975f5c5SAndroid Build Coastguard Worker    group.add_argument('--query-app', action='store_true',
1867*8975f5c5SAndroid Build Coastguard Worker        help='Do not add --local to "dumpsys meminfo". This will output '
1868*8975f5c5SAndroid Build Coastguard Worker             'additional metrics (e.g. Context count), but also cause memory '
1869*8975f5c5SAndroid Build Coastguard Worker             'to be used in order to gather the metrics.')
1870*8975f5c5SAndroid Build Coastguard Worker
1871*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
1872*8975f5c5SAndroid Build Coastguard Worker    _RunMemUsage(self.devices, self.args.package_name,
1873*8975f5c5SAndroid Build Coastguard Worker                 query_app=self.args.query_app)
1874*8975f5c5SAndroid Build Coastguard Worker
1875*8975f5c5SAndroid Build Coastguard Worker
1876*8975f5c5SAndroid Build Coastguard Workerclass _ShellCommand(_Command):
1877*8975f5c5SAndroid Build Coastguard Worker  name = 'shell'
1878*8975f5c5SAndroid Build Coastguard Worker  description = ('Same as "adb shell <command>", but runs as the apk\'s uid '
1879*8975f5c5SAndroid Build Coastguard Worker                 '(via run-as). Useful for inspecting the app\'s data '
1880*8975f5c5SAndroid Build Coastguard Worker                 'directory.')
1881*8975f5c5SAndroid Build Coastguard Worker  needs_package_name = True
1882*8975f5c5SAndroid Build Coastguard Worker
1883*8975f5c5SAndroid Build Coastguard Worker  @property
1884*8975f5c5SAndroid Build Coastguard Worker  def calls_exec(self):
1885*8975f5c5SAndroid Build Coastguard Worker    return not self.args.cmd
1886*8975f5c5SAndroid Build Coastguard Worker
1887*8975f5c5SAndroid Build Coastguard Worker  @property
1888*8975f5c5SAndroid Build Coastguard Worker  def supports_multiple_devices(self):
1889*8975f5c5SAndroid Build Coastguard Worker    return not self.args.cmd
1890*8975f5c5SAndroid Build Coastguard Worker
1891*8975f5c5SAndroid Build Coastguard Worker  def _RegisterExtraArgs(self, group):
1892*8975f5c5SAndroid Build Coastguard Worker    group.add_argument(
1893*8975f5c5SAndroid Build Coastguard Worker        'cmd', nargs=argparse.REMAINDER, help='Command to run.')
1894*8975f5c5SAndroid Build Coastguard Worker
1895*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
1896*8975f5c5SAndroid Build Coastguard Worker    _RunShell(self.devices, self.args.package_name, self.args.cmd)
1897*8975f5c5SAndroid Build Coastguard Worker
1898*8975f5c5SAndroid Build Coastguard Worker
1899*8975f5c5SAndroid Build Coastguard Workerclass _CompileDexCommand(_Command):
1900*8975f5c5SAndroid Build Coastguard Worker  name = 'compile-dex'
1901*8975f5c5SAndroid Build Coastguard Worker  description = ('Applicable only for Android N+. Forces .odex files to be '
1902*8975f5c5SAndroid Build Coastguard Worker                 'compiled with the given compilation filter. To see existing '
1903*8975f5c5SAndroid Build Coastguard Worker                 'filter, use "disk-usage" command.')
1904*8975f5c5SAndroid Build Coastguard Worker  needs_package_name = True
1905*8975f5c5SAndroid Build Coastguard Worker  all_devices_by_default = True
1906*8975f5c5SAndroid Build Coastguard Worker
1907*8975f5c5SAndroid Build Coastguard Worker  def _RegisterExtraArgs(self, group):
1908*8975f5c5SAndroid Build Coastguard Worker    group.add_argument(
1909*8975f5c5SAndroid Build Coastguard Worker        'compilation_filter',
1910*8975f5c5SAndroid Build Coastguard Worker        choices=['verify', 'quicken', 'space-profile', 'space',
1911*8975f5c5SAndroid Build Coastguard Worker                 'speed-profile', 'speed'],
1912*8975f5c5SAndroid Build Coastguard Worker        help='For WebView/Monochrome, use "speed". For other apks, use '
1913*8975f5c5SAndroid Build Coastguard Worker             '"speed-profile".')
1914*8975f5c5SAndroid Build Coastguard Worker
1915*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
1916*8975f5c5SAndroid Build Coastguard Worker    outputs = _RunCompileDex(self.devices, self.args.package_name,
1917*8975f5c5SAndroid Build Coastguard Worker                             self.args.compilation_filter)
1918*8975f5c5SAndroid Build Coastguard Worker    for output in _PrintPerDeviceOutput(self.devices, outputs):
1919*8975f5c5SAndroid Build Coastguard Worker      for line in output:
1920*8975f5c5SAndroid Build Coastguard Worker        print(line)
1921*8975f5c5SAndroid Build Coastguard Worker
1922*8975f5c5SAndroid Build Coastguard Worker
1923*8975f5c5SAndroid Build Coastguard Workerclass _PrintCertsCommand(_Command):
1924*8975f5c5SAndroid Build Coastguard Worker  name = 'print-certs'
1925*8975f5c5SAndroid Build Coastguard Worker  description = 'Print info about certificates used to sign this APK.'
1926*8975f5c5SAndroid Build Coastguard Worker  need_device_args = False
1927*8975f5c5SAndroid Build Coastguard Worker  needs_apk_helper = True
1928*8975f5c5SAndroid Build Coastguard Worker
1929*8975f5c5SAndroid Build Coastguard Worker  def _RegisterExtraArgs(self, group):
1930*8975f5c5SAndroid Build Coastguard Worker    group.add_argument(
1931*8975f5c5SAndroid Build Coastguard Worker        '--full-cert',
1932*8975f5c5SAndroid Build Coastguard Worker        action='store_true',
1933*8975f5c5SAndroid Build Coastguard Worker        help=("Print the certificate's full signature, Base64-encoded. "
1934*8975f5c5SAndroid Build Coastguard Worker              "Useful when configuring an Android image's "
1935*8975f5c5SAndroid Build Coastguard Worker              "config_webview_packages.xml."))
1936*8975f5c5SAndroid Build Coastguard Worker
1937*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
1938*8975f5c5SAndroid Build Coastguard Worker    keytool = os.path.join(_JAVA_HOME, 'bin', 'keytool')
1939*8975f5c5SAndroid Build Coastguard Worker    pem_certificate_pattern = re.compile(
1940*8975f5c5SAndroid Build Coastguard Worker        r'-+BEGIN CERTIFICATE-+([\r\n0-9A-Za-z+/=]+)-+END CERTIFICATE-+[\r\n]*')
1941*8975f5c5SAndroid Build Coastguard Worker    if self.is_bundle:
1942*8975f5c5SAndroid Build Coastguard Worker      # Bundles are not signed until converted to .apks. The wrapper scripts
1943*8975f5c5SAndroid Build Coastguard Worker      # record which key will be used to sign though.
1944*8975f5c5SAndroid Build Coastguard Worker      with tempfile.NamedTemporaryFile() as f:
1945*8975f5c5SAndroid Build Coastguard Worker        logging.warning('Bundles are not signed until turned into .apk files.')
1946*8975f5c5SAndroid Build Coastguard Worker        logging.warning('Showing signing info based on associated keystore.')
1947*8975f5c5SAndroid Build Coastguard Worker        cmd = [
1948*8975f5c5SAndroid Build Coastguard Worker            keytool, '-exportcert', '-keystore',
1949*8975f5c5SAndroid Build Coastguard Worker            self.bundle_generation_info.keystore_path, '-storepass',
1950*8975f5c5SAndroid Build Coastguard Worker            self.bundle_generation_info.keystore_password, '-alias',
1951*8975f5c5SAndroid Build Coastguard Worker            self.bundle_generation_info.keystore_alias, '-file', f.name
1952*8975f5c5SAndroid Build Coastguard Worker        ]
1953*8975f5c5SAndroid Build Coastguard Worker        logging.warning('Running: %s', shlex.join(cmd))
1954*8975f5c5SAndroid Build Coastguard Worker        subprocess.check_output(cmd, stderr=subprocess.STDOUT)
1955*8975f5c5SAndroid Build Coastguard Worker        cmd = [keytool, '-printcert', '-file', f.name]
1956*8975f5c5SAndroid Build Coastguard Worker        logging.warning('Running: %s', shlex.join(cmd))
1957*8975f5c5SAndroid Build Coastguard Worker        subprocess.check_call(cmd)
1958*8975f5c5SAndroid Build Coastguard Worker        if self.args.full_cert:
1959*8975f5c5SAndroid Build Coastguard Worker          # Redirect stderr to hide a keytool warning about using non-standard
1960*8975f5c5SAndroid Build Coastguard Worker          # keystore format.
1961*8975f5c5SAndroid Build Coastguard Worker          cmd += ['-rfc']
1962*8975f5c5SAndroid Build Coastguard Worker          logging.warning('Running: %s', shlex.join(cmd))
1963*8975f5c5SAndroid Build Coastguard Worker          pem_encoded_certificate = subprocess.check_output(
1964*8975f5c5SAndroid Build Coastguard Worker              cmd, stderr=subprocess.STDOUT).decode()
1965*8975f5c5SAndroid Build Coastguard Worker    else:
1966*8975f5c5SAndroid Build Coastguard Worker
1967*8975f5c5SAndroid Build Coastguard Worker      def run_apksigner(min_sdk_version):
1968*8975f5c5SAndroid Build Coastguard Worker        cmd = [
1969*8975f5c5SAndroid Build Coastguard Worker            build_tools.GetPath('apksigner'), 'verify', '--min-sdk-version',
1970*8975f5c5SAndroid Build Coastguard Worker            str(min_sdk_version), '--print-certs-pem', '--verbose',
1971*8975f5c5SAndroid Build Coastguard Worker            self.apk_helper.path
1972*8975f5c5SAndroid Build Coastguard Worker        ]
1973*8975f5c5SAndroid Build Coastguard Worker        logging.warning('Running: %s', shlex.join(cmd))
1974*8975f5c5SAndroid Build Coastguard Worker        env = os.environ.copy()
1975*8975f5c5SAndroid Build Coastguard Worker        env['PATH'] = os.path.pathsep.join(
1976*8975f5c5SAndroid Build Coastguard Worker            [os.path.join(_JAVA_HOME, 'bin'),
1977*8975f5c5SAndroid Build Coastguard Worker             env.get('PATH')])
1978*8975f5c5SAndroid Build Coastguard Worker        # Redirect stderr to hide verification failures (see explanation below).
1979*8975f5c5SAndroid Build Coastguard Worker        return subprocess.check_output(cmd,
1980*8975f5c5SAndroid Build Coastguard Worker                                       env=env,
1981*8975f5c5SAndroid Build Coastguard Worker                                       universal_newlines=True,
1982*8975f5c5SAndroid Build Coastguard Worker                                       stderr=subprocess.STDOUT)
1983*8975f5c5SAndroid Build Coastguard Worker
1984*8975f5c5SAndroid Build Coastguard Worker      # apksigner's default behavior is nonintuitive: it will print "Verified
1985*8975f5c5SAndroid Build Coastguard Worker      # using <scheme number>...: false" for any scheme which is obsolete for
1986*8975f5c5SAndroid Build Coastguard Worker      # the APK's minSdkVersion even if it actually was signed with that scheme
1987*8975f5c5SAndroid Build Coastguard Worker      # (ex. it prints "Verified using v1 scheme: false" for Monochrome because
1988*8975f5c5SAndroid Build Coastguard Worker      # v1 was obsolete by N). To workaround this, we force apksigner to use the
1989*8975f5c5SAndroid Build Coastguard Worker      # lowest possible minSdkVersion. We need to fallback to higher
1990*8975f5c5SAndroid Build Coastguard Worker      # minSdkVersions in case the APK fails to verify for that minSdkVersion
1991*8975f5c5SAndroid Build Coastguard Worker      # (which means the APK is genuinely not signed with that scheme). These
1992*8975f5c5SAndroid Build Coastguard Worker      # SDK values are the highest SDK version before the next scheme is
1993*8975f5c5SAndroid Build Coastguard Worker      # available:
1994*8975f5c5SAndroid Build Coastguard Worker      versions = [
1995*8975f5c5SAndroid Build Coastguard Worker          version_codes.MARSHMALLOW,  # before v2 launched in N
1996*8975f5c5SAndroid Build Coastguard Worker          version_codes.OREO_MR1,  # before v3 launched in P
1997*8975f5c5SAndroid Build Coastguard Worker          version_codes.Q,  # before v4 launched in R
1998*8975f5c5SAndroid Build Coastguard Worker          version_codes.R,
1999*8975f5c5SAndroid Build Coastguard Worker      ]
2000*8975f5c5SAndroid Build Coastguard Worker      stdout = None
2001*8975f5c5SAndroid Build Coastguard Worker      for min_sdk_version in versions:
2002*8975f5c5SAndroid Build Coastguard Worker        try:
2003*8975f5c5SAndroid Build Coastguard Worker          stdout = run_apksigner(min_sdk_version)
2004*8975f5c5SAndroid Build Coastguard Worker          break
2005*8975f5c5SAndroid Build Coastguard Worker        except subprocess.CalledProcessError:
2006*8975f5c5SAndroid Build Coastguard Worker          # Doesn't verify with this min-sdk-version, so try again with a higher
2007*8975f5c5SAndroid Build Coastguard Worker          # one
2008*8975f5c5SAndroid Build Coastguard Worker          continue
2009*8975f5c5SAndroid Build Coastguard Worker      if not stdout:
2010*8975f5c5SAndroid Build Coastguard Worker        raise RuntimeError('apksigner was not able to verify APK')
2011*8975f5c5SAndroid Build Coastguard Worker
2012*8975f5c5SAndroid Build Coastguard Worker      # Separate what the '--print-certs' flag would output vs. the additional
2013*8975f5c5SAndroid Build Coastguard Worker      # signature output included by '--print-certs-pem'. The additional PEM
2014*8975f5c5SAndroid Build Coastguard Worker      # output is only printed when self.args.full_cert is specified.
2015*8975f5c5SAndroid Build Coastguard Worker      verification_hash_info = pem_certificate_pattern.sub('', stdout)
2016*8975f5c5SAndroid Build Coastguard Worker      print(verification_hash_info)
2017*8975f5c5SAndroid Build Coastguard Worker      if self.args.full_cert:
2018*8975f5c5SAndroid Build Coastguard Worker        m = pem_certificate_pattern.search(stdout)
2019*8975f5c5SAndroid Build Coastguard Worker        if not m:
2020*8975f5c5SAndroid Build Coastguard Worker          raise Exception('apksigner did not print a certificate')
2021*8975f5c5SAndroid Build Coastguard Worker        pem_encoded_certificate = m.group(0)
2022*8975f5c5SAndroid Build Coastguard Worker
2023*8975f5c5SAndroid Build Coastguard Worker
2024*8975f5c5SAndroid Build Coastguard Worker    if self.args.full_cert:
2025*8975f5c5SAndroid Build Coastguard Worker      m = pem_certificate_pattern.search(pem_encoded_certificate)
2026*8975f5c5SAndroid Build Coastguard Worker      if not m:
2027*8975f5c5SAndroid Build Coastguard Worker        raise Exception(
2028*8975f5c5SAndroid Build Coastguard Worker            'Unable to parse certificate:\n{}'.format(pem_encoded_certificate))
2029*8975f5c5SAndroid Build Coastguard Worker      signature = re.sub(r'[\r\n]+', '', m.group(1))
2030*8975f5c5SAndroid Build Coastguard Worker      print()
2031*8975f5c5SAndroid Build Coastguard Worker      print('Full Signature:')
2032*8975f5c5SAndroid Build Coastguard Worker      print(signature)
2033*8975f5c5SAndroid Build Coastguard Worker
2034*8975f5c5SAndroid Build Coastguard Worker
2035*8975f5c5SAndroid Build Coastguard Workerclass _ProfileCommand(_Command):
2036*8975f5c5SAndroid Build Coastguard Worker  name = 'profile'
2037*8975f5c5SAndroid Build Coastguard Worker  description = ('Run the simpleperf sampling CPU profiler on the currently-'
2038*8975f5c5SAndroid Build Coastguard Worker                 'running APK. If --args is used, the extra arguments will be '
2039*8975f5c5SAndroid Build Coastguard Worker                 'passed on to simpleperf; otherwise, the following default '
2040*8975f5c5SAndroid Build Coastguard Worker                 'arguments are used: -g -f 1000 -o /data/local/tmp/perf.data')
2041*8975f5c5SAndroid Build Coastguard Worker  needs_package_name = True
2042*8975f5c5SAndroid Build Coastguard Worker  needs_output_directory = True
2043*8975f5c5SAndroid Build Coastguard Worker  supports_multiple_devices = False
2044*8975f5c5SAndroid Build Coastguard Worker  accepts_args = True
2045*8975f5c5SAndroid Build Coastguard Worker
2046*8975f5c5SAndroid Build Coastguard Worker  def _RegisterExtraArgs(self, group):
2047*8975f5c5SAndroid Build Coastguard Worker    group.add_argument(
2048*8975f5c5SAndroid Build Coastguard Worker        '--profile-process', default='browser',
2049*8975f5c5SAndroid Build Coastguard Worker        help=('Which process to profile. This may be a process name or pid '
2050*8975f5c5SAndroid Build Coastguard Worker              'such as you would get from running `%s ps`; or '
2051*8975f5c5SAndroid Build Coastguard Worker              'it can be one of (browser, renderer, gpu).' % sys.argv[0]))
2052*8975f5c5SAndroid Build Coastguard Worker    group.add_argument(
2053*8975f5c5SAndroid Build Coastguard Worker        '--profile-thread', default=None,
2054*8975f5c5SAndroid Build Coastguard Worker        help=('(Optional) Profile only a single thread. This may be either a '
2055*8975f5c5SAndroid Build Coastguard Worker              'thread ID such as you would get by running `adb shell ps -t` '
2056*8975f5c5SAndroid Build Coastguard Worker              '(pre-Oreo) or `adb shell ps -e -T` (Oreo and later); or it may '
2057*8975f5c5SAndroid Build Coastguard Worker              'be one of (io, compositor, main, render), in which case '
2058*8975f5c5SAndroid Build Coastguard Worker              '--profile-process is also required. (Note that "render" thread '
2059*8975f5c5SAndroid Build Coastguard Worker              'refers to a thread in the browser process that manages a '
2060*8975f5c5SAndroid Build Coastguard Worker              'renderer; to profile the main thread of the renderer process, '
2061*8975f5c5SAndroid Build Coastguard Worker              'use --profile-thread=main).'))
2062*8975f5c5SAndroid Build Coastguard Worker    group.add_argument('--profile-output', default='profile.pb',
2063*8975f5c5SAndroid Build Coastguard Worker                       help='Output file for profiling data')
2064*8975f5c5SAndroid Build Coastguard Worker    group.add_argument('--profile-events', default='cpu-cycles',
2065*8975f5c5SAndroid Build Coastguard Worker                      help=('A comma separated list of perf events to capture '
2066*8975f5c5SAndroid Build Coastguard Worker                      '(e.g. \'cpu-cycles,branch-misses\'). Run '
2067*8975f5c5SAndroid Build Coastguard Worker                      '`simpleperf list` on your device to see available '
2068*8975f5c5SAndroid Build Coastguard Worker                      'events.'))
2069*8975f5c5SAndroid Build Coastguard Worker
2070*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
2071*8975f5c5SAndroid Build Coastguard Worker    extra_args = shlex.split(self.args.args or '')
2072*8975f5c5SAndroid Build Coastguard Worker    _RunProfile(self.devices[0], self.args.package_name,
2073*8975f5c5SAndroid Build Coastguard Worker                self.args.output_directory, self.args.profile_output,
2074*8975f5c5SAndroid Build Coastguard Worker                self.args.profile_process, self.args.profile_thread,
2075*8975f5c5SAndroid Build Coastguard Worker                self.args.profile_events, extra_args)
2076*8975f5c5SAndroid Build Coastguard Worker
2077*8975f5c5SAndroid Build Coastguard Worker
2078*8975f5c5SAndroid Build Coastguard Workerclass _RunCommand(_InstallCommand, _LaunchCommand, _LogcatCommand):
2079*8975f5c5SAndroid Build Coastguard Worker  name = 'run'
2080*8975f5c5SAndroid Build Coastguard Worker  description = 'Install, launch, and show logcat (when targeting one device).'
2081*8975f5c5SAndroid Build Coastguard Worker  all_devices_by_default = False
2082*8975f5c5SAndroid Build Coastguard Worker  supports_multiple_devices = True
2083*8975f5c5SAndroid Build Coastguard Worker
2084*8975f5c5SAndroid Build Coastguard Worker  def _RegisterExtraArgs(self, group):
2085*8975f5c5SAndroid Build Coastguard Worker    _InstallCommand._RegisterExtraArgs(self, group)
2086*8975f5c5SAndroid Build Coastguard Worker    _LaunchCommand._RegisterExtraArgs(self, group)
2087*8975f5c5SAndroid Build Coastguard Worker    _LogcatCommand._RegisterExtraArgs(self, group)
2088*8975f5c5SAndroid Build Coastguard Worker    group.add_argument('--no-logcat', action='store_true',
2089*8975f5c5SAndroid Build Coastguard Worker                       help='Install and launch, but do not enter logcat.')
2090*8975f5c5SAndroid Build Coastguard Worker
2091*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
2092*8975f5c5SAndroid Build Coastguard Worker    if self.is_test_apk:
2093*8975f5c5SAndroid Build Coastguard Worker      raise Exception('Use the bin/run_* scripts to run test apks.')
2094*8975f5c5SAndroid Build Coastguard Worker    logging.warning('Installing...')
2095*8975f5c5SAndroid Build Coastguard Worker    _InstallCommand.Run(self)
2096*8975f5c5SAndroid Build Coastguard Worker    logging.warning('Sending launch intent...')
2097*8975f5c5SAndroid Build Coastguard Worker    _LaunchCommand.Run(self)
2098*8975f5c5SAndroid Build Coastguard Worker    if len(self.devices) == 1 and not self.args.no_logcat:
2099*8975f5c5SAndroid Build Coastguard Worker      logging.warning('Entering logcat...')
2100*8975f5c5SAndroid Build Coastguard Worker      _LogcatCommand.Run(self)
2101*8975f5c5SAndroid Build Coastguard Worker
2102*8975f5c5SAndroid Build Coastguard Worker
2103*8975f5c5SAndroid Build Coastguard Workerclass _BuildBundleApks(_Command):
2104*8975f5c5SAndroid Build Coastguard Worker  name = 'build-bundle-apks'
2105*8975f5c5SAndroid Build Coastguard Worker  description = ('Build the .apks archive from an Android app bundle, and '
2106*8975f5c5SAndroid Build Coastguard Worker                 'optionally copy it to a specific destination.')
2107*8975f5c5SAndroid Build Coastguard Worker  need_device_args = False
2108*8975f5c5SAndroid Build Coastguard Worker
2109*8975f5c5SAndroid Build Coastguard Worker  def _RegisterExtraArgs(self, group):
2110*8975f5c5SAndroid Build Coastguard Worker    group.add_argument(
2111*8975f5c5SAndroid Build Coastguard Worker        '--output-apks', required=True, help='Destination path for .apks file.')
2112*8975f5c5SAndroid Build Coastguard Worker    group.add_argument(
2113*8975f5c5SAndroid Build Coastguard Worker        '--minimal',
2114*8975f5c5SAndroid Build Coastguard Worker        action='store_true',
2115*8975f5c5SAndroid Build Coastguard Worker        help='Build .apks archive that targets the bundle\'s minSdkVersion and '
2116*8975f5c5SAndroid Build Coastguard Worker        'contains only english splits. It still contains optional splits.')
2117*8975f5c5SAndroid Build Coastguard Worker    group.add_argument(
2118*8975f5c5SAndroid Build Coastguard Worker        '--sdk-version', help='The sdkVersion to build the .apks for.')
2119*8975f5c5SAndroid Build Coastguard Worker    group.add_argument(
2120*8975f5c5SAndroid Build Coastguard Worker        '--build-mode',
2121*8975f5c5SAndroid Build Coastguard Worker        choices=app_bundle_utils.BUILD_APKS_MODES,
2122*8975f5c5SAndroid Build Coastguard Worker        help='Specify which type of APKs archive to build. "default" '
2123*8975f5c5SAndroid Build Coastguard Worker        'generates regular splits, "universal" generates an archive with a '
2124*8975f5c5SAndroid Build Coastguard Worker        'single universal APK, "system" generates an archive with a system '
2125*8975f5c5SAndroid Build Coastguard Worker        'image APK, while "system_compressed" generates a compressed system '
2126*8975f5c5SAndroid Build Coastguard Worker        'APK, with an additional stub APK for the system image.')
2127*8975f5c5SAndroid Build Coastguard Worker    group.add_argument(
2128*8975f5c5SAndroid Build Coastguard Worker        '--optimize-for',
2129*8975f5c5SAndroid Build Coastguard Worker        choices=app_bundle_utils.OPTIMIZE_FOR_OPTIONS,
2130*8975f5c5SAndroid Build Coastguard Worker        help='Override split configuration.')
2131*8975f5c5SAndroid Build Coastguard Worker
2132*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
2133*8975f5c5SAndroid Build Coastguard Worker    _GenerateBundleApks(
2134*8975f5c5SAndroid Build Coastguard Worker        self.bundle_generation_info,
2135*8975f5c5SAndroid Build Coastguard Worker        output_path=self.args.output_apks,
2136*8975f5c5SAndroid Build Coastguard Worker        minimal=self.args.minimal,
2137*8975f5c5SAndroid Build Coastguard Worker        minimal_sdk_version=self.args.sdk_version,
2138*8975f5c5SAndroid Build Coastguard Worker        mode=self.args.build_mode,
2139*8975f5c5SAndroid Build Coastguard Worker        optimize_for=self.args.optimize_for)
2140*8975f5c5SAndroid Build Coastguard Worker
2141*8975f5c5SAndroid Build Coastguard Worker
2142*8975f5c5SAndroid Build Coastguard Workerclass _ManifestCommand(_Command):
2143*8975f5c5SAndroid Build Coastguard Worker  name = 'dump-manifest'
2144*8975f5c5SAndroid Build Coastguard Worker  description = 'Dump the android manifest as XML, to stdout.'
2145*8975f5c5SAndroid Build Coastguard Worker  need_device_args = False
2146*8975f5c5SAndroid Build Coastguard Worker  needs_apk_helper = True
2147*8975f5c5SAndroid Build Coastguard Worker
2148*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
2149*8975f5c5SAndroid Build Coastguard Worker    if self.is_bundle:
2150*8975f5c5SAndroid Build Coastguard Worker      sys.stdout.write(
2151*8975f5c5SAndroid Build Coastguard Worker          bundletool.RunBundleTool([
2152*8975f5c5SAndroid Build Coastguard Worker              'dump', 'manifest', '--bundle',
2153*8975f5c5SAndroid Build Coastguard Worker              self.bundle_generation_info.bundle_path
2154*8975f5c5SAndroid Build Coastguard Worker          ]))
2155*8975f5c5SAndroid Build Coastguard Worker    else:
2156*8975f5c5SAndroid Build Coastguard Worker      apkanalyzer = os.path.join(_DIR_SOURCE_ROOT, 'third_party', 'android_sdk',
2157*8975f5c5SAndroid Build Coastguard Worker                                 'public', 'cmdline-tools', 'latest', 'bin',
2158*8975f5c5SAndroid Build Coastguard Worker                                 'apkanalyzer')
2159*8975f5c5SAndroid Build Coastguard Worker      cmd = [apkanalyzer, 'manifest', 'print', self.apk_helper.path]
2160*8975f5c5SAndroid Build Coastguard Worker      logging.info('Running: %s', shlex.join(cmd))
2161*8975f5c5SAndroid Build Coastguard Worker      subprocess.check_call(cmd)
2162*8975f5c5SAndroid Build Coastguard Worker
2163*8975f5c5SAndroid Build Coastguard Worker
2164*8975f5c5SAndroid Build Coastguard Workerclass _StackCommand(_Command):
2165*8975f5c5SAndroid Build Coastguard Worker  name = 'stack'
2166*8975f5c5SAndroid Build Coastguard Worker  description = 'Decodes an Android stack.'
2167*8975f5c5SAndroid Build Coastguard Worker  need_device_args = False
2168*8975f5c5SAndroid Build Coastguard Worker
2169*8975f5c5SAndroid Build Coastguard Worker  def _RegisterExtraArgs(self, group):
2170*8975f5c5SAndroid Build Coastguard Worker    group.add_argument(
2171*8975f5c5SAndroid Build Coastguard Worker        'file',
2172*8975f5c5SAndroid Build Coastguard Worker        nargs='?',
2173*8975f5c5SAndroid Build Coastguard Worker        help='File to decode. If not specified, stdin is processed.')
2174*8975f5c5SAndroid Build Coastguard Worker
2175*8975f5c5SAndroid Build Coastguard Worker  def Run(self):
2176*8975f5c5SAndroid Build Coastguard Worker    apk_path = self.args.apk_path or self.incremental_apk_path
2177*8975f5c5SAndroid Build Coastguard Worker    context = _StackScriptContext(self.args.output_directory, apk_path,
2178*8975f5c5SAndroid Build Coastguard Worker                                  self.bundle_generation_info)
2179*8975f5c5SAndroid Build Coastguard Worker    try:
2180*8975f5c5SAndroid Build Coastguard Worker      proc = context.Popen(input_file=self.args.file)
2181*8975f5c5SAndroid Build Coastguard Worker      if proc.wait():
2182*8975f5c5SAndroid Build Coastguard Worker        raise Exception('stack script returned {}'.format(proc.returncode))
2183*8975f5c5SAndroid Build Coastguard Worker    finally:
2184*8975f5c5SAndroid Build Coastguard Worker      context.Close()
2185*8975f5c5SAndroid Build Coastguard Worker
2186*8975f5c5SAndroid Build Coastguard Worker
2187*8975f5c5SAndroid Build Coastguard Worker# Shared commands for regular APKs and app bundles.
2188*8975f5c5SAndroid Build Coastguard Worker_COMMANDS = [
2189*8975f5c5SAndroid Build Coastguard Worker    _DevicesCommand,
2190*8975f5c5SAndroid Build Coastguard Worker    _PackageInfoCommand,
2191*8975f5c5SAndroid Build Coastguard Worker    _InstallCommand,
2192*8975f5c5SAndroid Build Coastguard Worker    _UninstallCommand,
2193*8975f5c5SAndroid Build Coastguard Worker    _SetWebViewProviderCommand,
2194*8975f5c5SAndroid Build Coastguard Worker    _LaunchCommand,
2195*8975f5c5SAndroid Build Coastguard Worker    _StopCommand,
2196*8975f5c5SAndroid Build Coastguard Worker    _ClearDataCommand,
2197*8975f5c5SAndroid Build Coastguard Worker    _ArgvCommand,
2198*8975f5c5SAndroid Build Coastguard Worker    _GdbCommand,
2199*8975f5c5SAndroid Build Coastguard Worker    _LldbCommand,
2200*8975f5c5SAndroid Build Coastguard Worker    _LogcatCommand,
2201*8975f5c5SAndroid Build Coastguard Worker    _PsCommand,
2202*8975f5c5SAndroid Build Coastguard Worker    _DiskUsageCommand,
2203*8975f5c5SAndroid Build Coastguard Worker    _MemUsageCommand,
2204*8975f5c5SAndroid Build Coastguard Worker    _ShellCommand,
2205*8975f5c5SAndroid Build Coastguard Worker    _CompileDexCommand,
2206*8975f5c5SAndroid Build Coastguard Worker    _PrintCertsCommand,
2207*8975f5c5SAndroid Build Coastguard Worker    _ProfileCommand,
2208*8975f5c5SAndroid Build Coastguard Worker    _RunCommand,
2209*8975f5c5SAndroid Build Coastguard Worker    _StackCommand,
2210*8975f5c5SAndroid Build Coastguard Worker    _ManifestCommand,
2211*8975f5c5SAndroid Build Coastguard Worker]
2212*8975f5c5SAndroid Build Coastguard Worker
2213*8975f5c5SAndroid Build Coastguard Worker# Commands specific to app bundles.
2214*8975f5c5SAndroid Build Coastguard Worker_BUNDLE_COMMANDS = [
2215*8975f5c5SAndroid Build Coastguard Worker    _BuildBundleApks,
2216*8975f5c5SAndroid Build Coastguard Worker]
2217*8975f5c5SAndroid Build Coastguard Worker
2218*8975f5c5SAndroid Build Coastguard Worker
2219*8975f5c5SAndroid Build Coastguard Workerdef _ParseArgs(parser, from_wrapper_script, is_bundle, is_test_apk):
2220*8975f5c5SAndroid Build Coastguard Worker  subparsers = parser.add_subparsers()
2221*8975f5c5SAndroid Build Coastguard Worker  command_list = _COMMANDS + (_BUNDLE_COMMANDS if is_bundle else [])
2222*8975f5c5SAndroid Build Coastguard Worker  commands = [
2223*8975f5c5SAndroid Build Coastguard Worker      clazz(from_wrapper_script, is_bundle, is_test_apk)
2224*8975f5c5SAndroid Build Coastguard Worker      for clazz in command_list
2225*8975f5c5SAndroid Build Coastguard Worker  ]
2226*8975f5c5SAndroid Build Coastguard Worker
2227*8975f5c5SAndroid Build Coastguard Worker  for command in commands:
2228*8975f5c5SAndroid Build Coastguard Worker    if from_wrapper_script or not command.needs_output_directory:
2229*8975f5c5SAndroid Build Coastguard Worker      command.RegisterArgs(subparsers)
2230*8975f5c5SAndroid Build Coastguard Worker
2231*8975f5c5SAndroid Build Coastguard Worker  # Show extended help when no command is passed.
2232*8975f5c5SAndroid Build Coastguard Worker  argv = sys.argv[1:]
2233*8975f5c5SAndroid Build Coastguard Worker  if not argv:
2234*8975f5c5SAndroid Build Coastguard Worker    argv = ['--help']
2235*8975f5c5SAndroid Build Coastguard Worker
2236*8975f5c5SAndroid Build Coastguard Worker  return parser.parse_args(argv)
2237*8975f5c5SAndroid Build Coastguard Worker
2238*8975f5c5SAndroid Build Coastguard Worker
2239*8975f5c5SAndroid Build Coastguard Workerdef _RunInternal(parser,
2240*8975f5c5SAndroid Build Coastguard Worker                 output_directory=None,
2241*8975f5c5SAndroid Build Coastguard Worker                 additional_apk_paths=None,
2242*8975f5c5SAndroid Build Coastguard Worker                 bundle_generation_info=None,
2243*8975f5c5SAndroid Build Coastguard Worker                 is_test_apk=False):
2244*8975f5c5SAndroid Build Coastguard Worker  colorama.init()
2245*8975f5c5SAndroid Build Coastguard Worker  parser.set_defaults(
2246*8975f5c5SAndroid Build Coastguard Worker      additional_apk_paths=additional_apk_paths,
2247*8975f5c5SAndroid Build Coastguard Worker      output_directory=output_directory)
2248*8975f5c5SAndroid Build Coastguard Worker  from_wrapper_script = bool(output_directory)
2249*8975f5c5SAndroid Build Coastguard Worker  args = _ParseArgs(parser,
2250*8975f5c5SAndroid Build Coastguard Worker                    from_wrapper_script,
2251*8975f5c5SAndroid Build Coastguard Worker                    is_bundle=bool(bundle_generation_info),
2252*8975f5c5SAndroid Build Coastguard Worker                    is_test_apk=is_test_apk)
2253*8975f5c5SAndroid Build Coastguard Worker  run_tests_helper.SetLogLevel(args.verbose_count)
2254*8975f5c5SAndroid Build Coastguard Worker  if bundle_generation_info:
2255*8975f5c5SAndroid Build Coastguard Worker    args.command.RegisterBundleGenerationInfo(bundle_generation_info)
2256*8975f5c5SAndroid Build Coastguard Worker  if args.additional_apk_paths:
2257*8975f5c5SAndroid Build Coastguard Worker    for i, path in enumerate(args.additional_apk_paths):
2258*8975f5c5SAndroid Build Coastguard Worker      if path and not os.path.exists(path):
2259*8975f5c5SAndroid Build Coastguard Worker        inc_path = path.replace('.apk', '_incremental.apk')
2260*8975f5c5SAndroid Build Coastguard Worker        if os.path.exists(inc_path):
2261*8975f5c5SAndroid Build Coastguard Worker          args.additional_apk_paths[i] = inc_path
2262*8975f5c5SAndroid Build Coastguard Worker          path = inc_path
2263*8975f5c5SAndroid Build Coastguard Worker      if not path or not os.path.exists(path):
2264*8975f5c5SAndroid Build Coastguard Worker        raise Exception('Invalid additional APK path "{}"'.format(path))
2265*8975f5c5SAndroid Build Coastguard Worker  args.command.ProcessArgs(args)
2266*8975f5c5SAndroid Build Coastguard Worker  args.command.Run()
2267*8975f5c5SAndroid Build Coastguard Worker  # Incremental install depends on the cache being cleared when uninstalling.
2268*8975f5c5SAndroid Build Coastguard Worker  if args.command.name != 'uninstall':
2269*8975f5c5SAndroid Build Coastguard Worker    _SaveDeviceCaches(args.command.devices, output_directory)
2270*8975f5c5SAndroid Build Coastguard Worker
2271*8975f5c5SAndroid Build Coastguard Worker
2272*8975f5c5SAndroid Build Coastguard Workerdef Run(output_directory, apk_path, additional_apk_paths, incremental_json,
2273*8975f5c5SAndroid Build Coastguard Worker        command_line_flags_file, target_cpu, proguard_mapping_path):
2274*8975f5c5SAndroid Build Coastguard Worker  """Entry point for generated wrapper scripts."""
2275*8975f5c5SAndroid Build Coastguard Worker  constants.SetOutputDirectory(output_directory)
2276*8975f5c5SAndroid Build Coastguard Worker  devil_chromium.Initialize(output_directory=output_directory)
2277*8975f5c5SAndroid Build Coastguard Worker  parser = argparse.ArgumentParser()
2278*8975f5c5SAndroid Build Coastguard Worker  exists_or_none = lambda p: p if p and os.path.exists(p) else None
2279*8975f5c5SAndroid Build Coastguard Worker
2280*8975f5c5SAndroid Build Coastguard Worker  parser.set_defaults(
2281*8975f5c5SAndroid Build Coastguard Worker      command_line_flags_file=command_line_flags_file,
2282*8975f5c5SAndroid Build Coastguard Worker      target_cpu=target_cpu,
2283*8975f5c5SAndroid Build Coastguard Worker      apk_path=exists_or_none(apk_path),
2284*8975f5c5SAndroid Build Coastguard Worker      incremental_json=exists_or_none(incremental_json),
2285*8975f5c5SAndroid Build Coastguard Worker      proguard_mapping_path=proguard_mapping_path)
2286*8975f5c5SAndroid Build Coastguard Worker  _RunInternal(
2287*8975f5c5SAndroid Build Coastguard Worker      parser,
2288*8975f5c5SAndroid Build Coastguard Worker      output_directory=output_directory,
2289*8975f5c5SAndroid Build Coastguard Worker      additional_apk_paths=additional_apk_paths)
2290*8975f5c5SAndroid Build Coastguard Worker
2291*8975f5c5SAndroid Build Coastguard Worker
2292*8975f5c5SAndroid Build Coastguard Workerdef RunForBundle(output_directory, bundle_path, bundle_apks_path,
2293*8975f5c5SAndroid Build Coastguard Worker                 additional_apk_paths, aapt2_path, keystore_path,
2294*8975f5c5SAndroid Build Coastguard Worker                 keystore_password, keystore_alias, package_name,
2295*8975f5c5SAndroid Build Coastguard Worker                 command_line_flags_file, proguard_mapping_path, target_cpu,
2296*8975f5c5SAndroid Build Coastguard Worker                 system_image_locales, default_modules, is_official_build):
2297*8975f5c5SAndroid Build Coastguard Worker  """Entry point for generated app bundle wrapper scripts.
2298*8975f5c5SAndroid Build Coastguard Worker
2299*8975f5c5SAndroid Build Coastguard Worker  Args:
2300*8975f5c5SAndroid Build Coastguard Worker    output_dir: Chromium output directory path.
2301*8975f5c5SAndroid Build Coastguard Worker    bundle_path: Input bundle path.
2302*8975f5c5SAndroid Build Coastguard Worker    bundle_apks_path: Output bundle .apks archive path.
2303*8975f5c5SAndroid Build Coastguard Worker    additional_apk_paths: Additional APKs to install prior to bundle install.
2304*8975f5c5SAndroid Build Coastguard Worker    aapt2_path: Aapt2 tool path.
2305*8975f5c5SAndroid Build Coastguard Worker    keystore_path: Keystore file path.
2306*8975f5c5SAndroid Build Coastguard Worker    keystore_password: Keystore password.
2307*8975f5c5SAndroid Build Coastguard Worker    keystore_alias: Signing key name alias in keystore file.
2308*8975f5c5SAndroid Build Coastguard Worker    package_name: Application's package name.
2309*8975f5c5SAndroid Build Coastguard Worker    command_line_flags_file: Optional. Name of an on-device file that will be
2310*8975f5c5SAndroid Build Coastguard Worker      used to store command-line flags for this bundle.
2311*8975f5c5SAndroid Build Coastguard Worker    proguard_mapping_path: Input path to the Proguard mapping file, used to
2312*8975f5c5SAndroid Build Coastguard Worker      deobfuscate Java stack traces.
2313*8975f5c5SAndroid Build Coastguard Worker    target_cpu: Chromium target CPU name, used by the 'gdb' command.
2314*8975f5c5SAndroid Build Coastguard Worker    system_image_locales: List of Chromium locales that should be included in
2315*8975f5c5SAndroid Build Coastguard Worker      system image APKs.
2316*8975f5c5SAndroid Build Coastguard Worker    default_modules: List of modules that are installed in addition to those
2317*8975f5c5SAndroid Build Coastguard Worker      given by the '-m' switch.
2318*8975f5c5SAndroid Build Coastguard Worker  """
2319*8975f5c5SAndroid Build Coastguard Worker  constants.SetOutputDirectory(output_directory)
2320*8975f5c5SAndroid Build Coastguard Worker  devil_chromium.Initialize(output_directory=output_directory)
2321*8975f5c5SAndroid Build Coastguard Worker  bundle_generation_info = BundleGenerationInfo(
2322*8975f5c5SAndroid Build Coastguard Worker      bundle_path=bundle_path,
2323*8975f5c5SAndroid Build Coastguard Worker      bundle_apks_path=bundle_apks_path,
2324*8975f5c5SAndroid Build Coastguard Worker      aapt2_path=aapt2_path,
2325*8975f5c5SAndroid Build Coastguard Worker      keystore_path=keystore_path,
2326*8975f5c5SAndroid Build Coastguard Worker      keystore_password=keystore_password,
2327*8975f5c5SAndroid Build Coastguard Worker      keystore_alias=keystore_alias,
2328*8975f5c5SAndroid Build Coastguard Worker      system_image_locales=system_image_locales)
2329*8975f5c5SAndroid Build Coastguard Worker  _InstallCommand.default_modules = default_modules
2330*8975f5c5SAndroid Build Coastguard Worker
2331*8975f5c5SAndroid Build Coastguard Worker  parser = argparse.ArgumentParser()
2332*8975f5c5SAndroid Build Coastguard Worker  parser.set_defaults(package_name=package_name,
2333*8975f5c5SAndroid Build Coastguard Worker                      command_line_flags_file=command_line_flags_file,
2334*8975f5c5SAndroid Build Coastguard Worker                      proguard_mapping_path=proguard_mapping_path,
2335*8975f5c5SAndroid Build Coastguard Worker                      target_cpu=target_cpu,
2336*8975f5c5SAndroid Build Coastguard Worker                      is_official_build=is_official_build)
2337*8975f5c5SAndroid Build Coastguard Worker  _RunInternal(parser,
2338*8975f5c5SAndroid Build Coastguard Worker               output_directory=output_directory,
2339*8975f5c5SAndroid Build Coastguard Worker               additional_apk_paths=additional_apk_paths,
2340*8975f5c5SAndroid Build Coastguard Worker               bundle_generation_info=bundle_generation_info)
2341*8975f5c5SAndroid Build Coastguard Worker
2342*8975f5c5SAndroid Build Coastguard Worker
2343*8975f5c5SAndroid Build Coastguard Workerdef RunForTestApk(*, output_directory, package_name, test_apk_path,
2344*8975f5c5SAndroid Build Coastguard Worker                  test_apk_json, proguard_mapping_path, additional_apk_paths):
2345*8975f5c5SAndroid Build Coastguard Worker  """Entry point for generated test apk wrapper scripts.
2346*8975f5c5SAndroid Build Coastguard Worker
2347*8975f5c5SAndroid Build Coastguard Worker  This is intended to make commands like logcat (with proguard deobfuscation)
2348*8975f5c5SAndroid Build Coastguard Worker  available. The run_* scripts should be used to actually run tests.
2349*8975f5c5SAndroid Build Coastguard Worker
2350*8975f5c5SAndroid Build Coastguard Worker  Args:
2351*8975f5c5SAndroid Build Coastguard Worker    output_dir: Chromium output directory path.
2352*8975f5c5SAndroid Build Coastguard Worker    package_name: The package name for the test apk.
2353*8975f5c5SAndroid Build Coastguard Worker    test_apk_path: The test apk to install.
2354*8975f5c5SAndroid Build Coastguard Worker    test_apk_json: The incremental json dict for the test apk.
2355*8975f5c5SAndroid Build Coastguard Worker    proguard_mapping_path: Input path to the Proguard mapping file, used to
2356*8975f5c5SAndroid Build Coastguard Worker      deobfuscate Java stack traces.
2357*8975f5c5SAndroid Build Coastguard Worker    additional_apk_paths: Additional APKs to install.
2358*8975f5c5SAndroid Build Coastguard Worker  """
2359*8975f5c5SAndroid Build Coastguard Worker  constants.SetOutputDirectory(output_directory)
2360*8975f5c5SAndroid Build Coastguard Worker  devil_chromium.Initialize(output_directory=output_directory)
2361*8975f5c5SAndroid Build Coastguard Worker
2362*8975f5c5SAndroid Build Coastguard Worker  parser = argparse.ArgumentParser()
2363*8975f5c5SAndroid Build Coastguard Worker  exists_or_none = lambda p: p if p and os.path.exists(p) else None
2364*8975f5c5SAndroid Build Coastguard Worker
2365*8975f5c5SAndroid Build Coastguard Worker  parser.set_defaults(apk_path=exists_or_none(test_apk_path),
2366*8975f5c5SAndroid Build Coastguard Worker                      incremental_json=exists_or_none(test_apk_json),
2367*8975f5c5SAndroid Build Coastguard Worker                      package_name=package_name,
2368*8975f5c5SAndroid Build Coastguard Worker                      proguard_mapping_path=proguard_mapping_path)
2369*8975f5c5SAndroid Build Coastguard Worker
2370*8975f5c5SAndroid Build Coastguard Worker  _RunInternal(parser,
2371*8975f5c5SAndroid Build Coastguard Worker               output_directory=output_directory,
2372*8975f5c5SAndroid Build Coastguard Worker               additional_apk_paths=additional_apk_paths,
2373*8975f5c5SAndroid Build Coastguard Worker               is_test_apk=True)
2374*8975f5c5SAndroid Build Coastguard Worker
2375*8975f5c5SAndroid Build Coastguard Worker
2376*8975f5c5SAndroid Build Coastguard Workerdef main():
2377*8975f5c5SAndroid Build Coastguard Worker  devil_chromium.Initialize()
2378*8975f5c5SAndroid Build Coastguard Worker  _RunInternal(argparse.ArgumentParser())
2379*8975f5c5SAndroid Build Coastguard Worker
2380*8975f5c5SAndroid Build Coastguard Worker
2381*8975f5c5SAndroid Build Coastguard Workerif __name__ == '__main__':
2382*8975f5c5SAndroid Build Coastguard Worker  main()
2383