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