| OLD | NEW | 
|---|
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be | 
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. | 
| 4 | 4 | 
| 5 """Provides a variety of device interactions based on adb. | 5 """Provides a variety of device interactions based on adb. | 
| 6 | 6 | 
| 7 Eventually, this will be based on adb_wrapper. | 7 Eventually, this will be based on adb_wrapper. | 
| 8 """ | 8 """ | 
| 9 # pylint: disable=unused-argument | 9 # pylint: disable=unused-argument | 
| 10 | 10 | 
| 11 import collections | 11 import collections | 
| 12 import contextlib | 12 import contextlib | 
| 13 import itertools | 13 import itertools | 
| 14 import logging | 14 import logging | 
| 15 import multiprocessing | 15 import multiprocessing | 
| 16 import os | 16 import os | 
| 17 import posixpath | 17 import posixpath | 
| 18 import re | 18 import re | 
| 19 import shutil | 19 import shutil | 
| 20 import sys | 20 import sys | 
| 21 import tempfile | 21 import tempfile | 
| 22 import time | 22 import time | 
| 23 import zipfile | 23 import zipfile | 
| 24 | 24 | 
| 25 import pylib.android_commands | 25 import pylib.android_commands | 
| 26 from pylib import cmd_helper | 26 from pylib import cmd_helper | 
| 27 from pylib import constants | 27 from pylib import constants | 
| 28 from pylib import device_signal | 28 from pylib import device_signal | 
|  | 29 from pylib.constants import keyevent | 
| 29 from pylib.device import adb_wrapper | 30 from pylib.device import adb_wrapper | 
| 30 from pylib.device import decorators | 31 from pylib.device import decorators | 
| 31 from pylib.device import device_blacklist | 32 from pylib.device import device_blacklist | 
| 32 from pylib.device import device_errors | 33 from pylib.device import device_errors | 
| 33 from pylib.device import intent | 34 from pylib.device import intent | 
| 34 from pylib.device import logcat_monitor | 35 from pylib.device import logcat_monitor | 
| 35 from pylib.device.commands import install_commands | 36 from pylib.device.commands import install_commands | 
|  | 37 from pylib.sdk import split_select | 
| 36 from pylib.utils import apk_helper | 38 from pylib.utils import apk_helper | 
| 37 from pylib.utils import base_error | 39 from pylib.utils import base_error | 
| 38 from pylib.utils import device_temp_file | 40 from pylib.utils import device_temp_file | 
| 39 from pylib.utils import host_utils | 41 from pylib.utils import host_utils | 
| 40 from pylib.utils import md5sum | 42 from pylib.utils import md5sum | 
| 41 from pylib.utils import parallelizer | 43 from pylib.utils import parallelizer | 
| 42 from pylib.utils import timeout_retry | 44 from pylib.utils import timeout_retry | 
| 43 from pylib.utils import zip_utils | 45 from pylib.utils import zip_utils | 
| 44 | 46 | 
| 45 _DEFAULT_TIMEOUT = 30 | 47 _DEFAULT_TIMEOUT = 30 | 
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 128   # makes sure that the last line is also terminated, and is more memory | 130   # makes sure that the last line is also terminated, and is more memory | 
| 129   # efficient than first appending an end-line to each line and then joining | 131   # efficient than first appending an end-line to each line and then joining | 
| 130   # all of them together. | 132   # all of them together. | 
| 131   return ''.join(s for line in lines for s in (line, '\n')) | 133   return ''.join(s for line in lines for s in (line, '\n')) | 
| 132 | 134 | 
| 133 | 135 | 
| 134 class DeviceUtils(object): | 136 class DeviceUtils(object): | 
| 135 | 137 | 
| 136   _MAX_ADB_COMMAND_LENGTH = 512 | 138   _MAX_ADB_COMMAND_LENGTH = 512 | 
| 137   _MAX_ADB_OUTPUT_LENGTH = 32768 | 139   _MAX_ADB_OUTPUT_LENGTH = 32768 | 
|  | 140   _LAUNCHER_FOCUSED_RE = re.compile( | 
|  | 141       '\s*mCurrentFocus.*(Launcher|launcher).*') | 
| 138   _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') | 142   _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') | 
| 139 | 143 | 
| 140   # Property in /data/local.prop that controls Java assertions. | 144   # Property in /data/local.prop that controls Java assertions. | 
| 141   JAVA_ASSERT_PROPERTY = 'dalvik.vm.enableassertions' | 145   JAVA_ASSERT_PROPERTY = 'dalvik.vm.enableassertions' | 
| 142 | 146 | 
| 143   def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT, | 147   def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT, | 
| 144                default_retries=_DEFAULT_RETRIES): | 148                default_retries=_DEFAULT_RETRIES): | 
| 145     """DeviceUtils constructor. | 149     """DeviceUtils constructor. | 
| 146 | 150 | 
| 147     Args: | 151     Args: | 
| (...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 332     value = self.RunShellCommand('echo $EXTERNAL_STORAGE', | 336     value = self.RunShellCommand('echo $EXTERNAL_STORAGE', | 
| 333                                  single_line=True, | 337                                  single_line=True, | 
| 334                                  check_return=True) | 338                                  check_return=True) | 
| 335     if not value: | 339     if not value: | 
| 336       raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set', | 340       raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set', | 
| 337                                              str(self)) | 341                                              str(self)) | 
| 338     self._cache['external_storage'] = value | 342     self._cache['external_storage'] = value | 
| 339     return value | 343     return value | 
| 340 | 344 | 
| 341   @decorators.WithTimeoutAndRetriesFromInstance() | 345   @decorators.WithTimeoutAndRetriesFromInstance() | 
| 342   def GetApplicationPath(self, package, timeout=None, retries=None): | 346   def GetApplicationPaths(self, package, timeout=None, retries=None): | 
| 343     """Get the path of the installed apk on the device for the given package. | 347     """Get the paths of the installed apks on the device for the given package. | 
| 344 | 348 | 
| 345     Args: | 349     Args: | 
| 346       package: Name of the package. | 350       package: Name of the package. | 
| 347 | 351 | 
| 348     Returns: | 352     Returns: | 
| 349       Path to the apk on the device if it exists, None otherwise. | 353       List of paths to the apks on the device for the given package. | 
| 350     """ | 354     """ | 
| 351     # 'pm path' is liable to incorrectly exit with a nonzero number starting | 355     # 'pm path' is liable to incorrectly exit with a nonzero number starting | 
| 352     # in Lollipop. | 356     # in Lollipop. | 
| 353     # TODO(jbudorick): Check if this is fixed as new Android versions are | 357     # TODO(jbudorick): Check if this is fixed as new Android versions are | 
| 354     # released to put an upper bound on this. | 358     # released to put an upper bound on this. | 
| 355     should_check_return = (self.build_version_sdk < | 359     should_check_return = (self.build_version_sdk < | 
| 356                            constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) | 360                            constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) | 
| 357     output = self.RunShellCommand(['pm', 'path', package], single_line=True, | 361     output = self.RunShellCommand( | 
| 358                                   check_return=should_check_return) | 362         ['pm', 'path', package], check_return=should_check_return) | 
| 359     if not output: | 363     apks = [] | 
| 360       return None | 364     for line in output: | 
| 361     if not output.startswith('package:'): | 365       if not line.startswith('package:'): | 
| 362       raise device_errors.CommandFailedError('pm path returned: %r' % output, | 366         raise device_errors.CommandFailedError( | 
| 363                                              str(self)) | 367             'pm path returned: %r' % '\n'.join(output), str(self)) | 
| 364     return output[len('package:'):] | 368       apks.append(line[len('package:'):]) | 
|  | 369     return apks | 
|  | 370 | 
|  | 371   @decorators.WithTimeoutAndRetriesFromInstance() | 
|  | 372   def GetApplicationDataDirectory(self, package, timeout=None, retries=None): | 
|  | 373     """Get the data directory on the device for the given package. | 
|  | 374 | 
|  | 375     Args: | 
|  | 376       package: Name of the package. | 
|  | 377 | 
|  | 378     Returns: | 
|  | 379       The package's data directory, or None if the package doesn't exist on the | 
|  | 380       device. | 
|  | 381     """ | 
|  | 382     try: | 
|  | 383       output = self._RunPipedShellCommand( | 
|  | 384           'pm dump %s | grep dataDir=' % cmd_helper.SingleQuote(package)) | 
|  | 385       for line in output: | 
|  | 386         _, _, dataDir = line.partition('dataDir=') | 
|  | 387         if dataDir: | 
|  | 388           return dataDir | 
|  | 389     except device_errors.CommandFailedError: | 
|  | 390       logging.exception('Could not find data directory for %s', package) | 
|  | 391     return None | 
| 365 | 392 | 
| 366   @decorators.WithTimeoutAndRetriesFromInstance() | 393   @decorators.WithTimeoutAndRetriesFromInstance() | 
| 367   def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None): | 394   def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None): | 
| 368     """Wait for the device to fully boot. | 395     """Wait for the device to fully boot. | 
| 369 | 396 | 
| 370     This means waiting for the device to boot, the package manager to be | 397     This means waiting for the device to boot, the package manager to be | 
| 371     available, and the SD card to be ready. It can optionally mean waiting | 398     available, and the SD card to be ready. It can optionally mean waiting | 
| 372     for wifi to come up, too. | 399     for wifi to come up, too. | 
| 373 | 400 | 
| 374     Args: | 401     Args: | 
| 375       wifi: A boolean indicating if we should wait for wifi to come up or not. | 402       wifi: A boolean indicating if we should wait for wifi to come up or not. | 
| 376       timeout: timeout in seconds | 403       timeout: timeout in seconds | 
| 377       retries: number of retries | 404       retries: number of retries | 
| 378 | 405 | 
| 379     Raises: | 406     Raises: | 
| 380       CommandFailedError on failure. | 407       CommandFailedError on failure. | 
| 381       CommandTimeoutError if one of the component waits times out. | 408       CommandTimeoutError if one of the component waits times out. | 
| 382       DeviceUnreachableError if the device becomes unresponsive. | 409       DeviceUnreachableError if the device becomes unresponsive. | 
| 383     """ | 410     """ | 
| 384     def sd_card_ready(): | 411     def sd_card_ready(): | 
| 385       try: | 412       try: | 
| 386         self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()], | 413         self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()], | 
| 387                              check_return=True) | 414                              check_return=True) | 
| 388         return True | 415         return True | 
| 389       except device_errors.AdbCommandFailedError: | 416       except device_errors.AdbCommandFailedError: | 
| 390         return False | 417         return False | 
| 391 | 418 | 
| 392     def pm_ready(): | 419     def pm_ready(): | 
| 393       try: | 420       try: | 
| 394         return self.GetApplicationPath('android') | 421         return self.GetApplicationPaths('android') | 
| 395       except device_errors.CommandFailedError: | 422       except device_errors.CommandFailedError: | 
| 396         return False | 423         return False | 
| 397 | 424 | 
| 398     def boot_completed(): | 425     def boot_completed(): | 
| 399       return self.GetProp('sys.boot_completed') == '1' | 426       return self.GetProp('sys.boot_completed') == '1' | 
| 400 | 427 | 
| 401     def wifi_enabled(): | 428     def wifi_enabled(): | 
| 402       return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'], | 429       return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'], | 
| 403                                                         check_return=False) | 430                                                         check_return=False) | 
| 404 | 431 | 
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 454       reinstall: A boolean indicating if we should keep any existing app data. | 481       reinstall: A boolean indicating if we should keep any existing app data. | 
| 455       timeout: timeout in seconds | 482       timeout: timeout in seconds | 
| 456       retries: number of retries | 483       retries: number of retries | 
| 457 | 484 | 
| 458     Raises: | 485     Raises: | 
| 459       CommandFailedError if the installation fails. | 486       CommandFailedError if the installation fails. | 
| 460       CommandTimeoutError if the installation times out. | 487       CommandTimeoutError if the installation times out. | 
| 461       DeviceUnreachableError on missing device. | 488       DeviceUnreachableError on missing device. | 
| 462     """ | 489     """ | 
| 463     package_name = apk_helper.GetPackageName(apk_path) | 490     package_name = apk_helper.GetPackageName(apk_path) | 
| 464     device_path = self.GetApplicationPath(package_name) | 491     device_paths = self.GetApplicationPaths(package_name) | 
| 465     if device_path is not None: | 492     if device_paths: | 
| 466       should_install = bool(self._GetChangedFilesImpl(apk_path, device_path)) | 493       if len(device_paths) > 1: | 
|  | 494         logging.warning( | 
|  | 495             'Installing single APK (%s) when split APKs (%s) are currently ' | 
|  | 496             'installed.', apk_path, ' '.join(device_paths)) | 
|  | 497       (files_to_push, _) = self._GetChangedAndStaleFiles( | 
|  | 498           apk_path, device_paths[0]) | 
|  | 499       should_install = bool(files_to_push) | 
| 467       if should_install and not reinstall: | 500       if should_install and not reinstall: | 
| 468         self.adb.Uninstall(package_name) | 501         self.adb.Uninstall(package_name) | 
| 469     else: | 502     else: | 
| 470       should_install = True | 503       should_install = True | 
| 471     if should_install: | 504     if should_install: | 
| 472       self.adb.Install(apk_path, reinstall=reinstall) | 505       self.adb.Install(apk_path, reinstall=reinstall) | 
| 473 | 506 | 
|  | 507   @decorators.WithTimeoutAndRetriesDefaults( | 
|  | 508       INSTALL_DEFAULT_TIMEOUT, | 
|  | 509       INSTALL_DEFAULT_RETRIES) | 
|  | 510   def InstallSplitApk(self, base_apk, split_apks, reinstall=False, | 
|  | 511                       timeout=None, retries=None): | 
|  | 512     """Install a split APK. | 
|  | 513 | 
|  | 514     Noop if all of the APK splits are already installed. | 
|  | 515 | 
|  | 516     Args: | 
|  | 517       base_apk: A string of the path to the base APK. | 
|  | 518       split_apks: A list of strings of paths of all of the APK splits. | 
|  | 519       reinstall: A boolean indicating if we should keep any existing app data. | 
|  | 520       timeout: timeout in seconds | 
|  | 521       retries: number of retries | 
|  | 522 | 
|  | 523     Raises: | 
|  | 524       CommandFailedError if the installation fails. | 
|  | 525       CommandTimeoutError if the installation times out. | 
|  | 526       DeviceUnreachableError on missing device. | 
|  | 527       DeviceVersionError if device SDK is less than Android L. | 
|  | 528     """ | 
|  | 529     self._CheckSdkLevel(constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP) | 
|  | 530 | 
|  | 531     all_apks = [base_apk] + split_select.SelectSplits( | 
|  | 532         self, base_apk, split_apks) | 
|  | 533     package_name = apk_helper.GetPackageName(base_apk) | 
|  | 534     device_apk_paths = self.GetApplicationPaths(package_name) | 
|  | 535 | 
|  | 536     if device_apk_paths: | 
|  | 537       partial_install_package = package_name | 
|  | 538       device_checksums = md5sum.CalculateDeviceMd5Sums(device_apk_paths, self) | 
|  | 539       host_checksums = md5sum.CalculateHostMd5Sums(all_apks) | 
|  | 540       apks_to_install = [k for (k, v) in host_checksums.iteritems() | 
|  | 541                          if v not in device_checksums.values()] | 
|  | 542       if apks_to_install and not reinstall: | 
|  | 543         self.adb.Uninstall(package_name) | 
|  | 544         partial_install_package = None | 
|  | 545         apks_to_install = all_apks | 
|  | 546     else: | 
|  | 547       partial_install_package = None | 
|  | 548       apks_to_install = all_apks | 
|  | 549     if apks_to_install: | 
|  | 550       self.adb.InstallMultiple( | 
|  | 551           apks_to_install, partial=partial_install_package, reinstall=reinstall) | 
|  | 552 | 
|  | 553   def _CheckSdkLevel(self, required_sdk_level): | 
|  | 554     """Raises an exception if the device does not have the required SDK level. | 
|  | 555     """ | 
|  | 556     if self.build_version_sdk < required_sdk_level: | 
|  | 557       raise device_errors.DeviceVersionError( | 
|  | 558           ('Requires SDK level %s, device is SDK level %s' % | 
|  | 559            (required_sdk_level, self.build_version_sdk)), | 
|  | 560            device_serial=self.adb.GetDeviceSerial()) | 
|  | 561 | 
|  | 562 | 
| 474   @decorators.WithTimeoutAndRetriesFromInstance() | 563   @decorators.WithTimeoutAndRetriesFromInstance() | 
| 475   def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None, | 564   def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None, | 
| 476                       as_root=False, single_line=False, large_output=False, | 565                       as_root=False, single_line=False, large_output=False, | 
| 477                       timeout=None, retries=None): | 566                       timeout=None, retries=None): | 
| 478     """Run an ADB shell command. | 567     """Run an ADB shell command. | 
| 479 | 568 | 
| 480     The command to run |cmd| should be a sequence of program arguments or else | 569     The command to run |cmd| should be a sequence of program arguments or else | 
| 481     a single string. | 570     a single string. | 
| 482 | 571 | 
| 483     When |cmd| is a sequence, it is assumed to contain the name of the command | 572     When |cmd| is a sequence, it is assumed to contain the name of the command | 
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 550         with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script: | 639         with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script: | 
| 551           self._WriteFileWithPush(script.name, cmd) | 640           self._WriteFileWithPush(script.name, cmd) | 
| 552           logging.info('Large shell command will be run from file: %s ...', | 641           logging.info('Large shell command will be run from file: %s ...', | 
| 553                        cmd[:100]) | 642                        cmd[:100]) | 
| 554           return handle_check_return('sh %s' % script.name_quoted) | 643           return handle_check_return('sh %s' % script.name_quoted) | 
| 555 | 644 | 
| 556     def handle_large_output(cmd, large_output_mode): | 645     def handle_large_output(cmd, large_output_mode): | 
| 557       if large_output_mode: | 646       if large_output_mode: | 
| 558         with device_temp_file.DeviceTempFile(self.adb) as large_output_file: | 647         with device_temp_file.DeviceTempFile(self.adb) as large_output_file: | 
| 559           cmd = '%s > %s' % (cmd, large_output_file.name) | 648           cmd = '%s > %s' % (cmd, large_output_file.name) | 
| 560           logging.info('Large output mode enabled. Will write output to device ' | 649           logging.debug('Large output mode enabled. Will write output to ' | 
| 561                        'and read results from file.') | 650                         'device and read results from file.') | 
| 562           handle_large_command(cmd) | 651           handle_large_command(cmd) | 
| 563           return self.ReadFile(large_output_file.name, force_pull=True) | 652           return self.ReadFile(large_output_file.name, force_pull=True) | 
| 564       else: | 653       else: | 
| 565         try: | 654         try: | 
| 566           return handle_large_command(cmd) | 655           return handle_large_command(cmd) | 
| 567         except device_errors.AdbCommandFailedError as exc: | 656         except device_errors.AdbCommandFailedError as exc: | 
| 568           if exc.status is None: | 657           if exc.status is None: | 
| 569             logging.exception('No output found for %s', cmd) | 658             logging.exception('No output found for %s', cmd) | 
| 570             logging.warning('Attempting to run in large_output mode.') | 659             logging.warning('Attempting to run in large_output mode.') | 
| 571             logging.warning('Use RunShellCommand(..., large_output=True) for ' | 660             logging.warning('Use RunShellCommand(..., large_output=True) for ' | 
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 707       extras = {} | 796       extras = {} | 
| 708 | 797 | 
| 709     cmd = ['am', 'instrument'] | 798     cmd = ['am', 'instrument'] | 
| 710     if finish: | 799     if finish: | 
| 711       cmd.append('-w') | 800       cmd.append('-w') | 
| 712     if raw: | 801     if raw: | 
| 713       cmd.append('-r') | 802       cmd.append('-r') | 
| 714     for k, v in extras.iteritems(): | 803     for k, v in extras.iteritems(): | 
| 715       cmd.extend(['-e', str(k), str(v)]) | 804       cmd.extend(['-e', str(k), str(v)]) | 
| 716     cmd.append(component) | 805     cmd.append(component) | 
| 717     return self.RunShellCommand(cmd, check_return=True) | 806     return self.RunShellCommand(cmd, check_return=True, large_output=True) | 
| 718 | 807 | 
| 719   @decorators.WithTimeoutAndRetriesFromInstance() | 808   @decorators.WithTimeoutAndRetriesFromInstance() | 
| 720   def BroadcastIntent(self, intent_obj, timeout=None, retries=None): | 809   def BroadcastIntent(self, intent_obj, timeout=None, retries=None): | 
| 721     """Send a broadcast intent. | 810     """Send a broadcast intent. | 
| 722 | 811 | 
| 723     Args: | 812     Args: | 
| 724       intent: An Intent to broadcast. | 813       intent: An Intent to broadcast. | 
| 725       timeout: timeout in seconds | 814       timeout: timeout in seconds | 
| 726       retries: number of retries | 815       retries: number of retries | 
| 727 | 816 | 
| 728     Raises: | 817     Raises: | 
| 729       CommandTimeoutError on timeout. | 818       CommandTimeoutError on timeout. | 
| 730       DeviceUnreachableError on missing device. | 819       DeviceUnreachableError on missing device. | 
| 731     """ | 820     """ | 
| 732     cmd = ['am', 'broadcast'] + intent_obj.am_args | 821     cmd = ['am', 'broadcast'] + intent_obj.am_args | 
| 733     self.RunShellCommand(cmd, check_return=True) | 822     self.RunShellCommand(cmd, check_return=True) | 
| 734 | 823 | 
| 735   @decorators.WithTimeoutAndRetriesFromInstance() | 824   @decorators.WithTimeoutAndRetriesFromInstance() | 
| 736   def GoHome(self, timeout=None, retries=None): | 825   def GoHome(self, timeout=None, retries=None): | 
| 737     """Return to the home screen. | 826     """Return to the home screen and obtain launcher focus. | 
|  | 827 | 
|  | 828     This command launches the home screen and attempts to obtain | 
|  | 829     launcher focus until the timeout is reached. | 
| 738 | 830 | 
| 739     Args: | 831     Args: | 
| 740       timeout: timeout in seconds | 832       timeout: timeout in seconds | 
| 741       retries: number of retries | 833       retries: number of retries | 
| 742 | 834 | 
| 743     Raises: | 835     Raises: | 
| 744       CommandTimeoutError on timeout. | 836       CommandTimeoutError on timeout. | 
| 745       DeviceUnreachableError on missing device. | 837       DeviceUnreachableError on missing device. | 
| 746     """ | 838     """ | 
|  | 839     def is_launcher_focused(): | 
|  | 840       output = self.RunShellCommand(['dumpsys', 'window', 'windows'], | 
|  | 841                                     check_return=True, large_output=True) | 
|  | 842       return any(self._LAUNCHER_FOCUSED_RE.match(l) for l in output) | 
|  | 843 | 
|  | 844     def dismiss_popups(): | 
|  | 845       # There is a dialog present; attempt to get rid of it. | 
|  | 846       # Not all dialogs can be dismissed with back. | 
|  | 847       self.SendKeyEvent(keyevent.KEYCODE_ENTER) | 
|  | 848       self.SendKeyEvent(keyevent.KEYCODE_BACK) | 
|  | 849       return is_launcher_focused() | 
|  | 850 | 
|  | 851     # If Home is already focused, return early to avoid unnecessary work. | 
|  | 852     if is_launcher_focused(): | 
|  | 853       return | 
|  | 854 | 
| 747     self.StartActivity( | 855     self.StartActivity( | 
| 748         intent.Intent(action='android.intent.action.MAIN', | 856         intent.Intent(action='android.intent.action.MAIN', | 
| 749                       category='android.intent.category.HOME'), | 857                       category='android.intent.category.HOME'), | 
| 750         blocking=True) | 858         blocking=True) | 
| 751 | 859 | 
|  | 860     if not is_launcher_focused(): | 
|  | 861       timeout_retry.WaitFor(dismiss_popups, wait_period=1) | 
|  | 862 | 
| 752   @decorators.WithTimeoutAndRetriesFromInstance() | 863   @decorators.WithTimeoutAndRetriesFromInstance() | 
| 753   def ForceStop(self, package, timeout=None, retries=None): | 864   def ForceStop(self, package, timeout=None, retries=None): | 
| 754     """Close the application. | 865     """Close the application. | 
| 755 | 866 | 
| 756     Args: | 867     Args: | 
| 757       package: A string containing the name of the package to stop. | 868       package: A string containing the name of the package to stop. | 
| 758       timeout: timeout in seconds | 869       timeout: timeout in seconds | 
| 759       retries: number of retries | 870       retries: number of retries | 
| 760 | 871 | 
| 761     Raises: | 872     Raises: | 
| (...skipping 13 matching lines...) Expand all  Loading... | 
| 775 | 886 | 
| 776     Raises: | 887     Raises: | 
| 777       CommandTimeoutError on timeout. | 888       CommandTimeoutError on timeout. | 
| 778       DeviceUnreachableError on missing device. | 889       DeviceUnreachableError on missing device. | 
| 779     """ | 890     """ | 
| 780     # Check that the package exists before clearing it for android builds below | 891     # Check that the package exists before clearing it for android builds below | 
| 781     # JB MR2. Necessary because calling pm clear on a package that doesn't exist | 892     # JB MR2. Necessary because calling pm clear on a package that doesn't exist | 
| 782     # may never return. | 893     # may never return. | 
| 783     if ((self.build_version_sdk >= | 894     if ((self.build_version_sdk >= | 
| 784          constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN_MR2) | 895          constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN_MR2) | 
| 785         or self.GetApplicationPath(package)): | 896         or self.GetApplicationPaths(package)): | 
| 786       self.RunShellCommand(['pm', 'clear', package], check_return=True) | 897       self.RunShellCommand(['pm', 'clear', package], check_return=True) | 
| 787 | 898 | 
| 788   @decorators.WithTimeoutAndRetriesFromInstance() | 899   @decorators.WithTimeoutAndRetriesFromInstance() | 
| 789   def SendKeyEvent(self, keycode, timeout=None, retries=None): | 900   def SendKeyEvent(self, keycode, timeout=None, retries=None): | 
| 790     """Sends a keycode to the device. | 901     """Sends a keycode to the device. | 
| 791 | 902 | 
| 792     See the pylib.constants.keyevent module for suitable keycode values. | 903     See the pylib.constants.keyevent module for suitable keycode values. | 
| 793 | 904 | 
| 794     Args: | 905     Args: | 
| 795       keycode: A integer keycode to send to the device. | 906       keycode: A integer keycode to send to the device. | 
| 796       timeout: timeout in seconds | 907       timeout: timeout in seconds | 
| 797       retries: number of retries | 908       retries: number of retries | 
| 798 | 909 | 
| 799     Raises: | 910     Raises: | 
| 800       CommandTimeoutError on timeout. | 911       CommandTimeoutError on timeout. | 
| 801       DeviceUnreachableError on missing device. | 912       DeviceUnreachableError on missing device. | 
| 802     """ | 913     """ | 
| 803     self.RunShellCommand(['input', 'keyevent', format(keycode, 'd')], | 914     self.RunShellCommand(['input', 'keyevent', format(keycode, 'd')], | 
| 804                          check_return=True) | 915                          check_return=True) | 
| 805 | 916 | 
| 806   PUSH_CHANGED_FILES_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT | 917   PUSH_CHANGED_FILES_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT | 
| 807   PUSH_CHANGED_FILES_DEFAULT_RETRIES = _DEFAULT_RETRIES | 918   PUSH_CHANGED_FILES_DEFAULT_RETRIES = _DEFAULT_RETRIES | 
| 808 | 919 | 
| 809   @decorators.WithTimeoutAndRetriesDefaults( | 920   @decorators.WithTimeoutAndRetriesDefaults( | 
| 810       PUSH_CHANGED_FILES_DEFAULT_TIMEOUT, | 921       PUSH_CHANGED_FILES_DEFAULT_TIMEOUT, | 
| 811       PUSH_CHANGED_FILES_DEFAULT_RETRIES) | 922       PUSH_CHANGED_FILES_DEFAULT_RETRIES) | 
| 812   def PushChangedFiles(self, host_device_tuples, timeout=None, | 923   def PushChangedFiles(self, host_device_tuples, timeout=None, | 
| 813                        retries=None): | 924                        retries=None, delete_device_stale=False): | 
| 814     """Push files to the device, skipping files that don't need updating. | 925     """Push files to the device, skipping files that don't need updating. | 
| 815 | 926 | 
|  | 927     When a directory is pushed, it is traversed recursively on the host and | 
|  | 928     all files in it are pushed to the device as needed. | 
|  | 929     Additionally, if delete_device_stale option is True, | 
|  | 930     files that exist on the device but don't exist on the host are deleted. | 
|  | 931 | 
| 816     Args: | 932     Args: | 
| 817       host_device_tuples: A list of (host_path, device_path) tuples, where | 933       host_device_tuples: A list of (host_path, device_path) tuples, where | 
| 818         |host_path| is an absolute path of a file or directory on the host | 934         |host_path| is an absolute path of a file or directory on the host | 
| 819         that should be minimially pushed to the device, and |device_path| is | 935         that should be minimially pushed to the device, and |device_path| is | 
| 820         an absolute path of the destination on the device. | 936         an absolute path of the destination on the device. | 
| 821       timeout: timeout in seconds | 937       timeout: timeout in seconds | 
| 822       retries: number of retries | 938       retries: number of retries | 
|  | 939       delete_device_stale: option to delete stale files on device | 
| 823 | 940 | 
| 824     Raises: | 941     Raises: | 
| 825       CommandFailedError on failure. | 942       CommandFailedError on failure. | 
| 826       CommandTimeoutError on timeout. | 943       CommandTimeoutError on timeout. | 
| 827       DeviceUnreachableError on missing device. | 944       DeviceUnreachableError on missing device. | 
| 828     """ | 945     """ | 
| 829 | 946 | 
| 830     files = [] | 947     all_changed_files = [] | 
|  | 948     all_stale_files = [] | 
| 831     for h, d in host_device_tuples: | 949     for h, d in host_device_tuples: | 
| 832       if os.path.isdir(h): | 950       if os.path.isdir(h): | 
| 833         self.RunShellCommand(['mkdir', '-p', d], check_return=True) | 951         self.RunShellCommand(['mkdir', '-p', d], check_return=True) | 
| 834       files += self._GetChangedFilesImpl(h, d) | 952       (changed_files, stale_files) = self._GetChangedAndStaleFiles(h, d) | 
|  | 953       all_changed_files += changed_files | 
|  | 954       all_stale_files += stale_files | 
| 835 | 955 | 
| 836     if not files: | 956     if delete_device_stale: | 
|  | 957       self.RunShellCommand(['rm', '-f'] + all_stale_files, | 
|  | 958                              check_return=True) | 
|  | 959 | 
|  | 960     if not all_changed_files: | 
| 837       return | 961       return | 
| 838 | 962 | 
|  | 963     self._PushFilesImpl(host_device_tuples, all_changed_files) | 
|  | 964 | 
|  | 965   def _GetChangedAndStaleFiles(self, host_path, device_path): | 
|  | 966     """Get files to push and delete | 
|  | 967 | 
|  | 968     Args: | 
|  | 969       host_path: an absolute path of a file or directory on the host | 
|  | 970       device_path: an absolute path of a file or directory on the device | 
|  | 971 | 
|  | 972     Returns: | 
|  | 973       a two-element tuple | 
|  | 974       1st element: a list of (host_files_path, device_files_path) tuples to push | 
|  | 975       2nd element: a list of stale files under device_path | 
|  | 976     """ | 
|  | 977     real_host_path = os.path.realpath(host_path) | 
|  | 978     try: | 
|  | 979       real_device_path = self.RunShellCommand( | 
|  | 980           ['realpath', device_path], single_line=True, check_return=True) | 
|  | 981     except device_errors.CommandFailedError: | 
|  | 982       real_device_path = None | 
|  | 983     if not real_device_path: | 
|  | 984       return ([(host_path, device_path)], []) | 
|  | 985 | 
|  | 986     try: | 
|  | 987       host_checksums = md5sum.CalculateHostMd5Sums([real_host_path]) | 
|  | 988       device_checksums = md5sum.CalculateDeviceMd5Sums( | 
|  | 989         [real_device_path], self) | 
|  | 990     except EnvironmentError as e: | 
|  | 991       logging.warning('Error calculating md5: %s', e) | 
|  | 992       return ([(host_path, device_path)], []) | 
|  | 993 | 
|  | 994     if os.path.isfile(host_path): | 
|  | 995       host_checksum = host_checksums.get(real_host_path) | 
|  | 996       device_checksum = device_checksums.get(real_device_path) | 
|  | 997       if host_checksum != device_checksum: | 
|  | 998         return ([(host_path, device_path)], []) | 
|  | 999       else: | 
|  | 1000         return ([], []) | 
|  | 1001     else: | 
|  | 1002       to_push = [] | 
|  | 1003       for host_abs_path, host_checksum in host_checksums.iteritems(): | 
|  | 1004         device_abs_path = '%s/%s' % ( | 
|  | 1005             real_device_path, os.path.relpath(host_abs_path, real_host_path)) | 
|  | 1006         device_checksum = device_checksums.pop(device_abs_path, None) | 
|  | 1007         if device_checksum != host_checksum: | 
|  | 1008           to_push.append((host_abs_path, device_abs_path)) | 
|  | 1009       to_delete = device_checksums.keys() | 
|  | 1010       return (to_push, to_delete) | 
|  | 1011 | 
|  | 1012   def _PushFilesImpl(self, host_device_tuples, files): | 
| 839     size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files) | 1013     size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files) | 
| 840     file_count = len(files) | 1014     file_count = len(files) | 
| 841     dir_size = sum(host_utils.GetRecursiveDiskUsage(h) | 1015     dir_size = sum(host_utils.GetRecursiveDiskUsage(h) | 
| 842                    for h, _ in host_device_tuples) | 1016                    for h, _ in host_device_tuples) | 
| 843     dir_file_count = 0 | 1017     dir_file_count = 0 | 
| 844     for h, _ in host_device_tuples: | 1018     for h, _ in host_device_tuples: | 
| 845       if os.path.isdir(h): | 1019       if os.path.isdir(h): | 
| 846         dir_file_count += sum(len(f) for _r, _d, f in os.walk(h)) | 1020         dir_file_count += sum(len(f) for _r, _d, f in os.walk(h)) | 
| 847       else: | 1021       else: | 
| 848         dir_file_count += 1 | 1022         dir_file_count += 1 | 
| (...skipping 10 matching lines...) Expand all  Loading... | 
| 859         dir_push_duration < zip_duration or not self._commands_installed): | 1033         dir_push_duration < zip_duration or not self._commands_installed): | 
| 860       self._PushChangedFilesIndividually(host_device_tuples) | 1034       self._PushChangedFilesIndividually(host_device_tuples) | 
| 861     elif push_duration < zip_duration or not self._commands_installed: | 1035     elif push_duration < zip_duration or not self._commands_installed: | 
| 862       self._PushChangedFilesIndividually(files) | 1036       self._PushChangedFilesIndividually(files) | 
| 863     else: | 1037     else: | 
| 864       self._PushChangedFilesZipped(files) | 1038       self._PushChangedFilesZipped(files) | 
| 865       self.RunShellCommand( | 1039       self.RunShellCommand( | 
| 866           ['chmod', '-R', '777'] + [d for _, d in host_device_tuples], | 1040           ['chmod', '-R', '777'] + [d for _, d in host_device_tuples], | 
| 867           as_root=True, check_return=True) | 1041           as_root=True, check_return=True) | 
| 868 | 1042 | 
| 869   def _GetChangedFilesImpl(self, host_path, device_path): |  | 
| 870     real_host_path = os.path.realpath(host_path) |  | 
| 871     try: |  | 
| 872       real_device_path = self.RunShellCommand( |  | 
| 873           ['realpath', device_path], single_line=True, check_return=True) |  | 
| 874     except device_errors.CommandFailedError: |  | 
| 875       real_device_path = None |  | 
| 876     if not real_device_path: |  | 
| 877       return [(host_path, device_path)] |  | 
| 878 |  | 
| 879     try: |  | 
| 880       host_checksums = md5sum.CalculateHostMd5Sums([real_host_path]) |  | 
| 881       device_paths_to_md5 = ( |  | 
| 882           real_device_path if os.path.isfile(real_host_path) |  | 
| 883           else ('%s/%s' % (real_device_path, os.path.relpath(p, real_host_path)) |  | 
| 884                 for p in host_checksums.iterkeys())) |  | 
| 885       device_checksums = md5sum.CalculateDeviceMd5Sums( |  | 
| 886           device_paths_to_md5, self) |  | 
| 887     except EnvironmentError as e: |  | 
| 888       logging.warning('Error calculating md5: %s', e) |  | 
| 889       return [(host_path, device_path)] |  | 
| 890 |  | 
| 891     if os.path.isfile(host_path): |  | 
| 892       host_checksum = host_checksums.get(real_host_path) |  | 
| 893       device_checksum = device_checksums.get(real_device_path) |  | 
| 894       if host_checksum != device_checksum: |  | 
| 895         return [(host_path, device_path)] |  | 
| 896       else: |  | 
| 897         return [] |  | 
| 898     else: |  | 
| 899       to_push = [] |  | 
| 900       for host_abs_path, host_checksum in host_checksums.iteritems(): |  | 
| 901         device_abs_path = '%s/%s' % ( |  | 
| 902             real_device_path, os.path.relpath(host_abs_path, real_host_path)) |  | 
| 903         if (device_checksums.get(device_abs_path) != host_checksum): |  | 
| 904           to_push.append((host_abs_path, device_abs_path)) |  | 
| 905       return to_push |  | 
| 906 |  | 
| 907   def _InstallCommands(self): | 1043   def _InstallCommands(self): | 
| 908     if self._commands_installed is None: | 1044     if self._commands_installed is None: | 
| 909       try: | 1045       try: | 
| 910         if not install_commands.Installed(self): | 1046         if not install_commands.Installed(self): | 
| 911           install_commands.InstallCommands(self) | 1047           install_commands.InstallCommands(self) | 
| 912         self._commands_installed = True | 1048         self._commands_installed = True | 
| 913       except Exception as e: | 1049       except Exception as e: | 
| 914         logging.warning('unzip not available: %s' % str(e)) | 1050         logging.warning('unzip not available: %s' % str(e)) | 
| 915         self._commands_installed = False | 1051         self._commands_installed = False | 
| 916 | 1052 | 
| (...skipping 320 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 1237 | 1373 | 
| 1238     # Next, check the current runtime value is what we need, and | 1374     # Next, check the current runtime value is what we need, and | 
| 1239     # if not, set it and report that a reboot is required. | 1375     # if not, set it and report that a reboot is required. | 
| 1240     value = self.GetProp(self.JAVA_ASSERT_PROPERTY) | 1376     value = self.GetProp(self.JAVA_ASSERT_PROPERTY) | 
| 1241     if new_value != value: | 1377     if new_value != value: | 
| 1242       self.SetProp(self.JAVA_ASSERT_PROPERTY, new_value) | 1378       self.SetProp(self.JAVA_ASSERT_PROPERTY, new_value) | 
| 1243       return True | 1379       return True | 
| 1244     else: | 1380     else: | 
| 1245       return False | 1381       return False | 
| 1246 | 1382 | 
|  | 1383   @property | 
|  | 1384   def language(self): | 
|  | 1385     """Returns the language setting on the device.""" | 
|  | 1386     return self.GetProp('persist.sys.language', cache=False) | 
|  | 1387 | 
|  | 1388   @property | 
|  | 1389   def country(self): | 
|  | 1390     """Returns the country setting on the device.""" | 
|  | 1391     return self.GetProp('persist.sys.country', cache=False) | 
|  | 1392 | 
|  | 1393   @property | 
|  | 1394   def screen_density(self): | 
|  | 1395     """Returns the screen density of the device.""" | 
|  | 1396     DPI_TO_DENSITY = { | 
|  | 1397       120: 'ldpi', | 
|  | 1398       160: 'mdpi', | 
|  | 1399       240: 'hdpi', | 
|  | 1400       320: 'xhdpi', | 
|  | 1401       480: 'xxhdpi', | 
|  | 1402       640: 'xxxhdpi', | 
|  | 1403     } | 
|  | 1404     dpi = int(self.GetProp('ro.sf.lcd_density', cache=True)) | 
|  | 1405     return DPI_TO_DENSITY.get(dpi, 'tvdpi') | 
| 1247 | 1406 | 
| 1248   @property | 1407   @property | 
| 1249   def build_description(self): | 1408   def build_description(self): | 
| 1250     """Returns the build description of the system. | 1409     """Returns the build description of the system. | 
| 1251 | 1410 | 
| 1252     For example: | 1411     For example: | 
| 1253       nakasi-user 4.4.4 KTU84P 1227136 release-keys | 1412       nakasi-user 4.4.4 KTU84P 1227136 release-keys | 
| 1254     """ | 1413     """ | 
| 1255     return self.GetProp('ro.build.description', cache=True) | 1414     return self.GetProp('ro.build.description', cache=True) | 
| 1256 | 1415 | 
| (...skipping 321 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 1578   def HealthyDevices(cls): | 1737   def HealthyDevices(cls): | 
| 1579     blacklist = device_blacklist.ReadBlacklist() | 1738     blacklist = device_blacklist.ReadBlacklist() | 
| 1580     def blacklisted(adb): | 1739     def blacklisted(adb): | 
| 1581       if adb.GetDeviceSerial() in blacklist: | 1740       if adb.GetDeviceSerial() in blacklist: | 
| 1582         logging.warning('Device %s is blacklisted.', adb.GetDeviceSerial()) | 1741         logging.warning('Device %s is blacklisted.', adb.GetDeviceSerial()) | 
| 1583         return True | 1742         return True | 
| 1584       return False | 1743       return False | 
| 1585 | 1744 | 
| 1586     return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices() | 1745     return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices() | 
| 1587             if not blacklisted(adb)] | 1746             if not blacklisted(adb)] | 
| 1588 |  | 
| OLD | NEW | 
|---|