Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(18)

Side by Side Diff: build/android/pylib/device/adb_wrapper.py

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

Powered by Google App Engine
This is Rietveld 408576698