Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(468)

Unified Diff: build/android/pylib/device/adb_wrapper.py

Issue 64553012: [android] Adds AdbWrapper which is the base of the eventual AndroidCommands replacement. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: addressed comments Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « build/android/pylib/device/__init__.py ('k') | build/android/pylib/device/adb_wrapper_test.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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)
+
« no previous file with comments | « build/android/pylib/device/__init__.py ('k') | build/android/pylib/device/adb_wrapper_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698