| Index: build/android/pylib/forwarder.py
|
| diff --git a/build/android/pylib/forwarder.py b/build/android/pylib/forwarder.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..15d4c46fece786ff3f945f391a364b118d0e77bd
|
| --- /dev/null
|
| +++ b/build/android/pylib/forwarder.py
|
| @@ -0,0 +1,120 @@
|
| +# Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +
|
| +import logging
|
| +import os
|
| +import pexpect
|
| +import re
|
| +import sys
|
| +
|
| +import android_commands
|
| +from constants import CHROME_DIR
|
| +
|
| +class Forwarder(object):
|
| + """Class to manage port forwards from the device to the host."""
|
| +
|
| + _FORWARDER_PATH = '/data/local/tmp/forwarder'
|
| + _TIMEOUT_SECS = 30
|
| +
|
| + def __init__(self, adb, port_pairs, tool, host_name):
|
| + """Forwards TCP ports on the device back to the host.
|
| +
|
| + Works like adb forward, but in reverse.
|
| +
|
| + Args:
|
| + adb: Instance of AndroidCommands for talking to the device.
|
| + port_pairs: A list of tuples (device_port, host_port) to forward. Note
|
| + that you can specify 0 as a device_port, in which case a
|
| + port will by dynamically assigned on the device. You can
|
| + get the number of the assigned port using the
|
| + DevicePortForHostPort method.
|
| + tool: Tool class to use to get wrapper, if necessary, for executing the
|
| + forwarder (see valgrind_tools.py).
|
| + host_name: Optional. Address to forward to, must be addressable from the
|
| + host machine. Usually this is omitted and loopback is used.
|
| +
|
| + Raises:
|
| + Exception on failure to forward the port.
|
| + """
|
| + self._adb = adb
|
| + self._host_to_device_port_map = dict()
|
| + self._process = None
|
| + adb.PushIfNeeded(
|
| + os.path.join(CHROME_DIR, 'out', 'Release', 'forwarder'),
|
| + Forwarder._FORWARDER_PATH)
|
| + forward_string = ['%d:%d:%s' %
|
| + (device, host, host_name) for device, host in port_pairs]
|
| +
|
| + # Kill off any existing forwarders on conflicting non-dynamically allocated
|
| + # ports.
|
| + for device_port, _ in port_pairs:
|
| + if device_port != 0:
|
| + self._KillForwardersUsingDevicePort(device_port)
|
| +
|
| + logging.info('Forwarding ports: %s' % (forward_string))
|
| + process = pexpect.spawn(
|
| + 'adb', ['-s', adb._adb.GetSerialNumber(),
|
| + 'shell', '%s %s -D %s' % (
|
| + tool.GetUtilWrapper(), Forwarder._FORWARDER_PATH,
|
| + ' '.join(forward_string))])
|
| +
|
| + # Read the output of the command to determine which device ports where
|
| + # forwarded to which host ports (necessary if
|
| + success_re = re.compile('Forwarding device port (\d+) to host (\d+):')
|
| + failure_re = re.compile('Couldn\'t start forwarder server for port spec: '
|
| + '(\d+):(\d+)')
|
| + for pair in port_pairs:
|
| + index = process.expect([success_re, failure_re, pexpect.EOF,
|
| + pexpect.TIMEOUT],
|
| + Forwarder._TIMEOUT_SECS)
|
| + if index == 0:
|
| + # Success
|
| + device_port = int(process.match.group(1))
|
| + host_port = int(process.match.group(2))
|
| + self._host_to_device_port_map[host_port] = device_port
|
| + logging.info("Forwarding device port: %d to host port: %d." %
|
| + (device_port, host_port))
|
| + elif index == 1:
|
| + # Failure
|
| + device_port = int(process.match.group(1))
|
| + host_port = int(process.match.group(2))
|
| + process.close()
|
| + raise Exception('Failed to forward port %d to %d' % (device_port,
|
| + host_port))
|
| + elif index == 2:
|
| + logging.error(process.before)
|
| + process.close()
|
| + raise Exception('Unexpected EOF while trying to forward ports %s' %
|
| + port_pairs)
|
| + elif index == 3:
|
| + logging.error(process.before)
|
| + process.close()
|
| + raise Exception('Timeout while trying to forward ports %s' % port_pairs)
|
| +
|
| + self._process = process
|
| +
|
| + def _KillForwardersUsingDevicePort(self, device_port):
|
| + """Check if the device port is in use and if it is try to terminate the
|
| + forwarder process (if any) that may already be forwarding it"""
|
| + processes = self._adb.ProcessesUsingDevicePort(device_port)
|
| + for pid, name in processes:
|
| + if name == 'forwarder':
|
| + logging.warning(
|
| + 'Killing forwarder process with pid %d using device_port %d' % (
|
| + pid, device_port))
|
| + self._adb.RunShellCommand('kill %d' % pid)
|
| + else:
|
| + logging.error(
|
| + 'Not killing process with pid %d (%s) using device_port %d' % (
|
| + pid, name, device_port))
|
| +
|
| + def DevicePortForHostPort(self, host_port):
|
| + """Get the device port that corresponds to a given host port."""
|
| + return self._host_to_device_port_map.get(host_port)
|
| +
|
| + def Close(self):
|
| + """Terminate the forwarder process."""
|
| + if self._process:
|
| + self._process.close()
|
| + self._process = None
|
|
|