1*6777b538SAndroid Build Coastguard Worker#!/usr/bin/env vpython3 2*6777b538SAndroid Build Coastguard Worker# 3*6777b538SAndroid Build Coastguard Worker# Copyright 2015 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"""Install *_incremental.apk targets as well as their dependent files.""" 8*6777b538SAndroid Build Coastguard Worker 9*6777b538SAndroid Build Coastguard Workerimport argparse 10*6777b538SAndroid Build Coastguard Workerimport collections 11*6777b538SAndroid Build Coastguard Workerimport functools 12*6777b538SAndroid Build Coastguard Workerimport glob 13*6777b538SAndroid Build Coastguard Workerimport hashlib 14*6777b538SAndroid Build Coastguard Workerimport json 15*6777b538SAndroid Build Coastguard Workerimport logging 16*6777b538SAndroid Build Coastguard Workerimport os 17*6777b538SAndroid Build Coastguard Workerimport posixpath 18*6777b538SAndroid Build Coastguard Workerimport shutil 19*6777b538SAndroid Build Coastguard Workerimport sys 20*6777b538SAndroid Build Coastguard Worker 21*6777b538SAndroid Build Coastguard Workersys.path.append( 22*6777b538SAndroid Build Coastguard Worker os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))) 23*6777b538SAndroid Build Coastguard Workerimport devil_chromium 24*6777b538SAndroid Build Coastguard Workerfrom devil.android import apk_helper 25*6777b538SAndroid Build Coastguard Workerfrom devil.android import device_utils 26*6777b538SAndroid Build Coastguard Workerfrom devil.utils import reraiser_thread 27*6777b538SAndroid Build Coastguard Workerfrom devil.utils import run_tests_helper 28*6777b538SAndroid Build Coastguard Workerfrom pylib import constants 29*6777b538SAndroid Build Coastguard Workerfrom pylib.utils import time_profile 30*6777b538SAndroid Build Coastguard Worker 31*6777b538SAndroid Build Coastguard Workerprev_sys_path = list(sys.path) 32*6777b538SAndroid Build Coastguard Workersys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir, 'gyp')) 33*6777b538SAndroid Build Coastguard Workerimport dex 34*6777b538SAndroid Build Coastguard Workerfrom util import build_utils 35*6777b538SAndroid Build Coastguard Workersys.path = prev_sys_path 36*6777b538SAndroid Build Coastguard Worker 37*6777b538SAndroid Build Coastguard Worker 38*6777b538SAndroid Build Coastguard Worker_R8_PATH = os.path.join(build_utils.DIR_SOURCE_ROOT, 'third_party', 'r8', 'lib', 39*6777b538SAndroid Build Coastguard Worker 'r8.jar') 40*6777b538SAndroid Build Coastguard Worker_SHARD_JSON_FILENAME = 'shards.json' 41*6777b538SAndroid Build Coastguard Worker 42*6777b538SAndroid Build Coastguard Worker 43*6777b538SAndroid Build Coastguard Workerdef _DeviceCachePath(device): 44*6777b538SAndroid Build Coastguard Worker file_name = 'device_cache_%s.json' % device.adb.GetDeviceSerial() 45*6777b538SAndroid Build Coastguard Worker return os.path.join(constants.GetOutDirectory(), file_name) 46*6777b538SAndroid Build Coastguard Worker 47*6777b538SAndroid Build Coastguard Worker 48*6777b538SAndroid Build Coastguard Workerdef _Execute(concurrently, *funcs): 49*6777b538SAndroid Build Coastguard Worker """Calls all functions in |funcs| concurrently or in sequence.""" 50*6777b538SAndroid Build Coastguard Worker timer = time_profile.TimeProfile() 51*6777b538SAndroid Build Coastguard Worker if concurrently: 52*6777b538SAndroid Build Coastguard Worker reraiser_thread.RunAsync(funcs) 53*6777b538SAndroid Build Coastguard Worker else: 54*6777b538SAndroid Build Coastguard Worker for f in funcs: 55*6777b538SAndroid Build Coastguard Worker f() 56*6777b538SAndroid Build Coastguard Worker timer.Stop(log=False) 57*6777b538SAndroid Build Coastguard Worker return timer 58*6777b538SAndroid Build Coastguard Worker 59*6777b538SAndroid Build Coastguard Worker 60*6777b538SAndroid Build Coastguard Workerdef _GetDeviceIncrementalDir(package): 61*6777b538SAndroid Build Coastguard Worker """Returns the device path to put incremental files for the given package.""" 62*6777b538SAndroid Build Coastguard Worker return '/data/local/tmp/incremental-app-%s' % package 63*6777b538SAndroid Build Coastguard Worker 64*6777b538SAndroid Build Coastguard Worker 65*6777b538SAndroid Build Coastguard Workerdef _IsStale(src_paths, old_src_paths, dest_path): 66*6777b538SAndroid Build Coastguard Worker """Returns if |dest| is older than any of |src_paths|, or missing.""" 67*6777b538SAndroid Build Coastguard Worker if not os.path.exists(dest_path): 68*6777b538SAndroid Build Coastguard Worker return True 69*6777b538SAndroid Build Coastguard Worker # Always mark as stale if any paths were added or removed. 70*6777b538SAndroid Build Coastguard Worker if set(src_paths) != set(old_src_paths): 71*6777b538SAndroid Build Coastguard Worker return True 72*6777b538SAndroid Build Coastguard Worker dest_time = os.path.getmtime(dest_path) 73*6777b538SAndroid Build Coastguard Worker for path in src_paths: 74*6777b538SAndroid Build Coastguard Worker if os.path.getmtime(path) > dest_time: 75*6777b538SAndroid Build Coastguard Worker return True 76*6777b538SAndroid Build Coastguard Worker return False 77*6777b538SAndroid Build Coastguard Worker 78*6777b538SAndroid Build Coastguard Worker 79*6777b538SAndroid Build Coastguard Workerdef _LoadPrevShards(dex_staging_dir): 80*6777b538SAndroid Build Coastguard Worker shards_json_path = os.path.join(dex_staging_dir, _SHARD_JSON_FILENAME) 81*6777b538SAndroid Build Coastguard Worker if not os.path.exists(shards_json_path): 82*6777b538SAndroid Build Coastguard Worker return {} 83*6777b538SAndroid Build Coastguard Worker with open(shards_json_path) as f: 84*6777b538SAndroid Build Coastguard Worker return json.load(f) 85*6777b538SAndroid Build Coastguard Worker 86*6777b538SAndroid Build Coastguard Worker 87*6777b538SAndroid Build Coastguard Workerdef _SaveNewShards(shards, dex_staging_dir): 88*6777b538SAndroid Build Coastguard Worker shards_json_path = os.path.join(dex_staging_dir, _SHARD_JSON_FILENAME) 89*6777b538SAndroid Build Coastguard Worker with open(shards_json_path, 'w') as f: 90*6777b538SAndroid Build Coastguard Worker json.dump(shards, f) 91*6777b538SAndroid Build Coastguard Worker 92*6777b538SAndroid Build Coastguard Worker 93*6777b538SAndroid Build Coastguard Workerdef _AllocateDexShards(dex_files): 94*6777b538SAndroid Build Coastguard Worker """Divides input dex files into buckets.""" 95*6777b538SAndroid Build Coastguard Worker # Goals: 96*6777b538SAndroid Build Coastguard Worker # * Make shards small enough that they are fast to merge. 97*6777b538SAndroid Build Coastguard Worker # * Minimize the number of shards so they load quickly on device. 98*6777b538SAndroid Build Coastguard Worker # * Partition files into shards such that a change in one file results in only 99*6777b538SAndroid Build Coastguard Worker # one shard having to be re-created. 100*6777b538SAndroid Build Coastguard Worker shards = collections.defaultdict(list) 101*6777b538SAndroid Build Coastguard Worker # As of Oct 2019, 10 shards results in a min/max size of 582K/2.6M. 102*6777b538SAndroid Build Coastguard Worker NUM_CORE_SHARDS = 10 103*6777b538SAndroid Build Coastguard Worker # As of Oct 2019, 17 dex files are larger than 1M. 104*6777b538SAndroid Build Coastguard Worker SHARD_THRESHOLD = 2**20 105*6777b538SAndroid Build Coastguard Worker for src_path in dex_files: 106*6777b538SAndroid Build Coastguard Worker if os.path.getsize(src_path) >= SHARD_THRESHOLD: 107*6777b538SAndroid Build Coastguard Worker # Use the path as the name rather than an incrementing number to ensure 108*6777b538SAndroid Build Coastguard Worker # that it shards to the same name every time. 109*6777b538SAndroid Build Coastguard Worker name = os.path.relpath(src_path, constants.GetOutDirectory()).replace( 110*6777b538SAndroid Build Coastguard Worker os.sep, '.') 111*6777b538SAndroid Build Coastguard Worker shards[name].append(src_path) 112*6777b538SAndroid Build Coastguard Worker else: 113*6777b538SAndroid Build Coastguard Worker # The stdlib hash(string) function is salted differently across python3 114*6777b538SAndroid Build Coastguard Worker # invocations. Thus we use md5 instead to consistently shard the same 115*6777b538SAndroid Build Coastguard Worker # file to the same shard across runs. 116*6777b538SAndroid Build Coastguard Worker hex_hash = hashlib.md5(src_path.encode('utf-8')).hexdigest() 117*6777b538SAndroid Build Coastguard Worker name = 'shard{}.dex.jar'.format(int(hex_hash, 16) % NUM_CORE_SHARDS) 118*6777b538SAndroid Build Coastguard Worker shards[name].append(src_path) 119*6777b538SAndroid Build Coastguard Worker logging.info('Sharding %d dex files into %d buckets', len(dex_files), 120*6777b538SAndroid Build Coastguard Worker len(shards)) 121*6777b538SAndroid Build Coastguard Worker return shards 122*6777b538SAndroid Build Coastguard Worker 123*6777b538SAndroid Build Coastguard Worker 124*6777b538SAndroid Build Coastguard Workerdef _CreateDexFiles(shards, prev_shards, dex_staging_dir, min_api, 125*6777b538SAndroid Build Coastguard Worker use_concurrency): 126*6777b538SAndroid Build Coastguard Worker """Creates dex files within |dex_staging_dir| defined by |shards|.""" 127*6777b538SAndroid Build Coastguard Worker tasks = [] 128*6777b538SAndroid Build Coastguard Worker for name, src_paths in shards.items(): 129*6777b538SAndroid Build Coastguard Worker dest_path = os.path.join(dex_staging_dir, name) 130*6777b538SAndroid Build Coastguard Worker if _IsStale(src_paths=src_paths, 131*6777b538SAndroid Build Coastguard Worker old_src_paths=prev_shards.get(name, []), 132*6777b538SAndroid Build Coastguard Worker dest_path=dest_path): 133*6777b538SAndroid Build Coastguard Worker tasks.append( 134*6777b538SAndroid Build Coastguard Worker functools.partial(dex.MergeDexForIncrementalInstall, _R8_PATH, 135*6777b538SAndroid Build Coastguard Worker src_paths, dest_path, min_api)) 136*6777b538SAndroid Build Coastguard Worker 137*6777b538SAndroid Build Coastguard Worker # TODO(agrieve): It would be more performant to write a custom d8.jar 138*6777b538SAndroid Build Coastguard Worker # wrapper in java that would process these in bulk, rather than spinning 139*6777b538SAndroid Build Coastguard Worker # up a new process for each one. 140*6777b538SAndroid Build Coastguard Worker _Execute(use_concurrency, *tasks) 141*6777b538SAndroid Build Coastguard Worker 142*6777b538SAndroid Build Coastguard Worker # Remove any stale shards. 143*6777b538SAndroid Build Coastguard Worker for name in os.listdir(dex_staging_dir): 144*6777b538SAndroid Build Coastguard Worker if name not in shards: 145*6777b538SAndroid Build Coastguard Worker os.unlink(os.path.join(dex_staging_dir, name)) 146*6777b538SAndroid Build Coastguard Worker 147*6777b538SAndroid Build Coastguard Worker 148*6777b538SAndroid Build Coastguard Workerdef Uninstall(device, package, enable_device_cache=False): 149*6777b538SAndroid Build Coastguard Worker """Uninstalls and removes all incremental files for the given package.""" 150*6777b538SAndroid Build Coastguard Worker main_timer = time_profile.TimeProfile() 151*6777b538SAndroid Build Coastguard Worker device.Uninstall(package) 152*6777b538SAndroid Build Coastguard Worker if enable_device_cache: 153*6777b538SAndroid Build Coastguard Worker # Uninstall is rare, so just wipe the cache in this case. 154*6777b538SAndroid Build Coastguard Worker cache_path = _DeviceCachePath(device) 155*6777b538SAndroid Build Coastguard Worker if os.path.exists(cache_path): 156*6777b538SAndroid Build Coastguard Worker os.unlink(cache_path) 157*6777b538SAndroid Build Coastguard Worker device.RunShellCommand(['rm', '-rf', _GetDeviceIncrementalDir(package)], 158*6777b538SAndroid Build Coastguard Worker check_return=True) 159*6777b538SAndroid Build Coastguard Worker logging.info('Uninstall took %s seconds.', main_timer.GetDelta()) 160*6777b538SAndroid Build Coastguard Worker 161*6777b538SAndroid Build Coastguard Worker 162*6777b538SAndroid Build Coastguard Workerdef Install(device, install_json, apk=None, enable_device_cache=False, 163*6777b538SAndroid Build Coastguard Worker use_concurrency=True, permissions=()): 164*6777b538SAndroid Build Coastguard Worker """Installs the given incremental apk and all required supporting files. 165*6777b538SAndroid Build Coastguard Worker 166*6777b538SAndroid Build Coastguard Worker Args: 167*6777b538SAndroid Build Coastguard Worker device: A DeviceUtils instance (to install to). 168*6777b538SAndroid Build Coastguard Worker install_json: Path to .json file or already parsed .json object. 169*6777b538SAndroid Build Coastguard Worker apk: An existing ApkHelper instance for the apk (optional). 170*6777b538SAndroid Build Coastguard Worker enable_device_cache: Whether to enable on-device caching of checksums. 171*6777b538SAndroid Build Coastguard Worker use_concurrency: Whether to speed things up using multiple threads. 172*6777b538SAndroid Build Coastguard Worker permissions: A list of the permissions to grant, or None to grant all 173*6777b538SAndroid Build Coastguard Worker non-denylisted permissions in the manifest. 174*6777b538SAndroid Build Coastguard Worker """ 175*6777b538SAndroid Build Coastguard Worker if isinstance(install_json, str): 176*6777b538SAndroid Build Coastguard Worker with open(install_json) as f: 177*6777b538SAndroid Build Coastguard Worker install_dict = json.load(f) 178*6777b538SAndroid Build Coastguard Worker else: 179*6777b538SAndroid Build Coastguard Worker install_dict = install_json 180*6777b538SAndroid Build Coastguard Worker 181*6777b538SAndroid Build Coastguard Worker main_timer = time_profile.TimeProfile() 182*6777b538SAndroid Build Coastguard Worker install_timer = time_profile.TimeProfile() 183*6777b538SAndroid Build Coastguard Worker push_native_timer = time_profile.TimeProfile() 184*6777b538SAndroid Build Coastguard Worker merge_dex_timer = time_profile.TimeProfile() 185*6777b538SAndroid Build Coastguard Worker push_dex_timer = time_profile.TimeProfile() 186*6777b538SAndroid Build Coastguard Worker 187*6777b538SAndroid Build Coastguard Worker def fix_path(p): 188*6777b538SAndroid Build Coastguard Worker return os.path.normpath(os.path.join(constants.GetOutDirectory(), p)) 189*6777b538SAndroid Build Coastguard Worker 190*6777b538SAndroid Build Coastguard Worker if not apk: 191*6777b538SAndroid Build Coastguard Worker apk = apk_helper.ToHelper(fix_path(install_dict['apk_path'])) 192*6777b538SAndroid Build Coastguard Worker split_globs = [fix_path(p) for p in install_dict['split_globs']] 193*6777b538SAndroid Build Coastguard Worker native_libs = [fix_path(p) for p in install_dict['native_libs']] 194*6777b538SAndroid Build Coastguard Worker dex_files = [fix_path(p) for p in install_dict['dex_files']] 195*6777b538SAndroid Build Coastguard Worker show_proguard_warning = install_dict.get('show_proguard_warning') 196*6777b538SAndroid Build Coastguard Worker 197*6777b538SAndroid Build Coastguard Worker apk_package = apk.GetPackageName() 198*6777b538SAndroid Build Coastguard Worker device_incremental_dir = _GetDeviceIncrementalDir(apk_package) 199*6777b538SAndroid Build Coastguard Worker dex_staging_dir = os.path.join(constants.GetOutDirectory(), 200*6777b538SAndroid Build Coastguard Worker 'incremental-install', 201*6777b538SAndroid Build Coastguard Worker install_dict['apk_path']) 202*6777b538SAndroid Build Coastguard Worker device_dex_dir = posixpath.join(device_incremental_dir, 'dex') 203*6777b538SAndroid Build Coastguard Worker 204*6777b538SAndroid Build Coastguard Worker # Install .apk(s) if any of them have changed. 205*6777b538SAndroid Build Coastguard Worker def do_install(): 206*6777b538SAndroid Build Coastguard Worker install_timer.Start() 207*6777b538SAndroid Build Coastguard Worker if split_globs: 208*6777b538SAndroid Build Coastguard Worker splits = [] 209*6777b538SAndroid Build Coastguard Worker for split_glob in split_globs: 210*6777b538SAndroid Build Coastguard Worker splits.extend((f for f in glob.glob(split_glob))) 211*6777b538SAndroid Build Coastguard Worker device.InstallSplitApk( 212*6777b538SAndroid Build Coastguard Worker apk, 213*6777b538SAndroid Build Coastguard Worker splits, 214*6777b538SAndroid Build Coastguard Worker allow_downgrade=True, 215*6777b538SAndroid Build Coastguard Worker reinstall=True, 216*6777b538SAndroid Build Coastguard Worker allow_cached_props=True, 217*6777b538SAndroid Build Coastguard Worker permissions=permissions) 218*6777b538SAndroid Build Coastguard Worker else: 219*6777b538SAndroid Build Coastguard Worker device.Install( 220*6777b538SAndroid Build Coastguard Worker apk, allow_downgrade=True, reinstall=True, permissions=permissions) 221*6777b538SAndroid Build Coastguard Worker install_timer.Stop(log=False) 222*6777b538SAndroid Build Coastguard Worker 223*6777b538SAndroid Build Coastguard Worker # Push .so and .dex files to the device (if they have changed). 224*6777b538SAndroid Build Coastguard Worker def do_push_files(): 225*6777b538SAndroid Build Coastguard Worker 226*6777b538SAndroid Build Coastguard Worker def do_push_native(): 227*6777b538SAndroid Build Coastguard Worker push_native_timer.Start() 228*6777b538SAndroid Build Coastguard Worker if native_libs: 229*6777b538SAndroid Build Coastguard Worker with build_utils.TempDir() as temp_dir: 230*6777b538SAndroid Build Coastguard Worker device_lib_dir = posixpath.join(device_incremental_dir, 'lib') 231*6777b538SAndroid Build Coastguard Worker for path in native_libs: 232*6777b538SAndroid Build Coastguard Worker # Note: Can't use symlinks as they don't work when 233*6777b538SAndroid Build Coastguard Worker # "adb push parent_dir" is used (like we do here). 234*6777b538SAndroid Build Coastguard Worker shutil.copy(path, os.path.join(temp_dir, os.path.basename(path))) 235*6777b538SAndroid Build Coastguard Worker device.PushChangedFiles([(temp_dir, device_lib_dir)], 236*6777b538SAndroid Build Coastguard Worker delete_device_stale=True) 237*6777b538SAndroid Build Coastguard Worker push_native_timer.Stop(log=False) 238*6777b538SAndroid Build Coastguard Worker 239*6777b538SAndroid Build Coastguard Worker def do_merge_dex(): 240*6777b538SAndroid Build Coastguard Worker merge_dex_timer.Start() 241*6777b538SAndroid Build Coastguard Worker prev_shards = _LoadPrevShards(dex_staging_dir) 242*6777b538SAndroid Build Coastguard Worker shards = _AllocateDexShards(dex_files) 243*6777b538SAndroid Build Coastguard Worker build_utils.MakeDirectory(dex_staging_dir) 244*6777b538SAndroid Build Coastguard Worker _CreateDexFiles(shards, prev_shards, dex_staging_dir, 245*6777b538SAndroid Build Coastguard Worker apk.GetMinSdkVersion(), use_concurrency) 246*6777b538SAndroid Build Coastguard Worker # New shard information must be saved after _CreateDexFiles since 247*6777b538SAndroid Build Coastguard Worker # _CreateDexFiles removes all non-dex files from the staging dir. 248*6777b538SAndroid Build Coastguard Worker _SaveNewShards(shards, dex_staging_dir) 249*6777b538SAndroid Build Coastguard Worker merge_dex_timer.Stop(log=False) 250*6777b538SAndroid Build Coastguard Worker 251*6777b538SAndroid Build Coastguard Worker def do_push_dex(): 252*6777b538SAndroid Build Coastguard Worker push_dex_timer.Start() 253*6777b538SAndroid Build Coastguard Worker device.PushChangedFiles([(dex_staging_dir, device_dex_dir)], 254*6777b538SAndroid Build Coastguard Worker delete_device_stale=True) 255*6777b538SAndroid Build Coastguard Worker push_dex_timer.Stop(log=False) 256*6777b538SAndroid Build Coastguard Worker 257*6777b538SAndroid Build Coastguard Worker _Execute(use_concurrency, do_push_native, do_merge_dex) 258*6777b538SAndroid Build Coastguard Worker do_push_dex() 259*6777b538SAndroid Build Coastguard Worker 260*6777b538SAndroid Build Coastguard Worker cache_path = _DeviceCachePath(device) 261*6777b538SAndroid Build Coastguard Worker def restore_cache(): 262*6777b538SAndroid Build Coastguard Worker if not enable_device_cache: 263*6777b538SAndroid Build Coastguard Worker return 264*6777b538SAndroid Build Coastguard Worker if os.path.exists(cache_path): 265*6777b538SAndroid Build Coastguard Worker logging.info('Using device cache: %s', cache_path) 266*6777b538SAndroid Build Coastguard Worker with open(cache_path) as f: 267*6777b538SAndroid Build Coastguard Worker device.LoadCacheData(f.read()) 268*6777b538SAndroid Build Coastguard Worker # Delete the cached file so that any exceptions cause it to be cleared. 269*6777b538SAndroid Build Coastguard Worker os.unlink(cache_path) 270*6777b538SAndroid Build Coastguard Worker else: 271*6777b538SAndroid Build Coastguard Worker logging.info('No device cache present: %s', cache_path) 272*6777b538SAndroid Build Coastguard Worker 273*6777b538SAndroid Build Coastguard Worker def save_cache(): 274*6777b538SAndroid Build Coastguard Worker if not enable_device_cache: 275*6777b538SAndroid Build Coastguard Worker return 276*6777b538SAndroid Build Coastguard Worker with open(cache_path, 'w') as f: 277*6777b538SAndroid Build Coastguard Worker f.write(device.DumpCacheData()) 278*6777b538SAndroid Build Coastguard Worker logging.info('Wrote device cache: %s', cache_path) 279*6777b538SAndroid Build Coastguard Worker 280*6777b538SAndroid Build Coastguard Worker # Create 2 lock files: 281*6777b538SAndroid Build Coastguard Worker # * install.lock tells the app to pause on start-up (until we release it). 282*6777b538SAndroid Build Coastguard Worker # * firstrun.lock is used by the app to pause all secondary processes until 283*6777b538SAndroid Build Coastguard Worker # the primary process finishes loading the .dex / .so files. 284*6777b538SAndroid Build Coastguard Worker def create_lock_files(): 285*6777b538SAndroid Build Coastguard Worker # Creates or zeros out lock files. 286*6777b538SAndroid Build Coastguard Worker cmd = ('D="%s";' 287*6777b538SAndroid Build Coastguard Worker 'mkdir -p $D &&' 288*6777b538SAndroid Build Coastguard Worker 'echo -n >$D/install.lock 2>$D/firstrun.lock') 289*6777b538SAndroid Build Coastguard Worker device.RunShellCommand( 290*6777b538SAndroid Build Coastguard Worker cmd % device_incremental_dir, shell=True, check_return=True) 291*6777b538SAndroid Build Coastguard Worker 292*6777b538SAndroid Build Coastguard Worker # The firstrun.lock is released by the app itself. 293*6777b538SAndroid Build Coastguard Worker def release_installer_lock(): 294*6777b538SAndroid Build Coastguard Worker device.RunShellCommand('echo > %s/install.lock' % device_incremental_dir, 295*6777b538SAndroid Build Coastguard Worker check_return=True, shell=True) 296*6777b538SAndroid Build Coastguard Worker 297*6777b538SAndroid Build Coastguard Worker # Concurrency here speeds things up quite a bit, but DeviceUtils hasn't 298*6777b538SAndroid Build Coastguard Worker # been designed for multi-threading. Enabling only because this is a 299*6777b538SAndroid Build Coastguard Worker # developer-only tool. 300*6777b538SAndroid Build Coastguard Worker setup_timer = _Execute(use_concurrency, create_lock_files, restore_cache) 301*6777b538SAndroid Build Coastguard Worker 302*6777b538SAndroid Build Coastguard Worker _Execute(use_concurrency, do_install, do_push_files) 303*6777b538SAndroid Build Coastguard Worker 304*6777b538SAndroid Build Coastguard Worker finalize_timer = _Execute(use_concurrency, release_installer_lock, save_cache) 305*6777b538SAndroid Build Coastguard Worker 306*6777b538SAndroid Build Coastguard Worker logging.info( 307*6777b538SAndroid Build Coastguard Worker 'Install of %s took %s seconds (setup=%s, install=%s, lib_push=%s, ' 308*6777b538SAndroid Build Coastguard Worker 'dex_merge=%s dex_push=%s, finalize=%s)', os.path.basename(apk.path), 309*6777b538SAndroid Build Coastguard Worker main_timer.GetDelta(), setup_timer.GetDelta(), install_timer.GetDelta(), 310*6777b538SAndroid Build Coastguard Worker push_native_timer.GetDelta(), merge_dex_timer.GetDelta(), 311*6777b538SAndroid Build Coastguard Worker push_dex_timer.GetDelta(), finalize_timer.GetDelta()) 312*6777b538SAndroid Build Coastguard Worker if show_proguard_warning: 313*6777b538SAndroid Build Coastguard Worker logging.warning('Target had proguard enabled, but incremental install uses ' 314*6777b538SAndroid Build Coastguard Worker 'non-proguarded .dex files. Performance characteristics ' 315*6777b538SAndroid Build Coastguard Worker 'may differ.') 316*6777b538SAndroid Build Coastguard Worker 317*6777b538SAndroid Build Coastguard Worker 318*6777b538SAndroid Build Coastguard Workerdef main(): 319*6777b538SAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 320*6777b538SAndroid Build Coastguard Worker parser.add_argument('json_path', 321*6777b538SAndroid Build Coastguard Worker help='The path to the generated incremental apk .json.') 322*6777b538SAndroid Build Coastguard Worker parser.add_argument('-d', '--device', dest='device', 323*6777b538SAndroid Build Coastguard Worker help='Target device for apk to install on.') 324*6777b538SAndroid Build Coastguard Worker parser.add_argument('--uninstall', 325*6777b538SAndroid Build Coastguard Worker action='store_true', 326*6777b538SAndroid Build Coastguard Worker default=False, 327*6777b538SAndroid Build Coastguard Worker help='Remove the app and all side-loaded files.') 328*6777b538SAndroid Build Coastguard Worker parser.add_argument('--output-directory', 329*6777b538SAndroid Build Coastguard Worker help='Path to the root build directory.') 330*6777b538SAndroid Build Coastguard Worker parser.add_argument('--no-threading', 331*6777b538SAndroid Build Coastguard Worker action='store_false', 332*6777b538SAndroid Build Coastguard Worker default=True, 333*6777b538SAndroid Build Coastguard Worker dest='threading', 334*6777b538SAndroid Build Coastguard Worker help='Do not install and push concurrently') 335*6777b538SAndroid Build Coastguard Worker parser.add_argument('--no-cache', 336*6777b538SAndroid Build Coastguard Worker action='store_false', 337*6777b538SAndroid Build Coastguard Worker default=True, 338*6777b538SAndroid Build Coastguard Worker dest='cache', 339*6777b538SAndroid Build Coastguard Worker help='Do not use cached information about what files are ' 340*6777b538SAndroid Build Coastguard Worker 'currently on the target device.') 341*6777b538SAndroid Build Coastguard Worker parser.add_argument('-v', 342*6777b538SAndroid Build Coastguard Worker '--verbose', 343*6777b538SAndroid Build Coastguard Worker dest='verbose_count', 344*6777b538SAndroid Build Coastguard Worker default=0, 345*6777b538SAndroid Build Coastguard Worker action='count', 346*6777b538SAndroid Build Coastguard Worker help='Verbose level (multiple times for more)') 347*6777b538SAndroid Build Coastguard Worker 348*6777b538SAndroid Build Coastguard Worker args = parser.parse_args() 349*6777b538SAndroid Build Coastguard Worker 350*6777b538SAndroid Build Coastguard Worker run_tests_helper.SetLogLevel(args.verbose_count) 351*6777b538SAndroid Build Coastguard Worker if args.output_directory: 352*6777b538SAndroid Build Coastguard Worker constants.SetOutputDirectory(args.output_directory) 353*6777b538SAndroid Build Coastguard Worker 354*6777b538SAndroid Build Coastguard Worker devil_chromium.Initialize(output_directory=constants.GetOutDirectory()) 355*6777b538SAndroid Build Coastguard Worker 356*6777b538SAndroid Build Coastguard Worker # Retries are annoying when commands fail for legitimate reasons. Might want 357*6777b538SAndroid Build Coastguard Worker # to enable them if this is ever used on bots though. 358*6777b538SAndroid Build Coastguard Worker device = device_utils.DeviceUtils.HealthyDevices( 359*6777b538SAndroid Build Coastguard Worker device_arg=args.device, 360*6777b538SAndroid Build Coastguard Worker default_retries=0, 361*6777b538SAndroid Build Coastguard Worker enable_device_files_cache=True)[0] 362*6777b538SAndroid Build Coastguard Worker 363*6777b538SAndroid Build Coastguard Worker if args.uninstall: 364*6777b538SAndroid Build Coastguard Worker with open(args.json_path) as f: 365*6777b538SAndroid Build Coastguard Worker install_dict = json.load(f) 366*6777b538SAndroid Build Coastguard Worker apk = apk_helper.ToHelper(install_dict['apk_path']) 367*6777b538SAndroid Build Coastguard Worker Uninstall(device, apk.GetPackageName(), enable_device_cache=args.cache) 368*6777b538SAndroid Build Coastguard Worker else: 369*6777b538SAndroid Build Coastguard Worker Install(device, args.json_path, enable_device_cache=args.cache, 370*6777b538SAndroid Build Coastguard Worker use_concurrency=args.threading) 371*6777b538SAndroid Build Coastguard Worker 372*6777b538SAndroid Build Coastguard Worker 373*6777b538SAndroid Build Coastguard Workerif __name__ == '__main__': 374*6777b538SAndroid Build Coastguard Worker sys.exit(main()) 375