Ben Murdoch | 097c5b2 | 2016-05-18 11:27:45 +0100 | [diff] [blame] | 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 | |