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

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: TELEMETRY_DEPS 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 real_host_path = os.path.realpath(host_path)
1077 try:
1078 real_device_path = self.RunShellCommand(
1079 ['realpath', device_path], single_line=True, check_return=True)
1080 except device_errors.CommandFailedError:
1081 real_device_path = None
1082 if not real_device_path:
1083 return ([(host_path, device_path)], [], [])
1084
1085 try:
1086 host_checksums = md5sum.CalculateHostMd5Sums([real_host_path])
1087 interesting_device_paths = [real_device_path]
1088 if not track_stale and os.path.isdir(real_host_path):
1089 interesting_device_paths = [
1090 posixpath.join(real_device_path, os.path.relpath(p, real_host_path))
1091 for p in host_checksums.keys()]
1092 device_checksums = md5sum.CalculateDeviceMd5Sums(
1093 interesting_device_paths, self)
1094 except EnvironmentError as e:
1095 logging.warning('Error calculating md5: %s', e)
1096 return ([(host_path, device_path)], [], [])
1097
1098 to_push = []
1099 up_to_date = []
1100 to_delete = []
1101 if os.path.isfile(host_path):
1102 host_checksum = host_checksums.get(real_host_path)
1103 device_checksum = device_checksums.get(real_device_path)
1104 if host_checksum == device_checksum:
1105 up_to_date.append(host_path)
1106 else:
1107 to_push.append((host_path, device_path))
1108 else:
1109 for host_abs_path, host_checksum in host_checksums.iteritems():
1110 device_abs_path = posixpath.join(
1111 real_device_path, os.path.relpath(host_abs_path, real_host_path))
1112 device_checksum = device_checksums.pop(device_abs_path, None)
1113 if device_checksum == host_checksum:
1114 up_to_date.append(host_abs_path)
1115 else:
1116 to_push.append((host_abs_path, device_abs_path))
1117 to_delete = device_checksums.keys()
1118 return (to_push, up_to_date, to_delete)
1119
1120 def _ComputeDeviceChecksumsForApks(self, package_name):
1121 ret = self._cache['package_apk_checksums'].get(package_name)
1122 if ret is None:
1123 device_paths = self._GetApplicationPathsInternal(package_name)
1124 file_to_checksums = md5sum.CalculateDeviceMd5Sums(device_paths, self)
1125 ret = set(file_to_checksums.values())
1126 self._cache['package_apk_checksums'][package_name] = ret
1127 return ret
1128
1129 def _ComputeStaleApks(self, package_name, host_apk_paths):
1130 host_checksums_holder = [None]
1131 def compute_host_checksums():
1132 host_checksums_holder[0] = md5sum.CalculateHostMd5Sums(host_apk_paths)
1133
1134 host_thread = threading.Thread(target=compute_host_checksums)
1135 host_thread.start()
1136 device_checksums = self._ComputeDeviceChecksumsForApks(package_name)
1137 host_thread.join()
1138 host_checksums = host_checksums_holder[0]
1139 stale_apks = [k for (k, v) in host_checksums.iteritems()
1140 if v not in device_checksums]
1141 return stale_apks, set(host_checksums.values())
1142
1143 def _PushFilesImpl(self, host_device_tuples, files):
1144 size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files)
1145 file_count = len(files)
1146 dir_size = sum(host_utils.GetRecursiveDiskUsage(h)
1147 for h, _ in host_device_tuples)
1148 dir_file_count = 0
1149 for h, _ in host_device_tuples:
1150 if os.path.isdir(h):
1151 dir_file_count += sum(len(f) for _r, _d, f in os.walk(h))
1152 else:
1153 dir_file_count += 1
1154
1155 push_duration = self._ApproximateDuration(
1156 file_count, file_count, size, False)
1157 dir_push_duration = self._ApproximateDuration(
1158 len(host_device_tuples), dir_file_count, dir_size, False)
1159 zip_duration = self._ApproximateDuration(1, 1, size, True)
1160
1161 self._InstallCommands()
1162
1163 if dir_push_duration < push_duration and (
1164 dir_push_duration < zip_duration or not self._commands_installed):
1165 self._PushChangedFilesIndividually(host_device_tuples)
1166 elif push_duration < zip_duration or not self._commands_installed:
1167 self._PushChangedFilesIndividually(files)
1168 else:
1169 self._PushChangedFilesZipped(files)
1170 self.RunShellCommand(
1171 ['chmod', '-R', '777'] + [d for _, d in host_device_tuples],
1172 as_root=True, check_return=True)
1173
1174 def _InstallCommands(self):
1175 if self._commands_installed is None:
1176 try:
1177 if not install_commands.Installed(self):
1178 install_commands.InstallCommands(self)
1179 self._commands_installed = True
1180 except Exception as e:
1181 logging.warning('unzip not available: %s' % str(e))
1182 self._commands_installed = False
1183
1184 @staticmethod
1185 def _ApproximateDuration(adb_calls, file_count, byte_count, is_zipping):
1186 # We approximate the time to push a set of files to a device as:
1187 # t = c1 * a + c2 * f + c3 + b / c4 + b / (c5 * c6), where
1188 # t: total time (sec)
1189 # c1: adb call time delay (sec)
1190 # a: number of times adb is called (unitless)
1191 # c2: push time delay (sec)
1192 # f: number of files pushed via adb (unitless)
1193 # c3: zip time delay (sec)
1194 # c4: zip rate (bytes/sec)
1195 # b: total number of bytes (bytes)
1196 # c5: transfer rate (bytes/sec)
1197 # c6: compression ratio (unitless)
1198
1199 # All of these are approximations.
1200 ADB_CALL_PENALTY = 0.1 # seconds
1201 ADB_PUSH_PENALTY = 0.01 # seconds
1202 ZIP_PENALTY = 2.0 # seconds
1203 ZIP_RATE = 10000000.0 # bytes / second
1204 TRANSFER_RATE = 2000000.0 # bytes / second
1205 COMPRESSION_RATIO = 2.0 # unitless
1206
1207 adb_call_time = ADB_CALL_PENALTY * adb_calls
1208 adb_push_setup_time = ADB_PUSH_PENALTY * file_count
1209 if is_zipping:
1210 zip_time = ZIP_PENALTY + byte_count / ZIP_RATE
1211 transfer_time = byte_count / (TRANSFER_RATE * COMPRESSION_RATIO)
1212 else:
1213 zip_time = 0
1214 transfer_time = byte_count / TRANSFER_RATE
1215 return adb_call_time + adb_push_setup_time + zip_time + transfer_time
1216
1217 def _PushChangedFilesIndividually(self, files):
1218 for h, d in files:
1219 self.adb.Push(h, d)
1220
1221 def _PushChangedFilesZipped(self, files):
1222 if not files:
1223 return
1224
1225 with tempfile.NamedTemporaryFile(suffix='.zip') as zip_file:
1226 zip_proc = multiprocessing.Process(
1227 target=DeviceUtils._CreateDeviceZip,
1228 args=(zip_file.name, files))
1229 zip_proc.start()
1230 zip_proc.join()
1231
1232 zip_on_device = '%s/tmp.zip' % self.GetExternalStoragePath()
1233 try:
1234 self.adb.Push(zip_file.name, zip_on_device)
1235 self.RunShellCommand(
1236 ['unzip', zip_on_device],
1237 as_root=True,
1238 env={'PATH': '%s:$PATH' % install_commands.BIN_DIR},
1239 check_return=True)
1240 finally:
1241 if zip_proc.is_alive():
1242 zip_proc.terminate()
1243 if self.IsOnline():
1244 self.RunShellCommand(['rm', zip_on_device], check_return=True)
1245
1246 @staticmethod
1247 def _CreateDeviceZip(zip_path, host_device_tuples):
1248 with zipfile.ZipFile(zip_path, 'w') as zip_file:
1249 for host_path, device_path in host_device_tuples:
1250 zip_utils.WriteToZipFile(zip_file, host_path, device_path)
1251
1252 # TODO(nednguyen): remove this and migrate the callsite to PathExists().
1253 def FileExists(self, device_path, timeout=None, retries=None):
1254 """Checks whether the given file exists on the device.
1255
1256 Arguments are the same as PathExists.
1257 """
1258 return self.PathExists(device_path, timeout=timeout, retries=retries)
1259
1260 @decorators.WithTimeoutAndRetriesFromInstance()
1261 def PathExists(self, device_path, timeout=None, retries=None):
1262 """Checks whether the given path exists on the device.
1263
1264 Args:
1265 device_path: A string containing the absolute path to the file on the
1266 device.
1267 timeout: timeout in seconds
1268 retries: number of retries
1269
1270 Returns:
1271 True if the file exists on the device, False otherwise.
1272
1273 Raises:
1274 CommandTimeoutError on timeout.
1275 DeviceUnreachableError on missing device.
1276 """
1277 try:
1278 self.RunShellCommand(['test', '-e', device_path], check_return=True)
1279 return True
1280 except device_errors.AdbCommandFailedError:
1281 return False
1282
1283 @decorators.WithTimeoutAndRetriesFromInstance()
1284 def PullFile(self, device_path, host_path, timeout=None, retries=None):
1285 """Pull a file from the device.
1286
1287 Args:
1288 device_path: A string containing the absolute path of the file to pull
1289 from the device.
1290 host_path: A string containing the absolute path of the destination on
1291 the host.
1292 timeout: timeout in seconds
1293 retries: number of retries
1294
1295 Raises:
1296 CommandFailedError on failure.
1297 CommandTimeoutError on timeout.
1298 """
1299 # Create the base dir if it doesn't exist already
1300 dirname = os.path.dirname(host_path)
1301 if dirname and not os.path.exists(dirname):
1302 os.makedirs(dirname)
1303 self.adb.Pull(device_path, host_path)
1304
1305 def _ReadFileWithPull(self, device_path):
1306 try:
1307 d = tempfile.mkdtemp()
1308 host_temp_path = os.path.join(d, 'tmp_ReadFileWithPull')
1309 self.adb.Pull(device_path, host_temp_path)
1310 with open(host_temp_path, 'r') as host_temp:
1311 return host_temp.read()
1312 finally:
1313 if os.path.exists(d):
1314 shutil.rmtree(d)
1315
1316 _LS_RE = re.compile(
1317 r'(?P<perms>\S+) +(?P<owner>\S+) +(?P<group>\S+) +(?:(?P<size>\d+) +)?'
1318 + r'(?P<date>\S+) +(?P<time>\S+) +(?P<name>.+)$')
1319
1320 @decorators.WithTimeoutAndRetriesFromInstance()
1321 def ReadFile(self, device_path, as_root=False, force_pull=False,
1322 timeout=None, retries=None):
1323 """Reads the contents of a file from the device.
1324
1325 Args:
1326 device_path: A string containing the absolute path of the file to read
1327 from the device.
1328 as_root: A boolean indicating whether the read should be executed with
1329 root privileges.
1330 force_pull: A boolean indicating whether to force the operation to be
1331 performed by pulling a file from the device. The default is, when the
1332 contents are short, to retrieve the contents using cat instead.
1333 timeout: timeout in seconds
1334 retries: number of retries
1335
1336 Returns:
1337 The contents of |device_path| as a string. Contents are intepreted using
1338 universal newlines, so the caller will see them encoded as '\n'. Also,
1339 all lines will be terminated.
1340
1341 Raises:
1342 AdbCommandFailedError if the file can't be read.
1343 CommandTimeoutError on timeout.
1344 DeviceUnreachableError on missing device.
1345 """
1346 def get_size(path):
1347 # TODO(jbudorick): Implement a generic version of Stat() that handles
1348 # as_root=True, then switch this implementation to use that.
1349 ls_out = self.RunShellCommand(['ls', '-l', device_path], as_root=as_root,
1350 check_return=True)
1351 for line in ls_out:
1352 m = self._LS_RE.match(line)
1353 if m and m.group('name') == posixpath.basename(device_path):
1354 return int(m.group('size'))
1355 logging.warning('Could not determine size of %s.', device_path)
1356 return None
1357
1358 if (not force_pull
1359 and 0 < get_size(device_path) <= self._MAX_ADB_OUTPUT_LENGTH):
1360 return _JoinLines(self.RunShellCommand(
1361 ['cat', device_path], as_root=as_root, check_return=True))
1362 elif as_root and self.NeedsSU():
1363 with device_temp_file.DeviceTempFile(self.adb) as device_temp:
1364 self.RunShellCommand(['cp', device_path, device_temp.name],
1365 as_root=True, check_return=True)
1366 return self._ReadFileWithPull(device_temp.name)
1367 else:
1368 return self._ReadFileWithPull(device_path)
1369
1370 def _WriteFileWithPush(self, device_path, contents):
1371 with tempfile.NamedTemporaryFile() as host_temp:
1372 host_temp.write(contents)
1373 host_temp.flush()
1374 self.adb.Push(host_temp.name, device_path)
1375
1376 @decorators.WithTimeoutAndRetriesFromInstance()
1377 def WriteFile(self, device_path, contents, as_root=False, force_push=False,
1378 timeout=None, retries=None):
1379 """Writes |contents| to a file on the device.
1380
1381 Args:
1382 device_path: A string containing the absolute path to the file to write
1383 on the device.
1384 contents: A string containing the data to write to the device.
1385 as_root: A boolean indicating whether the write should be executed with
1386 root privileges (if available).
1387 force_push: A boolean indicating whether to force the operation to be
1388 performed by pushing a file to the device. The default is, when the
1389 contents are short, to pass the contents using a shell script instead.
1390 timeout: timeout in seconds
1391 retries: number of retries
1392
1393 Raises:
1394 CommandFailedError if the file could not be written on the device.
1395 CommandTimeoutError on timeout.
1396 DeviceUnreachableError on missing device.
1397 """
1398 if not force_push and len(contents) < self._MAX_ADB_COMMAND_LENGTH:
1399 # If the contents are small, for efficieny we write the contents with
1400 # a shell command rather than pushing a file.
1401 cmd = 'echo -n %s > %s' % (cmd_helper.SingleQuote(contents),
1402 cmd_helper.SingleQuote(device_path))
1403 self.RunShellCommand(cmd, as_root=as_root, check_return=True)
1404 elif as_root and self.NeedsSU():
1405 # Adb does not allow to "push with su", so we first push to a temp file
1406 # on a safe location, and then copy it to the desired location with su.
1407 with device_temp_file.DeviceTempFile(self.adb) as device_temp:
1408 self._WriteFileWithPush(device_temp.name, contents)
1409 # Here we need 'cp' rather than 'mv' because the temp and
1410 # destination files might be on different file systems (e.g.
1411 # on internal storage and an external sd card).
1412 self.RunShellCommand(['cp', device_temp.name, device_path],
1413 as_root=True, check_return=True)
1414 else:
1415 # If root is not needed, we can push directly to the desired location.
1416 self._WriteFileWithPush(device_path, contents)
1417
1418 @decorators.WithTimeoutAndRetriesFromInstance()
1419 def Ls(self, device_path, timeout=None, retries=None):
1420 """Lists the contents of a directory on the device.
1421
1422 Args:
1423 device_path: A string containing the path of the directory on the device
1424 to list.
1425 timeout: timeout in seconds
1426 retries: number of retries
1427
1428 Returns:
1429 A list of pairs (filename, stat) for each file found in the directory,
1430 where the stat object has the properties: st_mode, st_size, and st_time.
1431
1432 Raises:
1433 AdbCommandFailedError if |device_path| does not specify a valid and
1434 accessible directory in the device.
1435 CommandTimeoutError on timeout.
1436 DeviceUnreachableError on missing device.
1437 """
1438 return self.adb.Ls(device_path)
1439
1440 @decorators.WithTimeoutAndRetriesFromInstance()
1441 def Stat(self, device_path, timeout=None, retries=None):
1442 """Get the stat attributes of a file or directory on the device.
1443
1444 Args:
1445 device_path: A string containing the path of from which to get attributes
1446 on the device.
1447 timeout: timeout in seconds
1448 retries: number of retries
1449
1450 Returns:
1451 A stat object with the properties: st_mode, st_size, and st_time
1452
1453 Raises:
1454 CommandFailedError if device_path cannot be found on the device.
1455 CommandTimeoutError on timeout.
1456 DeviceUnreachableError on missing device.
1457 """
1458 dirname, target = device_path.rsplit('/', 1)
1459 for filename, stat in self.adb.Ls(dirname):
1460 if filename == target:
1461 return stat
1462 raise device_errors.CommandFailedError(
1463 'Cannot find file or directory: %r' % device_path, str(self))
1464
1465 @decorators.WithTimeoutAndRetriesFromInstance()
1466 def SetJavaAsserts(self, enabled, timeout=None, retries=None):
1467 """Enables or disables Java asserts.
1468
1469 Args:
1470 enabled: A boolean indicating whether Java asserts should be enabled
1471 or disabled.
1472 timeout: timeout in seconds
1473 retries: number of retries
1474
1475 Returns:
1476 True if the device-side property changed and a restart is required as a
1477 result, False otherwise.
1478
1479 Raises:
1480 CommandTimeoutError on timeout.
1481 """
1482 def find_property(lines, property_name):
1483 for index, line in enumerate(lines):
1484 if line.strip() == '':
1485 continue
1486 key, value = (s.strip() for s in line.split('=', 1))
1487 if key == property_name:
1488 return index, value
1489 return None, ''
1490
1491 new_value = 'all' if enabled else ''
1492
1493 # First ensure the desired property is persisted.
1494 try:
1495 properties = self.ReadFile(
1496 constants.DEVICE_LOCAL_PROPERTIES_PATH).splitlines()
1497 except device_errors.CommandFailedError:
1498 properties = []
1499 index, value = find_property(properties, self.JAVA_ASSERT_PROPERTY)
1500 if new_value != value:
1501 if new_value:
1502 new_line = '%s=%s' % (self.JAVA_ASSERT_PROPERTY, new_value)
1503 if index is None:
1504 properties.append(new_line)
1505 else:
1506 properties[index] = new_line
1507 else:
1508 assert index is not None # since new_value == '' and new_value != value
1509 properties.pop(index)
1510 self.WriteFile(constants.DEVICE_LOCAL_PROPERTIES_PATH,
1511 _JoinLines(properties))
1512
1513 # Next, check the current runtime value is what we need, and
1514 # if not, set it and report that a reboot is required.
1515 value = self.GetProp(self.JAVA_ASSERT_PROPERTY)
1516 if new_value != value:
1517 self.SetProp(self.JAVA_ASSERT_PROPERTY, new_value)
1518 return True
1519 else:
1520 return False
1521
1522 @property
1523 def language(self):
1524 """Returns the language setting on the device."""
1525 return self.GetProp('persist.sys.language', cache=False)
1526
1527 @property
1528 def country(self):
1529 """Returns the country setting on the device."""
1530 return self.GetProp('persist.sys.country', cache=False)
1531
1532 @property
1533 def screen_density(self):
1534 """Returns the screen density of the device."""
1535 DPI_TO_DENSITY = {
1536 120: 'ldpi',
1537 160: 'mdpi',
1538 240: 'hdpi',
1539 320: 'xhdpi',
1540 480: 'xxhdpi',
1541 640: 'xxxhdpi',
1542 }
1543 dpi = int(self.GetProp('ro.sf.lcd_density', cache=True))
1544 return DPI_TO_DENSITY.get(dpi, 'tvdpi')
1545
1546 @property
1547 def build_description(self):
1548 """Returns the build description of the system.
1549
1550 For example:
1551 nakasi-user 4.4.4 KTU84P 1227136 release-keys
1552 """
1553 return self.GetProp('ro.build.description', cache=True)
1554
1555 @property
1556 def build_fingerprint(self):
1557 """Returns the build fingerprint of the system.
1558
1559 For example:
1560 google/nakasi/grouper:4.4.4/KTU84P/1227136:user/release-keys
1561 """
1562 return self.GetProp('ro.build.fingerprint', cache=True)
1563
1564 @property
1565 def build_id(self):
1566 """Returns the build ID of the system (e.g. 'KTU84P')."""
1567 return self.GetProp('ro.build.id', cache=True)
1568
1569 @property
1570 def build_product(self):
1571 """Returns the build product of the system (e.g. 'grouper')."""
1572 return self.GetProp('ro.build.product', cache=True)
1573
1574 @property
1575 def build_type(self):
1576 """Returns the build type of the system (e.g. 'user')."""
1577 return self.GetProp('ro.build.type', cache=True)
1578
1579 @property
1580 def build_version_sdk(self):
1581 """Returns the build version sdk of the system as a number (e.g. 19).
1582
1583 For version code numbers see:
1584 http://developer.android.com/reference/android/os/Build.VERSION_CODES.html
1585
1586 For named constants see:
1587 pylib.constants.ANDROID_SDK_VERSION_CODES
1588
1589 Raises:
1590 CommandFailedError if the build version sdk is not a number.
1591 """
1592 value = self.GetProp('ro.build.version.sdk', cache=True)
1593 try:
1594 return int(value)
1595 except ValueError:
1596 raise device_errors.CommandFailedError(
1597 'Invalid build version sdk: %r' % value)
1598
1599 @property
1600 def product_cpu_abi(self):
1601 """Returns the product cpu abi of the device (e.g. 'armeabi-v7a')."""
1602 return self.GetProp('ro.product.cpu.abi', cache=True)
1603
1604 @property
1605 def product_model(self):
1606 """Returns the name of the product model (e.g. 'Nexus 7')."""
1607 return self.GetProp('ro.product.model', cache=True)
1608
1609 @property
1610 def product_name(self):
1611 """Returns the product name of the device (e.g. 'nakasi')."""
1612 return self.GetProp('ro.product.name', cache=True)
1613
1614 def GetProp(self, property_name, cache=False, timeout=DEFAULT,
1615 retries=DEFAULT):
1616 """Gets a property from the device.
1617
1618 Args:
1619 property_name: A string containing the name of the property to get from
1620 the device.
1621 cache: A boolean indicating whether to cache the value of this property.
1622 timeout: timeout in seconds
1623 retries: number of retries
1624
1625 Returns:
1626 The value of the device's |property_name| property.
1627
1628 Raises:
1629 CommandTimeoutError on timeout.
1630 """
1631 assert isinstance(property_name, basestring), (
1632 "property_name is not a string: %r" % property_name)
1633
1634 cache_key = '_prop:' + property_name
1635 if cache and cache_key in self._cache:
1636 return self._cache[cache_key]
1637 else:
1638 # timeout and retries are handled down at run shell, because we don't
1639 # want to apply them in the other branch when reading from the cache
1640 value = self.RunShellCommand(
1641 ['getprop', property_name], single_line=True, check_return=True,
1642 timeout=self._default_timeout if timeout is DEFAULT else timeout,
1643 retries=self._default_retries if retries is DEFAULT else retries)
1644 if cache or cache_key in self._cache:
1645 self._cache[cache_key] = value
1646 return value
1647
1648 @decorators.WithTimeoutAndRetriesFromInstance()
1649 def SetProp(self, property_name, value, check=False, timeout=None,
1650 retries=None):
1651 """Sets a property on the device.
1652
1653 Args:
1654 property_name: A string containing the name of the property to set on
1655 the device.
1656 value: A string containing the value to set to the property on the
1657 device.
1658 check: A boolean indicating whether to check that the property was
1659 successfully set on the device.
1660 timeout: timeout in seconds
1661 retries: number of retries
1662
1663 Raises:
1664 CommandFailedError if check is true and the property was not correctly
1665 set on the device (e.g. because it is not rooted).
1666 CommandTimeoutError on timeout.
1667 """
1668 assert isinstance(property_name, basestring), (
1669 "property_name is not a string: %r" % property_name)
1670 assert isinstance(value, basestring), "value is not a string: %r" % value
1671
1672 self.RunShellCommand(['setprop', property_name, value], check_return=True)
1673 cache_key = '_prop:' + property_name
1674 if cache_key in self._cache:
1675 del self._cache[cache_key]
1676 # TODO(perezju) remove the option and make the check mandatory, but using a
1677 # single shell script to both set- and getprop.
1678 if check and value != self.GetProp(property_name):
1679 raise device_errors.CommandFailedError(
1680 'Unable to set property %r on the device to %r'
1681 % (property_name, value), str(self))
1682
1683 @decorators.WithTimeoutAndRetriesFromInstance()
1684 def GetABI(self, timeout=None, retries=None):
1685 """Gets the device main ABI.
1686
1687 Args:
1688 timeout: timeout in seconds
1689 retries: number of retries
1690
1691 Returns:
1692 The device's main ABI name.
1693
1694 Raises:
1695 CommandTimeoutError on timeout.
1696 """
1697 return self.GetProp('ro.product.cpu.abi')
1698
1699 @decorators.WithTimeoutAndRetriesFromInstance()
1700 def GetPids(self, process_name, timeout=None, retries=None):
1701 """Returns the PIDs of processes with the given name.
1702
1703 Note that the |process_name| is often the package name.
1704
1705 Args:
1706 process_name: A string containing the process name to get the PIDs for.
1707 timeout: timeout in seconds
1708 retries: number of retries
1709
1710 Returns:
1711 A dict mapping process name to a list of PIDs for each process that
1712 contained the provided |process_name|.
1713
1714 Raises:
1715 CommandTimeoutError on timeout.
1716 DeviceUnreachableError on missing device.
1717 """
1718 procs_pids = collections.defaultdict(list)
1719 try:
1720 ps_output = self._RunPipedShellCommand(
1721 'ps | grep -F %s' % cmd_helper.SingleQuote(process_name))
1722 except device_errors.AdbShellCommandFailedError as e:
1723 if e.status and isinstance(e.status, list) and not e.status[0]:
1724 # If ps succeeded but grep failed, there were no processes with the
1725 # given name.
1726 return procs_pids
1727 else:
1728 raise
1729
1730 for line in ps_output:
1731 try:
1732 ps_data = line.split()
1733 if process_name in ps_data[-1]:
1734 pid, process = ps_data[1], ps_data[-1]
1735 procs_pids[process].append(pid)
1736 except IndexError:
1737 pass
1738 return procs_pids
1739
1740 @decorators.WithTimeoutAndRetriesFromInstance()
1741 def TakeScreenshot(self, host_path=None, timeout=None, retries=None):
1742 """Takes a screenshot of the device.
1743
1744 Args:
1745 host_path: A string containing the path on the host to save the
1746 screenshot to. If None, a file name in the current
1747 directory will be generated.
1748 timeout: timeout in seconds
1749 retries: number of retries
1750
1751 Returns:
1752 The name of the file on the host to which the screenshot was saved.
1753
1754 Raises:
1755 CommandFailedError on failure.
1756 CommandTimeoutError on timeout.
1757 DeviceUnreachableError on missing device.
1758 """
1759 if not host_path:
1760 host_path = os.path.abspath('screenshot-%s.png' % _GetTimeStamp())
1761 with device_temp_file.DeviceTempFile(self.adb, suffix='.png') as device_tmp:
1762 self.RunShellCommand(['/system/bin/screencap', '-p', device_tmp.name],
1763 check_return=True)
1764 self.PullFile(device_tmp.name, host_path)
1765 return host_path
1766
1767 @decorators.WithTimeoutAndRetriesFromInstance()
1768 def GetMemoryUsageForPid(self, pid, timeout=None, retries=None):
1769 """Gets the memory usage for the given PID.
1770
1771 Args:
1772 pid: PID of the process.
1773 timeout: timeout in seconds
1774 retries: number of retries
1775
1776 Returns:
1777 A dict containing memory usage statistics for the PID. May include:
1778 Size, Rss, Pss, Shared_Clean, Shared_Dirty, Private_Clean,
1779 Private_Dirty, VmHWM
1780
1781 Raises:
1782 CommandTimeoutError on timeout.
1783 """
1784 result = collections.defaultdict(int)
1785
1786 try:
1787 result.update(self._GetMemoryUsageForPidFromSmaps(pid))
1788 except device_errors.CommandFailedError:
1789 logging.exception('Error getting memory usage from smaps')
1790
1791 try:
1792 result.update(self._GetMemoryUsageForPidFromStatus(pid))
1793 except device_errors.CommandFailedError:
1794 logging.exception('Error getting memory usage from status')
1795
1796 return result
1797
1798 @decorators.WithTimeoutAndRetriesFromInstance()
1799 def DismissCrashDialogIfNeeded(self, timeout=None, retries=None):
1800 """Dismiss the error/ANR dialog if present.
1801
1802 Returns: Name of the crashed package if a dialog is focused,
1803 None otherwise.
1804 """
1805 def _FindFocusedWindow():
1806 match = None
1807 # TODO(jbudorick): Try to grep the output on the device instead of using
1808 # large_output if/when DeviceUtils exposes a public interface for piped
1809 # shell command handling.
1810 for line in self.RunShellCommand(['dumpsys', 'window', 'windows'],
1811 check_return=True, large_output=True):
1812 match = re.match(_CURRENT_FOCUS_CRASH_RE, line)
1813 if match:
1814 break
1815 return match
1816
1817 match = _FindFocusedWindow()
1818 if not match:
1819 return None
1820 package = match.group(2)
1821 logging.warning('Trying to dismiss %s dialog for %s' % match.groups())
1822 self.SendKeyEvent(keyevent.KEYCODE_DPAD_RIGHT)
1823 self.SendKeyEvent(keyevent.KEYCODE_DPAD_RIGHT)
1824 self.SendKeyEvent(keyevent.KEYCODE_ENTER)
1825 match = _FindFocusedWindow()
1826 if match:
1827 logging.error('Still showing a %s dialog for %s' % match.groups())
1828 return package
1829
1830 def _GetMemoryUsageForPidFromSmaps(self, pid):
1831 SMAPS_COLUMNS = (
1832 'Size', 'Rss', 'Pss', 'Shared_Clean', 'Shared_Dirty', 'Private_Clean',
1833 'Private_Dirty')
1834
1835 showmap_out = self._RunPipedShellCommand(
1836 'showmap %d | grep TOTAL' % int(pid), as_root=True)
1837
1838 split_totals = showmap_out[-1].split()
1839 if (not split_totals
1840 or len(split_totals) != 9
1841 or split_totals[-1] != 'TOTAL'):
1842 raise device_errors.CommandFailedError(
1843 'Invalid output from showmap: %s' % '\n'.join(showmap_out))
1844
1845 return dict(itertools.izip(SMAPS_COLUMNS, (int(n) for n in split_totals)))
1846
1847 def _GetMemoryUsageForPidFromStatus(self, pid):
1848 for line in self.ReadFile(
1849 '/proc/%s/status' % str(pid), as_root=True).splitlines():
1850 if line.startswith('VmHWM:'):
1851 return {'VmHWM': int(line.split()[1])}
1852 else:
1853 raise device_errors.CommandFailedError(
1854 'Could not find memory peak value for pid %s', str(pid))
1855
1856 @decorators.WithTimeoutAndRetriesFromInstance()
1857 def GetLogcatMonitor(self, timeout=None, retries=None, *args, **kwargs):
1858 """Returns a new LogcatMonitor associated with this device.
1859
1860 Parameters passed to this function are passed directly to
1861 |logcat_monitor.LogcatMonitor| and are documented there.
1862
1863 Args:
1864 timeout: timeout in seconds
1865 retries: number of retries
1866 """
1867 return logcat_monitor.LogcatMonitor(self.adb, *args, **kwargs)
1868
1869 def GetClientCache(self, client_name):
1870 """Returns client cache."""
1871 if client_name not in self._client_caches:
1872 self._client_caches[client_name] = {}
1873 return self._client_caches[client_name]
1874
1875 def _ClearCache(self):
1876 """Clears all caches."""
1877 for client in self._client_caches:
1878 self._client_caches[client].clear()
1879 self._cache = {
1880 # Map of packageId -> list of on-device .apk paths
1881 'package_apk_paths': {},
1882 # Map of packageId -> set of on-device .apk checksums
1883 'package_apk_checksums': {},
1884 }
1885
1886 @classmethod
1887 def parallel(cls, devices, async=False):
1888 """Creates a Parallelizer to operate over the provided list of devices.
1889
1890 If |devices| is either |None| or an empty list, the Parallelizer will
1891 operate over all attached devices that have not been blacklisted.
1892
1893 Args:
1894 devices: A list of either DeviceUtils instances or objects from
1895 from which DeviceUtils instances can be constructed. If None,
1896 all attached devices will be used.
1897 async: If true, returns a Parallelizer that runs operations
1898 asynchronously.
1899
1900 Returns:
1901 A Parallelizer operating over |devices|.
1902 """
1903 if not devices:
1904 raise device_errors.NoDevicesError()
1905
1906 devices = [d if isinstance(d, cls) else cls(d) for d in devices]
1907 if async:
1908 return parallelizer.Parallelizer(devices)
1909 else:
1910 return parallelizer.SyncParallelizer(devices)
1911
1912 @classmethod
1913 def HealthyDevices(cls, blacklist=None):
1914 if not blacklist:
1915 # TODO(jbudorick): Remove once clients pass in the blacklist.
1916 blacklist = device_blacklist.Blacklist(device_blacklist.BLACKLIST_JSON)
1917
1918 blacklisted_devices = blacklist.Read()
1919 def blacklisted(adb):
1920 if adb.GetDeviceSerial() in blacklisted_devices:
1921 logging.warning('Device %s is blacklisted.', adb.GetDeviceSerial())
1922 return True
1923 return False
1924
1925 return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices()
1926 if not blacklisted(adb)]
1927
1928 @decorators.WithTimeoutAndRetriesFromInstance()
1929 def RestartAdbd(self, timeout=None, retries=None):
1930 logging.info('Restarting adbd on device.')
1931 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script:
1932 self.WriteFile(script.name, _RESTART_ADBD_SCRIPT)
1933 self.RunShellCommand(['source', script.name], as_root=True)
1934 self.adb.WaitForDevice()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698