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