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 |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
102 emulator-5554 offline | 102 emulator-5554 offline |
103 """ | 103 """ |
104 re_device = re.compile('^([a-zA-Z0-9_:.-]+)\tdevice$', re.MULTILINE) | 104 re_device = re.compile('^([a-zA-Z0-9_:.-]+)\tdevice$', re.MULTILINE) |
105 devices = re_device.findall(cmd_helper.GetCmdOutput(['adb', 'devices'])) | 105 devices = re_device.findall(cmd_helper.GetCmdOutput(['adb', 'devices'])) |
106 preferred_device = os.environ.get('ANDROID_SERIAL') | 106 preferred_device = os.environ.get('ANDROID_SERIAL') |
107 if preferred_device in devices: | 107 if preferred_device in devices: |
108 devices.remove(preferred_device) | 108 devices.remove(preferred_device) |
109 devices.insert(0, preferred_device) | 109 devices.insert(0, preferred_device) |
110 return devices | 110 return devices |
111 | 111 |
| 112 |
112 def IsDeviceAttached(device): | 113 def IsDeviceAttached(device): |
113 return device in GetAttachedDevices() | 114 return device in GetAttachedDevices() |
114 | 115 |
| 116 |
115 def _GetFilesFromRecursiveLsOutput(path, ls_output, re_file, utc_offset=None): | 117 def _GetFilesFromRecursiveLsOutput(path, ls_output, re_file, utc_offset=None): |
116 """Gets a list of files from `ls` command output. | 118 """Gets a list of files from `ls` command output. |
117 | 119 |
118 Python's os.walk isn't used because it doesn't work over adb shell. | 120 Python's os.walk isn't used because it doesn't work over adb shell. |
119 | 121 |
120 Args: | 122 Args: |
121 path: The path to list. | 123 path: The path to list. |
122 ls_output: A list of lines returned by an `ls -lR` command. | 124 ls_output: A list of lines returned by an `ls -lR` command. |
123 re_file: A compiled regular expression which parses a line into named groups | 125 re_file: A compiled regular expression which parses a line into named groups |
124 consisting of at minimum "filename", "date", "time", "size" and | 126 consisting of at minimum "filename", "date", "time", "size" and |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
157 utc_offset = file_match.group('timezone') | 159 utc_offset = file_match.group('timezone') |
158 if isinstance(utc_offset, str) and len(utc_offset) == 5: | 160 if isinstance(utc_offset, str) and len(utc_offset) == 5: |
159 utc_delta = datetime.timedelta(hours=int(utc_offset[1:3]), | 161 utc_delta = datetime.timedelta(hours=int(utc_offset[1:3]), |
160 minutes=int(utc_offset[3:5])) | 162 minutes=int(utc_offset[3:5])) |
161 if utc_offset[0:1] == '-': | 163 if utc_offset[0:1] == '-': |
162 utc_delta = -utc_delta | 164 utc_delta = -utc_delta |
163 lastmod -= utc_delta | 165 lastmod -= utc_delta |
164 files[filename] = (int(file_match.group('size')), lastmod) | 166 files[filename] = (int(file_match.group('size')), lastmod) |
165 return files | 167 return files |
166 | 168 |
| 169 |
167 def _ComputeFileListHash(md5sum_output): | 170 def _ComputeFileListHash(md5sum_output): |
168 """Returns a list of MD5 strings from the provided md5sum output.""" | 171 """Returns a list of MD5 strings from the provided md5sum output.""" |
169 return [line.split(' ')[0] for line in md5sum_output] | 172 return [line.split(' ')[0] for line in md5sum_output] |
170 | 173 |
| 174 |
171 def _HasAdbPushSucceeded(command_output): | 175 def _HasAdbPushSucceeded(command_output): |
172 """Returns whether adb push has succeeded from the provided output.""" | 176 """Returns whether adb push has succeeded from the provided output.""" |
173 if not command_output: | 177 if not command_output: |
174 return False | 178 return False |
175 # Success looks like this: "3035 KB/s (12512056 bytes in 4.025s)" | 179 # Success looks like this: "3035 KB/s (12512056 bytes in 4.025s)" |
176 # Errors look like this: "failed to copy ... " | 180 # Errors look like this: "failed to copy ... " |
177 if not re.search('^[0-9]', command_output.splitlines()[-1]): | 181 if not re.search('^[0-9]', command_output.splitlines()[-1]): |
178 logging.critical('PUSH FAILED: ' + command_output) | 182 logging.critical('PUSH FAILED: ' + command_output) |
179 return False | 183 return False |
180 return True | 184 return True |
181 | 185 |
| 186 |
182 def GetLogTimestamp(log_line, year): | 187 def GetLogTimestamp(log_line, year): |
183 """Returns the timestamp of the given |log_line| in the given year.""" | 188 """Returns the timestamp of the given |log_line| in the given year.""" |
184 try: | 189 try: |
185 return datetime.datetime.strptime('%s-%s' % (year, log_line[:18]), | 190 return datetime.datetime.strptime('%s-%s' % (year, log_line[:18]), |
186 '%Y-%m-%d %H:%M:%S.%f') | 191 '%Y-%m-%d %H:%M:%S.%f') |
187 except (ValueError, IndexError): | 192 except (ValueError, IndexError): |
188 logging.critical('Error reading timestamp from ' + log_line) | 193 logging.critical('Error reading timestamp from ' + log_line) |
189 return None | 194 return None |
190 | 195 |
191 | 196 |
(...skipping 12 matching lines...) Expand all Loading... |
204 self._device = device | 209 self._device = device |
205 self._logcat = None | 210 self._logcat = None |
206 self.logcat_process = None | 211 self.logcat_process = None |
207 self._logcat_tmpoutfile = None | 212 self._logcat_tmpoutfile = None |
208 self._pushed_files = [] | 213 self._pushed_files = [] |
209 self._device_utc_offset = self.RunShellCommand('date +%z')[0] | 214 self._device_utc_offset = self.RunShellCommand('date +%z')[0] |
210 self._md5sum_path = '' | 215 self._md5sum_path = '' |
211 self._external_storage = '' | 216 self._external_storage = '' |
212 self._util_wrapper = '' | 217 self._util_wrapper = '' |
213 | 218 |
| 219 def _LogShell(self, cmd): |
| 220 """Logs the adb shell command.""" |
| 221 if self._device: |
| 222 device_repr = self._device[-4:] |
| 223 else: |
| 224 device_repr = '????' |
| 225 logging.info('[%s]> %s', device_repr, cmd) |
| 226 |
214 def Adb(self): | 227 def Adb(self): |
215 """Returns our AdbInterface to avoid us wrapping all its methods.""" | 228 """Returns our AdbInterface to avoid us wrapping all its methods.""" |
216 return self._adb | 229 return self._adb |
217 | 230 |
218 def IsOnline(self): | 231 def IsOnline(self): |
219 """Checks whether the device is online. | 232 """Checks whether the device is online. |
220 | 233 |
221 Returns: | 234 Returns: |
222 True if device is in 'device' mode, False otherwise. | 235 True if device is in 'device' mode, False otherwise. |
223 """ | 236 """ |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
310 """Uninstalls the specified package from the device. | 323 """Uninstalls the specified package from the device. |
311 | 324 |
312 Args: | 325 Args: |
313 package: Name of the package to remove. | 326 package: Name of the package to remove. |
314 | 327 |
315 Returns: | 328 Returns: |
316 A status string returned by adb uninstall | 329 A status string returned by adb uninstall |
317 """ | 330 """ |
318 uninstall_command = 'uninstall %s' % package | 331 uninstall_command = 'uninstall %s' % package |
319 | 332 |
320 logging.info('>>> $' + uninstall_command) | 333 self._LogShell(uninstall_command) |
321 return self._adb.SendCommand(uninstall_command, timeout_time=60) | 334 return self._adb.SendCommand(uninstall_command, timeout_time=60) |
322 | 335 |
323 def Install(self, package_file_path, reinstall=False): | 336 def Install(self, package_file_path, reinstall=False): |
324 """Installs the specified package to the device. | 337 """Installs the specified package to the device. |
325 | 338 |
326 Args: | 339 Args: |
327 package_file_path: Path to .apk file to install. | 340 package_file_path: Path to .apk file to install. |
328 reinstall: Reinstall an existing apk, keeping the data. | 341 reinstall: Reinstall an existing apk, keeping the data. |
329 | 342 |
330 Returns: | 343 Returns: |
331 A status string returned by adb install | 344 A status string returned by adb install |
332 """ | 345 """ |
333 assert os.path.isfile(package_file_path), ('<%s> is not file' % | 346 assert os.path.isfile(package_file_path), ('<%s> is not file' % |
334 package_file_path) | 347 package_file_path) |
335 | 348 |
336 install_cmd = ['install'] | 349 install_cmd = ['install'] |
337 | 350 |
338 if reinstall: | 351 if reinstall: |
339 install_cmd.append('-r') | 352 install_cmd.append('-r') |
340 | 353 |
341 install_cmd.append(package_file_path) | 354 install_cmd.append(package_file_path) |
342 install_cmd = ' '.join(install_cmd) | 355 install_cmd = ' '.join(install_cmd) |
343 | 356 |
344 logging.info('>>> $' + install_cmd) | 357 self._LogShell(install_cmd) |
345 return self._adb.SendCommand(install_cmd, | 358 return self._adb.SendCommand(install_cmd, |
346 timeout_time=2 * 60, | 359 timeout_time=2 * 60, |
347 retry_count=0) | 360 retry_count=0) |
348 | 361 |
349 def ManagedInstall(self, apk_path, keep_data=False, package_name=None, | 362 def ManagedInstall(self, apk_path, keep_data=False, package_name=None, |
350 reboots_on_failure=2): | 363 reboots_on_failure=2): |
351 """Installs specified package and reboots device on timeouts. | 364 """Installs specified package and reboots device on timeouts. |
352 | 365 |
353 Args: | 366 Args: |
354 apk_path: Path to .apk file to install. | 367 apk_path: Path to .apk file to install. |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
465 command: String containing the shell command to send. Must not include | 478 command: String containing the shell command to send. Must not include |
466 the single quotes as we use them to escape the whole command. | 479 the single quotes as we use them to escape the whole command. |
467 timeout_time: Number of seconds to wait for command to respond before | 480 timeout_time: Number of seconds to wait for command to respond before |
468 retrying, used by AdbInterface.SendShellCommand. | 481 retrying, used by AdbInterface.SendShellCommand. |
469 log_result: Boolean to indicate whether we should log the result of the | 482 log_result: Boolean to indicate whether we should log the result of the |
470 shell command. | 483 shell command. |
471 | 484 |
472 Returns: | 485 Returns: |
473 list containing the lines of output received from running the command | 486 list containing the lines of output received from running the command |
474 """ | 487 """ |
475 logging.info('>>> $' + command) | 488 self._LogShell(command) |
476 if "'" in command: logging.warning(command + " contains ' quotes") | 489 if "'" in command: logging.warning(command + " contains ' quotes") |
477 result = self._adb.SendShellCommand( | 490 result = self._adb.SendShellCommand( |
478 "'%s'" % command, timeout_time).splitlines() | 491 "'%s'" % command, timeout_time).splitlines() |
479 if ['error: device not found'] == result: | 492 if ['error: device not found'] == result: |
480 raise errors.DeviceUnresponsiveError('device not found') | 493 raise errors.DeviceUnresponsiveError('device not found') |
481 if log_result: | 494 if log_result: |
482 logging.info('\n>>> '.join(result)) | 495 self._LogShell('\n'.join(result)) |
483 return result | 496 return result |
484 | 497 |
485 def GetShellCommandStatusAndOutput(self, command, timeout_time=20, | 498 def GetShellCommandStatusAndOutput(self, command, timeout_time=20, |
486 log_result=False): | 499 log_result=False): |
487 """See RunShellCommand() above. | 500 """See RunShellCommand() above. |
488 | 501 |
489 Returns: | 502 Returns: |
490 The tuple (exit code, list of output lines). | 503 The tuple (exit code, list of output lines). |
491 """ | 504 """ |
492 lines = self.RunShellCommand( | 505 lines = self.RunShellCommand( |
(...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
690 return | 703 return |
691 | 704 |
692 # They don't match, so remove everything first and then create it. | 705 # They don't match, so remove everything first and then create it. |
693 if os.path.isdir(local_path): | 706 if os.path.isdir(local_path): |
694 self.RunShellCommand('rm -r %s' % device_path, timeout_time=2 * 60) | 707 self.RunShellCommand('rm -r %s' % device_path, timeout_time=2 * 60) |
695 self.RunShellCommand('mkdir -p %s' % device_path) | 708 self.RunShellCommand('mkdir -p %s' % device_path) |
696 | 709 |
697 # NOTE: We can't use adb_interface.Push() because it hardcodes a timeout of | 710 # NOTE: We can't use adb_interface.Push() because it hardcodes a timeout of |
698 # 60 seconds which isn't sufficient for a lot of users of this method. | 711 # 60 seconds which isn't sufficient for a lot of users of this method. |
699 push_command = 'push %s %s' % (local_path, device_path) | 712 push_command = 'push %s %s' % (local_path, device_path) |
700 logging.info('>>> $' + push_command) | 713 self._LogShell(push_command) |
701 output = self._adb.SendCommand(push_command, timeout_time=30 * 60) | 714 output = self._adb.SendCommand(push_command, timeout_time=30 * 60) |
702 assert _HasAdbPushSucceeded(output) | 715 assert _HasAdbPushSucceeded(output) |
703 | 716 |
704 | 717 |
705 def GetFileContents(self, filename, log_result=False): | 718 def GetFileContents(self, filename, log_result=False): |
706 """Gets contents from the file specified by |filename|.""" | 719 """Gets contents from the file specified by |filename|.""" |
707 return self.RunShellCommand('cat "%s" 2>/dev/null' % filename, | 720 return self.RunShellCommand('cat "%s" 2>/dev/null' % filename, |
708 log_result=log_result) | 721 log_result=log_result) |
709 | 722 |
710 def SetFileContents(self, filename, contents): | 723 def SetFileContents(self, filename, contents): |
(...skipping 535 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1246 | 1259 |
1247 Args: | 1260 Args: |
1248 test: Test class/method. | 1261 test: Test class/method. |
1249 test_package: Name of the test jar. | 1262 test_package: Name of the test jar. |
1250 timeout: Timeout time in seconds. | 1263 timeout: Timeout time in seconds. |
1251 | 1264 |
1252 Returns: | 1265 Returns: |
1253 An instance of am_instrument_parser.TestResult object. | 1266 An instance of am_instrument_parser.TestResult object. |
1254 """ | 1267 """ |
1255 cmd = 'uiautomator runtest %s -e class %s' % (test_package, test) | 1268 cmd = 'uiautomator runtest %s -e class %s' % (test_package, test) |
1256 logging.info('>>> $' + cmd) | 1269 self._LogShell(cmd) |
1257 output = self._adb.SendShellCommand(cmd, timeout_time=timeout) | 1270 output = self._adb.SendShellCommand(cmd, timeout_time=timeout) |
1258 # uiautomator doesn't fully conform to the instrumenation test runner | 1271 # uiautomator doesn't fully conform to the instrumenation test runner |
1259 # convention and doesn't terminate with INSTRUMENTATION_CODE. | 1272 # convention and doesn't terminate with INSTRUMENTATION_CODE. |
1260 # Just assume the first result is valid. | 1273 # Just assume the first result is valid. |
1261 (test_results, _) = am_instrument_parser.ParseAmInstrumentOutput(output) | 1274 (test_results, _) = am_instrument_parser.ParseAmInstrumentOutput(output) |
1262 return test_results[0] | 1275 return test_results[0] |
1263 | 1276 |
1264 | 1277 |
1265 class NewLineNormalizer(object): | 1278 class NewLineNormalizer(object): |
1266 """A file-like object to normalize EOLs to '\n'. | 1279 """A file-like object to normalize EOLs to '\n'. |
1267 | 1280 |
1268 Pexpect runs adb within a pseudo-tty device (see | 1281 Pexpect runs adb within a pseudo-tty device (see |
1269 http://www.noah.org/wiki/pexpect), so any '\n' printed by adb is written | 1282 http://www.noah.org/wiki/pexpect), so any '\n' printed by adb is written |
1270 as '\r\n' to the logfile. Since adb already uses '\r\n' to terminate | 1283 as '\r\n' to the logfile. Since adb already uses '\r\n' to terminate |
1271 lines, the log ends up having '\r\r\n' at the end of each line. This | 1284 lines, the log ends up having '\r\r\n' at the end of each line. This |
1272 filter replaces the above with a single '\n' in the data stream. | 1285 filter replaces the above with a single '\n' in the data stream. |
1273 """ | 1286 """ |
1274 def __init__(self, output): | 1287 def __init__(self, output): |
1275 self._output = output | 1288 self._output = output |
1276 | 1289 |
1277 def write(self, data): | 1290 def write(self, data): |
1278 data = data.replace('\r\r\n', '\n') | 1291 data = data.replace('\r\r\n', '\n') |
1279 self._output.write(data) | 1292 self._output.write(data) |
1280 | 1293 |
1281 def flush(self): | 1294 def flush(self): |
1282 self._output.flush() | 1295 self._output.flush() |
OLD | NEW |