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