| Index: build/android/pylib/device/adb_wrapper.py
|
| diff --git a/build/android/pylib/device/adb_wrapper.py b/build/android/pylib/device/adb_wrapper.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..aa21ce9425f3b2be5c8a07d4bd4dc33fba751b5e
|
| --- /dev/null
|
| +++ b/build/android/pylib/device/adb_wrapper.py
|
| @@ -0,0 +1,423 @@
|
| +# Copyright (c) 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.
|
| +
|
| +"""This module wraps Android's adb tool.
|
| +
|
| +This is a thin wrapper around the adb interface. Any additional complexity
|
| +should be delegated to a higher level (ex. DeviceUtils).
|
| +"""
|
| +
|
| +import errno
|
| +import logging
|
| +import os
|
| +
|
| +from pylib import cmd_helper
|
| +
|
| +from pylib.utils import reraiser_thread
|
| +from pylib.utils import timeout_retry
|
| +
|
| +_DEFAULT_TIMEOUT = 30
|
| +_DEFAULT_RETRIES = 2
|
| +
|
| +
|
| +class BaseError(Exception):
|
| + """Base exception for all device and command errors."""
|
| + pass
|
| +
|
| +
|
| +class CommandFailedError(BaseError):
|
| + """Exception for command failures."""
|
| +
|
| + def __init__(self, cmd, msg, device=None):
|
| + super(CommandFailedError, self).__init__(
|
| + (('device %s: ' % device) if device else '') +
|
| + 'adb command \'%s\' failed with message: \'%s\'' % (' '.join(cmd), msg))
|
| +
|
| +
|
| +class CommandTimeoutError(BaseError):
|
| + """Exception for command timeouts."""
|
| + pass
|
| +
|
| +
|
| +def _VerifyLocalFileExists(path):
|
| + """Verifies a local file exists.
|
| +
|
| + Args:
|
| + path: Path to the local file.
|
| +
|
| + Raises:
|
| + IOError: If the file doesn't exist.
|
| + """
|
| + if not os.path.exists(path):
|
| + raise IOError(errno.ENOENT, os.strerror(errno.ENOENT), path)
|
| +
|
| +
|
| +class AdbWrapper(object):
|
| + """A wrapper around a local Android Debug Bridge executable."""
|
| +
|
| + def __init__(self, device_serial):
|
| + """Initializes the AdbWrapper.
|
| +
|
| + Args:
|
| + device_serial: The device serial number as a string.
|
| + """
|
| + self._device_serial = str(device_serial)
|
| +
|
| + @classmethod
|
| + def _AdbCmd(cls, arg_list, timeout, retries, check_error=True):
|
| + """Runs an adb command with a timeout and retries.
|
| +
|
| + Args:
|
| + arg_list: A list of arguments to adb.
|
| + timeout: Timeout in seconds.
|
| + retries: Number of retries.
|
| + check_error: Check that the command doesn't return an error message. This
|
| + does NOT check the return code of shell commands.
|
| +
|
| + Returns:
|
| + The output of the command.
|
| + """
|
| + cmd = ['adb'] + arg_list
|
| +
|
| + # This method runs inside the timeout/retries.
|
| + def RunCmd():
|
| + exit_code, output = cmd_helper.GetCmdStatusAndOutput(cmd)
|
| + if exit_code != 0:
|
| + raise CommandFailedError(
|
| + cmd, 'returned non-zero exit code %s, output: %s' %
|
| + (exit_code, output))
|
| + # This catches some errors, including when the device drops offline;
|
| + # unfortunately adb is very inconsistent with error reporting so many
|
| + # command failures present differently.
|
| + if check_error and output[:len('error:')] == 'error:':
|
| + raise CommandFailedError(arg_list, output)
|
| + return output
|
| +
|
| + try:
|
| + return timeout_retry.Run(RunCmd, timeout, retries)
|
| + except reraiser_thread.TimeoutError as e:
|
| + raise CommandTimeoutError(str(e))
|
| +
|
| + def _DeviceAdbCmd(self, arg_list, timeout, retries, check_error=True):
|
| + """Runs an adb command on the device associated with this object.
|
| +
|
| + Args:
|
| + arg_list: A list of arguments to adb.
|
| + timeout: Timeout in seconds.
|
| + retries: Number of retries.
|
| + check_error: Check that the command doesn't return an error message. This
|
| + does NOT check the return code of shell commands.
|
| +
|
| + Returns:
|
| + The output of the command.
|
| + """
|
| + return self._AdbCmd(
|
| + ['-s', self._device_serial] + arg_list, timeout, retries,
|
| + check_error=check_error)
|
| +
|
| + def __eq__(self, other):
|
| + """Consider instances equal if they refer to the same device.
|
| +
|
| + Args:
|
| + other: The instance to compare equality with.
|
| +
|
| + Returns:
|
| + True if the instances are considered equal, false otherwise.
|
| + """
|
| + return self._device_serial == str(other)
|
| +
|
| + def __str__(self):
|
| + """The string representation of an instance.
|
| +
|
| + Returns:
|
| + The device serial number as a string.
|
| + """
|
| + return self._device_serial
|
| +
|
| + def __repr__(self):
|
| + return '%s(\'%s\')' % (self.__class__.__name__, self)
|
| +
|
| + # TODO(craigdh): Determine the filter criteria that should be supported.
|
| + @classmethod
|
| + def GetDevices(cls, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
|
| + """Get the list of active attached devices.
|
| +
|
| + Args:
|
| + timeout: (optional) Timeout per try in seconds.
|
| + retries: (optional) Number of retries to attempt.
|
| +
|
| + Yields:
|
| + AdbWrapper instances.
|
| + """
|
| + output = cls._AdbCmd(['devices'], timeout, retries)
|
| + lines = [line.split() for line in output.split('\n')]
|
| + return [AdbWrapper(line[0]) for line in lines
|
| + if len(line) == 2 and line[1] == 'device']
|
| +
|
| + def GetDeviceSerial(self):
|
| + """Gets the device serial number associated with this object.
|
| +
|
| + Returns:
|
| + Device serial number as a string.
|
| + """
|
| + return self._device_serial
|
| +
|
| + def Push(self, local, remote, timeout=60*5, retries=_DEFAULT_RETRIES):
|
| + """Pushes a file from the host to the device.
|
| +
|
| + Args:
|
| + local: Path on the host filesystem.
|
| + remote: Path on the device filesystem.
|
| + timeout: (optional) Timeout per try in seconds.
|
| + retries: (optional) Number of retries to attempt.
|
| + """
|
| + _VerifyLocalFileExists(local)
|
| + self._DeviceAdbCmd(['push', local, remote], timeout, retries)
|
| +
|
| + def Pull(self, remote, local, timeout=60*5, retries=_DEFAULT_RETRIES):
|
| + """Pulls a file from the device to the host.
|
| +
|
| + Args:
|
| + remote: Path on the device filesystem.
|
| + local: Path on the host filesystem.
|
| + timeout: (optional) Timeout per try in seconds.
|
| + retries: (optional) Number of retries to attempt.
|
| + """
|
| + self._DeviceAdbCmd(['pull', remote, local], timeout, retries)
|
| + _VerifyLocalFileExists(local)
|
| +
|
| + def Shell(self, command, expect_rc=None, timeout=_DEFAULT_TIMEOUT,
|
| + retries=_DEFAULT_RETRIES):
|
| + """Runs a shell command on the device.
|
| +
|
| + Args:
|
| + command: The shell command to run.
|
| + expect_rc: (optional) If set checks that the command's return code matches
|
| + this value.
|
| + timeout: (optional) Timeout per try in seconds.
|
| + retries: (optional) Number of retries to attempt.
|
| +
|
| + Returns:
|
| + The output of the shell command as a string.
|
| +
|
| + Raises:
|
| + CommandFailedError: If the return code doesn't match |expect_rc|.
|
| + """
|
| + if expect_rc is None:
|
| + actual_command = command
|
| + else:
|
| + actual_command = '%s; echo $?;' % command
|
| + output = self._DeviceAdbCmd(
|
| + ['shell', actual_command], timeout, retries, check_error=False)
|
| + if expect_rc is not None:
|
| + output_end = output.rstrip().rfind('\n') + 1
|
| + rc = output[output_end:].strip()
|
| + output = output[:output_end]
|
| + if int(rc) != expect_rc:
|
| + raise CommandFailedError(
|
| + ['shell', command],
|
| + 'shell command exited with code: %s' % rc,
|
| + self._device_serial)
|
| + return output
|
| +
|
| + def Logcat(self, filter_spec=None, timeout=_DEFAULT_TIMEOUT,
|
| + retries=_DEFAULT_RETRIES):
|
| + """Get the logcat output.
|
| +
|
| + Args:
|
| + filter_spec: (optional) Spec to filter the logcat.
|
| + timeout: (optional) Timeout per try in seconds.
|
| + retries: (optional) Number of retries to attempt.
|
| +
|
| + Returns:
|
| + logcat output as a string.
|
| + """
|
| + cmd = ['logcat']
|
| + if filter_spec is not None:
|
| + cmd.append(filter_spec)
|
| + return self._DeviceAdbCmd(cmd, timeout, retries, check_error=False)
|
| +
|
| + def Forward(self, local, remote, timeout=_DEFAULT_TIMEOUT,
|
| + retries=_DEFAULT_RETRIES):
|
| + """Forward socket connections from the local socket to the remote socket.
|
| +
|
| + Sockets are specified by one of:
|
| + tcp:<port>
|
| + localabstract:<unix domain socket name>
|
| + localreserved:<unix domain socket name>
|
| + localfilesystem:<unix domain socket name>
|
| + dev:<character device name>
|
| + jdwp:<process pid> (remote only)
|
| +
|
| + Args:
|
| + local: The host socket.
|
| + remote: The device socket.
|
| + timeout: (optional) Timeout per try in seconds.
|
| + retries: (optional) Number of retries to attempt.
|
| + """
|
| + self._DeviceAdbCmd(['forward', str(local), str(remote)], timeout, retries)
|
| +
|
| + def JDWP(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
|
| + """List of PIDs of processes hosting a JDWP transport.
|
| +
|
| + Args:
|
| + timeout: (optional) Timeout per try in seconds.
|
| + retries: (optional) Number of retries to attempt.
|
| +
|
| + Returns:
|
| + A list of PIDs as strings.
|
| + """
|
| + return [a.strip() for a in
|
| + self._DeviceAdbCmd(['jdwp'], timeout, retries).split('\n')]
|
| +
|
| + def Install(self, apk_path, forward_lock=False, reinstall=False,
|
| + sd_card=False, timeout=60*2, retries=_DEFAULT_RETRIES):
|
| + """Install an apk on the device.
|
| +
|
| + Args:
|
| + apk_path: Host path to the APK file.
|
| + forward_lock: (optional) If set forward-locks the app.
|
| + reinstall: (optional) If set reinstalls the app, keeping its data.
|
| + sd_card: (optional) If set installs on the SD card.
|
| + timeout: (optional) Timeout per try in seconds.
|
| + retries: (optional) Number of retries to attempt.
|
| + """
|
| + _VerifyLocalFileExists(apk_path)
|
| + cmd = ['install']
|
| + if forward_lock:
|
| + cmd.append('-l')
|
| + if reinstall:
|
| + cmd.append('-r')
|
| + if sd_card:
|
| + cmd.append('-s')
|
| + cmd.append(apk_path)
|
| + output = self._DeviceAdbCmd(cmd, timeout, retries)
|
| + if 'Success' not in output:
|
| + raise CommandFailedError(cmd, output)
|
| +
|
| + def Uninstall(self, package, keep_data=False, timeout=_DEFAULT_TIMEOUT,
|
| + retries=_DEFAULT_RETRIES):
|
| + """Remove the app |package| from the device.
|
| +
|
| + Args:
|
| + package: The package to uninstall.
|
| + keep_data: (optional) If set keep the data and cache directories.
|
| + timeout: (optional) Timeout per try in seconds.
|
| + retries: (optional) Number of retries to attempt.
|
| + """
|
| + cmd = ['uninstall']
|
| + if keep_data:
|
| + cmd.append('-k')
|
| + cmd.append(package)
|
| + output = self._DeviceAdbCmd(cmd, timeout, retries)
|
| + if 'Failure' in output:
|
| + raise CommandFailedError(cmd, output)
|
| +
|
| + def Backup(self, path, packages=None, apk=False, shared=False,
|
| + nosystem=True, include_all=False, timeout=_DEFAULT_TIMEOUT,
|
| + retries=_DEFAULT_RETRIES):
|
| + """Write an archive of the device's data to |path|.
|
| +
|
| + Args:
|
| + path: Local path to store the backup file.
|
| + packages: List of to packages to be backed up.
|
| + apk: (optional) If set include the .apk files in the archive.
|
| + shared: (optional) If set buckup the device's SD card.
|
| + nosystem: (optional) If set exclude system applications.
|
| + include_all: (optional) If set back up all installed applications and
|
| + |packages| is optional.
|
| + timeout: (optional) Timeout per try in seconds.
|
| + retries: (optional) Number of retries to attempt.
|
| + """
|
| + cmd = ['backup', path]
|
| + if apk:
|
| + cmd.append('-apk')
|
| + if shared:
|
| + cmd.append('-shared')
|
| + if nosystem:
|
| + cmd.append('-nosystem')
|
| + if include_all:
|
| + cmd.append('-all')
|
| + if packages:
|
| + cmd.extend(packages)
|
| + assert bool(packages) ^ bool(include_all), (
|
| + 'Provide \'packages\' or set \'include_all\' but not both.')
|
| + ret = self._DeviceAdbCmd(cmd, timeout, retries)
|
| + _VerifyLocalFileExists(path)
|
| + return ret
|
| +
|
| + def Restore(self, path, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
|
| + """Restore device contents from the backup archive.
|
| +
|
| + Args:
|
| + path: Host path to the backup archive.
|
| + timeout: (optional) Timeout per try in seconds.
|
| + retries: (optional) Number of retries to attempt.
|
| + """
|
| + _VerifyLocalFileExists(path)
|
| + self._DeviceAdbCmd(['restore'] + [path], timeout, retries)
|
| +
|
| + def WaitForDevice(self, timeout=60*5, retries=_DEFAULT_RETRIES):
|
| + """Block until the device is online.
|
| +
|
| + Args:
|
| + timeout: (optional) Timeout per try in seconds.
|
| + retries: (optional) Number of retries to attempt.
|
| + """
|
| + self._DeviceAdbCmd(['wait-for-device'], timeout, retries)
|
| +
|
| + def GetState(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
|
| + """Get device state.
|
| +
|
| + Args:
|
| + timeout: (optional) Timeout per try in seconds.
|
| + retries: (optional) Number of retries to attempt.
|
| +
|
| + Returns:
|
| + One of 'offline', 'bootloader', or 'device'.
|
| + """
|
| + return self._DeviceAdbCmd(['get-state'], timeout, retries).strip()
|
| +
|
| + def GetDevPath(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
|
| + """Gets the device path.
|
| +
|
| + Args:
|
| + timeout: (optional) Timeout per try in seconds.
|
| + retries: (optional) Number of retries to attempt.
|
| +
|
| + Returns:
|
| + The device path (e.g. usb:3-4)
|
| + """
|
| + return self._DeviceAdbCmd(['get-devpath'], timeout, retries)
|
| +
|
| + def Remount(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
|
| + """Remounts the /system partition on the device read-write."""
|
| + self._DeviceAdbCmd(['remount'], timeout, retries)
|
| +
|
| + def Reboot(self, to_bootloader=False, timeout=60*5,
|
| + retries=_DEFAULT_RETRIES):
|
| + """Reboots the device.
|
| +
|
| + Args:
|
| + to_bootloader: (optional) If set reboots to the bootloader.
|
| + timeout: (optional) Timeout per try in seconds.
|
| + retries: (optional) Number of retries to attempt.
|
| + """
|
| + if to_bootloader:
|
| + cmd = ['reboot-bootloader']
|
| + else:
|
| + cmd = ['reboot']
|
| + self._DeviceAdbCmd(cmd, timeout, retries)
|
| +
|
| + def Root(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
|
| + """Restarts the adbd daemon with root permissions, if possible.
|
| +
|
| + Args:
|
| + timeout: (optional) Timeout per try in seconds.
|
| + retries: (optional) Number of retries to attempt.
|
| + """
|
| + output = self._DeviceAdbCmd(['root'], timeout, retries)
|
| + if 'cannot' in output:
|
| + raise CommandFailedError(['root'], output)
|
| +
|
|
|