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
3 # found in the LICENSE file.
5 """Provides a variety of device interactions based on adb.
7 Eventually, this will be based on adb_wrapper.
9 # pylint: disable=W0613
14 import pylib.android_commands
15 from pylib.device import adb_wrapper
16 from pylib.device import decorators
17 from pylib.device import device_errors
18 from pylib.utils import apk_helper
19 from pylib.utils import parallelizer
25 @decorators.WithExplicitTimeoutAndRetries(
26 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
28 """Returns a list of Android Virtual Devices.
31 A list containing the configured AVDs.
33 return pylib.android_commands.GetAVDs()
36 @decorators.WithExplicitTimeoutAndRetries(
37 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
39 """Restarts the adb server.
42 CommandFailedError if we fail to kill or restart the server.
44 pylib.android_commands.AndroidCommands().RestartAdbServer()
47 class DeviceUtils(object):
49 def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT,
50 default_retries=_DEFAULT_RETRIES):
51 """DeviceUtils constructor.
54 device: Either a device serial, an existing AdbWrapper instance, an
55 an existing AndroidCommands instance, or nothing.
56 default_timeout: An integer containing the default number of seconds to
57 wait for an operation to complete if no explicit value
59 default_retries: An integer containing the default number or times an
60 operation should be retried on failure if no explicit
63 self.old_interface = None
64 if isinstance(device, basestring):
65 self.old_interface = pylib.android_commands.AndroidCommands(device)
66 elif isinstance(device, adb_wrapper.AdbWrapper):
67 self.old_interface = pylib.android_commands.AndroidCommands(str(device))
68 elif isinstance(device, pylib.android_commands.AndroidCommands):
69 self.old_interface = device
71 self.old_interface = pylib.android_commands.AndroidCommands()
73 raise ValueError('Unsupported type passed for argument "device"')
74 self._default_timeout = default_timeout
75 self._default_retries = default_retries
76 assert(hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR))
77 assert(hasattr(self, decorators.DEFAULT_RETRIES_ATTR))
79 @decorators.WithTimeoutAndRetriesFromInstance()
80 def IsOnline(self, timeout=None, retries=None):
81 """Checks whether the device is online.
84 timeout: timeout in seconds
85 retries: number of retries
88 True if the device is online, False otherwise.
91 CommandTimeoutError on timeout.
93 return self.old_interface.IsOnline()
95 @decorators.WithTimeoutAndRetriesFromInstance()
96 def HasRoot(self, timeout=None, retries=None):
97 """Checks whether or not adbd has root privileges.
100 timeout: timeout in seconds
101 retries: number of retries
104 True if adbd has root privileges, False otherwise.
107 CommandTimeoutError on timeout.
108 DeviceUnreachableError on missing device.
110 return self._HasRootImpl()
112 def _HasRootImpl(self):
113 """Implementation of HasRoot.
115 This is split from HasRoot to allow other DeviceUtils methods to call
116 HasRoot without spawning a new timeout thread.
119 Same as for |HasRoot|.
122 Same as for |HasRoot|.
124 return self.old_interface.IsRootEnabled()
126 @decorators.WithTimeoutAndRetriesFromInstance()
127 def EnableRoot(self, timeout=None, retries=None):
128 """Restarts adbd with root privileges.
131 timeout: timeout in seconds
132 retries: number of retries
135 CommandFailedError if root could not be enabled.
136 CommandTimeoutError on timeout.
138 if not self.old_interface.EnableAdbRoot():
139 raise device_errors.CommandFailedError(
140 'Could not enable root.', device=str(self))
142 @decorators.WithTimeoutAndRetriesFromInstance()
143 def GetExternalStoragePath(self, timeout=None, retries=None):
144 """Get the device's path to its SD card.
147 timeout: timeout in seconds
148 retries: number of retries
151 The device's path to its SD card.
154 CommandFailedError if the external storage path could not be determined.
155 CommandTimeoutError on timeout.
156 DeviceUnreachableError on missing device.
159 return self.old_interface.GetExternalStorage()
160 except AssertionError as e:
161 raise device_errors.CommandFailedError(
162 str(e), device=str(self)), None, sys.exc_info()[2]
164 @decorators.WithTimeoutAndRetriesFromInstance()
165 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None):
166 """Wait for the device to fully boot.
168 This means waiting for the device to boot, the package manager to be
169 available, and the SD card to be ready. It can optionally mean waiting
170 for wifi to come up, too.
173 wifi: A boolean indicating if we should wait for wifi to come up or not.
174 timeout: timeout in seconds
175 retries: number of retries
178 CommandFailedError on failure.
179 CommandTimeoutError if one of the component waits times out.
180 DeviceUnreachableError if the device becomes unresponsive.
182 self._WaitUntilFullyBootedImpl(wifi=wifi, timeout=timeout)
184 def _WaitUntilFullyBootedImpl(self, wifi=False, timeout=None):
185 """Implementation of WaitUntilFullyBooted.
187 This is split from WaitUntilFullyBooted to allow other DeviceUtils methods
188 to call WaitUntilFullyBooted without spawning a new timeout thread.
190 TODO(jbudorick) Remove the timeout parameter once this is no longer
191 implemented via AndroidCommands.
194 wifi: Same as for |WaitUntilFullyBooted|.
195 timeout: timeout in seconds
198 Same as for |WaitUntilFullyBooted|.
201 timeout = self._default_timeout
202 self.old_interface.WaitForSystemBootCompleted(timeout)
203 self.old_interface.WaitForDevicePm()
204 self.old_interface.WaitForSdCardReady(timeout)
206 while not 'Wi-Fi is enabled' in (
207 self._RunShellCommandImpl('dumpsys wifi')):
210 REBOOT_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT
211 REBOOT_DEFAULT_RETRIES = _DEFAULT_RETRIES
213 @decorators.WithTimeoutAndRetriesDefaults(
214 REBOOT_DEFAULT_TIMEOUT,
215 REBOOT_DEFAULT_RETRIES)
216 def Reboot(self, block=True, timeout=None, retries=None):
217 """Reboot the device.
220 block: A boolean indicating if we should wait for the reboot to complete.
221 timeout: timeout in seconds
222 retries: number of retries
225 CommandTimeoutError on timeout.
226 DeviceUnreachableError on missing device.
228 self.old_interface.Reboot()
230 self._WaitUntilFullyBootedImpl(timeout=timeout)
232 INSTALL_DEFAULT_TIMEOUT = 4 * _DEFAULT_TIMEOUT
233 INSTALL_DEFAULT_RETRIES = _DEFAULT_RETRIES
235 @decorators.WithTimeoutAndRetriesDefaults(
236 INSTALL_DEFAULT_TIMEOUT,
237 INSTALL_DEFAULT_RETRIES)
238 def Install(self, apk_path, reinstall=False, timeout=None, retries=None):
241 Noop if an identical APK is already installed.
244 apk_path: A string containing the path to the APK to install.
245 reinstall: A boolean indicating if we should keep any existing app data.
246 timeout: timeout in seconds
247 retries: number of retries
250 CommandFailedError if the installation fails.
251 CommandTimeoutError if the installation times out.
252 DeviceUnreachableError on missing device.
254 package_name = apk_helper.GetPackageName(apk_path)
255 device_path = self.old_interface.GetApplicationPath(package_name)
256 if device_path is not None:
257 files_changed = self.old_interface.GetFilesChanged(
258 apk_path, device_path, ignore_filenames=True)
259 if len(files_changed) > 0:
260 should_install = True
262 out = self.old_interface.Uninstall(package_name)
263 for line in out.splitlines():
264 if 'Failure' in line:
265 raise device_errors.CommandFailedError(
266 line.strip(), device=str(self))
268 should_install = False
270 should_install = True
273 out = self.old_interface.Install(apk_path, reinstall=reinstall)
274 for line in out.splitlines():
275 if 'Failure' in line:
276 raise device_errors.CommandFailedError(
277 line.strip(), device=str(self))
278 except AssertionError as e:
279 raise device_errors.CommandFailedError(
280 str(e), device=str(self)), None, sys.exc_info()[2]
282 @decorators.WithTimeoutAndRetriesFromInstance()
283 def RunShellCommand(self, cmd, check_return=False, as_root=False,
284 timeout=None, retries=None):
285 """Run an ADB shell command.
287 TODO(jbudorick) Switch the default value of check_return to True after
288 AndroidCommands is gone.
291 cmd: A list containing the command to run on the device and any arguments.
292 check_return: A boolean indicating whether or not the return code should
294 as_root: A boolean indicating whether the shell command should be run
295 with root privileges.
296 timeout: timeout in seconds
297 retries: number of retries
300 The output of the command.
303 CommandFailedError if check_return is True and the return code is nozero.
304 CommandTimeoutError on timeout.
305 DeviceUnreachableError on missing device.
307 return self._RunShellCommandImpl(cmd, check_return=check_return,
308 as_root=as_root, timeout=timeout)
310 def _RunShellCommandImpl(self, cmd, check_return=False, as_root=False,
312 """Implementation of RunShellCommand.
314 This is split from RunShellCommand to allow other DeviceUtils methods to
315 call RunShellCommand without spawning a new timeout thread.
317 TODO(jbudorick) Remove the timeout parameter once this is no longer
318 implemented via AndroidCommands.
321 cmd: Same as for |RunShellCommand|.
322 check_return: Same as for |RunShellCommand|.
323 as_root: Same as for |RunShellCommand|.
324 timeout: timeout in seconds
327 Same as for |RunShellCommand|.
330 Same as for |RunShellCommand|.
332 if isinstance(cmd, list):
334 if as_root and not self.HasRoot():
335 cmd = 'su -c %s' % cmd
337 code, output = self.old_interface.GetShellCommandStatusAndOutput(
338 cmd, timeout_time=timeout)
340 raise device_errors.AdbCommandFailedError(
341 cmd.split(), 'Nonzero exit code (%d)' % code, device=str(self))
343 output = self.old_interface.RunShellCommand(cmd, timeout_time=timeout)
346 @decorators.WithTimeoutAndRetriesFromInstance()
347 def KillAll(self, process_name, signum=9, as_root=False, blocking=False,
348 timeout=None, retries=None):
349 """Kill all processes with the given name on the device.
352 process_name: A string containing the name of the process to kill.
353 signum: An integer containing the signal number to send to kill. Defaults
355 as_root: A boolean indicating whether the kill should be executed with
357 blocking: A boolean indicating whether we should wait until all processes
358 with the given |process_name| are dead.
359 timeout: timeout in seconds
360 retries: number of retries
363 CommandFailedError if no process was killed.
364 CommandTimeoutError on timeout.
365 DeviceUnreachableError on missing device.
367 pids = self.old_interface.ExtractPid(process_name)
369 raise device_errors.CommandFailedError(
370 'No process "%s"' % process_name, device=str(self))
373 total_killed = self.old_interface.KillAllBlocking(
374 process_name, signum=signum, with_su=as_root, timeout_sec=timeout)
376 total_killed = self.old_interface.KillAll(
377 process_name, signum=signum, with_su=as_root)
378 if total_killed == 0:
379 raise device_errors.CommandFailedError(
380 'Failed to kill "%s"' % process_name, device=str(self))
382 @decorators.WithTimeoutAndRetriesFromInstance()
383 def StartActivity(self, intent, blocking=False, trace_file_name=None,
384 force_stop=False, timeout=None, retries=None):
385 """Start package's activity on the device.
388 intent: An Intent to send.
389 blocking: A boolean indicating whether we should wait for the activity to
391 trace_file_name: If present, a string that both indicates that we want to
392 profile the activity and contains the path to which the
393 trace should be saved.
394 force_stop: A boolean indicating whether we should stop the activity
396 timeout: timeout in seconds
397 retries: number of retries
400 CommandFailedError if the activity could not be started.
401 CommandTimeoutError on timeout.
402 DeviceUnreachableError on missing device.
404 single_category = (intent.category[0] if isinstance(intent.category, list)
405 else intent.category)
406 output = self.old_interface.StartActivity(
407 intent.package, intent.activity, wait_for_completion=blocking,
408 action=intent.action, category=single_category, data=intent.data,
409 extras=intent.extras, trace_file_name=trace_file_name,
410 force_stop=force_stop, flags=intent.flags)
412 if l.startswith('Error:'):
413 raise device_errors.CommandFailedError(l, device=str(self))
415 @decorators.WithTimeoutAndRetriesFromInstance()
416 def BroadcastIntent(self, intent, timeout=None, retries=None):
417 """Send a broadcast intent.
420 intent: An Intent to broadcast.
421 timeout: timeout in seconds
422 retries: number of retries
425 CommandTimeoutError on timeout.
426 DeviceUnreachableError on missing device.
428 package, old_intent = intent.action.rsplit('.', 1)
429 if intent.extras is None:
432 args = ['-e %s%s' % (k, ' "%s"' % v if v else '')
433 for k, v in intent.extras.items() if len(k) > 0]
434 self.old_interface.BroadcastIntent(package, old_intent, *args)
436 @decorators.WithTimeoutAndRetriesFromInstance()
437 def GoHome(self, timeout=None, retries=None):
438 """Return to the home screen.
441 timeout: timeout in seconds
442 retries: number of retries
445 CommandTimeoutError on timeout.
446 DeviceUnreachableError on missing device.
448 self.old_interface.GoHome()
450 @decorators.WithTimeoutAndRetriesFromInstance()
451 def ForceStop(self, package, timeout=None, retries=None):
452 """Close the application.
455 package: A string containing the name of the package to stop.
456 timeout: timeout in seconds
457 retries: number of retries
460 CommandTimeoutError on timeout.
461 DeviceUnreachableError on missing device.
463 self.old_interface.CloseApplication(package)
465 @decorators.WithTimeoutAndRetriesFromInstance()
466 def ClearApplicationState(self, package, timeout=None, retries=None):
467 """Clear all state for the given package.
470 package: A string containing the name of the package to stop.
471 timeout: timeout in seconds
472 retries: number of retries
475 CommandTimeoutError on timeout.
476 DeviceUnreachableError on missing device.
478 self.old_interface.ClearApplicationState(package)
480 @decorators.WithTimeoutAndRetriesFromInstance()
481 def SendKeyEvent(self, keycode, timeout=None, retries=None):
482 """Sends a keycode to the device.
484 See: http://developer.android.com/reference/android/view/KeyEvent.html
487 keycode: A integer keycode to send to the device.
488 timeout: timeout in seconds
489 retries: number of retries
492 CommandTimeoutError on timeout.
493 DeviceUnreachableError on missing device.
495 self.old_interface.SendKeyEvent(keycode)
497 PUSH_CHANGED_FILES_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT
498 PUSH_CHANGED_FILES_DEFAULT_RETRIES = _DEFAULT_RETRIES
500 @decorators.WithTimeoutAndRetriesDefaults(
501 PUSH_CHANGED_FILES_DEFAULT_TIMEOUT,
502 PUSH_CHANGED_FILES_DEFAULT_RETRIES)
503 def PushChangedFiles(self, host_path, device_path, timeout=None,
505 """Push files to the device, skipping files that don't need updating.
508 host_path: A string containing the absolute path to the file or directory
509 on the host that should be minimally pushed to the device.
510 device_path: A string containing the absolute path of the destination on
512 timeout: timeout in seconds
513 retries: number of retries
516 CommandFailedError on failure.
517 CommandTimeoutError on timeout.
518 DeviceUnreachableError on missing device.
520 self.old_interface.PushIfNeeded(host_path, device_path)
522 @decorators.WithTimeoutAndRetriesFromInstance()
523 def FileExists(self, device_path, timeout=None, retries=None):
524 """Checks whether the given file exists on the device.
527 device_path: A string containing the absolute path to the file on the
529 timeout: timeout in seconds
530 retries: number of retries
533 True if the file exists on the device, False otherwise.
536 CommandTimeoutError on timeout.
537 DeviceUnreachableError on missing device.
539 return self._FileExistsImpl(device_path)
541 def _FileExistsImpl(self, device_path):
542 """Implementation of FileExists.
544 This is split from FileExists to allow other DeviceUtils methods to call
545 FileExists without spawning a new timeout thread.
548 device_path: Same as for |FileExists|.
551 True if the file exists on the device, False otherwise.
554 Same as for |FileExists|.
556 return self.old_interface.FileExistsOnDevice(device_path)
558 @decorators.WithTimeoutAndRetriesFromInstance()
559 def PullFile(self, device_path, host_path, timeout=None, retries=None):
560 """Pull a file from the device.
563 device_path: A string containing the absolute path of the file to pull
565 host_path: A string containing the absolute path of the destination on
567 timeout: timeout in seconds
568 retries: number of retries
571 CommandFailedError on failure.
572 CommandTimeoutError on timeout.
575 self.old_interface.PullFileFromDevice(device_path, host_path)
576 except AssertionError as e:
577 raise device_errors.CommandFailedError(
578 str(e), device=str(self)), None, sys.exc_info()[2]
580 @decorators.WithTimeoutAndRetriesFromInstance()
581 def ReadFile(self, device_path, as_root=False, timeout=None, retries=None):
582 """Reads the contents of a file from the device.
585 device_path: A string containing the absolute path of the file to read
587 as_root: A boolean indicating whether the read should be executed with
589 timeout: timeout in seconds
590 retries: number of retries
593 The contents of the file at |device_path| as a list of lines.
596 CommandFailedError if the file can't be read.
597 CommandTimeoutError on timeout.
598 DeviceUnreachableError on missing device.
600 # TODO(jbudorick) Evaluate whether we awant to return a list of lines after
601 # the implementation switch, and if file not found should raise exception.
603 if not self.old_interface.CanAccessProtectedFileContents():
604 raise device_errors.CommandFailedError(
605 'Cannot read from %s with root privileges.' % device_path)
606 return self.old_interface.GetProtectedFileContents(device_path)
608 return self.old_interface.GetFileContents(device_path)
610 @decorators.WithTimeoutAndRetriesFromInstance()
611 def WriteFile(self, device_path, contents, as_root=False, timeout=None,
613 """Writes |contents| to a file on the device.
616 device_path: A string containing the absolute path to the file to write
618 contents: A string containing the data to write to the device.
619 as_root: A boolean indicating whether the write should be executed with
621 timeout: timeout in seconds
622 retries: number of retries
625 CommandFailedError if the file could not be written on the device.
626 CommandTimeoutError on timeout.
627 DeviceUnreachableError on missing device.
630 if not self.old_interface.CanAccessProtectedFileContents():
631 raise device_errors.CommandFailedError(
632 'Cannot write to %s with root privileges.' % device_path)
633 self.old_interface.SetProtectedFileContents(device_path, contents)
635 self.old_interface.SetFileContents(device_path, contents)
637 @decorators.WithTimeoutAndRetriesFromInstance()
638 def Ls(self, device_path, timeout=None, retries=None):
639 """Lists the contents of a directory on the device.
642 device_path: A string containing the path of the directory on the device
644 timeout: timeout in seconds
645 retries: number of retries
648 The contents of the directory specified by |device_path|.
651 CommandTimeoutError on timeout.
652 DeviceUnreachableError on missing device.
654 return self.old_interface.ListPathContents(device_path)
656 @decorators.WithTimeoutAndRetriesFromInstance()
657 def SetJavaAsserts(self, enabled, timeout=None, retries=None):
658 """Enables or disables Java asserts.
661 enabled: A boolean indicating whether Java asserts should be enabled
663 timeout: timeout in seconds
664 retries: number of retries
667 CommandTimeoutError on timeout.
669 self.old_interface.SetJavaAssertsEnabled(enabled)
671 @decorators.WithTimeoutAndRetriesFromInstance()
672 def GetProp(self, property_name, timeout=None, retries=None):
673 """Gets a property from the device.
676 property_name: A string containing the name of the property to get from
678 timeout: timeout in seconds
679 retries: number of retries
682 The value of the device's |property_name| property.
685 CommandTimeoutError on timeout.
687 return self.old_interface.system_properties[property_name]
689 @decorators.WithTimeoutAndRetriesFromInstance()
690 def SetProp(self, property_name, value, timeout=None, retries=None):
691 """Sets a property on the device.
694 property_name: A string containing the name of the property to set on
696 value: A string containing the value to set to the property on the
698 timeout: timeout in seconds
699 retries: number of retries
702 CommandTimeoutError on timeout.
704 self.old_interface.system_properties[property_name] = value
706 @decorators.WithTimeoutAndRetriesFromInstance()
707 def GetPids(self, process_name, timeout=None, retries=None):
708 """Returns the PIDs of processes with the given name.
710 Note that the |process_name| is often the package name.
713 process_name: A string containing the process name to get the PIDs for.
714 timeout: timeout in seconds
715 retries: number of retries
718 A dict mapping process name to PID for each process that contained the
719 provided |process_name|.
722 CommandTimeoutError on timeout.
723 DeviceUnreachableError on missing device.
726 for line in self._RunShellCommandImpl('ps'):
728 ps_data = line.split()
729 if process_name in ps_data[-1]:
730 procs_pids[ps_data[-1]] = ps_data[1]
735 @decorators.WithTimeoutAndRetriesFromInstance()
736 def TakeScreenshot(self, host_path=None, timeout=None, retries=None):
737 """Takes a screenshot of the device.
740 host_path: A string containing the path on the host to save the
741 screenshot to. If None, a file name will be generated.
742 timeout: timeout in seconds
743 retries: number of retries
746 The name of the file on the host to which the screenshot was saved.
749 CommandFailedError on failure.
750 CommandTimeoutError on timeout.
751 DeviceUnreachableError on missing device.
753 return self.old_interface.TakeScreenshot(host_path)
755 @decorators.WithTimeoutAndRetriesFromInstance()
756 def GetIOStats(self, timeout=None, retries=None):
757 """Gets cumulative disk IO stats since boot for all processes.
760 timeout: timeout in seconds
761 retries: number of retries
764 A dict containing |num_reads|, |num_writes|, |read_ms|, and |write_ms|.
767 CommandTimeoutError on timeout.
768 DeviceUnreachableError on missing device.
770 return self.old_interface.GetIoStats()
772 @decorators.WithTimeoutAndRetriesFromInstance()
773 def GetMemoryUsageForPid(self, pid, timeout=None, retries=None):
774 """Gets the memory usage for the given PID.
777 pid: PID of the process.
778 timeout: timeout in seconds
779 retries: number of retries
782 A 2-tuple containing:
783 - A dict containing the overall memory usage statistics for the PID.
784 - A dict containing memory usage statistics broken down by mapping.
787 CommandTimeoutError on timeout.
789 return self.old_interface.GetMemoryUsageForPid(pid)
792 """Returns the device serial."""
793 s = self.old_interface.GetDevice()
795 s = self.old_interface.Adb().GetSerialNumber()
797 raise device_errors.NoDevicesError()
801 def parallel(devices=None, async=False):
802 """Creates a Parallelizer to operate over the provided list of devices.
804 If |devices| is either |None| or an empty list, the Parallelizer will
805 operate over all attached devices.
808 devices: A list of either DeviceUtils instances or objects from
809 from which DeviceUtils instances can be constructed. If None,
810 all attached devices will be used.
811 async: If true, returns a Parallelizer that runs operations
815 A Parallelizer operating over |devices|.
817 if not devices or len(devices) == 0:
818 devices = pylib.android_commands.GetAttachedDevices()
819 parallelizer_type = (parallelizer.Parallelizer if async
820 else parallelizer.SyncParallelizer)
821 return parallelizer_type([
822 d if isinstance(d, DeviceUtils) else DeviceUtils(d)