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' |
jbudorick
2014/10/30 02:27:03
return self.adb.getState() == 'device'
?
perezju
2014/10/30 18:44:45
:S .. probably a left-over from some (now missing)
| |
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' |
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 13 matching lines...) Expand all Loading... | |
196 value = self._RunShellCommandImpl('echo $EXTERNAL_STORAGE', | 195 value = self._RunShellCommandImpl('echo $EXTERNAL_STORAGE', |
197 single_line=True, | 196 single_line=True, |
198 check_return=True) | 197 check_return=True) |
199 if not value: | 198 if not value: |
200 raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set', | 199 raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set', |
201 str(self)) | 200 str(self)) |
202 self._cache['external_storage'] = value | 201 self._cache['external_storage'] = value |
203 return value | 202 return value |
204 | 203 |
205 @decorators.WithTimeoutAndRetriesFromInstance() | 204 @decorators.WithTimeoutAndRetriesFromInstance() |
205 def GetApplicationPath(self, package, timeout=None, retries=None): | |
206 """Get the path of the installed apk on the device for the given package. | |
207 | |
208 Args: | |
209 package: Name of the package. | |
210 | |
211 Returns: | |
212 Path to the apk on the device if it exists, None otherwise. | |
213 """ | |
214 output = self.RunShellCommand(['pm', 'path', package], single_line=True, | |
215 check_return=True) | |
216 if not output: | |
217 return None | |
218 if not output.startswith('package:'): | |
219 raise device_errors.CommandFailedError('pm path returned: %r' % output, | |
220 str(self)) | |
221 return output[len('package:'):] | |
222 | |
223 @decorators.WithTimeoutAndRetriesFromInstance() | |
206 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None): | 224 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None): |
207 """Wait for the device to fully boot. | 225 """Wait for the device to fully boot. |
208 | 226 |
209 This means waiting for the device to boot, the package manager to be | 227 This means waiting for the device to boot, the package manager to be |
210 available, and the SD card to be ready. It can optionally mean waiting | 228 available, and the SD card to be ready. It can optionally mean waiting |
211 for wifi to come up, too. | 229 for wifi to come up, too. |
212 | 230 |
213 Args: | 231 Args: |
214 wifi: A boolean indicating if we should wait for wifi to come up or not. | 232 wifi: A boolean indicating if we should wait for wifi to come up or not. |
215 timeout: timeout in seconds | 233 timeout: timeout in seconds |
216 retries: number of retries | 234 retries: number of retries |
217 | 235 |
218 Raises: | 236 Raises: |
219 CommandFailedError on failure. | 237 CommandFailedError on failure. |
220 CommandTimeoutError if one of the component waits times out. | 238 CommandTimeoutError if one of the component waits times out. |
221 DeviceUnreachableError if the device becomes unresponsive. | 239 DeviceUnreachableError if the device becomes unresponsive. |
222 """ | 240 """ |
223 self._WaitUntilFullyBootedImpl(wifi=wifi, timeout=timeout) | 241 def sd_card_ready(): |
242 return self.RunShellCommand(['ls', self.GetExternalStoragePath()], | |
243 single_line=True, check_return=True) | |
perezju
2014/10/29 18:16:50
Note that this might throw a CommandFailedError if
jbudorick
2014/10/30 02:27:03
I've never seen this either, so I have no idea.
| |
224 | 244 |
225 def _WaitUntilFullyBootedImpl(self, wifi=False, timeout=None): | 245 def pm_ready(): |
226 if timeout is None: | 246 try: |
227 timeout = self._default_timeout | 247 return self.GetApplicationPath('android') |
228 self.old_interface.WaitForSystemBootCompleted(timeout) | 248 except device_errors.CommandFailedError: |
229 self.old_interface.WaitForDevicePm() | 249 return False |
perezju
2014/10/29 18:16:50
We need to catch CommandFailedError here because t
| |
230 self.old_interface.WaitForSdCardReady(timeout) | 250 |
251 def boot_completed(): | |
252 return self.GetProp('sys.boot_completed') == '1' | |
253 | |
254 def wifi_enabled(): | |
255 return 'Wi-Fi is enabled' in self.RunShellCommand(['dumpsys', 'wifi']) | |
256 | |
257 self.adb.WaitForDevice() | |
258 self._WaitFor(sd_card_ready) | |
259 self._WaitFor(pm_ready) | |
260 self._WaitFor(boot_completed) | |
231 if wifi: | 261 if wifi: |
232 while not 'Wi-Fi is enabled' in ( | 262 self._WaitFor(wifi_enabled) |
233 self.old_interface.RunShellCommand('dumpsys wifi')): | 263 |
234 time.sleep(1) | 264 def _WaitFor(self, condition, wait_period=5, max_tries=None): |
265 """Wait for a condition to become true. | |
266 | |
267 Repeadly call the function condition(), with no arguments, until it returns | |
268 a true value. | |
269 | |
270 This method assumes that it is being called within a TimeoutRetryThread. | |
271 | |
272 Args: | |
273 condition: function with the condition to check | |
274 wait_period: how long to wait before retrying to check the condition | |
275 max_tries: maximum number of checks to make, the default tries forever | |
276 (usually until a timeout error is raised) | |
277 | |
278 Returns: | |
279 The true value returned by the condition, or None if the condition was | |
280 not met after max_tries. | |
281 """ | |
282 thread = threading.current_thread() | |
283 while max_tries is None or max_tries > 0: | |
284 r = condition() | |
285 if max_tries is not None: | |
286 max_tries -= 1 | |
287 if r: | |
288 logging.info('(device: %s) condition %r met (%1.2fs) at %s', | |
289 str(self), condition.__name__, thread.ElapsedTime(), thread.name) | |
jbudorick
2014/10/30 02:27:03
no need for the thread name, we already print it w
perezju
2014/10/30 18:44:45
Looking at that message, we don't need to include
| |
290 return r | |
291 else: | |
292 logging.info('(device: %s) condition %r not yet met (%1.2fs) at %s', | |
293 str(self), condition.__name__, thread.ElapsedTime(), thread.name) | |
jbudorick
2014/10/30 02:27:03
ditto
| |
294 self.adb.Wait(wait_period) | |
295 return None | |
235 | 296 |
236 REBOOT_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT | 297 REBOOT_DEFAULT_TIMEOUT = 10 * _DEFAULT_TIMEOUT |
237 REBOOT_DEFAULT_RETRIES = _DEFAULT_RETRIES | 298 REBOOT_DEFAULT_RETRIES = _DEFAULT_RETRIES |
238 | 299 |
239 @decorators.WithTimeoutAndRetriesDefaults( | 300 @decorators.WithTimeoutAndRetriesDefaults( |
240 REBOOT_DEFAULT_TIMEOUT, | 301 REBOOT_DEFAULT_TIMEOUT, |
241 REBOOT_DEFAULT_RETRIES) | 302 REBOOT_DEFAULT_RETRIES) |
242 def Reboot(self, block=True, timeout=None, retries=None): | 303 def Reboot(self, block=True, timeout=None, retries=None): |
243 """Reboot the device. | 304 """Reboot the device. |
244 | 305 |
245 Args: | 306 Args: |
246 block: A boolean indicating if we should wait for the reboot to complete. | 307 block: A boolean indicating if we should wait for the reboot to complete. |
247 timeout: timeout in seconds | 308 timeout: timeout in seconds |
248 retries: number of retries | 309 retries: number of retries |
249 | 310 |
250 Raises: | 311 Raises: |
251 CommandTimeoutError on timeout. | 312 CommandTimeoutError on timeout. |
252 DeviceUnreachableError on missing device. | 313 DeviceUnreachableError on missing device. |
253 """ | 314 """ |
254 self.old_interface.Reboot() | 315 def device_offline(): |
316 return not self.IsOnline() | |
317 | |
318 self.adb.Reboot() | |
319 self._cache = {} | |
320 self._WaitFor(device_offline, wait_period=1, max_tries=5) | |
255 if block: | 321 if block: |
256 self._WaitUntilFullyBootedImpl(timeout=timeout) | 322 self.WaitUntilFullyBooted() |
257 | 323 |
258 INSTALL_DEFAULT_TIMEOUT = 4 * _DEFAULT_TIMEOUT | 324 INSTALL_DEFAULT_TIMEOUT = 4 * _DEFAULT_TIMEOUT |
259 INSTALL_DEFAULT_RETRIES = _DEFAULT_RETRIES | 325 INSTALL_DEFAULT_RETRIES = _DEFAULT_RETRIES |
260 | 326 |
261 @decorators.WithTimeoutAndRetriesDefaults( | 327 @decorators.WithTimeoutAndRetriesDefaults( |
262 INSTALL_DEFAULT_TIMEOUT, | 328 INSTALL_DEFAULT_TIMEOUT, |
263 INSTALL_DEFAULT_RETRIES) | 329 INSTALL_DEFAULT_RETRIES) |
264 def Install(self, apk_path, reinstall=False, timeout=None, retries=None): | 330 def Install(self, apk_path, reinstall=False, timeout=None, retries=None): |
265 """Install an APK. | 331 """Install an APK. |
266 | 332 |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
385 # adb.Shell without a timeout-and-retries wrapper. | 451 # adb.Shell without a timeout-and-retries wrapper. |
386 output = self.adb.Shell(cmd, expect_rc=0, timeout=timeout, retries=0) | 452 output = self.adb.Shell(cmd, expect_rc=0, timeout=timeout, retries=0) |
387 except device_errors.AdbShellCommandFailedError as e: | 453 except device_errors.AdbShellCommandFailedError as e: |
388 if check_return: | 454 if check_return: |
389 raise | 455 raise |
390 else: | 456 else: |
391 output = e.output | 457 output = e.output |
392 | 458 |
393 output = output.splitlines() | 459 output = output.splitlines() |
394 if single_line: | 460 if single_line: |
395 if len(output) != 1: | 461 if not output: |
396 msg = 'exactly one line of output expected, but got: %s' | 462 return '' |
jbudorick
2014/10/30 02:27:03
What's up with this? If a caller requests a single
perezju
2014/10/30 18:44:45
I explained this on a previous review of the code,
| |
397 raise device_errors.CommandFailedError(msg % output) | 463 elif len(output) == 1: |
398 return output[0] | 464 return output[0] |
465 else: | |
466 msg = 'one line of output was expected, but got: %s' | |
467 raise device_errors.CommandFailedError(msg % output, str(self)) | |
399 else: | 468 else: |
400 return output | 469 return output |
401 | 470 |
402 @decorators.WithTimeoutAndRetriesFromInstance() | 471 @decorators.WithTimeoutAndRetriesFromInstance() |
403 def KillAll(self, process_name, signum=9, as_root=False, blocking=False, | 472 def KillAll(self, process_name, signum=9, as_root=False, blocking=False, |
404 timeout=None, retries=None): | 473 timeout=None, retries=None): |
405 """Kill all processes with the given name on the device. | 474 """Kill all processes with the given name on the device. |
406 | 475 |
407 Args: | 476 Args: |
408 process_name: A string containing the name of the process to kill. | 477 process_name: A string containing the name of the process to kill. |
(...skipping 299 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
708 try: | 777 try: |
709 self.adb.Push(zip_file.name, zip_on_device) | 778 self.adb.Push(zip_file.name, zip_on_device) |
710 self._RunShellCommandImpl( | 779 self._RunShellCommandImpl( |
711 ['unzip', zip_on_device], | 780 ['unzip', zip_on_device], |
712 as_root=True, | 781 as_root=True, |
713 env={'PATH': '$PATH:%s' % install_commands.BIN_DIR}, | 782 env={'PATH': '$PATH:%s' % install_commands.BIN_DIR}, |
714 check_return=True) | 783 check_return=True) |
715 finally: | 784 finally: |
716 if zip_proc.is_alive(): | 785 if zip_proc.is_alive(): |
717 zip_proc.terminate() | 786 zip_proc.terminate() |
718 if self._IsOnlineImpl(): | 787 if self.IsOnline(): |
719 self._RunShellCommandImpl(['rm', zip_on_device], check_return=True) | 788 self._RunShellCommandImpl(['rm', zip_on_device], check_return=True) |
720 | 789 |
721 @staticmethod | 790 @staticmethod |
722 def _CreateDeviceZip(zip_path, host_device_tuples): | 791 def _CreateDeviceZip(zip_path, host_device_tuples): |
723 with zipfile.ZipFile(zip_path, 'w') as zip_file: | 792 with zipfile.ZipFile(zip_path, 'w') as zip_file: |
724 for host_path, device_path in host_device_tuples: | 793 for host_path, device_path in host_device_tuples: |
725 if os.path.isfile(host_path): | 794 if os.path.isfile(host_path): |
726 zip_file.write(host_path, device_path, zipfile.ZIP_DEFLATED) | 795 zip_file.write(host_path, device_path, zipfile.ZIP_DEFLATED) |
727 else: | 796 else: |
728 for hd, _, files in os.walk(host_path): | 797 for hd, _, files in os.walk(host_path): |
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
887 | 956 |
888 Returns: | 957 Returns: |
889 True if the device-side property changed and a restart is required as a | 958 True if the device-side property changed and a restart is required as a |
890 result, False otherwise. | 959 result, False otherwise. |
891 | 960 |
892 Raises: | 961 Raises: |
893 CommandTimeoutError on timeout. | 962 CommandTimeoutError on timeout. |
894 """ | 963 """ |
895 return self.old_interface.SetJavaAssertsEnabled(enabled) | 964 return self.old_interface.SetJavaAssertsEnabled(enabled) |
896 | 965 |
897 @decorators.WithTimeoutAndRetriesFromInstance() | 966 @property |
898 def GetProp(self, property_name, timeout=None, retries=None): | 967 def build_type(self): |
968 """Returns the build type of the system (e.g. userdebug).""" | |
969 return self.GetProp('ro.build.type', cache=True) | |
970 | |
971 def GetProp(self, property_name, cache=False, timeout=None, retries=None): | |
899 """Gets a property from the device. | 972 """Gets a property from the device. |
900 | 973 |
901 Args: | 974 Args: |
902 property_name: A string containing the name of the property to get from | 975 property_name: A string containing the name of the property to get from |
903 the device. | 976 the device. |
977 cache: A boolean indicating whether to cache the value of this property. | |
904 timeout: timeout in seconds | 978 timeout: timeout in seconds |
905 retries: number of retries | 979 retries: number of retries |
906 | 980 |
907 Returns: | 981 Returns: |
908 The value of the device's |property_name| property. | 982 The value of the device's |property_name| property. |
909 | 983 |
910 Raises: | 984 Raises: |
911 CommandTimeoutError on timeout. | 985 CommandTimeoutError on timeout. |
912 """ | 986 """ |
913 return self._GetPropImpl(property_name) | 987 cache_key = '_prop:' + property_name |
914 | 988 if cache and cache_key in self._cache: |
915 def _GetPropImpl(self, property_name): | 989 return self._cache[cache_key] |
916 return self.old_interface.system_properties[property_name] | 990 else: |
991 # timeout and retries are handled down at run shell, because we don't | |
992 # want to apply them in the other branch when reading from the cache | |
993 value = self.RunShellCommand(['getprop', property_name], | |
994 single_line=True, check_return=True, | |
995 timeout=timeout, retries=retries) | |
996 if cache or cache_key in self._cache: | |
997 self._cache[cache_key] = value | |
998 return value | |
917 | 999 |
918 @decorators.WithTimeoutAndRetriesFromInstance() | 1000 @decorators.WithTimeoutAndRetriesFromInstance() |
919 def SetProp(self, property_name, value, timeout=None, retries=None): | 1001 def SetProp(self, property_name, value, check=False, timeout=None, |
1002 retries=None): | |
920 """Sets a property on the device. | 1003 """Sets a property on the device. |
921 | 1004 |
922 Args: | 1005 Args: |
923 property_name: A string containing the name of the property to set on | 1006 property_name: A string containing the name of the property to set on |
924 the device. | 1007 the device. |
925 value: A string containing the value to set to the property on the | 1008 value: A string containing the value to set to the property on the |
926 device. | 1009 device. |
1010 check: A boolean indicating whether to check that the property was | |
1011 successfully set on the device. | |
927 timeout: timeout in seconds | 1012 timeout: timeout in seconds |
928 retries: number of retries | 1013 retries: number of retries |
929 | 1014 |
930 Raises: | 1015 Raises: |
1016 CommandFailedError if check is true and the property was not correctly | |
1017 set on the device (e.g. because it is not rooted). | |
931 CommandTimeoutError on timeout. | 1018 CommandTimeoutError on timeout. |
932 """ | 1019 """ |
933 self.old_interface.system_properties[property_name] = value | 1020 self.RunShellCommand(['setprop', property_name, value], check_return=True) |
1021 if property_name in self._cache: | |
1022 del self._cache[property_name] | |
1023 # TODO(perezju) remove the option and make the check mandatory, but using a | |
1024 # single shell script to both set- and getprop. | |
1025 if check and value != self.GetProp(property_name): | |
1026 raise device_errors.CommandFailedError( | |
1027 'Unable to set property %r on the device to %r' | |
1028 % (property_name, value), str(self)) | |
934 | 1029 |
935 @decorators.WithTimeoutAndRetriesFromInstance() | 1030 @decorators.WithTimeoutAndRetriesFromInstance() |
936 def GetABI(self, timeout=None, retries=None): | 1031 def GetABI(self, timeout=None, retries=None): |
937 """Gets the device main ABI. | 1032 """Gets the device main ABI. |
938 | 1033 |
939 Args: | 1034 Args: |
940 timeout: timeout in seconds | 1035 timeout: timeout in seconds |
941 retries: number of retries | 1036 retries: number of retries |
942 | 1037 |
943 Returns: | 1038 Returns: |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1062 Returns: | 1157 Returns: |
1063 A Parallelizer operating over |devices|. | 1158 A Parallelizer operating over |devices|. |
1064 """ | 1159 """ |
1065 if not devices or len(devices) == 0: | 1160 if not devices or len(devices) == 0: |
1066 devices = pylib.android_commands.GetAttachedDevices() | 1161 devices = pylib.android_commands.GetAttachedDevices() |
1067 parallelizer_type = (parallelizer.Parallelizer if async | 1162 parallelizer_type = (parallelizer.Parallelizer if async |
1068 else parallelizer.SyncParallelizer) | 1163 else parallelizer.SyncParallelizer) |
1069 return parallelizer_type([ | 1164 return parallelizer_type([ |
1070 d if isinstance(d, DeviceUtils) else DeviceUtils(d) | 1165 d if isinstance(d, DeviceUtils) else DeviceUtils(d) |
1071 for d in devices]) | 1166 for d in devices]) |
OLD | NEW |