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.
11 import android_commands
12 from constants
import CHROME_DIR
14 class Forwarder(object):
15 """Class to manage port forwards from the device to the host."""
17 _FORWARDER_PATH
= '/data/local/tmp/forwarder'
20 def __init__(self
, adb
, port_pairs
, tool
, host_name
, build_type
):
21 """Forwards TCP ports on the device back to the host.
23 Works like adb forward, but in reverse.
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: Address to forward to, must be addressable from the
35 host machine. Usually use loopback '127.0.0.1'.
36 build_type: 'Release' or 'Debug'.
39 Exception on failure to forward the port.
42 self
._host
_to
_device
_port
_map
= dict()
45 os
.path
.join(CHROME_DIR
, 'out', build_type
, 'forwarder'),
46 Forwarder
._FORWARDER
_PATH
)
47 forward_string
= ['%d:%d:%s' %
48 (device
, host
, host_name
) for device
, host
in port_pairs
]
50 # Kill off any existing forwarders on conflicting non-dynamically allocated
52 for device_port
, _
in port_pairs
:
54 self
._KillForwardersUsingDevicePort
(device_port
)
56 logging
.info('Forwarding ports: %s' % (forward_string
))
57 process
= pexpect
.spawn(
58 'adb', ['-s', adb
._adb
.GetSerialNumber(),
59 'shell', '%s %s -D %s' % (
60 tool
.GetUtilWrapper(), Forwarder
._FORWARDER
_PATH
,
61 ' '.join(forward_string
))])
63 # Read the output of the command to determine which device ports where
64 # forwarded to which host ports (necessary if
65 success_re
= re
.compile('Forwarding device port (\d+) to host (\d+):')
66 failure_re
= re
.compile('Couldn\'t start forwarder server for port spec: '
68 for pair
in port_pairs
:
69 index
= process
.expect([success_re
, failure_re
, pexpect
.EOF
,
71 Forwarder
._TIMEOUT
_SECS
)
74 device_port
= int(process
.match
.group(1))
75 host_port
= int(process
.match
.group(2))
76 self
._host
_to
_device
_port
_map
[host_port
] = device_port
77 logging
.info("Forwarding device port: %d to host port: %d." %
78 (device_port
, host_port
))
81 device_port
= int(process
.match
.group(1))
82 host_port
= int(process
.match
.group(2))
84 raise Exception('Failed to forward port %d to %d' % (device_port
,
87 logging
.error(process
.before
)
89 raise Exception('Unexpected EOF while trying to forward ports %s' %
92 logging
.error(process
.before
)
94 raise Exception('Timeout while trying to forward ports %s' % port_pairs
)
96 self
._process
= process
98 def _KillForwardersUsingDevicePort(self
, device_port
):
99 """Check if the device port is in use and if it is try to terminate the
100 forwarder process (if any) that may already be forwarding it"""
101 processes
= self
._adb
.ProcessesUsingDevicePort(device_port
)
102 for pid
, name
in processes
:
103 if name
== 'forwarder':
105 'Killing forwarder process with pid %d using device_port %d' % (
107 self
._adb
.RunShellCommand('kill %d' % pid
)
110 'Not killing process with pid %d (%s) using device_port %d' % (
111 pid
, name
, device_port
))
113 def DevicePortForHostPort(self
, host_port
):
114 """Get the device port that corresponds to a given host port."""
115 return self
._host
_to
_device
_port
_map
.get(host_port
)
118 """Terminate the forwarder process."""
120 self
._process
.close()