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