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