Chromium Code Reviews| 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 logging |
| 12 import multiprocessing | 12 import multiprocessing |
| 13 import os | 13 import os |
| 14 import re | 14 import re |
| 15 import sys | 15 import sys |
| 16 import tempfile | 16 import tempfile |
| 17 import threading | |
| 17 import time | 18 import time |
| 18 import zipfile | 19 import zipfile |
| 19 | 20 |
| 20 import pylib.android_commands | 21 import pylib.android_commands |
| 21 from pylib import cmd_helper | 22 from pylib import cmd_helper |
| 22 from pylib.device import adb_wrapper | 23 from pylib.device import adb_wrapper |
| 23 from pylib.device import decorators | 24 from pylib.device import decorators |
| 24 from pylib.device import device_errors | 25 from pylib.device import device_errors |
| 25 from pylib.device.commands import install_commands | 26 from pylib.device.commands import install_commands |
| 26 from pylib.utils import apk_helper | 27 from pylib.utils import apk_helper |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 101 Args: | 102 Args: |
| 102 timeout: timeout in seconds | 103 timeout: timeout in seconds |
| 103 retries: number of retries | 104 retries: number of retries |
| 104 | 105 |
| 105 Returns: | 106 Returns: |
| 106 True if the device is online, False otherwise. | 107 True if the device is online, False otherwise. |
| 107 | 108 |
| 108 Raises: | 109 Raises: |
| 109 CommandTimeoutError on timeout. | 110 CommandTimeoutError on timeout. |
| 110 """ | 111 """ |
| 111 return self._IsOnlineImpl() | 112 state = self.adb.GetState() |
| 112 | 113 return state == 'device' |
| 113 def _IsOnlineImpl(self): | |
| 114 return self.old_interface.IsOnline() | |
| 115 | 114 |
| 116 @decorators.WithTimeoutAndRetriesFromInstance() | 115 @decorators.WithTimeoutAndRetriesFromInstance() |
| 117 def HasRoot(self, timeout=None, retries=None): | 116 def HasRoot(self, timeout=None, retries=None): |
| 118 """Checks whether or not adbd has root privileges. | 117 """Checks whether or not adbd has root privileges. |
| 119 | 118 |
| 120 Args: | 119 Args: |
| 121 timeout: timeout in seconds | 120 timeout: timeout in seconds |
| 122 retries: number of retries | 121 retries: number of retries |
| 123 | 122 |
| 124 Returns: | 123 Returns: |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 162 retries: number of retries | 161 retries: number of retries |
| 163 | 162 |
| 164 Returns: | 163 Returns: |
| 165 True if the device is running a user build, False otherwise (i.e. if | 164 True if the device is running a user build, False otherwise (i.e. if |
| 166 it's running a userdebug build). | 165 it's running a userdebug build). |
| 167 | 166 |
| 168 Raises: | 167 Raises: |
| 169 CommandTimeoutError on timeout. | 168 CommandTimeoutError on timeout. |
| 170 DeviceUnreachableError on missing device. | 169 DeviceUnreachableError on missing device. |
| 171 """ | 170 """ |
| 172 return self._GetPropImpl('ro.build.type') == 'user' | 171 return self.build_type == 'user' |
|
perezju
2014/10/27 17:43:34
This would be a property of the class, see below.
| |
| 173 | 172 |
| 174 @decorators.WithTimeoutAndRetriesFromInstance() | 173 @decorators.WithTimeoutAndRetriesFromInstance() |
| 175 def GetExternalStoragePath(self, timeout=None, retries=None): | 174 def GetExternalStoragePath(self, timeout=None, retries=None): |
| 176 """Get the device's path to its SD card. | 175 """Get the device's path to its SD card. |
| 177 | 176 |
| 178 Args: | 177 Args: |
| 179 timeout: timeout in seconds | 178 timeout: timeout in seconds |
| 180 retries: number of retries | 179 retries: number of retries |
| 181 | 180 |
| 182 Returns: | 181 Returns: |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 213 Args: | 212 Args: |
| 214 wifi: A boolean indicating if we should wait for wifi to come up or not. | 213 wifi: A boolean indicating if we should wait for wifi to come up or not. |
| 215 timeout: timeout in seconds | 214 timeout: timeout in seconds |
| 216 retries: number of retries | 215 retries: number of retries |
| 217 | 216 |
| 218 Raises: | 217 Raises: |
| 219 CommandFailedError on failure. | 218 CommandFailedError on failure. |
| 220 CommandTimeoutError if one of the component waits times out. | 219 CommandTimeoutError if one of the component waits times out. |
| 221 DeviceUnreachableError if the device becomes unresponsive. | 220 DeviceUnreachableError if the device becomes unresponsive. |
| 222 """ | 221 """ |
| 223 self._WaitUntilFullyBootedImpl(wifi=wifi, timeout=timeout) | 222 def sd_card_ready(): |
| 223 return self.RunShellCommand(['ls', self.GetExternalStoragePath()], | |
| 224 single_line=True, check_return=True) | |
| 224 | 225 |
| 225 def _WaitUntilFullyBootedImpl(self, wifi=False, timeout=None): | 226 def pm_ready(): |
| 226 if timeout is None: | 227 return self.GetApplicationPath('android') |
| 227 timeout = self._default_timeout | 228 |
| 228 self.old_interface.WaitForSystemBootCompleted(timeout) | 229 def boot_completed(): |
| 229 self.old_interface.WaitForDevicePm() | 230 return self.GetProp('sys.boot_completed') == '1' |
| 230 self.old_interface.WaitForSdCardReady(timeout) | 231 |
| 232 def wifi_enabled(): | |
| 233 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi']) | |
| 234 | |
| 235 self.adb.WaitForDevice() | |
| 236 self._WaitFor(sd_card_ready) | |
| 237 self._WaitFor(pm_ready) | |
| 238 self._WaitFor(boot_completed) | |
| 231 if wifi: | 239 if wifi: |
| 232 while not 'Wi-Fi is enabled' in ( | 240 self._WaitFor(wifi_enabled) |
| 233 self.old_interface.RunShellCommand('dumpsys wifi')): | 241 |
| 234 time.sleep(1) | 242 def _WaitFor(self, condition, wait_period=5, max_tries=None): |
| 243 thread = threading.current_thread() | |
| 244 while max_tries is None or max_tries > 0: | |
| 245 try: | |
| 246 r = condition() | |
| 247 except device_errors.CommandFailedError: | |
| 248 r = None | |
| 249 thread.CheckTimeout() | |
|
perezju
2014/10/27 17:43:34
This would raise an exception if the current threa
jbudorick
2014/10/27 19:15:36
I really don't like delegating this responsibility
| |
| 250 if max_tries is not None: | |
| 251 max_tries -= 1 | |
| 252 if r: | |
| 253 logging.info('(device: %s) condition %r met (%1.2fs) at %s', | |
| 254 str(self), condition.__name__, thread.ElapsedTime(), thread.name) | |
| 255 return r | |
| 256 else: | |
| 257 logging.info('(device: %s) condition %r not yet met (%1.2fs) at %s', | |
| 258 str(self), condition.__name__, thread.ElapsedTime(), thread.name) | |
| 259 time.sleep(wait_period) | |
| 260 thread.CheckTimeout() | |
| 261 return None | |
| 262 | |
| 263 @decorators.WithTimeoutAndRetriesFromInstance() | |
| 264 def GetApplicationPath(self, package, timeout=None, retries=None): | |
| 265 output = self.RunShellCommand(['pm', 'path', package], single_line=True, | |
| 266 check_return=True) | |
| 267 if not output.startswith('package:'): | |
| 268 raise device_errors.CommandFailedError('pm path returned: %r' % output, | |
| 269 str(self)) | |
| 270 return output[len('package:'):] | |
| 271 | |
| 235 | 272 |
| 236 REBOOT_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT | 273 REBOOT_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT |
| 237 REBOOT_DEFAULT_RETRIES = _DEFAULT_RETRIES | 274 REBOOT_DEFAULT_RETRIES = _DEFAULT_RETRIES |
| 238 | 275 |
| 239 @decorators.WithTimeoutAndRetriesDefaults( | 276 @decorators.WithTimeoutAndRetriesDefaults( |
| 240 REBOOT_DEFAULT_TIMEOUT, | 277 REBOOT_DEFAULT_TIMEOUT, |
| 241 REBOOT_DEFAULT_RETRIES) | 278 REBOOT_DEFAULT_RETRIES) |
| 242 def Reboot(self, block=True, timeout=None, retries=None): | 279 def Reboot(self, block=True, timeout=None, retries=None): |
|
perezju
2014/10/27 17:43:34
This brings in some of the logic from android_comm
jbudorick
2014/10/27 19:15:36
public cs: https://code.google.com/p/chromium/code
perezju
2014/10/28 14:31:17
Just checked with him. HIVE is not used anymore, s
| |
| 243 """Reboot the device. | 280 """Reboot the device. |
| 244 | 281 |
| 245 Args: | 282 Args: |
| 246 block: A boolean indicating if we should wait for the reboot to complete. | 283 block: A boolean indicating if we should wait for the reboot to complete. |
| 247 timeout: timeout in seconds | 284 timeout: timeout in seconds |
| 248 retries: number of retries | 285 retries: number of retries |
| 249 | 286 |
| 250 Raises: | 287 Raises: |
| 251 CommandTimeoutError on timeout. | 288 CommandTimeoutError on timeout. |
| 252 DeviceUnreachableError on missing device. | 289 DeviceUnreachableError on missing device. |
| 253 """ | 290 """ |
| 254 self.old_interface.Reboot() | 291 def device_offline(): |
|
jbudorick
2014/10/27 19:15:36
nit: for something this simple, just use a lambda
| |
| 292 return not self.IsOnline() | |
| 293 | |
| 294 self.adb.Reboot() | |
| 295 self._cache = {} | |
| 296 self._WaitFor(device_offline, wait_period=1, max_tries=5) | |
|
jbudorick
2014/10/27 19:15:36
With the lambda, this becomes:
self._WaitFor(la
perezju
2014/10/28 14:31:17
Yes, but if we use a named function then _WaitFor
jbudorick
2014/10/28 17:28:46
Ah. Stick with device_offline, then.
| |
| 255 if block: | 297 if block: |
| 256 self._WaitUntilFullyBootedImpl(timeout=timeout) | 298 self.WaitUntilFullyBooted() |
| 257 | 299 |
| 258 INSTALL_DEFAULT_TIMEOUT = 4 * _DEFAULT_TIMEOUT | 300 INSTALL_DEFAULT_TIMEOUT = 4 * _DEFAULT_TIMEOUT |
| 259 INSTALL_DEFAULT_RETRIES = _DEFAULT_RETRIES | 301 INSTALL_DEFAULT_RETRIES = _DEFAULT_RETRIES |
| 260 | 302 |
| 261 @decorators.WithTimeoutAndRetriesDefaults( | 303 @decorators.WithTimeoutAndRetriesDefaults( |
| 262 INSTALL_DEFAULT_TIMEOUT, | 304 INSTALL_DEFAULT_TIMEOUT, |
| 263 INSTALL_DEFAULT_RETRIES) | 305 INSTALL_DEFAULT_RETRIES) |
| 264 def Install(self, apk_path, reinstall=False, timeout=None, retries=None): | 306 def Install(self, apk_path, reinstall=False, timeout=None, retries=None): |
| 265 """Install an APK. | 307 """Install an APK. |
| 266 | 308 |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 387 except device_errors.AdbShellCommandFailedError as e: | 429 except device_errors.AdbShellCommandFailedError as e: |
| 388 if check_return: | 430 if check_return: |
| 389 raise | 431 raise |
| 390 else: | 432 else: |
| 391 output = e.output | 433 output = e.output |
| 392 | 434 |
| 393 output = output.splitlines() | 435 output = output.splitlines() |
| 394 if single_line: | 436 if single_line: |
| 395 if len(output) != 1: | 437 if len(output) != 1: |
| 396 msg = 'exactly one line of output expected, but got: %s' | 438 msg = 'exactly one line of output expected, but got: %s' |
| 397 raise device_errors.CommandFailedError(msg % output) | 439 raise device_errors.CommandFailedError(msg % output, str(self)) |
| 398 return output[0] | 440 return output[0] |
| 399 else: | 441 else: |
| 400 return output | 442 return output |
| 401 | 443 |
| 402 @decorators.WithTimeoutAndRetriesFromInstance() | 444 @decorators.WithTimeoutAndRetriesFromInstance() |
| 403 def KillAll(self, process_name, signum=9, as_root=False, blocking=False, | 445 def KillAll(self, process_name, signum=9, as_root=False, blocking=False, |
| 404 timeout=None, retries=None): | 446 timeout=None, retries=None): |
| 405 """Kill all processes with the given name on the device. | 447 """Kill all processes with the given name on the device. |
| 406 | 448 |
| 407 Args: | 449 Args: |
| (...skipping 300 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 708 try: | 750 try: |
| 709 self.adb.Push(zip_file.name, zip_on_device) | 751 self.adb.Push(zip_file.name, zip_on_device) |
| 710 self._RunShellCommandImpl( | 752 self._RunShellCommandImpl( |
| 711 ['unzip', zip_on_device], | 753 ['unzip', zip_on_device], |
| 712 as_root=True, | 754 as_root=True, |
| 713 env={'PATH': '$PATH:%s' % install_commands.BIN_DIR}, | 755 env={'PATH': '$PATH:%s' % install_commands.BIN_DIR}, |
| 714 check_return=True) | 756 check_return=True) |
| 715 finally: | 757 finally: |
| 716 if zip_proc.is_alive(): | 758 if zip_proc.is_alive(): |
| 717 zip_proc.terminate() | 759 zip_proc.terminate() |
| 718 if self._IsOnlineImpl(): | 760 if self.IsOnline(): |
| 719 self._RunShellCommandImpl(['rm', zip_on_device], check_return=True) | 761 self._RunShellCommandImpl(['rm', zip_on_device], check_return=True) |
| 720 | 762 |
| 721 @staticmethod | 763 @staticmethod |
| 722 def _CreateDeviceZip(zip_path, host_device_tuples): | 764 def _CreateDeviceZip(zip_path, host_device_tuples): |
| 723 with zipfile.ZipFile(zip_path, 'w') as zip_file: | 765 with zipfile.ZipFile(zip_path, 'w') as zip_file: |
| 724 for host_path, device_path in host_device_tuples: | 766 for host_path, device_path in host_device_tuples: |
| 725 if os.path.isfile(host_path): | 767 if os.path.isfile(host_path): |
| 726 zip_file.write(host_path, device_path, zipfile.ZIP_DEFLATED) | 768 zip_file.write(host_path, device_path, zipfile.ZIP_DEFLATED) |
| 727 else: | 769 else: |
| 728 for hd, _, files in os.walk(host_path): | 770 for hd, _, files in os.walk(host_path): |
| (...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 887 | 929 |
| 888 Returns: | 930 Returns: |
| 889 True if the device-side property changed and a restart is required as a | 931 True if the device-side property changed and a restart is required as a |
| 890 result, False otherwise. | 932 result, False otherwise. |
| 891 | 933 |
| 892 Raises: | 934 Raises: |
| 893 CommandTimeoutError on timeout. | 935 CommandTimeoutError on timeout. |
| 894 """ | 936 """ |
| 895 return self.old_interface.SetJavaAssertsEnabled(enabled) | 937 return self.old_interface.SetJavaAssertsEnabled(enabled) |
| 896 | 938 |
| 897 @decorators.WithTimeoutAndRetriesFromInstance() | 939 @property |
| 898 def GetProp(self, property_name, timeout=None, retries=None): | 940 def build_type(self): |
| 941 return self.GetProp('ro.build.type', cache=True) | |
|
perezju
2014/10/27 17:43:34
This is the sort of thing that I propose to both:
jbudorick
2014/10/27 19:15:36
I'll have to think about this. It bloats the inter
perezju
2014/10/28 14:31:18
I say we could start with properties that (1) are
jbudorick
2014/10/28 17:28:45
Sounds good to me.
| |
| 942 | |
| 943 def GetProp(self, property_name, cache=False, timeout=None, retries=None): | |
| 899 """Gets a property from the device. | 944 """Gets a property from the device. |
| 900 | 945 |
| 901 Args: | 946 Args: |
| 902 property_name: A string containing the name of the property to get from | 947 property_name: A string containing the name of the property to get from |
| 903 the device. | 948 the device. |
| 949 cache: A boolean indicating whether to cache the value of this property. | |
| 904 timeout: timeout in seconds | 950 timeout: timeout in seconds |
| 905 retries: number of retries | 951 retries: number of retries |
| 906 | 952 |
| 907 Returns: | 953 Returns: |
| 908 The value of the device's |property_name| property. | 954 The value of the device's |property_name| property. |
| 909 | 955 |
| 910 Raises: | 956 Raises: |
| 911 CommandTimeoutError on timeout. | 957 CommandTimeoutError on timeout. |
| 912 """ | 958 """ |
| 913 return self._GetPropImpl(property_name) | 959 cache_name = '_prop:' + property_name |
| 914 | 960 if cache and cache_name in self._cache: |
| 915 def _GetPropImpl(self, property_name): | 961 return self._cache[cache_name] |
| 916 return self.old_interface.system_properties[property_name] | 962 else: |
| 963 # timeout and retries are handled down at run shell, because we don't | |
| 964 # want to apply them in the other branch when reading from the cache | |
| 965 value = self.RunShellCommand(['getprop', property_name], | |
| 966 single_line=True, check_return=True, | |
| 967 timeout=timeout, retries=retries) | |
| 968 if cache or cache_name in self._cache: | |
|
jbudorick
2014/10/27 19:15:36
The second clause here is interesting. Should we u
perezju
2014/10/28 14:31:18
I say we update. From the point of view of the cal
jbudorick
2014/10/28 17:28:45
Makes sense.
| |
| 969 self._cache[cache_name] = value | |
| 970 return value | |
| 917 | 971 |
| 918 @decorators.WithTimeoutAndRetriesFromInstance() | 972 @decorators.WithTimeoutAndRetriesFromInstance() |
| 919 def SetProp(self, property_name, value, timeout=None, retries=None): | 973 def SetProp(self, property_name, value, check=False, timeout=None, |
| 974 retries=None): | |
| 920 """Sets a property on the device. | 975 """Sets a property on the device. |
| 921 | 976 |
| 922 Args: | 977 Args: |
| 923 property_name: A string containing the name of the property to set on | 978 property_name: A string containing the name of the property to set on |
| 924 the device. | 979 the device. |
| 925 value: A string containing the value to set to the property on the | 980 value: A string containing the value to set to the property on the |
| 926 device. | 981 device. |
| 927 timeout: timeout in seconds | 982 timeout: timeout in seconds |
| 928 retries: number of retries | 983 retries: number of retries |
| 929 | 984 |
| 930 Raises: | 985 Raises: |
| 931 CommandTimeoutError on timeout. | 986 CommandTimeoutError on timeout. |
| 932 """ | 987 """ |
| 933 self.old_interface.system_properties[property_name] = value | 988 self.RunShellCommand(['setprop', property_name, value], check_return=True) |
| 989 if check and value != self.GetProp(property_name): | |
| 990 raise device_errors.CommandFailedError( | |
| 991 'Unable to set property %r on the device to %r' | |
| 992 % (property_name, value), str(self)) | |
|
perezju
2014/10/27 17:43:34
Still wondering whether to always make the check,
jbudorick
2014/10/27 19:15:36
Not yet. In a separate CL, definitely.
| |
| 934 | 993 |
| 935 @decorators.WithTimeoutAndRetriesFromInstance() | 994 @decorators.WithTimeoutAndRetriesFromInstance() |
| 936 def GetABI(self, timeout=None, retries=None): | 995 def GetABI(self, timeout=None, retries=None): |
| 937 """Gets the device main ABI. | 996 """Gets the device main ABI. |
| 938 | 997 |
| 939 Args: | 998 Args: |
| 940 timeout: timeout in seconds | 999 timeout: timeout in seconds |
| 941 retries: number of retries | 1000 retries: number of retries |
| 942 | 1001 |
| 943 Returns: | 1002 Returns: |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1062 Returns: | 1121 Returns: |
| 1063 A Parallelizer operating over |devices|. | 1122 A Parallelizer operating over |devices|. |
| 1064 """ | 1123 """ |
| 1065 if not devices or len(devices) == 0: | 1124 if not devices or len(devices) == 0: |
| 1066 devices = pylib.android_commands.GetAttachedDevices() | 1125 devices = pylib.android_commands.GetAttachedDevices() |
| 1067 parallelizer_type = (parallelizer.Parallelizer if async | 1126 parallelizer_type = (parallelizer.Parallelizer if async |
| 1068 else parallelizer.SyncParallelizer) | 1127 else parallelizer.SyncParallelizer) |
| 1069 return parallelizer_type([ | 1128 return parallelizer_type([ |
| 1070 d if isinstance(d, DeviceUtils) else DeviceUtils(d) | 1129 d if isinstance(d, DeviceUtils) else DeviceUtils(d) |
| 1071 for d in devices]) | 1130 for d in devices]) |
| OLD | NEW |