Home | History | Annotate | Download | only in device
      1 # Copyright 2013 The Chromium Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 """This module wraps Android's adb tool.
      6 
      7 This is a thin wrapper around the adb interface. Any additional complexity
      8 should be delegated to a higher level (ex. DeviceUtils).
      9 """
     10 
     11 import errno
     12 import os
     13 
     14 from pylib import cmd_helper
     15 from pylib.device import decorators
     16 from pylib.device import device_errors
     17 
     18 
     19 _DEFAULT_TIMEOUT = 30
     20 _DEFAULT_RETRIES = 2
     21 
     22 
     23 def _VerifyLocalFileExists(path):
     24   """Verifies a local file exists.
     25 
     26   Args:
     27     path: Path to the local file.
     28 
     29   Raises:
     30     IOError: If the file doesn't exist.
     31   """
     32   if not os.path.exists(path):
     33     raise IOError(errno.ENOENT, os.strerror(errno.ENOENT), path)
     34 
     35 
     36 class AdbWrapper(object):
     37   """A wrapper around a local Android Debug Bridge executable."""
     38 
     39   def __init__(self, device_serial):
     40     """Initializes the AdbWrapper.
     41 
     42     Args:
     43       device_serial: The device serial number as a string.
     44     """
     45     self._device_serial = str(device_serial)
     46 
     47   # pylint: disable=W0613
     48   @classmethod
     49   @decorators.WithTimeoutAndRetries
     50   def _RunAdbCmd(cls, arg_list, timeout=None, retries=None, check_error=True):
     51     cmd = ['adb'] + arg_list
     52     exit_code, output = cmd_helper.GetCmdStatusAndOutput(cmd)
     53     if exit_code != 0:
     54       raise device_errors.AdbCommandFailedError(
     55           cmd, 'returned non-zero exit code %s, output: %s' %
     56           (exit_code, output))
     57     # This catches some errors, including when the device drops offline;
     58     # unfortunately adb is very inconsistent with error reporting so many
     59     # command failures present differently.
     60     if check_error and output[:len('error:')] == 'error:':
     61       raise device_errors.AdbCommandFailedError(arg_list, output)
     62     return output
     63   # pylint: enable=W0613
     64 
     65   def _DeviceAdbCmd(self, arg_list, timeout, retries, check_error=True):
     66     """Runs an adb command on the device associated with this object.
     67 
     68     Args:
     69       arg_list: A list of arguments to adb.
     70       timeout: Timeout in seconds.
     71       retries: Number of retries.
     72       check_error: Check that the command doesn't return an error message. This
     73         does NOT check the return code of shell commands.
     74 
     75     Returns:
     76       The output of the command.
     77     """
     78     return self._RunAdbCmd(
     79         ['-s', self._device_serial] + arg_list, timeout=timeout,
     80         retries=retries, check_error=check_error)
     81 
     82   def __eq__(self, other):
     83     """Consider instances equal if they refer to the same device.
     84 
     85     Args:
     86       other: The instance to compare equality with.
     87 
     88     Returns:
     89       True if the instances are considered equal, false otherwise.
     90     """
     91     return self._device_serial == str(other)
     92 
     93   def __str__(self):
     94     """The string representation of an instance.
     95 
     96     Returns:
     97       The device serial number as a string.
     98     """
     99     return self._device_serial
    100 
    101   def __repr__(self):
    102     return '%s(\'%s\')' % (self.__class__.__name__, self)
    103 
    104   # TODO(craigdh): Determine the filter criteria that should be supported.
    105   @classmethod
    106   def GetDevices(cls, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
    107     """Get the list of active attached devices.
    108 
    109     Args:
    110       timeout: (optional) Timeout per try in seconds.
    111       retries: (optional) Number of retries to attempt.
    112 
    113     Yields:
    114       AdbWrapper instances.
    115     """
    116     output = cls._RunAdbCmd(['devices'], timeout=timeout, retries=retries)
    117     lines = [line.split() for line in output.split('\n')]
    118     return [AdbWrapper(line[0]) for line in lines
    119             if len(line) == 2 and line[1] == 'device']
    120 
    121   def GetDeviceSerial(self):
    122     """Gets the device serial number associated with this object.
    123 
    124     Returns:
    125       Device serial number as a string.
    126     """
    127     return self._device_serial
    128 
    129   def Push(self, local, remote, timeout=60*5, retries=_DEFAULT_RETRIES):
    130     """Pushes a file from the host to the device.
    131 
    132     Args:
    133       local: Path on the host filesystem.
    134       remote: Path on the device filesystem.
    135       timeout: (optional) Timeout per try in seconds.
    136       retries: (optional) Number of retries to attempt.
    137     """
    138     _VerifyLocalFileExists(local)
    139     self._DeviceAdbCmd(['push', local, remote], timeout, retries)
    140 
    141   def Pull(self, remote, local, timeout=60*5, retries=_DEFAULT_RETRIES):
    142     """Pulls a file from the device to the host.
    143 
    144     Args:
    145       remote: Path on the device filesystem.
    146       local: Path on the host filesystem.
    147       timeout: (optional) Timeout per try in seconds.
    148       retries: (optional) Number of retries to attempt.
    149     """
    150     self._DeviceAdbCmd(['pull', remote, local], timeout, retries)
    151     _VerifyLocalFileExists(local)
    152 
    153   def Shell(self, command, expect_rc=None, timeout=_DEFAULT_TIMEOUT,
    154             retries=_DEFAULT_RETRIES):
    155     """Runs a shell command on the device.
    156 
    157     Args:
    158       command: The shell command to run.
    159       expect_rc: (optional) If set checks that the command's return code matches
    160         this value.
    161       timeout: (optional) Timeout per try in seconds.
    162       retries: (optional) Number of retries to attempt.
    163 
    164     Returns:
    165       The output of the shell command as a string.
    166 
    167     Raises:
    168       device_errors.AdbCommandFailedError: If the return code doesn't match
    169         |expect_rc|.
    170     """
    171     if expect_rc is None:
    172       actual_command = command
    173     else:
    174       actual_command = '%s; echo $?;' % command
    175     output = self._DeviceAdbCmd(
    176         ['shell', actual_command], timeout, retries, check_error=False)
    177     if expect_rc is not None:
    178       output_end = output.rstrip().rfind('\n') + 1
    179       rc = output[output_end:].strip()
    180       output = output[:output_end]
    181       if int(rc) != expect_rc:
    182         raise device_errors.AdbCommandFailedError(
    183             ['shell', command],
    184             'shell command exited with code: %s' % rc,
    185             self._device_serial)
    186     return output
    187 
    188   def Logcat(self, filter_spec=None, timeout=_DEFAULT_TIMEOUT,
    189              retries=_DEFAULT_RETRIES):
    190     """Get the logcat output.
    191 
    192     Args:
    193       filter_spec: (optional) Spec to filter the logcat.
    194       timeout: (optional) Timeout per try in seconds.
    195       retries: (optional) Number of retries to attempt.
    196 
    197     Returns:
    198       logcat output as a string.
    199     """
    200     cmd = ['logcat']
    201     if filter_spec is not None:
    202       cmd.append(filter_spec)
    203     return self._DeviceAdbCmd(cmd, timeout, retries, check_error=False)
    204 
    205   def Forward(self, local, remote, timeout=_DEFAULT_TIMEOUT,
    206               retries=_DEFAULT_RETRIES):
    207     """Forward socket connections from the local socket to the remote socket.
    208 
    209     Sockets are specified by one of:
    210       tcp:<port>
    211       localabstract:<unix domain socket name>
    212       localreserved:<unix domain socket name>
    213       localfilesystem:<unix domain socket name>
    214       dev:<character device name>
    215       jdwp:<process pid> (remote only)
    216 
    217     Args:
    218       local: The host socket.
    219       remote: The device socket.
    220       timeout: (optional) Timeout per try in seconds.
    221       retries: (optional) Number of retries to attempt.
    222     """
    223     self._DeviceAdbCmd(['forward', str(local), str(remote)], timeout, retries)
    224 
    225   def JDWP(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
    226     """List of PIDs of processes hosting a JDWP transport.
    227 
    228     Args:
    229       timeout: (optional) Timeout per try in seconds.
    230       retries: (optional) Number of retries to attempt.
    231 
    232     Returns:
    233       A list of PIDs as strings.
    234     """
    235     return [a.strip() for a in
    236             self._DeviceAdbCmd(['jdwp'], timeout, retries).split('\n')]
    237 
    238   def Install(self, apk_path, forward_lock=False, reinstall=False,
    239               sd_card=False, timeout=60*2, retries=_DEFAULT_RETRIES):
    240     """Install an apk on the device.
    241 
    242     Args:
    243       apk_path: Host path to the APK file.
    244       forward_lock: (optional) If set forward-locks the app.
    245       reinstall: (optional) If set reinstalls the app, keeping its data.
    246       sd_card: (optional) If set installs on the SD card.
    247       timeout: (optional) Timeout per try in seconds.
    248       retries: (optional) Number of retries to attempt.
    249     """
    250     _VerifyLocalFileExists(apk_path)
    251     cmd = ['install']
    252     if forward_lock:
    253       cmd.append('-l')
    254     if reinstall:
    255       cmd.append('-r')
    256     if sd_card:
    257       cmd.append('-s')
    258     cmd.append(apk_path)
    259     output = self._DeviceAdbCmd(cmd, timeout, retries)
    260     if 'Success' not in output:
    261       raise device_errors.AdbCommandFailedError(cmd, output)
    262 
    263   def Uninstall(self, package, keep_data=False, timeout=_DEFAULT_TIMEOUT,
    264                 retries=_DEFAULT_RETRIES):
    265     """Remove the app |package| from the device.
    266 
    267     Args:
    268       package: The package to uninstall.
    269       keep_data: (optional) If set keep the data and cache directories.
    270       timeout: (optional) Timeout per try in seconds.
    271       retries: (optional) Number of retries to attempt.
    272     """
    273     cmd = ['uninstall']
    274     if keep_data:
    275       cmd.append('-k')
    276     cmd.append(package)
    277     output = self._DeviceAdbCmd(cmd, timeout, retries)
    278     if 'Failure' in output:
    279       raise device_errors.AdbCommandFailedError(cmd, output)
    280 
    281   def Backup(self, path, packages=None, apk=False, shared=False,
    282              nosystem=True, include_all=False, timeout=_DEFAULT_TIMEOUT,
    283              retries=_DEFAULT_RETRIES):
    284     """Write an archive of the device's data to |path|.
    285 
    286     Args:
    287       path: Local path to store the backup file.
    288       packages: List of to packages to be backed up.
    289       apk: (optional) If set include the .apk files in the archive.
    290       shared: (optional) If set buckup the device's SD card.
    291       nosystem: (optional) If set exclude system applications.
    292       include_all: (optional) If set back up all installed applications and
    293         |packages| is optional.
    294       timeout: (optional) Timeout per try in seconds.
    295       retries: (optional) Number of retries to attempt.
    296     """
    297     cmd = ['backup', path]
    298     if apk:
    299       cmd.append('-apk')
    300     if shared:
    301       cmd.append('-shared')
    302     if nosystem:
    303       cmd.append('-nosystem')
    304     if include_all:
    305       cmd.append('-all')
    306     if packages:
    307       cmd.extend(packages)
    308     assert bool(packages) ^ bool(include_all), (
    309         'Provide \'packages\' or set \'include_all\' but not both.')
    310     ret = self._DeviceAdbCmd(cmd, timeout, retries)
    311     _VerifyLocalFileExists(path)
    312     return ret
    313 
    314   def Restore(self, path, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
    315     """Restore device contents from the backup archive.
    316 
    317     Args:
    318       path: Host path to the backup archive.
    319       timeout: (optional) Timeout per try in seconds.
    320       retries: (optional) Number of retries to attempt.
    321     """
    322     _VerifyLocalFileExists(path)
    323     self._DeviceAdbCmd(['restore'] + [path], timeout, retries)
    324 
    325   def WaitForDevice(self, timeout=60*5, retries=_DEFAULT_RETRIES):
    326     """Block until the device is online.
    327 
    328     Args:
    329       timeout: (optional) Timeout per try in seconds.
    330       retries: (optional) Number of retries to attempt.
    331     """
    332     self._DeviceAdbCmd(['wait-for-device'], timeout, retries)
    333 
    334   def GetState(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
    335     """Get device state.
    336 
    337     Args:
    338       timeout: (optional) Timeout per try in seconds.
    339       retries: (optional) Number of retries to attempt.
    340 
    341     Returns:
    342       One of 'offline', 'bootloader', or 'device'.
    343     """
    344     return self._DeviceAdbCmd(['get-state'], timeout, retries).strip()
    345 
    346   def GetDevPath(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
    347     """Gets the device path.
    348 
    349     Args:
    350       timeout: (optional) Timeout per try in seconds.
    351       retries: (optional) Number of retries to attempt.
    352 
    353     Returns:
    354       The device path (e.g. usb:3-4)
    355     """
    356     return self._DeviceAdbCmd(['get-devpath'], timeout, retries)
    357 
    358   def Remount(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
    359     """Remounts the /system partition on the device read-write."""
    360     self._DeviceAdbCmd(['remount'], timeout, retries)
    361 
    362   def Reboot(self, to_bootloader=False, timeout=60*5,
    363              retries=_DEFAULT_RETRIES):
    364     """Reboots the device.
    365 
    366     Args:
    367       to_bootloader: (optional) If set reboots to the bootloader.
    368       timeout: (optional) Timeout per try in seconds.
    369       retries: (optional) Number of retries to attempt.
    370     """
    371     if to_bootloader:
    372       cmd = ['reboot-bootloader']
    373     else:
    374       cmd = ['reboot']
    375     self._DeviceAdbCmd(cmd, timeout, retries)
    376 
    377   def Root(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
    378     """Restarts the adbd daemon with root permissions, if possible.
    379 
    380     Args:
    381       timeout: (optional) Timeout per try in seconds.
    382       retries: (optional) Number of retries to attempt.
    383     """
    384     output = self._DeviceAdbCmd(['root'], timeout, retries)
    385     if 'cannot' in output:
    386       raise device_errors.AdbCommandFailedError(['root'], output)
    387 
    388