[Android] Reland of Remove android_commands and android_testrunner.

Original CL: https://codereview.chromium.org/1290773009/
Revert: https://codereview.chromium.org/1288993002/

BUG=267773
TBR=thakis@chromium.org,maruel@chromium.org

Review URL: https://codereview.chromium.org/1290173003

Cr-Commit-Position: refs/heads/master@{#343257}


CrOS-Libchrome-Original-Commit: cd661e4ff1097b6916891a99ec9654e39e265fba
diff --git a/build/android/gyp/util/build_device.py b/build/android/gyp/util/build_device.py
index 8ab1112..e187c73 100644
--- a/build/android/gyp/util/build_device.py
+++ b/build/android/gyp/util/build_device.py
@@ -16,11 +16,14 @@
 BUILD_ANDROID_DIR = os.path.join(os.path.dirname(__file__), '..', '..')
 sys.path.append(BUILD_ANDROID_DIR)
 
-from pylib import android_commands
+from pylib.device import adb_wrapper
 from pylib.device import device_errors
 from pylib.device import device_utils
 
-GetAttachedDevices = android_commands.GetAttachedDevices
+
+def GetAttachedDevices():
+  return [a.GetDeviceSerial()
+          for a in adb_wrapper.Devices()]
 
 
 class BuildDevice(object):
diff --git a/build/android/pylib/android_commands.py b/build/android/pylib/android_commands.py
deleted file mode 100644
index f7191f7..0000000
--- a/build/android/pylib/android_commands.py
+++ /dev/null
@@ -1,1976 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Provides an interface to communicate with the device via the adb command.
-
-Assumes adb binary is currently on system path.
-
-Note that this module is deprecated.
-"""
-# TODO(jbudorick): Delete this file once no clients use it.
-
-# pylint: skip-file
-
-import collections
-import datetime
-import inspect
-import logging
-import os
-import random
-import re
-import shlex
-import signal
-import subprocess
-import sys
-import tempfile
-import time
-
-import cmd_helper
-import constants
-import system_properties
-from utils import host_utils
-
-try:
-  from pylib import pexpect
-except ImportError:
-  pexpect = None
-
-sys.path.append(os.path.join(
-    constants.DIR_SOURCE_ROOT, 'third_party', 'android_testrunner'))
-import adb_interface
-import am_instrument_parser
-import errors
-
-from pylib.device import device_blacklist
-from pylib.device import device_errors
-
-# Pattern to search for the next whole line of pexpect output and capture it
-# into a match group. We can't use ^ and $ for line start end with pexpect,
-# see http://www.noah.org/python/pexpect/#doc for explanation why.
-PEXPECT_LINE_RE = re.compile('\n([^\r]*)\r')
-
-# Set the adb shell prompt to be a unique marker that will [hopefully] not
-# appear at the start of any line of a command's output.
-SHELL_PROMPT = '~+~PQ\x17RS~+~'
-
-# Java properties file
-LOCAL_PROPERTIES_PATH = constants.DEVICE_LOCAL_PROPERTIES_PATH
-
-# Property in /data/local.prop that controls Java assertions.
-JAVA_ASSERT_PROPERTY = 'dalvik.vm.enableassertions'
-
-# Keycode "enum" suitable for passing to AndroidCommands.SendKey().
-KEYCODE_HOME = 3
-KEYCODE_BACK = 4
-KEYCODE_DPAD_UP = 19
-KEYCODE_DPAD_DOWN = 20
-KEYCODE_DPAD_RIGHT = 22
-KEYCODE_ENTER = 66
-KEYCODE_MENU = 82
-
-MD5SUM_DEVICE_FOLDER = constants.TEST_EXECUTABLE_DIR + '/md5sum/'
-MD5SUM_DEVICE_PATH = MD5SUM_DEVICE_FOLDER + 'md5sum_bin'
-
-PIE_WRAPPER_PATH = constants.TEST_EXECUTABLE_DIR + '/run_pie'
-
-CONTROL_USB_CHARGING_COMMANDS = [
-  {
-    # Nexus 4
-    'witness_file': '/sys/module/pm8921_charger/parameters/disabled',
-    'enable_command': 'echo 0 > /sys/module/pm8921_charger/parameters/disabled',
-    'disable_command':
-        'echo 1 > /sys/module/pm8921_charger/parameters/disabled',
-  },
-  {
-    # Nexus 5
-    # Setting the HIZ bit of the bq24192 causes the charger to actually ignore
-    # energy coming from USB. Setting the power_supply offline just updates the
-    # Android system to reflect that.
-    'witness_file': '/sys/kernel/debug/bq24192/INPUT_SRC_CONT',
-    'enable_command': (
-        'echo 0x4A > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && '
-        'echo 1 > /sys/class/power_supply/usb/online'),
-    'disable_command': (
-        'echo 0xCA > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && '
-        'chmod 644 /sys/class/power_supply/usb/online && '
-        'echo 0 > /sys/class/power_supply/usb/online'),
-  },
-]
-
-class DeviceTempFile(object):
-  def __init__(self, android_commands, prefix='temp_file', suffix=''):
-    """Find an unused temporary file path in the devices external directory.
-
-    When this object is closed, the file will be deleted on the device.
-    """
-    self.android_commands = android_commands
-    while True:
-      # TODO(cjhopman): This could actually return the same file in multiple
-      # calls if the caller doesn't write to the files immediately. This is
-      # expected to never happen.
-      i = random.randint(0, 1000000)
-      self.name = '%s/%s-%d-%010d%s' % (
-          android_commands.GetExternalStorage(),
-          prefix, int(time.time()), i, suffix)
-      if not android_commands.FileExistsOnDevice(self.name):
-        break
-
-  def __enter__(self):
-    return self
-
-  def __exit__(self, type, value, traceback):
-    self.close()
-
-  def close(self):
-    self.android_commands.RunShellCommand('rm ' + self.name)
-
-
-def GetAVDs():
-  """Returns a list of AVDs."""
-  re_avd = re.compile('^[ ]+Name: ([a-zA-Z0-9_:.-]+)', re.MULTILINE)
-  avds = re_avd.findall(cmd_helper.GetCmdOutput(['android', 'list', 'avd']))
-  return avds
-
-def ResetBadDevices():
-  """Removes the blacklist that keeps track of bad devices for a current
-     build.
-  """
-  device_blacklist.ResetBlacklist()
-
-def ExtendBadDevices(devices):
-  """Adds devices to the blacklist that keeps track of bad devices for a
-     current build.
-
-  The devices listed in the bad devices file will not be returned by
-  GetAttachedDevices.
-
-  Args:
-    devices: list of bad devices to be added to the bad devices file.
-  """
-  device_blacklist.ExtendBlacklist(devices)
-
-
-def GetAttachedDevices(hardware=True, emulator=True, offline=False):
-  """Returns a list of attached, android devices and emulators.
-
-  If a preferred device has been set with ANDROID_SERIAL, it will be first in
-  the returned list. The arguments specify what devices to include in the list.
-
-  Example output:
-
-    * daemon not running. starting it now on port 5037 *
-    * daemon started successfully *
-    List of devices attached
-    027c10494100b4d7        device
-    emulator-5554   offline
-
-  Args:
-    hardware: Include attached actual devices that are online.
-    emulator: Include emulators (i.e. AVD's) currently on host.
-    offline: Include devices and emulators that are offline.
-
-  Returns: List of devices.
-  """
-  adb_devices_output = cmd_helper.GetCmdOutput([constants.GetAdbPath(),
-                                                'devices'])
-
-  re_device = re.compile('^([a-zA-Z0-9_:.-]+)\tdevice$', re.MULTILINE)
-  online_devices = re_device.findall(adb_devices_output)
-
-  re_device = re.compile('^(emulator-[0-9]+)\tdevice', re.MULTILINE)
-  emulator_devices = re_device.findall(adb_devices_output)
-
-  re_device = re.compile('^([a-zA-Z0-9_:.-]+)\t(?:offline|unauthorized)$',
-                         re.MULTILINE)
-  offline_devices = re_device.findall(adb_devices_output)
-
-  devices = []
-  # First determine list of online devices (e.g. hardware and/or emulator).
-  if hardware and emulator:
-    devices = online_devices
-  elif hardware:
-    devices = [device for device in online_devices
-               if device not in emulator_devices]
-  elif emulator:
-    devices = emulator_devices
-
-  # Now add offline devices if offline is true
-  if offline:
-    devices = devices + offline_devices
-
-  # Remove any devices in the blacklist.
-  blacklist = device_blacklist.ReadBlacklist()
-  if len(blacklist):
-    logging.info('Avoiding bad devices %s', ' '.join(blacklist))
-    devices = [device for device in devices if device not in blacklist]
-
-  preferred_device = os.environ.get('ANDROID_SERIAL')
-  if preferred_device in devices:
-    devices.remove(preferred_device)
-    devices.insert(0, preferred_device)
-  return devices
-
-
-def IsDeviceAttached(device):
-  """Return true if the device is attached and online."""
-  return device in GetAttachedDevices()
-
-
-def _GetFilesFromRecursiveLsOutput(path, ls_output, re_file, utc_offset=None):
-  """Gets a list of files from `ls` command output.
-
-  Python's os.walk isn't used because it doesn't work over adb shell.
-
-  Args:
-    path: The path to list.
-    ls_output: A list of lines returned by an `ls -lR` command.
-    re_file: A compiled regular expression which parses a line into named groups
-        consisting of at minimum "filename", "date", "time", "size" and
-        optionally "timezone".
-    utc_offset: A 5-character string of the form +HHMM or -HHMM, where HH is a
-        2-digit string giving the number of UTC offset hours, and MM is a
-        2-digit string giving the number of UTC offset minutes. If the input
-        utc_offset is None, will try to look for the value of "timezone" if it
-        is specified in re_file.
-
-  Returns:
-    A dict of {"name": (size, lastmod), ...} where:
-      name: The file name relative to |path|'s directory.
-      size: The file size in bytes (0 for directories).
-      lastmod: The file last modification date in UTC.
-  """
-  re_directory = re.compile('^%s/(?P<dir>[^:]+):$' % re.escape(path))
-  path_dir = os.path.dirname(path)
-
-  current_dir = ''
-  files = {}
-  for line in ls_output:
-    directory_match = re_directory.match(line)
-    if directory_match:
-      current_dir = directory_match.group('dir')
-      continue
-    file_match = re_file.match(line)
-    if file_match:
-      filename = os.path.join(current_dir, file_match.group('filename'))
-      if filename.startswith(path_dir):
-        filename = filename[len(path_dir) + 1:]
-      lastmod = datetime.datetime.strptime(
-          file_match.group('date') + ' ' + file_match.group('time')[:5],
-          '%Y-%m-%d %H:%M')
-      if not utc_offset and 'timezone' in re_file.groupindex:
-        utc_offset = file_match.group('timezone')
-      if isinstance(utc_offset, str) and len(utc_offset) == 5:
-        utc_delta = datetime.timedelta(hours=int(utc_offset[1:3]),
-                                       minutes=int(utc_offset[3:5]))
-        if utc_offset[0:1] == '-':
-          utc_delta = -utc_delta
-        lastmod -= utc_delta
-      files[filename] = (int(file_match.group('size')), lastmod)
-  return files
-
-
-def _ParseMd5SumOutput(md5sum_output):
-  """Returns a list of tuples from the provided md5sum output.
-
-  Args:
-    md5sum_output: output directly from md5sum binary.
-
-  Returns:
-    List of namedtuples with attributes |hash| and |path|, where |path| is the
-    absolute path to the file with an Md5Sum of |hash|.
-  """
-  HashAndPath = collections.namedtuple('HashAndPath', ['hash', 'path'])
-  split_lines = [line.split('  ') for line in md5sum_output]
-  return [HashAndPath._make(s) for s in split_lines if len(s) == 2]
-
-
-def _HasAdbPushSucceeded(command_output):
-  """Returns whether adb push has succeeded from the provided output."""
-  # TODO(frankf): We should look at the return code instead of the command
-  # output for many of the commands in this file.
-  if not command_output:
-    return True
-  # Success looks like this: "3035 KB/s (12512056 bytes in 4.025s)"
-  # Errors look like this: "failed to copy  ... "
-  if not re.search('^[0-9]', command_output.splitlines()[-1]):
-    logging.critical('PUSH FAILED: ' + command_output)
-    return False
-  return True
-
-
-def GetLogTimestamp(log_line, year):
-  """Returns the timestamp of the given |log_line| in the given year."""
-  try:
-    return datetime.datetime.strptime('%s-%s' % (year, log_line[:18]),
-                                      '%Y-%m-%d %H:%M:%S.%f')
-  except (ValueError, IndexError):
-    logging.critical('Error reading timestamp from ' + log_line)
-    return None
-
-
-class AndroidCommands(object):
-  """Helper class for communicating with Android device via adb."""
-
-  def __init__(self, device=None):
-    """Constructor.
-
-    Args:
-      device: If given, adb commands are only send to the device of this ID.
-          Otherwise commands are sent to all attached devices.
-    """
-    self._adb = adb_interface.AdbInterface(constants.GetAdbPath())
-    if device:
-      self._adb.SetTargetSerial(device)
-    self._device = device
-    self._logcat = None
-    self.logcat_process = None
-    self._logcat_tmpoutfile = None
-    self._pushed_files = []
-    self._device_utc_offset = None
-    self._potential_push_size = 0
-    self._actual_push_size = 0
-    self._external_storage = ''
-    self._util_wrapper = ''
-    self._system_properties = system_properties.SystemProperties(self.Adb())
-    self._push_if_needed_cache = {}
-    self._control_usb_charging_command = {
-        'command': None,
-        'cached': False,
-    }
-    self._protected_file_access_method_initialized = None
-    self._privileged_command_runner = None
-    self._pie_wrapper = None
-
-  @property
-  def system_properties(self):
-    return self._system_properties
-
-  def _LogShell(self, cmd):
-    """Logs the adb shell command."""
-    if self._device:
-      device_repr = self._device[-4:]
-    else:
-      device_repr = '????'
-    logging.info('[%s]> %s', device_repr, cmd)
-
-  def Adb(self):
-    """Returns our AdbInterface to avoid us wrapping all its methods."""
-    # TODO(tonyg): Goal should be to git rid of this method by making this API
-    # complete and alleviating the need.
-    return self._adb
-
-  def GetDevice(self):
-    """Returns the device serial."""
-    return self._device
-
-  def IsOnline(self):
-    """Checks whether the device is online.
-
-    Returns:
-      True if device is in 'device' mode, False otherwise.
-    """
-    # TODO(aurimas): revert to using adb get-state when android L adb is fixed.
-    #out = self._adb.SendCommand('get-state')
-    #return out.strip() == 'device'
-
-    out = self._adb.SendCommand('devices')
-    for line in out.split('\n'):
-      if self._device in line and 'device' in line:
-        return True
-    return False
-
-  def IsRootEnabled(self):
-    """Checks if root is enabled on the device."""
-    root_test_output = self.RunShellCommand('ls /root') or ['']
-    return not 'Permission denied' in root_test_output[0]
-
-  def EnableAdbRoot(self):
-    """Enables adb root on the device.
-
-    Returns:
-      True: if output from executing adb root was as expected.
-      False: otherwise.
-    """
-    if self.GetBuildType() == 'user':
-      logging.warning("Can't enable root in production builds with type user")
-      return False
-    else:
-      return_value = self._adb.EnableAdbRoot()
-      # EnableAdbRoot inserts a call for wait-for-device only when adb logcat
-      # output matches what is expected. Just to be safe add a call to
-      # wait-for-device.
-      self._adb.SendCommand('wait-for-device')
-      return return_value
-
-  def GetDeviceYear(self):
-    """Returns the year information of the date on device."""
-    return self.RunShellCommand('date +%Y')[0]
-
-  def GetExternalStorage(self):
-    if not self._external_storage:
-      self._external_storage = self.RunShellCommand('echo $EXTERNAL_STORAGE')[0]
-      if not self._external_storage:
-        raise device_errors.CommandFailedError(
-            ['shell', "'echo $EXTERNAL_STORAGE'"],
-            'Unable to find $EXTERNAL_STORAGE')
-    return self._external_storage
-
-  def WaitForDevicePm(self, timeout=120):
-    """Blocks until the device's package manager is available.
-
-    To workaround http://b/5201039, we restart the shell and retry if the
-    package manager isn't back after 120 seconds.
-
-    Raises:
-      errors.WaitForResponseTimedOutError after max retries reached.
-    """
-    last_err = None
-    retries = 3
-    while retries:
-      try:
-        self._adb.WaitForDevicePm(wait_time=timeout)
-        return  # Success
-      except errors.WaitForResponseTimedOutError as e:
-        last_err = e
-        logging.warning('Restarting and retrying after timeout: %s', e)
-        retries -= 1
-        self.RestartShell()
-    raise last_err # Only reached after max retries, re-raise the last error.
-
-  def RestartShell(self):
-    """Restarts the shell on the device. Does not block for it to return."""
-    self.RunShellCommand('stop')
-    self.RunShellCommand('start')
-
-  def Reboot(self, full_reboot=True):
-    """Reboots the device and waits for the package manager to return.
-
-    Args:
-      full_reboot: Whether to fully reboot the device or just restart the shell.
-    """
-    # TODO(torne): hive can't reboot the device either way without breaking the
-    # connection; work out if we can handle this better
-    if os.environ.get('USING_HIVE'):
-      logging.warning('Ignoring reboot request as we are on hive')
-      return
-    if full_reboot or not self.IsRootEnabled():
-      self._adb.SendCommand('reboot')
-      self._system_properties = system_properties.SystemProperties(self.Adb())
-      timeout = 300
-      retries = 1
-      # Wait for the device to disappear.
-      while retries < 10 and self.IsOnline():
-        time.sleep(1)
-        retries += 1
-    else:
-      self.RestartShell()
-      timeout = 120
-    # To run tests we need at least the package manager and the sd card (or
-    # other external storage) to be ready.
-    self.WaitForDevicePm(timeout)
-    self.WaitForSdCardReady(timeout)
-
-  def Shutdown(self):
-    """Shuts down the device."""
-    self._adb.SendCommand('reboot -p')
-    self._system_properties = system_properties.SystemProperties(self.Adb())
-
-  def Uninstall(self, package):
-    """Uninstalls the specified package from the device.
-
-    Args:
-      package: Name of the package to remove.
-
-    Returns:
-      A status string returned by adb uninstall
-    """
-    uninstall_command = 'uninstall %s' % package
-
-    self._LogShell(uninstall_command)
-    return self._adb.SendCommand(uninstall_command, timeout_time=60)
-
-  def Install(self, package_file_path, reinstall=False):
-    """Installs the specified package to the device.
-
-    Args:
-      package_file_path: Path to .apk file to install.
-      reinstall: Reinstall an existing apk, keeping the data.
-
-    Returns:
-      A status string returned by adb install
-    """
-    assert os.path.isfile(package_file_path), ('<%s> is not file' %
-                                               package_file_path)
-
-    install_cmd = ['install']
-
-    if reinstall:
-      install_cmd.append('-r')
-
-    install_cmd.append(package_file_path)
-    install_cmd = ' '.join(install_cmd)
-
-    self._LogShell(install_cmd)
-    return self._adb.SendCommand(install_cmd,
-                                 timeout_time=2 * 60,
-                                 retry_count=0)
-
-  def ManagedInstall(self, apk_path, keep_data=False, package_name=None,
-                     reboots_on_timeout=2):
-    """Installs specified package and reboots device on timeouts.
-
-    If package_name is supplied, checks if the package is already installed and
-    doesn't reinstall if the apk md5sums match.
-
-    Args:
-      apk_path: Path to .apk file to install.
-      keep_data: Reinstalls instead of uninstalling first, preserving the
-        application data.
-      package_name: Package name (only needed if keep_data=False).
-      reboots_on_timeout: number of time to reboot if package manager is frozen.
-    """
-    # Check if package is already installed and up to date.
-    if package_name:
-      installed_apk_path = self.GetApplicationPath(package_name)
-      if (installed_apk_path and
-          not self.GetFilesChanged(apk_path, installed_apk_path,
-                                   ignore_filenames=True)):
-        logging.info('Skipped install: identical %s APK already installed' %
-            package_name)
-        return
-    # Install.
-    reboots_left = reboots_on_timeout
-    while True:
-      try:
-        if not keep_data:
-          assert package_name
-          self.Uninstall(package_name)
-        install_status = self.Install(apk_path, reinstall=keep_data)
-        if 'Success' in install_status:
-          return
-        else:
-          raise Exception('Install failure: %s' % install_status)
-      except errors.WaitForResponseTimedOutError:
-        print '@@@STEP_WARNINGS@@@'
-        logging.info('Timeout on installing %s on device %s', apk_path,
-                     self._device)
-
-        if reboots_left <= 0:
-          raise Exception('Install timed out')
-
-        # Force a hard reboot on last attempt
-        self.Reboot(full_reboot=(reboots_left == 1))
-        reboots_left -= 1
-
-  def MakeSystemFolderWritable(self):
-    """Remounts the /system folder rw."""
-    out = self._adb.SendCommand('remount')
-    if out.strip() != 'remount succeeded':
-      raise errors.MsgException('Remount failed: %s' % out)
-
-  def RestartAdbdOnDevice(self):
-    logging.info('Restarting adbd on the device...')
-    with DeviceTempFile(self, suffix=".sh") as temp_script_file:
-      host_script_path = os.path.join(constants.DIR_SOURCE_ROOT,
-                                      'build',
-                                      'android',
-                                      'pylib',
-                                      'restart_adbd.sh')
-      self._adb.Push(host_script_path, temp_script_file.name)
-      self.RunShellCommand('. %s' % temp_script_file.name)
-      self._adb.SendCommand('wait-for-device')
-
-  def RestartAdbServer(self):
-    """Restart the adb server."""
-    ret = self.KillAdbServer()
-    if ret != 0:
-      raise errors.MsgException('KillAdbServer: %d' % ret)
-
-    ret = self.StartAdbServer()
-    if ret != 0:
-      raise errors.MsgException('StartAdbServer: %d' % ret)
-
-  @staticmethod
-  def KillAdbServer():
-    """Kill adb server."""
-    adb_cmd = [constants.GetAdbPath(), 'kill-server']
-    ret = cmd_helper.RunCmd(adb_cmd)
-    retry = 0
-    while retry < 3:
-      ret, _ = cmd_helper.GetCmdStatusAndOutput(['pgrep', 'adb'])
-      if ret != 0:
-        # pgrep didn't find adb, kill-server succeeded.
-        return 0
-      retry += 1
-      time.sleep(retry)
-    return ret
-
-  def StartAdbServer(self):
-    """Start adb server."""
-    adb_cmd = ['taskset', '-c', '0', constants.GetAdbPath(), 'start-server']
-    ret, _ = cmd_helper.GetCmdStatusAndOutput(adb_cmd)
-    retry = 0
-    while retry < 3:
-      ret, _ = cmd_helper.GetCmdStatusAndOutput(['pgrep', 'adb'])
-      if ret == 0:
-        # pgrep found adb, start-server succeeded.
-        # Waiting for device to reconnect before returning success.
-        self._adb.SendCommand('wait-for-device')
-        return 0
-      retry += 1
-      time.sleep(retry)
-    return ret
-
-  def WaitForSystemBootCompleted(self, wait_time):
-    """Waits for targeted system's boot_completed flag to be set.
-
-    Args:
-      wait_time: time in seconds to wait
-
-    Raises:
-      WaitForResponseTimedOutError if wait_time elapses and flag still not
-      set.
-    """
-    logging.info('Waiting for system boot completed...')
-    self._adb.SendCommand('wait-for-device')
-    # Now the device is there, but system not boot completed.
-    # Query the sys.boot_completed flag with a basic command
-    boot_completed = False
-    attempts = 0
-    wait_period = 5
-    while not boot_completed and (attempts * wait_period) < wait_time:
-      output = self.system_properties['sys.boot_completed']
-      output = output.strip()
-      if output == '1':
-        boot_completed = True
-      else:
-        # If 'error: xxx' returned when querying the flag, it means
-        # adb server lost the connection to the emulator, so restart the adb
-        # server.
-        if 'error:' in output:
-          self.RestartAdbServer()
-        time.sleep(wait_period)
-        attempts += 1
-    if not boot_completed:
-      raise errors.WaitForResponseTimedOutError(
-          'sys.boot_completed flag was not set after %s seconds' % wait_time)
-
-  def WaitForSdCardReady(self, timeout_time):
-    """Wait for the SD card ready before pushing data into it."""
-    logging.info('Waiting for SD card ready...')
-    sdcard_ready = False
-    attempts = 0
-    wait_period = 5
-    external_storage = self.GetExternalStorage()
-    while not sdcard_ready and attempts * wait_period < timeout_time:
-      output = self.RunShellCommand('ls ' + external_storage)
-      if output:
-        sdcard_ready = True
-      else:
-        time.sleep(wait_period)
-        attempts += 1
-    if not sdcard_ready:
-      raise errors.WaitForResponseTimedOutError(
-          'SD card not ready after %s seconds' % timeout_time)
-
-  def GetAndroidToolStatusAndOutput(self, command, lib_path=None, *args, **kw):
-    """Runs a native Android binary, wrapping the command as necessary.
-
-    This is a specialization of GetShellCommandStatusAndOutput, which is meant
-    for running tools/android/ binaries and handle properly: (1) setting the
-    lib path (for component=shared_library), (2) using the PIE wrapper on ICS.
-    See crbug.com/373219 for more context.
-
-    Args:
-      command: String containing the command to send.
-      lib_path: (optional) path to the folder containing the dependent libs.
-      Same other arguments of GetCmdStatusAndOutput.
-    """
-    # The first time this command is run the device is inspected to check
-    # whether a wrapper for running PIE executable is needed (only Android ICS)
-    # or not. The results is cached, so the wrapper is pushed only once.
-    if self._pie_wrapper is None:
-      # None: did not check; '': did check and not needed; '/path': use /path.
-      self._pie_wrapper = ''
-      if self.GetBuildId().startswith('I'):  # Ixxxx = Android ICS.
-        run_pie_dist_path = os.path.join(constants.GetOutDirectory(), 'run_pie')
-        assert os.path.exists(run_pie_dist_path), 'Please build run_pie'
-        # The PIE loader must be pushed manually (i.e. no PushIfNeeded) because
-        # PushIfNeeded requires md5sum and md5sum requires the wrapper as well.
-        adb_command = 'push %s %s' % (run_pie_dist_path, PIE_WRAPPER_PATH)
-        assert _HasAdbPushSucceeded(self._adb.SendCommand(adb_command))
-        self._pie_wrapper = PIE_WRAPPER_PATH
-
-    if self._pie_wrapper:
-      command = '%s %s' % (self._pie_wrapper, command)
-    if lib_path:
-      command = 'LD_LIBRARY_PATH=%s %s' % (lib_path, command)
-    return self.GetShellCommandStatusAndOutput(command, *args, **kw)
-
-  # It is tempting to turn this function into a generator, however this is not
-  # possible without using a private (local) adb_shell instance (to ensure no
-  # other command interleaves usage of it), which would defeat the main aim of
-  # being able to reuse the adb shell instance across commands.
-  def RunShellCommand(self, command, timeout_time=20, log_result=False):
-    """Send a command to the adb shell and return the result.
-
-    Args:
-      command: String containing the shell command to send.
-      timeout_time: Number of seconds to wait for command to respond before
-        retrying, used by AdbInterface.SendShellCommand.
-      log_result: Boolean to indicate whether we should log the result of the
-                  shell command.
-
-    Returns:
-      list containing the lines of output received from running the command
-    """
-    self._LogShell(command)
-    if "'" in command:
-      command = command.replace('\'', '\'\\\'\'')
-    result = self._adb.SendShellCommand(
-        "'%s'" % command, timeout_time).splitlines()
-    # TODO(b.kelemen): we should really be able to drop the stderr of the
-    # command or raise an exception based on what the caller wants.
-    result = [ l for l in result if not l.startswith('WARNING') ]
-    if ['error: device not found'] == result:
-      raise errors.DeviceUnresponsiveError('device not found')
-    if log_result:
-      self._LogShell('\n'.join(result))
-    return result
-
-  def GetShellCommandStatusAndOutput(self, command, timeout_time=20,
-                                     log_result=False):
-    """See RunShellCommand() above.
-
-    Returns:
-      The tuple (exit code, list of output lines).
-    """
-    lines = self.RunShellCommand(
-        command + '; echo %$?', timeout_time, log_result)
-    last_line = lines[-1]
-    status_pos = last_line.rfind('%')
-    assert status_pos >= 0
-    status = int(last_line[status_pos + 1:])
-    if status_pos == 0:
-      lines = lines[:-1]
-    else:
-      lines = lines[:-1] + [last_line[:status_pos]]
-    return (status, lines)
-
-  def KillAll(self, process, signum=9, with_su=False):
-    """Android version of killall, connected via adb.
-
-    Args:
-      process: name of the process to kill off.
-      signum: signal to use, 9 (SIGKILL) by default.
-      with_su: wether or not to use su to kill the processes.
-
-    Returns:
-      the number of processes killed
-    """
-    pids = self.ExtractPid(process)
-    if pids:
-      cmd = 'kill -%d %s' % (signum, ' '.join(pids))
-      if with_su:
-        self.RunShellCommandWithSU(cmd)
-      else:
-        self.RunShellCommand(cmd)
-    return len(pids)
-
-  def KillAllBlocking(self, process, timeout_sec, signum=9, with_su=False):
-    """Blocking version of killall, connected via adb.
-
-    This waits until no process matching the corresponding name appears in ps'
-    output anymore.
-
-    Args:
-      process: name of the process to kill off
-      timeout_sec: the timeout in seconds
-      signum: same as |KillAll|
-      with_su: same as |KillAll|
-    Returns:
-      the number of processes killed
-    """
-    processes_killed = self.KillAll(process, signum=signum, with_su=with_su)
-    if processes_killed:
-      elapsed = 0
-      wait_period = 0.1
-      # Note that this doesn't take into account the time spent in ExtractPid().
-      while self.ExtractPid(process) and elapsed < timeout_sec:
-        time.sleep(wait_period)
-        elapsed += wait_period
-      if elapsed >= timeout_sec:
-        return processes_killed - self.ExtractPid(process)
-    return processes_killed
-
-  @staticmethod
-  def _GetActivityCommand(package, activity, wait_for_completion, action,
-                          category, data, extras, trace_file_name, force_stop,
-                          flags):
-    """Creates command to start |package|'s activity on the device.
-
-    Args - as for StartActivity
-
-    Returns:
-      the command to run on the target to start the activity
-    """
-    cmd = 'am start -a %s' % action
-    if force_stop:
-      cmd += ' -S'
-    if wait_for_completion:
-      cmd += ' -W'
-    if category:
-      cmd += ' -c %s' % category
-    if package and activity:
-      cmd += ' -n %s/%s' % (package, activity)
-    if data:
-      cmd += ' -d "%s"' % data
-    if extras:
-      for key in extras:
-        value = extras[key]
-        if isinstance(value, str):
-          cmd += ' --es'
-        elif isinstance(value, bool):
-          cmd += ' --ez'
-        elif isinstance(value, int):
-          cmd += ' --ei'
-        else:
-          raise NotImplementedError(
-              'Need to teach StartActivity how to pass %s extras' % type(value))
-        cmd += ' %s %s' % (key, value)
-    if trace_file_name:
-      cmd += ' --start-profiler ' + trace_file_name
-    if flags:
-      cmd += ' -f %s' % flags
-    return cmd
-
-  def StartActivity(self, package, activity, wait_for_completion=False,
-                    action='android.intent.action.VIEW',
-                    category=None, data=None,
-                    extras=None, trace_file_name=None,
-                    force_stop=False, flags=None):
-    """Starts |package|'s activity on the device.
-
-    Args:
-      package: Name of package to start (e.g. 'com.google.android.apps.chrome').
-      activity: Name of activity (e.g. '.Main' or
-        'com.google.android.apps.chrome.Main').
-      wait_for_completion: wait for the activity to finish launching (-W flag).
-      action: string (e.g. "android.intent.action.MAIN"). Default is VIEW.
-      category: string (e.g. "android.intent.category.HOME")
-      data: Data string to pass to activity (e.g. 'http://www.example.com/').
-      extras: Dict of extras to pass to activity. Values are significant.
-      trace_file_name: If used, turns on and saves the trace to this file name.
-      force_stop: force stop the target app before starting the activity (-S
-        flag).
-    Returns:
-      The output of the underlying command as a list of lines.
-    """
-    cmd = self._GetActivityCommand(package, activity, wait_for_completion,
-                                   action, category, data, extras,
-                                   trace_file_name, force_stop, flags)
-    return self.RunShellCommand(cmd)
-
-  def StartActivityTimed(self, package, activity, wait_for_completion=False,
-                         action='android.intent.action.VIEW',
-                         category=None, data=None,
-                         extras=None, trace_file_name=None,
-                         force_stop=False, flags=None):
-    """Starts |package|'s activity on the device, returning the start time
-
-    Args - as for StartActivity
-
-    Returns:
-      A tuple containing:
-        - the output of the underlying command as a list of lines, and
-        - a timestamp string for the time at which the activity started
-    """
-    cmd = self._GetActivityCommand(package, activity, wait_for_completion,
-                                   action, category, data, extras,
-                                   trace_file_name, force_stop, flags)
-    self.StartMonitoringLogcat()
-    out = self.RunShellCommand('log starting activity; ' + cmd)
-    activity_started_re = re.compile('.*starting activity.*')
-    m = self.WaitForLogMatch(activity_started_re, None)
-    assert m
-    start_line = m.group(0)
-    return (out, GetLogTimestamp(start_line, self.GetDeviceYear()))
-
-  def StartCrashUploadService(self, package):
-    # TODO(frankf): We really need a python wrapper around Intent
-    # to be shared with StartActivity/BroadcastIntent.
-    cmd = (
-      'am startservice -a %s.crash.ACTION_FIND_ALL -n '
-      '%s/%s.crash.MinidumpUploadService' %
-      (constants.PACKAGE_INFO['chrome'].package,
-       package,
-       constants.PACKAGE_INFO['chrome'].package))
-    am_output = self.RunShellCommandWithSU(cmd)
-    assert am_output and 'Starting' in am_output[-1], (
-        'Service failed to start: %s' % am_output)
-    time.sleep(15)
-
-  def BroadcastIntent(self, package, intent, *args):
-    """Send a broadcast intent.
-
-    Args:
-      package: Name of package containing the intent.
-      intent: Name of the intent.
-      args: Optional extra arguments for the intent.
-    """
-    cmd = 'am broadcast -a %s.%s %s' % (package, intent, ' '.join(args))
-    self.RunShellCommand(cmd)
-
-  def GoHome(self):
-    """Tell the device to return to the home screen. Blocks until completion."""
-    self.RunShellCommand('am start -W '
-        '-a android.intent.action.MAIN -c android.intent.category.HOME')
-
-  def CloseApplication(self, package):
-    """Attempt to close down the application, using increasing violence.
-
-    Args:
-      package: Name of the process to kill off, e.g.
-      com.google.android.apps.chrome
-    """
-    self.RunShellCommand('am force-stop ' + package)
-
-  def GetApplicationPath(self, package):
-    """Get the installed apk path on the device for the given package.
-
-    Args:
-      package: Name of the package.
-
-    Returns:
-      Path to the apk on the device if it exists, None otherwise.
-    """
-    pm_path_output  = self.RunShellCommand('pm path ' + package)
-    # The path output contains anything if and only if the package
-    # exists.
-    if pm_path_output:
-      # pm_path_output is of the form: "package:/path/to/foo.apk"
-      return pm_path_output[0].split(':')[1]
-    else:
-      return None
-
-  def ClearApplicationState(self, package):
-    """Closes and clears all state for the given |package|."""
-    # Check that the package exists before clearing it. Necessary because
-    # calling pm clear on a package that doesn't exist may never return.
-    pm_path_output  = self.RunShellCommand('pm path ' + package)
-    # The path output only contains anything if and only if the package exists.
-    if pm_path_output:
-      self.RunShellCommand('pm clear ' + package)
-
-  def SendKeyEvent(self, keycode):
-    """Sends keycode to the device.
-
-    Args:
-      keycode: Numeric keycode to send (see "enum" at top of file).
-    """
-    self.RunShellCommand('input keyevent %d' % keycode)
-
-  def _RunMd5Sum(self, host_path, device_path):
-    """Gets the md5sum of a host path and device path.
-
-    Args:
-      host_path: Path (file or directory) on the host.
-      device_path: Path on the device.
-
-    Returns:
-      A tuple containing lists of the host and device md5sum results as
-      created by _ParseMd5SumOutput().
-    """
-    md5sum_dist_path = os.path.join(constants.GetOutDirectory(),
-                                    'md5sum_dist')
-    assert os.path.exists(md5sum_dist_path), 'Please build md5sum.'
-    md5sum_dist_mtime = os.stat(md5sum_dist_path).st_mtime
-    if (md5sum_dist_path not in self._push_if_needed_cache or
-        self._push_if_needed_cache[md5sum_dist_path] != md5sum_dist_mtime):
-      command = 'push %s %s' % (md5sum_dist_path, MD5SUM_DEVICE_FOLDER)
-      assert _HasAdbPushSucceeded(self._adb.SendCommand(command))
-      self._push_if_needed_cache[md5sum_dist_path] = md5sum_dist_mtime
-
-    (_, md5_device_output) = self.GetAndroidToolStatusAndOutput(
-        self._util_wrapper + ' ' + MD5SUM_DEVICE_PATH + ' ' + device_path,
-        lib_path=MD5SUM_DEVICE_FOLDER,
-        timeout_time=2 * 60)
-    device_hash_tuples = _ParseMd5SumOutput(md5_device_output)
-    assert os.path.exists(host_path), 'Local path not found %s' % host_path
-    md5sum_output = cmd_helper.GetCmdOutput(
-        [os.path.join(constants.GetOutDirectory(), 'md5sum_bin_host'),
-         host_path])
-    host_hash_tuples = _ParseMd5SumOutput(md5sum_output.splitlines())
-    return (host_hash_tuples, device_hash_tuples)
-
-  def GetFilesChanged(self, host_path, device_path, ignore_filenames=False):
-    """Compares the md5sum of a host path against a device path.
-
-    Note: Ignores extra files on the device.
-
-    Args:
-      host_path: Path (file or directory) on the host.
-      device_path: Path on the device.
-      ignore_filenames: If True only the file contents are considered when
-          checking whether a file has changed, otherwise the relative path
-          must also match.
-
-    Returns:
-      A list of tuples of the form (host_path, device_path) for files whose
-      md5sums do not match.
-    """
-
-    # Md5Sum resolves symbolic links in path names so the calculation of
-    # relative path names from its output will need the real path names of the
-    # base directories. Having calculated these they are used throughout the
-    # function since this makes us less subject to any future changes to Md5Sum.
-    real_host_path = os.path.realpath(host_path)
-    real_device_path = self.RunShellCommand('realpath "%s"' % device_path)[0]
-
-    host_hash_tuples, device_hash_tuples = self._RunMd5Sum(
-        real_host_path, real_device_path)
-
-    if len(host_hash_tuples) > len(device_hash_tuples):
-      logging.info('%s files do not exist on the device' %
-                   (len(host_hash_tuples) - len(device_hash_tuples)))
-
-    host_rel = [(os.path.relpath(os.path.normpath(t.path), real_host_path),
-                 t.hash)
-                for t in host_hash_tuples]
-
-    if os.path.isdir(real_host_path):
-      def RelToRealPaths(rel_path):
-        return (os.path.join(real_host_path, rel_path),
-                os.path.join(real_device_path, rel_path))
-    else:
-      assert len(host_rel) == 1
-      def RelToRealPaths(_):
-        return (real_host_path, real_device_path)
-
-    if ignore_filenames:
-      # If we are ignoring file names, then we want to push any file for which
-      # a file with an equivalent MD5 sum does not exist on the device.
-      device_hashes = set([h.hash for h in device_hash_tuples])
-      ShouldPush = lambda p, h: h not in device_hashes
-    else:
-      # Otherwise, we want to push any file on the host for which a file with
-      # an equivalent MD5 sum does not exist at the same relative path on the
-      # device.
-      device_rel = dict([(os.path.relpath(os.path.normpath(t.path),
-                                          real_device_path),
-                          t.hash)
-                         for t in device_hash_tuples])
-      ShouldPush = lambda p, h: p not in device_rel or h != device_rel[p]
-
-    return [RelToRealPaths(path) for path, host_hash in host_rel
-            if ShouldPush(path, host_hash)]
-
-  def PushIfNeeded(self, host_path, device_path):
-    """Pushes |host_path| to |device_path|.
-
-    Works for files and directories. This method skips copying any paths in
-    |test_data_paths| that already exist on the device with the same hash.
-
-    All pushed files can be removed by calling RemovePushedFiles().
-    """
-    MAX_INDIVIDUAL_PUSHES = 50
-    if not os.path.exists(host_path):
-      raise device_errors.CommandFailedError(
-          'Local path not found %s' % host_path, device=str(self))
-
-    # See if the file on the host changed since the last push (if any) and
-    # return early if it didn't. Note that this shortcut assumes that the tests
-    # on the device don't modify the files.
-    if not os.path.isdir(host_path):
-      if host_path in self._push_if_needed_cache:
-        host_path_mtime = self._push_if_needed_cache[host_path]
-        if host_path_mtime == os.stat(host_path).st_mtime:
-          return
-
-    size = host_utils.GetRecursiveDiskUsage(host_path)
-    self._pushed_files.append(device_path)
-    self._potential_push_size += size
-
-    if os.path.isdir(host_path):
-      self.RunShellCommand('mkdir -p "%s"' % device_path)
-
-    changed_files = self.GetFilesChanged(host_path, device_path)
-    logging.info('Found %d files that need to be pushed to %s',
-        len(changed_files), device_path)
-    if not changed_files:
-      return
-
-    def Push(host, device):
-      # NOTE: We can't use adb_interface.Push() because it hardcodes a timeout
-      # of 60 seconds which isn't sufficient for a lot of users of this method.
-      push_command = 'push %s %s' % (host, device)
-      self._LogShell(push_command)
-
-      # Retry push with increasing backoff if the device is busy.
-      retry = 0
-      while True:
-        output = self._adb.SendCommand(push_command, timeout_time=30 * 60)
-        if _HasAdbPushSucceeded(output):
-          if not os.path.isdir(host_path):
-            self._push_if_needed_cache[host] = os.stat(host).st_mtime
-          return
-        if retry < 3:
-          retry += 1
-          wait_time = 5 * retry
-          logging.error('Push failed, retrying in %d seconds: %s' %
-                        (wait_time, output))
-          time.sleep(wait_time)
-        else:
-          raise Exception('Push failed: %s' % output)
-
-    diff_size = 0
-    if len(changed_files) <= MAX_INDIVIDUAL_PUSHES:
-      diff_size = sum(host_utils.GetRecursiveDiskUsage(f[0])
-                      for f in changed_files)
-
-    # TODO(craigdh): Replace this educated guess with a heuristic that
-    # approximates the push time for each method.
-    if len(changed_files) > MAX_INDIVIDUAL_PUSHES or diff_size > 0.5 * size:
-      self._actual_push_size += size
-      Push(host_path, device_path)
-    else:
-      for f in changed_files:
-        Push(f[0], f[1])
-      self._actual_push_size += diff_size
-
-  def GetPushSizeInfo(self):
-    """Get total size of pushes to the device done via PushIfNeeded()
-
-    Returns:
-      A tuple:
-        1. Total size of push requests to PushIfNeeded (MB)
-        2. Total size that was actually pushed (MB)
-    """
-    return (self._potential_push_size, self._actual_push_size)
-
-  def GetFileContents(self, filename, log_result=False):
-    """Gets contents from the file specified by |filename|."""
-    return self.RunShellCommand('cat "%s" 2>/dev/null' % filename,
-                                log_result=log_result)
-
-  def SetFileContents(self, filename, contents):
-    """Writes |contents| to the file specified by |filename|."""
-    with tempfile.NamedTemporaryFile() as f:
-      f.write(contents)
-      f.flush()
-      self._adb.Push(f.name, filename)
-
-  def RunShellCommandWithSU(self, command, timeout_time=20, log_result=False):
-    return self.RunShellCommand('su -c %s' % command, timeout_time, log_result)
-
-  def CanAccessProtectedFileContents(self):
-    """Returns True if Get/SetProtectedFileContents would work via "su" or adb
-    shell running as root.
-
-    Devices running user builds don't have adb root, but may provide "su" which
-    can be used for accessing protected files.
-    """
-    return (self._GetProtectedFileCommandRunner() != None)
-
-  def _GetProtectedFileCommandRunner(self):
-    """Finds the best method to access protected files on the device.
-
-    Returns:
-      1. None when privileged files cannot be accessed on the device.
-      2. Otherwise: A function taking a single parameter: a string with command
-         line arguments. Running that function executes the command with
-         the appropriate method.
-    """
-    if self._protected_file_access_method_initialized:
-      return self._privileged_command_runner
-
-    self._privileged_command_runner = None
-    self._protected_file_access_method_initialized = True
-
-    for cmd in [self.RunShellCommand, self.RunShellCommandWithSU]:
-      # Get contents of the auxv vector for the init(8) process from a small
-      # binary file that always exists on linux and is always read-protected.
-      contents = cmd('cat /proc/1/auxv')
-      # The leading 4 or 8-bytes of auxv vector is a_type. There are not many
-      # reserved a_type values, hence byte 2 must always be '\0' for a realistic
-      # auxv. See /usr/include/elf.h.
-      if len(contents) > 0 and (contents[0][2] == '\0'):
-        self._privileged_command_runner = cmd
-        break
-    return self._privileged_command_runner
-
-  def GetProtectedFileContents(self, filename):
-    """Gets contents from the protected file specified by |filename|.
-
-    This is potentially less efficient than GetFileContents.
-    """
-    command = 'cat "%s" 2> /dev/null' % filename
-    command_runner = self._GetProtectedFileCommandRunner()
-    if command_runner:
-      return command_runner(command)
-    else:
-      logging.warning('Could not access protected file: %s' % filename)
-      return []
-
-  def SetProtectedFileContents(self, filename, contents):
-    """Writes |contents| to the protected file specified by |filename|.
-
-    This is less efficient than SetFileContents.
-    """
-    with DeviceTempFile(self) as temp_file:
-      with DeviceTempFile(self, suffix=".sh") as temp_script:
-        # Put the contents in a temporary file
-        self.SetFileContents(temp_file.name, contents)
-        # Create a script to copy the file contents to its final destination
-        self.SetFileContents(temp_script.name,
-                             'cat %s > %s' % (temp_file.name, filename))
-
-        command = 'sh %s' % temp_script.name
-        command_runner = self._GetProtectedFileCommandRunner()
-        if command_runner:
-          return command_runner(command)
-        else:
-          logging.warning(
-              'Could not set contents of protected file: %s' % filename)
-
-
-  def RemovePushedFiles(self):
-    """Removes all files pushed with PushIfNeeded() from the device."""
-    for p in self._pushed_files:
-      self.RunShellCommand('rm -r %s' % p, timeout_time=2 * 60)
-
-  def ListPathContents(self, path):
-    """Lists files in all subdirectories of |path|.
-
-    Args:
-      path: The path to list.
-
-    Returns:
-      A dict of {"name": (size, lastmod), ...}.
-    """
-    # Example output:
-    # /foo/bar:
-    # -rw-r----- user group   102 2011-05-12 12:29:54.131623387 +0100 baz.txt
-    re_file = re.compile('^-(?P<perms>[^\s]+)\s+'
-                         '(?P<user>[^\s]+)\s+'
-                         '(?P<group>[^\s]+)\s+'
-                         '(?P<size>[^\s]+)\s+'
-                         '(?P<date>[^\s]+)\s+'
-                         '(?P<time>[^\s]+)\s+'
-                         '(?P<filename>[^\s]+)$')
-    return _GetFilesFromRecursiveLsOutput(
-        path, self.RunShellCommand('ls -lR %s' % path), re_file,
-        self.GetUtcOffset())
-
-  def GetUtcOffset(self):
-    if not self._device_utc_offset:
-      self._device_utc_offset = self.RunShellCommand('date +%z')[0]
-    return self._device_utc_offset
-
-  def SetJavaAssertsEnabled(self, enable):
-    """Sets or removes the device java assertions property.
-
-    Args:
-      enable: If True the property will be set.
-
-    Returns:
-      True if the file was modified (reboot is required for it to take effect).
-    """
-    # First ensure the desired property is persisted.
-    temp_props_file = tempfile.NamedTemporaryFile()
-    properties = ''
-    if self._adb.Pull(LOCAL_PROPERTIES_PATH, temp_props_file.name):
-      with open(temp_props_file.name) as f:
-        properties = f.read()
-    re_search = re.compile(r'^\s*' + re.escape(JAVA_ASSERT_PROPERTY) +
-                           r'\s*=\s*all\s*$', re.MULTILINE)
-    if enable != bool(re.search(re_search, properties)):
-      re_replace = re.compile(r'^\s*' + re.escape(JAVA_ASSERT_PROPERTY) +
-                              r'\s*=\s*\w+\s*$', re.MULTILINE)
-      properties = re.sub(re_replace, '', properties)
-      if enable:
-        properties += '\n%s=all\n' % JAVA_ASSERT_PROPERTY
-
-      file(temp_props_file.name, 'w').write(properties)
-      self._adb.Push(temp_props_file.name, LOCAL_PROPERTIES_PATH)
-
-    # Next, check the current runtime value is what we need, and
-    # if not, set it and report that a reboot is required.
-    was_set = 'all' in self.system_properties[JAVA_ASSERT_PROPERTY]
-    if was_set == enable:
-      return False
-    self.system_properties[JAVA_ASSERT_PROPERTY] = enable and 'all' or ''
-    return True
-
-  def GetBuildId(self):
-    """Returns the build ID of the system (e.g. JRM79C)."""
-    build_id = self.system_properties['ro.build.id']
-    assert build_id
-    return build_id
-
-  def GetBuildType(self):
-    """Returns the build type of the system (e.g. eng)."""
-    build_type = self.system_properties['ro.build.type']
-    assert build_type
-    return build_type
-
-  def GetBuildProduct(self):
-    """Returns the build product of the device (e.g. maguro)."""
-    build_product = self.system_properties['ro.build.product']
-    assert build_product
-    return build_product
-
-  def GetProductName(self):
-    """Returns the product name of the device (e.g. takju)."""
-    name = self.system_properties['ro.product.name']
-    assert name
-    return name
-
-  def GetBuildFingerprint(self):
-    """Returns the build fingerprint of the device."""
-    build_fingerprint = self.system_properties['ro.build.fingerprint']
-    assert build_fingerprint
-    return build_fingerprint
-
-  def GetDescription(self):
-    """Returns the description of the system.
-
-    For example, "yakju-userdebug 4.1 JRN54F 364167 dev-keys".
-    """
-    description = self.system_properties['ro.build.description']
-    assert description
-    return description
-
-  def GetProductModel(self):
-    """Returns the name of the product model (e.g. "Galaxy Nexus") """
-    model = self.system_properties['ro.product.model']
-    assert model
-    return model
-
-  def GetWifiIP(self):
-    """Returns the wifi IP on the device."""
-    wifi_ip = self.system_properties['dhcp.wlan0.ipaddress']
-    # Do not assert here. Devices (e.g. emulators) may not have a WifiIP.
-    return wifi_ip
-
-  def GetSubscriberInfo(self):
-    """Returns the device subscriber info (e.g. GSM and device ID) as string."""
-    iphone_sub = self.RunShellCommand('dumpsys iphonesubinfo')
-    # Do not assert here. Devices (e.g. Nakasi on K) may not have iphonesubinfo.
-    return '\n'.join(iphone_sub)
-
-  def GetBatteryInfo(self):
-    """Returns a {str: str} dict of battery info (e.g. status, level, etc)."""
-    battery = self.RunShellCommand('dumpsys battery')
-    assert battery
-    battery_info = {}
-    for line in battery[1:]:
-      k, _, v = line.partition(': ')
-      battery_info[k.strip()] = v.strip()
-    return battery_info
-
-  def GetSetupWizardStatus(self):
-    """Returns the status of the device setup wizard (e.g. DISABLED)."""
-    status = self.system_properties['ro.setupwizard.mode']
-    # On some devices, the status is empty if not otherwise set. In such cases
-    # the caller should expect an empty string to be returned.
-    return status
-
-  def StartMonitoringLogcat(self, clear=True, logfile=None, filters=None):
-    """Starts monitoring the output of logcat, for use with WaitForLogMatch.
-
-    Args:
-      clear: If True the existing logcat output will be cleared, to avoiding
-             matching historical output lurking in the log.
-      filters: A list of logcat filters to be used.
-    """
-    if clear:
-      self.RunShellCommand('logcat -c')
-    args = []
-    if self._adb._target_arg:
-      args += shlex.split(self._adb._target_arg)
-    args += ['logcat', '-v', 'threadtime']
-    if filters:
-      args.extend(filters)
-    else:
-      args.append('*:v')
-
-    if logfile:
-      logfile = NewLineNormalizer(logfile)
-
-    # Spawn logcat and synchronize with it.
-    for _ in range(4):
-      self._logcat = pexpect.spawn(constants.GetAdbPath(), args, timeout=10,
-                                   logfile=logfile)
-      if not clear or self.SyncLogCat():
-        break
-      self._logcat.close(force=True)
-    else:
-      logging.critical('Error reading from logcat: ' + str(self._logcat.match))
-      sys.exit(1)
-
-  def SyncLogCat(self):
-    """Synchronize with logcat.
-
-    Synchronize with the monitored logcat so that WaitForLogMatch will only
-    consider new message that are received after this point in time.
-
-    Returns:
-      True if the synchronization succeeded.
-    """
-    assert self._logcat
-    tag = 'logcat_sync_%s' % time.time()
-    self.RunShellCommand('log ' + tag)
-    return self._logcat.expect([tag, pexpect.EOF, pexpect.TIMEOUT]) == 0
-
-  def GetMonitoredLogCat(self):
-    """Returns an "adb logcat" command as created by pexpected.spawn."""
-    if not self._logcat:
-      self.StartMonitoringLogcat(clear=False)
-    return self._logcat
-
-  def WaitForLogMatch(self, success_re, error_re, clear=False, timeout=10):
-    """Blocks until a matching line is logged or a timeout occurs.
-
-    Args:
-      success_re: A compiled re to search each line for.
-      error_re: A compiled re which, if found, terminates the search for
-          |success_re|. If None is given, no error condition will be detected.
-      clear: If True the existing logcat output will be cleared, defaults to
-          false.
-      timeout: Timeout in seconds to wait for a log match.
-
-    Raises:
-      pexpect.TIMEOUT after |timeout| seconds without a match for |success_re|
-      or |error_re|.
-
-    Returns:
-      The re match object if |success_re| is matched first or None if |error_re|
-      is matched first.
-    """
-    logging.info('<<< Waiting for logcat:' + str(success_re.pattern))
-    t0 = time.time()
-    while True:
-      if not self._logcat:
-        self.StartMonitoringLogcat(clear)
-      try:
-        while True:
-          # Note this will block for upto the timeout _per log line_, so we need
-          # to calculate the overall timeout remaining since t0.
-          time_remaining = t0 + timeout - time.time()
-          if time_remaining < 0:
-            raise pexpect.TIMEOUT(self._logcat)
-          self._logcat.expect(PEXPECT_LINE_RE, timeout=time_remaining)
-          line = self._logcat.match.group(1)
-          if error_re:
-            error_match = error_re.search(line)
-            if error_match:
-              return None
-          success_match = success_re.search(line)
-          if success_match:
-            return success_match
-          logging.info('<<< Skipped Logcat Line:' + str(line))
-      except pexpect.TIMEOUT:
-        raise pexpect.TIMEOUT(
-            'Timeout (%ds) exceeded waiting for pattern "%s" (tip: use -vv '
-            'to debug)' %
-            (timeout, success_re.pattern))
-      except pexpect.EOF:
-        # It seems that sometimes logcat can end unexpectedly. This seems
-        # to happen during Chrome startup after a reboot followed by a cache
-        # clean. I don't understand why this happens, but this code deals with
-        # getting EOF in logcat.
-        logging.critical('Found EOF in adb logcat. Restarting...')
-        # Rerun spawn with original arguments. Note that self._logcat.args[0] is
-        # the path of adb, so we don't want it in the arguments.
-        self._logcat = pexpect.spawn(constants.GetAdbPath(),
-                                     self._logcat.args[1:],
-                                     timeout=self._logcat.timeout,
-                                     logfile=self._logcat.logfile)
-
-  def StartRecordingLogcat(self, clear=True, filters=None):
-    """Starts recording logcat output to eventually be saved as a string.
-
-    This call should come before some series of tests are run, with either
-    StopRecordingLogcat or SearchLogcatRecord following the tests.
-
-    Args:
-      clear: True if existing log output should be cleared.
-      filters: A list of logcat filters to be used.
-    """
-    if not filters:
-      filters = ['*:v']
-    if clear:
-      self._adb.SendCommand('logcat -c')
-    logcat_command = 'adb %s logcat -v threadtime %s' % (self._adb._target_arg,
-                                                         ' '.join(filters))
-    self._logcat_tmpoutfile = tempfile.NamedTemporaryFile(bufsize=0)
-    self.logcat_process = subprocess.Popen(logcat_command, shell=True,
-                                           stdout=self._logcat_tmpoutfile)
-
-  def GetCurrentRecordedLogcat(self):
-    """Return the current content of the logcat being recorded.
-       Call this after StartRecordingLogcat() and before StopRecordingLogcat().
-       This can be useful to perform timed polling/parsing.
-    Returns:
-       Current logcat output as a single string, or None if
-       StopRecordingLogcat() was already called.
-    """
-    if not self._logcat_tmpoutfile:
-      return None
-
-    with open(self._logcat_tmpoutfile.name) as f:
-      return f.read()
-
-  def StopRecordingLogcat(self):
-    """Stops an existing logcat recording subprocess and returns output.
-
-    Returns:
-      The logcat output as a string or an empty string if logcat was not
-      being recorded at the time.
-    """
-    if not self.logcat_process:
-      return ''
-    # Cannot evaluate directly as 0 is a possible value.
-    # Better to read the self.logcat_process.stdout before killing it,
-    # Otherwise the communicate may return incomplete output due to pipe break.
-    if self.logcat_process.poll() is None:
-      self.logcat_process.kill()
-    self.logcat_process.wait()
-    self.logcat_process = None
-    self._logcat_tmpoutfile.seek(0)
-    output = self._logcat_tmpoutfile.read()
-    self._logcat_tmpoutfile.close()
-    self._logcat_tmpoutfile = None
-    return output
-
-  @staticmethod
-  def SearchLogcatRecord(record, message, thread_id=None, proc_id=None,
-                         log_level=None, component=None):
-    """Searches the specified logcat output and returns results.
-
-    This method searches through the logcat output specified by record for a
-    certain message, narrowing results by matching them against any other
-    specified criteria.  It returns all matching lines as described below.
-
-    Args:
-      record: A string generated by Start/StopRecordingLogcat to search.
-      message: An output string to search for.
-      thread_id: The thread id that is the origin of the message.
-      proc_id: The process that is the origin of the message.
-      log_level: The log level of the message.
-      component: The name of the component that would create the message.
-
-    Returns:
-      A list of dictionaries represeting matching entries, each containing keys
-      thread_id, proc_id, log_level, component, and message.
-    """
-    if thread_id:
-      thread_id = str(thread_id)
-    if proc_id:
-      proc_id = str(proc_id)
-    results = []
-    reg = re.compile('(\d+)\s+(\d+)\s+([A-Z])\s+([A-Za-z]+)\s*:(.*)$',
-                     re.MULTILINE)
-    log_list = reg.findall(record)
-    for (tid, pid, log_lev, comp, msg) in log_list:
-      if ((not thread_id or thread_id == tid) and
-          (not proc_id or proc_id == pid) and
-          (not log_level or log_level == log_lev) and
-          (not component or component == comp) and msg.find(message) > -1):
-        match = dict({'thread_id': tid, 'proc_id': pid,
-                      'log_level': log_lev, 'component': comp,
-                      'message': msg})
-        results.append(match)
-    return results
-
-  def ExtractPid(self, process_name):
-    """Extracts Process Ids for a given process name from Android Shell.
-
-    Args:
-      process_name: name of the process on the device.
-
-    Returns:
-      List of all the process ids (as strings) that match the given name.
-      If the name of a process exactly matches the given name, the pid of
-      that process will be inserted to the front of the pid list.
-    """
-    pids = []
-    for line in self.RunShellCommand('ps', log_result=False):
-      data = line.split()
-      try:
-        if process_name in data[-1]:  # name is in the last column
-          if process_name == data[-1]:
-            pids.insert(0, data[1])  # PID is in the second column
-          else:
-            pids.append(data[1])
-      except IndexError:
-        pass
-    return pids
-
-  def GetIoStats(self):
-    """Gets cumulative disk IO stats since boot (for all processes).
-
-    Returns:
-      Dict of {num_reads, num_writes, read_ms, write_ms} or None if there
-      was an error.
-    """
-    IoStats = collections.namedtuple(
-        'IoStats',
-        ['device',
-         'num_reads_issued',
-         'num_reads_merged',
-         'num_sectors_read',
-         'ms_spent_reading',
-         'num_writes_completed',
-         'num_writes_merged',
-         'num_sectors_written',
-         'ms_spent_writing',
-         'num_ios_in_progress',
-         'ms_spent_doing_io',
-         'ms_spent_doing_io_weighted',
-        ])
-
-    for line in self.GetFileContents('/proc/diskstats', log_result=False):
-      fields = line.split()
-      stats = IoStats._make([fields[2]] + [int(f) for f in fields[3:]])
-      if stats.device == 'mmcblk0':
-        return {
-            'num_reads': stats.num_reads_issued,
-            'num_writes': stats.num_writes_completed,
-            'read_ms': stats.ms_spent_reading,
-            'write_ms': stats.ms_spent_writing,
-        }
-    logging.warning('Could not find disk IO stats.')
-    return None
-
-  def GetMemoryUsageForPid(self, pid):
-    """Returns the memory usage for given pid.
-
-    Args:
-      pid: The pid number of the specific process running on device.
-
-    Returns:
-      Dict of {metric:usage_kb}, for the process which has specified pid.
-      The metric keys which may be included are: Size, Rss, Pss, Shared_Clean,
-      Shared_Dirty, Private_Clean, Private_Dirty, VmHWM.
-    """
-    showmap = self.RunShellCommand('showmap %d' % pid)
-    if not showmap or not showmap[-1].endswith('TOTAL'):
-      logging.warning('Invalid output for showmap %s', str(showmap))
-      return {}
-    items = showmap[-1].split()
-    if len(items) != 9:
-      logging.warning('Invalid TOTAL for showmap %s', str(items))
-      return {}
-    usage_dict = collections.defaultdict(int)
-    usage_dict.update({
-        'Size': int(items[0].strip()),
-        'Rss': int(items[1].strip()),
-        'Pss': int(items[2].strip()),
-        'Shared_Clean': int(items[3].strip()),
-        'Shared_Dirty': int(items[4].strip()),
-        'Private_Clean': int(items[5].strip()),
-        'Private_Dirty': int(items[6].strip()),
-    })
-    peak_value_kb = 0
-    for line in self.GetProtectedFileContents('/proc/%s/status' % pid):
-      if not line.startswith('VmHWM:'):  # Format: 'VmHWM: +[0-9]+ kB'
-        continue
-      peak_value_kb = int(line.split(':')[1].strip().split(' ')[0])
-      break
-    usage_dict['VmHWM'] = peak_value_kb
-    if not peak_value_kb:
-      logging.warning('Could not find memory peak value for pid ' + str(pid))
-
-    return usage_dict
-
-  def ProcessesUsingDevicePort(self, device_port):
-    """Lists processes using the specified device port on loopback interface.
-
-    Args:
-      device_port: Port on device we want to check.
-
-    Returns:
-      A list of (pid, process_name) tuples using the specified port.
-    """
-    tcp_results = self.RunShellCommand('cat /proc/net/tcp', log_result=False)
-    tcp_address = '0100007F:%04X' % device_port
-    pids = []
-    for single_connect in tcp_results:
-      connect_results = single_connect.split()
-      # Column 1 is the TCP port, and Column 9 is the inode of the socket
-      if connect_results[1] == tcp_address:
-        socket_inode = connect_results[9]
-        socket_name = 'socket:[%s]' % socket_inode
-        lsof_results = self.RunShellCommand('lsof', log_result=False)
-        for single_process in lsof_results:
-          process_results = single_process.split()
-          # Ignore the line if it has less than nine columns in it, which may
-          # be the case when a process stops while lsof is executing.
-          if len(process_results) <= 8:
-            continue
-          # Column 0 is the executable name
-          # Column 1 is the pid
-          # Column 8 is the Inode in use
-          if process_results[8] == socket_name:
-            pids.append((int(process_results[1]), process_results[0]))
-        break
-    logging.info('PidsUsingDevicePort: %s', pids)
-    return pids
-
-  def FileExistsOnDevice(self, file_name):
-    """Checks whether the given file exists on the device.
-
-    Args:
-      file_name: Full path of file to check.
-
-    Returns:
-      True if the file exists, False otherwise.
-    """
-    assert '"' not in file_name, 'file_name cannot contain double quotes'
-    try:
-      status = self._adb.SendShellCommand(
-          '\'test -e "%s"; echo $?\'' % (file_name))
-      if 'test: not found' not in status:
-        return int(status) == 0
-
-      status = self._adb.SendShellCommand(
-          '\'ls "%s" >/dev/null 2>&1; echo $?\'' % (file_name))
-      return int(status) == 0
-    except ValueError:
-      if IsDeviceAttached(self._device):
-        raise errors.DeviceUnresponsiveError('Device may be offline.')
-
-      return False
-
-  def IsFileWritableOnDevice(self, file_name):
-    """Checks whether the given file (or directory) is writable on the device.
-
-    Args:
-      file_name: Full path of file/directory to check.
-
-    Returns:
-      True if writable, False otherwise.
-    """
-    assert '"' not in file_name, 'file_name cannot contain double quotes'
-    try:
-      status = self._adb.SendShellCommand(
-          '\'test -w "%s"; echo $?\'' % (file_name))
-      if 'test: not found' not in status:
-        return int(status) == 0
-      raise errors.AbortError('"test" binary not found. OS too old.')
-
-    except ValueError:
-      if IsDeviceAttached(self._device):
-        raise errors.DeviceUnresponsiveError('Device may be offline.')
-
-      return False
-
-  @staticmethod
-  def GetTimestamp():
-    return time.strftime('%Y-%m-%d-%H%M%S', time.localtime())
-
-  @staticmethod
-  def EnsureHostDirectory(host_file):
-    host_dir = os.path.dirname(os.path.abspath(host_file))
-    if not os.path.exists(host_dir):
-      os.makedirs(host_dir)
-
-  def TakeScreenshot(self, host_file=None):
-    """Saves a screenshot image to |host_file| on the host.
-
-    Args:
-      host_file: Absolute path to the image file to store on the host or None to
-                 use an autogenerated file name.
-
-    Returns:
-      Resulting host file name of the screenshot.
-    """
-    host_file = os.path.abspath(host_file or
-                                'screenshot-%s.png' % self.GetTimestamp())
-    self.EnsureHostDirectory(host_file)
-    device_file = '%s/screenshot.png' % self.GetExternalStorage()
-    self.RunShellCommand(
-        '/system/bin/screencap -p %s' % device_file)
-    self.PullFileFromDevice(device_file, host_file)
-    self.RunShellCommand('rm -f "%s"' % device_file)
-    return host_file
-
-  def PullFileFromDevice(self, device_file, host_file):
-    """Download |device_file| on the device from to |host_file| on the host.
-
-    Args:
-      device_file: Absolute path to the file to retrieve from the device.
-      host_file: Absolute path to the file to store on the host.
-    """
-    if not self._adb.Pull(device_file, host_file):
-      raise device_errors.AdbCommandFailedError(
-          ['pull', device_file, host_file], 'Failed to pull file from device.')
-    assert os.path.exists(host_file)
-
-  def SetUtilWrapper(self, util_wrapper):
-    """Sets a wrapper prefix to be used when running a locally-built
-    binary on the device (ex.: md5sum_bin).
-    """
-    self._util_wrapper = util_wrapper
-
-  def RunUIAutomatorTest(self, test, test_package, timeout):
-    """Runs a single uiautomator test.
-
-    Args:
-      test: Test class/method.
-      test_package: Name of the test jar.
-      timeout: Timeout time in seconds.
-
-    Returns:
-      An instance of am_instrument_parser.TestResult object.
-    """
-    cmd = 'uiautomator runtest %s -e class %s' % (test_package, test)
-    self._LogShell(cmd)
-    output = self._adb.SendShellCommand(cmd, timeout_time=timeout)
-    # uiautomator doesn't fully conform to the instrumenation test runner
-    # convention and doesn't terminate with INSTRUMENTATION_CODE.
-    # Just assume the first result is valid.
-    (test_results, _) = am_instrument_parser.ParseAmInstrumentOutput(output)
-    if not test_results:
-      raise errors.InstrumentationError(
-          'no test results... device setup correctly?')
-    return test_results[0]
-
-  def DismissCrashDialogIfNeeded(self):
-    """Dismiss the error/ANR dialog if present.
-
-    Returns: Name of the crashed package if a dialog is focused,
-             None otherwise.
-    """
-    re_focus = re.compile(
-        r'\s*mCurrentFocus.*Application (Error|Not Responding): (\S+)}')
-
-    def _FindFocusedWindow():
-      match = None
-      for line in self.RunShellCommand('dumpsys window windows'):
-        match = re.match(re_focus, line)
-        if match:
-          break
-      return match
-
-    match = _FindFocusedWindow()
-    if not match:
-      return
-    package = match.group(2)
-    logging.warning('Trying to dismiss %s dialog for %s' % match.groups())
-    self.SendKeyEvent(KEYCODE_DPAD_RIGHT)
-    self.SendKeyEvent(KEYCODE_DPAD_RIGHT)
-    self.SendKeyEvent(KEYCODE_ENTER)
-    match = _FindFocusedWindow()
-    if match:
-      logging.error('Still showing a %s dialog for %s' % match.groups())
-    return package
-
-  def EfficientDeviceDirectoryCopy(self, source, dest):
-    """ Copy a directory efficiently on the device
-
-    Uses a shell script running on the target to copy new and changed files the
-    source directory to the destination directory and remove added files. This
-    is in some cases much faster than cp -r.
-
-    Args:
-      source: absolute path of source directory
-      dest: absolute path of destination directory
-    """
-    logging.info('In EfficientDeviceDirectoryCopy %s %s', source, dest)
-    with DeviceTempFile(self, suffix=".sh") as temp_script_file:
-      host_script_path = os.path.join(constants.DIR_SOURCE_ROOT,
-                                      'build',
-                                      'android',
-                                      'pylib',
-                                      'efficient_android_directory_copy.sh')
-      self._adb.Push(host_script_path, temp_script_file.name)
-      out = self.RunShellCommand(
-          'sh %s %s %s' % (temp_script_file.name, source, dest),
-          timeout_time=120)
-      if self._device:
-        device_repr = self._device[-4:]
-      else:
-        device_repr = '????'
-      for line in out:
-        logging.info('[%s]> %s', device_repr, line)
-
-  def _GetControlUsbChargingCommand(self):
-    if self._control_usb_charging_command['cached']:
-      return self._control_usb_charging_command['command']
-    self._control_usb_charging_command['cached'] = True
-    if not self.IsRootEnabled():
-      return None
-    for command in CONTROL_USB_CHARGING_COMMANDS:
-      # Assert command is valid.
-      assert 'disable_command' in command
-      assert 'enable_command' in command
-      assert 'witness_file' in command
-      witness_file = command['witness_file']
-      if self.FileExistsOnDevice(witness_file):
-        self._control_usb_charging_command['command'] = command
-        return command
-    return None
-
-  def CanControlUsbCharging(self):
-    return self._GetControlUsbChargingCommand() is not None
-
-  def DisableUsbCharging(self, timeout=10):
-    command = self._GetControlUsbChargingCommand()
-    if not command:
-      raise Exception('Unable to act on usb charging.')
-    disable_command = command['disable_command']
-    t0 = time.time()
-    # Do not loop directly on self.IsDeviceCharging to cut the number of calls
-    # to the device.
-    while True:
-      if t0 + timeout - time.time() < 0:
-        raise pexpect.TIMEOUT('Unable to disable USB charging in time: %s' % (
-            self.GetBatteryInfo()))
-      self.RunShellCommand(disable_command)
-      if not self.IsDeviceCharging():
-        break
-
-  def EnableUsbCharging(self, timeout=10):
-    command = self._GetControlUsbChargingCommand()
-    if not command:
-      raise Exception('Unable to act on usb charging.')
-    disable_command = command['enable_command']
-    t0 = time.time()
-    # Do not loop directly on self.IsDeviceCharging to cut the number of calls
-    # to the device.
-    while True:
-      if t0 + timeout - time.time() < 0:
-        raise pexpect.TIMEOUT('Unable to enable USB charging in time.')
-      self.RunShellCommand(disable_command)
-      if self.IsDeviceCharging():
-        break
-
-  def IsDeviceCharging(self):
-    for line in self.RunShellCommand('dumpsys battery'):
-      if 'powered: ' in line:
-        if line.split('powered: ')[1] == 'true':
-          return True
-
-
-class NewLineNormalizer(object):
-  """A file-like object to normalize EOLs to '\n'.
-
-  Pexpect runs adb within a pseudo-tty device (see
-  http://www.noah.org/wiki/pexpect), so any '\n' printed by adb is written
-  as '\r\n' to the logfile. Since adb already uses '\r\n' to terminate
-  lines, the log ends up having '\r\r\n' at the end of each line. This
-  filter replaces the above with a single '\n' in the data stream.
-  """
-  def __init__(self, output):
-    self._output = output
-
-  def write(self, data):
-    data = data.replace('\r\r\n', '\n')
-    self._output.write(data)
-
-  def flush(self):
-    self._output.flush()
diff --git a/build/android/pylib/android_commands_unittest.py b/build/android/pylib/android_commands_unittest.py
deleted file mode 100644
index 21c34f9..0000000
--- a/build/android/pylib/android_commands_unittest.py
+++ /dev/null
@@ -1,191 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import os
-import shutil
-import sys
-import unittest
-
-sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir))
-
-from pylib import android_commands
-
-# pylint: disable=W0212,W0702
-
-class TestDeviceTempFile(unittest.TestCase):
-  def setUp(self):
-    if not os.getenv('BUILDTYPE'):
-      os.environ['BUILDTYPE'] = 'Debug'
-
-    devices = android_commands.GetAttachedDevices()
-    self.assertGreater(len(devices), 0, 'No device attached!')
-    self.ac = android_commands.AndroidCommands(device=devices[0])
-
-  def testTempFileDeleted(self):
-    """Tests that DeviceTempFile deletes files when closed."""
-    temp_file = android_commands.DeviceTempFile(self.ac)
-    self.assertFalse(self.ac.FileExistsOnDevice(temp_file.name))
-    self.ac.SetFileContents(temp_file.name, "contents")
-    self.assertTrue(self.ac.FileExistsOnDevice(temp_file.name))
-    temp_file.close()
-    self.assertFalse(self.ac.FileExistsOnDevice(temp_file.name))
-
-    with android_commands.DeviceTempFile(self.ac) as with_temp_file:
-      self.assertFalse(self.ac.FileExistsOnDevice(with_temp_file.name))
-      self.ac.SetFileContents(with_temp_file.name, "contents")
-      self.assertTrue(self.ac.FileExistsOnDevice(with_temp_file.name))
-
-    self.assertFalse(self.ac.FileExistsOnDevice(with_temp_file.name))
-
-  def testTempFileNotWritten(self):
-    """Tests that device temp files work successfully even if not written to."""
-    temp_file = android_commands.DeviceTempFile(self.ac)
-    temp_file.close()
-    self.assertFalse(self.ac.FileExistsOnDevice(temp_file.name))
-
-    with android_commands.DeviceTempFile(self.ac) as with_temp_file:
-      pass
-    self.assertFalse(self.ac.FileExistsOnDevice(with_temp_file.name))
-
-  def testNaming(self):
-    """Tests that returned filenames are as requested."""
-    temp_file = android_commands.DeviceTempFile(self.ac, prefix="cat")
-    self.assertTrue(os.path.basename(temp_file.name).startswith("cat"))
-
-    temp_file = android_commands.DeviceTempFile(self.ac, suffix="dog")
-    self.assertTrue(temp_file.name.endswith("dog"))
-
-    temp_file = android_commands.DeviceTempFile(
-        self.ac, prefix="cat", suffix="dog")
-    self.assertTrue(os.path.basename(temp_file.name).startswith("cat"))
-    self.assertTrue(temp_file.name.endswith("dog"))
-
-
-class TestGetFilesChanged(unittest.TestCase):
-
-  def setUp(self):
-    if not os.getenv('BUILDTYPE'):
-      os.environ['BUILDTYPE'] = 'Debug'
-
-    devices = android_commands.GetAttachedDevices()
-    self.assertGreater(len(devices), 0, 'No device attached!')
-    self.ac = android_commands.AndroidCommands(device=devices[0])
-    self.host_data_dir = os.path.realpath('test_push_data')
-    self.device_data_dir = '%s/test_push_data' % (
-        self.ac.RunShellCommand('realpath %s' %
-            self.ac.GetExternalStorage())[0])
-
-    os.mkdir(self.host_data_dir)
-    for i in xrange(1, 10):
-      with open('%s/%d.txt' % (self.host_data_dir, i), 'w') as f:
-        f.write('file #%d' % i)
-
-    self.ac.RunShellCommand('mkdir %s' % self.device_data_dir)
-
-  def testGetFilesChangedAllNeeded(self):
-    """ Tests GetFilesChanged when none of the files are on the device.
-    """
-    expected = [('%s/%d.txt' % (self.host_data_dir, i),
-                 '%s/%d.txt' % (self.device_data_dir, i))
-                for i in xrange(1, 10)]
-    actual = self.ac.GetFilesChanged(self.host_data_dir, self.device_data_dir)
-    self.assertSequenceEqual(expected, actual)
-
-  def testGetFilesChangedSomeIdentical(self):
-    """ Tests GetFilesChanged when some of the files are on the device.
-    """
-    for i in xrange(1, 5):
-      self.ac._adb.Push('%s/%d.txt' % (self.host_data_dir, i),
-                        self.device_data_dir)
-    expected = [('%s/%d.txt' % (self.host_data_dir, i),
-                 '%s/%d.txt' % (self.device_data_dir, i))
-                for i in xrange(5, 10)]
-    actual = self.ac.GetFilesChanged(self.host_data_dir, self.device_data_dir)
-    self.assertSequenceEqual(expected, actual)
-
-  def testGetFilesChangedAllIdentical(self):
-    """ Tests GetFilesChanged when all of the files are on the device.
-    """
-    for i in xrange(1, 10):
-      self.ac._adb.Push('%s/%d.txt' % (self.host_data_dir, i),
-                        self.device_data_dir)
-    expected = []
-    actual = self.ac.GetFilesChanged(self.host_data_dir, self.device_data_dir)
-    self.assertSequenceEqual(expected, actual)
-
-  def testGetFilesChangedRename(self):
-    """ Tests GetFilesChanged when one of the files has been renamed.
-
-        This tests both with and without the ignore_filenames flag set.
-    """
-    for i in xrange(5, 10):
-      self.ac._adb.Push('%s/%d.txt' % (self.host_data_dir, i),
-                        self.device_data_dir)
-    os.rename('%s/5.txt' % (self.host_data_dir),
-              '%s/99.txt' % (self.host_data_dir))
-
-    expected = [('%s/%d.txt' % (self.host_data_dir, i),
-                 '%s/%d.txt' % (self.device_data_dir, i))
-                for i in xrange(1, 5)]
-    actual = self.ac.GetFilesChanged(self.host_data_dir, self.device_data_dir,
-                                     ignore_filenames=True)
-    self.assertSequenceEqual(expected, actual)
-
-    expected.append(('%s/99.txt' % self.host_data_dir,
-                     '%s/99.txt' % self.device_data_dir))
-    actual = self.ac.GetFilesChanged(self.host_data_dir, self.device_data_dir)
-    self.assertSequenceEqual(expected, actual)
-
-  def testGetFilesChangedCopy(self):
-    """ Tests GetFilesChanged when one of the files has been copied.
-
-        This tests both with and without the ignore_filenames flag set.
-    """
-    for i in xrange(5, 10):
-      self.ac._adb.Push('%s/%d.txt' % (self.host_data_dir, i),
-                        self.device_data_dir)
-    shutil.copy('%s/5.txt' % self.host_data_dir,
-                '%s/99.txt' % self.host_data_dir)
-
-    expected = [('%s/%d.txt' % (self.host_data_dir, i),
-                 '%s/%d.txt' % (self.device_data_dir, i))
-                for i in xrange(1, 5)]
-    actual = self.ac.GetFilesChanged(self.host_data_dir, self.device_data_dir,
-                                     ignore_filenames=True)
-    self.assertSequenceEqual(expected, actual)
-
-    expected.append(('%s/99.txt' % self.host_data_dir,
-                     '%s/99.txt' % self.device_data_dir))
-    actual = self.ac.GetFilesChanged(self.host_data_dir, self.device_data_dir)
-    self.assertSequenceEqual(expected, actual)
-
-  def testGetFilesChangedIndividual(self):
-    """ Tests GetFilesChanged when provided one file.
-    """
-    expected = [('%s/1.txt' % self.host_data_dir,
-                 '%s/1.txt' % self.device_data_dir)]
-    actual = self.ac.GetFilesChanged('%s/1.txt' % self.host_data_dir,
-                                     '%s/1.txt' % self.device_data_dir)
-    self.assertSequenceEqual(expected, actual)
-
-  def testGetFilesChangedFileToDirectory(self):
-    """ Tests GetFilesChanged when provided a file from the host and a
-        directory on the device.
-    """
-    expected = [('%s/1.txt' % self.host_data_dir,
-                 '%s' % self.device_data_dir)]
-    actual = self.ac.GetFilesChanged('%s/1.txt' % self.host_data_dir,
-                                     '%s' % self.device_data_dir)
-    self.assertSequenceEqual(expected, actual)
-
-  def tearDown(self):
-    try:
-      shutil.rmtree(self.host_data_dir)
-      self.ac.RunShellCommand('rm -rf %s' % self.device_data_dir)
-    except:
-      pass
-
-if __name__ == '__main__':
-  unittest.main()
-
diff --git a/build/android/pylib/system_properties.py b/build/android/pylib/system_properties.py
deleted file mode 100644
index 3f16f86..0000000
--- a/build/android/pylib/system_properties.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright 2013 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-
-class SystemProperties(dict):
-
-  """A dict interface to interact with device system properties.
-
-  System properties are key/value pairs as exposed by adb shell getprop/setprop.
-
-  This implementation minimizes interaction with the physical device. It is
-  valid for the lifetime of a boot.
-  """
-
-  def __init__(self, android_commands):
-    super(SystemProperties, self).__init__()
-    self._adb = android_commands
-    self._cached_static_properties = {}
-
-  def __getitem__(self, key):
-    if self._IsStatic(key):
-      if key not in self._cached_static_properties:
-        self._cached_static_properties[key] = self._GetProperty(key)
-      return self._cached_static_properties[key]
-    return self._GetProperty(key)
-
-  def __setitem__(self, key, value):
-    # TODO(tonyg): This can fail with no root. Verify that it succeeds.
-    self._adb.SendShellCommand('setprop %s "%s"' % (key, value), retry_count=3)
-
-  @staticmethod
-  def _IsStatic(key):
-    # TODO(tonyg): This list is conservative and could be expanded as needed.
-    return (key.startswith('ro.boot.') or
-            key.startswith('ro.build.') or
-            key.startswith('ro.product.'))
-
-  def _GetProperty(self, key):
-    return self._adb.SendShellCommand('getprop %s' % key, retry_count=3).strip()
diff --git a/build/android/pylib/valgrind_tools.py b/build/android/pylib/valgrind_tools.py
index 99719d0..8e34adf 100644
--- a/build/android/pylib/valgrind_tools.py
+++ b/build/android/pylib/valgrind_tools.py
@@ -103,10 +103,6 @@
   def __init__(self, device):
     super(AddressSanitizerTool, self).__init__()
     self._device = device
-    # Configure AndroidCommands to run utils (such as md5sum_bin) under ASan.
-    # This is required because ASan is a compiler-based tool, and md5sum
-    # includes instrumented code from base.
-    device.old_interface.SetUtilWrapper(self.GetUtilWrapper())
 
   @classmethod
   def CopyFiles(cls, device):