Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(19)

Side by Side Diff: build/android/pylib/device/device_utils.py

Issue 1314913009: [Android] Move some pylib modules into devil/ (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright 2014 The Chromium Authors. All rights reserved. 1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Provides a variety of device interactions based on adb. 5 # pylint: disable=unused-wildcard-import
6 # pylint: disable=wildcard-import
6 7
7 Eventually, this will be based on adb_wrapper. 8 from devil.android.device_utils import *
8 """
9 # pylint: disable=unused-argument
10
11 import collections
12 import contextlib
13 import itertools
14 import logging
15 import multiprocessing
16 import os
17 import posixpath
18 import re
19 import shutil
20 import sys
21 import tempfile
22 import threading
23 import time
24 import zipfile
25
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
46
47 _DEFAULT_TIMEOUT = 30
48 _DEFAULT_RETRIES = 3
49
50 # A sentinel object for default values
51 # TODO(jbudorick,perezju): revisit how default values are handled by
52 # the timeout_retry decorators.
53 DEFAULT = object()
54
55 _CONTROL_CHARGING_COMMANDS = [
56 {
57 # Nexus 4
58 'witness_file': '/sys/module/pm8921_charger/parameters/disabled',
59 'enable_command': 'echo 0 > /sys/module/pm8921_charger/parameters/disabled',
60 'disable_command':
61 'echo 1 > /sys/module/pm8921_charger/parameters/disabled',
62 },
63 {
64 # Nexus 5
65 # Setting the HIZ bit of the bq24192 causes the charger to actually ignore
66 # energy coming from USB. Setting the power_supply offline just updates the
67 # Android system to reflect that.
68 'witness_file': '/sys/kernel/debug/bq24192/INPUT_SRC_CONT',
69 'enable_command': (
70 'echo 0x4A > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && '
71 'echo 1 > /sys/class/power_supply/usb/online'),
72 'disable_command': (
73 'echo 0xCA > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && '
74 'chmod 644 /sys/class/power_supply/usb/online && '
75 'echo 0 > /sys/class/power_supply/usb/online'),
76 },
77 ]
78
79 _RESTART_ADBD_SCRIPT = """
80 trap '' HUP
81 trap '' TERM
82 trap '' PIPE
83 function restart() {
84 stop adbd
85 start adbd
86 }
87 restart &
88 """
89
90 _CURRENT_FOCUS_CRASH_RE = re.compile(
91 r'\s*mCurrentFocus.*Application (Error|Not Responding): (\S+)}')
92
93
94 @decorators.WithExplicitTimeoutAndRetries(
95 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
96 def GetAVDs():
97 """Returns a list of Android Virtual Devices.
98
99 Returns:
100 A list containing the configured AVDs.
101 """
102 lines = cmd_helper.GetCmdOutput([
103 os.path.join(constants.ANDROID_SDK_ROOT, 'tools', 'android'),
104 'list', 'avd']).splitlines()
105 avds = []
106 for line in lines:
107 if 'Name:' not in line:
108 continue
109 key, value = (s.strip() for s in line.split(':', 1))
110 if key == 'Name':
111 avds.append(value)
112 return avds
113
114
115 @decorators.WithExplicitTimeoutAndRetries(
116 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES)
117 def RestartServer():
118 """Restarts the adb server.
119
120 Raises:
121 CommandFailedError if we fail to kill or restart the server.
122 """
123 def adb_killed():
124 return not adb_wrapper.AdbWrapper.IsServerOnline()
125
126 def adb_started():
127 return adb_wrapper.AdbWrapper.IsServerOnline()
128
129 adb_wrapper.AdbWrapper.KillServer()
130 if not timeout_retry.WaitFor(adb_killed, wait_period=1, max_tries=5):
131 # TODO(perezju): raise an exception after fixng http://crbug.com/442319
132 logging.warning('Failed to kill adb server')
133 adb_wrapper.AdbWrapper.StartServer()
134 if not timeout_retry.WaitFor(adb_started, wait_period=1, max_tries=5):
135 raise device_errors.CommandFailedError('Failed to start adb server')
136
137
138 def _GetTimeStamp():
139 """Return a basic ISO 8601 time stamp with the current local time."""
140 return time.strftime('%Y%m%dT%H%M%S', time.localtime())
141
142
143 def _JoinLines(lines):
144 # makes sure that the last line is also terminated, and is more memory
145 # efficient than first appending an end-line to each line and then joining
146 # all of them together.
147 return ''.join(s for line in lines for s in (line, '\n'))
148
149
150 class DeviceUtils(object):
151
152 _MAX_ADB_COMMAND_LENGTH = 512
153 _MAX_ADB_OUTPUT_LENGTH = 32768
154 _LAUNCHER_FOCUSED_RE = re.compile(
155 '\s*mCurrentFocus.*(Launcher|launcher).*')
156 _VALID_SHELL_VARIABLE = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$')
157
158 # Property in /data/local.prop that controls Java assertions.
159 JAVA_ASSERT_PROPERTY = 'dalvik.vm.enableassertions'
160
161 def __init__(self, device, default_timeout=_DEFAULT_TIMEOUT,
162 default_retries=_DEFAULT_RETRIES):
163 """DeviceUtils constructor.
164
165 Args:
166 device: Either a device serial, an existing AdbWrapper instance, or an
167 an existing AndroidCommands instance.
168 default_timeout: An integer containing the default number of seconds to
169 wait for an operation to complete if no explicit value
170 is provided.
171 default_retries: An integer containing the default number or times an
172 operation should be retried on failure if no explicit
173 value is provided.
174 """
175 self.adb = None
176 if isinstance(device, basestring):
177 self.adb = adb_wrapper.AdbWrapper(device)
178 elif isinstance(device, adb_wrapper.AdbWrapper):
179 self.adb = device
180 else:
181 raise ValueError('Unsupported device value: %r' % device)
182 self._commands_installed = None
183 self._default_timeout = default_timeout
184 self._default_retries = default_retries
185 self._cache = {}
186 self._client_caches = {}
187 assert hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR)
188 assert hasattr(self, decorators.DEFAULT_RETRIES_ATTR)
189
190 self._ClearCache()
191
192 def __eq__(self, other):
193 """Checks whether |other| refers to the same device as |self|.
194
195 Args:
196 other: The object to compare to. This can be a basestring, an instance
197 of adb_wrapper.AdbWrapper, or an instance of DeviceUtils.
198 Returns:
199 Whether |other| refers to the same device as |self|.
200 """
201 return self.adb.GetDeviceSerial() == str(other)
202
203 def __lt__(self, other):
204 """Compares two instances of DeviceUtils.
205
206 This merely compares their serial numbers.
207
208 Args:
209 other: The instance of DeviceUtils to compare to.
210 Returns:
211 Whether |self| is less than |other|.
212 """
213 return self.adb.GetDeviceSerial() < other.adb.GetDeviceSerial()
214
215 def __str__(self):
216 """Returns the device serial."""
217 return self.adb.GetDeviceSerial()
218
219 @decorators.WithTimeoutAndRetriesFromInstance()
220 def IsOnline(self, timeout=None, retries=None):
221 """Checks whether the device is online.
222
223 Args:
224 timeout: timeout in seconds
225 retries: number of retries
226
227 Returns:
228 True if the device is online, False otherwise.
229
230 Raises:
231 CommandTimeoutError on timeout.
232 """
233 try:
234 return self.adb.GetState() == 'device'
235 except base_error.BaseError as exc:
236 logging.info('Failed to get state: %s', exc)
237 return False
238
239 @decorators.WithTimeoutAndRetriesFromInstance()
240 def HasRoot(self, timeout=None, retries=None):
241 """Checks whether or not adbd has root privileges.
242
243 Args:
244 timeout: timeout in seconds
245 retries: number of retries
246
247 Returns:
248 True if adbd has root privileges, False otherwise.
249
250 Raises:
251 CommandTimeoutError on timeout.
252 DeviceUnreachableError on missing device.
253 """
254 try:
255 self.RunShellCommand('ls /root', check_return=True)
256 return True
257 except device_errors.AdbCommandFailedError:
258 return False
259
260 def NeedsSU(self, timeout=DEFAULT, retries=DEFAULT):
261 """Checks whether 'su' is needed to access protected resources.
262
263 Args:
264 timeout: timeout in seconds
265 retries: number of retries
266
267 Returns:
268 True if 'su' is available on the device and is needed to to access
269 protected resources; False otherwise if either 'su' is not available
270 (e.g. because the device has a user build), or not needed (because adbd
271 already has root privileges).
272
273 Raises:
274 CommandTimeoutError on timeout.
275 DeviceUnreachableError on missing device.
276 """
277 if 'needs_su' not in self._cache:
278 try:
279 self.RunShellCommand(
280 '%s && ! ls /root' % self._Su('ls /root'), check_return=True,
281 timeout=self._default_timeout if timeout is DEFAULT else timeout,
282 retries=self._default_retries if retries is DEFAULT else retries)
283 self._cache['needs_su'] = True
284 except device_errors.AdbCommandFailedError:
285 self._cache['needs_su'] = False
286 return self._cache['needs_su']
287
288 def _Su(self, command):
289 if (self.build_version_sdk
290 >= constants.ANDROID_SDK_VERSION_CODES.MARSHMALLOW):
291 return 'su 0 %s' % command
292 return 'su -c %s' % command
293
294 @decorators.WithTimeoutAndRetriesFromInstance()
295 def EnableRoot(self, timeout=None, retries=None):
296 """Restarts adbd with root privileges.
297
298 Args:
299 timeout: timeout in seconds
300 retries: number of retries
301
302 Raises:
303 CommandFailedError if root could not be enabled.
304 CommandTimeoutError on timeout.
305 """
306 if self.IsUserBuild():
307 raise device_errors.CommandFailedError(
308 'Cannot enable root in user builds.', str(self))
309 if 'needs_su' in self._cache:
310 del self._cache['needs_su']
311 self.adb.Root()
312 self.WaitUntilFullyBooted()
313
314 @decorators.WithTimeoutAndRetriesFromInstance()
315 def IsUserBuild(self, timeout=None, retries=None):
316 """Checks whether or not the device is running a user build.
317
318 Args:
319 timeout: timeout in seconds
320 retries: number of retries
321
322 Returns:
323 True if the device is running a user build, False otherwise (i.e. if
324 it's running a userdebug build).
325
326 Raises:
327 CommandTimeoutError on timeout.
328 DeviceUnreachableError on missing device.
329 """
330 return self.build_type == 'user'
331
332 @decorators.WithTimeoutAndRetriesFromInstance()
333 def GetExternalStoragePath(self, timeout=None, retries=None):
334 """Get the device's path to its SD card.
335
336 Args:
337 timeout: timeout in seconds
338 retries: number of retries
339
340 Returns:
341 The device's path to its SD card.
342
343 Raises:
344 CommandFailedError if the external storage path could not be determined.
345 CommandTimeoutError on timeout.
346 DeviceUnreachableError on missing device.
347 """
348 if 'external_storage' in self._cache:
349 return self._cache['external_storage']
350
351 value = self.RunShellCommand('echo $EXTERNAL_STORAGE',
352 single_line=True,
353 check_return=True)
354 if not value:
355 raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set',
356 str(self))
357 self._cache['external_storage'] = value
358 return value
359
360 @decorators.WithTimeoutAndRetriesFromInstance()
361 def GetApplicationPaths(self, package, timeout=None, retries=None):
362 """Get the paths of the installed apks on the device for the given package.
363
364 Args:
365 package: Name of the package.
366
367 Returns:
368 List of paths to the apks on the device for the given package.
369 """
370 return self._GetApplicationPathsInternal(package)
371
372 def _GetApplicationPathsInternal(self, package, skip_cache=False):
373 cached_result = self._cache['package_apk_paths'].get(package)
374 if cached_result is not None and not skip_cache:
375 return list(cached_result)
376 # 'pm path' is liable to incorrectly exit with a nonzero number starting
377 # in Lollipop.
378 # TODO(jbudorick): Check if this is fixed as new Android versions are
379 # released to put an upper bound on this.
380 should_check_return = (self.build_version_sdk <
381 constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP)
382 output = self.RunShellCommand(
383 ['pm', 'path', package], check_return=should_check_return)
384 apks = []
385 for line in output:
386 if not line.startswith('package:'):
387 raise device_errors.CommandFailedError(
388 'pm path returned: %r' % '\n'.join(output), str(self))
389 apks.append(line[len('package:'):])
390 self._cache['package_apk_paths'][package] = list(apks)
391 return apks
392
393 @decorators.WithTimeoutAndRetriesFromInstance()
394 def GetApplicationVersion(self, package, timeout=None, retries=None):
395 """Get the version name of a package installed on the device.
396
397 Args:
398 package: Name of the package.
399
400 Returns:
401 A string with the version name or None if the package is not found
402 on the device.
403 """
404 output = self.RunShellCommand(
405 ['dumpsys', 'package', package], check_return=True)
406 if not output:
407 return None
408 for line in output:
409 line = line.strip()
410 if line.startswith('versionName='):
411 return line[len('versionName='):]
412 raise device_errors.CommandFailedError(
413 'Version name for %s not found on dumpsys output' % package, str(self))
414
415 @decorators.WithTimeoutAndRetriesFromInstance()
416 def GetApplicationDataDirectory(self, package, timeout=None, retries=None):
417 """Get the data directory on the device for the given package.
418
419 Args:
420 package: Name of the package.
421
422 Returns:
423 The package's data directory, or None if the package doesn't exist on the
424 device.
425 """
426 try:
427 output = self._RunPipedShellCommand(
428 'pm dump %s | grep dataDir=' % cmd_helper.SingleQuote(package))
429 for line in output:
430 _, _, dataDir = line.partition('dataDir=')
431 if dataDir:
432 return dataDir
433 except device_errors.CommandFailedError:
434 logging.exception('Could not find data directory for %s', package)
435 return None
436
437 @decorators.WithTimeoutAndRetriesFromInstance()
438 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None):
439 """Wait for the device to fully boot.
440
441 This means waiting for the device to boot, the package manager to be
442 available, and the SD card to be ready. It can optionally mean waiting
443 for wifi to come up, too.
444
445 Args:
446 wifi: A boolean indicating if we should wait for wifi to come up or not.
447 timeout: timeout in seconds
448 retries: number of retries
449
450 Raises:
451 CommandFailedError on failure.
452 CommandTimeoutError if one of the component waits times out.
453 DeviceUnreachableError if the device becomes unresponsive.
454 """
455 def sd_card_ready():
456 try:
457 self.RunShellCommand(['test', '-d', self.GetExternalStoragePath()],
458 check_return=True)
459 return True
460 except device_errors.AdbCommandFailedError:
461 return False
462
463 def pm_ready():
464 try:
465 return self._GetApplicationPathsInternal('android', skip_cache=True)
466 except device_errors.CommandFailedError:
467 return False
468
469 def boot_completed():
470 return self.GetProp('sys.boot_completed') == '1'
471
472 def wifi_enabled():
473 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi'],
474 check_return=False)
475
476 self.adb.WaitForDevice()
477 timeout_retry.WaitFor(sd_card_ready)
478 timeout_retry.WaitFor(pm_ready)
479 timeout_retry.WaitFor(boot_completed)
480 if wifi:
481 timeout_retry.WaitFor(wifi_enabled)
482
483 REBOOT_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT
484 REBOOT_DEFAULT_RETRIES = _DEFAULT_RETRIES
485
486 @decorators.WithTimeoutAndRetriesDefaults(
487 REBOOT_DEFAULT_TIMEOUT,
488 REBOOT_DEFAULT_RETRIES)
489 def Reboot(self, block=True, wifi=False, timeout=None, retries=None):
490 """Reboot the device.
491
492 Args:
493 block: A boolean indicating if we should wait for the reboot to complete.
494 wifi: A boolean indicating if we should wait for wifi to be enabled after
495 the reboot. The option has no effect unless |block| is also True.
496 timeout: timeout in seconds
497 retries: number of retries
498
499 Raises:
500 CommandTimeoutError on timeout.
501 DeviceUnreachableError on missing device.
502 """
503 def device_offline():
504 return not self.IsOnline()
505
506 self.adb.Reboot()
507 self._ClearCache()
508 timeout_retry.WaitFor(device_offline, wait_period=1)
509 if block:
510 self.WaitUntilFullyBooted(wifi=wifi)
511
512 INSTALL_DEFAULT_TIMEOUT = 4 * _DEFAULT_TIMEOUT
513 INSTALL_DEFAULT_RETRIES = _DEFAULT_RETRIES
514
515 @decorators.WithTimeoutAndRetriesDefaults(
516 INSTALL_DEFAULT_TIMEOUT,
517 INSTALL_DEFAULT_RETRIES)
518 def Install(self, apk_path, reinstall=False, timeout=None, retries=None):
519 """Install an APK.
520
521 Noop if an identical APK is already installed.
522
523 Args:
524 apk_path: A string containing the path to the APK to install.
525 reinstall: A boolean indicating if we should keep any existing app data.
526 timeout: timeout in seconds
527 retries: number of retries
528
529 Raises:
530 CommandFailedError if the installation fails.
531 CommandTimeoutError if the installation times out.
532 DeviceUnreachableError on missing device.
533 """
534 package_name = apk_helper.GetPackageName(apk_path)
535 device_paths = self._GetApplicationPathsInternal(package_name)
536 if device_paths:
537 if len(device_paths) > 1:
538 logging.warning(
539 'Installing single APK (%s) when split APKs (%s) are currently '
540 'installed.', apk_path, ' '.join(device_paths))
541 apks_to_install, host_checksums = (
542 self._ComputeStaleApks(package_name, [apk_path]))
543 should_install = bool(apks_to_install)
544 if should_install and not reinstall:
545 self.Uninstall(package_name)
546 else:
547 should_install = True
548 host_checksums = None
549
550 if should_install:
551 # We won't know the resulting device apk names.
552 self._cache['package_apk_paths'].pop(package_name, 0)
553 self.adb.Install(apk_path, reinstall=reinstall)
554 self._cache['package_apk_checksums'][package_name] = host_checksums
555
556 @decorators.WithTimeoutAndRetriesDefaults(
557 INSTALL_DEFAULT_TIMEOUT,
558 INSTALL_DEFAULT_RETRIES)
559 def InstallSplitApk(self, base_apk, split_apks, reinstall=False,
560 timeout=None, retries=None):
561 """Install a split APK.
562
563 Noop if all of the APK splits are already installed.
564
565 Args:
566 base_apk: A string of the path to the base APK.
567 split_apks: A list of strings of paths of all of the APK splits.
568 reinstall: A boolean indicating if we should keep any existing app data.
569 timeout: timeout in seconds
570 retries: number of retries
571
572 Raises:
573 CommandFailedError if the installation fails.
574 CommandTimeoutError if the installation times out.
575 DeviceUnreachableError on missing device.
576 DeviceVersionError if device SDK is less than Android L.
577 """
578 self._CheckSdkLevel(constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP)
579
580 all_apks = [base_apk] + split_select.SelectSplits(
581 self, base_apk, split_apks)
582 package_name = apk_helper.GetPackageName(base_apk)
583 device_apk_paths = self._GetApplicationPathsInternal(package_name)
584
585 if device_apk_paths:
586 partial_install_package = package_name
587 apks_to_install, host_checksums = (
588 self._ComputeStaleApks(package_name, all_apks))
589 if apks_to_install and not reinstall:
590 self.Uninstall(package_name)
591 partial_install_package = None
592 apks_to_install = all_apks
593 else:
594 partial_install_package = None
595 apks_to_install = all_apks
596 host_checksums = None
597
598 if apks_to_install:
599 # We won't know the resulting device apk names.
600 self._cache['package_apk_paths'].pop(package_name, 0)
601 self.adb.InstallMultiple(
602 apks_to_install, partial=partial_install_package,
603 reinstall=reinstall)
604 self._cache['package_apk_checksums'][package_name] = host_checksums
605
606 @decorators.WithTimeoutAndRetriesFromInstance()
607 def Uninstall(self, package_name, keep_data=False, timeout=None,
608 retries=None):
609 """Remove the app |package_name| from the device.
610
611 Args:
612 package_name: The package to uninstall.
613 keep_data: (optional) Whether to keep the data and cache directories.
614 timeout: Timeout in seconds.
615 retries: Number of retries.
616
617 Raises:
618 CommandFailedError if the uninstallation fails.
619 CommandTimeoutError if the uninstallation times out.
620 DeviceUnreachableError on missing device.
621 """
622 try:
623 self.adb.Uninstall(package_name, keep_data)
624 self._cache['package_apk_paths'][package_name] = []
625 self._cache['package_apk_checksums'][package_name] = set()
626 except:
627 # Clear cache since we can't be sure of the state.
628 self._cache['package_apk_paths'].pop(package_name, 0)
629 self._cache['package_apk_checksums'].pop(package_name, 0)
630 raise
631
632 def _CheckSdkLevel(self, required_sdk_level):
633 """Raises an exception if the device does not have the required SDK level.
634 """
635 if self.build_version_sdk < required_sdk_level:
636 raise device_errors.DeviceVersionError(
637 ('Requires SDK level %s, device is SDK level %s' %
638 (required_sdk_level, self.build_version_sdk)),
639 device_serial=self.adb.GetDeviceSerial())
640
641
642 @decorators.WithTimeoutAndRetriesFromInstance()
643 def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None,
644 as_root=False, single_line=False, large_output=False,
645 timeout=None, retries=None):
646 """Run an ADB shell command.
647
648 The command to run |cmd| should be a sequence of program arguments or else
649 a single string.
650
651 When |cmd| is a sequence, it is assumed to contain the name of the command
652 to run followed by its arguments. In this case, arguments are passed to the
653 command exactly as given, without any further processing by the shell. This
654 allows to easily pass arguments containing spaces or special characters
655 without having to worry about getting quoting right. Whenever possible, it
656 is recomended to pass |cmd| as a sequence.
657
658 When |cmd| is given as a string, it will be interpreted and run by the
659 shell on the device.
660
661 This behaviour is consistent with that of command runners in cmd_helper as
662 well as Python's own subprocess.Popen.
663
664 TODO(perezju) Change the default of |check_return| to True when callers
665 have switched to the new behaviour.
666
667 Args:
668 cmd: A string with the full command to run on the device, or a sequence
669 containing the command and its arguments.
670 check_return: A boolean indicating whether or not the return code should
671 be checked.
672 cwd: The device directory in which the command should be run.
673 env: The environment variables with which the command should be run.
674 as_root: A boolean indicating whether the shell command should be run
675 with root privileges.
676 single_line: A boolean indicating if only a single line of output is
677 expected.
678 large_output: Uses a work-around for large shell command output. Without
679 this large output will be truncated.
680 timeout: timeout in seconds
681 retries: number of retries
682
683 Returns:
684 If single_line is False, the output of the command as a list of lines,
685 otherwise, a string with the unique line of output emmited by the command
686 (with the optional newline at the end stripped).
687
688 Raises:
689 AdbCommandFailedError if check_return is True and the exit code of
690 the command run on the device is non-zero.
691 CommandFailedError if single_line is True but the output contains two or
692 more lines.
693 CommandTimeoutError on timeout.
694 DeviceUnreachableError on missing device.
695 """
696 def env_quote(key, value):
697 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key):
698 raise KeyError('Invalid shell variable name %r' % key)
699 # using double quotes here to allow interpolation of shell variables
700 return '%s=%s' % (key, cmd_helper.DoubleQuote(value))
701
702 def run(cmd):
703 return self.adb.Shell(cmd)
704
705 def handle_check_return(cmd):
706 try:
707 return run(cmd)
708 except device_errors.AdbCommandFailedError as exc:
709 if check_return:
710 raise
711 else:
712 return exc.output
713
714 def handle_large_command(cmd):
715 if len(cmd) < self._MAX_ADB_COMMAND_LENGTH:
716 return handle_check_return(cmd)
717 else:
718 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script:
719 self._WriteFileWithPush(script.name, cmd)
720 logging.info('Large shell command will be run from file: %s ...',
721 cmd[:100])
722 return handle_check_return('sh %s' % script.name_quoted)
723
724 def handle_large_output(cmd, large_output_mode):
725 if large_output_mode:
726 with device_temp_file.DeviceTempFile(self.adb) as large_output_file:
727 cmd = '%s > %s' % (cmd, large_output_file.name)
728 logging.debug('Large output mode enabled. Will write output to '
729 'device and read results from file.')
730 handle_large_command(cmd)
731 return self.ReadFile(large_output_file.name, force_pull=True)
732 else:
733 try:
734 return handle_large_command(cmd)
735 except device_errors.AdbCommandFailedError as exc:
736 if exc.status is None:
737 logging.exception('No output found for %s', cmd)
738 logging.warning('Attempting to run in large_output mode.')
739 logging.warning('Use RunShellCommand(..., large_output=True) for '
740 'shell commands that expect a lot of output.')
741 return handle_large_output(cmd, True)
742 else:
743 raise
744
745 if not isinstance(cmd, basestring):
746 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd)
747 if env:
748 env = ' '.join(env_quote(k, v) for k, v in env.iteritems())
749 cmd = '%s %s' % (env, cmd)
750 if cwd:
751 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd)
752 if as_root and self.NeedsSU():
753 # "su -c sh -c" allows using shell features in |cmd|
754 cmd = self._Su('sh -c %s' % cmd_helper.SingleQuote(cmd))
755
756 output = handle_large_output(cmd, large_output).splitlines()
757
758 if single_line:
759 if not output:
760 return ''
761 elif len(output) == 1:
762 return output[0]
763 else:
764 msg = 'one line of output was expected, but got: %s'
765 raise device_errors.CommandFailedError(msg % output, str(self))
766 else:
767 return output
768
769 def _RunPipedShellCommand(self, script, **kwargs):
770 PIPESTATUS_LEADER = 'PIPESTATUS: '
771
772 script += '; echo "%s${PIPESTATUS[@]}"' % PIPESTATUS_LEADER
773 kwargs['check_return'] = True
774 output = self.RunShellCommand(script, **kwargs)
775 pipestatus_line = output[-1]
776
777 if not pipestatus_line.startswith(PIPESTATUS_LEADER):
778 logging.error('Pipe exit statuses of shell script missing.')
779 raise device_errors.AdbShellCommandFailedError(
780 script, output, status=None,
781 device_serial=self.adb.GetDeviceSerial())
782
783 output = output[:-1]
784 statuses = [
785 int(s) for s in pipestatus_line[len(PIPESTATUS_LEADER):].split()]
786 if any(statuses):
787 raise device_errors.AdbShellCommandFailedError(
788 script, output, status=statuses,
789 device_serial=self.adb.GetDeviceSerial())
790 return output
791
792 @decorators.WithTimeoutAndRetriesFromInstance()
793 def KillAll(self, process_name, exact=False, signum=device_signal.SIGKILL,
794 as_root=False, blocking=False, quiet=False,
795 timeout=None, retries=None):
796 """Kill all processes with the given name on the device.
797
798 Args:
799 process_name: A string containing the name of the process to kill.
800 exact: A boolean indicating whether to kill all processes matching
801 the string |process_name| exactly, or all of those which contain
802 |process_name| as a substring. Defaults to False.
803 signum: An integer containing the signal number to send to kill. Defaults
804 to SIGKILL (9).
805 as_root: A boolean indicating whether the kill should be executed with
806 root privileges.
807 blocking: A boolean indicating whether we should wait until all processes
808 with the given |process_name| are dead.
809 quiet: A boolean indicating whether to ignore the fact that no processes
810 to kill were found.
811 timeout: timeout in seconds
812 retries: number of retries
813
814 Returns:
815 The number of processes attempted to kill.
816
817 Raises:
818 CommandFailedError if no process was killed and |quiet| is False.
819 CommandTimeoutError on timeout.
820 DeviceUnreachableError on missing device.
821 """
822 procs_pids = self.GetPids(process_name)
823 if exact:
824 procs_pids = {process_name: procs_pids.get(process_name, [])}
825 pids = set(itertools.chain(*procs_pids.values()))
826 if not pids:
827 if quiet:
828 return 0
829 else:
830 raise device_errors.CommandFailedError(
831 'No process "%s"' % process_name, str(self))
832
833 logging.info(
834 'KillAll(%r, ...) attempting to kill the following:', process_name)
835 for name, ids in procs_pids.iteritems():
836 for i in ids:
837 logging.info(' %05s %s', str(i), name)
838
839 cmd = ['kill', '-%d' % signum] + sorted(pids)
840 self.RunShellCommand(cmd, as_root=as_root, check_return=True)
841
842 def all_pids_killed():
843 procs_pids_remain = self.GetPids(process_name)
844 return not pids.intersection(itertools.chain(*procs_pids_remain.values()))
845
846 if blocking:
847 timeout_retry.WaitFor(all_pids_killed, wait_period=0.1)
848
849 return len(pids)
850
851 @decorators.WithTimeoutAndRetriesFromInstance()
852 def StartActivity(self, intent_obj, blocking=False, trace_file_name=None,
853 force_stop=False, timeout=None, retries=None):
854 """Start package's activity on the device.
855
856 Args:
857 intent_obj: An Intent object to send.
858 blocking: A boolean indicating whether we should wait for the activity to
859 finish launching.
860 trace_file_name: If present, a string that both indicates that we want to
861 profile the activity and contains the path to which the
862 trace should be saved.
863 force_stop: A boolean indicating whether we should stop the activity
864 before starting it.
865 timeout: timeout in seconds
866 retries: number of retries
867
868 Raises:
869 CommandFailedError if the activity could not be started.
870 CommandTimeoutError on timeout.
871 DeviceUnreachableError on missing device.
872 """
873 cmd = ['am', 'start']
874 if blocking:
875 cmd.append('-W')
876 if trace_file_name:
877 cmd.extend(['--start-profiler', trace_file_name])
878 if force_stop:
879 cmd.append('-S')
880 cmd.extend(intent_obj.am_args)
881 for line in self.RunShellCommand(cmd, check_return=True):
882 if line.startswith('Error:'):
883 raise device_errors.CommandFailedError(line, str(self))
884
885 @decorators.WithTimeoutAndRetriesFromInstance()
886 def StartInstrumentation(self, component, finish=True, raw=False,
887 extras=None, timeout=None, retries=None):
888 if extras is None:
889 extras = {}
890
891 cmd = ['am', 'instrument']
892 if finish:
893 cmd.append('-w')
894 if raw:
895 cmd.append('-r')
896 for k, v in extras.iteritems():
897 cmd.extend(['-e', str(k), str(v)])
898 cmd.append(component)
899 return self.RunShellCommand(cmd, check_return=True, large_output=True)
900
901 @decorators.WithTimeoutAndRetriesFromInstance()
902 def BroadcastIntent(self, intent_obj, timeout=None, retries=None):
903 """Send a broadcast intent.
904
905 Args:
906 intent: An Intent to broadcast.
907 timeout: timeout in seconds
908 retries: number of retries
909
910 Raises:
911 CommandTimeoutError on timeout.
912 DeviceUnreachableError on missing device.
913 """
914 cmd = ['am', 'broadcast'] + intent_obj.am_args
915 self.RunShellCommand(cmd, check_return=True)
916
917 @decorators.WithTimeoutAndRetriesFromInstance()
918 def GoHome(self, timeout=None, retries=None):
919 """Return to the home screen and obtain launcher focus.
920
921 This command launches the home screen and attempts to obtain
922 launcher focus until the timeout is reached.
923
924 Args:
925 timeout: timeout in seconds
926 retries: number of retries
927
928 Raises:
929 CommandTimeoutError on timeout.
930 DeviceUnreachableError on missing device.
931 """
932 def is_launcher_focused():
933 output = self.RunShellCommand(['dumpsys', 'window', 'windows'],
934 check_return=True, large_output=True)
935 return any(self._LAUNCHER_FOCUSED_RE.match(l) for l in output)
936
937 def dismiss_popups():
938 # There is a dialog present; attempt to get rid of it.
939 # Not all dialogs can be dismissed with back.
940 self.SendKeyEvent(keyevent.KEYCODE_ENTER)
941 self.SendKeyEvent(keyevent.KEYCODE_BACK)
942 return is_launcher_focused()
943
944 # If Home is already focused, return early to avoid unnecessary work.
945 if is_launcher_focused():
946 return
947
948 self.StartActivity(
949 intent.Intent(action='android.intent.action.MAIN',
950 category='android.intent.category.HOME'),
951 blocking=True)
952
953 if not is_launcher_focused():
954 timeout_retry.WaitFor(dismiss_popups, wait_period=1)
955
956 @decorators.WithTimeoutAndRetriesFromInstance()
957 def ForceStop(self, package, timeout=None, retries=None):
958 """Close the application.
959
960 Args:
961 package: A string containing the name of the package to stop.
962 timeout: timeout in seconds
963 retries: number of retries
964
965 Raises:
966 CommandTimeoutError on timeout.
967 DeviceUnreachableError on missing device.
968 """
969 self.RunShellCommand(['am', 'force-stop', package], check_return=True)
970
971 @decorators.WithTimeoutAndRetriesFromInstance()
972 def ClearApplicationState(self, package, timeout=None, retries=None):
973 """Clear all state for the given package.
974
975 Args:
976 package: A string containing the name of the package to stop.
977 timeout: timeout in seconds
978 retries: number of retries
979
980 Raises:
981 CommandTimeoutError on timeout.
982 DeviceUnreachableError on missing device.
983 """
984 # Check that the package exists before clearing it for android builds below
985 # JB MR2. Necessary because calling pm clear on a package that doesn't exist
986 # may never return.
987 if ((self.build_version_sdk >=
988 constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN_MR2)
989 or self._GetApplicationPathsInternal(package)):
990 self.RunShellCommand(['pm', 'clear', package], check_return=True)
991
992 @decorators.WithTimeoutAndRetriesFromInstance()
993 def SendKeyEvent(self, keycode, timeout=None, retries=None):
994 """Sends a keycode to the device.
995
996 See the pylib.constants.keyevent module for suitable keycode values.
997
998 Args:
999 keycode: A integer keycode to send to the device.
1000 timeout: timeout in seconds
1001 retries: number of retries
1002
1003 Raises:
1004 CommandTimeoutError on timeout.
1005 DeviceUnreachableError on missing device.
1006 """
1007 self.RunShellCommand(['input', 'keyevent', format(keycode, 'd')],
1008 check_return=True)
1009
1010 PUSH_CHANGED_FILES_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT
1011 PUSH_CHANGED_FILES_DEFAULT_RETRIES = _DEFAULT_RETRIES
1012
1013 @decorators.WithTimeoutAndRetriesDefaults(
1014 PUSH_CHANGED_FILES_DEFAULT_TIMEOUT,
1015 PUSH_CHANGED_FILES_DEFAULT_RETRIES)
1016 def PushChangedFiles(self, host_device_tuples, timeout=None,
1017 retries=None, delete_device_stale=False):
1018 """Push files to the device, skipping files that don't need updating.
1019
1020 When a directory is pushed, it is traversed recursively on the host and
1021 all files in it are pushed to the device as needed.
1022 Additionally, if delete_device_stale option is True,
1023 files that exist on the device but don't exist on the host are deleted.
1024
1025 Args:
1026 host_device_tuples: A list of (host_path, device_path) tuples, where
1027 |host_path| is an absolute path of a file or directory on the host
1028 that should be minimially pushed to the device, and |device_path| is
1029 an absolute path of the destination on the device.
1030 timeout: timeout in seconds
1031 retries: number of retries
1032 delete_device_stale: option to delete stale files on device
1033
1034 Raises:
1035 CommandFailedError on failure.
1036 CommandTimeoutError on timeout.
1037 DeviceUnreachableError on missing device.
1038 """
1039
1040 all_changed_files = []
1041 all_stale_files = []
1042 missing_dirs = []
1043 for h, d in host_device_tuples:
1044 changed_files, up_to_date_files, stale_files = (
1045 self._GetChangedAndStaleFiles(h, d, delete_device_stale))
1046 all_changed_files += changed_files
1047 all_stale_files += stale_files
1048 if (os.path.isdir(h) and changed_files and not up_to_date_files
1049 and not stale_files):
1050 missing_dirs.append(d)
1051
1052 if delete_device_stale and all_stale_files:
1053 self.RunShellCommand(['rm', '-f'] + all_stale_files,
1054 check_return=True)
1055
1056 if all_changed_files:
1057 if missing_dirs:
1058 self.RunShellCommand(['mkdir', '-p'] + missing_dirs, check_return=True)
1059 self._PushFilesImpl(host_device_tuples, all_changed_files)
1060
1061 def _GetChangedAndStaleFiles(self, host_path, device_path, track_stale=False):
1062 """Get files to push and delete
1063
1064 Args:
1065 host_path: an absolute path of a file or directory on the host
1066 device_path: an absolute path of a file or directory on the device
1067 track_stale: whether to bother looking for stale files (slower)
1068
1069 Returns:
1070 a three-element tuple
1071 1st element: a list of (host_files_path, device_files_path) tuples to push
1072 2nd element: a list of host_files_path that are up-to-date
1073 3rd element: a list of stale files under device_path, or [] when
1074 track_stale == False
1075 """
1076 try:
1077 host_checksums = md5sum.CalculateHostMd5Sums([host_path])
1078 interesting_device_paths = [device_path]
1079 if not track_stale and os.path.isdir(host_path):
1080 interesting_device_paths = [
1081 posixpath.join(device_path, os.path.relpath(p, host_path))
1082 for p in host_checksums.keys()]
1083 device_checksums = md5sum.CalculateDeviceMd5Sums(
1084 interesting_device_paths, self)
1085 except EnvironmentError as e:
1086 logging.warning('Error calculating md5: %s', e)
1087 return ([(host_path, device_path)], [], [])
1088
1089 to_push = []
1090 up_to_date = []
1091 to_delete = []
1092 if os.path.isfile(host_path):
1093 host_checksum = host_checksums.get(host_path)
1094 device_checksum = device_checksums.get(device_path)
1095 if host_checksum == device_checksum:
1096 up_to_date.append(host_path)
1097 else:
1098 to_push.append((host_path, device_path))
1099 else:
1100 for host_abs_path, host_checksum in host_checksums.iteritems():
1101 device_abs_path = posixpath.join(
1102 device_path, os.path.relpath(host_abs_path, host_path))
1103 device_checksum = device_checksums.pop(device_abs_path, None)
1104 if device_checksum == host_checksum:
1105 up_to_date.append(host_abs_path)
1106 else:
1107 to_push.append((host_abs_path, device_abs_path))
1108 to_delete = device_checksums.keys()
1109 return (to_push, up_to_date, to_delete)
1110
1111 def _ComputeDeviceChecksumsForApks(self, package_name):
1112 ret = self._cache['package_apk_checksums'].get(package_name)
1113 if ret is None:
1114 device_paths = self._GetApplicationPathsInternal(package_name)
1115 file_to_checksums = md5sum.CalculateDeviceMd5Sums(device_paths, self)
1116 ret = set(file_to_checksums.values())
1117 self._cache['package_apk_checksums'][package_name] = ret
1118 return ret
1119
1120 def _ComputeStaleApks(self, package_name, host_apk_paths):
1121 host_checksums_holder = [None]
1122 def compute_host_checksums():
1123 host_checksums_holder[0] = md5sum.CalculateHostMd5Sums(host_apk_paths)
1124
1125 host_thread = threading.Thread(target=compute_host_checksums)
1126 host_thread.start()
1127 device_checksums = self._ComputeDeviceChecksumsForApks(package_name)
1128 host_thread.join()
1129 host_checksums = host_checksums_holder[0]
1130 stale_apks = [k for (k, v) in host_checksums.iteritems()
1131 if v not in device_checksums]
1132 return stale_apks, set(host_checksums.values())
1133
1134 def _PushFilesImpl(self, host_device_tuples, files):
1135 size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files)
1136 file_count = len(files)
1137 dir_size = sum(host_utils.GetRecursiveDiskUsage(h)
1138 for h, _ in host_device_tuples)
1139 dir_file_count = 0
1140 for h, _ in host_device_tuples:
1141 if os.path.isdir(h):
1142 dir_file_count += sum(len(f) for _r, _d, f in os.walk(h))
1143 else:
1144 dir_file_count += 1
1145
1146 push_duration = self._ApproximateDuration(
1147 file_count, file_count, size, False)
1148 dir_push_duration = self._ApproximateDuration(
1149 len(host_device_tuples), dir_file_count, dir_size, False)
1150 zip_duration = self._ApproximateDuration(1, 1, size, True)
1151
1152 self._InstallCommands()
1153
1154 if dir_push_duration < push_duration and (
1155 dir_push_duration < zip_duration or not self._commands_installed):
1156 self._PushChangedFilesIndividually(host_device_tuples)
1157 elif push_duration < zip_duration or not self._commands_installed:
1158 self._PushChangedFilesIndividually(files)
1159 else:
1160 self._PushChangedFilesZipped(files)
1161 self.RunShellCommand(
1162 ['chmod', '-R', '777'] + [d for _, d in host_device_tuples],
1163 as_root=True, check_return=True)
1164
1165 def _InstallCommands(self):
1166 if self._commands_installed is None:
1167 try:
1168 if not install_commands.Installed(self):
1169 install_commands.InstallCommands(self)
1170 self._commands_installed = True
1171 except Exception as e:
1172 logging.warning('unzip not available: %s' % str(e))
1173 self._commands_installed = False
1174
1175 @staticmethod
1176 def _ApproximateDuration(adb_calls, file_count, byte_count, is_zipping):
1177 # We approximate the time to push a set of files to a device as:
1178 # t = c1 * a + c2 * f + c3 + b / c4 + b / (c5 * c6), where
1179 # t: total time (sec)
1180 # c1: adb call time delay (sec)
1181 # a: number of times adb is called (unitless)
1182 # c2: push time delay (sec)
1183 # f: number of files pushed via adb (unitless)
1184 # c3: zip time delay (sec)
1185 # c4: zip rate (bytes/sec)
1186 # b: total number of bytes (bytes)
1187 # c5: transfer rate (bytes/sec)
1188 # c6: compression ratio (unitless)
1189
1190 # All of these are approximations.
1191 ADB_CALL_PENALTY = 0.1 # seconds
1192 ADB_PUSH_PENALTY = 0.01 # seconds
1193 ZIP_PENALTY = 2.0 # seconds
1194 ZIP_RATE = 10000000.0 # bytes / second
1195 TRANSFER_RATE = 2000000.0 # bytes / second
1196 COMPRESSION_RATIO = 2.0 # unitless
1197
1198 adb_call_time = ADB_CALL_PENALTY * adb_calls
1199 adb_push_setup_time = ADB_PUSH_PENALTY * file_count
1200 if is_zipping:
1201 zip_time = ZIP_PENALTY + byte_count / ZIP_RATE
1202 transfer_time = byte_count / (TRANSFER_RATE * COMPRESSION_RATIO)
1203 else:
1204 zip_time = 0
1205 transfer_time = byte_count / TRANSFER_RATE
1206 return adb_call_time + adb_push_setup_time + zip_time + transfer_time
1207
1208 def _PushChangedFilesIndividually(self, files):
1209 for h, d in files:
1210 self.adb.Push(h, d)
1211
1212 def _PushChangedFilesZipped(self, files):
1213 if not files:
1214 return
1215
1216 with tempfile.NamedTemporaryFile(suffix='.zip') as zip_file:
1217 zip_proc = multiprocessing.Process(
1218 target=DeviceUtils._CreateDeviceZip,
1219 args=(zip_file.name, files))
1220 zip_proc.start()
1221 zip_proc.join()
1222
1223 zip_on_device = '%s/tmp.zip' % self.GetExternalStoragePath()
1224 try:
1225 self.adb.Push(zip_file.name, zip_on_device)
1226 self.RunShellCommand(
1227 ['unzip', zip_on_device],
1228 as_root=True,
1229 env={'PATH': '%s:$PATH' % install_commands.BIN_DIR},
1230 check_return=True)
1231 finally:
1232 if zip_proc.is_alive():
1233 zip_proc.terminate()
1234 if self.IsOnline():
1235 self.RunShellCommand(['rm', zip_on_device], check_return=True)
1236
1237 @staticmethod
1238 def _CreateDeviceZip(zip_path, host_device_tuples):
1239 with zipfile.ZipFile(zip_path, 'w') as zip_file:
1240 for host_path, device_path in host_device_tuples:
1241 zip_utils.WriteToZipFile(zip_file, host_path, device_path)
1242
1243 # TODO(nednguyen): remove this and migrate the callsite to PathExists().
1244 def FileExists(self, device_path, timeout=None, retries=None):
1245 """Checks whether the given file exists on the device.
1246
1247 Arguments are the same as PathExists.
1248 """
1249 return self.PathExists(device_path, timeout=timeout, retries=retries)
1250
1251 @decorators.WithTimeoutAndRetriesFromInstance()
1252 def PathExists(self, device_path, timeout=None, retries=None):
1253 """Checks whether the given path exists on the device.
1254
1255 Args:
1256 device_path: A string containing the absolute path to the file on the
1257 device.
1258 timeout: timeout in seconds
1259 retries: number of retries
1260
1261 Returns:
1262 True if the file exists on the device, False otherwise.
1263
1264 Raises:
1265 CommandTimeoutError on timeout.
1266 DeviceUnreachableError on missing device.
1267 """
1268 try:
1269 self.RunShellCommand(['test', '-e', device_path], check_return=True)
1270 return True
1271 except device_errors.AdbCommandFailedError:
1272 return False
1273
1274 @decorators.WithTimeoutAndRetriesFromInstance()
1275 def PullFile(self, device_path, host_path, timeout=None, retries=None):
1276 """Pull a file from the device.
1277
1278 Args:
1279 device_path: A string containing the absolute path of the file to pull
1280 from the device.
1281 host_path: A string containing the absolute path of the destination on
1282 the host.
1283 timeout: timeout in seconds
1284 retries: number of retries
1285
1286 Raises:
1287 CommandFailedError on failure.
1288 CommandTimeoutError on timeout.
1289 """
1290 # Create the base dir if it doesn't exist already
1291 dirname = os.path.dirname(host_path)
1292 if dirname and not os.path.exists(dirname):
1293 os.makedirs(dirname)
1294 self.adb.Pull(device_path, host_path)
1295
1296 def _ReadFileWithPull(self, device_path):
1297 try:
1298 d = tempfile.mkdtemp()
1299 host_temp_path = os.path.join(d, 'tmp_ReadFileWithPull')
1300 self.adb.Pull(device_path, host_temp_path)
1301 with open(host_temp_path, 'r') as host_temp:
1302 return host_temp.read()
1303 finally:
1304 if os.path.exists(d):
1305 shutil.rmtree(d)
1306
1307 _LS_RE = re.compile(
1308 r'(?P<perms>\S+) +(?P<owner>\S+) +(?P<group>\S+) +(?:(?P<size>\d+) +)?'
1309 + r'(?P<date>\S+) +(?P<time>\S+) +(?P<name>.+)$')
1310
1311 @decorators.WithTimeoutAndRetriesFromInstance()
1312 def ReadFile(self, device_path, as_root=False, force_pull=False,
1313 timeout=None, retries=None):
1314 """Reads the contents of a file from the device.
1315
1316 Args:
1317 device_path: A string containing the absolute path of the file to read
1318 from the device.
1319 as_root: A boolean indicating whether the read should be executed with
1320 root privileges.
1321 force_pull: A boolean indicating whether to force the operation to be
1322 performed by pulling a file from the device. The default is, when the
1323 contents are short, to retrieve the contents using cat instead.
1324 timeout: timeout in seconds
1325 retries: number of retries
1326
1327 Returns:
1328 The contents of |device_path| as a string. Contents are intepreted using
1329 universal newlines, so the caller will see them encoded as '\n'. Also,
1330 all lines will be terminated.
1331
1332 Raises:
1333 AdbCommandFailedError if the file can't be read.
1334 CommandTimeoutError on timeout.
1335 DeviceUnreachableError on missing device.
1336 """
1337 def get_size(path):
1338 # TODO(jbudorick): Implement a generic version of Stat() that handles
1339 # as_root=True, then switch this implementation to use that.
1340 ls_out = self.RunShellCommand(['ls', '-l', device_path], as_root=as_root,
1341 check_return=True)
1342 for line in ls_out:
1343 m = self._LS_RE.match(line)
1344 if m and m.group('name') == posixpath.basename(device_path):
1345 return int(m.group('size'))
1346 logging.warning('Could not determine size of %s.', device_path)
1347 return None
1348
1349 if (not force_pull
1350 and 0 < get_size(device_path) <= self._MAX_ADB_OUTPUT_LENGTH):
1351 return _JoinLines(self.RunShellCommand(
1352 ['cat', device_path], as_root=as_root, check_return=True))
1353 elif as_root and self.NeedsSU():
1354 with device_temp_file.DeviceTempFile(self.adb) as device_temp:
1355 self.RunShellCommand(['cp', device_path, device_temp.name],
1356 as_root=True, check_return=True)
1357 return self._ReadFileWithPull(device_temp.name)
1358 else:
1359 return self._ReadFileWithPull(device_path)
1360
1361 def _WriteFileWithPush(self, device_path, contents):
1362 with tempfile.NamedTemporaryFile() as host_temp:
1363 host_temp.write(contents)
1364 host_temp.flush()
1365 self.adb.Push(host_temp.name, device_path)
1366
1367 @decorators.WithTimeoutAndRetriesFromInstance()
1368 def WriteFile(self, device_path, contents, as_root=False, force_push=False,
1369 timeout=None, retries=None):
1370 """Writes |contents| to a file on the device.
1371
1372 Args:
1373 device_path: A string containing the absolute path to the file to write
1374 on the device.
1375 contents: A string containing the data to write to the device.
1376 as_root: A boolean indicating whether the write should be executed with
1377 root privileges (if available).
1378 force_push: A boolean indicating whether to force the operation to be
1379 performed by pushing a file to the device. The default is, when the
1380 contents are short, to pass the contents using a shell script instead.
1381 timeout: timeout in seconds
1382 retries: number of retries
1383
1384 Raises:
1385 CommandFailedError if the file could not be written on the device.
1386 CommandTimeoutError on timeout.
1387 DeviceUnreachableError on missing device.
1388 """
1389 if not force_push and len(contents) < self._MAX_ADB_COMMAND_LENGTH:
1390 # If the contents are small, for efficieny we write the contents with
1391 # a shell command rather than pushing a file.
1392 cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents),
1393 cmd_helper.SingleQuote(device_path))
1394 self.RunShellCommand(cmd, as_root=as_root, check_return=True)
1395 elif as_root and self.NeedsSU():
1396 # Adb does not allow to "push with su", so we first push to a temp file
1397 # on a safe location, and then copy it to the desired location with su.
1398 with device_temp_file.DeviceTempFile(self.adb) as device_temp:
1399 self._WriteFileWithPush(device_temp.name, contents)
1400 # Here we need 'cp' rather than 'mv' because the temp and
1401 # destination files might be on different file systems (e.g.
1402 # on internal storage and an external sd card).
1403 self.RunShellCommand(['cp', device_temp.name, device_path],
1404 as_root=True, check_return=True)
1405 else:
1406 # If root is not needed, we can push directly to the desired location.
1407 self._WriteFileWithPush(device_path, contents)
1408
1409 @decorators.WithTimeoutAndRetriesFromInstance()
1410 def Ls(self, device_path, timeout=None, retries=None):
1411 """Lists the contents of a directory on the device.
1412
1413 Args:
1414 device_path: A string containing the path of the directory on the device
1415 to list.
1416 timeout: timeout in seconds
1417 retries: number of retries
1418
1419 Returns:
1420 A list of pairs (filename, stat) for each file found in the directory,
1421 where the stat object has the properties: st_mode, st_size, and st_time.
1422
1423 Raises:
1424 AdbCommandFailedError if |device_path| does not specify a valid and
1425 accessible directory in the device.
1426 CommandTimeoutError on timeout.
1427 DeviceUnreachableError on missing device.
1428 """
1429 return self.adb.Ls(device_path)
1430
1431 @decorators.WithTimeoutAndRetriesFromInstance()
1432 def Stat(self, device_path, timeout=None, retries=None):
1433 """Get the stat attributes of a file or directory on the device.
1434
1435 Args:
1436 device_path: A string containing the path of from which to get attributes
1437 on the device.
1438 timeout: timeout in seconds
1439 retries: number of retries
1440
1441 Returns:
1442 A stat object with the properties: st_mode, st_size, and st_time
1443
1444 Raises:
1445 CommandFailedError if device_path cannot be found on the device.
1446 CommandTimeoutError on timeout.
1447 DeviceUnreachableError on missing device.
1448 """
1449 dirname, target = device_path.rsplit('/', 1)
1450 for filename, stat in self.adb.Ls(dirname):
1451 if filename == target:
1452 return stat
1453 raise device_errors.CommandFailedError(
1454 'Cannot find file or directory: %r' % device_path, str(self))
1455
1456 @decorators.WithTimeoutAndRetriesFromInstance()
1457 def SetJavaAsserts(self, enabled, timeout=None, retries=None):
1458 """Enables or disables Java asserts.
1459
1460 Args:
1461 enabled: A boolean indicating whether Java asserts should be enabled
1462 or disabled.
1463 timeout: timeout in seconds
1464 retries: number of retries
1465
1466 Returns:
1467 True if the device-side property changed and a restart is required as a
1468 result, False otherwise.
1469
1470 Raises:
1471 CommandTimeoutError on timeout.
1472 """
1473 def find_property(lines, property_name):
1474 for index, line in enumerate(lines):
1475 if line.strip() == '':
1476 continue
1477 key, value = (s.strip() for s in line.split('=', 1))
1478 if key == property_name:
1479 return index, value
1480 return None, ''
1481
1482 new_value = 'all' if enabled else ''
1483
1484 # First ensure the desired property is persisted.
1485 try:
1486 properties = self.ReadFile(
1487 constants.DEVICE_LOCAL_PROPERTIES_PATH).splitlines()
1488 except device_errors.CommandFailedError:
1489 properties = []
1490 index, value = find_property(properties, self.JAVA_ASSERT_PROPERTY)
1491 if new_value != value:
1492 if new_value:
1493 new_line = '%s=%s' % (self.JAVA_ASSERT_PROPERTY, new_value)
1494 if index is None:
1495 properties.append(new_line)
1496 else:
1497 properties[index] = new_line
1498 else:
1499 assert index is not None # since new_value == '' and new_value != value
1500 properties.pop(index)
1501 self.WriteFile(constants.DEVICE_LOCAL_PROPERTIES_PATH,
1502 _JoinLines(properties))
1503
1504 # Next, check the current runtime value is what we need, and
1505 # if not, set it and report that a reboot is required.
1506 value = self.GetProp(self.JAVA_ASSERT_PROPERTY)
1507 if new_value != value:
1508 self.SetProp(self.JAVA_ASSERT_PROPERTY, new_value)
1509 return True
1510 else:
1511 return False
1512
1513 @property
1514 def language(self):
1515 """Returns the language setting on the device."""
1516 return self.GetProp('persist.sys.language', cache=False)
1517
1518 @property
1519 def country(self):
1520 """Returns the country setting on the device."""
1521 return self.GetProp('persist.sys.country', cache=False)
1522
1523 @property
1524 def screen_density(self):
1525 """Returns the screen density of the device."""
1526 DPI_TO_DENSITY = {
1527 120: 'ldpi',
1528 160: 'mdpi',
1529 240: 'hdpi',
1530 320: 'xhdpi',
1531 480: 'xxhdpi',
1532 640: 'xxxhdpi',
1533 }
1534 dpi = int(self.GetProp('ro.sf.lcd_density', cache=True))
1535 return DPI_TO_DENSITY.get(dpi, 'tvdpi')
1536
1537 @property
1538 def build_description(self):
1539 """Returns the build description of the system.
1540
1541 For example:
1542 nakasi-user 4.4.4 KTU84P 1227136 release-keys
1543 """
1544 return self.GetProp('ro.build.description', cache=True)
1545
1546 @property
1547 def build_fingerprint(self):
1548 """Returns the build fingerprint of the system.
1549
1550 For example:
1551 google/nakasi/grouper:4.4.4/KTU84P/1227136:user/release-keys
1552 """
1553 return self.GetProp('ro.build.fingerprint', cache=True)
1554
1555 @property
1556 def build_id(self):
1557 """Returns the build ID of the system (e.g. 'KTU84P')."""
1558 return self.GetProp('ro.build.id', cache=True)
1559
1560 @property
1561 def build_product(self):
1562 """Returns the build product of the system (e.g. 'grouper')."""
1563 return self.GetProp('ro.build.product', cache=True)
1564
1565 @property
1566 def build_type(self):
1567 """Returns the build type of the system (e.g. 'user')."""
1568 return self.GetProp('ro.build.type', cache=True)
1569
1570 @property
1571 def build_version_sdk(self):
1572 """Returns the build version sdk of the system as a number (e.g. 19).
1573
1574 For version code numbers see:
1575 http://developer.android.com/reference/android/os/Build.VERSION_CODES.html
1576
1577 For named constants see:
1578 pylib.constants.ANDROID_SDK_VERSION_CODES
1579
1580 Raises:
1581 CommandFailedError if the build version sdk is not a number.
1582 """
1583 value = self.GetProp('ro.build.version.sdk', cache=True)
1584 try:
1585 return int(value)
1586 except ValueError:
1587 raise device_errors.CommandFailedError(
1588 'Invalid build version sdk: %r' % value)
1589
1590 @property
1591 def product_cpu_abi(self):
1592 """Returns the product cpu abi of the device (e.g. 'armeabi-v7a')."""
1593 return self.GetProp('ro.product.cpu.abi', cache=True)
1594
1595 @property
1596 def product_model(self):
1597 """Returns the name of the product model (e.g. 'Nexus 7')."""
1598 return self.GetProp('ro.product.model', cache=True)
1599
1600 @property
1601 def product_name(self):
1602 """Returns the product name of the device (e.g. 'nakasi')."""
1603 return self.GetProp('ro.product.name', cache=True)
1604
1605 def GetProp(self, property_name, cache=False, timeout=DEFAULT,
1606 retries=DEFAULT):
1607 """Gets a property from the device.
1608
1609 Args:
1610 property_name: A string containing the name of the property to get from
1611 the device.
1612 cache: A boolean indicating whether to cache the value of this property.
1613 timeout: timeout in seconds
1614 retries: number of retries
1615
1616 Returns:
1617 The value of the device's |property_name| property.
1618
1619 Raises:
1620 CommandTimeoutError on timeout.
1621 """
1622 assert isinstance(property_name, basestring), (
1623 "property_name is not a string: %r" % property_name)
1624
1625 cache_key = '_prop:' + property_name
1626 if cache and cache_key in self._cache:
1627 return self._cache[cache_key]
1628 else:
1629 # timeout and retries are handled down at run shell, because we don't
1630 # want to apply them in the other branch when reading from the cache
1631 value = self.RunShellCommand(
1632 ['getprop', property_name], single_line=True, check_return=True,
1633 timeout=self._default_timeout if timeout is DEFAULT else timeout,
1634 retries=self._default_retries if retries is DEFAULT else retries)
1635 if cache or cache_key in self._cache:
1636 self._cache[cache_key] = value
1637 return value
1638
1639 @decorators.WithTimeoutAndRetriesFromInstance()
1640 def SetProp(self, property_name, value, check=False, timeout=None,
1641 retries=None):
1642 """Sets a property on the device.
1643
1644 Args:
1645 property_name: A string containing the name of the property to set on
1646 the device.
1647 value: A string containing the value to set to the property on the
1648 device.
1649 check: A boolean indicating whether to check that the property was
1650 successfully set on the device.
1651 timeout: timeout in seconds
1652 retries: number of retries
1653
1654 Raises:
1655 CommandFailedError if check is true and the property was not correctly
1656 set on the device (e.g. because it is not rooted).
1657 CommandTimeoutError on timeout.
1658 """
1659 assert isinstance(property_name, basestring), (
1660 "property_name is not a string: %r" % property_name)
1661 assert isinstance(value, basestring), "value is not a string: %r" % value
1662
1663 self.RunShellCommand(['setprop', property_name, value], check_return=True)
1664 cache_key = '_prop:' + property_name
1665 if cache_key in self._cache:
1666 del self._cache[cache_key]
1667 # TODO(perezju) remove the option and make the check mandatory, but using a
1668 # single shell script to both set- and getprop.
1669 if check and value != self.GetProp(property_name):
1670 raise device_errors.CommandFailedError(
1671 'Unable to set property %r on the device to %r'
1672 % (property_name, value), str(self))
1673
1674 @decorators.WithTimeoutAndRetriesFromInstance()
1675 def GetABI(self, timeout=None, retries=None):
1676 """Gets the device main ABI.
1677
1678 Args:
1679 timeout: timeout in seconds
1680 retries: number of retries
1681
1682 Returns:
1683 The device's main ABI name.
1684
1685 Raises:
1686 CommandTimeoutError on timeout.
1687 """
1688 return self.GetProp('ro.product.cpu.abi')
1689
1690 @decorators.WithTimeoutAndRetriesFromInstance()
1691 def GetPids(self, process_name, timeout=None, retries=None):
1692 """Returns the PIDs of processes with the given name.
1693
1694 Note that the |process_name| is often the package name.
1695
1696 Args:
1697 process_name: A string containing the process name to get the PIDs for.
1698 timeout: timeout in seconds
1699 retries: number of retries
1700
1701 Returns:
1702 A dict mapping process name to a list of PIDs for each process that
1703 contained the provided |process_name|.
1704
1705 Raises:
1706 CommandTimeoutError on timeout.
1707 DeviceUnreachableError on missing device.
1708 """
1709 procs_pids = collections.defaultdict(list)
1710 try:
1711 ps_output = self._RunPipedShellCommand(
1712 'ps | grep -F %s' % cmd_helper.SingleQuote(process_name))
1713 except device_errors.AdbShellCommandFailedError as e:
1714 if e.status and isinstance(e.status, list) and not e.status[0]:
1715 # If ps succeeded but grep failed, there were no processes with the
1716 # given name.
1717 return procs_pids
1718 else:
1719 raise
1720
1721 for line in ps_output:
1722 try:
1723 ps_data = line.split()
1724 if process_name in ps_data[-1]:
1725 pid, process = ps_data[1], ps_data[-1]
1726 procs_pids[process].append(pid)
1727 except IndexError:
1728 pass
1729 return procs_pids
1730
1731 @decorators.WithTimeoutAndRetriesFromInstance()
1732 def TakeScreenshot(self, host_path=None, timeout=None, retries=None):
1733 """Takes a screenshot of the device.
1734
1735 Args:
1736 host_path: A string containing the path on the host to save the
1737 screenshot to. If None, a file name in the current
1738 directory will be generated.
1739 timeout: timeout in seconds
1740 retries: number of retries
1741
1742 Returns:
1743 The name of the file on the host to which the screenshot was saved.
1744
1745 Raises:
1746 CommandFailedError on failure.
1747 CommandTimeoutError on timeout.
1748 DeviceUnreachableError on missing device.
1749 """
1750 if not host_path:
1751 host_path = os.path.abspath('screenshot-%s.png' % _GetTimeStamp())
1752 with device_temp_file.DeviceTempFile(self.adb, suffix='.png') as device_tmp:
1753 self.RunShellCommand(['/system/bin/screencap', '-p', device_tmp.name],
1754 check_return=True)
1755 self.PullFile(device_tmp.name, host_path)
1756 return host_path
1757
1758 @decorators.WithTimeoutAndRetriesFromInstance()
1759 def GetMemoryUsageForPid(self, pid, timeout=None, retries=None):
1760 """Gets the memory usage for the given PID.
1761
1762 Args:
1763 pid: PID of the process.
1764 timeout: timeout in seconds
1765 retries: number of retries
1766
1767 Returns:
1768 A dict containing memory usage statistics for the PID. May include:
1769 Size, Rss, Pss, Shared_Clean, Shared_Dirty, Private_Clean,
1770 Private_Dirty, VmHWM
1771
1772 Raises:
1773 CommandTimeoutError on timeout.
1774 """
1775 result = collections.defaultdict(int)
1776
1777 try:
1778 result.update(self._GetMemoryUsageForPidFromSmaps(pid))
1779 except device_errors.CommandFailedError:
1780 logging.exception('Error getting memory usage from smaps')
1781
1782 try:
1783 result.update(self._GetMemoryUsageForPidFromStatus(pid))
1784 except device_errors.CommandFailedError:
1785 logging.exception('Error getting memory usage from status')
1786
1787 return result
1788
1789 @decorators.WithTimeoutAndRetriesFromInstance()
1790 def DismissCrashDialogIfNeeded(self, timeout=None, retries=None):
1791 """Dismiss the error/ANR dialog if present.
1792
1793 Returns: Name of the crashed package if a dialog is focused,
1794 None otherwise.
1795 """
1796 def _FindFocusedWindow():
1797 match = None
1798 # TODO(jbudorick): Try to grep the output on the device instead of using
1799 # large_output if/when DeviceUtils exposes a public interface for piped
1800 # shell command handling.
1801 for line in self.RunShellCommand(['dumpsys', 'window', 'windows'],
1802 check_return=True, large_output=True):
1803 match = re.match(_CURRENT_FOCUS_CRASH_RE, line)
1804 if match:
1805 break
1806 return match
1807
1808 match = _FindFocusedWindow()
1809 if not match:
1810 return None
1811 package = match.group(2)
1812 logging.warning('Trying to dismiss %s dialog for %s' % match.groups())
1813 self.SendKeyEvent(keyevent.KEYCODE_DPAD_RIGHT)
1814 self.SendKeyEvent(keyevent.KEYCODE_DPAD_RIGHT)
1815 self.SendKeyEvent(keyevent.KEYCODE_ENTER)
1816 match = _FindFocusedWindow()
1817 if match:
1818 logging.error('Still showing a %s dialog for %s' % match.groups())
1819 return package
1820
1821 def _GetMemoryUsageForPidFromSmaps(self, pid):
1822 SMAPS_COLUMNS = (
1823 'Size', 'Rss', 'Pss', 'Shared_Clean', 'Shared_Dirty', 'Private_Clean',
1824 'Private_Dirty')
1825
1826 showmap_out = self._RunPipedShellCommand(
1827 'showmap %d | grep TOTAL' % int(pid), as_root=True)
1828
1829 split_totals = showmap_out[-1].split()
1830 if (not split_totals
1831 or len(split_totals) != 9
1832 or split_totals[-1] != 'TOTAL'):
1833 raise device_errors.CommandFailedError(
1834 'Invalid output from showmap: %s' % '\n'.join(showmap_out))
1835
1836 return dict(itertools.izip(SMAPS_COLUMNS, (int(n) for n in split_totals)))
1837
1838 def _GetMemoryUsageForPidFromStatus(self, pid):
1839 for line in self.ReadFile(
1840 '/proc/%s/status' % str(pid), as_root=True).splitlines():
1841 if line.startswith('VmHWM:'):
1842 return {'VmHWM': int(line.split()[1])}
1843 else:
1844 raise device_errors.CommandFailedError(
1845 'Could not find memory peak value for pid %s', str(pid))
1846
1847 @decorators.WithTimeoutAndRetriesFromInstance()
1848 def GetLogcatMonitor(self, timeout=None, retries=None, *args, **kwargs):
1849 """Returns a new LogcatMonitor associated with this device.
1850
1851 Parameters passed to this function are passed directly to
1852 |logcat_monitor.LogcatMonitor| and are documented there.
1853
1854 Args:
1855 timeout: timeout in seconds
1856 retries: number of retries
1857 """
1858 return logcat_monitor.LogcatMonitor(self.adb, *args, **kwargs)
1859
1860 def GetClientCache(self, client_name):
1861 """Returns client cache."""
1862 if client_name not in self._client_caches:
1863 self._client_caches[client_name] = {}
1864 return self._client_caches[client_name]
1865
1866 def _ClearCache(self):
1867 """Clears all caches."""
1868 for client in self._client_caches:
1869 self._client_caches[client].clear()
1870 self._cache = {
1871 # Map of packageId -> list of on-device .apk paths
1872 'package_apk_paths': {},
1873 # Map of packageId -> set of on-device .apk checksums
1874 'package_apk_checksums': {},
1875 }
1876
1877 @classmethod
1878 def parallel(cls, devices, async=False):
1879 """Creates a Parallelizer to operate over the provided list of devices.
1880
1881 If |devices| is either |None| or an empty list, the Parallelizer will
1882 operate over all attached devices that have not been blacklisted.
1883
1884 Args:
1885 devices: A list of either DeviceUtils instances or objects from
1886 from which DeviceUtils instances can be constructed. If None,
1887 all attached devices will be used.
1888 async: If true, returns a Parallelizer that runs operations
1889 asynchronously.
1890
1891 Returns:
1892 A Parallelizer operating over |devices|.
1893 """
1894 if not devices:
1895 raise device_errors.NoDevicesError()
1896
1897 devices = [d if isinstance(d, cls) else cls(d) for d in devices]
1898 if async:
1899 return parallelizer.Parallelizer(devices)
1900 else:
1901 return parallelizer.SyncParallelizer(devices)
1902
1903 @classmethod
1904 def HealthyDevices(cls, blacklist=None, **kwargs):
1905 if not blacklist:
1906 # TODO(jbudorick): Remove once clients pass in the blacklist.
1907 blacklist = device_blacklist.Blacklist(device_blacklist.BLACKLIST_JSON)
1908
1909 blacklisted_devices = blacklist.Read()
1910 def blacklisted(adb):
1911 if adb.GetDeviceSerial() in blacklisted_devices:
1912 logging.warning('Device %s is blacklisted.', adb.GetDeviceSerial())
1913 return True
1914 return False
1915
1916 return [cls(adb, **kwargs) for adb in adb_wrapper.AdbWrapper.Devices()
1917 if not blacklisted(adb)]
1918
1919 @decorators.WithTimeoutAndRetriesFromInstance()
1920 def RestartAdbd(self, timeout=None, retries=None):
1921 logging.info('Restarting adbd on device.')
1922 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script:
1923 self.WriteFile(script.name, _RESTART_ADBD_SCRIPT)
1924 self.RunShellCommand(['source', script.name], as_root=True)
1925 self.adb.WaitForDevice()
OLDNEW
« no previous file with comments | « build/android/pylib/device/device_list.py ('k') | build/android/pylib/device/device_utils_device_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698