Add InstallSplitApk function to device utils.
[chromium-blink-merge.git] / build / android / pylib / device / device_utils.py
blob558461fc15453165e6a368d0fb6f3fa8e38e99c2
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.
8 """
9 # pylint: disable=unused-argument
11 import collections
12 import contextlib
13 import itertools
14 import logging
15 import multiprocessing
16 import os
17 import posixpath
18 import re
19 import shutil
20 import sys
21 import tempfile
22 import time
23 import zipfile
25 import pylib.android_commands
26 from pylib import cmd_helper
27 from pylib import constants
28 from pylib import device_signal
29 from pylib.device import adb_wrapper
30 from pylib.device import decorators
31 from pylib.device import device_blacklist
32 from pylib.device import device_errors
33 from pylib.device import intent
34 from pylib.device import logcat_monitor
35 from pylib.device.commands import install_commands
36 from pylib.sdk import split_select
37 from pylib.utils import apk_helper
38 from pylib.utils import base_error
39 from pylib.utils import device_temp_file
40 from pylib.utils import host_utils
41 from pylib.utils import md5sum
42 from pylib.utils import parallelizer
43 from pylib.utils import timeout_retry
44 from pylib.utils import zip_utils
46 _DEFAULT_TIMEOUT = 30
47 _DEFAULT_RETRIES = 3
49 # A sentinel object for default values
50 # TODO(jbudorick,perezju): revisit how default values are handled by
51 # the timeout_retry decorators.
52 DEFAULT = object()
54 _CONTROL_CHARGING_COMMANDS = [
56 # Nexus 4
57 'witness_file': '/sys/module/pm8921_charger/parameters/disabled',
58 'enable_command': 'echo 0 > /sys/module/pm8921_charger/parameters/disabled',
59 'disable_command':
60 'echo 1 > /sys/module/pm8921_charger/parameters/disabled',
63 # Nexus 5
64 # Setting the HIZ bit of the bq24192 causes the charger to actually ignore
65 # energy coming from USB. Setting the power_supply offline just updates the
66 # Android system to reflect that.
67 'witness_file': '/sys/kernel/debug/bq24192/INPUT_SRC_CONT',
68 'enable_command': (
69 'echo 0x4A > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && '
70 'echo 1 > /sys/class/power_supply/usb/online'),
71 'disable_command': (
72 'echo 0xCA > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && '
73 'chmod 644 /sys/class/power_supply/usb/online && '
74 'echo 0 > /sys/class/power_supply/usb/online'),
79 @decorators.WithExplicitTimeoutAndRetries(
80 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
81 def GetAVDs():
82 """Returns a list of Android Virtual Devices.
84 Returns:
85 A list containing the configured AVDs.
86 """
87 lines = cmd_helper.GetCmdOutput([
88 os.path.join(constants.ANDROID_SDK_ROOT, 'tools', 'android'),
89 'list', 'avd']).splitlines()
90 avds = []
91 for line in lines:
92 if 'Name:' not in line:
93 continue
94 key, value = (s.strip() for s in line.split(':', 1))
95 if key == 'Name':
96 avds.append(value)
97 return avds
100 @decorators.WithExplicitTimeoutAndRetries(
101 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
102 def RestartServer():
103 """Restarts the adb server.
105 Raises:
106 CommandFailedError if we fail to kill or restart the server.
108 def adb_killed():
109 return not adb_wrapper.AdbWrapper.IsServerOnline()
111 def adb_started():
112 return adb_wrapper.AdbWrapper.IsServerOnline()
114 adb_wrapper.AdbWrapper.KillServer()
115 if not timeout_retry.WaitFor(adb_killed, wait_period=1, max_tries=5):
116 # TODO(perezju): raise an exception after fixng http://crbug.com/442319
117 logging.warning('Failed to kill adb server')
118 adb_wrapper.AdbWrapper.StartServer()
119 if not timeout_retry.WaitFor(adb_started, wait_period=1, max_tries=5):
120 raise device_errors.CommandFailedError('Failed to start adb server')
123 def _GetTimeStamp():
124 """Return a basic ISO 8601 time stamp with the current local time."""
125 return time.strftime('%Y%m%dT%H%M%S', time.localtime())
128 def _JoinLines(lines):
129 # makes sure that the last line is also terminated, and is more memory
130 # efficient than first appending an end-line to each line and then joining
131 # all of them together.
132 return ''.join(s for line in lines for s in (line, '\n'))
135 class DeviceUtils(object):
137 _MAX_ADB_COMMAND_LENGTH = 512
138 _MAX_ADB_OUTPUT_LENGTH = 32768
139 _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$')
141 # Property in /data/local.prop that controls Java assertions.
142 JAVA_ASSERT_PROPERTY = 'dalvik.vm.enableassertions'
144 def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT,
145 default_retries=_DEFAULT_RETRIES):
146 """DeviceUtils constructor.
148 Args:
149 device: Either a device serial, an existing AdbWrapper instance, or an
150 an existing AndroidCommands instance.
151 default_timeout: An integer containing the default number of seconds to
152 wait for an operation to complete if no explicit value
153 is provided.
154 default_retries: An integer containing the default number or times an
155 operation should be retried on failure if no explicit
156 value is provided.
158 self.adb = None
159 self.old_interface = None
160 if isinstance(device, basestring):
161 self.adb = adb_wrapper.AdbWrapper(device)
162 self.old_interface = pylib.android_commands.AndroidCommands(device)
163 elif isinstance(device, adb_wrapper.AdbWrapper):
164 self.adb = device
165 self.old_interface = pylib.android_commands.AndroidCommands(str(device))
166 elif isinstance(device, pylib.android_commands.AndroidCommands):
167 self.adb = adb_wrapper.AdbWrapper(device.GetDevice())
168 self.old_interface = device
169 else:
170 raise ValueError('Unsupported device value: %r' % device)
171 self._commands_installed = None
172 self._default_timeout = default_timeout
173 self._default_retries = default_retries
174 self._cache = {}
175 self._client_caches = {}
176 assert hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR)
177 assert hasattr(self, decorators.DEFAULT_RETRIES_ATTR)
179 def __eq__(self, other):
180 """Checks whether |other| refers to the same device as |self|.
182 Args:
183 other: The object to compare to. This can be a basestring, an instance
184 of adb_wrapper.AdbWrapper, or an instance of DeviceUtils.
185 Returns:
186 Whether |other| refers to the same device as |self|.
188 return self.adb.GetDeviceSerial() == str(other)
190 def __lt__(self, other):
191 """Compares two instances of DeviceUtils.
193 This merely compares their serial numbers.
195 Args:
196 other: The instance of DeviceUtils to compare to.
197 Returns:
198 Whether |self| is less than |other|.
200 return self.adb.GetDeviceSerial() < other.adb.GetDeviceSerial()
202 def __str__(self):
203 """Returns the device serial."""
204 return self.adb.GetDeviceSerial()
206 @decorators.WithTimeoutAndRetriesFromInstance()
207 def IsOnline(self, timeout=None, retries=None):
208 """Checks whether the device is online.
210 Args:
211 timeout: timeout in seconds
212 retries: number of retries
214 Returns:
215 True if the device is online, False otherwise.
217 Raises:
218 CommandTimeoutError on timeout.
220 try:
221 return self.adb.GetState() == 'device'
222 except base_error.BaseError as exc:
223 logging.info('Failed to get state: %s', exc)
224 return False
226 @decorators.WithTimeoutAndRetriesFromInstance()
227 def HasRoot(self, timeout=None, retries=None):
228 """Checks whether or not adbd has root privileges.
230 Args:
231 timeout: timeout in seconds
232 retries: number of retries
234 Returns:
235 True if adbd has root privileges, False otherwise.
237 Raises:
238 CommandTimeoutError on timeout.
239 DeviceUnreachableError on missing device.
241 try:
242 self.RunShellCommand('ls /root', check_return=True)
243 return True
244 except device_errors.AdbCommandFailedError:
245 return False
247 def NeedsSU(self, timeout=DEFAULT, retries=DEFAULT):
248 """Checks whether 'su' is needed to access protected resources.
250 Args:
251 timeout: timeout in seconds
252 retries: number of retries
254 Returns:
255 True if 'su' is available on the device and is needed to to access
256 protected resources; False otherwise if either 'su' is not available
257 (e.g. because the device has a user build), or not needed (because adbd
258 already has root privileges).
260 Raises:
261 CommandTimeoutError on timeout.
262 DeviceUnreachableError on missing device.
264 if 'needs_su' not in self._cache:
265 try:
266 self.RunShellCommand(
267 'su -c ls /root && ! ls /root', check_return=True,
268 timeout=self._default_timeout if timeout is DEFAULT else timeout,
269 retries=self._default_retries if retries is DEFAULT else retries)
270 self._cache['needs_su'] = True
271 except device_errors.AdbCommandFailedError:
272 self._cache['needs_su'] = False
273 return self._cache['needs_su']
276 @decorators.WithTimeoutAndRetriesFromInstance()
277 def EnableRoot(self, timeout=None, retries=None):
278 """Restarts adbd with root privileges.
280 Args:
281 timeout: timeout in seconds
282 retries: number of retries
284 Raises:
285 CommandFailedError if root could not be enabled.
286 CommandTimeoutError on timeout.
288 if self.IsUserBuild():
289 raise device_errors.CommandFailedError(
290 'Cannot enable root in user builds.', str(self))
291 if 'needs_su' in self._cache:
292 del self._cache['needs_su']
293 self.adb.Root()
294 self.WaitUntilFullyBooted()
296 @decorators.WithTimeoutAndRetriesFromInstance()
297 def IsUserBuild(self, timeout=None, retries=None):
298 """Checks whether or not the device is running a user build.
300 Args:
301 timeout: timeout in seconds
302 retries: number of retries
304 Returns:
305 True if the device is running a user build, False otherwise (i.e. if
306 it's running a userdebug build).
308 Raises:
309 CommandTimeoutError on timeout.
310 DeviceUnreachableError on missing device.
312 return self.build_type == 'user'
314 @decorators.WithTimeoutAndRetriesFromInstance()
315 def GetExternalStoragePath(self, timeout=None, retries=None):
316 """Get the device's path to its SD card.
318 Args:
319 timeout: timeout in seconds
320 retries: number of retries
322 Returns:
323 The device's path to its SD card.
325 Raises:
326 CommandFailedError if the external storage path could not be determined.
327 CommandTimeoutError on timeout.
328 DeviceUnreachableError on missing device.
330 if 'external_storage' in self._cache:
331 return self._cache['external_storage']
333 value = self.RunShellCommand('echo $EXTERNAL_STORAGE',
334 single_line=True,
335 check_return=True)
336 if not value:
337 raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set',
338 str(self))
339 self._cache['external_storage'] = value
340 return value
342 @decorators.WithTimeoutAndRetriesFromInstance()
343 def GetApplicationPaths(self, package, timeout=None, retries=None):
344 """Get the paths of the installed apks on the device for the given package.
346 Args:
347 package: Name of the package.
349 Returns:
350 List of paths to the apks on the device for the given package.
352 # 'pm path' is liable to incorrectly exit with a nonzero number starting
353 # in Lollipop.
354 # TODO(jbudorick): Check if this is fixed as new Android versions are
355 # released to put an upper bound on this.
356 should_check_return = (self.build_version_sdk <
357 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP)
358 output = self.RunShellCommand(
359 ['pm', 'path', package], check_return=should_check_return)
360 apks = []
361 for line in output:
362 if not line.startswith('package:'):
363 raise device_errors.CommandFailedError(
364 'pm path returned: %r' % '\n'.join(output), str(self))
365 apks.append(line[len('package:'):])
366 return apks
368 @decorators.WithTimeoutAndRetriesFromInstance()
369 def GetApplicationDataDirectory(self, package, timeout=None, retries=None):
370 """Get the data directory on the device for the given package.
372 Args:
373 package: Name of the package.
375 Returns:
376 The package's data directory, or None if the package doesn't exist on the
377 device.
379 try:
380 output = self._RunPipedShellCommand(
381 'pm dump %s | grep dataDir=' % cmd_helper.SingleQuote(package))
382 for line in output:
383 _, _, dataDir = line.partition('dataDir=')
384 if dataDir:
385 return dataDir
386 except device_errors.CommandFailedError:
387 logging.exception('Could not find data directory for %s', package)
388 return None
390 @decorators.WithTimeoutAndRetriesFromInstance()
391 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None):
392 """Wait for the device to fully boot.
394 This means waiting for the device to boot, the package manager to be
395 available, and the SD card to be ready. It can optionally mean waiting
396 for wifi to come up, too.
398 Args:
399 wifi: A boolean indicating if we should wait for wifi to come up or not.
400 timeout: timeout in seconds
401 retries: number of retries
403 Raises:
404 CommandFailedError on failure.
405 CommandTimeoutError if one of the component waits times out.
406 DeviceUnreachableError if the device becomes unresponsive.
408 def sd_card_ready():
409 try:
410 self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()],
411 check_return=True)
412 return True
413 except device_errors.AdbCommandFailedError:
414 return False
416 def pm_ready():
417 try:
418 return self.GetApplicationPaths('android')
419 except device_errors.CommandFailedError:
420 return False
422 def boot_completed():
423 return self.GetProp('sys.boot_completed') == '1'
425 def wifi_enabled():
426 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'],
427 check_return=False)
429 self.adb.WaitForDevice()
430 timeout_retry.WaitFor(sd_card_ready)
431 timeout_retry.WaitFor(pm_ready)
432 timeout_retry.WaitFor(boot_completed)
433 if wifi:
434 timeout_retry.WaitFor(wifi_enabled)
436 REBOOT_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT
437 REBOOT_DEFAULT_RETRIES = _DEFAULT_RETRIES
439 @decorators.WithTimeoutAndRetriesDefaults(
440 REBOOT_DEFAULT_TIMEOUT,
441 REBOOT_DEFAULT_RETRIES)
442 def Reboot(self, block=True, wifi=False, timeout=None, retries=None):
443 """Reboot the device.
445 Args:
446 block: A boolean indicating if we should wait for the reboot to complete.
447 wifi: A boolean indicating if we should wait for wifi to be enabled after
448 the reboot. The option has no effect unless |block| is also True.
449 timeout: timeout in seconds
450 retries: number of retries
452 Raises:
453 CommandTimeoutError on timeout.
454 DeviceUnreachableError on missing device.
456 def device_offline():
457 return not self.IsOnline()
459 self.adb.Reboot()
460 self._ClearCache()
461 timeout_retry.WaitFor(device_offline, wait_period=1)
462 if block:
463 self.WaitUntilFullyBooted(wifi=wifi)
465 INSTALL_DEFAULT_TIMEOUT = 4 * _DEFAULT_TIMEOUT
466 INSTALL_DEFAULT_RETRIES = _DEFAULT_RETRIES
468 @decorators.WithTimeoutAndRetriesDefaults(
469 INSTALL_DEFAULT_TIMEOUT,
470 INSTALL_DEFAULT_RETRIES)
471 def Install(self, apk_path, reinstall=False, timeout=None, retries=None):
472 """Install an APK.
474 Noop if an identical APK is already installed.
476 Args:
477 apk_path: A string containing the path to the APK to install.
478 reinstall: A boolean indicating if we should keep any existing app data.
479 timeout: timeout in seconds
480 retries: number of retries
482 Raises:
483 CommandFailedError if the installation fails.
484 CommandTimeoutError if the installation times out.
485 DeviceUnreachableError on missing device.
487 package_name = apk_helper.GetPackageName(apk_path)
488 device_paths = self.GetApplicationPaths(package_name)
489 if device_paths:
490 if len(device_paths) > 1:
491 logging.warning(
492 'Installing single APK (%s) when split APKs (%s) are currently '
493 'installed.', apk_path, ' '.join(device_paths))
494 (files_to_push, _) = self._GetChangedAndStaleFiles(
495 apk_path, device_paths[0])
496 should_install = bool(files_to_push)
497 if should_install and not reinstall:
498 self.adb.Uninstall(package_name)
499 else:
500 should_install = True
501 if should_install:
502 self.adb.Install(apk_path, reinstall=reinstall)
504 @decorators.WithTimeoutAndRetriesDefaults(
505 INSTALL_DEFAULT_TIMEOUT,
506 INSTALL_DEFAULT_RETRIES)
507 def InstallSplitApk(self, base_apk, split_apks, reinstall=False,
508 timeout=None, retries=None):
509 """Install a split APK.
511 Noop if all of the APK splits are already installed.
513 Args:
514 base_apk: A string of the path to the base APK.
515 split_apks: A list of strings of paths of all of the APK splits.
516 reinstall: A boolean indicating if we should keep any existing app data.
517 timeout: timeout in seconds
518 retries: number of retries
520 Raises:
521 CommandFailedError if the installation fails.
522 CommandTimeoutError if the installation times out.
523 DeviceUnreachableError on missing device.
524 DeviceVersionError if device SDK is less than Android L.
526 self._CheckSdkLevel(constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP)
528 all_apks = [base_apk] + split_select.SelectSplits(
529 self, base_apk, split_apks)
530 package_name = apk_helper.GetPackageName(base_apk)
531 device_apk_paths = self.GetApplicationPaths(package_name)
533 if device_apk_paths:
534 partial_install_package = package_name
535 device_checksums = md5sum.CalculateDeviceMd5Sums(device_apk_paths, self)
536 host_checksums = md5sum.CalculateHostMd5Sums(all_apks)
537 apks_to_install = [k for (k, v) in host_checksums.iteritems()
538 if v not in device_checksums.values()]
539 if apks_to_install and not reinstall:
540 self.adb.Uninstall(package_name)
541 partial_install_package = None
542 apks_to_install = all_apks
543 else:
544 partial_install_package = None
545 apks_to_install = all_apks
546 if apks_to_install:
547 self.adb.InstallMultiple(
548 apks_to_install, partial=partial_install_package, reinstall=reinstall)
550 def _CheckSdkLevel(self, required_sdk_level):
551 """Raises an exception if the device does not have the required SDK level.
553 if self.build_version_sdk < required_sdk_level:
554 raise device_errors.DeviceVersionError(
555 ('Requires SDK level %s, device is SDK level %s' %
556 (required_sdk_level, self.build_version_sdk)),
557 device_serial=self.adb.GetDeviceSerial())
560 @decorators.WithTimeoutAndRetriesFromInstance()
561 def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None,
562 as_root=False, single_line=False, large_output=False,
563 timeout=None, retries=None):
564 """Run an ADB shell command.
566 The command to run |cmd| should be a sequence of program arguments or else
567 a single string.
569 When |cmd| is a sequence, it is assumed to contain the name of the command
570 to run followed by its arguments. In this case, arguments are passed to the
571 command exactly as given, without any further processing by the shell. This
572 allows to easily pass arguments containing spaces or special characters
573 without having to worry about getting quoting right. Whenever possible, it
574 is recomended to pass |cmd| as a sequence.
576 When |cmd| is given as a string, it will be interpreted and run by the
577 shell on the device.
579 This behaviour is consistent with that of command runners in cmd_helper as
580 well as Python's own subprocess.Popen.
582 TODO(perezju) Change the default of |check_return| to True when callers
583 have switched to the new behaviour.
585 Args:
586 cmd: A string with the full command to run on the device, or a sequence
587 containing the command and its arguments.
588 check_return: A boolean indicating whether or not the return code should
589 be checked.
590 cwd: The device directory in which the command should be run.
591 env: The environment variables with which the command should be run.
592 as_root: A boolean indicating whether the shell command should be run
593 with root privileges.
594 single_line: A boolean indicating if only a single line of output is
595 expected.
596 large_output: Uses a work-around for large shell command output. Without
597 this large output will be truncated.
598 timeout: timeout in seconds
599 retries: number of retries
601 Returns:
602 If single_line is False, the output of the command as a list of lines,
603 otherwise, a string with the unique line of output emmited by the command
604 (with the optional newline at the end stripped).
606 Raises:
607 AdbCommandFailedError if check_return is True and the exit code of
608 the command run on the device is non-zero.
609 CommandFailedError if single_line is True but the output contains two or
610 more lines.
611 CommandTimeoutError on timeout.
612 DeviceUnreachableError on missing device.
614 def env_quote(key, value):
615 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key):
616 raise KeyError('Invalid shell variable name %r' % key)
617 # using double quotes here to allow interpolation of shell variables
618 return '%s=%s' % (key, cmd_helper.DoubleQuote(value))
620 def run(cmd):
621 return self.adb.Shell(cmd)
623 def handle_check_return(cmd):
624 try:
625 return run(cmd)
626 except device_errors.AdbCommandFailedError as exc:
627 if check_return:
628 raise
629 else:
630 return exc.output
632 def handle_large_command(cmd):
633 if len(cmd) < self._MAX_ADB_COMMAND_LENGTH:
634 return handle_check_return(cmd)
635 else:
636 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script:
637 self._WriteFileWithPush(script.name, cmd)
638 logging.info('Large shell command will be run from file: %s ...',
639 cmd[:100])
640 return handle_check_return('sh %s' % script.name_quoted)
642 def handle_large_output(cmd, large_output_mode):
643 if large_output_mode:
644 with device_temp_file.DeviceTempFile(self.adb) as large_output_file:
645 cmd = '%s > %s' % (cmd, large_output_file.name)
646 logging.debug('Large output mode enabled. Will write output to '
647 'device and read results from file.')
648 handle_large_command(cmd)
649 return self.ReadFile(large_output_file.name, force_pull=True)
650 else:
651 try:
652 return handle_large_command(cmd)
653 except device_errors.AdbCommandFailedError as exc:
654 if exc.status is None:
655 logging.exception('No output found for %s', cmd)
656 logging.warning('Attempting to run in large_output mode.')
657 logging.warning('Use RunShellCommand(..., large_output=True) for '
658 'shell commands that expect a lot of output.')
659 return handle_large_output(cmd, True)
660 else:
661 raise
663 if not isinstance(cmd, basestring):
664 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd)
665 if env:
666 env = ' '.join(env_quote(k, v) for k, v in env.iteritems())
667 cmd = '%s %s' % (env, cmd)
668 if cwd:
669 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd)
670 if as_root and self.NeedsSU():
671 # "su -c sh -c" allows using shell features in |cmd|
672 cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd)
674 output = handle_large_output(cmd, large_output).splitlines()
676 if single_line:
677 if not output:
678 return ''
679 elif len(output) == 1:
680 return output[0]
681 else:
682 msg = 'one line of output was expected, but got: %s'
683 raise device_errors.CommandFailedError(msg % output, str(self))
684 else:
685 return output
687 def _RunPipedShellCommand(self, script, **kwargs):
688 PIPESTATUS_LEADER = 'PIPESTATUS: '
690 script += '; echo "%s${PIPESTATUS[@]}"' % PIPESTATUS_LEADER
691 kwargs['check_return'] = True
692 output = self.RunShellCommand(script, **kwargs)
693 pipestatus_line = output[-1]
695 if not pipestatus_line.startswith(PIPESTATUS_LEADER):
696 logging.error('Pipe exit statuses of shell script missing.')
697 raise device_errors.AdbShellCommandFailedError(
698 script, output, status=None,
699 device_serial=self.adb.GetDeviceSerial())
701 output = output[:-1]
702 statuses = [
703 int(s) for s in pipestatus_line[len(PIPESTATUS_LEADER):].split()]
704 if any(statuses):
705 raise device_errors.AdbShellCommandFailedError(
706 script, output, status=statuses,
707 device_serial=self.adb.GetDeviceSerial())
708 return output
710 @decorators.WithTimeoutAndRetriesFromInstance()
711 def KillAll(self, process_name, signum=device_signal.SIGKILL, as_root=False,
712 blocking=False, quiet=False, timeout=None, retries=None):
713 """Kill all processes with the given name on the device.
715 Args:
716 process_name: A string containing the name of the process to kill.
717 signum: An integer containing the signal number to send to kill. Defaults
718 to SIGKILL (9).
719 as_root: A boolean indicating whether the kill should be executed with
720 root privileges.
721 blocking: A boolean indicating whether we should wait until all processes
722 with the given |process_name| are dead.
723 quiet: A boolean indicating whether to ignore the fact that no processes
724 to kill were found.
725 timeout: timeout in seconds
726 retries: number of retries
728 Returns:
729 The number of processes attempted to kill.
731 Raises:
732 CommandFailedError if no process was killed and |quiet| is False.
733 CommandTimeoutError on timeout.
734 DeviceUnreachableError on missing device.
736 pids = self.GetPids(process_name)
737 if not pids:
738 if quiet:
739 return 0
740 else:
741 raise device_errors.CommandFailedError(
742 'No process "%s"' % process_name, str(self))
744 cmd = ['kill', '-%d' % signum] + pids.values()
745 self.RunShellCommand(cmd, as_root=as_root, check_return=True)
747 if blocking:
748 # TODO(perezu): use timeout_retry.WaitFor
749 wait_period = 0.1
750 while self.GetPids(process_name):
751 time.sleep(wait_period)
753 return len(pids)
755 @decorators.WithTimeoutAndRetriesFromInstance()
756 def StartActivity(self, intent_obj, blocking=False, trace_file_name=None,
757 force_stop=False, timeout=None, retries=None):
758 """Start package's activity on the device.
760 Args:
761 intent_obj: An Intent object to send.
762 blocking: A boolean indicating whether we should wait for the activity to
763 finish launching.
764 trace_file_name: If present, a string that both indicates that we want to
765 profile the activity and contains the path to which the
766 trace should be saved.
767 force_stop: A boolean indicating whether we should stop the activity
768 before starting it.
769 timeout: timeout in seconds
770 retries: number of retries
772 Raises:
773 CommandFailedError if the activity could not be started.
774 CommandTimeoutError on timeout.
775 DeviceUnreachableError on missing device.
777 cmd = ['am', 'start']
778 if blocking:
779 cmd.append('-W')
780 if trace_file_name:
781 cmd.extend(['--start-profiler', trace_file_name])
782 if force_stop:
783 cmd.append('-S')
784 cmd.extend(intent_obj.am_args)
785 for line in self.RunShellCommand(cmd, check_return=True):
786 if line.startswith('Error:'):
787 raise device_errors.CommandFailedError(line, str(self))
789 @decorators.WithTimeoutAndRetriesFromInstance()
790 def StartInstrumentation(self, component, finish=True, raw=False,
791 extras=None, timeout=None, retries=None):
792 if extras is None:
793 extras = {}
795 cmd = ['am', 'instrument']
796 if finish:
797 cmd.append('-w')
798 if raw:
799 cmd.append('-r')
800 for k, v in extras.iteritems():
801 cmd.extend(['-e', str(k), str(v)])
802 cmd.append(component)
803 return self.RunShellCommand(cmd, check_return=True, large_output=True)
805 @decorators.WithTimeoutAndRetriesFromInstance()
806 def BroadcastIntent(self, intent_obj, timeout=None, retries=None):
807 """Send a broadcast intent.
809 Args:
810 intent: An Intent to broadcast.
811 timeout: timeout in seconds
812 retries: number of retries
814 Raises:
815 CommandTimeoutError on timeout.
816 DeviceUnreachableError on missing device.
818 cmd = ['am', 'broadcast'] + intent_obj.am_args
819 self.RunShellCommand(cmd, check_return=True)
821 @decorators.WithTimeoutAndRetriesFromInstance()
822 def GoHome(self, timeout=None, retries=None):
823 """Return to the home screen.
825 Args:
826 timeout: timeout in seconds
827 retries: number of retries
829 Raises:
830 CommandTimeoutError on timeout.
831 DeviceUnreachableError on missing device.
833 self.StartActivity(
834 intent.Intent(action='android.intent.action.MAIN',
835 category='android.intent.category.HOME'),
836 blocking=True)
838 @decorators.WithTimeoutAndRetriesFromInstance()
839 def ForceStop(self, package, timeout=None, retries=None):
840 """Close the application.
842 Args:
843 package: A string containing the name of the package to stop.
844 timeout: timeout in seconds
845 retries: number of retries
847 Raises:
848 CommandTimeoutError on timeout.
849 DeviceUnreachableError on missing device.
851 self.RunShellCommand(['am', 'force-stop', package], check_return=True)
853 @decorators.WithTimeoutAndRetriesFromInstance()
854 def ClearApplicationState(self, package, timeout=None, retries=None):
855 """Clear all state for the given package.
857 Args:
858 package: A string containing the name of the package to stop.
859 timeout: timeout in seconds
860 retries: number of retries
862 Raises:
863 CommandTimeoutError on timeout.
864 DeviceUnreachableError on missing device.
866 # Check that the package exists before clearing it for android builds below
867 # JB MR2. Necessary because calling pm clear on a package that doesn't exist
868 # may never return.
869 if ((self.build_version_sdk >=
870 constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN_MR2)
871 or self.GetApplicationPaths(package)):
872 self.RunShellCommand(['pm', 'clear', package], check_return=True)
874 @decorators.WithTimeoutAndRetriesFromInstance()
875 def SendKeyEvent(self, keycode, timeout=None, retries=None):
876 """Sends a keycode to the device.
878 See the pylib.constants.keyevent module for suitable keycode values.
880 Args:
881 keycode: A integer keycode to send to the device.
882 timeout: timeout in seconds
883 retries: number of retries
885 Raises:
886 CommandTimeoutError on timeout.
887 DeviceUnreachableError on missing device.
889 self.RunShellCommand(['input', 'keyevent', format(keycode, 'd')],
890 check_return=True)
892 PUSH_CHANGED_FILES_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT
893 PUSH_CHANGED_FILES_DEFAULT_RETRIES = _DEFAULT_RETRIES
895 @decorators.WithTimeoutAndRetriesDefaults(
896 PUSH_CHANGED_FILES_DEFAULT_TIMEOUT,
897 PUSH_CHANGED_FILES_DEFAULT_RETRIES)
898 def PushChangedFiles(self, host_device_tuples, timeout=None,
899 retries=None, delete_device_stale=False):
900 """Push files to the device, skipping files that don't need updating.
902 When a directory is pushed, it is traversed recursively on the host and
903 all files in it are pushed to the device as needed.
904 Additionally, if delete_device_stale option is True,
905 files that exist on the device but don't exist on the host are deleted.
907 Args:
908 host_device_tuples: A list of (host_path, device_path) tuples, where
909 |host_path| is an absolute path of a file or directory on the host
910 that should be minimially pushed to the device, and |device_path| is
911 an absolute path of the destination on the device.
912 timeout: timeout in seconds
913 retries: number of retries
914 delete_device_stale: option to delete stale files on device
916 Raises:
917 CommandFailedError on failure.
918 CommandTimeoutError on timeout.
919 DeviceUnreachableError on missing device.
922 all_changed_files = []
923 all_stale_files = []
924 for h, d in host_device_tuples:
925 if os.path.isdir(h):
926 self.RunShellCommand(['mkdir', '-p', d], check_return=True)
927 (changed_files, stale_files) = self._GetChangedAndStaleFiles(h, d)
928 all_changed_files += changed_files
929 all_stale_files += stale_files
931 if delete_device_stale:
932 self.RunShellCommand(['rm', '-f'] + all_stale_files,
933 check_return=True)
935 if not all_changed_files:
936 return
938 self._PushFilesImpl(host_device_tuples, all_changed_files)
940 def _GetChangedAndStaleFiles(self, host_path, device_path):
941 """Get files to push and delete
943 Args:
944 host_path: an absolute path of a file or directory on the host
945 device_path: an absolute path of a file or directory on the device
947 Returns:
948 a two-element tuple
949 1st element: a list of (host_files_path, device_files_path) tuples to push
950 2nd element: a list of stale files under device_path
952 real_host_path = os.path.realpath(host_path)
953 try:
954 real_device_path = self.RunShellCommand(
955 ['realpath', device_path], single_line=True, check_return=True)
956 except device_errors.CommandFailedError:
957 real_device_path = None
958 if not real_device_path:
959 return ([(host_path, device_path)], [])
961 try:
962 host_checksums = md5sum.CalculateHostMd5Sums([real_host_path])
963 device_checksums = md5sum.CalculateDeviceMd5Sums(
964 [real_device_path], self)
965 except EnvironmentError as e:
966 logging.warning('Error calculating md5: %s', e)
967 return ([(host_path, device_path)], [])
969 if os.path.isfile(host_path):
970 host_checksum = host_checksums.get(real_host_path)
971 device_checksum = device_checksums.get(real_device_path)
972 if host_checksum != device_checksum:
973 return ([(host_path, device_path)], [])
974 else:
975 return ([], [])
976 else:
977 to_push = []
978 for host_abs_path, host_checksum in host_checksums.iteritems():
979 device_abs_path = '%s/%s' % (
980 real_device_path, os.path.relpath(host_abs_path, real_host_path))
981 device_checksum = device_checksums.pop(device_abs_path, None)
982 if device_checksum != host_checksum:
983 to_push.append((host_abs_path, device_abs_path))
984 to_delete = device_checksums.keys()
985 return (to_push, to_delete)
987 def _PushFilesImpl(self, host_device_tuples, files):
988 size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files)
989 file_count = len(files)
990 dir_size = sum(host_utils.GetRecursiveDiskUsage(h)
991 for h, _ in host_device_tuples)
992 dir_file_count = 0
993 for h, _ in host_device_tuples:
994 if os.path.isdir(h):
995 dir_file_count += sum(len(f) for _r, _d, f in os.walk(h))
996 else:
997 dir_file_count += 1
999 push_duration = self._ApproximateDuration(
1000 file_count, file_count, size, False)
1001 dir_push_duration = self._ApproximateDuration(
1002 len(host_device_tuples), dir_file_count, dir_size, False)
1003 zip_duration = self._ApproximateDuration(1, 1, size, True)
1005 self._InstallCommands()
1007 if dir_push_duration < push_duration and (
1008 dir_push_duration < zip_duration or not self._commands_installed):
1009 self._PushChangedFilesIndividually(host_device_tuples)
1010 elif push_duration < zip_duration or not self._commands_installed:
1011 self._PushChangedFilesIndividually(files)
1012 else:
1013 self._PushChangedFilesZipped(files)
1014 self.RunShellCommand(
1015 ['chmod', '-R', '777'] + [d for _, d in host_device_tuples],
1016 as_root=True, check_return=True)
1018 def _InstallCommands(self):
1019 if self._commands_installed is None:
1020 try:
1021 if not install_commands.Installed(self):
1022 install_commands.InstallCommands(self)
1023 self._commands_installed = True
1024 except Exception as e:
1025 logging.warning('unzip not available: %s' % str(e))
1026 self._commands_installed = False
1028 @staticmethod
1029 def _ApproximateDuration(adb_calls, file_count, byte_count, is_zipping):
1030 # We approximate the time to push a set of files to a device as:
1031 # t = c1 * a + c2 * f + c3 + b / c4 + b / (c5 * c6), where
1032 # t: total time (sec)
1033 # c1: adb call time delay (sec)
1034 # a: number of times adb is called (unitless)
1035 # c2: push time delay (sec)
1036 # f: number of files pushed via adb (unitless)
1037 # c3: zip time delay (sec)
1038 # c4: zip rate (bytes/sec)
1039 # b: total number of bytes (bytes)
1040 # c5: transfer rate (bytes/sec)
1041 # c6: compression ratio (unitless)
1043 # All of these are approximations.
1044 ADB_CALL_PENALTY = 0.1 # seconds
1045 ADB_PUSH_PENALTY = 0.01 # seconds
1046 ZIP_PENALTY = 2.0 # seconds
1047 ZIP_RATE = 10000000.0 # bytes / second
1048 TRANSFER_RATE = 2000000.0 # bytes / second
1049 COMPRESSION_RATIO = 2.0 # unitless
1051 adb_call_time = ADB_CALL_PENALTY * adb_calls
1052 adb_push_setup_time = ADB_PUSH_PENALTY * file_count
1053 if is_zipping:
1054 zip_time = ZIP_PENALTY + byte_count / ZIP_RATE
1055 transfer_time = byte_count / (TRANSFER_RATE * COMPRESSION_RATIO)
1056 else:
1057 zip_time = 0
1058 transfer_time = byte_count / TRANSFER_RATE
1059 return adb_call_time + adb_push_setup_time + zip_time + transfer_time
1061 def _PushChangedFilesIndividually(self, files):
1062 for h, d in files:
1063 self.adb.Push(h, d)
1065 def _PushChangedFilesZipped(self, files):
1066 if not files:
1067 return
1069 with tempfile.NamedTemporaryFile(suffix='.zip') as zip_file:
1070 zip_proc = multiprocessing.Process(
1071 target=DeviceUtils._CreateDeviceZip,
1072 args=(zip_file.name, files))
1073 zip_proc.start()
1074 zip_proc.join()
1076 zip_on_device = '%s/tmp.zip' % self.GetExternalStoragePath()
1077 try:
1078 self.adb.Push(zip_file.name, zip_on_device)
1079 self.RunShellCommand(
1080 ['unzip', zip_on_device],
1081 as_root=True,
1082 env={'PATH': '%s:$PATH' % install_commands.BIN_DIR},
1083 check_return=True)
1084 finally:
1085 if zip_proc.is_alive():
1086 zip_proc.terminate()
1087 if self.IsOnline():
1088 self.RunShellCommand(['rm', zip_on_device], check_return=True)
1090 @staticmethod
1091 def _CreateDeviceZip(zip_path, host_device_tuples):
1092 with zipfile.ZipFile(zip_path, 'w') as zip_file:
1093 for host_path, device_path in host_device_tuples:
1094 zip_utils.WriteToZipFile(zip_file, host_path, device_path)
1096 @decorators.WithTimeoutAndRetriesFromInstance()
1097 def FileExists(self, device_path, timeout=None, retries=None):
1098 """Checks whether the given file exists on the device.
1100 Args:
1101 device_path: A string containing the absolute path to the file on the
1102 device.
1103 timeout: timeout in seconds
1104 retries: number of retries
1106 Returns:
1107 True if the file exists on the device, False otherwise.
1109 Raises:
1110 CommandTimeoutError on timeout.
1111 DeviceUnreachableError on missing device.
1113 try:
1114 self.RunShellCommand(['test', '-e', device_path], check_return=True)
1115 return True
1116 except device_errors.AdbCommandFailedError:
1117 return False
1119 @decorators.WithTimeoutAndRetriesFromInstance()
1120 def PullFile(self, device_path, host_path, timeout=None, retries=None):
1121 """Pull a file from the device.
1123 Args:
1124 device_path: A string containing the absolute path of the file to pull
1125 from the device.
1126 host_path: A string containing the absolute path of the destination on
1127 the host.
1128 timeout: timeout in seconds
1129 retries: number of retries
1131 Raises:
1132 CommandFailedError on failure.
1133 CommandTimeoutError on timeout.
1135 # Create the base dir if it doesn't exist already
1136 dirname = os.path.dirname(host_path)
1137 if dirname and not os.path.exists(dirname):
1138 os.makedirs(dirname)
1139 self.adb.Pull(device_path, host_path)
1141 def _ReadFileWithPull(self, device_path):
1142 try:
1143 d = tempfile.mkdtemp()
1144 host_temp_path = os.path.join(d, 'tmp_ReadFileWithPull')
1145 self.adb.Pull(device_path, host_temp_path)
1146 with open(host_temp_path, 'r') as host_temp:
1147 return host_temp.read()
1148 finally:
1149 if os.path.exists(d):
1150 shutil.rmtree(d)
1152 _LS_RE = re.compile(
1153 r'(?P<perms>\S+) +(?P<owner>\S+) +(?P<group>\S+) +(?:(?P<size>\d+) +)?'
1154 + r'(?P<date>\S+) +(?P<time>\S+) +(?P<name>.+)$')
1156 @decorators.WithTimeoutAndRetriesFromInstance()
1157 def ReadFile(self, device_path, as_root=False, force_pull=False,
1158 timeout=None, retries=None):
1159 """Reads the contents of a file from the device.
1161 Args:
1162 device_path: A string containing the absolute path of the file to read
1163 from the device.
1164 as_root: A boolean indicating whether the read should be executed with
1165 root privileges.
1166 force_pull: A boolean indicating whether to force the operation to be
1167 performed by pulling a file from the device. The default is, when the
1168 contents are short, to retrieve the contents using cat instead.
1169 timeout: timeout in seconds
1170 retries: number of retries
1172 Returns:
1173 The contents of |device_path| as a string. Contents are intepreted using
1174 universal newlines, so the caller will see them encoded as '\n'. Also,
1175 all lines will be terminated.
1177 Raises:
1178 AdbCommandFailedError if the file can't be read.
1179 CommandTimeoutError on timeout.
1180 DeviceUnreachableError on missing device.
1182 def get_size(path):
1183 # TODO(jbudorick): Implement a generic version of Stat() that handles
1184 # as_root=True, then switch this implementation to use that.
1185 ls_out = self.RunShellCommand(['ls', '-l', device_path], as_root=as_root,
1186 check_return=True)
1187 for line in ls_out:
1188 m = self._LS_RE.match(line)
1189 if m and m.group('name') == posixpath.basename(device_path):
1190 return int(m.group('size'))
1191 logging.warning('Could not determine size of %s.', device_path)
1192 return None
1194 if (not force_pull
1195 and 0 < get_size(device_path) <= self._MAX_ADB_OUTPUT_LENGTH):
1196 return _JoinLines(self.RunShellCommand(
1197 ['cat', device_path], as_root=as_root, check_return=True))
1198 elif as_root and self.NeedsSU():
1199 with device_temp_file.DeviceTempFile(self.adb) as device_temp:
1200 self.RunShellCommand(['cp', device_path, device_temp.name],
1201 as_root=True, check_return=True)
1202 return self._ReadFileWithPull(device_temp.name)
1203 else:
1204 return self._ReadFileWithPull(device_path)
1206 def _WriteFileWithPush(self, device_path, contents):
1207 with tempfile.NamedTemporaryFile() as host_temp:
1208 host_temp.write(contents)
1209 host_temp.flush()
1210 self.adb.Push(host_temp.name, device_path)
1212 @decorators.WithTimeoutAndRetriesFromInstance()
1213 def WriteFile(self, device_path, contents, as_root=False, force_push=False,
1214 timeout=None, retries=None):
1215 """Writes |contents| to a file on the device.
1217 Args:
1218 device_path: A string containing the absolute path to the file to write
1219 on the device.
1220 contents: A string containing the data to write to the device.
1221 as_root: A boolean indicating whether the write should be executed with
1222 root privileges (if available).
1223 force_push: A boolean indicating whether to force the operation to be
1224 performed by pushing a file to the device. The default is, when the
1225 contents are short, to pass the contents using a shell script instead.
1226 timeout: timeout in seconds
1227 retries: number of retries
1229 Raises:
1230 CommandFailedError if the file could not be written on the device.
1231 CommandTimeoutError on timeout.
1232 DeviceUnreachableError on missing device.
1234 if not force_push and len(contents) < self._MAX_ADB_COMMAND_LENGTH:
1235 # If the contents are small, for efficieny we write the contents with
1236 # a shell command rather than pushing a file.
1237 cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents),
1238 cmd_helper.SingleQuote(device_path))
1239 self.RunShellCommand(cmd, as_root=as_root, check_return=True)
1240 elif as_root and self.NeedsSU():
1241 # Adb does not allow to "push with su", so we first push to a temp file
1242 # on a safe location, and then copy it to the desired location with su.
1243 with device_temp_file.DeviceTempFile(self.adb) as device_temp:
1244 self._WriteFileWithPush(device_temp.name, contents)
1245 # Here we need 'cp' rather than 'mv' because the temp and
1246 # destination files might be on different file systems (e.g.
1247 # on internal storage and an external sd card).
1248 self.RunShellCommand(['cp', device_temp.name, device_path],
1249 as_root=True, check_return=True)
1250 else:
1251 # If root is not needed, we can push directly to the desired location.
1252 self._WriteFileWithPush(device_path, contents)
1254 @decorators.WithTimeoutAndRetriesFromInstance()
1255 def Ls(self, device_path, timeout=None, retries=None):
1256 """Lists the contents of a directory on the device.
1258 Args:
1259 device_path: A string containing the path of the directory on the device
1260 to list.
1261 timeout: timeout in seconds
1262 retries: number of retries
1264 Returns:
1265 A list of pairs (filename, stat) for each file found in the directory,
1266 where the stat object has the properties: st_mode, st_size, and st_time.
1268 Raises:
1269 AdbCommandFailedError if |device_path| does not specify a valid and
1270 accessible directory in the device.
1271 CommandTimeoutError on timeout.
1272 DeviceUnreachableError on missing device.
1274 return self.adb.Ls(device_path)
1276 @decorators.WithTimeoutAndRetriesFromInstance()
1277 def Stat(self, device_path, timeout=None, retries=None):
1278 """Get the stat attributes of a file or directory on the device.
1280 Args:
1281 device_path: A string containing the path of from which to get attributes
1282 on the device.
1283 timeout: timeout in seconds
1284 retries: number of retries
1286 Returns:
1287 A stat object with the properties: st_mode, st_size, and st_time
1289 Raises:
1290 CommandFailedError if device_path cannot be found on the device.
1291 CommandTimeoutError on timeout.
1292 DeviceUnreachableError on missing device.
1294 dirname, target = device_path.rsplit('/', 1)
1295 for filename, stat in self.adb.Ls(dirname):
1296 if filename == target:
1297 return stat
1298 raise device_errors.CommandFailedError(
1299 'Cannot find file or directory: %r' % device_path, str(self))
1301 @decorators.WithTimeoutAndRetriesFromInstance()
1302 def SetJavaAsserts(self, enabled, timeout=None, retries=None):
1303 """Enables or disables Java asserts.
1305 Args:
1306 enabled: A boolean indicating whether Java asserts should be enabled
1307 or disabled.
1308 timeout: timeout in seconds
1309 retries: number of retries
1311 Returns:
1312 True if the device-side property changed and a restart is required as a
1313 result, False otherwise.
1315 Raises:
1316 CommandTimeoutError on timeout.
1318 def find_property(lines, property_name):
1319 for index, line in enumerate(lines):
1320 if line.strip() == '':
1321 continue
1322 key, value = (s.strip() for s in line.split('=', 1))
1323 if key == property_name:
1324 return index, value
1325 return None, ''
1327 new_value = 'all' if enabled else ''
1329 # First ensure the desired property is persisted.
1330 try:
1331 properties = self.ReadFile(
1332 constants.DEVICE_LOCAL_PROPERTIES_PATH).splitlines()
1333 except device_errors.CommandFailedError:
1334 properties = []
1335 index, value = find_property(properties, self.JAVA_ASSERT_PROPERTY)
1336 if new_value != value:
1337 if new_value:
1338 new_line = '%s=%s' % (self.JAVA_ASSERT_PROPERTY, new_value)
1339 if index is None:
1340 properties.append(new_line)
1341 else:
1342 properties[index] = new_line
1343 else:
1344 assert index is not None # since new_value == '' and new_value != value
1345 properties.pop(index)
1346 self.WriteFile(constants.DEVICE_LOCAL_PROPERTIES_PATH,
1347 _JoinLines(properties))
1349 # Next, check the current runtime value is what we need, and
1350 # if not, set it and report that a reboot is required.
1351 value = self.GetProp(self.JAVA_ASSERT_PROPERTY)
1352 if new_value != value:
1353 self.SetProp(self.JAVA_ASSERT_PROPERTY, new_value)
1354 return True
1355 else:
1356 return False
1358 @property
1359 def language(self):
1360 """Returns the language setting on the device."""
1361 return self.GetProp('persist.sys.language', cache=False)
1363 @property
1364 def country(self):
1365 """Returns the country setting on the device."""
1366 return self.GetProp('persist.sys.country', cache=False)
1368 @property
1369 def screen_density(self):
1370 """Returns the screen density of the device."""
1371 DPI_TO_DENSITY = {
1372 120: 'ldpi',
1373 160: 'mdpi',
1374 240: 'hdpi',
1375 320: 'xhdpi',
1376 480: 'xxhdpi',
1378 dpi = int(self.GetProp('ro.sf.lcd_density', cache=True))
1379 return DPI_TO_DENSITY.get(dpi, 'tvdpi')
1381 @property
1382 def build_description(self):
1383 """Returns the build description of the system.
1385 For example:
1386 nakasi-user 4.4.4 KTU84P 1227136 release-keys
1388 return self.GetProp('ro.build.description', cache=True)
1390 @property
1391 def build_fingerprint(self):
1392 """Returns the build fingerprint of the system.
1394 For example:
1395 google/nakasi/grouper:4.4.4/KTU84P/1227136:user/release-keys
1397 return self.GetProp('ro.build.fingerprint', cache=True)
1399 @property
1400 def build_id(self):
1401 """Returns the build ID of the system (e.g. 'KTU84P')."""
1402 return self.GetProp('ro.build.id', cache=True)
1404 @property
1405 def build_product(self):
1406 """Returns the build product of the system (e.g. 'grouper')."""
1407 return self.GetProp('ro.build.product', cache=True)
1409 @property
1410 def build_type(self):
1411 """Returns the build type of the system (e.g. 'user')."""
1412 return self.GetProp('ro.build.type', cache=True)
1414 @property
1415 def build_version_sdk(self):
1416 """Returns the build version sdk of the system as a number (e.g. 19).
1418 For version code numbers see:
1419 http://developer.android.com/reference/android/os/Build.VERSION_CODES.html
1421 For named constants see:
1422 pylib.constants.ANDROID_SDK_VERSION_CODES
1424 Raises:
1425 CommandFailedError if the build version sdk is not a number.
1427 value = self.GetProp('ro.build.version.sdk', cache=True)
1428 try:
1429 return int(value)
1430 except ValueError:
1431 raise device_errors.CommandFailedError(
1432 'Invalid build version sdk: %r' % value)
1434 @property
1435 def product_cpu_abi(self):
1436 """Returns the product cpu abi of the device (e.g. 'armeabi-v7a')."""
1437 return self.GetProp('ro.product.cpu.abi', cache=True)
1439 @property
1440 def product_model(self):
1441 """Returns the name of the product model (e.g. 'Nexus 7')."""
1442 return self.GetProp('ro.product.model', cache=True)
1444 @property
1445 def product_name(self):
1446 """Returns the product name of the device (e.g. 'nakasi')."""
1447 return self.GetProp('ro.product.name', cache=True)
1449 def GetProp(self, property_name, cache=False, timeout=DEFAULT,
1450 retries=DEFAULT):
1451 """Gets a property from the device.
1453 Args:
1454 property_name: A string containing the name of the property to get from
1455 the device.
1456 cache: A boolean indicating whether to cache the value of this property.
1457 timeout: timeout in seconds
1458 retries: number of retries
1460 Returns:
1461 The value of the device's |property_name| property.
1463 Raises:
1464 CommandTimeoutError on timeout.
1466 assert isinstance(property_name, basestring), (
1467 "property_name is not a string: %r" % property_name)
1469 cache_key = '_prop:' + property_name
1470 if cache and cache_key in self._cache:
1471 return self._cache[cache_key]
1472 else:
1473 # timeout and retries are handled down at run shell, because we don't
1474 # want to apply them in the other branch when reading from the cache
1475 value = self.RunShellCommand(
1476 ['getprop', property_name], single_line=True, check_return=True,
1477 timeout=self._default_timeout if timeout is DEFAULT else timeout,
1478 retries=self._default_retries if retries is DEFAULT else retries)
1479 if cache or cache_key in self._cache:
1480 self._cache[cache_key] = value
1481 return value
1483 @decorators.WithTimeoutAndRetriesFromInstance()
1484 def SetProp(self, property_name, value, check=False, timeout=None,
1485 retries=None):
1486 """Sets a property on the device.
1488 Args:
1489 property_name: A string containing the name of the property to set on
1490 the device.
1491 value: A string containing the value to set to the property on the
1492 device.
1493 check: A boolean indicating whether to check that the property was
1494 successfully set on the device.
1495 timeout: timeout in seconds
1496 retries: number of retries
1498 Raises:
1499 CommandFailedError if check is true and the property was not correctly
1500 set on the device (e.g. because it is not rooted).
1501 CommandTimeoutError on timeout.
1503 assert isinstance(property_name, basestring), (
1504 "property_name is not a string: %r" % property_name)
1505 assert isinstance(value, basestring), "value is not a string: %r" % value
1507 self.RunShellCommand(['setprop', property_name, value], check_return=True)
1508 if property_name in self._cache:
1509 del self._cache[property_name]
1510 # TODO(perezju) remove the option and make the check mandatory, but using a
1511 # single shell script to both set- and getprop.
1512 if check and value != self.GetProp(property_name):
1513 raise device_errors.CommandFailedError(
1514 'Unable to set property %r on the device to %r'
1515 % (property_name, value), str(self))
1517 @decorators.WithTimeoutAndRetriesFromInstance()
1518 def GetABI(self, timeout=None, retries=None):
1519 """Gets the device main ABI.
1521 Args:
1522 timeout: timeout in seconds
1523 retries: number of retries
1525 Returns:
1526 The device's main ABI name.
1528 Raises:
1529 CommandTimeoutError on timeout.
1531 return self.GetProp('ro.product.cpu.abi')
1533 @decorators.WithTimeoutAndRetriesFromInstance()
1534 def GetPids(self, process_name, timeout=None, retries=None):
1535 """Returns the PIDs of processes with the given name.
1537 Note that the |process_name| is often the package name.
1539 Args:
1540 process_name: A string containing the process name to get the PIDs for.
1541 timeout: timeout in seconds
1542 retries: number of retries
1544 Returns:
1545 A dict mapping process name to PID for each process that contained the
1546 provided |process_name|.
1548 Raises:
1549 CommandTimeoutError on timeout.
1550 DeviceUnreachableError on missing device.
1552 procs_pids = {}
1553 try:
1554 ps_output = self._RunPipedShellCommand(
1555 'ps | grep -F %s' % cmd_helper.SingleQuote(process_name))
1556 except device_errors.AdbShellCommandFailedError as e:
1557 if e.status and isinstance(e.status, list) and not e.status[0]:
1558 # If ps succeeded but grep failed, there were no processes with the
1559 # given name.
1560 return procs_pids
1561 else:
1562 raise
1564 for line in ps_output:
1565 try:
1566 ps_data = line.split()
1567 if process_name in ps_data[-1]:
1568 procs_pids[ps_data[-1]] = ps_data[1]
1569 except IndexError:
1570 pass
1571 return procs_pids
1573 @decorators.WithTimeoutAndRetriesFromInstance()
1574 def TakeScreenshot(self, host_path=None, timeout=None, retries=None):
1575 """Takes a screenshot of the device.
1577 Args:
1578 host_path: A string containing the path on the host to save the
1579 screenshot to. If None, a file name in the current
1580 directory will be generated.
1581 timeout: timeout in seconds
1582 retries: number of retries
1584 Returns:
1585 The name of the file on the host to which the screenshot was saved.
1587 Raises:
1588 CommandFailedError on failure.
1589 CommandTimeoutError on timeout.
1590 DeviceUnreachableError on missing device.
1592 if not host_path:
1593 host_path = os.path.abspath('screenshot-%s.png' % _GetTimeStamp())
1594 with device_temp_file.DeviceTempFile(self.adb, suffix='.png') as device_tmp:
1595 self.RunShellCommand(['/system/bin/screencap', '-p', device_tmp.name],
1596 check_return=True)
1597 self.PullFile(device_tmp.name, host_path)
1598 return host_path
1600 @decorators.WithTimeoutAndRetriesFromInstance()
1601 def GetMemoryUsageForPid(self, pid, timeout=None, retries=None):
1602 """Gets the memory usage for the given PID.
1604 Args:
1605 pid: PID of the process.
1606 timeout: timeout in seconds
1607 retries: number of retries
1609 Returns:
1610 A dict containing memory usage statistics for the PID. May include:
1611 Size, Rss, Pss, Shared_Clean, Shared_Dirty, Private_Clean,
1612 Private_Dirty, VmHWM
1614 Raises:
1615 CommandTimeoutError on timeout.
1617 result = collections.defaultdict(int)
1619 try:
1620 result.update(self._GetMemoryUsageForPidFromSmaps(pid))
1621 except device_errors.CommandFailedError:
1622 logging.exception('Error getting memory usage from smaps')
1624 try:
1625 result.update(self._GetMemoryUsageForPidFromStatus(pid))
1626 except device_errors.CommandFailedError:
1627 logging.exception('Error getting memory usage from status')
1629 return result
1631 def _GetMemoryUsageForPidFromSmaps(self, pid):
1632 SMAPS_COLUMNS = (
1633 'Size', 'Rss', 'Pss', 'Shared_Clean', 'Shared_Dirty', 'Private_Clean',
1634 'Private_Dirty')
1636 showmap_out = self._RunPipedShellCommand(
1637 'showmap %d | grep TOTAL' % int(pid), as_root=True)
1639 split_totals = showmap_out[-1].split()
1640 if (not split_totals
1641 or len(split_totals) != 9
1642 or split_totals[-1] != 'TOTAL'):
1643 raise device_errors.CommandFailedError(
1644 'Invalid output from showmap: %s' % '\n'.join(showmap_out))
1646 return dict(itertools.izip(SMAPS_COLUMNS, (int(n) for n in split_totals)))
1648 def _GetMemoryUsageForPidFromStatus(self, pid):
1649 for line in self.ReadFile(
1650 '/proc/%s/status' % str(pid), as_root=True).splitlines():
1651 if line.startswith('VmHWM:'):
1652 return {'VmHWM': int(line.split()[1])}
1653 else:
1654 raise device_errors.CommandFailedError(
1655 'Could not find memory peak value for pid %s', str(pid))
1657 @decorators.WithTimeoutAndRetriesFromInstance()
1658 def GetLogcatMonitor(self, timeout=None, retries=None, *args, **kwargs):
1659 """Returns a new LogcatMonitor associated with this device.
1661 Parameters passed to this function are passed directly to
1662 |logcat_monitor.LogcatMonitor| and are documented there.
1664 Args:
1665 timeout: timeout in seconds
1666 retries: number of retries
1668 return logcat_monitor.LogcatMonitor(self.adb, *args, **kwargs)
1670 def GetClientCache(self, client_name):
1671 """Returns client cache."""
1672 if client_name not in self._client_caches:
1673 self._client_caches[client_name] = {}
1674 return self._client_caches[client_name]
1676 def _ClearCache(self):
1677 """Clears all caches."""
1678 for client in self._client_caches:
1679 self._client_caches[client].clear()
1680 self._cache.clear()
1682 @classmethod
1683 def parallel(cls, devices=None, async=False):
1684 """Creates a Parallelizer to operate over the provided list of devices.
1686 If |devices| is either |None| or an empty list, the Parallelizer will
1687 operate over all attached devices that have not been blacklisted.
1689 Args:
1690 devices: A list of either DeviceUtils instances or objects from
1691 from which DeviceUtils instances can be constructed. If None,
1692 all attached devices will be used.
1693 async: If true, returns a Parallelizer that runs operations
1694 asynchronously.
1696 Returns:
1697 A Parallelizer operating over |devices|.
1699 if not devices:
1700 devices = cls.HealthyDevices()
1701 if not devices:
1702 raise device_errors.NoDevicesError()
1704 devices = [d if isinstance(d, cls) else cls(d) for d in devices]
1705 if async:
1706 return parallelizer.Parallelizer(devices)
1707 else:
1708 return parallelizer.SyncParallelizer(devices)
1710 @classmethod
1711 def HealthyDevices(cls):
1712 blacklist = device_blacklist.ReadBlacklist()
1713 def blacklisted(adb):
1714 if adb.GetDeviceSerial() in blacklist:
1715 logging.warning('Device %s is blacklisted.', adb.GetDeviceSerial())
1716 return True
1717 return False
1719 return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices()
1720 if not blacklisted(adb)]