def _initializeApkAttributes(self, args, error_func):
    if args.apk_under_test:
      apk_under_test_path = args.apk_under_test
      if not args.apk_under_test.endswith('.apk'):
        apk_under_test_path = os.path.join(
            constants.GetOutDirectory(), constants.SDK_BUILD_APKS_DIR,
            '%s.apk' % args.apk_under_test)

      if not os.path.exists(apk_under_test_path):
        error_func('Unable to find APK under test: %s' % apk_under_test_path)

      self._apk_under_test = apk_helper.ToHelper(apk_under_test_path)

    if args.test_apk.endswith('.apk'):
      self._suite = os.path.splitext(os.path.basename(args.test_apk))[0]
      self._test_apk = apk_helper.ToHelper(args.test_apk)
    else:
      self._suite = args.test_apk
      self._test_apk = apk_helper.ToHelper(os.path.join(
          constants.GetOutDirectory(), constants.SDK_BUILD_APKS_DIR,
          '%s.apk' % args.test_apk))

    self._apk_under_test_incremental_install_script = (
        args.apk_under_test_incremental_install_script)
    self._test_apk_incremental_install_script = (
        args.test_apk_incremental_install_script)

    if self._test_apk_incremental_install_script:
      assert self._suite.endswith('_incremental')
      self._suite = self._suite[:-len('_incremental')]

    self._test_jar = os.path.join(
        constants.GetOutDirectory(), constants.SDK_BUILD_TEST_JAVALIB_DIR,
        '%s.jar' % self._suite)
    self._test_support_apk = apk_helper.ToHelper(os.path.join(
        constants.GetOutDirectory(), constants.SDK_BUILD_TEST_JAVALIB_DIR,
        '%sSupport.apk' % self._suite))

    if not os.path.exists(self._test_apk.path):
      error_func('Unable to find test APK: %s' % self._test_apk.path)
    if not os.path.exists(self._test_jar):
      error_func('Unable to find test JAR: %s' % self._test_jar)

    self._test_package = self._test_apk.GetPackageName()
    self._test_runner = self._test_apk.GetInstrumentationName()

    self._package_info = None
    if self._apk_under_test:
      package_under_test = self._apk_under_test.GetPackageName()
      for package_info in constants.PACKAGE_INFO.itervalues():
        if package_under_test == package_info.package:
          self._package_info = package_info
    if not self._package_info:
      logging.warning('Unable to find package info for %s', self._test_package)

    for apk in args.additional_apks:
      if not os.path.exists(apk):
        error_func('Unable to find additional APK: %s' % apk)
    self._additional_apks = (
        [apk_helper.ToHelper(x) for x in args.additional_apks])
def _CanPossiblyHandlePath(apk_path):
  if not apk_path:
    return False
  _, ext = os.path.splitext(apk_path)
  if ext.lower() == '.apk':
    return True
  return apk_helper.ToHelper(apk_path).is_bundle
def _CanPossiblyHandlePath(apk_path):
    if not apk_path:
        return False
    try:
        apk_helper.ToHelper(apk_path)
        return True
    except apk_helper.ApkHelperError:
        return False
示例#4
0
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        'json_path', help='The path to the generated incremental apk .json.')
    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_false',
                        default=True,
                        dest='threading',
                        help='Do not install and push concurrently')
    parser.add_argument(
        '--no-cache',
        action='store_false',
        default=True,
        dest='cache',
        help='Do not use cached information about what files are '
        'currently on the target device.')
    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)
    if args.output_directory:
        constants.SetOutputDirectory(args.output_directory)

    devil_chromium.Initialize(output_directory=constants.GetOutDirectory())

    # 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.HealthyDevices(
        device_arg=args.device,
        default_retries=0,
        enable_device_files_cache=True)[0]

    if args.uninstall:
        with open(args.json_path) as f:
            install_dict = json.load(f)
        apk = apk_helper.ToHelper(install_dict['apk_path'])
        Uninstall(device, apk.GetPackageName(), enable_device_cache=args.cache)
    else:
        Install(device,
                args.json_path,
                enable_device_cache=args.cache,
                use_concurrency=args.threading)
