| OLD | NEW |
| 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 import atexit | 5 import atexit |
| 6 import hashlib | 6 import hashlib |
| 7 import json | 7 import json |
| 8 import logging | 8 import logging |
| 9 import os | 9 import os |
| 10 import os.path | 10 import os.path |
| (...skipping 20 matching lines...) Expand all Loading... |
| 31 ] | 31 ] |
| 32 | 32 |
| 33 # Tags used by native logging reflected in the logcat. | 33 # Tags used by native logging reflected in the logcat. |
| 34 _LOGCAT_NATIVE_TAGS = [ | 34 _LOGCAT_NATIVE_TAGS = [ |
| 35 'chromium', | 35 'chromium', |
| 36 'sky', | 36 'sky', |
| 37 ] | 37 ] |
| 38 | 38 |
| 39 _MOJO_SHELL_PACKAGE_NAME = 'org.chromium.mojo.shell' | 39 _MOJO_SHELL_PACKAGE_NAME = 'org.chromium.mojo.shell' |
| 40 | 40 |
| 41 # Used to parse the output of `adb devices`. |
| 42 _ADB_DEVICES_HEADER = 'List of devices attached' |
| 43 |
| 41 | 44 |
| 42 _logger = logging.getLogger() | 45 _logger = logging.getLogger() |
| 43 | 46 |
| 44 | 47 |
| 45 def _exit_if_needed(process): | 48 def _exit_if_needed(process): |
| 46 """Exits |process| if it is still alive.""" | 49 """Exits |process| if it is still alive.""" |
| 47 if process.poll() is None: | 50 if process.poll() is None: |
| 48 process.kill() | 51 process.kill() |
| 49 | 52 |
| 50 | 53 |
| 51 def _find_available_port(netstat_output, max_attempts=10000): | 54 def _find_available_port(netstat_output, max_attempts=10000): |
| 52 opened = [int(x.strip().split()[3].split(':')[1]) | 55 opened = [int(x.strip().split()[3].split(':')[1]) |
| 53 for x in netstat_output if x.startswith(' tcp')] | 56 for x in netstat_output if x.startswith(' tcp')] |
| 54 for _ in xrange(max_attempts): | 57 for _ in xrange(max_attempts): |
| 55 port = random.randint(4096, 16384) | 58 port = random.randint(4096, 16384) |
| 56 if port not in opened: | 59 if port not in opened: |
| 57 return port | 60 return port |
| 58 else: | 61 else: |
| 59 raise Exception('Failed to identify an available port.') | 62 raise Exception('Failed to identify an available port.') |
| 60 | 63 |
| 61 | 64 |
| 62 def _find_available_host_port(): | 65 def _find_available_host_port(): |
| 63 netstat_output = subprocess.check_output(['netstat']) | 66 netstat_output = subprocess.check_output(['netstat']) |
| 64 return _find_available_port(netstat_output) | 67 return _find_available_port(netstat_output) |
| 65 | 68 |
| 69 def parse_adb_devices_output(adb_devices_output): |
| 70 """Parses the output of the `adb devices` command, returning a dictionary |
| 71 mapping device id to the status of the device, as printed by `adb devices`. |
| 72 """ |
| 73 # Split into lines skipping empty ones. |
| 74 lines = [line.strip() for line in adb_devices_output.split('\n') |
| 75 if line.strip()] |
| 76 |
| 77 if _ADB_DEVICES_HEADER not in lines: |
| 78 return None |
| 79 |
| 80 # The header can be preceeded by output informing of adb server being spawned, |
| 81 # but all non-empty lines after the header describe connected devices. |
| 82 device_specs = lines[lines.index(_ADB_DEVICES_HEADER) + 1:] |
| 83 split_specs = [spec.split() for spec in device_specs] |
| 84 return {split_spec[0]: split_spec[1] for split_spec in split_specs |
| 85 if len(split_spec) == 2} |
| 86 |
| 66 | 87 |
| 67 class AndroidShell(Shell): | 88 class AndroidShell(Shell): |
| 68 """Wrapper around Mojo shell running on an Android device. | 89 """Wrapper around Mojo shell running on an Android device. |
| 69 | 90 |
| 70 Args: | 91 Args: |
| 71 adb_path: Path to adb, optional if adb is in PATH. | 92 adb_path: Path to adb, optional if adb is in PATH. |
| 72 target_device: Device to run on, if multiple devices are connected. | 93 target_device: Device to run on, if multiple devices are connected. |
| 73 logcat_tags: Comma-separated list of additional logcat tags to use. | 94 logcat_tags: Comma-separated list of additional logcat tags to use. |
| 74 """ | 95 """ |
| 75 | 96 |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 206 device is available. Otherwise, it checks that there is exactly one | 227 device is available. Otherwise, it checks that there is exactly one |
| 207 available device. | 228 available device. |
| 208 | 229 |
| 209 Returns: | 230 Returns: |
| 210 A tuple of (result, msg). |result| is True iff if the device is correctly | 231 A tuple of (result, msg). |result| is True iff if the device is correctly |
| 211 configured and False otherwise. |msg| is the reason for failure if | 232 configured and False otherwise. |msg| is the reason for failure if |
| 212 |result| is False and None otherwise. | 233 |result| is False and None otherwise. |
| 213 """ | 234 """ |
| 214 adb_devices_output = subprocess.check_output( | 235 adb_devices_output = subprocess.check_output( |
| 215 self._adb_command(['devices'])) | 236 self._adb_command(['devices'])) |
| 216 # Skip the header line, strip empty lines at the end. | 237 devices = parse_adb_devices_output(adb_devices_output) |
| 217 device_list = [line.strip() for line in adb_devices_output.split('\n')[1:] | 238 |
| 218 if line.strip()] | 239 if not devices: |
| 240 return False, 'No devices connected.' |
| 219 | 241 |
| 220 if self.target_device: | 242 if self.target_device: |
| 221 if any([line.startswith(self.target_device) and | 243 if (self.target_device in devices and |
| 222 line.endswith('device') for line in device_list]): | 244 devices[self.target_device] == 'device'): |
| 223 return True, None | 245 return True, None |
| 224 else: | 246 else: |
| 225 return False, 'Cannot connect to the selected device.' | 247 return False, ('Cannot connect to the selected device, status: ' + |
| 248 devices[self.target_device]) |
| 226 | 249 |
| 227 if len(device_list) > 1: | 250 if len(devices) > 1: |
| 228 return False, ('More than one device connected and target device not ' | 251 return False, ('More than one device connected and target device not ' |
| 229 'specified.') | 252 'specified.') |
| 230 | 253 |
| 231 if not len(device_list): | 254 if not devices.itervalues().next() == 'device': |
| 232 return False, 'No devices connected.' | |
| 233 | |
| 234 if not device_list[0].endswith('device'): | |
| 235 return False, 'Connected device is not available.' | 255 return False, 'Connected device is not available.' |
| 236 | 256 |
| 237 if require_root and not self._run_adb_as_root(): | 257 if require_root and not self._run_adb_as_root(): |
| 238 return False, 'Cannot run on an unrooted device.' | 258 return False, 'Cannot run on an unrooted device.' |
| 239 | 259 |
| 240 return True, None | 260 return True, None |
| 241 | 261 |
| 242 def install_apk(self, shell_apk_path): | 262 def install_apk(self, shell_apk_path): |
| 243 """Installs the apk on the device. | 263 """Installs the apk on the device. |
| 244 | 264 |
| (...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 421 Results.output = rf.read() | 441 Results.output = rf.read() |
| 422 | 442 |
| 423 run_thread = threading.Thread(target=do_run) | 443 run_thread = threading.Thread(target=do_run) |
| 424 run_thread.start() | 444 run_thread.start() |
| 425 run_thread.join(timeout) | 445 run_thread.join(timeout) |
| 426 | 446 |
| 427 if run_thread.is_alive(): | 447 if run_thread.is_alive(): |
| 428 self.stop_shell() | 448 self.stop_shell() |
| 429 return None, Results.output, True | 449 return None, Results.output, True |
| 430 return None, Results.output, False | 450 return None, Results.output, False |
| OLD | NEW |