示例#1
0
def Uninstall(device, package):
    """Uninstalls and removes all incremental files for the given package."""
    main_timer = time_profile.TimeProfile()
    device.Uninstall(package)
    device.RunShellCommand(
        ['rm', '-rf', _GetDeviceIncrementalDir(package)], check_return=True)
    logging.info('Uninstall took %s seconds.', main_timer.GetDelta())
示例#2
0
def LaunchTempEmulators(emulator_count, abi, api_level, wait_for_boot=True):
  """Create and launch temporary emulators and wait for them to boot.

  Args:
    emulator_count: number of emulators to launch.
    abi: the emulator target platform
    api_level: the api level (e.g., 19 for Android v4.4 - KitKat release)
    wait_for_boot: whether or not to wait for emulators to boot up

  Returns:
    List of emulators.
  """
  emulators = []
  for n in xrange(emulator_count):
    t = time_profile.TimeProfile('Emulator launch %d' % n)
    # Creates a temporary AVD.
    avd_name = 'run_tests_avd_%d' % n
    logging.info('Emulator launch %d with avd_name=%s and api=%d',
        n, avd_name, api_level)
    emulator = Emulator(avd_name, abi)
    emulator.CreateAVD(api_level)
    emulator.Launch(kill_all_emulators=n == 0)
    t.Stop()
    emulators.append(emulator)
  # Wait for all emulators to boot completed.
  if wait_for_boot:
    for emulator in emulators:
      emulator.ConfirmLaunch(True)
  return emulators
示例#3
0
def _Execute(concurrently, *funcs):
    """Calls all functions in |funcs| concurrently or in sequence."""
    timer = time_profile.TimeProfile()
    if concurrently:
        reraiser_thread.RunAsync(funcs)
    else:
        for f in funcs:
            f()
    timer.Stop(log=False)
    return timer
示例#4
0
def Uninstall(device, package, enable_device_cache=False):
    """Uninstalls and removes all incremental files for the given package."""
    main_timer = time_profile.TimeProfile()
    device.Uninstall(package)
    if enable_device_cache:
        # Uninstall is rare, so just wipe the cache in this case.
        cache_path = _DeviceCachePath(device)
        if os.path.exists(cache_path):
            os.unlink(cache_path)
    device.RunShellCommand(
        ['rm', '-rf', _GetDeviceIncrementalDir(package)], check_return=True)
    logging.info('Uninstall took %s seconds.', main_timer.GetDelta())
示例#5
0
def LaunchTempEmulators(emulator_count,
                        abi,
                        api_level,
                        enable_kvm=False,
                        kill_and_launch=True,
                        sdcard_size=DEFAULT_SDCARD_SIZE,
                        storage_size=DEFAULT_STORAGE_SIZE,
                        wait_for_boot=True,
                        headless=False):
    """Create and launch temporary emulators and wait for them to boot.

  Args:
    emulator_count: number of emulators to launch.
    abi: the emulator target platform
    api_level: the api level (e.g., 19 for Android v4.4 - KitKat release)
    wait_for_boot: whether or not to wait for emulators to boot up
    headless: running emulator with no ui

  Returns:
    List of emulators.
  """
    emulators = []
    for n in xrange(emulator_count):
        t = time_profile.TimeProfile('Emulator launch %d' % n)
        # Creates a temporary AVD.
        avd_name = 'run_tests_avd_%d' % n
        logging.info('Emulator launch %d with avd_name=%s and api=%d', n,
                     avd_name, api_level)
        emulator = Emulator(avd_name,
                            abi,
                            enable_kvm=enable_kvm,
                            sdcard_size=sdcard_size,
                            storage_size=storage_size,
                            headless=headless)
        emulator.CreateAVD(api_level)
        emulator.Launch(kill_all_emulators=(n == 0 and kill_and_launch))
        t.Stop()
        emulators.append(emulator)
    # Wait for all emulators to boot completed.
    if wait_for_boot:
        for emulator in emulators:
            emulator.ConfirmLaunch(True)
        logging.info('All emulators are fully booted')
    return emulators
