Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # Copyright (c) 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 """Module wrapping Android's Adb tool.""" | |
|
frankf
2013/11/20 19:28:39
"adb"
craigdh
2013/11/21 00:35:48
Done.
| |
| 6 | |
| 7 from pylib import cmd_helper | |
| 8 | |
| 9 from pylib.utils import reraiser_thread | |
| 10 from pylib.utils import timeout_retry | |
| 11 | |
| 12 _DEFAULT_TIMEOUT = 30 | |
|
frankf
2013/11/20 19:28:39
add comments for each global constant
| |
| 13 _DEFAULT_RETRIES = 2 | |
| 14 | |
| 15 | |
| 16 class BaseError(Exception): | |
| 17 pass | |
| 18 | |
| 19 | |
| 20 class CommandFailedError(BaseError): | |
| 21 """Exception for command failures.""" | |
| 22 def __init__(self, cmd, msg, device=None): | |
| 23 super(CommandFailedError, self).__init__( | |
| 24 (('%s: ' % device) if device else '') + | |
| 25 'adb command \'%s\' failed with message: %s' % (cmd, msg)) | |
|
frankf
2013/11/20 19:28:39
add quotation marks around msg
craigdh
2013/11/21 00:35:48
Done.
| |
| 26 | |
| 27 | |
| 28 class CommandTimeoutError(BaseError): | |
|
frankf
2013/11/20 19:28:39
docstring, run pylint
craigdh
2013/11/21 00:35:48
Done.
| |
| 29 pass | |
| 30 | |
| 31 | |
| 32 class AdbWrapper(object): | |
| 33 """A wrapper around a local Android Debug Bridge executable.""" | |
| 34 | |
| 35 def __init__(self, device_serial): | |
| 36 """Initializes the AdbWrapper. | |
| 37 | |
| 38 Args: | |
| 39 device_serial: The device serial number as a string. | |
| 40 """ | |
| 41 self._device_serial = str(device_serial) | |
|
frankf
2013/11/20 19:28:39
hmm, why wouldn't this be a str?
craigdh
2013/11/21 00:35:48
To be pythonic and support ducktyping with anythin
| |
| 42 | |
| 43 @classmethod | |
| 44 def _AdbCmd(cls, arg_list, timeout, retries, check_error=True): | |
| 45 """Runs an adb command with a timeout and retries. | |
| 46 | |
| 47 Args: | |
| 48 arg_list: a list of arguments to adb. | |
| 49 timeout: timeout in seconds. | |
| 50 retries: number of retries. | |
| 51 check_error: check that the command doesn't return an error message. | |
|
frankf
2013/11/20 19:28:39
Expand this comment to state that this doesn't wor
craigdh
2013/11/21 00:35:48
Clarified.
| |
| 52 | |
| 53 Returns: | |
| 54 The output of the command. | |
| 55 """ | |
| 56 cmd = ['adb'] + list(arg_list) | |
|
frankf
2013/11/20 19:28:39
same here, do you mean to assert it's a list? If s
craigdh
2013/11/21 00:35:48
Not, list concatenation only works for lists but I
| |
| 57 | |
| 58 # This method runs inside the timeout/retries. | |
| 59 def RunCmd(): | |
| 60 exit_code, output = cmd_helper.GetCmdStatusAndOutput(cmd) | |
| 61 if exit_code != 0: | |
| 62 raise CommandFailedError( | |
| 63 'Command \'%s\' failed with code %d' % (' '.join(cmd), exit_code)) | |
| 64 if check_error and output[:len('error:')] == 'error:': | |
| 65 raise CommandFailedError(arg_list, output) | |
| 66 return output | |
| 67 | |
| 68 try: | |
| 69 return timeout_retry.Run(RunCmd, timeout, retries) | |
| 70 except reraiser_thread.TimeoutError as e: | |
| 71 raise CommandTimeoutError(str(e)) | |
| 72 | |
| 73 def _DeviceAdbCmd(self, arg_list, timeout, retries, check_error=True): | |
| 74 """Runs an adb command on the device associated with this object. | |
| 75 | |
| 76 Args: | |
| 77 arg_list: a list of arguments to adb. | |
| 78 timeout: timeout in seconds. | |
| 79 retries: number of retries. | |
| 80 check_error: check that the command doesn't return an error message. | |
| 81 | |
| 82 Returns: | |
| 83 The output of the command. | |
| 84 """ | |
| 85 return self._AdbCmd( | |
| 86 ['-s', self._device_serial] + list(arg_list), timeout, retries, | |
| 87 check_error=check_error) | |
| 88 | |
| 89 def __eq__(self, other): | |
| 90 """Consider instances equal if they refer to the same device. | |
| 91 | |
| 92 Args: | |
| 93 other: the instance to compare equality with. | |
| 94 | |
| 95 Returns: | |
| 96 True if the instances are considered equal, false otherwise. | |
| 97 """ | |
| 98 return self._device_serial == str(other) | |
| 99 | |
| 100 def __str__(self): | |
| 101 """The string representation of an instance. | |
| 102 | |
| 103 Returns: | |
| 104 The device serial number as a string. | |
| 105 """ | |
| 106 return self._device_serial | |
| 107 | |
| 108 def __repr__(self): | |
| 109 return '%s(\'%s\')' % (self.__class__.__name__, str(self)) | |
| 110 | |
| 111 # TODO(craigdh): Determine the filter criteria that should be supported. | |
| 112 @classmethod | |
| 113 def GetDevices(cls, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES): | |
|
frankf
2013/11/20 19:28:39
Take a look at current adb_commands. You want to g
craigdh
2013/11/21 00:35:48
Yes, that falls under the TODO.
| |
| 114 """Get the list of active attached devices. | |
| 115 | |
| 116 Args: | |
| 117 timeout: timeout per try in seconds. | |
| 118 retries: number of retries to attempt. | |
| 119 | |
| 120 Yields: | |
| 121 AdbWrapper instances. | |
| 122 """ | |
| 123 output = cls._AdbCmd(['devices'], timeout, retries) | |
| 124 for line in output.split('\n'): | |
| 125 fields = line.split() | |
| 126 if len(fields) == 2 and fields[1] == 'device': | |
| 127 yield AdbWrapper(fields[0]) | |
| 128 | |
| 129 def GetDeviceSerial(self): | |
| 130 """Gets the device serial number associated with this object. | |
| 131 | |
| 132 Returns: | |
| 133 Device serial number as a string. | |
| 134 """ | |
| 135 return self._device_serial | |
| 136 | |
| 137 def Push(self, local, remote, timeout=_DEFAULT_TIMEOUT, | |
| 138 retries=_DEFAULT_RETRIES): | |
| 139 """Pushes a file from the host to the device. | |
| 140 | |
| 141 Args: | |
| 142 local: path on the host filesystem. | |
| 143 remote: path on the device filesystem. | |
| 144 timeout: timeout per try in seconds. | |
| 145 retries: number of retries to attempt. | |
| 146 """ | |
| 147 self._DeviceAdbCmd(['push', local, remote], timeout, retries) | |
|
frankf
2013/11/20 19:28:39
YOu need check preconditions for these operations
craigdh
2013/11/21 00:35:48
Done.
craigdh
2013/11/21 00:35:48
Done.
| |
| 148 | |
| 149 def Pull(self, remote, local, timeout=_DEFAULT_TIMEOUT, | |
| 150 retries=_DEFAULT_RETRIES): | |
| 151 """Pulls a file from the device to the host. | |
| 152 | |
| 153 Args: | |
| 154 remote: path on the device filesystem. | |
| 155 local: path on the host filesystem. | |
| 156 timeout: timeout per try in seconds. | |
| 157 retries: number of retries to attempt. | |
| 158 """ | |
| 159 self._DeviceAdbCmd(['pull', local, remote], timeout, retries) | |
| 160 | |
| 161 def Shell(self, command, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES): | |
|
frankf
2013/11/20 19:28:39
how about returning the rc in the shell?
craigdh
2013/11/21 00:35:48
Done.
| |
| 162 """Runs a shell command on the device. | |
| 163 | |
| 164 Args: | |
| 165 command: the shell command to run. | |
| 166 timeout: timeout per try in seconds. | |
| 167 retries: number of retries to attempt. | |
| 168 | |
| 169 Returns: | |
| 170 The output of the shell command as a string. | |
| 171 """ | |
| 172 return self._DeviceAdbCmd( | |
| 173 ['shell', command], timeout, retries, check_error=False) | |
| 174 | |
| 175 def Logcat(self, filter_spec=None, timeout=_DEFAULT_TIMEOUT, | |
| 176 retries=_DEFAULT_RETRIES): | |
| 177 """Get the logcat output. | |
| 178 | |
| 179 Args: | |
| 180 filter_spec: optional spec to filter the logcat. | |
| 181 timeout: timeout per try in seconds. | |
| 182 retries: number of retries to attempt. | |
| 183 | |
| 184 Returns: | |
| 185 logcat output as a string. | |
| 186 """ | |
| 187 cmd = ['logcat'] | |
| 188 if filter_spec is not None: | |
| 189 cmd.append(filter_spec) | |
| 190 return self._DeviceAdbCmd(cmd, timeout, retries, check_error=False) | |
| 191 | |
| 192 def Forward(self, local, remote, timeout=_DEFAULT_TIMEOUT, | |
| 193 retries=_DEFAULT_RETRIES): | |
| 194 """Forward socket connections from the local socket to the remote socket. | |
| 195 | |
| 196 Sockets are specified by one of: | |
| 197 tcp:<port> | |
| 198 localabstract:<unix domain socket name> | |
| 199 localreserved:<unix domain socket name> | |
| 200 localfilesystem:<unix domain socket name> | |
| 201 dev:<character device name> | |
| 202 jdwp:<process pid> (remote only) | |
| 203 | |
| 204 Args: | |
| 205 local: the host socket. | |
| 206 remote: the device socket. | |
| 207 timeout: timeout per try in seconds. | |
| 208 retries: number of retries to attempt. | |
| 209 """ | |
| 210 self._DeviceAdbCmd(['forward', local, remote], timeout, retries) | |
| 211 | |
| 212 def JDWP(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES): | |
| 213 """List of PIDs of processes hosting a JDWP transport. | |
| 214 | |
| 215 Args: | |
| 216 timeout: timeout per try in seconds. | |
| 217 retries: number of retries to attempt. | |
| 218 | |
| 219 Returns: | |
| 220 A list of PIDs as strings. | |
| 221 """ | |
| 222 return [a.strip() for a in | |
| 223 self._DeviceAdbCmd(['jdwp'], timeout, retries).split('\n')] | |
| 224 | |
| 225 def Install(self, apk_path, forward_lock=False, reinstall=False, | |
| 226 sd_card=False, timeout=_DEFAULT_TIMEOUT, | |
| 227 retries=_DEFAULT_RETRIES): | |
| 228 """Push a package file to the device and install it. | |
| 229 | |
| 230 Args: | |
| 231 apk_path: host path to the APK file. | |
| 232 forward_lock: optional, if set forward-locks the app. | |
| 233 reinstall: optional, if set reinstalls the app, keeping its data. | |
| 234 sd_card: optional, if set installs on the SD card. | |
| 235 timeout: timeout per try in seconds. | |
| 236 retries: number of retries to attempt. | |
| 237 """ | |
| 238 cmd = ['install'] | |
| 239 if forward_lock: | |
| 240 cmd.append('-l') | |
| 241 if reinstall: | |
| 242 cmd.append('-r') | |
| 243 if sd_card: | |
| 244 cmd.append('-s') | |
| 245 cmd.append(apk_path) | |
| 246 output = self._DeviceAdbCmd(cmd, timeout, retries) | |
| 247 if 'Success' not in output: | |
| 248 raise CommandFailedError(cmd, output) | |
| 249 | |
| 250 def Uninstall(self, package, keep_data=False, timeout=_DEFAULT_TIMEOUT, | |
| 251 retries=_DEFAULT_RETRIES): | |
| 252 """Remove the app |package| from the device. | |
| 253 | |
| 254 Args: | |
| 255 package: the package to uninstall. | |
| 256 keep_data: optional, if set keep the data and cache directories. | |
| 257 timeout: timeout per try in seconds. | |
| 258 retries: number of retries to attempt. | |
| 259 """ | |
| 260 cmd = ['uninstall'] | |
| 261 if keep_data: | |
| 262 cmd.append('-k') | |
| 263 cmd.append(package) | |
| 264 return self._DeviceAdbCmd(cmd, timeout, retries) | |
| 265 | |
| 266 def Backup(self, path, packages=None, apk=False, shared=False, | |
| 267 nosystem=True, include_all=False, timeout=_DEFAULT_TIMEOUT, | |
| 268 retries=_DEFAULT_RETRIES): | |
| 269 """Write an archive of the device's data to |path|. | |
| 270 | |
| 271 Args: | |
| 272 path: local path to store the backup file. | |
| 273 packages: list of to packages to be backed up. | |
| 274 apk: optional, if set include the .apk files in the archive. | |
| 275 shared: optional, if set buckup the device's SD card. | |
| 276 nosystem: optional, if set exclude system applications. | |
| 277 include_all: optional, if set back up all installed applications and | |
| 278 |packages| is optional. | |
| 279 timeout: timeout per try in seconds. | |
| 280 retries: number of retries to attempt. | |
| 281 """ | |
| 282 cmd = ['backup', path] | |
| 283 if apk: | |
| 284 cmd.append('-apk') | |
| 285 if shared: | |
| 286 cmd.append('-shared') | |
| 287 if nosystem: | |
| 288 cmd.append('-nosystem') | |
| 289 if include_all: | |
| 290 cmd.append('-all') | |
| 291 if packages: | |
| 292 cmd.extend(packages) | |
| 293 assert bool(packages) ^ bool(include_all), ( | |
| 294 'Provide \'packages\' or set \'include_all\' but not both.') | |
| 295 return self._DeviceAdbCmd(cmd, timeout, retries) | |
| 296 | |
| 297 def Restore(self, path, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES): | |
| 298 """Restore device contents from the backup archive. | |
| 299 | |
| 300 Args: | |
| 301 path: host path to the backup archive. | |
| 302 timeout: timeout per try in seconds. | |
| 303 retries: number of retries to attempt. | |
| 304 """ | |
| 305 self._DeviceAdbCmd(['restore'] + [path], timeout, retries) | |
| 306 | |
| 307 def WaitForDevice(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES): | |
| 308 """Block until the device is online. | |
| 309 | |
| 310 Args: | |
| 311 timeout: timeout per try in seconds. | |
| 312 retries: number of retries to attempt. | |
| 313 """ | |
| 314 self._DeviceAdbCmd(['wait-for-device'], timeout, retries) | |
| 315 | |
| 316 def GetState(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES): | |
| 317 """Get device state. | |
| 318 | |
| 319 Args: | |
| 320 timeout: timeout per try in seconds. | |
| 321 retries: number of retries to attempt. | |
| 322 | |
| 323 Returns: | |
| 324 One of 'offline', 'bootloader', or 'device'. | |
| 325 """ | |
| 326 return self._DeviceAdbCmd(['get-state'], timeout, retries) | |
| 327 | |
| 328 def GetDevPath(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES): | |
| 329 """Gets the device path. | |
| 330 | |
| 331 Args: | |
| 332 timeout: timeout per try in seconds. | |
| 333 retries: number of retries to attempt. | |
| 334 | |
| 335 Returns: | |
| 336 The device path (e.g. usb:3-4) | |
| 337 """ | |
| 338 return self._DeviceAdbCmd(['get-devpath'], timeout, retries) | |
| 339 | |
| 340 def Remount(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES): | |
| 341 """Remounts the /system partition on the device read-write.""" | |
| 342 self._DeviceAdbCmd(['remount'], timeout, retries) | |
| 343 | |
| 344 def Reboot(self, to_bootloader=False, timeout=_DEFAULT_TIMEOUT, | |
| 345 retries=_DEFAULT_RETRIES): | |
| 346 """Reboots the device. | |
| 347 | |
| 348 Args: | |
| 349 to_bootloader: optional, if set reboots to the bootloader. | |
| 350 timeout: timeout per try in seconds. | |
| 351 retries: number of retries to attempt. | |
| 352 """ | |
| 353 if to_bootloader: | |
| 354 cmd = ['reboot-bootloader'] | |
| 355 else: | |
| 356 cmd = ['reboot'] | |
| 357 self._DeviceAdbCmd(cmd, timeout, retries) | |
| 358 | |
| 359 def Root(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES): | |
| 360 """Restarts the adbd daemon with root permissions, if possible. | |
| 361 | |
| 362 Args: | |
| 363 timeout: timeout per try in seconds. | |
| 364 retries: number of retries to attempt. | |
| 365 """ | |
| 366 output = self._DeviceAdbCmd('root', timeout, retries) | |
| 367 if 'cannot' in output: | |
| 368 raise CommandFailedError('root', output) | |
| 369 | |
| OLD | NEW |