示例#5
0
 def testGetSplitsApks(self):
     apk = apk_helper.ToHelper('abc.apks')
     with self.assertCalls(
         (mock.call.tempfile.mkdtemp(),
          '/tmp'),
         (mock.call.devil.android.sdk.bundletool.ExtractApks(
             '/tmp', 'abc.apks', ['arm64-v8a', 'armeabi-v7a'], [('en', 'US')],
             ['android.hardware.wifi', 'android.hardware.nfc'], 500, 28, None)),
         (mock.call.os.listdir('/tmp'), ['base-master.apk', 'foo-master.apk']),
         (mock.call.shutil.rmtree('/tmp')),
     ),\
     apk.GetApkPaths(_MockDeviceUtils()) as apk_paths:
         self.assertEquals(apk_paths,
                           ['/tmp/base-master.apk', '/tmp/foo-master.apk'])
示例#6
0
  def GetWebViewLibraryNameAndPath(self, package_name):
    """Get WebView library name and path on the device."""
    apk_path = self._GetWebViewApkPath(package_name)
    logging.debug('WebView APK path:' + apk_path)
    # TODO(changwan): check if we need support for bundle.
    tmp_apk_path = os.path.join(self.tmp_dir, 'base.apk')
    self.device.adb.Pull(apk_path, tmp_apk_path)
    self.apk_helper = apk_helper.ToHelper(tmp_apk_path)
    metadata = self.apk_helper.GetAllMetadata()
    lib_name = None
    for key, value in metadata:
      if key == 'com.android.webview.WebViewLibrary':
        lib_name = value

    lib_path = os.path.join(apk_path, 'lib', self._GetFormattedArch(), lib_name)
    logging.debug("WebView's library path on the device should be:" + lib_path)
    return lib_name, lib_path
示例#7
0
    def __init__(self,
                 browser_type,
                 finder_options,
                 android_platform,
                 backend_settings,
                 local_apk=None):
        super(PossibleAndroidBrowser,
              self).__init__(browser_type, 'android',
                             backend_settings.supports_tab_control)
        assert browser_type in FindAllBrowserTypes(), (
            'Please add %s to android_browser_finder.FindAllBrowserTypes' %
            browser_type)
        self._platform = android_platform
        self._platform_backend = (android_platform._platform_backend)  # pylint: disable=protected-access
        self._backend_settings = backend_settings
        self._local_apk = local_apk
        self._flag_changer = None
        self._modules_to_install = None

        if self._local_apk is None and finder_options.chrome_root is not None:
            self._local_apk = self._backend_settings.FindLocalApk(
                self._platform_backend.device, finder_options.chrome_root)

        # At this point the local_apk, if any, must exist.
        assert self._local_apk is None or os.path.exists(self._local_apk)

        if self._local_apk and apk_helper.ToHelper(self._local_apk).is_bundle:
            self._modules_to_install = set(finder_options.modules_to_install)

        self._embedder_apk = None
        if self._backend_settings.requires_embedder:
            if finder_options.webview_embedder_apk:
                self._embedder_apk = finder_options.webview_embedder_apk
            else:
                self._embedder_apk = self._backend_settings.FindEmbedderApk(
                    self._local_apk, finder_options.chrome_root)
        elif finder_options.webview_embedder_apk:
            logging.warning(
                'No embedder needed for %s, ignoring --webview-embedder-apk option',
                self._backend_settings.browser_type)

        # At this point the embedder_apk, if any, must exist.
        assert self._embedder_apk is None or os.path.exists(self._embedder_apk)
示例#8
0
 def testGetSplitsBundleScript(self):
     apk = apk_helper.ToHelper('abc_bundle')
     device = _MockDeviceUtils()
     with self.assertCalls(
         (mock.call.tempfile.mkstemp(suffix='.apks'), (0, '/tmp/abc.apks')),
         (mock.call.devil.utils.cmd_helper.GetCmdStatusOutputAndError([
             'abc_bundle', 'build-bundle-apks', '--output-apks', '/tmp/abc.apks'
         ]), (0, '', '')),
         (mock.call.tempfile.mkdtemp(), '/tmp2'),
         (mock.call.devil.android.sdk.bundletool.ExtractApks(
             '/tmp2', '/tmp/abc.apks', ['arm64-v8a', 'armeabi-v7a'],
             [('en', 'US')], ['android.hardware.wifi', 'android.hardware.nfc'],
             500, 28, ['bar'])),
         (mock.call.os.listdir('/tmp2'), ['base-master.apk', 'bar-master.apk']),
         (mock.call.os.path.isfile('/tmp/abc.apks'), True),
         (mock.call.os.remove('/tmp/abc.apks')),
         (mock.call.os.path.isfile('/tmp2'), False),
         (mock.call.os.path.isdir('/tmp2'), True),
         (mock.call.shutil.rmtree('/tmp2')),
     ),\
     apk.GetApkPaths(device, modules=['bar']) as apk_paths:
         self.assertEquals(
             apk_paths, ['/tmp2/base-master.apk', '/tmp2/bar-master.apk'])
