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

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: do not catch CommandFailedError in _WaitFor 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'
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
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
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
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
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
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
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])
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698