OLD | NEW |
(Empty) | |
| 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 |
| 3 # found in the LICENSE file. |
| 4 |
| 5 import logging |
| 6 import os |
| 7 import pexpect |
| 8 import re |
| 9 import sys |
| 10 |
| 11 import android_commands |
| 12 from constants import CHROME_DIR |
| 13 |
| 14 class Forwarder(object): |
| 15 """Class to manage port forwards from the device to the host.""" |
| 16 |
| 17 _FORWARDER_PATH = '/data/local/tmp/forwarder' |
| 18 _TIMEOUT_SECS = 30 |
| 19 |
| 20 def __init__(self, adb, port_pairs, tool, host_name): |
| 21 """Forwards TCP ports on the device back to the host. |
| 22 |
| 23 Works like adb forward, but in reverse. |
| 24 |
| 25 Args: |
| 26 adb: Instance of AndroidCommands for talking to the device. |
| 27 port_pairs: A list of tuples (device_port, host_port) to forward. Note |
| 28 that you can specify 0 as a device_port, in which case a |
| 29 port will by dynamically assigned on the device. You can |
| 30 get the number of the assigned port using the |
| 31 DevicePortForHostPort method. |
| 32 tool: Tool class to use to get wrapper, if necessary, for executing the |
| 33 forwarder (see valgrind_tools.py). |
| 34 host_name: Optional. Address to forward to, must be addressable from the |
| 35 host machine. Usually this is omitted and loopback is used. |
| 36 |
| 37 Raises: |
| 38 Exception on failure to forward the port. |
| 39 """ |
| 40 self._adb = adb |
| 41 self._host_to_device_port_map = dict() |
| 42 self._process = None |
| 43 adb.PushIfNeeded( |
| 44 os.path.join(CHROME_DIR, 'out', 'Release', 'forwarder'), |
| 45 Forwarder._FORWARDER_PATH) |
| 46 forward_string = ['%d:%d:%s' % |
| 47 (device, host, host_name) for device, host in port_pairs] |
| 48 |
| 49 # Kill off any existing forwarders on conflicting non-dynamically allocated |
| 50 # ports. |
| 51 for device_port, _ in port_pairs: |
| 52 if device_port != 0: |
| 53 self._KillForwardersUsingDevicePort(device_port) |
| 54 |
| 55 logging.info('Forwarding ports: %s' % (forward_string)) |
| 56 process = pexpect.spawn( |
| 57 'adb', ['-s', adb._adb.GetSerialNumber(), |
| 58 'shell', '%s %s -D %s' % ( |
| 59 tool.GetUtilWrapper(), Forwarder._FORWARDER_PATH, |
| 60 ' '.join(forward_string))]) |
| 61 |
| 62 # Read the output of the command to determine which device ports where |
| 63 # forwarded to which host ports (necessary if |
| 64 success_re = re.compile('Forwarding device port (\d+) to host (\d+):') |
| 65 failure_re = re.compile('Couldn\'t start forwarder server for port spec: ' |
| 66 '(\d+):(\d+)') |
| 67 for pair in port_pairs: |
| 68 index = process.expect([success_re, failure_re, pexpect.EOF, |
| 69 pexpect.TIMEOUT], |
| 70 Forwarder._TIMEOUT_SECS) |
| 71 if index == 0: |
| 72 # Success |
| 73 device_port = int(process.match.group(1)) |
| 74 host_port = int(process.match.group(2)) |
| 75 self._host_to_device_port_map[host_port] = device_port |
| 76 logging.info("Forwarding device port: %d to host port: %d." % |
| 77 (device_port, host_port)) |
| 78 elif index == 1: |
| 79 # Failure |
| 80 device_port = int(process.match.group(1)) |
| 81 host_port = int(process.match.group(2)) |
| 82 process.close() |
| 83 raise Exception('Failed to forward port %d to %d' % (device_port, |
| 84 host_port)) |
| 85 elif index == 2: |
| 86 logging.error(process.before) |
| 87 process.close() |
| 88 raise Exception('Unexpected EOF while trying to forward ports %s' % |
| 89 port_pairs) |
| 90 elif index == 3: |
| 91 logging.error(process.before) |
| 92 process.close() |
| 93 raise Exception('Timeout while trying to forward ports %s' % port_pairs) |
| 94 |
| 95 self._process = process |
| 96 |
| 97 def _KillForwardersUsingDevicePort(self, device_port): |
| 98 """Check if the device port is in use and if it is try to terminate the |
| 99 forwarder process (if any) that may already be forwarding it""" |
| 100 processes = self._adb.ProcessesUsingDevicePort(device_port) |
| 101 for pid, name in processes: |
| 102 if name == 'forwarder': |
| 103 logging.warning( |
| 104 'Killing forwarder process with pid %d using device_port %d' % ( |
| 105 pid, device_port)) |
| 106 self._adb.RunShellCommand('kill %d' % pid) |
| 107 else: |
| 108 logging.error( |
| 109 'Not killing process with pid %d (%s) using device_port %d' % ( |
| 110 pid, name, device_port)) |
| 111 |
| 112 def DevicePortForHostPort(self, host_port): |
| 113 """Get the device port that corresponds to a given host port.""" |
| 114 return self._host_to_device_port_map.get(host_port) |
| 115 |
| 116 def Close(self): |
| 117 """Terminate the forwarder process.""" |
| 118 if self._process: |
| 119 self._process.close() |
| 120 self._process = None |
OLD | NEW |