示例#6
0
def Install(device,
            apk,
            split_globs=None,
            native_libs=None,
            dex_files=None,
            enable_device_cache=False,
            use_concurrency=True,
            show_proguard_warning=False,
            permissions=()):
    """Installs the given incremental apk and all required supporting files.

  Args:
    device: A DeviceUtils instance.
    apk: The path to the apk, or an ApkHelper instance.
    split_globs: Glob patterns for any required apk splits (optional).
    native_libs: List of app's native libraries (optional).
    dex_files: List of .dex.jar files that comprise the app's Dalvik code.
    enable_device_cache: Whether to enable on-device caching of checksums.
    use_concurrency: Whether to speed things up using multiple threads.
    show_proguard_warning: Whether to print a warning about Proguard not being
        enabled after installing.
    permissions: A list of the permissions to grant, or None to grant all
                 non-blacklisted permissions in the manifest.
  """
    main_timer = time_profile.TimeProfile()
    install_timer = time_profile.TimeProfile()
    push_native_timer = time_profile.TimeProfile()
    push_dex_timer = time_profile.TimeProfile()

    apk = apk_helper.ToHelper(apk)
    apk_package = apk.GetPackageName()
    device_incremental_dir = _GetDeviceIncrementalDir(apk_package)

    # Install .apk(s) if any of them have changed.
    def do_install():
        install_timer.Start()
        if split_globs:
            splits = []
            for split_glob in split_globs:
                splits.extend((f for f in glob.glob(split_glob)))
            device.InstallSplitApk(apk,
                                   splits,
                                   reinstall=True,
                                   allow_cached_props=True,
                                   permissions=permissions)
        else:
            device.Install(apk, reinstall=True, permissions=permissions)
        install_timer.Stop(log=False)

    # Push .so and .dex files to the device (if they have changed).
    def do_push_files():
        if native_libs:
            push_native_timer.Start()
            with build_utils.TempDir() as temp_dir:
                device_lib_dir = posixpath.join(device_incremental_dir, 'lib')
                for path in native_libs:
                    # Note: Can't use symlinks as they don't work when
                    # "adb push parent_dir" is used (like we do here).
                    shutil.copy(path,
                                os.path.join(temp_dir, os.path.basename(path)))
                device.PushChangedFiles([(temp_dir, device_lib_dir)],
                                        delete_device_stale=True)
            push_native_timer.Stop(log=False)

        if dex_files:
            push_dex_timer.Start()
            # Put all .dex files to be pushed into a temporary directory so that we
            # can use delete_device_stale=True.
            with build_utils.TempDir() as temp_dir:
                device_dex_dir = posixpath.join(device_incremental_dir, 'dex')
                # Ensure no two files have the same name.
                transformed_names = _TransformDexPaths(dex_files)
                for src_path, dest_name in zip(dex_files, transformed_names):
                    # Binary targets with no extra classes create .dex.jar without a
                    # classes.dex (which Android chokes on).
                    if _HasClasses(src_path):
                        shutil.copy(src_path,
                                    os.path.join(temp_dir, dest_name))
                device.PushChangedFiles([(temp_dir, device_dex_dir)],
                                        delete_device_stale=True)
            push_dex_timer.Stop(log=False)

    def check_selinux():
        # Marshmallow has no filesystem access whatsoever. It might be possible to
        # get things working on Lollipop, but attempts so far have failed.
        # http://crbug.com/558818
        has_selinux = device.build_version_sdk >= version_codes.LOLLIPOP
        if has_selinux and apk.HasIsolatedProcesses():
            raise Exception(
                'Cannot use incremental installs on Android L+ without '
                'first disabling isoloated processes.\n'
                'To do so, use GN arg:\n'
                '    disable_incremental_isolated_processes=true')

    cache_path = _DeviceCachePath(device)

    def restore_cache():
        if not enable_device_cache:
            logging.info('Ignoring device cache')
            return
        if os.path.exists(cache_path):
            logging.info('Using device cache: %s', cache_path)
            with open(cache_path) as f:
                device.LoadCacheData(f.read())
            # Delete the cached file so that any exceptions cause it to be cleared.
            os.unlink(cache_path)
        else:
            logging.info('No device cache present: %s', cache_path)

    def save_cache():
        with open(cache_path, 'w') as f:
            f.write(device.DumpCacheData())
            logging.info('Wrote device cache: %s', cache_path)

    # Create 2 lock files:
    # * install.lock tells the app to pause on start-up (until we release it).
    # * firstrun.lock is used by the app to pause all secondary processes until
    #   the primary process finishes loading the .dex / .so files.
    def create_lock_files():
        # Creates or zeros out lock files.
        cmd = ('D="%s";'
               'mkdir -p $D &&'
               'echo -n >$D/install.lock 2>$D/firstrun.lock')
        device.RunShellCommand(cmd % device_incremental_dir, check_return=True)

    # The firstrun.lock is released by the app itself.
    def release_installer_lock():
        device.RunShellCommand('echo > %s/install.lock' %
                               device_incremental_dir,
                               check_return=True)

    # Concurrency here speeds things up quite a bit, but DeviceUtils hasn't
    # been designed for multi-threading. Enabling only because this is a
    # developer-only tool.
    setup_timer = _Execute(use_concurrency, create_lock_files, restore_cache,
                           check_selinux)

    _Execute(use_concurrency, do_install, do_push_files)

    finalize_timer = _Execute(use_concurrency, release_installer_lock,
                              save_cache)

    logging.info(
        'Took %s seconds (setup=%s, install=%s, libs=%s, dex=%s, finalize=%s)',
        main_timer.GetDelta(), setup_timer.GetDelta(),
        install_timer.GetDelta(), push_native_timer.GetDelta(),
        push_dex_timer.GetDelta(), finalize_timer.GetDelta())
    if show_proguard_warning:
        logging.warning(
            'Target had proguard enabled, but incremental install uses '
            'non-proguarded .dex files. Performance characteristics '
            'may differ.')
