[Cronet] Delay StartNetLog and StopNetLog until native request context is initialized
[chromium-blink-merge.git] / build / android / pylib / device / device_utils.py
blob9f538e1b0efd9f51ffaa4c1345febe8149edccc6
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 assert hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR)
172 assert hasattr(self, decorators.DEFAULT_RETRIES_ATTR)
174 def __str__(self):
175 """Returns the device serial."""
176 return self.adb.GetDeviceSerial()
178 @decorators.WithTimeoutAndRetriesFromInstance()
179 def IsOnline(self, timeout=None, retries=None):
180 """Checks whether the device is online.
182 Args:
183 timeout: timeout in seconds
184 retries: number of retries
186 Returns:
187 True if the device is online, False otherwise.
189 Raises:
190 CommandTimeoutError on timeout.
192 try:
193 return self.adb.GetState() == 'device'
194 except base_error.BaseError as exc:
195 logging.info('Failed to get state: %s', exc)
196 return False
198 @decorators.WithTimeoutAndRetriesFromInstance()
199 def HasRoot(self, timeout=None, retries=None):
200 """Checks whether or not adbd has root privileges.
202 Args:
203 timeout: timeout in seconds
204 retries: number of retries
206 Returns:
207 True if adbd has root privileges, False otherwise.
209 Raises:
210 CommandTimeoutError on timeout.
211 DeviceUnreachableError on missing device.
213 try:
214 self.RunShellCommand('ls /root', check_return=True)
215 return True
216 except device_errors.AdbCommandFailedError:
217 return False
219 def NeedsSU(self, timeout=DEFAULT, retries=DEFAULT):
220 """Checks whether 'su' is needed to access protected resources.
222 Args:
223 timeout: timeout in seconds
224 retries: number of retries
226 Returns:
227 True if 'su' is available on the device and is needed to to access
228 protected resources; False otherwise if either 'su' is not available
229 (e.g. because the device has a user build), or not needed (because adbd
230 already has root privileges).
232 Raises:
233 CommandTimeoutError on timeout.
234 DeviceUnreachableError on missing device.
236 if 'needs_su' not in self._cache:
237 try:
238 self.RunShellCommand(
239 'su -c ls /root && ! ls /root', check_return=True,
240 timeout=self._default_timeout if timeout is DEFAULT else timeout,
241 retries=self._default_retries if retries is DEFAULT else retries)
242 self._cache['needs_su'] = True
243 except device_errors.AdbCommandFailedError:
244 self._cache['needs_su'] = False
245 return self._cache['needs_su']
248 @decorators.WithTimeoutAndRetriesFromInstance()
249 def EnableRoot(self, timeout=None, retries=None):
250 """Restarts adbd with root privileges.
252 Args:
253 timeout: timeout in seconds
254 retries: number of retries
256 Raises:
257 CommandFailedError if root could not be enabled.
258 CommandTimeoutError on timeout.
260 if self.IsUserBuild():
261 raise device_errors.CommandFailedError(
262 'Cannot enable root in user builds.', str(self))
263 if 'needs_su' in self._cache:
264 del self._cache['needs_su']
265 self.adb.Root()
266 self.adb.WaitForDevice()
268 @decorators.WithTimeoutAndRetriesFromInstance()
269 def IsUserBuild(self, timeout=None, retries=None):
270 """Checks whether or not the device is running a user build.
272 Args:
273 timeout: timeout in seconds
274 retries: number of retries
276 Returns:
277 True if the device is running a user build, False otherwise (i.e. if
278 it's running a userdebug build).
280 Raises:
281 CommandTimeoutError on timeout.
282 DeviceUnreachableError on missing device.
284 return self.build_type == 'user'
286 @decorators.WithTimeoutAndRetriesFromInstance()
287 def GetExternalStoragePath(self, timeout=None, retries=None):
288 """Get the device's path to its SD card.
290 Args:
291 timeout: timeout in seconds
292 retries: number of retries
294 Returns:
295 The device's path to its SD card.
297 Raises:
298 CommandFailedError if the external storage path could not be determined.
299 CommandTimeoutError on timeout.
300 DeviceUnreachableError on missing device.
302 if 'external_storage' in self._cache:
303 return self._cache['external_storage']
305 value = self.RunShellCommand('echo $EXTERNAL_STORAGE',
306 single_line=True,
307 check_return=True)
308 if not value:
309 raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set',
310 str(self))
311 self._cache['external_storage'] = value
312 return value
314 @decorators.WithTimeoutAndRetriesFromInstance()
315 def GetApplicationPath(self, package, timeout=None, retries=None):
316 """Get the path of the installed apk on the device for the given package.
318 Args:
319 package: Name of the package.
321 Returns:
322 Path to the apk on the device if it exists, None otherwise.
324 # 'pm path' is liable to incorrectly exit with a nonzero number starting
325 # in Lollipop.
326 # TODO(jbudorick): Check if this is fixed as new Android versions are
327 # released to put an upper bound on this.
328 should_check_return = (self.build_version_sdk <
329 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP)
330 output = self.RunShellCommand(['pm', 'path', package], single_line=True,
331 check_return=should_check_return)
332 if not output:
333 return None
334 if not output.startswith('package:'):
335 raise device_errors.CommandFailedError('pm path returned: %r' % output,
336 str(self))
337 return output[len('package:'):]
339 @decorators.WithTimeoutAndRetriesFromInstance()
340 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None):
341 """Wait for the device to fully boot.
343 This means waiting for the device to boot, the package manager to be
344 available, and the SD card to be ready. It can optionally mean waiting
345 for wifi to come up, too.
347 Args:
348 wifi: A boolean indicating if we should wait for wifi to come up or not.
349 timeout: timeout in seconds
350 retries: number of retries
352 Raises:
353 CommandFailedError on failure.
354 CommandTimeoutError if one of the component waits times out.
355 DeviceUnreachableError if the device becomes unresponsive.
357 def sd_card_ready():
358 try:
359 self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()],
360 check_return=True)
361 return True
362 except device_errors.AdbCommandFailedError:
363 return False
365 def pm_ready():
366 try:
367 return self.GetApplicationPath('android')
368 except device_errors.CommandFailedError:
369 return False
371 def boot_completed():
372 return self.GetProp('sys.boot_completed') == '1'
374 def wifi_enabled():
375 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'],
376 check_return=False)
378 self.adb.WaitForDevice()
379 timeout_retry.WaitFor(sd_card_ready)
380 timeout_retry.WaitFor(pm_ready)
381 timeout_retry.WaitFor(boot_completed)
382 if wifi:
383 timeout_retry.WaitFor(wifi_enabled)
385 REBOOT_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT
386 REBOOT_DEFAULT_RETRIES = _DEFAULT_RETRIES
388 @decorators.WithTimeoutAndRetriesDefaults(
389 REBOOT_DEFAULT_TIMEOUT,
390 REBOOT_DEFAULT_RETRIES)
391 def Reboot(self, block=True, wifi=False, timeout=None, retries=None):
392 """Reboot the device.
394 Args:
395 block: A boolean indicating if we should wait for the reboot to complete.
396 wifi: A boolean indicating if we should wait for wifi to be enabled after
397 the reboot. The option has no effect unless |block| is also True.
398 timeout: timeout in seconds
399 retries: number of retries
401 Raises:
402 CommandTimeoutError on timeout.
403 DeviceUnreachableError on missing device.
405 def device_offline():
406 return not self.IsOnline()
408 self.adb.Reboot()
409 self._cache = {}
410 timeout_retry.WaitFor(device_offline, wait_period=1)
411 if block:
412 self.WaitUntilFullyBooted(wifi=wifi)
414 INSTALL_DEFAULT_TIMEOUT = 4 * _DEFAULT_TIMEOUT
415 INSTALL_DEFAULT_RETRIES = _DEFAULT_RETRIES
417 @decorators.WithTimeoutAndRetriesDefaults(
418 INSTALL_DEFAULT_TIMEOUT,
419 INSTALL_DEFAULT_RETRIES)
420 def Install(self, apk_path, reinstall=False, timeout=None, retries=None):
421 """Install an APK.
423 Noop if an identical APK is already installed.
425 Args:
426 apk_path: A string containing the path to the APK to install.
427 reinstall: A boolean indicating if we should keep any existing app data.
428 timeout: timeout in seconds
429 retries: number of retries
431 Raises:
432 CommandFailedError if the installation fails.
433 CommandTimeoutError if the installation times out.
434 DeviceUnreachableError on missing device.
436 package_name = apk_helper.GetPackageName(apk_path)
437 device_path = self.GetApplicationPath(package_name)
438 if device_path is not None:
439 should_install = bool(self._GetChangedFilesImpl(apk_path, device_path))
440 if should_install and not reinstall:
441 self.adb.Uninstall(package_name)
442 else:
443 should_install = True
444 if should_install:
445 self.adb.Install(apk_path, reinstall=reinstall)
447 @decorators.WithTimeoutAndRetriesFromInstance()
448 def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None,
449 as_root=False, single_line=False, timeout=None,
450 retries=None):
451 """Run an ADB shell command.
453 The command to run |cmd| should be a sequence of program arguments or else
454 a single string.
456 When |cmd| is a sequence, it is assumed to contain the name of the command
457 to run followed by its arguments. In this case, arguments are passed to the
458 command exactly as given, without any further processing by the shell. This
459 allows to easily pass arguments containing spaces or special characters
460 without having to worry about getting quoting right. Whenever possible, it
461 is recomended to pass |cmd| as a sequence.
463 When |cmd| is given as a string, it will be interpreted and run by the
464 shell on the device.
466 This behaviour is consistent with that of command runners in cmd_helper as
467 well as Python's own subprocess.Popen.
469 TODO(perezju) Change the default of |check_return| to True when callers
470 have switched to the new behaviour.
472 Args:
473 cmd: A string with the full command to run on the device, or a sequence
474 containing the command and its arguments.
475 check_return: A boolean indicating whether or not the return code should
476 be checked.
477 cwd: The device directory in which the command should be run.
478 env: The environment variables with which the command should be run.
479 as_root: A boolean indicating whether the shell command should be run
480 with root privileges.
481 single_line: A boolean indicating if only a single line of output is
482 expected.
483 timeout: timeout in seconds
484 retries: number of retries
486 Returns:
487 If single_line is False, the output of the command as a list of lines,
488 otherwise, a string with the unique line of output emmited by the command
489 (with the optional newline at the end stripped).
491 Raises:
492 AdbCommandFailedError if check_return is True and the exit code of
493 the command run on the device is non-zero.
494 CommandFailedError if single_line is True but the output contains two or
495 more lines.
496 CommandTimeoutError on timeout.
497 DeviceUnreachableError on missing device.
499 def env_quote(key, value):
500 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key):
501 raise KeyError('Invalid shell variable name %r' % key)
502 # using double quotes here to allow interpolation of shell variables
503 return '%s=%s' % (key, cmd_helper.DoubleQuote(value))
505 def do_run(cmd):
506 try:
507 return self.adb.Shell(cmd)
508 except device_errors.AdbCommandFailedError as exc:
509 if check_return:
510 raise
511 else:
512 return exc.output
514 if not isinstance(cmd, basestring):
515 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd)
516 if env:
517 env = ' '.join(env_quote(k, v) for k, v in env.iteritems())
518 cmd = '%s %s' % (env, cmd)
519 if cwd:
520 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd)
521 if as_root and self.NeedsSU():
522 # "su -c sh -c" allows using shell features in |cmd|
523 cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd)
524 if timeout is None:
525 timeout = self._default_timeout
527 if len(cmd) < self._MAX_ADB_COMMAND_LENGTH:
528 output = do_run(cmd)
529 else:
530 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script:
531 self._WriteFileWithPush(script.name, cmd)
532 logging.info('Large shell command will be run from file: %s ...',
533 cmd[:100])
534 output = do_run('sh %s' % script.name_quoted)
536 output = output.splitlines()
537 if single_line:
538 if not output:
539 return ''
540 elif len(output) == 1:
541 return output[0]
542 else:
543 msg = 'one line of output was expected, but got: %s'
544 raise device_errors.CommandFailedError(msg % output, str(self))
545 else:
546 return output
548 @decorators.WithTimeoutAndRetriesFromInstance()
549 def KillAll(self, process_name, signum=9, as_root=False, blocking=False,
550 timeout=None, retries=None):
551 """Kill all processes with the given name on the device.
553 Args:
554 process_name: A string containing the name of the process to kill.
555 signum: An integer containing the signal number to send to kill. Defaults
556 to 9 (SIGKILL).
557 as_root: A boolean indicating whether the kill should be executed with
558 root privileges.
559 blocking: A boolean indicating whether we should wait until all processes
560 with the given |process_name| are dead.
561 timeout: timeout in seconds
562 retries: number of retries
564 Raises:
565 CommandFailedError if no process was killed.
566 CommandTimeoutError on timeout.
567 DeviceUnreachableError on missing device.
569 pids = self._GetPidsImpl(process_name)
570 if not pids:
571 raise device_errors.CommandFailedError(
572 'No process "%s"' % process_name, str(self))
574 cmd = ['kill', '-%d' % signum] + pids.values()
575 self.RunShellCommand(cmd, as_root=as_root, check_return=True)
577 if blocking:
578 wait_period = 0.1
579 while self._GetPidsImpl(process_name):
580 time.sleep(wait_period)
582 return len(pids)
584 @decorators.WithTimeoutAndRetriesFromInstance()
585 def StartActivity(self, intent_obj, blocking=False, trace_file_name=None,
586 force_stop=False, timeout=None, retries=None):
587 """Start package's activity on the device.
589 Args:
590 intent_obj: An Intent object to send.
591 blocking: A boolean indicating whether we should wait for the activity to
592 finish launching.
593 trace_file_name: If present, a string that both indicates that we want to
594 profile the activity and contains the path to which the
595 trace should be saved.
596 force_stop: A boolean indicating whether we should stop the activity
597 before starting it.
598 timeout: timeout in seconds
599 retries: number of retries
601 Raises:
602 CommandFailedError if the activity could not be started.
603 CommandTimeoutError on timeout.
604 DeviceUnreachableError on missing device.
606 cmd = ['am', 'start']
607 if blocking:
608 cmd.append('-W')
609 if trace_file_name:
610 cmd.extend(['--start-profiler', trace_file_name])
611 if force_stop:
612 cmd.append('-S')
613 cmd.extend(intent_obj.am_args)
614 for line in self.RunShellCommand(cmd, check_return=True):
615 if line.startswith('Error:'):
616 raise device_errors.CommandFailedError(line, str(self))
618 @decorators.WithTimeoutAndRetriesFromInstance()
619 def StartInstrumentation(self, component, finish=True, raw=False,
620 extras=None, timeout=None, retries=None):
621 if extras is None:
622 extras = {}
624 cmd = ['am', 'instrument']
625 if finish:
626 cmd.append('-w')
627 if raw:
628 cmd.append('-r')
629 for k, v in extras.iteritems():
630 cmd.extend(['-e', str(k), str(v)])
631 cmd.append(component)
632 return self.RunShellCommand(cmd, check_return=True)
634 @decorators.WithTimeoutAndRetriesFromInstance()
635 def BroadcastIntent(self, intent_obj, timeout=None, retries=None):
636 """Send a broadcast intent.
638 Args:
639 intent: An Intent to broadcast.
640 timeout: timeout in seconds
641 retries: number of retries
643 Raises:
644 CommandTimeoutError on timeout.
645 DeviceUnreachableError on missing device.
647 cmd = ['am', 'broadcast'] + intent_obj.am_args
648 self.RunShellCommand(cmd, check_return=True)
650 @decorators.WithTimeoutAndRetriesFromInstance()
651 def GoHome(self, timeout=None, retries=None):
652 """Return to the home screen.
654 Args:
655 timeout: timeout in seconds
656 retries: number of retries
658 Raises:
659 CommandTimeoutError on timeout.
660 DeviceUnreachableError on missing device.
662 self.StartActivity(
663 intent.Intent(action='android.intent.action.MAIN',
664 category='android.intent.category.HOME'),
665 blocking=True)
667 @decorators.WithTimeoutAndRetriesFromInstance()
668 def ForceStop(self, package, timeout=None, retries=None):
669 """Close the application.
671 Args:
672 package: A string containing the name of the package to stop.
673 timeout: timeout in seconds
674 retries: number of retries
676 Raises:
677 CommandTimeoutError on timeout.
678 DeviceUnreachableError on missing device.
680 self.RunShellCommand(['am', 'force-stop', package], check_return=True)
682 @decorators.WithTimeoutAndRetriesFromInstance()
683 def ClearApplicationState(self, package, timeout=None, retries=None):
684 """Clear all state for the given package.
686 Args:
687 package: A string containing the name of the package to stop.
688 timeout: timeout in seconds
689 retries: number of retries
691 Raises:
692 CommandTimeoutError on timeout.
693 DeviceUnreachableError on missing device.
695 # Check that the package exists before clearing it for android builds below
696 # JB MR2. Necessary because calling pm clear on a package that doesn't exist
697 # may never return.
698 if ((self.build_version_sdk >=
699 constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN_MR2)
700 or self.GetApplicationPath(package)):
701 self.RunShellCommand(['pm', 'clear', package], check_return=True)
703 @decorators.WithTimeoutAndRetriesFromInstance()
704 def SendKeyEvent(self, keycode, timeout=None, retries=None):
705 """Sends a keycode to the device.
707 See: http://developer.android.com/reference/android/view/KeyEvent.html
709 Args:
710 keycode: A integer keycode to send to the device.
711 timeout: timeout in seconds
712 retries: number of retries
714 Raises:
715 CommandTimeoutError on timeout.
716 DeviceUnreachableError on missing device.
718 self.RunShellCommand(['input', 'keyevent', format(keycode, 'd')],
719 check_return=True)
721 PUSH_CHANGED_FILES_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT
722 PUSH_CHANGED_FILES_DEFAULT_RETRIES = _DEFAULT_RETRIES
724 @decorators.WithTimeoutAndRetriesDefaults(
725 PUSH_CHANGED_FILES_DEFAULT_TIMEOUT,
726 PUSH_CHANGED_FILES_DEFAULT_RETRIES)
727 def PushChangedFiles(self, host_device_tuples, timeout=None,
728 retries=None):
729 """Push files to the device, skipping files that don't need updating.
731 Args:
732 host_device_tuples: A list of (host_path, device_path) tuples, where
733 |host_path| is an absolute path of a file or directory on the host
734 that should be minimially pushed to the device, and |device_path| is
735 an absolute path of the destination on the device.
736 timeout: timeout in seconds
737 retries: number of retries
739 Raises:
740 CommandFailedError on failure.
741 CommandTimeoutError on timeout.
742 DeviceUnreachableError on missing device.
745 files = []
746 for h, d in host_device_tuples:
747 if os.path.isdir(h):
748 self.RunShellCommand(['mkdir', '-p', d], check_return=True)
749 files += self._GetChangedFilesImpl(h, d)
751 if not files:
752 return
754 size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files)
755 file_count = len(files)
756 dir_size = sum(host_utils.GetRecursiveDiskUsage(h)
757 for h, _ in host_device_tuples)
758 dir_file_count = 0
759 for h, _ in host_device_tuples:
760 if os.path.isdir(h):
761 dir_file_count += sum(len(f) for _r, _d, f in os.walk(h))
762 else:
763 dir_file_count += 1
765 push_duration = self._ApproximateDuration(
766 file_count, file_count, size, False)
767 dir_push_duration = self._ApproximateDuration(
768 len(host_device_tuples), dir_file_count, dir_size, False)
769 zip_duration = self._ApproximateDuration(1, 1, size, True)
771 self._InstallCommands()
773 if dir_push_duration < push_duration and (
774 dir_push_duration < zip_duration or not self._commands_installed):
775 self._PushChangedFilesIndividually(host_device_tuples)
776 elif push_duration < zip_duration or not self._commands_installed:
777 self._PushChangedFilesIndividually(files)
778 else:
779 self._PushChangedFilesZipped(files)
780 self.RunShellCommand(
781 ['chmod', '-R', '777'] + [d for _, d in host_device_tuples],
782 as_root=True, check_return=True)
784 def _GetChangedFilesImpl(self, host_path, device_path):
785 real_host_path = os.path.realpath(host_path)
786 try:
787 real_device_path = self.RunShellCommand(
788 ['realpath', device_path], single_line=True, check_return=True)
789 except device_errors.CommandFailedError:
790 real_device_path = None
791 if not real_device_path:
792 return [(host_path, device_path)]
794 host_hash_tuples = md5sum.CalculateHostMd5Sums([real_host_path])
795 device_paths_to_md5 = (
796 real_device_path if os.path.isfile(real_host_path)
797 else ('%s/%s' % (real_device_path, os.path.relpath(p, real_host_path))
798 for _, p in host_hash_tuples))
799 device_hash_tuples = md5sum.CalculateDeviceMd5Sums(
800 device_paths_to_md5, self)
802 if os.path.isfile(host_path):
803 if (not device_hash_tuples
804 or device_hash_tuples[0].hash != host_hash_tuples[0].hash):
805 return [(host_path, device_path)]
806 else:
807 return []
808 else:
809 device_tuple_dict = dict((d.path, d.hash) for d in device_hash_tuples)
810 to_push = []
811 for host_hash, host_abs_path in (
812 (h.hash, h.path) for h in host_hash_tuples):
813 device_abs_path = '%s/%s' % (
814 real_device_path, os.path.relpath(host_abs_path, real_host_path))
815 if (device_abs_path not in device_tuple_dict
816 or device_tuple_dict[device_abs_path] != host_hash):
817 to_push.append((host_abs_path, device_abs_path))
818 return to_push
820 def _InstallCommands(self):
821 if self._commands_installed is None:
822 try:
823 if not install_commands.Installed(self):
824 install_commands.InstallCommands(self)
825 self._commands_installed = True
826 except Exception as e:
827 logging.warning('unzip not available: %s' % str(e))
828 self._commands_installed = False
830 @staticmethod
831 def _ApproximateDuration(adb_calls, file_count, byte_count, is_zipping):
832 # We approximate the time to push a set of files to a device as:
833 # t = c1 * a + c2 * f + c3 + b / c4 + b / (c5 * c6), where
834 # t: total time (sec)
835 # c1: adb call time delay (sec)
836 # a: number of times adb is called (unitless)
837 # c2: push time delay (sec)
838 # f: number of files pushed via adb (unitless)
839 # c3: zip time delay (sec)
840 # c4: zip rate (bytes/sec)
841 # b: total number of bytes (bytes)
842 # c5: transfer rate (bytes/sec)
843 # c6: compression ratio (unitless)
845 # All of these are approximations.
846 ADB_CALL_PENALTY = 0.1 # seconds
847 ADB_PUSH_PENALTY = 0.01 # seconds
848 ZIP_PENALTY = 2.0 # seconds
849 ZIP_RATE = 10000000.0 # bytes / second
850 TRANSFER_RATE = 2000000.0 # bytes / second
851 COMPRESSION_RATIO = 2.0 # unitless
853 adb_call_time = ADB_CALL_PENALTY * adb_calls
854 adb_push_setup_time = ADB_PUSH_PENALTY * file_count
855 if is_zipping:
856 zip_time = ZIP_PENALTY + byte_count / ZIP_RATE
857 transfer_time = byte_count / (TRANSFER_RATE * COMPRESSION_RATIO)
858 else:
859 zip_time = 0
860 transfer_time = byte_count / TRANSFER_RATE
861 return adb_call_time + adb_push_setup_time + zip_time + transfer_time
863 def _PushChangedFilesIndividually(self, files):
864 for h, d in files:
865 self.adb.Push(h, d)
867 def _PushChangedFilesZipped(self, files):
868 if not files:
869 return
871 with tempfile.NamedTemporaryFile(suffix='.zip') as zip_file:
872 zip_proc = multiprocessing.Process(
873 target=DeviceUtils._CreateDeviceZip,
874 args=(zip_file.name, files))
875 zip_proc.start()
876 zip_proc.join()
878 zip_on_device = '%s/tmp.zip' % self.GetExternalStoragePath()
879 try:
880 self.adb.Push(zip_file.name, zip_on_device)
881 self.RunShellCommand(
882 ['unzip', zip_on_device],
883 as_root=True,
884 env={'PATH': '%s:$PATH' % install_commands.BIN_DIR},
885 check_return=True)
886 finally:
887 if zip_proc.is_alive():
888 zip_proc.terminate()
889 if self.IsOnline():
890 self.RunShellCommand(['rm', zip_on_device], check_return=True)
892 @staticmethod
893 def _CreateDeviceZip(zip_path, host_device_tuples):
894 with zipfile.ZipFile(zip_path, 'w') as zip_file:
895 for host_path, device_path in host_device_tuples:
896 zip_utils.WriteToZipFile(zip_file, host_path, device_path)
898 @decorators.WithTimeoutAndRetriesFromInstance()
899 def FileExists(self, device_path, timeout=None, retries=None):
900 """Checks whether the given file exists on the device.
902 Args:
903 device_path: A string containing the absolute path to the file on the
904 device.
905 timeout: timeout in seconds
906 retries: number of retries
908 Returns:
909 True if the file exists on the device, False otherwise.
911 Raises:
912 CommandTimeoutError on timeout.
913 DeviceUnreachableError on missing device.
915 try:
916 self.RunShellCommand(['test', '-e', device_path], check_return=True)
917 return True
918 except device_errors.AdbCommandFailedError:
919 return False
921 @decorators.WithTimeoutAndRetriesFromInstance()
922 def PullFile(self, device_path, host_path, timeout=None, retries=None):
923 """Pull a file from the device.
925 Args:
926 device_path: A string containing the absolute path of the file to pull
927 from the device.
928 host_path: A string containing the absolute path of the destination on
929 the host.
930 timeout: timeout in seconds
931 retries: number of retries
933 Raises:
934 CommandFailedError on failure.
935 CommandTimeoutError on timeout.
937 # Create the base dir if it doesn't exist already
938 dirname = os.path.dirname(host_path)
939 if dirname and not os.path.exists(dirname):
940 os.makedirs(dirname)
941 self.adb.Pull(device_path, host_path)
943 def _ReadFileWithPull(self, device_path):
944 try:
945 d = tempfile.mkdtemp()
946 host_temp_path = os.path.join(d, 'tmp_ReadFileWithPull')
947 self.adb.Pull(device_path, host_temp_path)
948 with open(host_temp_path, 'r') as host_temp:
949 return host_temp.read()
950 finally:
951 if os.path.exists(d):
952 shutil.rmtree(d)
954 _LS_RE = re.compile(
955 r'(?P<perms>\S+) +(?P<owner>\S+) +(?P<group>\S+) +(?:(?P<size>\d+) +)?'
956 + r'(?P<date>\S+) +(?P<time>\S+) +(?P<name>.+)$')
958 @decorators.WithTimeoutAndRetriesFromInstance()
959 def ReadFile(self, device_path, as_root=False,
960 timeout=None, retries=None):
961 """Reads the contents of a file from the device.
963 Args:
964 device_path: A string containing the absolute path of the file to read
965 from the device.
966 as_root: A boolean indicating whether the read should be executed with
967 root privileges.
968 timeout: timeout in seconds
969 retries: number of retries
971 Returns:
972 The contents of |device_path| as a string. Contents are intepreted using
973 universal newlines, so the caller will see them encoded as '\n'. Also,
974 all lines will be terminated.
976 Raises:
977 AdbCommandFailedError if the file can't be read.
978 CommandTimeoutError on timeout.
979 DeviceUnreachableError on missing device.
981 # TODO(jbudorick): Implement a generic version of Stat() that handles
982 # as_root=True, then switch this implementation to use that.
983 size = None
984 ls_out = self.RunShellCommand(['ls', '-l', device_path], as_root=as_root,
985 check_return=True)
986 for line in ls_out:
987 m = self._LS_RE.match(line)
988 if m and m.group('name') == posixpath.basename(device_path):
989 size = int(m.group('size'))
990 break
991 else:
992 logging.warning('Could not determine size of %s.', device_path)
994 if size is None or size <= self._MAX_ADB_OUTPUT_LENGTH:
995 return _JoinLines(self.RunShellCommand(
996 ['cat', device_path], as_root=as_root, check_return=True))
997 elif as_root and self.NeedsSU():
998 with device_temp_file.DeviceTempFile(self.adb) as device_temp:
999 self.RunShellCommand(['cp', device_path, device_temp.name],
1000 as_root=True, check_return=True)
1001 return self._ReadFileWithPull(device_temp.name)
1002 else:
1003 return self._ReadFileWithPull(device_path)
1005 def _WriteFileWithPush(self, device_path, contents):
1006 with tempfile.NamedTemporaryFile() as host_temp:
1007 host_temp.write(contents)
1008 host_temp.flush()
1009 self.adb.Push(host_temp.name, device_path)
1011 @decorators.WithTimeoutAndRetriesFromInstance()
1012 def WriteFile(self, device_path, contents, as_root=False, force_push=False,
1013 timeout=None, retries=None):
1014 """Writes |contents| to a file on the device.
1016 Args:
1017 device_path: A string containing the absolute path to the file to write
1018 on the device.
1019 contents: A string containing the data to write to the device.
1020 as_root: A boolean indicating whether the write should be executed with
1021 root privileges (if available).
1022 force_push: A boolean indicating whether to force the operation to be
1023 performed by pushing a file to the device. The default is, when the
1024 contents are short, to pass the contents using a shell script instead.
1025 timeout: timeout in seconds
1026 retries: number of retries
1028 Raises:
1029 CommandFailedError if the file could not be written on the device.
1030 CommandTimeoutError on timeout.
1031 DeviceUnreachableError on missing device.
1033 if not force_push and len(contents) < self._MAX_ADB_COMMAND_LENGTH:
1034 # If the contents are small, for efficieny we write the contents with
1035 # a shell command rather than pushing a file.
1036 cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents),
1037 cmd_helper.SingleQuote(device_path))
1038 self.RunShellCommand(cmd, as_root=as_root, check_return=True)
1039 elif as_root and self.NeedsSU():
1040 # Adb does not allow to "push with su", so we first push to a temp file
1041 # on a safe location, and then copy it to the desired location with su.
1042 with device_temp_file.DeviceTempFile(self.adb) as device_temp:
1043 self._WriteFileWithPush(device_temp.name, contents)
1044 # Here we need 'cp' rather than 'mv' because the temp and
1045 # destination files might be on different file systems (e.g.
1046 # on internal storage and an external sd card).
1047 self.RunShellCommand(['cp', device_temp.name, device_path],
1048 as_root=True, check_return=True)
1049 else:
1050 # If root is not needed, we can push directly to the desired location.
1051 self._WriteFileWithPush(device_path, contents)
1053 @decorators.WithTimeoutAndRetriesFromInstance()
1054 def Ls(self, device_path, timeout=None, retries=None):
1055 """Lists the contents of a directory on the device.
1057 Args:
1058 device_path: A string containing the path of the directory on the device
1059 to list.
1060 timeout: timeout in seconds
1061 retries: number of retries
1063 Returns:
1064 A list of pairs (filename, stat) for each file found in the directory,
1065 where the stat object has the properties: st_mode, st_size, and st_time.
1067 Raises:
1068 AdbCommandFailedError if |device_path| does not specify a valid and
1069 accessible directory in the device.
1070 CommandTimeoutError on timeout.
1071 DeviceUnreachableError on missing device.
1073 return self.adb.Ls(device_path)
1075 @decorators.WithTimeoutAndRetriesFromInstance()
1076 def Stat(self, device_path, timeout=None, retries=None):
1077 """Get the stat attributes of a file or directory on the device.
1079 Args:
1080 device_path: A string containing the path of from which to get attributes
1081 on the device.
1082 timeout: timeout in seconds
1083 retries: number of retries
1085 Returns:
1086 A stat object with the properties: st_mode, st_size, and st_time
1088 Raises:
1089 CommandFailedError if device_path cannot be found on the device.
1090 CommandTimeoutError on timeout.
1091 DeviceUnreachableError on missing device.
1093 dirname, target = device_path.rsplit('/', 1)
1094 for filename, stat in self.adb.Ls(dirname):
1095 if filename == target:
1096 return stat
1097 raise device_errors.CommandFailedError(
1098 'Cannot find file or directory: %r' % device_path, str(self))
1100 @decorators.WithTimeoutAndRetriesFromInstance()
1101 def SetJavaAsserts(self, enabled, timeout=None, retries=None):
1102 """Enables or disables Java asserts.
1104 Args:
1105 enabled: A boolean indicating whether Java asserts should be enabled
1106 or disabled.
1107 timeout: timeout in seconds
1108 retries: number of retries
1110 Returns:
1111 True if the device-side property changed and a restart is required as a
1112 result, False otherwise.
1114 Raises:
1115 CommandTimeoutError on timeout.
1117 def find_property(lines, property_name):
1118 for index, line in enumerate(lines):
1119 if line.strip() == '':
1120 continue
1121 key, value = (s.strip() for s in line.split('=', 1))
1122 if key == property_name:
1123 return index, value
1124 return None, ''
1126 new_value = 'all' if enabled else ''
1128 # First ensure the desired property is persisted.
1129 try:
1130 properties = self.ReadFile(
1131 constants.DEVICE_LOCAL_PROPERTIES_PATH).splitlines()
1132 except device_errors.CommandFailedError:
1133 properties = []
1134 index, value = find_property(properties, self.JAVA_ASSERT_PROPERTY)
1135 if new_value != value:
1136 if new_value:
1137 new_line = '%s=%s' % (self.JAVA_ASSERT_PROPERTY, new_value)
1138 if index is None:
1139 properties.append(new_line)
1140 else:
1141 properties[index] = new_line
1142 else:
1143 assert index is not None # since new_value == '' and new_value != value
1144 properties.pop(index)
1145 self.WriteFile(constants.DEVICE_LOCAL_PROPERTIES_PATH,
1146 _JoinLines(properties))
1148 # Next, check the current runtime value is what we need, and
1149 # if not, set it and report that a reboot is required.
1150 value = self.GetProp(self.JAVA_ASSERT_PROPERTY)
1151 if new_value != value:
1152 self.SetProp(self.JAVA_ASSERT_PROPERTY, new_value)
1153 return True
1154 else:
1155 return False
1158 @property
1159 def build_description(self):
1160 """Returns the build description of the system.
1162 For example:
1163 nakasi-user 4.4.4 KTU84P 1227136 release-keys
1165 return self.GetProp('ro.build.description', cache=True)
1167 @property
1168 def build_fingerprint(self):
1169 """Returns the build fingerprint of the system.
1171 For example:
1172 google/nakasi/grouper:4.4.4/KTU84P/1227136:user/release-keys
1174 return self.GetProp('ro.build.fingerprint', cache=True)
1176 @property
1177 def build_id(self):
1178 """Returns the build ID of the system (e.g. 'KTU84P')."""
1179 return self.GetProp('ro.build.id', cache=True)
1181 @property
1182 def build_product(self):
1183 """Returns the build product of the system (e.g. 'grouper')."""
1184 return self.GetProp('ro.build.product', cache=True)
1186 @property
1187 def build_type(self):
1188 """Returns the build type of the system (e.g. 'user')."""
1189 return self.GetProp('ro.build.type', cache=True)
1191 @property
1192 def build_version_sdk(self):
1193 """Returns the build version sdk of the system as a number (e.g. 19).
1195 For version code numbers see:
1196 http://developer.android.com/reference/android/os/Build.VERSION_CODES.html
1198 For named constants see:
1199 pylib.constants.ANDROID_SDK_VERSION_CODES
1201 Raises:
1202 CommandFailedError if the build version sdk is not a number.
1204 value = self.GetProp('ro.build.version.sdk', cache=True)
1205 try:
1206 return int(value)
1207 except ValueError:
1208 raise device_errors.CommandFailedError(
1209 'Invalid build version sdk: %r' % value)
1211 @property
1212 def product_cpu_abi(self):
1213 """Returns the product cpu abi of the device (e.g. 'armeabi-v7a')."""
1214 return self.GetProp('ro.product.cpu.abi', cache=True)
1216 @property
1217 def product_model(self):
1218 """Returns the name of the product model (e.g. 'Nexus 7')."""
1219 return self.GetProp('ro.product.model', cache=True)
1221 @property
1222 def product_name(self):
1223 """Returns the product name of the device (e.g. 'nakasi')."""
1224 return self.GetProp('ro.product.name', cache=True)
1226 def GetProp(self, property_name, cache=False, timeout=DEFAULT,
1227 retries=DEFAULT):
1228 """Gets a property from the device.
1230 Args:
1231 property_name: A string containing the name of the property to get from
1232 the device.
1233 cache: A boolean indicating whether to cache the value of this property.
1234 timeout: timeout in seconds
1235 retries: number of retries
1237 Returns:
1238 The value of the device's |property_name| property.
1240 Raises:
1241 CommandTimeoutError on timeout.
1243 assert isinstance(property_name, basestring), (
1244 "property_name is not a string: %r" % property_name)
1246 cache_key = '_prop:' + property_name
1247 if cache and cache_key in self._cache:
1248 return self._cache[cache_key]
1249 else:
1250 # timeout and retries are handled down at run shell, because we don't
1251 # want to apply them in the other branch when reading from the cache
1252 value = self.RunShellCommand(
1253 ['getprop', property_name], single_line=True, check_return=True,
1254 timeout=self._default_timeout if timeout is DEFAULT else timeout,
1255 retries=self._default_retries if retries is DEFAULT else retries)
1256 if cache or cache_key in self._cache:
1257 self._cache[cache_key] = value
1258 return value
1260 @decorators.WithTimeoutAndRetriesFromInstance()
1261 def SetProp(self, property_name, value, check=False, timeout=None,
1262 retries=None):
1263 """Sets a property on the device.
1265 Args:
1266 property_name: A string containing the name of the property to set on
1267 the device.
1268 value: A string containing the value to set to the property on the
1269 device.
1270 check: A boolean indicating whether to check that the property was
1271 successfully set on the device.
1272 timeout: timeout in seconds
1273 retries: number of retries
1275 Raises:
1276 CommandFailedError if check is true and the property was not correctly
1277 set on the device (e.g. because it is not rooted).
1278 CommandTimeoutError on timeout.
1280 assert isinstance(property_name, basestring), (
1281 "property_name is not a string: %r" % property_name)
1282 assert isinstance(value, basestring), "value is not a string: %r" % value
1284 self.RunShellCommand(['setprop', property_name, value], check_return=True)
1285 if property_name in self._cache:
1286 del self._cache[property_name]
1287 # TODO(perezju) remove the option and make the check mandatory, but using a
1288 # single shell script to both set- and getprop.
1289 if check and value != self.GetProp(property_name):
1290 raise device_errors.CommandFailedError(
1291 'Unable to set property %r on the device to %r'
1292 % (property_name, value), str(self))
1294 @decorators.WithTimeoutAndRetriesFromInstance()
1295 def GetABI(self, timeout=None, retries=None):
1296 """Gets the device main ABI.
1298 Args:
1299 timeout: timeout in seconds
1300 retries: number of retries
1302 Returns:
1303 The device's main ABI name.
1305 Raises:
1306 CommandTimeoutError on timeout.
1308 return self.GetProp('ro.product.cpu.abi')
1310 @decorators.WithTimeoutAndRetriesFromInstance()
1311 def GetPids(self, process_name, timeout=None, retries=None):
1312 """Returns the PIDs of processes with the given name.
1314 Note that the |process_name| is often the package name.
1316 Args:
1317 process_name: A string containing the process name to get the PIDs for.
1318 timeout: timeout in seconds
1319 retries: number of retries
1321 Returns:
1322 A dict mapping process name to PID for each process that contained the
1323 provided |process_name|.
1325 Raises:
1326 CommandTimeoutError on timeout.
1327 DeviceUnreachableError on missing device.
1329 return self._GetPidsImpl(process_name)
1331 def _GetPidsImpl(self, process_name):
1332 procs_pids = {}
1333 for line in self.RunShellCommand('ps', check_return=True):
1334 try:
1335 ps_data = line.split()
1336 if process_name in ps_data[-1]:
1337 procs_pids[ps_data[-1]] = ps_data[1]
1338 except IndexError:
1339 pass
1340 return procs_pids
1342 @decorators.WithTimeoutAndRetriesFromInstance()
1343 def TakeScreenshot(self, host_path=None, timeout=None, retries=None):
1344 """Takes a screenshot of the device.
1346 Args:
1347 host_path: A string containing the path on the host to save the
1348 screenshot to. If None, a file name in the current
1349 directory will be generated.
1350 timeout: timeout in seconds
1351 retries: number of retries
1353 Returns:
1354 The name of the file on the host to which the screenshot was saved.
1356 Raises:
1357 CommandFailedError on failure.
1358 CommandTimeoutError on timeout.
1359 DeviceUnreachableError on missing device.
1361 if not host_path:
1362 host_path = os.path.abspath('screenshot-%s.png' % _GetTimeStamp())
1363 with device_temp_file.DeviceTempFile(self.adb, suffix='.png') as device_tmp:
1364 self.RunShellCommand(['/system/bin/screencap', '-p', device_tmp.name],
1365 check_return=True)
1366 self.PullFile(device_tmp.name, host_path)
1367 return host_path
1369 @decorators.WithTimeoutAndRetriesFromInstance()
1370 def GetMemoryUsageForPid(self, pid, timeout=None, retries=None):
1371 """Gets the memory usage for the given PID.
1373 Args:
1374 pid: PID of the process.
1375 timeout: timeout in seconds
1376 retries: number of retries
1378 Returns:
1379 A dict containing memory usage statistics for the PID. May include:
1380 Size, Rss, Pss, Shared_Clean, Shared_Dirty, Private_Clean,
1381 Private_Dirty, VmHWM
1383 Raises:
1384 CommandTimeoutError on timeout.
1386 result = collections.defaultdict(int)
1388 try:
1389 result.update(self._GetMemoryUsageForPidFromSmaps(pid))
1390 except device_errors.CommandFailedError:
1391 logging.exception('Error getting memory usage from smaps')
1393 try:
1394 result.update(self._GetMemoryUsageForPidFromStatus(pid))
1395 except device_errors.CommandFailedError:
1396 logging.exception('Error getting memory usage from status')
1398 return result
1400 def _GetMemoryUsageForPidFromSmaps(self, pid):
1401 SMAPS_COLUMNS = (
1402 'Size', 'Rss', 'Pss', 'Shared_Clean', 'Shared_Dirty', 'Private_Clean',
1403 'Private_Dirty')
1405 showmap_out = self.RunShellCommand(
1406 ['showmap', str(pid)], as_root=True, check_return=True)
1407 if not showmap_out:
1408 raise device_errors.CommandFailedError('No output from showmap')
1410 split_totals = showmap_out[-1].split()
1411 if (not split_totals
1412 or len(split_totals) != 9
1413 or split_totals[-1] != 'TOTAL'):
1414 raise device_errors.CommandFailedError(
1415 'Invalid output from showmap: %s' % '\n'.join(showmap_out))
1417 return dict(itertools.izip(SMAPS_COLUMNS, (int(n) for n in split_totals)))
1419 def _GetMemoryUsageForPidFromStatus(self, pid):
1420 for line in self.ReadFile(
1421 '/proc/%s/status' % str(pid), as_root=True).splitlines():
1422 if line.startswith('VmHWM:'):
1423 return {'VmHWM': int(line.split()[1])}
1424 else:
1425 raise device_errors.CommandFailedError(
1426 'Could not find memory peak value for pid %s', str(pid))
1428 @decorators.WithTimeoutAndRetriesFromInstance()
1429 def GetLogcatMonitor(self, timeout=None, retries=None, *args, **kwargs):
1430 """Returns a new LogcatMonitor associated with this device.
1432 Parameters passed to this function are passed directly to
1433 |logcat_monitor.LogcatMonitor| and are documented there.
1435 Args:
1436 timeout: timeout in seconds
1437 retries: number of retries
1439 return logcat_monitor.LogcatMonitor(self.adb, *args, **kwargs)
1441 @decorators.WithTimeoutAndRetriesFromInstance()
1442 def GetBatteryInfo(self, timeout=None, retries=None):
1443 """Gets battery info for the device.
1445 Args:
1446 timeout: timeout in seconds
1447 retries: number of retries
1448 Returns:
1449 A dict containing various battery information as reported by dumpsys
1450 battery.
1452 result = {}
1453 # Skip the first line, which is just a header.
1454 for line in self.RunShellCommand(
1455 ['dumpsys', 'battery'], check_return=True)[1:]:
1456 # If usb charging has been disabled, an extra line of header exists.
1457 if 'UPDATES STOPPED' in line:
1458 logging.warning('Dumpsys battery not receiving updates. '
1459 'Run dumpsys battery reset if this is in error.')
1460 elif ':' not in line:
1461 logging.warning('Unknown line found in dumpsys battery.')
1462 logging.warning(line)
1463 else:
1464 k, v = line.split(': ', 1)
1465 result[k.strip()] = v.strip()
1466 return result
1468 @decorators.WithTimeoutAndRetriesFromInstance()
1469 def GetCharging(self, timeout=None, retries=None):
1470 """Gets the charging state of the device.
1472 Args:
1473 timeout: timeout in seconds
1474 retries: number of retries
1475 Returns:
1476 True if the device is charging, false otherwise.
1478 battery_info = self.GetBatteryInfo()
1479 for k in ('AC powered', 'USB powered', 'Wireless powered'):
1480 if (k in battery_info and
1481 battery_info[k].lower() in ('true', '1', 'yes')):
1482 return True
1483 return False
1485 @decorators.WithTimeoutAndRetriesFromInstance()
1486 def SetCharging(self, enabled, timeout=None, retries=None):
1487 """Enables or disables charging on the device.
1489 Args:
1490 enabled: A boolean indicating whether charging should be enabled or
1491 disabled.
1492 timeout: timeout in seconds
1493 retries: number of retries
1495 if 'charging_config' not in self._cache:
1496 for c in _CONTROL_CHARGING_COMMANDS:
1497 if self.FileExists(c['witness_file']):
1498 self._cache['charging_config'] = c
1499 break
1500 else:
1501 raise device_errors.CommandFailedError(
1502 'Unable to find charging commands.')
1504 if enabled:
1505 command = self._cache['charging_config']['enable_command']
1506 else:
1507 command = self._cache['charging_config']['disable_command']
1509 def set_and_verify_charging():
1510 self.RunShellCommand(command, check_return=True)
1511 return self.GetCharging() == enabled
1513 timeout_retry.WaitFor(set_and_verify_charging, wait_period=1)
1515 # TODO(rnephew): Make private when all use cases can use the context manager.
1516 @decorators.WithTimeoutAndRetriesFromInstance()
1517 def DisableBatteryUpdates(self, timeout=None, retries=None):
1518 """ Resets battery data and makes device appear like it is not
1519 charging so that it will collect power data since last charge.
1521 Args:
1522 timeout: timeout in seconds
1523 retries: number of retries
1525 def battery_updates_disabled():
1526 return self.GetCharging() is False
1528 self.RunShellCommand(
1529 ['dumpsys', 'batterystats', '--reset'], check_return=True)
1530 battery_data = self.RunShellCommand(
1531 ['dumpsys', 'batterystats', '--charged', '--checkin'],
1532 check_return=True)
1533 ROW_TYPE_INDEX = 3
1534 PWI_POWER_INDEX = 5
1535 for line in battery_data:
1536 l = line.split(',')
1537 if (len(l) > PWI_POWER_INDEX and l[ROW_TYPE_INDEX] == 'pwi'
1538 and l[PWI_POWER_INDEX] != 0):
1539 raise device_errors.CommandFailedError(
1540 'Non-zero pmi value found after reset.')
1541 self.RunShellCommand(['dumpsys', 'battery', 'set', 'usb', '0'],
1542 check_return=True)
1543 timeout_retry.WaitFor(battery_updates_disabled, wait_period=1)
1545 # TODO(rnephew): Make private when all use cases can use the context manager.
1546 @decorators.WithTimeoutAndRetriesFromInstance()
1547 def EnableBatteryUpdates(self, timeout=None, retries=None):
1548 """ Restarts device charging so that dumpsys no longer collects power data.
1550 Args:
1551 timeout: timeout in seconds
1552 retries: number of retries
1554 def battery_updates_enabled():
1555 return self.GetCharging() is True
1557 self.RunShellCommand(['dumpsys', 'battery', 'set', 'usb', '1'],
1558 check_return=True)
1559 self.RunShellCommand(['dumpsys', 'battery', 'reset'], check_return=True)
1560 timeout_retry.WaitFor(battery_updates_enabled, wait_period=1)
1562 @contextlib.contextmanager
1563 def BatteryMeasurement(self, timeout=None, retries=None):
1564 """Context manager that enables battery data collection. It makes
1565 the device appear to stop charging so that dumpsys will start collecting
1566 power data since last charge. Once the with block is exited, charging is
1567 resumed and power data since last charge is no longer collected.
1569 Only for devices L and higher.
1571 Example usage:
1572 with BatteryMeasurement():
1573 browser_actions()
1574 get_power_data() # report usage within this block
1575 after_measurements() # Anything that runs after power
1576 # measurements are collected
1578 Args:
1579 timeout: timeout in seconds
1580 retries: number of retries
1582 if self.build_version_sdk < constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP:
1583 raise device_errors.CommandFailedError('Device must be L or higher.')
1584 try:
1585 self.DisableBatteryUpdates(timeout=timeout, retries=retries)
1586 yield
1587 finally:
1588 self.EnableBatteryUpdates(timeout=timeout, retries=retries)
1590 @decorators.WithTimeoutAndRetriesFromInstance()
1591 def GetDevicePieWrapper(self, timeout=None, retries=None):
1592 """Gets the absolute path to the run_pie wrapper on the device.
1594 We have to build our device executables to be PIE, but they need to be able
1595 to run on versions of android that don't support PIE (i.e. ICS and below).
1596 To do so, we push a wrapper to the device that lets older android versions
1597 run PIE executables. This method pushes that wrapper to the device if
1598 necessary and returns the path to it.
1600 This is exposed publicly to allow clients to write scripts using run_pie
1601 (e.g. md5sum.CalculateDeviceMd5Sum).
1603 Args:
1604 timeout: timeout in seconds
1605 retries: number of retries
1607 Returns:
1608 The path to the PIE wrapper on the device, or an empty string if the
1609 device does not require the wrapper.
1611 if 'run_pie' not in self._cache:
1612 pie = ''
1613 if (self.build_version_sdk <
1614 constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN):
1615 host_pie_path = os.path.join(constants.GetOutDirectory(), 'run_pie')
1616 if not os.path.exists(host_pie_path):
1617 raise device_errors.CommandFailedError('Please build run_pie')
1618 pie = '%s/run_pie' % constants.TEST_EXECUTABLE_DIR
1619 self.adb.Push(host_pie_path, pie)
1621 self._cache['run_pie'] = pie
1623 return self._cache['run_pie']
1625 @classmethod
1626 def parallel(cls, devices=None, async=False):
1627 """Creates a Parallelizer to operate over the provided list of devices.
1629 If |devices| is either |None| or an empty list, the Parallelizer will
1630 operate over all attached devices.
1632 Args:
1633 devices: A list of either DeviceUtils instances or objects from
1634 from which DeviceUtils instances can be constructed. If None,
1635 all attached devices will be used.
1636 async: If true, returns a Parallelizer that runs operations
1637 asynchronously.
1639 Returns:
1640 A Parallelizer operating over |devices|.
1642 if not devices:
1643 devices = adb_wrapper.AdbWrapper.GetDevices()
1644 if not devices:
1645 raise device_errors.NoDevicesError()
1646 devices = [d if isinstance(d, cls) else cls(d) for d in devices]
1647 if async:
1648 return parallelizer.Parallelizer(devices)
1649 else:
1650 return parallelizer.SyncParallelizer(devices)