OLD | NEW |
1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """Provides a variety of device interactions based on adb. | 5 """Provides a variety of device interactions based on adb. |
6 | 6 |
7 Eventually, this will be based on adb_wrapper. | 7 Eventually, this will be based on adb_wrapper. |
8 """ | 8 """ |
9 # pylint: disable=W0613 | 9 # pylint: disable=W0613 |
10 | 10 |
| 11 import sys |
11 import time | 12 import time |
12 | 13 |
13 import pylib.android_commands | 14 import pylib.android_commands |
14 from pylib.device import adb_wrapper | 15 from pylib.device import adb_wrapper |
15 from pylib.device import decorators | 16 from pylib.device import decorators |
16 from pylib.device import device_errors | 17 from pylib.device import device_errors |
17 from pylib.utils import apk_helper | 18 from pylib.utils import apk_helper |
18 from pylib.utils import parallelizer | 19 from pylib.utils import parallelizer |
19 | 20 |
20 _DEFAULT_TIMEOUT = 30 | 21 _DEFAULT_TIMEOUT = 30 |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
117 """Restarts adbd with root privileges. | 118 """Restarts adbd with root privileges. |
118 | 119 |
119 Args: | 120 Args: |
120 timeout: Same as for |IsOnline|. | 121 timeout: Same as for |IsOnline|. |
121 retries: Same as for |IsOnline|. | 122 retries: Same as for |IsOnline|. |
122 Raises: | 123 Raises: |
123 CommandFailedError if root could not be enabled. | 124 CommandFailedError if root could not be enabled. |
124 """ | 125 """ |
125 if not self.old_interface.EnableAdbRoot(): | 126 if not self.old_interface.EnableAdbRoot(): |
126 raise device_errors.CommandFailedError( | 127 raise device_errors.CommandFailedError( |
127 ['adb', 'root'], 'Could not enable root.') | 128 'Could not enable root.', device=str(self)) |
128 | 129 |
129 @decorators.WithTimeoutAndRetriesFromInstance() | 130 @decorators.WithTimeoutAndRetriesFromInstance() |
130 def GetExternalStoragePath(self, timeout=None, retries=None): | 131 def GetExternalStoragePath(self, timeout=None, retries=None): |
131 """Get the device's path to its SD card. | 132 """Get the device's path to its SD card. |
132 | 133 |
133 Args: | 134 Args: |
134 timeout: Same as for |IsOnline|. | 135 timeout: Same as for |IsOnline|. |
135 retries: Same as for |IsOnline|. | 136 retries: Same as for |IsOnline|. |
136 Returns: | 137 Returns: |
137 The device's path to its SD card. | 138 The device's path to its SD card. |
138 """ | 139 """ |
139 try: | 140 try: |
140 return self.old_interface.GetExternalStorage() | 141 return self.old_interface.GetExternalStorage() |
141 except AssertionError as e: | 142 except AssertionError as e: |
142 raise device_errors.CommandFailedError( | 143 raise device_errors.CommandFailedError( |
143 ['adb', 'shell', 'echo', '$EXTERNAL_STORAGE'], str(e)) | 144 str(e), device=str(self)), None, sys.exc_info()[2] |
144 | 145 |
145 @decorators.WithTimeoutAndRetriesFromInstance() | 146 @decorators.WithTimeoutAndRetriesFromInstance() |
146 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None): | 147 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None): |
147 """Wait for the device to fully boot. | 148 """Wait for the device to fully boot. |
148 | 149 |
149 This means waiting for the device to boot, the package manager to be | 150 This means waiting for the device to boot, the package manager to be |
150 available, and the SD card to be ready. It can optionally mean waiting | 151 available, and the SD card to be ready. It can optionally mean waiting |
151 for wifi to come up, too. | 152 for wifi to come up, too. |
152 | 153 |
153 Args: | 154 Args: |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
228 if device_path is not None: | 229 if device_path is not None: |
229 files_changed = self.old_interface.GetFilesChanged( | 230 files_changed = self.old_interface.GetFilesChanged( |
230 apk_path, device_path, ignore_filenames=True) | 231 apk_path, device_path, ignore_filenames=True) |
231 if len(files_changed) > 0: | 232 if len(files_changed) > 0: |
232 should_install = True | 233 should_install = True |
233 if not reinstall: | 234 if not reinstall: |
234 out = self.old_interface.Uninstall(package_name) | 235 out = self.old_interface.Uninstall(package_name) |
235 for line in out.splitlines(): | 236 for line in out.splitlines(): |
236 if 'Failure' in line: | 237 if 'Failure' in line: |
237 raise device_errors.CommandFailedError( | 238 raise device_errors.CommandFailedError( |
238 ['adb', 'uninstall', package_name], line.strip()) | 239 line.strip(), device=str(self)) |
239 else: | 240 else: |
240 should_install = False | 241 should_install = False |
241 else: | 242 else: |
242 should_install = True | 243 should_install = True |
243 if should_install: | 244 if should_install: |
244 try: | 245 try: |
245 out = self.old_interface.Install(apk_path, reinstall=reinstall) | 246 out = self.old_interface.Install(apk_path, reinstall=reinstall) |
246 for line in out.splitlines(): | 247 for line in out.splitlines(): |
247 if 'Failure' in line: | 248 if 'Failure' in line: |
248 raise device_errors.CommandFailedError( | 249 raise device_errors.CommandFailedError( |
249 ['adb', 'install', apk_path], line.strip()) | 250 line.strip(), device=str(self)) |
250 except AssertionError as e: | 251 except AssertionError as e: |
251 raise device_errors.CommandFailedError( | 252 raise device_errors.CommandFailedError( |
252 ['adb', 'install', apk_path], str(e)) | 253 str(e), device=str(self)), None, sys.exc_info()[2] |
253 | 254 |
254 @decorators.WithTimeoutAndRetriesFromInstance() | 255 @decorators.WithTimeoutAndRetriesFromInstance() |
255 def RunShellCommand(self, cmd, check_return=False, root=False, timeout=None, | 256 def RunShellCommand(self, cmd, check_return=False, as_root=False, |
256 retries=None): | 257 timeout=None, retries=None): |
257 """Run an ADB shell command. | 258 """Run an ADB shell command. |
258 | 259 |
259 TODO(jbudorick) Switch the default value of check_return to True after | 260 TODO(jbudorick) Switch the default value of check_return to True after |
260 AndroidCommands is gone. | 261 AndroidCommands is gone. |
261 | 262 |
262 Args: | 263 Args: |
263 cmd: A list containing the command to run on the device and any arguments. | 264 cmd: A list containing the command to run on the device and any arguments. |
264 check_return: A boolean indicating whether or not the return code should | 265 check_return: A boolean indicating whether or not the return code should |
265 be checked. | 266 be checked. |
| 267 as_root: A boolean indicating whether the shell command should be run |
| 268 with root privileges. |
266 timeout: Same as for |IsOnline|. | 269 timeout: Same as for |IsOnline|. |
267 retries: Same as for |IsOnline|. | 270 retries: Same as for |IsOnline|. |
268 Raises: | 271 Raises: |
269 CommandFailedError if check_return is True and the return code is nozero. | 272 CommandFailedError if check_return is True and the return code is nozero. |
270 Returns: | 273 Returns: |
271 The output of the command. | 274 The output of the command. |
272 """ | 275 """ |
273 return self._RunShellCommandImpl(cmd, check_return=check_return, root=root, | 276 return self._RunShellCommandImpl(cmd, check_return=check_return, |
274 timeout=timeout) | 277 as_root=as_root, timeout=timeout) |
275 | 278 |
276 def _RunShellCommandImpl(self, cmd, check_return=False, root=False, | 279 def _RunShellCommandImpl(self, cmd, check_return=False, as_root=False, |
277 timeout=None): | 280 timeout=None): |
278 """Implementation of RunShellCommand. | 281 """Implementation of RunShellCommand. |
279 | 282 |
280 This is split from RunShellCommand to allow other DeviceUtils methods to | 283 This is split from RunShellCommand to allow other DeviceUtils methods to |
281 call RunShellCommand without spawning a new timeout thread. | 284 call RunShellCommand without spawning a new timeout thread. |
282 | 285 |
283 TODO(jbudorick) Remove the timeout parameter once this is no longer | 286 TODO(jbudorick) Remove the timeout parameter once this is no longer |
284 implemented via AndroidCommands. | 287 implemented via AndroidCommands. |
285 | 288 |
286 Args: | 289 Args: |
287 cmd: Same as for |RunShellCommand|. | 290 cmd: Same as for |RunShellCommand|. |
288 check_return: Same as for |RunShellCommand|. | 291 check_return: Same as for |RunShellCommand|. |
| 292 as_root: Same as for |RunShellCommand|. |
289 timeout: Same as for |IsOnline|. | 293 timeout: Same as for |IsOnline|. |
290 Raises: | 294 Raises: |
291 Same as for |RunShellCommand|. | 295 Same as for |RunShellCommand|. |
292 Returns: | 296 Returns: |
293 Same as for |RunShellCommand|. | 297 Same as for |RunShellCommand|. |
294 """ | 298 """ |
295 if isinstance(cmd, list): | 299 if isinstance(cmd, list): |
296 cmd = ' '.join(cmd) | 300 cmd = ' '.join(cmd) |
297 if root and not self.HasRoot(): | 301 if as_root and not self.HasRoot(): |
298 cmd = 'su -c %s' % cmd | 302 cmd = 'su -c %s' % cmd |
299 if check_return: | 303 if check_return: |
300 code, output = self.old_interface.GetShellCommandStatusAndOutput( | 304 code, output = self.old_interface.GetShellCommandStatusAndOutput( |
301 cmd, timeout_time=timeout) | 305 cmd, timeout_time=timeout) |
302 if int(code) != 0: | 306 if int(code) != 0: |
303 raise device_errors.CommandFailedError( | 307 raise device_errors.AdbCommandFailedError( |
304 cmd.split(), 'Nonzero exit code (%d)' % code) | 308 cmd.split(), 'Nonzero exit code (%d)' % code, device=str(self)) |
305 else: | 309 else: |
306 output = self.old_interface.RunShellCommand(cmd, timeout_time=timeout) | 310 output = self.old_interface.RunShellCommand(cmd, timeout_time=timeout) |
307 return output | 311 return output |
308 | 312 |
| 313 @decorators.WithTimeoutAndRetriesFromInstance() |
| 314 def KillAll(self, process_name, signum=9, as_root=False, blocking=False, |
| 315 timeout=None, retries=None): |
| 316 """Kill all processes with the given name on the device. |
| 317 |
| 318 Args: |
| 319 process_name: A string containing the name of the process to kill. |
| 320 signum: An integer containing the signal number to send to kill. Defaults |
| 321 to 9 (SIGKILL). |
| 322 as_root: A boolean indicating whether the kill should be executed with |
| 323 root priveleges. |
| 324 blocking: A boolean indicating whether we should wait until all processes |
| 325 with the given |process_name| are dead. |
| 326 timeout: Same as for |IsOnline|. |
| 327 retries: Same as for |IsOnline|. |
| 328 Raises: |
| 329 CommandFailedError if no process was killed. |
| 330 """ |
| 331 pids = self.old_interface.ExtractPid(process_name) |
| 332 if len(pids) == 0: |
| 333 raise device_errors.CommandFailedError( |
| 334 'No process "%s"' % process_name, device=str(self)) |
| 335 |
| 336 if blocking: |
| 337 total_killed = self.old_interface.KillAllBlocking( |
| 338 process_name, signum=signum, with_su=as_root, timeout_sec=timeout) |
| 339 else: |
| 340 total_killed = self.old_interface.KillAll( |
| 341 process_name, signum=signum, with_su=as_root) |
| 342 if total_killed == 0: |
| 343 raise device_errors.CommandFailedError( |
| 344 'Failed to kill "%s"' % process_name, device=str(self)) |
| 345 |
| 346 @decorators.WithTimeoutAndRetriesFromInstance() |
| 347 def StartActivity(self, intent, blocking=False, trace_file_name=None, |
| 348 force_stop=False, timeout=None, retries=None): |
| 349 """Start package's activity on the device. |
| 350 |
| 351 Args: |
| 352 intent: An Intent to send. |
| 353 blocking: A boolean indicating whether we should wait for the activity to |
| 354 finish launching. |
| 355 trace_file_name: If present, a string that both indicates that we want to |
| 356 profile the activity and contains the path to which the |
| 357 trace should be saved. |
| 358 force_stop: A boolean indicating whether we should stop the activity |
| 359 before starting it. |
| 360 timeout: Same as for |IsOnline|. |
| 361 retries: Same as for |IsOnline|. |
| 362 Raises: |
| 363 CommandFailedError if the activity could not be started. |
| 364 """ |
| 365 single_category = (intent.category[0] if isinstance(intent.category, list) |
| 366 else intent.category) |
| 367 output = self.old_interface.StartActivity( |
| 368 intent.package, intent.activity, wait_for_completion=blocking, |
| 369 action=intent.action, category=single_category, data=intent.data, |
| 370 extras=intent.extras, trace_file_name=trace_file_name, |
| 371 force_stop=force_stop, flags=intent.flags) |
| 372 for l in output: |
| 373 if l.startswith('Error:'): |
| 374 raise device_errors.CommandFailedError(l, device=str(self)) |
| 375 |
| 376 @decorators.WithTimeoutAndRetriesFromInstance() |
| 377 def BroadcastIntent(self, intent, timeout=None, retries=None): |
| 378 """Send a broadcast intent. |
| 379 |
| 380 Args: |
| 381 intent: An Intent to broadcast. |
| 382 timeout: Same as for |IsOnline|. |
| 383 retries: Same as for |IsOnline|. |
| 384 """ |
| 385 package, old_intent = intent.action.rsplit('.', 1) |
| 386 if intent.extras is None: |
| 387 args = [] |
| 388 else: |
| 389 args = ['-e %s%s' % (k, ' "%s"' % v if v else '') |
| 390 for k, v in intent.extras.items() if len(k) > 0] |
| 391 self.old_interface.BroadcastIntent(package, old_intent, *args) |
| 392 |
309 def __str__(self): | 393 def __str__(self): |
310 """Returns the device serial.""" | 394 """Returns the device serial.""" |
311 return self.old_interface.GetDevice() | 395 return self.old_interface.GetDevice() |
312 | 396 |
313 @staticmethod | 397 @staticmethod |
314 def parallel(devices=None, async=False): | 398 def parallel(devices=None, async=False): |
315 """Creates a Parallelizer to operate over the provided list of devices. | 399 """Creates a Parallelizer to operate over the provided list of devices. |
316 | 400 |
317 If |devices| is either |None| or an empty list, the Parallelizer will | 401 If |devices| is either |None| or an empty list, the Parallelizer will |
318 operate over all attached devices. | 402 operate over all attached devices. |
319 | 403 |
320 Args: | 404 Args: |
321 devices: A list of either DeviceUtils instances or objects from | 405 devices: A list of either DeviceUtils instances or objects from |
322 from which DeviceUtils instances can be constructed. If None, | 406 from which DeviceUtils instances can be constructed. If None, |
323 all attached devices will be used. | 407 all attached devices will be used. |
324 async: If true, returns a Parallelizer that runs operations | 408 async: If true, returns a Parallelizer that runs operations |
325 asynchronously. | 409 asynchronously. |
326 Returns: | 410 Returns: |
327 A Parallelizer operating over |devices|. | 411 A Parallelizer operating over |devices|. |
328 """ | 412 """ |
329 if not devices or len(devices) == 0: | 413 if not devices or len(devices) == 0: |
330 devices = pylib.android_commands.GetAttachedDevices() | 414 devices = pylib.android_commands.GetAttachedDevices() |
331 parallelizer_type = (parallelizer.Parallelizer if async | 415 parallelizer_type = (parallelizer.Parallelizer if async |
332 else parallelizer.SyncParallelizer) | 416 else parallelizer.SyncParallelizer) |
333 return parallelizer_type([ | 417 return parallelizer_type([ |
334 d if isinstance(d, DeviceUtils) else DeviceUtils(d) | 418 d if isinstance(d, DeviceUtils) else DeviceUtils(d) |
335 for d in devices]) | 419 for d in devices]) |
336 | 420 |
OLD | NEW |