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