示例#7
0
def _RunATestSuite(options):
  """Run a single test suite.

  Helper for Dispatch() to allow stop/restart of the emulator across
  test bundles.  If using the emulator, we start it on entry and stop
  it on exit.

  Args:
    options: options for running the tests.

  Returns:
    0 if successful, number of failing tests otherwise.
  """
  step_name = os.path.basename(options.test_suite).replace('-debug.apk', '')
  buildbot_report.PrintNamedStep(step_name)
  attached_devices = []
  buildbot_emulators = []

  if options.use_emulator:
    for n in range(options.emulator_count):
      t = time_profile.TimeProfile('Emulator launch %d' % n)
      avd_name = None
      if n > 0:
        # Creates a temporary AVD for the extra emulators.
        avd_name = 'run_tests_avd_%d' % n
      buildbot_emulator = emulator.Emulator(avd_name, options.fast_and_loose)
      buildbot_emulator.Launch(kill_all_emulators=n == 0)
      t.Stop()
      buildbot_emulators.append(buildbot_emulator)
      attached_devices.append(buildbot_emulator.device)
    # Wait for all emulators to boot completed.
    map(lambda buildbot_emulator: buildbot_emulator.ConfirmLaunch(True),
        buildbot_emulators)
  elif options.test_device:
    attached_devices = [options.test_device]
  else:
    attached_devices = android_commands.GetAttachedDevices()

  if not attached_devices:
    logging.critical('A device must be attached and online.')
    buildbot_report.PrintError()
    return 1

  # Reset the test port allocation. It's important to do it before starting
  # to dispatch any tests.
  if not ports.ResetTestServerPortAllocation():
    raise Exception('Failed to reset test server port.')

  if options.gtest_filter:
    logging.warning('Sharding is not possible with these configurations.')
    attached_devices = [attached_devices[0]]

  sharder = TestSharder(
      attached_devices,
      options.test_suite,
      options.gtest_filter,
      options.test_arguments,
      options.timeout,
      options.cleanup_test_files,
      options.tool,
      options.log_dump,
      options.fast_and_loose,
      options.build_type,
      options.webkit,
      options.flakiness_dashboard_server)
  test_results = sharder.RunShardedTests()

  for buildbot_emulator in buildbot_emulators:
    buildbot_emulator.Shutdown()

  return len(test_results.failed)
