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