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