示例#8
0
def Install(device,
            install_json,
            apk=None,
            enable_device_cache=False,
            use_concurrency=True,
            permissions=()):
    """Installs the given incremental apk and all required supporting files.

  Args:
    device: A DeviceUtils instance (to install to).
    install_json: Path to .json file or already parsed .json object.
    apk: An existing ApkHelper instance for the apk (optional).
    enable_device_cache: Whether to enable on-device caching of checksums.
    use_concurrency: Whether to speed things up using multiple threads.
    permissions: A list of the permissions to grant, or None to grant all
                 non-denylisted permissions in the manifest.
  """
    if isinstance(install_json, basestring):
        with open(install_json) as f:
            install_dict = json.load(f)
    else:
        install_dict = install_json

    if install_dict.get('dont_even_try'):
        raise Exception(install_dict['dont_even_try'])

    main_timer = time_profile.TimeProfile()
    install_timer = time_profile.TimeProfile()
    push_native_timer = time_profile.TimeProfile()
    merge_dex_timer = time_profile.TimeProfile()
    push_dex_timer = time_profile.TimeProfile()

    def fix_path(p):
        return os.path.normpath(os.path.join(constants.GetOutDirectory(), p))

    if not apk:
        apk = apk_helper.ToHelper(fix_path(install_dict['apk_path']))
    split_globs = [fix_path(p) for p in install_dict['split_globs']]
    native_libs = [fix_path(p) for p in install_dict['native_libs']]
    dex_files = [fix_path(p) for p in install_dict['dex_files']]
    show_proguard_warning = install_dict.get('show_proguard_warning')

    apk_package = apk.GetPackageName()
    device_incremental_dir = _GetDeviceIncrementalDir(apk_package)
    dex_staging_dir = os.path.join(constants.GetOutDirectory(),
                                   'incremental-install',
                                   install_dict['apk_path'])
    device_dex_dir = posixpath.join(device_incremental_dir, 'dex')

    # Install .apk(s) if any of them have changed.
    def do_install():
        install_timer.Start()
        if split_globs:
            splits = []
            for split_glob in split_globs:
                splits.extend((f for f in glob.glob(split_glob)))
            device.InstallSplitApk(apk,
                                   splits,
                                   allow_downgrade=True,
                                   reinstall=True,
                                   allow_cached_props=True,
                                   permissions=permissions)
        else:
            device.Install(apk,
                           allow_downgrade=True,
                           reinstall=True,
                           permissions=permissions)
        install_timer.Stop(log=False)

    # Push .so and .dex files to the device (if they have changed).
    def do_push_files():
        def do_push_native():
            push_native_timer.Start()
            if native_libs:
                with build_utils.TempDir() as temp_dir:
                    device_lib_dir = posixpath.join(device_incremental_dir,
                                                    'lib')
                    for path in native_libs:
                        # Note: Can't use symlinks as they don't work when
                        # "adb push parent_dir" is used (like we do here).
                        shutil.copy(
                            path, os.path.join(temp_dir,
                                               os.path.basename(path)))
                    device.PushChangedFiles([(temp_dir, device_lib_dir)],
                                            delete_device_stale=True)
            push_native_timer.Stop(log=False)

        def do_merge_dex():
            merge_dex_timer.Start()
            shards = _AllocateDexShards(dex_files)
            build_utils.MakeDirectory(dex_staging_dir)
            _CreateDexFiles(shards, dex_staging_dir, use_concurrency)
            merge_dex_timer.Stop(log=False)

        def do_push_dex():
            push_dex_timer.Start()
            device.PushChangedFiles([(dex_staging_dir, device_dex_dir)],
                                    delete_device_stale=True)
            push_dex_timer.Stop(log=False)

        _Execute(use_concurrency, do_push_native, do_merge_dex)
        do_push_dex()

    def check_device_configured():
        target_sdk_version = int(apk.GetTargetSdkVersion())
        # Beta Q builds apply whitelist to targetSdk=28 as well.
        if target_sdk_version >= 28 and device.build_version_sdk >= 28:
            # In P, there are two settings:
            #  * hidden_api_policy_p_apps
            #  * hidden_api_policy_pre_p_apps
            # In Q, there is just one:
            #  * hidden_api_policy
            if device.build_version_sdk == 28:
                setting_name = 'hidden_api_policy_p_apps'
            else:
                setting_name = 'hidden_api_policy'
            apis_allowed = ''.join(
                device.RunShellCommand(
                    ['settings', 'get', 'global', setting_name],
                    check_return=True))
            if apis_allowed.strip() not in '01':
                msg = """\
Cannot use incremental installs on Android P+ without first enabling access to
non-SDK interfaces (https://developer.android.com/preview/non-sdk-q).

To enable access:
   adb -s {0} shell settings put global {1} 0
To restore back to default:
   adb -s {0} shell settings delete global {1}"""
                raise Exception(msg.format(device.serial, setting_name))

    cache_path = _DeviceCachePath(device)

    def restore_cache():
        if not enable_device_cache:
            return
        if os.path.exists(cache_path):
            logging.info('Using device cache: %s', cache_path)
            with open(cache_path) as f:
                device.LoadCacheData(f.read())
            # Delete the cached file so that any exceptions cause it to be cleared.
            os.unlink(cache_path)
        else:
            logging.info('No device cache present: %s', cache_path)

    def save_cache():
        if not enable_device_cache:
            return
        with open(cache_path, 'w') as f:
            f.write(device.DumpCacheData())
            logging.info('Wrote device cache: %s', cache_path)

    # Create 2 lock files:
    # * install.lock tells the app to pause on start-up (until we release it).
    # * firstrun.lock is used by the app to pause all secondary processes until
    #   the primary process finishes loading the .dex / .so files.
    def create_lock_files():
        # Creates or zeros out lock files.
        cmd = ('D="%s";'
               'mkdir -p $D &&'
               'echo -n >$D/install.lock 2>$D/firstrun.lock')
        device.RunShellCommand(cmd % device_incremental_dir,
                               shell=True,
                               check_return=True)

    # The firstrun.lock is released by the app itself.
    def release_installer_lock():
        device.RunShellCommand('echo > %s/install.lock' %
                               device_incremental_dir,
                               check_return=True,
                               shell=True)

    # Concurrency here speeds things up quite a bit, but DeviceUtils hasn't
    # been designed for multi-threading. Enabling only because this is a
    # developer-only tool.
    setup_timer = _Execute(use_concurrency, create_lock_files, restore_cache,
                           check_device_configured)

    _Execute(use_concurrency, do_install, do_push_files)

    finalize_timer = _Execute(use_concurrency, release_installer_lock,
                              save_cache)

    logging.info(
        'Install of %s took %s seconds (setup=%s, install=%s, lib_push=%s, '
        'dex_merge=%s dex_push=%s, finalize=%s)', os.path.basename(apk.path),
        main_timer.GetDelta(), setup_timer.GetDelta(),
        install_timer.GetDelta(), push_native_timer.GetDelta(),
        merge_dex_timer.GetDelta(), push_dex_timer.GetDelta(),
        finalize_timer.GetDelta())
    if show_proguard_warning:
        logging.warning(
            'Target had proguard enabled, but incremental install uses '
            'non-proguarded .dex files. Performance characteristics '
            'may differ.')
