Adding Peter Thatcher to the owners file.
[chromium-blink-merge.git] / build / android / pylib / device / device_utils.py
blob5c92e261dccc02b99b29479bbacd9b55e48edf30
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.device import adb_wrapper
29 from pylib.device import decorators
30 from pylib.device import device_errors
31 from pylib.device import intent
32 from pylib.device import logcat_monitor
33 from pylib.device.commands import install_commands
34 from pylib.utils import apk_helper
35 from pylib.utils import base_error
36 from pylib.utils import device_temp_file
37 from pylib.utils import host_utils
38 from pylib.utils import md5sum
39 from pylib.utils import parallelizer
40 from pylib.utils import timeout_retry
41 from pylib.utils import zip_utils
43 _DEFAULT_TIMEOUT = 30
44 _DEFAULT_RETRIES = 3
46 # A sentinel object for default values
47 # TODO(jbudorick,perezju): revisit how default values are handled by
48 # the timeout_retry decorators.
49 DEFAULT = object()
51 _CONTROL_CHARGING_COMMANDS = [
53 # Nexus 4
54 'witness_file': '/sys/module/pm8921_charger/parameters/disabled',
55 'enable_command': 'echo 0 > /sys/module/pm8921_charger/parameters/disabled',
56 'disable_command':
57 'echo 1 > /sys/module/pm8921_charger/parameters/disabled',
60 # Nexus 5
61 # Setting the HIZ bit of the bq24192 causes the charger to actually ignore
62 # energy coming from USB. Setting the power_supply offline just updates the
63 # Android system to reflect that.
64 'witness_file': '/sys/kernel/debug/bq24192/INPUT_SRC_CONT',
65 'enable_command': (
66 'echo 0x4A > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && '
67 'echo 1 > /sys/class/power_supply/usb/online'),
68 'disable_command': (
69 'echo 0xCA > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && '
70 'chmod 644 /sys/class/power_supply/usb/online && '
71 'echo 0 > /sys/class/power_supply/usb/online'),
75 @decorators.WithExplicitTimeoutAndRetries(
76 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
77 def GetAVDs():
78 """Returns a list of Android Virtual Devices.
80 Returns:
81 A list containing the configured AVDs.
82 """
83 lines = cmd_helper.GetCmdOutput([
84 os.path.join(constants.ANDROID_SDK_ROOT, 'tools', 'android'),
85 'list', 'avd']).splitlines()
86 avds = []
87 for line in lines:
88 if 'Name:' not in line:
89 continue
90 key, value = (s.strip() for s in line.split(':', 1))
91 if key == 'Name':
92 avds.append(value)
93 return avds
96 @decorators.WithExplicitTimeoutAndRetries(
97 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
98 def RestartServer():
99 """Restarts the adb server.
101 Raises:
102 CommandFailedError if we fail to kill or restart the server.
104 def adb_killed():
105 return not adb_wrapper.AdbWrapper.IsServerOnline()
107 def adb_started():
108 return adb_wrapper.AdbWrapper.IsServerOnline()
110 adb_wrapper.AdbWrapper.KillServer()
111 if not timeout_retry.WaitFor(adb_killed, wait_period=1, max_tries=5):
112 # TODO(perezju): raise an exception after fixng http://crbug.com/442319
113 logging.warning('Failed to kill adb server')
114 adb_wrapper.AdbWrapper.StartServer()
115 if not timeout_retry.WaitFor(adb_started, wait_period=1, max_tries=5):
116 raise device_errors.CommandFailedError('Failed to start adb server')
119 def _GetTimeStamp():
120 """Return a basic ISO 8601 time stamp with the current local time."""
121 return time.strftime('%Y%m%dT%H%M%S', time.localtime())
124 def _JoinLines(lines):
125 # makes sure that the last line is also terminated, and is more memory
126 # efficient than first appending an end-line to each line and then joining
127 # all of them together.
128 return ''.join(s for line in lines for s in (line, '\n'))
131 class DeviceUtils(object):
133 _MAX_ADB_COMMAND_LENGTH = 512
134 _MAX_ADB_OUTPUT_LENGTH = 32768
135 _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$')
137 # Property in /data/local.prop that controls Java assertions.
138 JAVA_ASSERT_PROPERTY = 'dalvik.vm.enableassertions'
140 def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT,
141 default_retries=_DEFAULT_RETRIES):
142 """DeviceUtils constructor.
144 Args:
145 device: Either a device serial, an existing AdbWrapper instance, or an
146 an existing AndroidCommands instance.
147 default_timeout: An integer containing the default number of seconds to
148 wait for an operation to complete if no explicit value
149 is provided.
150 default_retries: An integer containing the default number or times an
151 operation should be retried on failure if no explicit
152 value is provided.
154 self.adb = None
155 self.old_interface = None
156 if isinstance(device, basestring):
157 self.adb = adb_wrapper.AdbWrapper(device)
158 self.old_interface = pylib.android_commands.AndroidCommands(device)
159 elif isinstance(device, adb_wrapper.AdbWrapper):
160 self.adb = device
161 self.old_interface = pylib.android_commands.AndroidCommands(str(device))
162 elif isinstance(device, pylib.android_commands.AndroidCommands):
163 self.adb = adb_wrapper.AdbWrapper(device.GetDevice())
164 self.old_interface = device
165 else:
166 raise ValueError('Unsupported device value: %r' % device)
167 self._commands_installed = None
168 self._default_timeout = default_timeout
169 self._default_retries = default_retries
170 self._cache = {}
171 self._client_caches = {}
172 assert hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR)
173 assert hasattr(self, decorators.DEFAULT_RETRIES_ATTR)
175 def __str__(self):
176 """Returns the device serial."""
177 return self.adb.GetDeviceSerial()
179 @decorators.WithTimeoutAndRetriesFromInstance()
180 def IsOnline(self, timeout=None, retries=None):
181 """Checks whether the device is online.
183 Args:
184 timeout: timeout in seconds
185 retries: number of retries
187 Returns:
188 True if the device is online, False otherwise.
190 Raises:
191 CommandTimeoutError on timeout.
193 try:
194 return self.adb.GetState() == 'device'
195 except base_error.BaseError as exc:
196 logging.info('Failed to get state: %s', exc)
197 return False
199 @decorators.WithTimeoutAndRetriesFromInstance()
200 def HasRoot(self, timeout=None, retries=None):
201 """Checks whether or not adbd has root privileges.
203 Args:
204 timeout: timeout in seconds
205 retries: number of retries
207 Returns:
208 True if adbd has root privileges, False otherwise.
210 Raises:
211 CommandTimeoutError on timeout.
212 DeviceUnreachableError on missing device.
214 try:
215 self.RunShellCommand('ls /root', check_return=True)
216 return True
217 except device_errors.AdbCommandFailedError:
218 return False
220 def NeedsSU(self, timeout=DEFAULT, retries=DEFAULT):
221 """Checks whether 'su' is needed to access protected resources.
223 Args:
224 timeout: timeout in seconds
225 retries: number of retries
227 Returns:
228 True if 'su' is available on the device and is needed to to access
229 protected resources; False otherwise if either 'su' is not available
230 (e.g. because the device has a user build), or not needed (because adbd
231 already has root privileges).
233 Raises:
234 CommandTimeoutError on timeout.
235 DeviceUnreachableError on missing device.
237 if 'needs_su' not in self._cache:
238 try:
239 self.RunShellCommand(
240 'su -c ls /root && ! ls /root', check_return=True,
241 timeout=self._default_timeout if timeout is DEFAULT else timeout,
242 retries=self._default_retries if retries is DEFAULT else retries)
243 self._cache['needs_su'] = True
244 except device_errors.AdbCommandFailedError:
245 self._cache['needs_su'] = False
246 return self._cache['needs_su']
249 @decorators.WithTimeoutAndRetriesFromInstance()
250 def EnableRoot(self, timeout=None, retries=None):
251 """Restarts adbd with root privileges.
253 Args:
254 timeout: timeout in seconds
255 retries: number of retries
257 Raises:
258 CommandFailedError if root could not be enabled.
259 CommandTimeoutError on timeout.
261 if self.IsUserBuild():
262 raise device_errors.CommandFailedError(
263 'Cannot enable root in user builds.', str(self))
264 if 'needs_su' in self._cache:
265 del self._cache['needs_su']
266 self.adb.Root()
267 self.adb.WaitForDevice()
269 @decorators.WithTimeoutAndRetriesFromInstance()
270 def IsUserBuild(self, timeout=None, retries=None):
271 """Checks whether or not the device is running a user build.
273 Args:
274 timeout: timeout in seconds
275 retries: number of retries
277 Returns:
278 True if the device is running a user build, False otherwise (i.e. if
279 it's running a userdebug build).
281 Raises:
282 CommandTimeoutError on timeout.
283 DeviceUnreachableError on missing device.
285 return self.build_type == 'user'
287 @decorators.WithTimeoutAndRetriesFromInstance()
288 def GetExternalStoragePath(self, timeout=None, retries=None):
289 """Get the device's path to its SD card.
291 Args:
292 timeout: timeout in seconds
293 retries: number of retries
295 Returns:
296 The device's path to its SD card.
298 Raises:
299 CommandFailedError if the external storage path could not be determined.
300 CommandTimeoutError on timeout.
301 DeviceUnreachableError on missing device.
303 if 'external_storage' in self._cache:
304 return self._cache['external_storage']
306 value = self.RunShellCommand('echo $EXTERNAL_STORAGE',
307 single_line=True,
308 check_return=True)
309 if not value:
310 raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set',
311 str(self))
312 self._cache['external_storage'] = value
313 return value
315 @decorators.WithTimeoutAndRetriesFromInstance()
316 def GetApplicationPath(self, package, timeout=None, retries=None):
317 """Get the path of the installed apk on the device for the given package.
319 Args:
320 package: Name of the package.
322 Returns:
323 Path to the apk on the device if it exists, None otherwise.
325 # 'pm path' is liable to incorrectly exit with a nonzero number starting
326 # in Lollipop.
327 # TODO(jbudorick): Check if this is fixed as new Android versions are
328 # released to put an upper bound on this.
329 should_check_return = (self.build_version_sdk <
330 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP)
331 output = self.RunShellCommand(['pm', 'path', package], single_line=True,
332 check_return=should_check_return)
333 if not output:
334 return None
335 if not output.startswith('package:'):
336 raise device_errors.CommandFailedError('pm path returned: %r' % output,
337 str(self))
338 return output[len('package:'):]
340 @decorators.WithTimeoutAndRetriesFromInstance()
341 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None):
342 """Wait for the device to fully boot.
344 This means waiting for the device to boot, the package manager to be
345 available, and the SD card to be ready. It can optionally mean waiting
346 for wifi to come up, too.
348 Args:
349 wifi: A boolean indicating if we should wait for wifi to come up or not.
350 timeout: timeout in seconds
351 retries: number of retries
353 Raises:
354 CommandFailedError on failure.
355 CommandTimeoutError if one of the component waits times out.
356 DeviceUnreachableError if the device becomes unresponsive.
358 def sd_card_ready():
359 try:
360 self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()],
361 check_return=True)
362 return True
363 except device_errors.AdbCommandFailedError:
364 return False
366 def pm_ready():
367 try:
368 return self.GetApplicationPath('android')
369 except device_errors.CommandFailedError:
370 return False
372 def boot_completed():
373 return self.GetProp('sys.boot_completed') == '1'
375 def wifi_enabled():
376 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'],
377 check_return=False)
379 self.adb.WaitForDevice()
380 timeout_retry.WaitFor(sd_card_ready)
381 timeout_retry.WaitFor(pm_ready)
382 timeout_retry.WaitFor(boot_completed)
383 if wifi:
384 timeout_retry.WaitFor(wifi_enabled)
386 REBOOT_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT
387 REBOOT_DEFAULT_RETRIES = _DEFAULT_RETRIES
389 @decorators.WithTimeoutAndRetriesDefaults(
390 REBOOT_DEFAULT_TIMEOUT,
391 REBOOT_DEFAULT_RETRIES)
392 def Reboot(self, block=True, wifi=False, timeout=None, retries=None):
393 """Reboot the device.
395 Args:
396 block: A boolean indicating if we should wait for the reboot to complete.
397 wifi: A boolean indicating if we should wait for wifi to be enabled after
398 the reboot. The option has no effect unless |block| is also True.
399 timeout: timeout in seconds
400 retries: number of retries
402 Raises:
403 CommandTimeoutError on timeout.
404 DeviceUnreachableError on missing device.
406 def device_offline():
407 return not self.IsOnline()
409 self.adb.Reboot()
410 self._ClearCache()
411 timeout_retry.WaitFor(device_offline, wait_period=1)
412 if block:
413 self.WaitUntilFullyBooted(wifi=wifi)
415 INSTALL_DEFAULT_TIMEOUT = 4 * _DEFAULT_TIMEOUT
416 INSTALL_DEFAULT_RETRIES = _DEFAULT_RETRIES
418 @decorators.WithTimeoutAndRetriesDefaults(
419 INSTALL_DEFAULT_TIMEOUT,
420 INSTALL_DEFAULT_RETRIES)
421 def Install(self, apk_path, reinstall=False, timeout=None, retries=None):
422 """Install an APK.
424 Noop if an identical APK is already installed.
426 Args:
427 apk_path: A string containing the path to the APK to install.
428 reinstall: A boolean indicating if we should keep any existing app data.
429 timeout: timeout in seconds
430 retries: number of retries
432 Raises:
433 CommandFailedError if the installation fails.
434 CommandTimeoutError if the installation times out.
435 DeviceUnreachableError on missing device.
437 package_name = apk_helper.GetPackageName(apk_path)
438 device_path = self.GetApplicationPath(package_name)
439 if device_path is not None:
440 should_install = bool(self._GetChangedFilesImpl(apk_path, device_path))
441 if should_install and not reinstall:
442 self.adb.Uninstall(package_name)
443 else:
444 should_install = True
445 if should_install:
446 self.adb.Install(apk_path, reinstall=reinstall)
448 @decorators.WithTimeoutAndRetriesFromInstance()
449 def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None,
450 as_root=False, single_line=False, timeout=None,
451 retries=None):
452 """Run an ADB shell command.
454 The command to run |cmd| should be a sequence of program arguments or else
455 a single string.
457 When |cmd| is a sequence, it is assumed to contain the name of the command
458 to run followed by its arguments. In this case, arguments are passed to the
459 command exactly as given, without any further processing by the shell. This
460 allows to easily pass arguments containing spaces or special characters
461 without having to worry about getting quoting right. Whenever possible, it
462 is recomended to pass |cmd| as a sequence.
464 When |cmd| is given as a string, it will be interpreted and run by the
465 shell on the device.
467 This behaviour is consistent with that of command runners in cmd_helper as
468 well as Python's own subprocess.Popen.
470 TODO(perezju) Change the default of |check_return| to True when callers
471 have switched to the new behaviour.
473 Args:
474 cmd: A string with the full command to run on the device, or a sequence
475 containing the command and its arguments.
476 check_return: A boolean indicating whether or not the return code should
477 be checked.
478 cwd: The device directory in which the command should be run.
479 env: The environment variables with which the command should be run.
480 as_root: A boolean indicating whether the shell command should be run
481 with root privileges.
482 single_line: A boolean indicating if only a single line of output is
483 expected.
484 timeout: timeout in seconds
485 retries: number of retries
487 Returns:
488 If single_line is False, the output of the command as a list of lines,
489 otherwise, a string with the unique line of output emmited by the command
490 (with the optional newline at the end stripped).
492 Raises:
493 AdbCommandFailedError if check_return is True and the exit code of
494 the command run on the device is non-zero.
495 CommandFailedError if single_line is True but the output contains two or
496 more lines.
497 CommandTimeoutError on timeout.
498 DeviceUnreachableError on missing device.
500 def env_quote(key, value):
501 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key):
502 raise KeyError('Invalid shell variable name %r' % key)
503 # using double quotes here to allow interpolation of shell variables
504 return '%s=%s' % (key, cmd_helper.DoubleQuote(value))
506 def do_run(cmd):
507 try:
508 return self.adb.Shell(cmd)
509 except device_errors.AdbCommandFailedError as exc:
510 if check_return:
511 raise
512 else:
513 return exc.output
515 if not isinstance(cmd, basestring):
516 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd)
517 if env:
518 env = ' '.join(env_quote(k, v) for k, v in env.iteritems())
519 cmd = '%s %s' % (env, cmd)
520 if cwd:
521 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd)
522 if as_root and self.NeedsSU():
523 # "su -c sh -c" allows using shell features in |cmd|
524 cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd)
525 if timeout is None:
526 timeout = self._default_timeout
528 if len(cmd) < self._MAX_ADB_COMMAND_LENGTH:
529 output = do_run(cmd)
530 else:
531 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script:
532 self._WriteFileWithPush(script.name, cmd)
533 logging.info('Large shell command will be run from file: %s ...',
534 cmd[:100])
535 output = do_run('sh %s' % script.name_quoted)
537 output = output.splitlines()
538 if single_line:
539 if not output:
540 return ''
541 elif len(output) == 1:
542 return output[0]
543 else:
544 msg = 'one line of output was expected, but got: %s'
545 raise device_errors.CommandFailedError(msg % output, str(self))
546 else:
547 return output
549 @decorators.WithTimeoutAndRetriesFromInstance()
550 def KillAll(self, process_name, signum=9, as_root=False, blocking=False,
551 timeout=None, retries=None):
552 """Kill all processes with the given name on the device.
554 Args:
555 process_name: A string containing the name of the process to kill.
556 signum: An integer containing the signal number to send to kill. Defaults
557 to 9 (SIGKILL).
558 as_root: A boolean indicating whether the kill should be executed with
559 root privileges.
560 blocking: A boolean indicating whether we should wait until all processes
561 with the given |process_name| are dead.
562 timeout: timeout in seconds
563 retries: number of retries
565 Raises:
566 CommandFailedError if no process was killed.
567 CommandTimeoutError on timeout.
568 DeviceUnreachableError on missing device.
570 pids = self._GetPidsImpl(process_name)
571 if not pids:
572 raise device_errors.CommandFailedError(
573 'No process "%s"' % process_name, str(self))
575 cmd = ['kill', '-%d' % signum] + pids.values()
576 self.RunShellCommand(cmd, as_root=as_root, check_return=True)
578 if blocking:
579 wait_period = 0.1
580 while self._GetPidsImpl(process_name):
581 time.sleep(wait_period)
583 return len(pids)
585 @decorators.WithTimeoutAndRetriesFromInstance()
586 def StartActivity(self, intent_obj, blocking=False, trace_file_name=None,
587 force_stop=False, timeout=None, retries=None):
588 """Start package's activity on the device.
590 Args:
591 intent_obj: An Intent object to send.
592 blocking: A boolean indicating whether we should wait for the activity to
593 finish launching.
594 trace_file_name: If present, a string that both indicates that we want to
595 profile the activity and contains the path to which the
596 trace should be saved.
597 force_stop: A boolean indicating whether we should stop the activity
598 before starting it.
599 timeout: timeout in seconds
600 retries: number of retries
602 Raises:
603 CommandFailedError if the activity could not be started.
604 CommandTimeoutError on timeout.
605 DeviceUnreachableError on missing device.
607 cmd = ['am', 'start']
608 if blocking:
609 cmd.append('-W')
610 if trace_file_name:
611 cmd.extend(['--start-profiler', trace_file_name])
612 if force_stop:
613 cmd.append('-S')
614 cmd.extend(intent_obj.am_args)
615 for line in self.RunShellCommand(cmd, check_return=True):
616 if line.startswith('Error:'):
617 raise device_errors.CommandFailedError(line, str(self))
619 @decorators.WithTimeoutAndRetriesFromInstance()
620 def StartInstrumentation(self, component, finish=True, raw=False,
621 extras=None, timeout=None, retries=None):
622 if extras is None:
623 extras = {}
625 cmd = ['am', 'instrument']
626 if finish:
627 cmd.append('-w')
628 if raw:
629 cmd.append('-r')
630 for k, v in extras.iteritems():
631 cmd.extend(['-e', str(k), str(v)])
632 cmd.append(component)
633 return self.RunShellCommand(cmd, check_return=True)
635 @decorators.WithTimeoutAndRetriesFromInstance()
636 def BroadcastIntent(self, intent_obj, timeout=None, retries=None):
637 """Send a broadcast intent.
639 Args:
640 intent: An Intent to broadcast.
641 timeout: timeout in seconds
642 retries: number of retries
644 Raises:
645 CommandTimeoutError on timeout.
646 DeviceUnreachableError on missing device.
648 cmd = ['am', 'broadcast'] + intent_obj.am_args
649 self.RunShellCommand(cmd, check_return=True)
651 @decorators.WithTimeoutAndRetriesFromInstance()
652 def GoHome(self, timeout=None, retries=None):
653 """Return to the home screen.
655 Args:
656 timeout: timeout in seconds
657 retries: number of retries
659 Raises:
660 CommandTimeoutError on timeout.
661 DeviceUnreachableError on missing device.
663 self.StartActivity(
664 intent.Intent(action='android.intent.action.MAIN',
665 category='android.intent.category.HOME'),
666 blocking=True)
668 @decorators.WithTimeoutAndRetriesFromInstance()
669 def ForceStop(self, package, timeout=None, retries=None):
670 """Close the application.
672 Args:
673 package: A string containing the name of the package to stop.
674 timeout: timeout in seconds
675 retries: number of retries
677 Raises:
678 CommandTimeoutError on timeout.
679 DeviceUnreachableError on missing device.
681 self.RunShellCommand(['am', 'force-stop', package], check_return=True)
683 @decorators.WithTimeoutAndRetriesFromInstance()
684 def ClearApplicationState(self, package, timeout=None, retries=None):
685 """Clear all state for the given package.
687 Args:
688 package: A string containing the name of the package to stop.
689 timeout: timeout in seconds
690 retries: number of retries
692 Raises:
693 CommandTimeoutError on timeout.
694 DeviceUnreachableError on missing device.
696 # Check that the package exists before clearing it for android builds below
697 # JB MR2. Necessary because calling pm clear on a package that doesn't exist
698 # may never return.
699 if ((self.build_version_sdk >=
700 constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN_MR2)
701 or self.GetApplicationPath(package)):
702 self.RunShellCommand(['pm', 'clear', package], check_return=True)
704 @decorators.WithTimeoutAndRetriesFromInstance()
705 def SendKeyEvent(self, keycode, timeout=None, retries=None):
706 """Sends a keycode to the device.
708 See: http://developer.android.com/reference/android/view/KeyEvent.html
710 Args:
711 keycode: A integer keycode to send to the device.
712 timeout: timeout in seconds
713 retries: number of retries
715 Raises:
716 CommandTimeoutError on timeout.
717 DeviceUnreachableError on missing device.
719 self.RunShellCommand(['input', 'keyevent', format(keycode, 'd')],
720 check_return=True)
722 PUSH_CHANGED_FILES_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT
723 PUSH_CHANGED_FILES_DEFAULT_RETRIES = _DEFAULT_RETRIES
725 @decorators.WithTimeoutAndRetriesDefaults(
726 PUSH_CHANGED_FILES_DEFAULT_TIMEOUT,
727 PUSH_CHANGED_FILES_DEFAULT_RETRIES)
728 def PushChangedFiles(self, host_device_tuples, timeout=None,
729 retries=None):
730 """Push files to the device, skipping files that don't need updating.
732 Args:
733 host_device_tuples: A list of (host_path, device_path) tuples, where
734 |host_path| is an absolute path of a file or directory on the host
735 that should be minimially pushed to the device, and |device_path| is
736 an absolute path of the destination on the device.
737 timeout: timeout in seconds
738 retries: number of retries
740 Raises:
741 CommandFailedError on failure.
742 CommandTimeoutError on timeout.
743 DeviceUnreachableError on missing device.
746 files = []
747 for h, d in host_device_tuples:
748 if os.path.isdir(h):
749 self.RunShellCommand(['mkdir', '-p', d], check_return=True)
750 files += self._GetChangedFilesImpl(h, d)
752 if not files:
753 return
755 size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files)
756 file_count = len(files)
757 dir_size = sum(host_utils.GetRecursiveDiskUsage(h)
758 for h, _ in host_device_tuples)
759 dir_file_count = 0
760 for h, _ in host_device_tuples:
761 if os.path.isdir(h):
762 dir_file_count += sum(len(f) for _r, _d, f in os.walk(h))
763 else:
764 dir_file_count += 1
766 push_duration = self._ApproximateDuration(
767 file_count, file_count, size, False)
768 dir_push_duration = self._ApproximateDuration(
769 len(host_device_tuples), dir_file_count, dir_size, False)
770 zip_duration = self._ApproximateDuration(1, 1, size, True)
772 self._InstallCommands()
774 if dir_push_duration < push_duration and (
775 dir_push_duration < zip_duration or not self._commands_installed):
776 self._PushChangedFilesIndividually(host_device_tuples)
777 elif push_duration < zip_duration or not self._commands_installed:
778 self._PushChangedFilesIndividually(files)
779 else:
780 self._PushChangedFilesZipped(files)
781 self.RunShellCommand(
782 ['chmod', '-R', '777'] + [d for _, d in host_device_tuples],
783 as_root=True, check_return=True)
785 def _GetChangedFilesImpl(self, host_path, device_path):
786 real_host_path = os.path.realpath(host_path)
787 try:
788 real_device_path = self.RunShellCommand(
789 ['realpath', device_path], single_line=True, check_return=True)
790 except device_errors.CommandFailedError:
791 real_device_path = None
792 if not real_device_path:
793 return [(host_path, device_path)]
795 host_hash_tuples = md5sum.CalculateHostMd5Sums([real_host_path])
796 device_paths_to_md5 = (
797 real_device_path if os.path.isfile(real_host_path)
798 else ('%s/%s' % (real_device_path, os.path.relpath(p, real_host_path))
799 for _, p in host_hash_tuples))
800 device_hash_tuples = md5sum.CalculateDeviceMd5Sums(
801 device_paths_to_md5, self)
803 if os.path.isfile(host_path):
804 if (not device_hash_tuples
805 or device_hash_tuples[0].hash != host_hash_tuples[0].hash):
806 return [(host_path, device_path)]
807 else:
808 return []
809 else:
810 device_tuple_dict = dict((d.path, d.hash) for d in device_hash_tuples)
811 to_push = []
812 for host_hash, host_abs_path in (
813 (h.hash, h.path) for h in host_hash_tuples):
814 device_abs_path = '%s/%s' % (
815 real_device_path, os.path.relpath(host_abs_path, real_host_path))
816 if (device_abs_path not in device_tuple_dict
817 or device_tuple_dict[device_abs_path] != host_hash):
818 to_push.append((host_abs_path, device_abs_path))
819 return to_push
821 def _InstallCommands(self):
822 if self._commands_installed is None:
823 try:
824 if not install_commands.Installed(self):
825 install_commands.InstallCommands(self)
826 self._commands_installed = True
827 except Exception as e:
828 logging.warning('unzip not available: %s' % str(e))
829 self._commands_installed = False
831 @staticmethod
832 def _ApproximateDuration(adb_calls, file_count, byte_count, is_zipping):
833 # We approximate the time to push a set of files to a device as:
834 # t = c1 * a + c2 * f + c3 + b / c4 + b / (c5 * c6), where
835 # t: total time (sec)
836 # c1: adb call time delay (sec)
837 # a: number of times adb is called (unitless)
838 # c2: push time delay (sec)
839 # f: number of files pushed via adb (unitless)
840 # c3: zip time delay (sec)
841 # c4: zip rate (bytes/sec)
842 # b: total number of bytes (bytes)
843 # c5: transfer rate (bytes/sec)
844 # c6: compression ratio (unitless)
846 # All of these are approximations.
847 ADB_CALL_PENALTY = 0.1 # seconds
848 ADB_PUSH_PENALTY = 0.01 # seconds
849 ZIP_PENALTY = 2.0 # seconds
850 ZIP_RATE = 10000000.0 # bytes / second
851 TRANSFER_RATE = 2000000.0 # bytes / second
852 COMPRESSION_RATIO = 2.0 # unitless
854 adb_call_time = ADB_CALL_PENALTY * adb_calls
855 adb_push_setup_time = ADB_PUSH_PENALTY * file_count
856 if is_zipping:
857 zip_time = ZIP_PENALTY + byte_count / ZIP_RATE
858 transfer_time = byte_count / (TRANSFER_RATE * COMPRESSION_RATIO)
859 else:
860 zip_time = 0
861 transfer_time = byte_count / TRANSFER_RATE
862 return adb_call_time + adb_push_setup_time + zip_time + transfer_time
864 def _PushChangedFilesIndividually(self, files):
865 for h, d in files:
866 self.adb.Push(h, d)
868 def _PushChangedFilesZipped(self, files):
869 if not files:
870 return
872 with tempfile.NamedTemporaryFile(suffix='.zip') as zip_file:
873 zip_proc = multiprocessing.Process(
874 target=DeviceUtils._CreateDeviceZip,
875 args=(zip_file.name, files))
876 zip_proc.start()
877 zip_proc.join()
879 zip_on_device = '%s/tmp.zip' % self.GetExternalStoragePath()
880 try:
881 self.adb.Push(zip_file.name, zip_on_device)
882 self.RunShellCommand(
883 ['unzip', zip_on_device],
884 as_root=True,
885 env={'PATH': '%s:$PATH' % install_commands.BIN_DIR},
886 check_return=True)
887 finally:
888 if zip_proc.is_alive():
889 zip_proc.terminate()
890 if self.IsOnline():
891 self.RunShellCommand(['rm', zip_on_device], check_return=True)
893 @staticmethod
894 def _CreateDeviceZip(zip_path, host_device_tuples):
895 with zipfile.ZipFile(zip_path, 'w') as zip_file:
896 for host_path, device_path in host_device_tuples:
897 zip_utils.WriteToZipFile(zip_file, host_path, device_path)
899 @decorators.WithTimeoutAndRetriesFromInstance()
900 def FileExists(self, device_path, timeout=None, retries=None):
901 """Checks whether the given file exists on the device.
903 Args:
904 device_path: A string containing the absolute path to the file on the
905 device.
906 timeout: timeout in seconds
907 retries: number of retries
909 Returns:
910 True if the file exists on the device, False otherwise.
912 Raises:
913 CommandTimeoutError on timeout.
914 DeviceUnreachableError on missing device.
916 try:
917 self.RunShellCommand(['test', '-e', device_path], check_return=True)
918 return True
919 except device_errors.AdbCommandFailedError:
920 return False
922 @decorators.WithTimeoutAndRetriesFromInstance()
923 def PullFile(self, device_path, host_path, timeout=None, retries=None):
924 """Pull a file from the device.
926 Args:
927 device_path: A string containing the absolute path of the file to pull
928 from the device.
929 host_path: A string containing the absolute path of the destination on
930 the host.
931 timeout: timeout in seconds
932 retries: number of retries
934 Raises:
935 CommandFailedError on failure.
936 CommandTimeoutError on timeout.
938 # Create the base dir if it doesn't exist already
939 dirname = os.path.dirname(host_path)
940 if dirname and not os.path.exists(dirname):
941 os.makedirs(dirname)
942 self.adb.Pull(device_path, host_path)
944 def _ReadFileWithPull(self, device_path):
945 try:
946 d = tempfile.mkdtemp()
947 host_temp_path = os.path.join(d, 'tmp_ReadFileWithPull')
948 self.adb.Pull(device_path, host_temp_path)
949 with open(host_temp_path, 'r') as host_temp:
950 return host_temp.read()
951 finally:
952 if os.path.exists(d):
953 shutil.rmtree(d)
955 _LS_RE = re.compile(
956 r'(?P<perms>\S+) +(?P<owner>\S+) +(?P<group>\S+) +(?:(?P<size>\d+) +)?'
957 + r'(?P<date>\S+) +(?P<time>\S+) +(?P<name>.+)$')
959 @decorators.WithTimeoutAndRetriesFromInstance()
960 def ReadFile(self, device_path, as_root=False,
961 timeout=None, retries=None):
962 """Reads the contents of a file from the device.
964 Args:
965 device_path: A string containing the absolute path of the file to read
966 from the device.
967 as_root: A boolean indicating whether the read should be executed with
968 root privileges.
969 timeout: timeout in seconds
970 retries: number of retries
972 Returns:
973 The contents of |device_path| as a string. Contents are intepreted using
974 universal newlines, so the caller will see them encoded as '\n'. Also,
975 all lines will be terminated.
977 Raises:
978 AdbCommandFailedError if the file can't be read.
979 CommandTimeoutError on timeout.
980 DeviceUnreachableError on missing device.
982 # TODO(jbudorick): Implement a generic version of Stat() that handles
983 # as_root=True, then switch this implementation to use that.
984 size = None
985 ls_out = self.RunShellCommand(['ls', '-l', device_path], as_root=as_root,
986 check_return=True)
987 for line in ls_out:
988 m = self._LS_RE.match(line)
989 if m and m.group('name') == posixpath.basename(device_path):
990 size = int(m.group('size'))
991 break
992 else:
993 logging.warning('Could not determine size of %s.', device_path)
995 if size is None or size <= self._MAX_ADB_OUTPUT_LENGTH:
996 return _JoinLines(self.RunShellCommand(
997 ['cat', device_path], as_root=as_root, check_return=True))
998 elif as_root and self.NeedsSU():
999 with device_temp_file.DeviceTempFile(self.adb) as device_temp:
1000 self.RunShellCommand(['cp', device_path, device_temp.name],
1001 as_root=True, check_return=True)
1002 return self._ReadFileWithPull(device_temp.name)
1003 else:
1004 return self._ReadFileWithPull(device_path)
1006 def _WriteFileWithPush(self, device_path, contents):
1007 with tempfile.NamedTemporaryFile() as host_temp:
1008 host_temp.write(contents)
1009 host_temp.flush()
1010 self.adb.Push(host_temp.name, device_path)
1012 @decorators.WithTimeoutAndRetriesFromInstance()
1013 def WriteFile(self, device_path, contents, as_root=False, force_push=False,
1014 timeout=None, retries=None):
1015 """Writes |contents| to a file on the device.
1017 Args:
1018 device_path: A string containing the absolute path to the file to write
1019 on the device.
1020 contents: A string containing the data to write to the device.
1021 as_root: A boolean indicating whether the write should be executed with
1022 root privileges (if available).
1023 force_push: A boolean indicating whether to force the operation to be
1024 performed by pushing a file to the device. The default is, when the
1025 contents are short, to pass the contents using a shell script instead.
1026 timeout: timeout in seconds
1027 retries: number of retries
1029 Raises:
1030 CommandFailedError if the file could not be written on the device.
1031 CommandTimeoutError on timeout.
1032 DeviceUnreachableError on missing device.
1034 if not force_push and len(contents) < self._MAX_ADB_COMMAND_LENGTH:
1035 # If the contents are small, for efficieny we write the contents with
1036 # a shell command rather than pushing a file.
1037 cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents),
1038 cmd_helper.SingleQuote(device_path))
1039 self.RunShellCommand(cmd, as_root=as_root, check_return=True)
1040 elif as_root and self.NeedsSU():
1041 # Adb does not allow to "push with su", so we first push to a temp file
1042 # on a safe location, and then copy it to the desired location with su.
1043 with device_temp_file.DeviceTempFile(self.adb) as device_temp:
1044 self._WriteFileWithPush(device_temp.name, contents)
1045 # Here we need 'cp' rather than 'mv' because the temp and
1046 # destination files might be on different file systems (e.g.
1047 # on internal storage and an external sd card).
1048 self.RunShellCommand(['cp', device_temp.name, device_path],
1049 as_root=True, check_return=True)
1050 else:
1051 # If root is not needed, we can push directly to the desired location.
1052 self._WriteFileWithPush(device_path, contents)
1054 @decorators.WithTimeoutAndRetriesFromInstance()
1055 def Ls(self, device_path, timeout=None, retries=None):
1056 """Lists the contents of a directory on the device.
1058 Args:
1059 device_path: A string containing the path of the directory on the device
1060 to list.
1061 timeout: timeout in seconds
1062 retries: number of retries
1064 Returns:
1065 A list of pairs (filename, stat) for each file found in the directory,
1066 where the stat object has the properties: st_mode, st_size, and st_time.
1068 Raises:
1069 AdbCommandFailedError if |device_path| does not specify a valid and
1070 accessible directory in the device.
1071 CommandTimeoutError on timeout.
1072 DeviceUnreachableError on missing device.
1074 return self.adb.Ls(device_path)
1076 @decorators.WithTimeoutAndRetriesFromInstance()
1077 def Stat(self, device_path, timeout=None, retries=None):
1078 """Get the stat attributes of a file or directory on the device.
1080 Args:
1081 device_path: A string containing the path of from which to get attributes
1082 on the device.
1083 timeout: timeout in seconds
1084 retries: number of retries
1086 Returns:
1087 A stat object with the properties: st_mode, st_size, and st_time
1089 Raises:
1090 CommandFailedError if device_path cannot be found on the device.
1091 CommandTimeoutError on timeout.
1092 DeviceUnreachableError on missing device.
1094 dirname, target = device_path.rsplit('/', 1)
1095 for filename, stat in self.adb.Ls(dirname):
1096 if filename == target:
1097 return stat
1098 raise device_errors.CommandFailedError(
1099 'Cannot find file or directory: %r' % device_path, str(self))
1101 @decorators.WithTimeoutAndRetriesFromInstance()
1102 def SetJavaAsserts(self, enabled, timeout=None, retries=None):
1103 """Enables or disables Java asserts.
1105 Args:
1106 enabled: A boolean indicating whether Java asserts should be enabled
1107 or disabled.
1108 timeout: timeout in seconds
1109 retries: number of retries
1111 Returns:
1112 True if the device-side property changed and a restart is required as a
1113 result, False otherwise.
1115 Raises:
1116 CommandTimeoutError on timeout.
1118 def find_property(lines, property_name):
1119 for index, line in enumerate(lines):
1120 if line.strip() == '':
1121 continue
1122 key, value = (s.strip() for s in line.split('=', 1))
1123 if key == property_name:
1124 return index, value
1125 return None, ''
1127 new_value = 'all' if enabled else ''
1129 # First ensure the desired property is persisted.
1130 try:
1131 properties = self.ReadFile(
1132 constants.DEVICE_LOCAL_PROPERTIES_PATH).splitlines()
1133 except device_errors.CommandFailedError:
1134 properties = []
1135 index, value = find_property(properties, self.JAVA_ASSERT_PROPERTY)
1136 if new_value != value:
1137 if new_value:
1138 new_line = '%s=%s' % (self.JAVA_ASSERT_PROPERTY, new_value)
1139 if index is None:
1140 properties.append(new_line)
1141 else:
1142 properties[index] = new_line
1143 else:
1144 assert index is not None # since new_value == '' and new_value != value
1145 properties.pop(index)
1146 self.WriteFile(constants.DEVICE_LOCAL_PROPERTIES_PATH,
1147 _JoinLines(properties))
1149 # Next, check the current runtime value is what we need, and
1150 # if not, set it and report that a reboot is required.
1151 value = self.GetProp(self.JAVA_ASSERT_PROPERTY)
1152 if new_value != value:
1153 self.SetProp(self.JAVA_ASSERT_PROPERTY, new_value)
1154 return True
1155 else:
1156 return False
1159 @property
1160 def build_description(self):
1161 """Returns the build description of the system.
1163 For example:
1164 nakasi-user 4.4.4 KTU84P 1227136 release-keys
1166 return self.GetProp('ro.build.description', cache=True)
1168 @property
1169 def build_fingerprint(self):
1170 """Returns the build fingerprint of the system.
1172 For example:
1173 google/nakasi/grouper:4.4.4/KTU84P/1227136:user/release-keys
1175 return self.GetProp('ro.build.fingerprint', cache=True)
1177 @property
1178 def build_id(self):
1179 """Returns the build ID of the system (e.g. 'KTU84P')."""
1180 return self.GetProp('ro.build.id', cache=True)
1182 @property
1183 def build_product(self):
1184 """Returns the build product of the system (e.g. 'grouper')."""
1185 return self.GetProp('ro.build.product', cache=True)
1187 @property
1188 def build_type(self):
1189 """Returns the build type of the system (e.g. 'user')."""
1190 return self.GetProp('ro.build.type', cache=True)
1192 @property
1193 def build_version_sdk(self):
1194 """Returns the build version sdk of the system as a number (e.g. 19).
1196 For version code numbers see:
1197 http://developer.android.com/reference/android/os/Build.VERSION_CODES.html
1199 For named constants see:
1200 pylib.constants.ANDROID_SDK_VERSION_CODES
1202 Raises:
1203 CommandFailedError if the build version sdk is not a number.
1205 value = self.GetProp('ro.build.version.sdk', cache=True)
1206 try:
1207 return int(value)
1208 except ValueError:
1209 raise device_errors.CommandFailedError(
1210 'Invalid build version sdk: %r' % value)
1212 @property
1213 def product_cpu_abi(self):
1214 """Returns the product cpu abi of the device (e.g. 'armeabi-v7a')."""
1215 return self.GetProp('ro.product.cpu.abi', cache=True)
1217 @property
1218 def product_model(self):
1219 """Returns the name of the product model (e.g. 'Nexus 7')."""
1220 return self.GetProp('ro.product.model', cache=True)
1222 @property
1223 def product_name(self):
1224 """Returns the product name of the device (e.g. 'nakasi')."""
1225 return self.GetProp('ro.product.name', cache=True)
1227 def GetProp(self, property_name, cache=False, timeout=DEFAULT,
1228 retries=DEFAULT):
1229 """Gets a property from the device.
1231 Args:
1232 property_name: A string containing the name of the property to get from
1233 the device.
1234 cache: A boolean indicating whether to cache the value of this property.
1235 timeout: timeout in seconds
1236 retries: number of retries
1238 Returns:
1239 The value of the device's |property_name| property.
1241 Raises:
1242 CommandTimeoutError on timeout.
1244 assert isinstance(property_name, basestring), (
1245 "property_name is not a string: %r" % property_name)
1247 cache_key = '_prop:' + property_name
1248 if cache and cache_key in self._cache:
1249 return self._cache[cache_key]
1250 else:
1251 # timeout and retries are handled down at run shell, because we don't
1252 # want to apply them in the other branch when reading from the cache
1253 value = self.RunShellCommand(
1254 ['getprop', property_name], single_line=True, check_return=True,
1255 timeout=self._default_timeout if timeout is DEFAULT else timeout,
1256 retries=self._default_retries if retries is DEFAULT else retries)
1257 if cache or cache_key in self._cache:
1258 self._cache[cache_key] = value
1259 return value
1261 @decorators.WithTimeoutAndRetriesFromInstance()
1262 def SetProp(self, property_name, value, check=False, timeout=None,
1263 retries=None):
1264 """Sets a property on the device.
1266 Args:
1267 property_name: A string containing the name of the property to set on
1268 the device.
1269 value: A string containing the value to set to the property on the
1270 device.
1271 check: A boolean indicating whether to check that the property was
1272 successfully set on the device.
1273 timeout: timeout in seconds
1274 retries: number of retries
1276 Raises:
1277 CommandFailedError if check is true and the property was not correctly
1278 set on the device (e.g. because it is not rooted).
1279 CommandTimeoutError on timeout.
1281 assert isinstance(property_name, basestring), (
1282 "property_name is not a string: %r" % property_name)
1283 assert isinstance(value, basestring), "value is not a string: %r" % value
1285 self.RunShellCommand(['setprop', property_name, value], check_return=True)
1286 if property_name in self._cache:
1287 del self._cache[property_name]
1288 # TODO(perezju) remove the option and make the check mandatory, but using a
1289 # single shell script to both set- and getprop.
1290 if check and value != self.GetProp(property_name):
1291 raise device_errors.CommandFailedError(
1292 'Unable to set property %r on the device to %r'
1293 % (property_name, value), str(self))
1295 @decorators.WithTimeoutAndRetriesFromInstance()
1296 def GetABI(self, timeout=None, retries=None):
1297 """Gets the device main ABI.
1299 Args:
1300 timeout: timeout in seconds
1301 retries: number of retries
1303 Returns:
1304 The device's main ABI name.
1306 Raises:
1307 CommandTimeoutError on timeout.
1309 return self.GetProp('ro.product.cpu.abi')
1311 @decorators.WithTimeoutAndRetriesFromInstance()
1312 def GetPids(self, process_name, timeout=None, retries=None):
1313 """Returns the PIDs of processes with the given name.
1315 Note that the |process_name| is often the package name.
1317 Args:
1318 process_name: A string containing the process name to get the PIDs for.
1319 timeout: timeout in seconds
1320 retries: number of retries
1322 Returns:
1323 A dict mapping process name to PID for each process that contained the
1324 provided |process_name|.
1326 Raises:
1327 CommandTimeoutError on timeout.
1328 DeviceUnreachableError on missing device.
1330 return self._GetPidsImpl(process_name)
1332 def _GetPidsImpl(self, process_name):
1333 procs_pids = {}
1334 for line in self.RunShellCommand('ps', check_return=True):
1335 try:
1336 ps_data = line.split()
1337 if process_name in ps_data[-1]:
1338 procs_pids[ps_data[-1]] = ps_data[1]
1339 except IndexError:
1340 pass
1341 return procs_pids
1343 @decorators.WithTimeoutAndRetriesFromInstance()
1344 def TakeScreenshot(self, host_path=None, timeout=None, retries=None):
1345 """Takes a screenshot of the device.
1347 Args:
1348 host_path: A string containing the path on the host to save the
1349 screenshot to. If None, a file name in the current
1350 directory will be generated.
1351 timeout: timeout in seconds
1352 retries: number of retries
1354 Returns:
1355 The name of the file on the host to which the screenshot was saved.
1357 Raises:
1358 CommandFailedError on failure.
1359 CommandTimeoutError on timeout.
1360 DeviceUnreachableError on missing device.
1362 if not host_path:
1363 host_path = os.path.abspath('screenshot-%s.png' % _GetTimeStamp())
1364 with device_temp_file.DeviceTempFile(self.adb, suffix='.png') as device_tmp:
1365 self.RunShellCommand(['/system/bin/screencap', '-p', device_tmp.name],
1366 check_return=True)
1367 self.PullFile(device_tmp.name, host_path)
1368 return host_path
1370 @decorators.WithTimeoutAndRetriesFromInstance()
1371 def GetMemoryUsageForPid(self, pid, timeout=None, retries=None):
1372 """Gets the memory usage for the given PID.
1374 Args:
1375 pid: PID of the process.
1376 timeout: timeout in seconds
1377 retries: number of retries
1379 Returns:
1380 A dict containing memory usage statistics for the PID. May include:
1381 Size, Rss, Pss, Shared_Clean, Shared_Dirty, Private_Clean,
1382 Private_Dirty, VmHWM
1384 Raises:
1385 CommandTimeoutError on timeout.
1387 result = collections.defaultdict(int)
1389 try:
1390 result.update(self._GetMemoryUsageForPidFromSmaps(pid))
1391 except device_errors.CommandFailedError:
1392 logging.exception('Error getting memory usage from smaps')
1394 try:
1395 result.update(self._GetMemoryUsageForPidFromStatus(pid))
1396 except device_errors.CommandFailedError:
1397 logging.exception('Error getting memory usage from status')
1399 return result
1401 def _GetMemoryUsageForPidFromSmaps(self, pid):
1402 SMAPS_COLUMNS = (
1403 'Size', 'Rss', 'Pss', 'Shared_Clean', 'Shared_Dirty', 'Private_Clean',
1404 'Private_Dirty')
1406 showmap_out = self.RunShellCommand(
1407 ['showmap', str(pid)], as_root=True, check_return=True)
1408 if not showmap_out:
1409 raise device_errors.CommandFailedError('No output from showmap')
1411 split_totals = showmap_out[-1].split()
1412 if (not split_totals
1413 or len(split_totals) != 9
1414 or split_totals[-1] != 'TOTAL'):
1415 raise device_errors.CommandFailedError(
1416 'Invalid output from showmap: %s' % '\n'.join(showmap_out))
1418 return dict(itertools.izip(SMAPS_COLUMNS, (int(n) for n in split_totals)))
1420 def _GetMemoryUsageForPidFromStatus(self, pid):
1421 for line in self.ReadFile(
1422 '/proc/%s/status' % str(pid), as_root=True).splitlines():
1423 if line.startswith('VmHWM:'):
1424 return {'VmHWM': int(line.split()[1])}
1425 else:
1426 raise device_errors.CommandFailedError(
1427 'Could not find memory peak value for pid %s', str(pid))
1429 @decorators.WithTimeoutAndRetriesFromInstance()
1430 def GetLogcatMonitor(self, timeout=None, retries=None, *args, **kwargs):
1431 """Returns a new LogcatMonitor associated with this device.
1433 Parameters passed to this function are passed directly to
1434 |logcat_monitor.LogcatMonitor| and are documented there.
1436 Args:
1437 timeout: timeout in seconds
1438 retries: number of retries
1440 return logcat_monitor.LogcatMonitor(self.adb, *args, **kwargs)
1442 @decorators.WithTimeoutAndRetriesFromInstance()
1443 def GetDevicePieWrapper(self, timeout=None, retries=None):
1444 """Gets the absolute path to the run_pie wrapper on the device.
1446 We have to build our device executables to be PIE, but they need to be able
1447 to run on versions of android that don't support PIE (i.e. ICS and below).
1448 To do so, we push a wrapper to the device that lets older android versions
1449 run PIE executables. This method pushes that wrapper to the device if
1450 necessary and returns the path to it.
1452 This is exposed publicly to allow clients to write scripts using run_pie
1453 (e.g. md5sum.CalculateDeviceMd5Sum).
1455 Args:
1456 timeout: timeout in seconds
1457 retries: number of retries
1459 Returns:
1460 The path to the PIE wrapper on the device, or an empty string if the
1461 device does not require the wrapper.
1463 if 'run_pie' not in self._cache:
1464 pie = ''
1465 if (self.build_version_sdk <
1466 constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN):
1467 host_pie_path = os.path.join(constants.GetOutDirectory(), 'run_pie')
1468 if not os.path.exists(host_pie_path):
1469 raise device_errors.CommandFailedError('Please build run_pie')
1470 pie = '%s/run_pie' % constants.TEST_EXECUTABLE_DIR
1471 self.adb.Push(host_pie_path, pie)
1473 self._cache['run_pie'] = pie
1475 return self._cache['run_pie']
1477 @classmethod
1478 def parallel(cls, devices=None, async=False):
1479 """Creates a Parallelizer to operate over the provided list of devices.
1481 If |devices| is either |None| or an empty list, the Parallelizer will
1482 operate over all attached devices.
1484 Args:
1485 devices: A list of either DeviceUtils instances or objects from
1486 from which DeviceUtils instances can be constructed. If None,
1487 all attached devices will be used.
1488 async: If true, returns a Parallelizer that runs operations
1489 asynchronously.
1491 Returns:
1492 A Parallelizer operating over |devices|.
1494 if not devices:
1495 devices = adb_wrapper.AdbWrapper.GetDevices()
1496 if not devices:
1497 raise device_errors.NoDevicesError()
1498 devices = [d if isinstance(d, cls) else cls(d) for d in devices]
1499 if async:
1500 return parallelizer.Parallelizer(devices)
1501 else:
1502 return parallelizer.SyncParallelizer(devices)
1504 def GetClientCache(self, client_name):
1505 """Returns client cache."""
1506 if client_name not in self._client_caches:
1507 self._client_caches[client_name] = {}
1508 return self._client_caches[client_name]
1510 def _ClearCache(self):
1511 """Clears all caches."""
1512 for client in self._client_caches:
1513 self._client_caches[client].clear()
1514 self._cache.clear()