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 import logging | 5 import logging |
6 import os | 6 import os |
7 import pexpect | 7 import pexpect |
8 import re | 8 import re |
9 import sys | 9 import sys |
10 | 10 |
11 import android_commands | 11 import android_commands |
12 import cmd_helper | |
13 import constants | 12 import constants |
14 import ports | |
15 | 13 |
16 class Forwarder(object): | 14 class Forwarder(object): |
17 """Class to manage port forwards from the device to the host.""" | 15 """Class to manage port forwards from the device to the host.""" |
18 | 16 |
19 _DEVICE_FORWARDER_PATH = constants.TEST_EXECUTABLE_DIR + '/device_forwarder' | 17 _FORWARDER_PATH = constants.TEST_EXECUTABLE_DIR + '/forwarder' |
20 | |
21 # Unix Abstract socket path: | |
22 _DEVICE_ADB_CONTROL_PORT = 'chrome_device_forwarder' | |
23 _TIMEOUT_SECS = 30 | 18 _TIMEOUT_SECS = 30 |
24 | 19 |
25 def __init__(self, adb, port_pairs, tool, host_name, build_type): | 20 def __init__(self, adb, port_pairs, tool, host_name, build_type): |
26 """Forwards TCP ports on the device back to the host. | 21 """Forwards TCP ports on the device back to the host. |
27 | 22 |
28 Works like adb forward, but in reverse. | 23 Works like adb forward, but in reverse. |
29 | 24 |
30 Args: | 25 Args: |
31 adb: Instance of AndroidCommands for talking to the device. | 26 adb: Instance of AndroidCommands for talking to the device. |
32 port_pairs: A list of tuples (device_port, host_port) to forward. Note | 27 port_pairs: A list of tuples (device_port, host_port) to forward. Note |
33 that you can specify 0 as a device_port, in which case a | 28 that you can specify 0 as a device_port, in which case a |
34 port will by dynamically assigned on the device. You can | 29 port will by dynamically assigned on the device. You can |
35 get the number of the assigned port using the | 30 get the number of the assigned port using the |
36 DevicePortForHostPort method. | 31 DevicePortForHostPort method. |
37 tool: Tool class to use to get wrapper, if necessary, for executing the | 32 tool: Tool class to use to get wrapper, if necessary, for executing the |
38 forwarder (see valgrind_tools.py). | 33 forwarder (see valgrind_tools.py). |
39 host_name: Address to forward to, must be addressable from the | 34 host_name: Address to forward to, must be addressable from the |
40 host machine. Usually use loopback '127.0.0.1'. | 35 host machine. Usually use loopback '127.0.0.1'. |
41 build_type: 'Release' or 'Debug'. | 36 build_type: 'Release' or 'Debug'. |
42 | 37 |
43 Raises: | 38 Raises: |
44 Exception on failure to forward the port. | 39 Exception on failure to forward the port. |
45 """ | 40 """ |
46 self._adb = adb | 41 self._adb = adb |
47 self._host_to_device_port_map = dict() | 42 self._host_to_device_port_map = dict() |
48 self._host_process = None | 43 self._process = None |
49 self._device_process = None | |
50 self._adb_forward_process = None | |
51 | |
52 self._host_adb_control_port = ports.AllocateTestServerPort() | |
53 adb.PushIfNeeded( | 44 adb.PushIfNeeded( |
54 os.path.join(constants.CHROME_DIR, 'out', build_type, | 45 os.path.join(constants.CHROME_DIR, 'out', build_type, 'forwarder'), |
55 'device_forwarder'), | 46 Forwarder._FORWARDER_PATH) |
56 Forwarder._DEVICE_FORWARDER_PATH) | |
57 self._host_forwarder_path = os.path.join(constants.CHROME_DIR, | |
58 'out', | |
59 build_type, | |
60 'host_forwarder') | |
61 forward_string = ['%d:%d:%s' % | 47 forward_string = ['%d:%d:%s' % |
62 (device, host, host_name) for device, host in port_pairs] | 48 (device, host, host_name) for device, host in port_pairs] |
63 logging.info('Forwarding ports: %s', forward_string) | 49 |
64 # Kill off any existing device forwarders on conflicting non-dynamically | 50 # Kill off any existing forwarders on conflicting non-dynamically allocated |
65 # allocated ports. | 51 # ports. |
66 for device_port, _ in port_pairs: | 52 for device_port, _ in port_pairs: |
67 if device_port != 0: | 53 if device_port != 0: |
68 self._KillForwardersUsingDevicePort(device_port) | 54 self._KillForwardersUsingDevicePort(device_port) |
69 | 55 |
70 # Kill any existing host forwarders. | 56 logging.info('Forwarding ports: %s' % (forward_string)) |
71 cmd_helper.RunCmd(['killall', 'host_forwarder']) | 57 process = pexpect.spawn( |
72 self._adb_forward_process = pexpect.spawn( | 58 'adb', ['-s', adb._adb.GetSerialNumber(), |
73 'adb', ['-s', | 59 'shell', '%s %s -D %s' % ( |
74 adb._adb.GetSerialNumber(), | 60 tool.GetUtilWrapper(), Forwarder._FORWARDER_PATH, |
75 'forward', | 61 ' '.join(forward_string))]) |
76 'tcp:%s' % self._host_adb_control_port, | |
77 'localabstract:%s' % Forwarder._DEVICE_ADB_CONTROL_PORT]) | |
78 self._device_process = pexpect.spawn( | |
79 'adb', ['-s', | |
80 adb._adb.GetSerialNumber(), | |
81 'shell', | |
82 '%s %s -D --adb_sock=%s' % ( | |
83 tool.GetUtilWrapper(), | |
84 Forwarder._DEVICE_FORWARDER_PATH, | |
85 Forwarder._DEVICE_ADB_CONTROL_PORT)]) | |
86 | |
87 device_success_re = re.compile('Starting Device Forwarder.') | |
88 device_failure_re = re.compile('.*:ERROR:(.*)') | |
89 index = self._device_process.expect([device_success_re, | |
90 device_failure_re, | |
91 pexpect.EOF, | |
92 pexpect.TIMEOUT], | |
93 Forwarder._TIMEOUT_SECS) | |
94 if index == 1: | |
95 # Failure | |
96 error_msg = str(self._device_process.match.group(1)) | |
97 logging.error(self._device_process.before) | |
98 self._CloseProcess() | |
99 raise Exception('Failed to start Device Forwarder with Error: %s' % | |
100 error_msg) | |
101 elif index == 2: | |
102 logging.error(self._device_process.before) | |
103 self._CloseProcess() | |
104 raise Exception('Unexpected EOF while trying to start Device Forwarder.') | |
105 elif index == 3: | |
106 logging.error(self._device_process.before) | |
107 self._CloseProcess() | |
108 raise Exception('Timeout while trying start Device Forwarder') | |
109 | |
110 self._host_process = pexpect.spawn(self._host_forwarder_path, | |
111 ['--adb_port=%s' % ( | |
112 self._host_adb_control_port), | |
113 ' '.join(forward_string)]) | |
114 | 62 |
115 # Read the output of the command to determine which device ports where | 63 # Read the output of the command to determine which device ports where |
116 # forwarded to which host ports (necessary if | 64 # forwarded to which host ports (necessary if |
117 host_success_re = re.compile('Forwarding device port (\d+) to host (\d+):') | 65 success_re = re.compile('Forwarding device port (\d+) to host (\d+):') |
118 host_failure_re = re.compile('Couldn\'t start forwarder server for port ' | 66 failure_re = re.compile('Couldn\'t start forwarder server for port spec: ' |
119 'spec: (\d+):(\d+)') | 67 '(\d+):(\d+)') |
120 for pair in port_pairs: | 68 for pair in port_pairs: |
121 index = self._host_process.expect([host_success_re, | 69 index = process.expect([success_re, failure_re, pexpect.EOF, |
122 host_failure_re, | 70 pexpect.TIMEOUT], |
123 pexpect.EOF, | 71 Forwarder._TIMEOUT_SECS) |
124 pexpect.TIMEOUT], | |
125 Forwarder._TIMEOUT_SECS) | |
126 if index == 0: | 72 if index == 0: |
127 # Success | 73 # Success |
128 device_port = int(self._host_process.match.group(1)) | 74 device_port = int(process.match.group(1)) |
129 host_port = int(self._host_process.match.group(2)) | 75 host_port = int(process.match.group(2)) |
130 self._host_to_device_port_map[host_port] = device_port | 76 self._host_to_device_port_map[host_port] = device_port |
131 logging.info("Forwarding device port: %d to host port: %d." % | 77 logging.info("Forwarding device port: %d to host port: %d." % |
132 (device_port, host_port)) | 78 (device_port, host_port)) |
133 elif index == 1: | 79 elif index == 1: |
134 # Failure | 80 # Failure |
135 device_port = int(self._host_process.match.group(1)) | 81 device_port = int(process.match.group(1)) |
136 host_port = int(self._host_process.match.group(2)) | 82 host_port = int(process.match.group(2)) |
137 self._CloseProcess() | 83 process.close() |
138 raise Exception('Failed to forward port %d to %d' % (device_port, | 84 raise Exception('Failed to forward port %d to %d' % (device_port, |
139 host_port)) | 85 host_port)) |
140 elif index == 2: | 86 elif index == 2: |
141 logging.error(self._host_process.before) | 87 logging.error(process.before) |
142 self._CloseProcess() | 88 process.close() |
143 raise Exception('Unexpected EOF while trying to forward ports %s' % | 89 raise Exception('Unexpected EOF while trying to forward ports %s' % |
144 port_pairs) | 90 port_pairs) |
145 elif index == 3: | 91 elif index == 3: |
146 logging.error(self._host_process.before) | 92 logging.error(process.before) |
147 self._CloseProcess() | 93 process.close() |
148 raise Exception('Timeout while trying to forward ports %s' % port_pairs) | 94 raise Exception('Timeout while trying to forward ports %s' % port_pairs) |
149 | 95 |
150 def _CloseProcess(self): | 96 self._process = process |
151 if self._host_process: | |
152 self._host_process.close() | |
153 if self._device_process: | |
154 self._device_process.close() | |
155 if self._adb_forward_process: | |
156 self._adb_forward_process.close() | |
157 self._host_process = None | |
158 self._device_process = None | |
159 self._adb_forward_process = None | |
160 | 97 |
161 def _KillForwardersUsingDevicePort(self, device_port): | 98 def _KillForwardersUsingDevicePort(self, device_port): |
162 """Check if the device port is in use and if it is try to terminate the | 99 """Check if the device port is in use and if it is try to terminate the |
163 forwarder process (if any) that may already be forwarding it""" | 100 forwarder process (if any) that may already be forwarding it""" |
164 processes = self._adb.ProcessesUsingDevicePort(device_port) | 101 processes = self._adb.ProcessesUsingDevicePort(device_port) |
165 for pid, name in processes: | 102 for pid, name in processes: |
166 if name == 'device_forwarder': | 103 if name == 'forwarder': |
167 logging.warning( | 104 logging.warning( |
168 'Killing forwarder process with pid %d using device_port %d' % ( | 105 'Killing forwarder process with pid %d using device_port %d' % ( |
169 pid, device_port)) | 106 pid, device_port)) |
170 self._adb.RunShellCommand('kill %d' % pid) | 107 self._adb.RunShellCommand('kill %d' % pid) |
171 else: | 108 else: |
172 logging.error( | 109 logging.error( |
173 'Not killing process with pid %d (%s) using device_port %d' % ( | 110 'Not killing process with pid %d (%s) using device_port %d' % ( |
174 pid, name, device_port)) | 111 pid, name, device_port)) |
175 | 112 |
176 def DevicePortForHostPort(self, host_port): | 113 def DevicePortForHostPort(self, host_port): |
177 """Get the device port that corresponds to a given host port.""" | 114 """Get the device port that corresponds to a given host port.""" |
178 return self._host_to_device_port_map.get(host_port) | 115 return self._host_to_device_port_map.get(host_port) |
179 | 116 |
180 def Close(self): | 117 def Close(self): |
181 """Terminate the forwarder process.""" | 118 """Terminate the forwarder process.""" |
182 self._CloseProcess() | 119 if self._process: |
| 120 self._process.close() |
| 121 self._process = None |
OLD | NEW |