1# Copyright 2014 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5"""Provides a variety of device interactions based on adb. 6 7Eventually, this will be based on adb_wrapper. 8""" 9# pylint: disable=unused-argument 10 11import calendar 12import collections 13import contextlib 14import fnmatch 15import json 16import logging 17import math 18import os 19import posixpath 20import pprint 21import random 22import re 23import shutil 24import stat 25import sys 26import tempfile 27import time 28import threading 29import uuid 30 31from devil import base_error 32from devil import devil_env 33from devil.utils import cmd_helper 34from devil.android import apk_helper 35from devil.android import device_signal 36from devil.android import decorators 37from devil.android import device_errors 38from devil.android import device_temp_file 39from devil.android import install_commands 40from devil.android import logcat_monitor 41from devil.android import md5sum 42from devil.android.sdk import adb_wrapper 43from devil.android.sdk import intent 44from devil.android.sdk import keyevent 45from devil.android.sdk import split_select 46from devil.android.sdk import version_codes 47from devil.utils import host_utils 48from devil.utils import parallelizer 49from devil.utils import reraiser_thread 50from devil.utils import timeout_retry 51from devil.utils import zip_utils 52 53from py_utils import tempfile_ext 54 55try: 56 from devil.utils import reset_usb 57except ImportError: 58 # Fail silently if we can't import reset_usb. We're likely on windows. 59 reset_usb = None 60 61logger = logging.getLogger(__name__) 62 63_DEFAULT_TIMEOUT = 30 64_DEFAULT_RETRIES = 3 65 66# A sentinel object for default values 67# TODO(jbudorick,perezju): revisit how default values are handled by 68# the timeout_retry decorators. 69DEFAULT = object() 70 71# A sentinel object to require that calls to RunShellCommand force running the 72# command with su even if the device has been rooted. To use, pass into the 73# as_root param. 74_FORCE_SU = object() 75 76_RECURSIVE_DIRECTORY_LIST_SCRIPT = """ 77 function list_subdirs() { 78 for f in "$1"/* ; 79 do 80 if [ -d "$f" ] ; 81 then 82 if [ "$f" == "." ] || [ "$f" == ".." ] ; 83 then 84 continue ; 85 fi ; 86 echo "$f" ; 87 list_subdirs "$f" ; 88 fi ; 89 done ; 90 } ; 91 list_subdirs %s 92""" 93 94_RESTART_ADBD_SCRIPT = """ 95 trap '' HUP 96 trap '' TERM 97 trap '' PIPE 98 function restart() { 99 stop adbd 100 start adbd 101 } 102 restart & 103""" 104 105# Not all permissions can be set. 106_PERMISSIONS_BLACKLIST_RE = re.compile('|'.join(fnmatch.translate(p) for p in [ 107 'android.permission.ACCESS_LOCATION_EXTRA_COMMANDS', 108 'android.permission.ACCESS_MOCK_LOCATION', 109 'android.permission.ACCESS_NETWORK_STATE', 110 'android.permission.ACCESS_NOTIFICATION_POLICY', 111 'android.permission.ACCESS_VR_STATE', 112 'android.permission.ACCESS_WIFI_STATE', 113 'android.permission.AUTHENTICATE_ACCOUNTS', 114 'android.permission.BLUETOOTH', 115 'android.permission.BLUETOOTH_ADMIN', 116 'android.permission.BROADCAST_STICKY', 117 'android.permission.CHANGE_NETWORK_STATE', 118 'android.permission.CHANGE_WIFI_MULTICAST_STATE', 119 'android.permission.CHANGE_WIFI_STATE', 120 'android.permission.DISABLE_KEYGUARD', 121 'android.permission.DOWNLOAD_WITHOUT_NOTIFICATION', 122 'android.permission.EXPAND_STATUS_BAR', 123 'android.permission.FOREGROUND_SERVICE', 124 'android.permission.GET_PACKAGE_SIZE', 125 'android.permission.INSTALL_SHORTCUT', 126 'android.permission.INJECT_EVENTS', 127 'android.permission.INTERNET', 128 'android.permission.KILL_BACKGROUND_PROCESSES', 129 'android.permission.MANAGE_ACCOUNTS', 130 'android.permission.MODIFY_AUDIO_SETTINGS', 131 'android.permission.NFC', 132 'android.permission.READ_SYNC_SETTINGS', 133 'android.permission.READ_SYNC_STATS', 134 'android.permission.RECEIVE_BOOT_COMPLETED', 135 'android.permission.RECORD_VIDEO', 136 'android.permission.REORDER_TASKS', 137 'android.permission.REQUEST_INSTALL_PACKAGES', 138 'android.permission.RESTRICTED_VR_ACCESS', 139 'android.permission.RUN_INSTRUMENTATION', 140 'android.permission.SET_ALARM', 141 'android.permission.SET_TIME_ZONE', 142 'android.permission.SET_WALLPAPER', 143 'android.permission.SET_WALLPAPER_HINTS', 144 'android.permission.TRANSMIT_IR', 145 'android.permission.USE_CREDENTIALS', 146 'android.permission.USE_FINGERPRINT', 147 'android.permission.VIBRATE', 148 'android.permission.WAKE_LOCK', 149 'android.permission.WRITE_SYNC_SETTINGS', 150 'com.android.browser.permission.READ_HISTORY_BOOKMARKS', 151 'com.android.browser.permission.WRITE_HISTORY_BOOKMARKS', 152 'com.android.launcher.permission.INSTALL_SHORTCUT', 153 'com.chrome.permission.DEVICE_EXTRAS', 154 'com.google.android.apps.now.CURRENT_ACCOUNT_ACCESS', 155 'com.google.android.c2dm.permission.RECEIVE', 156 'com.google.android.providers.gsf.permission.READ_GSERVICES', 157 'com.google.vr.vrcore.permission.VRCORE_INTERNAL', 158 'com.sec.enterprise.knox.MDM_CONTENT_PROVIDER', 159 '*.permission.C2D_MESSAGE', 160 '*.permission.READ_WRITE_BOOKMARK_FOLDERS', 161 '*.TOS_ACKED', 162])) 163_SHELL_OUTPUT_SEPARATOR = '~X~' 164_PERMISSIONS_EXCEPTION_RE = re.compile( 165 r'java\.lang\.\w+Exception: .*$', re.MULTILINE) 166 167_CURRENT_FOCUS_CRASH_RE = re.compile( 168 r'\s*mCurrentFocus.*Application (Error|Not Responding): (\S+)}') 169 170_GETPROP_RE = re.compile(r'\[(.*?)\]: \[(.*?)\]') 171 172# Regex to parse the long (-l) output of 'ls' command, c.f. 173# https://github.com/landley/toybox/blob/master/toys/posix/ls.c#L446 174_LONG_LS_OUTPUT_RE = re.compile( 175 r'(?P<st_mode>[\w-]{10})\s+' # File permissions 176 r'(?:(?P<st_nlink>\d+)\s+)?' # Number of links (optional) 177 r'(?P<st_owner>\w+)\s+' # Name of owner 178 r'(?P<st_group>\w+)\s+' # Group of owner 179 r'(?:' # Either ... 180 r'(?P<st_rdev_major>\d+),\s+' # Device major, and 181 r'(?P<st_rdev_minor>\d+)\s+' # Device minor 182 r'|' # .. or 183 r'(?P<st_size>\d+)\s+' # Size in bytes 184 r')?' # .. or nothing 185 r'(?P<st_mtime>\d{4}-\d\d-\d\d \d\d:\d\d)\s+' # Modification date/time 186 r'(?P<filename>.+?)' # File name 187 r'(?: -> (?P<symbolic_link_to>.+))?' # Symbolic link (optional) 188 r'$' # End of string 189) 190_LS_DATE_FORMAT = '%Y-%m-%d %H:%M' 191_FILE_MODE_RE = re.compile(r'[dbclps-](?:[r-][w-][xSs-]){2}[r-][w-][xTt-]$') 192_FILE_MODE_KIND = { 193 'd': stat.S_IFDIR, 'b': stat.S_IFBLK, 'c': stat.S_IFCHR, 194 'l': stat.S_IFLNK, 'p': stat.S_IFIFO, 's': stat.S_IFSOCK, 195 '-': stat.S_IFREG} 196_FILE_MODE_PERMS = [ 197 stat.S_IRUSR, stat.S_IWUSR, stat.S_IXUSR, 198 stat.S_IRGRP, stat.S_IWGRP, stat.S_IXGRP, 199 stat.S_IROTH, stat.S_IWOTH, stat.S_IXOTH, 200] 201_FILE_MODE_SPECIAL = [ 202 ('s', stat.S_ISUID), 203 ('s', stat.S_ISGID), 204 ('t', stat.S_ISVTX), 205] 206_PS_COLUMNS = { 207 'pid': 1, 208 'ppid': 2, 209 'name': -1 210} 211_SELINUX_MODE = { 212 'enforcing': True, 213 'permissive': False, 214 'disabled': None 215} 216# Some devices require different logic for checking if root is necessary 217_SPECIAL_ROOT_DEVICE_LIST = [ 218 'marlin', # Pixel XL 219 'sailfish', # Pixel 220 'taimen', # Pixel 2 XL 221 'vega', # Lenovo Mirage Solo 222 'walleye', # Pixel 2 223 'crosshatch', # Pixel 3 XL 224 'blueline', # Pixel 3 225] 226_SPECIAL_ROOT_DEVICE_LIST += ['aosp_%s' % _d for _d in 227 _SPECIAL_ROOT_DEVICE_LIST] 228 229_IMEI_RE = re.compile(r' Device ID = (.+)$') 230# The following regex is used to match result parcels like: 231""" 232Result: Parcel( 233 0x00000000: 00000000 0000000f 00350033 00360033 '........3.5.3.6.' 234 0x00000010: 00360032 00370030 00300032 00300039 '2.6.0.7.2.0.9.0.' 235 0x00000020: 00380033 00000039 '3.8.9... ') 236""" 237_PARCEL_RESULT_RE = re.compile( 238 r'0x[0-9a-f]{8}\: (?:[0-9a-f]{8}\s+){1,4}\'(.{16})\'') 239_EBUSY_RE = re.compile( 240 r'mkdir failed for ([^,]*), Device or resource busy') 241 242# http://bit.ly/2WLZhUF added a timeout to adb wait-for-device. We sometimes 243# want to wait longer than the implicit call within adb root allows. 244_WAIT_FOR_DEVICE_TIMEOUT_STR = 'timeout expired while waiting for device' 245 246_WEBVIEW_SYSUPDATE_CURRENT_PKG_RE = re.compile( 247 r'Current WebView package.*:.*\(([a-z.]*),') 248_WEBVIEW_SYSUPDATE_NULL_PKG_RE = re.compile( 249 r'Current WebView package is null') 250_WEBVIEW_SYSUPDATE_FALLBACK_LOGIC_RE = re.compile( 251 r'Fallback logic enabled: (true|false)') 252_WEBVIEW_SYSUPDATE_PACKAGE_INSTALLED_RE = re.compile( 253 r'(?:Valid|Invalid) package\s+(\S+)\s+\(.*\),?\s+(.*)$') 254_WEBVIEW_SYSUPDATE_PACKAGE_NOT_INSTALLED_RE = re.compile( 255 r'(\S+)\s+(is NOT installed\.)') 256_WEBVIEW_SYSUPDATE_MIN_VERSION_CODE = re.compile( 257 r'Minimum WebView version code: (\d+)') 258 259_GOOGLE_FEATURES_RE = re.compile(r'^\s*com\.google\.') 260 261PS_COLUMNS = ('name', 'pid', 'ppid') 262ProcessInfo = collections.namedtuple('ProcessInfo', PS_COLUMNS) 263 264 265@decorators.WithExplicitTimeoutAndRetries( 266 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES) 267def GetAVDs(): 268 """Returns a list of Android Virtual Devices. 269 270 Returns: 271 A list containing the configured AVDs. 272 """ 273 lines = cmd_helper.GetCmdOutput([ 274 os.path.join(devil_env.config.LocalPath('android_sdk'), 275 'tools', 'android'), 276 'list', 'avd']).splitlines() 277 avds = [] 278 for line in lines: 279 if 'Name:' not in line: 280 continue 281 key, value = (s.strip() for s in line.split(':', 1)) 282 if key == 'Name': 283 avds.append(value) 284 return avds 285 286 287@decorators.WithExplicitTimeoutAndRetries( 288 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES) 289def RestartServer(): 290 """Restarts the adb server. 291 292 Raises: 293 CommandFailedError if we fail to kill or restart the server. 294 """ 295 def adb_killed(): 296 return not adb_wrapper.AdbWrapper.IsServerOnline() 297 298 def adb_started(): 299 return adb_wrapper.AdbWrapper.IsServerOnline() 300 301 adb_wrapper.AdbWrapper.KillServer() 302 if not timeout_retry.WaitFor(adb_killed, wait_period=1, max_tries=5): 303 # TODO(perezju): raise an exception after fixng http://crbug.com/442319 304 logger.warning('Failed to kill adb server') 305 adb_wrapper.AdbWrapper.StartServer() 306 if not timeout_retry.WaitFor(adb_started, wait_period=1, max_tries=5): 307 raise device_errors.CommandFailedError('Failed to start adb server') 308 309 310def _ParseModeString(mode_str): 311 """Parse a mode string, e.g. 'drwxrwxrwx', into a st_mode value. 312 313 Effectively the reverse of |mode_to_string| in, e.g.: 314 https://github.com/landley/toybox/blob/master/lib/lib.c#L896 315 """ 316 if not _FILE_MODE_RE.match(mode_str): 317 raise ValueError('Unexpected file mode %r', mode_str) 318 mode = _FILE_MODE_KIND[mode_str[0]] 319 for c, flag in zip(mode_str[1:], _FILE_MODE_PERMS): 320 if c != '-' and c.islower(): 321 mode |= flag 322 for c, (t, flag) in zip(mode_str[3::3], _FILE_MODE_SPECIAL): 323 if c.lower() == t: 324 mode |= flag 325 return mode 326 327 328def _GetTimeStamp(): 329 """Return a basic ISO 8601 time stamp with the current local time.""" 330 return time.strftime('%Y%m%dT%H%M%S', time.localtime()) 331 332 333def _JoinLines(lines): 334 # makes sure that the last line is also terminated, and is more memory 335 # efficient than first appending an end-line to each line and then joining 336 # all of them together. 337 return ''.join(s for line in lines for s in (line, '\n')) 338 339 340def _CreateAdbWrapper(device): 341 if isinstance(device, adb_wrapper.AdbWrapper): 342 return device 343 else: 344 return adb_wrapper.AdbWrapper(device) 345 346 347def _FormatPartialOutputError(output): 348 lines = output.splitlines() if isinstance(output, basestring) else output 349 message = ['Partial output found:'] 350 if len(lines) > 11: 351 message.extend('- %s' % line for line in lines[:5]) 352 message.extend('<snip>') 353 message.extend('- %s' % line for line in lines[-5:]) 354 else: 355 message.extend('- %s' % line for line in lines) 356 return '\n'.join(message) 357 358 359class DeviceUtils(object): 360 361 _MAX_ADB_COMMAND_LENGTH = 512 362 _MAX_ADB_OUTPUT_LENGTH = 32768 363 _LAUNCHER_FOCUSED_RE = re.compile( 364 r'\s*mCurrentFocus.*(Launcher|launcher).*') 365 _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') 366 367 LOCAL_PROPERTIES_PATH = posixpath.join('/', 'data', 'local.prop') 368 369 # Property in /data/local.prop that controls Java assertions. 370 JAVA_ASSERT_PROPERTY = 'dalvik.vm.enableassertions' 371 372 def __init__(self, device, enable_device_files_cache=False, 373 default_timeout=_DEFAULT_TIMEOUT, 374 default_retries=_DEFAULT_RETRIES): 375 """DeviceUtils constructor. 376 377 Args: 378 device: Either a device serial, an existing AdbWrapper instance, or an 379 an existing AndroidCommands instance. 380 enable_device_files_cache: For PushChangedFiles(), cache checksums of 381 pushed files rather than recomputing them on a subsequent call. 382 default_timeout: An integer containing the default number of seconds to 383 wait for an operation to complete if no explicit value is provided. 384 default_retries: An integer containing the default number or times an 385 operation should be retried on failure if no explicit value is provided. 386 """ 387 self.adb = None 388 if isinstance(device, basestring): 389 self.adb = _CreateAdbWrapper(device) 390 elif isinstance(device, adb_wrapper.AdbWrapper): 391 self.adb = device 392 else: 393 raise ValueError('Unsupported device value: %r' % device) 394 self._commands_installed = None 395 self._default_timeout = default_timeout 396 self._default_retries = default_retries 397 self._enable_device_files_cache = enable_device_files_cache 398 self._cache = {} 399 self._client_caches = {} 400 self._cache_lock = threading.RLock() 401 assert hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR) 402 assert hasattr(self, decorators.DEFAULT_RETRIES_ATTR) 403 404 self.ClearCache() 405 406 @property 407 def serial(self): 408 """Returns the device serial.""" 409 return self.adb.GetDeviceSerial() 410 411 def __eq__(self, other): 412 """Checks whether |other| refers to the same device as |self|. 413 414 Args: 415 other: The object to compare to. This can be a basestring, an instance 416 of adb_wrapper.AdbWrapper, or an instance of DeviceUtils. 417 Returns: 418 Whether |other| refers to the same device as |self|. 419 """ 420 return self.serial == str(other) 421 422 def __lt__(self, other): 423 """Compares two instances of DeviceUtils. 424 425 This merely compares their serial numbers. 426 427 Args: 428 other: The instance of DeviceUtils to compare to. 429 Returns: 430 Whether |self| is less than |other|. 431 """ 432 return self.serial < other.serial 433 434 def __str__(self): 435 """Returns the device serial.""" 436 return self.serial 437 438 @decorators.WithTimeoutAndRetriesFromInstance() 439 def IsOnline(self, timeout=None, retries=None): 440 """Checks whether the device is online. 441 442 Args: 443 timeout: timeout in seconds 444 retries: number of retries 445 446 Returns: 447 True if the device is online, False otherwise. 448 449 Raises: 450 CommandTimeoutError on timeout. 451 """ 452 try: 453 return self.adb.GetState() == 'device' 454 except base_error.BaseError as exc: 455 logger.info('Failed to get state: %s', exc) 456 return False 457 458 @decorators.WithTimeoutAndRetriesFromInstance() 459 def HasRoot(self, timeout=None, retries=None): 460 """Checks whether or not adbd has root privileges. 461 462 A device is considered to have root if all commands are implicitly run 463 with elevated privileges, i.e. without having to use "su" to run them. 464 465 Note that some devices do not allow this implicit privilige elevation, 466 but _can_ run commands as root just fine when done explicitly with "su". 467 To check if your device can run commands with elevated privileges at all 468 use: 469 470 device.HasRoot() or device.NeedsSU() 471 472 Luckily, for the most part you don't need to worry about this and using 473 RunShellCommand(cmd, as_root=True) will figure out for you the right 474 command incantation to run with elevated privileges. 475 476 Args: 477 timeout: timeout in seconds 478 retries: number of retries 479 480 Returns: 481 True if adbd has root privileges, False otherwise. 482 483 Raises: 484 CommandTimeoutError on timeout. 485 DeviceUnreachableError on missing device. 486 """ 487 try: 488 if self.build_type == 'eng': 489 # 'eng' builds have root enabled by default and the adb session cannot 490 # be unrooted. 491 return True 492 if self.product_name in _SPECIAL_ROOT_DEVICE_LIST: 493 return self.GetProp('service.adb.root') == '1' 494 self.RunShellCommand(['ls', '/root'], check_return=True) 495 return True 496 except device_errors.AdbCommandFailedError: 497 return False 498 499 def NeedsSU(self, timeout=DEFAULT, retries=DEFAULT): 500 """Checks whether 'su' is needed to access protected resources. 501 502 Args: 503 timeout: timeout in seconds 504 retries: number of retries 505 506 Returns: 507 True if 'su' is available on the device and is needed to to access 508 protected resources; False otherwise if either 'su' is not available 509 (e.g. because the device has a user build), or not needed (because adbd 510 already has root privileges). 511 512 Raises: 513 CommandTimeoutError on timeout. 514 DeviceUnreachableError on missing device. 515 """ 516 if 'needs_su' not in self._cache: 517 cmd = '%s && ! ls /root' % self._Su('ls /root') 518 if self.product_name in _SPECIAL_ROOT_DEVICE_LIST: 519 if self.HasRoot(): 520 self._cache['needs_su'] = False 521 return False 522 cmd = 'which which && which su' 523 try: 524 self.RunShellCommand(cmd, shell=True, check_return=True, 525 timeout=self._default_timeout if timeout is DEFAULT else timeout, 526 retries=self._default_retries if retries is DEFAULT else retries) 527 self._cache['needs_su'] = True 528 except device_errors.AdbCommandFailedError: 529 self._cache['needs_su'] = False 530 return self._cache['needs_su'] 531 532 533 def _Su(self, command): 534 if self.build_version_sdk >= version_codes.MARSHMALLOW: 535 return 'su 0 %s' % command 536 return 'su -c %s' % command 537 538 @decorators.WithTimeoutAndRetriesFromInstance() 539 def EnableRoot(self, timeout=None, retries=None): 540 """Restarts adbd with root privileges. 541 542 Args: 543 timeout: timeout in seconds 544 retries: number of retries 545 546 Raises: 547 CommandFailedError if root could not be enabled. 548 CommandTimeoutError on timeout. 549 """ 550 if 'needs_su' in self._cache: 551 del self._cache['needs_su'] 552 553 try: 554 self.adb.Root() 555 except device_errors.AdbCommandFailedError as e: 556 if self.IsUserBuild(): 557 raise device_errors.CommandFailedError( 558 'Unable to root device with user build.', str(self)) 559 elif e.output and _WAIT_FOR_DEVICE_TIMEOUT_STR in e.output: 560 # adb 1.0.41 added a call to wait-for-device *inside* root 561 # with a timeout that can be too short in some cases. 562 # If we hit that timeout, ignore it & do our own wait below. 563 pass 564 else: 565 raise # Failed probably due to some other reason. 566 567 def device_online_with_root(): 568 try: 569 self.adb.WaitForDevice() 570 return self.HasRoot() 571 except (device_errors.AdbCommandFailedError, 572 device_errors.DeviceUnreachableError): 573 return False 574 575 timeout_retry.WaitFor(device_online_with_root, wait_period=1) 576 577 @decorators.WithTimeoutAndRetriesFromInstance() 578 def IsUserBuild(self, timeout=None, retries=None): 579 """Checks whether or not the device is running a user build. 580 581 Args: 582 timeout: timeout in seconds 583 retries: number of retries 584 585 Returns: 586 True if the device is running a user build, False otherwise (i.e. if 587 it's running a userdebug build). 588 589 Raises: 590 CommandTimeoutError on timeout. 591 DeviceUnreachableError on missing device. 592 """ 593 return self.build_type == 'user' 594 595 @decorators.WithTimeoutAndRetriesFromInstance() 596 def GetExternalStoragePath(self, timeout=None, retries=None): 597 """Get the device's path to its SD card. 598 599 Args: 600 timeout: timeout in seconds 601 retries: number of retries 602 603 Returns: 604 The device's path to its SD card. 605 606 Raises: 607 CommandFailedError if the external storage path could not be determined. 608 CommandTimeoutError on timeout. 609 DeviceUnreachableError on missing device. 610 """ 611 self._EnsureCacheInitialized() 612 if not self._cache['external_storage']: 613 raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set', 614 str(self)) 615 return self._cache['external_storage'] 616 617 @decorators.WithTimeoutAndRetriesFromInstance() 618 def GetIMEI(self, timeout=None, retries=None): 619 """Get the device's IMEI. 620 621 Args: 622 timeout: timeout in seconds 623 retries: number of retries 624 625 Returns: 626 The device's IMEI. 627 628 Raises: 629 AdbCommandFailedError on error 630 """ 631 if self._cache.get('imei') is not None: 632 return self._cache.get('imei') 633 634 if self.build_version_sdk < 21: 635 out = self.RunShellCommand(['dumpsys', 'iphonesubinfo'], 636 raw_output=True, check_return=True) 637 if out: 638 match = re.search(_IMEI_RE, out) 639 if match: 640 self._cache['imei'] = match.group(1) 641 return self._cache['imei'] 642 else: 643 out = self.RunShellCommand(['service', 'call', 'iphonesubinfo', '1'], 644 check_return=True) 645 if out: 646 imei = '' 647 for line in out: 648 match = re.search(_PARCEL_RESULT_RE, line) 649 if match: 650 imei = imei + match.group(1) 651 imei = imei.replace('.', '').strip() 652 if imei: 653 self._cache['imei'] = imei 654 return self._cache['imei'] 655 656 raise device_errors.CommandFailedError('Unable to fetch IMEI.') 657 658 @decorators.WithTimeoutAndRetriesFromInstance() 659 def GetApplicationPaths(self, package, timeout=None, retries=None): 660 """Get the paths of the installed apks on the device for the given package. 661 662 Args: 663 package: Name of the package. 664 665 Returns: 666 List of paths to the apks on the device for the given package. 667 """ 668 return self._GetApplicationPathsInternal(package) 669 670 def _GetApplicationPathsInternal(self, package, skip_cache=False): 671 cached_result = self._cache['package_apk_paths'].get(package) 672 if cached_result is not None and not skip_cache: 673 if package in self._cache['package_apk_paths_to_verify']: 674 self._cache['package_apk_paths_to_verify'].remove(package) 675 # Don't verify an app that is not thought to be installed. We are 676 # concerned only with apps we think are installed having been 677 # uninstalled manually. 678 if cached_result and not self.PathExists(cached_result): 679 cached_result = None 680 self._cache['package_apk_checksums'].pop(package, 0) 681 if cached_result is not None: 682 return list(cached_result) 683 # 'pm path' is liable to incorrectly exit with a nonzero number starting 684 # in Lollipop. 685 # TODO(jbudorick): Check if this is fixed as new Android versions are 686 # released to put an upper bound on this. 687 should_check_return = (self.build_version_sdk < version_codes.LOLLIPOP) 688 output = self.RunShellCommand( 689 ['pm', 'path', package], check_return=should_check_return) 690 apks = [] 691 bad_output = False 692 for line in output: 693 if line.startswith('package:'): 694 apks.append(line[len('package:'):]) 695 elif line.startswith('WARNING:'): 696 continue 697 else: 698 bad_output = True # Unexpected line in output. 699 if not apks and output: 700 if bad_output: 701 raise device_errors.CommandFailedError( 702 'Unexpected pm path output: %r' % '\n'.join(output), str(self)) 703 else: 704 logger.warning('pm returned no paths but the following warnings:') 705 for line in output: 706 logger.warning('- %s', line) 707 self._cache['package_apk_paths'][package] = list(apks) 708 return apks 709 710 @decorators.WithTimeoutAndRetriesFromInstance() 711 def GetApplicationVersion(self, package, timeout=None, retries=None): 712 """Get the version name of a package installed on the device. 713 714 Args: 715 package: Name of the package. 716 717 Returns: 718 A string with the version name or None if the package is not found 719 on the device. 720 """ 721 output = self.RunShellCommand( 722 ['dumpsys', 'package', package], check_return=True) 723 if not output: 724 return None 725 for line in output: 726 line = line.strip() 727 if line.startswith('versionName='): 728 return line[len('versionName='):] 729 raise device_errors.CommandFailedError( 730 'Version name for %s not found on dumpsys output' % package, str(self)) 731 732 @decorators.WithTimeoutAndRetriesFromInstance() 733 def GetPackageArchitecture(self, package, timeout=None, retries=None): 734 """Get the architecture of a package installed on the device. 735 736 Args: 737 package: Name of the package. 738 739 Returns: 740 A string with the architecture, or None if the package is missing. 741 """ 742 lines = self._GetDumpsysOutput(['package', package], 'primaryCpuAbi') 743 if lines: 744 _, _, package_arch = lines[-1].partition('=') 745 return package_arch.strip() 746 return None 747 748 @decorators.WithTimeoutAndRetriesFromInstance() 749 def GetApplicationDataDirectory(self, package, timeout=None, retries=None): 750 """Get the data directory on the device for the given package. 751 752 Args: 753 package: Name of the package. 754 755 Returns: 756 The package's data directory. 757 Raises: 758 CommandFailedError if the package's data directory can't be found, 759 whether because it's not installed or otherwise. 760 """ 761 output = self._RunPipedShellCommand( 762 'pm dump %s | grep dataDir=' % cmd_helper.SingleQuote(package)) 763 for line in output: 764 _, _, dataDir = line.partition('dataDir=') 765 if dataDir: 766 return dataDir 767 raise device_errors.CommandFailedError( 768 'Could not find data directory for %s', package) 769 770 @decorators.WithTimeoutAndRetriesFromInstance() 771 def GetSecurityContextForPackage(self, package, encrypted=False, timeout=None, 772 retries=None): 773 """Gets the SELinux security context for the given package. 774 775 Args: 776 package: Name of the package. 777 encrypted: Whether to check in the encrypted data directory 778 (/data/user_de/0/) or the unencrypted data directory (/data/data/). 779 780 Returns: 781 The package's security context as a string, or None if not found. 782 """ 783 directory = '/data/user_de/0/' if encrypted else '/data/data/' 784 for line in self.RunShellCommand(['ls', '-Z', directory], 785 as_root=True, check_return=True): 786 split_line = line.split() 787 # ls -Z output differs between Android versions, but the package is 788 # always last and the context always starts with "u:object" 789 if split_line[-1] == package: 790 for column in split_line: 791 if column.startswith('u:object'): 792 return column 793 return None 794 795 def TakeBugReport(self, path, timeout=60*5, retries=None): 796 """Takes a bug report and dumps it to the specified path. 797 798 This doesn't use adb's bugreport option since its behavior is dependent on 799 both adb version and device OS version. To make it simpler, this directly 800 runs the bugreport command on the device itself and dumps the stdout to a 801 file. 802 803 Args: 804 path: Path on the host to drop the bug report. 805 timeout: (optional) Timeout per try in seconds. 806 retries: (optional) Number of retries to attempt. 807 """ 808 with device_temp_file.DeviceTempFile(self.adb) as device_tmp_file: 809 cmd = '( bugreport )>%s 2>&1' % device_tmp_file.name 810 self.RunShellCommand( 811 cmd, check_return=True, shell=True, timeout=timeout, retries=retries) 812 self.PullFile(device_tmp_file.name, path) 813 814 @decorators.WithTimeoutAndRetriesFromInstance() 815 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None): 816 """Wait for the device to fully boot. 817 818 This means waiting for the device to boot, the package manager to be 819 available, and the SD card to be ready. It can optionally mean waiting 820 for wifi to come up, too. 821 822 Args: 823 wifi: A boolean indicating if we should wait for wifi to come up or not. 824 timeout: timeout in seconds 825 retries: number of retries 826 827 Raises: 828 CommandFailedError on failure. 829 CommandTimeoutError if one of the component waits times out. 830 DeviceUnreachableError if the device becomes unresponsive. 831 """ 832 def sd_card_ready(): 833 try: 834 self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()], 835 check_return=True) 836 return True 837 except device_errors.AdbCommandFailedError: 838 return False 839 840 def pm_ready(): 841 try: 842 return self._GetApplicationPathsInternal('android', skip_cache=True) 843 except device_errors.CommandFailedError: 844 return False 845 846 def boot_completed(): 847 try: 848 return self.GetProp('sys.boot_completed', cache=False) == '1' 849 except device_errors.CommandFailedError: 850 return False 851 852 def wifi_enabled(): 853 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'], 854 check_return=False) 855 856 self.adb.WaitForDevice() 857 timeout_retry.WaitFor(sd_card_ready) 858 timeout_retry.WaitFor(pm_ready) 859 timeout_retry.WaitFor(boot_completed) 860 if wifi: 861 timeout_retry.WaitFor(wifi_enabled) 862 863 REBOOT_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT 864 865 @decorators.WithTimeoutAndRetriesFromInstance( 866 min_default_timeout=REBOOT_DEFAULT_TIMEOUT) 867 def Reboot(self, block=True, wifi=False, timeout=None, retries=None): 868 """Reboot the device. 869 870 Args: 871 block: A boolean indicating if we should wait for the reboot to complete. 872 wifi: A boolean indicating if we should wait for wifi to be enabled after 873 the reboot. The option has no effect unless |block| is also True. 874 timeout: timeout in seconds 875 retries: number of retries 876 877 Raises: 878 CommandTimeoutError on timeout. 879 DeviceUnreachableError on missing device. 880 """ 881 def device_offline(): 882 return not self.IsOnline() 883 884 self.adb.Reboot() 885 self.ClearCache() 886 timeout_retry.WaitFor(device_offline, wait_period=1) 887 if block: 888 self.WaitUntilFullyBooted(wifi=wifi) 889 890 INSTALL_DEFAULT_TIMEOUT = 8 * _DEFAULT_TIMEOUT 891 892 @decorators.WithTimeoutAndRetriesFromInstance( 893 min_default_timeout=INSTALL_DEFAULT_TIMEOUT) 894 def Install(self, apk, allow_downgrade=False, reinstall=False, 895 permissions=None, timeout=None, retries=None, modules=None): 896 """Install an APK or app bundle. 897 898 Noop if an identical APK is already installed. If installing a bundle, the 899 bundletools helper script (bin/*_bundle) should be used rather than the .aab 900 file. 901 902 Args: 903 apk: An ApkHelper instance or string containing the path to the APK or 904 bundle. 905 allow_downgrade: A boolean indicating if we should allow downgrades. 906 reinstall: A boolean indicating if we should keep any existing app data. 907 Ignored if |apk| is a bundle. 908 permissions: Set of permissions to set. If not set, finds permissions with 909 apk helper. To set no permissions, pass []. 910 timeout: timeout in seconds 911 retries: number of retries 912 modules: An iterable containing specific bundle modules to install. 913 Error if set and |apk| points to an APK instead of a bundle. 914 915 Raises: 916 CommandFailedError if the installation fails. 917 CommandTimeoutError if the installation times out. 918 DeviceUnreachableError on missing device. 919 """ 920 self._InstallInternal(apk, None, allow_downgrade=allow_downgrade, 921 reinstall=reinstall, permissions=permissions, 922 modules=modules) 923 924 @decorators.WithTimeoutAndRetriesFromInstance( 925 min_default_timeout=INSTALL_DEFAULT_TIMEOUT) 926 def InstallSplitApk(self, base_apk, split_apks, allow_downgrade=False, 927 reinstall=False, allow_cached_props=False, 928 permissions=None, timeout=None, retries=None): 929 """Install a split APK. 930 931 Noop if all of the APK splits are already installed. 932 933 Args: 934 base_apk: An ApkHelper instance or string containing the path to the base 935 APK. 936 split_apks: A list of strings of paths of all of the APK splits. 937 allow_downgrade: A boolean indicating if we should allow downgrades. 938 reinstall: A boolean indicating if we should keep any existing app data. 939 allow_cached_props: Whether to use cached values for device properties. 940 permissions: Set of permissions to set. If not set, finds permissions with 941 apk helper. To set no permissions, pass []. 942 timeout: timeout in seconds 943 retries: number of retries 944 945 Raises: 946 CommandFailedError if the installation fails. 947 CommandTimeoutError if the installation times out. 948 DeviceUnreachableError on missing device. 949 DeviceVersionError if device SDK is less than Android L. 950 """ 951 self._InstallInternal(base_apk, split_apks, reinstall=reinstall, 952 allow_cached_props=allow_cached_props, 953 permissions=permissions, 954 allow_downgrade=allow_downgrade) 955 956 def _InstallInternal(self, base_apk, split_apks, allow_downgrade=False, 957 reinstall=False, allow_cached_props=False, 958 permissions=None, modules=None): 959 base_apk = apk_helper.ToHelper(base_apk) 960 if base_apk.is_bundle: 961 if split_apks: 962 raise device_errors.CommandFailedError( 963 'Attempted to install a bundle {} while specifying split apks' 964 .format(base_apk)) 965 if allow_downgrade: 966 logging.warning('Installation of a bundle requested with ' 967 'allow_downgrade=False. This is not possible with ' 968 'bundletools, no downgrading is possible. This ' 969 'flag will be ignored and installation will proceed.') 970 # |allow_cached_props| is unused and ignored for bundles. 971 self._InstallBundleInternal(base_apk, permissions, modules) 972 return 973 974 if modules: 975 raise device_errors.CommandFailedError( 976 'Attempted to specify modules to install when providing an APK') 977 978 if split_apks: 979 self._CheckSdkLevel(version_codes.LOLLIPOP) 980 981 all_apks = [base_apk.path] 982 if split_apks: 983 all_apks += split_select.SelectSplits( 984 self, base_apk.path, split_apks, allow_cached_props=allow_cached_props) 985 if len(all_apks) == 1: 986 logger.warning('split-select did not select any from %s', split_apks) 987 988 missing_apks = [apk for apk in all_apks if not os.path.exists(apk)] 989 if missing_apks: 990 raise device_errors.CommandFailedError( 991 'Attempted to install non-existent apks: %s' 992 % pprint.pformat(missing_apks)) 993 994 package_name = base_apk.GetPackageName() 995 device_apk_paths = self._GetApplicationPathsInternal(package_name) 996 997 apks_to_install = None 998 host_checksums = None 999 if not device_apk_paths: 1000 apks_to_install = all_apks 1001 elif len(device_apk_paths) > 1 and not split_apks: 1002 logger.warning( 1003 'Installing non-split APK when split APK was previously installed') 1004 apks_to_install = all_apks 1005 elif len(device_apk_paths) == 1 and split_apks: 1006 logger.warning( 1007 'Installing split APK when non-split APK was previously installed') 1008 apks_to_install = all_apks 1009 else: 1010 try: 1011 apks_to_install, host_checksums = ( 1012 self._ComputeStaleApks(package_name, all_apks)) 1013 except EnvironmentError as e: 1014 logger.warning('Error calculating md5: %s', e) 1015 apks_to_install, host_checksums = all_apks, None 1016 if apks_to_install and not reinstall: 1017 apks_to_install = all_apks 1018 1019 if device_apk_paths and apks_to_install and not reinstall: 1020 self.Uninstall(package_name) 1021 1022 if apks_to_install: 1023 # Assume that we won't know the resulting device state. 1024 self._cache['package_apk_paths'].pop(package_name, 0) 1025 self._cache['package_apk_checksums'].pop(package_name, 0) 1026 if split_apks: 1027 partial = package_name if len(apks_to_install) < len(all_apks) else None 1028 self.adb.InstallMultiple( 1029 apks_to_install, partial=partial, reinstall=reinstall, 1030 allow_downgrade=allow_downgrade) 1031 else: 1032 self.adb.Install( 1033 base_apk.path, reinstall=reinstall, allow_downgrade=allow_downgrade) 1034 else: 1035 # Running adb install terminates running instances of the app, so to be 1036 # consistent, we explicitly terminate it when skipping the install. 1037 self.ForceStop(package_name) 1038 1039 if (permissions is None 1040 and self.build_version_sdk >= version_codes.MARSHMALLOW): 1041 permissions = base_apk.GetPermissions() 1042 self.GrantPermissions(package_name, permissions) 1043 # Upon success, we know the device checksums, but not their paths. 1044 if host_checksums is not None: 1045 self._cache['package_apk_checksums'][package_name] = host_checksums 1046 1047 def _InstallBundleInternal(self, bundle, permissions, modules): 1048 cmd = [bundle.path, 'install', '--device', self.serial] 1049 if modules: 1050 for m in modules: 1051 cmd.extend(['-m', m]) 1052 status = cmd_helper.RunCmd(cmd) 1053 if status != 0: 1054 raise device_errors.CommandFailedError('Cound not install {}'.format( 1055 bundle.path)) 1056 if (permissions is None 1057 and self.build_version_sdk >= version_codes.MARSHMALLOW): 1058 permissions = bundle.GetPermissions() 1059 self.GrantPermissions(bundle.GetPackageName(), permissions) 1060 1061 @decorators.WithTimeoutAndRetriesFromInstance() 1062 def Uninstall(self, package_name, keep_data=False, timeout=None, 1063 retries=None): 1064 """Remove the app |package_name| from the device. 1065 1066 This is a no-op if the app is not already installed. 1067 1068 Args: 1069 package_name: The package to uninstall. 1070 keep_data: (optional) Whether to keep the data and cache directories. 1071 timeout: Timeout in seconds. 1072 retries: Number of retries. 1073 1074 Raises: 1075 CommandFailedError if the uninstallation fails. 1076 CommandTimeoutError if the uninstallation times out. 1077 DeviceUnreachableError on missing device. 1078 """ 1079 installed = self._GetApplicationPathsInternal(package_name) 1080 if not installed: 1081 return 1082 # cached package paths are indeterminate due to system apps taking over 1083 # user apps after uninstall, so clear it 1084 self._cache['package_apk_paths'].pop(package_name, 0) 1085 self._cache['package_apk_checksums'].pop(package_name, 0) 1086 self.adb.Uninstall(package_name, keep_data) 1087 1088 def _CheckSdkLevel(self, required_sdk_level): 1089 """Raises an exception if the device does not have the required SDK level. 1090 """ 1091 if self.build_version_sdk < required_sdk_level: 1092 raise device_errors.DeviceVersionError( 1093 ('Requires SDK level %s, device is SDK level %s' % 1094 (required_sdk_level, self.build_version_sdk)), 1095 device_serial=self.serial) 1096 1097 @decorators.WithTimeoutAndRetriesFromInstance() 1098 def RunShellCommand(self, cmd, shell=False, check_return=False, cwd=None, 1099 env=None, run_as=None, as_root=False, single_line=False, 1100 large_output=False, raw_output=False, timeout=None, 1101 retries=None): 1102 """Run an ADB shell command. 1103 1104 The command to run |cmd| should be a sequence of program arguments 1105 (preferred) or a single string with a shell script to run. 1106 1107 When |cmd| is a sequence, it is assumed to contain the name of the command 1108 to run followed by its arguments. In this case, arguments are passed to the 1109 command exactly as given, preventing any further processing by the shell. 1110 This allows callers to easily pass arguments with spaces or special 1111 characters without having to worry about quoting rules. Whenever possible, 1112 it is recomended to pass |cmd| as a sequence. 1113 1114 When |cmd| is passed as a single string, |shell| should be set to True. 1115 The command will be interpreted and run by the shell on the device, 1116 allowing the use of shell features such as pipes, wildcards, or variables. 1117 Failing to set shell=True will issue a warning, but this will be changed 1118 to a hard failure in the future (see: catapult:#3242). 1119 1120 This behaviour is consistent with that of command runners in cmd_helper as 1121 well as Python's own subprocess.Popen. 1122 1123 TODO(perezju) Change the default of |check_return| to True when callers 1124 have switched to the new behaviour. 1125 1126 Args: 1127 cmd: A sequence containing the command to run and its arguments, or a 1128 string with a shell script to run (should also set shell=True). 1129 shell: A boolean indicating whether shell features may be used in |cmd|. 1130 check_return: A boolean indicating whether or not the return code should 1131 be checked. 1132 cwd: The device directory in which the command should be run. 1133 env: The environment variables with which the command should be run. 1134 run_as: A string containing the package as which the command should be 1135 run. 1136 as_root: A boolean indicating whether the shell command should be run 1137 with root privileges. 1138 single_line: A boolean indicating if only a single line of output is 1139 expected. 1140 large_output: Uses a work-around for large shell command output. Without 1141 this large output will be truncated. 1142 raw_output: Whether to only return the raw output 1143 (no splitting into lines). 1144 timeout: timeout in seconds 1145 retries: number of retries 1146 1147 Returns: 1148 If single_line is False, the output of the command as a list of lines, 1149 otherwise, a string with the unique line of output emmited by the command 1150 (with the optional newline at the end stripped). 1151 1152 Raises: 1153 AdbCommandFailedError if check_return is True and the exit code of 1154 the command run on the device is non-zero. 1155 CommandFailedError if single_line is True but the output contains two or 1156 more lines. 1157 CommandTimeoutError on timeout. 1158 DeviceUnreachableError on missing device. 1159 """ 1160 def env_quote(key, value): 1161 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key): 1162 raise KeyError('Invalid shell variable name %r' % key) 1163 # using double quotes here to allow interpolation of shell variables 1164 return '%s=%s' % (key, cmd_helper.DoubleQuote(value)) 1165 1166 def run(cmd): 1167 return self.adb.Shell(cmd) 1168 1169 def handle_check_return(cmd): 1170 try: 1171 return run(cmd) 1172 except device_errors.AdbCommandFailedError as exc: 1173 if check_return: 1174 raise 1175 else: 1176 return exc.output 1177 1178 def handle_large_command(cmd): 1179 if len(cmd) < self._MAX_ADB_COMMAND_LENGTH: 1180 return handle_check_return(cmd) 1181 else: 1182 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script: 1183 self._WriteFileWithPush(script.name, cmd) 1184 logger.info('Large shell command will be run from file: %s ...', 1185 cmd[:self._MAX_ADB_COMMAND_LENGTH]) 1186 return handle_check_return('sh %s' % script.name_quoted) 1187 1188 def handle_large_output(cmd, large_output_mode): 1189 if large_output_mode: 1190 with device_temp_file.DeviceTempFile(self.adb) as large_output_file: 1191 large_output_cmd = '( %s )>%s 2>&1' % (cmd, large_output_file.name) 1192 logger.debug('Large output mode enabled. Will write output to ' 1193 'device and read results from file.') 1194 try: 1195 handle_large_command(large_output_cmd) 1196 return self.ReadFile(large_output_file.name, force_pull=True) 1197 except device_errors.AdbShellCommandFailedError as exc: 1198 output = self.ReadFile(large_output_file.name, force_pull=True) 1199 raise device_errors.AdbShellCommandFailedError( 1200 cmd, output, exc.status, exc.device_serial) 1201 else: 1202 try: 1203 return handle_large_command(cmd) 1204 except device_errors.AdbCommandFailedError as exc: 1205 if exc.status is None: 1206 logger.error(_FormatPartialOutputError(exc.output)) 1207 logger.warning('Attempting to run in large_output mode.') 1208 logger.warning('Use RunShellCommand(..., large_output=True) for ' 1209 'shell commands that expect a lot of output.') 1210 return handle_large_output(cmd, True) 1211 else: 1212 raise 1213 1214 if isinstance(cmd, basestring): 1215 if not shell: 1216 logger.warning( 1217 'The command to run should preferably be passed as a sequence of' 1218 ' args. If shell features are needed (pipes, wildcards, variables)' 1219 ' clients should explicitly set shell=True.') 1220 else: 1221 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd) 1222 if env: 1223 env = ' '.join(env_quote(k, v) for k, v in env.iteritems()) 1224 cmd = '%s %s' % (env, cmd) 1225 if cwd: 1226 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd) 1227 if run_as: 1228 cmd = 'run-as %s sh -c %s' % (cmd_helper.SingleQuote(run_as), 1229 cmd_helper.SingleQuote(cmd)) 1230 if (as_root is _FORCE_SU) or (as_root and self.NeedsSU()): 1231 # "su -c sh -c" allows using shell features in |cmd| 1232 cmd = self._Su('sh -c %s' % cmd_helper.SingleQuote(cmd)) 1233 1234 output = handle_large_output(cmd, large_output) 1235 1236 if raw_output: 1237 return output 1238 1239 output = output.splitlines() 1240 if single_line: 1241 if not output: 1242 return '' 1243 elif len(output) == 1: 1244 return output[0] 1245 else: 1246 msg = 'one line of output was expected, but got: %s' 1247 raise device_errors.CommandFailedError(msg % output, str(self)) 1248 else: 1249 return output 1250 1251 def _RunPipedShellCommand(self, script, **kwargs): 1252 PIPESTATUS_LEADER = 'PIPESTATUS: ' 1253 1254 script += '; echo "%s${PIPESTATUS[@]}"' % PIPESTATUS_LEADER 1255 kwargs.update(shell=True, check_return=True) 1256 output = self.RunShellCommand(script, **kwargs) 1257 pipestatus_line = output[-1] 1258 1259 if not pipestatus_line.startswith(PIPESTATUS_LEADER): 1260 logger.error('Pipe exit statuses of shell script missing.') 1261 raise device_errors.AdbShellCommandFailedError( 1262 script, output, status=None, 1263 device_serial=self.serial) 1264 1265 output = output[:-1] 1266 statuses = [ 1267 int(s) for s in pipestatus_line[len(PIPESTATUS_LEADER):].split()] 1268 if any(statuses): 1269 raise device_errors.AdbShellCommandFailedError( 1270 script, output, status=statuses, 1271 device_serial=self.serial) 1272 return output 1273 1274 @decorators.WithTimeoutAndRetriesFromInstance() 1275 def KillAll(self, process_name, exact=False, signum=device_signal.SIGKILL, 1276 as_root=False, blocking=False, quiet=False, 1277 timeout=None, retries=None): 1278 """Kill all processes with the given name on the device. 1279 1280 Args: 1281 process_name: A string containing the name of the process to kill. 1282 exact: A boolean indicating whether to kill all processes matching 1283 the string |process_name| exactly, or all of those which contain 1284 |process_name| as a substring. Defaults to False. 1285 signum: An integer containing the signal number to send to kill. Defaults 1286 to SIGKILL (9). 1287 as_root: A boolean indicating whether the kill should be executed with 1288 root privileges. 1289 blocking: A boolean indicating whether we should wait until all processes 1290 with the given |process_name| are dead. 1291 quiet: A boolean indicating whether to ignore the fact that no processes 1292 to kill were found. 1293 timeout: timeout in seconds 1294 retries: number of retries 1295 1296 Returns: 1297 The number of processes attempted to kill. 1298 1299 Raises: 1300 CommandFailedError if no process was killed and |quiet| is False. 1301 CommandTimeoutError on timeout. 1302 DeviceUnreachableError on missing device. 1303 """ 1304 processes = self.ListProcesses(process_name) 1305 if exact: 1306 processes = [p for p in processes if p.name == process_name] 1307 if not processes: 1308 if quiet: 1309 return 0 1310 else: 1311 raise device_errors.CommandFailedError( 1312 'No processes matching %r (exact=%r)' % (process_name, exact), 1313 str(self)) 1314 1315 logger.info( 1316 'KillAll(%r, ...) attempting to kill the following:', process_name) 1317 for p in processes: 1318 logger.info(' %05d %s', p.pid, p.name) 1319 1320 pids = set(p.pid for p in processes) 1321 cmd = ['kill', '-%d' % signum] + sorted(str(p) for p in pids) 1322 self.RunShellCommand(cmd, as_root=as_root, check_return=True) 1323 1324 def all_pids_killed(): 1325 pids_left = (p.pid for p in self.ListProcesses(process_name)) 1326 return not pids.intersection(pids_left) 1327 1328 if blocking: 1329 timeout_retry.WaitFor(all_pids_killed, wait_period=0.1) 1330 1331 return len(pids) 1332 1333 @decorators.WithTimeoutAndRetriesFromInstance() 1334 def StartActivity(self, intent_obj, blocking=False, trace_file_name=None, 1335 force_stop=False, timeout=None, retries=None): 1336 """Start package's activity on the device. 1337 1338 Args: 1339 intent_obj: An Intent object to send. 1340 blocking: A boolean indicating whether we should wait for the activity to 1341 finish launching. 1342 trace_file_name: If present, a string that both indicates that we want to 1343 profile the activity and contains the path to which the 1344 trace should be saved. 1345 force_stop: A boolean indicating whether we should stop the activity 1346 before starting it. 1347 timeout: timeout in seconds 1348 retries: number of retries 1349 1350 Raises: 1351 CommandFailedError if the activity could not be started. 1352 CommandTimeoutError on timeout. 1353 DeviceUnreachableError on missing device. 1354 """ 1355 cmd = ['am', 'start'] 1356 if blocking: 1357 cmd.append('-W') 1358 if trace_file_name: 1359 cmd.extend(['--start-profiler', trace_file_name]) 1360 if force_stop: 1361 cmd.append('-S') 1362 cmd.extend(intent_obj.am_args) 1363 for line in self.RunShellCommand(cmd, check_return=True): 1364 if line.startswith('Error:'): 1365 raise device_errors.CommandFailedError(line, str(self)) 1366 1367 @decorators.WithTimeoutAndRetriesFromInstance() 1368 def StartService(self, intent_obj, user_id=None, timeout=None, retries=None): 1369 """Start a service on the device. 1370 1371 Args: 1372 intent_obj: An Intent object to send describing the service to start. 1373 user_id: A specific user to start the service as, defaults to current. 1374 timeout: Timeout in seconds. 1375 retries: Number of retries 1376 1377 Raises: 1378 CommandFailedError if the service could not be started. 1379 CommandTimeoutError on timeout. 1380 DeviceUnreachableError on missing device. 1381 """ 1382 # For whatever reason, startservice was changed to start-service on O and 1383 # above. 1384 cmd = ['am', 'startservice'] 1385 if self.build_version_sdk >= version_codes.OREO: 1386 cmd[1] = 'start-service' 1387 if user_id: 1388 cmd.extend(['--user', str(user_id)]) 1389 cmd.extend(intent_obj.am_args) 1390 for line in self.RunShellCommand(cmd, check_return=True): 1391 if line.startswith('Error:'): 1392 raise device_errors.CommandFailedError(line, str(self)) 1393 1394 @decorators.WithTimeoutAndRetriesFromInstance() 1395 def StartInstrumentation(self, component, finish=True, raw=False, 1396 extras=None, timeout=None, retries=None): 1397 if extras is None: 1398 extras = {} 1399 1400 cmd = ['am', 'instrument'] 1401 if finish: 1402 cmd.append('-w') 1403 if raw: 1404 cmd.append('-r') 1405 for k, v in extras.iteritems(): 1406 cmd.extend(['-e', str(k), str(v)]) 1407 cmd.append(component) 1408 1409 # Store the package name in a shell variable to help the command stay under 1410 # the _MAX_ADB_COMMAND_LENGTH limit. 1411 package = component.split('/')[0] 1412 shell_snippet = 'p=%s;%s' % (package, 1413 cmd_helper.ShrinkToSnippet(cmd, 'p', package)) 1414 return self.RunShellCommand(shell_snippet, shell=True, check_return=True, 1415 large_output=True) 1416 1417 @decorators.WithTimeoutAndRetriesFromInstance() 1418 def BroadcastIntent(self, intent_obj, timeout=None, retries=None): 1419 """Send a broadcast intent. 1420 1421 Args: 1422 intent: An Intent to broadcast. 1423 timeout: timeout in seconds 1424 retries: number of retries 1425 1426 Raises: 1427 CommandTimeoutError on timeout. 1428 DeviceUnreachableError on missing device. 1429 """ 1430 cmd = ['am', 'broadcast'] + intent_obj.am_args 1431 self.RunShellCommand(cmd, check_return=True) 1432 1433 @decorators.WithTimeoutAndRetriesFromInstance() 1434 def GoHome(self, timeout=None, retries=None): 1435 """Return to the home screen and obtain launcher focus. 1436 1437 This command launches the home screen and attempts to obtain 1438 launcher focus until the timeout is reached. 1439 1440 Args: 1441 timeout: timeout in seconds 1442 retries: number of retries 1443 1444 Raises: 1445 CommandTimeoutError on timeout. 1446 DeviceUnreachableError on missing device. 1447 """ 1448 def is_launcher_focused(): 1449 output = self.RunShellCommand(['dumpsys', 'window', 'windows'], 1450 check_return=True, large_output=True) 1451 return any(self._LAUNCHER_FOCUSED_RE.match(l) for l in output) 1452 1453 def dismiss_popups(): 1454 # There is a dialog present; attempt to get rid of it. 1455 # Not all dialogs can be dismissed with back. 1456 self.SendKeyEvent(keyevent.KEYCODE_ENTER) 1457 self.SendKeyEvent(keyevent.KEYCODE_BACK) 1458 return is_launcher_focused() 1459 1460 # If Home is already focused, return early to avoid unnecessary work. 1461 if is_launcher_focused(): 1462 return 1463 1464 self.StartActivity( 1465 intent.Intent(action='android.intent.action.MAIN', 1466 category='android.intent.category.HOME'), 1467 blocking=True) 1468 1469 if not is_launcher_focused(): 1470 timeout_retry.WaitFor(dismiss_popups, wait_period=1) 1471 1472 @decorators.WithTimeoutAndRetriesFromInstance() 1473 def ForceStop(self, package, timeout=None, retries=None): 1474 """Close the application. 1475 1476 Args: 1477 package: A string containing the name of the package to stop. 1478 timeout: timeout in seconds 1479 retries: number of retries 1480 1481 Raises: 1482 CommandTimeoutError on timeout. 1483 DeviceUnreachableError on missing device. 1484 """ 1485 if self.GetApplicationPids(package): 1486 self.RunShellCommand(['am', 'force-stop', package], check_return=True) 1487 1488 @decorators.WithTimeoutAndRetriesFromInstance() 1489 def ClearApplicationState( 1490 self, package, permissions=None, timeout=None, retries=None): 1491 """Clear all state for the given package. 1492 1493 Args: 1494 package: A string containing the name of the package to stop. 1495 permissions: List of permissions to set after clearing data. 1496 timeout: timeout in seconds 1497 retries: number of retries 1498 1499 Raises: 1500 CommandTimeoutError on timeout. 1501 DeviceUnreachableError on missing device. 1502 """ 1503 # Check that the package exists before clearing it for android builds below 1504 # JB MR2. Necessary because calling pm clear on a package that doesn't exist 1505 # may never return. 1506 if ((self.build_version_sdk >= version_codes.JELLY_BEAN_MR2) 1507 or self._GetApplicationPathsInternal(package)): 1508 self.RunShellCommand(['pm', 'clear', package], check_return=True) 1509 self.GrantPermissions(package, permissions) 1510 1511 @decorators.WithTimeoutAndRetriesFromInstance() 1512 def SendKeyEvent(self, keycode, timeout=None, retries=None): 1513 """Sends a keycode to the device. 1514 1515 See the devil.android.sdk.keyevent module for suitable keycode values. 1516 1517 Args: 1518 keycode: A integer keycode to send to the device. 1519 timeout: timeout in seconds 1520 retries: number of retries 1521 1522 Raises: 1523 CommandTimeoutError on timeout. 1524 DeviceUnreachableError on missing device. 1525 """ 1526 self.RunShellCommand(['input', 'keyevent', format(keycode, 'd')], 1527 check_return=True) 1528 1529 PUSH_CHANGED_FILES_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT 1530 1531 @decorators.WithTimeoutAndRetriesFromInstance( 1532 min_default_timeout=PUSH_CHANGED_FILES_DEFAULT_TIMEOUT) 1533 def PushChangedFiles(self, host_device_tuples, timeout=None, 1534 retries=None, delete_device_stale=False): 1535 """Push files to the device, skipping files that don't need updating. 1536 1537 When a directory is pushed, it is traversed recursively on the host and 1538 all files in it are pushed to the device as needed. 1539 Additionally, if delete_device_stale option is True, 1540 files that exist on the device but don't exist on the host are deleted. 1541 1542 Args: 1543 host_device_tuples: A list of (host_path, device_path) tuples, where 1544 |host_path| is an absolute path of a file or directory on the host 1545 that should be minimially pushed to the device, and |device_path| is 1546 an absolute path of the destination on the device. 1547 timeout: timeout in seconds 1548 retries: number of retries 1549 delete_device_stale: option to delete stale files on device 1550 1551 Raises: 1552 CommandFailedError on failure. 1553 CommandTimeoutError on timeout. 1554 DeviceUnreachableError on missing device. 1555 """ 1556 1557 all_changed_files = [] 1558 all_stale_files = [] 1559 missing_dirs = set() 1560 cache_commit_funcs = [] 1561 for h, d in host_device_tuples: 1562 assert os.path.isabs(h) and posixpath.isabs(d) 1563 h = os.path.realpath(h) 1564 changed_files, up_to_date_files, stale_files, cache_commit_func = ( 1565 self._GetChangedAndStaleFiles(h, d, delete_device_stale)) 1566 all_changed_files += changed_files 1567 all_stale_files += stale_files 1568 cache_commit_funcs.append(cache_commit_func) 1569 if changed_files and not up_to_date_files and not stale_files: 1570 if os.path.isdir(h): 1571 missing_dirs.add(d) 1572 else: 1573 missing_dirs.add(posixpath.dirname(d)) 1574 1575 if delete_device_stale and all_stale_files: 1576 self.RemovePath(all_stale_files, force=True, recursive=True) 1577 1578 if all_changed_files: 1579 if missing_dirs: 1580 try: 1581 self.RunShellCommand(['mkdir', '-p'] + list(missing_dirs), 1582 check_return=True) 1583 except device_errors.AdbShellCommandFailedError as e: 1584 # TODO(crbug.com/739899): This is attempting to diagnose flaky EBUSY 1585 # errors that have been popping up in single-device scenarios. 1586 # Remove it once we've figured out what's causing them and how best 1587 # to handle them. 1588 m = _EBUSY_RE.search(e.output) 1589 if m: 1590 logging.error( 1591 'Hit EBUSY while attempting to make missing directories.') 1592 logging.error('lsof output:') 1593 # Don't check for return below since grep exits with a non-zero when 1594 # no match is found. 1595 for l in self.RunShellCommand( 1596 'lsof | grep %s' % cmd_helper.SingleQuote(m.group(1)), 1597 check_return=False): 1598 logging.error(' %s', l) 1599 raise 1600 self._PushFilesImpl(host_device_tuples, all_changed_files) 1601 for func in cache_commit_funcs: 1602 func() 1603 1604 def _GetChangedAndStaleFiles(self, host_path, device_path, track_stale=False): 1605 """Get files to push and delete 1606 1607 Args: 1608 host_path: an absolute path of a file or directory on the host 1609 device_path: an absolute path of a file or directory on the device 1610 track_stale: whether to bother looking for stale files (slower) 1611 1612 Returns: 1613 a four-element tuple 1614 1st element: a list of (host_files_path, device_files_path) tuples to push 1615 2nd element: a list of host_files_path that are up-to-date 1616 3rd element: a list of stale files under device_path, or [] when 1617 track_stale == False 1618 4th element: a cache commit function. 1619 """ 1620 try: 1621 # Length calculations below assume no trailing /. 1622 host_path = host_path.rstrip('/') 1623 device_path = device_path.rstrip('/') 1624 1625 specific_device_paths = [device_path] 1626 ignore_other_files = not track_stale and os.path.isdir(host_path) 1627 if ignore_other_files: 1628 specific_device_paths = [] 1629 for root, _, filenames in os.walk(host_path): 1630 relative_dir = root[len(host_path) + 1:] 1631 specific_device_paths.extend( 1632 posixpath.join(device_path, relative_dir, f) for f in filenames) 1633 1634 def calculate_host_checksums(): 1635 return md5sum.CalculateHostMd5Sums([host_path]) 1636 1637 def calculate_device_checksums(): 1638 if self._enable_device_files_cache: 1639 cache_entry = self._cache['device_path_checksums'].get(device_path) 1640 if cache_entry and cache_entry[0] == ignore_other_files: 1641 return dict(cache_entry[1]) 1642 1643 sums = md5sum.CalculateDeviceMd5Sums(specific_device_paths, self) 1644 1645 cache_entry = [ignore_other_files, sums] 1646 self._cache['device_path_checksums'][device_path] = cache_entry 1647 return dict(sums) 1648 1649 host_checksums, device_checksums = reraiser_thread.RunAsync(( 1650 calculate_host_checksums, 1651 calculate_device_checksums)) 1652 except EnvironmentError as e: 1653 logger.warning('Error calculating md5: %s', e) 1654 return ([(host_path, device_path)], [], [], lambda: 0) 1655 1656 to_push = [] 1657 up_to_date = [] 1658 to_delete = [] 1659 if os.path.isfile(host_path): 1660 host_checksum = host_checksums.get(host_path) 1661 device_checksum = device_checksums.get(device_path) 1662 if host_checksum == device_checksum: 1663 up_to_date.append(host_path) 1664 else: 1665 to_push.append((host_path, device_path)) 1666 else: 1667 for host_abs_path, host_checksum in host_checksums.iteritems(): 1668 device_abs_path = posixpath.join( 1669 device_path, os.path.relpath(host_abs_path, host_path)) 1670 device_checksum = device_checksums.pop(device_abs_path, None) 1671 if device_checksum == host_checksum: 1672 up_to_date.append(host_abs_path) 1673 else: 1674 to_push.append((host_abs_path, device_abs_path)) 1675 to_delete = device_checksums.keys() 1676 # We can't rely solely on the checksum approach since it does not catch 1677 # stale directories, which can result in empty directories that cause issues 1678 # during copying in efficient_android_directory_copy.sh. So, find any stale 1679 # directories here so they can be removed in addition to stale files. 1680 if track_stale: 1681 to_delete.extend(self._GetStaleDirectories(host_path, device_path)) 1682 1683 def cache_commit_func(): 1684 # When host_path is a not a directory, the path.join() call below would 1685 # have an '' as the second argument, causing an unwanted / to be appended. 1686 if os.path.isfile(host_path): 1687 assert len(host_checksums) == 1 1688 new_sums = {device_path: host_checksums[host_path]} 1689 else: 1690 new_sums = {posixpath.join(device_path, path[len(host_path) + 1:]): val 1691 for path, val in host_checksums.iteritems()} 1692 cache_entry = [ignore_other_files, new_sums] 1693 self._cache['device_path_checksums'][device_path] = cache_entry 1694 1695 return (to_push, up_to_date, to_delete, cache_commit_func) 1696 1697 def _GetStaleDirectories(self, host_path, device_path): 1698 """Gets a list of stale directories on the device. 1699 1700 Args: 1701 host_path: an absolute path of a directory on the host 1702 device_path: an absolute path of a directory on the device 1703 1704 Returns: 1705 A list containing absolute paths to directories on the device that are 1706 considered stale. 1707 """ 1708 def get_device_dirs(path): 1709 directories = set() 1710 command = _RECURSIVE_DIRECTORY_LIST_SCRIPT % cmd_helper.SingleQuote(path) 1711 # We use shell=True to evaluate the command as a script through the shell, 1712 # otherwise RunShellCommand tries to interpret it as the name of a (non 1713 # existent) command to run. 1714 for line in self.RunShellCommand( 1715 command, shell=True, check_return=True): 1716 directories.add(posixpath.relpath(posixpath.normpath(line), path)) 1717 return directories 1718 1719 def get_host_dirs(path): 1720 directories = set() 1721 if not os.path.isdir(path): 1722 return directories 1723 for root, _, _ in os.walk(path): 1724 if root != path: 1725 # Strip off the top level directory so we can compare the device and 1726 # host. 1727 directories.add( 1728 os.path.relpath(root, path).replace(os.sep, posixpath.sep)) 1729 return directories 1730 1731 host_dirs = get_host_dirs(host_path) 1732 device_dirs = get_device_dirs(device_path) 1733 stale_dirs = device_dirs - host_dirs 1734 return [posixpath.join(device_path, d) for d in stale_dirs] 1735 1736 def _ComputeDeviceChecksumsForApks(self, package_name): 1737 ret = self._cache['package_apk_checksums'].get(package_name) 1738 if ret is None: 1739 device_paths = self._GetApplicationPathsInternal(package_name) 1740 file_to_checksums = md5sum.CalculateDeviceMd5Sums(device_paths, self) 1741 ret = set(file_to_checksums.values()) 1742 self._cache['package_apk_checksums'][package_name] = ret 1743 return ret 1744 1745 def _ComputeStaleApks(self, package_name, host_apk_paths): 1746 def calculate_host_checksums(): 1747 return md5sum.CalculateHostMd5Sums(host_apk_paths) 1748 1749 def calculate_device_checksums(): 1750 return self._ComputeDeviceChecksumsForApks(package_name) 1751 1752 host_checksums, device_checksums = reraiser_thread.RunAsync(( 1753 calculate_host_checksums, calculate_device_checksums)) 1754 stale_apks = [k for (k, v) in host_checksums.iteritems() 1755 if v not in device_checksums] 1756 return stale_apks, set(host_checksums.values()) 1757 1758 def _PushFilesImpl(self, host_device_tuples, files): 1759 if not files: 1760 return 1761 1762 size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files) 1763 file_count = len(files) 1764 dir_size = sum(host_utils.GetRecursiveDiskUsage(h) 1765 for h, _ in host_device_tuples) 1766 dir_file_count = 0 1767 for h, _ in host_device_tuples: 1768 if os.path.isdir(h): 1769 dir_file_count += sum(len(f) for _r, _d, f in os.walk(h)) 1770 else: 1771 dir_file_count += 1 1772 1773 push_duration = self._ApproximateDuration( 1774 file_count, file_count, size, False) 1775 dir_push_duration = self._ApproximateDuration( 1776 len(host_device_tuples), dir_file_count, dir_size, False) 1777 zip_duration = self._ApproximateDuration(1, 1, size, True) 1778 1779 if (dir_push_duration < push_duration and dir_push_duration < zip_duration 1780 # TODO(jbudorick): Resume directory pushing once clients have switched 1781 # to 1.0.36-compatible syntax. 1782 and False): 1783 self._PushChangedFilesIndividually(host_device_tuples) 1784 elif push_duration < zip_duration: 1785 self._PushChangedFilesIndividually(files) 1786 elif self._commands_installed is False: 1787 # Already tried and failed to install unzip command. 1788 self._PushChangedFilesIndividually(files) 1789 elif not self._PushChangedFilesZipped( 1790 files, [d for _, d in host_device_tuples]): 1791 self._PushChangedFilesIndividually(files) 1792 1793 def _MaybeInstallCommands(self): 1794 if self._commands_installed is None: 1795 try: 1796 if not install_commands.Installed(self): 1797 install_commands.InstallCommands(self) 1798 self._commands_installed = True 1799 except device_errors.CommandFailedError as e: 1800 logger.warning('unzip not available: %s', str(e)) 1801 self._commands_installed = False 1802 return self._commands_installed 1803 1804 @staticmethod 1805 def _ApproximateDuration(adb_calls, file_count, byte_count, is_zipping): 1806 # We approximate the time to push a set of files to a device as: 1807 # t = c1 * a + c2 * f + c3 + b / c4 + b / (c5 * c6), where 1808 # t: total time (sec) 1809 # c1: adb call time delay (sec) 1810 # a: number of times adb is called (unitless) 1811 # c2: push time delay (sec) 1812 # f: number of files pushed via adb (unitless) 1813 # c3: zip time delay (sec) 1814 # c4: zip rate (bytes/sec) 1815 # b: total number of bytes (bytes) 1816 # c5: transfer rate (bytes/sec) 1817 # c6: compression ratio (unitless) 1818 1819 # All of these are approximations. 1820 ADB_CALL_PENALTY = 0.1 # seconds 1821 ADB_PUSH_PENALTY = 0.01 # seconds 1822 ZIP_PENALTY = 2.0 # seconds 1823 ZIP_RATE = 10000000.0 # bytes / second 1824 TRANSFER_RATE = 2000000.0 # bytes / second 1825 COMPRESSION_RATIO = 2.0 # unitless 1826 1827 adb_call_time = ADB_CALL_PENALTY * adb_calls 1828 adb_push_setup_time = ADB_PUSH_PENALTY * file_count 1829 if is_zipping: 1830 zip_time = ZIP_PENALTY + byte_count / ZIP_RATE 1831 transfer_time = byte_count / (TRANSFER_RATE * COMPRESSION_RATIO) 1832 else: 1833 zip_time = 0 1834 transfer_time = byte_count / TRANSFER_RATE 1835 return adb_call_time + adb_push_setup_time + zip_time + transfer_time 1836 1837 def _PushChangedFilesIndividually(self, files): 1838 for h, d in files: 1839 self.adb.Push(h, d) 1840 1841 def _PushChangedFilesZipped(self, files, dirs): 1842 if not self._MaybeInstallCommands(): 1843 return False 1844 1845 with tempfile_ext.NamedTemporaryDirectory() as working_dir: 1846 zip_path = os.path.join(working_dir, 'tmp.zip') 1847 try: 1848 zip_utils.WriteZipFile(zip_path, files) 1849 except zip_utils.ZipFailedError: 1850 return False 1851 1852 logger.info('Pushing %d files via .zip of size %d', len(files), 1853 os.path.getsize(zip_path)) 1854 self.NeedsSU() 1855 with device_temp_file.DeviceTempFile( 1856 self.adb, suffix='.zip') as device_temp: 1857 self.adb.Push(zip_path, device_temp.name) 1858 1859 quoted_dirs = ' '.join(cmd_helper.SingleQuote(d) for d in dirs) 1860 self.RunShellCommand( 1861 'unzip %s&&chmod -R 777 %s' % (device_temp.name, quoted_dirs), 1862 shell=True, as_root=True, 1863 env={'PATH': '%s:$PATH' % install_commands.BIN_DIR}, 1864 check_return=True) 1865 1866 return True 1867 1868 # TODO(nednguyen): remove this and migrate the callsite to PathExists(). 1869 @decorators.WithTimeoutAndRetriesFromInstance() 1870 def FileExists(self, device_path, timeout=None, retries=None): 1871 """Checks whether the given file exists on the device. 1872 1873 Arguments are the same as PathExists. 1874 """ 1875 return self.PathExists(device_path, timeout=timeout, retries=retries) 1876 1877 @decorators.WithTimeoutAndRetriesFromInstance() 1878 def PathExists(self, device_paths, as_root=False, timeout=None, retries=None): 1879 """Checks whether the given path(s) exists on the device. 1880 1881 Args: 1882 device_path: A string containing the absolute path to the file on the 1883 device, or an iterable of paths to check. 1884 as_root: Whether root permissions should be use to check for the existence 1885 of the given path(s). 1886 timeout: timeout in seconds 1887 retries: number of retries 1888 1889 Returns: 1890 True if the all given paths exist on the device, False otherwise. 1891 1892 Raises: 1893 CommandTimeoutError on timeout. 1894 DeviceUnreachableError on missing device. 1895 """ 1896 paths = device_paths 1897 if isinstance(paths, basestring): 1898 paths = (paths,) 1899 if not paths: 1900 return True 1901 cmd = ['test', '-e', paths[0]] 1902 for p in paths[1:]: 1903 cmd.extend(['-a', '-e', p]) 1904 try: 1905 self.RunShellCommand(cmd, as_root=as_root, check_return=True, 1906 timeout=timeout, retries=retries) 1907 return True 1908 except device_errors.CommandFailedError: 1909 return False 1910 1911 @decorators.WithTimeoutAndRetriesFromInstance() 1912 def RemovePath(self, device_path, force=False, recursive=False, 1913 as_root=False, rename=False, timeout=None, retries=None): 1914 """Removes the given path(s) from the device. 1915 1916 Args: 1917 device_path: A string containing the absolute path to the file on the 1918 device, or an iterable of paths to check. 1919 force: Whether to remove the path(s) with force (-f). 1920 recursive: Whether to remove any directories in the path(s) recursively. 1921 as_root: Whether root permissions should be use to remove the given 1922 path(s). 1923 rename: Whether to rename the path(s) before removing to help avoid 1924 filesystem errors. See https://stackoverflow.com/questions/11539657 1925 timeout: timeout in seconds 1926 retries: number of retries 1927 """ 1928 def _RenamePath(path): 1929 random_suffix = hex(random.randint(2 ** 12, 2 ** 16 - 1))[2:] 1930 dest = '%s-%s' % (path, random_suffix) 1931 try: 1932 self.RunShellCommand( 1933 ['mv', path, dest], as_root=as_root, check_return=True) 1934 return dest 1935 except device_errors.AdbShellCommandFailedError: 1936 # If it couldn't be moved, just try rm'ing the original path instead. 1937 return path 1938 args = ['rm'] 1939 if force: 1940 args.append('-f') 1941 if recursive: 1942 args.append('-r') 1943 if isinstance(device_path, basestring): 1944 args.append(device_path if not rename else _RenamePath(device_path)) 1945 else: 1946 args.extend( 1947 device_path if not rename else [_RenamePath(p) for p in device_path]) 1948 self.RunShellCommand(args, as_root=as_root, check_return=True) 1949 1950 @contextlib.contextmanager 1951 def _CopyToReadableLocation(self, device_path): 1952 """Context manager to copy a file to a globally readable temp file. 1953 1954 This uses root permission to copy a file to a globally readable named 1955 temporary file. The temp file is removed when this contextmanager is closed. 1956 1957 Args: 1958 device_path: A string containing the absolute path of the file (on the 1959 device) to copy. 1960 Yields: 1961 The globally readable file object. 1962 """ 1963 with device_temp_file.DeviceTempFile(self.adb) as device_temp: 1964 cmd = 'SRC=%s DEST=%s;cp "$SRC" "$DEST" && chmod 666 "$DEST"' % ( 1965 cmd_helper.SingleQuote(device_path), 1966 cmd_helper.SingleQuote(device_temp.name)) 1967 self.RunShellCommand(cmd, shell=True, as_root=True, check_return=True) 1968 yield device_temp 1969 1970 @decorators.WithTimeoutAndRetriesFromInstance() 1971 def PullFile(self, device_path, host_path, as_root=False, timeout=None, 1972 retries=None): 1973 """Pull a file from the device. 1974 1975 Args: 1976 device_path: A string containing the absolute path of the file to pull 1977 from the device. 1978 host_path: A string containing the absolute path of the destination on 1979 the host. 1980 as_root: Whether root permissions should be used to pull the file. 1981 timeout: timeout in seconds 1982 retries: number of retries 1983 1984 Raises: 1985 CommandFailedError on failure. 1986 CommandTimeoutError on timeout. 1987 """ 1988 # Create the base dir if it doesn't exist already 1989 dirname = os.path.dirname(host_path) 1990 if dirname and not os.path.exists(dirname): 1991 os.makedirs(dirname) 1992 if as_root and self.NeedsSU(): 1993 if not self.PathExists(device_path, as_root=True): 1994 raise device_errors.CommandFailedError( 1995 '%r: No such file or directory' % device_path, str(self)) 1996 with self._CopyToReadableLocation(device_path) as readable_temp_file: 1997 self.adb.Pull(readable_temp_file.name, host_path) 1998 else: 1999 self.adb.Pull(device_path, host_path) 2000 2001 def _ReadFileWithPull(self, device_path): 2002 try: 2003 d = tempfile.mkdtemp() 2004 host_temp_path = os.path.join(d, 'tmp_ReadFileWithPull') 2005 self.adb.Pull(device_path, host_temp_path) 2006 with open(host_temp_path, 'r') as host_temp: 2007 return host_temp.read() 2008 finally: 2009 if os.path.exists(d): 2010 shutil.rmtree(d) 2011 2012 @decorators.WithTimeoutAndRetriesFromInstance() 2013 def ReadFile(self, device_path, as_root=False, force_pull=False, 2014 timeout=None, retries=None): 2015 """Reads the contents of a file from the device. 2016 2017 Args: 2018 device_path: A string containing the absolute path of the file to read 2019 from the device. 2020 as_root: A boolean indicating whether the read should be executed with 2021 root privileges. 2022 force_pull: A boolean indicating whether to force the operation to be 2023 performed by pulling a file from the device. The default is, when the 2024 contents are short, to retrieve the contents using cat instead. 2025 timeout: timeout in seconds 2026 retries: number of retries 2027 2028 Returns: 2029 The contents of |device_path| as a string. Contents are intepreted using 2030 universal newlines, so the caller will see them encoded as '\n'. Also, 2031 all lines will be terminated. 2032 2033 Raises: 2034 AdbCommandFailedError if the file can't be read. 2035 CommandTimeoutError on timeout. 2036 DeviceUnreachableError on missing device. 2037 """ 2038 def get_size(path): 2039 return self.FileSize(path, as_root=as_root) 2040 2041 if (not force_pull 2042 and 0 < get_size(device_path) <= self._MAX_ADB_OUTPUT_LENGTH): 2043 return _JoinLines(self.RunShellCommand( 2044 ['cat', device_path], as_root=as_root, check_return=True)) 2045 elif as_root and self.NeedsSU(): 2046 with self._CopyToReadableLocation(device_path) as readable_temp_file: 2047 return self._ReadFileWithPull(readable_temp_file.name) 2048 else: 2049 return self._ReadFileWithPull(device_path) 2050 2051 def _WriteFileWithPush(self, device_path, contents): 2052 with tempfile.NamedTemporaryFile() as host_temp: 2053 host_temp.write(contents) 2054 host_temp.flush() 2055 self.adb.Push(host_temp.name, device_path) 2056 2057 @decorators.WithTimeoutAndRetriesFromInstance() 2058 def WriteFile(self, device_path, contents, as_root=False, force_push=False, 2059 timeout=None, retries=None): 2060 """Writes |contents| to a file on the device. 2061 2062 Args: 2063 device_path: A string containing the absolute path to the file to write 2064 on the device. 2065 contents: A string containing the data to write to the device. 2066 as_root: A boolean indicating whether the write should be executed with 2067 root privileges (if available). 2068 force_push: A boolean indicating whether to force the operation to be 2069 performed by pushing a file to the device. The default is, when the 2070 contents are short, to pass the contents using a shell script instead. 2071 timeout: timeout in seconds 2072 retries: number of retries 2073 2074 Raises: 2075 CommandFailedError if the file could not be written on the device. 2076 CommandTimeoutError on timeout. 2077 DeviceUnreachableError on missing device. 2078 """ 2079 if not force_push and len(contents) < self._MAX_ADB_COMMAND_LENGTH: 2080 # If the contents are small, for efficieny we write the contents with 2081 # a shell command rather than pushing a file. 2082 cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents), 2083 cmd_helper.SingleQuote(device_path)) 2084 self.RunShellCommand(cmd, shell=True, as_root=as_root, check_return=True) 2085 elif as_root and self.NeedsSU(): 2086 # Adb does not allow to "push with su", so we first push to a temp file 2087 # on a safe location, and then copy it to the desired location with su. 2088 with device_temp_file.DeviceTempFile(self.adb) as device_temp: 2089 self._WriteFileWithPush(device_temp.name, contents) 2090 # Here we need 'cp' rather than 'mv' because the temp and 2091 # destination files might be on different file systems (e.g. 2092 # on internal storage and an external sd card). 2093 self.RunShellCommand(['cp', device_temp.name, device_path], 2094 as_root=True, check_return=True) 2095 else: 2096 # If root is not needed, we can push directly to the desired location. 2097 self._WriteFileWithPush(device_path, contents) 2098 2099 def _ParseLongLsOutput(self, device_path, as_root=False, **kwargs): 2100 """Run and scrape the output of 'ls -a -l' on a device directory.""" 2101 device_path = posixpath.join(device_path, '') # Force trailing '/'. 2102 output = self.RunShellCommand( 2103 ['ls', '-a', '-l', device_path], as_root=as_root, 2104 check_return=True, env={'TZ': 'utc'}, **kwargs) 2105 if output and output[0].startswith('total '): 2106 output.pop(0) # pylint: disable=maybe-no-member 2107 2108 entries = [] 2109 for line in output: 2110 m = _LONG_LS_OUTPUT_RE.match(line) 2111 if m: 2112 if m.group('filename') not in ['.', '..']: 2113 item = m.groupdict() 2114 # A change in toybox is causing recent Android versions to escape 2115 # spaces in file names. Here we just unquote those spaces. If we 2116 # later find more essoteric characters in file names, a more careful 2117 # unquoting mechanism may be needed. But hopefully not. 2118 # See: https://goo.gl/JAebZj 2119 item['filename'] = item['filename'].replace('\\ ', ' ') 2120 entries.append(item) 2121 else: 2122 logger.info('Skipping: %s', line) 2123 2124 return entries 2125 2126 def ListDirectory(self, device_path, as_root=False, **kwargs): 2127 """List all files on a device directory. 2128 2129 Mirroring os.listdir (and most client expectations) the resulting list 2130 does not include the special entries '.' and '..' even if they are present 2131 in the directory. 2132 2133 Args: 2134 device_path: A string containing the path of the directory on the device 2135 to list. 2136 as_root: A boolean indicating whether the to use root privileges to list 2137 the directory contents. 2138 timeout: timeout in seconds 2139 retries: number of retries 2140 2141 Returns: 2142 A list of filenames for all entries contained in the directory. 2143 2144 Raises: 2145 AdbCommandFailedError if |device_path| does not specify a valid and 2146 accessible directory in the device. 2147 CommandTimeoutError on timeout. 2148 DeviceUnreachableError on missing device. 2149 """ 2150 entries = self._ParseLongLsOutput(device_path, as_root=as_root, **kwargs) 2151 return [d['filename'] for d in entries] 2152 2153 def StatDirectory(self, device_path, as_root=False, **kwargs): 2154 """List file and stat info for all entries on a device directory. 2155 2156 Implementation notes: this is currently implemented by parsing the output 2157 of 'ls -a -l' on the device. Whether possible and convenient, we attempt to 2158 make parsing strict and return values mirroring those of the standard |os| 2159 and |stat| Python modules. 2160 2161 Mirroring os.listdir (and most client expectations) the resulting list 2162 does not include the special entries '.' and '..' even if they are present 2163 in the directory. 2164 2165 Args: 2166 device_path: A string containing the path of the directory on the device 2167 to list. 2168 as_root: A boolean indicating whether the to use root privileges to list 2169 the directory contents. 2170 timeout: timeout in seconds 2171 retries: number of retries 2172 2173 Returns: 2174 A list of dictionaries, each containing the following keys: 2175 filename: A string with the file name. 2176 st_mode: File permissions, use the stat module to interpret these. 2177 st_nlink: Number of hard links (may be missing). 2178 st_owner: A string with the user name of the owner. 2179 st_group: A string with the group name of the owner. 2180 st_rdev_pair: Device type as (major, minior) (only if inode device). 2181 st_size: Size of file, in bytes (may be missing for non-regular files). 2182 st_mtime: Time of most recent modification, in seconds since epoch 2183 (although resolution is in minutes). 2184 symbolic_link_to: If entry is a symbolic link, path where it points to; 2185 missing otherwise. 2186 2187 Raises: 2188 AdbCommandFailedError if |device_path| does not specify a valid and 2189 accessible directory in the device. 2190 CommandTimeoutError on timeout. 2191 DeviceUnreachableError on missing device. 2192 """ 2193 entries = self._ParseLongLsOutput(device_path, as_root=as_root, **kwargs) 2194 for d in entries: 2195 for key, value in d.items(): 2196 if value is None: 2197 del d[key] # Remove missing fields. 2198 d['st_mode'] = _ParseModeString(d['st_mode']) 2199 d['st_mtime'] = calendar.timegm( 2200 time.strptime(d['st_mtime'], _LS_DATE_FORMAT)) 2201 for key in ['st_nlink', 'st_size', 'st_rdev_major', 'st_rdev_minor']: 2202 if key in d: 2203 d[key] = int(d[key]) 2204 if 'st_rdev_major' in d and 'st_rdev_minor' in d: 2205 d['st_rdev_pair'] = (d.pop('st_rdev_major'), d.pop('st_rdev_minor')) 2206 return entries 2207 2208 def StatPath(self, device_path, as_root=False, **kwargs): 2209 """Get the stat attributes of a file or directory on the device. 2210 2211 Args: 2212 device_path: A string containing the path of a file or directory from 2213 which to get attributes. 2214 as_root: A boolean indicating whether the to use root privileges to 2215 access the file information. 2216 timeout: timeout in seconds 2217 retries: number of retries 2218 2219 Returns: 2220 A dictionary with the stat info collected; see StatDirectory for details. 2221 2222 Raises: 2223 CommandFailedError if device_path cannot be found on the device. 2224 CommandTimeoutError on timeout. 2225 DeviceUnreachableError on missing device. 2226 """ 2227 dirname, filename = posixpath.split(posixpath.normpath(device_path)) 2228 for entry in self.StatDirectory(dirname, as_root=as_root, **kwargs): 2229 if entry['filename'] == filename: 2230 return entry 2231 raise device_errors.CommandFailedError( 2232 'Cannot find file or directory: %r' % device_path, str(self)) 2233 2234 def FileSize(self, device_path, as_root=False, **kwargs): 2235 """Get the size of a file on the device. 2236 2237 Note: This is implemented by parsing the output of the 'ls' command on 2238 the device. On some Android versions, when passing a directory or special 2239 file, the size is *not* reported and this function will throw an exception. 2240 2241 Args: 2242 device_path: A string containing the path of a file on the device. 2243 as_root: A boolean indicating whether the to use root privileges to 2244 access the file information. 2245 timeout: timeout in seconds 2246 retries: number of retries 2247 2248 Returns: 2249 The size of the file in bytes. 2250 2251 Raises: 2252 CommandFailedError if device_path cannot be found on the device, or 2253 its size cannot be determited for some reason. 2254 CommandTimeoutError on timeout. 2255 DeviceUnreachableError on missing device. 2256 """ 2257 entry = self.StatPath(device_path, as_root=as_root, **kwargs) 2258 try: 2259 return entry['st_size'] 2260 except KeyError: 2261 raise device_errors.CommandFailedError( 2262 'Could not determine the size of: %s' % device_path, str(self)) 2263 2264 @decorators.WithTimeoutAndRetriesFromInstance() 2265 def SetJavaAsserts(self, enabled, timeout=None, retries=None): 2266 """Enables or disables Java asserts. 2267 2268 Args: 2269 enabled: A boolean indicating whether Java asserts should be enabled 2270 or disabled. 2271 timeout: timeout in seconds 2272 retries: number of retries 2273 2274 Returns: 2275 True if the device-side property changed and a restart is required as a 2276 result, False otherwise. 2277 2278 Raises: 2279 CommandTimeoutError on timeout. 2280 """ 2281 def find_property(lines, property_name): 2282 for index, line in enumerate(lines): 2283 if line.strip() == '': 2284 continue 2285 key_value = tuple(s.strip() for s in line.split('=', 1)) 2286 if len(key_value) != 2: 2287 continue 2288 key, value = key_value 2289 if key == property_name: 2290 return index, value 2291 return None, '' 2292 2293 new_value = 'all' if enabled else '' 2294 2295 # First ensure the desired property is persisted. 2296 try: 2297 properties = self.ReadFile(self.LOCAL_PROPERTIES_PATH).splitlines() 2298 except device_errors.CommandFailedError: 2299 properties = [] 2300 index, value = find_property(properties, self.JAVA_ASSERT_PROPERTY) 2301 if new_value != value: 2302 if new_value: 2303 new_line = '%s=%s' % (self.JAVA_ASSERT_PROPERTY, new_value) 2304 if index is None: 2305 properties.append(new_line) 2306 else: 2307 properties[index] = new_line 2308 else: 2309 assert index is not None # since new_value == '' and new_value != value 2310 properties.pop(index) 2311 self.WriteFile(self.LOCAL_PROPERTIES_PATH, _JoinLines(properties)) 2312 2313 # Next, check the current runtime value is what we need, and 2314 # if not, set it and report that a reboot is required. 2315 value = self.GetProp(self.JAVA_ASSERT_PROPERTY) 2316 if new_value != value: 2317 self.SetProp(self.JAVA_ASSERT_PROPERTY, new_value) 2318 return True 2319 else: 2320 return False 2321 2322 def GetLocale(self, cache=False): 2323 """Returns the locale setting on the device. 2324 2325 Args: 2326 cache: Whether to use cached properties when available. 2327 Returns: 2328 A pair (language, country). 2329 """ 2330 locale = self.GetProp('persist.sys.locale', cache=cache) 2331 if locale: 2332 if '-' not in locale: 2333 logging.error('Unparsable locale: %s', locale) 2334 return ('', '') # Behave as if persist.sys.locale is undefined. 2335 return tuple(locale.split('-', 1)) 2336 return (self.GetProp('persist.sys.language', cache=cache), 2337 self.GetProp('persist.sys.country', cache=cache)) 2338 2339 def GetLanguage(self, cache=False): 2340 """Returns the language setting on the device. 2341 2342 DEPRECATED: Prefer GetLocale() instead. 2343 2344 Args: 2345 cache: Whether to use cached properties when available. 2346 """ 2347 return self.GetLocale(cache=cache)[0] 2348 2349 def GetCountry(self, cache=False): 2350 """Returns the country setting on the device. 2351 2352 DEPRECATED: Prefer GetLocale() instead. 2353 2354 Args: 2355 cache: Whether to use cached properties when available. 2356 """ 2357 return self.GetLocale(cache=cache)[1] 2358 2359 @property 2360 def screen_density(self): 2361 """Returns the screen density of the device.""" 2362 DPI_TO_DENSITY = { 2363 120: 'ldpi', 2364 160: 'mdpi', 2365 240: 'hdpi', 2366 320: 'xhdpi', 2367 480: 'xxhdpi', 2368 640: 'xxxhdpi', 2369 } 2370 return DPI_TO_DENSITY.get(self.pixel_density, 'tvdpi') 2371 2372 @property 2373 def pixel_density(self): 2374 return int(self.GetProp('ro.sf.lcd_density', cache=True)) 2375 2376 @property 2377 def build_description(self): 2378 """Returns the build description of the system. 2379 2380 For example: 2381 nakasi-user 4.4.4 KTU84P 1227136 release-keys 2382 """ 2383 return self.GetProp('ro.build.description', cache=True) 2384 2385 @property 2386 def build_fingerprint(self): 2387 """Returns the build fingerprint of the system. 2388 2389 For example: 2390 google/nakasi/grouper:4.4.4/KTU84P/1227136:user/release-keys 2391 """ 2392 return self.GetProp('ro.build.fingerprint', cache=True) 2393 2394 @property 2395 def build_id(self): 2396 """Returns the build ID of the system (e.g. 'KTU84P').""" 2397 return self.GetProp('ro.build.id', cache=True) 2398 2399 @property 2400 def build_product(self): 2401 """Returns the build product of the system (e.g. 'grouper').""" 2402 return self.GetProp('ro.build.product', cache=True) 2403 2404 @property 2405 def build_type(self): 2406 """Returns the build type of the system (e.g. 'user').""" 2407 return self.GetProp('ro.build.type', cache=True) 2408 2409 @property 2410 def build_version_sdk(self): 2411 """Returns the build version sdk of the system as a number (e.g. 19). 2412 2413 For version code numbers see: 2414 http://developer.android.com/reference/android/os/Build.VERSION_CODES.html 2415 2416 For named constants see devil.android.sdk.version_codes 2417 2418 Raises: 2419 CommandFailedError if the build version sdk is not a number. 2420 """ 2421 value = self.GetProp('ro.build.version.sdk', cache=True) 2422 try: 2423 return int(value) 2424 except ValueError: 2425 raise device_errors.CommandFailedError( 2426 'Invalid build version sdk: %r' % value) 2427 2428 @property 2429 def product_cpu_abi(self): 2430 """Returns the product cpu abi of the device (e.g. 'armeabi-v7a'). 2431 2432 For supported ABIs, the return value will be one of the values defined in 2433 devil.android.ndk.abis. 2434 """ 2435 return self.GetProp('ro.product.cpu.abi', cache=True) 2436 2437 @property 2438 def product_model(self): 2439 """Returns the name of the product model (e.g. 'Nexus 7').""" 2440 return self.GetProp('ro.product.model', cache=True) 2441 2442 @property 2443 def product_name(self): 2444 """Returns the product name of the device (e.g. 'nakasi').""" 2445 return self.GetProp('ro.product.name', cache=True) 2446 2447 @property 2448 def product_board(self): 2449 """Returns the product board name of the device (e.g. 'shamu').""" 2450 return self.GetProp('ro.product.board', cache=True) 2451 2452 def _EnsureCacheInitialized(self): 2453 """Populates cache token, runs getprop and fetches $EXTERNAL_STORAGE.""" 2454 if self._cache['token']: 2455 return 2456 with self._cache_lock: 2457 if self._cache['token']: 2458 return 2459 # Change the token every time to ensure that it will match only the 2460 # previously dumped cache. 2461 token = str(uuid.uuid1()) 2462 cmd = ( 2463 'c=/data/local/tmp/cache_token;' 2464 'echo $EXTERNAL_STORAGE;' 2465 'cat $c 2>/dev/null||echo;' 2466 'echo "%s">$c &&' % token + 2467 'getprop' 2468 ) 2469 output = self.RunShellCommand( 2470 cmd, shell=True, check_return=True, large_output=True) 2471 # Error-checking for this existing is done in GetExternalStoragePath(). 2472 self._cache['external_storage'] = output[0] 2473 self._cache['prev_token'] = output[1] 2474 output = output[2:] 2475 2476 prop_cache = self._cache['getprop'] 2477 prop_cache.clear() 2478 for key, value in _GETPROP_RE.findall(''.join(output)): 2479 prop_cache[key] = value 2480 self._cache['token'] = token 2481 2482 @decorators.WithTimeoutAndRetriesFromInstance() 2483 def GetProp(self, property_name, cache=False, timeout=None, retries=None): 2484 """Gets a property from the device. 2485 2486 Args: 2487 property_name: A string containing the name of the property to get from 2488 the device. 2489 cache: Whether to use cached properties when available. 2490 timeout: timeout in seconds 2491 retries: number of retries 2492 2493 Returns: 2494 The value of the device's |property_name| property. 2495 2496 Raises: 2497 CommandTimeoutError on timeout. 2498 """ 2499 assert isinstance(property_name, basestring), ( 2500 "property_name is not a string: %r" % property_name) 2501 2502 if cache: 2503 # It takes ~120ms to query a single property, and ~130ms to query all 2504 # properties. So, when caching we always query all properties. 2505 self._EnsureCacheInitialized() 2506 else: 2507 # timeout and retries are handled down at run shell, because we don't 2508 # want to apply them in the other branch when reading from the cache 2509 value = self.RunShellCommand( 2510 ['getprop', property_name], single_line=True, check_return=True, 2511 timeout=timeout, retries=retries) 2512 self._cache['getprop'][property_name] = value 2513 # Non-existent properties are treated as empty strings by getprop. 2514 return self._cache['getprop'].get(property_name, '') 2515 2516 @decorators.WithTimeoutAndRetriesFromInstance() 2517 def SetProp(self, property_name, value, check=False, timeout=None, 2518 retries=None): 2519 """Sets a property on the device. 2520 2521 Args: 2522 property_name: A string containing the name of the property to set on 2523 the device. 2524 value: A string containing the value to set to the property on the 2525 device. 2526 check: A boolean indicating whether to check that the property was 2527 successfully set on the device. 2528 timeout: timeout in seconds 2529 retries: number of retries 2530 2531 Raises: 2532 CommandFailedError if check is true and the property was not correctly 2533 set on the device (e.g. because it is not rooted). 2534 CommandTimeoutError on timeout. 2535 """ 2536 assert isinstance(property_name, basestring), ( 2537 "property_name is not a string: %r" % property_name) 2538 assert isinstance(value, basestring), "value is not a string: %r" % value 2539 2540 self.RunShellCommand(['setprop', property_name, value], check_return=True) 2541 prop_cache = self._cache['getprop'] 2542 if property_name in prop_cache: 2543 del prop_cache[property_name] 2544 # TODO(perezju) remove the option and make the check mandatory, but using a 2545 # single shell script to both set- and getprop. 2546 if check and value != self.GetProp(property_name, cache=False): 2547 raise device_errors.CommandFailedError( 2548 'Unable to set property %r on the device to %r' 2549 % (property_name, value), str(self)) 2550 2551 @decorators.WithTimeoutAndRetriesFromInstance() 2552 def GetABI(self, timeout=None, retries=None): 2553 """Gets the device main ABI. 2554 2555 Args: 2556 timeout: timeout in seconds 2557 retries: number of retries 2558 2559 Returns: 2560 The device's main ABI name. For supported ABIs, the return value will be 2561 one of the values defined in devil.android.ndk.abis. 2562 2563 Raises: 2564 CommandTimeoutError on timeout. 2565 """ 2566 return self.GetProp('ro.product.cpu.abi', cache=True) 2567 2568 def _GetPsOutput(self, pattern): 2569 """Runs |ps| command on the device and returns its output, 2570 2571 This private method abstracts away differences between Android verions for 2572 calling |ps|, and implements support for filtering the output by a given 2573 |pattern|, but does not do any output parsing. 2574 """ 2575 try: 2576 ps_cmd = 'ps' 2577 # ps behavior was changed in Android O and above, http://crbug.com/686716 2578 if self.build_version_sdk >= version_codes.OREO: 2579 ps_cmd = 'ps -e' 2580 if pattern: 2581 return self._RunPipedShellCommand( 2582 '%s | grep -F %s' % (ps_cmd, cmd_helper.SingleQuote(pattern))) 2583 else: 2584 return self.RunShellCommand( 2585 ps_cmd.split(), check_return=True, large_output=True) 2586 except device_errors.AdbShellCommandFailedError as e: 2587 if e.status and isinstance(e.status, list) and not e.status[0]: 2588 # If ps succeeded but grep failed, there were no processes with the 2589 # given name. 2590 return [] 2591 else: 2592 raise 2593 2594 @decorators.WithTimeoutAndRetriesFromInstance() 2595 def ListProcesses(self, process_name=None, timeout=None, retries=None): 2596 """Returns a list of tuples with info about processes on the device. 2597 2598 This essentially parses the output of the |ps| command into convenient 2599 ProcessInfo tuples. 2600 2601 Args: 2602 process_name: A string used to filter the returned processes. If given, 2603 only processes whose name have this value as a substring 2604 will be returned. 2605 timeout: timeout in seconds 2606 retries: number of retries 2607 2608 Returns: 2609 A list of ProcessInfo tuples with |name|, |pid|, and |ppid| fields. 2610 """ 2611 process_name = process_name or '' 2612 processes = [] 2613 for line in self._GetPsOutput(process_name): 2614 row = line.split() 2615 try: 2616 row = {k: row[i] for k, i in _PS_COLUMNS.iteritems()} 2617 if row['pid'] == 'PID' or process_name not in row['name']: 2618 # Skip over header and non-matching processes. 2619 continue 2620 row['pid'] = int(row['pid']) 2621 row['ppid'] = int(row['ppid']) 2622 except StandardError: # e.g. IndexError, TypeError, ValueError. 2623 logging.warning('failed to parse ps line: %r', line) 2624 continue 2625 processes.append(ProcessInfo(**row)) 2626 return processes 2627 2628 def _GetDumpsysOutput(self, extra_args, pattern=None): 2629 """Runs |dumpsys| command on the device and returns its output. 2630 2631 This private method implements support for filtering the output by a given 2632 |pattern|, but does not do any output parsing. 2633 """ 2634 try: 2635 cmd = ['dumpsys'] + extra_args 2636 if pattern: 2637 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd) 2638 return self._RunPipedShellCommand( 2639 '%s | grep -F %s' % (cmd, cmd_helper.SingleQuote(pattern))) 2640 else: 2641 cmd = ['dumpsys'] + extra_args 2642 return self.RunShellCommand(cmd, check_return=True, large_output=True) 2643 except device_errors.AdbShellCommandFailedError as e: 2644 if e.status and isinstance(e.status, list) and not e.status[0]: 2645 # If dumpsys succeeded but grep failed, there were no lines matching 2646 # the given pattern. 2647 return [] 2648 else: 2649 raise 2650 2651 # TODO(#4103): Remove after migrating clients to ListProcesses. 2652 @decorators.WithTimeoutAndRetriesFromInstance() 2653 def GetPids(self, process_name=None, timeout=None, retries=None): 2654 """Returns the PIDs of processes containing the given name as substring. 2655 2656 DEPRECATED 2657 2658 Note that the |process_name| is often the package name. 2659 2660 Args: 2661 process_name: A string containing the process name to get the PIDs for. 2662 If missing returns PIDs for all processes. 2663 timeout: timeout in seconds 2664 retries: number of retries 2665 2666 Returns: 2667 A dict mapping process name to a list of PIDs for each process that 2668 contained the provided |process_name|. 2669 2670 Raises: 2671 CommandTimeoutError on timeout. 2672 DeviceUnreachableError on missing device. 2673 """ 2674 procs_pids = collections.defaultdict(list) 2675 for p in self.ListProcesses(process_name): 2676 procs_pids[p.name].append(str(p.pid)) 2677 return procs_pids 2678 2679 @decorators.WithTimeoutAndRetriesFromInstance() 2680 def GetApplicationPids(self, process_name, at_most_one=False, 2681 timeout=None, retries=None): 2682 """Returns the PID or PIDs of a given process name. 2683 2684 Note that the |process_name|, often the package name, must match exactly. 2685 2686 Args: 2687 process_name: A string containing the process name to get the PIDs for. 2688 at_most_one: A boolean indicating that at most one PID is expected to 2689 be found. 2690 timeout: timeout in seconds 2691 retries: number of retries 2692 2693 Returns: 2694 A list of the PIDs for the named process. If at_most_one=True returns 2695 the single PID found or None otherwise. 2696 2697 Raises: 2698 CommandFailedError if at_most_one=True and more than one PID is found 2699 for the named process. 2700 CommandTimeoutError on timeout. 2701 DeviceUnreachableError on missing device. 2702 """ 2703 pids = [p.pid for p in self.ListProcesses(process_name) 2704 if p.name == process_name] 2705 if at_most_one: 2706 if len(pids) > 1: 2707 raise device_errors.CommandFailedError( 2708 'Expected a single PID for %r but found: %r.' % ( 2709 process_name, pids), 2710 device_serial=str(self)) 2711 return pids[0] if pids else None 2712 else: 2713 return pids 2714 2715 @decorators.WithTimeoutAndRetriesFromInstance() 2716 def GetEnforce(self, timeout=None, retries=None): 2717 """Get the current mode of SELinux. 2718 2719 Args: 2720 timeout: timeout in seconds 2721 retries: number of retries 2722 2723 Returns: 2724 True (enforcing), False (permissive), or None (disabled). 2725 2726 Raises: 2727 CommandFailedError on failure. 2728 CommandTimeoutError on timeout. 2729 DeviceUnreachableError on missing device. 2730 """ 2731 output = self.RunShellCommand( 2732 ['getenforce'], check_return=True, single_line=True).lower() 2733 if output not in _SELINUX_MODE: 2734 raise device_errors.CommandFailedError( 2735 'Unexpected getenforce output: %s' % output) 2736 return _SELINUX_MODE[output] 2737 2738 @decorators.WithTimeoutAndRetriesFromInstance() 2739 def SetEnforce(self, enabled, timeout=None, retries=None): 2740 """Modify the mode SELinux is running in. 2741 2742 Args: 2743 enabled: a boolean indicating whether to put SELinux in encorcing mode 2744 (if True), or permissive mode (otherwise). 2745 timeout: timeout in seconds 2746 retries: number of retries 2747 2748 Raises: 2749 CommandFailedError on failure. 2750 CommandTimeoutError on timeout. 2751 DeviceUnreachableError on missing device. 2752 """ 2753 self.RunShellCommand( 2754 ['setenforce', '1' if int(enabled) else '0'], as_root=True, 2755 check_return=True) 2756 2757 @decorators.WithTimeoutAndRetriesFromInstance() 2758 def GetWebViewUpdateServiceDump(self, timeout=None, retries=None): 2759 """Get the WebView update command sysdump on the device. 2760 2761 Returns: 2762 A dictionary with these possible entries: 2763 FallbackLogicEnabled: True|False 2764 CurrentWebViewPackage: "package name" or None 2765 MinimumWebViewVersionCode: int 2766 WebViewPackages: Dict of installed WebView providers, mapping "package 2767 name" to "reason it's valid/invalid." 2768 2769 It may return an empty dictionary if device does not 2770 support the "dumpsys webviewupdate" command. 2771 2772 Raises: 2773 CommandFailedError on failure. 2774 CommandTimeoutError on timeout. 2775 DeviceUnreachableError on missing device. 2776 """ 2777 result = {} 2778 2779 # Command was implemented starting in Oreo 2780 if self.build_version_sdk < version_codes.OREO: 2781 return result 2782 2783 output = self.RunShellCommand( 2784 ['dumpsys', 'webviewupdate'], check_return=True) 2785 webview_packages = {} 2786 for line in output: 2787 match = re.search(_WEBVIEW_SYSUPDATE_CURRENT_PKG_RE, line) 2788 if match: 2789 result['CurrentWebViewPackage'] = match.group(1) 2790 match = re.search(_WEBVIEW_SYSUPDATE_NULL_PKG_RE, line) 2791 if match: 2792 result['CurrentWebViewPackage'] = None 2793 match = re.search(_WEBVIEW_SYSUPDATE_FALLBACK_LOGIC_RE, line) 2794 if match: 2795 result['FallbackLogicEnabled'] = \ 2796 True if match.group(1) == 'true' else False 2797 match = re.search(_WEBVIEW_SYSUPDATE_PACKAGE_INSTALLED_RE, line) 2798 if match: 2799 package_name = match.group(1) 2800 reason = match.group(2) 2801 webview_packages[package_name] = reason 2802 match = re.search(_WEBVIEW_SYSUPDATE_PACKAGE_NOT_INSTALLED_RE, line) 2803 if match: 2804 package_name = match.group(1) 2805 reason = match.group(2) 2806 webview_packages[package_name] = reason 2807 match = re.search(_WEBVIEW_SYSUPDATE_MIN_VERSION_CODE, line) 2808 if match: 2809 result['MinimumWebViewVersionCode'] = int(match.group(1)) 2810 if webview_packages: 2811 result['WebViewPackages'] = webview_packages 2812 2813 missing_fields = set(['CurrentWebViewPackage', 'FallbackLogicEnabled']) - \ 2814 set(result.keys()) 2815 if len(missing_fields) > 0: 2816 raise device_errors.CommandFailedError( 2817 '%s not found in dumpsys webviewupdate' % str(list(missing_fields))) 2818 return result 2819 2820 @decorators.WithTimeoutAndRetriesFromInstance() 2821 def SetWebViewImplementation(self, package_name, timeout=None, retries=None): 2822 """Select the WebView implementation to the specified package. 2823 2824 Args: 2825 package_name: The package name of a WebView implementation. The package 2826 must be already installed on the device. 2827 timeout: timeout in seconds 2828 retries: number of retries 2829 2830 Raises: 2831 CommandFailedError on failure. 2832 CommandTimeoutError on timeout. 2833 DeviceUnreachableError on missing device. 2834 """ 2835 installed = self.GetApplicationPaths(package_name) 2836 if not installed: 2837 raise device_errors.CommandFailedError( 2838 '%s is not installed' % package_name, str(self)) 2839 output = self.RunShellCommand( 2840 ['cmd', 'webviewupdate', 'set-webview-implementation', package_name], 2841 single_line=True, 2842 check_return=False) 2843 if output == 'Success': 2844 logging.info('WebView provider set to: %s', package_name) 2845 else: 2846 dumpsys_output = self.GetWebViewUpdateServiceDump() 2847 webview_packages = dumpsys_output.get('WebViewPackages') 2848 if webview_packages: 2849 reason = webview_packages.get(package_name) 2850 if not reason: 2851 all_provider_package_names = webview_packages.keys() 2852 raise device_errors.CommandFailedError( 2853 '%s is not in the system WebView provider list. Must choose one ' 2854 'of %r.' % (package_name, all_provider_package_names), str(self)) 2855 if re.search(r'is\s+NOT\s+installed/enabled for all users', reason): 2856 raise device_errors.CommandFailedError( 2857 '%s is disabled, make sure to disable WebView fallback logic' % 2858 package_name, str(self)) 2859 if re.search(r'No WebView-library manifest flag', reason): 2860 raise device_errors.CommandFailedError( 2861 '%s does not declare a WebView native library, so it cannot ' 2862 'be a WebView provider' % package_name, str(self)) 2863 if re.search(r'SDK version too low', reason): 2864 raise device_errors.CommandFailedError( 2865 '%s needs a higher targetSdkVersion (must be >= %d)' % 2866 (package_name, self.build_version_sdk), str(self)) 2867 if re.search(r'Version code too low', reason): 2868 raise device_errors.CommandFailedError( 2869 '%s needs a higher versionCode (must be >= %d)' % 2870 (package_name, dumpsys_output.get('MinimumWebViewVersionCode')), 2871 str(self)) 2872 if re.search(r'Incorrect signature', reason): 2873 raise device_errors.CommandFailedError( 2874 '%s is not signed with release keys (but user builds require ' 2875 'this for WebView providers)' % package_name, str(self)) 2876 raise device_errors.CommandFailedError( 2877 'Error setting WebView provider: %s' % output, str(self)) 2878 2879 @decorators.WithTimeoutAndRetriesFromInstance() 2880 def SetWebViewFallbackLogic(self, enabled, timeout=None, retries=None): 2881 """Set whether WebViewUpdateService's "fallback logic" should be enabled. 2882 2883 WebViewUpdateService has nonintuitive "fallback logic" for devices where 2884 Monochrome (Chrome Stable) is preinstalled as the WebView provider, with a 2885 "stub" (little-to-no code) implementation of standalone WebView. 2886 2887 "Fallback logic" (enabled by default) is designed, in the case where the 2888 user has disabled Chrome, to fall back to the stub standalone WebView by 2889 enabling the package. The implementation plumbs through the Chrome APK until 2890 Play Store installs an update with the full implementation. 2891 2892 A surprising side-effect of "fallback logic" is that, immediately after 2893 sideloading WebView, WebViewUpdateService re-disables the package and 2894 uninstalls the update. This can prevent successfully using standalone 2895 WebView for development, although "fallback logic" can be disabled on 2896 userdebug/eng devices. 2897 2898 Because this is only relevant for devices with the standalone WebView stub, 2899 this command is only relevant on N-P (inclusive). 2900 2901 You can determine if "fallback logic" is currently enabled by checking 2902 FallbackLogicEnabled in the dictionary returned by 2903 GetWebViewUpdateServiceDump. 2904 2905 Args: 2906 enabled: bool - True for enabled, False for disabled 2907 timeout: timeout in seconds 2908 retries: number of retries 2909 2910 Raises: 2911 CommandFailedError on failure. 2912 CommandTimeoutError on timeout. 2913 DeviceUnreachableError on missing device. 2914 """ 2915 2916 # Command is only available on devices which preinstall stub WebView. 2917 if not version_codes.NOUGAT <= self.build_version_sdk <= version_codes.PIE: 2918 return 2919 2920 # redundant-packages is the opposite of fallback logic 2921 enable_string = 'disable' if enabled else 'enable' 2922 output = self.RunShellCommand( 2923 ['cmd', 'webviewupdate', '%s-redundant-packages' % enable_string], 2924 single_line=True, check_return=True) 2925 if output == 'Success': 2926 logging.info('WebView Fallback Logic is %s', 2927 'enabled' if enabled else 'disabled') 2928 else: 2929 raise device_errors.CommandFailedError( 2930 'Error setting WebView Fallback Logic: %s' % output, str(self)) 2931 2932 @decorators.WithTimeoutAndRetriesFromInstance() 2933 def TakeScreenshot(self, host_path=None, timeout=None, retries=None): 2934 """Takes a screenshot of the device. 2935 2936 Args: 2937 host_path: A string containing the path on the host to save the 2938 screenshot to. If None, a file name in the current 2939 directory will be generated. 2940 timeout: timeout in seconds 2941 retries: number of retries 2942 2943 Returns: 2944 The name of the file on the host to which the screenshot was saved. 2945 2946 Raises: 2947 CommandFailedError on failure. 2948 CommandTimeoutError on timeout. 2949 DeviceUnreachableError on missing device. 2950 """ 2951 if not host_path: 2952 host_path = os.path.abspath('screenshot-%s-%s.png' % ( 2953 self.serial, _GetTimeStamp())) 2954 with device_temp_file.DeviceTempFile(self.adb, suffix='.png') as device_tmp: 2955 self.RunShellCommand(['/system/bin/screencap', '-p', device_tmp.name], 2956 check_return=True) 2957 self.PullFile(device_tmp.name, host_path) 2958 return host_path 2959 2960 @decorators.WithTimeoutAndRetriesFromInstance() 2961 def DismissCrashDialogIfNeeded(self, timeout=None, retries=None): 2962 """Dismiss the error/ANR dialog if present. 2963 2964 Returns: Name of the crashed package if a dialog is focused, 2965 None otherwise. 2966 """ 2967 def _FindFocusedWindow(): 2968 match = None 2969 # TODO(jbudorick): Try to grep the output on the device instead of using 2970 # large_output if/when DeviceUtils exposes a public interface for piped 2971 # shell command handling. 2972 for line in self.RunShellCommand(['dumpsys', 'window', 'windows'], 2973 check_return=True, large_output=True): 2974 match = re.match(_CURRENT_FOCUS_CRASH_RE, line) 2975 if match: 2976 break 2977 return match 2978 2979 match = _FindFocusedWindow() 2980 if not match: 2981 return None 2982 package = match.group(2) 2983 logger.warning('Trying to dismiss %s dialog for %s', *match.groups()) 2984 self.SendKeyEvent(keyevent.KEYCODE_DPAD_RIGHT) 2985 self.SendKeyEvent(keyevent.KEYCODE_DPAD_RIGHT) 2986 self.SendKeyEvent(keyevent.KEYCODE_ENTER) 2987 match = _FindFocusedWindow() 2988 if match: 2989 logger.error('Still showing a %s dialog for %s', *match.groups()) 2990 return package 2991 2992 def GetLogcatMonitor(self, *args, **kwargs): 2993 """Returns a new LogcatMonitor associated with this device. 2994 2995 Parameters passed to this function are passed directly to 2996 |logcat_monitor.LogcatMonitor| and are documented there. 2997 """ 2998 return logcat_monitor.LogcatMonitor(self.adb, *args, **kwargs) 2999 3000 def GetClientCache(self, client_name): 3001 """Returns client cache.""" 3002 if client_name not in self._client_caches: 3003 self._client_caches[client_name] = {} 3004 return self._client_caches[client_name] 3005 3006 def ClearCache(self): 3007 """Clears all caches.""" 3008 for client in self._client_caches: 3009 self._client_caches[client].clear() 3010 self._cache = { 3011 # Map of packageId -> list of on-device .apk paths 3012 'package_apk_paths': {}, 3013 # Set of packageId that were loaded from LoadCacheData and not yet 3014 # verified. 3015 'package_apk_paths_to_verify': set(), 3016 # Map of packageId -> set of on-device .apk checksums 3017 'package_apk_checksums': {}, 3018 # Map of property_name -> value 3019 'getprop': {}, 3020 # Map of device_path -> [ignore_other_files, map of path->checksum] 3021 'device_path_checksums': {}, 3022 # Location of sdcard ($EXTERNAL_STORAGE). 3023 'external_storage': None, 3024 # Token used to detect when LoadCacheData is stale. 3025 'token': None, 3026 'prev_token': None, 3027 } 3028 3029 @decorators.WithTimeoutAndRetriesFromInstance() 3030 def LoadCacheData(self, data, timeout=None, retries=None): 3031 """Initializes the cache from data created using DumpCacheData. 3032 3033 The cache is used only if its token matches the one found on the device. 3034 This prevents a stale cache from being used (which can happen when sharing 3035 devices). 3036 3037 Args: 3038 data: A previously serialized cache (string). 3039 timeout: timeout in seconds 3040 retries: number of retries 3041 3042 Returns: 3043 Whether the cache was loaded. 3044 """ 3045 obj = json.loads(data) 3046 self._EnsureCacheInitialized() 3047 given_token = obj.get('token') 3048 if not given_token or self._cache['prev_token'] != given_token: 3049 logger.warning('Stale cache detected. Not using it.') 3050 return False 3051 3052 self._cache['package_apk_paths'] = obj.get('package_apk_paths', {}) 3053 # When using a cache across script invokations, verify that apps have 3054 # not been uninstalled. 3055 self._cache['package_apk_paths_to_verify'] = set( 3056 self._cache['package_apk_paths'].iterkeys()) 3057 3058 package_apk_checksums = obj.get('package_apk_checksums', {}) 3059 for k, v in package_apk_checksums.iteritems(): 3060 package_apk_checksums[k] = set(v) 3061 self._cache['package_apk_checksums'] = package_apk_checksums 3062 device_path_checksums = obj.get('device_path_checksums', {}) 3063 self._cache['device_path_checksums'] = device_path_checksums 3064 return True 3065 3066 @decorators.WithTimeoutAndRetriesFromInstance() 3067 def DumpCacheData(self, timeout=None, retries=None): 3068 """Dumps the current cache state to a string. 3069 3070 Args: 3071 timeout: timeout in seconds 3072 retries: number of retries 3073 3074 Returns: 3075 A serialized cache as a string. 3076 """ 3077 self._EnsureCacheInitialized() 3078 obj = {} 3079 obj['token'] = self._cache['token'] 3080 obj['package_apk_paths'] = self._cache['package_apk_paths'] 3081 obj['package_apk_checksums'] = self._cache['package_apk_checksums'] 3082 # JSON can't handle sets. 3083 for k, v in obj['package_apk_checksums'].iteritems(): 3084 obj['package_apk_checksums'][k] = list(v) 3085 obj['device_path_checksums'] = self._cache['device_path_checksums'] 3086 return json.dumps(obj, separators=(',', ':')) 3087 3088 @classmethod 3089 def parallel(cls, devices, async=False): 3090 """Creates a Parallelizer to operate over the provided list of devices. 3091 3092 Args: 3093 devices: A list of either DeviceUtils instances or objects from 3094 from which DeviceUtils instances can be constructed. If None, 3095 all attached devices will be used. 3096 async: If true, returns a Parallelizer that runs operations 3097 asynchronously. 3098 3099 Returns: 3100 A Parallelizer operating over |devices|. 3101 """ 3102 devices = [d if isinstance(d, cls) else cls(d) for d in devices] 3103 if async: 3104 return parallelizer.Parallelizer(devices) 3105 else: 3106 return parallelizer.SyncParallelizer(devices) 3107 3108 @classmethod 3109 def HealthyDevices(cls, blacklist=None, device_arg='default', retries=1, 3110 enable_usb_resets=False, abis=None, **kwargs): 3111 """Returns a list of DeviceUtils instances. 3112 3113 Returns a list of DeviceUtils instances that are attached, not blacklisted, 3114 and optionally filtered by --device flags or ANDROID_SERIAL environment 3115 variable. 3116 3117 Args: 3118 blacklist: A DeviceBlacklist instance (optional). Device serials in this 3119 blacklist will never be returned, but a warning will be logged if they 3120 otherwise would have been. 3121 device_arg: The value of the --device flag. This can be: 3122 'default' -> Same as [], but returns an empty list rather than raise a 3123 NoDevicesError. 3124 [] -> Returns all devices, unless $ANDROID_SERIAL is set. 3125 None -> Use $ANDROID_SERIAL if set, otherwise looks for a single 3126 attached device. Raises an exception if multiple devices are 3127 attached. 3128 'serial' -> Returns an instance for the given serial, if not 3129 blacklisted. 3130 ['A', 'B', ...] -> Returns instances for the subset that is not 3131 blacklisted. 3132 retries: Number of times to restart adb server and query it again if no 3133 devices are found on the previous attempts, with exponential backoffs 3134 up to 60s between each retry. 3135 enable_usb_resets: If true, will attempt to trigger a USB reset prior to 3136 the last attempt if there are no available devices. It will only reset 3137 those that appear to be android devices. 3138 abis: A list of ABIs for which the device needs to support at least one of 3139 (optional). See devil.android.ndk.abis for valid values. 3140 A device serial, or a list of device serials (optional). 3141 3142 Returns: 3143 A list of DeviceUtils instances. 3144 3145 Raises: 3146 NoDevicesError: Raised when no non-blacklisted devices exist and 3147 device_arg is passed. 3148 MultipleDevicesError: Raise when multiple devices exist, but |device_arg| 3149 is None. 3150 """ 3151 allow_no_devices = False 3152 if device_arg == 'default': 3153 allow_no_devices = True 3154 device_arg = () 3155 3156 select_multiple = True 3157 if not (isinstance(device_arg, tuple) or isinstance(device_arg, list)): 3158 select_multiple = False 3159 if device_arg: 3160 device_arg = (device_arg,) 3161 3162 blacklisted_devices = blacklist.Read() if blacklist else [] 3163 3164 # adb looks for ANDROID_SERIAL, so support it as well. 3165 android_serial = os.environ.get('ANDROID_SERIAL') 3166 if not device_arg and android_serial: 3167 device_arg = (android_serial,) 3168 3169 def blacklisted(serial): 3170 if serial in blacklisted_devices: 3171 logger.warning('Device %s is blacklisted.', serial) 3172 return True 3173 return False 3174 3175 def supports_abi(abi, serial): 3176 if abis and abi not in abis: 3177 logger.warning("Device %s doesn't support required ABIs.", serial) 3178 return False 3179 return True 3180 3181 def _get_devices(): 3182 if device_arg: 3183 devices = [cls(x, **kwargs) for x in device_arg if not blacklisted(x)] 3184 else: 3185 devices = [] 3186 for adb in adb_wrapper.AdbWrapper.Devices(): 3187 serial = adb.GetDeviceSerial() 3188 if not blacklisted(serial): 3189 device = cls(_CreateAdbWrapper(adb), **kwargs) 3190 if supports_abi(device.GetABI(), serial): 3191 devices.append(device) 3192 3193 if len(devices) == 0 and not allow_no_devices: 3194 raise device_errors.NoDevicesError() 3195 if len(devices) > 1 and not select_multiple: 3196 raise device_errors.MultipleDevicesError(devices) 3197 return sorted(devices) 3198 3199 def _reset_devices(): 3200 if not reset_usb: 3201 logging.error( 3202 'reset_usb.py not supported on this platform (%s). Skipping usb ' 3203 'resets.', sys.platform) 3204 return 3205 if device_arg: 3206 for serial in device_arg: 3207 reset_usb.reset_android_usb(serial) 3208 else: 3209 reset_usb.reset_all_android_devices() 3210 3211 for attempt in xrange(retries+1): 3212 try: 3213 return _get_devices() 3214 except device_errors.NoDevicesError: 3215 if attempt == retries: 3216 logging.error('No devices found after exhausting all retries.') 3217 raise 3218 elif attempt == retries - 1 and enable_usb_resets: 3219 logging.warning( 3220 'Attempting to reset relevant USB devices prior to the last ' 3221 'attempt.') 3222 _reset_devices() 3223 # math.pow returns floats, so cast to int for easier testing 3224 sleep_s = min(int(math.pow(2, attempt + 1)), 60) 3225 logger.warning( 3226 'No devices found. Will try again after restarting adb server ' 3227 'and a short nap of %d s.', sleep_s) 3228 time.sleep(sleep_s) 3229 RestartServer() 3230 3231 @decorators.WithTimeoutAndRetriesFromInstance() 3232 def RestartAdbd(self, timeout=None, retries=None): 3233 logger.info('Restarting adbd on device.') 3234 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script: 3235 self.WriteFile(script.name, _RESTART_ADBD_SCRIPT) 3236 self.RunShellCommand( 3237 ['source', script.name], check_return=True, as_root=True) 3238 self.adb.WaitForDevice() 3239 3240 @decorators.WithTimeoutAndRetriesFromInstance() 3241 def GrantPermissions(self, package, permissions, timeout=None, retries=None): 3242 # Permissions only need to be set on M and above because of the changes to 3243 # the permission model. 3244 if not permissions or self.build_version_sdk < version_codes.MARSHMALLOW: 3245 return 3246 3247 permissions = set( 3248 p for p in permissions if not _PERMISSIONS_BLACKLIST_RE.match(p)) 3249 3250 if ('android.permission.WRITE_EXTERNAL_STORAGE' in permissions 3251 and 'android.permission.READ_EXTERNAL_STORAGE' not in permissions): 3252 permissions.add('android.permission.READ_EXTERNAL_STORAGE') 3253 3254 script = ';'.join([ 3255 'p={package}', 3256 'for q in {permissions}', 3257 'do pm grant "$p" "$q"', 3258 'echo "{sep}$q{sep}$?{sep}"', 3259 'done' 3260 ]).format( 3261 package=cmd_helper.SingleQuote(package), 3262 permissions=' '.join( 3263 cmd_helper.SingleQuote(p) for p in sorted(permissions)), 3264 sep=_SHELL_OUTPUT_SEPARATOR) 3265 3266 logger.info('Setting permissions for %s.', package) 3267 res = self.RunShellCommand( 3268 script, shell=True, raw_output=True, large_output=True, 3269 check_return=True) 3270 res = res.split(_SHELL_OUTPUT_SEPARATOR) 3271 failures = [ 3272 (permission, output.strip()) 3273 for permission, status, output in zip(res[1::3], res[2::3], res[0::3]) 3274 if int(status)] 3275 3276 if failures: 3277 logger.warning( 3278 'Failed to grant some permissions. Blacklist may need to be updated?') 3279 for permission, output in failures: 3280 # Try to grab the relevant error message from the output. 3281 m = _PERMISSIONS_EXCEPTION_RE.search(output) 3282 if m: 3283 error_msg = m.group(0) 3284 elif len(output) > 200: 3285 error_msg = repr(output[:200]) + ' (truncated)' 3286 else: 3287 error_msg = repr(output) 3288 logger.warning('- %s: %s', permission, error_msg) 3289 3290 @decorators.WithTimeoutAndRetriesFromInstance() 3291 def IsScreenOn(self, timeout=None, retries=None): 3292 """Determines if screen is on. 3293 3294 Dumpsys input_method exposes screen on/off state. Below is an explination of 3295 the states. 3296 3297 Pre-L: 3298 On: mScreenOn=true 3299 Off: mScreenOn=false 3300 L+: 3301 On: mInteractive=true 3302 Off: mInteractive=false 3303 3304 Returns: 3305 True if screen is on, false if it is off. 3306 3307 Raises: 3308 device_errors.CommandFailedError: If screen state cannot be found. 3309 """ 3310 if self.build_version_sdk < version_codes.LOLLIPOP: 3311 input_check = 'mScreenOn' 3312 check_value = 'mScreenOn=true' 3313 else: 3314 input_check = 'mInteractive' 3315 check_value = 'mInteractive=true' 3316 dumpsys_out = self._RunPipedShellCommand( 3317 'dumpsys input_method | grep %s' % input_check) 3318 if not dumpsys_out: 3319 raise device_errors.CommandFailedError( 3320 'Unable to detect screen state', str(self)) 3321 return check_value in dumpsys_out[0] 3322 3323 @decorators.WithTimeoutAndRetriesFromInstance() 3324 def SetScreen(self, on, timeout=None, retries=None): 3325 """Turns screen on and off. 3326 3327 Args: 3328 on: bool to decide state to switch to. True = on False = off. 3329 """ 3330 def screen_test(): 3331 return self.IsScreenOn() == on 3332 3333 if screen_test(): 3334 logger.info('Screen already in expected state.') 3335 return 3336 self.SendKeyEvent(keyevent.KEYCODE_POWER) 3337 timeout_retry.WaitFor(screen_test, wait_period=1) 3338 3339 @decorators.WithTimeoutAndRetriesFromInstance() 3340 def ChangeOwner(self, owner_group, paths, timeout=None, retries=None): 3341 """Changes file system ownership for permissions. 3342 3343 Args: 3344 owner_group: New owner and group to assign. Note that this should be a 3345 string in the form user[.group] where the group is option. 3346 paths: Paths to change ownership of. 3347 3348 Note that the -R recursive option is not supported by all Android 3349 versions. 3350 """ 3351 if not paths: 3352 return 3353 self.RunShellCommand(['chown', owner_group] + paths, check_return=True) 3354 3355 @decorators.WithTimeoutAndRetriesFromInstance() 3356 def ChangeSecurityContext(self, security_context, paths, timeout=None, 3357 retries=None): 3358 """Changes the SELinux security context for files. 3359 3360 Args: 3361 security_context: The new security context as a string 3362 paths: Paths to change the security context of. 3363 3364 Note that the -R recursive option is not supported by all Android 3365 versions. 3366 """ 3367 if not paths: 3368 return 3369 command = ['chcon', security_context] + paths 3370 3371 # Note, need to force su because chcon can fail with permission errors even 3372 # if the device is rooted. 3373 self.RunShellCommand(command, as_root=_FORCE_SU, check_return=True) 3374