OLD | NEW |
1 # Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 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 """Provides a variety of device interactions based on adb. |
6 | 6 |
7 Eventually, this will be based on adb_wrapper. | 7 Eventually, this will be based on adb_wrapper. |
8 """ | 8 """ |
9 # pylint: disable=unused-argument | 9 # pylint: disable=unused-argument |
10 | 10 |
11 import collections | 11 import collections |
12 import contextlib | 12 import contextlib |
13 import itertools | 13 import itertools |
14 import logging | 14 import logging |
15 import multiprocessing | 15 import multiprocessing |
16 import os | 16 import os |
17 import posixpath | 17 import posixpath |
18 import re | 18 import re |
19 import shutil | 19 import shutil |
20 import sys | 20 import sys |
21 import tempfile | 21 import tempfile |
22 import time | 22 import time |
23 import zipfile | 23 import zipfile |
24 | 24 |
25 import pylib.android_commands | 25 import pylib.android_commands |
26 from pylib import cmd_helper | 26 from pylib import cmd_helper |
27 from pylib import constants | 27 from pylib import constants |
| 28 from pylib import device_signal |
28 from pylib.device import adb_wrapper | 29 from pylib.device import adb_wrapper |
29 from pylib.device import decorators | 30 from pylib.device import decorators |
| 31 from pylib.device import device_blacklist |
30 from pylib.device import device_errors | 32 from pylib.device import device_errors |
31 from pylib.device import intent | 33 from pylib.device import intent |
32 from pylib.device import logcat_monitor | 34 from pylib.device import logcat_monitor |
33 from pylib.device.commands import install_commands | 35 from pylib.device.commands import install_commands |
34 from pylib.utils import apk_helper | 36 from pylib.utils import apk_helper |
35 from pylib.utils import base_error | 37 from pylib.utils import base_error |
36 from pylib.utils import device_temp_file | 38 from pylib.utils import device_temp_file |
37 from pylib.utils import host_utils | 39 from pylib.utils import host_utils |
38 from pylib.utils import md5sum | 40 from pylib.utils import md5sum |
39 from pylib.utils import parallelizer | 41 from pylib.utils import parallelizer |
(...skipping 25 matching lines...) Expand all Loading... |
65 'enable_command': ( | 67 'enable_command': ( |
66 'echo 0x4A > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && ' | 68 'echo 0x4A > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && ' |
67 'echo 1 > /sys/class/power_supply/usb/online'), | 69 'echo 1 > /sys/class/power_supply/usb/online'), |
68 'disable_command': ( | 70 'disable_command': ( |
69 'echo 0xCA > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && ' | 71 'echo 0xCA > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && ' |
70 'chmod 644 /sys/class/power_supply/usb/online && ' | 72 'chmod 644 /sys/class/power_supply/usb/online && ' |
71 'echo 0 > /sys/class/power_supply/usb/online'), | 73 'echo 0 > /sys/class/power_supply/usb/online'), |
72 }, | 74 }, |
73 ] | 75 ] |
74 | 76 |
| 77 |
75 @decorators.WithExplicitTimeoutAndRetries( | 78 @decorators.WithExplicitTimeoutAndRetries( |
76 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES) | 79 _DEFAULT_TIMEOUT, _DEFAULT_RETRIES) |
77 def GetAVDs(): | 80 def GetAVDs(): |
78 """Returns a list of Android Virtual Devices. | 81 """Returns a list of Android Virtual Devices. |
79 | 82 |
80 Returns: | 83 Returns: |
81 A list containing the configured AVDs. | 84 A list containing the configured AVDs. |
82 """ | 85 """ |
83 lines = cmd_helper.GetCmdOutput([ | 86 lines = cmd_helper.GetCmdOutput([ |
84 os.path.join(constants.ANDROID_SDK_ROOT, 'tools', 'android'), | 87 os.path.join(constants.ANDROID_SDK_ROOT, 'tools', 'android'), |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
161 self.old_interface = pylib.android_commands.AndroidCommands(str(device)) | 164 self.old_interface = pylib.android_commands.AndroidCommands(str(device)) |
162 elif isinstance(device, pylib.android_commands.AndroidCommands): | 165 elif isinstance(device, pylib.android_commands.AndroidCommands): |
163 self.adb = adb_wrapper.AdbWrapper(device.GetDevice()) | 166 self.adb = adb_wrapper.AdbWrapper(device.GetDevice()) |
164 self.old_interface = device | 167 self.old_interface = device |
165 else: | 168 else: |
166 raise ValueError('Unsupported device value: %r' % device) | 169 raise ValueError('Unsupported device value: %r' % device) |
167 self._commands_installed = None | 170 self._commands_installed = None |
168 self._default_timeout = default_timeout | 171 self._default_timeout = default_timeout |
169 self._default_retries = default_retries | 172 self._default_retries = default_retries |
170 self._cache = {} | 173 self._cache = {} |
| 174 self._client_caches = {} |
171 assert hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR) | 175 assert hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR) |
172 assert hasattr(self, decorators.DEFAULT_RETRIES_ATTR) | 176 assert hasattr(self, decorators.DEFAULT_RETRIES_ATTR) |
173 | 177 |
174 def __str__(self): | 178 def __str__(self): |
175 """Returns the device serial.""" | 179 """Returns the device serial.""" |
176 return self.adb.GetDeviceSerial() | 180 return self.adb.GetDeviceSerial() |
177 | 181 |
178 @decorators.WithTimeoutAndRetriesFromInstance() | 182 @decorators.WithTimeoutAndRetriesFromInstance() |
179 def IsOnline(self, timeout=None, retries=None): | 183 def IsOnline(self, timeout=None, retries=None): |
180 """Checks whether the device is online. | 184 """Checks whether the device is online. |
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
399 retries: number of retries | 403 retries: number of retries |
400 | 404 |
401 Raises: | 405 Raises: |
402 CommandTimeoutError on timeout. | 406 CommandTimeoutError on timeout. |
403 DeviceUnreachableError on missing device. | 407 DeviceUnreachableError on missing device. |
404 """ | 408 """ |
405 def device_offline(): | 409 def device_offline(): |
406 return not self.IsOnline() | 410 return not self.IsOnline() |
407 | 411 |
408 self.adb.Reboot() | 412 self.adb.Reboot() |
409 self._cache = {} | 413 self._ClearCache() |
410 timeout_retry.WaitFor(device_offline, wait_period=1) | 414 timeout_retry.WaitFor(device_offline, wait_period=1) |
411 if block: | 415 if block: |
412 self.WaitUntilFullyBooted(wifi=wifi) | 416 self.WaitUntilFullyBooted(wifi=wifi) |
413 | 417 |
414 INSTALL_DEFAULT_TIMEOUT = 4 * _DEFAULT_TIMEOUT | 418 INSTALL_DEFAULT_TIMEOUT = 4 * _DEFAULT_TIMEOUT |
415 INSTALL_DEFAULT_RETRIES = _DEFAULT_RETRIES | 419 INSTALL_DEFAULT_RETRIES = _DEFAULT_RETRIES |
416 | 420 |
417 @decorators.WithTimeoutAndRetriesDefaults( | 421 @decorators.WithTimeoutAndRetriesDefaults( |
418 INSTALL_DEFAULT_TIMEOUT, | 422 INSTALL_DEFAULT_TIMEOUT, |
419 INSTALL_DEFAULT_RETRIES) | 423 INSTALL_DEFAULT_RETRIES) |
(...skipping 19 matching lines...) Expand all Loading... |
439 should_install = bool(self._GetChangedFilesImpl(apk_path, device_path)) | 443 should_install = bool(self._GetChangedFilesImpl(apk_path, device_path)) |
440 if should_install and not reinstall: | 444 if should_install and not reinstall: |
441 self.adb.Uninstall(package_name) | 445 self.adb.Uninstall(package_name) |
442 else: | 446 else: |
443 should_install = True | 447 should_install = True |
444 if should_install: | 448 if should_install: |
445 self.adb.Install(apk_path, reinstall=reinstall) | 449 self.adb.Install(apk_path, reinstall=reinstall) |
446 | 450 |
447 @decorators.WithTimeoutAndRetriesFromInstance() | 451 @decorators.WithTimeoutAndRetriesFromInstance() |
448 def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None, | 452 def RunShellCommand(self, cmd, check_return=False, cwd=None, env=None, |
449 as_root=False, single_line=False, timeout=None, | 453 as_root=False, single_line=False, large_output=False, |
450 retries=None): | 454 timeout=None, retries=None): |
451 """Run an ADB shell command. | 455 """Run an ADB shell command. |
452 | 456 |
453 The command to run |cmd| should be a sequence of program arguments or else | 457 The command to run |cmd| should be a sequence of program arguments or else |
454 a single string. | 458 a single string. |
455 | 459 |
456 When |cmd| is a sequence, it is assumed to contain the name of the command | 460 When |cmd| is a sequence, it is assumed to contain the name of the command |
457 to run followed by its arguments. In this case, arguments are passed to the | 461 to run followed by its arguments. In this case, arguments are passed to the |
458 command exactly as given, without any further processing by the shell. This | 462 command exactly as given, without any further processing by the shell. This |
459 allows to easily pass arguments containing spaces or special characters | 463 allows to easily pass arguments containing spaces or special characters |
460 without having to worry about getting quoting right. Whenever possible, it | 464 without having to worry about getting quoting right. Whenever possible, it |
(...skipping 12 matching lines...) Expand all Loading... |
473 cmd: A string with the full command to run on the device, or a sequence | 477 cmd: A string with the full command to run on the device, or a sequence |
474 containing the command and its arguments. | 478 containing the command and its arguments. |
475 check_return: A boolean indicating whether or not the return code should | 479 check_return: A boolean indicating whether or not the return code should |
476 be checked. | 480 be checked. |
477 cwd: The device directory in which the command should be run. | 481 cwd: The device directory in which the command should be run. |
478 env: The environment variables with which the command should be run. | 482 env: The environment variables with which the command should be run. |
479 as_root: A boolean indicating whether the shell command should be run | 483 as_root: A boolean indicating whether the shell command should be run |
480 with root privileges. | 484 with root privileges. |
481 single_line: A boolean indicating if only a single line of output is | 485 single_line: A boolean indicating if only a single line of output is |
482 expected. | 486 expected. |
| 487 large_output: Uses a work-around for large shell command output. Without |
| 488 this large output will be truncated. |
483 timeout: timeout in seconds | 489 timeout: timeout in seconds |
484 retries: number of retries | 490 retries: number of retries |
485 | 491 |
486 Returns: | 492 Returns: |
487 If single_line is False, the output of the command as a list of lines, | 493 If single_line is False, the output of the command as a list of lines, |
488 otherwise, a string with the unique line of output emmited by the command | 494 otherwise, a string with the unique line of output emmited by the command |
489 (with the optional newline at the end stripped). | 495 (with the optional newline at the end stripped). |
490 | 496 |
491 Raises: | 497 Raises: |
492 AdbCommandFailedError if check_return is True and the exit code of | 498 AdbCommandFailedError if check_return is True and the exit code of |
493 the command run on the device is non-zero. | 499 the command run on the device is non-zero. |
494 CommandFailedError if single_line is True but the output contains two or | 500 CommandFailedError if single_line is True but the output contains two or |
495 more lines. | 501 more lines. |
496 CommandTimeoutError on timeout. | 502 CommandTimeoutError on timeout. |
497 DeviceUnreachableError on missing device. | 503 DeviceUnreachableError on missing device. |
498 """ | 504 """ |
499 def env_quote(key, value): | 505 def env_quote(key, value): |
500 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key): | 506 if not DeviceUtils._VALID_SHELL_VARIABLE.match(key): |
501 raise KeyError('Invalid shell variable name %r' % key) | 507 raise KeyError('Invalid shell variable name %r' % key) |
502 # using double quotes here to allow interpolation of shell variables | 508 # using double quotes here to allow interpolation of shell variables |
503 return '%s=%s' % (key, cmd_helper.DoubleQuote(value)) | 509 return '%s=%s' % (key, cmd_helper.DoubleQuote(value)) |
504 | 510 |
505 def do_run(cmd): | 511 def run(cmd): |
| 512 return self.adb.Shell(cmd) |
| 513 |
| 514 def handle_check_return(cmd): |
506 try: | 515 try: |
507 return self.adb.Shell(cmd) | 516 return run(cmd) |
508 except device_errors.AdbCommandFailedError as exc: | 517 except device_errors.AdbCommandFailedError as exc: |
509 if check_return: | 518 if check_return: |
510 raise | 519 raise |
511 else: | 520 else: |
512 return exc.output | 521 return exc.output |
513 | 522 |
| 523 def handle_large_command(cmd): |
| 524 if len(cmd) < self._MAX_ADB_COMMAND_LENGTH: |
| 525 return handle_check_return(cmd) |
| 526 else: |
| 527 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script: |
| 528 self._WriteFileWithPush(script.name, cmd) |
| 529 logging.info('Large shell command will be run from file: %s ...', |
| 530 cmd[:100]) |
| 531 return handle_check_return('sh %s' % script.name_quoted) |
| 532 |
| 533 def handle_large_output(cmd, large_output_mode): |
| 534 if large_output_mode: |
| 535 with device_temp_file.DeviceTempFile(self.adb) as large_output_file: |
| 536 cmd = '%s > %s' % (cmd, large_output_file.name) |
| 537 logging.info('Large output mode enabled. Will write output to device ' |
| 538 ' and read results from file.') |
| 539 handle_large_command(cmd) |
| 540 return self.ReadFile(large_output_file.name) |
| 541 else: |
| 542 try: |
| 543 return handle_large_command(cmd) |
| 544 except device_errors.AdbCommandFailedError as exc: |
| 545 if exc.status is None: |
| 546 logging.exception('No output found for %s', cmd) |
| 547 logging.warning('Attempting to run in large_output mode.') |
| 548 logging.warning('Use RunShellCommand(..., large_output=True) for ' |
| 549 'shell commands that expect a lot of output.') |
| 550 return handle_large_output(cmd, True) |
| 551 else: |
| 552 raise |
| 553 |
514 if not isinstance(cmd, basestring): | 554 if not isinstance(cmd, basestring): |
515 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd) | 555 cmd = ' '.join(cmd_helper.SingleQuote(s) for s in cmd) |
516 if env: | 556 if env: |
517 env = ' '.join(env_quote(k, v) for k, v in env.iteritems()) | 557 env = ' '.join(env_quote(k, v) for k, v in env.iteritems()) |
518 cmd = '%s %s' % (env, cmd) | 558 cmd = '%s %s' % (env, cmd) |
519 if cwd: | 559 if cwd: |
520 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd) | 560 cmd = 'cd %s && %s' % (cmd_helper.SingleQuote(cwd), cmd) |
521 if as_root and self.NeedsSU(): | 561 if as_root and self.NeedsSU(): |
522 # "su -c sh -c" allows using shell features in |cmd| | 562 # "su -c sh -c" allows using shell features in |cmd| |
523 cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd) | 563 cmd = 'su -c sh -c %s' % cmd_helper.SingleQuote(cmd) |
524 if timeout is None: | |
525 timeout = self._default_timeout | |
526 | 564 |
527 if len(cmd) < self._MAX_ADB_COMMAND_LENGTH: | 565 output = handle_large_output(cmd, large_output).splitlines() |
528 output = do_run(cmd) | |
529 else: | |
530 with device_temp_file.DeviceTempFile(self.adb, suffix='.sh') as script: | |
531 self._WriteFileWithPush(script.name, cmd) | |
532 logging.info('Large shell command will be run from file: %s ...', | |
533 cmd[:100]) | |
534 output = do_run('sh %s' % script.name_quoted) | |
535 | 566 |
536 output = output.splitlines() | |
537 if single_line: | 567 if single_line: |
538 if not output: | 568 if not output: |
539 return '' | 569 return '' |
540 elif len(output) == 1: | 570 elif len(output) == 1: |
541 return output[0] | 571 return output[0] |
542 else: | 572 else: |
543 msg = 'one line of output was expected, but got: %s' | 573 msg = 'one line of output was expected, but got: %s' |
544 raise device_errors.CommandFailedError(msg % output, str(self)) | 574 raise device_errors.CommandFailedError(msg % output, str(self)) |
545 else: | 575 else: |
546 return output | 576 return output |
547 | 577 |
| 578 def _RunPipedShellCommand(self, script, **kwargs): |
| 579 PIPESTATUS_LEADER = 'PIPESTATUS: ' |
| 580 |
| 581 script += '; echo "%s${PIPESTATUS[@]}"' % PIPESTATUS_LEADER |
| 582 kwargs['check_return'] = True |
| 583 output = self.RunShellCommand(script, **kwargs) |
| 584 pipestatus_line = output[-1] |
| 585 |
| 586 if not pipestatus_line.startswith(PIPESTATUS_LEADER): |
| 587 logging.error('Pipe exit statuses of shell script missing.') |
| 588 raise device_errors.AdbShellCommandFailedError( |
| 589 script, output, status=None, |
| 590 device_serial=self.adb.GetDeviceSerial()) |
| 591 |
| 592 output = output[:-1] |
| 593 statuses = [ |
| 594 int(s) for s in pipestatus_line[len(PIPESTATUS_LEADER):].split()] |
| 595 if any(statuses): |
| 596 raise device_errors.AdbShellCommandFailedError( |
| 597 script, output, status=statuses, |
| 598 device_serial=self.adb.GetDeviceSerial()) |
| 599 return output |
| 600 |
548 @decorators.WithTimeoutAndRetriesFromInstance() | 601 @decorators.WithTimeoutAndRetriesFromInstance() |
549 def KillAll(self, process_name, signum=9, as_root=False, blocking=False, | 602 def KillAll(self, process_name, signum=device_signal.SIGKILL, as_root=False, |
550 timeout=None, retries=None): | 603 blocking=False, quiet=False, timeout=None, retries=None): |
551 """Kill all processes with the given name on the device. | 604 """Kill all processes with the given name on the device. |
552 | 605 |
553 Args: | 606 Args: |
554 process_name: A string containing the name of the process to kill. | 607 process_name: A string containing the name of the process to kill. |
555 signum: An integer containing the signal number to send to kill. Defaults | 608 signum: An integer containing the signal number to send to kill. Defaults |
556 to 9 (SIGKILL). | 609 to SIGKILL (9). |
557 as_root: A boolean indicating whether the kill should be executed with | 610 as_root: A boolean indicating whether the kill should be executed with |
558 root privileges. | 611 root privileges. |
559 blocking: A boolean indicating whether we should wait until all processes | 612 blocking: A boolean indicating whether we should wait until all processes |
560 with the given |process_name| are dead. | 613 with the given |process_name| are dead. |
| 614 quiet: A boolean indicating whether to ignore the fact that no processes |
| 615 to kill were found. |
561 timeout: timeout in seconds | 616 timeout: timeout in seconds |
562 retries: number of retries | 617 retries: number of retries |
563 | 618 |
| 619 Returns: |
| 620 The number of processes attempted to kill. |
| 621 |
564 Raises: | 622 Raises: |
565 CommandFailedError if no process was killed. | 623 CommandFailedError if no process was killed and |quiet| is False. |
566 CommandTimeoutError on timeout. | 624 CommandTimeoutError on timeout. |
567 DeviceUnreachableError on missing device. | 625 DeviceUnreachableError on missing device. |
568 """ | 626 """ |
569 pids = self._GetPidsImpl(process_name) | 627 pids = self.GetPids(process_name) |
570 if not pids: | 628 if not pids: |
571 raise device_errors.CommandFailedError( | 629 if quiet: |
572 'No process "%s"' % process_name, str(self)) | 630 return 0 |
| 631 else: |
| 632 raise device_errors.CommandFailedError( |
| 633 'No process "%s"' % process_name, str(self)) |
573 | 634 |
574 cmd = ['kill', '-%d' % signum] + pids.values() | 635 cmd = ['kill', '-%d' % signum] + pids.values() |
575 self.RunShellCommand(cmd, as_root=as_root, check_return=True) | 636 self.RunShellCommand(cmd, as_root=as_root, check_return=True) |
576 | 637 |
577 if blocking: | 638 if blocking: |
| 639 # TODO(perezu): use timeout_retry.WaitFor |
578 wait_period = 0.1 | 640 wait_period = 0.1 |
579 while self._GetPidsImpl(process_name): | 641 while self.GetPids(process_name): |
580 time.sleep(wait_period) | 642 time.sleep(wait_period) |
581 | 643 |
582 return len(pids) | 644 return len(pids) |
583 | 645 |
584 @decorators.WithTimeoutAndRetriesFromInstance() | 646 @decorators.WithTimeoutAndRetriesFromInstance() |
585 def StartActivity(self, intent_obj, blocking=False, trace_file_name=None, | 647 def StartActivity(self, intent_obj, blocking=False, trace_file_name=None, |
586 force_stop=False, timeout=None, retries=None): | 648 force_stop=False, timeout=None, retries=None): |
587 """Start package's activity on the device. | 649 """Start package's activity on the device. |
588 | 650 |
589 Args: | 651 Args: |
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
784 def _GetChangedFilesImpl(self, host_path, device_path): | 846 def _GetChangedFilesImpl(self, host_path, device_path): |
785 real_host_path = os.path.realpath(host_path) | 847 real_host_path = os.path.realpath(host_path) |
786 try: | 848 try: |
787 real_device_path = self.RunShellCommand( | 849 real_device_path = self.RunShellCommand( |
788 ['realpath', device_path], single_line=True, check_return=True) | 850 ['realpath', device_path], single_line=True, check_return=True) |
789 except device_errors.CommandFailedError: | 851 except device_errors.CommandFailedError: |
790 real_device_path = None | 852 real_device_path = None |
791 if not real_device_path: | 853 if not real_device_path: |
792 return [(host_path, device_path)] | 854 return [(host_path, device_path)] |
793 | 855 |
794 host_hash_tuples = md5sum.CalculateHostMd5Sums([real_host_path]) | 856 host_checksums = md5sum.CalculateHostMd5Sums([real_host_path]) |
795 device_paths_to_md5 = ( | 857 device_paths_to_md5 = ( |
796 real_device_path if os.path.isfile(real_host_path) | 858 real_device_path if os.path.isfile(real_host_path) |
797 else ('%s/%s' % (real_device_path, os.path.relpath(p, real_host_path)) | 859 else ('%s/%s' % (real_device_path, os.path.relpath(p, real_host_path)) |
798 for _, p in host_hash_tuples)) | 860 for p in host_checksums.iterkeys())) |
799 device_hash_tuples = md5sum.CalculateDeviceMd5Sums( | 861 device_checksums = md5sum.CalculateDeviceMd5Sums( |
800 device_paths_to_md5, self) | 862 device_paths_to_md5, self) |
801 | 863 |
802 if os.path.isfile(host_path): | 864 if os.path.isfile(host_path): |
803 if (not device_hash_tuples | 865 host_checksum = host_checksums.get(real_host_path) |
804 or device_hash_tuples[0].hash != host_hash_tuples[0].hash): | 866 device_checksum = device_checksums.get(real_device_path) |
| 867 if host_checksum != device_checksum: |
805 return [(host_path, device_path)] | 868 return [(host_path, device_path)] |
806 else: | 869 else: |
807 return [] | 870 return [] |
808 else: | 871 else: |
809 device_tuple_dict = dict((d.path, d.hash) for d in device_hash_tuples) | |
810 to_push = [] | 872 to_push = [] |
811 for host_hash, host_abs_path in ( | 873 for host_abs_path, host_checksum in host_checksums.iteritems(): |
812 (h.hash, h.path) for h in host_hash_tuples): | |
813 device_abs_path = '%s/%s' % ( | 874 device_abs_path = '%s/%s' % ( |
814 real_device_path, os.path.relpath(host_abs_path, real_host_path)) | 875 real_device_path, os.path.relpath(host_abs_path, real_host_path)) |
815 if (device_abs_path not in device_tuple_dict | 876 if (device_checksums.get(device_abs_path) != host_checksum): |
816 or device_tuple_dict[device_abs_path] != host_hash): | |
817 to_push.append((host_abs_path, device_abs_path)) | 877 to_push.append((host_abs_path, device_abs_path)) |
818 return to_push | 878 return to_push |
819 | 879 |
820 def _InstallCommands(self): | 880 def _InstallCommands(self): |
821 if self._commands_installed is None: | 881 if self._commands_installed is None: |
822 try: | 882 try: |
823 if not install_commands.Installed(self): | 883 if not install_commands.Installed(self): |
824 install_commands.InstallCommands(self) | 884 install_commands.InstallCommands(self) |
825 self._commands_installed = True | 885 self._commands_installed = True |
826 except Exception as e: | 886 except Exception as e: |
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
984 ls_out = self.RunShellCommand(['ls', '-l', device_path], as_root=as_root, | 1044 ls_out = self.RunShellCommand(['ls', '-l', device_path], as_root=as_root, |
985 check_return=True) | 1045 check_return=True) |
986 for line in ls_out: | 1046 for line in ls_out: |
987 m = self._LS_RE.match(line) | 1047 m = self._LS_RE.match(line) |
988 if m and m.group('name') == posixpath.basename(device_path): | 1048 if m and m.group('name') == posixpath.basename(device_path): |
989 size = int(m.group('size')) | 1049 size = int(m.group('size')) |
990 break | 1050 break |
991 else: | 1051 else: |
992 logging.warning('Could not determine size of %s.', device_path) | 1052 logging.warning('Could not determine size of %s.', device_path) |
993 | 1053 |
994 if size is None or size <= self._MAX_ADB_OUTPUT_LENGTH: | 1054 if 0 < size <= self._MAX_ADB_OUTPUT_LENGTH: |
995 return _JoinLines(self.RunShellCommand( | 1055 return _JoinLines(self.RunShellCommand( |
996 ['cat', device_path], as_root=as_root, check_return=True)) | 1056 ['cat', device_path], as_root=as_root, check_return=True)) |
997 elif as_root and self.NeedsSU(): | 1057 elif as_root and self.NeedsSU(): |
998 with device_temp_file.DeviceTempFile(self.adb) as device_temp: | 1058 with device_temp_file.DeviceTempFile(self.adb) as device_temp: |
999 self.RunShellCommand(['cp', device_path, device_temp.name], | 1059 self.RunShellCommand(['cp', device_path, device_temp.name], |
1000 as_root=True, check_return=True) | 1060 as_root=True, check_return=True) |
1001 return self._ReadFileWithPull(device_temp.name) | 1061 return self._ReadFileWithPull(device_temp.name) |
1002 else: | 1062 else: |
1003 return self._ReadFileWithPull(device_path) | 1063 return self._ReadFileWithPull(device_path) |
1004 | 1064 |
(...skipping 314 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1319 retries: number of retries | 1379 retries: number of retries |
1320 | 1380 |
1321 Returns: | 1381 Returns: |
1322 A dict mapping process name to PID for each process that contained the | 1382 A dict mapping process name to PID for each process that contained the |
1323 provided |process_name|. | 1383 provided |process_name|. |
1324 | 1384 |
1325 Raises: | 1385 Raises: |
1326 CommandTimeoutError on timeout. | 1386 CommandTimeoutError on timeout. |
1327 DeviceUnreachableError on missing device. | 1387 DeviceUnreachableError on missing device. |
1328 """ | 1388 """ |
1329 return self._GetPidsImpl(process_name) | 1389 procs_pids = {} |
| 1390 try: |
| 1391 ps_output = self._RunPipedShellCommand( |
| 1392 'ps | grep -F %s' % cmd_helper.SingleQuote(process_name)) |
| 1393 except device_errors.AdbShellCommandFailedError as e: |
| 1394 if e.status and isinstance(e.status, list) and not e.status[0]: |
| 1395 # If ps succeeded but grep failed, there were no processes with the |
| 1396 # given name. |
| 1397 return procs_pids |
| 1398 else: |
| 1399 raise |
1330 | 1400 |
1331 def _GetPidsImpl(self, process_name): | 1401 for line in ps_output: |
1332 procs_pids = {} | |
1333 for line in self.RunShellCommand('ps', check_return=True): | |
1334 try: | 1402 try: |
1335 ps_data = line.split() | 1403 ps_data = line.split() |
1336 if process_name in ps_data[-1]: | 1404 if process_name in ps_data[-1]: |
1337 procs_pids[ps_data[-1]] = ps_data[1] | 1405 procs_pids[ps_data[-1]] = ps_data[1] |
1338 except IndexError: | 1406 except IndexError: |
1339 pass | 1407 pass |
1340 return procs_pids | 1408 return procs_pids |
1341 | 1409 |
1342 @decorators.WithTimeoutAndRetriesFromInstance() | 1410 @decorators.WithTimeoutAndRetriesFromInstance() |
1343 def TakeScreenshot(self, host_path=None, timeout=None, retries=None): | 1411 def TakeScreenshot(self, host_path=None, timeout=None, retries=None): |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1395 except device_errors.CommandFailedError: | 1463 except device_errors.CommandFailedError: |
1396 logging.exception('Error getting memory usage from status') | 1464 logging.exception('Error getting memory usage from status') |
1397 | 1465 |
1398 return result | 1466 return result |
1399 | 1467 |
1400 def _GetMemoryUsageForPidFromSmaps(self, pid): | 1468 def _GetMemoryUsageForPidFromSmaps(self, pid): |
1401 SMAPS_COLUMNS = ( | 1469 SMAPS_COLUMNS = ( |
1402 'Size', 'Rss', 'Pss', 'Shared_Clean', 'Shared_Dirty', 'Private_Clean', | 1470 'Size', 'Rss', 'Pss', 'Shared_Clean', 'Shared_Dirty', 'Private_Clean', |
1403 'Private_Dirty') | 1471 'Private_Dirty') |
1404 | 1472 |
1405 showmap_out = self.RunShellCommand( | 1473 showmap_out = self._RunPipedShellCommand( |
1406 ['showmap', str(pid)], as_root=True, check_return=True) | 1474 'showmap %d | grep TOTAL' % int(pid), as_root=True) |
1407 if not showmap_out: | |
1408 raise device_errors.CommandFailedError('No output from showmap') | |
1409 | 1475 |
1410 split_totals = showmap_out[-1].split() | 1476 split_totals = showmap_out[-1].split() |
1411 if (not split_totals | 1477 if (not split_totals |
1412 or len(split_totals) != 9 | 1478 or len(split_totals) != 9 |
1413 or split_totals[-1] != 'TOTAL'): | 1479 or split_totals[-1] != 'TOTAL'): |
1414 raise device_errors.CommandFailedError( | 1480 raise device_errors.CommandFailedError( |
1415 'Invalid output from showmap: %s' % '\n'.join(showmap_out)) | 1481 'Invalid output from showmap: %s' % '\n'.join(showmap_out)) |
1416 | 1482 |
1417 return dict(itertools.izip(SMAPS_COLUMNS, (int(n) for n in split_totals))) | 1483 return dict(itertools.izip(SMAPS_COLUMNS, (int(n) for n in split_totals))) |
1418 | 1484 |
(...skipping 13 matching lines...) Expand all Loading... |
1432 Parameters passed to this function are passed directly to | 1498 Parameters passed to this function are passed directly to |
1433 |logcat_monitor.LogcatMonitor| and are documented there. | 1499 |logcat_monitor.LogcatMonitor| and are documented there. |
1434 | 1500 |
1435 Args: | 1501 Args: |
1436 timeout: timeout in seconds | 1502 timeout: timeout in seconds |
1437 retries: number of retries | 1503 retries: number of retries |
1438 """ | 1504 """ |
1439 return logcat_monitor.LogcatMonitor(self.adb, *args, **kwargs) | 1505 return logcat_monitor.LogcatMonitor(self.adb, *args, **kwargs) |
1440 | 1506 |
1441 @decorators.WithTimeoutAndRetriesFromInstance() | 1507 @decorators.WithTimeoutAndRetriesFromInstance() |
1442 def GetBatteryInfo(self, timeout=None, retries=None): | |
1443 """Gets battery info for the device. | |
1444 | |
1445 Args: | |
1446 timeout: timeout in seconds | |
1447 retries: number of retries | |
1448 Returns: | |
1449 A dict containing various battery information as reported by dumpsys | |
1450 battery. | |
1451 """ | |
1452 result = {} | |
1453 # Skip the first line, which is just a header. | |
1454 for line in self.RunShellCommand( | |
1455 ['dumpsys', 'battery'], check_return=True)[1:]: | |
1456 # If usb charging has been disabled, an extra line of header exists. | |
1457 if 'UPDATES STOPPED' in line: | |
1458 logging.warning('Dumpsys battery not receiving updates. ' | |
1459 'Run dumpsys battery reset if this is in error.') | |
1460 elif ':' not in line: | |
1461 logging.warning('Unknown line found in dumpsys battery.') | |
1462 logging.warning(line) | |
1463 else: | |
1464 k, v = line.split(': ', 1) | |
1465 result[k.strip()] = v.strip() | |
1466 return result | |
1467 | |
1468 @decorators.WithTimeoutAndRetriesFromInstance() | |
1469 def GetCharging(self, timeout=None, retries=None): | |
1470 """Gets the charging state of the device. | |
1471 | |
1472 Args: | |
1473 timeout: timeout in seconds | |
1474 retries: number of retries | |
1475 Returns: | |
1476 True if the device is charging, false otherwise. | |
1477 """ | |
1478 battery_info = self.GetBatteryInfo() | |
1479 for k in ('AC powered', 'USB powered', 'Wireless powered'): | |
1480 if (k in battery_info and | |
1481 battery_info[k].lower() in ('true', '1', 'yes')): | |
1482 return True | |
1483 return False | |
1484 | |
1485 @decorators.WithTimeoutAndRetriesFromInstance() | |
1486 def SetCharging(self, enabled, timeout=None, retries=None): | |
1487 """Enables or disables charging on the device. | |
1488 | |
1489 Args: | |
1490 enabled: A boolean indicating whether charging should be enabled or | |
1491 disabled. | |
1492 timeout: timeout in seconds | |
1493 retries: number of retries | |
1494 """ | |
1495 if 'charging_config' not in self._cache: | |
1496 for c in _CONTROL_CHARGING_COMMANDS: | |
1497 if self.FileExists(c['witness_file']): | |
1498 self._cache['charging_config'] = c | |
1499 break | |
1500 else: | |
1501 raise device_errors.CommandFailedError( | |
1502 'Unable to find charging commands.') | |
1503 | |
1504 if enabled: | |
1505 command = self._cache['charging_config']['enable_command'] | |
1506 else: | |
1507 command = self._cache['charging_config']['disable_command'] | |
1508 | |
1509 def set_and_verify_charging(): | |
1510 self.RunShellCommand(command, check_return=True) | |
1511 return self.GetCharging() == enabled | |
1512 | |
1513 timeout_retry.WaitFor(set_and_verify_charging, wait_period=1) | |
1514 | |
1515 # TODO(rnephew): Make private when all use cases can use the context manager. | |
1516 @decorators.WithTimeoutAndRetriesFromInstance() | |
1517 def DisableBatteryUpdates(self, timeout=None, retries=None): | |
1518 """ Resets battery data and makes device appear like it is not | |
1519 charging so that it will collect power data since last charge. | |
1520 | |
1521 Args: | |
1522 timeout: timeout in seconds | |
1523 retries: number of retries | |
1524 """ | |
1525 def battery_updates_disabled(): | |
1526 return self.GetCharging() is False | |
1527 | |
1528 self.RunShellCommand( | |
1529 ['dumpsys', 'batterystats', '--reset'], check_return=True) | |
1530 battery_data = self.RunShellCommand( | |
1531 ['dumpsys', 'batterystats', '--charged', '--checkin'], | |
1532 check_return=True) | |
1533 ROW_TYPE_INDEX = 3 | |
1534 PWI_POWER_INDEX = 5 | |
1535 for line in battery_data: | |
1536 l = line.split(',') | |
1537 if (len(l) > PWI_POWER_INDEX and l[ROW_TYPE_INDEX] == 'pwi' | |
1538 and l[PWI_POWER_INDEX] != 0): | |
1539 raise device_errors.CommandFailedError( | |
1540 'Non-zero pmi value found after reset.') | |
1541 self.RunShellCommand(['dumpsys', 'battery', 'set', 'usb', '0'], | |
1542 check_return=True) | |
1543 timeout_retry.WaitFor(battery_updates_disabled, wait_period=1) | |
1544 | |
1545 # TODO(rnephew): Make private when all use cases can use the context manager. | |
1546 @decorators.WithTimeoutAndRetriesFromInstance() | |
1547 def EnableBatteryUpdates(self, timeout=None, retries=None): | |
1548 """ Restarts device charging so that dumpsys no longer collects power data. | |
1549 | |
1550 Args: | |
1551 timeout: timeout in seconds | |
1552 retries: number of retries | |
1553 """ | |
1554 def battery_updates_enabled(): | |
1555 return self.GetCharging() is True | |
1556 | |
1557 self.RunShellCommand(['dumpsys', 'battery', 'set', 'usb', '1'], | |
1558 check_return=True) | |
1559 self.RunShellCommand(['dumpsys', 'battery', 'reset'], check_return=True) | |
1560 timeout_retry.WaitFor(battery_updates_enabled, wait_period=1) | |
1561 | |
1562 @contextlib.contextmanager | |
1563 def BatteryMeasurement(self, timeout=None, retries=None): | |
1564 """Context manager that enables battery data collection. It makes | |
1565 the device appear to stop charging so that dumpsys will start collecting | |
1566 power data since last charge. Once the with block is exited, charging is | |
1567 resumed and power data since last charge is no longer collected. | |
1568 | |
1569 Only for devices L and higher. | |
1570 | |
1571 Example usage: | |
1572 with BatteryMeasurement(): | |
1573 browser_actions() | |
1574 get_power_data() # report usage within this block | |
1575 after_measurements() # Anything that runs after power | |
1576 # measurements are collected | |
1577 | |
1578 Args: | |
1579 timeout: timeout in seconds | |
1580 retries: number of retries | |
1581 """ | |
1582 if self.build_version_sdk < constants.ANDROID_SDK_VERSION_CODES.LOLLIPOP: | |
1583 raise device_errors.CommandFailedError('Device must be L or higher.') | |
1584 try: | |
1585 self.DisableBatteryUpdates(timeout=timeout, retries=retries) | |
1586 yield | |
1587 finally: | |
1588 self.EnableBatteryUpdates(timeout=timeout, retries=retries) | |
1589 | |
1590 @decorators.WithTimeoutAndRetriesFromInstance() | |
1591 def GetDevicePieWrapper(self, timeout=None, retries=None): | 1508 def GetDevicePieWrapper(self, timeout=None, retries=None): |
1592 """Gets the absolute path to the run_pie wrapper on the device. | 1509 """Gets the absolute path to the run_pie wrapper on the device. |
1593 | 1510 |
1594 We have to build our device executables to be PIE, but they need to be able | 1511 We have to build our device executables to be PIE, but they need to be able |
1595 to run on versions of android that don't support PIE (i.e. ICS and below). | 1512 to run on versions of android that don't support PIE (i.e. ICS and below). |
1596 To do so, we push a wrapper to the device that lets older android versions | 1513 To do so, we push a wrapper to the device that lets older android versions |
1597 run PIE executables. This method pushes that wrapper to the device if | 1514 run PIE executables. This method pushes that wrapper to the device if |
1598 necessary and returns the path to it. | 1515 necessary and returns the path to it. |
1599 | 1516 |
1600 This is exposed publicly to allow clients to write scripts using run_pie | 1517 This is exposed publicly to allow clients to write scripts using run_pie |
(...skipping 14 matching lines...) Expand all Loading... |
1615 host_pie_path = os.path.join(constants.GetOutDirectory(), 'run_pie') | 1532 host_pie_path = os.path.join(constants.GetOutDirectory(), 'run_pie') |
1616 if not os.path.exists(host_pie_path): | 1533 if not os.path.exists(host_pie_path): |
1617 raise device_errors.CommandFailedError('Please build run_pie') | 1534 raise device_errors.CommandFailedError('Please build run_pie') |
1618 pie = '%s/run_pie' % constants.TEST_EXECUTABLE_DIR | 1535 pie = '%s/run_pie' % constants.TEST_EXECUTABLE_DIR |
1619 self.adb.Push(host_pie_path, pie) | 1536 self.adb.Push(host_pie_path, pie) |
1620 | 1537 |
1621 self._cache['run_pie'] = pie | 1538 self._cache['run_pie'] = pie |
1622 | 1539 |
1623 return self._cache['run_pie'] | 1540 return self._cache['run_pie'] |
1624 | 1541 |
| 1542 def GetClientCache(self, client_name): |
| 1543 """Returns client cache.""" |
| 1544 if client_name not in self._client_caches: |
| 1545 self._client_caches[client_name] = {} |
| 1546 return self._client_caches[client_name] |
| 1547 |
| 1548 def _ClearCache(self): |
| 1549 """Clears all caches.""" |
| 1550 for client in self._client_caches: |
| 1551 self._client_caches[client].clear() |
| 1552 self._cache.clear() |
| 1553 |
1625 @classmethod | 1554 @classmethod |
1626 def parallel(cls, devices=None, async=False): | 1555 def parallel(cls, devices=None, async=False): |
1627 """Creates a Parallelizer to operate over the provided list of devices. | 1556 """Creates a Parallelizer to operate over the provided list of devices. |
1628 | 1557 |
1629 If |devices| is either |None| or an empty list, the Parallelizer will | 1558 If |devices| is either |None| or an empty list, the Parallelizer will |
1630 operate over all attached devices. | 1559 operate over all attached devices that have not been blacklisted. |
1631 | 1560 |
1632 Args: | 1561 Args: |
1633 devices: A list of either DeviceUtils instances or objects from | 1562 devices: A list of either DeviceUtils instances or objects from |
1634 from which DeviceUtils instances can be constructed. If None, | 1563 from which DeviceUtils instances can be constructed. If None, |
1635 all attached devices will be used. | 1564 all attached devices will be used. |
1636 async: If true, returns a Parallelizer that runs operations | 1565 async: If true, returns a Parallelizer that runs operations |
1637 asynchronously. | 1566 asynchronously. |
1638 | 1567 |
1639 Returns: | 1568 Returns: |
1640 A Parallelizer operating over |devices|. | 1569 A Parallelizer operating over |devices|. |
1641 """ | 1570 """ |
1642 if not devices: | 1571 if not devices: |
1643 devices = adb_wrapper.AdbWrapper.GetDevices() | 1572 devices = cls.HealthyDevices() |
1644 if not devices: | 1573 if not devices: |
1645 raise device_errors.NoDevicesError() | 1574 raise device_errors.NoDevicesError() |
| 1575 |
1646 devices = [d if isinstance(d, cls) else cls(d) for d in devices] | 1576 devices = [d if isinstance(d, cls) else cls(d) for d in devices] |
1647 if async: | 1577 if async: |
1648 return parallelizer.Parallelizer(devices) | 1578 return parallelizer.Parallelizer(devices) |
1649 else: | 1579 else: |
1650 return parallelizer.SyncParallelizer(devices) | 1580 return parallelizer.SyncParallelizer(devices) |
| 1581 |
| 1582 @classmethod |
| 1583 def HealthyDevices(cls): |
| 1584 blacklist = device_blacklist.ReadBlacklist() |
| 1585 def blacklisted(adb): |
| 1586 if adb.GetDeviceSerial() in blacklist: |
| 1587 logging.warning('Device %s is blacklisted.', adb.GetDeviceSerial()) |
| 1588 return True |
| 1589 return False |
| 1590 |
| 1591 return [cls(adb) for adb in adb_wrapper.AdbWrapper.Devices() |
| 1592 if not blacklisted(adb)] |
| 1593 |
OLD | NEW |