def _InstallCipdPackages(self, packages): for cipd_root, pkgs in self._IterVersionedCipdPackages(packages): logging.info('Installing packages in %s', cipd_root) if not os.path.exists(cipd_root): os.makedirs(cipd_root) ensure_path = os.path.join(cipd_root, '.ensure') with open(ensure_path, 'w') as ensure_file: # Make CIPD ensure that all files are present and correct, # even if it thinks the package is installed. ensure_file.write('$ParanoidMode CheckIntegrity\n\n') for pkg in pkgs: ensure_file.write('%s %s\n' % (pkg.package_name, pkg.version)) logging.info(' %s %s', pkg.package_name, pkg.version) ensure_cmd = [ 'cipd', 'ensure', '-ensure-file', ensure_path, '-root', cipd_root, ] try: for line in cmd_helper.IterCmdOutputLines(ensure_cmd): logging.info(' %s', line) except subprocess.CalledProcessError as e: # avd.py is executed with python2. # pylint: disable=W0707 raise AvdException('Failed to install CIPD packages: %s' % str(e), command=ensure_cmd)
def testIterCmdOutputLines_exitStatusSkipped(self): for num, line in enumerate( cmd_helper.IterCmdOutputLines('seq 10 && false', shell=True), 1): self.assertEquals(num, int(line)) # no exception will be raised because we don't attempt to read past # the end of the output and, thus, the status never gets checked if num == 10: break
def Install(self, packages=_ALL_PACKAGES): """Installs the requested CIPD packages. Returns: None Raises: AvdException on failure to install. """ pkgs_by_dir = {} if packages is _ALL_PACKAGES: packages = [ self._config.avd_package, self._config.emulator_package, self._config.system_image_package, ] for pkg in packages: if not pkg.dest_path in pkgs_by_dir: pkgs_by_dir[pkg.dest_path] = [] pkgs_by_dir[pkg.dest_path].append(pkg) for pkg_dir, pkgs in pkgs_by_dir.iteritems(): logging.info('Installing packages in %s', pkg_dir) cipd_root = os.path.join(constants.DIR_SOURCE_ROOT, pkg_dir) if not os.path.exists(cipd_root): os.makedirs(cipd_root) ensure_path = os.path.join(cipd_root, '.ensure') with open(ensure_path, 'w') as ensure_file: # Make CIPD ensure that all files are present, even if # it thinks the package is installed. ensure_file.write('$ParanoidMode CheckPresence\n\n') for pkg in pkgs: ensure_file.write('%s %s\n' % (pkg.package_name, pkg.version)) logging.info(' %s %s', pkg.package_name, pkg.version) ensure_cmd = [ 'cipd', 'ensure', '-ensure-file', ensure_path, '-root', cipd_root, ] try: for line in cmd_helper.IterCmdOutputLines(ensure_cmd): logging.info(' %s', line) except subprocess.CalledProcessError as e: raise AvdException('Failed to install CIPD package %s: %s' % (pkg.package_name, str(e)), command=ensure_cmd) # The emulator requires that some files are writable. for dirname, _, filenames in os.walk(self._emulator_home): for f in filenames: path = os.path.join(dirname, f) mode = os.lstat(path).st_mode if mode & stat.S_IRUSR: mode = mode | stat.S_IWUSR os.chmod(path, mode)
def _IterRunDeviceAdbCmd(self, args, timeout): """Runs an adb command and returns an iterator over its output lines. Args: args: A list of arguments to adb. timeout: Timeout in seconds. Yields: The output of the command line by line. """ return cmd_helper.IterCmdOutputLines( self._BuildAdbCmd(args, self._device_serial), timeout=timeout)
def IterShell(self, command, timeout): """Runs a shell command and returns an iterator over its output lines. Args: command: A string with the shell command to run. timeout: Timeout in seconds. Yields: The output of the command line by line. """ args = ['shell', command] return cmd_helper.IterCmdOutputLines( self._BuildAdbCmd(args, self._device_serial), timeout=timeout)
def _IterRunDeviceAdbCmd(self, args, iter_timeout, timeout): """Runs an adb command and returns an iterator over its output lines. Args: args: A list of arguments to adb. iter_timeout: Timeout for each iteration in seconds. timeout: Timeout for the entire command in seconds. Yields: The output of the command line by line. """ return cmd_helper.IterCmdOutputLines(self._BuildAdbCmd( args, self._device_serial), iter_timeout=iter_timeout, timeout=timeout, env=self._ADB_ENV)
def DexDump(dexfiles, file_summary=False): """A wrapper around the Android SDK's dexdump tool. Args: dexfiles: The dexfile or list of dex files to dump. file_summary: Display summary information from the file header. (-f) Returns: An iterable over the output lines. """ # TODO(jbudorick): Add support for more options as necessary. if isinstance(dexfiles, basestring): dexfiles = [dexfiles] args = [_DEXDUMP_PATH] + dexfiles if file_summary: args.append('-f') return cmd_helper.IterCmdOutputLines(args)
def Delete(self, avd_name): """Call `avdmanager delete`. Args: avd_name: name of the AVD to delete. """ delete_cmd = [ _DEFAULT_AVDMANAGER_PATH, '-v', 'delete', 'avd', '-n', avd_name, ] try: for line in cmd_helper.IterCmdOutputLines(delete_cmd, env=self._env): logging.info(' %s', line) except subprocess.CalledProcessError as e: raise AvdException('AVD deletion failed: %s' % str(e), command=delete_cmd)
def _InstallCipdPackages(self, packages): pkgs_by_dir = {} if packages is _ALL_PACKAGES: packages = [ self._config.avd_package, self._config.emulator_package, self._config.system_image_package, ] for pkg in packages: if not pkg.dest_path in pkgs_by_dir: pkgs_by_dir[pkg.dest_path] = [] pkgs_by_dir[pkg.dest_path].append(pkg) for pkg_dir, pkgs in pkgs_by_dir.items(): logging.info('Installing packages in %s', pkg_dir) cipd_root = os.path.join(constants.DIR_SOURCE_ROOT, pkg_dir) if not os.path.exists(cipd_root): os.makedirs(cipd_root) ensure_path = os.path.join(cipd_root, '.ensure') with open(ensure_path, 'w') as ensure_file: # Make CIPD ensure that all files are present and correct, # even if it thinks the package is installed. ensure_file.write('$ParanoidMode CheckIntegrity\n\n') for pkg in pkgs: ensure_file.write('%s %s\n' % (pkg.package_name, pkg.version)) logging.info(' %s %s', pkg.package_name, pkg.version) ensure_cmd = [ 'cipd', 'ensure', '-ensure-file', ensure_path, '-root', cipd_root, ] try: for line in cmd_helper.IterCmdOutputLines(ensure_cmd): logging.info(' %s', line) except subprocess.CalledProcessError as e: raise AvdException( 'Failed to install CIPD package %s: %s' % (pkg.package_name, str(e)), command=ensure_cmd)
def _IterRunDeviceAdbCmd(self, args, iter_timeout, timeout, check_error=True): """Runs an adb command and returns an iterator over its output lines. Args: args: A list of arguments to adb. iter_timeout: Timeout for each iteration in seconds. timeout: Timeout for the entire command in seconds. check_error: Check that the command succeeded. This does check the error status of the adb command but DOES NOT check the exit status of shell commands. Yields: The output of the command line by line. """ return cmd_helper.IterCmdOutputLines( self._BuildAdbCmd(args, self._device_serial), iter_timeout=iter_timeout, timeout=timeout, env=self._ADB_ENV, check_status=check_error)
def main(): parser = argparse.ArgumentParser(description=(__doc__)) parser.add_argument( 'mapping_file', help='ProGuard mapping file from build which the stacktrace is from.') parser.add_argument('--stacktrace', help='Stacktrace file to be deobfuscated.') args = parser.parse_args() retrace_path = os.path.join(_THIRD_PARTY_DIR, 'proguard', 'lib', 'retrace.jar') base_args = [ 'java', '-jar', retrace_path, '-regex', _LINE_PARSE_REGEX, args.mapping_file ] if args.stacktrace: subprocess.call(base_args + [args.stacktrace]) else: for line in cmd_helper.IterCmdOutputLines(base_args): print line
def testIterCmdOutputLines_exitStatusFail(self): with self.assertRaises(subprocess.CalledProcessError): for num, line in enumerate( cmd_helper.IterCmdOutputLines('seq 10 && false', shell=True), 1): self.assertEquals(num, int(line))
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)
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)
def testIterCmdOutputLines_exitStatusIgnored(self): for num, line in enumerate( cmd_helper.IterCmdOutputLines('seq 10 && false', shell=True, check_status=False), 1): self.assertEquals(num, int(line))
def Create(self, force=False, snapshot=False, keep=False, cipd_json_output=None): """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) - creates and uploads an instance of the AVD CIPD package - 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. """ logging.info('Installing required packages.') self.Install(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, sdcard=self._config.avd_settings.sdcard.size) 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) avd_dir = os.path.join(android_avd_home, '%s.avd' % self._config.avd_name) config_ini = os.path.join(avd_dir, 'config.ini') with open(root_ini, 'a') as root_ini_file: root_ini_file.write('path.rel=avd/%s.avd\n' % self._config.avd_name) height = (self._config.avd_settings.screen.height or _DEFAULT_SCREEN_HEIGHT) width = (self._config.avd_settings.screen.width or _DEFAULT_SCREEN_WIDTH) density = (self._config.avd_settings.screen.density or _DEFAULT_SCREEN_DENSITY) with open(config_ini, 'a') as config_ini_file: config_ini_contents = _CONFIG_INI_CONTENTS.format( density=density, height=height, width=width) config_ini_file.write(config_ini_contents) # Start & stop the AVD. self._Initialize() instance = _AvdInstance(self._emulator_path, self._config.avd_name, self._emulator_home) instance.Start(read_only=False, snapshot_save=snapshot) device_utils.DeviceUtils(instance.serial).WaitUntilFullyBooted( 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) }, ], } 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, ] if cipd_json_output: cipd_create_cmd.extend([ '-json-output', cipd_json_output, ]) 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)
def testIterCmdOutputLines_success(self): for num, line in enumerate( cmd_helper.IterCmdOutputLines(['seq', '10']), 1): self.assertEquals(num, int(line))
def Create(self, force=False, snapshot=False, keep=False, cipd_json_output=None): """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) - creates and uploads an instance of the AVD CIPD package - 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. """ logging.info('Installing required packages.') self.Install(packages=[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.') root_ini = os.path.join(android_avd_home, '%s.ini' % self._config.avd_name) avd_dir = os.path.join(android_avd_home, '%s.avd' % self._config.avd_name) config_ini = os.path.join(avd_dir, 'config.ini') with open(root_ini, 'a') as root_ini_file: root_ini_file.write('path.rel=avd/%s.avd\n' % self._config.avd_name) with open(config_ini, 'a') as config_ini_file: config_ini_file.write('disk.dataPartition.size=4G\n') # Start & stop the AVD. if snapshot: # TODO(crbug.com/922145): Implement support for snapshotting. raise NotImplementedError('Snapshotting is not supported yet.') 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) }, ], } 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, ] if cipd_json_output: cipd_create_cmd.extend([ '-json-output', cipd_json_output, ]) 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)