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

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

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