示例#9
0
  def _initializeApkAttributes(self, args, error_func):
    if args.apk_under_test:
      apk_under_test_path = args.apk_under_test
      if (not args.apk_under_test.endswith('.apk')
          and not args.apk_under_test.endswith('.apks')):
        apk_under_test_path = os.path.join(
            constants.GetOutDirectory(), constants.SDK_BUILD_APKS_DIR,
            '%s.apk' % args.apk_under_test)

      # TODO(jbudorick): Move the realpath up to the argument parser once
      # APK-by-name is no longer supported.
      apk_under_test_path = os.path.realpath(apk_under_test_path)

      if not os.path.exists(apk_under_test_path):
        error_func('Unable to find APK under test: %s' % apk_under_test_path)

      self._apk_under_test = apk_helper.ToHelper(apk_under_test_path)

    test_apk_path = args.test_apk
    if not os.path.exists(test_apk_path):
      test_apk_path = os.path.join(
          constants.GetOutDirectory(), constants.SDK_BUILD_APKS_DIR,
          '%s.apk' % args.test_apk)
      # TODO(jbudorick): Move the realpath up to the argument parser once
      # APK-by-name is no longer supported.
      test_apk_path = os.path.realpath(test_apk_path)

    if not os.path.exists(test_apk_path):
      error_func('Unable to find test APK: %s' % test_apk_path)

    self._test_apk = apk_helper.ToHelper(test_apk_path)
    self._suite = os.path.splitext(os.path.basename(args.test_apk))[0]

    self._apk_under_test_incremental_install_json = (
        args.apk_under_test_incremental_install_json)
    self._test_apk_incremental_install_json = (
        args.test_apk_incremental_install_json)

    if self._test_apk_incremental_install_json:
      assert self._suite.endswith('_incremental')
      self._suite = self._suite[:-len('_incremental')]

    self._modules = args.modules
    self._fake_modules = args.fake_modules

    self._test_jar = args.test_jar
    self._test_support_apk = apk_helper.ToHelper(os.path.join(
        constants.GetOutDirectory(), constants.SDK_BUILD_TEST_JAVALIB_DIR,
        '%sSupport.apk' % self._suite))

    if not self._test_jar:
      logging.warning('Test jar not specified. Test runner will not have '
                      'Java annotation info available. May not handle test '
                      'timeouts correctly.')
    elif not os.path.exists(self._test_jar):
      error_func('Unable to find test JAR: %s' % self._test_jar)

    self._test_package = self._test_apk.GetPackageName()
    all_instrumentations = self._test_apk.GetAllInstrumentations()
    all_junit3_runner_classes = [
        x for x in all_instrumentations if ('0xffffffff' in x.get(
            'chromium-junit3', ''))]
    all_junit4_runner_classes = [
        x for x in all_instrumentations if ('0xffffffff' not in x.get(
            'chromium-junit3', ''))]

    if len(all_junit3_runner_classes) > 1:
      logging.warning('This test apk has more than one JUnit3 instrumentation')
    if len(all_junit4_runner_classes) > 1:
      logging.warning('This test apk has more than one JUnit4 instrumentation')

    self._junit3_runner_class = (
      all_junit3_runner_classes[0]['android:name']
      if all_junit3_runner_classes else self.test_apk.GetInstrumentationName())

    self._junit4_runner_class = (
      all_junit4_runner_classes[0]['android:name']
      if all_junit4_runner_classes else None)

    if self._junit4_runner_class:
      if self._test_apk_incremental_install_json:
        self._junit4_runner_supports_listing = next(
            (True for x in self._test_apk.GetAllMetadata()
             if 'real-instr' in x[0] and x[1] in _TEST_LIST_JUNIT4_RUNNERS),
            False)
      else:
        self._junit4_runner_supports_listing = (
            self._junit4_runner_class in _TEST_LIST_JUNIT4_RUNNERS)

    self._package_info = None
    if self._apk_under_test:
      package_under_test = self._apk_under_test.GetPackageName()
      for package_info in constants.PACKAGE_INFO.itervalues():
        if package_under_test == package_info.package:
          self._package_info = package_info
          break
    if not self._package_info:
      logging.warning(("Unable to find package info for %s. " +
                       "(This may just mean that the test package is" +
                       "currently being installed.)"),
                       self._test_package)

    for apk in args.additional_apks:
      if not os.path.exists(apk):
        error_func('Unable to find additional APK: %s' % apk)
    self._additional_apks = (
        [apk_helper.ToHelper(x) for x in args.additional_apks])
