tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

avd.py (49984B)


      1 # Copyright 2019 The Chromium Authors
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import collections
      6 import contextlib
      7 import glob
      8 import json
      9 import logging
     10 import os
     11 import socket
     12 import stat
     13 import subprocess
     14 import threading
     15 import time
     16 
     17 from google.protobuf import text_format  # pylint: disable=import-error
     18 
     19 from devil import base_error
     20 from devil.android import apk_helper
     21 from devil.android import device_utils
     22 from devil.android.sdk import adb_wrapper
     23 from devil.android.sdk import version_codes
     24 from devil.android.tools import system_app
     25 from devil.utils import cmd_helper
     26 from devil.utils import timeout_retry
     27 from py_utils import tempfile_ext
     28 from pylib import constants
     29 from pylib.local.emulator import ini
     30 from pylib.local.emulator.proto import avd_pb2
     31 
     32 from lib.proto import exception_recorder
     33 from lib.proto import measures
     34 
     35 # A common root directory to store the CIPD packages for creating or starting
     36 # the emulator instance, e.g. emulator binary, system images, AVDs.
     37 COMMON_CIPD_ROOT = os.path.join(constants.DIR_SOURCE_ROOT, '.android_emulator')
     38 
     39 # Packages that are needed for runtime.
     40 _PACKAGES_RUNTIME = object()
     41 # Packages that are needed during AVD creation.
     42 _PACKAGES_CREATION = object()
     43 # All the packages that could exist in the AVD config file.
     44 _PACKAGES_ALL = object()
     45 
     46 # These files are used as backing files for corresponding qcow2 images.
     47 _BACKING_FILES = ('system.img', 'vendor.img')
     48 
     49 _DEFAULT_AVDMANAGER_PATH = os.path.join(constants.ANDROID_SDK_ROOT,
     50                                        'cmdline-tools', 'latest', 'bin',
     51                                        'avdmanager')
     52 
     53 # Additional debug tags we would like to have when "--debug-tags" is passed.
     54 _DEFAULT_DEBUG_TAGS = (
     55    'time',  # Show the timestamp in the logs.
     56    '-asconnector',  # Keep reporting connection error so disable.
     57    # The following are disabled because they flood the logs.
     58    '-qemud',
     59    '-gps',
     60    '-sensors',
     61 )
     62 
     63 # Default to a 480dp mdpi screen (a relatively large phone).
     64 # See https://developer.android.com/training/multiscreen/screensizes
     65 # and https://developer.android.com/training/multiscreen/screendensities
     66 # for more information.
     67 _DEFAULT_SCREEN_DENSITY = 160
     68 _DEFAULT_SCREEN_HEIGHT = 960
     69 _DEFAULT_SCREEN_WIDTH = 480
     70 
     71 # Default to swiftshader_indirect since it works for most cases.
     72 _DEFAULT_GPU_MODE = 'swiftshader_indirect'
     73 
     74 # The snapshot name to load/save when writable_system=False.
     75 # This is the default name used by the emulator binary.
     76 _DEFAULT_SNAPSHOT_NAME = 'default_boot'
     77 
     78 # crbug.com/1275767: Set long press timeout to 1000ms to reduce the flakiness
     79 # caused by click being incorrectly interpreted as longclick.
     80 _LONG_PRESS_TIMEOUT = '1000'
     81 
     82 # The snapshot name to load/save when writable_system=True
     83 _SYSTEM_SNAPSHOT_NAME = 'boot_with_system'
     84 
     85 _SDCARD_NAME = 'cr-sdcard.img'
     86 
     87 
     88 class AvdException(Exception):
     89  """Raised when this module has a problem interacting with an AVD."""
     90 
     91  def __init__(self, summary, command=None, stdout=None, stderr=None):
     92    message_parts = [summary]
     93    if command:
     94      message_parts.append('  command: %s' % ' '.join(command))
     95    if stdout:
     96      message_parts.append('  stdout:')
     97      message_parts.extend('    %s' % line for line in stdout.splitlines())
     98    if stderr:
     99      message_parts.append('  stderr:')
    100      message_parts.extend('    %s' % line for line in stderr.splitlines())
    101 
    102    # avd.py is executed with python2.
    103    # pylint: disable=R1725
    104    super(AvdException, self).__init__('\n'.join(message_parts))
    105 
    106 
    107 class AvdStartException(AvdException):
    108  """Exception for AVD start failures."""
    109 
    110 
    111 def _Load(avd_proto_path):
    112  """Loads an Avd proto from a textpb file at the given path.
    113 
    114  Should not be called outside of this module.
    115 
    116  Args:
    117    avd_proto_path: path to a textpb file containing an Avd message.
    118  """
    119  with open(avd_proto_path) as avd_proto_file:
    120    # python generated codes are simplified since Protobuf v3.20.0 and cause
    121    # pylint error: https://github.com/protocolbuffers/protobuf/issues/9730
    122    # pylint: disable=no-member
    123    return text_format.Merge(avd_proto_file.read(), avd_pb2.Avd())
    124 
    125 
    126 def _FindMinSdkFile(apk_dir, min_sdk):
    127  """Finds the apk file associated with the min_sdk file.
    128 
    129  This reads a version.json file located in the apk_dir to find an apk file
    130  that is closest without going over the min_sdk.
    131 
    132  Args:
    133    apk_dir: The directory to look for apk files.
    134    min_sdk: The minimum sdk version supported by the device.
    135 
    136  Returns:
    137    The path to the file that suits the minSdkFile or None
    138  """
    139  json_file = os.path.join(apk_dir, 'version.json')
    140  if not os.path.exists(json_file):
    141    logging.error('Json version file not found: %s', json_file)
    142    return None
    143 
    144  min_sdk_found = None
    145  curr_min_sdk_version = 0
    146  with open(json_file) as f:
    147    data = json.loads(f.read())
    148    # Finds the entry that is closest to min_sdk without going over.
    149    for entry in data:
    150      if (entry['min_sdk'] > curr_min_sdk_version
    151          and entry['min_sdk'] <= min_sdk):
    152        min_sdk_found = entry
    153        curr_min_sdk_version = entry['min_sdk']
    154 
    155    if not min_sdk_found:
    156      logging.error('No suitable apk file found that suits the minimum sdk %d.',
    157                    min_sdk)
    158      return None
    159 
    160    logging.info('Found apk file for mininum sdk %d: %r with version %r',
    161                 min_sdk, min_sdk_found['file_name'],
    162                 min_sdk_found['version_name'])
    163    return os.path.join(apk_dir, min_sdk_found['file_name'])
    164 
    165 
    166 def ProcessDebugTags(debug_tags_str, default_debug_tags=None):
    167  """Given a string of debug tags, process them and return as a list."""
    168  tags = set(debug_tags_str.split(','))
    169  if default_debug_tags:
    170    tags |= set(default_debug_tags)
    171  # The disabled tags, i.e. tags with prefix '-', should come later otherwise
    172  # the logging will not work properly.
    173  return sorted(tags, key=lambda t: (t.startswith('-'), t))
    174 
    175 
    176 class _AvdManagerAgent:
    177  """Private utility for interacting with avdmanager."""
    178 
    179  def __init__(self, avd_home, sdk_root):
    180    """Create an _AvdManagerAgent.
    181 
    182    Args:
    183      avd_home: path to ANDROID_AVD_HOME directory.
    184        Typically something like /path/to/dir/.android/avd
    185      sdk_root: path to SDK root directory.
    186    """
    187    self._avd_home = avd_home
    188    self._sdk_root = sdk_root
    189 
    190    self._env = dict(os.environ)
    191 
    192    # The avdmanager from cmdline-tools would look two levels
    193    # up from toolsdir to find the SDK root.
    194    # Pass avdmanager a fake directory under the directory in which
    195    # we install the system images s.t. avdmanager can find the
    196    # system images.
    197    fake_tools_dir = os.path.join(self._sdk_root, 'non-existent-tools',
    198                                  'non-existent-version')
    199    self._env.update({
    200        'ANDROID_AVD_HOME':
    201        self._avd_home,
    202        'AVDMANAGER_OPTS':
    203        '-Dcom.android.sdkmanager.toolsdir=%s' % fake_tools_dir,
    204        'JAVA_HOME':
    205        constants.JAVA_HOME,
    206    })
    207 
    208  def Create(self, avd_name, system_image, force=False):
    209    """Call `avdmanager create`.
    210 
    211    Args:
    212      avd_name: name of the AVD to create.
    213      system_image: system image to use for the AVD.
    214      force: whether to force creation, overwriting any existing
    215        AVD with the same name.
    216    """
    217    create_cmd = [
    218        _DEFAULT_AVDMANAGER_PATH,
    219        '-v',
    220        'create',
    221        'avd',
    222        '-n',
    223        avd_name,
    224        '-k',
    225        system_image,
    226    ]
    227    if force:
    228      create_cmd += ['--force']
    229 
    230    create_proc = cmd_helper.Popen(create_cmd,
    231                                   stdin=subprocess.PIPE,
    232                                   stdout=subprocess.PIPE,
    233                                   stderr=subprocess.PIPE,
    234                                   env=self._env)
    235    output, error = create_proc.communicate(input='\n')
    236    if create_proc.returncode != 0:
    237      raise AvdException('AVD creation failed',
    238                         command=create_cmd,
    239                         stdout=output,
    240                         stderr=error)
    241 
    242    for line in output.splitlines():
    243      logging.info('  %s', line)
    244 
    245  def Delete(self, avd_name):
    246    """Call `avdmanager delete`.
    247 
    248    Args:
    249      avd_name: name of the AVD to delete.
    250    """
    251    delete_cmd = [
    252        _DEFAULT_AVDMANAGER_PATH,
    253        '-v',
    254        'delete',
    255        'avd',
    256        '-n',
    257        avd_name,
    258    ]
    259    try:
    260      for line in cmd_helper.IterCmdOutputLines(delete_cmd, env=self._env):
    261        logging.info('  %s', line)
    262    except subprocess.CalledProcessError as e:
    263      # avd.py is executed with python2.
    264      # pylint: disable=W0707
    265      raise AvdException('AVD deletion failed: %s' % str(e), command=delete_cmd)
    266 
    267  def List(self):
    268    """List existing AVDs by the name."""
    269    list_cmd = [
    270        _DEFAULT_AVDMANAGER_PATH,
    271        '-v',
    272        'list',
    273        'avd',
    274        '-c',
    275    ]
    276    output = cmd_helper.GetCmdOutput(list_cmd, env=self._env)
    277    return output.splitlines()
    278 
    279  def IsAvailable(self, avd_name):
    280    """Check if an AVD exists or not."""
    281    return avd_name in self.List()
    282 
    283 
    284 class AvdConfig:
    285  """Represents a particular AVD configuration.
    286 
    287  This class supports creation, installation, and execution of an AVD
    288  from a given Avd proto message, as defined in
    289  //build/android/pylib/local/emulator/proto/avd.proto.
    290  """
    291 
    292  def __init__(self, avd_proto_path):
    293    """Create an AvdConfig object.
    294 
    295    Args:
    296      avd_proto_path: path to a textpb file containing an Avd message.
    297    """
    298    self.avd_proto_path = avd_proto_path
    299    self.avd_proto_name = os.path.splitext(os.path.basename(avd_proto_path))[0]
    300    self._config = _Load(avd_proto_path)
    301 
    302    self._initialized = False
    303    self._initializer_lock = threading.Lock()
    304 
    305  @property
    306  def emulator_home(self):
    307    """User-specific emulator configuration directory.
    308 
    309    It corresponds to the environment variable $ANDROID_EMULATOR_HOME.
    310    Configs like advancedFeatures.ini are expected to be under this dir.
    311    """
    312    return os.path.join(COMMON_CIPD_ROOT,
    313                        self.GetDestPath(self._config.avd_package))
    314 
    315  @property
    316  def emulator_sdk_root(self):
    317    """The path to the SDK installation directory.
    318 
    319    It corresponds to the environment variable $ANDROID_HOME.
    320 
    321    To be a valid sdk root, it requires to have the subdirecotries "platforms"
    322    and "platform-tools". See http://bit.ly/2YAkyFE for context.
    323 
    324    Also, it is expected to have subdirecotries "emulator" and "system-images".
    325    """
    326    emulator_sdk_root = os.path.join(
    327        COMMON_CIPD_ROOT, self.GetDestPath(self._config.emulator_package))
    328    # Ensure this is a valid sdk root.
    329    required_dirs = [
    330        os.path.join(emulator_sdk_root, 'platforms'),
    331        os.path.join(emulator_sdk_root, 'platform-tools'),
    332    ]
    333    for d in required_dirs:
    334      if not os.path.exists(d):
    335        os.makedirs(d)
    336 
    337    return emulator_sdk_root
    338 
    339  @property
    340  def emulator_path(self):
    341    """The path to the emulator binary."""
    342    return os.path.join(self.emulator_sdk_root, 'emulator', 'emulator')
    343 
    344  @property
    345  def crashreport_path(self):
    346    """The path to the crashreport binary."""
    347    return os.path.join(self.emulator_sdk_root, 'emulator', 'crashreport')
    348 
    349  @property
    350  def qemu_img_path(self):
    351    """The path to the qemu-img binary.
    352 
    353    This is used to rebase the paths in qcow2 images.
    354    """
    355    return os.path.join(self.emulator_sdk_root, 'emulator', 'qemu-img')
    356 
    357  @property
    358  def mksdcard_path(self):
    359    """The path to the mksdcard binary.
    360 
    361    This is used to create a sdcard image.
    362    """
    363    return os.path.join(self.emulator_sdk_root, 'emulator', 'mksdcard')
    364 
    365  @property
    366  def avd_settings(self):
    367    """The AvdSettings in the avd proto file.
    368 
    369    This defines how to configure the AVD at creation.
    370    """
    371    return self._config.avd_settings
    372 
    373  @property
    374  def avd_variants(self):
    375    """Get the AvdVairants in the avd proto file as a map.
    376 
    377    An AvdVariant can include additional AvdSettings to apply to the AVD.
    378    """
    379    return self._config.avd_variants
    380 
    381  @property
    382  def avd_launch_settings(self):
    383    """The AvdLaunchSettings in the avd proto file.
    384 
    385    This defines AVD setting during launch time.
    386    """
    387    return self._config.avd_launch_settings
    388 
    389  @property
    390  def avd_name(self):
    391    """The name of the AVD to create or use."""
    392    return self._config.avd_name
    393 
    394  @property
    395  def avd_home(self):
    396    """The path that contains the files of one or multiple AVDs."""
    397    avd_home = os.path.join(self.emulator_home, 'avd')
    398    if not os.path.exists(avd_home):
    399      os.makedirs(avd_home)
    400 
    401    return avd_home
    402 
    403  @property
    404  def _avd_dir(self):
    405    """The path that contains the files of the given AVD."""
    406    return os.path.join(self.avd_home, '%s.avd' % self.avd_name)
    407 
    408  @property
    409  def _system_image_dir(self):
    410    """The path of the directory that directly contains the system images.
    411 
    412    For example, if the system_image_name is
    413    "system-images;android-33;google_apis;x86_64"
    414 
    415    The _system_image_dir will be:
    416    <COMMON_CIPD_ROOT>/<dest_path>/system-images/android-33/google_apis/x86_64
    417 
    418    This is used to rebase the paths in qcow2 images.
    419    """
    420    return os.path.join(COMMON_CIPD_ROOT,
    421                        self.GetDestPath(self._config.system_image_package),
    422                        *self._config.system_image_name.split(';'))
    423 
    424  @property
    425  def _root_ini_path(self):
    426    """The <avd_name>.ini file of the given AVD."""
    427    return os.path.join(self.avd_home, '%s.ini' % self.avd_name)
    428 
    429  @property
    430  def _config_ini_path(self):
    431    """The config.ini file under _avd_dir."""
    432    return os.path.join(self._avd_dir, 'config.ini')
    433 
    434  @property
    435  def _features_ini_path(self):
    436    return os.path.join(self.emulator_home, 'advancedFeatures.ini')
    437 
    438  @property
    439  def xdg_config_dir(self):
    440    """The base directory to store qt config file.
    441 
    442    This dir should be added to the env variable $XDG_CONFIG_DIRS so that
    443    _qt_config_path can take effect. See https://bit.ly/3HIQRZ3 for context.
    444    """
    445    config_dir = os.path.join(self.emulator_home, '.config')
    446    if not os.path.exists(config_dir):
    447      os.makedirs(config_dir)
    448 
    449    return config_dir
    450 
    451  @property
    452  def _qt_config_path(self):
    453    """The qt config file for emulator."""
    454    qt_config_dir = os.path.join(self.xdg_config_dir,
    455                                 'Android Open Source Project')
    456    if not os.path.exists(qt_config_dir):
    457      os.makedirs(qt_config_dir)
    458 
    459    return os.path.join(qt_config_dir, 'Emulator.conf')
    460 
    461  def GetMetadata(self):
    462    """Return a dict containing metadata of this avd config.
    463 
    464    Including avd proto path, avd name, avd variant names, and etc.
    465    """
    466    metadata = {
    467        'avd_proto_path': self.avd_proto_path,
    468        'is_available': self.IsAvailable(),
    469    }
    470    avd_variant_keys = sorted(self.avd_variants.keys())
    471    if avd_variant_keys:
    472      metadata['avd_variants'] = avd_variant_keys
    473 
    474    return metadata
    475 
    476  def GetDestPath(self, cipd_pkg):
    477    """Get the "dest_path" of a given CIPDPackage message.
    478 
    479    Fall back to "self.avd_proto_name" if "dest_path" is empty.
    480    """
    481    return cipd_pkg.dest_path or self.avd_proto_name
    482 
    483  def HasSnapshot(self, snapshot_name):
    484    """Check if a given snapshot exists or not."""
    485    snapshot_path = os.path.join(self._avd_dir, 'snapshots', snapshot_name)
    486    return os.path.exists(snapshot_path)
    487 
    488  def Create(self,
    489             avd_variant_name=None,
    490             force=False,
    491             snapshot=False,
    492             keep=False,
    493             additional_apks=None,
    494             privileged_apk_tuples=None,
    495             cipd_json_output=None,
    496             dry_run=False):
    497    """Create an instance of the AVD CIPD package.
    498 
    499    This method:
    500     - installs the requisite system image
    501     - creates the AVD
    502     - modifies the AVD's ini files to support running chromium tests
    503       in chromium infrastructure
    504     - optionally starts, installs additional apks and/or privileged apks, and
    505       stops the AVD for snapshotting (default no)
    506     - By default creates and uploads an instance of the AVD CIPD package
    507       (can be turned off by dry_run flag).
    508     - optionally deletes the AVD (default yes)
    509 
    510    Args:
    511      avd_variant_name: The name of the AvdVariant to use. Extra avd settings
    512        from the variant will be applied during creation.
    513      force: bool indicating whether to force create the AVD.
    514      snapshot: bool indicating whether to snapshot the AVD before creating
    515        the CIPD package.
    516      keep: bool indicating whether to keep the AVD after creating
    517        the CIPD package.
    518      additional_apks: a list of strings contains the paths to the APKs. These
    519        APKs will be installed after AVD is started.
    520      privileged_apk_tuples: a list of (apk_path, device_partition) tuples where
    521        |apk_path| is a string containing the path to the APK, and
    522        |device_partition| is a string indicating the system image partition on
    523        device that contains "priv-app" directory, e.g. "/system", "/product".
    524      cipd_json_output: string path to pass to `cipd create` via -json-output.
    525      dry_run: When set to True, it will skip the CIPD package creation
    526        after creating the AVD.
    527    """
    528    avd_settings = self.GetAvdSettings(avd_variant_name)
    529    logging.info('avd_settings: %r', avd_settings)
    530 
    531    logging.info('Installing required packages.')
    532    self._InstallCipdPackages(_PACKAGES_CREATION)
    533 
    534    avd_manager = _AvdManagerAgent(avd_home=self.avd_home,
    535                                   sdk_root=self.emulator_sdk_root)
    536 
    537    logging.info('Creating AVD.')
    538    avd_manager.Create(avd_name=self.avd_name,
    539                       system_image=self._config.system_image_name,
    540                       force=force)
    541 
    542    try:
    543      logging.info('Modifying AVD configuration.')
    544 
    545      # Clear out any previous configuration or state from this AVD.
    546      with ini.update_ini_file(self._root_ini_path) as r_ini_contents:
    547        r_ini_contents['path.rel'] = 'avd/%s.avd' % self.avd_name
    548 
    549      with ini.update_ini_file(self._features_ini_path) as f_ini_contents:
    550        # features_ini file will not be refreshed by avdmanager during
    551        # creation. So explicitly clear its content to exclude any leftover
    552        # from previous creation.
    553        f_ini_contents.clear()
    554        f_ini_contents.update(avd_settings.advanced_features)
    555 
    556      self._UpdateAvdConfigFile(self._config_ini_path, avd_settings)
    557 
    558      if not additional_apks:
    559        additional_apks = []
    560      for pkg in self._config.additional_apk:
    561        apk_dir = os.path.join(COMMON_CIPD_ROOT, self.GetDestPath(pkg))
    562        apk_file = _FindMinSdkFile(apk_dir, self._config.min_sdk)
    563        # Some of these files come from chrome internal, so may not be
    564        # available to non-internal permissioned users.
    565        if os.path.exists(apk_file):
    566          logging.info('Adding additional apk for install: %s', apk_file)
    567          additional_apks.append(apk_file)
    568 
    569      if not privileged_apk_tuples:
    570        privileged_apk_tuples = []
    571      for pkg in self._config.privileged_apk:
    572        apk_dir = os.path.join(COMMON_CIPD_ROOT, self.GetDestPath(pkg))
    573        apk_file = _FindMinSdkFile(apk_dir, self._config.min_sdk)
    574        # Some of these files come from chrome internal, so may not be
    575        # available to non-internal permissioned users.
    576        if os.path.exists(apk_file):
    577          logging.info('Adding privilege apk for install: %s', apk_file)
    578          privileged_apk_tuples.append(
    579              (apk_file, self._config.install_privileged_apk_partition))
    580 
    581      # Start & stop the AVD.
    582      self._Initialize()
    583      instance = _AvdInstance(self)
    584      # Enable debug for snapshot when it is set to True
    585      debug_tags = 'time,init,snapshot' if snapshot else None
    586      # Installing privileged apks requires modifying the system
    587      # image.
    588      writable_system = bool(privileged_apk_tuples)
    589      gpu_mode = self.avd_launch_settings.gpu_mode or _DEFAULT_GPU_MODE
    590      instance.Start(
    591          ensure_system_settings=False,
    592          read_only=False,
    593          writable_system=writable_system,
    594          gpu_mode=gpu_mode,
    595          debug_tags=debug_tags,
    596      )
    597 
    598      assert instance.device is not None, '`instance.device` not initialized.'
    599      # Android devices with full-disk encryption are encrypted on first boot,
    600      # and then get decrypted to continue the boot process (See details in
    601      # https://bit.ly/3agmjcM).
    602      # Wait for this step to complete since it can take a while for old OSs
    603      # like M, otherwise the avd may have "Encryption Unsuccessful" error.
    604      instance.device.WaitUntilFullyBooted(decrypt=True, timeout=360, retries=0)
    605      logging.info('The build fingerprint of the system is %r',
    606                   instance.device.build_fingerprint)
    607 
    608      if additional_apks:
    609        for apk in additional_apks:
    610          instance.device.Install(apk, allow_downgrade=True, reinstall=True)
    611          package_name = apk_helper.GetPackageName(apk)
    612          package_version = instance.device.GetApplicationVersion(package_name)
    613          logging.info('The version for package %r on the device is %r',
    614                       package_name, package_version)
    615 
    616      if privileged_apk_tuples:
    617        system_app.InstallPrivilegedApps(instance.device, privileged_apk_tuples)
    618        for apk, _ in privileged_apk_tuples:
    619          package_name = apk_helper.GetPackageName(apk)
    620          package_version = instance.device.GetApplicationVersion(package_name)
    621          logging.info('The version for package %r on the device is %r',
    622                       package_name, package_version)
    623 
    624      # Skip Marshmallow as svc commands fail on this version.
    625      if instance.device.build_version_sdk != 23:
    626        # Always disable the network to prevent built-in system apps from
    627        # updating themselves, which could take over package manager and
    628        # cause shell command timeout.
    629        # Use svc as this also works on the images with build type "user", and
    630        # does not require a reboot or broadcast compared to setting the
    631        # airplane_mode_on in "settings/global".
    632        logging.info('Disabling the network.')
    633        instance.device.RunShellCommand(['svc', 'wifi', 'disable'],
    634                                        as_root=True,
    635                                        check_return=True)
    636        # Certain system image like tablet does not have data service
    637        # So don't check return status here.
    638        instance.device.RunShellCommand(['svc', 'data', 'disable'],
    639                                        as_root=True,
    640                                        check_return=False)
    641 
    642      if snapshot:
    643        logging.info('Wait additional 60 secs before saving snapshot for AVD')
    644        time.sleep(60)
    645        instance.SaveSnapshot()
    646 
    647      instance.Stop()
    648 
    649      # The multiinstance lock file seems to interfere with the emulator's
    650      # operation in some circumstances (beyond the obvious -read-only ones),
    651      # and there seems to be no mechanism by which it gets closed or deleted.
    652      # See https://bit.ly/2pWQTH7 for context.
    653      multiInstanceLockFile = os.path.join(self._avd_dir, 'multiinstance.lock')
    654      if os.path.exists(multiInstanceLockFile):
    655        os.unlink(multiInstanceLockFile)
    656 
    657      package_def_content = {
    658          'package':
    659          self._config.avd_package.package_name,
    660          'root':
    661          self.emulator_home,
    662          'install_mode':
    663          'copy',
    664          'data': [{
    665              'dir': os.path.relpath(self._avd_dir, self.emulator_home)
    666          }, {
    667              'file':
    668              os.path.relpath(self._root_ini_path, self.emulator_home)
    669          }, {
    670              'file':
    671              os.path.relpath(self._features_ini_path, self.emulator_home)
    672          }],
    673      }
    674 
    675      logging.info('Creating AVD CIPD package.')
    676      logging.info('ensure file content: %s',
    677                   json.dumps(package_def_content, indent=2))
    678 
    679      with tempfile_ext.TemporaryFileName(suffix='.json') as package_def_path:
    680        with open(package_def_path, 'w') as package_def_file:
    681          json.dump(package_def_content, package_def_file)
    682 
    683        logging.info('  %s', self._config.avd_package.package_name)
    684        cipd_create_cmd = [
    685            'cipd',
    686            'create',
    687            '-pkg-def',
    688            package_def_path,
    689            '-tag',
    690            'emulator_version:%s' % self._config.emulator_package.version,
    691            '-tag',
    692            'system_image_version:%s' %
    693            self._config.system_image_package.version,
    694        ]
    695        if cipd_json_output:
    696          cipd_create_cmd.extend([
    697              '-json-output',
    698              cipd_json_output,
    699          ])
    700        logging.info('running %r%s', cipd_create_cmd,
    701                     ' (dry_run)' if dry_run else '')
    702        if not dry_run:
    703          try:
    704            for line in cmd_helper.IterCmdOutputLines(cipd_create_cmd):
    705              logging.info('    %s', line)
    706          except subprocess.CalledProcessError as e:
    707            # avd.py is executed with python2.
    708            # pylint: disable=W0707
    709            raise AvdException('CIPD package creation failed: %s' % str(e),
    710                               command=cipd_create_cmd)
    711 
    712    finally:
    713      if not keep:
    714        logging.info('Deleting AVD.')
    715        avd_manager.Delete(avd_name=self.avd_name)
    716 
    717  def GetAvdSettings(self, avd_variant_name=None):
    718    # python generated codes are simplified since Protobuf v3.20.0 and cause
    719    # pylint error: https://github.com/protocolbuffers/protobuf/issues/9730
    720    # pylint: disable=no-member
    721    avd_settings = avd_pb2.AvdSettings()
    722    avd_settings.MergeFrom(self.avd_settings)
    723 
    724    if self.avd_variants:
    725      if avd_variant_name is None:
    726        raise AvdException('Avd variant not set for the avd config.')
    727      if avd_variant_name not in self.avd_variants:
    728        raise AvdException(
    729            'Avd variant %r not found in avd config. Must be one of %r' %
    730            (avd_variant_name, list(self.avd_variants.keys())))
    731 
    732      avd_settings.MergeFrom(self.avd_variants[avd_variant_name])
    733    elif avd_variant_name is not None:
    734      raise AvdException('The avd config has no avd variants.')
    735 
    736    return avd_settings
    737 
    738  def _UpdateAvdConfigFile(self, config_file_path, avd_settings):
    739    config_contents = {
    740        'disk.dataPartition.size': '4G',
    741        'hw.keyboard': 'yes',
    742        'hw.mainKeys': 'no',  # Show nav buttons on screen
    743        'hw.sdCard': 'yes',
    744    }
    745    # Update avd_properties first so that they won't override settings
    746    # like screen and ram_size
    747    config_contents.update(avd_settings.avd_properties)
    748 
    749    height = avd_settings.screen.height or _DEFAULT_SCREEN_HEIGHT
    750    width = avd_settings.screen.width or _DEFAULT_SCREEN_WIDTH
    751    density = avd_settings.screen.density or _DEFAULT_SCREEN_DENSITY
    752    config_contents.update({
    753        'hw.lcd.density': density,
    754        'hw.lcd.height': height,
    755        'hw.lcd.width': width,
    756    })
    757 
    758    if avd_settings.ram_size:
    759      config_contents['hw.ramSize'] = avd_settings.ram_size
    760 
    761    if avd_settings.sdcard.size:
    762      sdcard_path = os.path.join(self._avd_dir, _SDCARD_NAME)
    763      cmd_helper.RunCmd([
    764          self.mksdcard_path,
    765          avd_settings.sdcard.size,
    766          sdcard_path,
    767      ])
    768      config_contents['hw.sdCard.path'] = sdcard_path
    769 
    770    with ini.update_ini_file(config_file_path) as config_ini_contents:
    771      config_ini_contents.update(config_contents)
    772 
    773  def IsAvailable(self):
    774    """Returns whether emulator is up-to-date."""
    775    if not os.path.exists(self._config_ini_path):
    776      return False
    777 
    778    # Skip when no version exists to prevent "IsAvailable()" returning False
    779    # for emualtors set up using Create() (rather than Install()).
    780    for cipd_root, pkgs in self._IterCipdPackages(_PACKAGES_RUNTIME,
    781                                                  check_version=False):
    782      stdout = subprocess.run(['cipd', 'installed', '--root', cipd_root],
    783                              capture_output=True,
    784                              check=False,
    785                              encoding='utf8').stdout
    786      # Output looks like:
    787      # Packages:
    788      #   name1:version1
    789      #   name2:version2
    790      installed = [l.strip().split(':', 1) for l in stdout.splitlines()[1:]]
    791 
    792      if any([p.package_name, p.version] not in installed for p in pkgs):
    793        return False
    794    return True
    795 
    796  def Uninstall(self):
    797    """Uninstall all the artifacts associated with the given config.
    798 
    799    Artifacts includes:
    800     - CIPD packages specified in the avd config.
    801     - The local AVD created by `Create`, if present.
    802 
    803    """
    804    # Delete any existing local AVD. This must occur before deleting CIPD
    805    # packages because a AVD needs system image to be recognized by avdmanager.
    806    avd_manager = _AvdManagerAgent(avd_home=self.avd_home,
    807                                   sdk_root=self.emulator_sdk_root)
    808    if avd_manager.IsAvailable(self.avd_name):
    809      logging.info('Deleting local AVD %s', self.avd_name)
    810      avd_manager.Delete(self.avd_name)
    811 
    812    # Delete installed CIPD packages.
    813    for cipd_root, _ in self._IterCipdPackages(_PACKAGES_ALL,
    814                                               check_version=False):
    815      logging.info('Uninstalling packages in %s', cipd_root)
    816      if not os.path.exists(cipd_root):
    817        continue
    818      # Create an empty ensure file to removed any installed CIPD packages.
    819      ensure_path = os.path.join(cipd_root, '.ensure')
    820      with open(ensure_path, 'w') as ensure_file:
    821        ensure_file.write('$ParanoidMode CheckIntegrity\n\n')
    822      ensure_cmd = [
    823          'cipd',
    824          'ensure',
    825          '-ensure-file',
    826          ensure_path,
    827          '-root',
    828          cipd_root,
    829      ]
    830      try:
    831        for line in cmd_helper.IterCmdOutputLines(ensure_cmd):
    832          logging.info('    %s', line)
    833      except subprocess.CalledProcessError as e:
    834        # avd.py is executed with python2.
    835        # pylint: disable=W0707
    836        raise AvdException('Failed to uninstall CIPD packages: %s' % str(e),
    837                           command=ensure_cmd)
    838 
    839  def Install(self):
    840    """Installs the requested CIPD packages and prepares them for use.
    841 
    842    This includes making files writeable and revising some of the
    843    emulator's internal config files.
    844 
    845    Returns: None
    846    Raises: AvdException on failure to install.
    847    """
    848    with measures.time_consumption('emulator', 'install', 'cipd_packages'):
    849      self._InstallCipdPackages(_PACKAGES_RUNTIME)
    850    self._MakeWriteable()
    851    self._UpdateConfigs()
    852    self._RebaseQcow2Images()
    853 
    854  def _RebaseQcow2Images(self):
    855    """Rebase the paths in qcow2 images.
    856 
    857    qcow2 files may exists in avd directory which have hard-coded paths to the
    858    backing files, e.g., system.img, vendor.img. Such paths need to be rebased
    859    if the avd is moved to a different directory in order to boot successfully.
    860    """
    861    for f in _BACKING_FILES:
    862      qcow2_image_path = os.path.join(self._avd_dir, '%s.qcow2' % f)
    863      if not os.path.exists(qcow2_image_path):
    864        continue
    865      backing_file_path = os.path.join(self._system_image_dir, f)
    866      logging.info('Rebasing the qcow2 image %r with the backing file %r',
    867                   qcow2_image_path, backing_file_path)
    868      cmd_helper.RunCmd([
    869          self.qemu_img_path,
    870          'rebase',
    871          '-u',
    872          '-f',
    873          'qcow2',
    874          '-b',
    875          # The path to backing file must be relative to the qcow2 image.
    876          os.path.relpath(backing_file_path, os.path.dirname(qcow2_image_path)),
    877          qcow2_image_path,
    878      ])
    879 
    880  def _ListPackages(self, packages):
    881    if packages is _PACKAGES_RUNTIME:
    882      packages = [
    883          self._config.avd_package,
    884          self._config.emulator_package,
    885          self._config.system_image_package,
    886      ]
    887    elif packages is _PACKAGES_CREATION:
    888      packages = [
    889          self._config.emulator_package,
    890          self._config.system_image_package,
    891          *self._config.privileged_apk,
    892          *self._config.additional_apk,
    893      ]
    894    elif packages is _PACKAGES_ALL:
    895      packages = [
    896          self._config.avd_package,
    897          self._config.emulator_package,
    898          self._config.system_image_package,
    899          *self._config.privileged_apk,
    900          *self._config.additional_apk,
    901      ]
    902    return packages
    903 
    904  def _IterCipdPackages(self, packages, check_version=True):
    905    """Iterate a list of CIPD packages by their CIPD roots.
    906 
    907    Args:
    908      packages: a list of packages from an AVD config.
    909      check_version: If set, raise Exception when a package has no version.
    910    """
    911    pkgs_by_dir = collections.defaultdict(list)
    912    for pkg in self._ListPackages(packages):
    913      if pkg.version:
    914        pkgs_by_dir[self.GetDestPath(pkg)].append(pkg)
    915      elif check_version:
    916        raise AvdException('Expecting a version for the package %s' %
    917                           pkg.package_name)
    918 
    919    for pkg_dir, pkgs in pkgs_by_dir.items():
    920      cipd_root = os.path.join(COMMON_CIPD_ROOT, pkg_dir)
    921      yield cipd_root, pkgs
    922 
    923  def _InstallCipdPackages(self, packages, check_version=True):
    924    for cipd_root, pkgs in self._IterCipdPackages(packages,
    925                                                  check_version=check_version):
    926      logging.info('Installing packages in %s', cipd_root)
    927      if not os.path.exists(cipd_root):
    928        os.makedirs(cipd_root)
    929      ensure_path = os.path.join(cipd_root, '.ensure')
    930      with open(ensure_path, 'w') as ensure_file:
    931        # Make CIPD ensure that all files are present and correct,
    932        # even if it thinks the package is installed.
    933        ensure_file.write('$ParanoidMode CheckIntegrity\n\n')
    934        for pkg in pkgs:
    935          ensure_file.write('%s %s\n' % (pkg.package_name, pkg.version))
    936          logging.info('  %s %s', pkg.package_name, pkg.version)
    937      ensure_cmd = [
    938          'cipd',
    939          'ensure',
    940          '-ensure-file',
    941          ensure_path,
    942          '-root',
    943          cipd_root,
    944      ]
    945      try:
    946        for line in cmd_helper.IterCmdOutputLines(ensure_cmd):
    947          logging.info('    %s', line)
    948      except subprocess.CalledProcessError as e:
    949        exception_recorder.register(e)
    950        # pylint: disable=W0707
    951        raise AvdException('Failed to install CIPD packages: %s' % str(e),
    952                           command=ensure_cmd)
    953 
    954  def _MakeWriteable(self):
    955    # The emulator requires that some files are writable.
    956    for dirname, _, filenames in os.walk(self.emulator_home):
    957      for f in filenames:
    958        path = os.path.join(dirname, f)
    959        mode = os.lstat(path).st_mode
    960        if mode & stat.S_IRUSR:
    961          mode = mode | stat.S_IWUSR
    962        os.chmod(path, mode)
    963 
    964  def _UpdateConfigs(self):
    965    """Update various properties in config files after installation.
    966 
    967    AVD config files contain some properties which can be different between AVD
    968    creation and installation, e.g. hw.sdCard.path, which is an absolute path.
    969    Update their values so that:
    970     * Emulator instance can be booted correctly.
    971     * The snapshot can be loaded successfully.
    972    """
    973    logging.info('Updating AVD configurations.')
    974    # Update the absolute avd path in root_ini file
    975    with ini.update_ini_file(self._root_ini_path) as r_ini_contents:
    976      r_ini_contents['path'] = self._avd_dir
    977 
    978    # Update hardware settings.
    979    config_paths = [self._config_ini_path]
    980    # The file hardware.ini within each snapshot need to be updated as well.
    981    hw_ini_glob_pattern = os.path.join(self._avd_dir, 'snapshots', '*',
    982                                       'hardware.ini')
    983    config_paths.extend(glob.glob(hw_ini_glob_pattern))
    984 
    985    properties = {}
    986    # Update hw.sdCard.path if applicable
    987    sdcard_path = os.path.join(self._avd_dir, _SDCARD_NAME)
    988    if os.path.exists(sdcard_path):
    989      properties['hw.sdCard.path'] = sdcard_path
    990 
    991    for config_path in config_paths:
    992      with ini.update_ini_file(config_path) as config_contents:
    993        config_contents.update(properties)
    994 
    995    # Create qt config file to disable certain warnings when launched in window.
    996    with ini.update_ini_file(self._qt_config_path) as config_contents:
    997      # Disable nested virtualization warning.
    998      config_contents['General'] = {'showNestedWarning': 'false'}
    999      # Disable adb warning.
   1000      config_contents['set'] = {'autoFindAdb': 'false'}
   1001 
   1002  def _Initialize(self):
   1003    if self._initialized:
   1004      return
   1005 
   1006    with self._initializer_lock:
   1007      if self._initialized:
   1008        return
   1009 
   1010      # Emulator start-up looks for the adb daemon. Make sure it's running.
   1011      adb_wrapper.AdbWrapper.StartServer()
   1012 
   1013      # Emulator start-up requires a valid sdk root.
   1014      assert self.emulator_sdk_root
   1015 
   1016  def CreateInstance(self, output_manager=None):
   1017    """Creates an AVD instance without starting it.
   1018 
   1019    Returns:
   1020      An _AvdInstance.
   1021    """
   1022    self._Initialize()
   1023    return _AvdInstance(self, output_manager=output_manager)
   1024 
   1025  def StartInstance(self):
   1026    """Starts an AVD instance.
   1027 
   1028    Returns:
   1029      An _AvdInstance.
   1030    """
   1031    instance = self.CreateInstance()
   1032    instance.Start()
   1033    return instance
   1034 
   1035 
   1036 class _AvdInstance:
   1037  """Represents a single running instance of an AVD.
   1038 
   1039  This class should only be created directly by AvdConfig.StartInstance,
   1040  but its other methods can be freely called.
   1041  """
   1042 
   1043  def __init__(self, avd_config, output_manager=None):
   1044    """Create an _AvdInstance object.
   1045 
   1046    Args:
   1047      avd_config: an AvdConfig instance.
   1048      output_manager: a pylib.base.output_manager.OutputManager instance.
   1049    """
   1050    self._avd_config = avd_config
   1051    self._avd_name = avd_config.avd_name
   1052    self._emulator_home = avd_config.emulator_home
   1053    self._emulator_path = avd_config.emulator_path
   1054    self._crashreport_path = avd_config.crashreport_path
   1055    self._emulator_proc = None
   1056    self._emulator_serial = None
   1057    self._emulator_device = None
   1058 
   1059    self._output_manager = output_manager
   1060    self._output_file = None
   1061 
   1062    self._writable_system = False
   1063    self._debug_tags = None
   1064 
   1065  def __str__(self):
   1066    return '%s|%s' % (self._avd_name, (self._emulator_serial or id(self)))
   1067 
   1068  def Start(self,
   1069            ensure_system_settings=True,
   1070            read_only=True,
   1071            window=False,
   1072            writable_system=False,
   1073            gpu_mode=None,
   1074            wipe_data=False,
   1075            debug_tags=None,
   1076            disk_size=None,
   1077            enable_network=False,
   1078            # TODO(crbug.com/364943269): Remove after clean all the references.
   1079            require_fast_start=False,  # pylint: disable=unused-argument
   1080            retries=0):
   1081    """Starts the emulator running an instance of the given AVD.
   1082 
   1083    Note when ensure_system_settings is True, the program will wait until the
   1084    emulator is fully booted, and then update system settings.
   1085    """
   1086    is_slow_start = False
   1087    # Force to load system snapshot if detected.
   1088    if self.HasSystemSnapshot():
   1089      if not writable_system:
   1090        logging.info('System snapshot found. Set "writable_system=True" '
   1091                     'to load it properly.')
   1092        writable_system = True
   1093      if read_only:
   1094        logging.info('System snapshot found. Set "read_only=False" '
   1095                     'to load it properly.')
   1096        read_only = False
   1097    elif writable_system:
   1098      is_slow_start = True
   1099      logging.warning('Emulator will be slow to start, as '
   1100                      '"writable_system=True" but system snapshot not found.')
   1101 
   1102    self._writable_system = writable_system
   1103 
   1104    with tempfile_ext.TemporaryFileName() as socket_path, (contextlib.closing(
   1105        socket.socket(socket.AF_UNIX))) as sock:
   1106      sock.bind(socket_path)
   1107      emulator_cmd = [
   1108          self._emulator_path,
   1109          '-avd',
   1110          self._avd_name,
   1111          '-report-console',
   1112          'unix:%s' % socket_path,
   1113          '-no-boot-anim',
   1114          # Explicitly prevent emulator from auto-saving to snapshot on exit.
   1115          '-no-snapshot-save',
   1116          # Explicitly set the snapshot name for auto-load
   1117          '-snapshot',
   1118          self.GetSnapshotName(),
   1119      ]
   1120 
   1121      avd_type = self._avd_name.split('_')[1]
   1122      logging.info('Emulator Type: %s', avd_type)
   1123 
   1124      if avd_type in ('car', '32', '34', '35'):
   1125        logging.info('Emulator will start slow')
   1126        is_slow_start = True
   1127 
   1128      if wipe_data:
   1129        emulator_cmd.append('-wipe-data')
   1130      if disk_size:
   1131        emulator_cmd.extend(['-partition-size', str(disk_size)])
   1132 
   1133      if read_only:
   1134        emulator_cmd.append('-read-only')
   1135      if writable_system:
   1136        emulator_cmd.append('-writable-system')
   1137      # Note when "--gpu-mode" is set to "host":
   1138      #  * It needs a valid DISPLAY env, even if "--emulator-window" is false.
   1139      #    Otherwise it may throw errors like "Failed to initialize backend
   1140      #    EGL display". See the code in https://bit.ly/3ruiMlB as an example
   1141      #    to setup the DISPLAY env with xvfb.
   1142      #  * It will not work under remote sessions like chrome remote desktop.
   1143      if not gpu_mode:
   1144        gpu_mode = (self._avd_config.avd_launch_settings.gpu_mode
   1145                    or _DEFAULT_GPU_MODE)
   1146      emulator_cmd.extend(['-gpu', gpu_mode])
   1147      if debug_tags:
   1148        self._debug_tags = ProcessDebugTags(
   1149            debug_tags, default_debug_tags=_DEFAULT_DEBUG_TAGS)
   1150        emulator_cmd.extend(['-debug', ','.join(self._debug_tags)])
   1151        if 'kernel' in self._debug_tags or 'all' in self._debug_tags:
   1152          # TODO(crbug.com/40885864): newer API levels need "-virtio-console"
   1153          # as well to print kernel log.
   1154          emulator_cmd.append('-show-kernel')
   1155 
   1156      emulator_env = {
   1157          # kill as early as possible when emulator hang.
   1158          'ANDROID_EMULATOR_WAIT_TIME_BEFORE_KILL': '1',
   1159          # Sets the emulator configuration directory
   1160          'ANDROID_EMULATOR_HOME': self._emulator_home,
   1161          # emulator tools like crashreport need $USER info to locate data.
   1162          'USER': os.environ.get('USER'),
   1163      }
   1164      if 'DISPLAY' in os.environ:
   1165        emulator_env['DISPLAY'] = os.environ.get('DISPLAY')
   1166      if window:
   1167        if 'DISPLAY' not in emulator_env:
   1168          raise AvdException('Emulator failed to start: DISPLAY not defined')
   1169      else:
   1170        emulator_cmd.append('-no-window')
   1171 
   1172      # Need this for the qt config file to take effect.
   1173      xdg_config_dirs = [self._avd_config.xdg_config_dir]
   1174      if 'XDG_CONFIG_DIRS' in os.environ:
   1175        xdg_config_dirs.append(os.environ.get('XDG_CONFIG_DIRS'))
   1176      emulator_env['XDG_CONFIG_DIRS'] = ':'.join(xdg_config_dirs)
   1177 
   1178      sock.listen(1)
   1179 
   1180      logging.info('Starting emulator...')
   1181      logging.info(
   1182          '  With environments: %s',
   1183          ' '.join(['%s=%s' % (k, v) for k, v in emulator_env.items()]))
   1184      logging.info('  With commands: %s', ' '.join(emulator_cmd))
   1185 
   1186      # Enable the emulator log when debug_tags is set.
   1187      if self._debug_tags:
   1188        # Write to an ArchivedFile if output manager is set, otherwise stdout.
   1189        if self._output_manager:
   1190          self._output_file = self._output_manager.CreateArchivedFile(
   1191              'emulator_%s' % time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()),
   1192              'emulator')
   1193      else:
   1194        self._output_file = open('/dev/null', 'w')
   1195      self._emulator_proc = cmd_helper.Popen(emulator_cmd,
   1196                                             stdout=self._output_file,
   1197                                             stderr=self._output_file,
   1198                                             env=emulator_env)
   1199 
   1200      # Waits for the emulator to report its serial as requested via
   1201      # -report-console. See http://bit.ly/2lK3L18 for more.
   1202      def listen_for_serial(s):
   1203        logging.info('Waiting for connection from emulator.')
   1204        with contextlib.closing(s.accept()[0]) as conn:
   1205          val = conn.recv(1024)
   1206          return 'emulator-%d' % int(val)
   1207 
   1208      try:
   1209        with measures.time_consumption('emulator', 'start',
   1210                                       'listen_for_serial'):
   1211          self._emulator_serial = timeout_retry.Run(
   1212              listen_for_serial,
   1213              timeout=300 if is_slow_start else 60,
   1214              retries=retries,
   1215              args=[sock])
   1216          logging.info('%s started', self._emulator_serial)
   1217      except base_error.BaseError as e:
   1218        self.Stop(force=True)
   1219        raise AvdStartException(str(e)) from e
   1220 
   1221    try:
   1222      # Set the system settings in "Start" here instead of setting in "Create"
   1223      # because "Create" is used during AVD creation, and we want to avoid extra
   1224      # turn-around on rolling AVD.
   1225      if ensure_system_settings:
   1226        assert self.device is not None, '`instance.device` not initialized.'
   1227        logging.info('Waiting for device to be fully booted.')
   1228        self.device.WaitUntilFullyBooted(timeout=360 if is_slow_start else 90,
   1229                                         retries=retries)
   1230        logging.info('Device fully booted, verifying system settings.')
   1231        _EnsureSystemSettings(self.device)
   1232 
   1233      if enable_network:
   1234        _EnableNetwork(self.device)
   1235    except base_error.BaseError as e:
   1236      self.UploadCrashreport()
   1237      raise AvdStartException(str(e)) from e
   1238 
   1239  def Stop(self, force=False):
   1240    """Stops the emulator process.
   1241 
   1242    When "force" is True, we will call "terminate" on the emulator process,
   1243    which is recommended when emulator is not responding to adb commands.
   1244    """
   1245    # Close output file first in case emulator process killing goes wrong.
   1246    if self._output_file:
   1247      if self._debug_tags:
   1248        if self._output_manager:
   1249          self._output_manager.ArchiveArchivedFile(self._output_file,
   1250                                                   delete=True)
   1251          link = self._output_file.Link()
   1252          if link:
   1253            logging.critical('Emulator logs saved to %s', link)
   1254      else:
   1255        self._output_file.close()
   1256      self._output_file = None
   1257 
   1258    if self._emulator_proc:
   1259      if self._emulator_proc.poll() is None:
   1260        if force or not self.device:
   1261          self._emulator_proc.terminate()
   1262        else:
   1263          self.device.adb.Emu('kill')
   1264        self._emulator_proc.wait()
   1265      self._emulator_proc = None
   1266      self._emulator_serial = None
   1267      self._emulator_device = None
   1268 
   1269  def UploadCrashreport(self):
   1270    # The crashreport binary only exists in newer emulator releases.
   1271    if not os.path.exists(self._crashreport_path):
   1272      return
   1273 
   1274    logging.info('Uploading local crashing reports.')
   1275    output = cmd_helper.GetCmdOutput([self._crashreport_path, '-u'])
   1276    for line in output.splitlines():
   1277      logging.info('  %s', line)
   1278 
   1279  def GetSnapshotName(self):
   1280    """Return the snapshot name to load/save.
   1281 
   1282    Emulator has a different snapshot process when '-writable-system' flag is
   1283    set (See https://issuetracker.google.com/issues/135857816#comment8).
   1284 
   1285    """
   1286    if self._writable_system:
   1287      return _SYSTEM_SNAPSHOT_NAME
   1288 
   1289    return _DEFAULT_SNAPSHOT_NAME
   1290 
   1291  def HasSystemSnapshot(self):
   1292    """Check if the instance has the snapshot named _SYSTEM_SNAPSHOT_NAME."""
   1293    return self._avd_config.HasSnapshot(_SYSTEM_SNAPSHOT_NAME)
   1294 
   1295  def SaveSnapshot(self):
   1296    snapshot_name = self.GetSnapshotName()
   1297    if self.device:
   1298      logging.info('Saving snapshot to %r.', snapshot_name)
   1299      self.device.adb.Emu(['avd', 'snapshot', 'save', snapshot_name])
   1300 
   1301  @property
   1302  def serial(self):
   1303    return self._emulator_serial
   1304 
   1305  @property
   1306  def device(self):
   1307    if not self._emulator_device and self._emulator_serial:
   1308      self._emulator_device = device_utils.DeviceUtils(self._emulator_serial)
   1309    return self._emulator_device
   1310 
   1311 
   1312 # TODO(crbug.com/40207212): Refactor it to a dict-based approach.
   1313 def _EnsureSystemSettings(device):
   1314  set_long_press_timeout_cmd = [
   1315      'settings', 'put', 'secure', 'long_press_timeout', _LONG_PRESS_TIMEOUT
   1316  ]
   1317  device.RunShellCommand(set_long_press_timeout_cmd, check_return=True)
   1318 
   1319  # Verify if long_press_timeout is set correctly.
   1320  get_long_press_timeout_cmd = [
   1321      'settings', 'get', 'secure', 'long_press_timeout'
   1322  ]
   1323  adb_output = device.RunShellCommand(get_long_press_timeout_cmd,
   1324                                      check_return=True)
   1325  if _LONG_PRESS_TIMEOUT in adb_output:
   1326    logging.info('long_press_timeout set to %r', _LONG_PRESS_TIMEOUT)
   1327  else:
   1328    logging.warning('long_press_timeout is not set correctly')
   1329 
   1330  # TODO(crbug.com/40283631): Move the date sync function to device_utils.py
   1331  if device.IsUserBuild():
   1332    logging.warning('Cannot sync the device date on "user" build')
   1333    return
   1334 
   1335  logging.info('Sync the device date.')
   1336  timezone = device.RunShellCommand(['date', '+"%Z"'],
   1337                                    single_line=True,
   1338                                    check_return=True)
   1339  if timezone != 'UTC':
   1340    device.RunShellCommand(['setprop', 'persist.sys.timezone', '"Etc/UTC"'],
   1341                           check_return=True,
   1342                           as_root=True)
   1343  set_date_format = '%Y%m%d.%H%M%S'
   1344  set_date_command = ['date', '-s']
   1345  if device.build_version_sdk >= version_codes.MARSHMALLOW:
   1346    set_date_format = '%m%d%H%M%Y.%S'
   1347    set_date_command = ['date']
   1348  strgmtime = time.strftime(set_date_format, time.gmtime())
   1349  set_date_command.append(strgmtime)
   1350  device.RunShellCommand(set_date_command, check_return=True, as_root=True)
   1351 
   1352  logging.info('Hide system error dialogs such as crash and ANR dialogs.')
   1353  device.RunShellCommand(
   1354      ['settings', 'put', 'global', 'hide_error_dialogs', '1'])
   1355 
   1356 
   1357 def _EnableNetwork(device):
   1358  logging.info('Enable the network on the emulator.')
   1359  # TODO(crbug.com/40282869): Remove airplane_mode once all AVD
   1360  # are rolled to svc-based version.
   1361  device.RunShellCommand(
   1362      ['settings', 'put', 'global', 'airplane_mode_on', '0'], as_root=True)
   1363  device.RunShellCommand(
   1364      ['am', 'broadcast', '-a', 'android.intent.action.AIRPLANE_MODE'],
   1365      as_root=True)
   1366  device.RunShellCommand(['svc', 'wifi', 'enable'], as_root=True)
   1367  device.RunShellCommand(['svc', 'data', 'enable'], as_root=True)