1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/media/webrtc/trunk/build/android/pylib/forwarder.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,198 @@ 1.4 +# Copyright (c) 2012 The Chromium Authors. All rights reserved. 1.5 +# Use of this source code is governed by a BSD-style license that can be 1.6 +# found in the LICENSE file. 1.7 + 1.8 +import logging 1.9 +import os 1.10 +import re 1.11 +import sys 1.12 +import time 1.13 + 1.14 +import android_commands 1.15 +import cmd_helper 1.16 +import constants 1.17 +import ports 1.18 + 1.19 +from pylib import pexpect 1.20 + 1.21 +class Forwarder(object): 1.22 + """Class to manage port forwards from the device to the host.""" 1.23 + 1.24 + _DEVICE_FORWARDER_PATH = constants.TEST_EXECUTABLE_DIR + '/device_forwarder' 1.25 + 1.26 + # Unix Abstract socket path: 1.27 + _DEVICE_ADB_CONTROL_PORT = 'chrome_device_forwarder' 1.28 + _TIMEOUT_SECS = 30 1.29 + 1.30 + def __init__(self, adb, port_pairs, tool, host_name, build_type): 1.31 + """Forwards TCP ports on the device back to the host. 1.32 + 1.33 + Works like adb forward, but in reverse. 1.34 + 1.35 + Args: 1.36 + adb: Instance of AndroidCommands for talking to the device. 1.37 + port_pairs: A list of tuples (device_port, host_port) to forward. Note 1.38 + that you can specify 0 as a device_port, in which case a 1.39 + port will by dynamically assigned on the device. You can 1.40 + get the number of the assigned port using the 1.41 + DevicePortForHostPort method. 1.42 + tool: Tool class to use to get wrapper, if necessary, for executing the 1.43 + forwarder (see valgrind_tools.py). 1.44 + host_name: Address to forward to, must be addressable from the 1.45 + host machine. Usually use loopback '127.0.0.1'. 1.46 + build_type: 'Release' or 'Debug'. 1.47 + 1.48 + Raises: 1.49 + Exception on failure to forward the port. 1.50 + """ 1.51 + self._adb = adb 1.52 + self._host_to_device_port_map = dict() 1.53 + self._host_process = None 1.54 + self._device_process = None 1.55 + self._adb_forward_process = None 1.56 + 1.57 + self._host_adb_control_port = ports.AllocateTestServerPort() 1.58 + if not self._host_adb_control_port: 1.59 + raise Exception('Failed to allocate a TCP port in the host machine.') 1.60 + adb.PushIfNeeded( 1.61 + os.path.join(constants.CHROME_DIR, 'out', build_type, 1.62 + 'device_forwarder'), 1.63 + Forwarder._DEVICE_FORWARDER_PATH) 1.64 + self._host_forwarder_path = os.path.join(constants.CHROME_DIR, 1.65 + 'out', 1.66 + build_type, 1.67 + 'host_forwarder') 1.68 + forward_string = ['%d:%d:%s' % 1.69 + (device, host, host_name) for device, host in port_pairs] 1.70 + logging.info('Forwarding ports: %s', forward_string) 1.71 + timeout_sec = 5 1.72 + host_pattern = 'host_forwarder.*' + ' '.join(forward_string) 1.73 + # TODO(felipeg): Rather than using a blocking kill() here, the device 1.74 + # forwarder could try to bind the Unix Domain Socket until it succeeds or 1.75 + # while it fails because the socket is already bound (with appropriate 1.76 + # timeout handling obviously). 1.77 + self._KillHostForwarderBlocking(host_pattern, timeout_sec) 1.78 + self._KillDeviceForwarderBlocking(timeout_sec) 1.79 + self._adb_forward_process = pexpect.spawn( 1.80 + 'adb', ['-s', 1.81 + adb._adb.GetSerialNumber(), 1.82 + 'forward', 1.83 + 'tcp:%s' % self._host_adb_control_port, 1.84 + 'localabstract:%s' % Forwarder._DEVICE_ADB_CONTROL_PORT]) 1.85 + self._device_process = pexpect.spawn( 1.86 + 'adb', ['-s', 1.87 + adb._adb.GetSerialNumber(), 1.88 + 'shell', 1.89 + '%s %s -D --adb_sock=%s' % ( 1.90 + tool.GetUtilWrapper(), 1.91 + Forwarder._DEVICE_FORWARDER_PATH, 1.92 + Forwarder._DEVICE_ADB_CONTROL_PORT)]) 1.93 + 1.94 + device_success_re = re.compile('Starting Device Forwarder.') 1.95 + device_failure_re = re.compile('.*:ERROR:(.*)') 1.96 + index = self._device_process.expect([device_success_re, 1.97 + device_failure_re, 1.98 + pexpect.EOF, 1.99 + pexpect.TIMEOUT], 1.100 + Forwarder._TIMEOUT_SECS) 1.101 + if index == 1: 1.102 + # Failure 1.103 + error_msg = str(self._device_process.match.group(1)) 1.104 + logging.error(self._device_process.before) 1.105 + self._CloseProcess() 1.106 + raise Exception('Failed to start Device Forwarder with Error: %s' % 1.107 + error_msg) 1.108 + elif index == 2: 1.109 + logging.error(self._device_process.before) 1.110 + self._CloseProcess() 1.111 + raise Exception('Unexpected EOF while trying to start Device Forwarder.') 1.112 + elif index == 3: 1.113 + logging.error(self._device_process.before) 1.114 + self._CloseProcess() 1.115 + raise Exception('Timeout while trying start Device Forwarder') 1.116 + 1.117 + self._host_process = pexpect.spawn(self._host_forwarder_path, 1.118 + ['--adb_port=%s' % ( 1.119 + self._host_adb_control_port)] + 1.120 + forward_string) 1.121 + 1.122 + # Read the output of the command to determine which device ports where 1.123 + # forwarded to which host ports (necessary if 1.124 + host_success_re = re.compile('Forwarding device port (\d+) to host (\d+):') 1.125 + host_failure_re = re.compile('Couldn\'t start forwarder server for port ' 1.126 + 'spec: (\d+):(\d+)') 1.127 + for pair in port_pairs: 1.128 + index = self._host_process.expect([host_success_re, 1.129 + host_failure_re, 1.130 + pexpect.EOF, 1.131 + pexpect.TIMEOUT], 1.132 + Forwarder._TIMEOUT_SECS) 1.133 + if index == 0: 1.134 + # Success 1.135 + device_port = int(self._host_process.match.group(1)) 1.136 + host_port = int(self._host_process.match.group(2)) 1.137 + self._host_to_device_port_map[host_port] = device_port 1.138 + logging.info("Forwarding device port: %d to host port: %d." % 1.139 + (device_port, host_port)) 1.140 + elif index == 1: 1.141 + # Failure 1.142 + device_port = int(self._host_process.match.group(1)) 1.143 + host_port = int(self._host_process.match.group(2)) 1.144 + self._CloseProcess() 1.145 + raise Exception('Failed to forward port %d to %d' % (device_port, 1.146 + host_port)) 1.147 + elif index == 2: 1.148 + logging.error(self._host_process.before) 1.149 + self._CloseProcess() 1.150 + raise Exception('Unexpected EOF while trying to forward ports %s' % 1.151 + port_pairs) 1.152 + elif index == 3: 1.153 + logging.error(self._host_process.before) 1.154 + self._CloseProcess() 1.155 + raise Exception('Timeout while trying to forward ports %s' % port_pairs) 1.156 + 1.157 + def _KillHostForwarderBlocking(self, host_pattern, timeout_sec): 1.158 + """Kills any existing host forwarders using the provided pattern. 1.159 + 1.160 + Note that this waits until the process terminates. 1.161 + """ 1.162 + cmd_helper.RunCmd(['pkill', '-f', host_pattern]) 1.163 + elapsed = 0 1.164 + wait_period = 0.1 1.165 + while not cmd_helper.RunCmd(['pgrep', '-f', host_pattern]) and ( 1.166 + elapsed < timeout_sec): 1.167 + time.sleep(wait_period) 1.168 + elapsed += wait_period 1.169 + if elapsed >= timeout_sec: 1.170 + raise Exception('Timed out while killing ' + host_pattern) 1.171 + 1.172 + def _KillDeviceForwarderBlocking(self, timeout_sec): 1.173 + """Kills any existing device forwarders. 1.174 + 1.175 + Note that this waits until the process terminates. 1.176 + """ 1.177 + processes_killed = self._adb.KillAllBlocking( 1.178 + 'device_forwarder', timeout_sec) 1.179 + if not processes_killed: 1.180 + pids = self._adb.ExtractPid('device_forwarder') 1.181 + if pids: 1.182 + raise Exception('Timed out while killing device_forwarder') 1.183 + 1.184 + def _CloseProcess(self): 1.185 + if self._host_process: 1.186 + self._host_process.close() 1.187 + if self._device_process: 1.188 + self._device_process.close() 1.189 + if self._adb_forward_process: 1.190 + self._adb_forward_process.close() 1.191 + self._host_process = None 1.192 + self._device_process = None 1.193 + self._adb_forward_process = None 1.194 + 1.195 + def DevicePortForHostPort(self, host_port): 1.196 + """Get the device port that corresponds to a given host port.""" 1.197 + return self._host_to_device_port_map.get(host_port) 1.198 + 1.199 + def Close(self): 1.200 + """Terminate the forwarder process.""" 1.201 + self._CloseProcess()