示例#10
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())
示例#11
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.')
示例#12
0
    def _initializeApkAttributes(self, args, error_func):
        if args.apk_under_test:
            apk_under_test_path = args.apk_under_test
            if not args.apk_under_test.endswith('.apk'):
                apk_under_test_path = os.path.join(
                    constants.GetOutDirectory(), constants.SDK_BUILD_APKS_DIR,
                    '%s.apk' % args.apk_under_test)

            # TODO(jbudorick): Move the realpath up to the argument parser once
            # APK-by-name is no longer supported.
            apk_under_test_path = os.path.realpath(apk_under_test_path)

            if not os.path.exists(apk_under_test_path):
                error_func('Unable to find APK under test: %s' %
                           apk_under_test_path)

            self._apk_under_test = apk_helper.ToHelper(apk_under_test_path)

        if args.test_apk.endswith('.apk'):
            self._suite = os.path.splitext(os.path.basename(args.test_apk))[0]
            test_apk_path = args.test_apk
            self._test_apk = apk_helper.ToHelper(args.test_apk)
        else:
            self._suite = args.test_apk
            test_apk_path = os.path.join(constants.GetOutDirectory(),
                                         constants.SDK_BUILD_APKS_DIR,
                                         '%s.apk' % args.test_apk)

        # TODO(jbudorick): Move the realpath up to the argument parser once
        # APK-by-name is no longer supported.
        test_apk_path = os.path.realpath(test_apk_path)

        if not os.path.exists(test_apk_path):
            error_func('Unable to find test APK: %s' % test_apk_path)

        self._test_apk = apk_helper.ToHelper(test_apk_path)

        self._apk_under_test_incremental_install_script = (
            args.apk_under_test_incremental_install_script)
        self._test_apk_incremental_install_script = (
            args.test_apk_incremental_install_script)

        if self._test_apk_incremental_install_script:
            assert self._suite.endswith('_incremental')
            self._suite = self._suite[:-len('_incremental')]

        self._test_jar = args.test_jar
        self._test_support_apk = apk_helper.ToHelper(
            os.path.join(constants.GetOutDirectory(),
                         constants.SDK_BUILD_TEST_JAVALIB_DIR,
                         '%sSupport.apk' % self._suite))

        if not os.path.exists(self._test_apk.path):
            error_func('Unable to find test APK: %s' % self._test_apk.path)
        if not self._test_jar:
            logging.warning(
                'Test jar not specified. Test runner will not have '
                'Java annotation info available. May not handle test '
                'timeouts correctly.')
        elif not os.path.exists(self._test_jar):
            error_func('Unable to find test JAR: %s' % self._test_jar)

        self._test_package = self._test_apk.GetPackageName()
        all_instrumentations = self._test_apk.GetAllInstrumentations()
        junit3_runners = [
            x for x in all_instrumentations
            if ('true' not in x.get('chromium-junit4', ''))
        ]
        junit4_runners = [
            x for x in all_instrumentations
            if ('true' in x.get('chromium-junit4', ''))
        ]

        if len(junit3_runners) > 1:
            logging.warning(
                'This test apk has more than one JUnit3 instrumentation')
        if len(junit4_runners) > 1:
            logging.warning(
                'This test apk has more than one JUnit4 instrumentation')

        self._test_runner = (junit3_runners[0]['android:name']
                             if junit3_runners else
                             self.test_apk.GetInstrumentationName())
        self._test_runner_junit4 = (junit4_runners[0]['android:name']
                                    if junit4_runners else None)

        self._package_info = None
        if self._apk_under_test:
            package_under_test = self._apk_under_test.GetPackageName()
            for package_info in constants.PACKAGE_INFO.itervalues():
                if package_under_test == package_info.package:
                    self._package_info = package_info
                    break
        if not self._package_info:
            logging.warning('Unable to find package info for %s',
                            self._test_package)

        for apk in args.additional_apks:
            if not os.path.exists(apk):
                error_func('Unable to find additional APK: %s' % apk)
        self._additional_apks = ([
            apk_helper.ToHelper(x) for x in args.additional_apks
        ])
