summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJack He <siyuanh@google.com>2021-11-23 13:00:46 -0800
committerJack He <siyuanh@google.com>2022-01-07 13:25:09 -0800
commit9b6a25306351ed6bcfad8c7651d6e2359ab44d73 (patch)
treec5734f898e8ed29c7c298f13554a63a1ae45aacf
parent8df0a19785fc4094435e0447570802da88345572 (diff)
downloadconnectivity-9b6a25306351ed6bcfad8c7651d6e2359ab44d73.tar.gz
SL4A: Add options to override client, server, and forwarded port
* When setting up SL4A tests over ssh-forwarding, we need to control which port is used on the host so that we can forward it to a remote machine Bug: 207525224 Test: gd/cert/run LeAdvancedScanningTest Change-Id: I684cf3f50eb0b68274423743606b796c8875eb91 Merged-In: I684cf3f50eb0b68274423743606b796c8875eb91 (cherry picked from commit f0378706d9e5faada04a3ed7ea67237c4a99ab26)
-rwxr-xr-xacts/framework/acts/controllers/android_device.py171
-rw-r--r--acts/framework/acts/controllers/sl4a_lib/sl4a_manager.py42
-rw-r--r--acts/framework/acts/controllers/sl4a_lib/sl4a_session.py37
3 files changed, 157 insertions, 93 deletions
diff --git a/acts/framework/acts/controllers/android_device.py b/acts/framework/acts/controllers/android_device.py
index e5f568f5e..3a35bf3bd 100755
--- a/acts/framework/acts/controllers/android_device.py
+++ b/acts/framework/acts/controllers/android_device.py
@@ -47,6 +47,10 @@ MOBLY_CONTROLLER_CONFIG_NAME = "AndroidDevice"
ACTS_CONTROLLER_REFERENCE_NAME = "android_devices"
ANDROID_DEVICE_PICK_ALL_TOKEN = "*"
+# Key name for SL4A extra params in config file
+ANDROID_DEVICE_SL4A_CLIENT_PORT_KEY = "sl4a_client_port"
+ANDROID_DEVICE_SL4A_FORWARDED_PORT_KEY = "sl4a_forwarded_port"
+ANDROID_DEVICE_SL4A_SERVER_PORT_KEY = "sl4a_server_port"
# Key name for adb logcat extra params in config file.
ANDROID_DEVICE_ADB_LOGCAT_PARAM_KEY = "adb_logcat_param"
ANDROID_DEVICE_EMPTY_CONFIG_MSG = "Configuration is empty, abort!"
@@ -232,12 +236,41 @@ def get_instances_with_configs(configs):
raise errors.AndroidDeviceConfigError(
"Required value 'serial' is missing in AndroidDevice config %s."
% c)
+ client_port = 0
+ if ANDROID_DEVICE_SL4A_CLIENT_PORT_KEY in c:
+ try:
+ client_port = int(c.pop(ANDROID_DEVICE_SL4A_CLIENT_PORT_KEY))
+ except ValueError:
+ raise errors.AndroidDeviceConfigError(
+ "'%s' is not a valid number for config %s" %
+ (ANDROID_DEVICE_SL4A_CLIENT_PORT_KEY, c))
+ server_port = None
+ if ANDROID_DEVICE_SL4A_SERVER_PORT_KEY in c:
+ try:
+ server_port = int(c.pop(ANDROID_DEVICE_SL4A_SERVER_PORT_KEY))
+ except ValueError:
+ raise errors.AndroidDeviceConfigError(
+ "'%s' is not a valid number for config %s" %
+ (ANDROID_DEVICE_SL4A_SERVER_PORT_KEY, c))
+ forwarded_port = 0
+ if ANDROID_DEVICE_SL4A_FORWARDED_PORT_KEY in c:
+ try:
+ forwarded_port = int(
+ c.pop(ANDROID_DEVICE_SL4A_FORWARDED_PORT_KEY))
+ except ValueError:
+ raise errors.AndroidDeviceConfigError(
+ "'%s' is not a valid number for config %s" %
+ (ANDROID_DEVICE_SL4A_FORWARDED_PORT_KEY, c))
ssh_config = c.pop('ssh_config', None)
ssh_connection = None
if ssh_config is not None:
ssh_settings = settings.from_config(ssh_config)
ssh_connection = connection.SshConnection(ssh_settings)
- ad = AndroidDevice(serial, ssh_connection=ssh_connection)
+ ad = AndroidDevice(serial,
+ ssh_connection=ssh_connection,
+ client_port=client_port,
+ forwarded_port=forwarded_port,
+ server_port=server_port)
ad.load_config(c)
results.append(ad)
return results
@@ -357,14 +390,27 @@ class AndroidDevice:
adb: An AdbProxy object used for interacting with the device via adb.
fastboot: A FastbootProxy object used for interacting with the device
via fastboot.
+ client_port: Preferred client port number on the PC host side for SL4A
+ forwarded_port: Preferred server port number forwarded from Android
+ to the host PC via adb for SL4A connections
+ server_port: Preferred server port used by SL4A on Android device
+
"""
- def __init__(self, serial='', ssh_connection=None):
+ def __init__(self,
+ serial='',
+ ssh_connection=None,
+ client_port=0,
+ forwarded_port=0,
+ server_port=None):
self.serial = serial
# logging.log_path only exists when this is used in an ACTS test run.
log_path_base = getattr(logging, 'log_path', '/tmp/logs')
self.log_dir = 'AndroidDevice%s' % serial
self.log_path = os.path.join(log_path_base, self.log_dir)
+ self.client_port = client_port
+ self.forwarded_port = forwarded_port
+ self.server_port = server_port
self.log = tracelogger.TraceLogger(
AndroidDeviceLoggerAdapter(logging.getLogger(),
{'serial': serial}))
@@ -374,8 +420,8 @@ class AndroidDevice:
self.register_service(services.Sl4aService(self))
self.adb_logcat_process = None
self.adb = adb.AdbProxy(serial, ssh_connection=ssh_connection)
- self.fastboot = fastboot.FastbootProxy(
- serial, ssh_connection=ssh_connection)
+ self.fastboot = fastboot.FastbootProxy(serial,
+ ssh_connection=ssh_connection)
if not self.is_bootloader:
self.root_adb()
self._ssh_connection = ssh_connection
@@ -425,8 +471,8 @@ class AndroidDevice:
Stop adb logcat and terminate sl4a sessions if exist.
"""
- event_bus.post(
- android_events.AndroidStopServicesEvent(self), ignore_errors=True)
+ event_bus.post(android_events.AndroidStopServicesEvent(self),
+ ignore_errors=True)
def is_connected(self):
out = self.adb.devices()
@@ -651,7 +697,13 @@ class AndroidDevice:
>>> ad = AndroidDevice()
>>> droid, ed = ad.get_droid()
"""
- session = self._sl4a_manager.create_session()
+ self.log.debug(
+ "Creating RPC client_port={}, forwarded_port={}, server_port={}".
+ format(self.client_port, self.forwarded_port, self.server_port))
+ session = self._sl4a_manager.create_session(
+ client_port=self.client_port,
+ forwarded_port=self.forwarded_port,
+ server_port=self.server_port)
droid = session.rpc_client
if handle_event:
ed = session.get_event_dispatcher()
@@ -671,9 +723,8 @@ class AndroidDevice:
"""
for cmd in ("ps -A", "ps"):
try:
- out = self.adb.shell(
- '%s | grep "S %s"' % (cmd, package_name),
- ignore_status=True)
+ out = self.adb.shell('%s | grep "S %s"' % (cmd, package_name),
+ ignore_status=True)
if package_name not in out:
continue
try:
@@ -765,10 +816,10 @@ class AndroidDevice:
return adb_excerpt_path
def search_logcat(self,
- matching_string,
- begin_time=None,
- end_time=None,
- logcat_path=None):
+ matching_string,
+ begin_time=None,
+ end_time=None,
+ logcat_path=None):
"""Search logcat message with given string.
Args:
@@ -798,13 +849,12 @@ class AndroidDevice:
"""
if not logcat_path:
logcat_path = os.path.join(self.device_log_path,
- 'adblog_%s_debug.txt' % self.serial)
+ 'adblog_%s_debug.txt' % self.serial)
if not os.path.exists(logcat_path):
self.log.warning("Logcat file %s does not exist." % logcat_path)
return
- output = job.run(
- "grep '%s' %s" % (matching_string, logcat_path),
- ignore_status=True)
+ output = job.run("grep '%s' %s" % (matching_string, logcat_path),
+ ignore_status=True)
if not output.stdout or output.exit_status != 0:
return []
if begin_time:
@@ -812,13 +862,13 @@ class AndroidDevice:
log_begin_time = acts_logger.epoch_to_log_line_timestamp(
begin_time)
begin_time = datetime.strptime(log_begin_time,
- "%Y-%m-%d %H:%M:%S.%f")
+ "%Y-%m-%d %H:%M:%S.%f")
if end_time:
if not isinstance(end_time, datetime):
log_end_time = acts_logger.epoch_to_log_line_timestamp(
end_time)
end_time = datetime.strptime(log_end_time,
- "%Y-%m-%d %H:%M:%S.%f")
+ "%Y-%m-%d %H:%M:%S.%f")
result = []
logs = re.findall(r'(\S+\s\S+)(.*)', output.stdout)
for log in logs:
@@ -890,8 +940,8 @@ class AndroidDevice:
Returns:
Linux UID for the apk.
"""
- output = self.adb.shell(
- "dumpsys package %s | grep userId=" % apk_name, ignore_status=True)
+ output = self.adb.shell("dumpsys package %s | grep userId=" % apk_name,
+ ignore_status=True)
result = re.search(r"userId=(\d+)", output)
if result:
return result.group(1)
@@ -934,9 +984,8 @@ class AndroidDevice:
"""
for cmd in ("ps -A", "ps"):
try:
- out = self.adb.shell(
- '%s | grep "S %s"' % (cmd, package_name),
- ignore_status=True)
+ out = self.adb.shell('%s | grep "S %s"' % (cmd, package_name),
+ ignore_status=True)
if package_name in out:
self.log.info("apk %s is running", package_name)
return True
@@ -961,8 +1010,8 @@ class AndroidDevice:
True if package is installed. False otherwise.
"""
try:
- self.adb.shell(
- 'am force-stop %s' % package_name, ignore_status=True)
+ self.adb.shell('am force-stop %s' % package_name,
+ ignore_status=True)
except Exception as e:
self.log.warn("Fail to stop package %s: %s", package_name, e)
@@ -1010,8 +1059,8 @@ class AndroidDevice:
br_out_path = out.split(':')[1].strip().split()[0]
self.adb.pull("%s %s" % (br_out_path, full_out_path))
else:
- self.adb.bugreport(
- " > {}".format(full_out_path), timeout=BUG_REPORT_TIMEOUT)
+ self.adb.bugreport(" > {}".format(full_out_path),
+ timeout=BUG_REPORT_TIMEOUT)
self.log.info("Bugreport for %s taken at %s.", test_name,
full_out_path)
self.adb.wait_for_device(timeout=WAIT_FOR_DEVICE_TIMEOUT)
@@ -1073,10 +1122,10 @@ class AndroidDevice:
if not host_path:
host_path = self.log_path
for device_path in device_paths:
- self.log.info(
- 'Pull from device: %s -> %s' % (device_path, host_path))
- self.adb.pull(
- "%s %s" % (device_path, host_path), timeout=PULL_TIMEOUT)
+ self.log.info('Pull from device: %s -> %s' %
+ (device_path, host_path))
+ self.adb.pull("%s %s" % (device_path, host_path),
+ timeout=PULL_TIMEOUT)
def check_crash_report(self,
test_name=None,
@@ -1091,10 +1140,9 @@ class AndroidDevice:
except Exception as e:
self.log.debug("received exception %s", e)
continue
- crashes = self.get_file_names(
- crash_path,
- skip_files=CRASH_REPORT_SKIPS,
- begin_time=begin_time)
+ crashes = self.get_file_names(crash_path,
+ skip_files=CRASH_REPORT_SKIPS,
+ begin_time=begin_time)
if crash_path == "/data/tombstones/" and crashes:
tombstones = crashes[:]
for tombstone in tombstones:
@@ -1117,18 +1165,18 @@ class AndroidDevice:
# Sleep 10 seconds for the buffered log to be written in qxdm log file
time.sleep(10)
log_path = getattr(self, "qxdm_log_path", DEFAULT_QXDM_LOG_PATH)
- qxdm_logs = self.get_file_names(
- log_path, begin_time=begin_time, match_string="*.qmdl")
+ qxdm_logs = self.get_file_names(log_path,
+ begin_time=begin_time,
+ match_string="*.qmdl")
if qxdm_logs:
qxdm_log_path = os.path.join(self.device_log_path,
"QXDM_%s" % self.serial)
os.makedirs(qxdm_log_path, exist_ok=True)
self.log.info("Pull QXDM Log %s to %s", qxdm_logs, qxdm_log_path)
self.pull_files(qxdm_logs, qxdm_log_path)
- self.adb.pull(
- "/firmware/image/qdsp6m.qdb %s" % qxdm_log_path,
- timeout=PULL_TIMEOUT,
- ignore_status=True)
+ self.adb.pull("/firmware/image/qdsp6m.qdb %s" % qxdm_log_path,
+ timeout=PULL_TIMEOUT,
+ ignore_status=True)
else:
self.log.error("Didn't find QXDM logs in %s." % log_path)
if "Verizon" in self.adb.getprop("gsm.sim.operator.alpha"):
@@ -1147,8 +1195,9 @@ class AndroidDevice:
# Sleep 10 seconds for the buffered log to be written in sdm log file
time.sleep(10)
log_path = getattr(self, "sdm_log_path", DEFAULT_SDM_LOG_PATH)
- sdm_logs = self.get_file_names(
- log_path, begin_time=begin_time, match_string="*.sdm*")
+ sdm_logs = self.get_file_names(log_path,
+ begin_time=begin_time,
+ match_string="*.sdm*")
if sdm_logs:
sdm_log_path = os.path.join(self.device_log_path,
"SDM_%s" % self.serial)
@@ -1234,8 +1283,8 @@ class AndroidDevice:
status: true if iperf client start successfully.
results: results have data flow information
"""
- out = self.adb.shell(
- "iperf3 -c {} {}".format(server_host, extra_args), timeout=timeout)
+ out = self.adb.shell("iperf3 -c {} {}".format(server_host, extra_args),
+ timeout=timeout)
clean_out = out.split('\n')
if "error" in clean_out[0].lower():
return False, clean_out
@@ -1286,7 +1335,9 @@ class AndroidDevice:
'Device %s booting process timed out.' % self.serial,
serial=self.serial)
- def reboot(self, stop_at_lock_screen=False, timeout=180,
+ def reboot(self,
+ stop_at_lock_screen=False,
+ timeout=180,
wait_after_reboot_complete=1):
"""Reboots the device.
@@ -1323,8 +1374,8 @@ class AndroidDevice:
# want the device to be missing to prove the device has kicked
# off the reboot.
break
- self.wait_for_boot_completion(
- timeout=(timeout - time.time() + timeout_start))
+ self.wait_for_boot_completion(timeout=(timeout - time.time() +
+ timeout_start))
self.log.debug('Wait for a while after boot completion.')
time.sleep(wait_after_reboot_complete)
@@ -1359,8 +1410,8 @@ class AndroidDevice:
break
except adb.AdbError as e:
if timer + 1 == timeout:
- self.log.warning(
- 'Unable to find IP address for %s.' % interface)
+ self.log.warning('Unable to find IP address for %s.' %
+ interface)
return None
else:
time.sleep(1)
@@ -1426,7 +1477,7 @@ class AndroidDevice:
for cmd in dumpsys_cmd:
output = self.adb.shell(cmd, ignore_status=True)
if not output or "not found" in output or "Can't find" in output or (
- "mFocusedApp=null" in output):
+ "mFocusedApp=null" in output):
result = ''
else:
result = output.split(' ')[-2]
@@ -1565,11 +1616,11 @@ class AndroidDevice:
return
if not self.is_user_setup_complete() or self.is_setupwizard_on():
# b/116709539 need this to prevent reboot after skip setup wizard
- self.adb.shell(
- "am start -a com.android.setupwizard.EXIT", ignore_status=True)
- self.adb.shell(
- "pm disable %s" % self.get_setupwizard_package_name(),
- ignore_status=True)
+ self.adb.shell("am start -a com.android.setupwizard.EXIT",
+ ignore_status=True)
+ self.adb.shell("pm disable %s" %
+ self.get_setupwizard_package_name(),
+ ignore_status=True)
# Wait up to 5 seconds for user_setup_complete to be updated
end_time = time.time() + 5
while time.time() < end_time:
@@ -1619,8 +1670,8 @@ class AndroidDevice:
try:
self.ensure_verity_disabled()
self.adb.remount()
- out = self.adb.push(
- '%s %s' % (src_file_path, dst_file_path), timeout=push_timeout)
+ out = self.adb.push('%s %s' % (src_file_path, dst_file_path),
+ timeout=push_timeout)
if 'error' in out:
self.log.error('Unable to push system file %s to %s due to %s',
src_file_path, dst_file_path, out)
diff --git a/acts/framework/acts/controllers/sl4a_lib/sl4a_manager.py b/acts/framework/acts/controllers/sl4a_lib/sl4a_manager.py
index 8d76f1fa4..181048dac 100644
--- a/acts/framework/acts/controllers/sl4a_lib/sl4a_manager.py
+++ b/acts/framework/acts/controllers/sl4a_lib/sl4a_manager.py
@@ -109,12 +109,12 @@ class Sl4aManager(object):
self._listen_for_port_lock = threading.Lock()
self._sl4a_ports = set()
self.adb = adb
- self.log = logger.create_logger(
- lambda msg: '[SL4A Manager|%s] %s' % (adb.serial, msg))
+ self.log = logger.create_logger(lambda msg: '[SL4A Manager|%s] %s' % (
+ adb.serial, msg))
self.sessions = {}
self._started = False
- self.error_reporter = error_reporter.ErrorReporter(
- 'SL4A %s' % adb.serial)
+ self.error_reporter = error_reporter.ErrorReporter('SL4A %s' %
+ adb.serial)
@property
def sl4a_ports_in_use(self):
@@ -161,8 +161,8 @@ class Sl4aManager(object):
raise rpc_client.Sl4aConnectionError(
'Unable to find a valid open port for a new server connection. '
- 'Expected port: %s. Open ports: %s' % (device_port,
- self._sl4a_ports))
+ 'Expected port: %s. Open ports: %s' %
+ (device_port, self._sl4a_ports))
def _get_all_ports_command(self):
"""Returns the list of all ports from the command to get ports."""
@@ -203,9 +203,8 @@ class Sl4aManager(object):
def is_sl4a_installed(self):
"""Returns True if SL4A is installed on the AndroidDevice."""
return bool(
- self.adb.shell(
- 'pm path com.googlecode\.android_scripting',
- ignore_status=True))
+ self.adb.shell('pm path com.googlecode\.android_scripting',
+ ignore_status=True))
def start_sl4a_service(self):
"""Starts the SL4A Service on the device.
@@ -219,7 +218,8 @@ class Sl4aManager(object):
raise rpc_client.Sl4aNotInstalledError(
'SL4A is not installed on device %s' % self.adb.serial)
if self.adb.shell(
- '(ps | grep "S com.googlecode.android_scripting") || true'):
+ '(ps | grep "S com.googlecode.android_scripting") || true'
+ ):
# Close all SL4A servers not opened by this manager.
# TODO(markdr): revert back to closing all ports after
# b/76147680 is resolved.
@@ -244,6 +244,7 @@ class Sl4aManager(object):
def create_session(self,
max_connections=None,
client_port=0,
+ forwarded_port=0,
server_port=None):
"""Creates an SL4A server with the given ports if possible.
@@ -252,7 +253,9 @@ class Sl4aManager(object):
be randomized.
Args:
- client_port: The port on the host machine
+ client_port: The client port on the host machine
+ forwarded_port: The server port on the host machine forwarded
+ by adb from the Android device
server_port: The port on the Android device.
max_connections: The max number of client connections for the
session.
@@ -268,14 +271,17 @@ class Sl4aManager(object):
# Otherwise, open a new server on a random port.
else:
server_port = 0
+ self.log.debug(
+ "Creating SL4A session client_port={}, forwarded_port={}, server_port={}"
+ .format(client_port, forwarded_port, server_port))
self.start_sl4a_service()
- session = sl4a_session.Sl4aSession(
- self.adb,
- client_port,
- server_port,
- self.obtain_sl4a_server,
- self.diagnose_failure,
- max_connections=max_connections)
+ session = sl4a_session.Sl4aSession(self.adb,
+ client_port,
+ server_port,
+ self.obtain_sl4a_server,
+ self.diagnose_failure,
+ forwarded_port,
+ max_connections=max_connections)
self.sessions[session.uid] = session
return session
diff --git a/acts/framework/acts/controllers/sl4a_lib/sl4a_session.py b/acts/framework/acts/controllers/sl4a_lib/sl4a_session.py
index c0a4e1dae..04fd78716 100644
--- a/acts/framework/acts/controllers/sl4a_lib/sl4a_session.py
+++ b/acts/framework/acts/controllers/sl4a_lib/sl4a_session.py
@@ -55,6 +55,7 @@ class Sl4aSession(object):
device_port,
get_server_port_func,
on_error_callback,
+ forwarded_port=0,
max_connections=None):
"""Creates an SL4A Session.
@@ -67,6 +68,8 @@ class Sl4aSession(object):
server for its first connection.
device_port: The SL4A server port to be used as a hint for which
SL4A server to connect to.
+ forwarded_port: The server port on host machine forwarded by adb
+ from Android device to accept SL4A connection
"""
self._event_dispatcher = None
self._terminate_lock = threading.Lock()
@@ -79,24 +82,24 @@ class Sl4aSession(object):
self.log = logger.create_logger(_log_formatter)
+ self.forwarded_port = forwarded_port
self.server_port = device_port
self.uid = UNKNOWN_UID
self.obtain_server_port = get_server_port_func
self._on_error_callback = on_error_callback
connection_creator = self._rpc_connection_creator(host_port)
- self.rpc_client = rpc_client.RpcClient(
- self.uid,
- self.adb.serial,
- self.diagnose_failure,
- connection_creator,
- max_connections=max_connections)
+ self.rpc_client = rpc_client.RpcClient(self.uid,
+ self.adb.serial,
+ self.diagnose_failure,
+ connection_creator,
+ max_connections=max_connections)
def _rpc_connection_creator(self, host_port):
def create_client(uid):
- return self._create_rpc_connection(
- ports=sl4a_ports.Sl4aPorts(host_port, 0, self.server_port),
- uid=uid)
+ return self._create_rpc_connection(ports=sl4a_ports.Sl4aPorts(
+ host_port, self.forwarded_port, self.server_port),
+ uid=uid)
return create_client
@@ -156,10 +159,14 @@ class Sl4aSession(object):
ports.server_port = self.obtain_server_port(ports.server_port)
self.server_port = ports.server_port
# Forward the device port to the host.
- ports.forwarded_port = self._create_forwarded_port(ports.server_port)
+ ports.forwarded_port = self._create_forwarded_port(
+ ports.server_port, hinted_port=ports.forwarded_port)
client_socket, fd = self._create_client_side_connection(ports)
- client = rpc_connection.RpcConnection(
- self.adb, ports, client_socket, fd, uid=uid)
+ client = rpc_connection.RpcConnection(self.adb,
+ ports,
+ client_socket,
+ fd,
+ uid=uid)
client.open()
if uid == UNKNOWN_UID:
self.uid = client.uid
@@ -195,9 +202,9 @@ class Sl4aSession(object):
except OSError as e:
# If the port is in use, log and ask for any open port.
if e.errno == errno.EADDRINUSE:
- self.log.warning(
- 'Port %s is already in use on the host. '
- 'Generating a random port.' % ports.client_port)
+ self.log.warning('Port %s is already in use on the host. '
+ 'Generating a random port.' %
+ ports.client_port)
ports.client_port = 0
return self._create_client_side_connection(ports)
raise