예제 #1
0
    def _UpdateConfigs(self):
        """Update various properties in config files after installation.

    AVD config files contain some properties which can be different between AVD
    creation and installation, e.g. hw.sdCard.path, which is an absolute path.
    Update their values so that:
     * Emulator instance can be booted correctly.
     * The snapshot can be loaded successfully.
    """
        config_files = [self._config_ini_path]
        # The file hardware.ini within each snapshot need to be updated as well.
        hw_ini_glob_pattern = os.path.join(self._avd_dir, 'snapshots', '*',
                                           'hardware.ini')
        config_files.extend(glob.glob(hw_ini_glob_pattern))

        properties = {}
        # Update hw.sdCard.path if applicable
        sdcard_path = os.path.join(self._avd_dir, _SDCARD_NAME)
        if os.path.exists(sdcard_path):
            properties['hw.sdCard.path'] = sdcard_path

        for config_file in config_files:
            with ini.update_ini_file(config_file) as config_contents:
                config_contents.update(properties)
예제 #2
0
파일: avd.py 프로젝트: wzis/chromium
  def Create(self,
             force=False,
             snapshot=False,
             keep=False,
             cipd_json_output=None,
             dry_run=False):
    """Create an instance of the AVD CIPD package.

    This method:
     - installs the requisite system image
     - creates the AVD
     - modifies the AVD's ini files to support running chromium tests
       in chromium infrastructure
     - optionally starts & stops the AVD for snapshotting (default no)
     - By default creates and uploads an instance of the AVD CIPD package
       (can be turned off by dry_run flag).
     - optionally deletes the AVD (default yes)

    Args:
      force: bool indicating whether to force create the AVD.
      snapshot: bool indicating whether to snapshot the AVD before creating
        the CIPD package.
      keep: bool indicating whether to keep the AVD after creating
        the CIPD package.
      cipd_json_output: string path to pass to `cipd create` via -json-output.
      dry_run: When set to True, it will skip the CIPD package creation
        after creating the AVD.
    """
    logging.info('Installing required packages.')
    self._InstallCipdPackages(packages=[
        self._config.emulator_package,
        self._config.system_image_package,
    ])

    android_avd_home = os.path.join(self._emulator_home, 'avd')

    if not os.path.exists(android_avd_home):
      os.makedirs(android_avd_home)

    avd_manager = _AvdManagerAgent(
        avd_home=android_avd_home, sdk_root=self._emulator_sdk_root)

    logging.info('Creating AVD.')
    avd_manager.Create(
        avd_name=self._config.avd_name,
        system_image=self._config.system_image_name,
        force=force)

    try:
      logging.info('Modifying AVD configuration.')

      # Clear out any previous configuration or state from this AVD.
      root_ini = os.path.join(android_avd_home,
                              '%s.ini' % self._config.avd_name)
      features_ini = os.path.join(self._emulator_home, 'advancedFeatures.ini')
      avd_dir = os.path.join(android_avd_home, '%s.avd' % self._config.avd_name)
      config_ini = os.path.join(avd_dir, 'config.ini')

      with ini.update_ini_file(root_ini) as root_ini_contents:
        root_ini_contents['path.rel'] = 'avd/%s.avd' % self._config.avd_name

      with ini.update_ini_file(features_ini) as features_ini_contents:
        # features_ini file will not be refreshed by avdmanager during
        # creation. So explicitly clear its content to exclude any leftover
        # from previous creation.
        features_ini_contents.clear()
        features_ini_contents.update(self.avd_settings.advanced_features)

      with ini.update_ini_file(config_ini) as config_ini_contents:
        height = self.avd_settings.screen.height or _DEFAULT_SCREEN_HEIGHT
        width = self.avd_settings.screen.width or _DEFAULT_SCREEN_WIDTH
        density = self.avd_settings.screen.density or _DEFAULT_SCREEN_DENSITY

        config_ini_contents.update({
            'disk.dataPartition.size': '4G',
            'hw.keyboard': 'yes',
            'hw.lcd.density': density,
            'hw.lcd.height': height,
            'hw.lcd.width': width,
        })

        if self.avd_settings.ram_size:
          config_ini_contents['hw.ramSize'] = self.avd_settings.ram_size

      # Start & stop the AVD.
      self._Initialize()
      instance = _AvdInstance(self._emulator_path, self._emulator_home,
                              self._config)
      # Enable debug for snapshot when it is set to True
      debug_tags = 'init,snapshot' if snapshot else None
      instance.Start(
          read_only=False, snapshot_save=snapshot, debug_tags=debug_tags)
      # Android devices with full-disk encryption are encrypted on first boot,
      # and then get decrypted to continue the boot process (See details in
      # https://bit.ly/3agmjcM).
      # Wait for this step to complete since it can take a while for old OSs
      # like M, otherwise the avd may have "Encryption Unsuccessful" error.
      device_utils.DeviceUtils(instance.serial).WaitUntilFullyBooted(
          decrypt=True, timeout=180, retries=0)
      instance.Stop()

      # The multiinstance lock file seems to interfere with the emulator's
      # operation in some circumstances (beyond the obvious -read-only ones),
      # and there seems to be no mechanism by which it gets closed or deleted.
      # See https://bit.ly/2pWQTH7 for context.
      multiInstanceLockFile = os.path.join(avd_dir, 'multiinstance.lock')
      if os.path.exists(multiInstanceLockFile):
        os.unlink(multiInstanceLockFile)

      package_def_content = {
          'package':
          self._config.avd_package.package_name,
          'root':
          self._emulator_home,
          'install_mode':
          'copy',
          'data': [{
              'dir': os.path.relpath(avd_dir, self._emulator_home)
          }, {
              'file': os.path.relpath(root_ini, self._emulator_home)
          }, {
              'file': os.path.relpath(features_ini, self._emulator_home)
          }],
      }

      logging.info('Creating AVD CIPD package.')
      logging.debug('ensure file content: %s',
                    json.dumps(package_def_content, indent=2))

      with tempfile_ext.TemporaryFileName(suffix='.json') as package_def_path:
        with open(package_def_path, 'w') as package_def_file:
          json.dump(package_def_content, package_def_file)

        logging.info('  %s', self._config.avd_package.package_name)
        cipd_create_cmd = [
            'cipd',
            'create',
            '-pkg-def',
            package_def_path,
            '-tag',
            'emulator_version:%s' % self._config.emulator_package.version,
            '-tag',
            'system_image_version:%s' %
            self._config.system_image_package.version,
        ]
        if cipd_json_output:
          cipd_create_cmd.extend([
              '-json-output',
              cipd_json_output,
          ])
        logging.info('running %r%s', cipd_create_cmd,
                     ' (dry_run)' if dry_run else '')
        if not dry_run:
          try:
            for line in cmd_helper.IterCmdOutputLines(cipd_create_cmd):
              logging.info('    %s', line)
          except subprocess.CalledProcessError as e:
            raise AvdException(
                'CIPD package creation failed: %s' % str(e),
                command=cipd_create_cmd)

    finally:
      if not keep:
        logging.info('Deleting AVD.')
        avd_manager.Delete(avd_name=self._config.avd_name)