示例#13
0
  def ProcessArgs(self, args):
    devices = device_utils.DeviceUtils.HealthyDevices(
        device_arg=args.devices,
        enable_device_files_cache=bool(args.output_directory),
        default_retries=0)
    self.args = args
    self.devices = devices
    # TODO(agrieve): Device cache should not depend on output directory.
    #     Maybe put int /tmp?
    _LoadDeviceCaches(devices, args.output_directory)
    # Ensure these keys always exist. They are set by wrapper scripts, but not
    # always added when not using wrapper scripts.
    args.__dict__.setdefault('apk_path', None)
    args.__dict__.setdefault('incremental_json', None)

    try:
      if len(devices) > 1:
        if not self.supports_multiple_devices:
          self._parser.error(device_errors.MultipleDevicesError(devices))
        if not args.all and not args.devices:
          self._parser.error(_GenerateMissingAllFlagMessage(devices))

      if self.supports_incremental:
        if args.incremental and args.non_incremental:
          self._parser.error('Must use only one of --incremental and '
                             '--non-incremental')
        elif args.non_incremental:
          if not args.apk_path:
            self._parser.error('Apk has not been built.')
          args.incremental_json = None
        elif args.incremental:
          if not args.incremental_json:
            self._parser.error('Incremental apk has not been built.')
          args.apk_path = None

        if args.apk_path and args.incremental_json:
          self._parser.error('Both incremental and non-incremental apks exist. '
                             'Select using --incremental or --non-incremental')

      if self.needs_apk_path or args.apk_path or args.incremental_json:
        if args.incremental_json:
          with open(args.incremental_json) as f:
            install_dict = json.load(f)
          apk_path = os.path.join(args.output_directory,
                                  install_dict['apk_path'])
          if os.path.exists(apk_path):
            self.install_dict = install_dict
            self.apk_helper = apk_helper.ToHelper(
                os.path.join(args.output_directory,
                             self.install_dict['apk_path']))
        if not self.apk_helper and args.apk_path:
          self.apk_helper = apk_helper.ToHelper(args.apk_path)
        if not self.apk_helper:
          self._parser.error(
              'Neither incremental nor non-incremental apk is built.')

      if self.needs_package_name and not args.package_name:
        if self.apk_helper:
          args.package_name = self.apk_helper.GetPackageName()
        elif self._from_wrapper_script:
          self._parser.error(
              'Neither incremental nor non-incremental apk is built.')
        else:
          self._parser.error('One of --package-name or --apk-path is required.')

      # Save cache now if command will not get a chance to afterwards.
      if self.calls_exec:
        _SaveDeviceCaches(devices, args.output_directory)
    except:
      _SaveDeviceCaches(devices, args.output_directory)
      raise
示例#14
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('--native_lib',
                        dest='native_libs',
                        help='Path to native library (repeatable)',
                        action='append',
                        default=[])
    parser.add_argument('--dex-file',
                        dest='dex_files',
                        help='Path to dex files (repeatable)',
                        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_false',
                        default=True,
                        dest='threading',
                        help='Do not install and push concurrently')
    parser.add_argument(
        '--no-cache',
        action='store_false',
        default=True,
        dest='cache',
        help='Do not use cached information about what files are '
        'currently on the target device.')
    parser.add_argument('--show-proguard-warning',
                        action='store_true',
                        default=False,
                        help='Print a warning about proguard being disabled')
    parser.add_argument('--dont-even-try',
                        help='Prints this message and exits.')
    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)

    devil_chromium.Initialize(output_directory=constants.GetOutDirectory())

    if args.dont_even_try:
        logging.fatal(args.dont_even_try)
        return 1

    # 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.HealthyDevices(
        device_arg=args.device,
        default_retries=0,
        enable_device_files_cache=True)[0]

    apk = apk_helper.ToHelper(args.apk_path)
    if args.uninstall:
        Uninstall(device, apk.GetPackageName(), enable_device_cache=args.cache)
    else:
        Install(device,
                apk,
                split_globs=args.splits,
                native_libs=args.native_libs,
                dex_files=args.dex_files,
                enable_device_cache=args.cache,
                use_concurrency=args.threading,
                show_proguard_warning=args.show_proguard_warning)
