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 |