示例#9
0
def Install(device,
            apk,
            split_globs=None,
            lib_dir=None,
            dex_files=None,
            enable_device_cache=True,
            use_concurrency=True):
    """Installs the given incremental apk and all required supporting files.

  Args:
    device: A DeviceUtils instance.
    apk: The path to the apk, or an ApkHelper instance.
    split_globs: Glob patterns for any required apk splits (optional).
    lib_dir: Directory containing the app's native libraries (optional).
    dex_files: List of .dex.jar files that comprise the app's Dalvik code.
    enable_device_cache: Whether to enable on-device caching of checksums.
    use_concurrency: Whether to speed things up using multiple threads.
  """
    main_timer = time_profile.TimeProfile()
    install_timer = time_profile.TimeProfile()
    push_native_timer = time_profile.TimeProfile()
    push_dex_timer = time_profile.TimeProfile()

    apk = apk_helper.ToHelper(apk)
    apk_package = apk.GetPackageName()
    device_incremental_dir = _GetDeviceIncrementalDir(apk_package)

    # Install .apk(s) if any of them have changed.
    def do_install():
        install_timer.Start()
        if split_globs:
            splits = []
            for split_glob in split_globs:
                splits.extend((f for f in glob.glob(split_glob)))
            device.InstallSplitApk(apk,
                                   splits,
                                   reinstall=True,
                                   allow_cached_props=True,
                                   permissions=())
        else:
            device.Install(apk, reinstall=True, permissions=())
        install_timer.Stop(log=False)

    # Push .so and .dex files to the device (if they have changed).
    def do_push_files():
        if lib_dir:
            push_native_timer.Start()
            device_lib_dir = posixpath.join(device_incremental_dir, 'lib')
            device.PushChangedFiles([(lib_dir, device_lib_dir)],
                                    delete_device_stale=True)
            push_native_timer.Stop(log=False)

        if dex_files:
            push_dex_timer.Start()
            # Put all .dex files to be pushed into a temporary directory so that we
            # can use delete_device_stale=True.
            with build_utils.TempDir() as temp_dir:
                device_dex_dir = posixpath.join(device_incremental_dir, 'dex')
                # Ensure no two files have the same name.
                transformed_names = _TransformDexPaths(dex_files)
                for src_path, dest_name in zip(dex_files, transformed_names):
                    shutil.copyfile(src_path,
                                    os.path.join(temp_dir, dest_name))
                device.PushChangedFiles([(temp_dir, device_dex_dir)],
                                        delete_device_stale=True)
            push_dex_timer.Stop(log=False)

    def check_selinux():
        # Samsung started using SELinux before Marshmallow. There may be even more
        # cases where this is required...
        has_selinux = (device.build_version_sdk >= version_codes.MARSHMALLOW
                       or device.GetProp('selinux.policy_version'))
        if has_selinux and apk.HasIsolatedProcesses():
            raise Exception(
                'Cannot use incremental installs on versions of Android '
                'where isoloated processes cannot access the filesystem '
                '(this includes Android M+, and Samsung L+) without '
                'first disabling isoloated processes.\n'
                'To do so, use GN arg:\n'
                '    disable_incremental_isolated_processes=true')

    cache_path = '%s/files-cache.json' % device_incremental_dir

    def restore_cache():
        if not enable_device_cache:
            logging.info('Ignoring device cache')
            return
        # Delete the cached file so that any exceptions cause the next attempt
        # to re-compute md5s.
        cmd = 'P=%s;cat $P 2>/dev/null && rm $P' % cache_path
        lines = device.RunShellCommand(cmd,
                                       check_return=False,
                                       large_output=True)
        if lines:
            device.LoadCacheData(lines[0])
        else:
            logging.info('Device cache not found: %s', cache_path)

    def save_cache():
        cache_data = device.DumpCacheData()
        device.WriteFile(cache_path, cache_data)

    # Create 2 lock files:
    # * install.lock tells the app to pause on start-up (until we release it).
    # * firstrun.lock is used by the app to pause all secondary processes until
    #   the primary process finishes loading the .dex / .so files.
    def create_lock_files():
        # Creates or zeros out lock files.
        cmd = ('D="%s";'
               'mkdir -p $D &&'
               'echo -n >$D/install.lock 2>$D/firstrun.lock')
        device.RunShellCommand(cmd % device_incremental_dir, check_return=True)

    # The firstrun.lock is released by the app itself.
    def release_installer_lock():
        device.RunShellCommand('echo > %s/install.lock' %
                               device_incremental_dir,
                               check_return=True)

    # Concurrency here speeds things up quite a bit, but DeviceUtils hasn't
    # been designed for multi-threading. Enabling only because this is a
    # developer-only tool.
    setup_timer = _Execute(use_concurrency, create_lock_files, restore_cache,
                           check_selinux)

    _Execute(use_concurrency, do_install, do_push_files)

    finalize_timer = _Execute(use_concurrency, release_installer_lock,
                              save_cache)

    logging.info(
        'Took %s seconds (setup=%s, install=%s, libs=%s, dex=%s, finalize=%s)',
        main_timer.GetDelta(), setup_timer.GetDelta(),
        install_timer.GetDelta(), push_native_timer.GetDelta(),
        push_dex_timer.GetDelta(), finalize_timer.GetDelta())
