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

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 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') | build/android/pylib/device/adb_wrapper_test.py » ('j') | 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 """This module wraps Android's adb tool.
6
7 This is a thin wrapper around the adb interface. Any additional complexity
8 should be delegated to a higher level (ex. DeviceUtils).
9 """
10
11 import errno
12 import logging
13 import os
14
15 from pylib import cmd_helper
16
17 from pylib.utils import reraiser_thread
18 from pylib.utils import timeout_retry
19
20 _DEFAULT_TIMEOUT = 30
21 _DEFAULT_RETRIES = 2
22
23
24 class BaseError(Exception):
25 """Base exception for all device and command errors."""
26 pass
27
28
29 class 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
38 class CommandTimeoutError(BaseError):
39 """Exception for command timeouts."""
40 pass
41
42
43 def _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
56 class 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
OLDNEW
« no previous file with comments | « build/android/pylib/device/__init__.py ('k') | build/android/pylib/device/adb_wrapper_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698