예제 #3
0
    def Create(self,
               force=False,
               snapshot=False,
               keep=False,
               additional_apks=None,
               privileged_apk_tuples=None,
               cipd_json_output=None,
               dry_run=False):
        """Create an instance of the AVD CIPD package.

    This method:
     - installs the requisite system image
     - creates the AVD
     - modifies the AVD's ini files to support running chromium tests
       in chromium infrastructure
     - optionally starts, installs additional apks and/or privileged apks, and
       stops the AVD for snapshotting (default no)
     - By default creates and uploads an instance of the AVD CIPD package
       (can be turned off by dry_run flag).
     - optionally deletes the AVD (default yes)

    Args:
      force: bool indicating whether to force create the AVD.
      snapshot: bool indicating whether to snapshot the AVD before creating
        the CIPD package.
      keep: bool indicating whether to keep the AVD after creating
        the CIPD package.
      additional_apks: a list of strings contains the paths to the APKs. These
        APKs will be installed after AVD is started.
      privileged_apk_tuples: a list of (apk_path, device_partition) tuples where
        |apk_path| is a string containing the path to the APK, and
        |device_partition| is a string indicating the system image partition on
        device that contains "priv-app" directory, e.g. "/system", "/product".
      cipd_json_output: string path to pass to `cipd create` via -json-output.
      dry_run: When set to True, it will skip the CIPD package creation
        after creating the AVD.
    """
        logging.info('Installing required packages.')
        self._InstallCipdPackages(packages=[
            self._config.emulator_package,
            self._config.system_image_package,
        ])

        android_avd_home = self._avd_home

        if not os.path.exists(android_avd_home):
            os.makedirs(android_avd_home)

        avd_manager = _AvdManagerAgent(avd_home=android_avd_home,
                                       sdk_root=self._emulator_sdk_root)

        logging.info('Creating AVD.')
        avd_manager.Create(avd_name=self._config.avd_name,
                           system_image=self._config.system_image_name,
                           force=force)

        try:
            logging.info('Modifying AVD configuration.')

            # Clear out any previous configuration or state from this AVD.
            root_ini = os.path.join(android_avd_home,
                                    '%s.ini' % self._config.avd_name)
            features_ini = os.path.join(self._emulator_home,
                                        'advancedFeatures.ini')
            avd_dir = self._avd_dir

            with ini.update_ini_file(root_ini) as root_ini_contents:
                root_ini_contents[
                    'path.rel'] = 'avd/%s.avd' % self._config.avd_name

            with ini.update_ini_file(features_ini) as features_ini_contents:
                # features_ini file will not be refreshed by avdmanager during
                # creation. So explicitly clear its content to exclude any leftover
                # from previous creation.
                features_ini_contents.clear()
                features_ini_contents.update(
                    self.avd_settings.advanced_features)

            with ini.update_ini_file(
                    self._config_ini_path) as config_ini_contents:
                height = self.avd_settings.screen.height or _DEFAULT_SCREEN_HEIGHT
                width = self.avd_settings.screen.width or _DEFAULT_SCREEN_WIDTH
                density = self.avd_settings.screen.density or _DEFAULT_SCREEN_DENSITY

                config_ini_contents.update({
                    'disk.dataPartition.size': '4G',
                    'hw.keyboard': 'yes',
                    'hw.lcd.density': density,
                    'hw.lcd.height': height,
                    'hw.lcd.width': width,
                    'hw.mainKeys': 'no',  # Show nav buttons on screen
                })

                if self.avd_settings.ram_size:
                    config_ini_contents[
                        'hw.ramSize'] = self.avd_settings.ram_size

                config_ini_contents['hw.sdCard'] = 'yes'
                if self.avd_settings.sdcard.size:
                    sdcard_path = os.path.join(avd_dir, _SDCARD_NAME)
                    mksdcard_path = os.path.join(
                        os.path.dirname(self._emulator_path), 'mksdcard')
                    cmd_helper.RunCmd([
                        mksdcard_path,
                        self.avd_settings.sdcard.size,
                        sdcard_path,
                    ])
                    config_ini_contents['hw.sdCard.path'] = sdcard_path

            # Start & stop the AVD.
            self._Initialize()
            instance = _AvdInstance(self._emulator_path, self._emulator_home,
                                    self._config)
            # Enable debug for snapshot when it is set to True
            debug_tags = 'init,snapshot' if snapshot else None
            # Installing privileged apks requires modifying the system image.
            writable_system = bool(privileged_apk_tuples)
            instance.Start(ensure_system_settings=False,
                           read_only=False,
                           writable_system=writable_system,
                           gpu_mode=_DEFAULT_GPU_MODE,
                           debug_tags=debug_tags)

            assert instance.device is not None, '`instance.device` not initialized.'
            # Android devices with full-disk encryption are encrypted on first boot,
            # and then get decrypted to continue the boot process (See details in
            # https://bit.ly/3agmjcM).
            # Wait for this step to complete since it can take a while for old OSs
            # like M, otherwise the avd may have "Encryption Unsuccessful" error.
            instance.device.WaitUntilFullyBooted(decrypt=True,
                                                 timeout=180,
                                                 retries=0)

            if additional_apks:
                for additional_apk in additional_apks:
                    instance.device.Install(additional_apk,
                                            allow_downgrade=True,
                                            reinstall=True)

            if privileged_apk_tuples:
                system_app.InstallPrivilegedApps(instance.device,
                                                 privileged_apk_tuples)

            # Always disable the network to prevent built-in system apps from
            # updating themselves, which could take over package manager and
            # cause shell command timeout.
            logging.info('Disabling the network.')
            settings.ConfigureContentSettings(
                instance.device, settings.NETWORK_DISABLED_SETTINGS)

            if snapshot:
                # Reboot so that changes like disabling network can take effect.
                instance.device.Reboot()
                instance.SaveSnapshot()

            instance.Stop()

            # The multiinstance lock file seems to interfere with the emulator's
            # operation in some circumstances (beyond the obvious -read-only ones),
            # and there seems to be no mechanism by which it gets closed or deleted.
            # See https://bit.ly/2pWQTH7 for context.
            multiInstanceLockFile = os.path.join(avd_dir, 'multiinstance.lock')
            if os.path.exists(multiInstanceLockFile):
                os.unlink(multiInstanceLockFile)

            package_def_content = {
                'package':
                self._config.avd_package.package_name,
                'root':
                self._emulator_home,
                'install_mode':
                'copy',
                'data': [{
                    'dir': os.path.relpath(avd_dir, self._emulator_home)
                }, {
                    'file': os.path.relpath(root_ini, self._emulator_home)
                }, {
                    'file':
                    os.path.relpath(features_ini, self._emulator_home)
                }],
            }

            logging.info('Creating AVD CIPD package.')
            logging.debug('ensure file content: %s',
                          json.dumps(package_def_content, indent=2))

            with tempfile_ext.TemporaryFileName(
                    suffix='.json') as package_def_path:
                with open(package_def_path, 'w') as package_def_file:
                    json.dump(package_def_content, package_def_file)

                logging.info('  %s', self._config.avd_package.package_name)
                cipd_create_cmd = [
                    'cipd',
                    'create',
                    '-pkg-def',
                    package_def_path,
                    '-tag',
                    'emulator_version:%s' %
                    self._config.emulator_package.version,
                    '-tag',
                    'system_image_version:%s' %
                    self._config.system_image_package.version,
                ]
                if cipd_json_output:
                    cipd_create_cmd.extend([
                        '-json-output',
                        cipd_json_output,
                    ])
                logging.info('running %r%s', cipd_create_cmd,
                             ' (dry_run)' if dry_run else '')
                if not dry_run:
                    try:
                        for line in cmd_helper.IterCmdOutputLines(
                                cipd_create_cmd):
                            logging.info('    %s', line)
                    except subprocess.CalledProcessError as e:
                        # avd.py is executed with python2.
                        # pylint: disable=W0707
                        raise AvdException('CIPD package creation failed: %s' %
                                           str(e),
                                           command=cipd_create_cmd)

        finally:
            if not keep:
                logging.info('Deleting AVD.')
                avd_manager.Delete(avd_name=self._config.avd_name)