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

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: addressed comments Created 7 years, 1 month 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
« no previous file with comments | « build/android/pylib/device/__init__.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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."""
6
7 import errno
8 import os
9
10 from pylib import cmd_helper
11
12 from pylib.utils import reraiser_thread
13 from pylib.utils import timeout_retry
14
15 _DEFAULT_TIMEOUT = 30
16 _DEFAULT_RETRIES = 2
17
18
19 class BaseError(Exception):
20 """Base exception for all device and command errors."""
21 pass
22
23
24 class CommandFailedError(BaseError):
25 """Exception for command failures."""
26
27 def __init__(self, cmd, msg, device=None):
28 super(CommandFailedError, self).__init__(
29 (('device %s: ' % device) if device else '') +
30 'adb command \'%s\' failed with message: \'%s\'' % (' '.join(cmd), msg))
31
32
33 class CommandTimeoutError(BaseError):
34 """Exception for command timeouts."""
35 pass
36
37
38 def _VerifyLocalFileExists(path):
39 """Verifies a local file exists.
40
41 Args:
42 path: path to the local file.
43
44 Raises:
45 IOError: if the file doesn't exist.
46 """
47 if not os.path.exists(path):
48 raise IOError(errno.ENOENT, os.strerror(errno.ENOENT), path)
49
50
51 class AdbWrapper(object):
52 """A wrapper around a local Android Debug Bridge executable."""
53
54 def __init__(self, device_serial):
55 """Initializes the AdbWrapper.
56
57 Args:
58 device_serial: The device serial number as a string.
59 """
60 self._device_serial = str(device_serial)
61
62 @classmethod
63 def _AdbCmd(cls, arg_list, timeout, retries, check_error=True):
64 """Runs an adb command with a timeout and retries.
65
66 Args:
67 arg_list: a list of arguments to adb.
68 timeout: timeout in seconds.
69 retries: number of retries.
70 check_error: check that the command doesn't return an error message. This
frankf 2013/11/21 01:12:57 actually, you'll only get 'error:' if device is of
craigdh 2013/11/26 19:00:34 Install already checks for 'Success'
71 does NOT check the return code of shell commands.
72
73 Returns:
74 The output of the command.
75 """
76 cmd = ['adb'] + list(arg_list)
frankf 2013/11/21 01:12:57 get rid of these conversions as discussed
craigdh 2013/11/26 19:00:34 Done.
77
78 # This method runs inside the timeout/retries.
79 def RunCmd():
80 exit_code, output = cmd_helper.GetCmdStatusAndOutput(cmd)
81 if exit_code != 0:
82 raise CommandFailedError(
83 'Command \'%s\' failed with code %d' % (' '.join(cmd), exit_code))
84 if check_error and output[:len('error:')] == 'error:':
85 raise CommandFailedError(arg_list, output)
86 return output
87
88 try:
89 return timeout_retry.Run(RunCmd, timeout, retries)
90 except reraiser_thread.TimeoutError as e:
91 raise CommandTimeoutError(str(e))
92
93 def _DeviceAdbCmd(self, arg_list, timeout, retries, check_error=True):
94 """Runs an adb command on the device associated with this object.
95
96 Args:
97 arg_list: a list of arguments to adb.
98 timeout: timeout in seconds.
99 retries: number of retries.
100 check_error: check that the command doesn't return an error message. This
101 does NOT check the return code of shell commands.
102
103 Returns:
104 The output of the command.
105 """
106 return self._AdbCmd(
107 ['-s', self._device_serial] + list(arg_list), timeout, retries,
108 check_error=check_error)
109
110 def __eq__(self, other):
111 """Consider instances equal if they refer to the same device.
112
113 Args:
114 other: the instance to compare equality with.
115
116 Returns:
117 True if the instances are considered equal, false otherwise.
118 """
119 return self._device_serial == str(other)
120
121 def __str__(self):
122 """The string representation of an instance.
123
124 Returns:
125 The device serial number as a string.
126 """
127 return self._device_serial
128
129 def __repr__(self):
130 return '%s(\'%s\')' % (self.__class__.__name__, str(self))
131
132 # TODO(craigdh): Determine the filter criteria that should be supported.
133 @classmethod
134 def GetDevices(cls, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
135 """Get the list of active attached devices.
136
137 Args:
138 timeout: timeout per try in seconds.
139 retries: number of retries to attempt.
140
141 Yields:
142 AdbWrapper instances.
143 """
144 output = cls._AdbCmd(['devices'], timeout, retries)
145 for line in output.split('\n'):
146 fields = line.split()
147 if len(fields) == 2 and fields[1] == 'device':
148 yield AdbWrapper(fields[0])
149
150 def GetDeviceSerial(self):
151 """Gets the device serial number associated with this object.
152
153 Returns:
154 Device serial number as a string.
155 """
156 return self._device_serial
157
158 def Push(self, local, remote, timeout=_DEFAULT_TIMEOUT,
frankf 2013/11/21 01:12:57 please add test cases for any method where feasabl
craigdh 2013/11/26 19:00:34 Done. I avoided testing all cases such as file/dir
159 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: timeout per try in seconds.
166 retries: 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,
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: timeout per try in seconds.
179 retries: number of retries to attempt.
180 """
181 self._DeviceAdbCmd(['pull', local, remote], 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
frankf 2013/11/21 01:12:57 (optional) if set, checks...
craigdh 2013/11/26 19:00:34 Done.
191 this value.
192 timeout: timeout per try in seconds.
193 retries: 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.
224 timeout: timeout per try in seconds.
225 retries: 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:
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: timeout per try in seconds.
251 retries: number of retries to attempt.
252 """
253 self._DeviceAdbCmd(['forward', local, remote], timeout, retries)
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: timeout per try in seconds.
260 retries: 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=_DEFAULT_TIMEOUT,
270 retries=_DEFAULT_RETRIES):
271 """Push a package file to the device and install it.
272
273 Args:
274 apk_path: host path to the APK file.
275 forward_lock: optional, if set forward-locks the app.
276 reinstall: optional, if set reinstalls the app, keeping its data.
277 sd_card: optional, if set installs on the SD card.
278 timeout: timeout per try in seconds.
279 retries: number of retries to attempt.
280 """
281 _VerifyLocalFileExists(apk_path)
282 cmd = ['install']
283 if forward_lock:
284 cmd.append('-l')
285 if reinstall:
286 cmd.append('-r')
287 if sd_card:
288 cmd.append('-s')
289 cmd.append(apk_path)
290 output = self._DeviceAdbCmd(cmd, timeout, retries)
291 if 'Success' not in output:
292 raise CommandFailedError(cmd, output)
293
294 def Uninstall(self, package, keep_data=False, timeout=_DEFAULT_TIMEOUT,
295 retries=_DEFAULT_RETRIES):
296 """Remove the app |package| from the device.
297
298 Args:
299 package: the package to uninstall.
300 keep_data: optional, if set keep the data and cache directories.
301 timeout: timeout per try in seconds.
302 retries: number of retries to attempt.
303 """
304 cmd = ['uninstall']
305 if keep_data:
306 cmd.append('-k')
307 cmd.append(package)
308 return self._DeviceAdbCmd(cmd, timeout, retries)
309
310 def Backup(self, path, packages=None, apk=False, shared=False,
311 nosystem=True, include_all=False, timeout=_DEFAULT_TIMEOUT,
312 retries=_DEFAULT_RETRIES):
313 """Write an archive of the device's data to |path|.
314
315 Args:
316 path: local path to store the backup file.
317 packages: list of to packages to be backed up.
318 apk: optional, if set include the .apk files in the archive.
319 shared: optional, if set buckup the device's SD card.
320 nosystem: optional, if set exclude system applications.
321 include_all: optional, if set back up all installed applications and
322 |packages| is optional.
323 timeout: timeout per try in seconds.
324 retries: number of retries to attempt.
325 """
326 cmd = ['backup', path]
327 if apk:
328 cmd.append('-apk')
329 if shared:
330 cmd.append('-shared')
331 if nosystem:
332 cmd.append('-nosystem')
333 if include_all:
334 cmd.append('-all')
335 if packages:
336 cmd.extend(packages)
337 assert bool(packages) ^ bool(include_all), (
338 'Provide \'packages\' or set \'include_all\' but not both.')
339 ret = self._DeviceAdbCmd(cmd, timeout, retries)
340 _VerifyLocalFileExists(path)
341 return ret
342
343 def Restore(self, path, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
344 """Restore device contents from the backup archive.
345
346 Args:
347 path: host path to the backup archive.
348 timeout: timeout per try in seconds.
349 retries: number of retries to attempt.
350 """
351 _VerifyLocalFileExists(path)
352 self._DeviceAdbCmd(['restore'] + [path], timeout, retries)
353
354 def WaitForDevice(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
355 """Block until the device is online.
356
357 Args:
358 timeout: timeout per try in seconds.
359 retries: number of retries to attempt.
360 """
361 self._DeviceAdbCmd(['wait-for-device'], timeout, retries)
362
363 def GetState(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
364 """Get device state.
365
366 Args:
367 timeout: timeout per try in seconds.
368 retries: number of retries to attempt.
369
370 Returns:
371 One of 'offline', 'bootloader', or 'device'.
372 """
373 return self._DeviceAdbCmd(['get-state'], timeout, retries)
374
375 def GetDevPath(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
376 """Gets the device path.
377
378 Args:
379 timeout: timeout per try in seconds.
380 retries: number of retries to attempt.
381
382 Returns:
383 The device path (e.g. usb:3-4)
384 """
385 return self._DeviceAdbCmd(['get-devpath'], timeout, retries)
386
387 def Remount(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
388 """Remounts the /system partition on the device read-write."""
389 self._DeviceAdbCmd(['remount'], timeout, retries)
390
391 def Reboot(self, to_bootloader=False, timeout=_DEFAULT_TIMEOUT,
392 retries=_DEFAULT_RETRIES):
393 """Reboots the device.
394
395 Args:
396 to_bootloader: optional, if set reboots to the bootloader.
397 timeout: timeout per try in seconds.
398 retries: number of retries to attempt.
399 """
400 if to_bootloader:
401 cmd = ['reboot-bootloader']
402 else:
403 cmd = ['reboot']
404 self._DeviceAdbCmd(cmd, timeout, retries)
405
406 def Root(self, timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
407 """Restarts the adbd daemon with root permissions, if possible.
408
409 Args:
410 timeout: timeout per try in seconds.
411 retries: number of retries to attempt.
412 """
413 output = self._DeviceAdbCmd('root', timeout, retries)
414 if 'cannot' in output:
415 raise CommandFailedError('root', output)
416
OLDNEW
« no previous file with comments | « build/android/pylib/device/__init__.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698