示例#10
0
def Install(device, install_json, apk=None, enable_device_cache=False,
            use_concurrency=True, permissions=()):
  """Installs the given incremental apk and all required supporting files.

  Args:
    device: A DeviceUtils instance (to install to).
    install_json: Path to .json file or already parsed .json object.
    apk: An existing ApkHelper instance for the apk (optional).
    enable_device_cache: Whether to enable on-device caching of checksums.
    use_concurrency: Whether to speed things up using multiple threads.
    permissions: A list of the permissions to grant, or None to grant all
                 non-blacklisted permissions in the manifest.
  """
  if isinstance(install_json, basestring):
    with open(install_json) as f:
      install_dict = json.load(f)
  else:
    install_dict = install_json

  if install_dict.get('dont_even_try'):
    raise Exception(install_dict['dont_even_try'])

  main_timer = time_profile.TimeProfile()
  install_timer = time_profile.TimeProfile()
  push_native_timer = time_profile.TimeProfile()
  push_dex_timer = time_profile.TimeProfile()

  def fix_path(p):
    return os.path.normpath(os.path.join(constants.GetOutDirectory(), p))

  if not apk:
    apk = apk_helper.ToHelper(fix_path(install_dict['apk_path']))
  split_globs = [fix_path(p) for p in install_dict['split_globs']]
  native_libs = [fix_path(p) for p in install_dict['native_libs']]
  dex_files = [fix_path(p) for p in install_dict['dex_files']]
  show_proguard_warning = install_dict.get('show_proguard_warning')

  apk_package = apk.GetPackageName()
  device_incremental_dir = _GetDeviceIncrementalDir(apk_package)

  # Install .apk(s) if any of them have changed.
  def do_install():
    install_timer.Start()
    if split_globs:
      splits = []
      for split_glob in split_globs:
        splits.extend((f for f in glob.glob(split_glob)))
      device.InstallSplitApk(
          apk,
          splits,
          allow_downgrade=True,
          reinstall=True,
          allow_cached_props=True,
          permissions=permissions)
    else:
      device.Install(
          apk, allow_downgrade=True, reinstall=True, permissions=permissions)
    install_timer.Stop(log=False)

  # Push .so and .dex files to the device (if they have changed).
  def do_push_files():
    push_native_timer.Start()
    if native_libs:
      with build_utils.TempDir() as temp_dir:
        device_lib_dir = posixpath.join(device_incremental_dir, 'lib')
        for path in native_libs:
          # Note: Can't use symlinks as they don't work when
          # "adb push parent_dir" is used (like we do here).
          shutil.copy(path, os.path.join(temp_dir, os.path.basename(path)))
        device.PushChangedFiles([(temp_dir, device_lib_dir)],
                                delete_device_stale=True)
    push_native_timer.Stop(log=False)

    push_dex_timer.Start()
    if dex_files:
      # Put all .dex files to be pushed into a temporary directory so that we
      # can use delete_device_stale=True.
      with build_utils.TempDir() as temp_dir:
        device_dex_dir = posixpath.join(device_incremental_dir, 'dex')
        # Ensure no two files have the same name.
        transformed_names = _TransformDexPaths(dex_files)
        for src_path, dest_name in zip(dex_files, transformed_names):
          # Binary targets with no extra classes create .dex.jar without a
          # classes.dex (which Android chokes on).
          if _HasClasses(src_path):
            shutil.copy(src_path, os.path.join(temp_dir, dest_name))
        device.PushChangedFiles([(temp_dir, device_dex_dir)],
                                delete_device_stale=True)
    push_dex_timer.Stop(log=False)

  def check_selinux():
    # Marshmallow has no filesystem access whatsoever. It might be possible to
    # get things working on Lollipop, but attempts so far have failed.
    # http://crbug.com/558818
    has_selinux = device.build_version_sdk >= version_codes.LOLLIPOP
    if has_selinux and apk.HasIsolatedProcesses():
      raise Exception('Cannot use incremental installs on Android L+ without '
                      'first disabling isolated processes.\n'
                      'To do so, use GN arg:\n'
                      '    disable_incremental_isolated_processes=true')

    target_sdk_version = int(apk.GetTargetSdkVersion())
    # Beta Q builds apply whitelist to targetSdk=28 as well.
    if target_sdk_version >= 28 and device.build_version_sdk >= 29:
      apis_allowed = ''.join(
          device.RunShellCommand(
              ['settings', 'get', 'global', 'hidden_api_policy'],
              check_return=True))
      if apis_allowed.strip() not in '01':
        msg = """\
Cannot use incremental installs on Android Q+ without first enabling access to
non-SDK interfaces (https://developer.android.com/preview/non-sdk-q).

To enable access:
   adb -s {0} shell settings put global hidden_api_policy 0
To restore back to default:
   adb -s {0} shell settings delete global hidden_api_policy"""
        raise Exception(msg.format(device.serial))

  cache_path = _DeviceCachePath(device)
  def restore_cache():
    if not enable_device_cache:
      return
    if os.path.exists(cache_path):
      logging.info('Using device cache: %s', cache_path)
      with open(cache_path) as f:
        device.LoadCacheData(f.read())
      # Delete the cached file so that any exceptions cause it to be cleared.
      os.unlink(cache_path)
    else:
      logging.info('No device cache present: %s', cache_path)

  def save_cache():
    if not enable_device_cache:
      return
    with open(cache_path, 'w') as f:
      f.write(device.DumpCacheData())
      logging.info('Wrote device cache: %s', cache_path)

  # Create 2 lock files:
  # * install.lock tells the app to pause on start-up (until we release it).
  # * firstrun.lock is used by the app to pause all secondary processes until
  #   the primary process finishes loading the .dex / .so files.
  def create_lock_files():
    # Creates or zeros out lock files.
    cmd = ('D="%s";'
           'mkdir -p $D &&'
           'echo -n >$D/install.lock 2>$D/firstrun.lock')
    device.RunShellCommand(
        cmd % device_incremental_dir, shell=True, check_return=True)

  # The firstrun.lock is released by the app itself.
  def release_installer_lock():
    device.RunShellCommand('echo > %s/install.lock' % device_incremental_dir,
                           check_return=True, shell=True)

  # Concurrency here speeds things up quite a bit, but DeviceUtils hasn't
  # been designed for multi-threading. Enabling only because this is a
  # developer-only tool.
  setup_timer = _Execute(
      use_concurrency, create_lock_files, restore_cache, check_selinux)

  _Execute(use_concurrency, do_install, do_push_files)

  finalize_timer = _Execute(use_concurrency, release_installer_lock, save_cache)

  logging.info(
      'Install of %s took %s seconds '
      '(setup=%s, install=%s, libs=%s, dex=%s, finalize=%s)',
      os.path.basename(apk.path), main_timer.GetDelta(), setup_timer.GetDelta(),
      install_timer.GetDelta(), push_native_timer.GetDelta(),
      push_dex_timer.GetDelta(), finalize_timer.GetDelta())
  if show_proguard_warning:
    logging.warning('Target had proguard enabled, but incremental install uses '
                    'non-proguarded .dex files. Performance characteristics '
                    'may differ.')
