OLD | NEW |
---|---|
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...) Loading... | |
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...) Loading... | |
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...) Loading... | |
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...) Loading... | |
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...) Loading... | |
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...) Loading... | |
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() |
OLD | NEW |