Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(217)

Unified Diff: tools/telemetry/telemetry/internal/forwarders/android_forwarder.py

Issue 1647513002: Delete tools/telemetry. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tools/telemetry/telemetry/internal/forwarders/android_forwarder.py
diff --git a/tools/telemetry/telemetry/internal/forwarders/android_forwarder.py b/tools/telemetry/telemetry/internal/forwarders/android_forwarder.py
deleted file mode 100644
index 34839c921ff1265b6a46d3fc8d7f3aa167202812..0000000000000000000000000000000000000000
--- a/tools/telemetry/telemetry/internal/forwarders/android_forwarder.py
+++ /dev/null
@@ -1,573 +0,0 @@
-# Copyright 2014 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 atexit
-import logging
-import os
-import re
-import socket
-import struct
-import subprocess
-
-from telemetry.internal.util import binary_manager
-from telemetry.core import platform
-from telemetry.core import util
-from telemetry.internal import forwarders
-from telemetry.internal.platform import android_device
-
-from devil.android import device_errors
-from devil.android import device_utils
-
-try:
- from devil.android import forwarder
-except ImportError:
- forwarder = None
-
-
-class AndroidForwarderFactory(forwarders.ForwarderFactory):
-
- def __init__(self, device, use_rndis):
- super(AndroidForwarderFactory, self).__init__()
- self._device = device
- self._rndis_configurator = None
- if use_rndis:
- self._rndis_configurator = AndroidRndisConfigurator(self._device)
-
- def Create(self, port_pairs):
- try:
- if self._rndis_configurator:
- return AndroidRndisForwarder(self._device, self._rndis_configurator,
- port_pairs)
- return AndroidForwarder(self._device, port_pairs)
- except Exception:
- try:
- logging.warning('Failed to create forwarder. '
- 'Currently forwarded connections:')
- for line in self._device.adb.ForwardList().splitlines():
- logging.warning(' %s', line)
- except Exception:
- logging.warning('Exception raised while listing forwarded connections.')
-
- logging.warning('Device tcp sockets in use:')
- try:
- for line in self._device.ReadFile('/proc/net/tcp', as_root=True,
- force_pull=True).splitlines():
- logging.warning(' %s', line)
- except Exception:
- logging.warning('Exception raised while listing tcp sockets.')
-
- logging.warning('Alive webpagereplay instances:')
- try:
- for line in subprocess.check_output(['ps', '-ef']).splitlines():
- if 'webpagereplay' in line:
- logging.warning(' %s', line)
- except Exception:
- logging.warning('Exception raised while listing WPR intances.')
-
- raise
-
- @property
- def host_ip(self):
- if self._rndis_configurator:
- return self._rndis_configurator.host_ip
- return super(AndroidForwarderFactory, self).host_ip
-
- @property
- def does_forwarder_override_dns(self):
- return bool(self._rndis_configurator)
-
-
-class AndroidForwarder(forwarders.Forwarder):
-
- def __init__(self, device, port_pairs):
- super(AndroidForwarder, self).__init__(port_pairs)
- self._device = device
- forwarder.Forwarder.Map([(p.remote_port, p.local_port)
- for p in port_pairs if p], self._device)
- self._port_pairs = forwarders.PortPairs(*[
- forwarders.PortPair(
- p.local_port,
- forwarder.Forwarder.DevicePortForHostPort(p.local_port))
- if p else None for p in port_pairs])
- atexit.register(self.Close)
- # TODO(tonyg): Verify that each port can connect to host.
-
- def Close(self):
- if self._forwarding:
- for port_pair in self._port_pairs:
- if port_pair:
- forwarder.Forwarder.UnmapDevicePort(
- port_pair.remote_port, self._device)
- super(AndroidForwarder, self).Close()
-
-
-class AndroidRndisForwarder(forwarders.Forwarder):
- """Forwards traffic using RNDIS. Assumes the device has root access."""
-
- def __init__(self, device, rndis_configurator, port_pairs):
- super(AndroidRndisForwarder, self).__init__(port_pairs)
-
- self._device = device
- self._rndis_configurator = rndis_configurator
- self._device_iface = rndis_configurator.device_iface
- self._host_ip = rndis_configurator.host_ip
- self._original_dns = None, None, None
- self._RedirectPorts(port_pairs)
- if port_pairs.dns:
- self._OverrideDns()
- self._OverrideDefaultGateway()
- # Need to override routing policy again since call to setifdns
- # sometimes resets policy table
- self._rndis_configurator.OverrideRoutingPolicy()
- atexit.register(self.Close)
- # TODO(tonyg): Verify that each port can connect to host.
-
- @property
- def host_ip(self):
- return self._host_ip
-
- def Close(self):
- if self._forwarding:
- self._rndis_configurator.RestoreRoutingPolicy()
- self._SetDns(*self._original_dns)
- self._RestoreDefaultGateway()
- super(AndroidRndisForwarder, self).Close()
-
- def _RedirectPorts(self, port_pairs):
- """Sets the local to remote pair mappings to use for RNDIS."""
- # Flush any old nat rules.
- self._device.RunShellCommand('iptables -F -t nat')
- for port_pair in port_pairs:
- if not port_pair or port_pair.local_port == port_pair.remote_port:
- continue
- protocol = 'udp' if port_pair.remote_port == 53 else 'tcp'
- self._device.RunShellCommand(
- 'iptables -t nat -A OUTPUT -p %s --dport %d'
- ' -j DNAT --to-destination %s:%d' %
- (protocol, port_pair.remote_port, self.host_ip, port_pair.local_port))
-
- def _OverrideDns(self):
- """Overrides DNS on device to point at the host."""
- self._original_dns = self._GetCurrentDns()
- self._SetDns(self._device_iface, self.host_ip, self.host_ip)
-
- def _SetDns(self, iface, dns1, dns2):
- """Overrides device's DNS configuration.
-
- Args:
- iface: name of the network interface to make default
- dns1, dns2: nameserver IP addresses
- """
- if not iface:
- return # If there is no route, then nobody cares about DNS.
- # DNS proxy in older versions of Android is configured via properties.
- # TODO(szym): run via su -c if necessary.
- self._device.SetProp('net.dns1', dns1)
- self._device.SetProp('net.dns2', dns2)
- dnschange = self._device.GetProp('net.dnschange')
- if dnschange:
- self._device.SetProp('net.dnschange', str(int(dnschange) + 1))
- # Since commit 8b47b3601f82f299bb8c135af0639b72b67230e6 to frameworks/base
- # the net.dns1 properties have been replaced with explicit commands for netd
- self._device.RunShellCommand('netd resolver setifdns %s %s %s' %
- (iface, dns1, dns2))
- # TODO(szym): if we know the package UID, we could setifaceforuidrange
- self._device.RunShellCommand('netd resolver setdefaultif %s' % iface)
-
- def _GetCurrentDns(self):
- """Returns current gateway, dns1, and dns2."""
- routes = self._device.RunShellCommand('cat /proc/net/route')[1:]
- routes = [route.split() for route in routes]
- default_routes = [route[0] for route in routes if route[1] == '00000000']
- return (
- default_routes[0] if default_routes else None,
- self._device.GetProp('net.dns1'),
- self._device.GetProp('net.dns2'),
- )
-
- def _OverrideDefaultGateway(self):
- """Force traffic to go through RNDIS interface.
-
- Override any default gateway route. Without this traffic may go through
- the wrong interface.
-
- This introduces the risk that _RestoreDefaultGateway() is not called
- (e.g. Telemetry crashes). A power cycle or "adb reboot" is a simple
- workaround around in that case.
- """
- self._device.RunShellCommand('route add default gw %s dev %s' %
- (self.host_ip, self._device_iface))
-
- def _RestoreDefaultGateway(self):
- self._device.RunShellCommand('netcfg %s down' % self._device_iface)
-
-
-class AndroidRndisConfigurator(object):
- """Configures a linux host to connect to an android device via RNDIS.
-
- Note that we intentionally leave RNDIS running on the device. This is
- because the setup is slow and potentially flaky and leaving it running
- doesn't seem to interfere with any other developer or bot use-cases.
- """
-
- _RNDIS_DEVICE = '/sys/class/android_usb/android0'
- _NETWORK_INTERFACES = '/etc/network/interfaces'
- _INTERFACES_INCLUDE = 'source /etc/network/interfaces.d/*.conf'
- _TELEMETRY_INTERFACE_FILE = '/etc/network/interfaces.d/telemetry-{}.conf'
-
- def __init__(self, device):
- self._device = device
-
- try:
- self._device.EnableRoot()
- except device_errors.CommandFailedError:
- logging.error('RNDIS forwarding requires a rooted device.')
- raise
-
- self._device_ip = None
- self._host_iface = None
- self._host_ip = None
- self.device_iface = None
-
- if platform.GetHostPlatform().GetOSName() == 'mac':
- self._InstallHorndis(platform.GetHostPlatform().GetArchName())
-
- assert self._IsRndisSupported(), 'Device does not support RNDIS.'
- self._CheckConfigureNetwork()
-
- @property
- def host_ip(self):
- return self._host_ip
-
- def _IsRndisSupported(self):
- """Checks that the device has RNDIS support in the kernel."""
- return self._device.FileExists('%s/f_rndis/device' % self._RNDIS_DEVICE)
-
- def _FindDeviceRndisInterface(self):
- """Returns the name of the RNDIS network interface if present."""
- config = self._device.RunShellCommand('ip -o link show')
- interfaces = [line.split(':')[1].strip() for line in config]
- candidates = [iface for iface in interfaces if re.match('rndis|usb', iface)]
- if candidates:
- candidates.sort()
- if len(candidates) == 2 and candidates[0].startswith('rndis') and \
- candidates[1].startswith('usb'):
- return candidates[0]
- assert len(candidates) == 1, 'Found more than one rndis device!'
- return candidates[0]
-
- def _EnumerateHostInterfaces(self):
- host_platform = platform.GetHostPlatform().GetOSName()
- if host_platform == 'linux':
- return subprocess.check_output(['ip', 'addr']).splitlines()
- if host_platform == 'mac':
- return subprocess.check_output(['ifconfig']).splitlines()
- raise NotImplementedError('Platform %s not supported!' % host_platform)
-
- def _FindHostRndisInterface(self):
- """Returns the name of the host-side network interface."""
- interface_list = self._EnumerateHostInterfaces()
- ether_address = self._device.ReadFile(
- '%s/f_rndis/ethaddr' % self._RNDIS_DEVICE).strip()
- interface_name = None
- for line in interface_list:
- if not line.startswith((' ', '\t')):
- interface_name = line.split(':')[-2].strip()
- elif ether_address in line:
- return interface_name
-
- def _WriteProtectedFile(self, file_path, contents):
- subprocess.check_call(
- ['/usr/bin/sudo', 'bash', '-c',
- 'echo -e "%s" > %s' % (contents, file_path)])
-
- def _LoadInstalledHoRNDIS(self):
- """Attempt to load HoRNDIS if installed.
- If kext could not be loaded or if HoRNDIS is not installed, return False.
- """
- if not os.path.isdir('/System/Library/Extensions/HoRNDIS.kext'):
- logging.info('HoRNDIS not present on system.')
- return False
-
- def HoRNDISLoaded():
- return 'HoRNDIS' in subprocess.check_output(['kextstat'])
-
- if HoRNDISLoaded():
- return True
-
- logging.info('HoRNDIS installed but not running, trying to load manually.')
- subprocess.check_call(
- ['/usr/bin/sudo', 'kextload', '-b', 'com.joshuawise.kexts.HoRNDIS'])
-
- return HoRNDISLoaded()
-
- def _InstallHorndis(self, arch_name):
- if self._LoadInstalledHoRNDIS():
- logging.info('HoRNDIS kext loaded successfully.')
- return
- logging.info('Installing HoRNDIS...')
- pkg_path = binary_manager.FetchPath('horndis', arch_name, 'mac')
- subprocess.check_call(
- ['/usr/bin/sudo', 'installer', '-pkg', pkg_path, '-target', '/'])
-
- def _DisableRndis(self):
- try:
- self._device.SetProp('sys.usb.config', 'adb')
- except device_errors.AdbCommandFailedError:
- # Ignore exception due to USB connection being reset.
- pass
- self._device.WaitUntilFullyBooted()
-
- def _EnableRndis(self):
- """Enables the RNDIS network interface."""
- script_prefix = '/data/local/tmp/rndis'
- # This could be accomplished via "svc usb setFunction rndis" but only on
- # devices which have the "USB tethering" feature.
- # Also, on some devices, it's necessary to go through "none" function.
- script = """
-trap '' HUP
-trap '' TERM
-trap '' PIPE
-
-function manual_config() {
- echo %(functions)s > %(dev)s/functions
- echo 224 > %(dev)s/bDeviceClass
- echo 1 > %(dev)s/enable
- start adbd
- setprop sys.usb.state %(functions)s
-}
-
-# This function kills adb transport, so it has to be run "detached".
-function doit() {
- setprop sys.usb.config none
- while [ `getprop sys.usb.state` != "none" ]; do
- sleep 1
- done
- manual_config
- # For some combinations of devices and host kernels, adb won't work unless the
- # interface is up, but if we bring it up immediately, it will break adb.
- #sleep 1
- #ifconfig rndis0 192.168.123.2 netmask 255.255.255.0 up
- echo DONE >> %(prefix)s.log
-}
-
-doit &
- """ % {'dev': self._RNDIS_DEVICE, 'functions': 'rndis,adb',
- 'prefix': script_prefix}
- self._device.WriteFile('%s.sh' % script_prefix, script)
- # TODO(szym): run via su -c if necessary.
- self._device.RunShellCommand('rm %s.log' % script_prefix)
- self._device.RunShellCommand('. %s.sh' % script_prefix)
- self._device.WaitUntilFullyBooted()
- result = self._device.ReadFile('%s.log' % script_prefix).splitlines()
- assert any('DONE' in line for line in result), 'RNDIS script did not run!'
-
- def _CheckEnableRndis(self, force):
- """Enables the RNDIS network interface, retrying if necessary.
- Args:
- force: Disable RNDIS first, even if it appears already enabled.
- Returns:
- device_iface: RNDIS interface name on the device
- host_iface: corresponding interface name on the host
- """
- for _ in range(3):
- if not force:
- device_iface = self._FindDeviceRndisInterface()
- if device_iface:
- host_iface = self._FindHostRndisInterface()
- if host_iface:
- return device_iface, host_iface
- self._DisableRndis()
- self._EnableRndis()
- force = False
- raise Exception('Could not enable RNDIS, giving up.')
-
- def _Ip2Long(self, addr):
- return struct.unpack('!L', socket.inet_aton(addr))[0]
-
- def _IpPrefix2AddressMask(self, addr):
- def _Length2Mask(length):
- return 0xFFFFFFFF & ~((1 << (32 - length)) - 1)
-
- addr, masklen = addr.split('/')
- return self._Ip2Long(addr), _Length2Mask(int(masklen))
-
- def _GetHostAddresses(self, iface):
- """Returns the IP addresses on host's interfaces, breaking out |iface|."""
- interface_list = self._EnumerateHostInterfaces()
- addresses = []
- iface_address = None
- found_iface = False
- for line in interface_list:
- if not line.startswith((' ', '\t')):
- found_iface = iface in line
- match = re.search(r'(?<=inet )\S+', line)
- if match:
- address = match.group(0)
- if '/' in address:
- address = self._IpPrefix2AddressMask(address)
- else:
- match = re.search(r'(?<=netmask )\S+', line)
- address = self._Ip2Long(address), int(match.group(0), 16)
- if found_iface:
- assert not iface_address, (
- 'Found %s twice when parsing host interfaces.' % iface)
- iface_address = address
- else:
- addresses.append(address)
- return addresses, iface_address
-
- def _GetDeviceAddresses(self, excluded_iface):
- """Returns the IP addresses on all connected devices.
- Excludes interface |excluded_iface| on the selected device.
- """
- my_device = str(self._device)
- addresses = []
- for device_serial in android_device.GetDeviceSerials(None):
- try:
- device = device_utils.DeviceUtils(device_serial)
- if device_serial == my_device:
- excluded = excluded_iface
- else:
- excluded = 'no interfaces excluded on other devices'
- addresses += [line.split()[3]
- for line in device.RunShellCommand('ip -o -4 addr')
- if excluded not in line]
- except device_errors.CommandFailedError:
- logging.warning('Unable to determine IP addresses for %s',
- device_serial)
- return addresses
-
- def _ConfigureNetwork(self, device_iface, host_iface):
- """Configures the |device_iface| to be on the same network as |host_iface|.
- """
- def _Long2Ip(value):
- return socket.inet_ntoa(struct.pack('!L', value))
-
- def _IsNetworkUnique(network, addresses):
- return all((addr & mask != network & mask) for addr, mask in addresses)
-
- def _NextUnusedAddress(network, netmask, used_addresses):
- # Excludes '0' and broadcast.
- for suffix in range(1, 0xFFFFFFFF & ~netmask):
- candidate = network | suffix
- if candidate not in used_addresses:
- return candidate
-
- def HasHostAddress():
- _, host_address = self._GetHostAddresses(host_iface)
- return bool(host_address)
-
- if not HasHostAddress():
- if platform.GetHostPlatform().GetOSName() == 'mac':
- if 'Telemetry' not in subprocess.check_output(
- ['networksetup', '-listallnetworkservices']):
- subprocess.check_call(
- ['/usr/bin/sudo', 'networksetup',
- '-createnetworkservice', 'Telemetry', host_iface])
- subprocess.check_call(
- ['/usr/bin/sudo', 'networksetup',
- '-setmanual', 'Telemetry', '192.168.123.1', '255.255.255.0'])
- elif platform.GetHostPlatform().GetOSName() == 'linux':
- with open(self._NETWORK_INTERFACES) as f:
- orig_interfaces = f.read()
- if self._INTERFACES_INCLUDE not in orig_interfaces:
- interfaces = '\n'.join([
- orig_interfaces,
- '',
- '# Added by Telemetry.',
- self._INTERFACES_INCLUDE])
- self._WriteProtectedFile(self._NETWORK_INTERFACES, interfaces)
- interface_conf_file = self._TELEMETRY_INTERFACE_FILE.format(host_iface)
- if not os.path.exists(interface_conf_file):
- interface_conf_dir = os.path.dirname(interface_conf_file)
- if not os.path.exists(interface_conf_dir):
- subprocess.call(['/usr/bin/sudo', '/bin/mkdir', interface_conf_dir])
- subprocess.call(
- ['/usr/bin/sudo', '/bin/chmod', '755', interface_conf_dir])
- interface_conf = '\n'.join([
- '# Added by Telemetry for RNDIS forwarding.',
- 'allow-hotplug %s' % host_iface,
- 'iface %s inet static' % host_iface,
- ' address 192.168.123.1',
- ' netmask 255.255.255.0',
- ])
- self._WriteProtectedFile(interface_conf_file, interface_conf)
- subprocess.check_call(['/usr/bin/sudo', 'ifup', host_iface])
- logging.info('Waiting for RNDIS connectivity...')
- util.WaitFor(HasHostAddress, 30)
-
- addresses, host_address = self._GetHostAddresses(host_iface)
- assert host_address, 'Interface %s could not be configured.' % host_iface
-
- host_ip, netmask = host_address # pylint: disable=unpacking-non-sequence
- network = host_ip & netmask
-
- if not _IsNetworkUnique(network, addresses):
- logging.warning(
- 'The IP address configuration %s of %s is not unique!\n'
- 'Check your /etc/network/interfaces. If this overlap is intended,\n'
- 'you might need to use: ip rule add from <device_ip> lookup <table>\n'
- 'or add the interface to a bridge in order to route to this network.'
- % (host_address, host_iface)
- )
-
- # Find unused IP address.
- used_addresses = [addr for addr, _ in addresses]
- used_addresses += [self._IpPrefix2AddressMask(addr)[0]
- for addr in self._GetDeviceAddresses(device_iface)]
- used_addresses += [host_ip]
-
- device_ip = _NextUnusedAddress(network, netmask, used_addresses)
- assert device_ip, ('The network %s on %s is full.' %
- (host_address, host_iface))
-
- host_ip = _Long2Ip(host_ip)
- device_ip = _Long2Ip(device_ip)
- netmask = _Long2Ip(netmask)
-
- # TODO(szym) run via su -c if necessary.
- self._device.RunShellCommand(
- 'ifconfig %s %s netmask %s up' % (device_iface, device_ip, netmask))
- # Enabling the interface sometimes breaks adb.
- self._device.WaitUntilFullyBooted()
- self._host_iface = host_iface
- self._host_ip = host_ip
- self.device_iface = device_iface
- self._device_ip = device_ip
-
- def _TestConnectivity(self):
- with open(os.devnull, 'wb') as devnull:
- return subprocess.call(['ping', '-q', '-c1', '-W1', self._device_ip],
- stdout=devnull) == 0
-
- def OverrideRoutingPolicy(self):
- """Override any routing policy that could prevent
- packets from reaching the rndis interface
- """
- policies = self._device.RunShellCommand('ip rule')
- if len(policies) > 1 and not 'lookup main' in policies[1]:
- self._device.RunShellCommand('ip rule add prio 1 from all table main')
- self._device.RunShellCommand('ip route flush cache')
-
- def RestoreRoutingPolicy(self):
- policies = self._device.RunShellCommand('ip rule')
- if len(policies) > 1 and re.match("^1:.*lookup main", policies[1]):
- self._device.RunShellCommand('ip rule del prio 1')
- self._device.RunShellCommand('ip route flush cache')
-
- def _CheckConfigureNetwork(self):
- """Enables RNDIS and configures it, retrying until we have connectivity."""
- force = False
- for _ in range(3):
- device_iface, host_iface = self._CheckEnableRndis(force)
- self._ConfigureNetwork(device_iface, host_iface)
- self.OverrideRoutingPolicy()
- # Sometimes the first packet will wake up the connection.
- for _ in range(3):
- if self._TestConnectivity():
- return
- force = True
- self.RestoreRoutingPolicy()
- raise Exception('No connectivity, giving up.')

Powered by Google App Engine
This is Rietveld 408576698