| 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 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 75 | 75 |
| 76 def __init__(self, adb_path="adb", target_device=None, logcat_tags=None, | 76 def __init__(self, adb_path="adb", target_device=None, logcat_tags=None, |
| 77 verbose_pipe=None): | 77 verbose_pipe=None): |
| 78 self.adb_path = adb_path | 78 self.adb_path = adb_path |
| 79 self.target_device = target_device | 79 self.target_device = target_device |
| 80 self.stop_shell_registered = False | 80 self.stop_shell_registered = False |
| 81 self.adb_running_as_root = None | 81 self.adb_running_as_root = None |
| 82 self.additional_logcat_tags = logcat_tags | 82 self.additional_logcat_tags = logcat_tags |
| 83 self.verbose_pipe = verbose_pipe if verbose_pipe else open(os.devnull, 'w') | 83 self.verbose_pipe = verbose_pipe if verbose_pipe else open(os.devnull, 'w') |
| 84 | 84 |
| 85 def _AdbCommand(self, args): | 85 def _adb_command(self, args): |
| 86 """Forms an adb command from the given arguments, prepending the adb path | 86 """Forms an adb command from the given arguments, prepending the adb path |
| 87 and adding a target device specifier, if needed. | 87 and adding a target device specifier, if needed. |
| 88 """ | 88 """ |
| 89 adb_command = [self.adb_path] | 89 adb_command = [self.adb_path] |
| 90 if self.target_device: | 90 if self.target_device: |
| 91 adb_command.extend(['-s', self.target_device]) | 91 adb_command.extend(['-s', self.target_device]) |
| 92 adb_command.extend(args) | 92 adb_command.extend(args) |
| 93 return adb_command | 93 return adb_command |
| 94 | 94 |
| 95 def _ReadFifo(self, fifo_path, pipe, on_fifo_closed, max_attempts=5): | 95 def _read_fifo(self, fifo_path, pipe, on_fifo_closed, max_attempts=5): |
| 96 """Reads |fifo_path| on the device and write the contents to |pipe|. | 96 """Reads |fifo_path| on the device and write the contents to |pipe|. |
| 97 | 97 |
| 98 Calls |on_fifo_closed| when the fifo is closed. This method will try to find | 98 Calls |on_fifo_closed| when the fifo is closed. This method will try to find |
| 99 the path up to |max_attempts|, waiting 1 second between each attempt. If it | 99 the path up to |max_attempts|, waiting 1 second between each attempt. If it |
| 100 cannot find |fifo_path|, a exception will be raised. | 100 cannot find |fifo_path|, a exception will be raised. |
| 101 """ | 101 """ |
| 102 fifo_command = self._AdbCommand( | 102 fifo_command = self._adb_command( |
| 103 ['shell', 'test -e "%s"; echo $?' % fifo_path]) | 103 ['shell', 'test -e "%s"; echo $?' % fifo_path]) |
| 104 | 104 |
| 105 def Run(): | 105 def _run(): |
| 106 def _WaitForFifo(): | 106 def _wait_for_fifo(): |
| 107 for _ in xrange(max_attempts): | 107 for _ in xrange(max_attempts): |
| 108 if subprocess.check_output(fifo_command)[0] == '0': | 108 if subprocess.check_output(fifo_command)[0] == '0': |
| 109 return | 109 return |
| 110 time.sleep(1) | 110 time.sleep(1) |
| 111 if on_fifo_closed: | 111 if on_fifo_closed: |
| 112 on_fifo_closed() | 112 on_fifo_closed() |
| 113 raise Exception("Unable to find fifo.") | 113 raise Exception("Unable to find fifo.") |
| 114 _WaitForFifo() | 114 _wait_for_fifo() |
| 115 stdout_cat = subprocess.Popen( | 115 stdout_cat = subprocess.Popen( |
| 116 self._AdbCommand(['shell', 'cat', fifo_path]), stdout=pipe) | 116 self._adb_command(['shell', 'cat', fifo_path]), stdout=pipe) |
| 117 atexit.register(_exit_if_needed, stdout_cat) | 117 atexit.register(_exit_if_needed, stdout_cat) |
| 118 stdout_cat.wait() | 118 stdout_cat.wait() |
| 119 if on_fifo_closed: | 119 if on_fifo_closed: |
| 120 on_fifo_closed() | 120 on_fifo_closed() |
| 121 | 121 |
| 122 thread = threading.Thread(target=Run, name="StdoutRedirector") | 122 thread = threading.Thread(target=_run, name="StdoutRedirector") |
| 123 thread.start() | 123 thread.start() |
| 124 | 124 |
| 125 def _FindAvailableDevicePort(self): | 125 def _find_available_device_port(self): |
| 126 netstat_output = subprocess.check_output( | 126 netstat_output = subprocess.check_output( |
| 127 self._AdbCommand(['shell', 'netstat'])) | 127 self._adb_command(['shell', 'netstat'])) |
| 128 return _find_available_port(netstat_output) | 128 return _find_available_port(netstat_output) |
| 129 | 129 |
| 130 def _ForwardDevicePortToHost(self, device_port, host_port): | 130 def _forward_device_port_to_host(self, device_port, host_port): |
| 131 """Maps the device port to the host port. If |device_port| is 0, a random | 131 """Maps the device port to the host port. If |device_port| is 0, a random |
| 132 available port is chosen. | 132 available port is chosen. |
| 133 | 133 |
| 134 Returns: | 134 Returns: |
| 135 The device port. | 135 The device port. |
| 136 """ | 136 """ |
| 137 assert host_port | 137 assert host_port |
| 138 # Root is not required for `adb forward` (hence we don't check the return | 138 # Root is not required for `adb forward` (hence we don't check the return |
| 139 # value), but if we can run adb as root, we have to do it now, because | 139 # value), but if we can run adb as root, we have to do it now, because |
| 140 # restarting adbd as root clears any port mappings. See | 140 # restarting adbd as root clears any port mappings. See |
| 141 # https://github.com/domokit/devtools/issues/20. | 141 # https://github.com/domokit/devtools/issues/20. |
| 142 self._RunAdbAsRoot() | 142 self._run_adb_as_root() |
| 143 | 143 |
| 144 if device_port == 0: | 144 if device_port == 0: |
| 145 # TODO(ppi): Should we have a retry loop to handle the unlikely races? | 145 # TODO(ppi): Should we have a retry loop to handle the unlikely races? |
| 146 device_port = self._FindAvailableDevicePort() | 146 device_port = self._find_available_device_port() |
| 147 subprocess.check_call(self._AdbCommand([ | 147 subprocess.check_call(self._adb_command([ |
| 148 "reverse", "tcp:%d" % device_port, "tcp:%d" % host_port])) | 148 "reverse", "tcp:%d" % device_port, "tcp:%d" % host_port])) |
| 149 | 149 |
| 150 def _UnmapPort(): | 150 def _unmap_port(): |
| 151 unmap_command = self._AdbCommand([ | 151 unmap_command = self._adb_command([ |
| 152 "reverse", "--remove", "tcp:%d" % device_port]) | 152 "reverse", "--remove", "tcp:%d" % device_port]) |
| 153 subprocess.Popen(unmap_command) | 153 subprocess.Popen(unmap_command) |
| 154 atexit.register(_UnmapPort) | 154 atexit.register(_unmap_port) |
| 155 return device_port | 155 return device_port |
| 156 | 156 |
| 157 def _ForwardHostPortToDevice(self, host_port, device_port): | 157 def _forward_host_port_to_device(self, host_port, device_port): |
| 158 """Maps the host port to the device port. If |host_port| is 0, a random | 158 """Maps the host port to the device port. If |host_port| is 0, a random |
| 159 available port is chosen. | 159 available port is chosen. |
| 160 | 160 |
| 161 Returns: | 161 Returns: |
| 162 The host port. | 162 The host port. |
| 163 """ | 163 """ |
| 164 assert device_port | 164 assert device_port |
| 165 self._RunAdbAsRoot() | 165 self._run_adb_as_root() |
| 166 | 166 |
| 167 if host_port == 0: | 167 if host_port == 0: |
| 168 # TODO(ppi): Should we have a retry loop to handle the unlikely races? | 168 # TODO(ppi): Should we have a retry loop to handle the unlikely races? |
| 169 host_port = _find_available_host_port() | 169 host_port = _find_available_host_port() |
| 170 subprocess.check_call(self._AdbCommand([ | 170 subprocess.check_call(self._adb_command([ |
| 171 "forward", 'tcp:%d' % host_port, 'tcp:%d' % device_port])) | 171 "forward", 'tcp:%d' % host_port, 'tcp:%d' % device_port])) |
| 172 | 172 |
| 173 def _UnmapPort(): | 173 def _unmap_port(): |
| 174 unmap_command = self._AdbCommand([ | 174 unmap_command = self._adb_command([ |
| 175 "forward", "--remove", "tcp:%d" % device_port]) | 175 "forward", "--remove", "tcp:%d" % device_port]) |
| 176 subprocess.Popen(unmap_command) | 176 subprocess.Popen(unmap_command) |
| 177 atexit.register(_UnmapPort) | 177 atexit.register(_unmap_port) |
| 178 return host_port | 178 return host_port |
| 179 | 179 |
| 180 def _RunAdbAsRoot(self): | 180 def _run_adb_as_root(self): |
| 181 if self.adb_running_as_root is not None: | 181 if self.adb_running_as_root is not None: |
| 182 return self.adb_running_as_root | 182 return self.adb_running_as_root |
| 183 | 183 |
| 184 if ('cannot run as root' not in subprocess.check_output( | 184 if ('cannot run as root' not in subprocess.check_output( |
| 185 self._AdbCommand(['root']))): | 185 self._adb_command(['root']))): |
| 186 # Wait for adbd to restart. | 186 # Wait for adbd to restart. |
| 187 subprocess.check_call( | 187 subprocess.check_call( |
| 188 self._AdbCommand(['wait-for-device']), | 188 self._adb_command(['wait-for-device']), |
| 189 stdout=self.verbose_pipe) | 189 stdout=self.verbose_pipe) |
| 190 self.adb_running_as_root = True | 190 self.adb_running_as_root = True |
| 191 else: | 191 else: |
| 192 self.adb_running_as_root = False | 192 self.adb_running_as_root = False |
| 193 | 193 |
| 194 return self.adb_running_as_root | 194 return self.adb_running_as_root |
| 195 | 195 |
| 196 def _IsShellPackageInstalled(self): | 196 def _is_shell_package_installed(self): |
| 197 # Adb should print one line if the package is installed and return empty | 197 # Adb should print one line if the package is installed and return empty |
| 198 # string otherwise. | 198 # string otherwise. |
| 199 return len(subprocess.check_output(self._AdbCommand([ | 199 return len(subprocess.check_output(self._adb_command([ |
| 200 'shell', 'pm', 'list', 'packages', _MOJO_SHELL_PACKAGE_NAME]))) > 0 | 200 'shell', 'pm', 'list', 'packages', _MOJO_SHELL_PACKAGE_NAME]))) > 0 |
| 201 | 201 |
| 202 def CheckDevice(self): | 202 def check_device(self): |
| 203 """Verifies if the device configuration allows adb to run. | 203 """Verifies if the device configuration allows adb to run. |
| 204 | 204 |
| 205 If a target device was indicated in the constructor, it checks that the | 205 If a target device was indicated in the constructor, it checks that the |
| 206 device is available. Otherwise, it checks that there is exactly one | 206 device is available. Otherwise, it checks that there is exactly one |
| 207 available device. | 207 available device. |
| 208 | 208 |
| 209 Returns: | 209 Returns: |
| 210 A tuple of (result, msg). |result| is True iff if the device is correctly | 210 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 | 211 configured and False otherwise. |msg| is the reason for failure if |
| 212 |result| is False and None otherwise. | 212 |result| is False and None otherwise. |
| 213 """ | 213 """ |
| 214 adb_devices_output = subprocess.check_output( | 214 adb_devices_output = subprocess.check_output( |
| 215 self._AdbCommand(['devices'])) | 215 self._adb_command(['devices'])) |
| 216 # Skip the header line, strip empty lines at the end. | 216 # Skip the header line, strip empty lines at the end. |
| 217 device_list = [line.strip() for line in adb_devices_output.split('\n')[1:] | 217 device_list = [line.strip() for line in adb_devices_output.split('\n')[1:] |
| 218 if line.strip()] | 218 if line.strip()] |
| 219 | 219 |
| 220 if self.target_device: | 220 if self.target_device: |
| 221 if any([line.startswith(self.target_device) and | 221 if any([line.startswith(self.target_device) and |
| 222 line.endswith('device') for line in device_list]): | 222 line.endswith('device') for line in device_list]): |
| 223 return True, None | 223 return True, None |
| 224 else: | 224 else: |
| 225 return False, 'Cannot connect to the selected device.' | 225 return False, 'Cannot connect to the selected device.' |
| 226 | 226 |
| 227 if len(device_list) > 1: | 227 if len(device_list) > 1: |
| 228 return False, ('More than one device connected and target device not ' | 228 return False, ('More than one device connected and target device not ' |
| 229 'specified.') | 229 'specified.') |
| 230 | 230 |
| 231 if not len(device_list): | 231 if not len(device_list): |
| 232 return False, 'No devices connected.' | 232 return False, 'No devices connected.' |
| 233 | 233 |
| 234 if not device_list[0].endswith('device'): | 234 if not device_list[0].endswith('device'): |
| 235 return False, 'Connected device is not available.' | 235 return False, 'Connected device is not available.' |
| 236 | 236 |
| 237 return True, None | 237 return True, None |
| 238 | 238 |
| 239 def InstallApk(self, shell_apk_path): | 239 def install_apk(self, shell_apk_path): |
| 240 """Installs the apk on the device. | 240 """Installs the apk on the device. |
| 241 | 241 |
| 242 This method computes checksum of the APK and skips the installation if the | 242 This method computes checksum of the APK and skips the installation if the |
| 243 fingerprint matches the one saved on the device upon the previous | 243 fingerprint matches the one saved on the device upon the previous |
| 244 installation. | 244 installation. |
| 245 | 245 |
| 246 Args: | 246 Args: |
| 247 shell_apk_path: Path to the shell Android binary. | 247 shell_apk_path: Path to the shell Android binary. |
| 248 """ | 248 """ |
| 249 device_sha1_path = '/sdcard/%s/%s.sha1' % (_MOJO_SHELL_PACKAGE_NAME, | 249 device_sha1_path = '/sdcard/%s/%s.sha1' % (_MOJO_SHELL_PACKAGE_NAME, |
| 250 'MojoShell') | 250 'MojoShell') |
| 251 apk_sha1 = hashlib.sha1(open(shell_apk_path, 'rb').read()).hexdigest() | 251 apk_sha1 = hashlib.sha1(open(shell_apk_path, 'rb').read()).hexdigest() |
| 252 device_apk_sha1 = subprocess.check_output(self._AdbCommand([ | 252 device_apk_sha1 = subprocess.check_output(self._adb_command([ |
| 253 'shell', 'cat', device_sha1_path])) | 253 'shell', 'cat', device_sha1_path])) |
| 254 do_install = (apk_sha1 != device_apk_sha1 or | 254 do_install = (apk_sha1 != device_apk_sha1 or |
| 255 not self._IsShellPackageInstalled()) | 255 not self._is_shell_package_installed()) |
| 256 | 256 |
| 257 if do_install: | 257 if do_install: |
| 258 subprocess.check_call( | 258 subprocess.check_call( |
| 259 self._AdbCommand(['install', '-r', shell_apk_path, '-i', | 259 self._adb_command(['install', '-r', shell_apk_path, '-i', |
| 260 _MOJO_SHELL_PACKAGE_NAME]), | 260 _MOJO_SHELL_PACKAGE_NAME]), |
| 261 stdout=self.verbose_pipe) | 261 stdout=self.verbose_pipe) |
| 262 | 262 |
| 263 # Update the stamp on the device. | 263 # Update the stamp on the device. |
| 264 with tempfile.NamedTemporaryFile() as fp: | 264 with tempfile.NamedTemporaryFile() as fp: |
| 265 fp.write(apk_sha1) | 265 fp.write(apk_sha1) |
| 266 fp.flush() | 266 fp.flush() |
| 267 subprocess.check_call(self._AdbCommand(['push', fp.name, | 267 subprocess.check_call(self._adb_command(['push', fp.name, |
| 268 device_sha1_path]), | 268 device_sha1_path]), |
| 269 stdout=self.verbose_pipe) | 269 stdout=self.verbose_pipe) |
| 270 else: | 270 else: |
| 271 # To ensure predictable state after running InstallApk(), we need to stop | 271 # To ensure predictable state after running install_apk(), we need to stop |
| 272 # the shell here, as this is what "adb install" implicitly does. | 272 # the shell here, as this is what "adb install" implicitly does. |
| 273 self.StopShell() | 273 self.stop_shell() |
| 274 | 274 |
| 275 def StartShell(self, | 275 def start_shell(self, |
| 276 arguments, | 276 arguments, |
| 277 stdout=None, | 277 stdout=None, |
| 278 on_application_stop=None): | 278 on_application_stop=None): |
| 279 """Starts the mojo shell, passing it the given arguments. | 279 """Starts the mojo shell, passing it the given arguments. |
| 280 | 280 |
| 281 Args: | 281 Args: |
| 282 arguments: List of arguments for the shell. It must contain the | 282 arguments: List of arguments for the shell. It must contain the |
| 283 "--origin=" arg. shell_arguments.configure_local_origin() can be used | 283 "--origin=" arg. shell_arguments.configure_local_origin() can be used |
| 284 to set up a local directory on the host machine as origin. | 284 to set up a local directory on the host machine as origin. |
| 285 stdout: Valid argument for subprocess.Popen() or None. | 285 stdout: Valid argument for subprocess.Popen() or None. |
| 286 """ | 286 """ |
| 287 if not self.stop_shell_registered: | 287 if not self.stop_shell_registered: |
| 288 atexit.register(self.StopShell) | 288 atexit.register(self.stop_shell) |
| 289 self.stop_shell_registered = True | 289 self.stop_shell_registered = True |
| 290 | 290 |
| 291 STDOUT_PIPE = "/data/data/%s/stdout.fifo" % _MOJO_SHELL_PACKAGE_NAME | 291 STDOUT_PIPE = "/data/data/%s/stdout.fifo" % _MOJO_SHELL_PACKAGE_NAME |
| 292 | 292 |
| 293 cmd = self._AdbCommand(['shell', 'am', 'start', | 293 cmd = self._adb_command(['shell', 'am', 'start', |
| 294 '-S', | 294 '-S', |
| 295 '-a', 'android.intent.action.VIEW', | 295 '-a', 'android.intent.action.VIEW', |
| 296 '-n', '%s/.MojoShellActivity' % | 296 '-n', '%s/.MojoShellActivity' % |
| 297 _MOJO_SHELL_PACKAGE_NAME]) | 297 _MOJO_SHELL_PACKAGE_NAME]) |
| 298 | 298 |
| 299 parameters = [] | 299 parameters = [] |
| 300 if stdout or on_application_stop: | 300 if stdout or on_application_stop: |
| 301 # We need to run as root to access the fifo file we use for stdout | 301 # We need to run as root to access the fifo file we use for stdout |
| 302 # redirection. | 302 # redirection. |
| 303 if self._RunAdbAsRoot(): | 303 if self._run_adb_as_root(): |
| 304 # Remove any leftover fifo file after the previous run. | 304 # Remove any leftover fifo file after the previous run. |
| 305 subprocess.check_call(self._AdbCommand( | 305 subprocess.check_call(self._adb_command( |
| 306 ['shell', 'rm', '-f', STDOUT_PIPE])) | 306 ['shell', 'rm', '-f', STDOUT_PIPE])) |
| 307 | 307 |
| 308 parameters.append('--fifo-path=%s' % STDOUT_PIPE) | 308 parameters.append('--fifo-path=%s' % STDOUT_PIPE) |
| 309 self._ReadFifo(STDOUT_PIPE, stdout, on_application_stop) | 309 self._read_fifo(STDOUT_PIPE, stdout, on_application_stop) |
| 310 else: | 310 else: |
| 311 _logger.warning("Running without root access, full stdout of the " | 311 _logger.warning("Running without root access, full stdout of the " |
| 312 "shell won't be available.") | 312 "shell won't be available.") |
| 313 # The origin has to be specified whether it's local or external. | 313 # The origin has to be specified whether it's local or external. |
| 314 assert any("--origin=" in arg for arg in arguments) | 314 assert any("--origin=" in arg for arg in arguments) |
| 315 parameters.extend(arguments) | 315 parameters.extend(arguments) |
| 316 | 316 |
| 317 if parameters: | 317 if parameters: |
| 318 encodedParameters = json.dumps(parameters) | 318 encodedParameters = json.dumps(parameters) |
| 319 cmd += ['--es', 'encodedParameters', encodedParameters] | 319 cmd += ['--es', 'encodedParameters', encodedParameters] |
| 320 | 320 |
| 321 subprocess.check_call(cmd, stdout=self.verbose_pipe) | 321 subprocess.check_call(cmd, stdout=self.verbose_pipe) |
| 322 | 322 |
| 323 def StopShell(self): | 323 def stop_shell(self): |
| 324 """Stops the mojo shell.""" | 324 """Stops the mojo shell.""" |
| 325 subprocess.check_call(self._AdbCommand(['shell', | 325 subprocess.check_call(self._adb_command(['shell', |
| 326 'am', | 326 'am', |
| 327 'force-stop', | 327 'force-stop', |
| 328 _MOJO_SHELL_PACKAGE_NAME])) | 328 _MOJO_SHELL_PACKAGE_NAME])) |
| 329 | 329 |
| 330 def CleanLogs(self): | 330 def clean_logs(self): |
| 331 """Cleans the logs on the device.""" | 331 """Cleans the logs on the device.""" |
| 332 subprocess.check_call(self._AdbCommand(['logcat', '-c'])) | 332 subprocess.check_call(self._adb_command(['logcat', '-c'])) |
| 333 | 333 |
| 334 def ShowLogs(self, include_native_logs=True): | 334 def show_logs(self, include_native_logs=True): |
| 335 """Displays the log for the mojo shell. | 335 """Displays the log for the mojo shell. |
| 336 | 336 |
| 337 Returns: | 337 Returns: |
| 338 The process responsible for reading the logs. | 338 The process responsible for reading the logs. |
| 339 """ | 339 """ |
| 340 tags = _LOGCAT_JAVA_TAGS | 340 tags = _LOGCAT_JAVA_TAGS |
| 341 if include_native_logs: | 341 if include_native_logs: |
| 342 tags.extend(_LOGCAT_NATIVE_TAGS) | 342 tags.extend(_LOGCAT_NATIVE_TAGS) |
| 343 if self.additional_logcat_tags is not None: | 343 if self.additional_logcat_tags is not None: |
| 344 tags.extend(self.additional_logcat_tags.split(",")) | 344 tags.extend(self.additional_logcat_tags.split(",")) |
| 345 logcat = subprocess.Popen( | 345 logcat = subprocess.Popen( |
| 346 self._AdbCommand(['logcat', '-s', ' '.join(tags)]), | 346 self._adb_command(['logcat', '-s', ' '.join(tags)]), |
| 347 stdout=sys.stdout) | 347 stdout=sys.stdout) |
| 348 atexit.register(_exit_if_needed, logcat) | 348 atexit.register(_exit_if_needed, logcat) |
| 349 return logcat | 349 return logcat |
| 350 | 350 |
| 351 def ForwardObservatoryPorts(self): | 351 def forward_observatory_ports(self): |
| 352 """Forwards the ports used by the dart observatories to the host machine. | 352 """Forwards the ports used by the dart observatories to the host machine. |
| 353 """ | 353 """ |
| 354 logcat = subprocess.Popen(self._AdbCommand(['logcat']), | 354 logcat = subprocess.Popen(self._adb_command(['logcat']), |
| 355 stdout=subprocess.PIPE) | 355 stdout=subprocess.PIPE) |
| 356 atexit.register(_exit_if_needed, logcat) | 356 atexit.register(_exit_if_needed, logcat) |
| 357 | 357 |
| 358 def _ForwardObservatoriesAsNeeded(): | 358 def _forward_observatories_as_needed(): |
| 359 while True: | 359 while True: |
| 360 line = logcat.stdout.readline() | 360 line = logcat.stdout.readline() |
| 361 if not line: | 361 if not line: |
| 362 break | 362 break |
| 363 match = re.search(r'Observatory listening on http://127.0.0.1:(\d+)', | 363 match = re.search(r'Observatory listening on http://127.0.0.1:(\d+)', |
| 364 line) | 364 line) |
| 365 if match: | 365 if match: |
| 366 device_port = int(match.group(1)) | 366 device_port = int(match.group(1)) |
| 367 host_port = self._ForwardHostPortToDevice(0, device_port) | 367 host_port = self._forward_host_port_to_device(0, device_port) |
| 368 print ("Dart observatory available at the host at http://127.0.0.1:%d" | 368 print ("Dart observatory available at the host at http://127.0.0.1:%d" |
| 369 % host_port) | 369 % host_port) |
| 370 | 370 |
| 371 logcat_watch_thread = threading.Thread(target=_ForwardObservatoriesAsNeeded) | 371 logcat_watch_thread = threading.Thread( |
| 372 target=_forward_observatories_as_needed) |
| 372 logcat_watch_thread.start() | 373 logcat_watch_thread.start() |
| 373 | 374 |
| 374 @overrides(Shell) | 375 @overrides(Shell) |
| 375 def ServeLocalDirectory(self, local_dir_path, port=0): | 376 def serve_local_directory(self, local_dir_path, port=0): |
| 376 assert local_dir_path | 377 assert local_dir_path |
| 377 mappings = [('', local_dir_path)] | 378 mappings = [('', local_dir_path)] |
| 378 server_address = start_http_server(mappings, host_port=port) | 379 server_address = start_http_server(mappings, host_port=port) |
| 379 | 380 |
| 380 return 'http://127.0.0.1:%d/' % self._ForwardDevicePortToHost( | 381 return 'http://127.0.0.1:%d/' % self._forward_device_port_to_host( |
| 381 port, server_address[1]) | 382 port, server_address[1]) |
| 382 | 383 |
| 383 @overrides(Shell) | 384 @overrides(Shell) |
| 384 def ServeLocalDirectories(self, mappings, port=0): | 385 def serve_local_directories(self, mappings, port=0): |
| 385 assert mappings | 386 assert mappings |
| 386 server_address = start_http_server(mappings, host_port=port) | 387 server_address = start_http_server(mappings, host_port=port) |
| 387 | 388 |
| 388 return 'http://127.0.0.1:%d/' % self._ForwardDevicePortToHost( | 389 return 'http://127.0.0.1:%d/' % self._forward_device_port_to_host( |
| 389 port, server_address[1]) | 390 port, server_address[1]) |
| 390 | 391 |
| 391 @overrides(Shell) | 392 @overrides(Shell) |
| 392 def ForwardHostPortToShell(self, host_port): | 393 def forward_host_port_to_shell(self, host_port): |
| 393 self._ForwardHostPortToDevice(host_port, host_port) | 394 self._forward_host_port_to_device(host_port, host_port) |
| 394 | 395 |
| 395 @overrides(Shell) | 396 @overrides(Shell) |
| 396 def Run(self, arguments): | 397 def run(self, arguments): |
| 397 self.CleanLogs() | 398 self.clean_logs() |
| 398 self.ForwardObservatoryPorts() | 399 self.forward_observatory_ports() |
| 399 | 400 |
| 400 # If we are running as root, don't carry over the native logs from logcat - | 401 # If we are running as root, don't carry over the native logs from logcat - |
| 401 # we will have these in the stdout. | 402 # we will have these in the stdout. |
| 402 p = self.ShowLogs(include_native_logs=(not self._RunAdbAsRoot())) | 403 p = self.show_logs(include_native_logs=(not self._run_adb_as_root())) |
| 403 self.StartShell(arguments, sys.stdout, p.terminate) | 404 self.start_shell(arguments, sys.stdout, p.terminate) |
| 404 p.wait() | 405 p.wait() |
| 405 return None | 406 return None |
| 406 | 407 |
| 407 @overrides(Shell) | 408 @overrides(Shell) |
| 408 def RunAndGetOutput(self, arguments, timeout=None): | 409 def run_and_get_output(self, arguments, timeout=None): |
| 409 class Results: | 410 class Results: |
| 410 """Workaround for Python scoping rules that prevent assigning to variables | 411 """Workaround for Python scoping rules that prevent assigning to variables |
| 411 from the outer scope. | 412 from the outer scope. |
| 412 """ | 413 """ |
| 413 output = None | 414 output = None |
| 414 | 415 |
| 415 def do_run(): | 416 def do_run(): |
| 416 (r, w) = os.pipe() | 417 (r, w) = os.pipe() |
| 417 with os.fdopen(r, "r") as rf: | 418 with os.fdopen(r, "r") as rf: |
| 418 with os.fdopen(w, "w") as wf: | 419 with os.fdopen(w, "w") as wf: |
| 419 self.StartShell(arguments, wf, wf.close) | 420 self.start_shell(arguments, wf, wf.close) |
| 420 Results.output = rf.read() | 421 Results.output = rf.read() |
| 421 | 422 |
| 422 run_thread = threading.Thread(target=do_run) | 423 run_thread = threading.Thread(target=do_run) |
| 423 run_thread.start() | 424 run_thread.start() |
| 424 run_thread.join(timeout) | 425 run_thread.join(timeout) |
| 425 | 426 |
| 426 if run_thread.is_alive(): | 427 if run_thread.is_alive(): |
| 427 self.StopShell() | 428 self.stop_shell() |
| 428 return None, Results.output, True | 429 return None, Results.output, True |
| 429 return None, Results.output, False | 430 return None, Results.output, False |
| OLD | NEW |