1# Copyright 2014 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5"""Provides a variety of device interactions based on adb. 6 7Eventually, this will be based on adb_wrapper. 8""" 9# pylint: disable=unused-argument 10 11import collections 12import itertools 13import json 14import logging 15import multiprocessing 16import os 17import posixpath 18import re 19import shutil 20import tempfile 21import time 22import zipfile 23 24from devil import base_error 25from devil import devil_env 26from devil.utils import cmd_helper 27from devil.android import apk_helper 28from devil.android import device_signal 29from devil.android import decorators 30from devil.android import device_errors 31from devil.android import device_temp_file 32from devil.android import install_commands 33from devil.android import logcat_monitor 34from devil.android import md5sum 35from devil.android.sdk import adb_wrapper 36from devil.android.sdk import gce_adb_wrapper 37from devil.android.sdk import intent 38from devil.android.sdk import keyevent 39from devil.android.sdk import split_select 40from devil.android.sdk import version_codes 41from devil.utils import host_utils 42from devil.utils import parallelizer 43from devil.utils import reraiser_thread 44from devil.utils import timeout_retry 45from devil.utils import zip_utils 46 47_DEFAULT_TIMEOUT = 30 48_DEFAULT_RETRIES = 3 49 50# A sentinel object for default values 51# TODO(jbudorick,perezju): revisit how default values are handled by 52# the timeout_retry decorators. 53DEFAULT = object() 54 55_RESTART_ADBD_SCRIPT = """ 56 trap '' HUP 57 trap '' TERM 58 trap '' PIPE 59 function restart() { 60 stop adbd 61 start adbd 62 } 63 restart & 64""" 65 66# Not all permissions can be set. 67_PERMISSIONS_BLACKLIST = [ 68 'android.permission.ACCESS_MOCK_LOCATION', 69 'android.permission.ACCESS_NETWORK_STATE', 70 'android.permission.ACCESS_WIFI_STATE', 71 'android.permission.AUTHENTICATE_ACCOUNTS', 72 'android.permission.BLUETOOTH', 73 'android.permission.BLUETOOTH_ADMIN', 74 'android.permission.DOWNLOAD_WITHOUT_NOTIFICATION', 75 'android.permission.INTERNET', 76 'android.permission.MANAGE_ACCOUNTS', 77 'android.permission.MODIFY_AUDIO_SETTINGS', 78 'android.permission.NFC', 79 'android.permission.READ_SYNC_SETTINGS', 80 'android.permission.READ_SYNC_STATS', 81 'android.permission.RECEIVE_BOOT_COMPLETED', 82 'android.permission.RECORD_VIDEO', 83 'android.permission.RUN_INSTRUMENTATION', 84 'android.permission.USE_CREDENTIALS', 85 'android.permission.VIBRATE', 86 'android.permission.WAKE_LOCK', 87 'android.permission.WRITE_SYNC_SETTINGS', 88 'com.android.browser.permission.READ_HISTORY_BOOKMARKS', 89 'com.android.browser.permission.WRITE_HISTORY_BOOKMARKS', 90 'com.android.launcher.permission.INSTALL_SHORTCUT', 91 'com.chrome.permission.DEVICE_EXTRAS', 92 'com.google.android.apps.chrome.permission.C2D_MESSAGE', 93 'com.google.android.apps.chrome.permission.READ_WRITE_BOOKMARK_FOLDERS', 94 'com.google.android.apps.chrome.TOS_ACKED', 95 'com.google.android.c2dm.permission.RECEIVE', 96 'com.google.android.providers.gsf.permission.READ_GSERVICES', 97 'com.sec.enterprise.knox.MDM_CONTENT_PROVIDER', 98 'org.chromium.chrome.permission.C2D_MESSAGE', 99 'org.chromium.chrome.permission.READ_WRITE_BOOKMARK_FOLDERS', 100 'org.chromium.chrome.TOS_ACKED', 101] 102 103_CURRENT_FOCUS_CRASH_RE = re.compile( 104 r'\s*mCurrentFocus.*Application (Error|Not Responding): (\S+)}') 105 106_GETPROP_RE = re.compile(r'\[(.*?)\]: \[(.*?)\]') 107_IPV4_ADDRESS_RE = re.compile(r'([0-9]{1,3}\.){3}[0-9]{1,3}\:[0-9]{4,5}') 108 109 110@decorators.WithExplicitTimeoutAndRetries( 111 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES) 112def GetAVDs(): 113 """Returns a list of Android Virtual Devices. 114 115 Returns: 116 A list containing the configured AVDs. 117 """ 118 lines = cmd_helper.GetCmdOutput([ 119 os.path.join(devil_env.config.LocalPath('android_sdk'), 120 'tools', 'android'), 121 'list', 'avd']).splitlines() 122 avds = [] 123 for line in lines: 124 if 'Name:' not in line: 125 continue 126 key, value = (s.strip() for s in line.split(':', 1)) 127 if key == 'Name': 128 avds.append(value) 129 return avds 130 131 132@decorators.WithExplicitTimeoutAndRetries( 133 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES) 134def RestartServer(): 135 """Restarts the adb server. 136 137 Raises: 138 CommandFailedError if we fail to kill or restart the server. 139 """ 140 def adb_killed(): 141 return not adb_wrapper.AdbWrapper.IsServerOnline() 142 143 def adb_started(): 144 return adb_wrapper.AdbWrapper.IsServerOnline() 145 146 adb_wrapper.AdbWrapper.KillServer() 147 if not timeout_retry.WaitFor(adb_killed, wait_period=1, max_tries=5): 148 # TODO(perezju): raise an exception after fixng http://crbug.com/442319 149 logging.warning('Failed to kill adb server') 150 adb_wrapper.AdbWrapper.StartServer() 151 if not timeout_retry.WaitFor(adb_started, wait_period=1, max_tries=5): 152 raise device_errors.CommandFailedError('Failed to start adb server') 153 154 155def _GetTimeStamp(): 156 """Return a basic ISO 8601 time stamp with the current local time.""" 157 return time.strftime('%Y%m%dT%H%M%S', time.localtime()) 158 159 160def _JoinLines(lines): 161 # makes sure that the last line is also terminated, and is more memory 162 # efficient than first appending an end-line to each line and then joining 163 # all of them together. 164 return ''.join(s for line in lines for s in (line, '\n')) 165 166 167def _IsGceInstance(serial): 168 return _IPV4_ADDRESS_RE.match(serial) 169 170 171def _CreateAdbWrapper(device): 172 if _IsGceInstance(str(device)): 173 return gce_adb_wrapper.GceAdbWrapper(str(device)) 174 else: 175 if isinstance(device, adb_wrapper.AdbWrapper): 176 return device 177 else: 178 return adb_wrapper.AdbWrapper(device) 179 180 181class DeviceUtils(object): 182 183 _MAX_ADB_COMMAND_LENGTH = 512 184 _MAX_ADB_OUTPUT_LENGTH = 32768 185 _LAUNCHER_FOCUSED_RE = re.compile( 186 r'\s*mCurrentFocus.*(Launcher|launcher).*') 187 _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') 188 189 LOCAL_PROPERTIES_PATH = posixpath.join('/', 'data', 'local.prop') 190 191 # Property in /data/local.prop that controls Java assertions. 192 JAVA_ASSERT_PROPERTY = 'dalvik.vm.enableassertions' 193 194 def __init__(self, device, enable_device_files_cache=False, 195 default_timeout=_DEFAULT_TIMEOUT, 196 default_retries=_DEFAULT_RETRIES): 197 """DeviceUtils constructor. 198 199 Args: 200 device: Either a device serial, an existing AdbWrapper instance, or an 201 an existing AndroidCommands instance. 202 enable_device_files_cache: For PushChangedFiles(), cache checksums of 203 pushed files rather than recomputing them on a subsequent call. 204 default_timeout: An integer containing the default number of seconds to 205 wait for an operation to complete if no explicit value is provided. 206 default_retries: An integer containing the default number or times an 207 operation should be retried on failure if no explicit value is provided. 208 """ 209 self.adb = None 210 if isinstance(device, basestring): 211 self.adb = _CreateAdbWrapper(device) 212 elif isinstance(device, adb_wrapper.AdbWrapper): 213 self.adb = device 214 else: 215 raise ValueError('Unsupported device value: %r' % device) 216 self._commands_installed = None 217 self._default_timeout = default_timeout 218 self._default_retries = default_retries 219 self._enable_device_files_cache = enable_device_files_cache 220 self._cache = {} 221 self._client_caches = {} 222 assert hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR) 223 assert hasattr(self, decorators.DEFAULT_RETRIES_ATTR) 224 225 self._ClearCache() 226 227 def __eq__(self, other): 228 """Checks whether |other| refers to the same device as |self|. 229 230 Args: 231 other: The object to compare to. This can be a basestring, an instance 232 of adb_wrapper.AdbWrapper, or an instance of DeviceUtils. 233 Returns: 234 Whether |other| refers to the same device as |self|. 235 """ 236 return self.adb.GetDeviceSerial() == str(other) 237 238 def __lt__(self, other): 239 """Compares two instances of DeviceUtils. 240 241 This merely compares their serial numbers. 242 243 Args: 244 other: The instance of DeviceUtils to compare to. 245 Returns: 246 Whether |self| is less than |other|. 247 """ 248 return self.adb.GetDeviceSerial() < other.adb.GetDeviceSerial() 249 250 def __str__(self): 251 """Returns the device serial.""" 252 return self.adb.GetDeviceSerial() 253 254 @decorators.WithTimeoutAndRetriesFromInstance() 255 def IsOnline(self, timeout=None, retries=None): 256 """Checks whether the device is online. 257 258 Args: 259 timeout: timeout in seconds 260 retries: number of retries 261 262 Returns: 263 True if the device is online, False otherwise. 264 265 Raises: 266 CommandTimeoutError on timeout. 267 """ 268 try: 269 return self.adb.GetState() == 'device' 270 except base_error.BaseError as exc: 271 logging.info('Failed to get state: %s', exc) 272 return False 273 274 @decorators.WithTimeoutAndRetriesFromInstance() 275 def HasRoot(self, timeout=None, retries=None): 276 """Checks whether or not adbd has root privileges. 277 278 Args: 279 timeout: timeout in seconds 280 retries: number of retries 281 282 Returns: 283 True if adbd has root privileges, False otherwise. 284 285 Raises: 286 CommandTimeoutError on timeout. 287 DeviceUnreachableError on missing device. 288 """ 289 try: 290 self.RunShellCommand('ls /root', check_return=True) 291 return True 292 except device_errors.AdbCommandFailedError: 293 return False 294 295 def NeedsSU(self, timeout=DEFAULT, retries=DEFAULT): 296 """Checks whether 'su' is needed to access protected resources. 297 298 Args: 299 timeout: timeout in seconds 300 retries: number of retries 301 302 Returns: 303 True if 'su' is available on the device and is needed to to access 304 protected resources; False otherwise if either 'su' is not available 305 (e.g. because the device has a user build), or not needed (because adbd 306 already has root privileges). 307 308 Raises: 309 CommandTimeoutError on timeout. 310 DeviceUnreachableError on missing device. 311 """ 312 if 'needs_su' not in self._cache: 313 try: 314 self.RunShellCommand( 315 '%s && ! ls /root' % self._Su('ls /root'), check_return=True, 316 timeout=self._default_timeout if timeout is DEFAULT else timeout, 317 retries=self._default_retries if retries is DEFAULT else retries) 318 self._cache['needs_su'] = True 319 except device_errors.AdbCommandFailedError: 320 self._cache['needs_su'] = False 321 return self._cache['needs_su'] 322 323 def _Su(self, command): 324 if self.build_version_sdk >= version_codes.MARSHMALLOW: 325 return 'su 0 %s' % command 326 return 'su -c %s' % command 327 328 @decorators.WithTimeoutAndRetriesFromInstance() 329 def EnableRoot(self, timeout=None, retries=None): 330 """Restarts adbd with root privileges. 331 332 Args: 333 timeout: timeout in seconds 334 retries: number of retries 335 336 Raises: 337 CommandFailedError if root could not be enabled. 338 CommandTimeoutError on timeout. 339 """ 340 if self.IsUserBuild(): 341 raise device_errors.CommandFailedError( 342 'Cannot enable root in user builds.', str(self)) 343 if 'needs_su' in self._cache: 344 del self._cache['needs_su'] 345 self.adb.Root() 346 self.WaitUntilFullyBooted() 347 348 @decorators.WithTimeoutAndRetriesFromInstance() 349 def IsUserBuild(self, timeout=None, retries=None): 350 """Checks whether or not the device is running a user build. 351 352 Args: 353 timeout: timeout in seconds 354 retries: number of retries 355 356 Returns: 357 True if the device is running a user build, False otherwise (i.e. if 358 it's running a userdebug build). 359 360 Raises: 361 CommandTimeoutError on timeout. 362 DeviceUnreachableError on missing device. 363 """ 364 return self.build_type == 'user' 365 366 @decorators.WithTimeoutAndRetriesFromInstance() 367 def GetExternalStoragePath(self, timeout=None, retries=None): 368 """Get the device's path to its SD card. 369 370 Args: 371 timeout: timeout in seconds 372 retries: number of retries 373 374 Returns: 375 The device's path to its SD card. 376 377 Raises: 378 CommandFailedError if the external storage path could not be determined. 379 CommandTimeoutError on timeout. 380 DeviceUnreachableError on missing device. 381 """ 382 if 'external_storage' in self._cache: 383 return self._cache['external_storage'] 384 385 value = self.RunShellCommand('echo $EXTERNAL_STORAGE', 386 single_line=True, 387 check_return=True) 388 if not value: 389 raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set', 390 str(self)) 391 self._cache['external_storage'] = value 392 return value 393 394 @decorators.WithTimeoutAndRetriesFromInstance() 395 def GetApplicationPaths(self, package, timeout=None, retries=None): 396 """Get the paths of the installed apks on the device for the given package. 397 398 Args: 399 package: Name of the package. 400 401 Returns: 402 List of paths to the apks on the device for the given package. 403 """ 404 return self._GetApplicationPathsInternal(package) 405 406 def _GetApplicationPathsInternal(self, package, skip_cache=False): 407 cached_result = self._cache['package_apk_paths'].get(package) 408 if cached_result is not None and not skip_cache: 409 if package in self._cache['package_apk_paths_to_verify']: 410 self._cache['package_apk_paths_to_verify'].remove(package) 411 # Don't verify an app that is not thought to be installed. We are 412 # concerned only with apps we think are installed having been 413 # uninstalled manually. 414 if cached_result and not self.PathExists(cached_result): 415 cached_result = None 416 self._cache['package_apk_checksums'].pop(package, 0) 417 if cached_result is not None: 418 return list(cached_result) 419 # 'pm path' is liable to incorrectly exit with a nonzero number starting 420 # in Lollipop. 421 # TODO(jbudorick): Check if this is fixed as new Android versions are 422 # released to put an upper bound on this. 423 should_check_return = (self.build_version_sdk < version_codes.LOLLIPOP) 424 output = self.RunShellCommand( 425 ['pm', 'path', package], check_return=should_check_return) 426 apks = [] 427 for line in output: 428 if not line.startswith('package:'): 429 raise device_errors.CommandFailedError( 430 'pm path returned: %r' % '\n'.join(output), str(self)) 431 apks.append(line[len('package:'):]) 432 self._cache['package_apk_paths'][package] = list(apks) 433 return apks 434 435 @decorators.WithTimeoutAndRetriesFromInstance() 436 def GetApplicationVersion(self, package, timeout=None, retries=None): 437 """Get the version name of a package installed on the device. 438 439 Args: 440 package: Name of the package. 441 442 Returns: 443 A string with the version name or None if the package is not found 444 on the device. 445 """ 446 output = self.RunShellCommand( 447 ['dumpsys', 'package', package], check_return=True) 448 if not output: 449 return None 450 for line in output: 451 line = line.strip() 452 if line.startswith('versionName='): 453 return line[len('versionName='):] 454 raise device_errors.CommandFailedError( 455 'Version name for %s not found on dumpsys output' % package, str(self)) 456 457 @decorators.WithTimeoutAndRetriesFromInstance() 458 def GetApplicationDataDirectory(self, package, timeout=None, retries=None): 459 """Get the data directory on the device for the given package. 460 461 Args: 462 package: Name of the package. 463 464 Returns: 465 The package's data directory, or None if the package doesn't exist on the 466 device. 467 """ 468 try: 469 output = self._RunPipedShellCommand( 470 'pm dump %s | grep dataDir=' % cmd_helper.SingleQuote(package)) 471 for line in output: 472 _, _, dataDir = line.partition('dataDir=') 473 if dataDir: 474 return dataDir 475 except device_errors.CommandFailedError: 476 logging.exception('Could not find data directory for %s', package) 477 return None 478 479 @decorators.WithTimeoutAndRetriesFromInstance() 480 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None): 481 """Wait for the device to fully boot. 482 483 This means waiting for the device to boot, the package manager to be 484 available, and the SD card to be ready. It can optionally mean waiting 485 for wifi to come up, too. 486 487 Args: 488 wifi: A boolean indicating if we should wait for wifi to come up or not. 489 timeout: timeout in seconds 490 retries: number of retries 491 492 Raises: 493 CommandFailedError on failure. 494 CommandTimeoutError if one of the component waits times out. 495 DeviceUnreachableError if the device becomes unresponsive. 496 """ 497 def sd_card_ready(): 498 try: 499 self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()], 500 check_return=True) 501 return True 502 except device_errors.AdbCommandFailedError: 503 return False 504 505 def pm_ready(): 506 try: 507 return self._GetApplicationPathsInternal('android', skip_cache=True) 508 except device_errors.CommandFailedError: 509 return False 510 511 def boot_completed(): 512 return self.GetProp('sys.boot_completed', cache=False) == '1' 513 514 def wifi_enabled(): 515 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'], 516 check_return=False) 517 518 self.adb.WaitForDevice() 519 timeout_retry.WaitFor(sd_card_ready) 520 timeout_retry.WaitFor(pm_ready) 521 timeout_retry.WaitFor(boot_completed) 522 if wifi: 523 timeout_retry.WaitFor(wifi_enabled) 524 525 REBOOT_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT 526 527 @decorators.WithTimeoutAndRetriesFromInstance( 528 min_default_timeout=REBOOT_DEFAULT_TIMEOUT) 529 def Reboot(self, block=True, wifi=False, timeout=None, retries=None): 530 """Reboot the device. 531 532 Args: 533 block: A boolean indicating if we should wait for the reboot to complete. 534 wifi: A boolean indicating if we should wait for wifi to be enabled after 535 the reboot. The option has no effect unless |block| is also True. 536 timeout: timeout in seconds 537 retries: number of retries 538 539 Raises: 540 CommandTimeoutError on timeout. 541 DeviceUnreachableError on missing device. 542 """ 543 def device_offline(): 544 return not self.IsOnline() 545 546 self.adb.Reboot() 547 self._ClearCache() 548 timeout_retry.WaitFor(device_offline, wait_period=1) 549 if block: 550 self.WaitUntilFullyBooted(wifi=wifi) 551 552 INSTALL_DEFAULT_TIMEOUT = 4 * _DEFAULT_TIMEOUT 553 554 @decorators.WithTimeoutAndRetriesFromInstance( 555 min_default_timeout=INSTALL_DEFAULT_TIMEOUT) 556 def Install(self, apk, allow_downgrade=False, reinstall=False, 557 permissions=None, timeout=None, retries=None): 558 """Install an APK. 559 560 Noop if an identical APK is already installed. 561 562 Args: 563 apk: An ApkHelper instance or string containing the path to the APK. 564 allow_downgrade: A boolean indicating if we should allow downgrades. 565 reinstall: A boolean indicating if we should keep any existing app data. 566 permissions: Set of permissions to set. If not set, finds permissions with 567 apk helper. To set no permissions, pass []. 568 timeout: timeout in seconds 569 retries: number of retries 570 571 Raises: 572 CommandFailedError if the installation fails. 573 CommandTimeoutError if the installation times out. 574 DeviceUnreachableError on missing device. 575 """ 576 self._InstallInternal(apk, None, allow_downgrade=allow_downgrade, 577 reinstall=reinstall, permissions=permissions) 578 579 @decorators.WithTimeoutAndRetriesFromInstance( 580 min_default_timeout=INSTALL_DEFAULT_TIMEOUT) 581 def InstallSplitApk(self, base_apk, split_apks, allow_downgrade=False, 582 reinstall=False, allow_cached_props=False, 583 permissions=None, timeout=None, retries=None): 584 """Install a split APK. 585 586 Noop if all of the APK splits are already installed. 587 588 Args: 589 base_apk: An ApkHelper instance or string containing the path to the base 590 APK. 591 split_apks: A list of strings of paths of all of the APK splits. 592 allow_downgrade: A boolean indicating if we should allow downgrades. 593 reinstall: A boolean indicating if we should keep any existing app data. 594 allow_cached_props: Whether to use cached values for device properties. 595 permissions: Set of permissions to set. If not set, finds permissions with 596 apk helper. To set no permissions, pass []. 597 timeout: timeout in seconds 598 retries: number of retries 599 600 Raises: 601 CommandFailedError if the installation fails. 602 CommandTimeoutError if the installation times out. 603 DeviceUnreachableError on missing device. 604 DeviceVersionError if device SDK is less than Android L. 605 """ 606 self._InstallInternal(base_apk, split_apks, reinstall=reinstall, 607 allow_cached_props=allow_cached_props, 608 permissions=permissions, 609 allow_downgrade=allow_downgrade) 610 611 def _InstallInternal(self, base_apk, split_apks, allow_downgrade=False, 612 reinstall=False, allow_cached_props=False, 613 permissions=None): 614 if split_apks: 615 self._CheckSdkLevel(version_codes.LOLLIPOP) 616 617 base_apk = apk_helper.ToHelper(base_apk) 618 619 all_apks = [base_apk.path] 620 if split_apks: 621 all_apks += split_select.SelectSplits( 622 self, base_apk.path, split_apks, allow_cached_props=allow_cached_props) 623 if len(all_apks) == 1: 624 logging.warning('split-select did not select any from %s', split_apks) 625 626 package_name = base_apk.GetPackageName() 627 device_apk_paths = self._GetApplicationPathsInternal(package_name) 628 629 apks_to_install = None 630 host_checksums = None 631 if not device_apk_paths: 632 apks_to_install = all_apks 633 elif len(device_apk_paths) > 1 and not split_apks: 634 logging.warning( 635 'Installing non-split APK when split APK was previously installed') 636 apks_to_install = all_apks 637 elif len(device_apk_paths) == 1 and split_apks: 638 logging.warning( 639 'Installing split APK when non-split APK was previously installed') 640 apks_to_install = all_apks 641 else: 642 try: 643 apks_to_install, host_checksums = ( 644 self._ComputeStaleApks(package_name, all_apks)) 645 except EnvironmentError as e: 646 logging.warning('Error calculating md5: %s', e) 647 apks_to_install, host_checksums = all_apks, None 648 if apks_to_install and not reinstall: 649 self.Uninstall(package_name) 650 apks_to_install = all_apks 651 652 if apks_to_install: 653 # Assume that we won't know the resulting device state. 654 self._cache['package_apk_paths'].pop(package_name, 0) 655 self._cache['package_apk_checksums'].pop(package_name, 0) 656 if split_apks: 657 partial = package_name if len(apks_to_install) < len(all_apks) else None 658 self.adb.InstallMultiple( 659 apks_to_install, partial=partial, reinstall=reinstall, 660 allow_downgrade=allow_downgrade) 661 else: 662 self.adb.Install( 663 base_apk.path, reinstall=reinstall, allow_downgrade=allow_downgrade) 664 if (permissions is None 665 and self.build_version_sdk >= version_codes.MARSHMALLOW): 666 permissions = base_apk.GetPermissions() 667 self.GrantPermissions(package_name, permissions) 668 # Upon success, we know the device checksums, but not their paths. 669 if host_checksums is not None: 670 self._cache['package_apk_checksums'][package_name] = host_checksums 671 else: 672 # Running adb install terminates running instances of the app, so to be 673 # consistent, we explicitly terminate it when skipping the install. 674 self.ForceStop(package_name) 675 676 @decorators.WithTimeoutAndRetriesFromInstance() 677 def Uninstall(self, package_name, keep_data=False, timeout=None, 678 retries=None): 679 """Remove the app |package_name| from the device. 680 681 This is a no-op if the app is not already installed. 682 683 Args: 684 package_name: The package to uninstall. 685 keep_data: (optional) Whether to keep the data and cache directories. 686 timeout: Timeout in seconds. 687 retries: Number of retries. 688 689 Raises: 690 CommandFailedError if the uninstallation fails. 691 CommandTimeoutError if the uninstallation times out. 692 DeviceUnreachableError on missing device. 693 """ 694 installed = self._GetApplicationPathsInternal(package_name) 695 if not installed: 696 return 697 try: 698 self.adb.Uninstall(package_name, keep_data) 699 self._cache['package_apk_paths'][package_name] = [] 700 self._cache['package_apk_checksums'][package_name] = set() 701 except: 702 # Clear cache since we can't be sure of the state. 703 self._cache['package_apk_paths'].pop(package_name, 0) 704 self._cache['package_apk_checksums'].pop(package_name, 0) 705 raise 706 707 def _CheckSdkLevel(self, required_sdk_level): 708 """Raises an exception if the device does not have the required SDK level. 709 """ 710 if self.build_version_sdk < required_sdk_level: 711 raise device_errors.DeviceVersionError( 712 ('Requires SDK level %s, device is SDK level %s' % 713 (required_sdk_level, self.build_version_sdk)), 714 device_serial=self.adb.GetDeviceSerial()) 715 716 @decorators.WithTimeoutAndRetriesFromInstance() 717 def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None, 718 as_root=False, single_line=False, large_output=False, 719 timeout=None, retries=None): 720 """Run an ADB shell command. 721 722 The command to run |cmd| should be a sequence of program arguments or else 723 a single string. 724 725 When |cmd| is a sequence, it is assumed to contain the name of the command 726 to run followed by its arguments. In this case, arguments are passed to the 727 command exactly as given, without any further processing by the shell. This 728 allows to easily pass arguments containing spaces or special characters 729 without having to worry about getting quoting right. Whenever possible, it 730 is recomended to pass |cmd| as a sequence. 731 732 When |cmd| is given as a string, it will be interpreted and run by the 733 shell on the device. 734 735 This behaviour is consistent with that of command runners in cmd_helper as 736 well as Python's own subprocess.Popen. 737 738 TODO(perezju) Change the default of |check_return| to True when callers 739 have switched to the new behaviour. 740 741 Args: 742 cmd: A string with the full command to run on the device, or a sequence 743 containing the command and its arguments. 744 check_return: A boolean indicating whether or not the return code should 745 be checked. 746 cwd: The device directory in which the command should be run. 747 env: The environment variables with which the command should be run. 748 as_root: A boolean indicating whether the shell command should be run 749 with root privileges. 750 single_line: A boolean indicating if only a single line of output is 751 expected. 752 large_output: Uses a work-around for large shell command output. Without 753 this large output will be truncated. 754 timeout: timeout in seconds 755 retries: number of retries 756 757 Returns: 758 If single_line is False, the output of the command as a list of lines, 759 otherwise, a string with the unique line of output emmited by the command 760 (with the optional newline at the end stripped). 761 762 Raises: 763 AdbCommandFailedError if check_return is True and the exit code of 764 the command run on the device is non-zero. 765 CommandFailedError if single_line is True but the output contains two or 766 more lines. 767 CommandTimeoutError on timeout. 768 DeviceUnreachableError on missing device. 769 """ 770 def env_quote(key, value): 771 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key): 772 raise KeyError('Invalid shell variable name %r' % key) 773 # using double quotes here to allow interpolation of shell variables 774 return '%s=%s' % (key, cmd_helper.DoubleQuote(value)) 775 776 def run(cmd): 777 return self.adb.Shell(cmd) 778 779 def handle_check_return(cmd): 780 try: 781 return run(cmd) 782 except device_errors.AdbCommandFailedError as exc: 783 if check_return: 784 raise 785 else: 786 return exc.output 787 788 def handle_large_command(cmd): 789 if len(cmd) < self._MAX_ADB_COMMAND_LENGTH: 790 return handle_check_return(cmd) 791 else: 792 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script: 793 self._WriteFileWithPush(script.name, cmd) 794 logging.info('Large shell command will be run from file: %s ...', 795 cmd[:self._MAX_ADB_COMMAND_LENGTH]) 796 return handle_check_return('sh %s' % script.name_quoted) 797 798 def handle_large_output(cmd, large_output_mode): 799 if large_output_mode: 800 with device_temp_file.DeviceTempFile(self.adb) as large_output_file: 801 cmd = '( %s )>%s' % (cmd, large_output_file.name) 802 logging.debug('Large output mode enabled. Will write output to ' 803 'device and read results from file.') 804 handle_large_command(cmd) 805 return self.ReadFile(large_output_file.name, force_pull=True) 806 else: 807 try: 808 return handle_large_command(cmd) 809 except device_errors.AdbCommandFailedError as exc: 810 if exc.status is None: 811 logging.exception('No output found for %s', cmd) 812 logging.warning('Attempting to run in large_output mode.') 813 logging.warning('Use RunShellCommand(..., large_output=True) for ' 814 'shell commands that expect a lot of output.') 815 return handle_large_output(cmd, True) 816 else: 817 raise 818 819 if not isinstance(cmd, basestring): 820 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd) 821 if env: 822 env = ' '.join(env_quote(k, v) for k, v in env.iteritems()) 823 cmd = '%s %s' % (env, cmd) 824 if cwd: 825 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd) 826 if as_root and self.NeedsSU(): 827 # "su -c sh -c" allows using shell features in |cmd| 828 cmd = self._Su('sh -c %s' % cmd_helper.SingleQuote(cmd)) 829 830 output = handle_large_output(cmd, large_output).splitlines() 831 832 if single_line: 833 if not output: 834 return '' 835 elif len(output) == 1: 836 return output[0] 837 else: 838 msg = 'one line of output was expected, but got: %s' 839 raise device_errors.CommandFailedError(msg % output, str(self)) 840 else: 841 return output 842 843 def _RunPipedShellCommand(self, script, **kwargs): 844 PIPESTATUS_LEADER = 'PIPESTATUS: ' 845 846 script += '; echo "%s${PIPESTATUS[@]}"' % PIPESTATUS_LEADER 847 kwargs['check_return'] = True 848 output = self.RunShellCommand(script, **kwargs) 849 pipestatus_line = output[-1] 850 851 if not pipestatus_line.startswith(PIPESTATUS_LEADER): 852 logging.error('Pipe exit statuses of shell script missing.') 853 raise device_errors.AdbShellCommandFailedError( 854 script, output, status=None, 855 device_serial=self.adb.GetDeviceSerial()) 856 857 output = output[:-1] 858 statuses = [ 859 int(s) for s in pipestatus_line[len(PIPESTATUS_LEADER):].split()] 860 if any(statuses): 861 raise device_errors.AdbShellCommandFailedError( 862 script, output, status=statuses, 863 device_serial=self.adb.GetDeviceSerial()) 864 return output 865 866 @decorators.WithTimeoutAndRetriesFromInstance() 867 def KillAll(self, process_name, exact=False, signum=device_signal.SIGKILL, 868 as_root=False, blocking=False, quiet=False, 869 timeout=None, retries=None): 870 """Kill all processes with the given name on the device. 871 872 Args: 873 process_name: A string containing the name of the process to kill. 874 exact: A boolean indicating whether to kill all processes matching 875 the string |process_name| exactly, or all of those which contain 876 |process_name| as a substring. Defaults to False. 877 signum: An integer containing the signal number to send to kill. Defaults 878 to SIGKILL (9). 879 as_root: A boolean indicating whether the kill should be executed with 880 root privileges. 881 blocking: A boolean indicating whether we should wait until all processes 882 with the given |process_name| are dead. 883 quiet: A boolean indicating whether to ignore the fact that no processes 884 to kill were found. 885 timeout: timeout in seconds 886 retries: number of retries 887 888 Returns: 889 The number of processes attempted to kill. 890 891 Raises: 892 CommandFailedError if no process was killed and |quiet| is False. 893 CommandTimeoutError on timeout. 894 DeviceUnreachableError on missing device. 895 """ 896 procs_pids = self.GetPids(process_name) 897 if exact: 898 procs_pids = {process_name: procs_pids.get(process_name, [])} 899 pids = set(itertools.chain(*procs_pids.values())) 900 if not pids: 901 if quiet: 902 return 0 903 else: 904 raise device_errors.CommandFailedError( 905 'No process "%s"' % process_name, str(self)) 906 907 logging.info( 908 'KillAll(%r, ...) attempting to kill the following:', process_name) 909 for name, ids in procs_pids.iteritems(): 910 for i in ids: 911 logging.info(' %05s %s', str(i), name) 912 913 cmd = ['kill', '-%d' % signum] + sorted(pids) 914 self.RunShellCommand(cmd, as_root=as_root, check_return=True) 915 916 def all_pids_killed(): 917 procs_pids_remain = self.GetPids(process_name) 918 return not pids.intersection(itertools.chain(*procs_pids_remain.values())) 919 920 if blocking: 921 timeout_retry.WaitFor(all_pids_killed, wait_period=0.1) 922 923 return len(pids) 924 925 @decorators.WithTimeoutAndRetriesFromInstance() 926 def StartActivity(self, intent_obj, blocking=False, trace_file_name=None, 927 force_stop=False, timeout=None, retries=None): 928 """Start package's activity on the device. 929 930 Args: 931 intent_obj: An Intent object to send. 932 blocking: A boolean indicating whether we should wait for the activity to 933 finish launching. 934 trace_file_name: If present, a string that both indicates that we want to 935 profile the activity and contains the path to which the 936 trace should be saved. 937 force_stop: A boolean indicating whether we should stop the activity 938 before starting it. 939 timeout: timeout in seconds 940 retries: number of retries 941 942 Raises: 943 CommandFailedError if the activity could not be started. 944 CommandTimeoutError on timeout. 945 DeviceUnreachableError on missing device. 946 """ 947 cmd = ['am', 'start'] 948 if blocking: 949 cmd.append('-W') 950 if trace_file_name: 951 cmd.extend(['--start-profiler', trace_file_name]) 952 if force_stop: 953 cmd.append('-S') 954 cmd.extend(intent_obj.am_args) 955 for line in self.RunShellCommand(cmd, check_return=True): 956 if line.startswith('Error:'): 957 raise device_errors.CommandFailedError(line, str(self)) 958 959 @decorators.WithTimeoutAndRetriesFromInstance() 960 def StartInstrumentation(self, component, finish=True, raw=False, 961 extras=None, timeout=None, retries=None): 962 if extras is None: 963 extras = {} 964 965 cmd = ['am', 'instrument'] 966 if finish: 967 cmd.append('-w') 968 if raw: 969 cmd.append('-r') 970 for k, v in extras.iteritems(): 971 cmd.extend(['-e', str(k), str(v)]) 972 cmd.append(component) 973 974 # Store the package name in a shell variable to help the command stay under 975 # the _MAX_ADB_COMMAND_LENGTH limit. 976 package = component.split('/')[0] 977 shell_snippet = 'p=%s;%s' % (package, 978 cmd_helper.ShrinkToSnippet(cmd, 'p', package)) 979 return self.RunShellCommand(shell_snippet, check_return=True, 980 large_output=True) 981 982 @decorators.WithTimeoutAndRetriesFromInstance() 983 def BroadcastIntent(self, intent_obj, timeout=None, retries=None): 984 """Send a broadcast intent. 985 986 Args: 987 intent: An Intent to broadcast. 988 timeout: timeout in seconds 989 retries: number of retries 990 991 Raises: 992 CommandTimeoutError on timeout. 993 DeviceUnreachableError on missing device. 994 """ 995 cmd = ['am', 'broadcast'] + intent_obj.am_args 996 self.RunShellCommand(cmd, check_return=True) 997 998 @decorators.WithTimeoutAndRetriesFromInstance() 999 def GoHome(self, timeout=None, retries=None): 1000 """Return to the home screen and obtain launcher focus. 1001 1002 This command launches the home screen and attempts to obtain 1003 launcher focus until the timeout is reached. 1004 1005 Args: 1006 timeout: timeout in seconds 1007 retries: number of retries 1008 1009 Raises: 1010 CommandTimeoutError on timeout. 1011 DeviceUnreachableError on missing device. 1012 """ 1013 def is_launcher_focused(): 1014 output = self.RunShellCommand(['dumpsys', 'window', 'windows'], 1015 check_return=True, large_output=True) 1016 return any(self._LAUNCHER_FOCUSED_RE.match(l) for l in output) 1017 1018 def dismiss_popups(): 1019 # There is a dialog present; attempt to get rid of it. 1020 # Not all dialogs can be dismissed with back. 1021 self.SendKeyEvent(keyevent.KEYCODE_ENTER) 1022 self.SendKeyEvent(keyevent.KEYCODE_BACK) 1023 return is_launcher_focused() 1024 1025 # If Home is already focused, return early to avoid unnecessary work. 1026 if is_launcher_focused(): 1027 return 1028 1029 self.StartActivity( 1030 intent.Intent(action='android.intent.action.MAIN', 1031 category='android.intent.category.HOME'), 1032 blocking=True) 1033 1034 if not is_launcher_focused(): 1035 timeout_retry.WaitFor(dismiss_popups, wait_period=1) 1036 1037 @decorators.WithTimeoutAndRetriesFromInstance() 1038 def ForceStop(self, package, timeout=None, retries=None): 1039 """Close the application. 1040 1041 Args: 1042 package: A string containing the name of the package to stop. 1043 timeout: timeout in seconds 1044 retries: number of retries 1045 1046 Raises: 1047 CommandTimeoutError on timeout. 1048 DeviceUnreachableError on missing device. 1049 """ 1050 cmd = 'p=%s;if [[ "$(ps)" = *$p* ]]; then am force-stop $p; fi' 1051 self.RunShellCommand(cmd % package, check_return=True) 1052 1053 @decorators.WithTimeoutAndRetriesFromInstance() 1054 def ClearApplicationState( 1055 self, package, permissions=None, timeout=None, retries=None): 1056 """Clear all state for the given package. 1057 1058 Args: 1059 package: A string containing the name of the package to stop. 1060 permissions: List of permissions to set after clearing data. 1061 timeout: timeout in seconds 1062 retries: number of retries 1063 1064 Raises: 1065 CommandTimeoutError on timeout. 1066 DeviceUnreachableError on missing device. 1067 """ 1068 # Check that the package exists before clearing it for android builds below 1069 # JB MR2. Necessary because calling pm clear on a package that doesn't exist 1070 # may never return. 1071 if ((self.build_version_sdk >= version_codes.JELLY_BEAN_MR2) 1072 or self._GetApplicationPathsInternal(package)): 1073 self.RunShellCommand(['pm', 'clear', package], check_return=True) 1074 self.GrantPermissions(package, permissions) 1075 1076 @decorators.WithTimeoutAndRetriesFromInstance() 1077 def SendKeyEvent(self, keycode, timeout=None, retries=None): 1078 """Sends a keycode to the device. 1079 1080 See the devil.android.sdk.keyevent module for suitable keycode values. 1081 1082 Args: 1083 keycode: A integer keycode to send to the device. 1084 timeout: timeout in seconds 1085 retries: number of retries 1086 1087 Raises: 1088 CommandTimeoutError on timeout. 1089 DeviceUnreachableError on missing device. 1090 """ 1091 self.RunShellCommand(['input', 'keyevent', format(keycode, 'd')], 1092 check_return=True) 1093 1094 PUSH_CHANGED_FILES_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT 1095 1096 @decorators.WithTimeoutAndRetriesFromInstance( 1097 min_default_timeout=PUSH_CHANGED_FILES_DEFAULT_TIMEOUT) 1098 def PushChangedFiles(self, host_device_tuples, timeout=None, 1099 retries=None, delete_device_stale=False): 1100 """Push files to the device, skipping files that don't need updating. 1101 1102 When a directory is pushed, it is traversed recursively on the host and 1103 all files in it are pushed to the device as needed. 1104 Additionally, if delete_device_stale option is True, 1105 files that exist on the device but don't exist on the host are deleted. 1106 1107 Args: 1108 host_device_tuples: A list of (host_path, device_path) tuples, where 1109 |host_path| is an absolute path of a file or directory on the host 1110 that should be minimially pushed to the device, and |device_path| is 1111 an absolute path of the destination on the device. 1112 timeout: timeout in seconds 1113 retries: number of retries 1114 delete_device_stale: option to delete stale files on device 1115 1116 Raises: 1117 CommandFailedError on failure. 1118 CommandTimeoutError on timeout. 1119 DeviceUnreachableError on missing device. 1120 """ 1121 1122 all_changed_files = [] 1123 all_stale_files = [] 1124 missing_dirs = [] 1125 cache_commit_funcs = [] 1126 for h, d in host_device_tuples: 1127 assert os.path.isabs(h) and posixpath.isabs(d) 1128 changed_files, up_to_date_files, stale_files, cache_commit_func = ( 1129 self._GetChangedAndStaleFiles(h, d, delete_device_stale)) 1130 all_changed_files += changed_files 1131 all_stale_files += stale_files 1132 cache_commit_funcs.append(cache_commit_func) 1133 if (os.path.isdir(h) and changed_files and not up_to_date_files 1134 and not stale_files): 1135 missing_dirs.append(d) 1136 1137 if delete_device_stale and all_stale_files: 1138 self.RunShellCommand(['rm', '-f'] + all_stale_files, 1139 check_return=True) 1140 1141 if all_changed_files: 1142 if missing_dirs: 1143 self.RunShellCommand(['mkdir', '-p'] + missing_dirs, check_return=True) 1144 self._PushFilesImpl(host_device_tuples, all_changed_files) 1145 for func in cache_commit_funcs: 1146 func() 1147 1148 def _GetChangedAndStaleFiles(self, host_path, device_path, track_stale=False): 1149 """Get files to push and delete 1150 1151 Args: 1152 host_path: an absolute path of a file or directory on the host 1153 device_path: an absolute path of a file or directory on the device 1154 track_stale: whether to bother looking for stale files (slower) 1155 1156 Returns: 1157 a three-element tuple 1158 1st element: a list of (host_files_path, device_files_path) tuples to push 1159 2nd element: a list of host_files_path that are up-to-date 1160 3rd element: a list of stale files under device_path, or [] when 1161 track_stale == False 1162 """ 1163 try: 1164 # Length calculations below assume no trailing /. 1165 host_path = host_path.rstrip('/') 1166 device_path = device_path.rstrip('/') 1167 1168 specific_device_paths = [device_path] 1169 ignore_other_files = not track_stale and os.path.isdir(host_path) 1170 if ignore_other_files: 1171 specific_device_paths = [] 1172 for root, _, filenames in os.walk(host_path): 1173 relative_dir = root[len(host_path) + 1:] 1174 specific_device_paths.extend( 1175 posixpath.join(device_path, relative_dir, f) for f in filenames) 1176 1177 def calculate_host_checksums(): 1178 return md5sum.CalculateHostMd5Sums([host_path]) 1179 1180 def calculate_device_checksums(): 1181 if self._enable_device_files_cache: 1182 cache_entry = self._cache['device_path_checksums'].get(device_path) 1183 if cache_entry and cache_entry[0] == ignore_other_files: 1184 return dict(cache_entry[1]) 1185 1186 sums = md5sum.CalculateDeviceMd5Sums(specific_device_paths, self) 1187 1188 cache_entry = [ignore_other_files, sums] 1189 self._cache['device_path_checksums'][device_path] = cache_entry 1190 return dict(sums) 1191 1192 host_checksums, device_checksums = reraiser_thread.RunAsync(( 1193 calculate_host_checksums, 1194 calculate_device_checksums)) 1195 except EnvironmentError as e: 1196 logging.warning('Error calculating md5: %s', e) 1197 return ([(host_path, device_path)], [], [], lambda: 0) 1198 1199 to_push = [] 1200 up_to_date = [] 1201 to_delete = [] 1202 if os.path.isfile(host_path): 1203 host_checksum = host_checksums.get(host_path) 1204 device_checksum = device_checksums.get(device_path) 1205 if host_checksum == device_checksum: 1206 up_to_date.append(host_path) 1207 else: 1208 to_push.append((host_path, device_path)) 1209 else: 1210 for host_abs_path, host_checksum in host_checksums.iteritems(): 1211 device_abs_path = posixpath.join( 1212 device_path, os.path.relpath(host_abs_path, host_path)) 1213 device_checksum = device_checksums.pop(device_abs_path, None) 1214 if device_checksum == host_checksum: 1215 up_to_date.append(host_abs_path) 1216 else: 1217 to_push.append((host_abs_path, device_abs_path)) 1218 to_delete = device_checksums.keys() 1219 1220 def cache_commit_func(): 1221 new_sums = {posixpath.join(device_path, path[len(host_path) + 1:]): val 1222 for path, val in host_checksums.iteritems()} 1223 cache_entry = [ignore_other_files, new_sums] 1224 self._cache['device_path_checksums'][device_path] = cache_entry 1225 1226 return (to_push, up_to_date, to_delete, cache_commit_func) 1227 1228 def _ComputeDeviceChecksumsForApks(self, package_name): 1229 ret = self._cache['package_apk_checksums'].get(package_name) 1230 if ret is None: 1231 device_paths = self._GetApplicationPathsInternal(package_name) 1232 file_to_checksums = md5sum.CalculateDeviceMd5Sums(device_paths, self) 1233 ret = set(file_to_checksums.values()) 1234 self._cache['package_apk_checksums'][package_name] = ret 1235 return ret 1236 1237 def _ComputeStaleApks(self, package_name, host_apk_paths): 1238 def calculate_host_checksums(): 1239 return md5sum.CalculateHostMd5Sums(host_apk_paths) 1240 1241 def calculate_device_checksums(): 1242 return self._ComputeDeviceChecksumsForApks(package_name) 1243 1244 host_checksums, device_checksums = reraiser_thread.RunAsync(( 1245 calculate_host_checksums, calculate_device_checksums)) 1246 stale_apks = [k for (k, v) in host_checksums.iteritems() 1247 if v not in device_checksums] 1248 return stale_apks, set(host_checksums.values()) 1249 1250 def _PushFilesImpl(self, host_device_tuples, files): 1251 if not files: 1252 return 1253 1254 size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files) 1255 file_count = len(files) 1256 dir_size = sum(host_utils.GetRecursiveDiskUsage(h) 1257 for h, _ in host_device_tuples) 1258 dir_file_count = 0 1259 for h, _ in host_device_tuples: 1260 if os.path.isdir(h): 1261 dir_file_count += sum(len(f) for _r, _d, f in os.walk(h)) 1262 else: 1263 dir_file_count += 1 1264 1265 push_duration = self._ApproximateDuration( 1266 file_count, file_count, size, False) 1267 dir_push_duration = self._ApproximateDuration( 1268 len(host_device_tuples), dir_file_count, dir_size, False) 1269 zip_duration = self._ApproximateDuration(1, 1, size, True) 1270 1271 if dir_push_duration < push_duration and dir_push_duration < zip_duration: 1272 self._PushChangedFilesIndividually(host_device_tuples) 1273 elif push_duration < zip_duration: 1274 self._PushChangedFilesIndividually(files) 1275 elif self._commands_installed is False: 1276 # Already tried and failed to install unzip command. 1277 self._PushChangedFilesIndividually(files) 1278 elif not self._PushChangedFilesZipped( 1279 files, [d for _, d in host_device_tuples]): 1280 self._PushChangedFilesIndividually(files) 1281 1282 def _MaybeInstallCommands(self): 1283 if self._commands_installed is None: 1284 try: 1285 if not install_commands.Installed(self): 1286 install_commands.InstallCommands(self) 1287 self._commands_installed = True 1288 except device_errors.CommandFailedError as e: 1289 logging.warning('unzip not available: %s', str(e)) 1290 self._commands_installed = False 1291 return self._commands_installed 1292 1293 @staticmethod 1294 def _ApproximateDuration(adb_calls, file_count, byte_count, is_zipping): 1295 # We approximate the time to push a set of files to a device as: 1296 # t = c1 * a + c2 * f + c3 + b / c4 + b / (c5 * c6), where 1297 # t: total time (sec) 1298 # c1: adb call time delay (sec) 1299 # a: number of times adb is called (unitless) 1300 # c2: push time delay (sec) 1301 # f: number of files pushed via adb (unitless) 1302 # c3: zip time delay (sec) 1303 # c4: zip rate (bytes/sec) 1304 # b: total number of bytes (bytes) 1305 # c5: transfer rate (bytes/sec) 1306 # c6: compression ratio (unitless) 1307 1308 # All of these are approximations. 1309 ADB_CALL_PENALTY = 0.1 # seconds 1310 ADB_PUSH_PENALTY = 0.01 # seconds 1311 ZIP_PENALTY = 2.0 # seconds 1312 ZIP_RATE = 10000000.0 # bytes / second 1313 TRANSFER_RATE = 2000000.0 # bytes / second 1314 COMPRESSION_RATIO = 2.0 # unitless 1315 1316 adb_call_time = ADB_CALL_PENALTY * adb_calls 1317 adb_push_setup_time = ADB_PUSH_PENALTY * file_count 1318 if is_zipping: 1319 zip_time = ZIP_PENALTY + byte_count / ZIP_RATE 1320 transfer_time = byte_count / (TRANSFER_RATE * COMPRESSION_RATIO) 1321 else: 1322 zip_time = 0 1323 transfer_time = byte_count / TRANSFER_RATE 1324 return adb_call_time + adb_push_setup_time + zip_time + transfer_time 1325 1326 def _PushChangedFilesIndividually(self, files): 1327 for h, d in files: 1328 self.adb.Push(h, d) 1329 1330 def _PushChangedFilesZipped(self, files, dirs): 1331 with tempfile.NamedTemporaryFile(suffix='.zip') as zip_file: 1332 zip_proc = multiprocessing.Process( 1333 target=DeviceUtils._CreateDeviceZip, 1334 args=(zip_file.name, files)) 1335 zip_proc.start() 1336 try: 1337 # While it's zipping, ensure the unzip command exists on the device. 1338 if not self._MaybeInstallCommands(): 1339 zip_proc.terminate() 1340 return False 1341 1342 # Warm up NeedsSU cache while we're still zipping. 1343 self.NeedsSU() 1344 with device_temp_file.DeviceTempFile( 1345 self.adb, suffix='.zip') as device_temp: 1346 zip_proc.join() 1347 self.adb.Push(zip_file.name, device_temp.name) 1348 quoted_dirs = ' '.join(cmd_helper.SingleQuote(d) for d in dirs) 1349 self.RunShellCommand( 1350 'unzip %s&&chmod -R 777 %s' % (device_temp.name, quoted_dirs), 1351 as_root=True, 1352 env={'PATH': '%s:$PATH' % install_commands.BIN_DIR}, 1353 check_return=True) 1354 finally: 1355 if zip_proc.is_alive(): 1356 zip_proc.terminate() 1357 return True 1358 1359 @staticmethod 1360 def _CreateDeviceZip(zip_path, host_device_tuples): 1361 with zipfile.ZipFile(zip_path, 'w') as zip_file: 1362 for host_path, device_path in host_device_tuples: 1363 zip_utils.WriteToZipFile(zip_file, host_path, device_path) 1364 1365 # TODO(nednguyen): remove this and migrate the callsite to PathExists(). 1366 @decorators.WithTimeoutAndRetriesFromInstance() 1367 def FileExists(self, device_path, timeout=None, retries=None): 1368 """Checks whether the given file exists on the device. 1369 1370 Arguments are the same as PathExists. 1371 """ 1372 return self.PathExists(device_path, timeout=timeout, retries=retries) 1373 1374 @decorators.WithTimeoutAndRetriesFromInstance() 1375 def PathExists(self, device_paths, as_root=False, timeout=None, retries=None): 1376 """Checks whether the given path(s) exists on the device. 1377 1378 Args: 1379 device_path: A string containing the absolute path to the file on the 1380 device, or an iterable of paths to check. 1381 as_root: Whether root permissions should be use to check for the existence 1382 of the given path(s). 1383 timeout: timeout in seconds 1384 retries: number of retries 1385 1386 Returns: 1387 True if the all given paths exist on the device, False otherwise. 1388 1389 Raises: 1390 CommandTimeoutError on timeout. 1391 DeviceUnreachableError on missing device. 1392 """ 1393 paths = device_paths 1394 if isinstance(paths, basestring): 1395 paths = (paths,) 1396 condition = ' -a '.join('-e %s' % cmd_helper.SingleQuote(p) for p in paths) 1397 cmd = 'test %s' % condition 1398 try: 1399 self.RunShellCommand(cmd, as_root=as_root, check_return=True, 1400 timeout=timeout, retries=retries) 1401 return True 1402 except device_errors.CommandFailedError: 1403 return False 1404 1405 @decorators.WithTimeoutAndRetriesFromInstance() 1406 def PullFile(self, device_path, host_path, timeout=None, retries=None): 1407 """Pull a file from the device. 1408 1409 Args: 1410 device_path: A string containing the absolute path of the file to pull 1411 from the device. 1412 host_path: A string containing the absolute path of the destination on 1413 the host. 1414 timeout: timeout in seconds 1415 retries: number of retries 1416 1417 Raises: 1418 CommandFailedError on failure. 1419 CommandTimeoutError on timeout. 1420 """ 1421 # Create the base dir if it doesn't exist already 1422 dirname = os.path.dirname(host_path) 1423 if dirname and not os.path.exists(dirname): 1424 os.makedirs(dirname) 1425 self.adb.Pull(device_path, host_path) 1426 1427 def _ReadFileWithPull(self, device_path): 1428 try: 1429 d = tempfile.mkdtemp() 1430 host_temp_path = os.path.join(d, 'tmp_ReadFileWithPull') 1431 self.adb.Pull(device_path, host_temp_path) 1432 with open(host_temp_path, 'r') as host_temp: 1433 return host_temp.read() 1434 finally: 1435 if os.path.exists(d): 1436 shutil.rmtree(d) 1437 1438 _LS_RE = re.compile( 1439 r'(?P<perms>\S+) (?:(?P<inodes>\d+) +)?(?P<owner>\S+) +(?P<group>\S+) +' 1440 r'(?:(?P<size>\d+) +)?(?P<date>\S+) +(?P<time>\S+) +(?P<name>.+)$') 1441 1442 @decorators.WithTimeoutAndRetriesFromInstance() 1443 def ReadFile(self, device_path, as_root=False, force_pull=False, 1444 timeout=None, retries=None): 1445 """Reads the contents of a file from the device. 1446 1447 Args: 1448 device_path: A string containing the absolute path of the file to read 1449 from the device. 1450 as_root: A boolean indicating whether the read should be executed with 1451 root privileges. 1452 force_pull: A boolean indicating whether to force the operation to be 1453 performed by pulling a file from the device. The default is, when the 1454 contents are short, to retrieve the contents using cat instead. 1455 timeout: timeout in seconds 1456 retries: number of retries 1457 1458 Returns: 1459 The contents of |device_path| as a string. Contents are intepreted using 1460 universal newlines, so the caller will see them encoded as '\n'. Also, 1461 all lines will be terminated. 1462 1463 Raises: 1464 AdbCommandFailedError if the file can't be read. 1465 CommandTimeoutError on timeout. 1466 DeviceUnreachableError on missing device. 1467 """ 1468 def get_size(path): 1469 # TODO(jbudorick): Implement a generic version of Stat() that handles 1470 # as_root=True, then switch this implementation to use that. 1471 ls_out = self.RunShellCommand(['ls', '-l', device_path], as_root=as_root, 1472 check_return=True) 1473 file_name = posixpath.basename(device_path) 1474 for line in ls_out: 1475 m = self._LS_RE.match(line) 1476 if m and file_name == posixpath.basename(m.group('name')): 1477 return int(m.group('size')) 1478 logging.warning('Could not determine size of %s.', device_path) 1479 return None 1480 1481 if (not force_pull 1482 and 0 < get_size(device_path) <= self._MAX_ADB_OUTPUT_LENGTH): 1483 return _JoinLines(self.RunShellCommand( 1484 ['cat', device_path], as_root=as_root, check_return=True)) 1485 elif as_root and self.NeedsSU(): 1486 with device_temp_file.DeviceTempFile(self.adb) as device_temp: 1487 cmd = 'SRC=%s DEST=%s;cp "$SRC" "$DEST" && chmod 666 "$DEST"' % ( 1488 cmd_helper.SingleQuote(device_path), 1489 cmd_helper.SingleQuote(device_temp.name)) 1490 self.RunShellCommand(cmd, as_root=True, check_return=True) 1491 return self._ReadFileWithPull(device_temp.name) 1492 else: 1493 return self._ReadFileWithPull(device_path) 1494 1495 def _WriteFileWithPush(self, device_path, contents): 1496 with tempfile.NamedTemporaryFile() as host_temp: 1497 host_temp.write(contents) 1498 host_temp.flush() 1499 self.adb.Push(host_temp.name, device_path) 1500 1501 @decorators.WithTimeoutAndRetriesFromInstance() 1502 def WriteFile(self, device_path, contents, as_root=False, force_push=False, 1503 timeout=None, retries=None): 1504 """Writes |contents| to a file on the device. 1505 1506 Args: 1507 device_path: A string containing the absolute path to the file to write 1508 on the device. 1509 contents: A string containing the data to write to the device. 1510 as_root: A boolean indicating whether the write should be executed with 1511 root privileges (if available). 1512 force_push: A boolean indicating whether to force the operation to be 1513 performed by pushing a file to the device. The default is, when the 1514 contents are short, to pass the contents using a shell script instead. 1515 timeout: timeout in seconds 1516 retries: number of retries 1517 1518 Raises: 1519 CommandFailedError if the file could not be written on the device. 1520 CommandTimeoutError on timeout. 1521 DeviceUnreachableError on missing device. 1522 """ 1523 if not force_push and len(contents) < self._MAX_ADB_COMMAND_LENGTH: 1524 # If the contents are small, for efficieny we write the contents with 1525 # a shell command rather than pushing a file. 1526 cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents), 1527 cmd_helper.SingleQuote(device_path)) 1528 self.RunShellCommand(cmd, as_root=as_root, check_return=True) 1529 elif as_root and self.NeedsSU(): 1530 # Adb does not allow to "push with su", so we first push to a temp file 1531 # on a safe location, and then copy it to the desired location with su. 1532 with device_temp_file.DeviceTempFile(self.adb) as device_temp: 1533 self._WriteFileWithPush(device_temp.name, contents) 1534 # Here we need 'cp' rather than 'mv' because the temp and 1535 # destination files might be on different file systems (e.g. 1536 # on internal storage and an external sd card). 1537 self.RunShellCommand(['cp', device_temp.name, device_path], 1538 as_root=True, check_return=True) 1539 else: 1540 # If root is not needed, we can push directly to the desired location. 1541 self._WriteFileWithPush(device_path, contents) 1542 1543 @decorators.WithTimeoutAndRetriesFromInstance() 1544 def Ls(self, device_path, timeout=None, retries=None): 1545 """Lists the contents of a directory on the device. 1546 1547 Args: 1548 device_path: A string containing the path of the directory on the device 1549 to list. 1550 timeout: timeout in seconds 1551 retries: number of retries 1552 1553 Returns: 1554 A list of pairs (filename, stat) for each file found in the directory, 1555 where the stat object has the properties: st_mode, st_size, and st_time. 1556 1557 Raises: 1558 AdbCommandFailedError if |device_path| does not specify a valid and 1559 accessible directory in the device. 1560 CommandTimeoutError on timeout. 1561 DeviceUnreachableError on missing device. 1562 """ 1563 return self.adb.Ls(device_path) 1564 1565 @decorators.WithTimeoutAndRetriesFromInstance() 1566 def Stat(self, device_path, timeout=None, retries=None): 1567 """Get the stat attributes of a file or directory on the device. 1568 1569 Args: 1570 device_path: A string containing the path of from which to get attributes 1571 on the device. 1572 timeout: timeout in seconds 1573 retries: number of retries 1574 1575 Returns: 1576 A stat object with the properties: st_mode, st_size, and st_time 1577 1578 Raises: 1579 CommandFailedError if device_path cannot be found on the device. 1580 CommandTimeoutError on timeout. 1581 DeviceUnreachableError on missing device. 1582 """ 1583 dirname, target = device_path.rsplit('/', 1) 1584 for filename, stat in self.adb.Ls(dirname): 1585 if filename == target: 1586 return stat 1587 raise device_errors.CommandFailedError( 1588 'Cannot find file or directory: %r' % device_path, str(self)) 1589 1590 @decorators.WithTimeoutAndRetriesFromInstance() 1591 def SetJavaAsserts(self, enabled, timeout=None, retries=None): 1592 """Enables or disables Java asserts. 1593 1594 Args: 1595 enabled: A boolean indicating whether Java asserts should be enabled 1596 or disabled. 1597 timeout: timeout in seconds 1598 retries: number of retries 1599 1600 Returns: 1601 True if the device-side property changed and a restart is required as a 1602 result, False otherwise. 1603 1604 Raises: 1605 CommandTimeoutError on timeout. 1606 """ 1607 def find_property(lines, property_name): 1608 for index, line in enumerate(lines): 1609 if line.strip() == '': 1610 continue 1611 key_value = tuple(s.strip() for s in line.split('=', 1)) 1612 if len(key_value) != 2: 1613 continue 1614 key, value = key_value 1615 if key == property_name: 1616 return index, value 1617 return None, '' 1618 1619 new_value = 'all' if enabled else '' 1620 1621 # First ensure the desired property is persisted. 1622 try: 1623 properties = self.ReadFile(self.LOCAL_PROPERTIES_PATH).splitlines() 1624 except device_errors.CommandFailedError: 1625 properties = [] 1626 index, value = find_property(properties, self.JAVA_ASSERT_PROPERTY) 1627 if new_value != value: 1628 if new_value: 1629 new_line = '%s=%s' % (self.JAVA_ASSERT_PROPERTY, new_value) 1630 if index is None: 1631 properties.append(new_line) 1632 else: 1633 properties[index] = new_line 1634 else: 1635 assert index is not None # since new_value == '' and new_value != value 1636 properties.pop(index) 1637 self.WriteFile(self.LOCAL_PROPERTIES_PATH, _JoinLines(properties)) 1638 1639 # Next, check the current runtime value is what we need, and 1640 # if not, set it and report that a reboot is required. 1641 value = self.GetProp(self.JAVA_ASSERT_PROPERTY) 1642 if new_value != value: 1643 self.SetProp(self.JAVA_ASSERT_PROPERTY, new_value) 1644 return True 1645 else: 1646 return False 1647 1648 def GetLanguage(self, cache=False): 1649 """Returns the language setting on the device. 1650 Args: 1651 cache: Whether to use cached properties when available. 1652 """ 1653 return self.GetProp('persist.sys.language', cache=cache) 1654 1655 def GetCountry(self, cache=False): 1656 """Returns the country setting on the device. 1657 1658 Args: 1659 cache: Whether to use cached properties when available. 1660 """ 1661 return self.GetProp('persist.sys.country', cache=cache) 1662 1663 @property 1664 def screen_density(self): 1665 """Returns the screen density of the device.""" 1666 DPI_TO_DENSITY = { 1667 120: 'ldpi', 1668 160: 'mdpi', 1669 240: 'hdpi', 1670 320: 'xhdpi', 1671 480: 'xxhdpi', 1672 640: 'xxxhdpi', 1673 } 1674 return DPI_TO_DENSITY.get(self.pixel_density, 'tvdpi') 1675 1676 @property 1677 def pixel_density(self): 1678 return int(self.GetProp('ro.sf.lcd_density', cache=True)) 1679 1680 @property 1681 def build_description(self): 1682 """Returns the build description of the system. 1683 1684 For example: 1685 nakasi-user 4.4.4 KTU84P 1227136 release-keys 1686 """ 1687 return self.GetProp('ro.build.description', cache=True) 1688 1689 @property 1690 def build_fingerprint(self): 1691 """Returns the build fingerprint of the system. 1692 1693 For example: 1694 google/nakasi/grouper:4.4.4/KTU84P/1227136:user/release-keys 1695 """ 1696 return self.GetProp('ro.build.fingerprint', cache=True) 1697 1698 @property 1699 def build_id(self): 1700 """Returns the build ID of the system (e.g. 'KTU84P').""" 1701 return self.GetProp('ro.build.id', cache=True) 1702 1703 @property 1704 def build_product(self): 1705 """Returns the build product of the system (e.g. 'grouper').""" 1706 return self.GetProp('ro.build.product', cache=True) 1707 1708 @property 1709 def build_type(self): 1710 """Returns the build type of the system (e.g. 'user').""" 1711 return self.GetProp('ro.build.type', cache=True) 1712 1713 @property 1714 def build_version_sdk(self): 1715 """Returns the build version sdk of the system as a number (e.g. 19). 1716 1717 For version code numbers see: 1718 http://developer.android.com/reference/android/os/Build.VERSION_CODES.html 1719 1720 For named constants see devil.android.sdk.version_codes 1721 1722 Raises: 1723 CommandFailedError if the build version sdk is not a number. 1724 """ 1725 value = self.GetProp('ro.build.version.sdk', cache=True) 1726 try: 1727 return int(value) 1728 except ValueError: 1729 raise device_errors.CommandFailedError( 1730 'Invalid build version sdk: %r' % value) 1731 1732 @property 1733 def product_cpu_abi(self): 1734 """Returns the product cpu abi of the device (e.g. 'armeabi-v7a').""" 1735 return self.GetProp('ro.product.cpu.abi', cache=True) 1736 1737 @property 1738 def product_model(self): 1739 """Returns the name of the product model (e.g. 'Nexus 7').""" 1740 return self.GetProp('ro.product.model', cache=True) 1741 1742 @property 1743 def product_name(self): 1744 """Returns the product name of the device (e.g. 'nakasi').""" 1745 return self.GetProp('ro.product.name', cache=True) 1746 1747 @property 1748 def product_board(self): 1749 """Returns the product board name of the device (e.g. 'shamu').""" 1750 return self.GetProp('ro.product.board', cache=True) 1751 1752 def GetProp(self, property_name, cache=False, timeout=DEFAULT, 1753 retries=DEFAULT): 1754 """Gets a property from the device. 1755 1756 Args: 1757 property_name: A string containing the name of the property to get from 1758 the device. 1759 cache: Whether to use cached properties when available. 1760 timeout: timeout in seconds 1761 retries: number of retries 1762 1763 Returns: 1764 The value of the device's |property_name| property. 1765 1766 Raises: 1767 CommandTimeoutError on timeout. 1768 """ 1769 assert isinstance(property_name, basestring), ( 1770 "property_name is not a string: %r" % property_name) 1771 1772 prop_cache = self._cache['getprop'] 1773 if cache: 1774 if property_name not in prop_cache: 1775 # It takes ~120ms to query a single property, and ~130ms to query all 1776 # properties. So, when caching we always query all properties. 1777 output = self.RunShellCommand( 1778 ['getprop'], check_return=True, large_output=True, 1779 timeout=self._default_timeout if timeout is DEFAULT else timeout, 1780 retries=self._default_retries if retries is DEFAULT else retries) 1781 prop_cache.clear() 1782 for key, value in _GETPROP_RE.findall(''.join(output)): 1783 prop_cache[key] = value 1784 if property_name not in prop_cache: 1785 prop_cache[property_name] = '' 1786 else: 1787 # timeout and retries are handled down at run shell, because we don't 1788 # want to apply them in the other branch when reading from the cache 1789 value = self.RunShellCommand( 1790 ['getprop', property_name], single_line=True, check_return=True, 1791 timeout=self._default_timeout if timeout is DEFAULT else timeout, 1792 retries=self._default_retries if retries is DEFAULT else retries) 1793 prop_cache[property_name] = value 1794 return prop_cache[property_name] 1795 1796 @decorators.WithTimeoutAndRetriesFromInstance() 1797 def SetProp(self, property_name, value, check=False, timeout=None, 1798 retries=None): 1799 """Sets a property on the device. 1800 1801 Args: 1802 property_name: A string containing the name of the property to set on 1803 the device. 1804 value: A string containing the value to set to the property on the 1805 device. 1806 check: A boolean indicating whether to check that the property was 1807 successfully set on the device. 1808 timeout: timeout in seconds 1809 retries: number of retries 1810 1811 Raises: 1812 CommandFailedError if check is true and the property was not correctly 1813 set on the device (e.g. because it is not rooted). 1814 CommandTimeoutError on timeout. 1815 """ 1816 assert isinstance(property_name, basestring), ( 1817 "property_name is not a string: %r" % property_name) 1818 assert isinstance(value, basestring), "value is not a string: %r" % value 1819 1820 self.RunShellCommand(['setprop', property_name, value], check_return=True) 1821 prop_cache = self._cache['getprop'] 1822 if property_name in prop_cache: 1823 del prop_cache[property_name] 1824 # TODO(perezju) remove the option and make the check mandatory, but using a 1825 # single shell script to both set- and getprop. 1826 if check and value != self.GetProp(property_name, cache=False): 1827 raise device_errors.CommandFailedError( 1828 'Unable to set property %r on the device to %r' 1829 % (property_name, value), str(self)) 1830 1831 @decorators.WithTimeoutAndRetriesFromInstance() 1832 def GetABI(self, timeout=None, retries=None): 1833 """Gets the device main ABI. 1834 1835 Args: 1836 timeout: timeout in seconds 1837 retries: number of retries 1838 1839 Returns: 1840 The device's main ABI name. 1841 1842 Raises: 1843 CommandTimeoutError on timeout. 1844 """ 1845 return self.GetProp('ro.product.cpu.abi', cache=True) 1846 1847 @decorators.WithTimeoutAndRetriesFromInstance() 1848 def GetPids(self, process_name, timeout=None, retries=None): 1849 """Returns the PIDs of processes with the given name. 1850 1851 Note that the |process_name| is often the package name. 1852 1853 Args: 1854 process_name: A string containing the process name to get the PIDs for. 1855 timeout: timeout in seconds 1856 retries: number of retries 1857 1858 Returns: 1859 A dict mapping process name to a list of PIDs for each process that 1860 contained the provided |process_name|. 1861 1862 Raises: 1863 CommandTimeoutError on timeout. 1864 DeviceUnreachableError on missing device. 1865 """ 1866 procs_pids = collections.defaultdict(list) 1867 try: 1868 ps_output = self._RunPipedShellCommand( 1869 'ps | grep -F %s' % cmd_helper.SingleQuote(process_name)) 1870 except device_errors.AdbShellCommandFailedError as e: 1871 if e.status and isinstance(e.status, list) and not e.status[0]: 1872 # If ps succeeded but grep failed, there were no processes with the 1873 # given name. 1874 return procs_pids 1875 else: 1876 raise 1877 1878 for line in ps_output: 1879 try: 1880 ps_data = line.split() 1881 if process_name in ps_data[-1]: 1882 pid, process = ps_data[1], ps_data[-1] 1883 procs_pids[process].append(pid) 1884 except IndexError: 1885 pass 1886 return procs_pids 1887 1888 @decorators.WithTimeoutAndRetriesFromInstance() 1889 def TakeScreenshot(self, host_path=None, timeout=None, retries=None): 1890 """Takes a screenshot of the device. 1891 1892 Args: 1893 host_path: A string containing the path on the host to save the 1894 screenshot to. If None, a file name in the current 1895 directory will be generated. 1896 timeout: timeout in seconds 1897 retries: number of retries 1898 1899 Returns: 1900 The name of the file on the host to which the screenshot was saved. 1901 1902 Raises: 1903 CommandFailedError on failure. 1904 CommandTimeoutError on timeout. 1905 DeviceUnreachableError on missing device. 1906 """ 1907 if not host_path: 1908 host_path = os.path.abspath('screenshot-%s-%s.png' % ( 1909 self.adb.GetDeviceSerial(), _GetTimeStamp())) 1910 with device_temp_file.DeviceTempFile(self.adb, suffix='.png') as device_tmp: 1911 self.RunShellCommand(['/system/bin/screencap', '-p', device_tmp.name], 1912 check_return=True) 1913 self.PullFile(device_tmp.name, host_path) 1914 return host_path 1915 1916 @decorators.WithTimeoutAndRetriesFromInstance() 1917 def GetMemoryUsageForPid(self, pid, timeout=None, retries=None): 1918 """Gets the memory usage for the given PID. 1919 1920 Args: 1921 pid: PID of the process. 1922 timeout: timeout in seconds 1923 retries: number of retries 1924 1925 Returns: 1926 A dict containing memory usage statistics for the PID. May include: 1927 Size, Rss, Pss, Shared_Clean, Shared_Dirty, Private_Clean, 1928 Private_Dirty, VmHWM 1929 1930 Raises: 1931 CommandTimeoutError on timeout. 1932 """ 1933 result = collections.defaultdict(int) 1934 1935 try: 1936 result.update(self._GetMemoryUsageForPidFromSmaps(pid)) 1937 except device_errors.CommandFailedError: 1938 logging.exception('Error getting memory usage from smaps') 1939 1940 try: 1941 result.update(self._GetMemoryUsageForPidFromStatus(pid)) 1942 except device_errors.CommandFailedError: 1943 logging.exception('Error getting memory usage from status') 1944 1945 return result 1946 1947 @decorators.WithTimeoutAndRetriesFromInstance() 1948 def DismissCrashDialogIfNeeded(self, timeout=None, retries=None): 1949 """Dismiss the error/ANR dialog if present. 1950 1951 Returns: Name of the crashed package if a dialog is focused, 1952 None otherwise. 1953 """ 1954 def _FindFocusedWindow(): 1955 match = None 1956 # TODO(jbudorick): Try to grep the output on the device instead of using 1957 # large_output if/when DeviceUtils exposes a public interface for piped 1958 # shell command handling. 1959 for line in self.RunShellCommand(['dumpsys', 'window', 'windows'], 1960 check_return=True, large_output=True): 1961 match = re.match(_CURRENT_FOCUS_CRASH_RE, line) 1962 if match: 1963 break 1964 return match 1965 1966 match = _FindFocusedWindow() 1967 if not match: 1968 return None 1969 package = match.group(2) 1970 logging.warning('Trying to dismiss %s dialog for %s', *match.groups()) 1971 self.SendKeyEvent(keyevent.KEYCODE_DPAD_RIGHT) 1972 self.SendKeyEvent(keyevent.KEYCODE_DPAD_RIGHT) 1973 self.SendKeyEvent(keyevent.KEYCODE_ENTER) 1974 match = _FindFocusedWindow() 1975 if match: 1976 logging.error('Still showing a %s dialog for %s', *match.groups()) 1977 return package 1978 1979 def _GetMemoryUsageForPidFromSmaps(self, pid): 1980 SMAPS_COLUMNS = ( 1981 'Size', 'Rss', 'Pss', 'Shared_Clean', 'Shared_Dirty', 'Private_Clean', 1982 'Private_Dirty') 1983 1984 showmap_out = self._RunPipedShellCommand( 1985 'showmap %d | grep TOTAL' % int(pid), as_root=True) 1986 1987 split_totals = showmap_out[-1].split() 1988 if (not split_totals 1989 or len(split_totals) != 9 1990 or split_totals[-1] != 'TOTAL'): 1991 raise device_errors.CommandFailedError( 1992 'Invalid output from showmap: %s' % '\n'.join(showmap_out)) 1993 1994 return dict(itertools.izip(SMAPS_COLUMNS, (int(n) for n in split_totals))) 1995 1996 def _GetMemoryUsageForPidFromStatus(self, pid): 1997 for line in self.ReadFile( 1998 '/proc/%s/status' % str(pid), as_root=True).splitlines(): 1999 if line.startswith('VmHWM:'): 2000 return {'VmHWM': int(line.split()[1])} 2001 raise device_errors.CommandFailedError( 2002 'Could not find memory peak value for pid %s', str(pid)) 2003 2004 def GetLogcatMonitor(self, *args, **kwargs): 2005 """Returns a new LogcatMonitor associated with this device. 2006 2007 Parameters passed to this function are passed directly to 2008 |logcat_monitor.LogcatMonitor| and are documented there. 2009 """ 2010 return logcat_monitor.LogcatMonitor(self.adb, *args, **kwargs) 2011 2012 def GetClientCache(self, client_name): 2013 """Returns client cache.""" 2014 if client_name not in self._client_caches: 2015 self._client_caches[client_name] = {} 2016 return self._client_caches[client_name] 2017 2018 def _ClearCache(self): 2019 """Clears all caches.""" 2020 for client in self._client_caches: 2021 self._client_caches[client].clear() 2022 self._cache = { 2023 # Map of packageId -> list of on-device .apk paths 2024 'package_apk_paths': {}, 2025 # Set of packageId that were loaded from LoadCacheData and not yet 2026 # verified. 2027 'package_apk_paths_to_verify': set(), 2028 # Map of packageId -> set of on-device .apk checksums 2029 'package_apk_checksums': {}, 2030 # Map of property_name -> value 2031 'getprop': {}, 2032 # Map of device_path -> [ignore_other_files, map of path->checksum] 2033 'device_path_checksums': {}, 2034 } 2035 2036 def LoadCacheData(self, data): 2037 """Initializes the cache from data created using DumpCacheData.""" 2038 obj = json.loads(data) 2039 self._cache['package_apk_paths'] = obj.get('package_apk_paths', {}) 2040 # When using a cache across script invokations, verify that apps have 2041 # not been uninstalled. 2042 self._cache['package_apk_paths_to_verify'] = set( 2043 self._cache['package_apk_paths'].iterkeys()) 2044 2045 package_apk_checksums = obj.get('package_apk_checksums', {}) 2046 for k, v in package_apk_checksums.iteritems(): 2047 package_apk_checksums[k] = set(v) 2048 self._cache['package_apk_checksums'] = package_apk_checksums 2049 device_path_checksums = obj.get('device_path_checksums', {}) 2050 self._cache['device_path_checksums'] = device_path_checksums 2051 2052 def DumpCacheData(self): 2053 """Dumps the current cache state to a string.""" 2054 obj = {} 2055 obj['package_apk_paths'] = self._cache['package_apk_paths'] 2056 obj['package_apk_checksums'] = self._cache['package_apk_checksums'] 2057 # JSON can't handle sets. 2058 for k, v in obj['package_apk_checksums'].iteritems(): 2059 obj['package_apk_checksums'][k] = list(v) 2060 obj['device_path_checksums'] = self._cache['device_path_checksums'] 2061 return json.dumps(obj, separators=(',', ':')) 2062 2063 @classmethod 2064 def parallel(cls, devices, async=False): 2065 """Creates a Parallelizer to operate over the provided list of devices. 2066 2067 Args: 2068 devices: A list of either DeviceUtils instances or objects from 2069 from which DeviceUtils instances can be constructed. If None, 2070 all attached devices will be used. 2071 async: If true, returns a Parallelizer that runs operations 2072 asynchronously. 2073 2074 Returns: 2075 A Parallelizer operating over |devices|. 2076 2077 Raises: 2078 device_errors.NoDevicesError: If no devices are passed. 2079 """ 2080 if not devices: 2081 raise device_errors.NoDevicesError() 2082 2083 devices = [d if isinstance(d, cls) else cls(d) for d in devices] 2084 if async: 2085 return parallelizer.Parallelizer(devices) 2086 else: 2087 return parallelizer.SyncParallelizer(devices) 2088 2089 @classmethod 2090 def HealthyDevices(cls, blacklist=None, **kwargs): 2091 blacklisted_devices = blacklist.Read() if blacklist else [] 2092 2093 def blacklisted(adb): 2094 if adb.GetDeviceSerial() in blacklisted_devices: 2095 logging.warning('Device %s is blacklisted.', adb.GetDeviceSerial()) 2096 return True 2097 return False 2098 2099 devices = [] 2100 for adb in adb_wrapper.AdbWrapper.Devices(): 2101 if not blacklisted(adb): 2102 devices.append(cls(_CreateAdbWrapper(adb), **kwargs)) 2103 return devices 2104 2105 @decorators.WithTimeoutAndRetriesFromInstance() 2106 def RestartAdbd(self, timeout=None, retries=None): 2107 logging.info('Restarting adbd on device.') 2108 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script: 2109 self.WriteFile(script.name, _RESTART_ADBD_SCRIPT) 2110 self.RunShellCommand(['source', script.name], as_root=True) 2111 self.adb.WaitForDevice() 2112 2113 @decorators.WithTimeoutAndRetriesFromInstance() 2114 def GrantPermissions(self, package, permissions, timeout=None, retries=None): 2115 # Permissions only need to be set on M and above because of the changes to 2116 # the permission model. 2117 if not permissions or self.build_version_sdk < version_codes.MARSHMALLOW: 2118 return 2119 logging.info('Setting permissions for %s.', package) 2120 permissions = [p for p in permissions if p not in _PERMISSIONS_BLACKLIST] 2121 if ('android.permission.WRITE_EXTERNAL_STORAGE' in permissions 2122 and 'android.permission.READ_EXTERNAL_STORAGE' not in permissions): 2123 permissions.append('android.permission.READ_EXTERNAL_STORAGE') 2124 cmd = '&&'.join('pm grant %s %s' % (package, p) for p in permissions) 2125 if cmd: 2126 output = self.RunShellCommand(cmd, check_return=True) 2127 if output: 2128 logging.warning('Possible problem when granting permissions. Blacklist ' 2129 'may need to be updated.') 2130 for line in output: 2131 logging.warning(' %s', line) 2132 2133 @decorators.WithTimeoutAndRetriesFromInstance() 2134 def IsScreenOn(self, timeout=None, retries=None): 2135 """Determines if screen is on. 2136 2137 Dumpsys input_method exposes screen on/off state. Below is an explination of 2138 the states. 2139 2140 Pre-L: 2141 On: mScreenOn=true 2142 Off: mScreenOn=false 2143 L+: 2144 On: mInteractive=true 2145 Off: mInteractive=false 2146 2147 Returns: 2148 True if screen is on, false if it is off. 2149 2150 Raises: 2151 device_errors.CommandFailedError: If screen state cannot be found. 2152 """ 2153 if self.build_version_sdk < version_codes.LOLLIPOP: 2154 input_check = 'mScreenOn' 2155 check_value = 'mScreenOn=true' 2156 else: 2157 input_check = 'mInteractive' 2158 check_value = 'mInteractive=true' 2159 dumpsys_out = self._RunPipedShellCommand( 2160 'dumpsys input_method | grep %s' % input_check) 2161 if not dumpsys_out: 2162 raise device_errors.CommandFailedError( 2163 'Unable to detect screen state', str(self)) 2164 return check_value in dumpsys_out[0] 2165 2166 @decorators.WithTimeoutAndRetriesFromInstance() 2167 def SetScreen(self, on, timeout=None, retries=None): 2168 """Turns screen on and off. 2169 2170 Args: 2171 on: bool to decide state to switch to. True = on False = off. 2172 """ 2173 def screen_test(): 2174 return self.IsScreenOn() == on 2175 2176 if screen_test(): 2177 logging.info('Screen already in expected state.') 2178 return 2179 self.RunShellCommand('input keyevent 26') 2180 timeout_retry.WaitFor(screen_test, wait_period=1) 2181