xref: /aosp_15_r20/external/cronet/build/android/adb_install_apk.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1#!/usr/bin/env vpython3
2#
3# Copyright 2012 The Chromium Authors
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Utility script to install APKs from the command line quickly."""
8
9import argparse
10import glob
11import logging
12import os
13import sys
14
15import devil_chromium
16from devil.android import apk_helper
17from devil.android import device_denylist
18from devil.android import device_errors
19from devil.android import device_utils
20from devil.utils import run_tests_helper
21from pylib import constants
22
23
24def main():
25  parser = argparse.ArgumentParser()
26
27  apk_group = parser.add_mutually_exclusive_group(required=True)
28  apk_group.add_argument('--apk', dest='apk_name',
29                         help='DEPRECATED The name of the apk containing the'
30                              ' application (with the .apk extension).')
31  apk_group.add_argument('apk_path', nargs='?',
32                         help='The path to the APK to install.')
33
34  # TODO(jbudorick): Remove once no clients pass --apk_package
35  parser.add_argument('--apk_package', help='DEPRECATED unused')
36  parser.add_argument('--split',
37                      action='append',
38                      dest='splits',
39                      help='A glob matching the apk splits. '
40                           'Can be specified multiple times.')
41  parser.add_argument('--keep_data',
42                      action='store_true',
43                      default=False,
44                      help='Keep the package data when installing '
45                           'the application.')
46  parser.add_argument('--debug', action='store_const', const='Debug',
47                      dest='build_type',
48                      default=os.environ.get('BUILDTYPE', 'Debug'),
49                      help='If set, run test suites under out/Debug. '
50                           'Default is env var BUILDTYPE or Debug')
51  parser.add_argument('--release', action='store_const', const='Release',
52                      dest='build_type',
53                      help='If set, run test suites under out/Release. '
54                           'Default is env var BUILDTYPE or Debug.')
55  parser.add_argument('-d', '--device', dest='devices', action='append',
56                      default=[],
57                      help='Target device for apk to install on. Enter multiple'
58                           ' times for multiple devices.')
59  parser.add_argument('--adb-path', type=os.path.abspath,
60                      help='Absolute path to the adb binary to use.')
61  parser.add_argument('--denylist-file', help='Device denylist JSON file.')
62  parser.add_argument('-v',
63                      '--verbose',
64                      action='count',
65                      help='Enable verbose logging.',
66                      default=0)
67  parser.add_argument('--downgrade', action='store_true',
68                      help='If set, allows downgrading of apk.')
69  parser.add_argument('--timeout', type=int,
70                      default=device_utils.DeviceUtils.INSTALL_DEFAULT_TIMEOUT,
71                      help='Seconds to wait for APK installation. '
72                           '(default: %(default)s)')
73
74  args = parser.parse_args()
75
76  run_tests_helper.SetLogLevel(args.verbose)
77  constants.SetBuildType(args.build_type)
78
79  devil_chromium.Initialize(
80      output_directory=constants.GetOutDirectory(),
81      adb_path=args.adb_path)
82
83  apk = args.apk_path or args.apk_name
84  if not apk.endswith('.apk'):
85    apk += '.apk'
86  if not os.path.exists(apk):
87    apk = os.path.join(constants.GetOutDirectory(), 'apks', apk)
88    if not os.path.exists(apk):
89      parser.error('%s not found.' % apk)
90
91  if args.splits:
92    splits = []
93    base_apk_package = apk_helper.ApkHelper(apk).GetPackageName()
94    for split_glob in args.splits:
95      apks = [f for f in glob.glob(split_glob) if f.endswith('.apk')]
96      if not apks:
97        logging.warning('No apks matched for %s.', split_glob)
98      for f in apks:
99        helper = apk_helper.ApkHelper(f)
100        if (helper.GetPackageName() == base_apk_package
101            and helper.GetSplitName()):
102          splits.append(f)
103
104  denylist = (device_denylist.Denylist(args.denylist_file)
105              if args.denylist_file else None)
106  devices = device_utils.DeviceUtils.HealthyDevices(denylist=denylist,
107                                                    device_arg=args.devices)
108
109  def denylisting_install(device):
110    try:
111      if args.splits:
112        device.InstallSplitApk(apk, splits, reinstall=args.keep_data,
113                               allow_downgrade=args.downgrade)
114      else:
115        device.Install(apk, reinstall=args.keep_data,
116                       allow_downgrade=args.downgrade,
117                       timeout=args.timeout)
118    except (device_errors.CommandFailedError,
119            device_errors.DeviceUnreachableError):
120      logging.exception('Failed to install %s', apk)
121      if denylist:
122        denylist.Extend([str(device)], reason='install_failure')
123        logging.warning('Denylisting %s', str(device))
124    except device_errors.CommandTimeoutError:
125      logging.exception('Timed out while installing %s', apk)
126      if denylist:
127        denylist.Extend([str(device)], reason='install_timeout')
128        logging.warning('Denylisting %s', str(device))
129
130  device_utils.DeviceUtils.parallel(devices).pMap(denylisting_install)
131
132
133if __name__ == '__main__':
134  sys.exit(main())
135