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