示例#15
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.')
示例#16
0
 def testGetSplitsApksWithAdditionalLocalesIncorrectFormat(self):
   apk = apk_helper.ToHelper('abc.apks')
   with self.assertRaises(apk_helper.ApkHelperError):
     apk.GetApkPaths(_MockDeviceUtils(), additional_locales=['es'])
示例#17
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('--native_lib',
                        dest='native_libs',
                        help='Path to native library (repeatable)',
                        action='append',
                        default=[])
    parser.add_argument('--dex-file',
                        dest='dex_files',
                        help='Path to dex files (repeatable)',
                        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_false',
                        default=True,
                        dest='threading',
                        help='Do not install and push concurrently')
    parser.add_argument(
        '--no-cache',
        action='store_false',
        default=True,
        dest='cache',
        help='Do not use cached information about what files are '
        'currently on the target device.')
    parser.add_argument('--show-proguard-warning',
                        action='store_true',
                        default=False,
                        help='Print a warning about proguard being disabled')
    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)

    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,
                                          enable_device_files_cache=True)
    else:
        devices = device_utils.DeviceUtils.HealthyDevices(
            default_retries=0, enable_device_files_cache=True)
        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 = apk_helper.ToHelper(args.apk_path)
    if args.uninstall:
        Uninstall(device, apk.GetPackageName())
    else:
        Install(device,
                apk,
                split_globs=args.splits,
                native_libs=args.native_libs,
                dex_files=args.dex_files,
                enable_device_cache=args.cache,
                use_concurrency=args.threading,
                show_proguard_warning=args.show_proguard_warning)
示例#18
0
 def testToHelperApks(self):
     apk = apk_helper.ToHelper('abc.apks')
     self.assertTrue(isinstance(apk, apk_helper.ApksHelper))
