Chromium Code Reviews

Side by Side Diff: build/android/pylib/android_commands.py

Issue 99713002: Factor out a system_properties interface for interacting with getprop/setprop. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | | Annotate | Revision Log
OLDNEW
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 # Copyright (c) 2012 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 an interface to communicate with the device via the adb command. 5 """Provides an interface to communicate with the device via the adb command.
6 6
7 Assumes adb binary is currently on system path. 7 Assumes adb binary is currently on system path.
8 """ 8 """
9 9
10 import collections 10 import collections
11 import datetime 11 import datetime
12 import logging 12 import logging
13 import os 13 import os
14 import re 14 import re
15 import shlex 15 import shlex
16 import signal 16 import signal
17 import subprocess 17 import subprocess
18 import sys 18 import sys
19 import tempfile 19 import tempfile
20 import time 20 import time
21 21
22 import cmd_helper 22 import cmd_helper
23 import constants 23 import constants
24 import screenshot 24 import screenshot
25 import system_properties
25 26
26 from utils import host_path_finder 27 from utils import host_path_finder
27 28
28 try: 29 try:
29 from pylib import pexpect 30 from pylib import pexpect
30 except: 31 except:
31 pexpect = None 32 pexpect = None
32 33
33 sys.path.append(os.path.join( 34 sys.path.append(os.path.join(
34 constants.DIR_SOURCE_ROOT, 'third_party', 'android_testrunner')) 35 constants.DIR_SOURCE_ROOT, 'third_party', 'android_testrunner'))
(...skipping 186 matching lines...)
221 """Returns the timestamp of the given |log_line| in the given year.""" 222 """Returns the timestamp of the given |log_line| in the given year."""
222 try: 223 try:
223 return datetime.datetime.strptime('%s-%s' % (year, log_line[:18]), 224 return datetime.datetime.strptime('%s-%s' % (year, log_line[:18]),
224 '%Y-%m-%d %H:%M:%S.%f') 225 '%Y-%m-%d %H:%M:%S.%f')
225 except (ValueError, IndexError): 226 except (ValueError, IndexError):
226 logging.critical('Error reading timestamp from ' + log_line) 227 logging.critical('Error reading timestamp from ' + log_line)
227 return None 228 return None
228 229
229 230
230 class AndroidCommands(object): 231 class AndroidCommands(object):
231 """Helper class for communicating with Android device via adb. 232 """Helper class for communicating with Android device via adb."""
232 233
233 Args: 234 def __init__(self, device=None, api_strict_mode=False):
234 device: If given, adb commands are only send to the device of this ID. 235 """Constructor.
235 Otherwise commands are sent to all attached devices.
236 """
237 236
238 def __init__(self, device=None): 237 Args:
238 device: If given, adb commands are only send to the device of this ID.
239 Otherwise commands are sent to all attached devices.
240 api_strict_mode: A boolean indicating whether fatal errors should be
241 raised if this API is used improperly.
242 """
239 adb_dir = os.path.dirname(constants.ADB_PATH) 243 adb_dir = os.path.dirname(constants.ADB_PATH)
240 if adb_dir and adb_dir not in os.environ['PATH'].split(os.pathsep): 244 if adb_dir and adb_dir not in os.environ['PATH'].split(os.pathsep):
241 # Required by third_party/android_testrunner to call directly 'adb'. 245 # Required by third_party/android_testrunner to call directly 'adb'.
242 os.environ['PATH'] += os.pathsep + adb_dir 246 os.environ['PATH'] += os.pathsep + adb_dir
243 self._adb = adb_interface.AdbInterface() 247 self._adb = adb_interface.AdbInterface()
244 if device: 248 if device:
245 self._adb.SetTargetSerial(device) 249 self._adb.SetTargetSerial(device)
246 self._device = device 250 self._device = device
247 self._logcat = None 251 self._logcat = None
248 self.logcat_process = None 252 self.logcat_process = None
249 self._logcat_tmpoutfile = None 253 self._logcat_tmpoutfile = None
250 self._pushed_files = [] 254 self._pushed_files = []
251 self._device_utc_offset = None 255 self._device_utc_offset = None
252 self._potential_push_size = 0 256 self._potential_push_size = 0
253 self._actual_push_size = 0 257 self._actual_push_size = 0
254 self._external_storage = '' 258 self._external_storage = ''
255 self._util_wrapper = '' 259 self._util_wrapper = ''
260 self._api_strict_mode = api_strict_mode
261 self._system_properties = system_properties.SystemProperties(self.Adb())
262
263 if not self._api_strict_mode:
264 logging.warning(
265 'API STRICT MODE IS DISABLED.\n'
266 'It should be enabled as soon as possible as it will eventually '
267 'become the default.')
268
269 @property
270 def system_properties(self):
271 return self._system_properties
256 272
257 def _LogShell(self, cmd): 273 def _LogShell(self, cmd):
258 """Logs the adb shell command.""" 274 """Logs the adb shell command."""
259 if self._device: 275 if self._device:
260 device_repr = self._device[-4:] 276 device_repr = self._device[-4:]
261 else: 277 else:
262 device_repr = '????' 278 device_repr = '????'
263 logging.info('[%s]> %s', device_repr, cmd) 279 logging.info('[%s]> %s', device_repr, cmd)
264 280
265 def Adb(self): 281 def Adb(self):
266 """Returns our AdbInterface to avoid us wrapping all its methods.""" 282 """Returns our AdbInterface to avoid us wrapping all its methods."""
283 # TODO(tonyg): Disable this method when in _api_strict_mode.
267 return self._adb 284 return self._adb
268 285
269 def GetDevice(self): 286 def GetDevice(self):
270 """Returns the device serial.""" 287 """Returns the device serial."""
271 return self._device 288 return self._device
272 289
273 def IsOnline(self): 290 def IsOnline(self):
274 """Checks whether the device is online. 291 """Checks whether the device is online.
275 292
276 Returns: 293 Returns:
(...skipping 68 matching lines...)
345 Args: 362 Args:
346 full_reboot: Whether to fully reboot the device or just restart the shell. 363 full_reboot: Whether to fully reboot the device or just restart the shell.
347 """ 364 """
348 # TODO(torne): hive can't reboot the device either way without breaking the 365 # TODO(torne): hive can't reboot the device either way without breaking the
349 # connection; work out if we can handle this better 366 # connection; work out if we can handle this better
350 if os.environ.get('USING_HIVE'): 367 if os.environ.get('USING_HIVE'):
351 logging.warning('Ignoring reboot request as we are on hive') 368 logging.warning('Ignoring reboot request as we are on hive')
352 return 369 return
353 if full_reboot or not self.IsRootEnabled(): 370 if full_reboot or not self.IsRootEnabled():
354 self._adb.SendCommand('reboot') 371 self._adb.SendCommand('reboot')
372 self._system_properties = system_properties.SystemProperties(self.Adb())
355 timeout = 300 373 timeout = 300
356 retries = 1 374 retries = 1
357 # Wait for the device to disappear. 375 # Wait for the device to disappear.
358 while retries < 10 and self.IsOnline(): 376 while retries < 10 and self.IsOnline():
359 time.sleep(1) 377 time.sleep(1)
360 retries += 1 378 retries += 1
361 else: 379 else:
362 self.RestartShell() 380 self.RestartShell()
363 timeout = 120 381 timeout = 120
364 # To run tests we need at least the package manager and the sd card (or 382 # To run tests we need at least the package manager and the sd card (or
365 # other external storage) to be ready. 383 # other external storage) to be ready.
366 self.WaitForDevicePm() 384 self.WaitForDevicePm()
367 self.WaitForSdCardReady(timeout) 385 self.WaitForSdCardReady(timeout)
368 386
369 def Shutdown(self): 387 def Shutdown(self):
370 """Shuts down the device.""" 388 """Shuts down the device."""
371 self._adb.SendCommand('reboot -p') 389 self._adb.SendCommand('reboot -p')
390 self._system_properties = system_properties.SystemProperties(self.Adb())
372 391
373 def Uninstall(self, package): 392 def Uninstall(self, package):
374 """Uninstalls the specified package from the device. 393 """Uninstalls the specified package from the device.
375 394
376 Args: 395 Args:
377 package: Name of the package to remove. 396 package: Name of the package to remove.
378 397
379 Returns: 398 Returns:
380 A status string returned by adb uninstall 399 A status string returned by adb uninstall
381 """ 400 """
(...skipping 147 matching lines...)
529 set. 548 set.
530 """ 549 """
531 logging.info('Waiting for system boot completed...') 550 logging.info('Waiting for system boot completed...')
532 self._adb.SendCommand('wait-for-device') 551 self._adb.SendCommand('wait-for-device')
533 # Now the device is there, but system not boot completed. 552 # Now the device is there, but system not boot completed.
534 # Query the sys.boot_completed flag with a basic command 553 # Query the sys.boot_completed flag with a basic command
535 boot_completed = False 554 boot_completed = False
536 attempts = 0 555 attempts = 0
537 wait_period = 5 556 wait_period = 5
538 while not boot_completed and (attempts * wait_period) < wait_time: 557 while not boot_completed and (attempts * wait_period) < wait_time:
539 output = self._adb.SendShellCommand('getprop sys.boot_completed', 558 output = self.system_properties['sys.boot_completed']
540 retry_count=1)
541 output = output.strip() 559 output = output.strip()
542 if output == '1': 560 if output == '1':
543 boot_completed = True 561 boot_completed = True
544 else: 562 else:
545 # If 'error: xxx' returned when querying the flag, it means 563 # If 'error: xxx' returned when querying the flag, it means
546 # adb server lost the connection to the emulator, so restart the adb 564 # adb server lost the connection to the emulator, so restart the adb
547 # server. 565 # server.
548 if 'error:' in output: 566 if 'error:' in output:
549 self.RestartAdbServer() 567 self.RestartAdbServer()
550 time.sleep(wait_period) 568 time.sleep(wait_period)
(...skipping 13 matching lines...)
564 output = self.RunShellCommand('ls ' + external_storage) 582 output = self.RunShellCommand('ls ' + external_storage)
565 if output: 583 if output:
566 sdcard_ready = True 584 sdcard_ready = True
567 else: 585 else:
568 time.sleep(wait_period) 586 time.sleep(wait_period)
569 attempts += 1 587 attempts += 1
570 if not sdcard_ready: 588 if not sdcard_ready:
571 raise errors.WaitForResponseTimedOutError( 589 raise errors.WaitForResponseTimedOutError(
572 'SD card not ready after %s seconds' % timeout_time) 590 'SD card not ready after %s seconds' % timeout_time)
573 591
592 def _CheckCommandIsValid(self, command):
593 """Raises a ValueError if the command is not valid."""
594
595 # A dict of commands the user should not run directly and a mapping to the
596 # API they should use instead.
597 managed_commands = {
598 'getprop': 'system_properties[<PROPERTY>]',
599 'setprop': 'system_properties[<PROPERTY>]',
frankf 2013/12/03 00:20:27 Could you add 'su' to this list? People should be
tonyg 2013/12/03 01:02:02 Done.
600 }
601
602 base_command = command.split()[0]
603 if base_command in managed_commands:
604 error_msg = ('%s cannot be run directly. Instead use: %s' %
605 (base_command, managed_commands[base_command]))
606 if self._api_strict_mode:
607 raise ValueError(error_msg)
608 else:
609 logging.warning(error_msg)
610
574 # It is tempting to turn this function into a generator, however this is not 611 # It is tempting to turn this function into a generator, however this is not
575 # possible without using a private (local) adb_shell instance (to ensure no 612 # possible without using a private (local) adb_shell instance (to ensure no
576 # other command interleaves usage of it), which would defeat the main aim of 613 # other command interleaves usage of it), which would defeat the main aim of
577 # being able to reuse the adb shell instance across commands. 614 # being able to reuse the adb shell instance across commands.
578 def RunShellCommand(self, command, timeout_time=20, log_result=False): 615 def RunShellCommand(self, command, timeout_time=20, log_result=False):
579 """Send a command to the adb shell and return the result. 616 """Send a command to the adb shell and return the result.
580 617
581 Args: 618 Args:
582 command: String containing the shell command to send. Must not include 619 command: String containing the shell command to send. Must not include
583 the single quotes as we use them to escape the whole command. 620 the single quotes as we use them to escape the whole command.
584 timeout_time: Number of seconds to wait for command to respond before 621 timeout_time: Number of seconds to wait for command to respond before
585 retrying, used by AdbInterface.SendShellCommand. 622 retrying, used by AdbInterface.SendShellCommand.
586 log_result: Boolean to indicate whether we should log the result of the 623 log_result: Boolean to indicate whether we should log the result of the
587 shell command. 624 shell command.
588 625
589 Returns: 626 Returns:
590 list containing the lines of output received from running the command 627 list containing the lines of output received from running the command
591 """ 628 """
629 self._CheckCommandIsValid(command)
592 self._LogShell(command) 630 self._LogShell(command)
593 if "'" in command: logging.warning(command + " contains ' quotes") 631 if "'" in command: logging.warning(command + " contains ' quotes")
594 result = self._adb.SendShellCommand( 632 result = self._adb.SendShellCommand(
595 "'%s'" % command, timeout_time).splitlines() 633 "'%s'" % command, timeout_time).splitlines()
596 if ['error: device not found'] == result: 634 if ['error: device not found'] == result:
597 raise errors.DeviceUnresponsiveError('device not found') 635 raise errors.DeviceUnresponsiveError('device not found')
598 if log_result: 636 if log_result:
599 self._LogShell('\n'.join(result)) 637 self._LogShell('\n'.join(result))
600 return result 638 return result
601 639
(...skipping 494 matching lines...)
1096 r'\s*=\s*\w+\s*$', re.MULTILINE) 1134 r'\s*=\s*\w+\s*$', re.MULTILINE)
1097 properties = re.sub(re_replace, '', properties) 1135 properties = re.sub(re_replace, '', properties)
1098 if enable: 1136 if enable:
1099 properties += '\n%s=all\n' % JAVA_ASSERT_PROPERTY 1137 properties += '\n%s=all\n' % JAVA_ASSERT_PROPERTY
1100 1138
1101 file(temp_props_file.name, 'w').write(properties) 1139 file(temp_props_file.name, 'w').write(properties)
1102 self._adb.Push(temp_props_file.name, LOCAL_PROPERTIES_PATH) 1140 self._adb.Push(temp_props_file.name, LOCAL_PROPERTIES_PATH)
1103 1141
1104 # Next, check the current runtime value is what we need, and 1142 # Next, check the current runtime value is what we need, and
1105 # if not, set it and report that a reboot is required. 1143 # if not, set it and report that a reboot is required.
1106 was_set = 'all' in self.RunShellCommand('getprop ' + JAVA_ASSERT_PROPERTY) 1144 was_set = 'all' in self.system_properties[JAVA_ASSERT_PROPERTY]
1107 if was_set == enable: 1145 if was_set == enable:
1108 return False 1146 return False
1109 1147 self.system_properties[JAVA_ASSERT_PROPERTY] = enable and 'all' or ''
1110 self.RunShellCommand('setprop %s "%s"' % (JAVA_ASSERT_PROPERTY,
1111 enable and 'all' or ''))
1112 return True 1148 return True
1113 1149
1114 def GetBuildId(self): 1150 def GetBuildId(self):
1115 """Returns the build ID of the system (e.g. JRM79C).""" 1151 """Returns the build ID of the system (e.g. JRM79C)."""
1116 build_id = self.RunShellCommand('getprop ro.build.id')[0] 1152 build_id = self.system_properties['ro.build.id']
1117 assert build_id 1153 assert build_id
1118 return build_id 1154 return build_id
1119 1155
1120 def GetBuildType(self): 1156 def GetBuildType(self):
1121 """Returns the build type of the system (e.g. eng).""" 1157 """Returns the build type of the system (e.g. eng)."""
1122 build_type = self.RunShellCommand('getprop ro.build.type')[0] 1158 build_type = self.system_properties['ro.build.type']
1123 assert build_type 1159 assert build_type
1124 return build_type 1160 return build_type
1125 1161
1126 def GetBuildProduct(self): 1162 def GetBuildProduct(self):
1127 """Returns the build product of the device (e.g. maguro).""" 1163 """Returns the build product of the device (e.g. maguro)."""
1128 build_product = self.RunShellCommand('getprop ro.build.product')[0] 1164 build_product = self.system_properties['ro.build.product']
1129 assert build_product 1165 assert build_product
1130 return build_product 1166 return build_product
1131 1167
1132 def GetProductName(self): 1168 def GetProductName(self):
1133 """Returns the product name of the device (e.g. takju).""" 1169 """Returns the product name of the device (e.g. takju)."""
1134 name = self.RunShellCommand('getprop ro.product.name')[0] 1170 name = self.system_properties['ro.product.name']
1135 assert name 1171 assert name
1136 return name 1172 return name
1137 1173
1138 def GetBuildFingerprint(self): 1174 def GetBuildFingerprint(self):
1139 """Returns the build fingerprint of the device.""" 1175 """Returns the build fingerprint of the device."""
1140 build_fingerprint = self.RunShellCommand('getprop ro.build.fingerprint')[0] 1176 build_fingerprint = self.system_properties['ro.build.fingerprint']
1141 assert build_fingerprint 1177 assert build_fingerprint
1142 return build_fingerprint 1178 return build_fingerprint
1143 1179
1144 def GetDescription(self): 1180 def GetDescription(self):
1145 """Returns the description of the system. 1181 """Returns the description of the system.
1146 1182
1147 For example, "yakju-userdebug 4.1 JRN54F 364167 dev-keys". 1183 For example, "yakju-userdebug 4.1 JRN54F 364167 dev-keys".
1148 """ 1184 """
1149 description = self.RunShellCommand('getprop ro.build.description')[0] 1185 description = self.system_properties['ro.build.description']
1150 assert description 1186 assert description
1151 return description 1187 return description
1152 1188
1153 def GetProductModel(self): 1189 def GetProductModel(self):
1154 """Returns the name of the product model (e.g. "Galaxy Nexus") """ 1190 """Returns the name of the product model (e.g. "Galaxy Nexus") """
1155 model = self.RunShellCommand('getprop ro.product.model')[0] 1191 model = self.system_properties['ro.product.model']
1156 assert model 1192 assert model
1157 return model 1193 return model
1158 1194
1159 def GetWifiIP(self): 1195 def GetWifiIP(self):
1160 """Returns the wifi IP on the device.""" 1196 """Returns the wifi IP on the device."""
1161 wifi_ip = self.RunShellCommand('getprop dhcp.wlan0.ipaddress')[0] 1197 wifi_ip = self.system_properties['dhcp.wlan0.ipaddress']
1162 # Do not assert here. Devices (e.g. emulators) may not have a WifiIP. 1198 # Do not assert here. Devices (e.g. emulators) may not have a WifiIP.
1163 return wifi_ip 1199 return wifi_ip
1164 1200
1165 def GetSubscriberInfo(self): 1201 def GetSubscriberInfo(self):
1166 """Returns the device subscriber info (e.g. GSM and device ID) as string.""" 1202 """Returns the device subscriber info (e.g. GSM and device ID) as string."""
1167 iphone_sub = self.RunShellCommand('dumpsys iphonesubinfo') 1203 iphone_sub = self.RunShellCommand('dumpsys iphonesubinfo')
1168 assert iphone_sub 1204 assert iphone_sub
1169 return '\n'.join(iphone_sub) 1205 return '\n'.join(iphone_sub)
1170 1206
1171 def GetBatteryInfo(self): 1207 def GetBatteryInfo(self):
1172 """Returns the device battery info (e.g. status, level, etc) as string.""" 1208 """Returns the device battery info (e.g. status, level, etc) as string."""
1173 battery = self.RunShellCommand('dumpsys battery') 1209 battery = self.RunShellCommand('dumpsys battery')
1174 assert battery 1210 assert battery
1175 return '\n'.join(battery) 1211 return '\n'.join(battery)
1176 1212
1177 def GetSetupWizardStatus(self): 1213 def GetSetupWizardStatus(self):
1178 """Returns the status of the device setup wizard (e.g. DISABLED).""" 1214 """Returns the status of the device setup wizard (e.g. DISABLED)."""
1179 status = self.RunShellCommand('getprop ro.setupwizard.mode')[0] 1215 status = self.system_properties['ro.setupwizard.mode']
1180 # On some devices, the status is empty if not otherwise set. In such cases 1216 # On some devices, the status is empty if not otherwise set. In such cases
1181 # the caller should expect an empty string to be returned. 1217 # the caller should expect an empty string to be returned.
1182 return status 1218 return status
1183 1219
1184 def StartMonitoringLogcat(self, clear=True, logfile=None, filters=None): 1220 def StartMonitoringLogcat(self, clear=True, logfile=None, filters=None):
1185 """Starts monitoring the output of logcat, for use with WaitForLogMatch. 1221 """Starts monitoring the output of logcat, for use with WaitForLogMatch.
1186 1222
1187 Args: 1223 Args:
1188 clear: If True the existing logcat output will be cleared, to avoiding 1224 clear: If True the existing logcat output will be cleared, to avoiding
1189 matching historical output lurking in the log. 1225 matching historical output lurking in the log.
(...skipping 523 matching lines...)
1713 """ 1749 """
1714 def __init__(self, output): 1750 def __init__(self, output):
1715 self._output = output 1751 self._output = output
1716 1752
1717 def write(self, data): 1753 def write(self, data):
1718 data = data.replace('\r\r\n', '\n') 1754 data = data.replace('\r\r\n', '\n')
1719 self._output.write(data) 1755 self._output.write(data)
1720 1756
1721 def flush(self): 1757 def flush(self):
1722 self._output.flush() 1758 self._output.flush()
OLDNEW
« no previous file with comments | « no previous file | build/android/pylib/system_properties.py » ('j') | build/android/pylib/system_properties.py » ('J')

Powered by Google App Engine