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

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

Issue 636273004: Make TimeoutRetryThread's stoppable (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: some fixes, tests ready Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
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])
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698