1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 """Provides a variety of device interactions based on adb.
7 Eventually, this will be based on adb_wrapper.
9 # pylint: disable=unused-argument
15 import multiprocessing
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
46 # A sentinel object for default values
47 # TODO(jbudorick,perezju): revisit how default values are handled by
48 # the timeout_retry decorators.
51 _CONTROL_CHARGING_COMMANDS
= [
54 'witness_file': '/sys/module/pm8921_charger/parameters/disabled',
55 'enable_command': 'echo 0 > /sys/module/pm8921_charger/parameters/disabled',
57 'echo 1 > /sys/module/pm8921_charger/parameters/disabled',
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',
66 'echo 0x4A > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && '
67 'echo 1 > /sys/class/power_supply/usb/online'),
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
)
78 """Returns a list of Android Virtual Devices.
81 A list containing the configured AVDs.
83 lines
= cmd_helper
.GetCmdOutput([
84 os
.path
.join(constants
.ANDROID_SDK_ROOT
, 'tools', 'android'),
85 'list', 'avd']).splitlines()
88 if 'Name:' not in line
:
90 key
, value
= (s
.strip() for s
in line
.split(':', 1))
96 @decorators.WithExplicitTimeoutAndRetries(
97 _DEFAULT_TIMEOUT
, _DEFAULT_RETRIES
)
99 """Restarts the adb server.
102 CommandFailedError if we fail to kill or restart the server.
105 return not adb_wrapper
.AdbWrapper
.IsServerOnline()
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')
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.
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
150 default_retries: An integer containing the default number or times an
151 operation should be retried on failure if no explicit
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
):
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
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
171 assert hasattr(self
, decorators
.DEFAULT_TIMEOUT_ATTR
)
172 assert hasattr(self
, decorators
.DEFAULT_RETRIES_ATTR
)
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.
183 timeout: timeout in seconds
184 retries: number of retries
187 True if the device is online, False otherwise.
190 CommandTimeoutError on timeout.
193 return self
.adb
.GetState() == 'device'
194 except base_error
.BaseError
as exc
:
195 logging
.info('Failed to get state: %s', exc
)
198 @decorators.WithTimeoutAndRetriesFromInstance()
199 def HasRoot(self
, timeout
=None, retries
=None):
200 """Checks whether or not adbd has root privileges.
203 timeout: timeout in seconds
204 retries: number of retries
207 True if adbd has root privileges, False otherwise.
210 CommandTimeoutError on timeout.
211 DeviceUnreachableError on missing device.
214 self
.RunShellCommand('ls /root', check_return
=True)
216 except device_errors
.AdbCommandFailedError
:
219 def NeedsSU(self
, timeout
=DEFAULT
, retries
=DEFAULT
):
220 """Checks whether 'su' is needed to access protected resources.
223 timeout: timeout in seconds
224 retries: number of retries
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).
233 CommandTimeoutError on timeout.
234 DeviceUnreachableError on missing device.
236 if 'needs_su' not in self
._cache
:
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.
253 timeout: timeout in seconds
254 retries: number of retries
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']
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.
273 timeout: timeout in seconds
274 retries: number of retries
277 True if the device is running a user build, False otherwise (i.e. if
278 it's running a userdebug build).
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.
291 timeout: timeout in seconds
292 retries: number of retries
295 The device's path to its SD card.
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',
309 raise device_errors
.CommandFailedError('$EXTERNAL_STORAGE is not set',
311 self
._cache
['external_storage'] = 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.
319 package: Name of the package.
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
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
)
334 if not output
.startswith('package:'):
335 raise device_errors
.CommandFailedError('pm path returned: %r' % output
,
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.
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
353 CommandFailedError on failure.
354 CommandTimeoutError if one of the component waits times out.
355 DeviceUnreachableError if the device becomes unresponsive.
359 self
.RunShellCommand(['test', '-d', self
.GetExternalStoragePath()],
362 except device_errors
.AdbCommandFailedError
:
367 return self
.GetApplicationPath('android')
368 except device_errors
.CommandFailedError
:
371 def boot_completed():
372 return self
.GetProp('sys.boot_completed') == '1'
375 return 'Wi-Fi is enabled' in self
.RunShellCommand(['dumpsys', 'wifi'],
378 self
.adb
.WaitForDevice()
379 timeout_retry
.WaitFor(sd_card_ready
)
380 timeout_retry
.WaitFor(pm_ready
)
381 timeout_retry
.WaitFor(boot_completed
)
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.
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
402 CommandTimeoutError on timeout.
403 DeviceUnreachableError on missing device.
405 def device_offline():
406 return not self
.IsOnline()
410 timeout_retry
.WaitFor(device_offline
, wait_period
=1)
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):
423 Noop if an identical APK is already installed.
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
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
)
443 should_install
= True
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,
451 """Run an ADB shell command.
453 The command to run |cmd| should be a sequence of program arguments or else
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
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.
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
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
483 timeout: timeout in seconds
484 retries: number of retries
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).
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
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
))
507 return self
.adb
.Shell(cmd
)
508 except device_errors
.AdbCommandFailedError
as exc
:
514 if not isinstance(cmd
, basestring
):
515 cmd
= ' '.join(cmd_helper
.SingleQuote(s
) for s
in cmd
)
517 env
= ' '.join(env_quote(k
, v
) for k
, v
in env
.iteritems())
518 cmd
= '%s %s' % (env
, cmd
)
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
)
525 timeout
= self
._default
_timeout
527 if len(cmd
) < self
._MAX
_ADB
_COMMAND
_LENGTH
:
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 ...',
534 output
= do_run('sh %s' % script
.name_quoted
)
536 output
= output
.splitlines()
540 elif len(output
) == 1:
543 msg
= 'one line of output was expected, but got: %s'
544 raise device_errors
.CommandFailedError(msg
% output
, str(self
))
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.
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
557 as_root: A boolean indicating whether the kill should be executed with
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
565 CommandFailedError if no process was killed.
566 CommandTimeoutError on timeout.
567 DeviceUnreachableError on missing device.
569 pids
= self
._GetPidsImpl
(process_name
)
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)
579 while self
._GetPidsImpl
(process_name
):
580 time
.sleep(wait_period
)
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.
590 intent_obj: An Intent object to send.
591 blocking: A boolean indicating whether we should wait for the activity to
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
598 timeout: timeout in seconds
599 retries: number of retries
602 CommandFailedError if the activity could not be started.
603 CommandTimeoutError on timeout.
604 DeviceUnreachableError on missing device.
606 cmd
= ['am', 'start']
610 cmd
.extend(['--start-profiler', trace_file_name
])
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):
624 cmd
= ['am', 'instrument']
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.
639 intent: An Intent to broadcast.
640 timeout: timeout in seconds
641 retries: number of retries
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.
655 timeout: timeout in seconds
656 retries: number of retries
659 CommandTimeoutError on timeout.
660 DeviceUnreachableError on missing device.
663 intent
.Intent(action
='android.intent.action.MAIN',
664 category
='android.intent.category.HOME'),
667 @decorators.WithTimeoutAndRetriesFromInstance()
668 def ForceStop(self
, package
, timeout
=None, retries
=None):
669 """Close the application.
672 package: A string containing the name of the package to stop.
673 timeout: timeout in seconds
674 retries: number of retries
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.
687 package: A string containing the name of the package to stop.
688 timeout: timeout in seconds
689 retries: number of retries
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
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
710 keycode: A integer keycode to send to the device.
711 timeout: timeout in seconds
712 retries: number of retries
715 CommandTimeoutError on timeout.
716 DeviceUnreachableError on missing device.
718 self
.RunShellCommand(['input', 'keyevent', format(keycode
, 'd')],
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,
729 """Push files to the device, skipping files that don't need updating.
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
740 CommandFailedError on failure.
741 CommandTimeoutError on timeout.
742 DeviceUnreachableError on missing device.
746 for h
, d
in host_device_tuples
:
748 self
.RunShellCommand(['mkdir', '-p', d
], check_return
=True)
749 files
+= self
._GetChangedFilesImpl
(h
, d
)
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
)
759 for h
, _
in host_device_tuples
:
761 dir_file_count
+= sum(len(f
) for _r
, _d
, f
in os
.walk(h
))
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
)
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
)
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
)]
809 device_tuple_dict
= dict((d
.path
, d
.hash) for d
in device_hash_tuples
)
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
))
820 def _InstallCommands(self
):
821 if self
._commands
_installed
is None:
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
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
856 zip_time
= ZIP_PENALTY
+ byte_count
/ ZIP_RATE
857 transfer_time
= byte_count
/ (TRANSFER_RATE
* COMPRESSION_RATIO
)
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
):
867 def _PushChangedFilesZipped(self
, files
):
871 with tempfile
.NamedTemporaryFile(suffix
='.zip') as zip_file
:
872 zip_proc
= multiprocessing
.Process(
873 target
=DeviceUtils
._CreateDeviceZip
,
874 args
=(zip_file
.name
, files
))
878 zip_on_device
= '%s/tmp.zip' % self
.GetExternalStoragePath()
880 self
.adb
.Push(zip_file
.name
, zip_on_device
)
881 self
.RunShellCommand(
882 ['unzip', zip_on_device
],
884 env
={'PATH': '%s:$PATH' % install_commands
.BIN_DIR
},
887 if zip_proc
.is_alive():
890 self
.RunShellCommand(['rm', zip_on_device
], check_return
=True)
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.
903 device_path: A string containing the absolute path to the file on the
905 timeout: timeout in seconds
906 retries: number of retries
909 True if the file exists on the device, False otherwise.
912 CommandTimeoutError on timeout.
913 DeviceUnreachableError on missing device.
916 self
.RunShellCommand(['test', '-e', device_path
], check_return
=True)
918 except device_errors
.AdbCommandFailedError
:
921 @decorators.WithTimeoutAndRetriesFromInstance()
922 def PullFile(self
, device_path
, host_path
, timeout
=None, retries
=None):
923 """Pull a file from the device.
926 device_path: A string containing the absolute path of the file to pull
928 host_path: A string containing the absolute path of the destination on
930 timeout: timeout in seconds
931 retries: number of retries
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
):
941 self
.adb
.Pull(device_path
, host_path
)
943 def _ReadFileWithPull(self
, device_path
):
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()
951 if os
.path
.exists(d
):
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.
964 device_path: A string containing the absolute path of the file to read
966 as_root: A boolean indicating whether the read should be executed with
968 timeout: timeout in seconds
969 retries: number of retries
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.
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.
984 ls_out
= self
.RunShellCommand(['ls', '-l', device_path
], as_root
=as_root
,
987 m
= self
._LS
_RE
.match(line
)
988 if m
and m
.group('name') == posixpath
.basename(device_path
):
989 size
= int(m
.group('size'))
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
)
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
)
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.
1017 device_path: A string containing the absolute path to the file to write
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
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)
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.
1058 device_path: A string containing the path of the directory on the device
1060 timeout: timeout in seconds
1061 retries: number of retries
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.
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.
1080 device_path: A string containing the path of from which to get attributes
1082 timeout: timeout in seconds
1083 retries: number of retries
1086 A stat object with the properties: st_mode, st_size, and st_time
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
:
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.
1105 enabled: A boolean indicating whether Java asserts should be enabled
1107 timeout: timeout in seconds
1108 retries: number of retries
1111 True if the device-side property changed and a restart is required as a
1112 result, False otherwise.
1115 CommandTimeoutError on timeout.
1117 def find_property(lines
, property_name
):
1118 for index
, line
in enumerate(lines
):
1119 if line
.strip() == '':
1121 key
, value
= (s
.strip() for s
in line
.split('=', 1))
1122 if key
== property_name
:
1126 new_value
= 'all' if enabled
else ''
1128 # First ensure the desired property is persisted.
1130 properties
= self
.ReadFile(
1131 constants
.DEVICE_LOCAL_PROPERTIES_PATH
).splitlines()
1132 except device_errors
.CommandFailedError
:
1134 index
, value
= find_property(properties
, self
.JAVA_ASSERT_PROPERTY
)
1135 if new_value
!= value
:
1137 new_line
= '%s=%s' % (self
.JAVA_ASSERT_PROPERTY
, new_value
)
1139 properties
.append(new_line
)
1141 properties
[index
] = new_line
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
)
1159 def build_description(self
):
1160 """Returns the build description of the system.
1163 nakasi-user 4.4.4 KTU84P 1227136 release-keys
1165 return self
.GetProp('ro.build.description', cache
=True)
1168 def build_fingerprint(self
):
1169 """Returns the build fingerprint of the system.
1172 google/nakasi/grouper:4.4.4/KTU84P/1227136:user/release-keys
1174 return self
.GetProp('ro.build.fingerprint', cache
=True)
1178 """Returns the build ID of the system (e.g. 'KTU84P')."""
1179 return self
.GetProp('ro.build.id', cache
=True)
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)
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)
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
1202 CommandFailedError if the build version sdk is not a number.
1204 value
= self
.GetProp('ro.build.version.sdk', cache
=True)
1208 raise device_errors
.CommandFailedError(
1209 'Invalid build version sdk: %r' % value
)
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)
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)
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
,
1228 """Gets a property from the device.
1231 property_name: A string containing the name of the property to get from
1233 cache: A boolean indicating whether to cache the value of this property.
1234 timeout: timeout in seconds
1235 retries: number of retries
1238 The value of the device's |property_name| property.
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
]
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
1260 @decorators.WithTimeoutAndRetriesFromInstance()
1261 def SetProp(self
, property_name
, value
, check
=False, timeout
=None,
1263 """Sets a property on the device.
1266 property_name: A string containing the name of the property to set on
1268 value: A string containing the value to set to the property on the
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
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.
1299 timeout: timeout in seconds
1300 retries: number of retries
1303 The device's main ABI name.
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.
1317 process_name: A string containing the process name to get the PIDs for.
1318 timeout: timeout in seconds
1319 retries: number of retries
1322 A dict mapping process name to PID for each process that contained the
1323 provided |process_name|.
1326 CommandTimeoutError on timeout.
1327 DeviceUnreachableError on missing device.
1329 return self
._GetPidsImpl
(process_name
)
1331 def _GetPidsImpl(self
, process_name
):
1333 for line
in self
.RunShellCommand('ps', check_return
=True):
1335 ps_data
= line
.split()
1336 if process_name
in ps_data
[-1]:
1337 procs_pids
[ps_data
[-1]] = ps_data
[1]
1342 @decorators.WithTimeoutAndRetriesFromInstance()
1343 def TakeScreenshot(self
, host_path
=None, timeout
=None, retries
=None):
1344 """Takes a screenshot of the device.
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
1354 The name of the file on the host to which the screenshot was saved.
1357 CommandFailedError on failure.
1358 CommandTimeoutError on timeout.
1359 DeviceUnreachableError on missing device.
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
],
1366 self
.PullFile(device_tmp
.name
, host_path
)
1369 @decorators.WithTimeoutAndRetriesFromInstance()
1370 def GetMemoryUsageForPid(self
, pid
, timeout
=None, retries
=None):
1371 """Gets the memory usage for the given PID.
1374 pid: PID of the process.
1375 timeout: timeout in seconds
1376 retries: number of retries
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
1384 CommandTimeoutError on timeout.
1386 result
= collections
.defaultdict(int)
1389 result
.update(self
._GetMemoryUsageForPidFromSmaps
(pid
))
1390 except device_errors
.CommandFailedError
:
1391 logging
.exception('Error getting memory usage from smaps')
1394 result
.update(self
._GetMemoryUsageForPidFromStatus
(pid
))
1395 except device_errors
.CommandFailedError
:
1396 logging
.exception('Error getting memory usage from status')
1400 def _GetMemoryUsageForPidFromSmaps(self
, pid
):
1402 'Size', 'Rss', 'Pss', 'Shared_Clean', 'Shared_Dirty', 'Private_Clean',
1405 showmap_out
= self
.RunShellCommand(
1406 ['showmap', str(pid
)], as_root
=True, check_return
=True)
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])}
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.
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.
1446 timeout: timeout in seconds
1447 retries: number of retries
1449 A dict containing various battery information as reported by dumpsys
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
)
1464 k
, v
= line
.split(': ', 1)
1465 result
[k
.strip()] = v
.strip()
1468 @decorators.WithTimeoutAndRetriesFromInstance()
1469 def GetCharging(self
, timeout
=None, retries
=None):
1470 """Gets the charging state of the device.
1473 timeout: timeout in seconds
1474 retries: number of retries
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')):
1485 @decorators.WithTimeoutAndRetriesFromInstance()
1486 def SetCharging(self
, enabled
, timeout
=None, retries
=None):
1487 """Enables or disables charging on the device.
1490 enabled: A boolean indicating whether charging should be enabled or
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
1501 raise device_errors
.CommandFailedError(
1502 'Unable to find charging commands.')
1505 command
= self
._cache
['charging_config']['enable_command']
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.
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'],
1535 for line
in battery_data
:
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'],
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.
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'],
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.
1572 with BatteryMeasurement():
1574 get_power_data() # report usage within this block
1575 after_measurements() # Anything that runs after power
1576 # measurements are collected
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.')
1585 self
.DisableBatteryUpdates(timeout
=timeout
, retries
=retries
)
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).
1604 timeout: timeout in seconds
1605 retries: number of retries
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
:
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']
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.
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
1640 A Parallelizer operating over |devices|.
1643 devices
= adb_wrapper
.AdbWrapper
.GetDevices()
1645 raise device_errors
.NoDevicesError()
1646 devices
= [d
if isinstance(d
, cls
) else cls(d
) for d
in devices
]
1648 return parallelizer
.Parallelizer(devices
)
1650 return parallelizer
.SyncParallelizer(devices
)