Home | History | Annotate | Download | only in pylib
      1 # Copyright (c) 2012 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 an interface to communicate with the device via the adb command.
      6 
      7 Assumes adb binary is currently on system path.
      8 """
      9 # pylint: disable-all
     10 
     11 import collections
     12 import datetime
     13 import inspect
     14 import logging
     15 import os
     16 import random
     17 import re
     18 import shlex
     19 import signal
     20 import subprocess
     21 import sys
     22 import tempfile
     23 import time
     24 
     25 import cmd_helper
     26 import constants
     27 import system_properties
     28 from utils import host_utils
     29 
     30 try:
     31   from pylib import pexpect
     32 except ImportError:
     33   pexpect = None
     34 
     35 sys.path.append(os.path.join(
     36     constants.DIR_SOURCE_ROOT, 'third_party', 'android_testrunner'))
     37 import adb_interface
     38 import am_instrument_parser
     39 import errors
     40 
     41 from pylib.device import device_blacklist
     42 from pylib.device import device_errors
     43 
     44 # Pattern to search for the next whole line of pexpect output and capture it
     45 # into a match group. We can't use ^ and $ for line start end with pexpect,
     46 # see http://www.noah.org/python/pexpect/#doc for explanation why.
     47 PEXPECT_LINE_RE = re.compile('\n([^\r]*)\r')
     48 
     49 # Set the adb shell prompt to be a unique marker that will [hopefully] not
     50 # appear at the start of any line of a command's output.
     51 SHELL_PROMPT = '~+~PQ\x17RS~+~'
     52 
     53 # Java properties file
     54 LOCAL_PROPERTIES_PATH = constants.DEVICE_LOCAL_PROPERTIES_PATH
     55 
     56 # Property in /data/local.prop that controls Java assertions.
     57 JAVA_ASSERT_PROPERTY = 'dalvik.vm.enableassertions'
     58 
     59 # Keycode "enum" suitable for passing to AndroidCommands.SendKey().
     60 KEYCODE_HOME = 3
     61 KEYCODE_BACK = 4
     62 KEYCODE_DPAD_UP = 19
     63 KEYCODE_DPAD_DOWN = 20
     64 KEYCODE_DPAD_RIGHT = 22
     65 KEYCODE_ENTER = 66
     66 KEYCODE_MENU = 82
     67 
     68 MD5SUM_DEVICE_FOLDER = constants.TEST_EXECUTABLE_DIR + '/md5sum/'
     69 MD5SUM_DEVICE_PATH = MD5SUM_DEVICE_FOLDER + 'md5sum_bin'
     70 
     71 PIE_WRAPPER_PATH = constants.TEST_EXECUTABLE_DIR + '/run_pie'
     72 
     73 CONTROL_USB_CHARGING_COMMANDS = [
     74   {
     75     # Nexus 4
     76     'witness_file': '/sys/module/pm8921_charger/parameters/disabled',
     77     'enable_command': 'echo 0 > /sys/module/pm8921_charger/parameters/disabled',
     78     'disable_command':
     79         'echo 1 > /sys/module/pm8921_charger/parameters/disabled',
     80   },
     81 ]
     82 
     83 class DeviceTempFile(object):
     84   def __init__(self, android_commands, prefix='temp_file', suffix=''):
     85     """Find an unused temporary file path in the devices external directory.
     86 
     87     When this object is closed, the file will be deleted on the device.
     88     """
     89     self.android_commands = android_commands
     90     while True:
     91       # TODO(cjhopman): This could actually return the same file in multiple
     92       # calls if the caller doesn't write to the files immediately. This is
     93       # expected to never happen.
     94       i = random.randint(0, 1000000)
     95       self.name = '%s/%s-%d-%010d%s' % (
     96           android_commands.GetExternalStorage(),
     97           prefix, int(time.time()), i, suffix)
     98       if not android_commands.FileExistsOnDevice(self.name):
     99         break
    100 
    101   def __enter__(self):
    102     return self
    103 
    104   def __exit__(self, type, value, traceback):
    105     self.close()
    106 
    107   def close(self):
    108     self.android_commands.RunShellCommand('rm ' + self.name)
    109 
    110 
    111 def GetAVDs():
    112   """Returns a list of AVDs."""
    113   re_avd = re.compile('^[ ]+Name: ([a-zA-Z0-9_:.-]+)', re.MULTILINE)
    114   avds = re_avd.findall(cmd_helper.GetCmdOutput(['android', 'list', 'avd']))
    115   return avds
    116 
    117 def ResetBadDevices():
    118   """Removes the blacklist that keeps track of bad devices for a current
    119      build.
    120   """
    121   device_blacklist.ResetBlacklist()
    122 
    123 def ExtendBadDevices(devices):
    124   """Adds devices to the blacklist that keeps track of bad devices for a
    125      current build.
    126 
    127   The devices listed in the bad devices file will not be returned by
    128   GetAttachedDevices.
    129 
    130   Args:
    131     devices: list of bad devices to be added to the bad devices file.
    132   """
    133   device_blacklist.ExtendBlacklist(devices)
    134 
    135 
    136 def GetAttachedDevices(hardware=True, emulator=True, offline=False):
    137   """Returns a list of attached, android devices and emulators.
    138 
    139   If a preferred device has been set with ANDROID_SERIAL, it will be first in
    140   the returned list. The arguments specify what devices to include in the list.
    141 
    142   Example output:
    143 
    144     * daemon not running. starting it now on port 5037 *
    145     * daemon started successfully *
    146     List of devices attached
    147     027c10494100b4d7        device
    148     emulator-5554   offline
    149 
    150   Args:
    151     hardware: Include attached actual devices that are online.
    152     emulator: Include emulators (i.e. AVD's) currently on host.
    153     offline: Include devices and emulators that are offline.
    154 
    155   Returns: List of devices.
    156   """
    157   adb_devices_output = cmd_helper.GetCmdOutput([constants.GetAdbPath(),
    158                                                 'devices'])
    159 
    160   re_device = re.compile('^([a-zA-Z0-9_:.-]+)\tdevice$', re.MULTILINE)
    161   online_devices = re_device.findall(adb_devices_output)
    162 
    163   re_device = re.compile('^(emulator-[0-9]+)\tdevice', re.MULTILINE)
    164   emulator_devices = re_device.findall(adb_devices_output)
    165 
    166   re_device = re.compile('^([a-zA-Z0-9_:.-]+)\toffline$', re.MULTILINE)
    167   offline_devices = re_device.findall(adb_devices_output)
    168 
    169   devices = []
    170   # First determine list of online devices (e.g. hardware and/or emulator).
    171   if hardware and emulator:
    172     devices = online_devices
    173   elif hardware:
    174     devices = [device for device in online_devices
    175                if device not in emulator_devices]
    176   elif emulator:
    177     devices = emulator_devices
    178 
    179   # Now add offline devices if offline is true
    180   if offline:
    181     devices = devices + offline_devices
    182 
    183   # Remove any devices in the blacklist.
    184   blacklist = device_blacklist.ReadBlacklist()
    185   if len(blacklist):
    186     logging.info('Avoiding bad devices %s', ' '.join(blacklist))
    187     devices = [device for device in devices if device not in blacklist]
    188 
    189   preferred_device = os.environ.get('ANDROID_SERIAL')
    190   if preferred_device in devices:
    191     devices.remove(preferred_device)
    192     devices.insert(0, preferred_device)
    193   return devices
    194 
    195 
    196 def IsDeviceAttached(device):
    197   """Return true if the device is attached and online."""
    198   return device in GetAttachedDevices()
    199 
    200 
    201 def _GetFilesFromRecursiveLsOutput(path, ls_output, re_file, utc_offset=None):
    202   """Gets a list of files from `ls` command output.
    203 
    204   Python's os.walk isn't used because it doesn't work over adb shell.
    205 
    206   Args:
    207     path: The path to list.
    208     ls_output: A list of lines returned by an `ls -lR` command.
    209     re_file: A compiled regular expression which parses a line into named groups
    210         consisting of at minimum "filename", "date", "time", "size" and
    211         optionally "timezone".
    212     utc_offset: A 5-character string of the form +HHMM or -HHMM, where HH is a
    213         2-digit string giving the number of UTC offset hours, and MM is a
    214         2-digit string giving the number of UTC offset minutes. If the input
    215         utc_offset is None, will try to look for the value of "timezone" if it
    216         is specified in re_file.
    217 
    218   Returns:
    219     A dict of {"name": (size, lastmod), ...} where:
    220       name: The file name relative to |path|'s directory.
    221       size: The file size in bytes (0 for directories).
    222       lastmod: The file last modification date in UTC.
    223   """
    224   re_directory = re.compile('^%s/(?P<dir>[^:]+):$' % re.escape(path))
    225   path_dir = os.path.dirname(path)
    226 
    227   current_dir = ''
    228   files = {}
    229   for line in ls_output:
    230     directory_match = re_directory.match(line)
    231     if directory_match:
    232       current_dir = directory_match.group('dir')
    233       continue
    234     file_match = re_file.match(line)
    235     if file_match:
    236       filename = os.path.join(current_dir, file_match.group('filename'))
    237       if filename.startswith(path_dir):
    238         filename = filename[len(path_dir) + 1:]
    239       lastmod = datetime.datetime.strptime(
    240           file_match.group('date') + ' ' + file_match.group('time')[:5],
    241           '%Y-%m-%d %H:%M')
    242       if not utc_offset and 'timezone' in re_file.groupindex:
    243         utc_offset = file_match.group('timezone')
    244       if isinstance(utc_offset, str) and len(utc_offset) == 5:
    245         utc_delta = datetime.timedelta(hours=int(utc_offset[1:3]),
    246                                        minutes=int(utc_offset[3:5]))
    247         if utc_offset[0:1] == '-':
    248           utc_delta = -utc_delta
    249         lastmod -= utc_delta
    250       files[filename] = (int(file_match.group('size')), lastmod)
    251   return files
    252 
    253 
    254 def _ParseMd5SumOutput(md5sum_output):
    255   """Returns a list of tuples from the provided md5sum output.
    256 
    257   Args:
    258     md5sum_output: output directly from md5sum binary.
    259 
    260   Returns:
    261     List of namedtuples with attributes |hash| and |path|, where |path| is the
    262     absolute path to the file with an Md5Sum of |hash|.
    263   """
    264   HashAndPath = collections.namedtuple('HashAndPath', ['hash', 'path'])
    265   split_lines = [line.split('  ') for line in md5sum_output]
    266   return [HashAndPath._make(s) for s in split_lines if len(s) == 2]
    267 
    268 
    269 def _HasAdbPushSucceeded(command_output):
    270   """Returns whether adb push has succeeded from the provided output."""
    271   # TODO(frankf): We should look at the return code instead of the command
    272   # output for many of the commands in this file.
    273   if not command_output:
    274     return True
    275   # Success looks like this: "3035 KB/s (12512056 bytes in 4.025s)"
    276   # Errors look like this: "failed to copy  ... "
    277   if not re.search('^[0-9]', command_output.splitlines()[-1]):
    278     logging.critical('PUSH FAILED: ' + command_output)
    279     return False
    280   return True
    281 
    282 
    283 def GetLogTimestamp(log_line, year):
    284   """Returns the timestamp of the given |log_line| in the given year."""
    285   try:
    286     return datetime.datetime.strptime('%s-%s' % (year, log_line[:18]),
    287                                       '%Y-%m-%d %H:%M:%S.%f')
    288   except (ValueError, IndexError):
    289     logging.critical('Error reading timestamp from ' + log_line)
    290     return None
    291 
    292 
    293 class AndroidCommands(object):
    294   """Helper class for communicating with Android device via adb."""
    295 
    296   def __init__(self, device=None):
    297     """Constructor.
    298 
    299     Args:
    300       device: If given, adb commands are only send to the device of this ID.
    301           Otherwise commands are sent to all attached devices.
    302     """
    303     adb_dir = os.path.dirname(constants.GetAdbPath())
    304     if adb_dir and adb_dir not in os.environ['PATH'].split(os.pathsep):
    305       # Required by third_party/android_testrunner to call directly 'adb'.
    306       os.environ['PATH'] += os.pathsep + adb_dir
    307     self._adb = adb_interface.AdbInterface()
    308     if device:
    309       self._adb.SetTargetSerial(device)
    310     self._device = device
    311     self._logcat = None
    312     self.logcat_process = None
    313     self._logcat_tmpoutfile = None
    314     self._pushed_files = []
    315     self._device_utc_offset = None
    316     self._potential_push_size = 0
    317     self._actual_push_size = 0
    318     self._external_storage = ''
    319     self._util_wrapper = ''
    320     self._system_properties = system_properties.SystemProperties(self.Adb())
    321     self._push_if_needed_cache = {}
    322     self._control_usb_charging_command = {
    323         'command': None,
    324         'cached': False,
    325     }
    326     self._protected_file_access_method_initialized = None
    327     self._privileged_command_runner = None
    328     self._pie_wrapper = None
    329 
    330   @property
    331   def system_properties(self):
    332     return self._system_properties
    333 
    334   def _LogShell(self, cmd):
    335     """Logs the adb shell command."""
    336     if self._device:
    337       device_repr = self._device[-4:]
    338     else:
    339       device_repr = '????'
    340     logging.info('[%s]> %s', device_repr, cmd)
    341 
    342   def Adb(self):
    343     """Returns our AdbInterface to avoid us wrapping all its methods."""
    344     # TODO(tonyg): Goal should be to git rid of this method by making this API
    345     # complete and alleviating the need.
    346     return self._adb
    347 
    348   def GetDevice(self):
    349     """Returns the device serial."""
    350     return self._device
    351 
    352   def IsOnline(self):
    353     """Checks whether the device is online.
    354 
    355     Returns:
    356       True if device is in 'device' mode, False otherwise.
    357     """
    358     # TODO(aurimas): revert to using adb get-state when android L adb is fixed.
    359     #out = self._adb.SendCommand('get-state')
    360     #return out.strip() == 'device'
    361 
    362     out = self._adb.SendCommand('devices')
    363     for line in out.split('\n'):
    364       if self._device in line and 'device' in line:
    365         return True
    366     return False
    367 
    368   def IsRootEnabled(self):
    369     """Checks if root is enabled on the device."""
    370     root_test_output = self.RunShellCommand('ls /root') or ['']
    371     return not 'Permission denied' in root_test_output[0]
    372 
    373   def EnableAdbRoot(self):
    374     """Enables adb root on the device.
    375 
    376     Returns:
    377       True: if output from executing adb root was as expected.
    378       False: otherwise.
    379     """
    380     if self.GetBuildType() == 'user':
    381       logging.warning("Can't enable root in production builds with type user")
    382       return False
    383     else:
    384       return_value = self._adb.EnableAdbRoot()
    385       # EnableAdbRoot inserts a call for wait-for-device only when adb logcat
    386       # output matches what is expected. Just to be safe add a call to
    387       # wait-for-device.
    388       self._adb.SendCommand('wait-for-device')
    389       return return_value
    390 
    391   def GetDeviceYear(self):
    392     """Returns the year information of the date on device."""
    393     return self.RunShellCommand('date +%Y')[0]
    394 
    395   def GetExternalStorage(self):
    396     if not self._external_storage:
    397       self._external_storage = self.RunShellCommand('echo $EXTERNAL_STORAGE')[0]
    398       if not self._external_storage:
    399         raise device_errors.CommandFailedError(
    400             ['shell', "'echo $EXTERNAL_STORAGE'"],
    401             'Unable to find $EXTERNAL_STORAGE')
    402     return self._external_storage
    403 
    404   def WaitForDevicePm(self, timeout=120):
    405     """Blocks until the device's package manager is available.
    406 
    407     To workaround http://b/5201039, we restart the shell and retry if the
    408     package manager isn't back after 120 seconds.
    409 
    410     Raises:
    411       errors.WaitForResponseTimedOutError after max retries reached.
    412     """
    413     last_err = None
    414     retries = 3
    415     while retries:
    416       try:
    417         self._adb.WaitForDevicePm(wait_time=timeout)
    418         return  # Success
    419       except errors.WaitForResponseTimedOutError as e:
    420         last_err = e
    421         logging.warning('Restarting and retrying after timeout: %s', e)
    422         retries -= 1
    423         self.RestartShell()
    424     raise last_err # Only reached after max retries, re-raise the last error.
    425 
    426   def RestartShell(self):
    427     """Restarts the shell on the device. Does not block for it to return."""
    428     self.RunShellCommand('stop')
    429     self.RunShellCommand('start')
    430 
    431   def Reboot(self, full_reboot=True):
    432     """Reboots the device and waits for the package manager to return.
    433 
    434     Args:
    435       full_reboot: Whether to fully reboot the device or just restart the shell.
    436     """
    437     # TODO(torne): hive can't reboot the device either way without breaking the
    438     # connection; work out if we can handle this better
    439     if os.environ.get('USING_HIVE'):
    440       logging.warning('Ignoring reboot request as we are on hive')
    441       return
    442     if full_reboot or not self.IsRootEnabled():
    443       self._adb.SendCommand('reboot')
    444       self._system_properties = system_properties.SystemProperties(self.Adb())
    445       timeout = 300
    446       retries = 1
    447       # Wait for the device to disappear.
    448       while retries < 10 and self.IsOnline():
    449         time.sleep(1)
    450         retries += 1
    451     else:
    452       self.RestartShell()
    453       timeout = 120
    454     # To run tests we need at least the package manager and the sd card (or
    455     # other external storage) to be ready.
    456     self.WaitForDevicePm(timeout)
    457     self.WaitForSdCardReady(timeout)
    458 
    459   def Shutdown(self):
    460     """Shuts down the device."""
    461     self._adb.SendCommand('reboot -p')
    462     self._system_properties = system_properties.SystemProperties(self.Adb())
    463 
    464   def Uninstall(self, package):
    465     """Uninstalls the specified package from the device.
    466 
    467     Args:
    468       package: Name of the package to remove.
    469 
    470     Returns:
    471       A status string returned by adb uninstall
    472     """
    473     uninstall_command = 'uninstall %s' % package
    474 
    475     self._LogShell(uninstall_command)
    476     return self._adb.SendCommand(uninstall_command, timeout_time=60)
    477 
    478   def Install(self, package_file_path, reinstall=False):
    479     """Installs the specified package to the device.
    480 
    481     Args:
    482       package_file_path: Path to .apk file to install.
    483       reinstall: Reinstall an existing apk, keeping the data.
    484 
    485     Returns:
    486       A status string returned by adb install
    487     """
    488     assert os.path.isfile(package_file_path), ('<%s> is not file' %
    489                                                package_file_path)
    490 
    491     install_cmd = ['install']
    492 
    493     if reinstall:
    494       install_cmd.append('-r')
    495 
    496     install_cmd.append(package_file_path)
    497     install_cmd = ' '.join(install_cmd)
    498 
    499     self._LogShell(install_cmd)
    500     return self._adb.SendCommand(install_cmd,
    501                                  timeout_time=2 * 60,
    502                                  retry_count=0)
    503 
    504   def ManagedInstall(self, apk_path, keep_data=False, package_name=None,
    505                      reboots_on_timeout=2):
    506     """Installs specified package and reboots device on timeouts.
    507 
    508     If package_name is supplied, checks if the package is already installed and
    509     doesn't reinstall if the apk md5sums match.
    510 
    511     Args:
    512       apk_path: Path to .apk file to install.
    513       keep_data: Reinstalls instead of uninstalling first, preserving the
    514         application data.
    515       package_name: Package name (only needed if keep_data=False).
    516       reboots_on_timeout: number of time to reboot if package manager is frozen.
    517     """
    518     # Check if package is already installed and up to date.
    519     if package_name:
    520       installed_apk_path = self.GetApplicationPath(package_name)
    521       if (installed_apk_path and
    522           not self.GetFilesChanged(apk_path, installed_apk_path,
    523                                    ignore_filenames=True)):
    524         logging.info('Skipped install: identical %s APK already installed' %
    525             package_name)
    526         return
    527     # Install.
    528     reboots_left = reboots_on_timeout
    529     while True:
    530       try:
    531         if not keep_data:
    532           assert package_name
    533           self.Uninstall(package_name)
    534         install_status = self.Install(apk_path, reinstall=keep_data)
    535         if 'Success' in install_status:
    536           return
    537         else:
    538           raise Exception('Install failure: %s' % install_status)
    539       except errors.WaitForResponseTimedOutError:
    540         print '@@@STEP_WARNINGS@@@'
    541         logging.info('Timeout on installing %s on device %s', apk_path,
    542                      self._device)
    543 
    544         if reboots_left <= 0:
    545           raise Exception('Install timed out')
    546 
    547         # Force a hard reboot on last attempt
    548         self.Reboot(full_reboot=(reboots_left == 1))
    549         reboots_left -= 1
    550 
    551   def MakeSystemFolderWritable(self):
    552     """Remounts the /system folder rw."""
    553     out = self._adb.SendCommand('remount')
    554     if out.strip() != 'remount succeeded':
    555       raise errors.MsgException('Remount failed: %s' % out)
    556 
    557   def RestartAdbdOnDevice(self):
    558     logging.info('Restarting adbd on the device...')
    559     with DeviceTempFile(self, suffix=".sh") as temp_script_file:
    560       host_script_path = os.path.join(constants.DIR_SOURCE_ROOT,
    561                                       'build',
    562                                       'android',
    563                                       'pylib',
    564                                       'restart_adbd.sh')
    565       self._adb.Push(host_script_path, temp_script_file.name)
    566       self.RunShellCommand('. %s' % temp_script_file.name)
    567       self._adb.SendCommand('wait-for-device')
    568 
    569   def RestartAdbServer(self):
    570     """Restart the adb server."""
    571     ret = self.KillAdbServer()
    572     if ret != 0:
    573       raise errors.MsgException('KillAdbServer: %d' % ret)
    574 
    575     ret = self.StartAdbServer()
    576     if ret != 0:
    577       raise errors.MsgException('StartAdbServer: %d' % ret)
    578 
    579   @staticmethod
    580   def KillAdbServer():
    581     """Kill adb server."""
    582     adb_cmd = [constants.GetAdbPath(), 'kill-server']
    583     ret = cmd_helper.RunCmd(adb_cmd)
    584     retry = 0
    585     while retry < 3:
    586       ret, _ = cmd_helper.GetCmdStatusAndOutput(['pgrep', 'adb'])
    587       if ret != 0:
    588         # pgrep didn't find adb, kill-server succeeded.
    589         return 0
    590       retry += 1
    591       time.sleep(retry)
    592     return ret
    593 
    594   def StartAdbServer(self):
    595     """Start adb server."""
    596     adb_cmd = ['taskset', '-c', '0', constants.GetAdbPath(), 'start-server']
    597     ret, _ = cmd_helper.GetCmdStatusAndOutput(adb_cmd)
    598     retry = 0
    599     while retry < 3:
    600       ret, _ = cmd_helper.GetCmdStatusAndOutput(['pgrep', 'adb'])
    601       if ret == 0:
    602         # pgrep found adb, start-server succeeded.
    603         # Waiting for device to reconnect before returning success.
    604         self._adb.SendCommand('wait-for-device')
    605         return 0
    606       retry += 1
    607       time.sleep(retry)
    608     return ret
    609 
    610   def WaitForSystemBootCompleted(self, wait_time):
    611     """Waits for targeted system's boot_completed flag to be set.
    612 
    613     Args:
    614       wait_time: time in seconds to wait
    615 
    616     Raises:
    617       WaitForResponseTimedOutError if wait_time elapses and flag still not
    618       set.
    619     """
    620     logging.info('Waiting for system boot completed...')
    621     self._adb.SendCommand('wait-for-device')
    622     # Now the device is there, but system not boot completed.
    623     # Query the sys.boot_completed flag with a basic command
    624     boot_completed = False
    625     attempts = 0
    626     wait_period = 5
    627     while not boot_completed and (attempts * wait_period) < wait_time:
    628       output = self.system_properties['sys.boot_completed']
    629       output = output.strip()
    630       if output == '1':
    631         boot_completed = True
    632       else:
    633         # If 'error: xxx' returned when querying the flag, it means
    634         # adb server lost the connection to the emulator, so restart the adb
    635         # server.
    636         if 'error:' in output:
    637           self.RestartAdbServer()
    638         time.sleep(wait_period)
    639         attempts += 1
    640     if not boot_completed:
    641       raise errors.WaitForResponseTimedOutError(
    642           'sys.boot_completed flag was not set after %s seconds' % wait_time)
    643 
    644   def WaitForSdCardReady(self, timeout_time):
    645     """Wait for the SD card ready before pushing data into it."""
    646     logging.info('Waiting for SD card ready...')
    647     sdcard_ready = False
    648     attempts = 0
    649     wait_period = 5
    650     external_storage = self.GetExternalStorage()
    651     while not sdcard_ready and attempts * wait_period < timeout_time:
    652       output = self.RunShellCommand('ls ' + external_storage)
    653       if output:
    654         sdcard_ready = True
    655       else:
    656         time.sleep(wait_period)
    657         attempts += 1
    658     if not sdcard_ready:
    659       raise errors.WaitForResponseTimedOutError(
    660           'SD card not ready after %s seconds' % timeout_time)
    661 
    662   def GetAndroidToolStatusAndOutput(self, command, lib_path=None, *args, **kw):
    663     """Runs a native Android binary, wrapping the command as necessary.
    664 
    665     This is a specialization of GetShellCommandStatusAndOutput, which is meant
    666     for running tools/android/ binaries and handle properly: (1) setting the
    667     lib path (for component=shared_library), (2) using the PIE wrapper on ICS.
    668     See crbug.com/373219 for more context.
    669 
    670     Args:
    671       command: String containing the command to send.
    672       lib_path: (optional) path to the folder containing the dependent libs.
    673       Same other arguments of GetCmdStatusAndOutput.
    674     """
    675     # The first time this command is run the device is inspected to check
    676     # whether a wrapper for running PIE executable is needed (only Android ICS)
    677     # or not. The results is cached, so the wrapper is pushed only once.
    678     if self._pie_wrapper is None:
    679       # None: did not check; '': did check and not needed; '/path': use /path.
    680       self._pie_wrapper = ''
    681       if self.GetBuildId().startswith('I'):  # Ixxxx = Android ICS.
    682         run_pie_dist_path = os.path.join(constants.GetOutDirectory(), 'run_pie')
    683         assert os.path.exists(run_pie_dist_path), 'Please build run_pie'
    684         # The PIE loader must be pushed manually (i.e. no PushIfNeeded) because
    685         # PushIfNeeded requires md5sum and md5sum requires the wrapper as well.
    686         command = 'push %s %s' % (run_pie_dist_path, PIE_WRAPPER_PATH)
    687         assert _HasAdbPushSucceeded(self._adb.SendCommand(command))
    688         self._pie_wrapper = PIE_WRAPPER_PATH
    689 
    690     if self._pie_wrapper:
    691       command = '%s %s' % (self._pie_wrapper, command)
    692     if lib_path:
    693       command = 'LD_LIBRARY_PATH=%s %s' % (lib_path, command)
    694     return self.GetShellCommandStatusAndOutput(command, *args, **kw)
    695 
    696   # It is tempting to turn this function into a generator, however this is not
    697   # possible without using a private (local) adb_shell instance (to ensure no
    698   # other command interleaves usage of it), which would defeat the main aim of
    699   # being able to reuse the adb shell instance across commands.
    700   def RunShellCommand(self, command, timeout_time=20, log_result=False):
    701     """Send a command to the adb shell and return the result.
    702 
    703     Args:
    704       command: String containing the shell command to send.
    705       timeout_time: Number of seconds to wait for command to respond before
    706         retrying, used by AdbInterface.SendShellCommand.
    707       log_result: Boolean to indicate whether we should log the result of the
    708                   shell command.
    709 
    710     Returns:
    711       list containing the lines of output received from running the command
    712     """
    713     self._LogShell(command)
    714     if "'" in command:
    715       command = command.replace('\'', '\'\\\'\'')
    716     result = self._adb.SendShellCommand(
    717         "'%s'" % command, timeout_time).splitlines()
    718     # TODO(b.kelemen): we should really be able to drop the stderr of the
    719     # command or raise an exception based on what the caller wants.
    720     result = [ l for l in result if not l.startswith('WARNING') ]
    721     if ['error: device not found'] == result:
    722       raise errors.DeviceUnresponsiveError('device not found')
    723     if log_result:
    724       self._LogShell('\n'.join(result))
    725     return result
    726 
    727   def GetShellCommandStatusAndOutput(self, command, timeout_time=20,
    728                                      log_result=False):
    729     """See RunShellCommand() above.
    730 
    731     Returns:
    732       The tuple (exit code, list of output lines).
    733     """
    734     lines = self.RunShellCommand(
    735         command + '; echo %$?', timeout_time, log_result)
    736     last_line = lines[-1]
    737     status_pos = last_line.rfind('%')
    738     assert status_pos >= 0
    739     status = int(last_line[status_pos + 1:])
    740     if status_pos == 0:
    741       lines = lines[:-1]
    742     else:
    743       lines = lines[:-1] + [last_line[:status_pos]]
    744     return (status, lines)
    745 
    746   def KillAll(self, process, signum=9, with_su=False):
    747     """Android version of killall, connected via adb.
    748 
    749     Args:
    750       process: name of the process to kill off.
    751       signum: signal to use, 9 (SIGKILL) by default.
    752       with_su: wether or not to use su to kill the processes.
    753 
    754     Returns:
    755       the number of processes killed
    756     """
    757     pids = self.ExtractPid(process)
    758     if pids:
    759       cmd = 'kill -%d %s' % (signum, ' '.join(pids))
    760       if with_su:
    761         self.RunShellCommandWithSU(cmd)
    762       else:
    763         self.RunShellCommand(cmd)
    764     return len(pids)
    765 
    766   def KillAllBlocking(self, process, timeout_sec, signum=9, with_su=False):
    767     """Blocking version of killall, connected via adb.
    768 
    769     This waits until no process matching the corresponding name appears in ps'
    770     output anymore.
    771 
    772     Args:
    773       process: name of the process to kill off
    774       timeout_sec: the timeout in seconds
    775       signum: same as |KillAll|
    776       with_su: same as |KillAll|
    777     Returns:
    778       the number of processes killed
    779     """
    780     processes_killed = self.KillAll(process, signum=signum, with_su=with_su)
    781     if processes_killed:
    782       elapsed = 0
    783       wait_period = 0.1
    784       # Note that this doesn't take into account the time spent in ExtractPid().
    785       while self.ExtractPid(process) and elapsed < timeout_sec:
    786         time.sleep(wait_period)
    787         elapsed += wait_period
    788       if elapsed >= timeout_sec:
    789         return processes_killed - self.ExtractPid(process)
    790     return processes_killed
    791 
    792   @staticmethod
    793   def _GetActivityCommand(package, activity, wait_for_completion, action,
    794                           category, data, extras, trace_file_name, force_stop,
    795                           flags):
    796     """Creates command to start |package|'s activity on the device.
    797 
    798     Args - as for StartActivity
    799 
    800     Returns:
    801       the command to run on the target to start the activity
    802     """
    803     cmd = 'am start -a %s' % action
    804     if force_stop:
    805       cmd += ' -S'
    806     if wait_for_completion:
    807       cmd += ' -W'
    808     if category:
    809       cmd += ' -c %s' % category
    810     if package and activity:
    811       cmd += ' -n %s/%s' % (package, activity)
    812     if data:
    813       cmd += ' -d "%s"' % data
    814     if extras:
    815       for key in extras:
    816         value = extras[key]
    817         if isinstance(value, str):
    818           cmd += ' --es'
    819         elif isinstance(value, bool):
    820           cmd += ' --ez'
    821         elif isinstance(value, int):
    822           cmd += ' --ei'
    823         else:
    824           raise NotImplementedError(
    825               'Need to teach StartActivity how to pass %s extras' % type(value))
    826         cmd += ' %s %s' % (key, value)
    827     if trace_file_name:
    828       cmd += ' --start-profiler ' + trace_file_name
    829     if flags:
    830       cmd += ' -f %s' % flags
    831     return cmd
    832 
    833   def StartActivity(self, package, activity, wait_for_completion=False,
    834                     action='android.intent.action.VIEW',
    835                     category=None, data=None,
    836                     extras=None, trace_file_name=None,
    837                     force_stop=False, flags=None):
    838     """Starts |package|'s activity on the device.
    839 
    840     Args:
    841       package: Name of package to start (e.g. 'com.google.android.apps.chrome').
    842       activity: Name of activity (e.g. '.Main' or
    843         'com.google.android.apps.chrome.Main').
    844       wait_for_completion: wait for the activity to finish launching (-W flag).
    845       action: string (e.g. "android.intent.action.MAIN"). Default is VIEW.
    846       category: string (e.g. "android.intent.category.HOME")
    847       data: Data string to pass to activity (e.g. 'http://www.example.com/').
    848       extras: Dict of extras to pass to activity. Values are significant.
    849       trace_file_name: If used, turns on and saves the trace to this file name.
    850       force_stop: force stop the target app before starting the activity (-S
    851         flag).
    852     Returns:
    853       The output of the underlying command as a list of lines.
    854     """
    855     cmd = self._GetActivityCommand(package, activity, wait_for_completion,
    856                                    action, category, data, extras,
    857                                    trace_file_name, force_stop, flags)
    858     return self.RunShellCommand(cmd)
    859 
    860   def StartActivityTimed(self, package, activity, wait_for_completion=False,
    861                          action='android.intent.action.VIEW',
    862                          category=None, data=None,
    863                          extras=None, trace_file_name=None,
    864                          force_stop=False, flags=None):
    865     """Starts |package|'s activity on the device, returning the start time
    866 
    867     Args - as for StartActivity
    868 
    869     Returns:
    870       A tuple containing:
    871         - the output of the underlying command as a list of lines, and
    872         - a timestamp string for the time at which the activity started
    873     """
    874     cmd = self._GetActivityCommand(package, activity, wait_for_completion,
    875                                    action, category, data, extras,
    876                                    trace_file_name, force_stop, flags)
    877     self.StartMonitoringLogcat()
    878     out = self.RunShellCommand('log starting activity; ' + cmd)
    879     activity_started_re = re.compile('.*starting activity.*')
    880     m = self.WaitForLogMatch(activity_started_re, None)
    881     assert m
    882     start_line = m.group(0)
    883     return (out, GetLogTimestamp(start_line, self.GetDeviceYear()))
    884 
    885   def StartCrashUploadService(self, package):
    886     # TODO(frankf): We really need a python wrapper around Intent
    887     # to be shared with StartActivity/BroadcastIntent.
    888     cmd = (
    889       'am startservice -a %s.crash.ACTION_FIND_ALL -n '
    890       '%s/%s.crash.MinidumpUploadService' %
    891       (constants.PACKAGE_INFO['chrome'].package,
    892        package,
    893        constants.PACKAGE_INFO['chrome'].package))
    894     am_output = self.RunShellCommandWithSU(cmd)
    895     assert am_output and 'Starting' in am_output[-1], (
    896         'Service failed to start: %s' % am_output)
    897     time.sleep(15)
    898 
    899   def BroadcastIntent(self, package, intent, *args):
    900     """Send a broadcast intent.
    901 
    902     Args:
    903       package: Name of package containing the intent.
    904       intent: Name of the intent.
    905       args: Optional extra arguments for the intent.
    906     """
    907     cmd = 'am broadcast -a %s.%s %s' % (package, intent, ' '.join(args))
    908     self.RunShellCommand(cmd)
    909 
    910   def GoHome(self):
    911     """Tell the device to return to the home screen. Blocks until completion."""
    912     self.RunShellCommand('am start -W '
    913         '-a android.intent.action.MAIN -c android.intent.category.HOME')
    914 
    915   def CloseApplication(self, package):
    916     """Attempt to close down the application, using increasing violence.
    917 
    918     Args:
    919       package: Name of the process to kill off, e.g.
    920       com.google.android.apps.chrome
    921     """
    922     self.RunShellCommand('am force-stop ' + package)
    923 
    924   def GetApplicationPath(self, package):
    925     """Get the installed apk path on the device for the given package.
    926 
    927     Args:
    928       package: Name of the package.
    929 
    930     Returns:
    931       Path to the apk on the device if it exists, None otherwise.
    932     """
    933     pm_path_output  = self.RunShellCommand('pm path ' + package)
    934     # The path output contains anything if and only if the package
    935     # exists.
    936     if pm_path_output:
    937       # pm_path_output is of the form: "package:/path/to/foo.apk"
    938       return pm_path_output[0].split(':')[1]
    939     else:
    940       return None
    941 
    942   def ClearApplicationState(self, package):
    943     """Closes and clears all state for the given |package|."""
    944     # Check that the package exists before clearing it. Necessary because
    945     # calling pm clear on a package that doesn't exist may never return.
    946     pm_path_output  = self.RunShellCommand('pm path ' + package)
    947     # The path output only contains anything if and only if the package exists.
    948     if pm_path_output:
    949       self.RunShellCommand('pm clear ' + package)
    950 
    951   def SendKeyEvent(self, keycode):
    952     """Sends keycode to the device.
    953 
    954     Args:
    955       keycode: Numeric keycode to send (see "enum" at top of file).
    956     """
    957     self.RunShellCommand('input keyevent %d' % keycode)
    958 
    959   def _RunMd5Sum(self, host_path, device_path):
    960     """Gets the md5sum of a host path and device path.
    961 
    962     Args:
    963       host_path: Path (file or directory) on the host.
    964       device_path: Path on the device.
    965 
    966     Returns:
    967       A tuple containing lists of the host and device md5sum results as
    968       created by _ParseMd5SumOutput().
    969     """
    970     md5sum_dist_path = os.path.join(constants.GetOutDirectory(),
    971                                     'md5sum_dist')
    972     assert os.path.exists(md5sum_dist_path), 'Please build md5sum.'
    973     md5sum_dist_mtime = os.stat(md5sum_dist_path).st_mtime
    974     if (md5sum_dist_path not in self._push_if_needed_cache or
    975         self._push_if_needed_cache[md5sum_dist_path] != md5sum_dist_mtime):
    976       command = 'push %s %s' % (md5sum_dist_path, MD5SUM_DEVICE_FOLDER)
    977       assert _HasAdbPushSucceeded(self._adb.SendCommand(command))
    978       self._push_if_needed_cache[md5sum_dist_path] = md5sum_dist_mtime
    979 
    980     (_, md5_device_output) = self.GetAndroidToolStatusAndOutput(
    981         self._util_wrapper + ' ' + MD5SUM_DEVICE_PATH + ' ' + device_path,
    982         lib_path=MD5SUM_DEVICE_FOLDER,
    983         timeout_time=2 * 60)
    984     device_hash_tuples = _ParseMd5SumOutput(md5_device_output)
    985     assert os.path.exists(host_path), 'Local path not found %s' % host_path
    986     md5sum_output = cmd_helper.GetCmdOutput(
    987         [os.path.join(constants.GetOutDirectory(), 'md5sum_bin_host'),
    988          host_path])
    989     host_hash_tuples = _ParseMd5SumOutput(md5sum_output.splitlines())
    990     return (host_hash_tuples, device_hash_tuples)
    991 
    992   def GetFilesChanged(self, host_path, device_path, ignore_filenames=False):
    993     """Compares the md5sum of a host path against a device path.
    994 
    995     Note: Ignores extra files on the device.
    996 
    997     Args:
    998       host_path: Path (file or directory) on the host.
    999       device_path: Path on the device.
   1000       ignore_filenames: If True only the file contents are considered when
   1001           checking whether a file has changed, otherwise the relative path
   1002           must also match.
   1003 
   1004     Returns:
   1005       A list of tuples of the form (host_path, device_path) for files whose
   1006       md5sums do not match.
   1007     """
   1008 
   1009     # Md5Sum resolves symbolic links in path names so the calculation of
   1010     # relative path names from its output will need the real path names of the
   1011     # base directories. Having calculated these they are used throughout the
   1012     # function since this makes us less subject to any future changes to Md5Sum.
   1013     real_host_path = os.path.realpath(host_path)
   1014     real_device_path = self.RunShellCommand('realpath "%s"' % device_path)[0]
   1015 
   1016     host_hash_tuples, device_hash_tuples = self._RunMd5Sum(
   1017         real_host_path, real_device_path)
   1018 
   1019     if len(host_hash_tuples) > len(device_hash_tuples):
   1020       logging.info('%s files do not exist on the device' %
   1021                    (len(host_hash_tuples) - len(device_hash_tuples)))
   1022 
   1023     host_rel = [(os.path.relpath(os.path.normpath(t.path), real_host_path),
   1024                  t.hash)
   1025                 for t in host_hash_tuples]
   1026 
   1027     if os.path.isdir(real_host_path):
   1028       def RelToRealPaths(rel_path):
   1029         return (os.path.join(real_host_path, rel_path),
   1030                 os.path.join(real_device_path, rel_path))
   1031     else:
   1032       assert len(host_rel) == 1
   1033       def RelToRealPaths(_):
   1034         return (real_host_path, real_device_path)
   1035 
   1036     if ignore_filenames:
   1037       # If we are ignoring file names, then we want to push any file for which
   1038       # a file with an equivalent MD5 sum does not exist on the device.
   1039       device_hashes = set([h.hash for h in device_hash_tuples])
   1040       ShouldPush = lambda p, h: h not in device_hashes
   1041     else:
   1042       # Otherwise, we want to push any file on the host for which a file with
   1043       # an equivalent MD5 sum does not exist at the same relative path on the
   1044       # device.
   1045       device_rel = dict([(os.path.relpath(os.path.normpath(t.path),
   1046                                           real_device_path),
   1047                           t.hash)
   1048                          for t in device_hash_tuples])
   1049       ShouldPush = lambda p, h: p not in device_rel or h != device_rel[p]
   1050 
   1051     return [RelToRealPaths(path) for path, host_hash in host_rel
   1052             if ShouldPush(path, host_hash)]
   1053 
   1054   def PushIfNeeded(self, host_path, device_path):
   1055     """Pushes |host_path| to |device_path|.
   1056 
   1057     Works for files and directories. This method skips copying any paths in
   1058     |test_data_paths| that already exist on the device with the same hash.
   1059 
   1060     All pushed files can be removed by calling RemovePushedFiles().
   1061     """
   1062     MAX_INDIVIDUAL_PUSHES = 50
   1063     if not os.path.exists(host_path):
   1064       raise device_errors.CommandFailedError(
   1065           'Local path not found %s' % host_path, device=str(self))
   1066 
   1067     # See if the file on the host changed since the last push (if any) and
   1068     # return early if it didn't. Note that this shortcut assumes that the tests
   1069     # on the device don't modify the files.
   1070     if not os.path.isdir(host_path):
   1071       if host_path in self._push_if_needed_cache:
   1072         host_path_mtime = self._push_if_needed_cache[host_path]
   1073         if host_path_mtime == os.stat(host_path).st_mtime:
   1074           return
   1075 
   1076     size = host_utils.GetRecursiveDiskUsage(host_path)
   1077     self._pushed_files.append(device_path)
   1078     self._potential_push_size += size
   1079 
   1080     if os.path.isdir(host_path):
   1081       self.RunShellCommand('mkdir -p "%s"' % device_path)
   1082 
   1083     changed_files = self.GetFilesChanged(host_path, device_path)
   1084     logging.info('Found %d files that need to be pushed to %s',
   1085         len(changed_files), device_path)
   1086     if not changed_files:
   1087       return
   1088 
   1089     def Push(host, device):
   1090       # NOTE: We can't use adb_interface.Push() because it hardcodes a timeout
   1091       # of 60 seconds which isn't sufficient for a lot of users of this method.
   1092       push_command = 'push %s %s' % (host, device)
   1093       self._LogShell(push_command)
   1094 
   1095       # Retry push with increasing backoff if the device is busy.
   1096       retry = 0
   1097       while True:
   1098         output = self._adb.SendCommand(push_command, timeout_time=30 * 60)
   1099         if _HasAdbPushSucceeded(output):
   1100           if not os.path.isdir(host_path):
   1101             self._push_if_needed_cache[host] = os.stat(host).st_mtime
   1102           return
   1103         if retry < 3:
   1104           retry += 1
   1105           wait_time = 5 * retry
   1106           logging.error('Push failed, retrying in %d seconds: %s' %
   1107                         (wait_time, output))
   1108           time.sleep(wait_time)
   1109         else:
   1110           raise Exception('Push failed: %s' % output)
   1111 
   1112     diff_size = 0
   1113     if len(changed_files) <= MAX_INDIVIDUAL_PUSHES:
   1114       diff_size = sum(host_utils.GetRecursiveDiskUsage(f[0])
   1115                       for f in changed_files)
   1116 
   1117     # TODO(craigdh): Replace this educated guess with a heuristic that
   1118     # approximates the push time for each method.
   1119     if len(changed_files) > MAX_INDIVIDUAL_PUSHES or diff_size > 0.5 * size:
   1120       self._actual_push_size += size
   1121       Push(host_path, device_path)
   1122     else:
   1123       for f in changed_files:
   1124         Push(f[0], f[1])
   1125       self._actual_push_size += diff_size
   1126 
   1127   def GetPushSizeInfo(self):
   1128     """Get total size of pushes to the device done via PushIfNeeded()
   1129 
   1130     Returns:
   1131       A tuple:
   1132         1. Total size of push requests to PushIfNeeded (MB)
   1133         2. Total size that was actually pushed (MB)
   1134     """
   1135     return (self._potential_push_size, self._actual_push_size)
   1136 
   1137   def GetFileContents(self, filename, log_result=False):
   1138     """Gets contents from the file specified by |filename|."""
   1139     return self.RunShellCommand('cat "%s" 2>/dev/null' % filename,
   1140                                 log_result=log_result)
   1141 
   1142   def SetFileContents(self, filename, contents):
   1143     """Writes |contents| to the file specified by |filename|."""
   1144     with tempfile.NamedTemporaryFile() as f:
   1145       f.write(contents)
   1146       f.flush()
   1147       self._adb.Push(f.name, filename)
   1148 
   1149   def RunShellCommandWithSU(self, command, timeout_time=20, log_result=False):
   1150     return self.RunShellCommand('su -c %s' % command, timeout_time, log_result)
   1151 
   1152   def CanAccessProtectedFileContents(self):
   1153     """Returns True if Get/SetProtectedFileContents would work via "su" or adb
   1154     shell running as root.
   1155 
   1156     Devices running user builds don't have adb root, but may provide "su" which
   1157     can be used for accessing protected files.
   1158     """
   1159     return (self._GetProtectedFileCommandRunner() != None)
   1160 
   1161   def _GetProtectedFileCommandRunner(self):
   1162     """Finds the best method to access protected files on the device.
   1163 
   1164     Returns:
   1165       1. None when privileged files cannot be accessed on the device.
   1166       2. Otherwise: A function taking a single parameter: a string with command
   1167          line arguments. Running that function executes the command with
   1168          the appropriate method.
   1169     """
   1170     if self._protected_file_access_method_initialized:
   1171       return self._privileged_command_runner
   1172 
   1173     self._privileged_command_runner = None
   1174     self._protected_file_access_method_initialized = True
   1175 
   1176     for cmd in [self.RunShellCommand, self.RunShellCommandWithSU]:
   1177       # Get contents of the auxv vector for the init(8) process from a small
   1178       # binary file that always exists on linux and is always read-protected.
   1179       contents = cmd('cat /proc/1/auxv')
   1180       # The leading 4 or 8-bytes of auxv vector is a_type. There are not many
   1181       # reserved a_type values, hence byte 2 must always be '\0' for a realistic
   1182       # auxv. See /usr/include/elf.h.
   1183       if len(contents) > 0 and (contents[0][2] == '\0'):
   1184         self._privileged_command_runner = cmd
   1185         break
   1186     return self._privileged_command_runner
   1187 
   1188   def GetProtectedFileContents(self, filename):
   1189     """Gets contents from the protected file specified by |filename|.
   1190 
   1191     This is potentially less efficient than GetFileContents.
   1192     """
   1193     command = 'cat "%s" 2> /dev/null' % filename
   1194     command_runner = self._GetProtectedFileCommandRunner()
   1195     if command_runner:
   1196       return command_runner(command)
   1197     else:
   1198       logging.warning('Could not access protected file: %s' % filename)
   1199       return []
   1200 
   1201   def SetProtectedFileContents(self, filename, contents):
   1202     """Writes |contents| to the protected file specified by |filename|.
   1203 
   1204     This is less efficient than SetFileContents.
   1205     """
   1206     with DeviceTempFile(self) as temp_file:
   1207       with DeviceTempFile(self, suffix=".sh") as temp_script:
   1208         # Put the contents in a temporary file
   1209         self.SetFileContents(temp_file.name, contents)
   1210         # Create a script to copy the file contents to its final destination
   1211         self.SetFileContents(temp_script.name,
   1212                              'cat %s > %s' % (temp_file.name, filename))
   1213 
   1214         command = 'sh %s' % temp_script.name
   1215         command_runner = self._GetProtectedFileCommandRunner()
   1216         if command_runner:
   1217           return command_runner(command)
   1218         else:
   1219           logging.warning(
   1220               'Could not set contents of protected file: %s' % filename)
   1221 
   1222 
   1223   def RemovePushedFiles(self):
   1224     """Removes all files pushed with PushIfNeeded() from the device."""
   1225     for p in self._pushed_files:
   1226       self.RunShellCommand('rm -r %s' % p, timeout_time=2 * 60)
   1227 
   1228   def ListPathContents(self, path):
   1229     """Lists files in all subdirectories of |path|.
   1230 
   1231     Args:
   1232       path: The path to list.
   1233 
   1234     Returns:
   1235       A dict of {"name": (size, lastmod), ...}.
   1236     """
   1237     # Example output:
   1238     # /foo/bar:
   1239     # -rw-r----- user group   102 2011-05-12 12:29:54.131623387 +0100 baz.txt
   1240     re_file = re.compile('^-(?P<perms>[^\s]+)\s+'
   1241                          '(?P<user>[^\s]+)\s+'
   1242                          '(?P<group>[^\s]+)\s+'
   1243                          '(?P<size>[^\s]+)\s+'
   1244                          '(?P<date>[^\s]+)\s+'
   1245                          '(?P<time>[^\s]+)\s+'
   1246                          '(?P<filename>[^\s]+)$')
   1247     return _GetFilesFromRecursiveLsOutput(
   1248         path, self.RunShellCommand('ls -lR %s' % path), re_file,
   1249         self.GetUtcOffset())
   1250 
   1251   def GetUtcOffset(self):
   1252     if not self._device_utc_offset:
   1253       self._device_utc_offset = self.RunShellCommand('date +%z')[0]
   1254     return self._device_utc_offset
   1255 
   1256   def SetJavaAssertsEnabled(self, enable):
   1257     """Sets or removes the device java assertions property.
   1258 
   1259     Args:
   1260       enable: If True the property will be set.
   1261 
   1262     Returns:
   1263       True if the file was modified (reboot is required for it to take effect).
   1264     """
   1265     # First ensure the desired property is persisted.
   1266     temp_props_file = tempfile.NamedTemporaryFile()
   1267     properties = ''
   1268     if self._adb.Pull(LOCAL_PROPERTIES_PATH, temp_props_file.name):
   1269       with open(temp_props_file.name) as f:
   1270         properties = f.read()
   1271     re_search = re.compile(r'^\s*' + re.escape(JAVA_ASSERT_PROPERTY) +
   1272                            r'\s*=\s*all\s*$', re.MULTILINE)
   1273     if enable != bool(re.search(re_search, properties)):
   1274       re_replace = re.compile(r'^\s*' + re.escape(JAVA_ASSERT_PROPERTY) +
   1275                               r'\s*=\s*\w+\s*$', re.MULTILINE)
   1276       properties = re.sub(re_replace, '', properties)
   1277       if enable:
   1278         properties += '\n%s=all\n' % JAVA_ASSERT_PROPERTY
   1279 
   1280       file(temp_props_file.name, 'w').write(properties)
   1281       self._adb.Push(temp_props_file.name, LOCAL_PROPERTIES_PATH)
   1282 
   1283     # Next, check the current runtime value is what we need, and
   1284     # if not, set it and report that a reboot is required.
   1285     was_set = 'all' in self.system_properties[JAVA_ASSERT_PROPERTY]
   1286     if was_set == enable:
   1287       return False
   1288     self.system_properties[JAVA_ASSERT_PROPERTY] = enable and 'all' or ''
   1289     return True
   1290 
   1291   def GetBuildId(self):
   1292     """Returns the build ID of the system (e.g. JRM79C)."""
   1293     build_id = self.system_properties['ro.build.id']
   1294     assert build_id
   1295     return build_id
   1296 
   1297   def GetBuildType(self):
   1298     """Returns the build type of the system (e.g. eng)."""
   1299     build_type = self.system_properties['ro.build.type']
   1300     assert build_type
   1301     return build_type
   1302 
   1303   def GetBuildProduct(self):
   1304     """Returns the build product of the device (e.g. maguro)."""
   1305     build_product = self.system_properties['ro.build.product']
   1306     assert build_product
   1307     return build_product
   1308 
   1309   def GetProductName(self):
   1310     """Returns the product name of the device (e.g. takju)."""
   1311     name = self.system_properties['ro.product.name']
   1312     assert name
   1313     return name
   1314 
   1315   def GetBuildFingerprint(self):
   1316     """Returns the build fingerprint of the device."""
   1317     build_fingerprint = self.system_properties['ro.build.fingerprint']
   1318     assert build_fingerprint
   1319     return build_fingerprint
   1320 
   1321   def GetDescription(self):
   1322     """Returns the description of the system.
   1323 
   1324     For example, "yakju-userdebug 4.1 JRN54F 364167 dev-keys".
   1325     """
   1326     description = self.system_properties['ro.build.description']
   1327     assert description
   1328     return description
   1329 
   1330   def GetProductModel(self):
   1331     """Returns the name of the product model (e.g. "Galaxy Nexus") """
   1332     model = self.system_properties['ro.product.model']
   1333     assert model
   1334     return model
   1335 
   1336   def GetWifiIP(self):
   1337     """Returns the wifi IP on the device."""
   1338     wifi_ip = self.system_properties['dhcp.wlan0.ipaddress']
   1339     # Do not assert here. Devices (e.g. emulators) may not have a WifiIP.
   1340     return wifi_ip
   1341 
   1342   def GetSubscriberInfo(self):
   1343     """Returns the device subscriber info (e.g. GSM and device ID) as string."""
   1344     iphone_sub = self.RunShellCommand('dumpsys iphonesubinfo')
   1345     # Do not assert here. Devices (e.g. Nakasi on K) may not have iphonesubinfo.
   1346     return '\n'.join(iphone_sub)
   1347 
   1348   def GetBatteryInfo(self):
   1349     """Returns a {str: str} dict of battery info (e.g. status, level, etc)."""
   1350     battery = self.RunShellCommand('dumpsys battery')
   1351     assert battery
   1352     battery_info = {}
   1353     for line in battery[1:]:
   1354       k, _, v = line.partition(': ')
   1355       battery_info[k.strip()] = v.strip()
   1356     return battery_info
   1357 
   1358   def GetSetupWizardStatus(self):
   1359     """Returns the status of the device setup wizard (e.g. DISABLED)."""
   1360     status = self.system_properties['ro.setupwizard.mode']
   1361     # On some devices, the status is empty if not otherwise set. In such cases
   1362     # the caller should expect an empty string to be returned.
   1363     return status
   1364 
   1365   def StartMonitoringLogcat(self, clear=True, logfile=None, filters=None):
   1366     """Starts monitoring the output of logcat, for use with WaitForLogMatch.
   1367 
   1368     Args:
   1369       clear: If True the existing logcat output will be cleared, to avoiding
   1370              matching historical output lurking in the log.
   1371       filters: A list of logcat filters to be used.
   1372     """
   1373     if clear:
   1374       self.RunShellCommand('logcat -c')
   1375     args = []
   1376     if self._adb._target_arg:
   1377       args += shlex.split(self._adb._target_arg)
   1378     args += ['logcat', '-v', 'threadtime']
   1379     if filters:
   1380       args.extend(filters)
   1381     else:
   1382       args.append('*:v')
   1383 
   1384     if logfile:
   1385       logfile = NewLineNormalizer(logfile)
   1386 
   1387     # Spawn logcat and synchronize with it.
   1388     for _ in range(4):
   1389       self._logcat = pexpect.spawn(constants.GetAdbPath(), args, timeout=10,
   1390                                    logfile=logfile)
   1391       if not clear or self.SyncLogCat():
   1392         break
   1393       self._logcat.close(force=True)
   1394     else:
   1395       logging.critical('Error reading from logcat: ' + str(self._logcat.match))
   1396       sys.exit(1)
   1397 
   1398   def SyncLogCat(self):
   1399     """Synchronize with logcat.
   1400 
   1401     Synchronize with the monitored logcat so that WaitForLogMatch will only
   1402     consider new message that are received after this point in time.
   1403 
   1404     Returns:
   1405       True if the synchronization succeeded.
   1406     """
   1407     assert self._logcat
   1408     tag = 'logcat_sync_%s' % time.time()
   1409     self.RunShellCommand('log ' + tag)
   1410     return self._logcat.expect([tag, pexpect.EOF, pexpect.TIMEOUT]) == 0
   1411 
   1412   def GetMonitoredLogCat(self):
   1413     """Returns an "adb logcat" command as created by pexpected.spawn."""
   1414     if not self._logcat:
   1415       self.StartMonitoringLogcat(clear=False)
   1416     return self._logcat
   1417 
   1418   def WaitForLogMatch(self, success_re, error_re, clear=False, timeout=10):
   1419     """Blocks until a matching line is logged or a timeout occurs.
   1420 
   1421     Args:
   1422       success_re: A compiled re to search each line for.
   1423       error_re: A compiled re which, if found, terminates the search for
   1424           |success_re|. If None is given, no error condition will be detected.
   1425       clear: If True the existing logcat output will be cleared, defaults to
   1426           false.
   1427       timeout: Timeout in seconds to wait for a log match.
   1428 
   1429     Raises:
   1430       pexpect.TIMEOUT after |timeout| seconds without a match for |success_re|
   1431       or |error_re|.
   1432 
   1433     Returns:
   1434       The re match object if |success_re| is matched first or None if |error_re|
   1435       is matched first.
   1436     """
   1437     logging.info('<<< Waiting for logcat:' + str(success_re.pattern))
   1438     t0 = time.time()
   1439     while True:
   1440       if not self._logcat:
   1441         self.StartMonitoringLogcat(clear)
   1442       try:
   1443         while True:
   1444           # Note this will block for upto the timeout _per log line_, so we need
   1445           # to calculate the overall timeout remaining since t0.
   1446           time_remaining = t0 + timeout - time.time()
   1447           if time_remaining < 0:
   1448             raise pexpect.TIMEOUT(self._logcat)
   1449           self._logcat.expect(PEXPECT_LINE_RE, timeout=time_remaining)
   1450           line = self._logcat.match.group(1)
   1451           if error_re:
   1452             error_match = error_re.search(line)
   1453             if error_match:
   1454               return None
   1455           success_match = success_re.search(line)
   1456           if success_match:
   1457             return success_match
   1458           logging.info('<<< Skipped Logcat Line:' + str(line))
   1459       except pexpect.TIMEOUT:
   1460         raise pexpect.TIMEOUT(
   1461             'Timeout (%ds) exceeded waiting for pattern "%s" (tip: use -vv '
   1462             'to debug)' %
   1463             (timeout, success_re.pattern))
   1464       except pexpect.EOF:
   1465         # It seems that sometimes logcat can end unexpectedly. This seems
   1466         # to happen during Chrome startup after a reboot followed by a cache
   1467         # clean. I don't understand why this happens, but this code deals with
   1468         # getting EOF in logcat.
   1469         logging.critical('Found EOF in adb logcat. Restarting...')
   1470         # Rerun spawn with original arguments. Note that self._logcat.args[0] is
   1471         # the path of adb, so we don't want it in the arguments.
   1472         self._logcat = pexpect.spawn(constants.GetAdbPath(),
   1473                                      self._logcat.args[1:],
   1474                                      timeout=self._logcat.timeout,
   1475                                      logfile=self._logcat.logfile)
   1476 
   1477   def StartRecordingLogcat(self, clear=True, filters=None):
   1478     """Starts recording logcat output to eventually be saved as a string.
   1479 
   1480     This call should come before some series of tests are run, with either
   1481     StopRecordingLogcat or SearchLogcatRecord following the tests.
   1482 
   1483     Args:
   1484       clear: True if existing log output should be cleared.
   1485       filters: A list of logcat filters to be used.
   1486     """
   1487     if not filters:
   1488       filters = ['*:v']
   1489     if clear:
   1490       self._adb.SendCommand('logcat -c')
   1491     logcat_command = 'adb %s logcat -v threadtime %s' % (self._adb._target_arg,
   1492                                                          ' '.join(filters))
   1493     self._logcat_tmpoutfile = tempfile.NamedTemporaryFile(bufsize=0)
   1494     self.logcat_process = subprocess.Popen(logcat_command, shell=True,
   1495                                            stdout=self._logcat_tmpoutfile)
   1496 
   1497   def GetCurrentRecordedLogcat(self):
   1498     """Return the current content of the logcat being recorded.
   1499        Call this after StartRecordingLogcat() and before StopRecordingLogcat().
   1500        This can be useful to perform timed polling/parsing.
   1501     Returns:
   1502        Current logcat output as a single string, or None if
   1503        StopRecordingLogcat() was already called.
   1504     """
   1505     if not self._logcat_tmpoutfile:
   1506       return None
   1507 
   1508     with open(self._logcat_tmpoutfile.name) as f:
   1509       return f.read()
   1510 
   1511   def StopRecordingLogcat(self):
   1512     """Stops an existing logcat recording subprocess and returns output.
   1513 
   1514     Returns:
   1515       The logcat output as a string or an empty string if logcat was not
   1516       being recorded at the time.
   1517     """
   1518     if not self.logcat_process:
   1519       return ''
   1520     # Cannot evaluate directly as 0 is a possible value.
   1521     # Better to read the self.logcat_process.stdout before killing it,
   1522     # Otherwise the communicate may return incomplete output due to pipe break.
   1523     if self.logcat_process.poll() is None:
   1524       self.logcat_process.kill()
   1525     self.logcat_process.wait()
   1526     self.logcat_process = None
   1527     self._logcat_tmpoutfile.seek(0)
   1528     output = self._logcat_tmpoutfile.read()
   1529     self._logcat_tmpoutfile.close()
   1530     self._logcat_tmpoutfile = None
   1531     return output
   1532 
   1533   @staticmethod
   1534   def SearchLogcatRecord(record, message, thread_id=None, proc_id=None,
   1535                          log_level=None, component=None):
   1536     """Searches the specified logcat output and returns results.
   1537 
   1538     This method searches through the logcat output specified by record for a
   1539     certain message, narrowing results by matching them against any other
   1540     specified criteria.  It returns all matching lines as described below.
   1541 
   1542     Args:
   1543       record: A string generated by Start/StopRecordingLogcat to search.
   1544       message: An output string to search for.
   1545       thread_id: The thread id that is the origin of the message.
   1546       proc_id: The process that is the origin of the message.
   1547       log_level: The log level of the message.
   1548       component: The name of the component that would create the message.
   1549 
   1550     Returns:
   1551       A list of dictionaries represeting matching entries, each containing keys
   1552       thread_id, proc_id, log_level, component, and message.
   1553     """
   1554     if thread_id:
   1555       thread_id = str(thread_id)
   1556     if proc_id:
   1557       proc_id = str(proc_id)
   1558     results = []
   1559     reg = re.compile('(\d+)\s+(\d+)\s+([A-Z])\s+([A-Za-z]+)\s*:(.*)$',
   1560                      re.MULTILINE)
   1561     log_list = reg.findall(record)
   1562     for (tid, pid, log_lev, comp, msg) in log_list:
   1563       if ((not thread_id or thread_id == tid) and
   1564           (not proc_id or proc_id == pid) and
   1565           (not log_level or log_level == log_lev) and
   1566           (not component or component == comp) and msg.find(message) > -1):
   1567         match = dict({'thread_id': tid, 'proc_id': pid,
   1568                       'log_level': log_lev, 'component': comp,
   1569                       'message': msg})
   1570         results.append(match)
   1571     return results
   1572 
   1573   def ExtractPid(self, process_name):
   1574     """Extracts Process Ids for a given process name from Android Shell.
   1575 
   1576     Args:
   1577       process_name: name of the process on the device.
   1578 
   1579     Returns:
   1580       List of all the process ids (as strings) that match the given name.
   1581       If the name of a process exactly matches the given name, the pid of
   1582       that process will be inserted to the front of the pid list.
   1583     """
   1584     pids = []
   1585     for line in self.RunShellCommand('ps', log_result=False):
   1586       data = line.split()
   1587       try:
   1588         if process_name in data[-1]:  # name is in the last column
   1589           if process_name == data[-1]:
   1590             pids.insert(0, data[1])  # PID is in the second column
   1591           else:
   1592             pids.append(data[1])
   1593       except IndexError:
   1594         pass
   1595     return pids
   1596 
   1597   def GetIoStats(self):
   1598     """Gets cumulative disk IO stats since boot (for all processes).
   1599 
   1600     Returns:
   1601       Dict of {num_reads, num_writes, read_ms, write_ms} or None if there
   1602       was an error.
   1603     """
   1604     IoStats = collections.namedtuple(
   1605         'IoStats',
   1606         ['device',
   1607          'num_reads_issued',
   1608          'num_reads_merged',
   1609          'num_sectors_read',
   1610          'ms_spent_reading',
   1611          'num_writes_completed',
   1612          'num_writes_merged',
   1613          'num_sectors_written',
   1614          'ms_spent_writing',
   1615          'num_ios_in_progress',
   1616          'ms_spent_doing_io',
   1617          'ms_spent_doing_io_weighted',
   1618         ])
   1619 
   1620     for line in self.GetFileContents('/proc/diskstats', log_result=False):
   1621       fields = line.split()
   1622       stats = IoStats._make([fields[2]] + [int(f) for f in fields[3:]])
   1623       if stats.device == 'mmcblk0':
   1624         return {
   1625             'num_reads': stats.num_reads_issued,
   1626             'num_writes': stats.num_writes_completed,
   1627             'read_ms': stats.ms_spent_reading,
   1628             'write_ms': stats.ms_spent_writing,
   1629         }
   1630     logging.warning('Could not find disk IO stats.')
   1631     return None
   1632 
   1633   def GetMemoryUsageForPid(self, pid):
   1634     """Returns the memory usage for given pid.
   1635 
   1636     Args:
   1637       pid: The pid number of the specific process running on device.
   1638 
   1639     Returns:
   1640       Dict of {metric:usage_kb}, for the process which has specified pid.
   1641       The metric keys which may be included are: Size, Rss, Pss, Shared_Clean,
   1642       Shared_Dirty, Private_Clean, Private_Dirty, VmHWM.
   1643     """
   1644     showmap = self.RunShellCommand('showmap %d' % pid)
   1645     if not showmap or not showmap[-1].endswith('TOTAL'):
   1646       logging.warning('Invalid output for showmap %s', str(showmap))
   1647       return {}
   1648     items = showmap[-1].split()
   1649     if len(items) != 9:
   1650       logging.warning('Invalid TOTAL for showmap %s', str(items))
   1651       return {}
   1652     usage_dict = collections.defaultdict(int)
   1653     usage_dict.update({
   1654         'Size': int(items[0].strip()),
   1655         'Rss': int(items[1].strip()),
   1656         'Pss': int(items[2].strip()),
   1657         'Shared_Clean': int(items[3].strip()),
   1658         'Shared_Dirty': int(items[4].strip()),
   1659         'Private_Clean': int(items[5].strip()),
   1660         'Private_Dirty': int(items[6].strip()),
   1661     })
   1662     peak_value_kb = 0
   1663     for line in self.GetProtectedFileContents('/proc/%s/status' % pid):
   1664       if not line.startswith('VmHWM:'):  # Format: 'VmHWM: +[0-9]+ kB'
   1665         continue
   1666       peak_value_kb = int(line.split(':')[1].strip().split(' ')[0])
   1667       break
   1668     usage_dict['VmHWM'] = peak_value_kb
   1669     if not peak_value_kb:
   1670       logging.warning('Could not find memory peak value for pid ' + str(pid))
   1671 
   1672     return usage_dict
   1673 
   1674   def ProcessesUsingDevicePort(self, device_port):
   1675     """Lists processes using the specified device port on loopback interface.
   1676 
   1677     Args:
   1678       device_port: Port on device we want to check.
   1679 
   1680     Returns:
   1681       A list of (pid, process_name) tuples using the specified port.
   1682     """
   1683     tcp_results = self.RunShellCommand('cat /proc/net/tcp', log_result=False)
   1684     tcp_address = '0100007F:%04X' % device_port
   1685     pids = []
   1686     for single_connect in tcp_results:
   1687       connect_results = single_connect.split()
   1688       # Column 1 is the TCP port, and Column 9 is the inode of the socket
   1689       if connect_results[1] == tcp_address:
   1690         socket_inode = connect_results[9]
   1691         socket_name = 'socket:[%s]' % socket_inode
   1692         lsof_results = self.RunShellCommand('lsof', log_result=False)
   1693         for single_process in lsof_results:
   1694           process_results = single_process.split()
   1695           # Ignore the line if it has less than nine columns in it, which may
   1696           # be the case when a process stops while lsof is executing.
   1697           if len(process_results) <= 8:
   1698             continue
   1699           # Column 0 is the executable name
   1700           # Column 1 is the pid
   1701           # Column 8 is the Inode in use
   1702           if process_results[8] == socket_name:
   1703             pids.append((int(process_results[1]), process_results[0]))
   1704         break
   1705     logging.info('PidsUsingDevicePort: %s', pids)
   1706     return pids
   1707 
   1708   def FileExistsOnDevice(self, file_name):
   1709     """Checks whether the given file exists on the device.
   1710 
   1711     Args:
   1712       file_name: Full path of file to check.
   1713 
   1714     Returns:
   1715       True if the file exists, False otherwise.
   1716     """
   1717     assert '"' not in file_name, 'file_name cannot contain double quotes'
   1718     try:
   1719       status = self._adb.SendShellCommand(
   1720           '\'test -e "%s"; echo $?\'' % (file_name))
   1721       if 'test: not found' not in status:
   1722         return int(status) == 0
   1723 
   1724       status = self._adb.SendShellCommand(
   1725           '\'ls "%s" >/dev/null 2>&1; echo $?\'' % (file_name))
   1726       return int(status) == 0
   1727     except ValueError:
   1728       if IsDeviceAttached(self._device):
   1729         raise errors.DeviceUnresponsiveError('Device may be offline.')
   1730 
   1731       return False
   1732 
   1733   def IsFileWritableOnDevice(self, file_name):
   1734     """Checks whether the given file (or directory) is writable on the device.
   1735 
   1736     Args:
   1737       file_name: Full path of file/directory to check.
   1738 
   1739     Returns:
   1740       True if writable, False otherwise.
   1741     """
   1742     assert '"' not in file_name, 'file_name cannot contain double quotes'
   1743     try:
   1744       status = self._adb.SendShellCommand(
   1745           '\'test -w "%s"; echo $?\'' % (file_name))
   1746       if 'test: not found' not in status:
   1747         return int(status) == 0
   1748       raise errors.AbortError('"test" binary not found. OS too old.')
   1749 
   1750     except ValueError:
   1751       if IsDeviceAttached(self._device):
   1752         raise errors.DeviceUnresponsiveError('Device may be offline.')
   1753 
   1754       return False
   1755 
   1756   @staticmethod
   1757   def GetTimestamp():
   1758     return time.strftime('%Y-%m-%d-%H%M%S', time.localtime())
   1759 
   1760   @staticmethod
   1761   def EnsureHostDirectory(host_file):
   1762     host_dir = os.path.dirname(os.path.abspath(host_file))
   1763     if not os.path.exists(host_dir):
   1764       os.makedirs(host_dir)
   1765 
   1766   def TakeScreenshot(self, host_file=None):
   1767     """Saves a screenshot image to |host_file| on the host.
   1768 
   1769     Args:
   1770       host_file: Absolute path to the image file to store on the host or None to
   1771                  use an autogenerated file name.
   1772 
   1773     Returns:
   1774       Resulting host file name of the screenshot.
   1775     """
   1776     host_file = os.path.abspath(host_file or
   1777                                 'screenshot-%s.png' % self.GetTimestamp())
   1778     self.EnsureHostDirectory(host_file)
   1779     device_file = '%s/screenshot.png' % self.GetExternalStorage()
   1780     self.RunShellCommand(
   1781         '/system/bin/screencap -p %s' % device_file)
   1782     self.PullFileFromDevice(device_file, host_file)
   1783     self.RunShellCommand('rm -f "%s"' % device_file)
   1784     return host_file
   1785 
   1786   def PullFileFromDevice(self, device_file, host_file):
   1787     """Download |device_file| on the device from to |host_file| on the host.
   1788 
   1789     Args:
   1790       device_file: Absolute path to the file to retrieve from the device.
   1791       host_file: Absolute path to the file to store on the host.
   1792     """
   1793     if not self._adb.Pull(device_file, host_file):
   1794       raise device_errors.AdbCommandFailedError(
   1795           ['pull', device_file, host_file], 'Failed to pull file from device.')
   1796     assert os.path.exists(host_file)
   1797 
   1798   def SetUtilWrapper(self, util_wrapper):
   1799     """Sets a wrapper prefix to be used when running a locally-built
   1800     binary on the device (ex.: md5sum_bin).
   1801     """
   1802     self._util_wrapper = util_wrapper
   1803 
   1804   def RunUIAutomatorTest(self, test, test_package, timeout):
   1805     """Runs a single uiautomator test.
   1806 
   1807     Args:
   1808       test: Test class/method.
   1809       test_package: Name of the test jar.
   1810       timeout: Timeout time in seconds.
   1811 
   1812     Returns:
   1813       An instance of am_instrument_parser.TestResult object.
   1814     """
   1815     cmd = 'uiautomator runtest %s -e class %s' % (test_package, test)
   1816     self._LogShell(cmd)
   1817     output = self._adb.SendShellCommand(cmd, timeout_time=timeout)
   1818     # uiautomator doesn't fully conform to the instrumenation test runner
   1819     # convention and doesn't terminate with INSTRUMENTATION_CODE.
   1820     # Just assume the first result is valid.
   1821     (test_results, _) = am_instrument_parser.ParseAmInstrumentOutput(output)
   1822     if not test_results:
   1823       raise errors.InstrumentationError(
   1824           'no test results... device setup correctly?')
   1825     return test_results[0]
   1826 
   1827   def DismissCrashDialogIfNeeded(self):
   1828     """Dismiss the error/ANR dialog if present.
   1829 
   1830     Returns: Name of the crashed package if a dialog is focused,
   1831              None otherwise.
   1832     """
   1833     re_focus = re.compile(
   1834         r'\s*mCurrentFocus.*Application (Error|Not Responding): (\S+)}')
   1835 
   1836     def _FindFocusedWindow():
   1837       match = None
   1838       for line in self.RunShellCommand('dumpsys window windows'):
   1839         match = re.match(re_focus, line)
   1840         if match:
   1841           break
   1842       return match
   1843 
   1844     match = _FindFocusedWindow()
   1845     if not match:
   1846       return
   1847     package = match.group(2)
   1848     logging.warning('Trying to dismiss %s dialog for %s' % match.groups())
   1849     self.SendKeyEvent(KEYCODE_DPAD_RIGHT)
   1850     self.SendKeyEvent(KEYCODE_DPAD_RIGHT)
   1851     self.SendKeyEvent(KEYCODE_ENTER)
   1852     match = _FindFocusedWindow()
   1853     if match:
   1854       logging.error('Still showing a %s dialog for %s' % match.groups())
   1855     return package
   1856 
   1857   def EfficientDeviceDirectoryCopy(self, source, dest):
   1858     """ Copy a directory efficiently on the device
   1859 
   1860     Uses a shell script running on the target to copy new and changed files the
   1861     source directory to the destination directory and remove added files. This
   1862     is in some cases much faster than cp -r.
   1863 
   1864     Args:
   1865       source: absolute path of source directory
   1866       dest: absolute path of destination directory
   1867     """
   1868     logging.info('In EfficientDeviceDirectoryCopy %s %s', source, dest)
   1869     with DeviceTempFile(self, suffix=".sh") as temp_script_file:
   1870       host_script_path = os.path.join(constants.DIR_SOURCE_ROOT,
   1871                                       'build',
   1872                                       'android',
   1873                                       'pylib',
   1874                                       'efficient_android_directory_copy.sh')
   1875       self._adb.Push(host_script_path, temp_script_file.name)
   1876       out = self.RunShellCommand(
   1877           'sh %s %s %s' % (temp_script_file.name, source, dest),
   1878           timeout_time=120)
   1879       if self._device:
   1880         device_repr = self._device[-4:]
   1881       else:
   1882         device_repr = '????'
   1883       for line in out:
   1884         logging.info('[%s]> %s', device_repr, line)
   1885 
   1886   def _GetControlUsbChargingCommand(self):
   1887     if self._control_usb_charging_command['cached']:
   1888       return self._control_usb_charging_command['command']
   1889     self._control_usb_charging_command['cached'] = True
   1890     if not self.IsRootEnabled():
   1891       return None
   1892     for command in CONTROL_USB_CHARGING_COMMANDS:
   1893       # Assert command is valid.
   1894       assert 'disable_command' in command
   1895       assert 'enable_command' in command
   1896       assert 'witness_file' in command
   1897       witness_file = command['witness_file']
   1898       if self.FileExistsOnDevice(witness_file):
   1899         self._control_usb_charging_command['command'] = command
   1900         return command
   1901     return None
   1902 
   1903   def CanControlUsbCharging(self):
   1904     return self._GetControlUsbChargingCommand() is not None
   1905 
   1906   def DisableUsbCharging(self, timeout=10):
   1907     command = self._GetControlUsbChargingCommand()
   1908     if not command:
   1909       raise Exception('Unable to act on usb charging.')
   1910     disable_command = command['disable_command']
   1911     t0 = time.time()
   1912     # Do not loop directly on self.IsDeviceCharging to cut the number of calls
   1913     # to the device.
   1914     while True:
   1915       if t0 + timeout - time.time() < 0:
   1916         raise pexpect.TIMEOUT('Unable to enable USB charging in time.')
   1917       self.RunShellCommand(disable_command)
   1918       if not self.IsDeviceCharging():
   1919         break
   1920 
   1921   def EnableUsbCharging(self, timeout=10):
   1922     command = self._GetControlUsbChargingCommand()
   1923     if not command:
   1924       raise Exception('Unable to act on usb charging.')
   1925     disable_command = command['enable_command']
   1926     t0 = time.time()
   1927     # Do not loop directly on self.IsDeviceCharging to cut the number of calls
   1928     # to the device.
   1929     while True:
   1930       if t0 + timeout - time.time() < 0:
   1931         raise pexpect.TIMEOUT('Unable to enable USB charging in time.')
   1932       self.RunShellCommand(disable_command)
   1933       if self.IsDeviceCharging():
   1934         break
   1935 
   1936   def IsDeviceCharging(self):
   1937     for line in self.RunShellCommand('dumpsys battery'):
   1938       if 'powered: ' in line:
   1939         if line.split('powered: ')[1] == 'true':
   1940           return True
   1941 
   1942 
   1943 class NewLineNormalizer(object):
   1944   """A file-like object to normalize EOLs to '\n'.
   1945 
   1946   Pexpect runs adb within a pseudo-tty device (see
   1947   http://www.noah.org/wiki/pexpect), so any '\n' printed by adb is written
   1948   as '\r\n' to the logfile. Since adb already uses '\r\n' to terminate
   1949   lines, the log ends up having '\r\r\n' at the end of each line. This
   1950   filter replaces the above with a single '\n' in the data stream.
   1951   """
   1952   def __init__(self, output):
   1953     self._output = output
   1954 
   1955   def write(self, data):
   1956     data = data.replace('\r\r\n', '\n')
   1957     self._output.write(data)
   1958 
   1959   def flush(self):
   1960     self._output.flush()
   1961