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