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