示例#19
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.')
示例#20
0
def Run(output_directory, apk_path, incremental_install_json_path,
        command_line_flags_file):
    constants.SetOutputDirectory(output_directory)

    parser = argparse.ArgumentParser()
    command_parsers = parser.add_subparsers(title='Apk operations',
                                            dest='command')
    subp = command_parsers.add_parser('install', help='Install the apk.')
    _AddCommonOptions(subp)

    subp = command_parsers.add_parser('uninstall', help='Uninstall the apk.')
    _AddCommonOptions(subp)

    subp = command_parsers.add_parser('launch',
                                      help='Launches the apk with the given '
                                      'command-line flags, and optionally the '
                                      'given URL')
    _AddCommonOptions(subp)
    _AddLaunchOptions(subp)
    _AddArgsOptions(subp)

    subp = command_parsers.add_parser('run', help='Install and launch.')
    _AddCommonOptions(subp)
    _AddLaunchOptions(subp)
    _AddArgsOptions(subp)

    subp = command_parsers.add_parser('stop', help='Stop apks on all devices')
    _AddCommonOptions(subp)

    subp = command_parsers.add_parser(
        'clear-data', help='Clear states for the given package')
    _AddCommonOptions(subp)

    subp = command_parsers.add_parser(
        'argv', help='Display and update flags on devices.')
    _AddCommonOptions(subp)
    _AddArgsOptions(subp)

    subp = command_parsers.add_parser('gdb',
                                      help='Run build/android/adb_gdb script.')
    _AddCommonOptions(subp)
    _AddArgsOptions(subp)

    subp = command_parsers.add_parser(
        'logcat', help='Run the shell command "adb logcat".')
    _AddCommonOptions(subp)

    args = parser.parse_args()
    run_tests_helper.SetLogLevel(args.verbose_count)
    command = args.command
    # Enable caching unless executing a helper script (where we won't get a chance
    # to update the cache afterwards).
    use_cache = command not in {'gdb', 'logcat'}

    devil_chromium.Initialize()

    devices = device_utils.DeviceUtils.HealthyDevices(
        device_arg=args.devices,
        enable_device_files_cache=use_cache,
        default_retries=0)
    devices_obj = device_utils.DeviceUtils.parallel(devices)

    if command in {'gdb', 'logcat'} and len(devices) > 1:
        raise device_errors.MultipleDevicesError(devices)
    if command in {'argv', 'stop', 'clear-data'} or len(args.devices) > 0:
        args.all = True
    if len(devices) > 1 and not args.all:
        raise Exception(_GenerateMissingAllFlagMessage(devices, devices_obj))

    apk_name = os.path.basename(apk_path)
    apk_path, incremental_install_json_path = _SelectApk(
        apk_path, incremental_install_json_path, parser, args)
    install_dict = None

    if incremental_install_json_path:
        with open(incremental_install_json_path) as f:
            install_dict = json.load(f)
        apk = apk_helper.ToHelper(
            os.path.join(output_directory, install_dict['apk_path']))
    else:
        apk = apk_helper.ToHelper(apk_path)

    apk_package = apk.GetPackageName()

    if use_cache:
        _LoadDeviceCaches(devices)

    if command == 'install':
        _InstallApk(apk, install_dict, devices_obj)
    elif command == 'uninstall':
        _UninstallApk(install_dict, devices_obj, apk_package)
    elif command == 'launch':
        _LaunchUrl(devices_obj, args.args, command_line_flags_file, args.url,
                   apk)
    elif command == 'run':
        logging.warning('Installing...')
        _InstallApk(apk, install_dict, devices_obj)
        logging.warning('Sending launch intent...')
        _LaunchUrl(devices_obj, args.args, command_line_flags_file, args.url,
                   apk)
    elif command == 'stop':
        devices_obj.ForceStop(apk_package)
    elif command == 'clear-data':
        devices_obj.ClearApplicationState(apk_package)
    elif command == 'argv':
        _ChangeFlags(devices, devices_obj, args.args, command_line_flags_file)
    elif command == 'gdb':
        gdb_script_path = os.path.dirname(__file__) + '/adb_gdb'
        program_name = '--program-name=%s' % os.path.splitext(apk_name)[0]
        package_name = '--package-name=%s' % apk_package
        output_dir = '--output-directory=%s' % output_directory
        adb_path = '--adb=%s' % adb_wrapper.AdbWrapper.GetAdbPath()
        device = '--device=%s' % devices[0].adb.GetDeviceSerial()
        # Use one lib dir per device so that changing between devices does require
        # refetching the device libs.
        libs_dir = '--pull-libs-dir=/tmp/adb-gdb-libs-%s' % (
            devices[0].adb.GetDeviceSerial())
        flags = [
            gdb_script_path, program_name, package_name, output_dir, device,
            adb_path, libs_dir
        ]
        if args.args:
            flags += shlex.split(args.args)
        # Enable verbose output of adb_gdb if it's set for this script.
        if args.verbose_count > 0:
            flags.append('--verbose')
        logging.warning('Running: %s', ' '.join(pipes.quote(f) for f in flags))
        logging.warning('All subsequent output is from adb_gdb script.')
        os.execv(gdb_script_path, flags)
    elif command == 'logcat':
        adb_path = adb_wrapper.AdbWrapper.GetAdbPath()
        flags = [adb_path, '-s', devices[0].adb.GetDeviceSerial(), 'logcat']
        os.execv(adb_path, flags)

    # Save back to the cache.
    if use_cache:
        _SaveDeviceCaches(devices)
示例#21
0
 def testToHelperBundleScript(self):
     apk = apk_helper.ToHelper('abc_bundle')
     self.assertTrue(isinstance(apk, apk_helper.BundleScriptHelper))
示例#22
0
 def testGetSplitsApkModulesException(self):
     apk = apk_helper.ToHelper('abc.apk')
     with self.assertRaises(apk_helper.ApkHelperError):
         apk.GetApkPaths(None, modules=['a'])
示例#23
0
 def testGetSplitsApk(self):
     apk = apk_helper.ToHelper('abc.apk')
     with apk.GetApkPaths(_MockDeviceUtils()) as apk_paths:
         self.assertEquals(apk_paths, ['abc.apk'])
示例#24
0
 def testToHelperSplitException(self):
     with self.assertRaises(apk_helper.ApkHelperError):
         apk_helper.ToSplitHelper(apk_helper.ToHelper('abc.apk'),
                                  ['a.apk', 'b.apk'])