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