Bug 1484238 - Add an 'adb_reverse' command to mozdevice.ADBAndroid r=gbrown,bc

Differential Revision: https://phabricator.services.mozilla.com/D4775

--HG--
extra : moz-landing-system : lando
This commit is contained in:
egao 2018-09-04 14:15:42 +00:00
parent 8d537bb798
commit 1a098a6537
2 changed files with 94 additions and 25 deletions

View file

@ -529,6 +529,9 @@ class ADBDevice(ADBCommand):
""" """
__metaclass__ = ABCMeta __metaclass__ = ABCMeta
SOCKET_DIRECTON_REVERSE = "reverse"
SOCKET_DIRECTON_FORWARD = "forward"
def __init__(self, def __init__(self,
device=None, device=None,
adb='adb', adb='adb',
@ -962,7 +965,7 @@ class ADBDevice(ADBCommand):
device_serial=self._device_serial, device_serial=self._device_serial,
timeout=timeout) timeout=timeout)
# Port forwarding methods # Networking methods
def _validate_port(self, port, is_local=True): def _validate_port(self, port, is_local=True):
"""Validate a port forwarding specifier. Raises ValueError on failure. """Validate a port forwarding specifier. Raises ValueError on failure.
@ -977,22 +980,24 @@ class ADBDevice(ADBCommand):
parts = port.split(":", 1) parts = port.split(":", 1)
if len(parts) != 2 or parts[0] not in prefixes: if len(parts) != 2 or parts[0] not in prefixes:
raise ValueError("Invalid forward specifier %s" % port) raise ValueError("Invalid port specifier %s" % port)
def forward(self, local, remote, allow_rebind=True, timeout=None): def _validate_direction(self, direction):
"""Forward a local port to a specific port on the device. """Validate direction of the socket connection. Raises ValueError on failure.
Ports are specified in the form: :param str direction: The socket direction specifier to validate
tcp:<port> :raises: * ValueError
localabstract:<unix domain socket name> """
localreserved:<unix domain socket name> if direction not in [self.SOCKET_DIRECTON_FORWARD, self.SOCKET_DIRECTON_REVERSE]:
localfilesystem:<unix domain socket name> raise ValueError('Invalid direction specifier {}'.format(direction))
dev:<character device name>
jdwp:<process pid> (remote only)
:param str local: Local port to forward def create_socket_connection(self, direction, local, remote, allow_rebind=True, timeout=None):
:param str remote: Remote port to which to forward """Sets up a socket connection in the specified direction.
:param bool allow_rebind: Don't error if the local port is already forwarded
:param str direction: Direction of the socket connection
:param str local: Local port
:param str remote: Remote port
:param bool allow_rebind: Do not fail if port is already bound
:param timeout: The maximum time in seconds :param timeout: The maximum time in seconds
for any spawned adb process to complete before throwing for any spawned adb process to complete before throwing
an ADBTimeoutError. If it is not specified, the value an ADBTimeoutError. If it is not specified, the value
@ -1002,34 +1007,46 @@ class ADBDevice(ADBCommand):
* ADBTimeoutError * ADBTimeoutError
* ADBError * ADBError
""" """
# validate socket direction, and local and remote port formatting.
self._validate_direction(direction)
for port, is_local in [(local, True), (remote, False)]: for port, is_local in [(local, True), (remote, False)]:
self._validate_port(port, is_local=is_local) self._validate_port(port, is_local=is_local)
cmd = ["forward", local, remote] cmd = [direction, local, remote]
if not allow_rebind: if not allow_rebind:
cmd.insert(1, "--no-rebind") cmd.insert(1, "--no-rebind")
# execute commands to establish socket connection.
self.command_output(cmd, timeout=timeout) self.command_output(cmd, timeout=timeout)
def list_forwards(self, timeout=None): def list_socket_connections(self, direction, timeout=None):
"""Return a list of tuples specifying active forwards """Return a list of tuples specifying active socket connectionss.
Return values are of the form (device, local, remote). Return values are of the form (device, local, remote).
:param str direction: 'forward' to list forward socket connections
'reverse' to list reverse socket connections
:param timeout: The maximum time in seconds :param timeout: The maximum time in seconds
for any spawned adb process to complete before throwing for any spawned adb process to complete before throwing
an ADBTimeoutError. If it is not specified, the value an ADBTimeoutError. If it is not specified, the value
set in the ADBDevice constructor is used. set in the ADBDevice constructor is used.
:type timeout: integer or None :type timeout: integer or None
:raises: * ADBTimeoutError :raises: * ValueError
* ADBTimeoutError
* ADBError * ADBError
""" """
forwards = self.command_output(["forward", "--list"], timeout=timeout) self._validate_direction(direction)
return [tuple(line.split(" ")) for line in forwards.splitlines() if line.strip()]
def remove_forwards(self, local=None, timeout=None): cmd = [direction, "--list"]
"""Remove existing port forwards. output = self.command_output(cmd, timeout=timeout)
return [tuple(line.split(" ")) for line in output.splitlines() if line.strip()]
def remove_socket_connections(self, direction, local=None, timeout=None):
"""Remove existing socket connections for a given direction.
:param str direction: 'forward' to remove forward socket connection
'reverse' to remove reverse socket connection
:param local: local port specifier as for ADBDevice.forward. If local :param local: local port specifier as for ADBDevice.forward. If local
is not specified removes all forwards. is not specified removes all forwards.
:type local: str or None :type local: str or None
@ -1042,7 +1059,10 @@ class ADBDevice(ADBCommand):
* ADBTimeoutError * ADBTimeoutError
* ADBError * ADBError
""" """
cmd = ["forward"] self._validate_direction(direction)
cmd = [direction]
if local is None: if local is None:
cmd.extend(["--remove-all"]) cmd.extend(["--remove-all"])
else: else:
@ -1051,6 +1071,55 @@ class ADBDevice(ADBCommand):
self.command_output(cmd, timeout=timeout) self.command_output(cmd, timeout=timeout)
# Legacy port forward methods
def forward(self, local, remote, allow_rebind=True, timeout=None):
"""Forward a local port to a specific port on the device.
See `ADBDevice.create_socket_connection`.
"""
self.create_socket_connection(self.SOCKET_DIRECTON_FORWARD,
local, remote, allow_rebind, timeout)
def list_forwards(self, timeout=None):
"""Return a list of tuples specifying active forwards.
See `ADBDevice.list_socket_connection`.
"""
return self.list_socket_connections(self.SOCKET_DIRECTON_FORWARD, timeout)
def remove_forwards(self, local=None, timeout=None):
"""Remove existing port forwards.
See `ADBDevice.remove_socket_connection`.
"""
self.remove_socket_connections(self.SOCKET_DIRECTON_FORWARD, local, timeout)
# Legacy port reverse methods
def reverse(self, local, remote, allow_rebind=True, timeout=None):
"""Sets up a reverse socket connection from device to host.
See `ADBDevice.create_socket_connection`.
"""
self.create_socket_connection(self.SOCKET_DIRECTON_REVERSE,
local, remote, allow_rebind, timeout)
def list_reverses(self, timeout=None):
"""Returns a list of tuples showing active reverse socket connections.
See `ADBDevice.list_socket_connection`.
"""
return self.list_socket_connections(self.SOCKET_DIRECTON_REVERSE, timeout)
def remove_reverses(self, local=None, timeout=None):
"""Remove existing reverse socket connections.
See `ADBDevice.remove_socket_connection`.
"""
self.remove_socket_connections(self.SOCKET_DIRECTON_REVERSE,
local, timeout)
# Device Shell methods # Device Shell methods
def shell(self, cmd, env=None, cwd=None, timeout=None, root=False, def shell(self, cmd, env=None, cwd=None, timeout=None, root=False,

View file

@ -8,7 +8,7 @@ from __future__ import absolute_import
from setuptools import setup from setuptools import setup
PACKAGE_NAME = 'mozdevice' PACKAGE_NAME = 'mozdevice'
PACKAGE_VERSION = '1.1.0' PACKAGE_VERSION = '1.1.1'
deps = ['mozfile >= 1.0', deps = ['mozfile >= 1.0',
'mozlog >= 3.0', 'mozlog >= 3.0',