示例#11
0
def main():
  parser = argparse.ArgumentParser()
  parser.add_argument('apk_path',
                      help='The path to the APK to install.')
  parser.add_argument('--split',
                      action='append',
                      dest='splits',
                      help='A glob matching the apk splits. '
                           'Can be specified multiple times.')
  parser.add_argument('--lib-dir',
                      help='Path to native libraries directory.')
  parser.add_argument('--dex-files',
                      help='List of dex files to push.',
                      action='append',
                      default=[])
  parser.add_argument('-d', '--device', dest='device',
                      help='Target device for apk to install on.')
  parser.add_argument('--uninstall',
                      action='store_true',
                      default=False,
                      help='Remove the app and all side-loaded files.')
  parser.add_argument('--output-directory',
                      help='Path to the root build directory.')
  parser.add_argument('--no-threading',
                      action='store_true',
                      default=False,
                      help='Do not install and push concurrently')
  parser.add_argument('-v',
                      '--verbose',
                      dest='verbose_count',
                      default=0,
                      action='count',
                      help='Verbose level (multiple times for more)')

  args = parser.parse_args()

  run_tests_helper.SetLogLevel(args.verbose_count)
  constants.SetBuildType('Debug')
  if args.output_directory:
    constants.SetOutputDirectory(args.output_directory)

  main_timer = time_profile.TimeProfile()
  install_timer = time_profile.TimeProfile()
  push_native_timer = time_profile.TimeProfile()
  push_dex_timer = time_profile.TimeProfile()

  if args.device:
    # Retries are annoying when commands fail for legitimate reasons. Might want
    # to enable them if this is ever used on bots though.
    device = device_utils.DeviceUtils(args.device, default_retries=0)
  else:
    devices = device_utils.DeviceUtils.HealthyDevices(default_retries=0)
    if not devices:
      raise device_errors.NoDevicesError()
    elif len(devices) == 1:
      device = devices[0]
    else:
      all_devices = device_utils.DeviceUtils.parallel(devices)
      msg = ('More than one device available.\n'
             'Use --device=SERIAL to select a device.\n'
             'Available devices:\n')
      descriptions = all_devices.pMap(lambda d: d.build_description).pGet(None)
      for d, desc in zip(devices, descriptions):
        msg += '  %s (%s)\n' % (d, desc)
      raise Exception(msg)

  apk_help = apk_helper.ApkHelper(args.apk_path)
  apk_package = apk_help.GetPackageName()
  device_incremental_dir = '/data/local/tmp/incremental-app-%s' % apk_package

  if args.uninstall:
    device.Uninstall(apk_package)
    device.RunShellCommand(['rm', '-rf', device_incremental_dir],
                           check_return=True)
    logging.info('Uninstall took %s seconds.', main_timer.GetDelta())
    return

  if device.build_version_sdk >= version_codes.MARSHMALLOW:
    if apk_help.HasIsolatedProcesses():
      raise Exception('Cannot use perform incremental installs on Android M+ '
                      'without first disabling isolated processes. Use GN arg: '
                      'disable_incremental_isolated_processes=true to do so.')

  # Install .apk(s) if any of them have changed.
  def do_install():
    install_timer.Start()
    if args.splits:
      splits = []
      for split_glob in args.splits:
        splits.extend((f for f in glob.glob(split_glob)))
      device.InstallSplitApk(args.apk_path, splits, reinstall=True,
                             allow_cached_props=True, permissions=())
    else:
      device.Install(args.apk_path, reinstall=True, permissions=())
    install_timer.Stop(log=False)

  # Push .so and .dex files to the device (if they have changed).
  def do_push_files():
    if args.lib_dir:
      push_native_timer.Start()
      device_lib_dir = posixpath.join(device_incremental_dir, 'lib')
      device.PushChangedFiles([(args.lib_dir, device_lib_dir)],
                              delete_device_stale=True)
      push_native_timer.Stop(log=False)

    if args.dex_files:
      push_dex_timer.Start()
      # Put all .dex files to be pushed into a temporary directory so that we
      # can use delete_device_stale=True.
      with build_utils.TempDir() as temp_dir:
        device_dex_dir = posixpath.join(device_incremental_dir, 'dex')
        # Ensure no two files have the same name.
        transformed_names = _TransformDexPaths(args.dex_files)
        for src_path, dest_name in zip(args.dex_files, transformed_names):
          shutil.copyfile(src_path, os.path.join(temp_dir, dest_name))
        device.PushChangedFiles([(temp_dir, device_dex_dir)],
                                delete_device_stale=True)
      push_dex_timer.Stop(log=False)

  # Create 2 lock files:
  # * install.lock tells the app to pause on start-up (until we release it).
  # * firstrun.lock is used by the app to pause all secondary processes until
  #   the primary process finishes loading the .dex / .so files.
  def create_lock_files():
    # Creates or zeros out lock files.
    cmd = ('D="%s";'
           'mkdir -p $D &&'
           'echo -n >$D/install.lock 2>$D/firstrun.lock')
    device.RunShellCommand(cmd % device_incremental_dir, check_return=True)

  # The firstrun.lock is released by the app itself.
  def release_installer_lock():
    device.RunShellCommand('echo > %s/install.lock' % device_incremental_dir,
                           check_return=True)

  create_lock_files()
  # Concurrency here speeds things up quite a bit, but DeviceUtils hasn't
  # been designed for multi-threading. Enabling only because this is a
  # developer-only tool.
  if args.no_threading:
    do_install()
    do_push_files()
  else:
    reraiser_thread.RunAsync((do_install, do_push_files))
  release_installer_lock()
  logging.info('Took %s seconds (install=%s, libs=%s, dex=%s)',
               main_timer.GetDelta(), install_timer.GetDelta(),
               push_native_timer.GetDelta(), push_dex_timer.GetDelta())