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