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=W0613 | 9 # pylint: disable=W0613 |
10 | 10 |
| 11 import logging |
11 import multiprocessing | 12 import multiprocessing |
12 import os | 13 import os |
13 import pipes | 14 import pipes |
14 import sys | 15 import sys |
15 import tempfile | 16 import tempfile |
16 import time | 17 import time |
17 import zipfile | 18 import zipfile |
18 | 19 |
19 import pylib.android_commands | 20 import pylib.android_commands |
20 from pylib.device import adb_wrapper | 21 from pylib.device import adb_wrapper |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
76 self.adb = device | 77 self.adb = device |
77 self.old_interface = pylib.android_commands.AndroidCommands(str(device)) | 78 self.old_interface = pylib.android_commands.AndroidCommands(str(device)) |
78 elif isinstance(device, pylib.android_commands.AndroidCommands): | 79 elif isinstance(device, pylib.android_commands.AndroidCommands): |
79 self.adb = adb_wrapper.AdbWrapper(device.GetDevice()) | 80 self.adb = adb_wrapper.AdbWrapper(device.GetDevice()) |
80 self.old_interface = device | 81 self.old_interface = device |
81 elif not device: | 82 elif not device: |
82 self.adb = adb_wrapper.AdbWrapper('') | 83 self.adb = adb_wrapper.AdbWrapper('') |
83 self.old_interface = pylib.android_commands.AndroidCommands() | 84 self.old_interface = pylib.android_commands.AndroidCommands() |
84 else: | 85 else: |
85 raise ValueError('Unsupported type passed for argument "device"') | 86 raise ValueError('Unsupported type passed for argument "device"') |
86 self._commands_installed = False | 87 self._commands_installed = None |
87 self._default_timeout = default_timeout | 88 self._default_timeout = default_timeout |
88 self._default_retries = default_retries | 89 self._default_retries = default_retries |
89 assert(hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR)) | 90 assert(hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR)) |
90 assert(hasattr(self, decorators.DEFAULT_RETRIES_ATTR)) | 91 assert(hasattr(self, decorators.DEFAULT_RETRIES_ATTR)) |
91 | 92 |
92 @decorators.WithTimeoutAndRetriesFromInstance() | 93 @decorators.WithTimeoutAndRetriesFromInstance() |
93 def IsOnline(self, timeout=None, retries=None): | 94 def IsOnline(self, timeout=None, retries=None): |
94 """Checks whether the device is online. | 95 """Checks whether the device is online. |
95 | 96 |
96 Args: | 97 Args: |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
138 | 139 |
139 Raises: | 140 Raises: |
140 CommandFailedError if root could not be enabled. | 141 CommandFailedError if root could not be enabled. |
141 CommandTimeoutError on timeout. | 142 CommandTimeoutError on timeout. |
142 """ | 143 """ |
143 if not self.old_interface.EnableAdbRoot(): | 144 if not self.old_interface.EnableAdbRoot(): |
144 raise device_errors.CommandFailedError( | 145 raise device_errors.CommandFailedError( |
145 'Could not enable root.', device=str(self)) | 146 'Could not enable root.', device=str(self)) |
146 | 147 |
147 @decorators.WithTimeoutAndRetriesFromInstance() | 148 @decorators.WithTimeoutAndRetriesFromInstance() |
| 149 def IsUserBuild(self, timeout=None, retries=None): |
| 150 """Checks whether or not the device is running a user build. |
| 151 |
| 152 Args: |
| 153 timeout: timeout in seconds |
| 154 retries: number of retries |
| 155 |
| 156 Returns: |
| 157 True if the device is running a user build, False otherwise (i.e. if |
| 158 it's running a userdebug build). |
| 159 |
| 160 Raises: |
| 161 CommandTimeoutError on timeout. |
| 162 DeviceUnreachableError on missing device. |
| 163 """ |
| 164 return self._GetPropImpl('ro.build.type') == 'user' |
| 165 |
| 166 @decorators.WithTimeoutAndRetriesFromInstance() |
148 def GetExternalStoragePath(self, timeout=None, retries=None): | 167 def GetExternalStoragePath(self, timeout=None, retries=None): |
149 """Get the device's path to its SD card. | 168 """Get the device's path to its SD card. |
150 | 169 |
151 Args: | 170 Args: |
152 timeout: timeout in seconds | 171 timeout: timeout in seconds |
153 retries: number of retries | 172 retries: number of retries |
154 | 173 |
155 Returns: | 174 Returns: |
156 The device's path to its SD card. | 175 The device's path to its SD card. |
157 | 176 |
(...skipping 363 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
521 dir_file_count += sum(len(f) for _r, _d, f in os.walk(h)) | 540 dir_file_count += sum(len(f) for _r, _d, f in os.walk(h)) |
522 else: | 541 else: |
523 dir_file_count += 1 | 542 dir_file_count += 1 |
524 | 543 |
525 push_duration = self._ApproximateDuration( | 544 push_duration = self._ApproximateDuration( |
526 file_count, file_count, size, False) | 545 file_count, file_count, size, False) |
527 dir_push_duration = self._ApproximateDuration( | 546 dir_push_duration = self._ApproximateDuration( |
528 len(host_device_tuples), dir_file_count, dir_size, False) | 547 len(host_device_tuples), dir_file_count, dir_size, False) |
529 zip_duration = self._ApproximateDuration(1, 1, size, True) | 548 zip_duration = self._ApproximateDuration(1, 1, size, True) |
530 | 549 |
531 if dir_push_duration < push_duration and dir_push_duration < zip_duration: | 550 self._InstallCommands() |
| 551 |
| 552 if dir_push_duration < push_duration and ( |
| 553 dir_push_duration < zip_duration or not self._commands_installed): |
532 self._PushChangedFilesIndividually(host_device_tuples) | 554 self._PushChangedFilesIndividually(host_device_tuples) |
533 elif push_duration < zip_duration: | 555 elif push_duration < zip_duration or not self._commands_installed: |
534 self._PushChangedFilesIndividually(files) | 556 self._PushChangedFilesIndividually(files) |
535 else: | 557 else: |
536 self._PushChangedFilesZipped(files) | 558 self._PushChangedFilesZipped(files) |
537 self._RunShellCommandImpl( | 559 self._RunShellCommandImpl( |
538 ['chmod', '-R', '777'] + [d for _, d in host_device_tuples], | 560 ['chmod', '-R', '777'] + [d for _, d in host_device_tuples], |
539 as_root=True) | 561 as_root=True) |
540 | 562 |
541 def _GetChangedFilesImpl(self, host_path, device_path): | 563 def _GetChangedFilesImpl(self, host_path, device_path): |
542 real_host_path = os.path.realpath(host_path) | 564 real_host_path = os.path.realpath(host_path) |
543 try: | 565 try: |
(...skipping 21 matching lines...) Expand all Loading... |
565 to_push = [] | 587 to_push = [] |
566 for host_hash, host_abs_path in ( | 588 for host_hash, host_abs_path in ( |
567 (h.hash, h.path) for h in host_hash_tuples): | 589 (h.hash, h.path) for h in host_hash_tuples): |
568 device_abs_path = '%s/%s' % ( | 590 device_abs_path = '%s/%s' % ( |
569 real_device_path, os.path.relpath(host_abs_path, real_host_path)) | 591 real_device_path, os.path.relpath(host_abs_path, real_host_path)) |
570 if (device_abs_path not in device_tuple_dict | 592 if (device_abs_path not in device_tuple_dict |
571 or device_tuple_dict[device_abs_path] != host_hash): | 593 or device_tuple_dict[device_abs_path] != host_hash): |
572 to_push.append((host_abs_path, device_abs_path)) | 594 to_push.append((host_abs_path, device_abs_path)) |
573 return to_push | 595 return to_push |
574 | 596 |
| 597 def _InstallCommands(self): |
| 598 if self._commands_installed is None: |
| 599 try: |
| 600 if not install_commands.Installed(self): |
| 601 install_commands.InstallCommands(self) |
| 602 self._commands_installed = True |
| 603 except Exception as e: |
| 604 logging.warning('unzip not available: %s' % str(e)) |
| 605 self._commands_installed = False |
| 606 |
575 @staticmethod | 607 @staticmethod |
576 def _ApproximateDuration(adb_calls, file_count, byte_count, is_zipping): | 608 def _ApproximateDuration(adb_calls, file_count, byte_count, is_zipping): |
577 # We approximate the time to push a set of files to a device as: | 609 # We approximate the time to push a set of files to a device as: |
578 # t = c1 * a + c2 * f + c3 + b / c4 + b / (c5 * c6), where | 610 # t = c1 * a + c2 * f + c3 + b / c4 + b / (c5 * c6), where |
579 # t: total time (sec) | 611 # t: total time (sec) |
580 # c1: adb call time delay (sec) | 612 # c1: adb call time delay (sec) |
581 # a: number of times adb is called (unitless) | 613 # a: number of times adb is called (unitless) |
582 # c2: push time delay (sec) | 614 # c2: push time delay (sec) |
583 # f: number of files pushed via adb (unitless) | 615 # f: number of files pushed via adb (unitless) |
584 # c3: zip time delay (sec) | 616 # c3: zip time delay (sec) |
(...skipping 21 matching lines...) Expand all Loading... |
606 return (adb_call_time + adb_push_setup_time + zip_time + transfer_time) | 638 return (adb_call_time + adb_push_setup_time + zip_time + transfer_time) |
607 | 639 |
608 def _PushChangedFilesIndividually(self, files): | 640 def _PushChangedFilesIndividually(self, files): |
609 for h, d in files: | 641 for h, d in files: |
610 self.adb.Push(h, d) | 642 self.adb.Push(h, d) |
611 | 643 |
612 def _PushChangedFilesZipped(self, files): | 644 def _PushChangedFilesZipped(self, files): |
613 if not files: | 645 if not files: |
614 return | 646 return |
615 | 647 |
616 self._InstallCommands() | |
617 | |
618 with tempfile.NamedTemporaryFile(suffix='.zip') as zip_file: | 648 with tempfile.NamedTemporaryFile(suffix='.zip') as zip_file: |
619 zip_proc = multiprocessing.Process( | 649 zip_proc = multiprocessing.Process( |
620 target=DeviceUtils._CreateDeviceZip, | 650 target=DeviceUtils._CreateDeviceZip, |
621 args=(zip_file.name, files)) | 651 args=(zip_file.name, files)) |
622 zip_proc.start() | 652 zip_proc.start() |
623 zip_proc.join() | 653 zip_proc.join() |
624 | 654 |
625 zip_on_device = '%s/tmp.zip' % self._GetExternalStoragePathImpl() | 655 zip_on_device = '%s/tmp.zip' % self._GetExternalStoragePathImpl() |
626 try: | 656 try: |
627 self.adb.Push(zip_file.name, zip_on_device) | 657 self.adb.Push(zip_file.name, zip_on_device) |
628 self._RunShellCommandImpl( | 658 self._RunShellCommandImpl( |
629 ['unzip', zip_on_device], | 659 ['unzip', zip_on_device], |
630 as_root=True, check_return=True, | 660 as_root=True, check_return=True, |
631 env={'PATH': '$PATH:%s' % install_commands.BIN_DIR}) | 661 env={'PATH': '$PATH:%s' % install_commands.BIN_DIR}) |
632 finally: | 662 finally: |
633 if zip_proc.is_alive(): | 663 if zip_proc.is_alive(): |
634 zip_proc.terminate() | 664 zip_proc.terminate() |
635 if self._IsOnlineImpl(): | 665 if self._IsOnlineImpl(): |
636 self._RunShellCommandImpl(['rm', zip_on_device]) | 666 self._RunShellCommandImpl(['rm', zip_on_device]) |
637 | 667 |
638 def _InstallCommands(self): | |
639 if not self._commands_installed and not install_commands.Installed(self): | |
640 install_commands.InstallCommands(self) | |
641 self._commands_installed = True | |
642 | |
643 @staticmethod | 668 @staticmethod |
644 def _CreateDeviceZip(zip_path, host_device_tuples): | 669 def _CreateDeviceZip(zip_path, host_device_tuples): |
645 with zipfile.ZipFile(zip_path, 'w') as zip_file: | 670 with zipfile.ZipFile(zip_path, 'w') as zip_file: |
646 for host_path, device_path in host_device_tuples: | 671 for host_path, device_path in host_device_tuples: |
647 if os.path.isfile(host_path): | 672 if os.path.isfile(host_path): |
648 zip_file.write(host_path, device_path, zipfile.ZIP_DEFLATED) | 673 zip_file.write(host_path, device_path, zipfile.ZIP_DEFLATED) |
649 else: | 674 else: |
650 for hd, _, files in os.walk(host_path): | 675 for hd, _, files in os.walk(host_path): |
651 dd = '%s/%s' % (device_path, os.path.relpath(host_path, hd)) | 676 dd = '%s/%s' % (device_path, os.path.relpath(host_path, hd)) |
652 zip_file.write(hd, dd, zipfile.ZIP_STORED) | 677 zip_file.write(hd, dd, zipfile.ZIP_STORED) |
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
824 the device. | 849 the device. |
825 timeout: timeout in seconds | 850 timeout: timeout in seconds |
826 retries: number of retries | 851 retries: number of retries |
827 | 852 |
828 Returns: | 853 Returns: |
829 The value of the device's |property_name| property. | 854 The value of the device's |property_name| property. |
830 | 855 |
831 Raises: | 856 Raises: |
832 CommandTimeoutError on timeout. | 857 CommandTimeoutError on timeout. |
833 """ | 858 """ |
| 859 return self._GetPropImpl(property_name) |
| 860 |
| 861 def _GetPropImpl(self, property_name): |
834 return self.old_interface.system_properties[property_name] | 862 return self.old_interface.system_properties[property_name] |
835 | 863 |
836 @decorators.WithTimeoutAndRetriesFromInstance() | 864 @decorators.WithTimeoutAndRetriesFromInstance() |
837 def SetProp(self, property_name, value, timeout=None, retries=None): | 865 def SetProp(self, property_name, value, timeout=None, retries=None): |
838 """Sets a property on the device. | 866 """Sets a property on the device. |
839 | 867 |
840 Args: | 868 Args: |
841 property_name: A string containing the name of the property to set on | 869 property_name: A string containing the name of the property to set on |
842 the device. | 870 the device. |
843 value: A string containing the value to set to the property on the | 871 value: A string containing the value to set to the property on the |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
965 A Parallelizer operating over |devices|. | 993 A Parallelizer operating over |devices|. |
966 """ | 994 """ |
967 if not devices or len(devices) == 0: | 995 if not devices or len(devices) == 0: |
968 devices = pylib.android_commands.GetAttachedDevices() | 996 devices = pylib.android_commands.GetAttachedDevices() |
969 parallelizer_type = (parallelizer.Parallelizer if async | 997 parallelizer_type = (parallelizer.Parallelizer if async |
970 else parallelizer.SyncParallelizer) | 998 else parallelizer.SyncParallelizer) |
971 return parallelizer_type([ | 999 return parallelizer_type([ |
972 d if isinstance(d, DeviceUtils) else DeviceUtils(d) | 1000 d if isinstance(d, DeviceUtils) else DeviceUtils(d) |
973 for d in devices]) | 1001 for d in devices]) |
974 | 1002 |
OLD | NEW |