diff options
author | android-build-prod (mdb) <android-build-team-robot@google.com> | 2020-10-01 20:24:51 +0000 |
---|---|---|
committer | android-build-prod (mdb) <android-build-team-robot@google.com> | 2020-10-01 20:24:51 +0000 |
commit | e362bce9620df69a5d8e57267859639bc9eee6cb (patch) | |
tree | c000ae990fbce94d54674e967833b9f3d1a2810e | |
parent | ceedf46a17f6d5168317f3442b6ead5ec13699c8 (diff) | |
parent | c1069cdee8f9535876a5116ec5f152da78b8cd45 (diff) | |
download | platform_tools_test_connectivity-sdk-release.tar.gz platform_tools_test_connectivity-sdk-release.tar.bz2 platform_tools_test_connectivity-sdk-release.zip |
Snap for 6877830 from c1069cdee8f9535876a5116ec5f152da78b8cd45 to sdk-releasesdk-release
Change-Id: I4a66b59d70ef241abc518f4c788c768fc57a333f
273 files changed, 16208 insertions, 3978 deletions
diff --git a/acts/README.md b/acts/README.md index a4e9950e11..55c69b8a74 100644 --- a/acts/README.md +++ b/acts/README.md @@ -5,7 +5,7 @@ devices. It provides a simple execution interface; a set of pluggable libraries for accessing commercially avilable devices, Android devices, and a collection of utility functions to further ease test development. It is an ideal desktop tool for a wireless stack developer or integrator whether exercising a new code -path, performing sanity testing, or running extended regression test suites. +path, performing confidence testing, or running extended regression test suites. Included in the tests/google directory are a bundle of tests, many of which can be run with as little as one or two Android devices with wifi, cellular, or @@ -76,7 +76,7 @@ displayed. Check the "Always" box and click "Yes". 2. Run "python3.4 setup.py install" with elevated permissions 3. To verify ACTS is ready to go, at the location for README, and run: cd framework/tests/ \ - && act.py -c acts_sanity_test_config.json -tc IntegrationTest + && act.py -c acts_confidence_test_config.json -tc IntegrationTest After installation, `act.py` will be in usr/bin and can be called as command line utilities. Components in ACTS are importable under the package "acts." @@ -90,11 +90,11 @@ $ python ## Breaking Down a Sample Command -Above, the command `act.py -c acts_sanity_test_config.json -tc IntegrationTest` +Above, the command `act.py -c acts_confidence_test_config.json -tc IntegrationTest` was run to verify ACTS was properly set up. Below are the components of that command: - `act.py`: is the script that runs the test -- -c acts_sanity_test_config: is the flag and name of the configuration file +- -c acts_confidence_test_config: is the flag and name of the configuration file to be used in the test - -tc IntegrationTest: is the name of the test case diff --git a/acts/framework/MANIFEST.in b/acts/framework/MANIFEST.in index f6607fdca6..03536f5be2 100644 --- a/acts/framework/MANIFEST.in +++ b/acts/framework/MANIFEST.in @@ -1,6 +1,5 @@ include setup.py README.txt sample_config.json -recursive-include acts *.py +recursive-include acts * recursive-include tests * global-exclude .DS_Store global-exclude *.pyc - diff --git a/acts/framework/acts/controllers/OWNERS b/acts/framework/acts/controllers/OWNERS index 9b32fd0c5d..ef0ddca385 100644 --- a/acts/framework/acts/controllers/OWNERS +++ b/acts/framework/acts/controllers/OWNERS @@ -1,2 +1,3 @@ per-file fuchsia_device.py = tturney@google.com,jmbrenna@google.com per-file bluetooth_pts_device.py = tturney@google.com +per-file cellular_simulator.py = iguarna@google.com, chaoyangf@google.com, codycaldwell@google.com, yixiang@google.com
\ No newline at end of file diff --git a/acts/framework/acts/controllers/__init__.py b/acts/framework/acts/controllers/__init__.py index 1c8ed4ca00..b805e9e206 100644 --- a/acts/framework/acts/controllers/__init__.py +++ b/acts/framework/acts/controllers/__init__.py @@ -26,5 +26,5 @@ def destroy(objs): __all__ = [ "android_device", "attenuator", "bluetooth_pts_device", "monsoon", "access_point", "iperf_server", "packet_sender", "arduino_wifi_dongle", - "packet_capture", "fuchsia_device", "pdu" + "packet_capture", "fuchsia_device", "pdu", "openwrt_ap" ] diff --git a/acts/framework/acts/controllers/access_point.py b/acts/framework/acts/controllers/access_point.py index 15a3ca05f1..38e59d7248 100755 --- a/acts/framework/acts/controllers/access_point.py +++ b/acts/framework/acts/controllers/access_point.py @@ -120,6 +120,7 @@ class AccessPoint(object): self.log = logger.create_logger(lambda msg: '[Access Point|%s] %s' % (self.ssh_settings.hostname, msg)) self.device_pdu_config = configs.get('PduDevice', None) + self.identifier = self.ssh_settings.hostname if 'ap_subnet' in configs: self._AP_2G_SUBNET_STR = configs['ap_subnet']['2g'] @@ -573,25 +574,38 @@ class AccessPoint(object): if ra_count_str: return int(ra_count_str.split()[1]) - def is_pingable(self): - """Attempts to ping the access point. - - Returns: - True if ping is successful, else False + def ping(self, + dest_ip, + count=3, + interval=1000, + timeout=1000, + size=56, + additional_ping_params=None): + """Pings from AP to dest_ip, returns dict of ping stats (see utils.ping) """ - return utils.is_pingable(self.ssh_settings.hostname) - - def is_sshable(self): - """Attempts to run command via ssh. - - Returns: - True if no exceptions, else False - """ - try: - self.ssh.run('echo') - except connection.Error: - return False - return True + return utils.ping(self.ssh, + dest_ip, + count=count, + interval=interval, + timeout=timeout, + size=size, + additional_ping_params=additional_ping_params) + + def can_ping(self, + dest_ip, + count=1, + interval=1000, + timeout=1000, + size=56, + additional_ping_params=None): + """Returns whether ap can ping dest_ip (see utils.can_ping)""" + return utils.can_ping(self.ssh, + dest_ip, + count=count, + interval=interval, + timeout=timeout, + size=size, + additional_ping_params=additional_ping_params) def hard_power_cycle(self, pdus, @@ -608,12 +622,12 @@ class AccessPoint(object): unreachable ping_timeout: int, time to wait for AccessPoint to responsd to pings ssh_timeout: int, time to wait for AccessPoint to allow SSH - hostapd_configs (optional): list, containing hostapd settings. If + hostapd_configs (optional): list, containing hostapd settings. If present, these networks will be spun up after the AP has rebooted. This list can either contain HostapdConfig objects, or - dictionaries with the start_ap params - (i.e { 'hostapd_config': <HostapdConfig>, - 'setup_bridge': <bool>, + dictionaries with the start_ap params + (i.e { 'hostapd_config': <HostapdConfig>, + 'setup_bridge': <bool>, 'additional_parameters': <dict> } ). Raise: Error, if no PduDevice is provided in AccessPoint config. @@ -637,7 +651,7 @@ class AccessPoint(object): self.log.info('Verifying AccessPoint is unreachable.') timeout = time.time() + unreachable_timeout while time.time() < timeout: - if not self.is_pingable(): + if not utils.can_ping(job, self.ssh_settings.hostname): self.log.info('AccessPoint is unreachable as expected.') break else: @@ -657,7 +671,7 @@ class AccessPoint(object): self.log.info('Waiting for AccessPoint to respond to pings.') timeout = time.time() + ping_timeout while time.time() < timeout: - if self.is_pingable(): + if utils.can_ping(job, self.ssh_settings.hostname): self.log.info('AccessPoint responded to pings.') break else: @@ -672,13 +686,15 @@ class AccessPoint(object): self.log.info('Waiting for AccessPoint to allow ssh connection.') timeout = time.time() + ssh_timeout while time.time() < timeout: - if self.is_sshable(): - self.log.info('AccessPoint available via ssh.') - break - else: + try: + self.ssh.run('echo') + except connection.Error: self.log.debug('AccessPoint is not allowing ssh connection. ' 'Retrying in 1 second.') time.sleep(1) + else: + self.log.info('AccessPoint available via ssh.') + break else: raise ConnectionError('Timed out waiting for AccessPoint (%s) to ' 'allow ssh connection.' % diff --git a/acts/framework/acts/controllers/adb.py b/acts/framework/acts/controllers/adb.py index ff1a2ab9b3..1d15e07e58 100644 --- a/acts/framework/acts/controllers/adb.py +++ b/acts/framework/acts/controllers/adb.py @@ -19,6 +19,7 @@ from builtins import str import logging import re import shlex +import shutil from acts.controllers.adb_lib.error import AdbError from acts.libs.proc import job @@ -73,7 +74,7 @@ class AdbProxy(object): """ self.serial = serial self._server_local_port = None - adb_path = job.run("which adb").stdout + adb_path = shutil.which('adb') adb_cmd = [shlex.quote(adb_path)] if serial: adb_cmd.append("-s %s" % serial) @@ -259,17 +260,12 @@ class AdbProxy(object): def shell_nb(self, command): return self._exec_adb_cmd_nb('shell', shlex.quote(command)) - def pull(self, - command, - ignore_status=False, - timeout=DEFAULT_ADB_PULL_TIMEOUT): - return self._exec_adb_cmd( - 'pull', command, ignore_status=ignore_status, timeout=timeout) - def __getattr__(self, name): def adb_call(*args, **kwargs): usage_metadata_logger.log_usage(self.__module__, name) clean_name = name.replace('_', '-') + if clean_name in ['pull', 'push'] and 'timeout' not in kwargs: + kwargs['timeout'] = DEFAULT_ADB_PULL_TIMEOUT arg_str = ' '.join(str(elem) for elem in args) return self._exec_adb_cmd(clean_name, arg_str, **kwargs) diff --git a/acts/framework/acts/controllers/anritsu_lib/OWNERS b/acts/framework/acts/controllers/anritsu_lib/OWNERS new file mode 100644 index 0000000000..e4010df218 --- /dev/null +++ b/acts/framework/acts/controllers/anritsu_lib/OWNERS @@ -0,0 +1,4 @@ +iguarna@google.com +chaoyangf@google.com +yixiang@google.com +codycaldwell@google.com
\ No newline at end of file diff --git a/acts/framework/acts/controllers/anritsu_lib/band_constants.py b/acts/framework/acts/controllers/anritsu_lib/band_constants.py new file mode 100644 index 0000000000..c7f5771e54 --- /dev/null +++ b/acts/framework/acts/controllers/anritsu_lib/band_constants.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# +# Copyright 2020 - The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# GSM BAND constants +GSM_BAND_GSM450 = "GSM450" +GSM_BAND_GSM480 = "GSM480" +GSM_BAND_GSM850 = "GSM850" +GSM_BAND_PGSM900 = "P-GSM900" +GSM_BAND_EGSM900 = "E-GSM900" +GSM_BAND_RGSM900 = "R-GSM900" +GSM_BAND_DCS1800 = "DCS1800" +GSM_BAND_PCS1900 = "PCS1900" + +LTE_BAND_2 = 2 +LTE_BAND_4 = 4 +LTE_BAND_12 = 12 +WCDMA_BAND_1 = 1 +WCDMA_BAND_2 = 2 diff --git a/acts/framework/acts/controllers/anritsu_lib/cell_configurations.py b/acts/framework/acts/controllers/anritsu_lib/cell_configurations.py index a8d79b5ebe..636b03f0a6 100644 --- a/acts/framework/acts/controllers/anritsu_lib/cell_configurations.py +++ b/acts/framework/acts/controllers/anritsu_lib/cell_configurations.py @@ -16,14 +16,14 @@ """ Sanity tests for voice tests in telephony """ +from acts.controllers.anritsu_lib.band_constants import GSM_BAND_PCS1900 +from acts.controllers.anritsu_lib.band_constants import GSM_BAND_GSM850 +from acts.controllers.anritsu_lib.band_constants import LTE_BAND_2 +from acts.controllers.anritsu_lib.band_constants import LTE_BAND_4 +from acts.controllers.anritsu_lib.band_constants import LTE_BAND_12 +from acts.controllers.anritsu_lib.band_constants import WCDMA_BAND_1 +from acts.controllers.anritsu_lib.band_constants import WCDMA_BAND_2 from acts.controllers.anritsu_lib.md8475a import BtsBandwidth -from acts.test_utils.tel.anritsu_utils import GSM_BAND_PCS1900 -from acts.test_utils.tel.anritsu_utils import GSM_BAND_GSM850 -from acts.test_utils.tel.anritsu_utils import LTE_BAND_2 -from acts.test_utils.tel.anritsu_utils import LTE_BAND_4 -from acts.test_utils.tel.anritsu_utils import LTE_BAND_12 -from acts.test_utils.tel.anritsu_utils import WCDMA_BAND_1 -from acts.test_utils.tel.anritsu_utils import WCDMA_BAND_2 # Different Cell configurations # TMO bands diff --git a/acts/framework/acts/controllers/anritsu_lib/md8475_cellular_simulator.py b/acts/framework/acts/controllers/anritsu_lib/md8475_cellular_simulator.py index 98a779a89f..eea1c8a291 100644 --- a/acts/framework/acts/controllers/anritsu_lib/md8475_cellular_simulator.py +++ b/acts/framework/acts/controllers/anritsu_lib/md8475_cellular_simulator.py @@ -18,7 +18,7 @@ import math import ntpath import time import acts.controllers.cellular_simulator as cc -from acts.test_utils.power.tel_simulations import LteSimulation +from acts.controllers.cellular_lib import LteSimulation from acts.controllers.anritsu_lib import md8475a from acts.controllers.anritsu_lib import _anritsu_utils as anritsu diff --git a/acts/framework/acts/controllers/ap_lib/hostapd.py b/acts/framework/acts/controllers/ap_lib/hostapd.py index f8c3734f57..92b5ca31a7 100644 --- a/acts/framework/acts/controllers/ap_lib/hostapd.py +++ b/acts/framework/acts/controllers/ap_lib/hostapd.py @@ -86,7 +86,8 @@ class Hostapd(object): hostapd_command = '%s -dd -t "%s"' % (self.PROGRAM_FILE, self._config_file) base_command = 'cd "%s"; %s' % (self._working_dir, hostapd_command) - job_str = '%s > "%s" 2>&1' % (base_command, self._log_file) + job_str = 'rfkill unblock all; %s > "%s" 2>&1' %\ + (base_command, self._log_file) self._runner.run_async(job_str) try: diff --git a/acts/framework/acts/controllers/buds_lib/apollo_lib.py b/acts/framework/acts/controllers/buds_lib/apollo_lib.py index 9b971ffa24..7b0f80f370 100644 --- a/acts/framework/acts/controllers/buds_lib/apollo_lib.py +++ b/acts/framework/acts/controllers/buds_lib/apollo_lib.py @@ -46,7 +46,7 @@ import subprocess import time import serial -from acts import tracelogger +from acts.controllers.buds_lib import tako_trace_logger from acts.controllers.buds_lib import logserial from acts.controllers.buds_lib.b29_lib import B29Device from acts.controllers.buds_lib.dev_utils import apollo_log_decoder @@ -55,7 +55,7 @@ from acts.controllers.buds_lib.dev_utils import apollo_sink_events from logging import Logger from retry import retry -logging = tracelogger.TakoTraceLogger(Logger('apollo')) +logging = tako_trace_logger.TakoTraceLogger(Logger('apollo')) BAUD_RATE = 115200 BYTE_SIZE = 8 diff --git a/acts/framework/acts/controllers/buds_lib/b29_lib.py b/acts/framework/acts/controllers/buds_lib/b29_lib.py index bb4ea7d76c..df6a16361c 100644 --- a/acts/framework/acts/controllers/buds_lib/b29_lib.py +++ b/acts/framework/acts/controllers/buds_lib/b29_lib.py @@ -28,12 +28,12 @@ fBvw0sXkgwCBkshU_l4SxWkKgAxVmk/edit for details about available operations. import os import re import time +from logging import Logger -from acts import tracelogger from acts import utils -from logging import Logger +from acts.controllers.buds_lib import tako_trace_logger -logging = tracelogger.TakoTraceLogger(Logger(__file__)) +logging = tako_trace_logger.TakoTraceLogger(Logger(__file__)) DEVICE_REGEX = ( r'_(?P<device_serial>[A-Z0-9]+)-(?P<interface>\w+)\s->\s' r'(\.\./){2}(?P<port>\w+)' diff --git a/acts/framework/acts/controllers/buds_lib/logserial.py b/acts/framework/acts/controllers/buds_lib/logserial.py index 58bf15431d..6b18e3cfc0 100644 --- a/acts/framework/acts/controllers/buds_lib/logserial.py +++ b/acts/framework/acts/controllers/buds_lib/logserial.py @@ -21,15 +21,15 @@ import subprocess import sys import time import uuid +from logging import Logger from threading import Thread import serial from serial.tools import list_ports -from acts import tracelogger -from logging import Logger +from acts.controllers.buds_lib import tako_trace_logger -logging = tracelogger.TakoTraceLogger(Logger(__file__)) +logging = tako_trace_logger.TakoTraceLogger(Logger(__file__)) RETRIES = 0 diff --git a/acts/framework/acts/controllers/buds_lib/tako_trace_logger.py b/acts/framework/acts/controllers/buds_lib/tako_trace_logger.py new file mode 100644 index 0000000000..ff3184056c --- /dev/null +++ b/acts/framework/acts/controllers/buds_lib/tako_trace_logger.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# +# Copyright 2019 - The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import logging + +from acts import tracelogger + + +class TakoTraceLogger(tracelogger.TraceLogger): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.d = self.debug + self.e = self.error + self.i = self.info + self.t = self.step + self.w = self.warning + + def _logger_level(self, level_name): + level = logging.getLevelName(level_name) + return lambda *args, **kwargs: self._logger.log(level, *args, **kwargs) + + def step(self, msg, *args, **kwargs): + """Delegate a step call to the underlying logger.""" + self._log_with(self._logger_level('STEP'), 1, msg, *args, **kwargs) + + def device(self, msg, *args, **kwargs): + """Delegate a device call to the underlying logger.""" + self._log_with(self._logger_level('DEVICE'), 1, msg, *args, **kwargs) + + def suite(self, msg, *args, **kwargs): + """Delegate a device call to the underlying logger.""" + self._log_with(self._logger_level('SUITE'), 1, msg, *args, **kwargs) + + def case(self, msg, *args, **kwargs): + """Delegate a case call to the underlying logger.""" + self._log_with(self._logger_level('CASE'), 1, msg, *args, **kwargs) + + def flush_log(self): + """This function exists for compatibility with Tako's logserial module. + + Note that flushing the log is handled automatically by python's logging + module. + """ + pass diff --git a/acts/framework/acts/controllers/buds_lib/test_actions/audio_utils.py b/acts/framework/acts/controllers/buds_lib/test_actions/audio_utils.py index 42f8c46309..e94fc52e4a 100644 --- a/acts/framework/acts/controllers/buds_lib/test_actions/audio_utils.py +++ b/acts/framework/acts/controllers/buds_lib/test_actions/audio_utils.py @@ -19,8 +19,8 @@ import datetime import time -from acts import tracelogger from acts import utils +from acts.controllers.buds_lib import tako_trace_logger class AudioUtilsError(Exception): @@ -36,7 +36,7 @@ class AudioUtils(object): """ def __init__(self): - self.logger = tracelogger.TakoTraceLogger() + self.logger = tako_trace_logger.TakoTraceLogger() def play_audio_into_device(self, audio_file_path, audio_player, dut): """Open mic on DUT, play audio into DUT, close mic on DUT. diff --git a/acts/framework/acts/controllers/buds_lib/test_actions/base_test_actions.py b/acts/framework/acts/controllers/buds_lib/test_actions/base_test_actions.py index 7b6cbc4c0d..8f4b37a1bb 100644 --- a/acts/framework/acts/controllers/buds_lib/test_actions/base_test_actions.py +++ b/acts/framework/acts/controllers/buds_lib/test_actions/base_test_actions.py @@ -22,7 +22,7 @@ import datetime import inspect import time -from acts import tracelogger +from acts.controllers.buds_lib import tako_trace_logger from acts.libs.utils.timer import TimeRecorder # All methods start with "_" are considered hidden. @@ -37,7 +37,7 @@ def timed_action(method): func_name = self._convert_default_action_name(method.__name__) if not func_name: func_name = method.__name__ - self.logger.step('%s...' % func_name) + self.log_step('%s...' % func_name) self.timer.start_timer(func_name, True) result = method(self, *args, **kw) # TODO: Method run time collected can be used for automatic KPI checks @@ -135,9 +135,11 @@ class BaseTestAction(object): def __init__(self, logger=None): if logger is None: - self.logger = tracelogger.TakoTraceLogger() + self.logger = tako_trace_logger.TakoTraceLogger() + self.log_step = self.logger.step else: self.logger = logger + self.log_step = self.logger.info self.timer = TimeRecorder() self._fill_default_action_map() @@ -172,15 +174,16 @@ class BaseTestAction(object): exceptions """ num_acts = len(self._action_map) - self.logger.i('I can do %d action%s:' % + + self.logger.info('I can do %d action%s:' % (num_acts, 's' if num_acts != 1 else '')) for act in self._action_map.keys(): - self.logger.i(' - %s' % act) + self.logger.info(' - %s' % act) return True @timed_action def sleep(self, seconds): - self.logger.i('%s seconds' % seconds) + self.logger.info('%s seconds' % seconds) time.sleep(seconds) diff --git a/acts/framework/acts/controllers/buds_lib/test_actions/bt_utils.py b/acts/framework/acts/controllers/buds_lib/test_actions/bt_utils.py index 258240228a..f0ac04140c 100644 --- a/acts/framework/acts/controllers/buds_lib/test_actions/bt_utils.py +++ b/acts/framework/acts/controllers/buds_lib/test_actions/bt_utils.py @@ -31,11 +31,12 @@ device that supports the following calls: """ import queue import time +from logging import Logger -from acts import tracelogger -from acts.utils import wait_until +from acts import asserts +from acts.controllers.buds_lib import tako_trace_logger from acts.utils import TimeoutError -from logging import Logger +from acts.utils import wait_until # Add connection profile for future devices in this dictionary WEARABLE_BT_PROTOCOLS = { @@ -69,7 +70,7 @@ class BTUtils(object): def __init__(self): self.default_timeout = 60 - self.logger = tracelogger.TakoTraceLogger(Logger(__file__)) + self.logger = tako_trace_logger.TakoTraceLogger(Logger(__file__)) def bt_pair_and_connect(self, pri_device, sec_device): """Pair and connect a pri_device to a sec_device. @@ -198,8 +199,9 @@ class BTUtils(object): return True, 0 self.logger.debug('Unpairing from %s' % target_address) start_time = end_time = time.time() - assert (True is pri_device.droid.bluetoothUnbond(target_address), - 'Failed to request device unpairing.') + asserts.assert_true( + pri_device.droid.bluetoothUnbond(target_address), + 'Failed to request device unpairing.') # Check that devices have unpaired successfully. self.logger.debug('Verifying devices are unpaired') @@ -290,4 +292,3 @@ class BTUtils(object): if expected[key] != actual[key]: return False return True - diff --git a/acts/framework/acts/controllers/cellular_lib/AndroidCellularDut.py b/acts/framework/acts/controllers/cellular_lib/AndroidCellularDut.py new file mode 100644 index 0000000000..aaef6f5a0a --- /dev/null +++ b/acts/framework/acts/controllers/cellular_lib/AndroidCellularDut.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +# +# Copyright 2020 - The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from acts.controllers.cellular_lib import BaseCellularDut +from acts.test_utils.tel import tel_test_utils as tel_utils +from acts.test_utils.tel import tel_defines + + +class AndroidCellularDut(BaseCellularDut.BaseCellularDut): + """ Android implementation of the cellular DUT class.""" + def __init__(self, ad, logger): + """ Keeps a handler to the android device. + + Args: + ad: Android device handler + logger: a handler to the logger object + """ + self.ad = ad + self.log = logger + + def toggle_airplane_mode(self, new_state=True): + """ Turns airplane mode on / off. + + Args: + new_state: True if airplane mode needs to be enabled. + """ + tel_utils.toggle_airplane_mode(self.log, self.ad, new_state) + + def toggle_data_roaming(self, new_state=True): + """ Enables or disables cellular data roaming. + + Args: + new_state: True if data roaming needs to be enabled. + """ + tel_utils.toggle_cell_data_roaming(self.ad, new_state) + + def get_rx_tx_power_levels(self): + """ Obtains Rx and Tx power levels measured from the DUT. + + Returns: + A tuple where the first element is an array with the RSRP value + in each Rx chain, and the second element is the Tx power in dBm. + Values for invalid or disabled Rx / Tx chains are set to None. + """ + return tel_utils.get_rx_tx_power_levels(self.log, self.ad) + + def set_apn(self, name, apn, type='default'): + """ Sets the Access Point Name. + + Args: + name: the APN name + apn: the APN + type: the APN type + """ + self.ad.droid.telephonySetAPN(name, apn, type) + + def set_preferred_network_type(self, type): + """ Sets the preferred RAT. + + Args: + type: an instance of class PreferredNetworkType + """ + if type == BaseCellularDut.PreferredNetworkType.LTE_ONLY: + formatted_type = tel_defines.NETWORK_MODE_LTE_ONLY + elif type == BaseCellularDut.PreferredNetworkType.WCDMA_ONLY: + formatted_type = tel_defines.NETWORK_MODE_WCDMA_ONLY + elif type == BaseCellularDut.PreferredNetworkType.GSM_ONLY: + formatted_type = tel_defines.NETWORK_MODE_GSM_ONLY + else: + raise ValueError('Invalid RAT type.') + + if not self.ad.droid.telephonySetPreferredNetworkTypesForSubscription( + formatted_type, self.ad.droid.subscriptionGetDefaultSubId()): + self.log.error("Could not set preferred network type.") + else: + self.log.info("Preferred network type set.") + + def get_telephony_signal_strength(self): + """ Wrapper for the method with the same name in tel_utils. + + Will be deprecated and replaced by get_rx_tx_power_levels. """ + tel_utils.get_telephony_signal_strength(self.ad) diff --git a/acts/framework/acts/controllers/cellular_lib/BaseCellularDut.py b/acts/framework/acts/controllers/cellular_lib/BaseCellularDut.py new file mode 100644 index 0000000000..31c19fa9c5 --- /dev/null +++ b/acts/framework/acts/controllers/cellular_lib/BaseCellularDut.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +# +# Copyright 2020 - The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from enum import Enum + + +class PreferredNetworkType(Enum): + """ Available preferred network types that can be passed to + set_preferred_network_type""" + LTE_ONLY = 'lte-only' + GSM_ONLY = 'gsm-only' + WCDMA_ONLY = 'wcdma-only' + + +class BaseCellularDut(): + """ Base class for DUTs used with cellular simulators. """ + def toggle_airplane_mode(self, new_state=True): + """ Turns airplane mode on / off. + + Args: + new_state: True if airplane mode needs to be enabled. + """ + raise NotImplementedError() + + def toggle_data_roaming(self, new_state=True): + """ Enables or disables cellular data roaming. + + Args: + new_state: True if data roaming needs to be enabled. + """ + raise NotImplementedError() + + def get_rx_tx_power_levels(self): + """ Obtains Rx and Tx power levels measured from the DUT. + + Returns: + A tuple where the first element is an array with the RSRP value + in each Rx chain, and the second element is the Tx power in dBm. + Values for invalid or disabled Rx / Tx chains are set to None. + """ + raise NotImplementedError() + + def set_apn(self, name, apn, type='default'): + """ Sets the Access Point Name. + + Args: + name: the APN name + apn: the APN + type: the APN type + """ + raise NotImplementedError() + + def set_preferred_network_type(self, type): + """ Sets the preferred RAT. + + Args: + type: an instance of class PreferredNetworkType + """ + raise NotImplementedError() + + def get_telephony_signal_strength(self): + """ Wrapper for the method with the same name in tel_utils. + + Will be deprecated and replaced by get_rx_tx_power_levels. """ + raise NotImplementedError() diff --git a/acts/framework/acts/test_utils/power/tel_simulations/BaseSimulation.py b/acts/framework/acts/controllers/cellular_lib/BaseSimulation.py index 7d38caeb95..9cc091f2a0 100644 --- a/acts/framework/acts/test_utils/power/tel_simulations/BaseSimulation.py +++ b/acts/framework/acts/controllers/cellular_lib/BaseSimulation.py @@ -19,10 +19,6 @@ from enum import Enum import numpy as np from acts.controllers import cellular_simulator -from acts.test_utils.tel.tel_test_utils import get_telephony_signal_strength -from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode -from acts.test_utils.tel.tel_test_utils import toggle_cell_data_roaming -from acts.test_utils.tel.tel_test_utils import get_rx_tx_power_levels class BaseSimulation(): @@ -106,7 +102,7 @@ class BaseSimulation(): Args: simulator: a cellular simulator controller log: a logger handle - dut: the android device handler + dut: a device handler implementing BaseCellularDut test_config: test configuration obtained from the config file calibration_table: a dictionary containing path losses for different bands. @@ -168,13 +164,13 @@ class BaseSimulation(): # Set to default APN log.info("Configuring APN.") - dut.droid.telephonySetAPN("test", "test", "default") + self.dut.set_apn('test', 'test') # Enable roaming on the phone - toggle_cell_data_roaming(self.dut, True) + self.dut.toggle_data_roaming(True) # Make sure airplane mode is on so the phone won't attach right away - toggle_airplane_mode(self.log, self.dut, True) + self.dut.toggle_airplane_mode(True) # Wait for airplane mode setting to propagate time.sleep(2) @@ -197,7 +193,7 @@ class BaseSimulation(): """ # Turn on airplane mode - toggle_airplane_mode(self.log, self.dut, True) + self.dut.toggle_airplane_mode(True) # Wait for airplane mode setting to propagate time.sleep(2) @@ -215,7 +211,7 @@ class BaseSimulation(): try: # Turn off airplane mode - toggle_airplane_mode(self.log, self.dut, False) + self.dut.toggle_airplane_mode(False) # Wait for the phone to attach. self.simulator.wait_until_attached(timeout=self.attach_timeout) @@ -227,7 +223,7 @@ class BaseSimulation(): "UE failed to attach on attempt number {}.".format(i + 1)) # Turn airplane mode on to prepare the phone for a retry. - toggle_airplane_mode(self.log, self.dut, True) + self.dut.toggle_airplane_mode(True) # Wait for APM to propagate time.sleep(3) @@ -256,7 +252,7 @@ class BaseSimulation(): # Set the DUT to airplane mode so it doesn't see the # cellular network going off - toggle_airplane_mode(self.log, self.dut, True) + self.dut.toggle_airplane_mode(True) # Wait for APM to propagate time.sleep(2) @@ -271,7 +267,7 @@ class BaseSimulation(): # Set the DUT to airplane mode so it doesn't see the # cellular network going off - toggle_airplane_mode(self.log, self.dut, True) + self.dut.toggle_airplane_mode(True) # Wait for APM to propagate time.sleep(2) @@ -312,7 +308,7 @@ class BaseSimulation(): # Verify signal level try: - rx_power, tx_power = get_rx_tx_power_levels(self.log, self.dut) + rx_power, tx_power = self.dut.get_rx_tx_power_levels() if not tx_power or not rx_power[0]: raise RuntimeError('The method return invalid Tx/Rx values.') @@ -617,35 +613,24 @@ class BaseSimulation(): restoration_config.output_power = self.primary_config.output_power # Set BTS to a good output level to minimize measurement error - initial_screen_timeout = self.dut.droid.getScreenTimeout() new_config = self.BtsConfig() new_config.output_power = self.simulator.MAX_DL_POWER - 5 self.simulator.configure_bts(new_config) - # Set phone sleep time out - self.dut.droid.setScreenTimeout(1800) - self.dut.droid.goToSleepNow() - time.sleep(2) - # Starting IP traffic self.start_traffic_for_calibration() down_power_measured = [] for i in range(0, self.NUM_DL_CAL_READS): # For some reason, the RSRP gets updated on Screen ON event - self.dut.droid.wakeUpNow() - time.sleep(4) - signal_strength = get_telephony_signal_strength(self.dut) + signal_strength = self.dut.get_telephony_signal_strength() down_power_measured.append(signal_strength[rat]) - self.dut.droid.goToSleepNow() time.sleep(5) # Stop IP traffic self.stop_traffic_for_calibration() - # Reset phone and bts to original settings - self.dut.droid.goToSleepNow() - self.dut.droid.setScreenTimeout(initial_screen_timeout) + # Reset bts to original settings self.simulator.configure_bts(restoration_config) time.sleep(2) @@ -691,16 +676,10 @@ class BaseSimulation(): # Set BTS1 to maximum input allowed in order to perform # uplink calibration target_power = self.MAX_PHONE_OUTPUT_POWER - initial_screen_timeout = self.dut.droid.getScreenTimeout() new_config = self.BtsConfig() new_config.input_power = self.MAX_BTS_INPUT_POWER self.simulator.configure_bts(new_config) - # Set phone sleep time out - self.dut.droid.setScreenTimeout(1800) - self.dut.droid.wakeUpNow() - time.sleep(2) - # Start IP traffic self.start_traffic_for_calibration() @@ -729,9 +708,7 @@ class BaseSimulation(): # Stop IP traffic self.stop_traffic_for_calibration() - # Reset phone and bts to original settings - self.dut.droid.goToSleepNow() - self.dut.droid.setScreenTimeout(initial_screen_timeout) + # Reset bts to original settings self.simulator.configure_bts(restoration_config) time.sleep(2) diff --git a/acts/framework/acts/test_utils/power/tel_simulations/GsmSimulation.py b/acts/framework/acts/controllers/cellular_lib/GsmSimulation.py index 6dc2082cd6..b7237f354c 100644 --- a/acts/framework/acts/test_utils/power/tel_simulations/GsmSimulation.py +++ b/acts/framework/acts/controllers/cellular_lib/GsmSimulation.py @@ -17,15 +17,15 @@ import ntpath import time +from acts.controllers.anritsu_lib.band_constants import GSM_BAND_DCS1800 +from acts.controllers.anritsu_lib.band_constants import GSM_BAND_EGSM900 +from acts.controllers.anritsu_lib.band_constants import GSM_BAND_GSM850 +from acts.controllers.anritsu_lib.band_constants import GSM_BAND_RGSM900 from acts.controllers.anritsu_lib.md8475a import BtsGprsMode from acts.controllers.anritsu_lib.md8475a import BtsNumber from acts.controllers.anritsu_lib import md8475_cellular_simulator as anritsusim -from acts.test_utils.power.tel_simulations.BaseSimulation import BaseSimulation -from acts.test_utils.tel.anritsu_utils import GSM_BAND_DCS1800 -from acts.test_utils.tel.anritsu_utils import GSM_BAND_EGSM900 -from acts.test_utils.tel.anritsu_utils import GSM_BAND_GSM850 -from acts.test_utils.tel.anritsu_utils import GSM_BAND_RGSM900 -from acts.test_utils.tel.tel_defines import NETWORK_MODE_GSM_ONLY +from acts.controllers.cellular_lib import BaseCellularDut +from acts.controllers.cellular_lib.BaseSimulation import BaseSimulation class GsmSimulation(BaseSimulation): @@ -63,7 +63,7 @@ class GsmSimulation(BaseSimulation): Args: simulator: a cellular simulator controller log: a logger handle - dut: the android device handler + dut: a device handler implementing BaseCellularDut test_config: test configuration obtained from the config file calibration_table: a dictionary containing path losses for different bands. @@ -82,12 +82,8 @@ class GsmSimulation(BaseSimulation): super().__init__(simulator, log, dut, test_config, calibration_table) - if not dut.droid.telephonySetPreferredNetworkTypesForSubscription( - NETWORK_MODE_GSM_ONLY, - dut.droid.subscriptionGetDefaultSubId()): - log.error("Coold not set preferred network type.") - else: - log.info("Preferred network type set.") + self.dut.set_preferred_network_type( + BaseCellularDut.PreferredNetworkType.GSM_ONLY) def setup_simulator(self): """ Do initial configuration in the simulator. """ diff --git a/acts/framework/acts/test_utils/power/tel_simulations/LteCaSimulation.py b/acts/framework/acts/controllers/cellular_lib/LteCaSimulation.py index addc3a8aab..3f98a34e60 100644 --- a/acts/framework/acts/test_utils/power/tel_simulations/LteCaSimulation.py +++ b/acts/framework/acts/controllers/cellular_lib/LteCaSimulation.py @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import re -from acts.test_utils.power.tel_simulations import LteSimulation +from acts.controllers.cellular_lib import LteSimulation class LteCaSimulation(LteSimulation.LteSimulation): @@ -80,7 +80,7 @@ class LteCaSimulation(LteSimulation.LteSimulation): Args: simulator: the cellular instrument controller log: a logger handle - dut: the android device handler + dut: a device handler implementing BaseCellularDut test_config: test configuration obtained from the config file calibration_table: a dictionary containing path losses for different bands. diff --git a/acts/framework/acts/test_utils/power/tel_simulations/LteImsSimulation.py b/acts/framework/acts/controllers/cellular_lib/LteImsSimulation.py index 71102463cf..e13eb297ba 100644 --- a/acts/framework/acts/test_utils/power/tel_simulations/LteImsSimulation.py +++ b/acts/framework/acts/controllers/cellular_lib/LteImsSimulation.py @@ -13,7 +13,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from acts.test_utils.power.tel_simulations.LteSimulation import LteSimulation +from acts.controllers.cellular_lib.LteSimulation import LteSimulation import acts.test_utils.tel.anritsu_utils as anritsu_utils import acts.controllers.anritsu_lib.md8475a as md8475a diff --git a/acts/framework/acts/test_utils/power/tel_simulations/LteSimulation.py b/acts/framework/acts/controllers/cellular_lib/LteSimulation.py index 44bcbf67dd..9627e9fa26 100644 --- a/acts/framework/acts/test_utils/power/tel_simulations/LteSimulation.py +++ b/acts/framework/acts/controllers/cellular_lib/LteSimulation.py @@ -17,8 +17,8 @@ import math from enum import Enum -from acts.test_utils.power.tel_simulations.BaseSimulation import BaseSimulation -from acts.test_utils.tel.tel_defines import NETWORK_MODE_LTE_ONLY +from acts.controllers.cellular_lib.BaseSimulation import BaseSimulation +from acts.controllers.cellular_lib import BaseCellularDut class TransmissionMode(Enum): @@ -459,7 +459,7 @@ class LteSimulation(BaseSimulation): Args: simulator: a cellular simulator controller log: a logger handle - dut: the android device handler + dut: a device handler implementing BaseCellularDut test_config: test configuration obtained from the config file calibration_table: a dictionary containing path losses for different bands. @@ -468,12 +468,8 @@ class LteSimulation(BaseSimulation): super().__init__(simulator, log, dut, test_config, calibration_table) - if not dut.droid.telephonySetPreferredNetworkTypesForSubscription( - NETWORK_MODE_LTE_ONLY, - dut.droid.subscriptionGetDefaultSubId()): - log.error("Couldn't set preferred network type.") - else: - log.info("Preferred network type set.") + self.dut.set_preferred_network_type( + BaseCellularDut.PreferredNetworkType.LTE_ONLY) # Get TBS pattern setting from the test configuration if self.KEY_TBS_PATTERN not in test_config: diff --git a/acts/framework/acts/controllers/cellular_lib/OWNERS b/acts/framework/acts/controllers/cellular_lib/OWNERS new file mode 100644 index 0000000000..e4010df218 --- /dev/null +++ b/acts/framework/acts/controllers/cellular_lib/OWNERS @@ -0,0 +1,4 @@ +iguarna@google.com +chaoyangf@google.com +yixiang@google.com +codycaldwell@google.com
\ No newline at end of file diff --git a/acts/framework/acts/test_utils/power/tel_simulations/UmtsSimulation.py b/acts/framework/acts/controllers/cellular_lib/UmtsSimulation.py index 4d4aeebf8b..b301a6b6f5 100644 --- a/acts/framework/acts/test_utils/power/tel_simulations/UmtsSimulation.py +++ b/acts/framework/acts/controllers/cellular_lib/UmtsSimulation.py @@ -20,8 +20,8 @@ import time from acts.controllers.anritsu_lib import md8475_cellular_simulator as anritsusim from acts.controllers.anritsu_lib.md8475a import BtsNumber from acts.controllers.anritsu_lib.md8475a import BtsPacketRate -from acts.test_utils.power.tel_simulations.BaseSimulation import BaseSimulation -from acts.test_utils.tel.tel_defines import NETWORK_MODE_WCDMA_ONLY +from acts.controllers.cellular_lib.BaseSimulation import BaseSimulation +from acts.controllers.cellular_lib import BaseCellularDut class UmtsSimulation(BaseSimulation): @@ -98,7 +98,7 @@ class UmtsSimulation(BaseSimulation): Args: simulator: a cellular simulator controller log: a logger handle - dut: the android device handler + dut: a device handler implementing BaseCellularDut test_config: test configuration obtained from the config file calibration_table: a dictionary containing path losses for different bands. @@ -117,12 +117,8 @@ class UmtsSimulation(BaseSimulation): super().__init__(simulator, log, dut, test_config, calibration_table) - if not dut.droid.telephonySetPreferredNetworkTypesForSubscription( - NETWORK_MODE_WCDMA_ONLY, - dut.droid.subscriptionGetDefaultSubId()): - log.error("Coold not set preferred network type.") - else: - log.info("Preferred network type set.") + self.dut.set_preferred_network_type( + BaseCellularDut.PreferredNetworkType.WCDMA_ONLY) self.release_version = None self.packet_rate = None diff --git a/acts/framework/acts/test_utils/power/tel_simulations/__init__.py b/acts/framework/acts/controllers/cellular_lib/__init__.py index e69de29bb2..e69de29bb2 100644 --- a/acts/framework/acts/test_utils/power/tel_simulations/__init__.py +++ b/acts/framework/acts/controllers/cellular_lib/__init__.py diff --git a/acts/framework/acts/controllers/cellular_simulator.py b/acts/framework/acts/controllers/cellular_simulator.py index 2408611996..99adbd87be 100644 --- a/acts/framework/acts/controllers/cellular_simulator.py +++ b/acts/framework/acts/controllers/cellular_simulator.py @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. from acts import logger -from acts.test_utils.power import tel_simulations as sims +from acts.controllers import cellular_lib as sims class AbstractCellularSimulator: diff --git a/acts/framework/acts/controllers/fuchsia_device.py b/acts/framework/acts/controllers/fuchsia_device.py index 322b653a32..12ce3f5289 100644 --- a/acts/framework/acts/controllers/fuchsia_device.py +++ b/acts/framework/acts/controllers/fuchsia_device.py @@ -51,6 +51,7 @@ from acts.controllers.fuchsia_lib.kernel_lib import FuchsiaKernelLib from acts.controllers.fuchsia_lib.location.regulatory_region_lib import FuchsiaRegulatoryRegionLib from acts.controllers.fuchsia_lib.logging_lib import FuchsiaLoggingLib from acts.controllers.fuchsia_lib.netstack.netstack_lib import FuchsiaNetstackLib +from acts.controllers.fuchsia_lib.ram_lib import FuchsiaRamLib from acts.controllers.fuchsia_lib.syslog_lib import FuchsiaSyslogError from acts.controllers.fuchsia_lib.syslog_lib import start_syslog from acts.controllers.fuchsia_lib.sysinfo_lib import FuchsiaSysInfoLib @@ -60,7 +61,7 @@ from acts.controllers.fuchsia_lib.wlan_deprecated_configuration_lib import Fuchs from acts.controllers.fuchsia_lib.wlan_lib import FuchsiaWlanLib from acts.controllers.fuchsia_lib.wlan_ap_policy_lib import FuchsiaWlanApPolicyLib from acts.controllers.fuchsia_lib.wlan_policy_lib import FuchsiaWlanPolicyLib -from acts.libs.proc.job import Error +from acts.libs.proc import job MOBLY_CONTROLLER_CONFIG_NAME = "FuchsiaDevice" ACTS_CONTROLLER_REFERENCE_NAME = "fuchsia_devices" @@ -280,6 +281,10 @@ class FuchsiaDevice: self.netstack_lib = FuchsiaNetstackLib(self.address, self.test_counter, self.client_id) + # Grab commands from FuchsiaLightLib + self.ram_lib = FuchsiaRamLib(self.address, self.test_counter, + self.client_id) + # Grab commands from FuchsiaProfileServerLib self.sdp_lib = FuchsiaProfileServerLib(self.address, self.test_counter, self.client_id) @@ -343,52 +348,6 @@ class FuchsiaDevice: """ return self.client_id + "." + str(test_id) - def verify_ping(self, timeout=30): - """Verify the fuchsia device can be pinged. - - Args: - timeout: int, seconds to retry before raising an exception - - Raise: - ConnecitonError, if device still can't be pinged after timeout. - """ - end_time = time.time() + timeout - while time.time() < end_time: - if utils.is_pingable(self.ip): - break - else: - self.log.debug('Device is not pingable. Retrying in 1 second.') - time.sleep(1) - else: - raise ConnectionError('Device never came back online.') - - def verify_ssh(self, timeout=30): - """Verify the fuchsia device can be reached via ssh. - - In self.reboot, this function is used to verify SSH is up before - attempting to restart SL4F, as that has more risky thread implications - if it fails. Also, create_ssh_connection has a short backoff loop, - but was not intended for waiting for SSH to come up. - - Args: - timeout: int, seconds to retry before raising an exception - - Raise: - ConnecitonError, if device still can't reached after timeout. - """ - end_time = time.time() + timeout - while time.time() < end_time: - try: - self.send_command_ssh('\n') - except Exception: - self.log.debug( - 'Could not SSH to device. Retrying in 1 second.') - time.sleep(1) - else: - break - else: - raise ConnectionError('Failed to connect to device via SSH.') - def reboot(self, use_ssh=False, unreachable_timeout=30, @@ -455,7 +414,7 @@ class FuchsiaDevice: self.log.info('Verifying device is unreachable.') timeout = time.time() + unreachable_timeout while (time.time() < timeout): - if utils.is_pingable(self.ip): + if utils.can_ping(job, self.ip): self.log.debug('Device is still pingable. Retrying.') else: if reboot_type == FUCHSIA_REBOOT_TYPE_HARD: @@ -475,11 +434,30 @@ class FuchsiaDevice: device_pdu.on(str(device_pdu_port)) self.log.info('Waiting for device to respond to pings.') - self.verify_ping(timeout=ping_timeout) + end_time = time.time() + ping_timeout + while time.time() < end_time: + if utils.can_ping(job, self.ip): + break + else: + self.log.debug('Device is not pingable. Retrying in 1 second.') + time.sleep(1) + else: + raise ConnectionError('Device never came back online.') self.log.info('Device responded to pings.') self.log.info('Waiting for device to allow ssh connection.') - self.verify_ssh(timeout=ssh_timeout) + end_time = time.time() + ssh_timeout + while time.time() < end_time: + try: + self.send_command_ssh('\n') + except Exception: + self.log.debug( + 'Could not SSH to device. Retrying in 1 second.') + time.sleep(1) + else: + break + else: + raise ConnectionError('Failed to connect to device via SSH.') self.log.info('Device now available via ssh.') # Creating new log process, start it, start new persistent ssh session, @@ -545,7 +523,13 @@ class FuchsiaDevice: ssh_conn.close() return command_result - def ping(self, dest_ip, count=3, interval=1000, timeout=1000, size=25): + def ping(self, + dest_ip, + count=3, + interval=1000, + timeout=1000, + size=25, + additional_ping_params=None): """Pings from a Fuchsia device to an IPv4 address or hostname Args: @@ -555,6 +539,8 @@ class FuchsiaDevice: timeout: (int) How long to wait before having the icmp packet timeout (ms). size: (int) Size of the icmp packet. + additional_ping_params: (str) command option flags to + append to the command string Returns: A dictionary for the results of the ping. The dictionary contains @@ -570,10 +556,12 @@ class FuchsiaDevice: rtt_max = None rtt_avg = None self.log.debug("Pinging %s..." % dest_ip) + if not additional_ping_params: + additional_ping_params = '' ping_result = self.send_command_ssh( - 'ping -c %s -i %s -t %s -s %s %s' % - (count, interval, timeout, size, dest_ip)) - if isinstance(ping_result, Error): + 'ping -c %s -i %s -t %s -s %s %s %s' % + (count, interval, timeout, size, additional_ping_params, dest_ip)) + if isinstance(ping_result, job.Error): ping_result = ping_result.result if ping_result.stderr: @@ -595,6 +583,22 @@ class FuchsiaDevice: 'stderr': ping_result.stderr } + def can_ping(self, + dest_ip, + count=1, + interval=1000, + timeout=1000, + size=25, + additional_ping_params=None): + """Returns whether fuchsia device can ping a given dest address""" + ping_result = self.ping(dest_ip, + count=count, + interval=interval, + timeout=timeout, + size=size, + additional_ping_params=additional_ping_params) + return ping_result['status'] + def print_clients(self): """Gets connected clients from SL4F server""" self.log.debug("Request to print clients") @@ -860,10 +864,23 @@ class FuchsiaDevice: acts_logger.epoch_to_log_line_timestamp(begin_time)) out_name = "FuchsiaDevice%s_%s" % ( self.serial, time_stamp.replace(" ", "_").replace(":", "-")) + bugreport_out_name = f"{out_name}.zip" out_name = "%s.txt" % out_name full_out_path = os.path.join(br_path, out_name) + full_br_out_path = os.path.join(br_path, bugreport_out_name) self.log.info("Taking bugreport for %s on FuchsiaDevice%s." % (test_name, self.serial)) + if self.ssh_config is not None: + try: + subprocess.run([ + f"ssh -F {self.ssh_config} {self.ip} bugreport > {full_br_out_path}" + ], + shell=True) + self.log.info( + "Bugreport saved at: {}".format(full_br_out_path)) + except Exception as err: + self.log.error("Failed to take bugreport with: {}".format(err)) + system_objects = self.send_command_ssh('iquery --find /hub').stdout system_objects = system_objects.split() diff --git a/acts/framework/acts/controllers/fuchsia_lib/base_lib.py b/acts/framework/acts/controllers/fuchsia_lib/base_lib.py index 9576e6850d..23035fca8e 100644 --- a/acts/framework/acts/controllers/fuchsia_lib/base_lib.py +++ b/acts/framework/acts/controllers/fuchsia_lib/base_lib.py @@ -28,6 +28,7 @@ import time from urllib.parse import urlparse from acts import utils +from acts.libs.proc import job class DeviceOffline(Exception): @@ -61,7 +62,7 @@ class BaseLib(): Returns: Dictionary, Result of sl4f command executed. """ - if not utils.is_pingable(urlparse(self.address).hostname): + if not utils.can_ping(job, urlparse(self.address).hostname): raise DeviceOffline("FuchsiaDevice %s is not reachable via the " "network." % urlparse(self.address).hostname) test_data = json.dumps({ @@ -75,12 +76,12 @@ class BaseLib(): data=test_data, timeout=response_timeout).json() except requests.exceptions.Timeout as e: - if not utils.is_pingable(urlparse(self.address).hostname): + if not utils.can_ping(job, urlparse(self.address).hostname): raise DeviceOffline( "FuchsiaDevice %s is not reachable via the " "network." % urlparse(self.address).hostname) else: logging.debug( - 'FuchsiaDevice is online but SL4f call timed out.' % + 'FuchsiaDevice %s is online but SL4f call timed out.' % urlparse(self.address).hostname) raise e diff --git a/acts/framework/acts/controllers/fuchsia_lib/ram_lib.py b/acts/framework/acts/controllers/fuchsia_lib/ram_lib.py new file mode 100644 index 0000000000..ce8bb73afd --- /dev/null +++ b/acts/framework/acts/controllers/fuchsia_lib/ram_lib.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# +# Copyright 2020 - The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from acts.controllers.fuchsia_lib.base_lib import BaseLib + + +class FuchsiaRamLib(BaseLib): + def __init__(self, addr, tc, client_id): + self.address = addr + self.test_counter = tc + self.client_id = client_id + + def measureBandwidth(self, cycles_to_measure, channels): + """ Measures the DDR bandwidth on the specified channels. + + Args: + cycles_to_measure: How many bus cycles to perform the measurement over. + channels: An array of 8 uint64, specifying which ports to aggregate + for each channel. + + Returns: + BandwidthInfo struct, prints an error message if error. + """ + test_cmd = "ram_facade.MeasureBandwidth" + test_args = { + "values": { + "cycles_to_measure": cycles_to_measure, + "channels": channels + } + } + test_id = self.build_id(self.test_counter) + self.test_counter += 1 + + return self.send_command(test_id, test_cmd, test_args) + + def getDdrWindowingResults(self): + """ Retrieves the results from the DDR Windowing tool, which runs in + the bootloader. + + Returns: + The register value, prints an error message if error. + """ + test_cmd = "ram_facade.GetDdrWindowingResults" + test_args = {} + test_id = self.build_id(self.test_counter) + self.test_counter += 1 + + return self.send_command(test_id, test_cmd, test_args) diff --git a/acts/framework/acts/controllers/fuchsia_lib/syslog_lib.py b/acts/framework/acts/controllers/fuchsia_lib/syslog_lib.py index a8c102d0c6..2b2f024b07 100644 --- a/acts/framework/acts/controllers/fuchsia_lib/syslog_lib.py +++ b/acts/framework/acts/controllers/fuchsia_lib/syslog_lib.py @@ -102,8 +102,9 @@ class FuchsiaSyslogProcess(object): def start(self): """Starts reading the data from the syslog ssh connection.""" if self._started: - raise FuchsiaSyslogError('Syslog has already started for ' - 'FuchsiaDevice (%s).' % self.ip_address) + logging.info('Syslog has already started for FuchsiaDevice (%s).' % + self.ip_address) + return None self._started = True self._listening_thread = Thread(target=self._exec_loop) @@ -122,8 +123,9 @@ class FuchsiaSyslogProcess(object): threads. """ if self._stopped: - raise FuchsiaSyslogError('Syslog is already being stopped for ' - 'FuchsiaDevice (%s).' % self.ip_address) + logging.info('Syslog is already stopped for FuchsiaDevice (%s).' % + self.ip_address) + return None self._stopped = True try: diff --git a/acts/framework/acts/controllers/fuchsia_lib/utils_lib.py b/acts/framework/acts/controllers/fuchsia_lib/utils_lib.py index 782d96e631..96a71319be 100644 --- a/acts/framework/acts/controllers/fuchsia_lib/utils_lib.py +++ b/acts/framework/acts/controllers/fuchsia_lib/utils_lib.py @@ -23,6 +23,7 @@ import time from acts import utils from acts.controllers.fuchsia_lib.base_lib import DeviceOffline +from acts.libs.proc import job logging.getLogger("paramiko").setLevel(logging.WARNING) # paramiko-ng will throw INFO messages when things get disconnect or cannot @@ -87,7 +88,7 @@ def create_ssh_connection(ip_address, Returns: A paramiko ssh object """ - if not utils.is_pingable(ip_address): + if not utils.can_ping(job, ip_address): raise DeviceOffline("Device %s is not reachable via " "the network." % ip_address) ssh_key = get_private_key(ip_address=ip_address, ssh_config=ssh_config) diff --git a/acts/framework/acts/controllers/fuchsia_lib/wlan_policy_lib.py b/acts/framework/acts/controllers/fuchsia_lib/wlan_policy_lib.py index 61122208b3..5ca778b703 100644 --- a/acts/framework/acts/controllers/fuchsia_lib/wlan_policy_lib.py +++ b/acts/framework/acts/controllers/fuchsia_lib/wlan_policy_lib.py @@ -25,6 +25,7 @@ COMMAND_STOP_CLIENT_CONNECTIONS = "wlan_policy.stop_client_connections" COMMAND_SCAN_FOR_NETWORKS = "wlan_policy.scan_for_networks" COMMAND_SAVE_NETWORK = "wlan_policy.save_network" COMMAND_REMOVE_NETWORK = "wlan_policy.remove_network" +COMMAND_REMOVE_ALL_NETWORKS = "wlan_policy.remove_all_networks" COMMAND_GET_SAVED_NETWORKS = "wlan_policy.get_saved_networks" COMMAND_CONNECT = "wlan_policy.connect" COMMAND_CREATE_CLIENT_CONTROLLER = "wlan_policy.create_client_controller" @@ -76,7 +77,7 @@ class FuchsiaWlanPolicyLib(BaseLib): return self.send_command(test_id, test_cmd, {}) - def wlanSaveNetwork(self, target_ssid, security_type, target_pwd=""): + def wlanSaveNetwork(self, target_ssid, security_type, target_pwd=None): """ Saveds a network to the device for future connections Args: target_ssid: the network to attempt a connection to @@ -87,7 +88,8 @@ class FuchsiaWlanPolicyLib(BaseLib): Returns: boolean indicating if the connection was successful """ - + if not target_pwd: + target_pwd = '' test_cmd = COMMAND_SAVE_NETWORK test_id = self.build_id(self.test_counter) self.test_counter += 1 @@ -99,7 +101,7 @@ class FuchsiaWlanPolicyLib(BaseLib): return self.send_command(test_id, test_cmd, test_args) - def wlanRemoveNetwork(self, target_ssid, security_type, target_pwd=""): + def wlanRemoveNetwork(self, target_ssid, security_type, target_pwd=None): """ Removes or "forgets" a network from saved networks Args: target_ssid: the network to attempt a connection to @@ -107,7 +109,8 @@ class FuchsiaWlanPolicyLib(BaseLib): target_pwd: (optional) credential of the network to remove. No password and empty string are equivalent. """ - + if not target_pwd: + target_pwd = '' test_cmd = COMMAND_REMOVE_NETWORK test_id = self.build_id(self.test_counter) self.test_counter += 1 @@ -119,6 +122,18 @@ class FuchsiaWlanPolicyLib(BaseLib): return self.send_command(test_id, test_cmd, test_args) + def wlanRemoveAllNetworks(self): + """ Removes or "forgets" all networks from saved networks + Returns: + A boolean indicating if the action was successful + """ + + test_cmd = COMMAND_REMOVE_ALL_NETWORKS + test_id = self.build_id(self.test_counter) + self.test_counter += 1 + + return self.send_command(test_id, test_cmd, {}) + def wlanGetSavedNetworks(self): """ Gets networks saved on device Returns: diff --git a/acts/framework/acts/controllers/iperf_server.py b/acts/framework/acts/controllers/iperf_server.py index 2b7d970442..78cb787458 100755 --- a/acts/framework/acts/controllers/iperf_server.py +++ b/acts/framework/acts/controllers/iperf_server.py @@ -243,8 +243,8 @@ class IPerfResult(object): """ if not self._has_data(): return None - instantaneous_rates = self.instantaneous_rates[ - iperf_ignored_interval:-1] + instantaneous_rates = self.instantaneous_rates[iperf_ignored_interval: + -1] avg_rate = math.fsum(instantaneous_rates) / len(instantaneous_rates) sqd_deviations = ([(rate - avg_rate)**2 for rate in instantaneous_rates]) @@ -295,25 +295,6 @@ class IPerfServerBase(object): """ raise NotImplementedError('stop() must be specified.') - def get_interface_ip_addresses(self, interface): - """Gets all of the ip addresses, ipv4 and ipv6, associated with a - particular interface name. - - Args: - interface: The interface name on the device, ie eth0 - - Returns: - A list of dictionaries of the the various IP addresses: - ipv4_private_local_addresses: Any 192.168, 172.16, or 10 - addresses - ipv4_public_addresses: Any IPv4 public addresses - ipv6_link_local_addresses: Any fe80:: addresses - ipv6_private_local_addresses: Any fd00:: addresses - ipv6_public_addresses: Any publicly routable addresses - """ - raise NotImplementedError('get_interface_ip_addresses' - ' must be specified.') - def _get_full_file_path(self, tag=None): """Returns the full file path for the IPerfServer log file. @@ -432,24 +413,6 @@ class IPerfServer(IPerfServerBase): return self._current_log_file - def get_interface_ip_addresses(self, interface): - """Gets all of the ip addresses, ipv4 and ipv6, associated with a - particular interface name. - - Args: - interface: The interface name on the device, ie eth0 - - Returns: - A list of dictionaries of the the various IP addresses: - ipv4_private_local_addresses: Any 192.168, 172.16, or 10 - addresses - ipv4_public_addresses: Any IPv4 public addresses - ipv6_link_local_addresses: Any fe80:: addresses - ipv6_private_local_addresses: Any fd00:: addresses - ipv6_public_addresses: Any publicly routable addresses - """ - return utils.get_interface_ip_addresses(job, interface) - def __del__(self): self.stop() @@ -523,6 +486,7 @@ class IPerfServerOverSsh(IPerfServerBase): """ if not self._ssh_session: self.start_ssh() + return utils.get_interface_ip_addresses(self._ssh_session, interface) def renew_test_interface_ip_address(self): @@ -748,22 +712,3 @@ class IPerfServerOverAdb(IPerfServerBase): self._iperf_process = None return log_file - - def get_interface_ip_addresses(self, interface): - """Gets all of the ip addresses, ipv4 and ipv6, associated with a - particular interface name. - - Args: - interface: The interface name on the device, ie eth0 - - Returns: - A list of dictionaries of the the various IP addresses: - ipv4_private_local_addresses: Any 192.168, 172.16, or 10 - addresses - ipv4_public_addresses: Any IPv4 public addresses - ipv6_link_local_addresses: Any fe80:: addresses - ipv6_private_local_addresses: Any fd00:: addresses - ipv6_public_addresses: Any publicly routable addresses - """ - return utils.get_interface_ip_addresses(self._android_device_or_serial, - interface) diff --git a/acts/framework/acts/controllers/monsoon_lib/api/common.py b/acts/framework/acts/controllers/monsoon_lib/api/common.py index f932535467..277598e0a1 100644 --- a/acts/framework/acts/controllers/monsoon_lib/api/common.py +++ b/acts/framework/acts/controllers/monsoon_lib/api/common.py @@ -42,33 +42,33 @@ PASSTHROUGH_STATES = { class MonsoonDataRecord(object): """A data class for Monsoon data points.""" - def __init__(self, time, current): + def __init__(self, sample_time, relative_time, current): """Creates a new MonsoonDataRecord. Args: - time: the string '{time}s', where time is measured in seconds since - the beginning of the data collection. + sample_time: the unix timestamp of the sample. + relative_time: the time since the start of the measurement. current: The current in Amperes as a string. """ - self._time = float(time[:-1]) - self._current = float(current) + self._sample_time = sample_time + self._relative_time = relative_time + self._current = current @property def time(self): """The time the record was fetched.""" - return self._time + return self._sample_time + + @property + def relative_time(self): + """The time the record was fetched, relative to collection start.""" + return self._relative_time @property def current(self): """The amount of current in Amperes measured for the given record.""" return self._current - @classmethod - def create_from_record_line(cls, line): - """Creates a data record from the line passed in from the output file. - """ - return cls(*line.split(' ')) - class MonsoonResult(object): """An object that contains aggregated data collected during sampling. @@ -107,10 +107,16 @@ class MonsoonResult(object): def __iter__(self): with open(self.file, 'r') as f: + start_time = None for line in f: # Remove the newline character. line.strip() - yield MonsoonDataRecord.create_from_record_line(line) + sample_time, current = map(float, line.split(' ')) + if start_time is None: + start_time = sample_time + yield MonsoonDataRecord(sample_time, + sample_time - start_time, + current) return MonsoonDataIterator(self.tag) diff --git a/acts/framework/acts/controllers/monsoon_lib/api/lvpm_stock/monsoon_proxy.py b/acts/framework/acts/controllers/monsoon_lib/api/lvpm_stock/monsoon_proxy.py index 615d69aa82..7d94d36972 100644 --- a/acts/framework/acts/controllers/monsoon_lib/api/lvpm_stock/monsoon_proxy.py +++ b/acts/framework/acts/controllers/monsoon_lib/api/lvpm_stock/monsoon_proxy.py @@ -214,7 +214,8 @@ class MonsoonProxy(object): self._flush_input() # discard stale input status = self.get_status() except Exception as e: - logging.exception('Error opening device %s: %s', dev, e) + logging.warning('Error opening device %s: %s', dev, e, + exc_info=True) continue if not status: diff --git a/acts/framework/acts/controllers/monsoon_lib/sampling/engine/transformers.py b/acts/framework/acts/controllers/monsoon_lib/sampling/engine/transformers.py index dc5e6f0455..debe82cbaf 100644 --- a/acts/framework/acts/controllers/monsoon_lib/sampling/engine/transformers.py +++ b/acts/framework/acts/controllers/monsoon_lib/sampling/engine/transformers.py @@ -41,6 +41,8 @@ class Tee(SequentialTransformer): self._filename = filename self._fd = None self.measure_after_seconds = measure_after_seconds + # The time of the first sample gathered. + self._start_time = None def on_begin(self): self._fd = open(self._filename, 'w+') @@ -55,11 +57,74 @@ class Tee(SequentialTransformer): buffer: A list of HvpmReadings. """ for sample in buffer: - if sample.sample_time < self.measure_after_seconds: + if self._start_time is None: + self._start_time = sample.sample_time + if (sample.sample_time - self._start_time < + self.measure_after_seconds): continue - self._fd.write('%.9fs %.12f\n' % - (sample.sample_time - self.measure_after_seconds, - sample.main_current)) + self._fd.write('%0.9f %.12f\n' % + (sample.sample_time, sample.main_current)) + self._fd.flush() + return BufferList([buffer]) + + +class PerfgateTee(SequentialTransformer): + """Outputs records of nanoseconds,current,voltage to the specified file. + + Similar to Tee, but this version includes voltage, which may help with + accuracy in the power calculations. + + This output type can be enabled by passing this transformer to the + transformers kwarg in Monsoon.measure_power(): + + # Uses the default Tee + > monsoon.measure_power(..., output_path=filename]) + + # Uses PerfgateTee + > monsoon.measure_power(..., transformers=[PerfgateTee(filename)]) + + Attributes: + _filename: the name of the file to open. + _fd: the filestream written to. + """ + + def __init__(self, filename, measure_after_seconds=0): + """Creates an OutputStream. + + Args: + filename: the path to the file to write the collected data to. + measure_after_seconds: the number of seconds to skip before logging + data as part of the measurement. + """ + super().__init__() + self._filename = filename + self._fd = None + self.measure_after_seconds = measure_after_seconds + # The time of the first sample gathered. + self._start_time = None + + def on_begin(self): + self._fd = open(self._filename, 'w+') + + def on_end(self): + self._fd.close() + + def _transform_buffer(self, buffer): + """Writes the reading values to a file. + + Args: + buffer: A list of HvpmReadings. + """ + for sample in buffer: + if self._start_time is None: + self._start_time = sample.sample_time + if (sample.sample_time - self._start_time < + self.measure_after_seconds): + continue + self._fd.write( + '%i,%.6f,%.6f\n' % + (sample.sample_time * 1e9, sample.main_current, + sample.main_voltage)) self._fd.flush() return BufferList([buffer]) @@ -79,6 +144,8 @@ class SampleAggregator(ParallelTransformer): self._num_samples = 0 self._sum_currents = 0 self.start_after_seconds = start_after_seconds + # The time of the first sample gathered. + self._start_time = None def _transform_buffer(self, buffer): """Aggregates the sample data. @@ -87,7 +154,9 @@ class SampleAggregator(ParallelTransformer): buffer: A buffer of H/LvpmReadings. """ for sample in buffer: - if sample.sample_time < self.start_after_seconds: + if self._start_time is None: + self._start_time = sample.sample_time + if sample.sample_time - self._start_time < self.start_after_seconds: continue self._num_samples += 1 self._sum_currents += sample.main_current diff --git a/acts/framework/acts/controllers/monsoon_lib/sampling/hvpm/packet.py b/acts/framework/acts/controllers/monsoon_lib/sampling/hvpm/packet.py index 223337054d..3319c3447e 100644 --- a/acts/framework/acts/controllers/monsoon_lib/sampling/hvpm/packet.py +++ b/acts/framework/acts/controllers/monsoon_lib/sampling/hvpm/packet.py @@ -150,11 +150,9 @@ class Packet(object): Attributes: _packet_data: The raw data received from the packet. - time_since_start: The timestamp (relative to start) this packet was - collected. + time_of_read: The unix timestamp this packet was collected at. time_since_last_sample: The differential between this packet's - time_since_start and the previous packet's. Note that for the first - packet, this value will be equal to time_since_start. + time_of_read and the previous packet's. """ FIRST_MEASUREMENT_OFFSET = 8 @@ -174,7 +172,7 @@ class Packet(object): (str(HvpmMeasurement.SIZE) + 's') * self.num_measurements) # yapf: disable. Yapf forces these to try to fit one after the other. - (self.time_since_start, + (self.time_of_read, self.time_since_last_sample, self.dropped_count, self.flags, @@ -195,7 +193,7 @@ class Packet(object): sample the values. """ time_per_sample = self.time_since_last_sample / self.num_measurements - return time_per_sample * (index + 1) + self.time_since_start + return time_per_sample * (index + 1) + self.time_of_read @property def packet_counter(self): diff --git a/acts/framework/acts/controllers/monsoon_lib/sampling/hvpm/transformers.py b/acts/framework/acts/controllers/monsoon_lib/sampling/hvpm/transformers.py index 5ddc23c13d..8525149435 100644 --- a/acts/framework/acts/controllers/monsoon_lib/sampling/hvpm/transformers.py +++ b/acts/framework/acts/controllers/monsoon_lib/sampling/hvpm/transformers.py @@ -126,7 +126,6 @@ class PacketCollector(SourceTransformer): A BufferList of a single buffer if collection is not yet finished. None if sampling is complete. """ - index = 0 if (self.sampling_duration and self.sampling_duration < time.time() - self.start_time): return None @@ -144,7 +143,7 @@ class PacketCollector(SourceTransformer): logging.warning(e) continue time_after_read = time.time() - time_data = struct.pack('dd', time_after_read - self.start_time, + time_data = struct.pack('dd', time_after_read, time_after_read - time_before_read) buffer[index] = time_data + data.tobytes() @@ -182,6 +181,7 @@ class PacketReader(ParallelTransformer): over it's maximum value (2^16-1). previous_dropped_count: The dropped count read from the last packet. Used for determining the true number of dropped samples. + start_time: The time of the first packet ever read. """ """The number of seconds before considering dropped_count to be meaningful. @@ -194,12 +194,18 @@ class PacketReader(ParallelTransformer): super().__init__() self.rollover_count = 0 self.previous_dropped_count = 0 + self.start_time = 0 def _transform_buffer(self, buffer): """Reads raw sample data and converts it into packet objects.""" + for i in range(len(buffer)): buffer[i] = Packet(buffer[i]) - if (buffer[i].time_since_start > + + if buffer and not self.start_time and i == 0: + self.start_time = buffer[0].time_of_read + + if (buffer[i].time_of_read - self.start_time > PacketReader.DROP_COUNT_TIMER_THRESHOLD): self._process_dropped_count(buffer[i]) @@ -216,7 +222,7 @@ class PacketReader(ParallelTransformer): self.previous_dropped_count = packet.dropped_count log_function = logging.info if __debug__ else logging.warning log_function('At %9f, total dropped count: %s' % - (packet.time_since_start, self.total_dropped_count)) + (packet.time_of_read, self.total_dropped_count)) @property def total_dropped_count(self): diff --git a/acts/framework/acts/controllers/monsoon_lib/sampling/lvpm_stock/packet.py b/acts/framework/acts/controllers/monsoon_lib/sampling/lvpm_stock/packet.py index b0f88394af..2fcdec8ce9 100644 --- a/acts/framework/acts/controllers/monsoon_lib/sampling/lvpm_stock/packet.py +++ b/acts/framework/acts/controllers/monsoon_lib/sampling/lvpm_stock/packet.py @@ -164,20 +164,18 @@ class Packet(object): Attributes: _packet_data: The raw data received from the packet. - time_since_start: The timestamp (relative to start) this packet was - collected. - time_since_last_sample: The differential between this packet's - time_since_start and the previous packet's. Note that for the first - packet, this value will be equal to time_since_start. + time_of_read: The unix timestamp this packet was collected at. + time_since_last_sample: The difference between this packet's + time_of_read and the previous packet's. """ # The number of bytes before the first packet. FIRST_MEASUREMENT_OFFSET = 4 - def __init__(self, sampled_bytes, time_since_start, + def __init__(self, sampled_bytes, time_of_read, time_since_last_sample): self._packet_data = sampled_bytes - self.time_since_start = time_since_start + self.time_of_read = time_of_read self.time_since_last_sample = time_since_last_sample num_data_bytes = len(sampled_bytes) - Packet.FIRST_MEASUREMENT_OFFSET @@ -207,7 +205,7 @@ class Packet(object): index: the index of the individual reading from within the sample. """ time_per_sample = self.time_since_last_sample / len(self.measurements) - return time_per_sample * (index + 1) + self.time_since_start + return time_per_sample * (index + 1) + self.time_of_read @property def packet_counter(self): diff --git a/acts/framework/acts/controllers/monsoon_lib/sampling/lvpm_stock/stock_transformers.py b/acts/framework/acts/controllers/monsoon_lib/sampling/lvpm_stock/stock_transformers.py index becc4ee99c..e0098bfba8 100644 --- a/acts/framework/acts/controllers/monsoon_lib/sampling/lvpm_stock/stock_transformers.py +++ b/acts/framework/acts/controllers/monsoon_lib/sampling/lvpm_stock/stock_transformers.py @@ -94,7 +94,7 @@ class PacketCollector(SourceTransformer): if data is None: continue time_after_read = time.time() - time_data = struct.pack('dd', time_after_read - self.start_time, + time_data = struct.pack('dd', time_after_read, time_after_read - time_before_read) buffer[index] = time_data + data @@ -169,7 +169,7 @@ class PacketReader(ParallelTransformer): for i, packet in enumerate(buffer): time_bytes_size = struct.calcsize('dd') # Unpacks the two time.time() values sent by PacketCollector. - time_since_start, time_of_read = struct.unpack( + time_of_read, time_since_last_read = struct.unpack( 'dd', packet[:time_bytes_size]) packet = packet[time_bytes_size:] # Magic number explanation: @@ -182,7 +182,7 @@ class PacketReader(ParallelTransformer): buffer[i] = None continue - buffer[i] = Packet(packet, time_since_start, time_of_read) + buffer[i] = Packet(packet, time_of_read, time_since_last_read) return buffer diff --git a/acts/framework/acts/controllers/openwrt_ap.py b/acts/framework/acts/controllers/openwrt_ap.py new file mode 100644 index 0000000000..05bd38c271 --- /dev/null +++ b/acts/framework/acts/controllers/openwrt_ap.py @@ -0,0 +1,253 @@ +"""Controller for Open WRT access point.""" + +import time + +from acts import logger +from acts.controllers.ap_lib import hostapd_constants +from acts.controllers.openwrt_lib import wireless_config +from acts.controllers.openwrt_lib import wireless_settings_applier +from acts.controllers.utils_lib.ssh import connection +from acts.controllers.utils_lib.ssh import settings + +MOBLY_CONTROLLER_CONFIG_NAME = "OpenWrtAP" +ACTS_CONTROLLER_REFERENCE_NAME = "access_points" +OPEN_SECURITY = "none" +PSK_SECURITY = "psk2" +WEP_SECURITY = "wep" +ENT_SECURITY = "wpa2" +ENABLE_RADIO = "0" +WIFI_2G = "wifi2g" +WIFI_5G = "wifi5g" + + +def create(configs): + """Creates ap controllers from a json config. + + Creates an ap controller from either a list, or a single element. The element + can either be just the hostname or a dictionary containing the hostname and + username of the AP to connect to over SSH. + + Args: + configs: The json configs that represent this controller. + + Returns: + AccessPoint object + + Example: + Below is the config file entry for OpenWrtAP as a list. A testbed can have + 1 or more APs to configure. Each AP has a "ssh_config" key to provide SSH + login information. OpenWrtAP#__init__() uses this to create SSH object. + + "OpenWrtAP": [ + { + "ssh_config": { + "user" : "root", + "host" : "192.168.1.1" + } + }, + { + "ssh_config": { + "user" : "root", + "host" : "192.168.1.2" + } + } + ] + """ + return [OpenWrtAP(c) for c in configs] + + +def destroy(aps): + """Destroys a list of AccessPoints. + + Args: + aps: The list of AccessPoints to destroy. + """ + for ap in aps: + ap.close() + ap.close_ssh() + + +def get_info(aps): + """Get information on a list of access points. + + Args: + aps: A list of AccessPoints. + + Returns: + A list of all aps hostname. + """ + return [ap.ssh_settings.hostname for ap in aps] + + +class OpenWrtAP(object): + """An AccessPoint controller. + + Attributes: + log: Logging object for AccessPoint. + ssh: The ssh connection to the AP. + ssh_settings: The ssh settings being used by the ssh connection. + wireless_setting: object holding wireless configuration. + """ + + def __init__(self, config): + """Initialize AP.""" + self.ssh_settings = settings.from_config(config["ssh_config"]) + self.ssh = connection.SshConnection(self.ssh_settings) + self.log = logger.create_logger( + lambda msg: "[OpenWrtAP|%s] %s" % (self.ssh_settings.hostname, msg)) + self.wireless_setting = None + + def configure_ap(self, wifi_configs, channel_2g, channel_5g): + """Configure AP with the required settings. + + Each test class inherits WifiBaseTest. Based on the test, we may need to + configure PSK, WEP, OPEN, ENT networks on 2G and 5G bands in any + combination. We call WifiBaseTest methods get_psk_network(), + get_open_network(), get_wep_network() and get_ent_network() to create + dictionaries which contains this information. 'wifi_configs' is a list of + such dictionaries. Example below configures 2 WiFi networks - 1 PSK 2G and + 1 Open 5G on one AP. configure_ap() is called from WifiBaseTest to + configure the APs. + + wifi_configs = [ + { + '2g': { + 'SSID': '2g_AkqXWPK4', + 'security': 'psk2', + 'password': 'YgYuXqDO9H', + 'hiddenSSID': False + }, + }, + { + '5g': { + 'SSID': '5g_8IcMR1Sg', + 'security': 'none', + 'hiddenSSID': False + }, + } + ] + + Args: + wifi_configs: list of network settings for 2G and 5G bands. + channel_2g: channel for 2G band. + channel_5g: channel for 5G band. + """ + # generate wifi configs to configure + wireless_configs = self.generate_wireless_configs(wifi_configs) + self.wireless_setting = wireless_settings_applier.WirelessSettingsApplier( + self.ssh, wireless_configs, channel_2g, channel_5g) + self.wireless_setting.apply_wireless_settings() + + def start_ap(self): + """Starts the AP with the settings in /etc/config/wireless.""" + self.ssh.run("wifi up") + time.sleep(9) # wait for sometime for AP to come up + + def stop_ap(self): + """Stops the AP.""" + self.ssh.run("wifi down") + time.sleep(9) # wait for sometime for AP to go down + + def generate_wireless_configs(self, wifi_configs): + """Generate wireless configs to configure. + + Converts wifi_configs from configure_ap() to a list of 'WirelessConfig' + objects. Each object represents a wifi network to configure on the AP. + + Args: + wifi_configs: Network list of different security types and bands. + + Returns: + wireless configuration for openwrt AP. + """ + num_2g = 1 + num_5g = 1 + wireless_configs = [] + + for i in range(len(wifi_configs)): + if hostapd_constants.BAND_2G in wifi_configs[i]: + config = wifi_configs[i][hostapd_constants.BAND_2G] + if config["security"] == PSK_SECURITY: + wireless_configs.append( + wireless_config.WirelessConfig("%s%s" % (WIFI_2G, num_2g), + config["SSID"], + config["security"], + hostapd_constants.BAND_2G, + password=config["password"], + hidden=config["hiddenSSID"])) + elif config["security"] == WEP_SECURITY: + wireless_configs.append( + wireless_config.WirelessConfig("%s%s" % (WIFI_2G, num_2g), + config["SSID"], + config["security"], + hostapd_constants.BAND_2G, + wep_key=config["wepKeys"][0], + hidden=config["hiddenSSID"])) + elif config["security"] == OPEN_SECURITY: + wireless_configs.append( + wireless_config.WirelessConfig("%s%s" % (WIFI_2G, num_2g), + config["SSID"], + config["security"], + hostapd_constants.BAND_2G, + hidden=config["hiddenSSID"])) + elif config["security"] == ENT_SECURITY: + wireless_configs.append( + wireless_config.WirelessConfig( + "%s%s" % (WIFI_2G, num_2g), + config["SSID"], + config["security"], + hostapd_constants.BAND_2G, + radius_server_ip=config["radius_server_ip"], + radius_server_port=config["radius_server_port"], + radius_server_secret=config["radius_server_secret"], + hidden=config["hiddenSSID"])) + num_2g += 1 + if hostapd_constants.BAND_5G in wifi_configs[i]: + config = wifi_configs[i][hostapd_constants.BAND_5G] + if config["security"] == PSK_SECURITY: + wireless_configs.append( + wireless_config.WirelessConfig("%s%s" % (WIFI_5G, num_5g), + config["SSID"], + config["security"], + hostapd_constants.BAND_5G, + password=config["password"], + hidden=config["hiddenSSID"])) + elif config["security"] == WEP_SECURITY: + wireless_configs.append( + wireless_config.WirelessConfig("%s%s" % (WIFI_5G, num_5g), + config["SSID"], + config["security"], + hostapd_constants.BAND_5G, + wep_key=config["wepKeys"][0], + hidden=config["hiddenSSID"])) + elif config["security"] == OPEN_SECURITY: + wireless_configs.append( + wireless_config.WirelessConfig("%s%s" % (WIFI_5G, num_5g), + config["SSID"], + config["security"], + hostapd_constants.BAND_5G, + hidden=config["hiddenSSID"])) + elif config["security"] == ENT_SECURITY: + wireless_configs.append( + wireless_config.WirelessConfig( + "%s%s" % (WIFI_5G, num_5g), + config["SSID"], + config["security"], + hostapd_constants.BAND_5G, + radius_server_ip=config["radius_server_ip"], + radius_server_port=config["radius_server_port"], + radius_server_secret=config["radius_server_secret"], + hidden=config["hiddenSSID"])) + num_5g += 1 + + return wireless_configs + + def close(self): + """Reset wireless settings to default and stop AP.""" + if self.wireless_setting: + self.wireless_setting.cleanup_wireless_settings() + + def close_ssh(self): + """Close SSH connection to AP.""" + self.ssh.close() + diff --git a/acts/tests/google/__init__.py b/acts/framework/acts/controllers/openwrt_lib/__init__.py index e69de29bb2..e69de29bb2 100644 --- a/acts/tests/google/__init__.py +++ b/acts/framework/acts/controllers/openwrt_lib/__init__.py diff --git a/acts/framework/acts/controllers/openwrt_lib/wireless_config.py b/acts/framework/acts/controllers/openwrt_lib/wireless_config.py new file mode 100644 index 0000000000..ea89636e2d --- /dev/null +++ b/acts/framework/acts/controllers/openwrt_lib/wireless_config.py @@ -0,0 +1,50 @@ +"""Class for Wireless config.""" + +NET_IFACE = "lan" + + +class WirelessConfig(object): + """Creates an object to hold wireless config. + + Attributes: + name: name of the wireless config + ssid: SSID of the network. + security: security of the wifi network. + band: band of the wifi network. + iface: network interface of the wifi network. + password: password for psk network. + wep_key: wep keys for wep network. + wep_key_num: key number for wep network. + radius_server_ip: IP address of radius server. + radius_server_port: Port number of radius server. + radius_server_secret: Secret key of radius server. + hidden: Boolean, if the wifi network is hidden. + """ + + def __init__( + self, + name, + ssid, + security, + band, + iface=NET_IFACE, + password=None, + wep_key=None, + wep_key_num=1, + radius_server_ip=None, + radius_server_port=None, + radius_server_secret=None, + hidden=False): + self.name = name + self.ssid = ssid + self.security = security + self.band = band + self.iface = iface + self.password = password + self.wep_key = wep_key + self.wep_key_num = wep_key_num + self.radius_server_ip = radius_server_ip + self.radius_server_port = radius_server_port + self.radius_server_secret = radius_server_secret + self.hidden = hidden + diff --git a/acts/framework/acts/controllers/openwrt_lib/wireless_settings_applier.py b/acts/framework/acts/controllers/openwrt_lib/wireless_settings_applier.py new file mode 100644 index 0000000000..cfb94d1503 --- /dev/null +++ b/acts/framework/acts/controllers/openwrt_lib/wireless_settings_applier.py @@ -0,0 +1,124 @@ +"""Class to configure wireless settings.""" + +import time +from acts.controllers.ap_lib import hostapd_constants + +LEASE_FILE = "/tmp/dhcp.leases" +DNSMASQ_RESTART = "/etc/init.d/dnsmasq restart" +OPEN_SECURITY = "none" +PSK_SECURITY = "psk2" +WEP_SECURITY = "wep" +ENT_SECURITY = "wpa2" +ENABLE_RADIO = "0" +DISABLE_RADIO = "1" +ENABLE_HIDDEN = "1" + + +class WirelessSettingsApplier(object): + """Class for wireless settings. + + Attributes: + ssh: ssh object for the AP. + wireless_configs: a list of + acts.controllers.openwrt_lib.wireless_config.WirelessConfig. + channel_2g: channel for 2G band. + channel_5g: channel for 5G band. + """ + + def __init__(self, ssh, configs, channel_2g, channel_5g): + """Initialize wireless settings. + + Args: + ssh: ssh connection object. + configs: a list of + acts.controllers.openwrt_lib.wireless_config.WirelessConfig. + channel_2g: channel for 2G band. + channel_5g: channel for 5G band. + """ + self.ssh = ssh + self.wireless_configs = configs + self.channel_2g = channel_2g + self.channel_5g = channel_5g + + def apply_wireless_settings(self): + """Configure wireless settings from a list of configs.""" + + # set channels for 2G and 5G bands + self.ssh.run("uci set wireless.radio1.channel='%s'" % self.channel_2g) + self.ssh.run("uci set wireless.radio0.channel='%s'" % self.channel_5g) + + # disable default OpenWrt SSID + self.ssh.run("uci set wireless.default_radio1.disabled='%s'" % + DISABLE_RADIO) + self.ssh.run("uci set wireless.default_radio0.disabled='%s'" % + DISABLE_RADIO) + + # Enable radios + self.ssh.run("uci set wireless.radio1.disabled='%s'" % ENABLE_RADIO) + self.ssh.run("uci set wireless.radio0.disabled='%s'" % ENABLE_RADIO) + + for config in self.wireless_configs: + + # configure open network + if config.security == OPEN_SECURITY: + if config.band == hostapd_constants.BAND_2G: + self.ssh.run("uci set wireless.default_radio1.ssid='%s'" % + config.ssid) + self.ssh.run("uci set wireless.default_radio1.disabled='%s'" % + ENABLE_RADIO) + if config.hidden: + self.ssh.run("uci set wireless.default_radio1.hidden='%s'" % + ENABLE_HIDDEN) + elif config.band == hostapd_constants.BAND_5G: + self.ssh.run("uci set wireless.default_radio0.ssid='%s'" % + config.ssid) + self.ssh.run("uci set wireless.default_radio0.disabled='%s'" % + ENABLE_RADIO) + if config.hidden: + self.ssh.run("uci set wireless.default_radio0.hidden='%s'" % + ENABLE_HIDDEN) + continue + + self.ssh.run("uci set wireless.%s='wifi-iface'" % config.name) + if config.band == hostapd_constants.BAND_2G: + self.ssh.run("uci set wireless.%s.device='radio1'" % config.name) + else: + self.ssh.run("uci set wireless.%s.device='radio0'" % config.name) + self.ssh.run("uci set wireless.%s.network='%s'" % + (config.name, config.iface)) + self.ssh.run("uci set wireless.%s.mode='ap'" % config.name) + self.ssh.run("uci set wireless.%s.ssid='%s'" % + (config.name, config.ssid)) + self.ssh.run("uci set wireless.%s.encryption='%s'" % + (config.name, config.security)) + if config.security == PSK_SECURITY: + self.ssh.run("uci set wireless.%s.key='%s'" % + (config.name, config.password)) + elif config.security == WEP_SECURITY: + self.ssh.run("uci set wireless.%s.key%s='%s'" % + (config.name, config.wep_key_num, config.wep_key)) + self.ssh.run("uci set wireless.%s.key='%s'" % + (config.name, config.wep_key_num)) + elif config.security == ENT_SECURITY: + self.ssh.run("uci set wireless.%s.auth_secret='%s'" % + (config.name, config.radius_server_secret)) + self.ssh.run("uci set wireless.%s.auth_server='%s'" % + (config.name, config.radius_server_ip)) + self.ssh.run("uci set wireless.%s.auth_port='%s'" % + (config.name, config.radius_server_port)) + if config.hidden: + self.ssh.run("uci set wireless.%s.hidden='%s'" % + (config.name, ENABLE_HIDDEN)) + + self.ssh.run("uci commit wireless") + self.ssh.run("cp %s %s.tmp" % (LEASE_FILE, LEASE_FILE)) + + def cleanup_wireless_settings(self): + """Reset wireless settings to default.""" + self.ssh.run("wifi down") + self.ssh.run("rm -f /etc/config/wireless") + self.ssh.run("wifi config") + self.ssh.run("cp %s.tmp %s" % (LEASE_FILE, LEASE_FILE)) + self.ssh.run(DNSMASQ_RESTART) + time.sleep(9) + diff --git a/acts/framework/acts/controllers/power_metrics.py b/acts/framework/acts/controllers/power_metrics.py new file mode 100644 index 0000000000..f68edccbfc --- /dev/null +++ b/acts/framework/acts/controllers/power_metrics.py @@ -0,0 +1,420 @@ +#!/usr/bin/env python3 +# +# Copyright 2019 - The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import math + +# Metrics timestamp keys +START_TIMESTAMP = 'start' +END_TIMESTAMP = 'end' + +# Unit type constants +CURRENT = 'current' +POWER = 'power' +TIME = 'time' +VOLTAGE = 'voltage' + +# Unit constants +MILLIVOLT = 'mV' +VOLT = 'V' +MILLIAMP = 'mA' +AMP = 'A' +AMPERE = AMP +MILLIWATT = 'mW' +WATT = 'W' +MILLISECOND = 'ms' +SECOND = 's' +MINUTE = 'm' +HOUR = 'h' + +CONVERSION_TABLES = { + CURRENT: { + MILLIAMP: 0.001, + AMP: 1 + }, + POWER: { + MILLIWATT: 0.001, + WATT: 1 + }, + TIME: { + MILLISECOND: 0.001, + SECOND: 1, + MINUTE: 60, + HOUR: 3600 + }, + VOLTAGE: { + MILLIVOLT: 0.001, + VOLT : 1 + } +} + + +class AbsoluteThresholds(object): + """Class to represent thresholds in absolute (non-relative) values. + + Attributes: + lower: Lower limit of the threshold represented by a measurement. + upper: Upper limit of the threshold represented by a measurement. + unit_type: Type of the unit (current, power, etc). + unit: The unit for this threshold (W, mW, uW). + """ + + def __init__(self, lower, upper, unit_type, unit): + self.unit_type = unit_type + self.unit = unit + self.lower = Metric(lower, unit_type, unit) + self.upper = Metric(upper, unit_type, unit) + + @staticmethod + def from_percentual_deviation(expected, percentage, unit_type, unit): + """Creates an AbsoluteThresholds object from an expected value and its + allowed percentual deviation (also in terms of the expected value). + + For example, if the expected value is 20 and the deviation 25%, this + would imply that the absolute deviation is 20 * 0.25 = 5 and therefore + the absolute threshold would be from (20-5, 20+5) or (15, 25). + + Args: + expected: Central value from which the deviation will be estimated. + percentage: Percentage of allowed deviation, the percentage itself + is in terms of the expected value. + unit_type: Type of the unit (current, power, etc). + unit: Unit for this threshold (W, mW, uW). + """ + return AbsoluteThresholds(expected * (1 - percentage / 100), + expected * (1 + percentage / 100), + unit_type, + unit) + + @staticmethod + def from_threshold_conf(thresholds_conf): + """Creates a AbsoluteThresholds object from a ConfigWrapper describing + a threshold (either absolute or percentual). + + Args: + thresholds_conf: ConfigWrapper object that describes a threshold. + Returns: + AbsolutesThresholds object. + Raises: + ValueError if configuration is incorrect or incomplete. + """ + if 'unit_type' not in thresholds_conf: + raise ValueError( + 'A threshold config must contain a unit_type. %s is incorrect' + % str(thresholds_conf)) + + if 'unit' not in thresholds_conf: + raise ValueError( + 'A threshold config must contain a unit. %s is incorrect' + % str(thresholds_conf)) + + unit_type = thresholds_conf['unit_type'] + unit = thresholds_conf['unit'] + + is_relative = ( + 'expected_value' in thresholds_conf and + 'percent_deviation' in thresholds_conf) + + is_almost_relative = ( + 'expected_value' in thresholds_conf or + 'percent_deviation' in thresholds_conf) + + is_absolute = ('lower_limit' in thresholds_conf or + 'upper_limit' in thresholds_conf) + + if is_absolute and is_almost_relative: + raise ValueError( + 'Thresholds can either be absolute (with lower_limit and' + 'upper_limit defined) or by percentual deviation (with' + 'expected_value and percent_deviation defined), but never' + 'a mixture of both. %s is incorrect' + % str(thresholds_conf)) + + if is_almost_relative and not is_relative: + if 'expected_value' not in thresholds_conf: + raise ValueError( + 'Incomplete definition of a threshold by percentual ' + 'deviation. percent_deviation given, but missing ' + 'expected_value. %s is incorrect' + % str(thresholds_conf)) + + if 'percent_deviation' not in thresholds_conf: + raise ValueError( + 'Incomplete definition of a threshold by percentual ' + 'deviation. expected_value given, but missing ' + 'percent_deviation. %s is incorrect' + % str(thresholds_conf)) + + if not is_absolute and not is_relative: + raise ValueError( + 'Thresholds must be either absolute (with lower_limit and' + 'upper_limit defined) or defined by percentual deviation (with' + 'expected_value and percent_deviation defined). %s is incorrect' + % str(thresholds_conf)) + + if is_relative: + expected = thresholds_conf.get_numeric('expected_value') + percent = thresholds_conf.get_numeric('percent_deviation') + + thresholds = ( + AbsoluteThresholds.from_percentual_deviation( + expected, + percent, + unit_type, unit)) + + else: + lower_value = thresholds_conf.get_numeric('lower_limit', + float('-inf')) + upper_value = thresholds_conf.get_numeric('upper_limit', + float('inf')) + thresholds = AbsoluteThresholds(lower_value, upper_value, unit_type, + unit) + return thresholds + + +class Metric(object): + """Base class for describing power measurement values. Each object contains + an value and a unit. Enables some basic arithmetic operations with other + measurements of the same unit type. + + Attributes: + value: Numeric value of the measurement + _unit_type: Unit type of the measurement (e.g. current, power) + unit: Unit of the measurement (e.g. W, mA) + """ + + def __init__(self, value, unit_type, unit, name=None): + if unit_type not in CONVERSION_TABLES: + raise TypeError( + '%s is not a valid unit type, valid unit types are %s' % ( + unit_type, str(CONVERSION_TABLES.keys))) + self.value = value + self.unit = unit + self.name = name + self._unit_type = unit_type + + # Convenience constructor methods + @staticmethod + def amps(amps, name=None): + """Create a new current measurement, in amps.""" + return Metric(amps, CURRENT, AMP, name=name) + + @staticmethod + def watts(watts, name=None): + """Create a new power measurement, in watts.""" + return Metric(watts, POWER, WATT, name=name) + + @staticmethod + def seconds(seconds, name=None): + """Create a new time measurement, in seconds.""" + return Metric(seconds, TIME, SECOND, name=name) + + # Comparison methods + + def __eq__(self, other): + return self.value == other.to_unit(self.unit).value + + def __lt__(self, other): + return self.value < other.to_unit(self.unit).value + + def __le__(self, other): + return self == other or self < other + + # Addition and subtraction with other measurements + + def __add__(self, other): + """Adds measurements of compatible unit types. The result will be in the + same units as self. + """ + return Metric(self.value + other.to_unit(self.unit).value, + self._unit_type, self.unit, name=self.name) + + def __sub__(self, other): + """Subtracts measurements of compatible unit types. The result will be + in the same units as self. + """ + return Metric(self.value - other.to_unit(self.unit).value, + self._unit_type, self.unit, name=self.name) + + # String representation + + def __str__(self): + return '%g%s' % (self.value, self.unit) + + def __repr__(self): + return str(self) + + def to_unit(self, new_unit): + """Create an equivalent measurement under a different unit. + e.g. 0.5W -> 500mW + + Args: + new_unit: Target unit. Must be compatible with current unit. + + Returns: A new measurement with the converted value and unit. + """ + try: + new_value = self.value * ( + CONVERSION_TABLES[self._unit_type][self.unit] / + CONVERSION_TABLES[self._unit_type][new_unit]) + except KeyError: + raise TypeError('Incompatible units: %s, %s' % + (self.unit, new_unit)) + return Metric(new_value, self._unit_type, new_unit, self.name) + + +def import_raw_data(path): + """Create a generator from a Monsoon data file. + + Args: + path: path to raw data file + + Returns: generator that yields (timestamp, sample) per line + """ + with open(path, 'r') as f: + for line in f: + time, sample = line.split() + yield float(time[:-1]), float(sample) + + +def generate_test_metrics(raw_data, timestamps=None, + voltage=None): + """Split the data into individual test metrics, based on the timestamps + given as a dict. + + Args: + raw_data: raw data as list or generator of (timestamp, sample) + timestamps: dict following the output format of + instrumentation_proto_parser.get_test_timestamps() + voltage: voltage used during measurements + """ + + # Initialize metrics for each test + if timestamps is None: + timestamps = {} + test_starts = {} + test_ends = {} + test_metrics = {} + for seg_name, times in timestamps.items(): + test_metrics[seg_name] = PowerMetrics(voltage) + try: + test_starts[seg_name] = Metric( + times[START_TIMESTAMP], TIME, MILLISECOND).to_unit( + SECOND).value + except KeyError: + raise ValueError( + 'Missing start timestamp for test scenario "%s". Refer to ' + 'instrumentation_proto.txt for details.' % seg_name) + try: + test_ends[seg_name] = Metric( + times[END_TIMESTAMP], TIME, MILLISECOND).to_unit( + SECOND).value + except KeyError: + raise ValueError( + 'Missing end timestamp for test scenario "%s". Test ' + 'scenario may have terminated with errors. Refer to ' + 'instrumentation_proto.txt for details.' % seg_name) + + # Assign data to tests based on timestamps + for timestamp, amps in raw_data: + for seg_name in timestamps: + if test_starts[seg_name] <= timestamp <= test_ends[seg_name]: + test_metrics[seg_name].update_metrics(amps) + + result = {} + for seg_name, power_metrics in test_metrics.items(): + result[seg_name] = [ + power_metrics.avg_current, + power_metrics.max_current, + power_metrics.min_current, + power_metrics.stdev_current, + power_metrics.avg_power] + return result + + +class PowerMetrics(object): + """Class for processing raw power metrics generated by Monsoon measurements. + Provides useful metrics such as average current, max current, and average + power. Can generate individual test metrics. + + See section "Numeric metrics" below for available metrics. + """ + + def __init__(self, voltage): + """Create a PowerMetrics. + + Args: + voltage: Voltage of the measurement + """ + self._voltage = voltage + self._num_samples = 0 + self._sum_currents = 0 + self._sum_squares = 0 + self._max_current = None + self._min_current = None + self.test_metrics = {} + + def update_metrics(self, sample): + """Update the running metrics with the current sample. + + Args: + sample: A current sample in Amps. + """ + self._num_samples += 1 + self._sum_currents += sample + self._sum_squares += sample ** 2 + if self._max_current is None or sample > self._max_current: + self._max_current = sample + if self._min_current is None or sample < self._min_current: + self._min_current = sample + + # Numeric metrics + @property + def avg_current(self): + """Average current, in milliamps.""" + if not self._num_samples: + return Metric.amps(0).to_unit(MILLIAMP) + return (Metric.amps(self._sum_currents / self._num_samples, + 'avg_current') + .to_unit(MILLIAMP)) + + @property + def max_current(self): + """Max current, in milliamps.""" + return Metric.amps(self._max_current or 0, 'max_current').to_unit( + MILLIAMP) + + @property + def min_current(self): + """Min current, in milliamps.""" + return Metric.amps(self._min_current or 0, 'min_current').to_unit( + MILLIAMP) + + @property + def stdev_current(self): + """Standard deviation of current values, in milliamps.""" + if self._num_samples < 2: + return Metric.amps(0, 'stdev_current').to_unit(MILLIAMP) + stdev = math.sqrt( + (self._sum_squares - ( + self._num_samples * self.avg_current.to_unit(AMP).value ** 2)) + / (self._num_samples - 1)) + return Metric.amps(stdev, 'stdev_current').to_unit(MILLIAMP) + + @property + def avg_power(self): + """Average power, in milliwatts.""" + return Metric.watts(self.avg_current.to_unit(AMP).value * self._voltage, + 'avg_power').to_unit(MILLIWATT) diff --git a/acts/framework/acts/controllers/power_monitor.py b/acts/framework/acts/controllers/power_monitor.py index eb13f54c00..694edd0208 100644 --- a/acts/framework/acts/controllers/power_monitor.py +++ b/acts/framework/acts/controllers/power_monitor.py @@ -14,6 +14,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +import tempfile +import logging +from acts.controllers.monsoon_lib.api.common import MonsoonError +from acts.controllers import power_metrics + class ResourcesRegistryError(Exception): pass @@ -45,3 +50,130 @@ def update_registry(registry): def get_registry(): return _REGISTRY + + +def _write_raw_data_in_standard_format(raw_data, path, start_time): + """Writes the raw data to a file in (seconds since epoch, amps). + + TODO(b/155294049): Deprecate this once Monsoon controller output + format is updated. + + Args: + start_time: Measurement start time in seconds since epoch + raw_data: raw data as list or generator of (timestamp, sample) + path: path to write output + """ + with open(path, 'w') as f: + for timestamp, amps in raw_data: + f.write('%s %s\n' % + (timestamp + start_time, amps)) + + +class BasePowerMonitor(object): + + def setup(self, **kwargs): + raise NotImplementedError() + + def connect_usb(self, **kwargs): + raise NotImplementedError() + + def measure(self, **kwargs): + raise NotImplementedError() + + def release_resources(self, **kwargs): + raise NotImplementedError() + + def disconnect_usb(self, **kwargs): + raise NotImplementedError() + + def get_metrics(self, **kwargs): + raise NotImplementedError() + + def teardown(self, **kwargs): + raise NotImplementedError() + + +class PowerMonitorMonsoonFacade(BasePowerMonitor): + + def __init__(self, monsoon): + """Constructs a PowerMonitorFacade. + + Args: + monsoon: delegate monsoon object, either + acts.controllers.monsoon_lib.api.hvpm.monsoon.Monsoon or + acts.controllers.monsoon_lib.api.lvpm_stock.monsoon.Monsoon. + """ + self.monsoon = monsoon + self._log = logging.getLogger() + + def setup(self, monsoon_config=None, **__): + """Set up the Monsoon controller for this testclass/testcase.""" + + if monsoon_config is None: + raise MonsoonError('monsoon_config can not be None') + + self._log.info('Setting up Monsoon %s' % self.monsoon.serial) + voltage = monsoon_config.get_numeric('voltage', 4.2) + self.monsoon.set_voltage_safe(voltage) + if 'max_current' in monsoon_config: + self.monsoon.set_max_current( + monsoon_config.get_numeric('max_current')) + + def connect_usb(self, **__): + self.monsoon.usb('on') + + def measure(self, measurement_args=None, start_time=None, + output_path=None, **__): + if measurement_args is None: + raise MonsoonError('measurement_args can not be None') + + with tempfile.NamedTemporaryFile(prefix='monsoon_') as tmon: + self.monsoon.measure_power(**measurement_args, + output_path=tmon.name) + + if output_path and start_time is not None: + _write_raw_data_in_standard_format( + power_metrics.import_raw_data(tmon.name), + output_path, + start_time) + + def release_resources(self, **__): + # nothing to do + pass + + def disconnect_usb(self, **__): + self.monsoon.usb('off') + + def get_metrics(self, start_time=None, voltage=None, monsoon_file_path=None, + timestamps=None, **__): + """Parses a monsoon_file_path to compute the consumed power and other + power related metrics. + + Args: + start_time: Time when the measurement started, this is used to + correlate timestamps from the device and from the power samples. + voltage: Voltage used when the measurement started. Used to compute + power from current. + monsoon_file_path: Path to a monsoon file. + timestamps: Named timestamps delimiting the segments of interest. + **__: + + Returns: + A list of power_metrics.Metric. + """ + if start_time is None: + raise MonsoonError('start_time can not be None') + if voltage is None: + raise MonsoonError('voltage can not be None') + if monsoon_file_path is None: + raise MonsoonError('monsoon_file_path can not be None') + if timestamps is None: + raise MonsoonError('timestamps can not be None') + + return power_metrics.generate_test_metrics( + power_metrics.import_raw_data(monsoon_file_path), + timestamps=timestamps, voltage=voltage) + + def teardown(self, **__): + # nothing to do + pass diff --git a/acts/framework/acts/controllers/rohdeschwarz_lib/OWNERS b/acts/framework/acts/controllers/rohdeschwarz_lib/OWNERS new file mode 100644 index 0000000000..e4010df218 --- /dev/null +++ b/acts/framework/acts/controllers/rohdeschwarz_lib/OWNERS @@ -0,0 +1,4 @@ +iguarna@google.com +chaoyangf@google.com +yixiang@google.com +codycaldwell@google.com
\ No newline at end of file diff --git a/acts/framework/acts/controllers/rohdeschwarz_lib/cmw500_cellular_simulator.py b/acts/framework/acts/controllers/rohdeschwarz_lib/cmw500_cellular_simulator.py index 91e71b767a..6a6c3ffd0a 100644 --- a/acts/framework/acts/controllers/rohdeschwarz_lib/cmw500_cellular_simulator.py +++ b/acts/framework/acts/controllers/rohdeschwarz_lib/cmw500_cellular_simulator.py @@ -17,7 +17,7 @@ import time from acts.controllers.rohdeschwarz_lib import cmw500 from acts.controllers import cellular_simulator as cc -from acts.test_utils.power.tel_simulations import LteSimulation +from acts.controllers.cellular_lib import LteSimulation CMW_TM_MAPPING = { LteSimulation.TransmissionMode.TM1: cmw500.TransmissionModes.TM1, diff --git a/acts/framework/acts/controllers/sl4a_lib/event_dispatcher.py b/acts/framework/acts/controllers/sl4a_lib/event_dispatcher.py index c37635e188..e8a49ff166 100644 --- a/acts/framework/acts/controllers/sl4a_lib/event_dispatcher.py +++ b/acts/framework/acts/controllers/sl4a_lib/event_dispatcher.py @@ -215,8 +215,10 @@ class EventDispatcher: # Block forever on event wait return e_queue.get(True) except queue.Empty: - raise queue.Empty('Timeout after {}s waiting for event: {}'.format( - timeout, event_name)) + msg = 'Timeout after {}s waiting for event: {}'.format( + timeout, event_name) + self.log.error(msg) + raise queue.Empty(msg) def wait_for_event(self, event_name, @@ -273,9 +275,10 @@ class EventDispatcher: if time.time() > deadline: for ignored_event in ignored_events: self.get_event_q(event_name).put(ignored_event) - raise queue.Empty( - 'Timeout after {}s waiting for event: {}'.format( - timeout, event_name)) + msg = 'Timeout after {}s waiting for event: {}'.format( + timeout, event_name) + self.log.error(msg) + raise queue.Empty(msg) def pop_events(self, regex_pattern, timeout, freq=1): """Pop events whose names match a regex pattern. @@ -312,8 +315,10 @@ class EventDispatcher: break time.sleep(freq) if len(results) == 0: - raise queue.Empty('Timeout after {}s waiting for event: {}'.format( - timeout, regex_pattern)) + msg = 'Timeout after {}s waiting for event: {}'.format( + timeout, regex_pattern) + self.log.error(msg) + raise queue.Empty(msg) return sorted(results, key=lambda event: event['time']) diff --git a/acts/tests/google/wifi/__init__.py b/acts/framework/acts/controllers/spectracom_lib/__init__.py index e69de29bb2..e69de29bb2 100644 --- a/acts/tests/google/wifi/__init__.py +++ b/acts/framework/acts/controllers/spectracom_lib/__init__.py diff --git a/acts/framework/acts/controllers/spectracom_lib/gsg6.py b/acts/framework/acts/controllers/spectracom_lib/gsg6.py new file mode 100644 index 0000000000..6b96456f3d --- /dev/null +++ b/acts/framework/acts/controllers/spectracom_lib/gsg6.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +# +# Copyright 2019 - The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Python module for Spectracom/Orolia GSG-6 GNSS simulator.""" + +from acts.controllers import abstract_inst + + +class GSG6Error(abstract_inst.SocketInstrumentError): + """GSG-6 Instrument Error Class.""" + + +class GSG6(abstract_inst.SocketInstrument): + """GSG-6 Class, inherted from abstract_inst SocketInstrument.""" + + def __init__(self, ip_addr, ip_port): + """Init method for GSG-6. + + Args: + ip_addr: IP Address. + Type, str. + ip_port: TCPIP Port. + Type, str. + """ + super(GSG6, self).__init__(ip_addr, ip_port) + + self.idn = '' + + def connect(self): + """Init and Connect to GSG-6.""" + self._connect_socket() + + self.get_idn() + + infmsg = 'Connected to GSG-6, with ID: {}'.format(self.idn) + self._logger.debug(infmsg) + + def close(self): + """Close GSG-6.""" + self._close_socket() + + self._logger.debug('Closed connection to GSG-6') + + def get_idn(self): + """Get the Idenification of GSG-6. + + Returns: + GSG-6 Identifier + """ + self.idn = self._query('*IDN?') + + return self.idn + + def start_scenario(self, scenario=''): + """Start to run scenario. + + Args: + scenario: Scenario to run. + Type, str. + Default, '', which will run current selected one. + """ + if scenario: + cmd = 'SOUR:SCEN:LOAD ' + scenario + self._send(cmd) + + self._send('SOUR:SCEN:CONT START') + + if scenario: + infmsg = 'Started running scenario {}'.format(scenario) + else: + infmsg = 'Started running current scenario' + + self._logger.debug(infmsg) + + def stop_scenario(self): + """Stop the running scenario.""" + + self._send('SOUR:SCEN:CONT STOP') + + self._logger.debug('Stopped running scenario') + + def preset(self): + """Preset GSG-6 to default status.""" + self._send('*RST') + + self._logger.debug('Reset GSG-6') + + def set_power(self, power_level): + """set GSG-6 transmit power on all bands. + + Args: + power_level: transmit power level + Type, float. + Decimal, unit [dBm] + + Raises: + GSG6Error: raise when power level is not in [-160, -65] range. + """ + if not -160 <= power_level <= -65: + errmsg = ('"power_level" must be within [-160, -65], ' + 'current input is {}').format(str(power_level)) + raise GSG6Error(error=errmsg, command='set_power') + + self._send(':SOUR:POW ' + str(round(power_level, 1))) + + infmsg = 'Set GSG-6 transmit power to "{}"'.format( + round(power_level, 1)) + self._logger.debug(infmsg) diff --git a/acts/framework/acts/controllers/spirent_lib/__init__.py b/acts/framework/acts/controllers/spirent_lib/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/acts/framework/acts/controllers/spirent_lib/__init__.py diff --git a/acts/framework/acts/controllers/spirent_lib/gss6450.py b/acts/framework/acts/controllers/spirent_lib/gss6450.py new file mode 100644 index 0000000000..aa84575b5a --- /dev/null +++ b/acts/framework/acts/controllers/spirent_lib/gss6450.py @@ -0,0 +1,381 @@ +#!/usr/bin/env python3 +# +# Copyright 2019 - The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Python module for Spirent GSS6450 GNSS RPS.""" + +import datetime +import numbers +from acts.controllers import abstract_inst + + +class GSS6450Error(abstract_inst.SocketInstrumentError): + """GSS6450 Instrument Error Class.""" + + +class GSS6450(abstract_inst.RequestInstrument): + """GSS6450 Class, inherted from abstract_inst RequestInstrument.""" + + def __init__(self, ip_addr): + """Init method for GSS6450. + + Args: + ip_addr: IP Address. + Type, str. + """ + super(GSS6450, self).__init__(ip_addr) + + self.idn = 'Spirent-GSS6450' + + def _put(self, cmd): + """Send put command via GSS6450 HTTP Request and get response. + + Args: + cmd: parameters listed in SHM_PUT. + Type, Str. + + Returns: + resp: Response from the _query method. + Type, Str. + """ + put_cmd = 'shm_put.shtml?' + cmd + resp = self._query(put_cmd) + + return resp + + def _get(self, cmd): + """Send get command via GSS6450 HTTP Request and get response. + + Args: + cmd: parameters listed in SHM_GET. + Type, Str. + + Returns: + resp: Response from the _query method. + Type, Str. + """ + get_cmd = 'shm_get.shtml?' + cmd + resp = self._query(get_cmd) + + return resp + + def get_scenario_filename(self): + """Get the scenario filename of GSS6450. + + Returns: + filename: RPS Scenario file name. + Type, Str. + """ + resp_raw = self._get('-f') + filename = resp_raw.split(':')[-1].strip(' ') + self._logger.debug('Got scenario file name: "%s".', filename) + + return filename + + def get_scenario_description(self): + """Get the scenario description of GSS6450. + + Returns: + description: RPS Scenario description. + Type, Str. + """ + resp_raw = self._get('-d') + description = resp_raw.split('-d')[-1].strip(' ') + + if description: + self._logger.debug('Got scenario description: "%s".', description) + else: + self._logger.warning('Got scenario description with empty string.') + + return description + + def get_scenario_location(self): + """Get the scenario location of GSS6450. + + Returns: + location: RPS Scenario location. + Type, Str. + """ + resp_raw = self._get('-i') + location = resp_raw.split('-i')[-1].strip(' ') + + if location: + self._logger.debug('Got scenario location: "%s".', location) + else: + self._logger.warning('Got scenario location with empty string.') + + return location + + def get_operation_mode(self): + """Get the operation mode of GSS6450. + + Returns: + mode: RPS Operation Mode. + Type, Str. + Option, STOPPED/PLAYING/RECORDING + """ + resp_raw = self._get('-m') + mode = resp_raw.split('-m')[-1].strip(' ') + self._logger.debug('Got operation mode: "%s".', mode) + + return mode + + def get_battery_level(self): + """Get the battery level of GSS6450. + + Returns: + batterylevel: RPS Battery Level. + Type, float. + """ + resp_raw = self._get('-l') + batterylevel = float(resp_raw.split('-l')[-1].strip(' ')) + self._logger.debug('Got battery level: %s%%.', batterylevel) + + return batterylevel + + def get_rfport_voltage(self): + """Get the RF port voltage of GSS6450. + + Returns: + voltageout: RPS RF port voltage. + Type, str + """ + resp_raw = self._get('-v') + voltageout = resp_raw.split('-v')[-1].strip(' ') + self._logger.debug('Got RF port voltage: "%s".', voltageout) + + return voltageout + + def get_storage_media(self): + """Get the storage media of GSS6450. + + Returns: + media: RPS storage. + Type, str + + Raises: + GSS6450Error: raise when request response is not support. + """ + resp_raw = self._get('-M') + resp_num = resp_raw.split('-M')[-1].strip(' ') + + if resp_num == '1': + media = '1-INTERNAL' + elif resp_num == '2': + media = '2-REMOVABLE' + else: + errmsg = ('"{}" is not recognized as GSS6450 valid storage media' + ' type'.format(resp_num)) + raise GSS6450Error(error=errmsg, command='get_storage_media') + + self._logger.debug('Got current storage media: %s.', media) + + return media + + def get_attenuation(self): + """Get the attenuation of GSS6450. + + Returns: + attenuation: RPS attenuation level, in dB. + Type, list of float. + """ + resp_raw = self._get('-a') + resp_str = resp_raw.split('-a')[-1].strip(' ') + self._logger.debug('Got attenuation: %s dB.', resp_str) + attenuation = [float(itm) for itm in resp_str.split(',')] + + return attenuation + + def get_elapsed_time(self): + """Get the running scenario elapsed time of GSS6450. + + Returns: + etime: RPS elapsed time. + Type, datetime.timedelta. + """ + resp_raw = self._get('-e') + resp_str = resp_raw.split('-e')[-1].strip(' ') + self._logger.debug('Got senario elapsed time: "%s".', resp_str) + etime_tmp = datetime.datetime.strptime(resp_str, '%H:%M:%S') + etime = datetime.timedelta(hours=etime_tmp.hour, + minutes=etime_tmp.minute, + seconds=etime_tmp.second) + + return etime + + def get_playback_offset(self): + """Get the running scenario playback offset of GSS6450. + + Returns: + offset: RPS playback offset. + Type, datetime.timedelta. + """ + resp_raw = self._get('-o') + offset_tmp = float(resp_raw.split('-o')[-1].strip(' ')) + self._logger.debug('Got senario playback offset: %s sec.', offset_tmp) + offset = datetime.timedelta(seconds=offset_tmp) + + return offset + + def play_scenario(self, scenario=''): + """Start to play scenario in GSS6450. + + Args: + scenario: Scenario to play. + Type, str. + Default, '', which will run current selected one. + """ + if scenario: + cmd = '-f{},-wP'.format(scenario) + else: + cmd = '-wP' + + _ = self._put(cmd) + + if scenario: + infmsg = 'Started playing scenario: "{}".'.format(scenario) + else: + infmsg = 'Started playing current scenario.' + + self._logger.debug(infmsg) + + def record_scenario(self, scenario=''): + """Start to record scenario in GSS6450. + + Args: + scenario: Scenario to record. + Type, str. + Default, '', which will run current selected one. + """ + if scenario: + cmd = '-f{},-wR'.format(scenario) + else: + cmd = '-wR' + + _ = self._put(cmd) + + if scenario: + infmsg = 'Started recording scenario: "{}".'.format(scenario) + else: + infmsg = 'Started recording scenario.' + + self._logger.debug(infmsg) + + def stop_scenario(self): + """Start to stop playing/recording scenario in GSS6450.""" + _ = self._put('-wS') + + self._logger.debug('Stopped playing/recording scanrio.') + + def set_rfport_voltage(self, voltageout): + """Set the RF port voltage of GSS6450. + + Args: + voltageout: RPS RF port voltage. + Type, str + + Raises: + GSS6450Error: raise when voltageout input is not valid. + """ + if voltageout == 'OFF': + voltage_cmd = '0' + elif voltageout == '3.3V': + voltage_cmd = '3' + elif voltageout == '5V': + voltage_cmd = '5' + else: + errmsg = ('"{}" is not recognized as GSS6450 valid RF port voltage' + ' type'.format(voltageout)) + raise GSS6450Error(error=errmsg, command='set_rfport_voltage') + + _ = self._put('-v{},-wV'.format(voltage_cmd)) + self._logger.debug('Set RF port voltage: "%s".', voltageout) + + def set_attenuation(self, attenuation): + """Set the attenuation of GSS6450. + + Args: + attenuation: RPS attenuation level, in dB. + Type, numerical. + + Raises: + GSS6450Error: raise when attenuation is not in range. + """ + if not 0 <= attenuation <= 31: + errmsg = ('"attenuation" must be within [0, 31], ' + 'current input is {}').format(str(attenuation)) + raise GSS6450Error(error=errmsg, command='set_attenuation') + + attenuation_raw = round(attenuation) + + if attenuation_raw != attenuation: + warningmsg = ('"attenuation" must be integer, current input ' + 'will be rounded to {}'.format(attenuation_raw)) + self._logger.warning(warningmsg) + + _ = self._put('-a{},-wA'.format(attenuation_raw)) + + self._logger.debug('Set attenuation: %s dB.', attenuation_raw) + + def set_playback_offset(self, offset): + """Set the playback offset of GSS6450. + + Args: + offset: RPS playback offset. + Type, datetime.timedelta, or numerical. + + Raises: + GSS6450Error: raise when offset is not numeric or timedelta. + """ + if isinstance(offset, datetime.timedelta): + offset_raw = offset.total_seconds() + elif isinstance(offset, numbers.Number): + offset_raw = offset + else: + raise GSS6450Error(error=('"offset" must be numerical value or ' + 'datetime.timedelta'), + command='set_playback_offset') + + _ = self._put('-o{}'.format(offset_raw)) + + self._logger.debug('Set playback offset: %s sec.', offset_raw) + + def set_storage_media(self, media): + """Set the storage media of GSS6450. + + Args: + media: RPS storage Media, Internal or External. + Type, str. Option, 'internal', 'removable' + + Raises: + GSS6450Error: raise when media option is not support. + """ + if media == 'internal': + raw_media = '1' + elif media == 'removable': + raw_media = '2' + else: + raise GSS6450Error( + error=('"media" input must be in ["internal", "removable"]. ' + ' Current input is {}'.format(media)), + command='set_storage_media') + + _ = self._put('-M{}-wM'.format(raw_media)) + + resp_raw = self.get_storage_media() + if raw_media != resp_raw[0]: + raise GSS6450Error( + error=('Setting media "{}" is not the same as queried media ' + '"{}".'.format(media, resp_raw)), + command='set_storage_media') diff --git a/acts/framework/acts/controllers/utils_lib/commands/shell.py b/acts/framework/acts/controllers/utils_lib/commands/shell.py index 5c74cd899b..81a2f13f3d 100644 --- a/acts/framework/acts/controllers/utils_lib/commands/shell.py +++ b/acts/framework/acts/controllers/utils_lib/commands/shell.py @@ -28,7 +28,6 @@ class ShellCommand(object): Note: At the moment this only works with the ssh runner. """ - def __init__(self, runner, working_dir=None): """Creates a new shell command invoker. @@ -40,7 +39,7 @@ class ShellCommand(object): self._runner = runner self._working_dir = working_dir - def run(self, command, timeout=3600): + def run(self, command, timeout=60): """Runs a generic command through the runner. Takes the command and prepares it to be run in the target shell using @@ -129,8 +128,7 @@ class ShellCommand(object): True if the string or pattern was found, False otherwise. """ try: - self.run('grep %s %s' % (shlex.quote(search_string), - file_name)) + self.run('grep %s %s' % (shlex.quote(search_string), file_name)) return True except job.Error: return False diff --git a/acts/framework/acts/controllers/utils_lib/ssh/connection.py b/acts/framework/acts/controllers/utils_lib/ssh/connection.py index c8e498345f..71758bb335 100644 --- a/acts/framework/acts/controllers/utils_lib/ssh/connection.py +++ b/acts/framework/acts/controllers/utils_lib/ssh/connection.py @@ -37,7 +37,6 @@ class CommandError(Exception): Attributes: result: The results of the ssh command that had the error. """ - def __init__(self, result): """ Args: @@ -62,7 +61,6 @@ class SshConnection(object): a command is run. If the persistent connection fails it will attempt to connect normally. """ - @property def socket_path(self): """Returns: The os path to the master socket file.""" @@ -120,7 +118,8 @@ class SshConnection(object): if self._master_ssh_proc is None: # Create a shared socket in a temp location. - self._master_ssh_tempdir = tempfile.mkdtemp(prefix='ssh-master') + self._master_ssh_tempdir = tempfile.mkdtemp( + prefix='ssh-master') # Setup flags and options for running the master ssh # -N: Do not execute a remote command. @@ -153,7 +152,7 @@ class SshConnection(object): def run(self, command, - timeout=3600, + timeout=60, ignore_status=False, env=None, io_encoding='utf-8', @@ -205,16 +204,16 @@ class SshConnection(object): dns_retry_count = 2 while True: - result = job.run( - terminal_command, - ignore_status=True, - timeout=timeout, - io_encoding=io_encoding) + result = job.run(terminal_command, + ignore_status=True, + timeout=timeout, + io_encoding=io_encoding) output = result.stdout # Check for a connected message to prevent false negatives. - valid_connection = re.search( - '^CONNECTED: %s' % identifier, output, flags=re.MULTILINE) + valid_connection = re.search('^CONNECTED: %s' % identifier, + output, + flags=re.MULTILINE) if valid_connection: # Remove the first line that contains the connect message. line_index = output.find('\n') + 1 @@ -222,14 +221,13 @@ class SshConnection(object): line_index = len(output) real_output = output[line_index:].encode(io_encoding) - result = job.Result( - command=result.command, - stdout=real_output, - stderr=result._raw_stderr, - exit_status=result.exit_status, - duration=result.duration, - did_timeout=result.did_timeout, - encoding=io_encoding) + result = job.Result(command=result.command, + stdout=real_output, + stderr=result._raw_stderr, + exit_status=result.exit_status, + duration=result.duration, + did_timeout=result.did_timeout, + encoding=io_encoding) if result.exit_status and not ignore_status: raise job.Error(result) return result @@ -268,9 +266,10 @@ class SshConnection(object): if unknown_host: raise Error('Unknown host.', result) - self.log.error('An unknown error has occurred. Job result: %s' % result) - ping_output = job.run( - 'ping %s -c 3 -w 1' % self._settings.hostname, ignore_status=True) + self.log.error('An unknown error has occurred. Job result: %s' % + result) + ping_output = job.run('ping %s -c 3 -w 1' % self._settings.hostname, + ignore_status=True) self.log.error('Ping result: %s' % ping_output) if attempts > 1: self._cleanup_master_ssh() @@ -403,9 +402,8 @@ class SshConnection(object): """ # TODO: This may belong somewhere else: b/32572515 user_host = self._formatter.format_host_name(self._settings) - job.run( - 'scp %s %s:%s' % (local_path, user_host, remote_path), - ignore_status=ignore_status) + job.run('scp %s %s:%s' % (local_path, user_host, remote_path), + ignore_status=ignore_status) def pull_file(self, local_path, remote_path, ignore_status=False): """Send a file from remote host to local host @@ -416,9 +414,8 @@ class SshConnection(object): ignore_status: Whether or not to ignore the command's exit_status. """ user_host = self._formatter.format_host_name(self._settings) - job.run( - 'scp %s:%s %s' % (user_host, remote_path, local_path), - ignore_status=ignore_status) + job.run('scp %s:%s %s' % (user_host, remote_path, local_path), + ignore_status=ignore_status) def find_free_port(self, interface_name='localhost'): """Find a unused port on the remote host. diff --git a/acts/framework/acts/keys.py b/acts/framework/acts/keys.py index eae9a3bb14..ae8945682c 100644 --- a/acts/framework/acts/keys.py +++ b/acts/framework/acts/keys.py @@ -57,6 +57,7 @@ class Config(enum.Enum): key_arduino_wifi_dongle = 'ArduinoWifiDongle' key_packet_capture = 'PacketCapture' key_pdu = 'PduDevice' + key_openwrt_ap = 'OpenWrtAP' # Internal keys, used internally, not exposed to user's config files. ikey_user_param = 'user_params' ikey_testbed_name = 'testbed_name' @@ -81,6 +82,7 @@ class Config(enum.Enum): m_key_arduino_wifi_dongle = 'arduino_wifi_dongle' m_key_packet_capture = 'packet_capture' m_key_pdu = 'pdu' + m_key_openwrt_ap = 'openwrt_ap' # A list of keys whose values in configs should not be passed to test # classes without unpacking first. @@ -104,7 +106,8 @@ class Config(enum.Enum): key_chameleon_device, key_arduino_wifi_dongle, key_packet_capture, - key_pdu + key_pdu, + key_openwrt_ap, ] # Keys that are file or folder paths. diff --git a/acts/framework/acts/libs/logging/log_stream.py b/acts/framework/acts/libs/logging/log_stream.py index fbf0474d13..1003a7da53 100644 --- a/acts/framework/acts/libs/logging/log_stream.py +++ b/acts/framework/acts/libs/logging/log_stream.py @@ -235,7 +235,8 @@ class _LogStream(object): # Add a NullHandler to suppress unwanted console output self.logger.addHandler(_null_handler) self.logger.propagate = False - self.base_path = base_path or logging.log_path + self.base_path = base_path or getattr(logging, 'log_path', + '/tmp/acts_logs') self.subcontext = subcontext context.TestContext.add_base_output_path(self.logger.name, self.base_path) context.TestContext.add_subcontext(self.logger.name, self.subcontext) diff --git a/acts/framework/acts/libs/proc/process.py b/acts/framework/acts/libs/proc/process.py index 6d444a41f6..3a49cfc8c9 100644 --- a/acts/framework/acts/libs/proc/process.py +++ b/acts/framework/acts/libs/proc/process.py @@ -159,8 +159,7 @@ class Process(object): if _on_windows: subprocess.check_call('taskkill /F /T /PID %s' % self._process.pid) else: - pgid = os.getpgid(self._process.pid) - os.killpg(pgid, signal.SIGKILL) + self.signal(signal.SIGKILL) def wait(self, kill_timeout=60.0): """Waits for the process to finish execution. @@ -186,6 +185,18 @@ class Process(object): self._join_threads() self._started = False + def signal(self, sig): + """Sends a signal to the process. + + Args: + sig: The signal to be sent. + """ + if _on_windows: + raise ProcessError('Unable to call Process.signal on windows.') + + pgid = os.getpgid(self._process.pid) + os.killpg(pgid, sig) + def stop(self): """Stops the process. diff --git a/acts/framework/acts/test_decorators.py b/acts/framework/acts/test_decorators.py index bf407d1652..937c6db58f 100644 --- a/acts/framework/acts/test_decorators.py +++ b/acts/framework/acts/test_decorators.py @@ -58,7 +58,9 @@ def repeated_test(num_passes, acceptable_failures=0, return the median, or gather and return standard deviation values. This decorator should be used on test cases, and should not be used on - static or class methods. + static or class methods. The test case must take in an additional argument, + `attempt_number`, which returns the current attempt number, starting from + 1. Note that any TestSignal intended to abort or skip the test will take abort or skip immediately. @@ -84,7 +86,7 @@ def repeated_test(num_passes, acceptable_failures=0, test_signals_received = [] for i in range(num_passes + acceptable_failures): try: - func(self) + func(self, i + 1) except (signals.TestFailure, signals.TestError, AssertionError) as signal: test_signals_received.append(signal) diff --git a/acts/framework/acts/test_utils/abstract_devices/wlan_device.py b/acts/framework/acts/test_utils/abstract_devices/wlan_device.py index 16da4ae41e..01a9231949 100644 --- a/acts/framework/acts/test_utils/abstract_devices/wlan_device.py +++ b/acts/framework/acts/test_utils/abstract_devices/wlan_device.py @@ -43,6 +43,9 @@ def create_wlan_device(hardware_device): type(hardware_device)) +FUCHSIA_VALID_SECURITY_TYPES = {"none", "wep", "wpa", "wpa2", "wpa3"} + + class WlanDevice(object): """Class representing a generic WLAN device. @@ -55,6 +58,7 @@ class WlanDevice(object): def __init__(self, device): self.device = device self.log = logging + self.identifier = None def wifi_toggle_state(self, state): """Base generic WLAN interface. Only called if not overridden by @@ -135,7 +139,23 @@ class WlanDevice(object): raise NotImplementedError("{} must be defined.".format( inspect.currentframe().f_code.co_name)) - def ping(self, dest_ip, count=3, interval=1000, timeout=1000, size=25): + def can_ping(self, + dest_ip, + count=3, + interval=1000, + timeout=1000, + size=25, + additional_ping_params=None): + raise NotImplementedError("{} must be defined.".format( + inspect.currentframe().f_code.co_name)) + + def ping(self, + dest_ip, + count=3, + interval=1000, + timeout=1000, + size=25, + additional_ping_params=None): raise NotImplementedError("{} must be defined.".format( inspect.currentframe().f_code.co_name)) @@ -143,6 +163,14 @@ class WlanDevice(object): raise NotImplementedError("{} must be defined.".format( inspect.currentframe().f_code.co_name)) + def save_network(self, ssid): + raise NotImplementedError("{} must be defined.".format( + inspect.currentframe().f_code.co_name)) + + def clear_saved_networks(self): + raise NotImplementedError("{} must be defined.".format( + inspect.currentframe().f_code.co_name)) + class AndroidWlanDevice(WlanDevice): """Class wrapper for an Android WLAN device. @@ -155,6 +183,7 @@ class AndroidWlanDevice(WlanDevice): """ def __init__(self, android_device): super().__init__(android_device) + self.identifier = android_device.serial def wifi_toggle_state(self, state): awutils.wifi_toggle_state(self.device, state) @@ -226,15 +255,30 @@ class AndroidWlanDevice(WlanDevice): return 'BSSID' in wifi_info and wifi_info['SSID'] == ssid return 'BSSID' in wifi_info - def ping(self, dest_ip, count=3, interval=1000, timeout=1000, size=25): + def can_ping(self, + dest_ip, + count=3, + interval=1000, + timeout=1000, + size=25, + additional_ping_params=None): return adb_shell_ping(self.device, dest_ip=dest_ip, count=count, timeout=timeout) + def ping(self, dest_ip, count=3, interval=1000, timeout=1000, size=25): + pass + def hard_power_cycle(self, pdus): pass + def save_network(self, ssid): + pass + + def clear_saved_networks(self): + pass + class FuchsiaWlanDevice(WlanDevice): """Class wrapper for an Fuchsia WLAN device. @@ -247,6 +291,7 @@ class FuchsiaWlanDevice(WlanDevice): """ def __init__(self, fuchsia_device): super().__init__(fuchsia_device) + self.identifier = fuchsia_device.ip def wifi_toggle_state(self, state): """Stub for Fuchsia implementation.""" @@ -302,13 +347,34 @@ class FuchsiaWlanDevice(WlanDevice): def status(self): return self.device.wlan_lib.wlanStatus() - def ping(self, dest_ip, count=3, interval=1000, timeout=1000, size=25): - ping_result = self.device.ping(dest_ip, - count=count, - interval=interval, - timeout=timeout, - size=size) - return ping_result['status'] + def can_ping(self, + dest_ip, + count=3, + interval=1000, + timeout=1000, + size=25, + additional_ping_params=None): + return self.device.can_ping( + dest_ip, + count=count, + interval=interval, + timeout=timeout, + size=size, + additional_ping_params=additional_ping_params) + + def ping(self, + dest_ip, + count=3, + interval=1000, + timeout=1000, + size=25, + additional_ping_params=None): + return self.device.ping(dest_ip, + count=count, + interval=interval, + timeout=timeout, + size=size, + additional_ping_params=additional_ping_params) def get_wlan_interface_id_list(self): """Function to list available WLAN interfaces. @@ -345,7 +411,7 @@ class FuchsiaWlanDevice(WlanDevice): def is_connected(self, ssid=None): """ Determines if wlan_device is connected to wlan network. - + Args: ssid (optional): string, to check if device is connect to a specific network. @@ -373,3 +439,18 @@ class FuchsiaWlanDevice(WlanDevice): def hard_power_cycle(self, pdus): self.device.reboot(reboot_type='hard', testbed_pdus=pdus) + + def save_network(self, target_ssid, security_type=None, target_pwd=None): + if security_type and security_type not in FUCHSIA_VALID_SECURITY_TYPES: + raise TypeError('Invalid security type: %s' % security_type) + response = self.device.wlan_policy_lib.wlanSaveNetwork( + target_ssid, security_type, target_pwd=target_pwd) + if response.get('error'): + raise EnvironmentError('Failed to save network %s. Err: %s' % + (target_ssid, response.get('error'))) + + def clear_saved_networks(self): + response = self.device.wlan_policy_lib.wlanRemoveAllNetworks() + if response.get('error'): + raise EnvironmentError('Failed to clear saved networks: %s' % + response.get('error')) diff --git a/acts/framework/acts/test_utils/abstract_devices/wlan_device_lib/AbstractDeviceWlanDeviceBaseTest.py b/acts/framework/acts/test_utils/abstract_devices/wlan_device_lib/AbstractDeviceWlanDeviceBaseTest.py index b18c4ec8a0..6dfa376321 100644 --- a/acts/framework/acts/test_utils/abstract_devices/wlan_device_lib/AbstractDeviceWlanDeviceBaseTest.py +++ b/acts/framework/acts/test_utils/abstract_devices/wlan_device_lib/AbstractDeviceWlanDeviceBaseTest.py @@ -21,8 +21,11 @@ class AbstractDeviceWlanDeviceBaseTest(WifiBaseTest): super().setup_class() def on_fail(self, test_name, begin_time): - self.dut.take_bug_report(test_name, begin_time) - self.dut.get_log(test_name, begin_time) + try: + self.dut.take_bug_report(test_name, begin_time) + self.dut.get_log(test_name, begin_time) + except Exception: + pass try: if self.dut.device.hard_reboot_on_fail: diff --git a/acts/framework/acts/test_utils/bt/A2dpBaseTest.py b/acts/framework/acts/test_utils/bt/A2dpBaseTest.py index 1b7afa890a..bcc5bdbf1e 100644 --- a/acts/framework/acts/test_utils/bt/A2dpBaseTest.py +++ b/acts/framework/acts/test_utils/bt/A2dpBaseTest.py @@ -27,6 +27,7 @@ from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest PHONE_MUSIC_FILE_DIRECTORY = '/sdcard/Music' INIT_ATTEN = 0 +WAIT_TIME = 1 class A2dpBaseTest(BluetoothBaseTest): @@ -124,7 +125,7 @@ class A2dpBaseTest(BluetoothBaseTest): self.log.info('Play and record audio for {} second'.format(duration)) self.media.play() self.audio_device.start() - time.sleep(duration) + time.sleep(duration + WAIT_TIME) audio_captured = self.audio_device.stop() self.media.stop() self.log.info('Audio play and record stopped') diff --git a/acts/framework/acts/test_utils/bt/BtInterferenceBaseTest.py b/acts/framework/acts/test_utils/bt/BtInterferenceBaseTest.py new file mode 100644 index 0000000000..5882bc63b5 --- /dev/null +++ b/acts/framework/acts/test_utils/bt/BtInterferenceBaseTest.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +"""Stream music through connected device from phone across different +attenuations.""" + +import json +import math +import time +import acts.controllers.iperf_client as ipc +import acts.controllers.iperf_server as ipf +import acts.test_utils.bt.bt_test_utils as btutils +from acts import asserts +from acts.test_utils.bt.A2dpBaseTest import A2dpBaseTest +from acts.test_utils.bt.loggers import bluetooth_metric_logger as log +from acts.test_utils.wifi import wifi_performance_test_utils as wpeutils +from acts.test_utils.wifi import wifi_power_test_utils as wputils +from acts.test_utils.wifi import wifi_test_utils as wutils +from acts.test_utils.power.PowerBaseTest import ObjNew + +MAX_ATTENUATION = 95 +TEMP_FILE = '/sdcard/Download/tmp.log' +IPERF_CLIENT_ERROR = 'the client has unexpectedly closed the connection' + + +def setup_ap_connection(dut, network, ap, bandwidth=20): + """Setup AP and connect DUT to it. + + Args: + dut: the android device to connect and run traffic + network: the network config for the AP to be setup + ap: access point object + bandwidth: bandwidth of the WiFi network to be setup + Returns: + self.brconfigs: dict for bridge interface configs + """ + wutils.wifi_toggle_state(dut, True) + brconfigs = wputils.ap_setup(ap, network, bandwidth=bandwidth) + wutils.wifi_connect(dut, network, num_of_tries=3) + return brconfigs + + +def start_iperf_client(traffic_pair_obj, duration): + """Setup iperf traffic for TCP downlink. + Args: + traffic_pair_obj: obj to contain info on traffic pair + duration: duration of iperf traffic to run + """ + # Construct the iperf command based on the test params + iperf_cmd = 'iperf3 -c {} -i 1 -t {} -p {} -J -R > {}'.format( + traffic_pair_obj.server_address, duration, + traffic_pair_obj.iperf_server.port, TEMP_FILE) + # Start IPERF client + traffic_pair_obj.dut.adb.shell_nb(iperf_cmd) + + +def unpack_custom_file(file): + """Unpack the json file to . + + Args: + file: custom json file. + """ + with open(file, 'r') as f: + params = json.load(f) + return params + + +def get_iperf_results(iperf_server_obj): + """Get the iperf results and process. + + Args: + iperf_server_obj: the IperfServer object + Returns: + throughput: the average throughput during tests. + """ + # Get IPERF results and add this to the plot title + iperf_file = iperf_server_obj.log_files[-1] + try: + iperf_result = ipf.IPerfResult(iperf_file) + # Compute the throughput in Mbit/s + if iperf_result.error == IPERF_CLIENT_ERROR: + rates = [] + for item in iperf_result.result['intervals']: + rates.append(item['sum']['bits_per_second'] / 8 / 1024 / 1024) + throughput = ((math.fsum(rates) / len(rates))) * 8 * (1.024**2) + else: + throughput = (math.fsum(iperf_result.instantaneous_rates) / len( + iperf_result.instantaneous_rates)) * 8 * (1.024**2) + except (ValueError, TypeError): + throughput = 0 + return throughput + + +class BtInterferenceBaseTest(A2dpBaseTest): + def __init__(self, configs): + super().__init__(configs) + self.bt_logger = log.BluetoothMetricLogger.for_test_case() + self.start_time = time.time() + req_params = [ + 'attenuation_vector', 'wifi_networks', 'codecs', 'custom_files', + 'audio_params' + ] + self.unpack_userparams(req_params) + for file in self.custom_files: + if 'static_interference' in file: + self.static_wifi_interference = unpack_custom_file(file) + elif 'dynamic_interference' in file: + self.dynamic_wifi_interference = unpack_custom_file(file) + + def setup_class(self): + super().setup_class() + # Build object to store all necessary information for each pair of wifi + # interference setup: phone, ap, network, channel, iperf server port/ip + # object and bridge interface configs + if len(self.android_devices) < 5 or len(self.attenuators) < 4: + self.log.error('Need a 4 channel attenuator and 5 android phones' + 'please update the config file') + self.wifi_int_pairs = [] + for i in range(len(self.attenuators) - 1): + tmp_dict = { + 'dut': self.android_devices[i + 1], + 'ap': self.access_points[i], + 'network': self.wifi_networks[i], + 'channel': self.wifi_networks[i]['channel'], + 'iperf_server': self.iperf_servers[i], + 'attenuator': self.attenuators[i + 1], + 'ether_int': self.packet_senders[i], + 'iperf_client': + ipc.IPerfClientOverAdb(self.android_devices[i + 1]) + } + tmp_obj = ObjNew(**tmp_dict) + self.wifi_int_pairs.append(tmp_obj) + ##Setup connection between WiFi APs and Phones and get DHCP address + # for the interface + for obj in self.wifi_int_pairs: + brconfigs = setup_ap_connection(obj.dut, obj.network, obj.ap) + iperf_server_address = wputils.wait_for_dhcp( + obj.ether_int.interface) + setattr(obj, 'server_address', iperf_server_address) + setattr(obj, 'brconfigs', brconfigs) + obj.attenuator.set_atten(MAX_ATTENUATION) + # Enable BQR on master and slave Android device + btutils.enable_bqr(self.dut) + btutils.enable_bqr(self.bt_device_controller) + + def teardown_class(self): + super().teardown_class() + for obj in self.wifi_int_pairs: + obj.ap.bridge.teardown(obj.brconfigs) + self.log.info('Stop IPERF server at port {}'.format( + obj.iperf_server.port)) + obj.iperf_server.stop() + self.log.info('Stop IPERF process on {}'.format(obj.dut.serial)) + obj.dut.adb.shell('pkill -9 iperf3') + #only for glinux machine + # wputils.bring_down_interface(obj.ether_int.interface) + obj.attenuator.set_atten(MAX_ATTENUATION) + obj.ap.close() + + def teardown_test(self): + + super().teardown_test() + + for obj in self.wifi_int_pairs: + obj.attenuator.set_atten(MAX_ATTENUATION) + + def play_and_record_audio(self, duration, queue): + """Play and record audio for a set duration. + + Args: + duration: duration in seconds for music playing + que: multiprocess que to store the return value of this function + Returns: + audio_captured: captured audio file path + """ + + self.log.info('Play and record audio for {} second'.format(duration)) + self.media.play() + self.audio_device.start() + time.sleep(duration) + audio_captured = self.audio_device.stop() + self.media.stop() + self.log.info('Audio play and record stopped') + asserts.assert_true(audio_captured, 'Audio not recorded') + queue.put(audio_captured) + + def locate_interference_pair_by_channel(self, interference_channels): + """Function to find which attenautor to set based on channel info + Args: + interference_channels: list of interference channels + Return: + interference_pair_indices: list of indices for interference pair + in self.wifi_int_pairs + """ + interference_pair_indices = [] + for chan in interference_channels: + for i in range(len(self.wifi_int_pairs)): + if self.wifi_int_pairs[i].channel == chan: + interference_pair_indices.append(i) + return interference_pair_indices + + def get_interference_rssi(self): + """Function to read wifi interference RSSI level.""" + + bssids = [] + self.interference_rssi = [] + wutils.wifi_toggle_state(self.android_devices[0], True) + for item in self.wifi_int_pairs: + ssid = item.network['SSID'] + bssid = item.ap.get_bssid_from_ssid(ssid, '2g') + bssids.append(bssid) + interference_rssi_dict = { + "ssid": ssid, + "bssid": bssid, + "chan": item.channel, + "rssi": 0 + } + self.interference_rssi.append(interference_rssi_dict) + scaned_rssi = wpeutils.get_scan_rssi(self.android_devices[0], + bssids, + num_measurements=2) + for item in self.interference_rssi: + item['rssi'] = scaned_rssi[item['bssid']]['mean'] + self.log.info('Interference RSSI at channel {} is {} dBm'.format( + item['chan'], item['rssi'])) + wutils.wifi_toggle_state(self.android_devices[0], False) diff --git a/acts/framework/acts/test_utils/bt/BtSarBaseTest.py b/acts/framework/acts/test_utils/bt/BtSarBaseTest.py index 77ef27e7e7..509140778b 100644 --- a/acts/framework/acts/test_utils/bt/BtSarBaseTest.py +++ b/acts/framework/acts/test_utils/bt/BtSarBaseTest.py @@ -24,6 +24,7 @@ from acts import asserts from acts.libs.proc import job from acts.base_test import BaseTestClass +from acts.metrics.loggers.blackbox import BlackboxMetricLogger from acts.test_utils.bt.bt_power_test_utils import MediaControl from acts.test_utils.bt.ble_performance_test_utils import run_ble_throughput_and_read_rssi from acts.test_utils.abstract_devices.bluetooth_handsfree_abstract_device import BluetoothHandsfreeAbstractDeviceFactory as bt_factory @@ -37,6 +38,8 @@ FORCE_SAR_ADB_COMMAND = ('am broadcast -n' 'com.google.android.apps.scone/.coex.TestReceiver -a ' 'com.google.android.apps.scone.coex.SIMULATE_STATE ') +SLEEP_DURATION = 2 + DEFAULT_DURATION = 5 DEFAULT_MAX_ERROR_THRESHOLD = 2 DEFAULT_AGG_MAX_ERROR_THRESHOLD = 2 @@ -56,6 +59,8 @@ class BtSarBaseTest(BaseTestClass): '/vendor/etc/bluetooth_power_limits.csv', '/data/vendor/radio/bluetooth_power_limits.csv' ] + self.sar_test_result = BlackboxMetricLogger.for_test_case( + metric_name='pass') self.sar_file_name = os.path.basename(self.power_file_paths[0]) self.power_column = 'BluetoothPower' self.REG_DOMAIN_DICT = { @@ -78,22 +83,25 @@ class BtSarBaseTest(BaseTestClass): 'bt_sar_test_params was not found in the config file.') self.user_params.update(self.test_params) - req_params = ['bt_devices', 'calibration_params'] + req_params = ['bt_devices', 'calibration_params', 'custom_files'] self.unpack_userparams( req_params, country_code='us', duration=DEFAULT_DURATION, - custom_sar_path=None, - music_files=None, sort_order=None, max_error_threshold=DEFAULT_MAX_ERROR_THRESHOLD, agg_error_threshold=DEFAULT_AGG_MAX_ERROR_THRESHOLD, tpc_threshold=[2, 8], - ) + sar_margin={ + 'BDR': 0, + 'EDR': 0, + 'BLE': 0 + }) self.attenuator = self.attenuators[0] self.dut = self.android_devices[0] + for key in self.REG_DOMAIN_DICT.keys(): if self.country_code.lower() in key: self.reg_domain = self.REG_DOMAIN_DICT[key] @@ -103,21 +111,32 @@ class BtSarBaseTest(BaseTestClass): if 'Error' not in self.dut.adb.shell('bluetooth_sar_test -r'): #Flag for SAR version 2 self.sar_version_2 = True - phone_sku = self.dut.adb.shell('getprop ro.boot.hardware.sku') self.power_column = 'BluetoothEDRPower' self.power_file_paths[0] = os.path.join( os.path.dirname(self.power_file_paths[0]), - 'bluetooth_power_limits_{}_{}.csv'.format( - phone_sku, self.reg_domain)) + 'bluetooth_power_limits_{}.csv'.format(self.reg_domain)) self.sar_file_name = os.path.basename(self.power_file_paths[0]) + if self.sar_version_2: + custom_file_suffix = 'version2' + else: + custom_file_suffix = 'version1' + + for file in self.custom_files: + if 'custom_sar_table_{}.csv'.format(custom_file_suffix) in file: + self.custom_sar_path = file + break + else: + raise RuntimeError('Custom Sar File is missing') + self.sar_file_path = self.power_file_paths[0] self.atten_min = 0 self.atten_max = int(self.attenuator.get_max_atten()) - # Initializing media controller - if self.music_files: - music_src = self.music_files[0] + # Get music file and push it to the phone and initialize Media controller + music_files = self.user_params.get('music_files', []) + if music_files: + music_src = music_files[0] music_dest = PHONE_MUSIC_FILE_DIRECTORY success = self.dut.push_system_file(music_src, music_dest) if success: @@ -145,6 +164,9 @@ class BtSarBaseTest(BaseTestClass): def setup_test(self): super().setup_test() + #Reset SAR test result to 0 before every test + self.sar_test_result.metric_value = 0 + # Starting BT on the master self.dut.droid.bluetoothFactoryReset() bt_utils.enable_bluetooth(self.dut.droid, self.dut.ed) @@ -197,24 +219,74 @@ class BtSarBaseTest(BaseTestClass): Args: df: Processed SAR table sweep results """ - self.plot.add_line(df.index, - df['expected_tx_power'], - legend='expected', - marker='circle') - self.plot.add_line(df.index, - df['measured_tx_power'], - legend='measured', - marker='circle') - self.plot.add_line(df.index, - df['delta'], - legend='delta', - marker='circle') - - results_file_path = os.path.join( - self.log_path, '{}.html'.format(self.current_test_name)) + self.plot.add_line( + df.index, + df['expected_tx_power'], + legend='expected', + marker='circle') + self.plot.add_line( + df.index, + df['measured_tx_power'], + legend='measured', + marker='circle') + self.plot.add_line( + df.index, df['delta'], legend='delta', marker='circle') + + results_file_path = os.path.join(self.log_path, '{}.html'.format( + self.current_test_name)) self.plot.generate_figure() wifi_utils.BokehFigure.save_figures([self.plot], results_file_path) + def sweep_power_cap(self): + sar_df = self.bt_sar_df + sar_df['BDR_power_cap'] = -128 + sar_df['EDR_power_cap'] = -128 + sar_df['BLE_power_cap'] = -128 + + if self.sar_version_2: + power_column_dict = { + 'BDR': 'BluetoothBDRPower', + 'EDR': 'BluetoothEDRPower', + 'BLE': 'BluetoothLEPower' + } + else: + power_column_dict = {'EDR': self.power_column} + + power_cap_error = False + + for type, column_name in power_column_dict.items(): + + self.log.info("Performing sanity test on {}".format(type)) + # Iterating through the BT SAR scenarios + for scenario in range(0, self.bt_sar_df.shape[0]): + # Reading BT SAR table row into dict + read_scenario = sar_df.loc[scenario].to_dict() + start_time = self.dut.adb.shell('date +%s.%m') + time.sleep(SLEEP_DURATION) + + # Setting SAR state to the read BT SAR row + self.set_sar_state(self.dut, read_scenario, self.country_code) + + # Reading device power cap from logcat after forcing SAR State + scenario_power_cap = self.get_current_power_cap( + self.dut, start_time, type=type) + sar_df.loc[scenario, '{}_power_cap'.format( + type)] = scenario_power_cap + self.log.info( + 'scenario: {}, ' + 'sar_power: {}, power_cap:{}'.format( + scenario, sar_df.loc[scenario, column_name], + sar_df.loc[scenario, '{}_power_cap'.format(type)])) + + if not sar_df['{}_power_cap'.format(type)].equals(sar_df[column_name]): + power_cap_error = True + + results_file_path = os.path.join(self.log_path, '{}.csv'.format( + self.current_test_name)) + sar_df.to_csv(results_file_path) + + return power_cap_error + def sweep_table(self, client_ad=None, server_ad=None, @@ -248,11 +320,11 @@ class BtSarBaseTest(BaseTestClass): # Sorts the table if self.sort_order: if self.sort_order.lower() == 'ascending': - sar_df = sar_df.sort_values(by=[self.power_column], - ascending=True) + sar_df = sar_df.sort_values( + by=[self.power_column], ascending=True) else: - sar_df = sar_df.sort_values(by=[self.power_column], - ascending=False) + sar_df = sar_df.sort_values( + by=[self.power_column], ascending=False) sar_df = sar_df.reset_index(drop=True) # Sweeping BT SAR table @@ -261,7 +333,7 @@ class BtSarBaseTest(BaseTestClass): read_scenario = sar_df.loc[scenario].to_dict() start_time = self.dut.adb.shell('date +%s.%m') - time.sleep(1) + time.sleep(SLEEP_DURATION) #Setting SAR State self.set_sar_state(self.dut, read_scenario, self.country_code) @@ -270,10 +342,10 @@ class BtSarBaseTest(BaseTestClass): sar_df.loc[scenario, 'power_cap'] = self.get_current_power_cap( self.dut, start_time, type='BLE') - sar_df.loc[scenario, - 'ble_rssi'] = run_ble_throughput_and_read_rssi( - client_ad, server_ad, client_conn_id, - gatt_server, gatt_callback) + sar_df.loc[ + scenario, 'ble_rssi'] = run_ble_throughput_and_read_rssi( + client_ad, server_ad, client_conn_id, gatt_server, + gatt_callback) self.log.info('scenario:{}, power_cap:{}, ble_rssi:{}'.format( scenario, sar_df.loc[scenario, 'power_cap'], @@ -327,17 +399,15 @@ class BtSarBaseTest(BaseTestClass): self.otp = bt_utils.read_otp(self.dut) #OTP backoff - edr_otp = min(0, float(self.otp['EDR']['10']) / 4.0) - bdr_otp = min(0, float(self.otp['BDR']['10']) / 4.0) - ble_otp = min(0, float(self.otp['BLE']['10']) / 4.0) + edr_otp = min(0, float(self.otp['EDR']['10'])) + bdr_otp = min(0, float(self.otp['BR']['10'])) + ble_otp = min(0, float(self.otp['BLE']['10'])) # EDR TX Power for PL10 - edr_tx_power_pl10 = self.calibration_params['target_power']['EDR'][ - '10'] - edr_otp + edr_tx_power_pl10 = self.calibration_params['target_power']['EDR']['10'] - edr_otp # BDR TX Power for PL10 - bdr_tx_power_pl10 = self.calibration_params['target_power']['BDR'][ - '10'] - bdr_otp + bdr_tx_power_pl10 = self.calibration_params['target_power']['BDR']['10'] - bdr_otp # RSSI being measured is BDR offset = bdr_tx_power_pl10 - edr_tx_power_pl10 @@ -350,8 +420,8 @@ class BtSarBaseTest(BaseTestClass): # Adding a target power column if 'ble_rssi' in sar_df.columns: - sar_df['target_power'] = self.calibration_params[ - 'target_power']['BLE']['10'] - ble_otp + sar_df[ + 'target_power'] = self.calibration_params['target_power']['BLE']['10'] - ble_otp else: sar_df['target_power'] = sar_df['pwlv'].astype(str).map( self.calibration_params['target_power']['EDR']) - edr_otp @@ -364,11 +434,11 @@ class BtSarBaseTest(BaseTestClass): ]].min(axis=1) if hasattr(self, 'pl10_atten'): - sar_df['measured_tx_power'] = sar_df['slave_rssi'] + sar_df[ - 'pathloss'] + self.pl10_atten - offset + sar_df[ + 'measured_tx_power'] = sar_df['slave_rssi'] + sar_df['pathloss'] + self.pl10_atten - offset else: - sar_df['measured_tx_power'] = sar_df['ble_rssi'] + sar_df[ - 'pathloss'] + FIXED_ATTENUATION + sar_df[ + 'measured_tx_power'] = sar_df['ble_rssi'] + sar_df['pathloss'] + FIXED_ATTENUATION else: @@ -384,11 +454,11 @@ class BtSarBaseTest(BaseTestClass): sar_df[ 'expected_tx_power'] = sar_df['ftm_power'] - sar_df['backoff'] - sar_df['measured_tx_power'] = sar_df['slave_rssi'] + sar_df[ - 'pathloss'] + self.pl10_atten + sar_df[ + 'measured_tx_power'] = sar_df['slave_rssi'] + sar_df['pathloss'] + self.pl10_atten - sar_df['delta'] = sar_df['expected_tx_power'] - sar_df[ - 'measured_tx_power'] + sar_df[ + 'delta'] = sar_df['expected_tx_power'] - sar_df['measured_tx_power'] self.log.info('Sweep results processed') @@ -407,21 +477,28 @@ class BtSarBaseTest(BaseTestClass): Args: sar_df: processed BT SAR table """ + if self.sar_version_2: + breach_error_result = ( + sar_df['expected_tx_power'] + self.sar_margin[type] > + sar_df['measured_tx_power']).all() + if not breach_error_result: + asserts.fail('Measured TX power exceeds expected') - # checks for errors at particular points in the sweep - max_error_result = abs( - sar_df['delta']) > self.max_error_threshold[type] - if False in max_error_result: - asserts.fail('Maximum Error Threshold Exceeded') + else: + # checks for errors at particular points in the sweep + max_error_result = abs( + sar_df['delta']) > self.max_error_threshold[type] + if max_error_result: + asserts.fail('Maximum Error Threshold Exceeded') - # checks for error accumulation across the sweep - if sar_df['delta'].sum() > self.agg_error_threshold[type]: - asserts.fail( - 'Aggregate Error Threshold Exceeded. Error: {} Threshold: {}'. - format(sar_df['delta'].sum(), self.agg_error_threshold)) + # checks for error accumulation across the sweep + if sar_df['delta'].sum() > self.agg_error_threshold[type]: + asserts.fail( + 'Aggregate Error Threshold Exceeded. Error: {} Threshold: {}'. + format(sar_df['delta'].sum(), self.agg_error_threshold)) - else: - asserts.explicit_pass('Measured and Expected Power Values in line') + self.sar_test_result.metric_value = 1 + asserts.explicit_pass('Measured and Expected Power Values in line') def set_sar_state(self, ad, signal_dict, country_code='us'): """Sets the SAR state corresponding to the BT SAR signal. @@ -453,16 +530,15 @@ class BtSarBaseTest(BaseTestClass): } if 'BTHotspot' in signal_dict.keys(): - device_state_dict[('BT Tethering', + device_state_dict[('Bluetooth tethering', 'bt_tethering')] = signal_dict['BTHotspot'] enforced_state = {} sar_state_command = FORCE_SAR_ADB_COMMAND for key in device_state_dict: enforced_state[key[0]] = device_state_dict[key] - sar_state_command = '{} --ei {} {}'.format(sar_state_command, - key[1], - device_state_dict[key]) + sar_state_command = '{} --ei {} {}'.format( + sar_state_command, key[1], device_state_dict[key]) if self.sar_version_2: sar_state_command = '{} --es country_iso "{}"'.format( sar_state_command, country_code.lower()) @@ -491,7 +567,7 @@ class BtSarBaseTest(BaseTestClass): stat: the desired BT stat. """ # Waiting for logcat to update - time.sleep(1) + time.sleep(SLEEP_DURATION) bt_adb_log = ad.adb.logcat('-b all -t %s' % begin_time) for line in bt_adb_log.splitlines(): if re.findall(regex, line): @@ -516,7 +592,7 @@ class BtSarBaseTest(BaseTestClass): def get_country_code(self, ad, begin_time): """Returns the enforced regulatory domain since begin_time - Returns the enforced regulatory domain since begin_time by parsing logcat. + Returns enforced regulatory domain since begin_time by parsing logcat. Function should follow a function call to set a country code Args: @@ -584,14 +660,14 @@ class BtSarBaseTest(BaseTestClass): """ device_state_regex = 'updateDeviceState: DeviceState: ([\s*\S+\s]+)' - time.sleep(2) + time.sleep(SLEEP_DURATION) device_state = self.parse_bt_logs(ad, begin_time, device_state_regex) if device_state: return device_state raise ValueError("Couldn't fetch device state") - def read_sar_table(self, ad): + def read_sar_table(self, ad, output_path=''): """Extracts the BT SAR table from the phone. Extracts the BT SAR table from the phone into the android device @@ -599,17 +675,19 @@ class BtSarBaseTest(BaseTestClass): Args: ad: android_device object. - + output_path: path to custom sar table Returns: df : BT SAR table (as pandas DataFrame). """ - output_path = os.path.join(ad.device_log_path, self.sar_file_name) - ad.adb.pull('{} {}'.format(self.sar_file_path, output_path)) - df = pd.read_csv(os.path.join(ad.device_log_path, self.sar_file_name)) + if not output_path: + output_path = os.path.join(ad.device_log_path, self.sar_file_name) + ad.adb.pull('{} {}'.format(self.sar_file_path, output_path)) + + df = pd.read_csv(output_path) self.log.info('BT SAR table read from the phone') return df - def push_table(self, ad, src_path): + def push_table(self, ad, src_path, dest_path=''): """Pushes a BT SAR table to the phone. Pushes a BT SAR table to the android device and reboots the device. @@ -624,10 +702,14 @@ class BtSarBaseTest(BaseTestClass): job.run('cp {} {}'.format(src_path, ad.device_log_path)) #Pushing the file provided in the config - ad.push_system_file(src_path, self.sar_file_path) + if dest_path: + ad.push_system_file(src_path, dest_path) + else: + ad.push_system_file(src_path, self.sar_file_path) self.log.info('BT SAR table pushed') ad.reboot() - self.bt_sar_df = self.read_sar_table(self.dut) + + self.bt_sar_df = self.read_sar_table(self.dut, src_path) def set_PL10_atten_level(self, ad): """Finds the attenuation level at which the phone is at PL10 @@ -646,11 +728,11 @@ class BtSarBaseTest(BaseTestClass): for atten in range(self.atten_min, self.atten_max, BT_SAR_ATTEN_STEP): self.attenuator.set_atten(atten) # Sleep required for BQR to reflect the change in parameters - time.sleep(2) + time.sleep(SLEEP_DURATION) metrics = bt_utils.get_bt_metric(ad) if metrics['pwlv'][ad.serial] == 10: - self.log.info('PL10 located at {}'.format(atten + - BT_SAR_ATTEN_STEP)) + self.log.info( + 'PL10 located at {}'.format(atten + BT_SAR_ATTEN_STEP)) return atten + BT_SAR_ATTEN_STEP self.log.warn( diff --git a/acts/framework/acts/test_utils/bt/bt_test_utils.py b/acts/framework/acts/test_utils/bt/bt_test_utils.py index 7acf6a559c..a7e51bd076 100644 --- a/acts/framework/acts/test_utils/bt/bt_test_utils.py +++ b/acts/framework/acts/test_utils/bt/bt_test_utils.py @@ -229,14 +229,21 @@ def connect_phone_to_headset(android, # If already connected, skip pair and connect attempt. while not connected and (time.time() - start_time < timeout): bonded_info = android.droid.bluetoothGetBondedDevices() + connected_info = android.droid.bluetoothGetConnectedDevices() if headset.mac_address not in [ info["address"] for info in bonded_info ]: # Use SL4A to pair and connect with headset. headset.enter_pairing_mode() android.droid.bluetoothDiscoverAndBond(headset_mac_address) - else: # Device is bonded but not connected + elif headset.mac_address not in [ + info["address"] for info in connected_info + ]: + #Device is bonded but not connected android.droid.bluetoothConnectBonded(headset_mac_address) + else: + #Headset is connected, but A2DP profile is not + android.droid.bluetoothA2dpConnect(headset_mac_address) log.info('Waiting for connection...') time.sleep(connection_check_period) # Check for connection. @@ -244,7 +251,6 @@ def connect_phone_to_headset(android, log.info('Devices connected after pair attempt: %s' % connected) return connected - def connect_pri_to_sec(pri_ad, sec_ad, profiles_set, attempts=2): """Connects pri droid to secondary droid. @@ -676,7 +682,7 @@ def read_otp(ad): ad.adb.shell('svc bluetooth enable') time.sleep(2) otp_dict = { - "BDR": { + "BR": { "10": 0, "9": 0, "8": 0 @@ -693,7 +699,7 @@ def read_otp(ad): } } - otp_regex = '\s+\[\s+PL10:([-]*\d+)\s+PL9:([-]*\d+)*\s+PL8:([-]*\d+)\s+\]' + otp_regex = '\s+\[\s+PL10:\s+(\d+)\s+PL9:\s+(\d+)*\s+PL8:\s+(\d+)\s+\]' for key in otp_dict: bank_list = re.findall("{}{}".format(key, otp_regex), otp_output) @@ -701,7 +707,6 @@ def read_otp(ad): if ('0', '0', '0') != bank_tuple: [otp_dict[key]["10"], otp_dict[key]["9"], otp_dict[key]["8"]] = bank_tuple - return otp_dict @@ -723,8 +728,12 @@ def get_bt_metric(ad_list, duration=1, tag="bt_metric", processed=True): """ # Defining bqr quantitites and their regex to extract - regex_dict = {"pwlv": "PwLv:\s(\S+)", "rssi": "RSSI:\s[-](\d+)"} - metrics_dict = {"rssi": {}, "pwlv": {}} + regex_dict = { + "vsp_txpl": "VSP_TxPL:\s(\S+)", + "pwlv": "PwLv:\s(\S+)", + "rssi": "RSSI:\s[-](\d+)" + } + metrics_dict = {"rssi": {}, "pwlv": {}, "vsp_txpl": {}} # Converting a single android device object to list if not isinstance(ad_list, list): @@ -741,7 +750,7 @@ def get_bt_metric(ad_list, duration=1, tag="bt_metric", processed=True): for ad in ad_list: bt_rssi_log = ad.cat_adb_log(tag, begin_time, end_time) - bqr_tag = "Monitoring , Handle:" + bqr_tag = "Handle:" # Extracting supporting bqr quantities for metric, regex in regex_dict.items(): @@ -754,6 +763,11 @@ def get_bt_metric(ad_list, duration=1, tag="bt_metric", processed=True): bqr_metric.append(m) metrics_dict[metric][ad.serial] = bqr_metric + # Ensures back-compatibility for vsp_txpl enabled DUTs + if metrics_dict["vsp_txpl"][ad.serial]: + metrics_dict["pwlv"][ad.serial] = metrics_dict["vsp_txpl"][ + ad.serial] + # Formatting the raw data metrics_dict["rssi"][ad.serial] = [ (-1) * int(x) for x in metrics_dict["rssi"][ad.serial] @@ -769,8 +783,9 @@ def get_bt_metric(ad_list, duration=1, tag="bt_metric", processed=True): sum(metrics_dict["rssi"][ad.serial]) / len(metrics_dict["rssi"][ad.serial]), 2) # Returns last noted value for power level - metrics_dict["pwlv"][ad.serial] = metrics_dict["pwlv"][ - ad.serial][-1] + metrics_dict["pwlv"][ad.serial] = float( + sum(metrics_dict["pwlv"][ad.serial]) / + len(metrics_dict["pwlv"][ad.serial])) return metrics_dict @@ -797,9 +812,9 @@ def get_bt_rssi(ad, duration=1, processed=True): def enable_bqr( - ad_list, - bqr_interval=10, - bqr_event_mask=15, + ad_list, + bqr_interval=10, + bqr_event_mask=15, ): """Sets up BQR reporting. diff --git a/acts/framework/acts/test_utils/gnss/gnss_test_utils.py b/acts/framework/acts/test_utils/gnss/gnss_test_utils.py index cca5417bee..367bb9907c 100644 --- a/acts/framework/acts/test_utils/gnss/gnss_test_utils.py +++ b/acts/framework/acts/test_utils/gnss/gnss_test_utils.py @@ -33,6 +33,8 @@ from acts.controllers.android_device import DEFAULT_QXDM_LOG_PATH from acts.controllers.android_device import SL4A_APK_NAME from acts.test_utils.wifi import wifi_test_utils as wutils from acts.test_utils.tel import tel_test_utils as tutils +from acts.test_utils.instrumentation.device.command.instrumentation_command_builder import InstrumentationCommandBuilder +from acts.test_utils.instrumentation.device.command.instrumentation_command_builder import InstrumentationTestCommandBuilder from acts.utils import get_current_epoch_time from acts.utils import epoch_to_human_time @@ -599,25 +601,35 @@ def clear_aiding_data_by_gtw_gpstool(ad): time.sleep(10) -def start_gnss_by_gtw_gpstool(ad, state, type="gnss", bgdisplay=False): +def start_gnss_by_gtw_gpstool(ad, + state, + type="gnss", + bgdisplay=False, + freq=0, + lowpower=False, + meas=False): """Start or stop GNSS on GTW_GPSTool. Args: ad: An AndroidDevice object. state: True to start GNSS. False to Stop GNSS. type: Different API for location fix. Use gnss/flp/nmea - bgdisplay: true to run GTW when Display off. - false to not run GTW when Display off. + bgdisplay: true to run GTW when Display off. false to not run GTW when + Display off. + freq: An integer to set location update frequency. + meas: A Boolean to set GNSS measurement registeration. + lowpower: A boolean to set GNSS LowPowerMode. """ - if state and not bgdisplay: - ad.adb.shell("am start -S -n com.android.gpstool/.GPSTool " - "--es mode gps --es type %s" % type) - elif state and bgdisplay: - ad.adb.shell("am start -S -n com.android.gpstool/.GPSTool --es mode " - "gps --es type {} --ez BG {}".format(type, bgdisplay)) + cmd = "am start -S -n com.android.gpstool/.GPSTool --es mode gps" if not state: ad.log.info("Stop %s on GTW_GPSTool." % type) - ad.adb.shell("am broadcast -a com.android.gpstool.stop_gps_action") + cmd = "am broadcast -a com.android.gpstool.stop_gps_action" + else: + options = ("--es type {} --ei freq {} --ez BG {} --ez meas {} --ez " + "lowpower {}").format(type, freq, bgdisplay, meas, lowpower) + cmd = cmd + " " + options + + ad.adb.shell(cmd) time.sleep(3) @@ -638,6 +650,9 @@ def process_gnss_by_gtw_gpstool(ad, criteria, type="gnss", clear_data=True): """ retries = 3 for i in range(retries): + if not ad.is_adb_logcat_on: + ad.start_adb_logcat() + check_adblog_functionality(ad) check_location_runtime_permissions( ad, GNSSTOOL_PACKAGE_NAME, GNSSTOOL_PERMISSIONS) begin_time = get_current_epoch_time() @@ -660,8 +675,6 @@ def process_gnss_by_gtw_gpstool(ad, criteria, type="gnss", clear_data=True): "within %d seconds criteria." % (type.upper(), criteria)) time.sleep(1) - if not ad.is_adb_logcat_on: - ad.start_adb_logcat() check_currrent_focus_app(ad) start_gnss_by_gtw_gpstool(ad, False, type) raise signals.TestFailure("Fail to get %s location fixed within %d " @@ -827,13 +840,11 @@ def process_ttff_by_gtw_gpstool(ad, begin_time, true_position, type="gnss"): "message in logcat. Abort test.") if not ad.is_adb_logcat_on: ad.start_adb_logcat() - logcat_results = ad.search_logcat("write TTFF log", begin_time) + logcat_results = ad.search_logcat("write TTFF log", ttff_loop_time) if logcat_results: + ttff_loop_time = get_current_epoch_time() ttff_log = logcat_results[-1]["log_message"].split() ttff_loop = int(ttff_log[8].split(":")[-1]) - if ttff_loop in ttff_data.keys(): - continue - ttff_loop_time = get_current_epoch_time() ttff_sec = float(ttff_log[11]) if ttff_sec != 0.0: ttff_ant_cn = float(ttff_log[18].strip("]")) @@ -897,6 +908,8 @@ def process_ttff_by_gtw_gpstool(ad, begin_time, true_position, type="gnss"): begin_time) if crash_result: raise signals.TestError("GPSTool crashed. Abort test.") + # wait 10 seconds to avoid logs not writing into logcat yet + time.sleep(10) return ttff_data @@ -1383,3 +1396,62 @@ def check_ttff_pe(ad, ttff_data, ttff_mode, pecriteria): raise signals.TestFailure("GTW_GPSTool didn't process TTFF properly.") ad.log.info("All TTFF %s are within test criteria %f meters.", (ttff_mode, pecriteria)) + + +def check_adblog_functionality(ad): + """Restart adb logcat if system can't write logs into file after checking + adblog file size. + + Args: + ad: An AndroidDevice object. + """ + logcat_path = os.path.join(ad.device_log_path, "adblog_%s_debug.txt" % + ad.serial) + if not os.path.exists(logcat_path): + raise signals.TestError("Logcat file %s does not exist." % logcat_path) + original_log_size = os.path.getsize(logcat_path) + ad.log.debug("Original adblog size is %d" % original_log_size) + time.sleep(.5) + current_log_size = os.path.getsize(logcat_path) + ad.log.debug("Current adblog size is %d" % current_log_size) + if current_log_size == original_log_size: + ad.log.warn("System can't write logs into file. Restart adb " + "logcat process now.") + ad.stop_adb_logcat() + ad.start_adb_logcat() + +def build_instrumentation_call(package, + runner, + test_methods=None, + options=None): + """Build an instrumentation call for the tests + + Args: + package: A string to identify test package. + runner: A string to identify test runner. + test_methods: A dictionary contains {class_name, test_method}. + options: A dictionary constaion {key, value} param for test. + + Returns: + An instrumentation call command. + """ + if test_methods is None: + test_methods = {} + cmd_builder = InstrumentationCommandBuilder() + else: + cmd_builder = InstrumentationTestCommandBuilder() + + if options is None: + options = {} + + cmd_builder.set_manifest_package(package) + cmd_builder.set_runner(runner) + cmd_builder.add_flag("-w") + + for class_name, test_method in test_methods.items(): + cmd_builder.add_test_method(class_name, test_method) + + for option_key, option_value in options.items(): + cmd_builder.add_key_value_param(option_key, option_value) + + return cmd_builder.build() diff --git a/acts/framework/acts/test_utils/gnss/gnss_testlog_utils.py b/acts/framework/acts/test_utils/gnss/gnss_testlog_utils.py index 0792da45c2..54d47d789e 100644 --- a/acts/framework/acts/test_utils/gnss/gnss_testlog_utils.py +++ b/acts/framework/acts/test_utils/gnss/gnss_testlog_utils.py @@ -25,50 +25,84 @@ from acts import logger # GPS API Log Reading Config CONFIG_GPSAPILOG = { 'phone_time': - r'(?P<date>\d+\/\d+\/\d+)\s+(?P<time>\d+:\d+:\d+)\s+' + r'^(?P<date>\d+\/\d+\/\d+)\s+(?P<time>\d+:\d+:\d+)\s+' r'Read:\s+(?P<logsize>\d+)\s+bytes', 'SpaceVehicle': - r'Fix:\s+(?P<Fix>\w+)\s+Type:\s+(?P<Type>\w+)\s+' + r'^Fix:\s+(?P<Fix>\w+)\s+Type:\s+(?P<Type>\w+)\s+' r'SV:\s+(?P<SV>\d+)\s+C\/No:\s+(?P<CNo>\d+\.\d+)\s+' r'Elevation:\s+(?P<Elevation>\d+\.\d+)\s+' r'Azimuth:\s+(?P<Azimuth>\d+\.\d+)\s+' r'Signal:\s+(?P<Signal>\w+)\s+' r'Frequency:\s+(?P<Frequency>\d+\.\d+)\s+' r'EPH:\s+(?P<EPH>\w+)\s+ALM:\s+(?P<ALM>\w+)', + 'SpaceVehicle_wBB': + r'^Fix:\s+(?P<Fix>\w+)\s+Type:\s+(?P<Type>\w+)\s+' + r'SV:\s+(?P<SV>\d+)\s+C\/No:\s+(?P<AntCNo>\d+\.\d+),\s+' + r'(?P<BbCNo>\d+\.\d+)\s+' + r'Elevation:\s+(?P<Elevation>\d+\.\d+)\s+' + r'Azimuth:\s+(?P<Azimuth>\d+\.\d+)\s+' + r'Signal:\s+(?P<Signal>\w+)\s+' + r'Frequency:\s+(?P<Frequency>\d+\.\d+)\s+' + r'EPH:\s+(?P<EPH>\w+)\s+ALM:\s+(?P<ALM>\w+)', 'HistoryAvgTop4CNo': - r'History\s+Avg\s+Top4\s+:\s+(?P<HistoryAvgTop4CNo>\d+\.\d+)', + r'^History\s+Avg\s+Top4\s+:\s+(?P<HistoryAvgTop4CNo>\d+\.\d+)', 'CurrentAvgTop4CNo': - r'Current\s+Avg\s+Top4\s+:\s+(?P<CurrentAvgTop4CNo>\d+\.\d+)', + r'^Current\s+Avg\s+Top4\s+:\s+(?P<CurrentAvgTop4CNo>\d+\.\d+)', 'HistoryAvgCNo': - r'History\s+Avg\s+:\s+(?P<HistoryAvgCNo>\d+\.\d+)', + r'^History\s+Avg\s+:\s+(?P<HistoryAvgCNo>\d+\.\d+)', 'CurrentAvgCNo': - r'Current\s+Avg\s+:\s+(?P<CurrentAvgCNo>\d+\.\d+)', + r'^Current\s+Avg\s+:\s+(?P<CurrentAvgCNo>\d+\.\d+)', + 'AntennaHistoryAvgTop4CNo': + r'^Antenna_History\s+Avg\s+Top4\s+:\s+(?P<AntennaHistoryAvgTop4CNo>\d+\.\d+)', + 'AntennaCurrentAvgTop4CNo': + r'^Antenna_Current\s+Avg\s+Top4\s+:\s+(?P<AntennaCurrentAvgTop4CNo>\d+\.\d+)', + 'AntennaHistoryAvgCNo': + r'^Antenna_History\s+Avg\s+:\s+(?P<AntennaHistoryAvgCNo>\d+\.\d+)', + 'AntennaCurrentAvgCNo': + r'^Antenna_Current\s+Avg\s+:\s+(?P<AntennaCurrentAvgCNo>\d+\.\d+)', + 'BasebandHistoryAvgTop4CNo': + r'^Baseband_History\s+Avg\s+Top4\s+:\s+(?P<BasebandHistoryAvgTop4CNo>\d+\.\d+)', + 'BasebandCurrentAvgTop4CNo': + r'^Baseband_Current\s+Avg\s+Top4\s+:\s+(?P<BasebandCurrentAvgTop4CNo>\d+\.\d+)', + 'BasebandHistoryAvgCNo': + r'^Baseband_History\s+Avg\s+:\s+(?P<BasebandHistoryAvgCNo>\d+\.\d+)', + 'BasebandCurrentAvgCNo': + r'^Baseband_Current\s+Avg\s+:\s+(?P<BasebandCurrentAvgCNo>\d+\.\d+)', 'L5inFix': - r'L5\s+used\s+in\s+fix:\s+(?P<L5inFix>\w+)', + r'^L5\s+used\s+in\s+fix:\s+(?P<L5inFix>\w+)', 'L5EngagingRate': - r'L5\s+engaging\s+rate:\s+(?P<L5EngagingRate>\d+.\d+)%', + r'^L5\s+engaging\s+rate:\s+(?P<L5EngagingRate>\d+.\d+)%', 'Provider': - r'Provider:\s+(?P<Provider>\w+)', + r'^Provider:\s+(?P<Provider>\w+)', 'Latitude': - r'Latitude:\s+(?P<Latitude>-?\d+.\d+)', + r'^Latitude:\s+(?P<Latitude>-?\d+.\d+)', 'Longitude': - r'Longitude:\s+(?P<Longitude>-?\d+.\d+)', + r'^Longitude:\s+(?P<Longitude>-?\d+.\d+)', 'Altitude': - r'Altitude:\s+(?P<Altitude>-?\d+.\d+)', + r'^Altitude:\s+(?P<Altitude>-?\d+.\d+)', 'GNSSTime': - r'Time:\s+(?P<Date>\d+\/\d+\/\d+)\s+' + r'^Time:\s+(?P<Date>\d+\/\d+\/\d+)\s+' r'(?P<Time>\d+:\d+:\d+)', 'Speed': - r'Speed:\s+(?P<Speed>\d+.\d+)', + r'^Speed:\s+(?P<Speed>\d+.\d+)', 'Bearing': - r'Bearing:\s+(?P<Bearing>\d+.\d+)', + r'^Bearing:\s+(?P<Bearing>\d+.\d+)', } # Space Vehicle Statistics Dataframe List +# Handle the pre GPSTool 2.12.24 case LIST_SVSTAT = [ 'HistoryAvgTop4CNo', 'CurrentAvgTop4CNo', 'HistoryAvgCNo', 'CurrentAvgCNo', 'L5inFix', 'L5EngagingRate' ] +# Handle the post GPSTool 2.12.24 case with baseband CNo +LIST_SVSTAT_WBB = [ + 'AntennaHistoryAvgTop4CNo', 'AntennaCurrentAvgTop4CNo', + 'AntennaHistoryAvgCNo', 'AntennaCurrentAvgCNo', + 'BasebandHistoryAvgTop4CNo', 'BasebandCurrentAvgTop4CNo', + 'BasebandHistoryAvgCNo', 'BasebandCurrentAvgCNo', 'L5inFix', + 'L5EngagingRate' +] # Location Fix Info Dataframe List LIST_LOCINFO = [ @@ -83,11 +117,12 @@ CONFIG_GPSTTFFLOG = { r'(?P<start_datetime>\d+\/\d+\/\d+-\d+:\d+:\d+.\d+)\s+' r'(?P<stop_datetime>\d+\/\d+\/\d+-\d+:\d+:\d+.\d+)\s+' r'(?P<ttff>\d+.\d+)\s+' - r'\[Avg Top4 : (?P<avg_top4_cn0>\d+.\d+)\]\s' - r'\[Avg : (?P<avg_cn0>\d+.\d+)\]\s+\[(?P<fix_type>\d+\w+ fix)\]\s+' + r'\[Antenna_Avg Top4 : (?P<ant_avg_top4_cn0>\d+.\d+)\]\s' + r'\[Antenna_Avg : (?P<ant_avg_cn0>\d+.\d+)\]\s' + r'\[Baseband_Avg Top4 : (?P<bb_avg_top4_cn0>\d+.\d+)\]\s' + r'\[Baseband_Avg : (?P<<bb_avg_cn0>\d+.\d+)\]\s+\[(?P<fix_type>\d+\w+ fix)\]\s+' r'\[Satellites used for fix : (?P<satnum_for_fix>\d+)\]' } - LOGPARSE_UTIL_LOGGER = logger.create_logger() @@ -150,7 +185,7 @@ def parse_log_to_df(filename, configs, index_rownum=True): if index_rownum and not parsed_data[key].empty: parsed_data[key].set_index('rownumber', inplace=True) elif parsed_data[key].empty: - LOGPARSE_UTIL_LOGGER.warning( + LOGPARSE_UTIL_LOGGER.debug( 'The parsed dataframe of "%s" is empty.', key) # Return parsed data list @@ -315,24 +350,43 @@ def parse_gpsapilog_to_df_v2(filename): # Add phone_time from timestamp dataframe by row number for key in parsed_data: - if key != 'phone_time': + if (key != 'phone_time') and (not parsed_data[key].empty): parsed_data[key] = pds.merge_asof(parsed_data[key], parsed_data['phone_time'], left_index=True, right_index=True) # Get space vehicle info dataframe - sv_info_df = parsed_data['SpaceVehicle'] + # Handle the pre GPSTool 2.12.24 case + if not parsed_data['SpaceVehicle'].empty: + sv_info_df = parsed_data['SpaceVehicle'] + + # Handle the post GPSTool 2.12.24 case with baseband CNo + elif not parsed_data['SpaceVehicle_wBB'].empty: + sv_info_df = parsed_data['SpaceVehicle_wBB'] # Get space vehicle statistics dataframe - # First merge all dataframe from LIST_SVSTAT[1:], - sv_stat_df = fts.reduce( - lambda item1, item2: pds.merge(item1, item2, on='phone_time'), - [parsed_data[key] for key in LIST_SVSTAT[1:]]) - # Then merge with LIST_SVSTAT[0] - sv_stat_df = pds.merge(sv_stat_df, - parsed_data[LIST_SVSTAT[0]], - on='phone_time') + # Handle the pre GPSTool 2.12.24 case + if not parsed_data['HistoryAvgTop4CNo'].empty: + # First merge all dataframe from LIST_SVSTAT[1:], + sv_stat_df = fts.reduce( + lambda item1, item2: pds.merge(item1, item2, on='phone_time'), + [parsed_data[key] for key in LIST_SVSTAT[1:]]) + # Then merge with LIST_SVSTAT[0] + sv_stat_df = pds.merge(sv_stat_df, + parsed_data[LIST_SVSTAT[0]], + on='phone_time') + + # Handle the post GPSTool 2.12.24 case with baseband CNo + elif not parsed_data['AntennaHistoryAvgTop4CNo'].empty: + # First merge all dataframe from LIST_SVSTAT[1:], + sv_stat_df = fts.reduce( + lambda item1, item2: pds.merge(item1, item2, on='phone_time'), + [parsed_data[key] for key in LIST_SVSTAT_WBB[1:]]) + # Then merge with LIST_SVSTAT[0] + sv_stat_df = pds.merge(sv_stat_df, + parsed_data[LIST_SVSTAT_WBB[0]], + on='phone_time') # Get location fix information dataframe # First merge all dataframe from LIST_LOCINFO[1:], @@ -353,17 +407,53 @@ def parse_gpsapilog_to_df_v2(filename): timestamp_df['logsize'] = timestamp_df['logsize'].astype(int) sv_info_df['SV'] = sv_info_df['SV'].astype(int) - sv_info_df['CNo'] = sv_info_df['CNo'].astype(float) sv_info_df['Elevation'] = sv_info_df['Elevation'].astype(float) sv_info_df['Azimuth'] = sv_info_df['Azimuth'].astype(float) sv_info_df['Frequency'] = sv_info_df['Frequency'].astype(float) - sv_stat_df['CurrentAvgTop4CNo'] = sv_stat_df['CurrentAvgTop4CNo'].astype( - float) - sv_stat_df['CurrentAvgCNo'] = sv_stat_df['CurrentAvgCNo'].astype(float) - sv_stat_df['HistoryAvgTop4CNo'] = sv_stat_df['HistoryAvgTop4CNo'].astype( - float) - sv_stat_df['HistoryAvgCNo'] = sv_stat_df['HistoryAvgCNo'].astype(float) + if 'CNo' in list(sv_info_df.columns): + sv_info_df['CNo'] = sv_info_df['CNo'].astype(float) + sv_info_df['AntCNo'] = sv_info_df['CNo'] + elif 'AntCNo' in list(sv_info_df.columns): + sv_info_df['AntCNo'] = sv_info_df['AntCNo'].astype(float) + sv_info_df['BbCNo'] = sv_info_df['BbCNo'].astype(float) + + if 'CurrentAvgTop4CNo' in list(sv_stat_df.columns): + sv_stat_df['CurrentAvgTop4CNo'] = sv_stat_df[ + 'CurrentAvgTop4CNo'].astype(float) + sv_stat_df['CurrentAvgCNo'] = sv_stat_df['CurrentAvgCNo'].astype(float) + sv_stat_df['HistoryAvgTop4CNo'] = sv_stat_df[ + 'HistoryAvgTop4CNo'].astype(float) + sv_stat_df['HistoryAvgCNo'] = sv_stat_df['HistoryAvgCNo'].astype(float) + sv_stat_df['AntennaCurrentAvgTop4CNo'] = sv_stat_df[ + 'CurrentAvgTop4CNo'] + sv_stat_df['AntennaCurrentAvgCNo'] = sv_stat_df['CurrentAvgCNo'] + sv_stat_df['AntennaHistoryAvgTop4CNo'] = sv_stat_df[ + 'HistoryAvgTop4CNo'] + sv_stat_df['AntennaHistoryAvgCNo'] = sv_stat_df['HistoryAvgCNo'] + sv_stat_df['BasebandCurrentAvgTop4CNo'] = npy.nan + sv_stat_df['BasebandCurrentAvgCNo'] = npy.nan + sv_stat_df['BasebandHistoryAvgTop4CNo'] = npy.nan + sv_stat_df['BasebandHistoryAvgCNo'] = npy.nan + + elif 'AntennaCurrentAvgTop4CNo' in list(sv_stat_df.columns): + sv_stat_df['AntennaCurrentAvgTop4CNo'] = sv_stat_df[ + 'AntennaCurrentAvgTop4CNo'].astype(float) + sv_stat_df['AntennaCurrentAvgCNo'] = sv_stat_df[ + 'AntennaCurrentAvgCNo'].astype(float) + sv_stat_df['AntennaHistoryAvgTop4CNo'] = sv_stat_df[ + 'AntennaHistoryAvgTop4CNo'].astype(float) + sv_stat_df['AntennaHistoryAvgCNo'] = sv_stat_df[ + 'AntennaHistoryAvgCNo'].astype(float) + sv_stat_df['BasebandCurrentAvgTop4CNo'] = sv_stat_df[ + 'BasebandCurrentAvgTop4CNo'].astype(float) + sv_stat_df['BasebandCurrentAvgCNo'] = sv_stat_df[ + 'BasebandCurrentAvgCNo'].astype(float) + sv_stat_df['BasebandHistoryAvgTop4CNo'] = sv_stat_df[ + 'BasebandHistoryAvgTop4CNo'].astype(float) + sv_stat_df['BasebandHistoryAvgCNo'] = sv_stat_df[ + 'BasebandHistoryAvgCNo'].astype(float) + sv_stat_df['L5EngagingRate'] = sv_stat_df['L5EngagingRate'].astype(float) loc_info_df['Latitude'] = loc_info_df['Latitude'].astype(float) diff --git a/acts/framework/acts/test_utils/net/connectivity_const.py b/acts/framework/acts/test_utils/net/connectivity_const.py index 37b8c80255..60d4f67e7c 100644 --- a/acts/framework/acts/test_utils/net/connectivity_const.py +++ b/acts/framework/acts/test_utils/net/connectivity_const.py @@ -66,6 +66,8 @@ MULTIPATH_PREFERENCE_PERFORMANCE = 1 << 2 # Private DNS constants DNS_GOOGLE = "dns.google" +DNS_QUAD9 = "dns.quad9.net" +DNS_CLOUDFLARE = "1dot1dot1dot1.cloudflare-dns.com" PRIVATE_DNS_MODE_OFF = "off" PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic" PRIVATE_DNS_MODE_STRICT = "hostname" @@ -121,6 +123,9 @@ class VpnProfileType(enum.Enum): IPSEC_XAUTH_PSK = 3 IPSEC_XAUTH_RSA = 4 IPSEC_HYBRID_RSA = 5 + IKEV2_IPSEC_USER_PASS = 6 + IKEV2_IPSEC_PSK = 7 + IKEV2_IPSEC_RSA = 8 # Constants for config file @@ -138,3 +143,6 @@ class VpnReqParams(object): cert_password = "cert_password" pptp_mppe = "pptp_mppe" ipsec_server_type = "ipsec_server_type" + wifi_network = "wifi_network" + vpn_identity = "vpn_identity" + vpn_server_hostname = "vpn_server_hostname" diff --git a/acts/framework/acts/test_utils/net/connectivity_test_utils.py b/acts/framework/acts/test_utils/net/connectivity_test_utils.py index 4b7668c315..153630ff7f 100644 --- a/acts/framework/acts/test_utils/net/connectivity_test_utils.py +++ b/acts/framework/acts/test_utils/net/connectivity_test_utils.py @@ -15,53 +15,94 @@ from acts import asserts from acts.test_utils.net import connectivity_const as cconst +from queue import Empty -def start_natt_keepalive(ad, src_ip, src_port, dst_ip, interval = 10): - """ Start NAT-T keep alive on dut """ +def _listen_for_keepalive_event(ad, key, msg, ka_event): + """Listen for keepalive event and return status - ad.log.info("Starting NATT Keepalive") - status = None - - key = ad.droid.connectivityStartNattKeepalive( - interval, src_ip, src_port, dst_ip) - - ad.droid.connectivityNattKeepaliveStartListeningForEvent(key, "Started") + Args: + ad: DUT object + key: keepalive key + msg: Error message + event: Keepalive event type + """ + ad.droid.socketKeepaliveStartListeningForEvent(key, ka_event) try: - event = ad.ed.pop_event("PacketKeepaliveCallback") - status = event["data"]["packetKeepaliveEvent"] + event = ad.ed.pop_event("SocketKeepaliveCallback") + status = event["data"]["socketKeepaliveEvent"] == ka_event except Empty: - msg = "Failed to receive confirmation of starting natt keepalive" asserts.fail(msg) finally: - ad.droid.connectivityNattKeepaliveStopListeningForEvent( - key, "Started") + ad.droid.socketKeepaliveStopListeningForEvent(key, ka_event) + if ka_event != "Started": + ad.droid.removeSocketKeepaliveReceiverKey(key) + if status: + ad.log.info("'%s' keepalive event successful" % ka_event) + return status - if status != "Started": - ad.log.error("Received keepalive status: %s" % status) - ad.droid.connectivityRemovePacketKeepaliveReceiverKey(key) - return None - return key +def start_natt_socket_keepalive(ad, udp_encap, src_ip, dst_ip, interval = 10): + """Start NATT SocketKeepalive on DUT -def stop_natt_keepalive(ad, key): - """ Stop NAT-T keep alive on dut """ + Args: + ad: DUT object + udp_encap: udp_encap socket key + src_ip: IP addr of the client + dst_ip: IP addr of the keepalive server + interval: keepalive time interval + """ + ad.log.info("Starting Natt Socket Keepalive") + key = ad.droid.startNattSocketKeepalive(udp_encap, src_ip, dst_ip, interval) + msg = "Failed to receive confirmation of starting natt socket keeaplive" + status = _listen_for_keepalive_event(ad, key, msg, "Started") + return key if status else None - ad.log.info("Stopping NATT keepalive") - status = False - ad.droid.connectivityStopNattKeepalive(key) +def start_tcp_socket_keepalive(ad, socket, time_interval = 10): + """Start TCP socket keepalive on DUT - ad.droid.connectivityNattKeepaliveStartListeningForEvent(key, "Stopped") - try: - event = ad.ed.pop_event("PacketKeepaliveCallback") - status = event["data"]["packetKeepaliveEvent"] == "Stopped" - except Empty: - msg = "Failed to receive confirmation of stopping natt keepalive" - asserts.fail(msg) - finally: - ad.droid.connectivityNattKeepaliveStopListeningForEvent( - key, "Stopped") + Args: + ad: DUT object + socket: TCP socket key + time_interval: Keepalive time interval + """ + ad.log.info("Starting TCP Socket Keepalive") + key = ad.droid.startTcpSocketKeepalive(socket, time_interval) + msg = "Failed to receive confirmation of starting tcp socket keeaplive" + status = _listen_for_keepalive_event(ad, key, msg, "Started") + return key if status else None - ad.droid.connectivityRemovePacketKeepaliveReceiverKey(key) - return status +def socket_keepalive_error(ad, key): + """Verify Error callback + + Args: + ad: DUT object + key: Keepalive key + """ + ad.log.info("Verify Error callback on keepalive: %s" % key) + msg = "Failed to receive confirmation of Error callback" + return _listen_for_keepalive_event(ad, key, msg, "Error") + +def socket_keepalive_data_received(ad, key): + """Verify OnDataReceived callback + + Args: + ad: DUT object + key: Keepalive key + """ + ad.log.info("Verify OnDataReceived callback on keepalive: %s" % key) + msg = "Failed to receive confirmation of OnDataReceived callback" + return _listen_for_keepalive_event(ad, key, msg, "OnDataReceived") + +def stop_socket_keepalive(ad, key): + """Stop SocketKeepalive on DUT + + Args: + ad: DUT object + key: Keepalive key + """ + ad.log.info("Stopping Socket keepalive: %s" % key) + ad.droid.stopSocketKeepalive(key) + msg = "Failed to receive confirmation of stopping socket keepalive" + return _listen_for_keepalive_event(ad, key, msg, "Stopped") def set_private_dns(ad, dns_mode, hostname=None): """ Set private DNS mode on dut """ diff --git a/acts/framework/acts/test_utils/net/net_test_utils.py b/acts/framework/acts/test_utils/net/net_test_utils.py index db0f90078c..7e6efa5ce6 100644 --- a/acts/framework/acts/test_utils/net/net_test_utils.py +++ b/acts/framework/acts/test_utils/net/net_test_utils.py @@ -14,13 +14,16 @@ # See the License for the specific language governing permissions and # limitations under the License. import logging +import os -from acts.controllers import adb from acts import asserts +from acts import signals +from acts import utils +from acts.controllers import adb from acts.controllers.adb_lib.error import AdbError from acts.logger import epoch_to_log_line_timestamp -from acts.utils import get_current_epoch_time from acts.logger import normalize_log_line_timestamp +from acts.utils import get_current_epoch_time from acts.utils import start_standing_subprocess from acts.utils import stop_standing_subprocess from acts.test_utils.net import connectivity_const as cconst @@ -43,6 +46,9 @@ USB_CHARGE_MODE = "svc usb setFunctions" USB_TETHERING_MODE = "svc usb setFunctions rndis" DEVICE_IP_ADDRESS = "ip address" +GCE_SSH = "gcloud compute ssh " +GCE_SCP = "gcloud compute scp " + def verify_lte_data_and_tethering_supported(ad): """Verify if LTE data is enabled and tethering supported""" @@ -87,6 +93,7 @@ def verify_ping_to_vpn_ip(ad, vpn_ping_addr): """ ping_result = None pkt_loss = "100% packet loss" + logging.info("Pinging: %s" % vpn_ping_addr) try: ping_result = ad.adb.shell("ping -c 3 -W 2 %s" % vpn_ping_addr) except AdbError: @@ -166,6 +173,7 @@ def download_load_certs(ad, vpn_params, vpn_type, vpn_server_addr, vpn_params['client_pkcs_file_name']) local_file_path = os.path.join(log_path, local_cert_name) + logging.info("URL is: %s" % url) try: ret = urllib.request.urlopen(url) with open(local_file_path, "wb") as f: @@ -226,6 +234,50 @@ def generate_legacy_vpn_profile(ad, return vpn_profile +def generate_ikev2_vpn_profile(ad, vpn_params, vpn_type, server_addr, log_path): + """Generate VPN profile for IKEv2 VPN. + + Args: + ad: android device object. + vpn_params: vpn params from config file. + vpn_type: ikev2 vpn type. + server_addr: vpn server addr. + log_path: log path to download cert. + + Returns: + Vpn profile. + """ + vpn_profile = { + VPN_CONST.TYPE: vpn_type.value, + VPN_CONST.SERVER: server_addr, + } + + if vpn_type.name == "IKEV2_IPSEC_USER_PASS": + vpn_profile[VPN_CONST.USER] = vpn_params["vpn_username"] + vpn_profile[VPN_CONST.PWD] = vpn_params["vpn_password"] + vpn_profile[VPN_CONST.IPSEC_ID] = vpn_params["vpn_identity"] + cert_name = download_load_certs( + ad, vpn_params, vpn_type, vpn_params["server_addr"], + "IKEV2_IPSEC_USER_PASS", log_path) + vpn_profile[VPN_CONST.IPSEC_CA_CERT] = cert_name.split('.')[0] + ad.droid.installCertificate( + vpn_profile, cert_name, vpn_params['cert_password']) + elif vpn_type.name == "IKEV2_IPSEC_PSK": + vpn_profile[VPN_CONST.IPSEC_ID] = vpn_params["vpn_identity"] + vpn_profile[VPN_CONST.IPSEC_SECRET] = vpn_params["psk_secret"] + else: + vpn_profile[VPN_CONST.IPSEC_ID] = "%s@%s" % ( + vpn_params["vpn_identity"], server_addr) + logging.info("ID: %s@%s" % (vpn_params["vpn_identity"], server_addr)) + cert_name = download_load_certs( + ad, vpn_params, vpn_type, vpn_params["server_addr"], + "IKEV2_IPSEC_RSA", log_path) + vpn_profile[VPN_CONST.IPSEC_USER_CERT] = cert_name.split('.')[0] + vpn_profile[VPN_CONST.IPSEC_CA_CERT] = cert_name.split('.')[0] + ad.droid.installCertificate( + vpn_profile, cert_name, vpn_params['cert_password']) + + return vpn_profile def start_tcpdump(ad, test_name): """Start tcpdump on all interfaces @@ -289,6 +341,85 @@ def stop_tcpdump(ad, file_name = "tcpdump_%s_%s.pcap" % (ad.serial, test_name) return "%s/%s" % (log_path, file_name) +def start_tcpdump_gce_server(ad, test_name, dest_port, gce): + """ Start tcpdump on gce server + + Args: + ad: android device object + test_name: test case name + dest_port: port to collect tcpdump + gce: dictionary of gce instance + + Returns: + process id and pcap file path from gce server + """ + ad.log.info("Starting tcpdump on gce server") + + # pcap file name + fname = "/tmp/%s_%s_%s_%s" % \ + (test_name, ad.model, ad.serial, + time.strftime('%Y-%m-%d_%H-%M-%S', time.localtime(time.time()))) + + # start tcpdump + tcpdump_cmd = "sudo bash -c \'tcpdump -i %s -w %s.pcap port %s > \ + %s.txt 2>&1 & echo $!\'" % (gce["interface"], fname, dest_port, fname) + gcloud_ssh_cmd = "%s --project=%s --zone=%s %s@%s --command " % \ + (GCE_SSH, gce["project"], gce["zone"], gce["username"], gce["hostname"]) + gce_ssh_cmd = '%s "%s"' % (gcloud_ssh_cmd, tcpdump_cmd) + utils.exe_cmd(gce_ssh_cmd) + + # get process id + ps_cmd = '%s "ps aux | grep tcpdump | grep %s"' % (gcloud_ssh_cmd, fname) + tcpdump_pid = utils.exe_cmd(ps_cmd).decode("utf-8", "ignore").split() + if not tcpdump_pid: + raise signals.TestFailure("Failed to start tcpdump on gce server") + return tcpdump_pid[1], fname + +def stop_tcpdump_gce_server(ad, tcpdump_pid, fname, gce): + """ Stop and pull tcpdump file from gce server + + Args: + ad: android device object + tcpdump_pid: process id for tcpdump file + fname: tcpdump file path + gce: dictionary of gce instance + + Returns: + pcap file from gce server + """ + ad.log.info("Stop and pull pcap file from gce server") + + # stop tcpdump + tcpdump_cmd = "sudo kill %s" % tcpdump_pid + gcloud_ssh_cmd = "%s --project=%s --zone=%s %s@%s --command " % \ + (GCE_SSH, gce["project"], gce["zone"], gce["username"], gce["hostname"]) + gce_ssh_cmd = '%s "%s"' % (gcloud_ssh_cmd, tcpdump_cmd) + utils.exe_cmd(gce_ssh_cmd) + + # verify tcpdump is stopped + ps_cmd = '%s "ps aux | grep tcpdump"' % gcloud_ssh_cmd + res = utils.exe_cmd(ps_cmd).decode("utf-8", "ignore") + if tcpdump_pid in res.split(): + raise signals.TestFailure("Failed to stop tcpdump on gce server") + if not fname: + return None + + # pull pcap file + gcloud_scp_cmd = "%s --project=%s --zone=%s %s@%s:" % \ + (GCE_SCP, gce["project"], gce["zone"], gce["username"], gce["hostname"]) + pull_file = '%s%s.pcap %s/' % (gcloud_scp_cmd, fname, ad.device_log_path) + utils.exe_cmd(pull_file) + if not os.path.exists( + "%s/%s.pcap" % (ad.device_log_path, fname.split('/')[-1])): + raise signals.TestFailure("Failed to pull tcpdump from gce server") + + # delete pcaps + utils.exe_cmd('%s "sudo rm %s.*"' % (gcloud_ssh_cmd, fname)) + + # return pcap file + pcap_file = "%s/%s.pcap" % (ad.device_log_path, fname.split('/')[-1]) + return pcap_file + def is_ipaddress_ipv6(ip_address): """Verify if the given string is a valid IPv6 address. @@ -379,4 +510,3 @@ def wait_for_new_iface(old_ifaces): return new_ifaces.pop() time.sleep(1) asserts.fail("Timeout waiting for tethering interface on host") - diff --git a/acts/framework/acts/test_utils/net/socket_test_utils.py b/acts/framework/acts/test_utils/net/socket_test_utils.py index a8a05fc206..42e4537de3 100644 --- a/acts/framework/acts/test_utils/net/socket_test_utils.py +++ b/acts/framework/acts/test_utils/net/socket_test_utils.py @@ -258,6 +258,18 @@ def close_server_socket(ad, socket): status = ad.droid.closeTcpServerSocket(socket) asserts.assert_true(status, "Failed to socket") +def shutdown_socket(ad, socket): + """ Shutdown socket + + Args: + 1. ad - androidandroid device object + 2. socket - socket key + """ + fd = ad.droid.getFileDescriptorOfSocket(socket) + asserts.assert_true(fd, "Failed to get FileDescriptor key") + status = ad.droid.shutdownFileDescriptor(fd) + asserts.assert_true(status, "Failed to shutdown socket") + def send_recv_data_sockets(client, server, client_sock, server_sock): """ Send data over TCP socket from client to server. Verify that server received the data diff --git a/acts/framework/acts/test_utils/power/PowerBTBaseTest.py b/acts/framework/acts/test_utils/power/PowerBTBaseTest.py index 897982288f..3ea3d80f27 100644 --- a/acts/framework/acts/test_utils/power/PowerBTBaseTest.py +++ b/acts/framework/acts/test_utils/power/PowerBTBaseTest.py @@ -27,7 +27,8 @@ PHONE_MUSIC_FILE_DIRECTORY = '/sdcard/Music' INIT_ATTEN = [0] -def ramp_attenuation(obj_atten, attenuation_target): +def ramp_attenuation(obj_atten, attenuation_target, attenuation_step_max=20, + time_wait_in_between=5 ): """Ramp the attenuation up or down for BT tests. Ramp the attenuation slowly so it won't have dramatic signal drop to affect @@ -36,15 +37,16 @@ def ramp_attenuation(obj_atten, attenuation_target): Args: obj_atten: attenuator object, a single port attenuator attenuation_target: target attenuation level to reach to. + attenuation_step_max: max step for attenuation set + time_wait_in_between: wait time between attenuation changes """ - attenuation_step_max = 20 sign = lambda x: copysign(1, x) attenuation_delta = obj_atten.get_atten() - attenuation_target while abs(attenuation_delta) > attenuation_step_max: attenuation_intermediate = obj_atten.get_atten( ) - sign(attenuation_delta) * attenuation_step_max obj_atten.set_atten(attenuation_intermediate) - time.sleep(5) + time.sleep(time_wait_in_between) attenuation_delta = obj_atten.get_atten() - attenuation_target obj_atten.set_atten(attenuation_target) diff --git a/acts/framework/acts/test_utils/power/PowerBaseTest.py b/acts/framework/acts/test_utils/power/PowerBaseTest.py index 40f2a6b53f..8e3482dacc 100644 --- a/acts/framework/acts/test_utils/power/PowerBaseTest.py +++ b/acts/framework/acts/test_utils/power/PowerBaseTest.py @@ -346,7 +346,6 @@ class PowerBaseTest(base_test.BaseTestClass): self.avg_current = result.average_current plot_utils.monsoon_data_plot(self.mon_info, result) - plot_utils.monsoon_histogram_plot(self.mon_info, result) return result @@ -386,8 +385,6 @@ class PowerBaseTest(base_test.BaseTestClass): Returns: mon_info: Dictionary with the monsoon packet config """ - if self.iperf_duration: - self.mon_duration = self.iperf_duration - 10 mon_info = ObjNew(dut=self.mon, freq=self.mon_freq, duration=self.mon_duration, diff --git a/acts/framework/acts/test_utils/power/PowerGTWGnssBaseTest.py b/acts/framework/acts/test_utils/power/PowerGTWGnssBaseTest.py new file mode 100644 index 0000000000..fe79e80159 --- /dev/null +++ b/acts/framework/acts/test_utils/power/PowerGTWGnssBaseTest.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +# +# Copyright 2020 - The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import time + +from acts import signals +from acts import utils +from acts.test_utils.power.PowerBaseTest import PowerBaseTest +from acts.test_utils.gnss import gnss_test_utils as gutils +from acts.test_utils.wifi import wifi_test_utils as wutils + +DEFAULT_WAIT_TIME = 120 +STANDALONE_WAIT_TIME = 1200 +DPO_NV_VALUE = '15DC' +MDS_TEST_PACKAGE = 'com.google.mdstest' +MDS_RUNNER = 'com.google.mdstest.instrument.ModemConfigInstrumentation' + + +class PowerGTWGnssBaseTest(PowerBaseTest): + """Power GTW Gnss Base test""" + + def setup_class(self): + super().setup_class() + self.ad = self.android_devices[0] + req_params = [ + 'wifi_network', 'test_location', 'qdsp6m_path', + 'calibrate_target' + ] + self.unpack_userparams(req_param_names=req_params) + gutils.disable_xtra_throttle(self.ad) + + def setup_test(self): + super().setup_test() + # Enable GNSS setting for GNSS standalone mode + self.ad.adb.shell('settings put secure location_mode 3') + + def teardown_test(self): + begin_time = utils.get_current_epoch_time() + self.ad.take_bug_report(self.test_name, begin_time) + gutils.get_gnss_qxdm_log(self.ad, self.qdsp6m_path) + + def baseline_test(self): + """Baseline power measurement""" + self.ad.droid.goToSleepNow() + result = self.collect_power_data() + self.ad.log.info('TestResult AVG_Current %.2f' % result.average_current) + + def start_gnss_tracking_with_power_data(self, + mode='default', + is_signal=True, + freq=0, + lowpower=False, + meas=False): + """Start GNSS tracking and collect power metrics. + + Args: + is_signal: default True, False for no Gnss signal test. + freq: an integer to set location update frequency. + lowpower: a boolean to set GNSS Low Power Mode. + mean: a boolean to set GNSS Measurement registeration. + """ + self.ad.adb.shell('settings put secure location_mode 3') + gutils.clear_aiding_data_by_gtw_gpstool(self.ad) + gutils.start_gnss_by_gtw_gpstool(self.ad, True, 'gnss', True, freq, + lowpower, meas) + self.ad.droid.goToSleepNow() + + sv_collecting_time = DEFAULT_WAIT_TIME + if mode == 'standalone': + sv_collecting_time = STANDALONE_WAIT_TIME + self.ad.log.info('Wait %d seconds for %s mode' % + (sv_collecting_time, mode)) + time.sleep(sv_collecting_time) + + result = self.collect_power_data() + self.ad.log.info('TestResult AVG_Current %.2f' % result.average_current) + self.calibrate_avg_current(result) + self.ad.send_keycode('WAKEUP') + gutils.start_gnss_by_gtw_gpstool(self.ad, False, 'gnss') + if is_signal: + gutils.parse_gtw_gpstool_log( + self.ad, self.test_location, type='gnss') + + def calibrate_avg_current(self, monsoon_results): + """Calibrate average current by filtering AP wake up current with target + value. + + Args: + monsoon_result: a MonsoonResult obj. + """ + monsoon_results = [monsoon_results] + calibrate_results = [ + data_point.current * 1000 + for monsoon_result in monsoon_results + for data_point in monsoon_result.get_data_points() + if data_point.current * 1000 < self.calibrate_target + ] + avg_current = sum(calibrate_results) / len(calibrate_results) + self.ad.log.info('TestResult Calibrate_AVG_Current %.2f' % avg_current) + + def enable_DPO(self, enable): + """Enable or disable the DPO option. + + Args: + enable: True or False to enable DPO. + """ + self.ad.log.info('Change DPO to new state: %s.' % enable) + val = '02' if enable else '00' + options = {'request': 'writeNV', 'item': DPO_NV_VALUE, 'data': val} + instrument_cmd = gutils.build_instrumentation_call( + MDS_TEST_PACKAGE, MDS_RUNNER, options=options) + result = self.ad.adb.shell(instrument_cmd) + if 'SUCCESS' not in result: + self.ad.log.info(result) + raise signals.TestFailure('DPO is not able to Turn: %s' % enable) + self.dut_rockbottom() diff --git a/acts/framework/acts/test_utils/power/PowerWiFiBaseTest.py b/acts/framework/acts/test_utils/power/PowerWiFiBaseTest.py index 80b563b292..c72c20cb7f 100644 --- a/acts/framework/acts/test_utils/power/PowerWiFiBaseTest.py +++ b/acts/framework/acts/test_utils/power/PowerWiFiBaseTest.py @@ -21,6 +21,7 @@ from acts.test_utils.power import plot_utils IPERF_DURATION = 'iperf_duration' INITIAL_ATTEN = [0, 0, 90, 90] +IPERF_TAIL = 5 class PowerWiFiBaseTest(PBT.PowerBaseTest): @@ -28,7 +29,6 @@ class PowerWiFiBaseTest(PBT.PowerBaseTest): Inherited from the PowerBaseTest class """ - def setup_class(self): super().setup_class() @@ -48,7 +48,7 @@ class PowerWiFiBaseTest(PBT.PowerBaseTest): if hasattr(self, 'iperf_servers'): self.iperf_server = self.iperf_servers[0] if self.iperf_duration: - self.mon_duration = self.iperf_duration - 10 + self.mon_duration = self.iperf_duration - self.mon_offset - IPERF_TAIL self.create_monsoon_info() def teardown_test(self): @@ -86,7 +86,10 @@ class PowerWiFiBaseTest(PBT.PowerBaseTest): for ap in self.access_points: ap.close() - def setup_ap_connection(self, network, bandwidth=80, connect=True, + def setup_ap_connection(self, + network, + bandwidth=80, + connect=True, ap=None): """Setup AP and connect DUT to it. @@ -101,8 +104,9 @@ class PowerWiFiBaseTest(PBT.PowerBaseTest): wutils.wifi_toggle_state(self.dut, True) if not ap: if hasattr(self, 'access_points'): - self.brconfigs = wputils.ap_setup( - self.access_point, network, bandwidth=bandwidth) + self.brconfigs = wputils.ap_setup(self.access_point, + network, + bandwidth=bandwidth) else: self.brconfigs = wputils.ap_setup(ap, network, bandwidth=bandwidth) if connect: diff --git a/acts/framework/acts/test_utils/power/cellular/cellular_power_base_test.py b/acts/framework/acts/test_utils/power/cellular/cellular_power_base_test.py index 10d93e9d69..1e422f535e 100644 --- a/acts/framework/acts/test_utils/power/cellular/cellular_power_base_test.py +++ b/acts/framework/acts/test_utils/power/cellular/cellular_power_base_test.py @@ -21,11 +21,12 @@ import acts.controllers.cellular_simulator as simulator from acts.controllers.anritsu_lib import md8475_cellular_simulator as anritsu from acts.controllers.rohdeschwarz_lib import cmw500_cellular_simulator as cmw from acts.controllers.rohdeschwarz_lib import cmx500_cellular_simulator as cmx -from acts.test_utils.power.tel_simulations.GsmSimulation import GsmSimulation -from acts.test_utils.power.tel_simulations.LteSimulation import LteSimulation -from acts.test_utils.power.tel_simulations.UmtsSimulation import UmtsSimulation -from acts.test_utils.power.tel_simulations.LteCaSimulation import LteCaSimulation -from acts.test_utils.power.tel_simulations.LteImsSimulation import LteImsSimulation +from acts.controllers.cellular_lib import AndroidCellularDut +from acts.controllers.cellular_lib import GsmSimulation +from acts.controllers.cellular_lib import LteSimulation +from acts.controllers.cellular_lib import UmtsSimulation +from acts.controllers.cellular_lib import LteCaSimulation +from acts.controllers.cellular_lib import LteImsSimulation from acts.test_utils.tel import tel_test_utils as telutils @@ -238,6 +239,14 @@ class PowerCellularLabBaseTest(PBT.PowerBaseTest): return True + def collect_power_data(self): + """ Collect power data using base class method and plot result + histogram. """ + + result = super().collect_power_data() + plot_utils.monsoon_histogram_plot(self.mon_info, result) + return result + def teardown_test(self): """ Executed after every test case, even if it failed or an exception happened. @@ -358,11 +367,11 @@ class PowerCellularLabBaseTest(PBT.PowerBaseTest): """ simulation_dictionary = { - self.PARAM_SIM_TYPE_LTE: LteSimulation, - self.PARAM_SIM_TYPE_UMTS: UmtsSimulation, - self.PARAM_SIM_TYPE_GSM: GsmSimulation, - self.PARAM_SIM_TYPE_LTE_CA: LteCaSimulation, - self.PARAM_SIM_TYPE_LTE_IMS: LteImsSimulation + self.PARAM_SIM_TYPE_LTE: LteSimulation.LteSimulation, + self.PARAM_SIM_TYPE_UMTS: UmtsSimulation.UmtsSimulation, + self.PARAM_SIM_TYPE_GSM: GsmSimulation.GsmSimulation, + self.PARAM_SIM_TYPE_LTE_CA: LteCaSimulation.LteCaSimulation, + self.PARAM_SIM_TYPE_LTE_IMS: LteImsSimulation.LteImsSimulation } if not sim_type in simulation_dictionary: @@ -383,9 +392,11 @@ class PowerCellularLabBaseTest(PBT.PowerBaseTest): if sim_type not in self.calibration_table: self.calibration_table[sim_type] = {} + cellular_dut = AndroidCellularDut.AndroidCellularDut( + self.dut, self.log) # Instantiate a new simulation self.simulation = simulation_class(self.cellular_simulator, self.log, - self.dut, self.test_params, + cellular_dut, self.test_params, self.calibration_table[sim_type]) def ensure_valid_calibration_table(self, calibration_table): diff --git a/acts/framework/acts/test_utils/power/cellular/cellular_traffic_power_test.py b/acts/framework/acts/test_utils/power/cellular/cellular_traffic_power_test.py index 58b62ad7be..9dd4a5e427 100644 --- a/acts/framework/acts/test_utils/power/cellular/cellular_traffic_power_test.py +++ b/acts/framework/acts/test_utils/power/cellular/cellular_traffic_power_test.py @@ -19,6 +19,7 @@ import time import scapy.all as scapy from acts import asserts +from acts import utils from acts.metrics.loggers.blackbox import BlackboxMetricLogger from acts.test_utils.power import IperfHelper as IPH from acts.test_utils.power import plot_utils @@ -281,6 +282,11 @@ class PowerTelTrafficTest(PWCEL.PowerCellularLabBaseTest): self.iperf_server_address = scapy.get_if_addr( self.packet_senders[0].interface) + self.log.info('Testing IP connectivity with ping.') + if not utils.adb_shell_ping( + client_host, count=10, dest_ip=self.iperf_server_address): + raise RuntimeError('Ping between DUT and host failed.') + # Start iPerf traffic iperf_helpers = [] diff --git a/acts/framework/acts/test_utils/tel/anritsu_utils.py b/acts/framework/acts/test_utils/tel/anritsu_utils.py index a06fd99350..4ffdd8b883 100644 --- a/acts/framework/acts/test_utils/tel/anritsu_utils.py +++ b/acts/framework/acts/test_utils/tel/anritsu_utils.py @@ -18,6 +18,7 @@ import time from queue import Empty from datetime import datetime +from acts.controllers.anritsu_lib import band_constants from acts.controllers.anritsu_lib._anritsu_utils import AnritsuUtils from acts.controllers.anritsu_lib.md8475a import BtsNumber from acts.controllers.anritsu_lib.md8475a import BtsNwNameEnable @@ -145,22 +146,6 @@ Fi_CSCF_IPV6_ADDR_IMS = "2001:0:0:1::3" Fi_CSCF_IPV4_ADDR_911 = "192.168.1.12" Fi_CSCF_IPV6_ADDR_911 = "2001:0:0:2::2" -# GSM BAND constants -GSM_BAND_GSM450 = "GSM450" -GSM_BAND_GSM480 = "GSM480" -GSM_BAND_GSM850 = "GSM850" -GSM_BAND_PGSM900 = "P-GSM900" -GSM_BAND_EGSM900 = "E-GSM900" -GSM_BAND_RGSM900 = "R-GSM900" -GSM_BAND_DCS1800 = "DCS1800" -GSM_BAND_PCS1900 = "PCS1900" - -LTE_BAND_2 = 2 -LTE_BAND_4 = 4 -LTE_BAND_12 = 12 -WCDMA_BAND_1 = 1 -WCDMA_BAND_2 = 2 - # Default Cell Parameters DEFAULT_OUTPUT_LEVEL = -30 DEFAULT_1X_OUTPUT_LEVEL = -35 @@ -169,10 +154,10 @@ DEFAULT_LTE_BAND = [2, 4] Fi_LTE_TMO_BAND = [4] Fi_LTE_SPR_BAND = [25] Fi_LTE_USCC_BAND = [12] -Fi_GSM_TMO_BAND = GSM_BAND_PGSM900 +Fi_GSM_TMO_BAND = band_constants.GSM_BAND_PGSM900 DEFAULT_WCDMA_BAND = 1 DEFAULT_WCDMA_PACKET_RATE = BtsPacketRate.WCDMA_DLHSAUTO_REL7_ULHSAUTO -DEFAULT_GSM_BAND = GSM_BAND_GSM850 +DEFAULT_GSM_BAND = band_constants.GSM_BAND_GSM850 #Google Fi CDMA Bands diff --git a/acts/framework/acts/test_utils/tel/tel_data_utils.py b/acts/framework/acts/test_utils/tel/tel_data_utils.py index 5b2f313b3f..3b6207eac6 100644 --- a/acts/framework/acts/test_utils/tel/tel_data_utils.py +++ b/acts/framework/acts/test_utils/tel/tel_data_utils.py @@ -22,11 +22,14 @@ from acts.utils import rand_ascii_str from acts.test_utils.tel.tel_subscription_utils import \ get_subid_from_slot_index from acts.test_utils.tel.tel_subscription_utils import set_subid_for_data +from acts.test_utils.tel.tel_subscription_utils import get_outgoing_message_sub_id +from acts.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA from acts.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING from acts.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_STATE_CHECK from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_FOR_STATE_CHANGE +from acts.test_utils.tel.tel_defines import WAIT_TIME_AFTER_REBOOT from acts.test_utils.tel.tel_subscription_utils import get_default_data_sub_id from acts.test_utils.tel.tel_test_utils import start_youtube_video from acts.test_utils.tel.tel_test_utils import start_wifi_tethering @@ -581,8 +584,8 @@ def browsing_test(log, ad, wifi_ssid=None, pass_threshold_in_mb = 1.0): ad.droid.goToSleepNow() time.sleep(rest_idle_time) ad.log.info("Wake up device.") - ad.droid.wakeLockAcquireBright() - ad.droid.wakeUpNow() + ad.wakeup_screen() + ad.adb.shell("input keyevent 82") time.sleep(3) else: time.sleep(idle_time) @@ -607,3 +610,108 @@ def browsing_test(log, ad, wifi_ssid=None, pass_threshold_in_mb = 1.0): usage = "unknown" ad.log.info("Usage of browsing: %s MB" % usage) return False + +def reboot_test(log, ad, wifi_ssid=None): + """ Reboot test to verify the service availability after reboot. + + Test procedure: + 1. Reboot + 2. Wait WAIT_TIME_AFTER_REBOOT for reboot complete. + 3. Check service state. False will be returned if service state is not "IN_SERVICE". + 4. Check if network is connected. False will be returned if not. + 5. Check if cellular data or Wi-Fi connection is available. False will be returned if not. + 6. Check if internet connection is available. False will be returned if not. + 7. Check if DSDS mode, data sub ID, voice sub ID and message sub ID still keep the same. + + Args: + log: log object. + ad: android object. + wifi_ssid: SSID of Wi-Fi AP for Wi-Fi connection. + + Returns: + True if pass; False if fail. + """ + try: + wifi_connected = False + if wifi_ssid and check_is_wifi_connected(ad.log, ad, wifi_ssid): + wifi_connected = True + + data_subid = get_default_data_sub_id(ad) + voice_subid = get_outgoing_voice_sub_id(ad) + sms_subid = get_outgoing_message_sub_id(ad) + + ad.reboot() + time.sleep(WAIT_TIME_AFTER_REBOOT) + + if not wait_for_state( + get_service_state_by_adb, + "IN_SERVICE", + MAX_WAIT_TIME_FOR_STATE_CHANGE, + WAIT_TIME_BETWEEN_STATE_CHECK, + log, + ad): + ad.log.error("Current service state: %s" % service_state) + return False + + if not ad.droid.connectivityNetworkIsConnected(): + ad.log.error("Network is NOT connected!") + return False + + if wifi_connected: + if not check_is_wifi_connected(ad.log, ad, wifi_ssid): + return False + else: + if not wait_for_cell_data_connection(log, ad, True): + ad.log.error("Failed to enable data connection.") + return False + + if not verify_internet_connection(log, ad): + ad.log.error("Internet connection is not available") + return False + + sim_mode = ad.droid.telephonyGetPhoneCount() + if hasattr(ad, "dsds"): + if sim_mode == 1: + ad.log.error("Phone is in single SIM mode after reboot.") + return False + elif sim_mode == 2: + ad.log.info("Phone keeps being in dual SIM mode after reboot.") + else: + if sim_mode == 1: + ad.log.info("Phone keeps being in single SIM mode after reboot.") + elif sim_mode == 2: + ad.log.error("Phone is in dual SIM mode after reboot.") + return False + + data_subid_after_reboot = get_default_data_sub_id(ad) + if data_subid_after_reboot != data_subid: + ad.log.error( + "Data sub ID changed! (Before reboot: %s; after reboot: %s)", + data_subid, data_subid_after_reboot) + return False + else: + ad.log.info("Data sub ID does not change after reboot.") + + voice_subid_after_reboot = get_outgoing_voice_sub_id(ad) + if voice_subid_after_reboot != voice_subid: + ad.log.error( + "Voice sub ID changed! (Before reboot: %s; after reboot: %s)", + voice_subid, voice_subid_after_reboot) + return False + else: + ad.log.info("Voice sub ID does not change after reboot.") + + sms_subid_after_reboot = get_outgoing_message_sub_id(ad) + if sms_subid_after_reboot != sms_subid: + ad.log.error( + "Message sub ID changed! (Before reboot: %s; after reboot: %s)", + sms_subid, sms_subid_after_reboot) + return False + else: + ad.log.info("Message sub ID does not change after reboot.") + + except Exception as e: + ad.log.error(e) + return False + + return True
\ No newline at end of file diff --git a/acts/framework/acts/test_utils/tel/tel_defines.py b/acts/framework/acts/test_utils/tel/tel_defines.py index ae66a83c3e..bcaf15f6a8 100644 --- a/acts/framework/acts/test_utils/tel/tel_defines.py +++ b/acts/framework/acts/test_utils/tel/tel_defines.py @@ -213,7 +213,10 @@ POWER_LEVEL_OUT_OF_SERVICE = -100 # Callbox Power level which will ensure full service on device POWER_LEVEL_FULL_SERVICE = -20 - +# set a fake time to test time recovering from network +FAKE_DATE_TIME = "010203042019.05" +FAKE_YEAR = "2019" +WAIT_TIME_SYNC_DATE_TIME_FROM_NETWORK = 2 # These are used in phone_number_formatter PHONE_NUMBER_STRING_FORMAT_7_DIGIT = 7 diff --git a/acts/framework/acts/test_utils/tel/tel_test_utils.py b/acts/framework/acts/test_utils/tel/tel_test_utils.py index fbb6917088..3783af200f 100644 --- a/acts/framework/acts/test_utils/tel/tel_test_utils.py +++ b/acts/framework/acts/test_utils/tel/tel_test_utils.py @@ -127,6 +127,7 @@ from acts.test_utils.tel.tel_defines import WAIT_TIME_CHANGE_DATA_SUB_ID from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL from acts.test_utils.tel.tel_defines import WAIT_TIME_LEAVE_VOICE_MAIL from acts.test_utils.tel.tel_defines import WAIT_TIME_REJECT_CALL +from acts.test_utils.tel.tel_defines import WAIT_TIME_SYNC_DATE_TIME_FROM_NETWORK from acts.test_utils.tel.tel_defines import WAIT_TIME_VOICE_MAIL_SERVER_RESPONSE from acts.test_utils.tel.tel_defines import WFC_MODE_DISABLED from acts.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED @@ -179,6 +180,7 @@ from acts.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id from acts.test_utils.tel.tel_subscription_utils import get_incoming_voice_sub_id from acts.test_utils.tel.tel_subscription_utils import get_incoming_message_sub_id from acts.test_utils.tel.tel_subscription_utils import set_subid_for_outgoing_call +from acts.test_utils.tel.tel_subscription_utils import set_incoming_voice_sub_id from acts.test_utils.tel.tel_subscription_utils import set_subid_for_message from acts.test_utils.tel.tel_subscription_utils import get_subid_on_same_network_of_host_ad from acts.test_utils.wifi import wifi_test_utils @@ -3747,6 +3749,7 @@ def active_file_download_task(log, ad, file_name="5MB", method="curl"): } url_map = { "1MB": [ + "http://146.148.91.8/download/1MB.zip", "http://ipv4.download.thinkbroadband.com/1MB.zip" ], "5MB": [ @@ -4943,16 +4946,25 @@ def show_enhanced_4g_lte(ad, sub_id): if capabilities: if "hide_enhanced_4g_lte" in capabilities: result = False - ad.log.info('"Enhanced 4G LTE MODE" is hidden for sub ID %s.', sub_id) - show_enhanced_4g_lte_mode = getattr(ad, "show_enhanced_4g_lte_mode", False) + ad.log.info( + '"Enhanced 4G LTE MODE" is hidden for sub ID %s.', sub_id) + show_enhanced_4g_lte_mode = getattr( + ad, "show_enhanced_4g_lte_mode", False) if show_enhanced_4g_lte_mode in ["true", "True"]: current_voice_sub_id = get_outgoing_voice_sub_id(ad) if sub_id != current_voice_sub_id: set_incoming_voice_sub_id(ad, sub_id) - ad.log.info('Show "Enhanced 4G LTE MODE" forcibly for sub ID %s.', sub_id) - ad.adb.shell("am broadcast -a com.google.android.carrier.action.LOCAL_OVERRIDE -n com.google.android.carrier/.ConfigOverridingReceiver --ez hide_enhanced_4g_lte_bool false") - ad.telephony["subscription"][sub_id]["capabilities"].remove("hide_enhanced_4g_lte") + ad.log.info( + 'Show "Enhanced 4G LTE MODE" forcibly for sub ID %s.', + sub_id) + ad.adb.shell( + "am broadcast \ + -a com.google.android.carrier.action.LOCAL_OVERRIDE \ + -n com.google.android.carrier/.ConfigOverridingReceiver \ + --ez hide_enhanced_4g_lte_bool false") + ad.telephony["subscription"][sub_id]["capabilities"].remove( + "hide_enhanced_4g_lte") if sub_id != current_voice_sub_id: set_incoming_voice_sub_id(ad, current_voice_sub_id) @@ -4963,11 +4975,13 @@ def show_enhanced_4g_lte(ad, sub_id): def toggle_volte(log, ad, new_state=None): """Toggle enable/disable VoLTE for default voice subscription. + Args: ad: Android device object. new_state: VoLTE mode state to set to. True for enable, False for disable. If None, opposite of the current state. + Raises: TelTestUtilsError if platform does not support VoLTE. """ @@ -4980,7 +4994,8 @@ def toggle_volte_for_subscription(log, ad, sub_id, new_state=None): Args: ad: Android device object. - sub_id: subscription ID + sub_id: Optional. If not assigned the default sub ID for voice call will + be used. new_state: VoLTE mode state to set to. True for enable, False for disable. If None, opposite of the current state. @@ -4989,19 +5004,74 @@ def toggle_volte_for_subscription(log, ad, sub_id, new_state=None): if not show_enhanced_4g_lte(ad, sub_id): return False - current_state = ad.droid.imsMmTelIsAdvancedCallingEnabled(sub_id) - if new_state is None: - new_state = not current_state - if new_state != current_state: - ad.log.info("Toggle Enhanced 4G LTE Mode from %s to %s on sub_id %s", current_state, - new_state, sub_id) - ad.droid.imsMmTelSetAdvancedCallingEnabled(sub_id, new_state) - check_state = ad.droid.imsMmTelIsAdvancedCallingEnabled(sub_id) - if check_state != new_state: - ad.log.error("Failed to toggle Enhanced 4G LTE Mode to %s, still set to %s on sub_id %s", - new_state, check_state, sub_id) - return False - return True + current_state = None + result = True + + if sub_id is None: + sub_id = ad.droid.subscriptionGetDefaultVoiceSubId() + + try: + current_state = ad.droid.imsMmTelIsAdvancedCallingEnabled(sub_id) + except Exception as e: + ad.log.warning(e) + + if current_state is not None: + if new_state is None: + new_state = not current_state + if new_state != current_state: + ad.log.info( + "Toggle Enhanced 4G LTE Mode from %s to %s on sub_id %s", + current_state, new_state, sub_id) + ad.droid.imsMmTelSetAdvancedCallingEnabled(sub_id, new_state) + check_state = ad.droid.imsMmTelIsAdvancedCallingEnabled(sub_id) + if check_state != new_state: + ad.log.error("Failed to toggle Enhanced 4G LTE Mode to %s, still \ + set to %s on sub_id %s", new_state, check_state, sub_id) + result = False + return result + else: + # TODO: b/26293960 No framework API available to set IMS by SubId. + voice_sub_id_changed = False + current_sub_id = get_incoming_voice_sub_id(ad) + if current_sub_id != sub_id: + set_incoming_voice_sub_id(ad, sub_id) + voice_sub_id_changed = True + + # b/139641554 + ad.terminate_all_sessions() + bring_up_sl4a(ad) + + if not ad.droid.imsIsEnhanced4gLteModeSettingEnabledByPlatform(): + ad.log.info( + "Enhanced 4G Lte Mode Setting is not enabled by platform for \ + sub ID %s.", sub_id) + return False + + current_state = ad.droid.imsIsEnhanced4gLteModeSettingEnabledByUser() + ad.log.info("Current state of Enhanced 4G Lte Mode Setting for sub \ + ID %s: %s", sub_id, current_state) + ad.log.info("New desired state of Enhanced 4G Lte Mode Setting for sub \ + ID %s: %s", sub_id, new_state) + + if new_state is None: + new_state = not current_state + if new_state != current_state: + ad.log.info( + "Toggle Enhanced 4G LTE Mode from %s to %s for sub ID %s.", + current_state, new_state, sub_id) + ad.droid.imsSetEnhanced4gMode(new_state) + time.sleep(5) + + check_state = ad.droid.imsIsEnhanced4gLteModeSettingEnabledByUser() + if check_state != new_state: + ad.log.error("Failed to toggle Enhanced 4G LTE Mode to %s, \ + still set to %s on sub_id %s", new_state, check_state, sub_id) + result = False + + if voice_sub_id_changed: + set_incoming_voice_sub_id(ad, current_sub_id) + + return result def toggle_wfc(log, ad, new_state=None): @@ -5010,43 +5080,124 @@ def toggle_wfc(log, ad, new_state=None): Args: log: Log object ad: Android device object. - new_state: True or False + new_state: WFC state to set to. + True for enable, False for disable. + If None, opposite of the current state. """ - if not ad.droid.imsIsWfcEnabledByPlatform(): - ad.log.info("WFC is not enabled by platform") - return False - current_state = ad.droid.imsIsWfcEnabledByUser() - if current_state is None: - new_state = not current_state - if new_state != current_state: - ad.log.info("Toggle WFC user enabled from %s to %s", current_state, - new_state) - ad.droid.imsSetWfcSetting(new_state) - return True + return toggle_wfc_for_subscription( + log, ad, new_state, get_outgoing_voice_sub_id(ad)) -def toggle_wfc_for_subscription(ad, new_state=None, sub_id=None): - """ Toggle WFC enable/disable +def toggle_wfc_for_subscription(log, ad, new_state=None, sub_id=None): + """ Toggle WFC enable/disable for specified voice subscription. Args: ad: Android device object. - sub_id: subscription Id - new_state: True or False + sub_id: Optional. If not assigned the default sub ID for voice call will + be used. + new_state: WFC state to set to. + True for enable, False for disable. + If None, opposite of the current state. """ + current_state = None + result = True + if sub_id is None: sub_id = ad.droid.subscriptionGetDefaultVoiceSubId() - current_state = ad.droid.imsMmTelIsVoWiFiSettingEnabled(sub_id) - if current_state is None: - new_state = not current_state - if new_state != current_state: - ad.log.info("SubId %s - Toggle WFC from %s to %s", sub_id, - current_state, new_state) - ad.droid.imsMmTelSetVoWiFiSettingEnabled(sub_id, new_state) + + try: + current_state = ad.droid.imsMmTelIsVoWiFiSettingEnabled(sub_id) + except Exception as e: + ad.log.warning(e) + + if current_state is not None: + if new_state is None: + new_state = not current_state + if new_state != current_state: + ad.log.info( + "Toggle Enhanced 4G LTE Mode from %s to %s on sub_id %s", + current_state, new_state, sub_id) + ad.droid.imsMmTelSetVoWiFiSettingEnabled(sub_id, new_state) + check_state = ad.droid.imsMmTelIsVoWiFiSettingEnabled(sub_id) + if check_state != new_state: + ad.log.error("Failed to toggle Enhanced 4G LTE Mode to %s, \ + still set to %s on sub_id %s", new_state, check_state, sub_id) + result = False + return result + else: + voice_sub_id_changed = False + if not sub_id: + sub_id = get_outgoing_voice_sub_id(ad) + else: + current_sub_id = get_incoming_voice_sub_id(ad) + if current_sub_id != sub_id: + set_incoming_voice_sub_id(ad, sub_id) + voice_sub_id_changed = True + + # b/139641554 + ad.terminate_all_sessions() + bring_up_sl4a(ad) + + if not ad.droid.imsIsWfcEnabledByPlatform(): + ad.log.info("WFC is not enabled by platform for sub ID %s.", sub_id) + return False + + current_state = ad.droid.imsIsWfcEnabledByUser() + ad.log.info("Current state of WFC Setting for sub ID %s: %s", + sub_id, current_state) + ad.log.info("New desired state of WFC Setting for sub ID %s: %s", + sub_id, new_state) + + if new_state is None: + new_state = not current_state + if new_state != current_state: + ad.log.info("Toggle WFC user enabled from %s to %s for sub ID %s", + current_state, new_state, sub_id) + ad.droid.imsSetWfcSetting(new_state) + + if voice_sub_id_changed: + set_incoming_voice_sub_id(ad, current_sub_id) + + return True + +def is_enhanced_4g_lte_mode_setting_enabled(ad, sub_id, enabled_by="platform"): + voice_sub_id_changed = False + current_sub_id = get_incoming_voice_sub_id(ad) + if current_sub_id != sub_id: + set_incoming_voice_sub_id(ad, sub_id) + voice_sub_id_changed = True + if enabled_by == "platform": + res = ad.droid.imsIsEnhanced4gLteModeSettingEnabledByPlatform() + else: + res = ad.droid.imsIsEnhanced4gLteModeSettingEnabledByUser() + if not res: + ad.log.info("Enhanced 4G Lte Mode Setting is NOT enabled by %s for sub \ + ID %s.", enabled_by, sub_id) + if voice_sub_id_changed: + set_incoming_voice_sub_id(ad, current_sub_id) + return False + if voice_sub_id_changed: + set_incoming_voice_sub_id(ad, current_sub_id) + ad.log.info("Enhanced 4G Lte Mode Setting is enabled by %s for sub ID %s.", + enabled_by, sub_id) return True +def set_enhanced_4g_mode(ad, sub_id, state): + voice_sub_id_changed = False + current_sub_id = get_incoming_voice_sub_id(ad) + if current_sub_id != sub_id: + set_incoming_voice_sub_id(ad, sub_id) + voice_sub_id_changed = True + + ad.droid.imsSetEnhanced4gMode(state) + time.sleep(5) + + if voice_sub_id_changed: + set_incoming_voice_sub_id(ad, current_sub_id) def wait_for_enhanced_4g_lte_setting(log, ad, + sub_id, max_time=MAX_WAIT_TIME_FOR_STATE_CHANGE): """Wait for android device to enable enhance 4G LTE setting. @@ -5060,9 +5211,13 @@ def wait_for_enhanced_4g_lte_setting(log, Return False if timeout. """ return wait_for_state( - ad.droid.imsIsEnhanced4gLteModeSettingEnabledByPlatform, + is_enhanced_4g_lte_mode_setting_enabled, True, - max_wait_time=max_time) + max_time, + WAIT_TIME_BETWEEN_STATE_CHECK, + ad, + sub_id, + enabled_by="platform") def set_wfc_mode(log, ad, wfc_mode): @@ -5078,29 +5233,8 @@ def set_wfc_mode(log, ad, wfc_mode): Returns: True if success. False if ad does not support WFC or error happened. """ - if wfc_mode != WFC_MODE_DISABLED and wfc_mode not in ad.telephony[ - "subscription"][get_outgoing_voice_sub_id(ad)].get("wfc_modes", []): - ad.log.error("WFC mode %s is not supported", wfc_mode) - raise signals.TestSkip("WFC mode %s is not supported" % wfc_mode) - try: - ad.log.info("Set wfc mode to %s", wfc_mode) - if wfc_mode != WFC_MODE_DISABLED: - start_adb_tcpdump(ad, interface="wlan0", mask="all") - if not ad.droid.imsIsWfcEnabledByPlatform(): - if wfc_mode == WFC_MODE_DISABLED: - return True - else: - ad.log.error("WFC not supported by platform.") - return False - ad.droid.imsSetWfcMode(wfc_mode) - mode = ad.droid.imsGetWfcMode() - if mode != wfc_mode: - ad.log.error("WFC mode is %s, not in %s", mode, wfc_mode) - return False - except Exception as e: - log.error(e) - return False - return True + return set_wfc_mode_for_subscription( + ad, wfc_mode, get_outgoing_voice_sub_id(ad)) def set_wfc_mode_for_subscription(ad, wfc_mode, sub_id=None): @@ -5116,22 +5250,97 @@ def set_wfc_mode_for_subscription(ad, wfc_mode, sub_id=None): Returns: True if success. False if ad does not support WFC or error happened. """ + if wfc_mode not in [ + WFC_MODE_WIFI_ONLY, + WFC_MODE_CELLULAR_PREFERRED, + WFC_MODE_WIFI_PREFERRED, + WFC_MODE_DISABLED]: + + ad.log.error("Given WFC mode (%s) is not correct.", wfc_mode) + return False + + current_mode = None + result = True + + if sub_id is None: + sub_id = ad.droid.subscriptionGetDefaultVoiceSubId() + try: - if sub_id is None: - sub_id = ad.droid.subscriptionGetDefaultVoiceSubId() - if not ad.droid.imsMmTelIsVoWiFiSettingEnabled(sub_id): - ad.log.info("SubId %s - Enabling WiFi Calling", sub_id) - ad.droid.imsMmTelSetVoWiFiSettingEnabled(sub_id, True) - ad.log.info("SubId %s - setwfcmode to %s", sub_id, wfc_mode) - ad.droid.imsMmTelSetVoWiFiModeSetting(sub_id, wfc_mode) - mode = ad.droid.imsMmTelGetVoWiFiModeSetting(sub_id) - if mode != wfc_mode: - ad.log.error("SubId %s - getwfcmode shows %s", sub_id, mode) - return False + current_mode = ad.droid.imsMmTelGetVoWiFiModeSetting(sub_id) + ad.log.info("Current WFC mode of sub ID: %s", current_mode) except Exception as e: - ad.log.error(e) - return False - return True + ad.log.warning(e) + + if current_mode is not None: + try: + if not ad.droid.imsMmTelIsVoWiFiSettingEnabled(sub_id): + if wfc_mode is WFC_MODE_DISABLED: + return True + ad.log.info( + "WFC is disabled for sub ID %s. Enabling WFC...", sub_id) + ad.droid.imsMmTelSetVoWiFiSettingEnabled(sub_id, True) + + if wfc_mode is WFC_MODE_DISABLED: + ad.droid.imsMmTelSetVoWiFiSettingEnabled(sub_id, False) + return True + + ad.log.info("Set wfc mode to %s for sub ID %s.", wfc_mode, sub_id) + ad.droid.imsMmTelSetVoWiFiModeSetting(sub_id, wfc_mode) + mode = ad.droid.imsMmTelGetVoWiFiModeSetting(sub_id) + if mode != wfc_mode: + ad.log.error("WFC mode for sub ID %s is %s, not in %s", + sub_id, mode, wfc_mode) + return False + except Exception as e: + ad.log.error(e) + return False + return True + else: + voice_sub_id_changed = False + if not sub_id: + sub_id = get_outgoing_voice_sub_id(ad) + else: + current_sub_id = get_incoming_voice_sub_id(ad) + if current_sub_id != sub_id: + set_incoming_voice_sub_id(ad, sub_id) + voice_sub_id_changed = True + + # b/139641554 + ad.terminate_all_sessions() + bring_up_sl4a(ad) + + if wfc_mode != WFC_MODE_DISABLED and wfc_mode not in ad.telephony[ + "subscription"][get_outgoing_voice_sub_id(ad)].get("wfc_modes", []): + ad.log.error("WFC mode %s is not supported", wfc_mode) + raise signals.TestSkip("WFC mode %s is not supported" % wfc_mode) + try: + ad.log.info("Set wfc mode to %s", wfc_mode) + if wfc_mode != WFC_MODE_DISABLED: + start_adb_tcpdump(ad, interface="wlan0", mask="all") + if not ad.droid.imsIsWfcEnabledByPlatform(): + if wfc_mode == WFC_MODE_DISABLED: + if voice_sub_id_changed: + set_incoming_voice_sub_id(ad, current_sub_id) + return True + else: + ad.log.error("WFC not supported by platform.") + if voice_sub_id_changed: + set_incoming_voice_sub_id(ad, current_sub_id) + return False + ad.droid.imsSetWfcMode(wfc_mode) + mode = ad.droid.imsGetWfcMode() + if voice_sub_id_changed: + set_incoming_voice_sub_id(ad, current_sub_id) + if mode != wfc_mode: + ad.log.error("WFC mode is %s, not in %s", mode, wfc_mode) + return False + except Exception as e: + log.error(e) + if voice_sub_id_changed: + set_incoming_voice_sub_id(ad, current_sub_id) + return False + return True + def set_ims_provisioning_for_subscription(ad, feature_flag, value, sub_id=None): @@ -5562,18 +5771,24 @@ def wait_for_data_attach_for_subscription(log, ad, sub_id, max_time): NETWORK_SERVICE_DATA) -def is_ims_registered(log, ad): +def is_ims_registered(log, ad, sub_id=None): """Return True if IMS registered. Args: log: log object. ad: android device. + sub_id: Optional. If not assigned the default sub ID of voice call will + be used. Returns: Return True if IMS registered. Return False if IMS not registered. """ - return ad.droid.telephonyIsImsRegistered() + if not sub_id: + return ad.droid.telephonyIsImsRegistered() + else: + return change_voice_subid_temporarily( + ad, sub_id, ad.droid.telephonyIsImsRegistered) def wait_for_ims_registered(log, ad, max_time=MAX_WAIT_TIME_WFC_ENABLED): @@ -5590,26 +5805,48 @@ def wait_for_ims_registered(log, ad, max_time=MAX_WAIT_TIME_WFC_ENABLED): """ return _wait_for_droid_in_state(log, ad, max_time, is_ims_registered) +def is_volte_available(log, ad, sub_id): + """Return True if VoLTE is available. + + Args: + log: log object. + ad: android device. + sub_id: Optional. If not assigned the default sub ID of voice call will + be used. + + Returns: + Return True if VoLTE is available. + Return False if VoLTE is not available. + """ + if not sub_id: + return ad.droid.telephonyIsVolteAvailable() + else: + return change_voice_subid_temporarily( + ad, sub_id, ad.droid.telephonyIsVolteAvailable) -def is_volte_enabled(log, ad): +def is_volte_enabled(log, ad, sub_id=None): """Return True if VoLTE feature bit is True. Args: log: log object. ad: android device. + sub_id: Optional. If not assigned the default sub ID of voice call will + be used. Returns: Return True if VoLTE feature bit is True and IMS registered. Return False if VoLTE feature bit is False or IMS not registered. """ - if not is_ims_registered(log, ad): - ad.log.info("IMS is not registered.") + if not is_ims_registered(log, ad, sub_id): + ad.log.info("IMS is not registered for sub ID %s.", sub_id) return False - if not ad.droid.telephonyIsVolteAvailable(): - ad.log.info("IMS is registered, IsVolteCallingAvailble is False") + if not is_volte_available(log, ad, sub_id): + ad.log.info("IMS is registered for sub ID %s, IsVolteCallingAvailable \ + is False", sub_id) return False else: - ad.log.info("IMS is registered, IsVolteCallingAvailble is True") + ad.log.info("IMS is registered for sub ID %s, IsVolteCallingAvailable \ + is True", sub_id) return True @@ -5632,7 +5869,8 @@ def is_video_enabled(log, ad): return video_status -def wait_for_volte_enabled(log, ad, max_time=MAX_WAIT_TIME_VOLTE_ENABLED): +def wait_for_volte_enabled( + log, ad, max_time=MAX_WAIT_TIME_VOLTE_ENABLED,sub_id=None): """Wait for android device to report VoLTE enabled bit true. Args: @@ -5644,7 +5882,11 @@ def wait_for_volte_enabled(log, ad, max_time=MAX_WAIT_TIME_VOLTE_ENABLED): Return True if device report VoLTE enabled bit true within max_time. Return False if timeout. """ - return _wait_for_droid_in_state(log, ad, max_time, is_volte_enabled) + if not sub_id: + return _wait_for_droid_in_state(log, ad, max_time, is_volte_enabled) + else: + return _wait_for_droid_in_state_for_subscription( + log, ad, sub_id, max_time, is_volte_enabled) def wait_for_video_enabled(log, ad, max_time=MAX_WAIT_TIME_VOLTE_ENABLED): @@ -5677,10 +5919,10 @@ def is_wfc_enabled(log, ad): ad.log.info("IMS is not registered.") return False if not ad.droid.telephonyIsWifiCallingAvailable(): - ad.log.info("IMS is registered, IsWifiCallingAvailble is False") + ad.log.info("IMS is registered, IsWifiCallingAvailable is False") return False else: - ad.log.info("IMS is registered, IsWifiCallingAvailble is True") + ad.log.info("IMS is registered, IsWifiCallingAvailable is True") return True @@ -6353,7 +6595,7 @@ def mms_receive_verify_after_call_hangup_for_subscription( if not wait_for_matching_mms(log, ad_rx, phonenumber_tx, message): return False finally: - ad_rx.droid.smsStopTrackingIncomingMmsMessage() + ad_rx.messaging_droid.smsStopTrackingIncomingMmsMessage() return True @@ -10312,44 +10554,110 @@ def wait_for_matching_multiple_sms(log, return True -def is_sms_in_collision_match(event, phonenumber_tx, phonenumber_tx2, text, text2): +def is_sms_in_collision_match( + event, phonenumber_tx, phonenumber_tx2, text, text2): event_text = event['data']['Text'].strip() if event_text.startswith("("): event_text = event_text.split(")")[-1] for phonenumber, txt in [[phonenumber_tx, text], [phonenumber_tx2, text2]]: - if check_phone_number_match(event['data']['Sender'], phonenumber) and txt.startswith(event_text): + if check_phone_number_match( + event['data']['Sender'], phonenumber) and txt.startswith(event_text): return True return False -def is_sms_in_collision_partial_match(event, phonenumber_tx, phonenumber_tx2, text, text2): +def is_sms_in_collision_partial_match( + event, phonenumber_tx, phonenumber_tx2, text, text2): for phonenumber, txt in [[phonenumber_tx, text], [phonenumber_tx2, text2]]: - if check_phone_number_match(event['data']['Sender'], phonenumber) and event['data']['Text'].strip() == txt: + if check_phone_number_match( + event['data']['Sender'], phonenumber) and \ + event['data']['Text'].strip() == txt: return True return False -def is_sms_match_among_multiple_sms(event, phonenumber_tx, phonenumber_tx2, texts=[], texts2=[]): +def is_sms_match_among_multiple_sms( + event, phonenumber_tx, phonenumber_tx2, texts=[], texts2=[]): for txt in texts: - if check_phone_number_match(event['data']['Sender'], phonenumber_tx) and event['data']['Text'].strip() == txt: + if check_phone_number_match( + event['data']['Sender'], phonenumber_tx) and \ + event['data']['Text'].strip() == txt: return True for txt in texts2: - if check_phone_number_match(event['data']['Sender'], phonenumber_tx2) and event['data']['Text'].strip() == txt: + if check_phone_number_match( + event['data']['Sender'], phonenumber_tx2) and \ + event['data']['Text'].strip() == txt: return True return False -def is_sms_partial_match_among_multiple_sms(event, phonenumber_tx, phonenumber_tx2, texts=[], texts2=[]): +def is_sms_partial_match_among_multiple_sms( + event, phonenumber_tx, phonenumber_tx2, texts=[], texts2=[]): event_text = event['data']['Text'].strip() if event_text.startswith("("): event_text = event_text.split(")")[-1] for txt in texts: - if check_phone_number_match(event['data']['Sender'], phonenumber_tx) and txt.startswith(event_text): + if check_phone_number_match( + event['data']['Sender'], phonenumber_tx) and \ + txt.startswith(event_text): return True for txt in texts2: - if check_phone_number_match(event['data']['Sender'], phonenumber_tx2) and txt.startswith(event_text): + if check_phone_number_match( + event['data']['Sender'], phonenumber_tx2) and \ + txt.startswith(event_text): return True return False + +def set_time_sync_from_network(ad, action): + if (action == 'enable'): + ad.log.info('Enabling sync time from network.') + ad.adb.shell('settings put global auto_time 1') + + elif (action == 'disable'): + ad.log.info('Disabling sync time from network.') + ad.adb.shell('settings put global auto_time 0') + + time.sleep(WAIT_TIME_SYNC_DATE_TIME_FROM_NETWORK) + +def datetime_handle(ad, action, set_datetime_value='', get_year=False): + get_value = '' + if (action == 'get'): + if (get_year): + datetime_string = ad.adb.shell('date') + datetime_list = datetime_string.split() + try: + get_value = datetime_list[5] + except Exception as e: + self.log.error("Fail to get year from datetime: %s. " \ + "Exception error: %s", datetime_list + , str(e)) + raise signals.TestSkip("Fail to get year from datetime" \ + ", the format is changed. Skip the test.") + else: + get_value = ad.adb.shell('date') + + elif (action == 'set'): + ad.adb.shell('date %s' % set_datetime_value) + time.sleep(WAIT_TIME_SYNC_DATE_TIME_FROM_NETWORK) + ad.adb.shell('am broadcast -a android.intent.action.TIME_SET') + + return get_value + +def change_voice_subid_temporarily(ad, sub_id, state_check_func): + result = False + voice_sub_id_changed = False + current_sub_id = get_incoming_voice_sub_id(ad) + if current_sub_id != sub_id: + set_incoming_voice_sub_id(ad, sub_id) + voice_sub_id_changed = True + + if state_check_func(): + result = True + + if voice_sub_id_changed: + set_incoming_voice_sub_id(ad, current_sub_id) + + return result
\ No newline at end of file diff --git a/acts/framework/acts/test_utils/tel/tel_voice_utils.py b/acts/framework/acts/test_utils/tel/tel_voice_utils.py index 4294bf3ea6..1c41658fa5 100644 --- a/acts/framework/acts/test_utils/tel/tel_voice_utils.py +++ b/acts/framework/acts/test_utils/tel/tel_voice_utils.py @@ -765,7 +765,6 @@ def phone_setup_iwlan(log, Make sure phone connect to WiFi. (If wifi_ssid is not None.) Wait for phone to be in iwlan data network type. Wait for phone to report wfc enabled flag to be true. - Args: log: Log object. ad: Android device object. @@ -774,14 +773,9 @@ def phone_setup_iwlan(log, wifi_ssid: WiFi network SSID. This is optional. If wifi_ssid is None, then phone_setup_iwlan will not attempt to connect to wifi. wifi_pwd: WiFi network password. This is optional. - Returns: True if success. False if fail. """ - if not get_capability_for_subscription(ad, CAPABILITY_WFC, - get_outgoing_voice_sub_id(ad)): - ad.log.error("WFC is not supported, abort test.") - raise signals.TestSkip("WFC is not supported, abort test.") return phone_setup_iwlan_for_subscription(log, ad, get_outgoing_voice_sub_id(ad), is_airplane_mode, wfc_mode, @@ -801,7 +795,6 @@ def phone_setup_iwlan_for_subscription(log, Make sure phone connect to WiFi. (If wifi_ssid is not None.) Wait for phone to be in iwlan data network type. Wait for phone to report wfc enabled flag to be true. - Args: log: Log object. ad: Android device object. @@ -811,19 +804,19 @@ def phone_setup_iwlan_for_subscription(log, wifi_ssid: WiFi network SSID. This is optional. If wifi_ssid is None, then phone_setup_iwlan will not attempt to connect to wifi. wifi_pwd: WiFi network password. This is optional. - Returns: True if success. False if fail. """ + if not get_capability_for_subscription(ad, CAPABILITY_WFC, sub_id): + ad.log.error("WFC is not supported, abort test.") + raise signals.TestSkip("WFC is not supported, abort test.") toggle_airplane_mode(log, ad, is_airplane_mode, strict_checking=False) - # check if WFC supported phones if wfc_mode != WFC_MODE_DISABLED and not ad.droid.imsIsWfcEnabledByPlatform( ): ad.log.error("WFC is not enabled on this device by checking " "ImsManager.isWfcEnabledByPlatform") return False - if wifi_ssid is not None: if not ensure_wifi_connected(log, ad, wifi_ssid, wifi_pwd, apm=is_airplane_mode): ad.log.error("Fail to bring up WiFi connection on %s.", wifi_ssid) @@ -832,11 +825,9 @@ def phone_setup_iwlan_for_subscription(log, ad.log.info("WiFi network SSID not specified, available user " "parameters are: wifi_network_ssid, wifi_network_ssid_2g, " "wifi_network_ssid_5g") - if not set_wfc_mode(log, ad, wfc_mode): ad.log.error("Unable to set WFC mode to %s.", wfc_mode) return False - if not wait_for_wfc_enabled(log, ad, max_time=MAX_WAIT_TIME_WFC_ENABLED): ad.log.error("WFC is not enabled") return False @@ -1077,8 +1068,9 @@ def phone_setup_csfb_for_subscription(log, ad, sub_id): if not phone_setup_4g_for_subscription(log, ad, sub_id): ad.log.error("Failed to set to 4G data.") return False - if ad.droid.imsIsEnhanced4gLteModeSettingEnabledByPlatform(): - toggle_volte(log, ad, False) + + toggle_volte(log, ad, False) + if not ensure_network_generation_for_subscription( log, ad, sub_id, GEN_4G, voice_or_data=NETWORK_SERVICE_DATA): return False @@ -1111,20 +1103,22 @@ def phone_setup_volte(log, ad): def phone_setup_volte_for_subscription(log, ad, sub_id): """Setup VoLTE enable for subscription id. - Args: log: log object ad: android device object. sub_id: subscription id. - Returns: True: if VoLTE is enabled successfully. False: for errors """ + if not get_capability_for_subscription(ad, CAPABILITY_VOLTE, + get_outgoing_voice_sub_id(ad)): + ad.log.error("VoLTE is not supported, abort test.") + raise signals.TestSkip("VoLTE is not supported, abort test.") if not phone_setup_4g_for_subscription(log, ad, sub_id): ad.log.error("Failed to set to 4G data.") return False - if not wait_for_enhanced_4g_lte_setting(log, ad): + if not wait_for_enhanced_4g_lte_setting(log, ad, sub_id): ad.log.error("Enhanced 4G LTE setting is not available") return False toggle_volte_for_subscription(log, ad, sub_id, True) @@ -1361,7 +1355,6 @@ def phone_idle_volte(log, ad): def phone_idle_volte_for_subscription(log, ad, sub_id): """Return if phone is idle for VoLTE call test for subscription id. - Args: ad: Android device object. sub_id: subscription id. @@ -1371,7 +1364,7 @@ def phone_idle_volte_for_subscription(log, ad, sub_id): voice_or_data=NETWORK_SERVICE_VOICE): ad.log.error("Voice rat not in LTE mode.") return False - if not wait_for_volte_enabled(log, ad, MAX_WAIT_TIME_VOLTE_ENABLED): + if not wait_for_volte_enabled(log, ad, MAX_WAIT_TIME_VOLTE_ENABLED, sub_id): ad.log.error( "Failed to <report volte enabled true> within %s seconds.", MAX_WAIT_TIME_VOLTE_ENABLED) @@ -1699,7 +1692,7 @@ def is_phone_in_call_iwlan(log, ad, call_id=None): ad.log.info("IMS is not registered.") return False if not ad.droid.telephonyIsWifiCallingAvailable(): - ad.log.info("IsWifiCallingAvailble is False") + ad.log.info("IsWifiCallingAvailable is False") return False if not call_id: call_ids = ad.droid.telecomCallGetCallIds() diff --git a/acts/framework/acts/test_utils/wifi/OWNERS b/acts/framework/acts/test_utils/wifi/OWNERS index 31966b7984..edb3e3ee80 100644 --- a/acts/framework/acts/test_utils/wifi/OWNERS +++ b/acts/framework/acts/test_utils/wifi/OWNERS @@ -1,5 +1,6 @@ -bmahadev@google.com +bkleung@google.com +dysu@google.com etancohen@google.com -mplass@google.com +gmoturu@google.com rpius@google.com satk@google.com diff --git a/acts/framework/acts/test_utils/wifi/WifiBaseTest.py b/acts/framework/acts/test_utils/wifi/WifiBaseTest.py index 82d655fff1..e79d03eda5 100644 --- a/acts/framework/acts/test_utils/wifi/WifiBaseTest.py +++ b/acts/framework/acts/test_utils/wifi/WifiBaseTest.py @@ -295,6 +295,112 @@ class WifiBaseTest(BaseTestClass): self.update_bssid(ap_instance, ap, network, hostapd_constants.BAND_2G) + def configure_openwrt_ap_and_start( + self, + channel_5g=hostapd_constants.AP_DEFAULT_CHANNEL_5G, + channel_2g=hostapd_constants.AP_DEFAULT_CHANNEL_2G, + ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G, + passphrase_length_2g=hostapd_constants.AP_PASSPHRASE_LENGTH_2G, + ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G, + passphrase_length_5g=hostapd_constants.AP_PASSPHRASE_LENGTH_5G, + mirror_ap=False, + hidden=False, + same_ssid=False, + open_network=False, + wpa_network=False, + wep_network=False, + ent_network=False, + ent_network_pwd=False, + radius_conf_2g=None, + radius_conf_5g=None, + radius_conf_pwd=None, + ap_count=1): + """Create, configure and start OpenWrt AP. + + Args: + channel_5g: 5G channel to configure. + channel_2g: 2G channel to configure. + ssid_length_2g: Int, number of characters to use for 2G SSID. + passphrase_length_2g: Int, length of password for 2G network. + ssid_length_5g: Int, number of characters to use for 5G SSID. + passphrase_length_5g: Int, length of password for 5G network. + same_ssid: Boolean, determines if both bands on AP use the same SSID. + open_network: Boolean, to check if open network should be configured. + wpa_network: Boolean, to check if wpa network should be configured. + wep_network: Boolean, to check if wep network should be configured. + ent_network: Boolean, to check if ent network should be configured. + ent_network_pwd: Boolean, to check if ent pwd network should be configured. + radius_conf_2g: dictionary with enterprise radius server details. + radius_conf_5g: dictionary with enterprise radius server details. + radius_conf_pwd: dictionary with enterprise radiuse server details. + ap_count: APs to configure. + """ + self.reference_networks = [] + self.wpa_networks = [] + self.wep_networks = [] + self.ent_networks = [] + self.ent_networks_pwd = [] + self.open_network = [] + for _ in range(ap_count): + network_list = [] + if wpa_network: + wpa_dict = self.get_psk_network(mirror_ap, + self.reference_networks, + hidden, + same_ssid, + ssid_length_2g, + ssid_length_5g, + passphrase_length_2g, + passphrase_length_5g) + wpa_dict[hostapd_constants.BAND_2G]["security"] = "psk2" + wpa_dict[hostapd_constants.BAND_5G]["security"] = "psk2" + self.wpa_networks.append(wpa_dict) + network_list.append(wpa_dict) + if wep_network: + wep_dict = self.get_wep_network(mirror_ap, + self.wep_networks, + hidden, + same_ssid, + ssid_length_2g, + ssid_length_5g) + network_list.append(wep_dict) + if ent_network: + ent_dict = self.get_open_network(mirror_ap, + self.ent_networks, + hidden, + same_ssid, + ssid_length_2g, + ssid_length_5g) + ent_dict["2g"]["security"] = "wpa2" + ent_dict["2g"].update(radius_conf_2g) + ent_dict["5g"]["security"] = "wpa2" + ent_dict["5g"].update(radius_conf_5g) + network_list.append(ent_dict) + if ent_network_pwd: + ent_pwd_dict = self.get_open_network(mirror_ap, + self.ent_networks_pwd, + hidden, + same_ssid, + ssid_length_2g, + ssid_length_5g) + ent_pwd_dict["2g"]["security"] = "wpa2" + ent_pwd_dict["2g"].update(radius_conf_pwd) + ent_pwd_dict["5g"]["security"] = "wpa2" + ent_pwd_dict["5g"].update(radius_conf_pwd) + network_list.append(ent_pwd_dict) + if open_network: + open_dict = self.get_open_network(mirror_ap, + self.open_network, + hidden, + same_ssid, + ssid_length_2g, + ssid_length_5g) + network_list.append(open_dict) + self.access_points[_].configure_ap(network_list, + channel_2g, + channel_5g) + self.access_points[_].start_ap() + def legacy_configure_ap_and_start( self, channel_5g=hostapd_constants.AP_DEFAULT_CHANNEL_5G, @@ -316,11 +422,6 @@ class WifiBaseTest(BaseTestClass): ent_network_pwd=False, radius_conf_pwd=None, ap_count=1): - asserts.assert_true( - len(self.user_params["AccessPoint"]) == 2, - "Exactly two access points must be specified. \ - Each access point has 2 radios, one each for 2.4GHZ \ - and 5GHz. A test can choose to use one or both APs.") config_count = 1 count = 0 diff --git a/acts/framework/acts/test_utils/wifi/aware/AwareBaseTest.py b/acts/framework/acts/test_utils/wifi/aware/AwareBaseTest.py index df303ced44..ed85f5b754 100644 --- a/acts/framework/acts/test_utils/wifi/aware/AwareBaseTest.py +++ b/acts/framework/acts/test_utils/wifi/aware/AwareBaseTest.py @@ -32,7 +32,8 @@ class AwareBaseTest(BaseTestClass): device_startup_offset = 2 def setup_test(self): - required_params = ("aware_default_power_mode", ) + required_params = ("aware_default_power_mode", + "dbs_supported_models",) self.unpack_userparams(required_params) for ad in self.android_devices: @@ -40,15 +41,14 @@ class AwareBaseTest(BaseTestClass): not ad.droid.doesDeviceSupportWifiAwareFeature(), "Device under test does not support Wi-Fi Aware - skipping test" ) - wutils.wifi_toggle_state(ad, True) + aware_avail = ad.droid.wifiIsAwareAvailable() ad.droid.wifiP2pClose() + wutils.wifi_toggle_state(ad, True) utils.set_location_service(ad, True) - aware_avail = ad.droid.wifiIsAwareAvailable() if not aware_avail: self.log.info('Aware not available. Waiting ...') autils.wait_for_event(ad, aconsts.BROADCAST_WIFI_AWARE_AVAILABLE) - ad.ed.clear_all_events() ad.aware_capabilities = autils.get_aware_capabilities(ad) self.reset_device_parameters(ad) self.reset_device_statistics(ad) @@ -58,6 +58,7 @@ class AwareBaseTest(BaseTestClass): # set randomization interval to 0 (disable) to reduce likelihood of # interference in tests autils.configure_mac_random_interval(ad, 0) + ad.ed.clear_all_events() def teardown_test(self): for ad in self.android_devices: diff --git a/acts/framework/acts/test_utils/wifi/aware/aware_test_utils.py b/acts/framework/acts/test_utils/wifi/aware/aware_test_utils.py index 2623f9bfcb..e2977999c1 100644 --- a/acts/framework/acts/test_utils/wifi/aware/aware_test_utils.py +++ b/acts/framework/acts/test_utils/wifi/aware/aware_test_utils.py @@ -468,6 +468,28 @@ def verify_socket_connect(dut_s, dut_c, ipv6_s, ipv6_c, port): return True +def run_ping6(dut, target_ip, duration=60): + """Run ping test and return the latency result + + Args: + dut: the dut which run the ping cmd + target_ip: target IP Address for ping + duration: the duration time of the ping + + return: dict contains "min/avg/max/mdev" result + """ + cmd = "ping6 -w %d %s" % (duration, target_ip) + ping_result = dut.adb.shell(cmd, timeout=duration + 1) + res = re.match(".*mdev = (\S+) .*", ping_result, re.S) + asserts.assert_true(res, "Cannot reach the IP address %s", target_ip) + title = ["min", "avg", "max", "mdev"] + result = res.group(1).split("/") + latency_result = {} + for i in range(len(title)): + latency_result[title[i]] = result[i] + return latency_result + + ######################################################### # Aware primitives ######################################################### diff --git a/acts/framework/acts/test_utils/wifi/p2p/WifiP2pBaseTest.py b/acts/framework/acts/test_utils/wifi/p2p/WifiP2pBaseTest.py index 7aca3c6ca9..1c5a53f120 100644 --- a/acts/framework/acts/test_utils/wifi/p2p/WifiP2pBaseTest.py +++ b/acts/framework/acts/test_utils/wifi/p2p/WifiP2pBaseTest.py @@ -24,16 +24,32 @@ from acts.base_test import BaseTestClass from acts.test_utils.wifi import wifi_test_utils as wutils from acts.test_utils.wifi.p2p import wifi_p2p_const as p2pconsts +WAIT_TIME = 60 + + class WifiP2pBaseTest(BaseTestClass): def __init__(self, controllers): if not hasattr(self, 'android_devices'): super(WifiP2pBaseTest, self).__init__(controllers) def setup_class(self): + for ad in self.android_devices: + ad.droid.wakeLockAcquireBright() + ad.droid.wakeUpNow() + required_params = () + optional_params = ("skip_read_factory_mac", ) + self.unpack_userparams(required_params, + optional_params, + skip_read_factory_mac=0) + self.dut1 = self.android_devices[0] self.dut2 = self.android_devices[1] - self.dut1_mac = self.get_p2p_mac_address(self.dut1) - self.dut2_mac = self.get_p2p_mac_address(self.dut2) + if self.skip_read_factory_mac: + self.dut1_mac = None + self.dut2_mac = None + else: + self.dut1_mac = self.get_p2p_mac_address(self.dut1) + self.dut2_mac = self.get_p2p_mac_address(self.dut2) #init location before init p2p acts.utils.set_location_service(self.dut1, True) @@ -44,7 +60,7 @@ class WifiP2pBaseTest(BaseTestClass): self.dut1.droid.wifiP2pInitialize() time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) asserts.assert_true(self.dut1.droid.wifiP2pIsEnabled(), - "DUT1's p2p should be initialized but it didn't") + "DUT1's p2p should be initialized but it didn't") self.dut1.name = "Android_" + self.dut1.serial self.dut1.droid.wifiP2pSetDeviceName(self.dut1.name) wutils.wifi_test_device_init(self.dut2) @@ -52,7 +68,7 @@ class WifiP2pBaseTest(BaseTestClass): self.dut2.droid.wifiP2pInitialize() time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) asserts.assert_true(self.dut2.droid.wifiP2pIsEnabled(), - "DUT2's p2p should be initialized but it didn't") + "DUT2's p2p should be initialized but it didn't") self.dut2.name = "Android_" + self.dut2.serial self.dut2.droid.wifiP2pSetDeviceName(self.dut2.name) @@ -63,12 +79,12 @@ class WifiP2pBaseTest(BaseTestClass): utils.sync_device_time(self.dut3) self.dut3.droid.wifiP2pInitialize() time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) - asserts.assert_true(self.dut3.droid.wifiP2pIsEnabled(), - "DUT3's p2p should be initialized but it didn't") + asserts.assert_true( + self.dut3.droid.wifiP2pIsEnabled(), + "DUT3's p2p should be initialized but it didn't") self.dut3.name = "Android_" + self.dut3.serial self.dut3.droid.wifiP2pSetDeviceName(self.dut3.name) - def teardown_class(self): self.dut1.droid.wifiP2pClose() self.dut2.droid.wifiP2pClose() @@ -78,11 +94,12 @@ class WifiP2pBaseTest(BaseTestClass): if len(self.android_devices) > 2: self.dut3.droid.wifiP2pClose() acts.utils.set_location_service(self.dut3, False) + for ad in self.android_devices: + ad.droid.wakeLockRelease() + ad.droid.goToSleepNow() def setup_test(self): for ad in self.android_devices: - ad.droid.wakeLockAcquireBright() - ad.droid.wakeUpNow() ad.ed.clear_all_events() def teardown_test(self): @@ -90,13 +107,11 @@ class WifiP2pBaseTest(BaseTestClass): # Clear p2p group info ad.droid.wifiP2pRequestPersistentGroupInfo() event = ad.ed.pop_event("WifiP2pOnPersistentGroupInfoAvailable", - p2pconsts.DEFAULT_TIMEOUT) + p2pconsts.DEFAULT_TIMEOUT) for network in event['data']: ad.droid.wifiP2pDeletePersistentGroup(network['NetworkId']) # Clear p2p local service ad.droid.wifiP2pClearLocalServices() - ad.droid.wakeLockRelease() - ad.droid.goToSleepNow() def on_fail(self, test_name, begin_time): for ad in self.android_devices: @@ -105,5 +120,7 @@ class WifiP2pBaseTest(BaseTestClass): def get_p2p_mac_address(self, dut): """Gets the current MAC address being used for Wi-Fi Direct.""" + dut.reboot() + time.sleep(WAIT_TIME) out = dut.adb.shell("ifconfig p2p0") return re.match(".* HWaddr (\S+).*", out, re.S).group(1) diff --git a/acts/framework/acts/test_utils/wifi/p2p/wifi_p2p_const.py b/acts/framework/acts/test_utils/wifi/p2p/wifi_p2p_const.py index b50a5d35fc..87f405967e 100644 --- a/acts/framework/acts/test_utils/wifi/p2p/wifi_p2p_const.py +++ b/acts/framework/acts/test_utils/wifi/p2p/wifi_p2p_const.py @@ -28,6 +28,11 @@ DEFAULT_TIMEOUT = 30 DEFAULT_SLEEPTIME = 5 DEFAULT_FUNCTION_SWITCH_TIME = 10 DEFAULT_SERVICE_WAITING_TIME = 20 +DEFAULT_GROUP_CLIENT_LOST_TIME = 60 + +P2P_CONNECT_NEGOTIATION = 0 +P2P_CONNECT_JOIN = 1 +P2P_CONNECT_INVITATION = 2 ###################################################### # Wifi P2p sl4a Event String ###################################################### @@ -40,7 +45,7 @@ ONGOING_PEER_SET_SUCCESS_EVENT = "WifiP2psetP2pPeerConfigureOnSuccess" CONNECT_SUCCESS_EVENT = "WifiP2pConnectOnSuccess" CREATE_GROUP_SUCCESS_EVENT = "WifiP2pCreateGroupOnSuccess" SET_CHANNEL_SUCCESS_EVENT = "WifiP2pSetChannelsOnSuccess" - +GROUP_INFO_AVAILABLE_EVENT = "WifiP2pOnGroupInfoAvailable" ###################################################### # Wifi P2p local service event @@ -60,14 +65,15 @@ UPNP_EVENT_SERVICELIST_KEY = "ServiceList" # Wifi P2p local service type #################################################### P2P_LOCAL_SERVICE_UPNP = 0 -P2P_LOCAL_SERVICE_IPP = 1 -P2P_LOCAL_SERVICE_AFP = 2 +P2P_LOCAL_SERVICE_IPP = 1 +P2P_LOCAL_SERVICE_AFP = 2 ###################################################### # Wifi P2p group capability ###################################################### P2P_GROUP_CAPAB_GROUP_OWNER = 1 + ###################################################### # Wifi P2p UPnP MediaRenderer local service ###################################################### @@ -78,17 +84,19 @@ class UpnpTestData(): uuid = "6859dede-8574-59ab-9332-123456789011" rootdevice = "upnp:rootdevice" + ###################################################### # Wifi P2p Bonjour IPP & AFP local service ###################################################### class IppTestData(): - ippInstanceName = "MyPrinter"; - ippRegistrationType = "_ipp._tcp"; - ippDomainName = "myprinter._ipp._tcp.local."; - ipp_txtRecord = {"txtvers":"1", "pdl": "application/postscript"} + ippInstanceName = "MyPrinter" + ippRegistrationType = "_ipp._tcp" + ippDomainName = "myprinter._ipp._tcp.local." + ipp_txtRecord = {"txtvers": "1", "pdl": "application/postscript"} + class AfpTestData(): - afpInstanceName = "Example"; - afpRegistrationType = "_afpovertcp._tcp"; - afpDomainName = "example._afpovertcp._tcp.local."; + afpInstanceName = "Example" + afpRegistrationType = "_afpovertcp._tcp" + afpDomainName = "example._afpovertcp._tcp.local." afp_txtRecord = {} diff --git a/acts/framework/acts/test_utils/wifi/p2p/wifi_p2p_test_utils.py b/acts/framework/acts/test_utils/wifi/p2p/wifi_p2p_test_utils.py index 21486b07ae..a4456a59bd 100755 --- a/acts/framework/acts/test_utils/wifi/p2p/wifi_p2p_test_utils.py +++ b/acts/framework/acts/test_utils/wifi/p2p/wifi_p2p_test_utils.py @@ -25,6 +25,7 @@ from acts import utils from acts.test_utils.wifi.p2p import wifi_p2p_const as p2pconsts import acts.utils + def is_discovered(event, ad): """Check an Android device exist in WifiP2pOnPeersAvailable event or not. @@ -41,7 +42,8 @@ def is_discovered(event, ad): return True return False -def check_disconnect(ad): + +def check_disconnect(ad, timeout=p2pconsts.DEFAULT_TIMEOUT): """Check an Android device disconnect or not Args: @@ -49,8 +51,7 @@ def check_disconnect(ad): """ ad.droid.wifiP2pRequestConnectionInfo() # wait disconnect event - ad.ed.pop_event(p2pconsts.DISCONNECTED_EVENT, - p2pconsts.DEFAULT_TIMEOUT) + ad.ed.pop_event(p2pconsts.DISCONNECTED_EVENT, timeout) def p2p_disconnect(ad): @@ -63,6 +64,7 @@ def p2p_disconnect(ad): ad.droid.wifiP2pRemoveGroup() check_disconnect(ad) + def p2p_connection_ping_test(ad, target_ip_address): """Let an Android device to start ping target_ip_address @@ -70,10 +72,13 @@ def p2p_connection_ping_test(ad, target_ip_address): ad: The android device target_ip_address: ip address which would like to ping """ - ad.log.debug("Run Ping Test, %s ping %s "% (ad.serial, target_ip_address)) + ad.log.debug("Run Ping Test, %s ping %s " % (ad.serial, target_ip_address)) asserts.assert_true( - acts.utils.adb_shell_ping(ad, count=3, dest_ip=target_ip_address, - timeout=20),"%s ping failed" % (ad.serial)) + acts.utils.adb_shell_ping(ad, + count=6, + dest_ip=target_ip_address, + timeout=20), "%s ping failed" % (ad.serial)) + def is_go(ad): """Check an Android p2p role is Go or not @@ -87,12 +92,12 @@ def is_go(ad): ad.log.debug("is go check") ad.droid.wifiP2pRequestConnectionInfo() ad_connect_info_event = ad.ed.pop_event( - p2pconsts.CONNECTION_INFO_AVAILABLE_EVENT, - p2pconsts.DEFAULT_TIMEOUT) + p2pconsts.CONNECTION_INFO_AVAILABLE_EVENT, p2pconsts.DEFAULT_TIMEOUT) if ad_connect_info_event['data']['isGroupOwner']: return True return False + def p2p_go_ip(ad): """Get GO IP address @@ -104,13 +109,40 @@ def p2p_go_ip(ad): ad.log.debug("p2p go ip") ad.droid.wifiP2pRequestConnectionInfo() ad_connect_info_event = ad.ed.pop_event( - p2pconsts.CONNECTION_INFO_AVAILABLE_EVENT, - p2pconsts.DEFAULT_TIMEOUT) - ad.log.debug("p2p go ip: %s" % ad_connect_info_event['data']['groupOwnerHostAddress']) + p2pconsts.CONNECTION_INFO_AVAILABLE_EVENT, p2pconsts.DEFAULT_TIMEOUT) + ad.log.debug("p2p go ip: %s" % + ad_connect_info_event['data']['groupOwnerHostAddress']) return ad_connect_info_event['data']['groupOwnerHostAddress'] + +def p2p_get_current_group(ad): + """Get current group information + + Args: + ad: The android device + Return: + p2p group information + """ + ad.log.debug("get current group") + ad.droid.wifiP2pRequestGroupInfo() + ad_group_info_event = ad.ed.pop_event(p2pconsts.GROUP_INFO_AVAILABLE_EVENT, + p2pconsts.DEFAULT_TIMEOUT) + ad.log.debug( + "p2p group: SSID:%s, password:%s, owner address: %s, interface: %s" % + (ad_group_info_event['data']['NetworkName'], + ad_group_info_event['data']['Passphrase'], + ad_group_info_event['data']['OwnerAddress'], + ad_group_info_event['data']['Interface'])) + return ad_group_info_event['data'] + + #trigger p2p connect to ad2 from ad1 -def p2p_connect(ad1, ad2, isReconnect, wpsSetup, isJoinExistingGroup=False): +def p2p_connect(ad1, + ad2, + isReconnect, + wpsSetup, + p2p_connect_type=p2pconsts.P2P_CONNECT_NEGOTIATION, + go_ad=None): """trigger p2p connect to ad2 from ad1 Args: @@ -119,70 +151,86 @@ def p2p_connect(ad1, ad2, isReconnect, wpsSetup, isJoinExistingGroup=False): isReconnect: boolean, if persist group is exist, isReconnect is true, otherswise is false. wpsSetup: which wps connection would like to use + p2p_connect_type: enumeration, which type this p2p connection is + go_ad: The group owner android device which is used for the invitation connection """ - ad1.log.info("Create p2p connection from %s to %s via wps: %s" % - (ad1.name, ad2.name, wpsSetup)) - if isJoinExistingGroup: + ad1.log.info("Create p2p connection from %s to %s via wps: %s type %d" % + (ad1.name, ad2.name, wpsSetup, p2p_connect_type)) + if p2p_connect_type == p2pconsts.P2P_CONNECT_INVITATION: + if go_ad is None: + go_ad = ad1 + find_p2p_device(ad1, ad2) + find_p2p_group_owner(ad2, go_ad) + elif p2p_connect_type == p2pconsts.P2P_CONNECT_JOIN: find_p2p_group_owner(ad1, ad2) else: find_p2p_device(ad1, ad2) time.sleep(p2pconsts.DEFAULT_SLEEPTIME) - wifi_p2p_config = {WifiP2PEnums.WifiP2pConfig.DEVICEADDRESS_KEY: - ad2.deviceAddress, WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY: - {WifiP2PEnums.WpsInfo.WPS_SETUP_KEY: wpsSetup}} + wifi_p2p_config = { + WifiP2PEnums.WifiP2pConfig.DEVICEADDRESS_KEY: ad2.deviceAddress, + WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY: { + WifiP2PEnums.WpsInfo.WPS_SETUP_KEY: wpsSetup + } + } ad1.droid.wifiP2pConnect(wifi_p2p_config) ad1.ed.pop_event(p2pconsts.CONNECT_SUCCESS_EVENT, - p2pconsts.DEFAULT_TIMEOUT) + p2pconsts.DEFAULT_TIMEOUT) time.sleep(p2pconsts.DEFAULT_SLEEPTIME) if not isReconnect: ad1.droid.requestP2pPeerConfigure() ad1_peerConfig = ad1.ed.pop_event( - p2pconsts.ONGOING_PEER_INFO_AVAILABLE_EVENT, - p2pconsts.DEFAULT_TIMEOUT) + p2pconsts.ONGOING_PEER_INFO_AVAILABLE_EVENT, + p2pconsts.DEFAULT_TIMEOUT) ad1.log.debug(ad1_peerConfig['data']) ad2.droid.requestP2pPeerConfigure() ad2_peerConfig = ad2.ed.pop_event( - p2pconsts.ONGOING_PEER_INFO_AVAILABLE_EVENT, - p2pconsts.DEFAULT_TIMEOUT) + p2pconsts.ONGOING_PEER_INFO_AVAILABLE_EVENT, + p2pconsts.DEFAULT_TIMEOUT) ad2.log.debug(ad2_peerConfig['data']) if wpsSetup == WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_DISPLAY: - asserts.assert_true(WifiP2PEnums.WpsInfo.WPS_PIN_KEY - in ad1_peerConfig['data'][ - WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY], - "Can't get pin value"); - ad2_peerConfig['data'][WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY][ - WifiP2PEnums.WpsInfo.WPS_PIN_KEY] = ad1_peerConfig[ - 'data'][WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY][ - WifiP2PEnums.WpsInfo.WPS_PIN_KEY] - ad2.droid.setP2pPeerConfigure(ad2_peerConfig['data']) - ad2.ed.pop_event(p2pconsts.ONGOING_PEER_SET_SUCCESS_EVENT, - p2pconsts.DEFAULT_TIMEOUT); - ad2.droid.wifiP2pAcceptConnection() + asserts.assert_true( + WifiP2PEnums.WpsInfo.WPS_PIN_KEY in ad1_peerConfig['data'][ + WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY], + "Can't get pin value") + ad2_peerConfig['data'][WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY][ + WifiP2PEnums.WpsInfo.WPS_PIN_KEY] = ad1_peerConfig['data'][ + WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY][ + WifiP2PEnums.WpsInfo.WPS_PIN_KEY] + ad2.droid.setP2pPeerConfigure(ad2_peerConfig['data']) + ad2.ed.pop_event(p2pconsts.ONGOING_PEER_SET_SUCCESS_EVENT, + p2pconsts.DEFAULT_TIMEOUT) + ad2.droid.wifiP2pAcceptConnection() elif wpsSetup == WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_KEYPAD: - asserts.assert_true( WifiP2PEnums.WpsInfo.WPS_PIN_KEY - in ad2_peerConfig['data'][ - WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY], - "Can't get pin value"); - ad1_peerConfig['data'][WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY][ - WifiP2PEnums.WpsInfo.WPS_PIN_KEY] = ad2_peerConfig[ - 'data'][WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY][ - WifiP2PEnums.WpsInfo.WPS_PIN_KEY] - ad1.droid.setP2pPeerConfigure(ad1_peerConfig['data']) - ad1.ed.pop_event(p2pconsts.ONGOING_PEER_SET_SUCCESS_EVENT, - p2pconsts.DEFAULT_TIMEOUT) - #Need to Accpet first in ad1 to avoid connect time out in ad2, - #the timeout just 1 sec in ad2 - ad1.droid.wifiP2pAcceptConnection() - time.sleep(p2pconsts.DEFAULT_SLEEPTIME) - ad2.droid.wifiP2pConfirmConnection() + asserts.assert_true( + WifiP2PEnums.WpsInfo.WPS_PIN_KEY in ad2_peerConfig['data'][ + WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY], + "Can't get pin value") + ad1_peerConfig['data'][WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY][ + WifiP2PEnums.WpsInfo.WPS_PIN_KEY] = ad2_peerConfig['data'][ + WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY][ + WifiP2PEnums.WpsInfo.WPS_PIN_KEY] + ad1.droid.setP2pPeerConfigure(ad1_peerConfig['data']) + ad1.ed.pop_event(p2pconsts.ONGOING_PEER_SET_SUCCESS_EVENT, + p2pconsts.DEFAULT_TIMEOUT) + #Need to Accept first in ad1 to avoid connect time out in ad2, + #the timeout just 1 sec in ad2 + ad1.droid.wifiP2pAcceptConnection() + time.sleep(p2pconsts.DEFAULT_SLEEPTIME) + ad2.droid.wifiP2pConfirmConnection() elif wpsSetup == WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_PBC: - ad2.droid.wifiP2pAcceptConnection() + ad2.droid.wifiP2pAcceptConnection() + if p2p_connect_type == p2pconsts.P2P_CONNECT_INVITATION: + time.sleep(p2pconsts.DEFAULT_SLEEPTIME) + go_ad.droid.wifiP2pAcceptConnection() #wait connected event - ad1.ed.pop_event(p2pconsts.CONNECTED_EVENT, - p2pconsts.DEFAULT_TIMEOUT) - ad2.ed.pop_event(p2pconsts.CONNECTED_EVENT, - p2pconsts.DEFAULT_TIMEOUT) + if p2p_connect_type == p2pconsts.P2P_CONNECT_INVITATION: + go_ad.ed.pop_event(p2pconsts.CONNECTED_EVENT, + p2pconsts.DEFAULT_TIMEOUT) + else: + ad1.ed.pop_event(p2pconsts.CONNECTED_EVENT, p2pconsts.DEFAULT_TIMEOUT) + ad2.ed.pop_event(p2pconsts.CONNECTED_EVENT, p2pconsts.DEFAULT_TIMEOUT) + def p2p_connect_with_config(ad1, ad2, network_name, passphrase, band): """trigger p2p connect to ad2 from ad1 with config @@ -194,28 +242,27 @@ def p2p_connect_with_config(ad1, ad2, network_name, passphrase, band): passphrase: the passphrase of the desired group. band: the operating band of the desired group. """ - ad1.log.info("Create p2p connection from %s to %s" % - (ad1.name, ad2.name)) + ad1.log.info("Create p2p connection from %s to %s" % (ad1.name, ad2.name)) find_p2p_device(ad1, ad2) time.sleep(p2pconsts.DEFAULT_SLEEPTIME) wifi_p2p_config = { - WifiP2PEnums.WifiP2pConfig.NETWORK_NAME: network_name, - WifiP2PEnums.WifiP2pConfig.PASSPHRASE: passphrase, - WifiP2PEnums.WifiP2pConfig.GROUP_BAND: band, - WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY: { - WifiP2PEnums.WpsInfo.WPS_SETUP_KEY: WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_PBC - } + WifiP2PEnums.WifiP2pConfig.NETWORK_NAME: network_name, + WifiP2PEnums.WifiP2pConfig.PASSPHRASE: passphrase, + WifiP2PEnums.WifiP2pConfig.GROUP_BAND: band, + WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY: { + WifiP2PEnums.WpsInfo.WPS_SETUP_KEY: + WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_PBC + } } ad1.droid.wifiP2pConnect(wifi_p2p_config) ad1.ed.pop_event(p2pconsts.CONNECT_SUCCESS_EVENT, - p2pconsts.DEFAULT_TIMEOUT) + p2pconsts.DEFAULT_TIMEOUT) time.sleep(p2pconsts.DEFAULT_SLEEPTIME) #wait connected event - ad1.ed.pop_event(p2pconsts.CONNECTED_EVENT, - p2pconsts.DEFAULT_TIMEOUT) - ad2.ed.pop_event(p2pconsts.CONNECTED_EVENT, - p2pconsts.DEFAULT_TIMEOUT) + ad1.ed.pop_event(p2pconsts.CONNECTED_EVENT, p2pconsts.DEFAULT_TIMEOUT) + ad2.ed.pop_event(p2pconsts.CONNECTED_EVENT, p2pconsts.DEFAULT_TIMEOUT) + def find_p2p_device(ad1, ad2): """Check an Android device ad1 can discover an Android device ad2 @@ -227,13 +274,14 @@ def find_p2p_device(ad1, ad2): ad1.droid.wifiP2pDiscoverPeers() ad2.droid.wifiP2pDiscoverPeers() p2p_find_result = False - while not p2p_find_result: + while not p2p_find_result: ad1_event = ad1.ed.pop_event(p2pconsts.PEER_AVAILABLE_EVENT, - p2pconsts.P2P_FIND_TIMEOUT) + p2pconsts.P2P_FIND_TIMEOUT) ad1.log.debug(ad1_event['data']) p2p_find_result = is_discovered(ad1_event, ad2) asserts.assert_true(p2p_find_result, - "DUT didn't discovered peer:%s device"% (ad2.name)) + "DUT didn't discovered peer:%s device" % (ad2.name)) + def find_p2p_group_owner(ad1, ad2): """Check an Android device ad1 can discover an Android device ad2 which @@ -243,21 +291,23 @@ def find_p2p_group_owner(ad1, ad2): ad1: The android device ad2: The android device which is a group owner """ - ad2.droid.wifiP2pStopPeerDiscovery(); + ad2.droid.wifiP2pStopPeerDiscovery() time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) ad1.droid.wifiP2pDiscoverPeers() p2p_find_result = False while not p2p_find_result: ad1_event = ad1.ed.pop_event(p2pconsts.PEER_AVAILABLE_EVENT, - p2pconsts.P2P_FIND_TIMEOUT) + p2pconsts.P2P_FIND_TIMEOUT) ad1.log.debug(ad1_event['data']) for device in ad1_event['data']['Peers']: - if (device['Name'] == ad2.name and - int(device['GroupCapability']) & p2pconsts.P2P_GROUP_CAPAB_GROUP_OWNER): + if (device['Name'] == ad2.name and int(device['GroupCapability']) + & p2pconsts.P2P_GROUP_CAPAB_GROUP_OWNER): ad2.deviceAddress = device['Address'] p2p_find_result = True - asserts.assert_true(p2p_find_result, - "DUT didn't discovered group owner peer:%s device"% (ad2.name)) + asserts.assert_true( + p2p_find_result, + "DUT didn't discovered group owner peer:%s device" % (ad2.name)) + def createP2pLocalService(ad, serviceCategory): """Based on serviceCategory to create p2p local service @@ -270,15 +320,16 @@ def createP2pLocalService(ad, serviceCategory): testData = genTestData(serviceCategory) if serviceCategory == p2pconsts.P2P_LOCAL_SERVICE_UPNP: ad.droid.wifiP2pCreateUpnpServiceInfo(testData[0], testData[1], - testData[2]) - elif (serviceCategory == p2pconsts.P2P_LOCAL_SERVICE_IPP or - serviceCategory == p2pconsts.P2P_LOCAL_SERVICE_AFP): + testData[2]) + elif (serviceCategory == p2pconsts.P2P_LOCAL_SERVICE_IPP + or serviceCategory == p2pconsts.P2P_LOCAL_SERVICE_AFP): ad.droid.wifiP2pCreateBonjourServiceInfo(testData[0], testData[1], - testData[2]) + testData[2]) ad.droid.wifiP2pAddLocalService() + def requestServiceAndCheckResult(ad_serviceProvider, ad_serviceReceiver, - serviceType, queryString1, queryString2): + serviceType, queryString1, queryString2): """Based on serviceType and query info, check service request result same as expect or not on an Android device ad_serviceReceiver. And remove p2p service request after result check. @@ -292,8 +343,8 @@ def requestServiceAndCheckResult(ad_serviceProvider, ad_serviceReceiver, """ expectData = genExpectTestData(serviceType, queryString1, queryString2) find_p2p_device(ad_serviceReceiver, ad_serviceProvider) - ad_serviceReceiver.droid.wifiP2pStopPeerDiscovery(); - ad_serviceReceiver.droid.wifiP2pClearServiceRequests(); + ad_serviceReceiver.droid.wifiP2pStopPeerDiscovery() + ad_serviceReceiver.droid.wifiP2pClearServiceRequests() time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) ad_serviceReceiver.droid.wifiP2pDiscoverServices() @@ -301,18 +352,19 @@ def requestServiceAndCheckResult(ad_serviceProvider, ad_serviceReceiver, service_id = 0 if (serviceType == WifiP2PEnums.WifiP2pServiceInfo.WIFI_P2P_SERVICE_TYPE_BONJOUR): - ad_serviceReceiver.log.info("Request bonjour service in \ + ad_serviceReceiver.log.info( + "Request bonjour service in \ %s with Query String %s and %s " % - (ad_serviceReceiver.name, queryString1, queryString2)) + (ad_serviceReceiver.name, queryString1, queryString2)) ad_serviceReceiver.log.info("expectData %s" % expectData) if queryString1 != None: service_id = ad_serviceReceiver.droid.wifiP2pAddDnssdServiceRequest( - queryString1,queryString2) + queryString1, queryString2) else: service_id = ad_serviceReceiver.droid.wifiP2pAddServiceRequest( - serviceType) + serviceType) ad_serviceReceiver.log.info("request bonjour service id %s" % - service_id) + service_id) ad_serviceReceiver.droid.wifiP2pSetDnsSdResponseListeners() ad_serviceReceiver.droid.wifiP2pDiscoverServices() ad_serviceReceiver.log.info("Check Service Listener") @@ -320,45 +372,47 @@ def requestServiceAndCheckResult(ad_serviceProvider, ad_serviceReceiver, try: dnssd_events = ad_serviceReceiver.ed.pop_all(p2pconsts.DNSSD_EVENT) dnssd_txrecord_events = ad_serviceReceiver.ed.pop_all( - p2pconsts.DNSSD_TXRECORD_EVENT) + p2pconsts.DNSSD_TXRECORD_EVENT) dns_service = WifiP2PEnums.WifiP2pDnsSdServiceResponse() for dnssd_event in dnssd_events: - if dnssd_event['data']['SourceDeviceAddress' - ] == ad_serviceProvider.deviceAddress: + if dnssd_event['data'][ + 'SourceDeviceAddress'] == ad_serviceProvider.deviceAddress: dns_service.InstanceName = dnssd_event['data'][ - p2pconsts.DNSSD_EVENT_INSTANCENAME_KEY] + p2pconsts.DNSSD_EVENT_INSTANCENAME_KEY] dns_service.RegistrationType = dnssd_event['data'][ - p2pconsts.DNSSD_EVENT_REGISTRATIONTYPE_KEY] + p2pconsts.DNSSD_EVENT_REGISTRATIONTYPE_KEY] dns_service.FullDomainName = "" dns_service.TxtRecordMap = "" serviceData[dns_service.toString()] = 1 for dnssd_txrecord_event in dnssd_txrecord_events: - if dnssd_txrecord_event['data']['SourceDeviceAddress' - ] == ad_serviceProvider.deviceAddress: + if dnssd_txrecord_event['data'][ + 'SourceDeviceAddress'] == ad_serviceProvider.deviceAddress: dns_service.InstanceName = "" dns_service.RegistrationType = "" dns_service.FullDomainName = dnssd_txrecord_event['data'][ - p2pconsts.DNSSD_TXRECORD_EVENT_FULLDOMAINNAME_KEY] + p2pconsts.DNSSD_TXRECORD_EVENT_FULLDOMAINNAME_KEY] dns_service.TxtRecordMap = dnssd_txrecord_event['data'][ - p2pconsts.DNSSD_TXRECORD_EVENT_TXRECORDMAP_KEY] + p2pconsts.DNSSD_TXRECORD_EVENT_TXRECORDMAP_KEY] serviceData[dns_service.toString()] = 1 ad_serviceReceiver.log.info("serviceData %s" % serviceData) if len(serviceData) == 0: - ad_serviceReceiver.droid.wifiP2pRemoveServiceRequest(service_id) - return -1; + ad_serviceReceiver.droid.wifiP2pRemoveServiceRequest( + service_id) + return -1 except queue.Empty as error: - ad_serviceReceiver.log.info("dnssd event is empty",) + ad_serviceReceiver.log.info("dnssd event is empty", ) elif (serviceType == - WifiP2PEnums.WifiP2pServiceInfo.WIFI_P2P_SERVICE_TYPE_UPNP): - ad_serviceReceiver.log.info("Request upnp service in %s with Query String %s "% - (ad_serviceReceiver.name, queryString1)) + WifiP2PEnums.WifiP2pServiceInfo.WIFI_P2P_SERVICE_TYPE_UPNP): + ad_serviceReceiver.log.info( + "Request upnp service in %s with Query String %s " % + (ad_serviceReceiver.name, queryString1)) ad_serviceReceiver.log.info("expectData %s" % expectData) if queryString1 != None: service_id = ad_serviceReceiver.droid.wifiP2pAddUpnpServiceRequest( - queryString1) + queryString1) else: service_id = ad_serviceReceiver.droid.wifiP2pAddServiceRequest( - WifiP2PEnums.WifiP2pServiceInfo.WIFI_P2P_SERVICE_TYPE_UPNP) + WifiP2PEnums.WifiP2pServiceInfo.WIFI_P2P_SERVICE_TYPE_UPNP) ad_serviceReceiver.droid.wifiP2pSetUpnpResponseListeners() ad_serviceReceiver.droid.wifiP2pDiscoverServices() ad_serviceReceiver.log.info("Check Service Listener") @@ -366,27 +420,33 @@ def requestServiceAndCheckResult(ad_serviceProvider, ad_serviceReceiver, try: upnp_events = ad_serviceReceiver.ed.pop_all(p2pconsts.UPNP_EVENT) for upnp_event in upnp_events: - if upnp_event['data']['Device']['Address' - ] == ad_serviceProvider.deviceAddress: + if upnp_event['data']['Device'][ + 'Address'] == ad_serviceProvider.deviceAddress: for service in upnp_event['data'][ p2pconsts.UPNP_EVENT_SERVICELIST_KEY]: serviceData[service] = 1 ad_serviceReceiver.log.info("serviceData %s" % serviceData) if len(serviceData) == 0: - ad_serviceReceiver.droid.wifiP2pRemoveServiceRequest(service_id) - return -1; + ad_serviceReceiver.droid.wifiP2pRemoveServiceRequest( + service_id) + return -1 except queue.Empty as error: - ad_serviceReceiver.log.info("p2p upnp event is empty",) + ad_serviceReceiver.log.info("p2p upnp event is empty", ) ad_serviceReceiver.log.info("Check ServiceList") - asserts.assert_true(checkServiceQueryResult(serviceData,expectData), - "ServiceList not same as Expect"); + asserts.assert_true(checkServiceQueryResult(serviceData, expectData), + "ServiceList not same as Expect") # After service checked, remove the service_id ad_serviceReceiver.droid.wifiP2pRemoveServiceRequest(service_id) return 0 -def requestServiceAndCheckResultWithRetry(ad_serviceProvider, ad_serviceReceiver, - serviceType, queryString1, queryString2, retryCount=3): + +def requestServiceAndCheckResultWithRetry(ad_serviceProvider, + ad_serviceReceiver, + serviceType, + queryString1, + queryString2, + retryCount=3): """ allow failures for requestServiceAndCheckResult. Service discovery might fail unexpectedly because the request packet might not be recevied by the service responder due to p2p state switch. @@ -401,14 +461,16 @@ def requestServiceAndCheckResultWithRetry(ad_serviceProvider, ad_serviceReceiver """ ret = 0 while retryCount > 0: - ret = requestServiceAndCheckResult(ad_serviceProvider, ad_serviceReceiver, - serviceType, queryString1, queryString2) + ret = requestServiceAndCheckResult(ad_serviceProvider, + ad_serviceReceiver, serviceType, + queryString1, queryString2) if (ret == 0): break retryCount -= 1 asserts.assert_equal(0, ret, "cannot find any services with retries.") + def checkServiceQueryResult(serviceList, expectServiceList): """Check serviceList same as expectServiceList or not @@ -427,6 +489,7 @@ def checkServiceQueryResult(serviceList, expectServiceList): del tempExpectServiceList[service] return len(tempExpectServiceList) == 0 and len(tempServiceList) == 0 + def genTestData(serviceCategory): """Based on serviceCategory to generator Test Data @@ -439,8 +502,10 @@ def genTestData(serviceCategory): if serviceCategory == p2pconsts.P2P_LOCAL_SERVICE_UPNP: testData.append(p2pconsts.UpnpTestData.uuid) testData.append(p2pconsts.UpnpTestData.serviceType) - testData.append([p2pconsts.UpnpTestData.AVTransport, - p2pconsts.UpnpTestData.ConnectionManager]) + testData.append([ + p2pconsts.UpnpTestData.AVTransport, + p2pconsts.UpnpTestData.ConnectionManager + ]) elif serviceCategory == p2pconsts.P2P_LOCAL_SERVICE_IPP: testData.append(p2pconsts.IppTestData.ippInstanceName) testData.append(p2pconsts.IppTestData.ippRegistrationType) @@ -452,6 +517,7 @@ def genTestData(serviceCategory): return testData + def genExpectTestData(serviceType, queryString1, queryString2): """Based on serviceCategory to generator expect serviceList @@ -477,7 +543,7 @@ def genExpectTestData(serviceType, queryString1, queryString2): return expectServiceList ipp_service.InstanceName = p2pconsts.IppTestData.ippInstanceName ipp_service.RegistrationType = ( - p2pconsts.IppTestData.ippRegistrationType + ".local.") + p2pconsts.IppTestData.ippRegistrationType + ".local.") ipp_service.FullDomainName = "" ipp_service.TxtRecordMap = "" expectServiceList[ipp_service.toString()] = 1 @@ -492,7 +558,7 @@ def genExpectTestData(serviceType, queryString1, queryString2): return expectServiceList ipp_service.InstanceName = p2pconsts.IppTestData.ippInstanceName ipp_service.RegistrationType = ( - p2pconsts.IppTestData.ippRegistrationType + ".local.") + p2pconsts.IppTestData.ippRegistrationType + ".local.") ipp_service.FullDomainName = "" ipp_service.TxtRecordMap = "" expectServiceList[ipp_service.toString()] = 1 @@ -505,7 +571,7 @@ def genExpectTestData(serviceType, queryString1, queryString2): afp_service.InstanceName = p2pconsts.AfpTestData.afpInstanceName afp_service.RegistrationType = ( - p2pconsts.AfpTestData.afpRegistrationType + ".local.") + p2pconsts.AfpTestData.afpRegistrationType + ".local.") afp_service.FullDomainName = "" afp_service.TxtRecordMap = "" expectServiceList[afp_service.toString()] = 1 @@ -519,23 +585,24 @@ def genExpectTestData(serviceType, queryString1, queryString2): return expectServiceList elif serviceType == WifiP2PEnums.WifiP2pServiceInfo.WIFI_P2P_SERVICE_TYPE_UPNP: upnp_service = "uuid:" + p2pconsts.UpnpTestData.uuid + "::" + ( - p2pconsts.UpnpTestData.rootdevice) + p2pconsts.UpnpTestData.rootdevice) expectServiceList[upnp_service] = 1 if queryString1 != "upnp:rootdevice": upnp_service = "uuid:" + p2pconsts.UpnpTestData.uuid + ( - "::" + p2pconsts.UpnpTestData.AVTransport) + "::" + p2pconsts.UpnpTestData.AVTransport) expectServiceList[upnp_service] = 1 upnp_service = "uuid:" + p2pconsts.UpnpTestData.uuid + ( - "::" + p2pconsts.UpnpTestData.ConnectionManager) + "::" + p2pconsts.UpnpTestData.ConnectionManager) expectServiceList[upnp_service] = 1 upnp_service = "uuid:" + p2pconsts.UpnpTestData.uuid + ( - "::" + p2pconsts.UpnpTestData.serviceType) + "::" + p2pconsts.UpnpTestData.serviceType) expectServiceList[upnp_service] = 1 - upnp_service = "uuid:"+p2pconsts.UpnpTestData.uuid + upnp_service = "uuid:" + p2pconsts.UpnpTestData.uuid expectServiceList[upnp_service] = 1 return expectServiceList + def p2p_create_group(ad): """Create a group as Group Owner @@ -544,9 +611,10 @@ def p2p_create_group(ad): """ ad.droid.wifiP2pCreateGroup() ad.ed.pop_event(p2pconsts.CREATE_GROUP_SUCCESS_EVENT, - p2pconsts.DEFAULT_TIMEOUT) + p2pconsts.DEFAULT_TIMEOUT) time.sleep(p2pconsts.DEFAULT_SLEEPTIME) + def p2p_create_group_with_config(ad, network_name, passphrase, band): """Create a group as Group Owner @@ -554,19 +622,22 @@ def p2p_create_group_with_config(ad, network_name, passphrase, band): ad: The android device """ wifi_p2p_config = { - WifiP2PEnums.WifiP2pConfig.NETWORK_NAME: network_name, - WifiP2PEnums.WifiP2pConfig.PASSPHRASE: passphrase, - WifiP2PEnums.WifiP2pConfig.GROUP_BAND: band, - WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY: { - WifiP2PEnums.WpsInfo.WPS_SETUP_KEY: WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_PBC - } + WifiP2PEnums.WifiP2pConfig.NETWORK_NAME: network_name, + WifiP2PEnums.WifiP2pConfig.PASSPHRASE: passphrase, + WifiP2PEnums.WifiP2pConfig.GROUP_BAND: band, + WifiP2PEnums.WifiP2pConfig.WPSINFO_KEY: { + WifiP2PEnums.WpsInfo.WPS_SETUP_KEY: + WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_PBC + } } ad.droid.wifiP2pCreateGroupWithConfig(wifi_p2p_config) ad.ed.pop_event(p2pconsts.CREATE_GROUP_SUCCESS_EVENT, - p2pconsts.DEFAULT_TIMEOUT) + p2pconsts.DEFAULT_TIMEOUT) time.sleep(p2pconsts.DEFAULT_SLEEPTIME) -def wifi_p2p_set_channels_for_current_group(ad, listening_chan, operating_chan): + +def wifi_p2p_set_channels_for_current_group(ad, listening_chan, + operating_chan): """Sets the listening channel and operating channel of the current group created with initialize. @@ -579,8 +650,8 @@ def wifi_p2p_set_channels_for_current_group(ad, listening_chan, operating_chan): ad.ed.pop_event(p2pconsts.SET_CHANNEL_SUCCESS_EVENT, p2pconsts.DEFAULT_TIMEOUT) -class WifiP2PEnums(): +class WifiP2PEnums(): class WifiP2pConfig(): DEVICEADDRESS_KEY = "deviceAddress" WPSINFO_KEY = "wpsInfo" @@ -612,12 +683,12 @@ class WifiP2PEnums(): class WifiP2pDnsSdServiceResponse(): def __init__(self): pass + InstanceName = "" RegistrationType = "" FullDomainName = "" TxtRecordMap = {} + def toString(self): return self.InstanceName + self.RegistrationType + ( - self.FullDomainName + str(self.TxtRecordMap)) - - + self.FullDomainName + str(self.TxtRecordMap)) diff --git a/acts/framework/acts/test_utils/wifi/wifi_constants.py b/acts/framework/acts/test_utils/wifi/wifi_constants.py index 21f13d2fc1..3a4990571c 100644 --- a/acts/framework/acts/test_utils/wifi/wifi_constants.py +++ b/acts/framework/acts/test_utils/wifi/wifi_constants.py @@ -37,18 +37,37 @@ SOFTAP_CALLBACK_EVENT = "WifiManagerSoftApCallback-" # Callback Event for softap state change # WifiManagerSoftApCallback-[callbackId]-OnStateChanged SOFTAP_STATE_CHANGED = "-OnStateChanged" -# Cllback Event for client number change: -# WifiManagerSoftApCallback-[callbackId]-OnNumClientsChanged -SOFTAP_NUMBER_CLIENTS_CHANGED = "-OnNumClientsChanged" -SOFTAP_NUMBER_CLIENTS_CALLBACK_KEY = "NumClients" SOFTAP_STATE_CHANGE_CALLBACK_KEY = "State" WIFI_AP_DISABLING_STATE = 10 WIFI_AP_DISABLED_STATE = 11 WIFI_AP_ENABLING_STATE = 12 WIFI_AP_ENABLED_STATE = 13 WIFI_AP_FAILED_STATE = 14 -DEFAULT_SOFTAP_TIMEOUT_S = 600 # 10 minutes +# Callback Event for client number change: +# WifiManagerSoftApCallback-[callbackId]-OnNumClientsChanged +SOFTAP_NUMBER_CLIENTS_CHANGED = "-OnNumClientsChanged" +SOFTAP_NUMBER_CLIENTS_CALLBACK_KEY = "NumClients" +SOFTAP_CLIENTS_MACS_CALLBACK_KEY = "MacAddresses" +# Callback Event for softap info change +SOFTAP_INFO_CHANGED = "-OnInfoChanged" +SOFTAP_INFO_FREQUENCY_CALLBACK_KEY = "frequency" +SOFTAP_INFO_BANDWIDTH_CALLBACK_KEY = "bandwidth" +# Callback Event for softap client blocking +SOFTAP_BLOCKING_CLIENT_CONNECTING = "-OnBlockedClientConnecting" +SOFTAP_BLOCKING_CLIENT_REASON_KEY = "BlockedReason" +SOFTAP_BLOCKING_CLIENT_WIFICLIENT_KEY = "WifiClient" +SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER = 0 +SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS = 1 + +# Callback Event for softap capability +SOFTAP_CAPABILITY_CHANGED = "-OnCapabilityChanged" +SOFTAP_CAPABILITY_MAX_SUPPORTED_CLIENTS = "maxSupportedClients" +SOFTAP_CAPABILITY_FEATURE_ACS = "acsOffloadSupported" +SOFTAP_CAPABILITY_FEATURE_CLIENT_CONTROL = "clientForceDisconnectSupported" +SOFTAP_CAPABILITY_FEATURE_WPA3_SAE = "wpa3SaeSupported" + +DEFAULT_SOFTAP_TIMEOUT_S = 600 # 10 minutes # AP related constants AP_MAIN = "main_AP" diff --git a/acts/framework/acts/test_utils/wifi/wifi_performance_test_utils.py b/acts/framework/acts/test_utils/wifi/wifi_performance_test_utils.py index 39896bc1f5..324d9db0cd 100644 --- a/acts/framework/acts/test_utils/wifi/wifi_performance_test_utils.py +++ b/acts/framework/acts/test_utils/wifi/wifi_performance_test_utils.py @@ -84,7 +84,8 @@ class LinkLayerStats(): def update_stats(self): if self.llstats_enabled: try: - llstats_output = self.dut.adb.shell(self.LLSTATS_CMD, timeout=0.1) + llstats_output = self.dut.adb.shell(self.LLSTATS_CMD, + timeout=0.1) except: llstats_output = '' else: @@ -682,12 +683,53 @@ def get_ping_stats_nb(src_device, dest_address, ping_duration, ping_interval, ping_interval, ping_size) +# Iperf utilities @nonblocking def start_iperf_client_nb(iperf_client, iperf_server_address, iperf_args, tag, timeout): return iperf_client.start(iperf_server_address, iperf_args, tag, timeout) +def get_iperf_arg_string(duration, + reverse_direction, + interval=1, + traffic_type='TCP', + socket_size=None, + num_processes=1, + udp_throughput='1000M', + ipv6=False): + """Function to format iperf client arguments. + + This function takes in iperf client parameters and returns a properly + formatter iperf arg string to be used in throughput tests. + + Args: + duration: iperf duration in seconds + reverse_direction: boolean controlling the -R flag for iperf clients + interval: iperf print interval + traffic_type: string specifying TCP or UDP traffic + socket_size: string specifying TCP window or socket buffer, e.g., 2M + num_processes: int specifying number of iperf processes + udp_throughput: string specifying TX throughput in UDP tests, e.g. 100M + ipv6: boolean controlling the use of IP V6 + Returns: + iperf_args: string of formatted iperf args + """ + iperf_args = '-i {} -t {} -J '.format(interval, duration) + if ipv6: + iperf_args = iperf_args + '-6 ' + if traffic_type.upper() == 'UDP': + iperf_args = iperf_args + '-u -b {} -l 1400 -P {} '.format( + udp_throughput, num_processes) + elif traffic_type.upper() == 'TCP': + iperf_args = iperf_args + '-P {} '.format(num_processes) + if socket_size: + iperf_args = iperf_args + '-w {} '.format(socket_size) + if reverse_direction: + iperf_args = iperf_args + ' -R' + return iperf_args + + # Rssi Utilities def empty_rssi_result(): return collections.OrderedDict([('data', []), ('mean', None), @@ -699,7 +741,8 @@ def get_connected_rssi(dut, polling_frequency=SHORT_SLEEP, first_measurement_delay=0, disconnect_warning=True, - ignore_samples=0): + ignore_samples=0, + interface=None): """Gets all RSSI values reported for the connected access point/BSSID. Args: @@ -716,7 +759,7 @@ def get_connected_rssi(dut, # yapf: disable connected_rssi = collections.OrderedDict( [('time_stamp', []), - ('bssid', []), ('frequency', []), + ('bssid', []), ('ssid', []), ('frequency', []), ('signal_poll_rssi', empty_rssi_result()), ('signal_poll_avg_rssi', empty_rssi_result()), ('chain_0_rssi', empty_rssi_result()), @@ -729,7 +772,11 @@ def get_connected_rssi(dut, measurement_start_time = time.time() connected_rssi['time_stamp'].append(measurement_start_time - t0) # Get signal poll RSSI - status_output = dut.adb.shell(WPA_CLI_STATUS) + if interface is None: + status_output = dut.adb.shell(WPA_CLI_STATUS) + else: + status_output = dut.adb.shell( + 'wpa_cli -i {} status'.format(interface)) match = re.search('bssid=.*', status_output) if match: current_bssid = match.group(0).split('=')[1] @@ -740,7 +787,17 @@ def get_connected_rssi(dut, if disconnect_warning and previous_bssid != 'disconnected': logging.warning('WIFI DISCONNECT DETECTED!') previous_bssid = current_bssid - signal_poll_output = dut.adb.shell(SIGNAL_POLL) + match = re.search('\s+ssid=.*', status_output) + if match: + ssid = match.group(0).split('=')[1] + connected_rssi['ssid'].append(ssid) + else: + connected_rssi['ssid'].append('disconnected') + if interface is None: + signal_poll_output = dut.adb.shell(SIGNAL_POLL) + else: + signal_poll_output = dut.adb.shell( + 'wpa_cli -i {} signal_poll'.format(interface)) match = re.search('FREQUENCY=.*', signal_poll_output) if match: frequency = int(match.group(0).split('=')[1]) @@ -764,8 +821,12 @@ def get_connected_rssi(dut, else: connected_rssi['signal_poll_avg_rssi']['data'].append( RSSI_ERROR_VAL) + # Get per chain RSSI - per_chain_rssi = dut.adb.shell(STATION_DUMP) + if interface is None: + per_chain_rssi = dut.adb.shell(STATION_DUMP) + else: + per_chain_rssi = '' match = re.search('.*signal avg:.*', per_chain_rssi) if match: per_chain_rssi = per_chain_rssi[per_chain_rssi.find('[') + @@ -808,10 +869,11 @@ def get_connected_rssi_nb(dut, polling_frequency=SHORT_SLEEP, first_measurement_delay=0, disconnect_warning=True, - ignore_samples=0): + ignore_samples=0, + interface=None): return get_connected_rssi(dut, num_measurements, polling_frequency, first_measurement_delay, disconnect_warning, - ignore_samples) + ignore_samples, interface) def get_scan_rssi(dut, tracked_bssids, num_measurements=1): @@ -1106,41 +1168,6 @@ def get_server_address(ssh_connection, dut_ip, subnet_mask): logging.error('No IP address found in requested subnet') -def get_iperf_arg_string(duration, - reverse_direction, - interval=1, - traffic_type='TCP', - tcp_window=None, - tcp_processes=1, - udp_throughput='1000M'): - """Function to format iperf client arguments. - - This function takes in iperf client parameters and returns a properly - formatter iperf arg string to be used in throughput tests. - - Args: - duration: iperf duration in seconds - reverse_direction: boolean controlling the -R flag for iperf clients - interval: iperf print interval - traffic_type: string specifying TCP or UDP traffic - tcp_window: string specifying TCP window, e.g., 2M - tcp_processes: int specifying number of tcp processes - udp_throughput: string specifying TX throughput in UDP tests, e.g. 100M - Returns: - iperf_args: string of formatted iperf args - """ - iperf_args = '-i {} -t {} -J '.format(interval, duration) - if traffic_type.upper() == 'UDP': - iperf_args = iperf_args + '-u -b {} -l 1400'.format(udp_throughput) - elif traffic_type.upper() == 'TCP': - iperf_args = iperf_args + '-P {}'.format(tcp_processes) - if tcp_window: - iperf_args = iperf_args + '-w {}'.format(tcp_window) - if reverse_direction: - iperf_args = iperf_args + ' -R' - return iperf_args - - def get_dut_temperature(dut): """Function to get dut temperature. diff --git a/acts/framework/acts/test_utils/wifi/wifi_test_utils.py b/acts/framework/acts/test_utils/wifi/wifi_test_utils.py index 0f75b5a378..22cc69c717 100755 --- a/acts/framework/acts/test_utils/wifi/wifi_test_utils.py +++ b/acts/framework/acts/test_utils/wifi/wifi_test_utils.py @@ -16,6 +16,7 @@ import logging import os +import re import shutil import time @@ -49,7 +50,7 @@ SPEED_OF_LIGHT = 299792458 DEFAULT_PING_ADDR = "https://www.google.com/robots.txt" -roaming_attn = { +ROAMING_ATTN = { "AP1_on_AP2_off": [ 0, 0, @@ -73,24 +74,46 @@ roaming_attn = { class WifiEnums(): - SSID_KEY = "SSID" + SSID_KEY = "SSID" # Used for Wifi & SoftAp SSID_PATTERN_KEY = "ssidPattern" NETID_KEY = "network_id" - BSSID_KEY = "BSSID" + BSSID_KEY = "BSSID" # Used for Wifi & SoftAp BSSID_PATTERN_KEY = "bssidPattern" - PWD_KEY = "password" + PWD_KEY = "password" # Used for Wifi & SoftAp frequency_key = "frequency" - APBAND_KEY = "apBand" - HIDDEN_KEY = "hiddenSSID" + HIDDEN_KEY = "hiddenSSID" # Used for Wifi & SoftAp IS_APP_INTERACTION_REQUIRED = "isAppInteractionRequired" IS_USER_INTERACTION_REQUIRED = "isUserInteractionRequired" - IS_METERED = "isMetered" + IS_SUGGESTION_METERED = "isMetered" PRIORITY = "priority" - SECURITY = "security" - - WIFI_CONFIG_APBAND_2G = 0 - WIFI_CONFIG_APBAND_5G = 1 - WIFI_CONFIG_APBAND_AUTO = -1 + SECURITY = "security" # Used for Wifi & SoftAp + + # Used for SoftAp + AP_BAND_KEY = "apBand" + AP_CHANNEL_KEY = "apChannel" + AP_MAXCLIENTS_KEY = "MaxNumberOfClients" + AP_SHUTDOWNTIMEOUT_KEY = "ShutdownTimeoutMillis" + AP_SHUTDOWNTIMEOUTENABLE_KEY = "AutoShutdownEnabled" + AP_CLIENTCONTROL_KEY = "ClientControlByUserEnabled" + AP_ALLOWEDLIST_KEY = "AllowedClientList" + AP_BLOCKEDLIST_KEY = "BlockedClientList" + + WIFI_CONFIG_SOFTAP_BAND_2G = 1 + WIFI_CONFIG_SOFTAP_BAND_5G = 2 + WIFI_CONFIG_SOFTAP_BAND_2G_5G = 3 + WIFI_CONFIG_SOFTAP_BAND_6G = 4 + WIFI_CONFIG_SOFTAP_BAND_2G_6G = 5 + WIFI_CONFIG_SOFTAP_BAND_5G_6G = 6 + WIFI_CONFIG_SOFTAP_BAND_ANY = 7 + + # DO NOT USE IT for new test case! Replaced by WIFI_CONFIG_SOFTAP_BAND_ + WIFI_CONFIG_APBAND_2G = WIFI_CONFIG_SOFTAP_BAND_2G + WIFI_CONFIG_APBAND_5G = WIFI_CONFIG_SOFTAP_BAND_5G + WIFI_CONFIG_APBAND_AUTO = WIFI_CONFIG_SOFTAP_BAND_2G_5G + + WIFI_CONFIG_APBAND_2G_OLD = 0 + WIFI_CONFIG_APBAND_5G_OLD = 1 + WIFI_CONFIG_APBAND_AUTO_OLD = -1 WIFI_WPS_INFO_PBC = 0 WIFI_WPS_INFO_DISPLAY = 1 @@ -98,6 +121,12 @@ class WifiEnums(): WIFI_WPS_INFO_LABEL = 3 WIFI_WPS_INFO_INVALID = 4 + class SoftApSecurityType(): + OPEN = "NONE" + WPA2 = "WPA2_PSK" + WPA3_SAE_TRANSITION = "WPA3_SAE_TRANSITION" + WPA3_SAE = "WPA3_SAE" + class CountryCode(): CHINA = "CN" JAPAN = "JP" @@ -147,6 +176,7 @@ class WifiEnums(): FQDN = "FQDN" FRIENDLY_NAME = "providerFriendlyName" ROAMING_IDS = "roamingConsortiumIds" + OCSP = "ocsp" # End of Macros for EAP # Macros for wifi p2p. @@ -642,6 +672,7 @@ def _wifi_toggle_state(ad, new_state=None): ad.ed.clear_all_events() # Setting wifi state. ad.droid.wifiToggleState(new_state) + time.sleep(2) fail_msg = "Failed to set Wi-Fi state to %s on %s." % (new_state, ad.serial) try: @@ -923,7 +954,7 @@ def start_wifi_tethering(ad, ssid, password, band=None, hidden=None): if password: config[WifiEnums.PWD_KEY] = password if band: - config[WifiEnums.APBAND_KEY] = band + config[WifiEnums.AP_BAND_KEY] = band if hidden: config[WifiEnums.HIDDEN_KEY] = hidden asserts.assert_true( @@ -943,20 +974,121 @@ def start_wifi_tethering(ad, ssid, password, band=None, hidden=None): ad.droid.wifiStopTrackingTetherStateChange() -def save_wifi_soft_ap_config(ad, wifi_config, band=None, hidden=None): - """ Save a soft ap configuration """ +def save_wifi_soft_ap_config(ad, wifi_config, band=None, hidden=None, + security=None, password=None, + channel=None, max_clients=None, + shutdown_timeout_enable=None, + shutdown_timeout_millis=None, + client_control_enable=None, + allowedList=None, blockedList=None): + """ Save a soft ap configuration and verified + Args: + ad: android_device to set soft ap configuration. + wifi_config: a soft ap configuration object, at least include SSID. + band: specifies the band for the soft ap. + hidden: specifies the soft ap need to broadcast its SSID or not. + security: specifies the security type for the soft ap. + password: specifies the password for the soft ap. + channel: specifies the channel for the soft ap. + max_clients: specifies the maximum connected client number. + shutdown_timeout_enable: specifies the auto shut down enable or not. + shutdown_timeout_millis: specifies the shut down timeout value. + client_control_enable: specifies the client control enable or not. + allowedList: specifies allowed clients list. + blockedList: specifies blocked clients list. + """ + if security and password: + wifi_config[WifiEnums.SECURITY] = security + wifi_config[WifiEnums.PWD_KEY] = password if band: - wifi_config[WifiEnums.APBAND_KEY] = band + wifi_config[WifiEnums.AP_BAND_KEY] = band if hidden: wifi_config[WifiEnums.HIDDEN_KEY] = hidden + if channel and band: + wifi_config[WifiEnums.AP_BAND_KEY] = band + wifi_config[WifiEnums.AP_CHANNEL_KEY] = channel + if max_clients: + wifi_config[WifiEnums.AP_MAXCLIENTS_KEY] = max_clients + if shutdown_timeout_enable: + wifi_config[ + WifiEnums.AP_SHUTDOWNTIMEOUTENABLE_KEY] = shutdown_timeout_enable + if shutdown_timeout_millis: + wifi_config[ + WifiEnums.AP_SHUTDOWNTIMEOUT_KEY] = shutdown_timeout_millis + if client_control_enable: + wifi_config[WifiEnums.AP_CLIENTCONTROL_KEY] = client_control_enable + if allowedList: + wifi_config[WifiEnums.AP_ALLOWEDLIST_KEY] = allowedList + if blockedList: + wifi_config[WifiEnums.AP_BLOCKEDLIST_KEY] = blockedList + + if WifiEnums.AP_CHANNEL_KEY in wifi_config and wifi_config[ + WifiEnums.AP_CHANNEL_KEY] == 0: + del wifi_config[WifiEnums.AP_CHANNEL_KEY] + + if WifiEnums.SECURITY in wifi_config and wifi_config[ + WifiEnums.SECURITY] == WifiEnums.SoftApSecurityType.OPEN: + del wifi_config[WifiEnums.SECURITY] + del wifi_config[WifiEnums.PWD_KEY] + asserts.assert_true(ad.droid.wifiSetWifiApConfiguration(wifi_config), "Failed to set WifiAp Configuration") wifi_ap = ad.droid.wifiGetApConfiguration() asserts.assert_true( wifi_ap[WifiEnums.SSID_KEY] == wifi_config[WifiEnums.SSID_KEY], - "Hotspot SSID doesn't match with expected SSID") + "Hotspot SSID doesn't match") + if WifiEnums.SECURITY in wifi_config: + asserts.assert_true( + wifi_ap[WifiEnums.SECURITY] == wifi_config[WifiEnums.SECURITY], + "Hotspot Security doesn't match") + if WifiEnums.PWD_KEY in wifi_config: + asserts.assert_true( + wifi_ap[WifiEnums.PWD_KEY] == wifi_config[WifiEnums.PWD_KEY], + "Hotspot Password doesn't match") + if WifiEnums.HIDDEN_KEY in wifi_config: + asserts.assert_true( + wifi_ap[WifiEnums.HIDDEN_KEY] == wifi_config[WifiEnums.HIDDEN_KEY], + "Hotspot hidden setting doesn't match") + + if WifiEnums.AP_BAND_KEY in wifi_config: + asserts.assert_true( + wifi_ap[WifiEnums.AP_BAND_KEY] == wifi_config[WifiEnums.AP_BAND_KEY], + "Hotspot Band doesn't match") + if WifiEnums.AP_CHANNEL_KEY in wifi_config: + asserts.assert_true( + wifi_ap[WifiEnums.AP_CHANNEL_KEY] == wifi_config[ + WifiEnums.AP_CHANNEL_KEY], "Hotspot Channel doesn't match") + if WifiEnums.AP_MAXCLIENTS_KEY in wifi_config: + asserts.assert_true( + wifi_ap[WifiEnums.AP_MAXCLIENTS_KEY] == wifi_config[ + WifiEnums.AP_MAXCLIENTS_KEY], "Hotspot Max Clients doesn't match") + if WifiEnums.AP_SHUTDOWNTIMEOUTENABLE_KEY in wifi_config: + asserts.assert_true( + wifi_ap[WifiEnums.AP_SHUTDOWNTIMEOUTENABLE_KEY] == wifi_config[ + WifiEnums.AP_SHUTDOWNTIMEOUTENABLE_KEY], + "Hotspot ShutDown feature flag doesn't match") + if WifiEnums.AP_SHUTDOWNTIMEOUT_KEY in wifi_config: + asserts.assert_true( + wifi_ap[WifiEnums.AP_SHUTDOWNTIMEOUT_KEY] == wifi_config[ + WifiEnums.AP_SHUTDOWNTIMEOUT_KEY], + "Hotspot ShutDown timeout setting doesn't match") + if WifiEnums.AP_CLIENTCONTROL_KEY in wifi_config: + asserts.assert_true( + wifi_ap[WifiEnums.AP_CLIENTCONTROL_KEY] == wifi_config[ + WifiEnums.AP_CLIENTCONTROL_KEY], + "Hotspot Client control flag doesn't match") + if WifiEnums.AP_ALLOWEDLIST_KEY in wifi_config: + asserts.assert_true( + wifi_ap[WifiEnums.AP_ALLOWEDLIST_KEY] == wifi_config[ + WifiEnums.AP_ALLOWEDLIST_KEY], + "Hotspot Allowed List doesn't match") + if WifiEnums.AP_BLOCKEDLIST_KEY in wifi_config: + asserts.assert_true( + wifi_ap[WifiEnums.AP_BLOCKEDLIST_KEY] == wifi_config[ + WifiEnums.AP_BLOCKEDLIST_KEY], + "Hotspot Blocked List doesn't match") def start_wifi_tethering_saved_config(ad): """ Turn on wifi hotspot with a config that is already saved """ @@ -974,7 +1106,6 @@ def start_wifi_tethering_saved_config(ad): def stop_wifi_tethering(ad): """Stops wifi tethering on an android_device. - Args: ad: android_device to stop wifi tethering on. """ @@ -1029,7 +1160,7 @@ def toggle_wifi_and_wait_for_reconnection(ad, num_of_tries=num_of_tries) -def _toggle_wifi_and_wait_for_reconnection(ad, network, num_of_tries=1): +def _toggle_wifi_and_wait_for_reconnection(ad, network, num_of_tries=3): """Toggle wifi state and then wait for Android device to reconnect to the provided wifi network. @@ -1345,11 +1476,8 @@ def _wifi_connect(ad, network, num_of_tries=1, check_connectivity=True): ad.serial) ad.log.info("Connected to Wi-Fi network %s.", actual_ssid) - # Wait for data connection to stabilize. - time.sleep(5) - if check_connectivity: - internet = validate_connection(ad, DEFAULT_PING_ADDR, 10) + internet = validate_connection(ad, DEFAULT_PING_ADDR) if not internet: raise signals.TestFailure("Failed to connect to internet on %s" % expected_ssid) @@ -1422,9 +1550,6 @@ def _wifi_connect_by_id(ad, network_id, num_of_tries=1): ad.log.info("Connected to Wi-Fi network %s with %d network id.", expected_ssid, network_id) - # Wait for data connection to stabilize. - time.sleep(5) - internet = validate_connection(ad, DEFAULT_PING_ADDR) if not internet: raise signals.TestFailure("Failed to connect to internet on %s" % @@ -1652,9 +1777,6 @@ def _wifi_passpoint_connect(ad, passpoint_network, num_of_tries=1): "Connected to the wrong network on %s." % ad.serial) ad.log.info("Connected to Wi-Fi passpoint network %s.", actual_ssid) - # Wait for data connection to stabilize. - time.sleep(5) - internet = validate_connection(ad, DEFAULT_PING_ADDR) if not internet: raise signals.TestFailure("Failed to connect to internet on %s" % @@ -1782,7 +1904,8 @@ def convert_pem_key_to_pkcs8(in_file, out_file): utils.exe_cmd(cmd) -def validate_connection(ad, ping_addr=DEFAULT_PING_ADDR, wait_time=2): +def validate_connection(ad, ping_addr=DEFAULT_PING_ADDR, wait_time=15, + ping_gateway=True): """Validate internet connection by pinging the address provided. Args: @@ -1794,9 +1917,22 @@ def validate_connection(ad, ping_addr=DEFAULT_PING_ADDR, wait_time=2): ping output if successful, NULL otherwise. """ # wait_time to allow for DHCP to complete. - time.sleep(wait_time) - ping = ad.droid.httpPing(ping_addr) - ad.log.info("Http ping result: %s.", ping) + for i in range(wait_time): + if ad.droid.connectivityNetworkIsConnected(): + break + time.sleep(1) + ping = False + try: + ping = ad.droid.httpPing(ping_addr) + ad.log.info("Http ping result: %s.", ping) + except: + pass + if not ping and ping_gateway: + ad.log.info("Http ping failed. Pinging default gateway") + gw = ad.droid.connectivityGetIPv4DefaultGateway() + result = ad.adb.shell("ping -c 6 {}".format(gw)) + ad.log.info("Default gateway ping result: %s" % result) + ping = False if "100% packet loss" in result else True return ping @@ -1952,12 +2088,13 @@ def group_attenuators(attenuators): return [attn0, attn1] -def set_attns(attenuator, attn_val_name): +def set_attns(attenuator, attn_val_name, roaming_attn=ROAMING_ATTN): """Sets attenuation values on attenuators used in this test. Args: attenuator: The attenuator object. attn_val_name: Name of the attenuation value pair to use. + roaming_attn: Dictionary specifying the attenuation params. """ logging.info("Set attenuation values to %s", roaming_attn[attn_val_name]) try: @@ -1970,7 +2107,11 @@ def set_attns(attenuator, attn_val_name): attn_val_name) raise -def set_attns_steps(attenuators, atten_val_name, steps=10, wait_time=12): +def set_attns_steps(attenuators, + atten_val_name, + roaming_attn=ROAMING_ATTN, + steps=10, + wait_time=12): """Set attenuation values on attenuators used in this test. It will change the attenuation values linearly from current value to target value step by step. @@ -1979,6 +2120,7 @@ def set_attns_steps(attenuators, atten_val_name, steps=10, wait_time=12): attenuators: The list of attenuator objects that you want to change their attenuation value. atten_val_name: Name of the attenuation value pair to use. + roaming_attn: Dictionary specifying the attenuation params. steps: Number of attenuator changes to reach the target value. wait_time: Sleep time for each change of attenuator. """ @@ -1994,7 +2136,11 @@ def set_attns_steps(attenuators, atten_val_name, steps=10, wait_time=12): time.sleep(wait_time) -def trigger_roaming_and_validate(dut, attenuator, attn_val_name, expected_con): +def trigger_roaming_and_validate(dut, + attenuator, + attn_val_name, + expected_con, + roaming_attn=ROAMING_ATTN): """Sets attenuators to trigger roaming and validate the DUT connected to the BSSID expected. @@ -2002,14 +2148,13 @@ def trigger_roaming_and_validate(dut, attenuator, attn_val_name, expected_con): attenuator: The attenuator object. attn_val_name: Name of the attenuation value pair to use. expected_con: The network information of the expected network. + roaming_attn: Dictionary specifying the attenaution params. """ expected_con = { WifiEnums.SSID_KEY: expected_con[WifiEnums.SSID_KEY], WifiEnums.BSSID_KEY: expected_con["bssid"], } - set_attns(attenuator, attn_val_name) - logging.info("Wait %ss for roaming to finish.", ROAMING_TIMEOUT) - time.sleep(ROAMING_TIMEOUT) + set_attns_steps(attenuator, attn_val_name, roaming_attn) verify_wifi_connection_info(dut, expected_con) expected_bssid = expected_con[WifiEnums.BSSID_KEY] @@ -2038,6 +2183,13 @@ def start_softap_and_verify(ad, band): Returns: dict, the softAP config. """ + # Register before start the test. + callbackId = ad.dut.droid.registerSoftApCallback() + # Check softap info value is default + frequency, bandwdith = get_current_softap_info(ad.dut, callbackId, True) + asserts.assert_true(frequency == 0, "Softap frequency is not reset") + asserts.assert_true(bandwdith == 0, "Softap bandwdith is not reset") + config = create_softap_config() start_wifi_tethering(ad.dut, config[WifiEnums.SSID_KEY], @@ -2046,6 +2198,15 @@ def start_softap_and_verify(ad, band): "SoftAp is not reported as running") start_wifi_connection_scan_and_ensure_network_found(ad.dut_client, config[WifiEnums.SSID_KEY]) + + # Check softap info can get from callback succeed and assert value should be + # valid. + frequency, bandwdith = get_current_softap_info(ad.dut, callbackId, True) + asserts.assert_true(frequency > 0, "Softap frequency is not valid") + asserts.assert_true(bandwdith > 0, "Softap bandwdith is not valid") + # Unregister callback + ad.dut.droid.unregisterSoftApCallback(callbackId) + return config def wait_for_expected_number_of_softap_clients(ad, callbackId, @@ -2057,11 +2218,28 @@ def wait_for_expected_number_of_softap_clients(ad, callbackId, """ eventStr = wifi_constants.SOFTAP_CALLBACK_EVENT + str( callbackId) + wifi_constants.SOFTAP_NUMBER_CLIENTS_CHANGED - asserts.assert_equal(ad.ed.pop_event(eventStr, - SHORT_TIMEOUT)['data'][wifi_constants. - SOFTAP_NUMBER_CLIENTS_CALLBACK_KEY], - expected_num_of_softap_clients, - "Number of softap clients doesn't match with expected number") + clientData = ad.ed.pop_event(eventStr, SHORT_TIMEOUT)['data'] + clientCount = clientData[wifi_constants.SOFTAP_NUMBER_CLIENTS_CALLBACK_KEY] + clientMacAddresses = clientData[wifi_constants.SOFTAP_CLIENTS_MACS_CALLBACK_KEY] + asserts.assert_equal(clientCount, expected_num_of_softap_clients, + "The number of softap clients doesn't match the expected number") + asserts.assert_equal(len(clientMacAddresses), expected_num_of_softap_clients, + "The number of mac addresses doesn't match the expected number") + for macAddress in clientMacAddresses: + asserts.assert_true(checkMacAddress(macAddress), "An invalid mac address was returned") + +def checkMacAddress(input): + """Validate whether a string is a valid mac address or not. + + Args: + input: The string to validate. + + Returns: True/False, returns true for a valid mac address and false otherwise. + """ + macValidationRegex = "[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$" + if re.match(macValidationRegex, input.lower()): + return True + return False def wait_for_expected_softap_state(ad, callbackId, expected_softap_state): """Wait for the expected softap state change. @@ -2096,6 +2274,41 @@ def get_current_number_of_softap_clients(ad, callbackId): return None return num_of_clients +def get_current_softap_info(ad, callbackId, least_one): + """pop up all of softap info changed event from queue. + Args: + callbackId: Id of the callback associated with registering. + least_one: Wait for the info callback event before pop all. + Returns: + Returns last updated information of softap. + """ + eventStr = wifi_constants.SOFTAP_CALLBACK_EVENT + str( + callbackId) + wifi_constants.SOFTAP_INFO_CHANGED + ad.log.info("softap info dump from eventStr %s", + eventStr) + frequency = 0 + bandwidth = 0 + if (least_one): + event = ad.ed.pop_event(eventStr, SHORT_TIMEOUT) + frequency = event['data'][wifi_constants. + SOFTAP_INFO_FREQUENCY_CALLBACK_KEY] + bandwidth = event['data'][wifi_constants. + SOFTAP_INFO_BANDWIDTH_CALLBACK_KEY] + ad.log.info("softap info updated, frequency is %s, bandwidth is %s", + frequency, bandwidth) + + events = ad.ed.pop_all(eventStr) + for event in events: + frequency = event['data'][wifi_constants. + SOFTAP_INFO_FREQUENCY_CALLBACK_KEY] + bandwidth = event['data'][wifi_constants. + SOFTAP_INFO_BANDWIDTH_CALLBACK_KEY] + ad.log.info("softap info, frequency is %s, bandwidth is %s", + frequency, bandwidth) + return frequency, bandwidth + + + def get_ssrdumps(ad, test_name=""): """Pulls dumps in the ssrdump dir Args: @@ -2155,30 +2368,33 @@ def stop_pcap(pcap, procs, test_status=None): if test_status: shutil.rmtree(os.path.dirname(fname)) -def verify_mac_not_found_in_pcap(mac, packets): +def verify_mac_not_found_in_pcap(ad, mac, packets): """Verify that a mac address is not found in the captured packets. Args: + ad: android device object mac: string representation of the mac address packets: packets obtained by rdpcap(pcap_fname) """ for pkt in packets: logging.debug("Packet Summary = %s", pkt.summary()) if mac in pkt.summary(): - asserts.fail("Caught Factory MAC: %s in packet sniffer." - "Packet = %s" % (mac, pkt.show())) + asserts.fail("Device %s caught Factory MAC: %s in packet sniffer." + "Packet = %s" % (ad.serial, mac, pkt.show())) -def verify_mac_is_found_in_pcap(mac, packets): +def verify_mac_is_found_in_pcap(ad, mac, packets): """Verify that a mac address is found in the captured packets. Args: + ad: android device object mac: string representation of the mac address packets: packets obtained by rdpcap(pcap_fname) """ for pkt in packets: if mac in pkt.summary(): return - asserts.fail("Did not find MAC = %s in packet sniffer." % mac) + asserts.fail("Did not find MAC = %s in packet sniffer." + "for device %s" % (mac, ad.serial)) def start_cnss_diags(ads): for ad in ads: diff --git a/acts/framework/acts/tracelogger.py b/acts/framework/acts/tracelogger.py index c7d920fcdb..d230fadd0f 100644 --- a/acts/framework/acts/tracelogger.py +++ b/acts/framework/acts/tracelogger.py @@ -14,11 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import datetime import inspect -import logging import os -import xml.etree.cElementTree as et class TraceLogger(object): @@ -65,41 +62,3 @@ class TraceLogger(object): def __getattr__(self, name): return getattr(self._logger, name) - - -class TakoTraceLogger(TraceLogger): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.d = self.debug - self.e = self.error - self.i = self.info - self.t = self.step - self.w = self.warning - - def _logger_level(self, level_name): - level = logging.getLevelName(level_name) - return lambda *args, **kwargs: self._logger.log(level, *args, **kwargs) - - def step(self, msg, *args, **kwargs): - """Delegate a step call to the underlying logger.""" - self._log_with(self._logger_level('STEP'), 1, msg, *args, **kwargs) - - def device(self, msg, *args, **kwargs): - """Delegate a device call to the underlying logger.""" - self._log_with(self._logger_level('DEVICE'), 1, msg, *args, **kwargs) - - def suite(self, msg, *args, **kwargs): - """Delegate a device call to the underlying logger.""" - self._log_with(self._logger_level('SUITE'), 1, msg, *args, **kwargs) - - def case(self, msg, *args, **kwargs): - """Delegate a case call to the underlying logger.""" - self._log_with(self._logger_level('CASE'), 1, msg, *args, **kwargs) - - def flush_log(self): - """This function exists for compatibility with Tako's logserial module. - - Note that flushing the log is handled automatically by python's logging - module. - """ - pass diff --git a/acts/framework/acts/utils.py b/acts/framework/acts/utils.py index c38f7c145d..3f57c91bdb 100755 --- a/acts/framework/acts/utils.py +++ b/acts/framework/acts/utils.py @@ -556,7 +556,6 @@ def timeout(sec): Raises: TimeoutError is raised when time out happens. """ - def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): @@ -952,10 +951,7 @@ def adb_shell_ping(ad, default www.google.com timeout: timeout for icmp pings to complete. """ - if is_valid_ipv6_address(dest_ip): - ping_cmd = "ping6 -W 1" - else: - ping_cmd = "ping -W 1" + ping_cmd = "ping -W 1" if count: ping_cmd += " -c %d" % count if dest_ip: @@ -1288,7 +1284,6 @@ class SuppressLogOutput(object): """Context manager used to suppress all logging output for the specified logger and level(s). """ - def __init__(self, logger=logging.getLogger(), log_levels=None): """Create a SuppressLogOutput context manager @@ -1321,7 +1316,6 @@ class BlockingTimer(object): """Context manager used to block until a specified amount of time has elapsed. """ - def __init__(self, secs): """Initializes a BlockingTimer @@ -1393,7 +1387,7 @@ def get_interface_ip_addresses(comm_channel, interface): Returns: A list of dictionaries of the the various IP addresses: - ipv4_private_local_addresses: Any 192.168, 172.16, 10, or 169.254 + ipv4_private_local_addresses: Any 192.168, 172.16, or 10 addresses ipv4_public_addresses: Any IPv4 public addresses ipv6_link_local_addresses: Any fe80:: addresses @@ -1501,6 +1495,14 @@ def get_interface_based_on_ip(comm_channel, desired_ip_address): all_ips_and_interfaces = comm_channel.run( '(ip -o -4 addr show; ip -o -6 addr show) | ' 'awk \'{print $2" "$4}\'').stdout + #ipv4_addresses = comm_channel.run( + # 'ip -o -4 addr show| awk \'{print $2": "$4}\'').stdout + #ipv6_addresses = comm_channel._ssh_session.run( + # 'ip -o -6 addr show| awk \'{print $2": "$4}\'').stdout + #if desired_ip_address in ipv4_addresses: + # ip_addresses_to_search = ipv4_addresses + #elif desired_ip_address in ipv6_addresses: + # ip_addresses_to_search = ipv6_addresses for ip_address_and_interface in all_ips_and_interfaces.split('\n'): if desired_ip_address in ip_address_and_interface: return ip_address_and_interface.split()[1][:-1] @@ -1510,54 +1512,189 @@ def get_interface_based_on_ip(comm_channel, desired_ip_address): def renew_linux_ip_address(comm_channel, interface): comm_channel.run('sudo ifconfig %s down' % interface) comm_channel.run('sudo ifconfig %s up' % interface) - comm_channel.run('sudo killall dhcpcd 2>/dev/null; echo""') - is_dhcpcd_dead = False - dhcpcd_counter = 0 - dhcpcd_checker_max_times = 3 - while not is_dhcpcd_dead: - if dhcpcd_counter == dhcpcd_checker_max_times: - raise TimeoutError('Unable to stop dhcpcd') - time.sleep(1) - if 'dhcpcd' in comm_channel.run('ps axu').stdout: - dhcpcd_counter += 1 - else: - is_dhcpcd_dead = True - comm_channel.run('sudo dhcpcd -q -b') comm_channel.run('sudo dhclient -r %s' % interface) comm_channel.run('sudo dhclient %s' % interface) -def is_pingable(ip): - """Returns whether an ip is pingable or not. +def get_ping_command(dest_ip, + count=3, + interval=1000, + timeout=1000, + size=56, + os_type='Linux', + additional_ping_params=None): + """Builds ping command string based on address type, os, and params. Args: - ip: string, ip address to ping + dest_ip: string, address to ping (ipv4 or ipv6) + count: int, number of requests to send + interval: int, time in seconds between requests + timeout: int, time in seconds to wait for response + size: int, number of bytes to send, + os_type: string, os type of the source device (supports 'Linux', + 'Darwin') + additional_ping_params: string, command option flags to + append to the command string + Returns: - True if ping was successful, else False + List of string, represetning the ping command. """ - if is_valid_ipv4_address(ip): + if is_valid_ipv4_address(dest_ip): ping_binary = 'ping' - elif is_valid_ipv6_address(ip): + elif is_valid_ipv6_address(dest_ip): ping_binary = 'ping6' else: - raise ValueError('Invalid ip addr: %s' % ip) + raise ValueError('Invalid ip addr: %s' % dest_ip) - os_type = platform.system() if os_type == 'Darwin': - if is_valid_ipv6_address(ip): + if is_valid_ipv6_address(dest_ip): # ping6 on MacOS doesn't support timeout + logging.warn( + 'Ignoring timeout, as ping6 on MacOS does not support it.') timeout_flag = [] else: - timeout_flag = ['-t', '1'] + timeout_flag = ['-t', str(timeout / 1000)] elif os_type == 'Linux': - timeout_flag = ['-W', '1'] + timeout_flag = ['-W', str(timeout / 1000)] else: raise ValueError('Invalid OS. Only Linux and MacOS are supported.') - ping_cmd = [ping_binary, *timeout_flag, '-c', '1', ip] + if not additional_ping_params: + additional_ping_params = '' + + ping_cmd = [ + ping_binary, *timeout_flag, '-c', + str(count), '-i', + str(interval / 1000), '-s', + str(size), additional_ping_params, dest_ip + ] + return ' '.join(ping_cmd) + + +def ping(comm_channel, + dest_ip, + count=3, + interval=1000, + timeout=1000, + size=56, + additional_ping_params=None): + """ Generic linux ping function, supports local (acts.libs.proc.job) and + SshConnections (acts.libs.proc.job over ssh) to Linux based OSs and MacOS. + + NOTES: This will work with Android over SSH, but does not function over ADB + as that has a unique return format. + + Args: + comm_channel: communication channel over which to send ping command. + Must have 'run' function that returns at least command, stdout, + stderr, and exit_status (see acts.libs.proc.job) + dest_ip: address to ping (ipv4 or ipv6) + count: int, number of packets to send + interval: int, time in milliseconds between pings + timeout: int, time in milliseconds to wait for response + size: int, size of packets in bytes + additional_ping_params: string, command option flags to + append to the command string + + Returns: + Dict containing: + command: string + exit_status: int (0 or 1) + stdout: string + stderr: string + transmitted: int, number of packets transmitted + received: int, number of packets received + packet_loss: int, percentage packet loss + time: int, time of ping command execution (in milliseconds) + rtt_min: float, minimum round trip time + rtt_avg: float, average round trip time + rtt_max: float, maximum round trip time + rtt_mdev: float, round trip time standard deviation + + Any values that cannot be parsed are left as None + """ + from acts.controllers.utils_lib.ssh.connection import SshConnection + is_local = comm_channel == job + os_type = platform.system() if is_local else 'Linux' + ping_cmd = get_ping_command(dest_ip, + count=count, + interval=interval, + timeout=timeout, + size=size, + os_type=os_type, + additional_ping_params=additional_ping_params) + + if (type(comm_channel) is SshConnection or is_local): + logging.debug( + 'Running ping with parameters (count: %s, interval: %s, timeout: ' + '%s, size: %s)' % (count, interval, timeout, size)) + ping_result = comm_channel.run(ping_cmd, ignore_status=True) + else: + raise ValueError('Unsupported comm_channel: %s' % type(comm_channel)) + + if isinstance(ping_result, job.Error): + ping_result = ping_result.result + + transmitted = None + received = None + packet_loss = None + time = None + rtt_min = None + rtt_avg = None + rtt_max = None + rtt_mdev = None + + summary = re.search( + '([0-9]+) packets transmitted.*?([0-9]+) received.*?([0-9]+)% packet ' + 'loss.*?time ([0-9]+)', ping_result.stdout) + if summary: + transmitted = summary[1] + received = summary[2] + packet_loss = summary[3] + time = summary[4] + + rtt_stats = re.search('= ([0-9.]+)/([0-9.]+)/([0-9.]+)/([0-9.]+)', + ping_result.stdout) + if rtt_stats: + rtt_min = rtt_stats[1] + rtt_avg = rtt_stats[2] + rtt_max = rtt_stats[3] + rtt_mdev = rtt_stats[4] + + return { + 'command': ping_result.command, + 'exit_status': ping_result.exit_status, + 'stdout': ping_result.stdout, + 'stderr': ping_result.stderr, + 'transmitted': transmitted, + 'received': received, + 'packet_loss': packet_loss, + 'time': time, + 'rtt_min': rtt_min, + 'rtt_avg': rtt_avg, + 'rtt_max': rtt_max, + 'rtt_mdev': rtt_mdev + } + - result = job.run(ping_cmd, timeout=10, ignore_status=True) - return result.exit_status == 0 +def can_ping(comm_channel, + dest_ip, + count=1, + interval=1000, + timeout=1000, + size=56, + additional_ping_params=None): + """Returns whether device connected via comm_channel can ping a dest + address""" + ping_results = ping(comm_channel, + dest_ip, + count=count, + interval=interval, + timeout=timeout, + size=size, + additional_ping_params=additional_ping_params) + + return ping_results['exit_status'] == 0 def ip_in_subnet(ip, subnet): diff --git a/acts/framework/tests/acts_confidence_test_config.json b/acts/framework/tests/acts_confidence_test_config.json new file mode 100644 index 0000000000..566bebada5 --- /dev/null +++ b/acts/framework/tests/acts_confidence_test_config.json @@ -0,0 +1,15 @@ +{ + "testbed": + [ + { + "_description": "ACTS confidence test bed, no device needed.", + "name": "Confidence", + "icecream": 42, + "MagicDevice": ["Magic!"] + } + ], + "logpath": "/tmp/logs", + "testpaths": ["./"], + "icecream": "mememe", + "extra_param": "haha" +} diff --git a/acts/framework/tests/acts_import_unit_test.py b/acts/framework/tests/acts_import_unit_test.py index 5e12e0f246..1d1ac7542c 100755 --- a/acts/framework/tests/acts_import_unit_test.py +++ b/acts/framework/tests/acts_import_unit_test.py @@ -47,7 +47,7 @@ else: PY_FILE_REGEX = re.compile('.+\.py$') -BLACKLIST = [ +DENYLIST = [ 'acts/controllers/rohdeschwarz_lib/contest.py', 'acts/controllers/native.py', 'acts/controllers/native_android_device.py', @@ -60,10 +60,9 @@ BLACKLIST = [ 'acts/test_utils/bt/bt_power_test_utils.py', 'acts/test_utils/coex/coex_test_utils.py', 'acts/test_utils/tel/twilio_client.py', - 'acts/test_utils/bt/A2dpBaseTest.py', - 'acts/test_utils/bt/BtSarBaseTest.py', 'tests/google/ble/beacon_tests/BeaconSwarmTest.py', 'tests/google/bt/pts/BtCmdLineTest.py', + 'tests/google/bt/performance/BtA2dpOtaRangeTest.py', 'tests/google/bt/headphone_automation/SineWaveQualityTest.py', 'tests/google/bt/audio_lab/BtChameleonTest.py', 'tests/google/native/bt/BtNativeTest.py', @@ -87,11 +86,12 @@ BLACKLIST = [ 'acts/test_utils/gnss/gnss_testlog_utils.py', ] -BLACKLIST_DIRECTORIES = [ +DENYLIST_DIRECTORIES = [ 'acts/controllers/buds_lib', 'acts/test_utils/audio_analysis_lib/', 'acts/test_utils/coex/', 'acts/test_utils/power/', + 'acts/test_utils/bt/', 'tests/google/coex/', 'tests/google/gnss/', 'tests/google/power/', @@ -125,9 +125,9 @@ class ActsImportUnitTest(unittest.TestCase): for root, _, files in os.walk(base_dir): for f in files: full_path = os.path.join(root, f) - if (any(full_path.endswith(e) for e in BLACKLIST) + if (any(full_path.endswith(e) for e in DENYLIST) or any(e in full_path - for e in BLACKLIST_DIRECTORIES)): + for e in DENYLIST_DIRECTORIES)): continue path = os.path.relpath(os.path.join(root, f), os.getcwd()) diff --git a/acts/framework/tests/acts_test_decorators_test.py b/acts/framework/tests/acts_test_decorators_test.py index c0e742a282..3ec357eeb6 100755 --- a/acts/framework/tests/acts_test_decorators_test.py +++ b/acts/framework/tests/acts_test_decorators_test.py @@ -238,16 +238,13 @@ class RepeatedTestTests(unittest.TestCase): self.assertIsInstance(results[3], IndexError) raise signals.TestPass('Expected failures occurred') - call_count = 0 @test_decorators.repeated_test(1, 3, result_selector) - def test_case(_): - nonlocal call_count - call_count += 1 - if call_count == 1: + def test_case(_, attempt_number): + if attempt_number == 1: raise AssertionError() - elif call_count == 2: + elif attempt_number == 2: raise signals.TestFailure('Failed') - elif call_count == 3: + elif attempt_number == 3: raise signals.TestError('Error') else: # Note that any Exception that does not fall into another bucket @@ -265,7 +262,7 @@ class RepeatedTestTests(unittest.TestCase): raise signals.TestPass('Expected passes occurred') @test_decorators.repeated_test(3, 0, result_selector) - def test_case(_): + def test_case(*_): raise signals.TestPass('Passed') with self.assertRaises(signals.TestPass): @@ -273,7 +270,7 @@ class RepeatedTestTests(unittest.TestCase): def test_abort_signals_are_uncaught(self): @test_decorators.repeated_test(3, 0) - def test_case(_): + def test_case(*_): raise signals.TestAbortClass('Abort All') with self.assertRaises(signals.TestAbortClass): @@ -281,7 +278,7 @@ class RepeatedTestTests(unittest.TestCase): def test_keyboard_interrupt_is_uncaught(self): @test_decorators.repeated_test(3, 0) - def test_case(_): + def test_case(*_): raise KeyboardInterrupt() with self.assertRaises(KeyboardInterrupt): @@ -290,7 +287,7 @@ class RepeatedTestTests(unittest.TestCase): def test_teardown_and_setup_are_called_between_test_cases(self): mock_test_class = mock.Mock() @test_decorators.repeated_test(1, 1) - def test_case(_): + def test_case(*_): raise signals.TestFailure('Failed') with self.assertRaises(signals.TestFailure): @@ -300,11 +297,11 @@ class RepeatedTestTests(unittest.TestCase): self.assertTrue(mock_test_class.teardown_test.called) def test_result_selector_returned_value_gets_raised(self): - def result_selector(_): + def result_selector(*_): return signals.TestPass('Expect this to be raised.') @test_decorators.repeated_test(3, 0, result_selector=result_selector) - def test_case(_): + def test_case(*_): raise signals.TestFailure('Result selector ignores this.') with self.assertRaises(signals.TestPass): diff --git a/acts/framework/tests/acts_utils_test.py b/acts/framework/tests/acts_utils_test.py index a08bf370bd..900a1e97a5 100755 --- a/acts/framework/tests/acts_utils_test.py +++ b/acts/framework/tests/acts_utils_test.py @@ -173,6 +173,7 @@ FUCHSIA_INIT_NETSTACK = ('acts.controllers.fuchsia_lib.netstack.' class ByPassSetupWizardTests(unittest.TestCase): """This test class for unit testing acts.utils.bypass_setup_wizard.""" + def test_start_standing_subproc(self): with self.assertRaisesRegex(utils.ActsUtilsError, 'Process .* has terminated'): @@ -306,6 +307,7 @@ class BypassSetupWizardReturn: class ConcurrentActionsTest(unittest.TestCase): """Tests acts.utils.run_concurrent_actions and related functions.""" + @staticmethod def function_returns_passed_in_arg(arg): return arg @@ -389,6 +391,7 @@ class ConcurrentActionsTest(unittest.TestCase): class SuppressLogOutputTest(unittest.TestCase): """Tests SuppressLogOutput""" + def test_suppress_log_output(self): """Tests that the SuppressLogOutput context manager removes handlers of the specified levels upon entry and re-adds handlers upon exit. @@ -411,6 +414,7 @@ class SuppressLogOutputTest(unittest.TestCase): class IpAddressUtilTest(unittest.TestCase): + def test_positive_ipv4_normal_address(self): ip_address = "192.168.1.123" self.assertTrue(utils.is_valid_ipv4_address(ip_address)) @@ -501,22 +505,20 @@ class IpAddressUtilTest(unittest.TestCase): utils.get_interface_ip_addresses(SshConnection('mock_settings'), 'wlan1'), CORRECT_EMPTY_IP_LIST) - @mock.patch('acts.controllers.adb.AdbProxy') + @mock.patch('acts.controllers.adb.AdbProxy.shell') def test_android_get_interface_ip_addresses_full(self, adb_mock): adb_mock().shell.side_effect = [ MOCK_IP_ADDRESSES, MOCK_IFCONFIG_OUTPUT ] self.assertEqual( utils.get_interface_ip_addresses(AndroidDevice(), 'eno1'), - CORRECT_FULL_IP_LIST) + CORRECT_FULL_IP_LIST) - @mock.patch('acts.controllers.adb.AdbProxy') + @mock.patch('acts.controllers.adb.AdbProxy.shell') def test_android_get_interface_ip_addresses_empty(self, adb_mock): - adb_mock().shell.side_effect = [ - MOCK_IP_ADDRESSES, MOCK_IFCONFIG_OUTPUT - ] - self.assertEqual( - utils.get_interface_ip_addresses(AndroidDevice(), 'wlan1'), + adb_mock.side_effect = [MOCK_IP_ADDRESSES, MOCK_IFCONFIG_OUTPUT] + self.assertTrue( + utils.get_interface_ip_addresses(AndroidDevice(), 'wlan1') == CORRECT_EMPTY_IP_LIST) @mock.patch(FUCHSIA_INIT_SERVER) diff --git a/acts/framework/tests/controllers/monsoon_lib/sampling/engine/transformers_test.py b/acts/framework/tests/controllers/monsoon_lib/sampling/engine/transformers_test.py index 4b6a7d0f73..0f97cec225 100755 --- a/acts/framework/tests/controllers/monsoon_lib/sampling/engine/transformers_test.py +++ b/acts/framework/tests/controllers/monsoon_lib/sampling/engine/transformers_test.py @@ -19,28 +19,15 @@ import unittest import mock from acts.controllers.monsoon_lib.sampling.engine.transformers import DownSampler +from acts.controllers.monsoon_lib.sampling.engine.transformers import PerfgateTee from acts.controllers.monsoon_lib.sampling.engine.transformers import SampleAggregator from acts.controllers.monsoon_lib.sampling.engine.transformers import Tee +from acts.controllers.monsoon_lib.sampling.hvpm.transformers import HvpmReading ARGS = 0 KWARGS = 1 -# TODO: Import HvpmReading directly when it is added to the codebase. -class HvpmReading(object): - def __init__(self, data, time): - self.main_current = data[0] - self.sample_time = time - - def __add__(self, other): - return HvpmReading([self.main_current + other.main_current], - self.sample_time + other.sample_time) - - def __truediv__(self, other): - return HvpmReading([self.main_current / other], - self.sample_time / other) - - class TeeTest(unittest.TestCase): """Unit tests the transformers.Tee class.""" @@ -67,8 +54,8 @@ class TeeTest(unittest.TestCase): tee.on_begin() expected_output = [ - '0.010000000s 1.414213562370\n', '0.020000000s 2.718281828460\n', - '0.030000000s 3.141592653590\n' + '0.010000000 1.414213562370\n', '0.020000000 2.718281828460\n', + '0.030000000 3.141592653590\n' ] tee._transform_buffer([ @@ -82,13 +69,55 @@ class TeeTest(unittest.TestCase): self.assertEqual(call[ARGS][0], out) +class PerfgateTeeTest(unittest.TestCase): + """Unit tests the transformers.PerfgateTee class.""" + + @mock.patch('builtins.open') + def test_begin_opens_file_on_expected_filename(self, open_mock): + expected_filename = 'foo' + + PerfgateTee(expected_filename).on_begin() + + open_mock.assert_called_with(expected_filename, 'w+') + + @mock.patch('builtins.open') + def test_end_closes_file(self, open_mock): + tee = PerfgateTee('foo') + tee.on_begin() + + tee.on_end() + + self.assertTrue(open_mock().close.called) + + @mock.patch('builtins.open') + def test_transform_buffer_outputs_correct_format(self, open_mock): + tee = PerfgateTee('foo') + tee.on_begin() + + expected_output = [ + '1596149635552503296,0.000223,4.193050\n', + '1596149635562476032,0.000212,4.193190\n', + '1596149635572549376,0.000225,4.193135\n', + ] + + tee._transform_buffer([ + HvpmReading([0.000223, 0, 0, 4.193050, 0], 1596149635.552503296), + HvpmReading([0.000212, 0, 0, 4.193190, 0], 1596149635.562476032), + HvpmReading([0.000225, 0, 0, 4.193135, 0], 1596149635.572549376), + ]) + + for call, out in zip(open_mock().write.call_args_list, + expected_output): + self.assertEqual(call[ARGS][0], out) + + class SampleAggregatorTest(unittest.TestCase): """Unit tests the transformers.SampleAggregator class.""" def test_transform_buffer_respects_start_after_seconds_flag(self): - sample_aggregator = SampleAggregator(start_after_seconds=1) + sample_aggregator = SampleAggregator(start_after_seconds=1.0) sample_aggregator._transform_buffer([ - HvpmReading([1.41421356237, 0, 0, 0, 0], 0.01), + HvpmReading([1.41421356237, 0, 0, 0, 0], 0.00), HvpmReading([2.71828182846, 0, 0, 0, 0], 0.99), HvpmReading([3.14159265359, 0, 0, 0, 0], 1.00), ]) diff --git a/acts/framework/tests/libs/proc/process_test.py b/acts/framework/tests/libs/proc/process_test.py index 600f5d6a3f..cb85782abf 100644 --- a/acts/framework/tests/libs/proc/process_test.py +++ b/acts/framework/tests/libs/proc/process_test.py @@ -13,12 +13,13 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import subprocess import unittest -import mock -import subprocess from acts.libs.proc.process import Process from acts.libs.proc.process import ProcessError +import mock + class FakeThread(object): def __init__(self, target=None): @@ -137,6 +138,31 @@ class ProcessTest(unittest.TestCase): self.assertEqual(process._kill_process.called, True) + @mock.patch('os.getpgid', side_effect=lambda id: id) + @mock.patch('os.killpg') + def test_sends_signal(self, mock_os, *_): + """Tests that signal is sent to process..""" + process = Process('cmd') + mock_process = mock.Mock() + mock_process.pid = -1 + process._process = mock_process + + process.signal(51641) + + mock_os.assert_called_with(-1, 51641) + + def test_signal_raises_error_on_windows(self, *_): + """Tests that signaling is unsupported in windows with appropriate + error msg.""" + process = Process('cmd') + mock_inner_process = mock.Mock() + mock_inner_process.pid = -1 + process._process = mock_inner_process + + with mock.patch('acts.libs.proc.process._on_windows', True): + with self.assertRaises(ProcessError): + process.signal(51641) + @mock.patch.object(Process, '_kill_process') def test_wait_sets_stopped_to_true_before_process_kill(self, *_): """Tests that stop() sets the _stopped attribute to True. @@ -318,9 +344,9 @@ class ProcessTest(unittest.TestCase): self.assertEqual(Process._Process__start_process.call_count, 2) self.assertEqual(Process._Process__start_process.call_args_list[0][0], - (['1st'], )) + (['1st'],)) self.assertEqual(Process._Process__start_process.call_args_list[1][0], - (['2nd'], )) + (['2nd'],)) def test_exec_loop_does_not_loop_if_stopped(self): process = Process('1st') diff --git a/acts/framework/tests/test_utils/instrumentation/__init__.py b/acts/framework/tests/test_utils/instrumentation/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/acts/framework/tests/test_utils/instrumentation/__init__.py diff --git a/acts/framework/tests/test_utils/instrumentation/device/__init__.py b/acts/framework/tests/test_utils/instrumentation/device/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/acts/framework/tests/test_utils/instrumentation/device/__init__.py diff --git a/acts/framework/tests/test_utils/instrumentation/device/command/instrumentation_command_builder_test.py b/acts/framework/tests/test_utils/instrumentation/device/command/instrumentation_command_builder_test.py new file mode 100755 index 0000000000..954c908654 --- /dev/null +++ b/acts/framework/tests/test_utils/instrumentation/device/command/instrumentation_command_builder_test.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 +# +# Copyright 2019 - The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +from acts.test_utils.instrumentation.device.command.instrumentation_command_builder \ + import InstrumentationCommandBuilder +from acts.test_utils.instrumentation.device.command.instrumentation_command_builder \ + import InstrumentationTestCommandBuilder + + +class InstrumentationCommandBuilderTest(unittest.TestCase): + + def test__runner_and_manifest_package_definition(self): + builder = InstrumentationCommandBuilder() + builder.set_manifest_package('package') + builder.set_runner('runner') + call = builder.build() + self.assertIn('package/runner', call) + + def test__manifest_package_must_be_defined(self): + builder = InstrumentationCommandBuilder() + + with self.assertRaisesRegex(Exception, '.*package cannot be none.*'): + builder.build() + + def test__runner_must_be_defined(self): + builder = InstrumentationCommandBuilder() + + with self.assertRaisesRegex(Exception, '.*runner cannot be none.*'): + builder.build() + + def test_proto_flag_without_set_proto_path(self): + builder = InstrumentationCommandBuilder() + builder.set_runner('runner') + builder.set_manifest_package('some.manifest.package') + + call = builder.build() + self.assertIn('-f', call) + + def test_proto_flag_with_set_proto_path(self): + builder = InstrumentationCommandBuilder() + builder.set_runner('runner') + builder.set_manifest_package('some.manifest.package') + builder.set_proto_path('/some/proto/path') + + call = builder.build() + self.assertIn('-f /some/proto/path', call) + + def test_set_nohup(self): + builder = InstrumentationCommandBuilder() + builder.set_runner('runner') + builder.set_manifest_package('some.manifest.package') + builder.set_nohup() + + call = builder.build() + self.assertEqual( + call, 'nohup am instrument -f some.manifest.package/runner >> ' + '$EXTERNAL_STORAGE/nohup.log 2>&1') + + def test__key_value_param_definition(self): + builder = InstrumentationCommandBuilder() + builder.set_runner('runner') + builder.set_manifest_package('some.manifest.package') + + builder.add_key_value_param('my_key_1', 'my_value_1') + builder.add_key_value_param('my_key_2', 'my_value_2') + + call = builder.build() + self.assertIn('-e my_key_1 my_value_1', call) + self.assertIn('-e my_key_2 my_value_2', call) + + def test__flags(self): + builder = InstrumentationCommandBuilder() + builder.set_runner('runner') + builder.set_manifest_package('some.manifest.package') + + builder.add_flag('--flag1') + builder.add_flag('--flag2') + + call = builder.build() + self.assertIn('--flag1', call) + self.assertIn('--flag2', call) + + +class InstrumentationTestCommandBuilderTest(unittest.TestCase): + """Test class for + acts/test_utils/instrumentation/instrumentation_call_builder.py + """ + + def test__test_packages_can_not_be_added_if_classes_were_added_first(self): + builder = InstrumentationTestCommandBuilder() + builder.add_test_class('some.tests.Class') + + with self.assertRaisesRegex(Exception, '.*only a list of classes.*'): + builder.add_test_package('some.tests.package') + + def test__test_classes_can_not_be_added_if_packages_were_added_first(self): + builder = InstrumentationTestCommandBuilder() + builder.add_test_package('some.tests.package') + + with self.assertRaisesRegex(Exception, '.*only a list of classes.*'): + builder.add_test_class('some.tests.Class') + + def test__test_classes_and_test_methods_can_be_combined(self): + builder = InstrumentationTestCommandBuilder() + builder.set_runner('runner') + builder.set_manifest_package('some.manifest.package') + builder.add_test_class('some.tests.Class1') + builder.add_test_method('some.tests.Class2', 'favoriteTestMethod') + + call = builder.build() + self.assertIn('some.tests.Class1', call) + self.assertIn('some.tests.Class2', call) + self.assertIn('favoriteTestMethod', call) + + +if __name__ == '__main__': + unittest.main() diff --git a/acts/framework/tests/test_utils/power/tel/lab/init_simulation_test.py b/acts/framework/tests/test_utils/power/tel/lab/init_simulation_test.py index 1bd2f074fc..24fe91a457 100644 --- a/acts/framework/tests/test_utils/power/tel/lab/init_simulation_test.py +++ b/acts/framework/tests/test_utils/power/tel/lab/init_simulation_test.py @@ -17,8 +17,8 @@ import unittest import mobly.config_parser as mobly_config_parser import mock_bokeh -from acts.test_utils.power.tel_simulations.LteSimulation import LteSimulation -from acts.test_utils.power.tel_simulations.UmtsSimulation import UmtsSimulation +from acts.controllers.cellular_lib.LteSimulation import LteSimulation +from acts.controllers.cellular_lib.UmtsSimulation import UmtsSimulation from unittest import mock diff --git a/acts/framework/tests/test_utils/power/tel/lab/power_tel_traffic_e2e_test.py b/acts/framework/tests/test_utils/power/tel/lab/power_tel_traffic_e2e_test.py index ff9599665f..065543ff1e 100644 --- a/acts/framework/tests/test_utils/power/tel/lab/power_tel_traffic_e2e_test.py +++ b/acts/framework/tests/test_utils/power/tel/lab/power_tel_traffic_e2e_test.py @@ -18,7 +18,7 @@ import unittest import mock_bokeh import acts.test_utils.power.cellular.cellular_traffic_power_test as ctpt import mobly.config_parser as mobly_config_parser -from acts.test_utils.power.tel_simulations.LteSimulation import LteSimulation +from acts.controllers.cellular_lib.LteSimulation import LteSimulation from acts.controllers.rohdeschwarz_lib import cmw500_cellular_simulator as cmw from unittest import mock diff --git a/acts/framework/tests/test_utils/power/tel/lab/save_summary_to_file_test.py b/acts/framework/tests/test_utils/power/tel/lab/save_summary_to_file_test.py index 1e1554a0dc..900c3a74da 100644 --- a/acts/framework/tests/test_utils/power/tel/lab/save_summary_to_file_test.py +++ b/acts/framework/tests/test_utils/power/tel/lab/save_summary_to_file_test.py @@ -17,7 +17,7 @@ import unittest import mobly.config_parser as mobly_config_parser import mock_bokeh -from acts.test_utils.power.tel_simulations.LteSimulation import LteSimulation +from acts.controllers.cellular_lib.LteSimulation import LteSimulation from unittest import mock from unittest.mock import mock_open diff --git a/acts/tests/google/wifi/OWNERS b/acts/tests/google/wifi/OWNERS deleted file mode 100644 index 7e868cfe07..0000000000 --- a/acts/tests/google/wifi/OWNERS +++ /dev/null @@ -1,6 +0,0 @@ -bmahadev@google.com -etancohen@google.com -krisr@google.com -mplass@google.com -rpius@google.com -satk@google.com diff --git a/acts/tests/google/wifi/WifiAutoUpdateTest.py b/acts/tests/google/wifi/WifiAutoUpdateTest.py deleted file mode 100755 index 33369a2775..0000000000 --- a/acts/tests/google/wifi/WifiAutoUpdateTest.py +++ /dev/null @@ -1,242 +0,0 @@ -#!/usr/bin/env python3.4 -# -# Copyright 2017 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import itertools -import pprint -import queue -import time - -import acts.base_test -import acts.signals as signals -import acts.test_utils.wifi.wifi_test_utils as wutils -import acts.utils - -from acts import asserts -from acts.libs.ota import ota_updater -from acts.test_decorators import test_tracker_info -from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest - -WifiEnums = wutils.WifiEnums -# Default timeout used for reboot, toggle WiFi and Airplane mode, -# for the system to settle down after the operation. -DEFAULT_TIMEOUT = 10 -BAND_2GHZ = 0 -BAND_5GHZ = 1 - - -class WifiAutoUpdateTest(WifiBaseTest): - """Tests for APIs in Android's WifiManager class. - - Test Bed Requirement: - * One Android device - * Several Wi-Fi networks visible to the device, including an open Wi-Fi - network. - """ - - def __init__(self, controllers): - WifiBaseTest.__init__(self, controllers) - self.tests = ( - "test_check_wifi_state_after_au", - "test_verify_networks_after_au", - "test_all_networks_connectable_after_au", - "test_connection_to_new_networks", - "test_check_wifi_toggling_after_au", - "test_reset_wifi_after_au") - - def setup_class(self): - super(WifiAutoUpdateTest, self).setup_class() - ota_updater.initialize(self.user_params, self.android_devices) - self.dut = self.android_devices[0] - wutils.wifi_test_device_init(self.dut) - req_params = [] - opt_param = [ - "open_network", "reference_networks", "iperf_server_address" - ] - self.unpack_userparams( - req_param_names=req_params, opt_param_names=opt_param) - - if "AccessPoint" in self.user_params: - self.legacy_configure_ap_and_start() - - asserts.assert_true( - len(self.reference_networks) > 0, - "Need at least two reference network with psk.") - asserts.assert_true( - len(self.open_network) > 0, - "Need at least two open network with psk.") - wutils.wifi_toggle_state(self.dut, True) - - self.wifi_config_list = [] - - # Disabling WiFi setup before OTA for debugging. - # Setup WiFi and add few open and wpa networks before OTA. - # self.add_network_and_enable(self.open_network[0]['2g']) - # self.add_network_and_enable(self.reference_networks[0]['5g']) - - # Add few dummy networks to the list. - # self.add_and_enable_dummy_networks() - - # Run OTA below, if ota fails then abort all tests. - try: - ota_updater.update(self.dut) - except Exception as err: - raise signals.TestAbortClass( - "Failed up apply OTA update. Aborting tests") - - def setup_test(self): - self.dut.droid.wakeLockAcquireBright() - self.dut.droid.wakeUpNow() - - def teardown_test(self): - self.dut.droid.wakeLockRelease() - self.dut.droid.goToSleepNow() - - def on_fail(self, test_name, begin_time): - self.dut.take_bug_report(test_name, begin_time) - self.dut.cat_adb_log(test_name, begin_time) - - def teardown_class(self): - if "AccessPoint" in self.user_params: - del self.user_params["reference_networks"] - del self.user_params["open_network"] - - """Helper Functions""" - - def add_network_and_enable(self, network): - """Add a network and enable it. - - Args: - network : Network details for the network to be added. - - """ - ret = self.dut.droid.wifiAddNetwork(network) - asserts.assert_true(ret != -1, "Add network %r failed" % network) - self.wifi_config_list.append({ - WifiEnums.SSID_KEY: network[WifiEnums.SSID_KEY], - WifiEnums.NETID_KEY: ret}) - self.dut.droid.wifiEnableNetwork(ret, 0) - - def add_and_enable_dummy_networks(self, num_networks=5): - """Add some dummy networks to the device and enable them. - - Args: - num_networks: Number of networks to add. - """ - ssid_name_base = "dummy_network_" - for i in range(0, num_networks): - network = {} - network[WifiEnums.SSID_KEY] = ssid_name_base + str(i) - network[WifiEnums.PWD_KEY] = "dummynet_password" - self.add_network_and_enable(network) - - def check_networks_after_autoupdate(self, networks): - """Verify that all previously configured networks are presistent after - reboot. - - Args: - networks: List of network dicts. - - Return: - None. Raises TestFailure. - - """ - network_info = self.dut.droid.wifiGetConfiguredNetworks() - if len(network_info) != len(networks): - msg = ( - "Number of configured networks before and after Auto-update " - "don't match. \nBefore reboot = %s \n After reboot = %s" % - (networks, network_info)) - raise signals.TestFailure(msg) - current_count = 0 - # For each network, check if it exists in configured list after Auto- - # update. - for network in networks: - exists = wutils.match_networks({ - WifiEnums.SSID_KEY: network[WifiEnums.SSID_KEY] - }, network_info) - if not len(exists): - raise signals.TestFailure("%s network is not present in the" - " configured list after Auto-update" % - network[WifiEnums.SSID_KEY]) - # Get the new network id for each network after reboot. - network[WifiEnums.NETID_KEY] = exists[0]['networkId'] - - """Tests""" - - @test_tracker_info(uuid="9ff1f01e-e5ff-408b-9a95-29e87a2df2d8") - def test_check_wifi_state_after_au(self): - """Check if the state of WiFi is enabled after Auto-update.""" - if not self.dut.droid.wifiCheckState(): - raise signals.TestFailure("WiFi is disabled after Auto-update!!!") - - @test_tracker_info(uuid="e3ebdbba-71dd-4281-aef8-5b3d42b88770") - def test_verify_networks_after_au(self): - """Check if the previously added networks are intact. - - Steps: - Number of networs should be the same and match each network. - - """ - self.check_networks_after_autoupdate(self.wifi_config_list) - - @test_tracker_info(uuid="b8e47a4f-62fe-4a0e-b999-27ae1ebf4d19") - def test_connection_to_new_networks(self): - """Check if we can connect to new networks after Auto-update. - - Steps: - 1. Connect to a PSK network. - 2. Connect to an open network. - 3. Forget ntworks added in 1 & 2. - TODO: (@bmahadev) Add WEP network once it's ready. - - """ - wutils.connect_to_wifi_network(self.dut, self.open_network[0]['5g']) - wutils.connect_to_wifi_network(self.dut, self.reference_networks[0]['2g']) - wutils.wifi_forget_network(self.dut, - self.reference_networks[0]['2g'][WifiEnums.SSID_KEY]) - wutils.wifi_forget_network(self.dut, - self.open_network[0]['5g'][WifiEnums.SSID_KEY]) - - @test_tracker_info(uuid="1d8309e4-d5a2-4f48-ba3b-895a58c9bf3a") - def test_all_networks_connectable_after_au(self): - """Check if previously added networks are connectable. - - Steps: - 1. Connect to previously added PSK network using network id. - 2. Connect to previously added open network using network id. - TODO: (@bmahadev) Add WEP network once it's ready. - - """ - for network in self.wifi_config_list: - if 'dummy' not in network[WifiEnums.SSID_KEY]: - if not wutils.connect_to_wifi_network_with_id(self.dut, - network[WifiEnums.NETID_KEY], - network[WifiEnums.SSID_KEY]): - raise signals.TestFailure("Failed to connect to %s after \ - Auto-update" % network[WifiEnums.SSID_KEY]) - - @test_tracker_info(uuid="05671859-38b1-4dbf-930c-18048971d075") - def test_check_wifi_toggling_after_au(self): - """Check if WiFi can be toggled ON/OFF after auto-update.""" - self.log.debug("Going from on to off.") - wutils.wifi_toggle_state(self.dut, False) - self.log.debug("Going from off to on.") - wutils.wifi_toggle_state(self.dut, True) - - @test_tracker_info(uuid="440edf32-4b00-42b0-9811-9f2bc4a83efb") - def test_reset_wifi_after_au(self): - """"Check if WiFi can be reset after auto-update.""" - wutils.reset_wifi(self.dut) diff --git a/acts/tests/google/wifi/WifiNetworkSuggestionTest.py b/acts/tests/google/wifi/WifiNetworkSuggestionTest.py deleted file mode 100644 index 6d6da35b42..0000000000 --- a/acts/tests/google/wifi/WifiNetworkSuggestionTest.py +++ /dev/null @@ -1,458 +0,0 @@ -#!/usr/bin/env python3.4 -# -# Copyright 2018 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import itertools -import pprint -import queue -import time - -import acts.base_test -import acts.signals as signals -import acts.test_utils.wifi.wifi_test_utils as wutils -import acts.utils - -from acts import asserts -from acts.controllers.android_device import SL4A_APK_NAME -from acts.test_decorators import test_tracker_info -from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest -from acts.test_utils.wifi import wifi_constants - -WifiEnums = wutils.WifiEnums -# EAP Macros -EAP = WifiEnums.Eap -EapPhase2 = WifiEnums.EapPhase2 -# Enterprise Config Macros -Ent = WifiEnums.Enterprise - -# Default timeout used for reboot, toggle WiFi and Airplane mode, -# for the system to settle down after the operation. -DEFAULT_TIMEOUT = 10 - - -class WifiNetworkSuggestionTest(WifiBaseTest): - """Tests for WifiNetworkSuggestion API surface. - - Test Bed Requirement: - * one Android device - * Several Wi-Fi networks visible to the device, including an open Wi-Fi - network. - """ - - def setup_class(self): - super().setup_class() - - self.dut = self.android_devices[0] - wutils.wifi_test_device_init(self.dut) - req_params = [] - opt_param = [ - "open_network", "reference_networks", "radius_conf_2g", "radius_conf_5g", "ca_cert", - "eap_identity", "eap_password", "hidden_networks" - ] - self.unpack_userparams( - req_param_names=req_params, opt_param_names=opt_param) - - if "AccessPoint" in self.user_params: - self.legacy_configure_ap_and_start( - wpa_network=True, ent_network=True, - radius_conf_2g=self.radius_conf_2g, - radius_conf_5g=self.radius_conf_5g,) - - asserts.assert_true( - len(self.reference_networks) > 0, - "Need at least one reference network with psk.") - if hasattr(self, "reference_networks"): - self.wpa_psk_2g = self.reference_networks[0]["2g"] - self.wpa_psk_5g = self.reference_networks[0]["5g"] - if hasattr(self, "open_network"): - self.open_2g = self.open_network[0]["2g"] - self.open_5g = self.open_network[0]["5g"] - if hasattr(self, "ent_networks"): - self.ent_network_2g = self.ent_networks[0]["2g"] - self.ent_network_5g = self.ent_networks[0]["5g"] - self.config_aka = { - Ent.EAP: int(EAP.AKA), - WifiEnums.SSID_KEY: self.ent_network_2g[WifiEnums.SSID_KEY], - } - self.config_ttls = { - Ent.EAP: int(EAP.TTLS), - Ent.CA_CERT: self.ca_cert, - Ent.IDENTITY: self.eap_identity, - Ent.PASSWORD: self.eap_password, - Ent.PHASE2: int(EapPhase2.MSCHAPV2), - WifiEnums.SSID_KEY: self.ent_network_2g[WifiEnums.SSID_KEY], - } - if hasattr(self, "hidden_networks"): - self.hidden_network = self.hidden_networks[0] - self.dut.droid.wifiRemoveNetworkSuggestions([]) - - def setup_test(self): - self.dut.droid.wakeLockAcquireBright() - self.dut.droid.wakeUpNow() - self.clear_deleted_ephemeral_networks() - wutils.wifi_toggle_state(self.dut, True) - self.dut.ed.clear_all_events() - - def teardown_test(self): - self.dut.droid.wakeLockRelease() - self.dut.droid.goToSleepNow() - self.dut.droid.wifiRemoveNetworkSuggestions([]) - self.dut.droid.wifiDisconnect() - wutils.reset_wifi(self.dut) - wutils.wifi_toggle_state(self.dut, False) - self.dut.ed.clear_all_events() - - def on_fail(self, test_name, begin_time): - self.dut.take_bug_report(test_name, begin_time) - self.dut.cat_adb_log(test_name, begin_time) - - def teardown_class(self): - if "AccessPoint" in self.user_params: - del self.user_params["reference_networks"] - del self.user_params["open_network"] - - """Helper Functions""" - def set_approved(self, approved): - self.dut.log.debug("Setting suggestions from sl4a app " - + "approved" if approved else "not approved") - self.dut.adb.shell("cmd wifi network-suggestions-set-user-approved" - + " " + SL4A_APK_NAME - + " " + ("yes" if approved else "no")) - - def is_approved(self): - is_approved_str = self.dut.adb.shell( - "cmd wifi network-suggestions-has-user-approved" - + " " + SL4A_APK_NAME) - return True if (is_approved_str == "yes") else False - - def clear_deleted_ephemeral_networks(self): - self.dut.log.debug("Clearing deleted ephemeral networks") - self.dut.adb.shell( - "cmd wifi clear-deleted-ephemeral-networks") - - def add_suggestions_and_ensure_connection(self, network_suggestions, - expected_ssid, - expect_post_connection_broadcast): - if expect_post_connection_broadcast is not None: - self.dut.droid.wifiStartTrackingNetworkSuggestionStateChange() - - self.dut.log.info("Adding network suggestions"); - asserts.assert_true( - self.dut.droid.wifiAddNetworkSuggestions(network_suggestions), - "Failed to add suggestions") - # Enable suggestions by the app. - self.dut.log.debug("Enabling suggestions from test"); - self.set_approved(True) - wutils.start_wifi_connection_scan_and_return_status(self.dut) - wutils.wait_for_connect(self.dut, expected_ssid) - - if expect_post_connection_broadcast is None: - return; - - # Check if we expected to get the broadcast. - try: - event = self.dut.ed.pop_event( - wifi_constants.WIFI_NETWORK_SUGGESTION_POST_CONNECTION, 60) - except queue.Empty: - if expect_post_connection_broadcast: - raise signals.TestFailure( - "Did not receive post connection broadcast") - else: - if not expect_post_connection_broadcast: - raise signals.TestFailure( - "Received post connection broadcast") - finally: - self.dut.droid.wifiStopTrackingNetworkSuggestionStateChange() - self.dut.ed.clear_all_events() - - def remove_suggestions_disconnect_and_ensure_no_connection_back(self, - network_suggestions, - expected_ssid): - self.dut.log.info("Removing network suggestions") - asserts.assert_true( - self.dut.droid.wifiRemoveNetworkSuggestions(network_suggestions), - "Failed to remove suggestions") - # Ensure we did not disconnect - wutils.ensure_no_disconnect(self.dut) - - # Trigger a disconnect and wait for the disconnect. - self.dut.droid.wifiDisconnect() - wutils.wait_for_disconnect(self.dut) - self.dut.ed.clear_all_events() - - # Now ensure that we didn't connect back. - asserts.assert_false( - wutils.wait_for_connect(self.dut, expected_ssid, assert_on_fail=False), - "Device should not connect back") - - def _test_connect_to_wifi_network_reboot_config_store(self, - network_suggestions, - wifi_network): - """ Test network suggestion with reboot config store - - Args: - 1. network_suggestions: network suggestions in list to add to the device. - 2. wifi_network: expected wifi network to connect to - """ - - self.add_suggestions_and_ensure_connection( - network_suggestions, wifi_network[WifiEnums.SSID_KEY], None) - - # Reboot and wait for connection back to the same suggestion. - self.dut.reboot() - time.sleep(DEFAULT_TIMEOUT) - - wutils.wait_for_connect(self.dut, wifi_network[WifiEnums.SSID_KEY]) - - self.remove_suggestions_disconnect_and_ensure_no_connection_back( - network_suggestions, wifi_network[WifiEnums.SSID_KEY]) - - @test_tracker_info(uuid="bda8ed20-4382-4380-831a-64cf77eca108") - def test_connect_to_wpa_psk_2g(self): - """ Adds a network suggestion and ensure that the device connected. - - Steps: - 1. Send a network suggestion to the device. - 2. Wait for the device to connect to it. - 3. Ensure that we did not receive the post connection broadcast - (isAppInteractionRequired = False). - 4. Remove the suggestions and ensure the device does not connect back. - """ - self.add_suggestions_and_ensure_connection( - [self.wpa_psk_2g], self.wpa_psk_2g[WifiEnums.SSID_KEY], - False) - - self.remove_suggestions_disconnect_and_ensure_no_connection_back( - [self.wpa_psk_2g], self.wpa_psk_2g[WifiEnums.SSID_KEY]) - - @test_tracker_info(uuid="f54bc250-d9e9-4f00-8b5b-b866e8550b43") - def test_connect_to_highest_priority(self): - """ - Adds network suggestions and ensures that device connects to - the suggestion with the highest priority. - - Steps: - 1. Send 2 network suggestions to the device (with different priorities). - 2. Wait for the device to connect to the network with the highest - priority. - 3. Re-add the suggestions with the priorities reversed. - 4. Again wait for the device to connect to the network with the highest - priority. - """ - network_suggestion_2g = self.wpa_psk_2g - network_suggestion_5g = self.wpa_psk_5g - - # Add suggestions & wait for the connection event. - network_suggestion_2g[WifiEnums.PRIORITY] = 5 - network_suggestion_5g[WifiEnums.PRIORITY] = 2 - self.add_suggestions_and_ensure_connection( - [network_suggestion_2g, network_suggestion_5g], - self.wpa_psk_2g[WifiEnums.SSID_KEY], - None) - - self.remove_suggestions_disconnect_and_ensure_no_connection_back( - [], self.wpa_psk_2g[WifiEnums.SSID_KEY]) - - # Reverse the priority. - # Add suggestions & wait for the connection event. - network_suggestion_2g[WifiEnums.PRIORITY] = 2 - network_suggestion_5g[WifiEnums.PRIORITY] = 5 - self.add_suggestions_and_ensure_connection( - [network_suggestion_2g, network_suggestion_5g], - self.wpa_psk_5g[WifiEnums.SSID_KEY], - None) - - @test_tracker_info(uuid="b1d27eea-23c8-4c4f-b944-ef118e4cc35f") - def test_connect_to_wpa_psk_2g_with_post_connection_broadcast(self): - """ Adds a network suggestion and ensure that the device connected. - - Steps: - 1. Send a network suggestion to the device with - isAppInteractionRequired set. - 2. Wait for the device to connect to it. - 3. Ensure that we did receive the post connection broadcast - (isAppInteractionRequired = True). - 4. Remove the suggestions and ensure the device does not connect back. - """ - network_suggestion = self.wpa_psk_2g - network_suggestion[WifiEnums.IS_APP_INTERACTION_REQUIRED] = True - self.add_suggestions_and_ensure_connection( - [network_suggestion], self.wpa_psk_2g[WifiEnums.SSID_KEY], - True) - self.remove_suggestions_disconnect_and_ensure_no_connection_back( - [self.wpa_psk_2g], self.wpa_psk_2g[WifiEnums.SSID_KEY]) - - @test_tracker_info(uuid="a036a24d-29c0-456d-ae6a-afdde34da710") - def test_connect_to_wpa_psk_5g_reboot_config_store(self): - """ - Adds a network suggestion and ensure that the device connects to it - after reboot. - - Steps: - 1. Send a network suggestion to the device. - 2. Wait for the device to connect to it. - 3. Ensure that we did not receive the post connection broadcast - (isAppInteractionRequired = False). - 4. Reboot the device. - 5. Wait for the device to connect to back to it. - 6. Remove the suggestions and ensure the device does not connect back. - """ - self._test_connect_to_wifi_network_reboot_config_store( - [self.wpa_psk_5g], self.wpa_psk_5g) - - @test_tracker_info(uuid="61649a2b-0f00-4272-9b9b-40ad5944da31") - def test_connect_to_wpa_ent_config_aka_reboot_config_store(self): - """ - Adds a network suggestion and ensure that the device connects to it - after reboot. - - Steps: - 1. Send a Enterprise AKA network suggestion to the device. - 2. Wait for the device to connect to it. - 3. Ensure that we did not receive the post connection broadcast. - 4. Reboot the device. - 5. Wait for the device to connect to the wifi network. - 6. Remove suggestions and ensure device doesn't connect back to it. - """ - self._test_connect_to_wifi_network_reboot_config_store( - [self.config_aka], self.ent_network_2g) - - @test_tracker_info(uuid="98b2d40a-acb4-4a2f-aba1-b069e2a1d09d") - def test_connect_to_wpa_ent_config_ttls_pap_reboot_config_store(self): - """ - Adds a network suggestion and ensure that the device connects to it - after reboot. - - Steps: - 1. Send a Enterprise TTLS PAP network suggestion to the device. - 2. Wait for the device to connect to it. - 3. Ensure that we did not receive the post connection broadcast. - 4. Reboot the device. - 5. Wait for the device to connect to the wifi network. - 6. Remove suggestions and ensure device doesn't connect back to it. - """ - config = dict(self.config_ttls) - config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.PAP.value - - self._test_connect_to_wifi_network_reboot_config_store( - [config], self.ent_network_2g) - - @test_tracker_info(uuid="554b5861-22d0-4922-a5f4-712b4cf564eb") - def test_fail_to_connect_to_wpa_psk_5g_when_not_approved(self): - """ - Adds a network suggestion and ensure that the device does not - connect to it until we approve the app. - - Steps: - 1. Send a network suggestion to the device with the app not approved. - 2. Ensure the network is present in scan results, but we don't connect - to it. - 3. Now approve the app. - 4. Wait for the device to connect to it. - """ - self.dut.log.info("Adding network suggestions"); - asserts.assert_true( - self.dut.droid.wifiAddNetworkSuggestions([self.wpa_psk_5g]), - "Failed to add suggestions") - - # Disable suggestions by the app. - self.set_approved(False) - - # Ensure the app is not approved. - asserts.assert_false( - self.is_approved(), - "Suggestions should be disabled") - - # Start a new scan to trigger auto-join. - wutils.start_wifi_connection_scan_and_ensure_network_found( - self.dut, self.wpa_psk_5g[WifiEnums.SSID_KEY]) - - # Ensure we don't connect to the network. - asserts.assert_false( - wutils.wait_for_connect( - self.dut, self.wpa_psk_5g[WifiEnums.SSID_KEY], assert_on_fail=False), - "Should not connect to network suggestions from unapproved app") - - self.dut.log.info("Enabling suggestions from test"); - # Now Enable suggestions by the app & ensure we connect to the network. - self.set_approved(True) - - # Ensure the app is approved. - asserts.assert_true( - self.is_approved(), - "Suggestions should be enabled") - - # Start a new scan to trigger auto-join. - wutils.start_wifi_connection_scan_and_ensure_network_found( - self.dut, self.wpa_psk_5g[WifiEnums.SSID_KEY]) - - wutils.wait_for_connect(self.dut, self.wpa_psk_5g[WifiEnums.SSID_KEY]) - - @test_tracker_info(uuid="98400dea-776e-4a0a-9024-18845b27331c") - def test_fail_to_connect_to_wpa_psk_2g_after_user_forgot_network(self): - """ - Adds a network suggestion and ensures that the device does not - connect to it after the user forgot the network previously. - - Steps: - 1. Send a network suggestion to the device with - isAppInteractionRequired set. - 2. Wait for the device to connect to it. - 3. Ensure that we did receive the post connection broadcast - (isAppInteractionRequired = True). - 4. Simulate user forgetting the network and the device does not - connecting back even though the suggestion is active from the app. - """ - network_suggestion = self.wpa_psk_2g - network_suggestion[WifiEnums.IS_APP_INTERACTION_REQUIRED] = True - self.add_suggestions_and_ensure_connection( - [network_suggestion], self.wpa_psk_2g[WifiEnums.SSID_KEY], - True) - - # Simulate user forgetting the ephemeral network. - self.dut.droid.wifiDisableEphemeralNetwork( - self.wpa_psk_2g[WifiEnums.SSID_KEY]) - wutils.wait_for_disconnect(self.dut) - self.dut.log.info("Disconnected from network %s", self.wpa_psk_2g) - self.dut.ed.clear_all_events() - - # Now ensure that we don't connect back even though the suggestion - # is still active. - asserts.assert_false( - wutils.wait_for_connect(self.dut, - self.wpa_psk_2g[WifiEnums.SSID_KEY], - assert_on_fail=False), - "Device should not connect back") - - @test_tracker_info(uuid="93c86b05-fa56-4d79-ad27-009a16f691b1") - def test_connect_to_hidden_network(self): - """ - Adds a network suggestion with hidden SSID config, ensure device can scan - and connect to this network. - - Steps: - 1. Send a hidden network suggestion to the device. - 2. Wait for the device to connect to it. - 3. Ensure that we did not receive the post connection broadcast - (isAppInteractionRequired = False). - 4. Remove the suggestions and ensure the device does not connect back. - """ - asserts.skip_if(not hasattr(self, "hidden_networks"), "No hidden networks, skip this test") - - network_suggestion = self.hidden_network - self.add_suggestions_and_ensure_connection( - [network_suggestion], network_suggestion[WifiEnums.SSID_KEY], False) - self.remove_suggestions_disconnect_and_ensure_no_connection_back( - [network_suggestion], network_suggestion[WifiEnums.SSID_KEY]) diff --git a/acts_tests/tests/OWNERS b/acts_tests/tests/OWNERS index 58c9ff5f73..c07c1cff8d 100644 --- a/acts_tests/tests/OWNERS +++ b/acts_tests/tests/OWNERS @@ -1,6 +1,8 @@ # Platform jaineelm@google.com satmaram@google.com +tccyp@google.com +yuhany@google.com # Pixel MTV ashutoshrsingh@google.com @@ -8,6 +10,7 @@ dvj@google.com gmoturu@google.com mrtyler@google.com codycaldwell@google.com +chaoyangf@google.com # Pixel GTW jasonkmlu@google.com @@ -25,7 +28,7 @@ abhinavjadon@google.com djfernan@google.com iguarna@google.com jingliang@google.com -klug@google.com +yixiang@google.com oelayach@google.com qijiang@google.com sriramsundar@google.com diff --git a/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisementDiscoveryTest.py b/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisementDiscoveryTest.py index 2af4c05bcc..c38fc935de 100644 --- a/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisementDiscoveryTest.py +++ b/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisementDiscoveryTest.py @@ -43,22 +43,21 @@ from acts.test_utils.bt.bt_test_utils import scan_and_verify_n_advertisements class ConcurrentBleAdvertisementDiscoveryTest(BluetoothBaseTest): default_timeout = 10 + droid_list = [] max_advertisements = -1 advertise_callback_list = [] def setup_class(self): super().setup_class() - self.droid_list = get_advanced_droid_list(self.android_devices) self.scn_ad = self.android_devices[0] self.adv_ad = self.android_devices[1] + self.droid_list = get_advanced_droid_list(self.android_devices) self.max_advertisements = self.droid_list[1]['max_advertisements'] def setup_test(self): - return reset_bluetooth(self.android_devices) - - def setup_test(self): - super(BluetoothBaseTest, self).setup_test() + super().setup_test() self.log.info("Setting up advertisements") + reset_bluetooth(self.android_devices) try: self.advertise_callback_list = setup_n_advertisements( self.adv_ad, self.max_advertisements) diff --git a/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py b/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py index 94ee0f771e..09c6cd3a5e 100644 --- a/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py +++ b/acts_tests/tests/google/ble/concurrency/ConcurrentBleAdvertisingTest.py @@ -43,17 +43,18 @@ from acts.test_utils.bt.bt_test_utils import teardown_n_advertisements class ConcurrentBleAdvertisingTest(BluetoothBaseTest): default_timeout = 10 + droid_list = [] max_advertisements = -1 def setup_class(self): super().setup_class() - self.droid_list = get_advanced_droid_list(self.android_devices) self.scn_ad = self.android_devices[0] self.adv_ad = self.android_devices[1] + self.droid_list = get_advanced_droid_list(self.android_devices) self.max_advertisements = self.droid_list[1]['max_advertisements'] def setup_test(self): - super(BluetoothBaseTest, self).setup_test() + super().setup_test() return reset_bluetooth(self.android_devices) def _verify_n_advertisements(self, num_advertisements): diff --git a/acts_tests/tests/google/ble/conn_oriented_chan/BleCocTest.py b/acts_tests/tests/google/ble/conn_oriented_chan/BleCocTest.py index 97ace9bbbb..166b848f26 100644 --- a/acts_tests/tests/google/ble/conn_oriented_chan/BleCocTest.py +++ b/acts_tests/tests/google/ble/conn_oriented_chan/BleCocTest.py @@ -49,14 +49,14 @@ class BleCocTest(BluetoothBaseTest): def setup_class(self): super().setup_class() self.client_ad = self.android_devices[0] - # The client which is scanning will need location to be enabled in order to - # start scan and get scan results. - utils.set_location_service(self.client_ad, True) self.server_ad = self.android_devices[1] # Note that some tests required a third device. if len(self.android_devices) > 2: self.server2_ad = self.android_devices[2] + # The client which is scanning will need location to be enabled in order to + # start scan and get scan results. + utils.set_location_service(self.client_ad, True) return setup_multiple_devices_for_bt_test(self.android_devices) def teardown_test(self): diff --git a/acts_tests/tests/google/ble/system_tests/BleStressTest.py b/acts_tests/tests/google/ble/system_tests/BleStressTest.py index f97907ff0e..afbb67a1cd 100644 --- a/acts_tests/tests/google/ble/system_tests/BleStressTest.py +++ b/acts_tests/tests/google/ble/system_tests/BleStressTest.py @@ -37,6 +37,7 @@ from acts.test_utils.bt.bt_constants import scan_result class BleStressTest(BluetoothBaseTest): default_timeout = 10 PAIRING_TIMEOUT = 20 + droid_list = [] def setup_class(self): super().setup_class() @@ -45,7 +46,7 @@ class BleStressTest(BluetoothBaseTest): self.adv_ad = self.android_devices[1] def teardown_test(self): - super(BluetoothBaseTest, self).teardown_test() + super().teardown_test() self.log_stats() def bleadvertise_verify_onsuccess_handler(self, event): diff --git a/acts_tests/tests/google/bt/performance/BtA2dpOtaRangeTest.py b/acts_tests/tests/google/bt/performance/BtA2dpOtaRangeTest.py new file mode 100755 index 0000000000..e0a7962788 --- /dev/null +++ b/acts_tests/tests/google/bt/performance/BtA2dpOtaRangeTest.py @@ -0,0 +1,554 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Thu Jul 16 22:58:03 2020 + +@author: qijiang +""" + +import os +import pyvisa +import time +import acts.test_utils.coex.audio_test_utils as atu +import acts.test_utils.bt.bt_test_utils as btutils +import pandas as pd +from acts import asserts +from acts.test_utils.abstract_devices.bluetooth_handsfree_abstract_device import BluetoothHandsfreeAbstractDeviceFactory as bt_factory +from acts.test_utils.bt.A2dpBaseTest import A2dpBaseTest +from acts.test_utils.power.PowerBTBaseTest import ramp_attenuation + +PHONE_MUSIC_FILE_DIRECTORY = '/sdcard/Music' + + +class RPIAxis(object): + def __init__(self, VisaConnectString): + """Constructor. + Create a Visa connection + + """ + rm = pyvisa.ResourceManager() + self.instrument = rm.open_resource(VisaConnectString) + self.instrument.read_termination = "\n" # make sure we look for newline at the end of strings we read + + def __getattr__(self, attr): + return getattr(self.instrument, attr) # Delegate all other attrs + + +class RPIAxis_card(RPIAxis): + """ RPIAxis_card() + Create an axis + + """ + def __init__(self, axis_object): + # create an object to communicate to an RPI2 (remote positioner instrument) axis + self.axis = axis_object # store pyvisa instrument connection + + def __getattr__(self, attr): + return getattr(self.axis, attr) # Delegate all other attrs + + def moveTo(self, where): + """ moveTo + move to a given position and make sure you arrived at the target. + """ + # max travale time in seconds. adjust this if you have a really slow positioner! + MAXTRAVELTIME = 150 + t0 = time.time() + self.axis.write("SK %d\n" % where) + done = False + while (not done): + if (time.time() - t0) > MAXTRAVELTIME: + print("looks like we are stuck!\n") + return False + response = self.axis.query("*opc?\n") + if (response == '1'): + return True + else: + response = self.axis.query("CP?\n") + + # stop the positioner + def Stop(self): + t0 = time.time() + done = False + self.axis.write("ST\n") + while (not done): + if (time.time() - t0) > 2: + print("Runaway positioner!\n") + return False + response = self.axis.query("*opc?\n") + if (response == '1'): + return True + + # set continuous rotation mode + def SetContinuousRotationMode(self): + self.axis.write("CR\n") + + # set non continuous rotation mode + def SetNonContinuousRotationMode(self): + self.axis.write("NCR\n") + + +class BtA2dpOtaRangeTest(A2dpBaseTest): + def setup_class(self): + + #'audio_params' is a dict, contains the audio device type, audio streaming + #settings such as volume, duration, audio recording parameters such as + #channel, sampling rate/width, and thdn parameters for audio processing + req_params = [ + 'audio_params', 'positioner', 'dut_config', 'attenuation_vector' + ] + opt_params = ['music_files'] + self.unpack_userparams(req_params) + if len(self.android_devices) > 1: + self.dut = self.android_devices[1] + self.unpack_userparams(opt_params) + music_src = self.music_files[0] + music_dest = PHONE_MUSIC_FILE_DIRECTORY + success = self.dut.push_system_file(music_src, music_dest) + if success: + self.music_file = os.path.join(PHONE_MUSIC_FILE_DIRECTORY, + os.path.basename(music_src)) + # Initialize media_control class + self.media = btutils.MediaControlOverSl4a(self.dut, + self.music_file) + # Set attenuator to minimum attenuation + self.attenuator = self.attenuators[0] + self.attenuator.set_atten(self.attenuation_vector['min']) + # Create the BTOE(Bluetooth-Other-End) device object + bt_devices = self.user_params.get('bt_devices', []) + attr, idx = bt_devices.split(':') + self.bt_device_controller = getattr(self, attr)[int(idx)] + self.bt_device = bt_factory().generate(self.bt_device_controller) + btutils.enable_bqr(self.bt_device_controller) + + #Setup positioner + self.PhiAxisAddress = "TCPIP0::{}::{}::SOCKET".format( + self.positioner["server_ip"], self.positioner["phi_axis_port"]) + self.ThetaAxisAddress = "TCPIP0::{}::{}::SOCKET".format( + self.positioner["server_ip"], self.positioner["theta_axis_port"]) + self.phi_axis = RPIAxis(self.PhiAxisAddress) + self.phi_card = RPIAxis_card(self.phi_axis) + self.log.info("*IDN? response: {}".format( + self.phi_card.query("*idn?\n"))) + self.theta_axis = RPIAxis(self.ThetaAxisAddress) + self.theta_card = RPIAxis_card(self.theta_axis) + self.log.info("*IDN? response: {}".format( + self.theta_card.query("*idn?\n"))) + self.phi_card.Stop() + self.theta_card.Stop() + + def teardown_class(self): + + if hasattr(self, 'media'): + self.media.stop() + if hasattr(self, 'attenuator'): + self.attenuator.set_atten(self.attenuation_vector['min']) + if hasattr(self, 'dut'): + self.dut.droid.bluetoothFactoryReset() + btutils.disable_bluetooth(self.dut.droid) + self.bt_device.reset() + self.bt_device.power_off() + self.phi_card.moveTo(0) + self.theta_card.moveTo(0) + + def setup_test(self): + + # Reset headset + self.bt_device.reset() + # Initialize audio capture devices + self.audio_device = atu.get_audio_capture_device( + self.bt_device_controller, self.audio_params) + # Connect BT link + connected = self.establish_bt_connection() + asserts.assert_true(connected, 'BT connection failed') + # Output file + file_name = 'OTA_Range_Over_Angle_{}_{}.csv'.format( + self.dut_config['model'], self.dut_config['screen_placement']) + self.file_output = os.path.join(self.log_path, file_name) + + def teardown_test(self): + + if hasattr(self, 'media'): + self.media.stop() + if hasattr(self, 'attenuator'): + self.attenuator.set_atten(self.attenuation_vector['min']) + if hasattr(self, 'dut'): + self.dut.droid.bluetoothFactoryReset() + btutils.disable_bluetooth(self.dut.droid) + self.bt_device.reset() + self.bt_device.power_off() + self.phi_card.moveTo(0) + self.theta_card.moveTo(0) + + def a2dp_play(self): + + if hasattr(self, 'dut'): + vol = self.dut.droid.getMaxMediaVolume( + ) * self.audio_params['volume'] + self.dut.droid.setMediaVolume(vol) + self.media.play() + else: + vol = self.bt_device_controller.droid.getMaxMediaVolume( + ) * self.audio_params['volume'] + self.bt_device_controller.droid.setMediaVolume(vol) + self.bt_device.previous_track() + self.bt_device.play() + + def a2dp_stop(self): + + if hasattr(self, 'dut'): + self.media.stop() + else: + self.bt_device.pause() + + def establish_bt_connection(self): + + if hasattr(self, 'dut'): + self.dut.droid.bluetoothFactoryReset() + self.bt_device.reset() + self.bt_device.power_on() + btutils.enable_bluetooth(self.dut.droid, self.dut.ed) + connected = btutils.connect_phone_to_headset( + self.dut, self.bt_device, 60) + vol = self.dut.droid.getMaxMediaVolume( + ) * self.audio_params['volume'] + self.dut.droid.setMediaVolume(0) + time.sleep(1) + self.dut.droid.setMediaVolume(int(vol)) + self.media.play() + return connected + + elif len(self.bt_device_controller.droid. + bluetoothA2dpSinkGetConnectedDevices()) == 0: + self.log.warning('Need manual intervention to connect BT link') + os.system( + 'spd-say "Please manually connect BT and start playback"') + input('Once fixed, please press ENTER to resume the test') + return 1 + + def run_thdn_analysis(self, audio_captured): + """Calculate Total Harmonic Distortion plus Noise for latest recording. + + Args: + audio_captured: the captured audio file + Returns: + thdn: thdn value in a list + """ + # Calculate Total Harmonic Distortion + Noise + audio_result = atu.AudioCaptureResult(audio_captured, + self.audio_params) + thdn = audio_result.THDN(**self.audio_params['thdn_params']) + return thdn + + def record_audio_and_analyze_thdn(self): + + self.a2dp_play() + time.sleep(1) + self.audio_device.start() + time.sleep(self.audio_params['duration']) + audio_captured = self.audio_device.stop() + audio_result = atu.AudioCaptureResult(audio_captured, + self.audio_params) + thdn = audio_result.THDN(**self.audio_params['thdn_params']) + self.log.info('THDN is {}'.format(thdn[0])) + + self.a2dp_stop() + + return thdn[0] + + def recover_bt_link(self): + """Recover BT link during test. + + Recover BT link from the a2dp sink device + + Returns: + connected: signal whether bt link is restored + """ + #Try to connect from the sink device + if len(self.bt_device_controller.droid.bluetoothGetConnectedDevices() + ) == 0: + self.log.warning('Try to recover BT link') + self.attenuator.set_atten(self.attenuation_vector['min']) + + if hasattr(self, 'dut'): + connected = self.establish_bt_connection() + return connected + else: + device_bonded = self.bt_device_controller.droid.bluetoothGetBondedDevices( + )[0]['address'] + trial_count = 0 + trial_limit = 3 + self.log.info('Try to reconnect from the sink device') + while trial_count < trial_limit: + #Connect master device from the sink device + time_start = time.time() + while time.time() < time_start + 5: + try: + self.bt_device_controller.droid.bluetoothConnectBonded( + device_bonded) + break + except: + pass + time.sleep(2) + if len(self.bt_device_controller.droid. + bluetoothA2dpSinkGetConnectedDevices()) > 0: + vol = self.bt_device_controller.droid.getMaxMediaVolume( + ) * self.audio_params['volume'] + self.bt_device_controller.droid.setMediaVolume(0) + time.sleep(1) + self.bt_device_controller.droid.setMediaVolume( + int(vol)) + return 1 + trial_count += 1 + #Automated reconnect from sink device doesn't work, start fresh connection + if trial_count >= trial_limit: + self.log.info( + 'Need manual intervention on the master device side') + connected = self.establish_bt_connection() + return connected + else: + return 1 + + def find_bt_max_range_bisection_search(self): + + #First linear search to narrow the bisection search + atten_min = self.attenuation_vector['min'] + atten_max = self.attenuation_vector['max'] + atten_step = self.attenuation_vector['step_bisection'] + #Start from initial attenuation + atten_left = atten_min + atten_right = atten_min + while atten_left == atten_right and atten_left < atten_max: + atten_now = self.attenuator.get_atten() + connected = self.recover_bt_link() + if connected == 0: + self.log.warning("Skip this angle as BT connection failed") + max_range = atten_max + return max_range + else: + self.log.info('Connection restored') + ramp_attenuation(self.attenuator, atten_now) + self.log.info("Attenuation set to {}".format(atten_now)) + time.sleep(2) + + thdn = self.record_audio_and_analyze_thdn() + if thdn > self.audio_params['thdn_threshold'] or thdn == 0: + #Hit the right limit for bisection search + if atten_right == atten_min: + self.log.warning('Link breaks at the minimum attenuation') + max_range = atten_min + return max_range + else: + atten_right = atten_now + self.log.info( + 'Right limit found at {} dB'.format(atten_right)) + else: + atten_left = atten_now + atten_right = atten_left + atten_next = min(atten_now + atten_step, atten_max) + ramp_attenuation(self.attenuator, atten_next) + if atten_left == atten_right: + self.log.warning('Could not reach max range') + max_range = atten_max + return max_range + + #Start the bisection search + self.log.info('Start bisection search between {} dB and {} dB'.format( + atten_left, atten_right)) + while atten_right - atten_left > 1: + connected = self.recover_bt_link() + if connected == 0: + self.log.warning("Skip this angle as BT connection failed") + max_range = atten_max + return max_range + else: + self.log.info('Connection restored') + + atten_mid = round((atten_left + atten_right) / 2) + ramp_attenuation(self.attenuator, atten_mid) + atten_now = self.attenuator.get_atten() + self.log.info("Attenuation set to {}".format(atten_now)) + time.sleep(5) + thdn = self.record_audio_and_analyze_thdn() + if thdn > self.audio_params['thdn_threshold'] or thdn == 0: + atten_right = atten_mid + max_range = atten_right - 1 + else: + atten_left = atten_mid + max_range = atten_left + self.log.info('Max range reached at {} dB'.format(max_range)) + return max_range + + def find_bt_max_range_linear_fine_search(self): + + thdn = 0.03 + atten_now = self.attenuator.get_atten() + + while thdn < self.audio_params[ + 'thdn_threshold'] and thdn != 0 and atten_now < self.attenuation_vector[ + 'max']: + atten_now = self.attenuator.get_atten() + self.log.info("Attenuation set to {}".format(atten_now)) + thdn = self.record_audio_and_analyze_thdn() + self.log.info("THDN is {}".format(thdn)) + self.attenuator.set_atten(atten_now + + self.attenuation_vector['step_fine']) + max_range = self.attenuator.get_atten( + ) - self.attenuation_vector['step_fine'] * 2 + if thdn == 0: + self.log.warning( + "Music play stopped, link might get lost, max range reached at {} dB" + .format(max_range)) + else: + self.log.info("Max range reached at {}".format(max_range)) + if atten_now == self.attenuation_vector['max']: + self.log.warning("Fail to reach max range") + return max_range + + def test_bisection_search_max(self): + + #Find the BT max range under each angle using bisection search + max_range_all = [] + + for phi in self.positioner['phi_range']: + + succeed = self.phi_card.moveTo(phi) + if succeed: + self.log.info("Phi positioner moved to {} degree".format(phi)) + else: + self.log.warning( + "Fail to move phi positioner to {} degree".format(phi)) + self.log.info("Phi positioner moved to {} degree".format(phi)) + max_ranges = [phi] + + for theta in self.positioner['theta_range']: + + succeed = self.theta_card.moveTo(theta) + if succeed: + self.log.info( + "Theta positioner moved to {} degree".format(theta)) + else: + self.log.warning( + "Failed to move theta positioner to {} degree".format( + theta)) + self.log.info( + "Theta positioner moved to {} degree".format(theta)) + + ramp_attenuation(self.attenuator, + self.attenuation_vector['min']) + time.sleep(2) + max_range = self.find_bt_max_range_bisection_search() + max_ranges.append(max_range) + max_range_all.append(max_ranges) + columns = ['Phi/Theta'] + columns.extend(self.positioner['theta_range']) + df = pd.DataFrame(max_range_all, columns=columns) + df.to_csv(self.file_output, index=False) + + def test_coarse_search(self): + + #Coarse search to find the highest minimum attenuation can be set to + #be a starting point for all angles + thdn = 0.03 + max_atten_reached = 0 + ramp_attenuation(self.attenuator, + self.attenuation_vector['start_coarse']) + self.log.info('Start attenuation at {} dB'.format( + self.attenuator.get_atten())) + while True: + atten_now = self.attenuator.get_atten() + if atten_now == self.attenuation_vector['max']: + if max_atten_reached > 1: + self.log.warning( + 'Can not reach to the highest minimum, attenuator is already set to be max, need to add more attenuation' + ) + break + for phi in self.positioner['phi_range']: + if thdn == 0 or thdn >= self.audio_params["thdn_threshold"]: + break + succeed = self.phi_card.moveTo(phi) + if succeed: + self.log.info( + "Phi positioner moved to {} degree".format(phi)) + else: + self.log.warning( + "Fail to move phi positioner to {} degree".format(phi)) + self.log.info("Phi positioner moved to {} degree".format(phi)) + + for theta in self.positioner['theta_range']: + + succeed = self.theta_card.moveTo(theta) + if succeed: + self.log.info( + "Theta positioner moved to {} degree".format( + theta)) + else: + self.log.warning( + "Failed to move theta positioner to {} degree". + format(theta)) + self.log.info( + "Theta positioner moved to {} degree".format(theta)) + + thdn = self.record_audio_and_analyze_thdn() + self.log.info( + 'THDN at thea {} degree, phi {} degree is {}'.format( + theta, phi, thdn)) + if thdn == 0 or thdn >= self.audio_params["thdn_threshold"]: + break + if thdn == 0 or thdn >= self.audio_params["thdn_threshold"]: + highest_max = self.attenuator.get_atten( + ) - self.attenuation_vector['step_coarse'] + self.log.info( + 'Highest minimum attenuation is {} dB, fine search can start from there' + .format(highest_max)) + break + atten_new = min(atten_now + self.attenuation_vector['step_coarse'], + self.attenuation_vector['max']) + if atten_new == self.attenuation_vector['max']: + max_atten_reached += 1 + self.attenuator.set_atten(atten_new) + self.log.info('\nSetting attenuator to {} dB'.format( + self.attenuator.get_atten())) + + def test_finestep_search_max(self): + + #Find the BT max range under each angle with a finer step search + max_range_all = [] + for phi in self.positioner['phi_range']: + + succeed = self.phi_card.moveTo(phi) + if succeed: + self.log.info("Phi positioner moved to {} degree".format(phi)) + else: + self.log.warning( + "Fail to move phi positioner to {} degree".format(phi)) + self.log.info("Phi positioner moved to {} degree".format(phi)) + max_ranges = [phi] + + for theta in self.positioner['theta_range']: + + succeed = self.theta_card.moveTo(theta) + if succeed: + self.log.info( + "Theta positioner moved to {} degree".format(theta)) + else: + self.log.warning( + "Failed to move theta positioner to {} degree".format( + theta)) + self.log.info( + "Theta positioner moved to {} degree".format(theta)) + connected = self.recover_bt_link() + if connected == 0: + self.log.warning("Skip this angle as BT connection failed") + max_range = self.attenuation_vector['max'] + return max_range + else: + self.log.info('Connection restored') + ramp_attenuation(self.attenuator, + self.attenuation_vector['start_fine']) + max_range = self.find_bt_max_range_linear_fine_search() + max_ranges.append(max_range) + max_range_all.append(max_ranges) + columns = ['Phi/Theta'] + columns.extend(self.positioner['theta_range']) + df_range = pd.DataFrame(max_range_all, columns=columns) + df_range.to_csv(self.file_output, index=False) diff --git a/acts_tests/tests/google/bt/performance/BtInterferenceDynamicTest.py b/acts_tests/tests/google/bt/performance/BtInterferenceDynamicTest.py new file mode 100644 index 0000000000..664121dd9d --- /dev/null +++ b/acts_tests/tests/google/bt/performance/BtInterferenceDynamicTest.py @@ -0,0 +1,357 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +"""Stream music through connected device from phone across different +attenuations.""" + +import random +import time +from acts.signals import TestFailure +from acts.test_utils.bt.BtInterferenceBaseTest import BtInterferenceBaseTest +from acts.test_utils.bt.BtInterferenceBaseTest import get_iperf_results +from acts.test_utils.power.PowerBTBaseTest import ramp_attenuation +from multiprocessing import Process, Queue + +DEFAULT_THDN_THRESHOLD = 0.9 +MAX_ATTENUATION = 95 +TIME_OVERHEAD = 2 + + +class BtInterferenceDynamicTest(BtInterferenceBaseTest): + def __init__(self, configs): + super().__init__(configs) + self.iperf_duration = self.audio_params['duration'] + TIME_OVERHEAD + self.wait_for_interference = self.dynamic_wifi_interference[ + 'waittime_to_inject_interference'] + self.channel_change_interval = self.dynamic_wifi_interference[ + 'channel_change_interval_second'] + self.interference_channels = self.dynamic_wifi_interference[ + 'two_hoppable_channels'] + self.dynamic_wifi_interference[ + 'one_hoppable_channel'] + + self.bt_signal_levels = list( + self.dynamic_wifi_interference['bt_signal_level'].keys()) + self.wifi_int_levels = list( + self.dynamic_wifi_interference['interference_level'].keys()) + self.bt_atten_levels = list( + self.dynamic_wifi_interference['bt_signal_level'].values()) + self.wifi_atten_levels = list( + self.dynamic_wifi_interference['interference_level'].values()) + for bt_level in self.bt_signal_levels: + bt_atten_level = self.dynamic_wifi_interference['bt_signal_level'][ + bt_level] + for wifi_level in self.wifi_int_levels: + interference_atten_level = self.dynamic_wifi_interference[ + 'interference_level'][wifi_level] + self.generate_test_case_randomchange( + bt_atten_level, interference_atten_level, + self.channel_change_interval) + for channels in self.interference_channels: + self.generate_test_case(bt_atten_level, + interference_atten_level, channels) + + def generate_test_case(self, bt_atten_level, interference_atten_level, + dynamic_channels): + """Function to generate test cases with different parameters. + Args: + bt_atten_level: bt path attenuation level + interference_atten_level: wifi interference path attenuation level + channels: wifi interference channel or channel combination + """ + def test_case_fn(): + self.bt_afh_with_dynamic_interference(bt_atten_level, + interference_atten_level, + dynamic_channels) + + bt_signal_level = self.bt_signal_levels[self.bt_atten_levels.index( + bt_atten_level)] + wifi_int_level = self.wifi_int_levels[self.wifi_atten_levels.index( + interference_atten_level)] + interference_chans_before = dynamic_channels[0] + interference_chans_after = dynamic_channels[1] + chans_before_str = 'channel_' + chans_after_str = 'channel_' + if 0 in interference_chans_before: + chans_before_str = 'no_interference' + else: + for i in interference_chans_before: + chans_before_str = chans_before_str + str(i) + '_' + for i in interference_chans_after: + chans_after_str = chans_after_str + str(i) + '_' + test_case_name = ('test_bt_afh_from_{}to_{}bt_signal_level_{}_' + 'interference_level_{}'.format( + chans_before_str, chans_after_str, + bt_signal_level, wifi_int_level)) + setattr(self, test_case_name, test_case_fn) + + def generate_test_case_randomchange(self, bt_atten_level, + interference_atten_level, interval): + def test_case_fn(): + self.bt_afh_with_fast_changing_interference( + bt_atten_level, interference_atten_level, interval) + + bt_signal_level = self.bt_signal_levels[self.bt_atten_levels.index( + bt_atten_level)] + wifi_int_level = self.wifi_atten_levels[self.wifi_atten_levels.index( + interference_atten_level)] + test_case_name = ('test_bt_afh_with_random_channel_interference_bt' + '_signal_level_{}_interference_level_{}'.format( + bt_signal_level, wifi_int_level)) + setattr(self, test_case_name, test_case_fn) + + def interference_rssi_mapping_from_attenuation(self, interference_level): + """Function to get wifi rssi-to-interference level mapping + Args: + interference_level: interference level in terms of attenuation + """ + self.log.info('Get WiFi RSSI at the desired attenuation level') + for obj in self.wifi_int_pairs: + obj.attenuator.set_atten(interference_level) + self.get_interference_rssi() + + def get_rssi_at_channel(self, channel): + """Function to get wifi rssi-to-interference level at each channel + Args: + channel: the channel to query the rssi + Returns: + rssi: wifi rssi at the queried channel + """ + for item in self.interference_rssi: + if item['chan'] == channel: + rssi = item['rssi'] + return rssi + + def inject_dynamic_wifi_interference(self, interference_level, + interference_channels, time_wait): + """Function to inject dynamic wifi interference to bt link. + Args: + interference_level: signal strength of interference, represented + by attenuation level + interference_channels: interference channel for before and after, + e.g. [chans_before, chans_after] + time_wait: time wait to inject new interference + """ + all_pair = range(len(self.wifi_int_pairs)) + #List of channels before and after changing the interference + interference_chans_before = interference_channels[0] + interference_chans_after = interference_channels[1] + #Set existing wifi interference attenuation level + if 0 not in interference_chans_before: + interference_pair_indices_before = self.locate_interference_pair_by_channel( + interference_chans_before) + inactive_interference_pair_indices_before = [ + item for item in all_pair + if item not in interference_pair_indices_before + ] + self.log.info( + 'Set pre-existing interference before A2DP streaming') + for i in interference_pair_indices_before: + self.log.info( + 'Set {} dB on attenuator {}, wifi rssi {} dBm at chan {}'. + format( + interference_level, i + 1, + self.get_rssi_at_channel( + self.wifi_int_pairs[i].channel), + self.wifi_int_pairs[i].channel)) + self.wifi_int_pairs[i].attenuator.set_atten(interference_level) + for i in inactive_interference_pair_indices_before: + self.log.info('Set attenuation {} dB on attenuator {}'.format( + MAX_ATTENUATION, i + 1)) + self.wifi_int_pairs[i].attenuator.set_atten(MAX_ATTENUATION) + ##Debug_purpose + for i in self.attenuators: + self.log.info(i.get_atten()) + + #Set after change wifi interference attenuation level + interference_pair_indices_after = self.locate_interference_pair_by_channel( + interference_chans_after) + inactive_interference_pair_indices_after = [ + item for item in all_pair + if item not in interference_pair_indices_after + ] + #Wait for time_wait second to inject new interference + time.sleep(time_wait) + self.log.info('Inject new interference during A2DP streaming') + for i in interference_pair_indices_after: + self.log.info( + 'Set {} dB on attenuator {}, with wifi rssi {} dBm at chan {}'. + format( + interference_level, i + 1, + self.get_rssi_at_channel(self.wifi_int_pairs[i].channel), + self.wifi_int_pairs[i].channel)) + self.wifi_int_pairs[i].attenuator.set_atten(interference_level) + for i in inactive_interference_pair_indices_after: + self.log.info('Set attenuation {} dB on attenuator {}'.format( + MAX_ATTENUATION, i + 1)) + self.wifi_int_pairs[i].attenuator.set_atten(MAX_ATTENUATION) + ##Debug_purpose + for i in self.attenuators: + self.log.info('Attenuator {} set to {} dB'.format( + self.attenuators.index(i) + 1, i.get_atten())) + self.log.info('Dymanic inteference injected') + + def inject_fast_changing_wifi_interference(self, interference_level, + interval): + """Function to inject changing wifi interference one channel a time. + Args: + interference_level: signal strength of interference, represented + by attenuation level + interval: interval between channel changes + """ + all_pair = range(len(self.wifi_int_pairs)) + #Set initial WiFi interference at channel 1 + self.log.info('Start with interference at channel 1') + self.wifi_int_pairs[0].attenuator.set_atten(interference_level) + self.wifi_int_pairs[1].attenuator.set_atten(MAX_ATTENUATION) + self.wifi_int_pairs[2].attenuator.set_atten(MAX_ATTENUATION) + current_int_pair = [0] + inactive_int_pairs = [ + item for item in all_pair if item not in current_int_pair + ] + time.sleep(interval) + #Inject randomlized channel wifi interference + self.log.info( + 'Inject random changing channel (1,6,11) wifi interference' + 'every {} second'.format(interval)) + while True: + current_int_pair = [ + random.randint(inactive_int_pairs[0], inactive_int_pairs[1]) + ] + inactive_int_pairs = [ + item for item in all_pair if item not in current_int_pair + ] + self.wifi_int_pairs[current_int_pair[0]].attenuator.set_atten( + interference_level) + self.log.info( + 'Current interference {} at channel {} with rssi {} dBm'. + format( + interference_level, + self.wifi_int_pairs[current_int_pair[0]].channel, + self.get_rssi_at_channel( + self.wifi_int_pairs[current_int_pair[0]].channel))) + for i in inactive_int_pairs: + self.wifi_int_pairs[i].attenuator.set_atten(MAX_ATTENUATION) + ##Debug_purpose + for i in self.attenuators: + self.log.info('Attenuator {} set to {} dB'.format( + self.attenuators.index(i) + 1, i.get_atten())) + time.sleep(interval) + + def bt_afh_with_dynamic_interference(self, bt_atten_level, + interference_atten_level, + dynamic_channels): + """Run a2dp audio quality with dynamic interference added. + Args: + bt_atten_level: signal level of bt in terms of attenuation + interference_atten_level: interference level in terms of attenuation + dynamic_channels: interference channels before and after + """ + ramp_attenuation(self.attenuator, bt_atten_level) + self.interference_rssi_mapping_from_attenuation( + interference_atten_level) + [rssi_master, pwl_master, rssi_slave] = self._get_bt_link_metrics() + tag_bt = 'bt_signal_level_{}_rssi_{}_dBm'.format( + bt_atten_level, rssi_master) + procs_iperf = [] + for obj in self.wifi_int_pairs: + obj.iperf_server.start() + iperf_args = '-i 1 -t {} -p {} -J -R'.format( + self.iperf_duration, obj.iperf_server.port) + tag = 'chan_{}'.format(obj.channel) + proc_iperf = Process(target=obj.iperf_client.start, + args=(obj.server_address, iperf_args, tag)) + proc_iperf.start() + procs_iperf.append(proc_iperf) + self.log.info('Start IPERF on all three channels') + queue = Queue() + proc_bt_audio = Process(target=self.play_and_record_audio, + args=(self.audio_params['duration'], queue)) + proc_interference = Process( + target=self.inject_dynamic_wifi_interference, + args=(interference_atten_level, dynamic_channels, + self.wait_for_interference)) + proc_bt_audio.start() + proc_interference.start() + proc_bt_audio.join() + proc_interference.join() + for proc in procs_iperf: + proc.join() + for obj in self.wifi_int_pairs: + iperf_throughput = get_iperf_results(obj.iperf_server) + self.log.info( + 'Throughput for channel {} interference is {} Mbps'.format( + obj.channel, iperf_throughput)) + obj.iperf_server.stop() + audio_captured = queue.get() + thdns = self.run_thdn_analysis(audio_captured, tag_bt) + self.log.info('THDN results are {}'.format(thdns)) + for thdn in thdns: + if thdn >= self.audio_params['thdn_threshold']: + raise TestFailure('AFH failed') + + def bt_afh_with_fast_changing_interference(self, bt_atten_level, + interference_atten_level, + interval): + """Run a2dp audio quality with random channel fast changing interference + Args: + bt_signale_level: signal level of bt in terms of attenuation + interference_level: interference level in terms of attenuation + interval: interval between channel changes + """ + ramp_attenuation(self.attenuator, bt_atten_level) + self.interference_rssi_mapping_from_attenuation( + interference_atten_level) + [rssi_master, pwl_master, rssi_slave] = self._get_bt_link_metrics() + tag_bt = 'bt_signal_level_{}_rssi_{}_dBm'.format( + bt_atten_level, rssi_master) + procs_iperf = [] + #Start IPERF on all three interference pairs + for obj in self.wifi_int_pairs: + obj.iperf_server.start() + iperf_args = '-i 1 -t {} -p {} -J -R'.format( + self.iperf_duration, obj.iperf_server.port) + tag = 'chan_{}'.format(obj.channel) + proc_iperf = Process(target=obj.iperf_client.start, + args=(obj.server_address, iperf_args, tag)) + proc_iperf.start() + procs_iperf.append(proc_iperf) + self.log.info('Start IPERF on all three channels') + queue = Queue() + proc_bt_audio = Process(target=self.play_and_record_audio, + args=(self.audio_params['duration'], queue)) + proc_interference = Process( + target=self.inject_fast_changing_wifi_interference, + args=(interference_atten_level, interval)) + proc_bt_audio.start() + proc_interference.start() + proc_bt_audio.join() + while proc_bt_audio.is_alive(): + continue + proc_interference.terminate() + proc_interference.join() + for proc in procs_iperf: + proc.join() + for obj in self.wifi_int_pairs: + iperf_throughput = get_iperf_results(obj.iperf_server) + self.log.info( + 'Throughput for channel {} interference is {} Mbps'.format( + obj.channel, iperf_throughput)) + obj.iperf_server.stop() + audio_captured = queue.get() + thdns = self.run_thdn_analysis(audio_captured, tag_bt) + self.log.info('THDN results are {}'.format(thdns)) + for thdn in thdns: + if thdn >= self.audio_params['thdn_threshold']: + raise TestFailure('AFH failed') diff --git a/acts_tests/tests/google/bt/performance/BtInterferenceStaticTest.py b/acts_tests/tests/google/bt/performance/BtInterferenceStaticTest.py new file mode 100644 index 0000000000..84ca6d418f --- /dev/null +++ b/acts_tests/tests/google/bt/performance/BtInterferenceStaticTest.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +"""Stream music through connected device from phone across different +attenuations.""" +from acts.signals import TestPass +from acts.test_utils.bt.BtInterferenceBaseTest import BtInterferenceBaseTest +from acts.metrics.loggers.blackbox import BlackboxMetricLogger +from acts.test_utils.bt.BtInterferenceBaseTest import get_iperf_results +from multiprocessing import Process, Queue + +DEFAULT_THDN_THRESHOLD = 0.9 +MAX_ATTENUATION = 95 +TIME_OVERHEAD = 2 + + +class BtInterferenceStaticTest(BtInterferenceBaseTest): + def __init__(self, configs): + super().__init__(configs) + self.bt_attenuation_range = range(self.attenuation_vector['start'], + self.attenuation_vector['stop'] + 1, + self.attenuation_vector['step']) + + self.iperf_duration = self.audio_params[ + 'duration'] + TIME_OVERHEAD + for level in list( + self.static_wifi_interference['interference_level'].keys()): + for channels in self.static_wifi_interference['channels']: + self.generate_test_case( + self.static_wifi_interference['interference_level'][level], + channels) + + test_metrics = [ + 'wifi_chan1_rssi', 'wifi_chan6_rssi', 'wifi_chan11_rssi', + 'bt_range' + ] + for metric in test_metrics: + setattr(self, '{}_metric'.format(metric), + BlackboxMetricLogger.for_test_case(metric_name=metric)) + + def generate_test_case(self, interference_level, channels): + """Function to generate test cases with different parameters. + + Args: + interference_level: wifi interference signal level + channels: wifi interference channel or channel combination + """ + def test_case_fn(): + self.bt_range_with_static_wifi_interference( + interference_level, channels) + + str_channel_test = '' + for i in channels: + str_channel_test = str_channel_test + str(i) + '_' + test_case_name = ('test_bt_range_with_static_interference_level_{}_' + 'channel_{}'.format(interference_level, + str_channel_test)) + setattr(self, test_case_name, test_case_fn) + + def inject_static_wifi_interference(self, interference_level, channels): + """Function to inject wifi interference to bt link and read rssi. + + Interference of IPERF traffic is always running, by setting attenuation, + the gate is opened to release the interference to the setup. + Args: + interference_level: the signal strength of wifi interference, use + attenuation level to represent this + channels: wifi channels where interference will + be injected, list + """ + all_pair = range(len(self.wifi_int_pairs)) + interference_pair_indices = self.locate_interference_pair_by_channel( + channels) + inactive_interference_pairs_indices = [ + item for item in all_pair if item not in interference_pair_indices + ] + self.log.info( + 'WiFi interference at {} and inactive channels at {}'.format( + interference_pair_indices, + inactive_interference_pairs_indices)) + for i in interference_pair_indices: + self.wifi_int_pairs[i].attenuator.set_atten(interference_level) + self.log.info('Set attenuation {} dB on attenuator {}'.format( + self.wifi_int_pairs[i].attenuator.get_atten(), i + 1)) + for i in inactive_interference_pairs_indices: + self.wifi_int_pairs[i].attenuator.set_atten(MAX_ATTENUATION) + self.log.info('Set attenuation {} dB on attenuator {}'.format( + self.wifi_int_pairs[i].attenuator.get_atten(), i + 1)) + #Read interference RSSI + self.get_interference_rssi() + self.wifi_chan1_rssi_metric.metric_value = self.interference_rssi[0][ + 'rssi'] + self.wifi_chan6_rssi_metric.metric_value = self.interference_rssi[1][ + 'rssi'] + self.wifi_chan11_rssi_metric.metric_value = self.interference_rssi[2][ + 'rssi'] + + def bt_range_with_static_wifi_interference(self, interference_level, + channels): + """Test function to measure bt range under interference. + + Args: + interference_level: wifi interference level + channels: wifi interference channels + """ + #setup wifi interference by setting the correct attenuator + self.inject_static_wifi_interference(interference_level, channels) + for atten in self.bt_attenuation_range: + # Set attenuation for BT link + self.attenuator.set_atten(atten) + [rssi_master, pwl_master, rssi_slave] = self._get_bt_link_metrics() + tag = 'attenuation_{}dB_'.format(atten) + self.log.info( + 'BT attenuation set to {} dB and start A2DP streaming'.format( + atten)) + procs_iperf = [] + for obj in self.wifi_int_pairs: + obj.iperf_server.start() + self.log.info('Started IPERF server at port {}'.format( + obj.iperf_server.port)) + iperf_args = '-i 1 -t {} -p {} -J -R'.format( + self.iperf_duration, obj.iperf_server.port) + tag = 'chan_{}'.format(obj.channel) + proc_iperf = Process(target=obj.iperf_client.start, + args=(obj.server_address, iperf_args, + tag)) + procs_iperf.append(proc_iperf) + + #play a2dp streaming and run thdn analysis + queue = Queue() + proc_bt = Process(target=self.play_and_record_audio, + args=(self.audio_params['duration'],queue)) + for proc in procs_iperf: + proc.start() + proc_bt.start() + proc_bt.join() + for proc in procs_iperf: + proc.join() + for obj in self.wifi_int_pairs: + iperf_throughput = get_iperf_results(obj.iperf_server) + self.log.info( + 'Throughput for channel {} interference is {} Mbps'.format( + obj.channel, iperf_throughput)) + obj.iperf_server.stop() + self.log.info('Stopped IPERF server at port {}'.format( + obj.iperf_server.port)) + audio_captured = queue.get() + thdns = self.run_thdn_analysis(audio_captured, tag) + self.log.info('THDN results are {} at {} dB attenuation' + .format(thdns, atten)) + self.log.info('master rssi {} dBm, master tx power level {}, ' + 'slave rssi {} dBm' + .format(rssi_master, pwl_master, rssi_slave)) + for thdn in thdns: + if thdn >= self.audio_params['thdn_threshold']: + self.log.info('Under the WiFi interference condition: ' + 'channel 1 RSSI: {} dBm, ' + 'channel 6 RSSI: {} dBm' + 'channel 11 RSSI: {} dBm' + .format(self.interference_rssi[0]['rssi'], + self.interference_rssi[1]['rssi'], + self.interference_rssi[2]['rssi'])) + raise TestPass( + 'Max range for this test is {}, with BT master RSSI at' + ' {} dBm'.format(atten, rssi_master)) diff --git a/acts_tests/tests/google/bt/sar/BleSarPowerLimitTest.py b/acts_tests/tests/google/bt/sar/BleSarPowerLimitTest.py index d732e7788e..490cb30cc0 100644 --- a/acts_tests/tests/google/bt/sar/BleSarPowerLimitTest.py +++ b/acts_tests/tests/google/bt/sar/BleSarPowerLimitTest.py @@ -46,6 +46,9 @@ class BleSarPowerLimitTest(BtSarBaseTest): #self.dut.droid.bluetoothFactoryReset() #bt_utils.enable_bluetooth(self.dut.droid, self.bt_device.ed) + #Reset SAR test result to 0 before every test + self.sar_test_result.metric_value = 0 + # To prevent default file from being overwritten self.dut.adb.shell('cp {} {}'.format(self.power_file_paths[0], self.power_file_paths[1])) diff --git a/acts_tests/tests/google/bt/sar/BtSarPowerLimitTest.py b/acts_tests/tests/google/bt/sar/BtSarPowerLimitTest.py index f4e667ce3a..74a1000231 100644 --- a/acts_tests/tests/google/bt/sar/BtSarPowerLimitTest.py +++ b/acts_tests/tests/google/bt/sar/BtSarPowerLimitTest.py @@ -18,8 +18,10 @@ import os import time import acts.test_utils.bt.bt_test_utils as bt_utils from acts.test_utils.bt.BtSarBaseTest import BtSarBaseTest +from acts.test_utils.power.PowerBTBaseTest import ramp_attenuation import acts.test_utils.wifi.wifi_performance_test_utils as wifi_utils +SLEEP_DURATION = 2 class BtSarPowerLimitTest(BtSarBaseTest): """Class to define BT SAR power cap tests. @@ -58,10 +60,15 @@ class BtSarPowerLimitTest(BtSarBaseTest): def test_bt_sar_custom_table(self): self.push_table(self.dut, self.custom_sar_path) + self.attenuator.set_atten(0) # Connect master and slave + self.bt_device.reset() + self.dut.droid.bluetoothFactoryReset() bt_utils.connect_phone_to_headset(self.dut, self.bt_device, 60) - + time.sleep(SLEEP_DURATION) + ramp_attenuation(self.attenuator, self.pl10_atten) + time.sleep(SLEEP_DURATION) sar_df = self.sweep_table() sar_df = self.process_table(sar_df) self.process_results(sar_df) diff --git a/acts_tests/tests/google/bt/sar/BtSarSanityTest.py b/acts_tests/tests/google/bt/sar/BtSarSanityTest.py index 6e49bbf048..7ef8840a25 100644 --- a/acts_tests/tests/google/bt/sar/BtSarSanityTest.py +++ b/acts_tests/tests/google/bt/sar/BtSarSanityTest.py @@ -19,6 +19,7 @@ import re import time from acts import asserts +import acts.test_utils.bt.bt_test_utils as bt_utils from acts.test_utils.bt.BtSarBaseTest import BtSarBaseTest @@ -28,8 +29,47 @@ class BtSarSanityTest(BtSarBaseTest): This class defines sanity test cases on BT SAR. The tests include a software state sanity check and a software power sanity check. """ - def __init__(self, controllers): - super().__init__(controllers) + + def setup_class(self): + super().setup_class() + + self.reg_domain_dict = { + 'US': 'bluetooth_power_limits_US.csv', + 'EU': 'bluetooth_power_limits_EU.csv', + 'JP': 'bluetooth_power_limits_JP.csv' + } + + #Backup BT SAR files on the device + for key in self.reg_domain_dict.keys(): + reg_file_path = os.path.join( + os.path.dirname(self.power_file_paths[0]), + self.reg_domain_dict[key]) + self.dut.adb.shell('cp {} {}.backup'.format( + reg_file_path, reg_file_path)) + + self.log.info('Regulatory files backed up') + + def setup_test(self): + + #Reset SAR test result to 0 before every test + self.sar_test_result.metric_value = 0 + + # Starting BT on the master + self.dut.droid.bluetoothFactoryReset() + bt_utils.enable_bluetooth(self.dut.droid, self.dut.ed) + + def teardown_class(self): + for key in self.reg_domain_dict.keys(): + reg_file_path = os.path.join( + os.path.dirname(self.power_file_paths[0]), + self.reg_domain_dict[key]) + self.dut.adb.shell('mv {}.backup {}'.format( + reg_file_path, reg_file_path)) + + self.log.info('Regulatory files restored') + + self.dut.reboot() #TODO: make this better + super().teardown_class() def test_bt_sar_sanity_check_state(self): """ Test for BT SAR State Sanity @@ -59,10 +99,11 @@ class BtSarSanityTest(BtSarBaseTest): try: propagated_value = int( re.findall(key_regex, device_state)[0]) - except TypeError: + except IndexError: propagated_value = 'NA' if enforced_state[key] == propagated_value: + self.sar_test_result.metric_value = 1 self.log.info( 'scenario: {}, state : {}, forced_value: {}, value:{}'. format(scenario, key, enforced_state[key], @@ -82,60 +123,12 @@ class BtSarSanityTest(BtSarBaseTest): the BT SAR file to the power cap read from logcat """ - sar_df = self.bt_sar_df - sar_df['BDR_power_cap'] = -128 - sar_df['EDR_power_cap'] = -128 - sar_df['BLE_power_cap'] = -128 - - if self.sar_version_2: - power_column_dict = { - 'BDR': 'BluetoothBDRPower', - 'EDR': 'BluetoothEDRPower', - 'BLE': 'BluetoothLEPower' - } - else: - power_column_dict = {'EDR': self.power_column} - - power_cap_error = False - - for type, column_name in power_column_dict.items(): - - self.log.info("Performing sanity test on {}".format(type)) - #Iterating through the BT SAR scenarios - for scenario in range(0, self.bt_sar_df.shape[0]): - - # Reading BT SAR table row into dict - read_scenario = sar_df.loc[scenario].to_dict() - start_time = self.dut.adb.shell('date +%s.%m') - time.sleep(1) - - #Setting SAR state to the read BT SAR row - self.set_sar_state(self.dut, read_scenario, self.country_code) + power_cap_error = self.sweep_power_cap() - #Reading device power cap from logcat after forcing SAR State - scenario_power_cap = self.get_current_power_cap(self.dut, - start_time, - type=type) - sar_df.loc[scenario, '{}_power_cap'. - format(type)] = scenario_power_cap - self.log.info( - 'scenario: {}, ' - 'sar_power: {}, power_cap:{}'.format( - scenario, sar_df.loc[scenario, column_name], - sar_df.loc[scenario, '{}_power_cap'.format(type)])) - - if not sar_df['{}_power_cap'.format(type)].equals( - sar_df[column_name]): - power_cap_error = True - - results_file_path = os.path.join( - self.log_path, '{}.csv'.format(self.current_test_name)) - sar_df.to_csv(results_file_path) - - # Comparing read device power cap to expected device power cap if power_cap_error: - asserts.fail("Power Caps didn't match powers in the {} table") + asserts.fail("Power Caps didn't match powers in the SAR table") else: + self.sar_test_result.metric_value = 1 asserts.explicit_pass('Power Caps were set according to the table') def test_bt_sar_sanity_country_code(self): @@ -152,6 +145,7 @@ class BtSarSanityTest(BtSarBaseTest): for country_code in country_code_tuple: start_time = self.dut.adb.shell('date +%s.%m') + time.sleep(1) #Force country code using adb command self.set_country_code(self.dut, country_code) @@ -159,8 +153,7 @@ class BtSarSanityTest(BtSarBaseTest): set_regulatory_domain = self.get_country_code( self.dut, start_time) - if set_regulatory_domain != self.REG_DOMAIN_DICT[ - country_code_tuple]: + if set_regulatory_domain != self.REG_DOMAIN_DICT[country_code_tuple]: error_flag = 1 self.log.error( 'Country Code: {} set to regulatory domain: {}'.format( @@ -174,5 +167,57 @@ class BtSarSanityTest(BtSarBaseTest): asserts.fail( 'Regulatory domains not set according to country code') else: + self.sar_test_result.metric_value = 1 asserts.explicit_pass( 'Regulatory domains set according to country code') + + def test_bt_sar_sanity_reg_domain(self): + """ Test for BT SAR Regulatory Domain Sanity + + BT SAR Regulatory Domain Sanity Test to ensure that the correct + SAR regulatory domain TX powers get propagated to the firmware. + This is done by measuring the TX power for different + regulatory domain files + """ + + reg_domain_test_error_flag = True + reg_file_phone_path = os.path.dirname(self.sar_file_path) + + #For different reg domain, sweep the sar table + for cc, reg_domain in self.REG_DOMAIN_DICT.items(): + self.country_code = cc[0] + for file in self.custom_files: + if 'bluetooth_power_limits_{}.csv'.format(reg_domain) in file: + custom_reg_file = file + reg_file_name = os.path.join( + reg_file_phone_path, + 'bluetooth_power_limits_{}.csv'.format(reg_domain)) + break + else: + self.log.error( + 'Regulatory file for {} missing'.format(reg_domain)) + + self.push_table(self.dut, custom_reg_file, reg_file_name) + + start_time = self.dut.adb.shell('date +%s.%m') + self.set_country_code(self.dut, cc[0]) + # Read regulatory code from logcat + read_reg_domain = self.get_country_code(self.dut, start_time) + self.log.info( + 'Regulatory domain set to {}'.format(read_reg_domain)) + self.bt_sar_df = self.read_sar_table(self.dut, custom_reg_file) + + reg_domain_error_flag = self.sweep_power_cap() + + if not reg_domain_error_flag: + self.log.info( + 'Regulatory Domain Sanity Test for {} passed'.format( + reg_domain)) + + reg_domain_test_error_flag &= reg_domain_error_flag + + if reg_domain_test_error_flag: + asserts.fail('Regulatory domain sanity tests failed') + else: + self.sar_test_result.metric_value = 1 + asserts.explicit_pass('Regulatory domain sanity tests passed') diff --git a/acts_tests/tests/google/bt/sar/BtSarTpcTest.py b/acts_tests/tests/google/bt/sar/BtSarTpcTest.py index 4b7256543f..cf29d68180 100644 --- a/acts_tests/tests/google/bt/sar/BtSarTpcTest.py +++ b/acts_tests/tests/google/bt/sar/BtSarTpcTest.py @@ -18,6 +18,7 @@ import os import time import numpy as np import acts.test_utils.bt.bt_test_utils as bt_utils +from acts.metrics.loggers.blackbox import BlackboxMetricLogger import acts.test_utils.wifi.wifi_performance_test_utils as wifi_utils from acts import asserts @@ -34,21 +35,16 @@ class BtSarTpcTest(BtSarBaseTest): def __init__(self, controllers): super().__init__(controllers) req_params = ['scenario_count'] + self.sar_tpc_test_result = BlackboxMetricLogger.for_test_case( + metric_name='pass_count') self.unpack_userparams(req_params) self.tests = self.generate_test_cases() def setup_class(self): super().setup_class() - - # Pushing custom table - self.backup_sar_path = os.path.join(self.dut.device_log_path, - self.BACKUP_BT_SAR_TABLE_NAME) - self.dut.adb.pull(self.sar_file_path, self.backup_sar_path) self.push_table(self.dut, self.custom_sar_path) - self.attenuator.set_atten(self.atten_min) self.pathloss = int(self.calibration_params['pathloss']) - self.sar_df = self.bt_sar_df.copy() self.sar_df['power_cap'] = -128 self.sar_df['TPC_result'] = -1 @@ -58,6 +54,7 @@ class BtSarTpcTest(BtSarBaseTest): self.tpc_sweep_range = range(self.atten_min, self.pl10_atten) self.log.info(self.current_test_name) + self.tpc_plots_figure = wifi_utils.BokehFigure( title='{}_{}'.format(self.current_test_name, 'curve'), x_label='Pathloss(dBm)', @@ -69,13 +66,8 @@ class BtSarTpcTest(BtSarBaseTest): primary_y_label='Tx Power(dB)') def teardown_class(self): + self.dut.adb.shell('rm {}'.format(self.power_file_paths[1])) super().teardown_class() - result_file_name = '{}.csv'.format(self.__class__.__name__) - result_file_path = os.path.join(self.log_path, result_file_name) - self.sar_df.to_csv(result_file_path) - - # Pushing default table back - self.push_table(self.dut, self.backup_sar_path) def generate_test_cases(self): """Function to generate test cases. @@ -106,23 +98,37 @@ class BtSarTpcTest(BtSarBaseTest): """ tpc_verdict = 'FAIL' + tx_power_derivative = np.diff(tx_power_list) - # Locating power level changes in the sweep - pwlv_derivative_bool = list(np.diff([pwlv_list[0]] + pwlv_list) == 1) - - # Diff-ing the list to get derivative of the list - tx_power_derivative = np.diff([tx_power_list[0]] + tx_power_list) + #Remove the PL transition points when checking pass/fail + pwlv_list_int = [item % 1 for item in pwlv_list] + pwlv_steady_index = [ + idx for idx, val in enumerate(pwlv_list_int) if val == 0 + ] + pwlv_steady_state_list = [ + pwlv_list[index] for index in pwlv_steady_index + ] + tx_power_steady_state_list = [ + tx_power_list[index] for index in pwlv_steady_index + ] + tx_power_steady_state_derivative = np.diff(tx_power_steady_state_list) - # Checking for negative peaks - if tx_power_derivative.min() < self.tpc_threshold['negative']: + #Report issue if the transition period is too long + transition_points_count = len( + [i for i in list(np.diff(pwlv_steady_index)) if i > 3]) + if transition_points_count > 0: + self.log.warning('TPC transition takes too long') return [tpc_verdict, tx_power_derivative] + # Locating power level changes in the sweep + pwlv_derivative_bool = list(np.diff(pwlv_steady_state_list) == 1) + # Locating legitimate tx power changes tx_power_derivative_bool = [ self.tpc_threshold['positive'][0] < x < - self.tpc_threshold['positive'][1] for x in tx_power_derivative + self.tpc_threshold['positive'][1] + for x in tx_power_steady_state_derivative ] - # Ensuring that changes in power level correspond to tx power changes if pwlv_derivative_bool == tx_power_derivative_bool: tpc_verdict = 'PASS' @@ -174,12 +180,12 @@ class BtSarTpcTest(BtSarBaseTest): ] = self.process_tpc_results(master_tx_power_list, pwlv_list) # Plot TPC curves - self.tpc_plots_figure.add_line(self.tpc_sweep_range, + self.tpc_plots_figure.add_line(self.tpc_sweep_range[:-1], master_tx_power_list, str(scenario), marker='circle') - self.tpc_plots_derivative_figure.add_line(self.tpc_sweep_range, + self.tpc_plots_derivative_figure.add_line(self.tpc_sweep_range[:-1], tx_power_derivative, str(scenario), marker='circle') @@ -199,5 +205,6 @@ class BtSarTpcTest(BtSarBaseTest): asserts.fail('TPC sweep failed for scenario: {}'.format(scenario)) else: + self.sar_test_result.metric_value = 1 asserts.explicit_pass( 'TPC sweep passed for scenario: {}'.format(scenario)) diff --git a/acts_tests/tests/google/experimental/BluetoothLatencyTest.py b/acts_tests/tests/google/experimental/BluetoothLatencyTest.py index 811a41cca0..ea6ed4a24d 100644 --- a/acts_tests/tests/google/experimental/BluetoothLatencyTest.py +++ b/acts_tests/tests/google/experimental/BluetoothLatencyTest.py @@ -21,6 +21,7 @@ import time from acts import asserts from acts.base_test import BaseTestClass from acts.signals import TestPass +from acts.test_decorators import test_tracker_info from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest from acts.test_utils.bt.bt_test_utils import orchestrate_rfcomm_connection from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test @@ -112,6 +113,7 @@ class BluetoothLatencyTest(BaseTestClass): return (end_time - start_time) * 1000 @BluetoothBaseTest.bt_test_wrap + @test_tracker_info(uuid='7748295d-204e-4ad0-adf5-7591380b940a') def test_bluetooth_latency(self): """Tests the latency for a data transfer over RFCOMM""" diff --git a/acts_tests/tests/google/experimental/BluetoothPairAndConnectTest.py b/acts_tests/tests/google/experimental/BluetoothPairAndConnectTest.py index e54e4e7417..2402fb5444 100644 --- a/acts_tests/tests/google/experimental/BluetoothPairAndConnectTest.py +++ b/acts_tests/tests/google/experimental/BluetoothPairAndConnectTest.py @@ -27,8 +27,11 @@ from acts.controllers.buds_lib.test_actions.bt_utils import BTUtilsError from acts.controllers.buds_lib.test_actions.apollo_acts import ApolloTestActions from acts.signals import TestFailure from acts.signals import TestPass +from acts.test_decorators import test_tracker_info +from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest from acts.test_utils.bt.bt_test_utils import factory_reset_bluetooth from acts.test_utils.bt.bt_test_utils import enable_bluetooth +from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test from acts.test_utils.bt.loggers.bluetooth_metric_logger import BluetoothMetricLogger from acts.utils import set_location_service @@ -74,10 +77,10 @@ class BluetoothPairAndConnectTest(BaseTestClass): self.bt_logger = BluetoothMetricLogger.for_test_case() def setup_test(self): + setup_multiple_devices_for_bt_test(self.android_devices) # Make sure Bluetooth is on enable_bluetooth(self.phone.droid, self.phone.ed) set_location_service(self.phone, True) - factory_reset_bluetooth([self.phone]) self.apollo_act.factory_reset() self.log.info('===== START BLUETOOTH PAIR AND CONNECT TEST =====') @@ -116,6 +119,8 @@ class BluetoothPairAndConnectTest(BaseTestClass): return pair_time, connection_time + @BluetoothBaseTest.bt_test_wrap + @test_tracker_info(uuid='c914fd08-350d-465a-96cf-970d40e71060') def test_bluetooth_connect(self): # Store metrics metrics = {} diff --git a/acts_tests/tests/google/experimental/BluetoothReconnectTest.py b/acts_tests/tests/google/experimental/BluetoothReconnectTest.py index a03ec7b14b..5339e517eb 100644 --- a/acts_tests/tests/google/experimental/BluetoothReconnectTest.py +++ b/acts_tests/tests/google/experimental/BluetoothReconnectTest.py @@ -22,9 +22,12 @@ import time from acts import asserts from acts.base_test import BaseTestClass from acts.controllers.buds_lib.test_actions.apollo_acts import ApolloTestActions -from acts.signals import TestPass, TestFailure +from acts.signals import TestFailure +from acts.signals import TestPass +from acts.test_decorators import test_tracker_info +from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest from acts.test_utils.bt.bt_test_utils import enable_bluetooth -from acts.test_utils.bt.bt_test_utils import factory_reset_bluetooth +from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test from acts.test_utils.bt.loggers.bluetooth_metric_logger import BluetoothMetricLogger from acts.utils import set_location_service @@ -67,10 +70,10 @@ class BluetoothReconnectTest(BaseTestClass): self.bt_logger = BluetoothMetricLogger.for_test_case() def setup_test(self): + setup_multiple_devices_for_bt_test(self.android_devices) # Make sure Bluetooth is on enable_bluetooth(self.phone.droid, self.phone.ed) set_location_service(self.phone, True) - factory_reset_bluetooth([self.phone]) self.apollo_act.factory_reset() # Initial pairing and connection of devices @@ -119,6 +122,8 @@ class BluetoothReconnectTest(BaseTestClass): end_time = time.perf_counter() return (end_time - start_time) * 1000 + @BluetoothBaseTest.bt_test_wrap + @test_tracker_info(uuid='da921903-92d0-471d-ae01-456058cc1297') def test_bluetooth_reconnect(self): """Reconnects Bluetooth between a phone and Apollo device a specified number of times and reports connection time statistics.""" diff --git a/acts_tests/tests/google/experimental/BluetoothThroughputTest.py b/acts_tests/tests/google/experimental/BluetoothThroughputTest.py index 3403ded238..1f53172d8e 100644 --- a/acts_tests/tests/google/experimental/BluetoothThroughputTest.py +++ b/acts_tests/tests/google/experimental/BluetoothThroughputTest.py @@ -18,6 +18,7 @@ import statistics from acts import asserts from acts.base_test import BaseTestClass from acts.signals import TestPass +from acts.test_decorators import test_tracker_info from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest from acts.test_utils.bt.bt_test_utils import orchestrate_rfcomm_connection from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test @@ -109,6 +110,7 @@ class BluetoothThroughputTest(BaseTestClass): return throughput @BluetoothBaseTest.bt_test_wrap + @test_tracker_info(uuid='23afba5b-5801-42c2-8d7a-41510e91a605') def test_bluetooth_throughput_large_buffer(self): """Tests the throughput over a series of data transfers with large buffer size. @@ -143,6 +145,7 @@ class BluetoothThroughputTest(BaseTestClass): extras=proto) @BluetoothBaseTest.bt_test_wrap + @test_tracker_info(uuid='5472fe33-891e-4fa1-ba84-78fc6f6a2327') def test_bluetooth_throughput_medium_buffer(self): """Tests the throughput over a series of data transfers with medium buffer size. @@ -177,6 +180,7 @@ class BluetoothThroughputTest(BaseTestClass): extras=proto) @BluetoothBaseTest.bt_test_wrap + @test_tracker_info(uuid='97589280-cefa-4ae4-b3fd-94ec9c1f4104') def test_bluetooth_throughput_small_buffer(self): """Tests the throughput over a series of data transfers with small buffer size. diff --git a/acts_tests/tests/google/fuchsia/bt/gatt/GattConnectionStressTest.py b/acts_tests/tests/google/fuchsia/bt/gatt/GattConnectionStressTest.py index 7d3ba45c23..2f2e666a8a 100644 --- a/acts_tests/tests/google/fuchsia/bt/gatt/GattConnectionStressTest.py +++ b/acts_tests/tests/google/fuchsia/bt/gatt/GattConnectionStressTest.py @@ -39,7 +39,7 @@ class GattConnectionStressTest(BaseTestClass): gatt_connect_err_message = "Gatt connection failed with: {}" gatt_disconnect_err_message = "Gatt disconnection failed with: {}" ble_advertise_interval = 50 - scan_timeout_seconds = 10 + scan_timeout_seconds = 60 default_iterations = 1000 def setup_class(self): @@ -49,6 +49,10 @@ class GattConnectionStressTest(BaseTestClass): self.default_iterations = self.user_params.get( "gatt_connect_stress_test_iterations", self.default_iterations) + def on_fail(self, test_name, begin_time): + for fd in self.fuchsia_devices: + fd.take_bug_report(test_name, begin_time) + def _orchestrate_single_connect_disconnect(self): adv_name = generate_id_by_size(10) adv_data = { diff --git a/acts_tests/tests/google/fuchsia/ram/RamTest.py b/acts_tests/tests/google/fuchsia/ram/RamTest.py new file mode 100644 index 0000000000..b9bd75cc49 --- /dev/null +++ b/acts_tests/tests/google/fuchsia/ram/RamTest.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +import os +import time +import uuid + +from acts import asserts, signals +from acts.base_test import BaseTestClass +from acts.libs.proc.job import Error +from acts.test_utils.tel.tel_test_utils import setup_droid_properties + +# Some number bigger than the minimum 512k: +MEMORY_CYCLES_TO_MEASURE = 1024 * 1024 +# Taken from the commandline tool. +#TODO(48254): Convert driver to fill in these values automatically. +MEMORY_CHANNELS = [1, 2, 0x600110, 0x1F0000, 0, 0, 0, 0] + + +class RamTest(BaseTestClass): + def setup_class(self): + super().setup_class() + self.fd = self.fuchsia_devices[0] + + def test_bandwidth(self): + """Test MeasureBandwidth FIDL calls. + + There should be no noticable effect. This test will only run + if the device config has a 'ram_tests' entry. + """ + asserts.skip_if('ram_tests' not in self.user_params, + 'ram_tests not specified in the config') + + result = self.fd.ram_lib.measureBandwidth(MEMORY_CYCLES_TO_MEASURE, + MEMORY_CHANNELS) + asserts.assert_true( + result['error'] is None, + "MeasureBandwidth failed with: {}".format(result['error'])) + + def test_get_ddr_results(self): + """Test GetDdrWindowingResults FIDL calls. + + There should be no noticable effect. This test will only run + if the device config has a 'ram_tests' entry. + """ + asserts.skip_if('ram_tests' not in self.user_params, + 'ram_tests not specified in the config') + + result = self.fd.ram_lib.getDdrWindowingResults() + asserts.assert_true( + result['error'] is None, + "GetDdrWindowingResults failed with: {}".format(result['error'])) diff --git a/acts_tests/tests/google/fuchsia/wlan/ChannelSweepTest.py b/acts_tests/tests/google/fuchsia/wlan/ChannelSweepTest.py index 6430da1c53..431a2a3234 100644 --- a/acts_tests/tests/google/fuchsia/wlan/ChannelSweepTest.py +++ b/acts_tests/tests/google/fuchsia/wlan/ChannelSweepTest.py @@ -66,6 +66,8 @@ TIME_TO_SLEEP_BETWEEN_RETRIES = 1 TIME_TO_WAIT_FOR_COUNTRY_CODE = 10 WEP_HEX_STRING_LENGTH = 10 +MEGABITS_PER_SECOND = 'Mbps' + def get_test_name(settings): """Retrieves the test_name value from test_settings""" @@ -326,7 +328,8 @@ class ChannelSweepTest(WifiBaseTest): iperf_results_file = self.iperf_client.start( iperf_server_address, '-i 1 -t 10 -J', 'channel_sweep_tx') if iperf_results_file: - iperf_results = IPerfResult(iperf_results_file) + iperf_results = IPerfResult( + iperf_results_file, reporting_speed_units=MEGABITS_PER_SECOND) return iperf_results.avg_send_rate else: return IPERF_NO_THROUGHPUT_VALUE @@ -423,13 +426,13 @@ class ChannelSweepTest(WifiBaseTest): Args: max_std_dev: float, max standard deviation of throughput for a test - to pass (in mb/s) + to pass (in Mb/s) Raises: TestFailure, if standard deviation of throughput exceeds max_std_dev """ self.log.info('Verifying standard deviation across channels does not ' - 'exceed max standard deviation of %s mb/s' % max_std_dev) + 'exceed max standard deviation of %s Mb/s' % max_std_dev) tx_values = [] rx_values = [] for channel in self.throughput_data['results']: @@ -446,14 +449,14 @@ class ChannelSweepTest(WifiBaseTest): if tx_std_dev > max_std_dev or rx_std_dev > max_std_dev: asserts.fail( 'With %smhz channel bandwidth, throughput standard ' - 'deviation (tx: %s mb/s, rx: %s mb/s) exceeds max standard ' - 'deviation (%s mb/s).' % + 'deviation (tx: %s Mb/s, rx: %s Mb/s) exceeds max standard ' + 'deviation (%s Mb/s).' % (self.throughput_data['channel_bandwidth'], tx_std_dev, rx_std_dev, max_std_dev)) else: asserts.explicit_pass( - 'Throughput standard deviation (tx: %s mb/s, rx: %s mb/s) ' - 'with %smhz channel bandwidth does not exceed maximum (%s mb/s).' + 'Throughput standard deviation (tx: %s Mb/s, rx: %s Mb/s) ' + 'with %smhz channel bandwidth does not exceed maximum (%s Mb/s).' % (tx_std_dev, rx_std_dev, self.throughput_data['channel_bandwidth'], max_std_dev)) @@ -470,13 +473,13 @@ class ChannelSweepTest(WifiBaseTest): test_security (optional): string, security type to use for test. min_tx_throughput (optional, default: 0): float, minimum tx throughput threshold to pass individual channel tests - (in mb/s). + (in Mb/s). min_rx_throughput (optional, default: 0): float, minimum rx throughput threshold to pass individual channel tests - (in mb/s). + (in Mb/s). max_std_dev (optional, default: 1): float, maximum standard deviation of throughput across all test channels to pass - test (in mb/s). + test (in Mb/s). base_test_name (optional): string, test name prefix to use with generated subtests. country_name (optional): string, country name from @@ -511,7 +514,7 @@ class ChannelSweepTest(WifiBaseTest): """ test_channels = settings['test_channels'] test_channel_bandwidth = settings['test_channel_bandwidth'] - test_security = settings.get('test_security') + test_security = settings.get('test_security', None) test_name = settings.get('test_name', self.test_name) base_test_name = settings.get('base_test_name', 'test') min_tx_throughput = settings.get('min_tx_throughput', @@ -538,9 +541,10 @@ class ChannelSweepTest(WifiBaseTest): } test_list = [] for channel in test_channels: - sub_test_name = '%schannel_%s_%smhz_performance' % ( + sub_test_name = 'test_%schannel_%s_%smhz_%s_performance' % ( '%s_' % country_label if country_label else '', channel, - test_channel_bandwidth) + test_channel_bandwidth, + test_security if test_security else 'open') test_list.append({ 'test_name': sub_test_name, 'channel': int(channel), @@ -564,7 +568,7 @@ class ChannelSweepTest(WifiBaseTest): 1. Sets up network with test settings 2. Associates DUT 3. Runs traffic between DUT and iperf server (both directions) - 4. Logs channel, tx_throughput (mb/s), and rx_throughput (mb/s) to + 4. Logs channel, tx_throughput (Mb/s), and rx_throughput (Mb/s) to log file and throughput data. 5. Checks throughput values against minimum throughput thresholds. @@ -620,8 +624,8 @@ class ChannelSweepTest(WifiBaseTest): reverse=True) self.log_to_file_and_throughput_data(channel, channel_bandwidth, tx_throughput, rx_throughput) - self.log.info('Throughput (tx, rx): (%s mb/s, %s mb/s), ' - 'Minimum threshold (tx, rx): (%s mb/s, %s mb/s)' % + self.log.info('Throughput (tx, rx): (%s Mb/s, %s Mb/s), ' + 'Minimum threshold (tx, rx): (%s Mb/s, %s Mb/s)' % (tx_throughput, rx_throughput, min_tx_throughput, min_rx_throughput)) base_message = ( @@ -674,9 +678,9 @@ class ChannelSweepTest(WifiBaseTest): for channel_bandwidth in test_channels[channel]: sub_test_name = '%s_channel_%s_%smhz' % ( base_test_name, channel, channel_bandwidth) - should_associate = ( - channel in allowed_channels - and channel_bandwidth in allowed_channels[channel]) + should_associate = (channel in allowed_channels + and channel_bandwidth + in allowed_channels[channel]) # Note: these int conversions because when these tests are # imported via JSON, they may be strings since the channels # will be keys. This makes the json/list test_channels param @@ -996,8 +1000,8 @@ _ """ asserts.skip_if( - 'debug_channel_performance_tests' not in self.user_params.get( - 'channel_sweep_test_params', {}), + 'debug_channel_performance_tests' + not in self.user_params.get('channel_sweep_test_params', {}), 'No custom channel performance tests provided in config.') base_tests = self.user_params['channel_sweep_test_params'][ 'debug_channel_performance_tests'] @@ -1029,8 +1033,8 @@ _ } """ asserts.skip_if( - 'regulatory_compliance_tests' not in self.user_params.get( - 'channel_sweep_test_params', {}), + 'regulatory_compliance_tests' + not in self.user_params.get('channel_sweep_test_params', {}), 'No custom regulatory compliance tests provided in config.') base_tests = self.user_params['channel_sweep_test_params'][ 'regulatory_compliance_tests'] diff --git a/acts_tests/tests/google/fuchsia/wlan/PingStressTest.py b/acts_tests/tests/google/fuchsia/wlan/PingStressTest.py index 39a8852183..8577f49e03 100644 --- a/acts_tests/tests/google/fuchsia/wlan/PingStressTest.py +++ b/acts_tests/tests/google/fuchsia/wlan/PingStressTest.py @@ -54,7 +54,8 @@ class PingStressTest(BaseTestClass): client=self.wlan_device, profile_name='whirlwind', channel=hostapd_constants.AP_DEFAULT_CHANNEL_2G, - ssid=self.ssid) + ssid=self.ssid, + setup_bridge=True) def teardown_test(self): self.wlan_device.disconnect() @@ -67,8 +68,8 @@ class PingStressTest(BaseTestClass): interval=1000, timeout=1000, size=25): - ping_result = self.wlan_device.ping(dest_ip, count, interval, timeout, - size) + ping_result = self.wlan_device.can_ping(dest_ip, count, interval, + timeout, size) if ping_result: self.log.info('Ping was successful.') else: @@ -80,7 +81,7 @@ class PingStressTest(BaseTestClass): return True def ping_thread(self, dest_ip): - ping_result = self.wlan_device.ping(dest_ip, count=10, size=50) + ping_result = self.wlan_device.can_ping(dest_ip, count=10, size=50) if ping_result: self.log.info('Success pinging: %s' % dest_ip) else: diff --git a/acts_tests/tests/google/fuchsia/wlan/RebootAPStressTest.py b/acts_tests/tests/google/fuchsia/wlan/RebootAPStressTest.py deleted file mode 100644 index 41cd21263d..0000000000 --- a/acts_tests/tests/google/fuchsia/wlan/RebootAPStressTest.py +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2019 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy of -# the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations under -# the License. -""" -Script for testing WiFi recovery after rebooting the AP. - -Override default number of iterations using the following -parameter in the test config file. - -"reboot_ap_stress_test_iterations": "10" -""" - -import os -import uuid -import time - -from acts import asserts -from acts import signals -from acts.base_test import BaseTestClass -from acts.controllers.ap_lib import hostapd_constants -from acts.test_utils.abstract_devices.utils_lib.wlan_utils import disconnect -from acts.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap -from acts.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap_and_associate -from acts.test_utils.abstract_devices.wlan_device import create_wlan_device -from acts.test_utils.fuchsia import utils -from acts.test_utils.tel.tel_test_utils import setup_droid_properties -from acts.utils import rand_ascii_str - - -class RebootAPStressTest(BaseTestClass): - # Default number of test iterations here. - # Override using parameter in config file. - # Eg: "reboot_ap_stress_test_iterations": "10" - num_of_iterations = 3 - - # Default wait time in seconds for the device - # to connect back after AP reboot. - # Override using parameter in config file. - # Eg: "wait_to_connect_after_ap_reboot_s": "60" - wait_to_connect_after_ap_reboot_s = 30 - - # Time to wait for device to disconnect - # after AP reboot. - wait_after_ap_reboot_s = 1 - - def setup_class(self): - super().setup_class() - self.ssid = rand_ascii_str(10) - self.wlan_device = create_wlan_device(self.fuchsia_devices[0]) - self.ap = self.access_points[0] - self.num_of_iterations = int( - self.user_params.get("reboot_ap_stress_test_iterations", - self.num_of_iterations)) - self.wait_to_connect_after_ap_reboot_s = int( - self.user_params.get("wait_to_connect_after_ap_reboot_s", - self.wait_to_connect_after_ap_reboot_s)) - - def teardown_test(self): - disconnect(self.wlan_device) - self.wlan_device.reset_wifi() - self.ap.stop_all_aps() - - def setup_ap(self): - setup_ap(access_point=self.ap, - profile_name='whirlwind', - channel=hostapd_constants.AP_DEFAULT_CHANNEL_2G, - ssid=self.ssid) - - def test_reboot_AP_stress(self): - setup_ap_and_associate(access_point=self.ap, - client=self.wlan_device, - profile_name='whirlwind', - channel=hostapd_constants.AP_DEFAULT_CHANNEL_2G, - ssid=self.ssid) - - asserts.assert_true(self.wlan_device.is_connected(), - 'Failed to connect.') - - for _ in range(0, self.num_of_iterations): - # Stop AP - self.ap.stop_all_aps() - time.sleep(self.wait_after_ap_reboot_s) - - # Did we disconnect from AP? - asserts.assert_false(self.wlan_device.is_connected(), - 'Failed to disconnect.') - - # Start AP - self.setup_ap() - - # Give the device time to connect back - time.sleep(self.wait_to_connect_after_ap_reboot_s) - - # Did we connect back to WiFi? - asserts.assert_true(self.wlan_device.is_connected(), - 'Failed to connect back.') - - return True diff --git a/acts_tests/tests/google/fuchsia/wlan/RebootStressTest.py b/acts_tests/tests/google/fuchsia/wlan/RebootStressTest.py deleted file mode 100644 index 41d7d550e3..0000000000 --- a/acts_tests/tests/google/fuchsia/wlan/RebootStressTest.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy of -# the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations under -# the License. -""" -Script for testing WiFi recovery after device reboot - -""" -from acts.base_test import BaseTestClass - -import os -import uuid - -from acts import asserts -from acts.controllers.ap_lib import hostapd_constants -from acts.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap_and_associate -from acts.test_utils.abstract_devices.utils_lib.wlan_utils import disconnect -from acts.test_utils.abstract_devices.wlan_device import create_wlan_device -from acts.test_utils.fuchsia import utils -from acts.test_utils.tel.tel_test_utils import setup_droid_properties -from acts.utils import rand_ascii_str - - -class RebootStressTest(BaseTestClass): - # Default number of test iterations here. - # Override using parameter in config file. - # Eg: "reboot_stress_test_iterations": "10" - num_of_iterations = 3 - - def setup_class(self): - super().setup_class() - self.ssid = rand_ascii_str(10) - self.fd = self.fuchsia_devices[0] - self.wlan_device = create_wlan_device(self.fd) - self.ap = self.access_points[0] - self.num_of_iterations = int( - self.user_params.get("reboot_stress_test_iterations", - self.num_of_iterations)) - - setup_ap_and_associate(access_point=self.ap, - client=self.wlan_device, - profile_name='whirlwind', - channel=hostapd_constants.AP_DEFAULT_CHANNEL_2G, - ssid=self.ssid) - - def teardown_test(self): - disconnect(self.wlan_device) - self.wlan_device.reset_wifi() - self.ap.stop_all_aps() - - def test_reboot_stress(self): - for x in range(0, self.num_of_iterations): - # Reboot device - self.fd.reboot() - - # Did we connect back to WiFi? - asserts.assert_true(self.wlan_device.is_connected(), - 'Failed to connect.') - return True diff --git a/acts_tests/tests/google/fuchsia/wlan/RegulatoryRegionFacadeTest.py b/acts_tests/tests/google/fuchsia/wlan/RegulatoryRegionFacadeTest.py deleted file mode 100644 index 843482eca0..0000000000 --- a/acts_tests/tests/google/fuchsia/wlan/RegulatoryRegionFacadeTest.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2020 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy of -# the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations under -# the License. -""" -Script for verifying that we can invoke the SetRegion API of the -RegulatoryRegionService. - -""" -from acts.base_test import BaseTestClass -from acts import asserts, signals - -class RegulatoryRegionFacadeTest(BaseTestClass): - - def setup_class(self): - super().setup_class() - if len(self.fuchsia_devices) < 1: - raise signals.TestAbortClass("Sorry, please try verifying FuchsiaDevice is in your " - "config file and try again.") - - def test_set_region(self): - result = self.fuchsia_devices[0].regulatory_region_lib.setRegion("JP") - error = result['error'] - asserts.assert_true(error is None, error) - return True diff --git a/acts_tests/tests/google/fuchsia/wlan/SoftApTest.py b/acts_tests/tests/google/fuchsia/wlan/SoftApTest.py index ffd9fc46a2..76ed0d252f 100644 --- a/acts_tests/tests/google/fuchsia/wlan/SoftApTest.py +++ b/acts_tests/tests/google/fuchsia/wlan/SoftApTest.py @@ -16,6 +16,7 @@ from mobly import signals import multiprocessing as mp +import random import time from acts import utils @@ -27,13 +28,17 @@ from acts.controllers.ap_lib import hostapd_constants from acts.controllers.ap_lib import hostapd_security from acts.test_utils.abstract_devices.utils_lib import wlan_utils from acts.test_utils.abstract_devices.wlan_device import create_wlan_device -from acts.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap_and_associate -from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest +from acts.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap -ANDROID_DEFAULT_WLAN_PORT = 'wlan0' +ANDROID_DEFAULT_WLAN_INTERFACE = 'wlan0' CONNECTIVITY_MODE_LOCAL = 'local_only' CONNECTIVITY_MODE_UNRESTRICTED = 'unrestricted' +DEFAULT_AP_PROFILE = 'whirlwind' DEFAULT_IPERF_PORT = 5201 +DEFAULT_STRESS_TEST_ITERATIONS = 10 +DEFAULT_TIMEOUT = 30 +DEFAULT_IPERF_TIMEOUT = 60 +DEFAULT_NO_ADDR_EXPECTED_TIMEOUT = 5 INTERFACE_ROLE_AP = 'Ap' INTERFACE_ROLE_CLIENT = 'Client' INTERFACE_ROLES = {INTERFACE_ROLE_AP, INTERFACE_ROLE_CLIENT} @@ -45,6 +50,8 @@ SECURITY_WEP = 'wep' SECURITY_WPA = 'wpa' SECURITY_WPA2 = 'wpa2' SECURITY_WPA3 = 'wpa3' +STATE_UP = True +STATE_DOWN = False TEST_TYPE_ASSOCIATE_ONLY = 'associate_only' TEST_TYPE_ASSOCIATE_AND_PING = 'associate_and_ping' TEST_TYPE_ASSOCIATE_AND_PASS_TRAFFIC = 'associate_and_pass_traffic' @@ -54,18 +61,69 @@ TEST_TYPES = { } -def generate_test_name(settings): - """Generates a string test name based on the channel and band. +def get_test_name_from_settings(settings): + return settings['test_name'] + + +def get_ap_params_from_config_or_default(config): + """Retrieves AP parameters from ACTS config, or returns default settings. + + Args: + config: dict, from ACTS config, that may contain custom ap parameters + + Returns: + dict, containing all AP parameters + """ + profile = config.get('profile', DEFAULT_AP_PROFILE) + ssid = config.get( + 'ssid', utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G)) + channel = config.get('channel', hostapd_constants.AP_DEFAULT_CHANNEL_2G) + security_mode = config.get('security_mode', None) + password = config.get('password', None) + if security_mode: + security = hostapd_security.Security(security_mode, password) + else: + security = None + + return { + 'profile': profile, + 'ssid': ssid, + 'channel': channel, + 'security': security, + 'password': password + } + + +def get_soft_ap_params_from_config_or_default(config): + """Retrieves SoftAp parameters from ACTS config or returns default settings. Args: - settings A dict with the soft ap config parameteres. + config: dict, from ACTS config, that may contain custom soft ap + parameters Returns: - A string test case name. + dict, containing all soft AP parameters """ - return 'test_soft_ap_band_%s_security_%s_mode_%s_loops_%s' % ( - settings['operating_band'], settings['security_type'], - settings['connectivity_mode'], settings['reconnect_loops']) + ssid = config.get( + 'ssid', utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G)) + connectivity_mode = config.get('connectivity_mode', + CONNECTIVITY_MODE_LOCAL) + operating_band = config.get('operating_band', OPERATING_BAND_2G) + security_type = config.get('security_type', SECURITY_OPEN) + password = config.get('password', '') + + return { + 'ssid': ssid, + 'connectivity_mode': connectivity_mode, + 'operating_band': operating_band, + 'security_type': security_type, + 'password': password + } + + +class StressTestIterationFailure(Exception): + """Used to differentiate a subtest failure from an actual exception""" + pass class SoftApClient(object): @@ -85,10 +143,18 @@ class SoftApTest(BaseTestClass): """Tests for Fuchsia SoftAP Testbed requirement: - * One Fuchsia Device - * One Client (Android) Device + * One Fuchsia device + * At least one dlient (Android) device + * For multi-client tests, at least two dlient (Android) devices are + required. Test will be skipped if less than two client devices are + present. + * For any tests that exercise client-mode (e.g. toggle tests, simultaneous + tests), a physical AP (whirlwind) is also required. Those tests will be + skipped if physical AP is not present. """ def setup_class(self): + self.soft_ap_test_params = self.user_params.get( + 'soft_ap_test_params', {}) self.dut = create_wlan_device(self.fuchsia_devices[0]) self.dut.device.netstack_lib.init() @@ -112,9 +178,11 @@ class SoftApTest(BaseTestClass): except AttributeError: self.access_point = None + self.ap_iperf_client = iperf_client.IPerfClientOverSsh( + self.user_params['AccessPoint'][0]['ssh_config']) + def teardown_class(self): - # Because this is using killall, it will stop all iperf processes, - # making it a great teardown cleanup + # Because this is using killall, it will stop all iperf processes self.iperf_server.stop() def setup_test(self): @@ -125,7 +193,7 @@ class SoftApTest(BaseTestClass): client.w_device.disconnect() client.w_device.reset_wifi() client.w_device.wifi_toggle_state(True) - self.dut.device.wlan_ap_policy_lib.wlanStopAllAccessPoint() + self.stop_all_soft_aps() if self.access_point: self.access_point.stop_all_aps() self.dut.disconnect() @@ -136,7 +204,7 @@ class SoftApTest(BaseTestClass): for ad in self.android_devices: ad.droid.wakeLockRelease() ad.droid.goToSleepNow() - self.dut.device.wlan_ap_policy_lib.wlanStopAllAccessPoint() + self.stop_all_soft_aps() if self.access_point: self.access_point.stop_all_aps() self.dut.disconnect() @@ -165,17 +233,48 @@ class SoftApTest(BaseTestClass): connectivity_mode = settings['connectivity_mode'] operating_band = settings['operating_band'] - self.log.info('Attempting to start SoftAP on DUT with settings: %s' % - settings) + self.log.info('Starting SoftAP on DUT with settings: %s' % settings) response = self.dut.device.wlan_ap_policy_lib.wlanStartAccessPoint( ssid, security_type, password, connectivity_mode, operating_band) if response.get('error'): - raise EnvironmentError('Failed to setup SoftAP. Err: %s' % + raise EnvironmentError('SL4F: Failed to setup SoftAP. Err: %s' % response['error']) self.log.info('SoftAp network (%s) is up.' % ssid) + def stop_soft_ap(self, settings): + """ Stops a specific SoftAP On Fuchsia device. + + Args: + settings: a dict containing softAP config params (see start_soft_ap) + for details + + Raises: + EnvironmentError, if StopSoftAP call fails. + """ + ssid = settings['ssid'] + security_type = settings['security_type'] + password = settings.get('password', '') + + response = self.dut.device.wlan_ap_policy_lib.wlanStopAccessPoint( + ssid, security_type, password) + if response.get('error'): + raise EnvironmentError('SL4F: Failed to stop SoftAP. Err: %s' % + response['error']) + + def stop_all_soft_aps(self): + """ Stops all SoftAPs on Fuchsia Device. + + Raises: + EnvironmentError, if StopAllAps call fails. + """ + response = self.dut.device.wlan_ap_policy_lib.wlanStopAllAccessPoint() + if response.get('error'): + raise EnvironmentError( + 'SL4F: Failed to stop all SoftAPs. Err: %s' % + response['error']) + def associate_with_soft_ap(self, w_device, settings): """Associates client device with softAP on Fuchsia device. @@ -200,9 +299,11 @@ class SoftApTest(BaseTestClass): check_connectivity=check_connectivity) if not associated: - asserts.fail('Failed to connect to SoftAP.') + self.log.error('Failed to connect to SoftAp.') + return False self.log.info('Client successfully associated with SoftAP.') + return True def disconnect_from_soft_ap(self, w_device): """Disconnects client device from SoftAP. @@ -214,7 +315,9 @@ class SoftApTest(BaseTestClass): w_device.device.serial) w_device.disconnect() - def get_dut_interface_by_role(self, role): + def get_dut_interface_by_role(self, + role, + wait_for_addr_timeout=DEFAULT_TIMEOUT): """Retrieves interface information from the FuchsiaDevice DUT based on the role. @@ -231,7 +334,6 @@ class SoftApTest(BaseTestClass): if not role in INTERFACE_ROLES: raise ValueError('Unsupported interface role %s' % role) - self.log.info('Getting %s interface info from DUT.' % role) interface = WlanInterface() # Determine WLAN interface with role @@ -251,7 +353,7 @@ class SoftApTest(BaseTestClass): interface.mac_addr = iface_info['result']['mac_addr'] break else: - raise AttributeError('Failed to find a %s interface.' % role) + raise LookupError('Failed to find a %s interface.' % role) # Retrieve interface info from netstack netstack_ifaces = self.dut.device.netstack_lib.netstackListInterfaces() @@ -270,12 +372,17 @@ class SoftApTest(BaseTestClass): for byte in netstack_iface['ipv4_addresses'][0]) else: interface.ipv4 = self.wait_for_ipv4_address( - self.dut, interface.name) + self.dut, + interface.name, + timeout=wait_for_addr_timeout) self.log.info('DUT %s interface: %s. Has ipv4 address %s' % (role, interface.name, interface.ipv4)) return interface - def wait_for_ipv4_address(self, w_device, interface_name, timeout=10): + def wait_for_ipv4_address(self, + w_device, + interface_name, + timeout=DEFAULT_TIMEOUT): # TODO(fxb/51315): Once subnet information is available in netstack, add a # subnet verification here. """ Waits for interface on a wlan_device to get an ipv4 address. @@ -288,10 +395,6 @@ class SoftApTest(BaseTestClass): Raises: ValueError, if interface does not have an ipv4 address after timeout """ - self.log.info( - 'Checking if device %s interface %s has an ipv4 address. ' - 'Will retrying for %s seconds.' % - (w_device.device.serial, interface_name, timeout)) end_time = time.time() + timeout while time.time() < end_time: @@ -303,12 +406,39 @@ class SoftApTest(BaseTestClass): return ips['ipv4_private'][0] else: time.sleep(1) - raise ValueError( - 'After %s seconds, device %s still doesn not have an ipv4 address ' + raise ConnectionError( + 'After %s seconds, device %s still does not have an ipv4 address ' 'on interface %s.' % (timeout, w_device.device.serial, interface_name)) - def verify_ping(self, w_device, dest_ip): + def get_ap_ipv4_address(self, channel, timeout=DEFAULT_TIMEOUT): + """Get APs ipv4 address (actual AP, not soft ap on DUT) + + Args: + channel: int, channel of the network used to determine + which interface to use + """ + lowest_5ghz_channel = 36 + if channel < lowest_5ghz_channel: + ap_interface = self.access_point.wlan_2g + else: + ap_interface = self.access_point.wlan_5g + end_time = time.time() + timeout + while time.time() < end_time: + ap_ipv4_addresses = utils.get_interface_ip_addresses( + self.access_point.ssh, ap_interface)['ipv4_private'] + if len(ap_ipv4_addresses) > 0: + return ap_ipv4_addresses[0] + else: + self.log.debug( + 'Access point does not have an ipv4 address on interface ' + '%s. Retrying in 1 second.' % ap_interface) + else: + raise ConnectionError( + 'Access point never had an ipv4 address on interface %s.' % + ap_interface) + + def device_can_ping_addr(self, w_device, dest_ip, timeout=DEFAULT_TIMEOUT): """ Verify wlan_device can ping a destination ip. Args: @@ -318,12 +448,24 @@ class SoftApTest(BaseTestClass): Raises: TestFailure, if ping fails """ - self.log.info('Attempting to ping from device %s to dest ip %s' % - (w_device.device.serial, dest_ip)) - if not w_device.ping(dest_ip): - asserts.fail('Device %s could not ping dest ip %s' % - (w_device.device.serial, dest_ip)) - self.log.info('Ping successful.') + end_time = time.time() + timeout + while time.time() < end_time: + with utils.SuppressLogOutput(): + ping_result = w_device.can_ping(dest_ip) + + if ping_result: + self.log.info('Ping successful from device %s to dest ip %s.' % + (w_device.identifier, dest_ip)) + return True + else: + self.log.debug( + 'Device %s could not ping dest ip %s. Retrying in 1 second.' + % (w_device.identifier, dest_ip)) + time.sleep(1) + else: + self.log.info('Failed to ping from device %s to dest ip %s.' % + (w_device.identifier, dest_ip)) + return False def run_iperf_traffic(self, ip_client, server_address, server_port=5201): """Runs traffic between client and ap an verifies throughput. @@ -337,6 +479,7 @@ class SoftApTest(BaseTestClass): TestFailure, if no traffic passes in either direction """ ip_client_identifier = self.get_iperf_client_identifier(ip_client) + self.log.info( 'Running traffic from iperf client %s to iperf server %s.' % (ip_client_identifier, server_address)) @@ -344,19 +487,9 @@ class SoftApTest(BaseTestClass): server_address, '-i 1 -t 10 -J -p %s' % server_port, 'client_to_soft_ap') - self.log.info( - 'Running traffic from iperf server %s to iperf client %s.' % - (server_address, ip_client_identifier)) - ap_to_client_path = ip_client.start( - server_address, '-i 1 -t 10 -R -J -p %s' % server_port, - 'soft_ap_to_client') - self.log.info('Getting iperf results') - client_to_ap_result = iperf_server.IPerfResult(client_to_ap_path) - ap_to_client_result = iperf_server.IPerfResult(ap_to_client_path) - if (not client_to_ap_result.avg_receive_rate): - asserts.fail( + raise ConnectionError( 'Failed to pass traffic from iperf client %s to iperf server %s.' % (ip_client_identifier, server_address)) @@ -365,8 +498,16 @@ class SoftApTest(BaseTestClass): 'rate of %s MB/s.' % (ip_client_identifier, server_address, client_to_ap_result.avg_receive_rate)) + self.log.info( + 'Running traffic from iperf server %s to iperf client %s.' % + (server_address, ip_client_identifier)) + ap_to_client_path = ip_client.start( + server_address, '-i 1 -t 10 -R -J -p %s' % server_port, + 'soft_ap_to_client') + + ap_to_client_result = iperf_server.IPerfResult(ap_to_client_path) if (not ap_to_client_result.avg_receive_rate): - asserts.fail( + raise ConnectionError( 'Failed to pass traffic from iperf server %s to iperf client %s.' % (server_address, ip_client_identifier)) @@ -393,7 +534,7 @@ class SoftApTest(BaseTestClass): self.run_iperf_traffic(ip_client, server_address, server_port=server_port) - except Exception as err: + except ConnectionError as err: error_queue.put('In iperf process from %s to %s: %s' % (self.get_iperf_client_identifier(ip_client), server_address, err)) @@ -408,508 +549,1026 @@ class SoftApTest(BaseTestClass): return ip_client._android_device_or_serial return ip_client._ssh_settings.hostname - def run_config_stress_test(self, settings): - """Runs test based on config parameters. + def dut_is_connected_as_client(self, + channel, + check_traffic=False, + wait_for_addr_timeout=DEFAULT_TIMEOUT): + """Checks if DUT is successfully connected to AP. + + Args: + channel: int, channel of the AP network (to retrieve interfaces) + check_traffic: bool, if true, verifies traffic between DUT and AP, + else just checks ping. + wait_for_addr_timeout: int, time, in seconds, to wait when getting + DUT and AP addresses + + Returns: + True, if connected correctly + False, otherwise + """ + try: + dut_client_interface = self.get_dut_interface_by_role( + INTERFACE_ROLE_CLIENT, + wait_for_addr_timeout=wait_for_addr_timeout) + ap_ipv4 = self.get_ap_ipv4_address(channel, + timeout=wait_for_addr_timeout) + except ConnectionError as err: + self.log.error( + 'Failed to retrieve interfaces and addresses. Err: %s' % err) + return False + + if not self.device_can_ping_addr(self.dut, ap_ipv4): + self.log.error('Failed to ping from DUT to AP.') + return False + + if not self.device_can_ping_addr(self.access_point, + dut_client_interface.ipv4): + self.log.error('Failed to ping from AP to DUT.') + return False + + if check_traffic: + try: + self.run_iperf_traffic(self.ap_iperf_client, + dut_client_interface.ipv4) + except ConnectionError as err: + self.log.error('Failed to run traffic between DUT and AP.') + return False + return True + + def client_is_connected_to_soft_ap( + self, + client, + client_interface=ANDROID_DEFAULT_WLAN_INTERFACE, + check_traffic=False, + wait_for_addr_timeout=DEFAULT_TIMEOUT): + """Checks if client is successfully connected to DUT SoftAP. + + Args: + client: SoftApClient to check + client_interface: string, wlan interface name of client + check_traffic: bool, if true, verifies traffic between client and + DUT, else just checks ping. + wait_for_addr_timeout: int, time, in seconds, to wait when getting + DUT and client addresses + + Returns: + True, if connected correctly + False, otherwise + """ + + try: + dut_ap_interface = self.get_dut_interface_by_role( + INTERFACE_ROLE_AP, wait_for_addr_timeout=wait_for_addr_timeout) + client_ipv4 = self.wait_for_ipv4_address( + client.w_device, + client_interface, + timeout=wait_for_addr_timeout) + except ConnectionError as err: + self.log.error( + 'Failed to retrieve interfaces and addresses. Err: %s' % err) + return False + + if not self.device_can_ping_addr(self.dut, client_ipv4): + self.log.error('Failed to ping client (%s) from DUT.' % + client_ipv4) + return False + if not self.device_can_ping_addr(client.w_device, + dut_ap_interface.ipv4): + self.log.error('Failed to ping DUT from client (%s)' % client_ipv4) + return False + + if check_traffic: + try: + self.run_iperf_traffic(client.ip_client, dut_ap_interface.ipv4) + except ConnectionError as err: + self.log.error( + 'Failed to pass traffic between client (%s) and DUT.' % + client_ipv4) + return False + return True + + def verify_soft_ap_connectivity_from_state(self, state, client): + """Verifies SoftAP state based on a client connection. + + Args: + state: bool, whether SoftAP should be up + client: SoftApClient, to verify connectivity (or lack therof) + """ + if state == STATE_UP: + return self.client_is_connected_to_soft_ap(client) + else: + with utils.SuppressLogOutput(): + return not self.client_is_connected_to_soft_ap( + client, + wait_for_addr_timeout=DEFAULT_NO_ADDR_EXPECTED_TIMEOUT) + + def verify_client_mode_connectivity_from_state(self, state, channel): + """Verifies client mode state based on DUT-AP connection. + + Args: + state: bool, whether client mode should be up + channel: int, channel of the APs network + """ + if state == STATE_UP: + return self.dut_is_connected_as_client(channel) + else: + with utils.SuppressLogOutput(): + return not self.dut_is_connected_as_client( + channel, + wait_for_addr_timeout=DEFAULT_NO_ADDR_EXPECTED_TIMEOUT) + +# Test Types + + def verify_soft_ap_associate_only(self, client, settings): + if not self.associate_with_soft_ap(client.w_device, settings): + asserts.fail('Failed to associate client with SoftAP.') + + def verify_soft_ap_associate_and_ping(self, client, settings): + self.verify_soft_ap_associate_only(client, settings) + if not self.client_is_connected_to_soft_ap(client): + asserts.fail('Client and SoftAP could not ping eachother.') + + def verify_soft_ap_associate_and_pass_traffic(self, client, settings): + self.verify_soft_ap_associate_only(client, settings) + if not self.client_is_connected_to_soft_ap(client, check_traffic=True): + asserts.fail( + 'Client and SoftAP not responding to pings and passing traffic ' + 'as expected.') + +# Runners for Generated Test Cases + + def run_soft_ap_association_stress_test(self, settings): + """Sets up a SoftAP, and repeatedly associates and disassociates a + client. Args: settings: test configuration settings, see - test_soft_ap_stress_from_config for details + test_soft_ap_association_stress for details """ client = settings['client'] + soft_ap_params = settings['soft_ap_params'] test_type = settings['test_type'] if not test_type in TEST_TYPES: raise ValueError('Unrecognized test type %s' % test_type) - reconnect_loops = settings['reconnect_loops'] - self.log.info('Running test type %s in loop %s times' % - (test_type, reconnect_loops)) + iterations = settings['iterations'] + self.log.info( + 'Running association stress test type %s in iteration %s times' % + (test_type, iterations)) - self.start_soft_ap(settings) + self.start_soft_ap(soft_ap_params) passed_count = 0 - for run in range(reconnect_loops): + for run in range(iterations): try: - # Associate with SoftAp - self.log.info('Starting SoftApTest run %s' % str(run + 1)) - self.associate_with_soft_ap(client.w_device, settings) - - if test_type != TEST_TYPE_ASSOCIATE_ONLY: - # Verify client and SoftAP can ping - dut_ap_interface = self.get_dut_interface_by_role( - INTERFACE_ROLE_AP) - client_ipv4 = self.wait_for_ipv4_address( - client.w_device, ANDROID_DEFAULT_WLAN_PORT) - self.verify_ping(client.w_device, dut_ap_interface.ipv4) - self.verify_ping(self.dut, client_ipv4) - - if test_type != TEST_TYPE_ASSOCIATE_AND_PING: - # Run traffic between client and SoftAp - self.run_iperf_traffic(client.ip_client, - dut_ap_interface.ipv4) - # Disconnect - self.disconnect_from_soft_ap(client.w_device) + self.log.info('Starting SoftAp association run %s' % + str(run + 1)) + + if test_type == TEST_TYPE_ASSOCIATE_ONLY: + self.verify_soft_ap_associate_only(client, soft_ap_params) + + elif test_type == TEST_TYPE_ASSOCIATE_AND_PING: + self.verify_soft_ap_associate_and_ping( + client, soft_ap_params) + + elif test_type == TEST_TYPE_ASSOCIATE_AND_PASS_TRAFFIC: + self.verify_soft_ap_associate_and_pass_traffic( + client, soft_ap_params) + + else: + raise AttributeError('Invalid test type: %s' % test_type) except signals.TestFailure as err: - self.log.error('SoftApTest run %s failed. Err: %s' % - (str(run + 1), err.details)) + self.log.error( + 'SoftAp association stress run %s failed. Err: %s' % + (str(run + 1), err.details)) else: - self.log.info('SoftApTest run %s successful.' % run) + self.log.info('SoftAp association stress run %s successful.' % + str(run + 1)) passed_count += 1 - if passed_count < reconnect_loops: - asserts.fail('SoftAp reconnect test passed on %s/%s runs.' % - (passed_count, reconnect_loops)) + if passed_count < iterations: + asserts.fail( + 'SoftAp association stress test passed on %s/%s runs.' % + (passed_count, iterations)) - asserts.explicit_pass('SoftAp reconnect test passed on %s/%s runs.' % - (passed_count, reconnect_loops)) + asserts.explicit_pass( + 'SoftAp association stress test passed on %s/%s runs.' % + (passed_count, iterations)) - # Test helper functions - def verify_soft_ap_associate_only(self, client, settings): - self.start_soft_ap(settings) - self.associate_with_soft_ap(client.w_device, settings) +# Alternate SoftAP and Client mode test - def verify_soft_ap_associate_and_ping(self, client, settings): - self.start_soft_ap(settings) - self.associate_with_soft_ap(client.w_device, settings) - dut_ap_interface = self.get_dut_interface_by_role(INTERFACE_ROLE_AP) - client_ipv4 = self.wait_for_ipv4_address(self.primary_client.w_device, - ANDROID_DEFAULT_WLAN_PORT) - self.verify_ping(client.w_device, dut_ap_interface.ipv4) - self.verify_ping(self.dut, client_ipv4) + def run_soft_ap_and_client_mode_alternating_test(self, settings): + """Runs a single soft_ap and client alternating stress test. - def verify_soft_ap_associate_and_pass_traffic(self, client, settings): - self.start_soft_ap(settings) - self.associate_with_soft_ap(client.w_device, settings) - dut_ap_interface = self.get_dut_interface_by_role(INTERFACE_ROLE_AP) - client_ipv4 = self.wait_for_ipv4_address(self.primary_client.w_device, - ANDROID_DEFAULT_WLAN_PORT) - self.verify_ping(client.w_device, dut_ap_interface.ipv4) - self.verify_ping(self.dut, client_ipv4) - self.run_iperf_traffic(client.ip_client, dut_ap_interface.ipv4) + See test_soft_ap_and_client_mode_alternating_stress for details. + """ + iterations = settings['iterations'] + pass_count = 0 + client = self.primary_client + current_soft_ap_state = STATE_DOWN + current_client_mode_state = STATE_DOWN + + self.client_mode_toggle_pre_test(settings) + for iteration in range(iterations): + passes = True + + # Attempt to toggle SoftAP on, then off + for _ in range(2): + (current_soft_ap_state, err) = self.run_toggle_iteration_func( + self.soft_ap_toggle_test_iteration, settings, + current_soft_ap_state) + if err: + self.log.error('Iteration %s failed. Err: %s' % + (str(iteration + 1), err)) + passes = False + if current_soft_ap_state == STATE_DOWN: + break + + # Attempt to toggle Client mode on, then off + for _ in range(2): + (current_client_mode_state, + err) = self.run_toggle_iteration_func( + self.client_mode_toggle_test_iteration, settings, + current_client_mode_state) + if err: + self.log.error('Iteration %s failed. Err: %s' % + (str(iteration + 1), err)) + passes = False + if current_client_mode_state == STATE_DOWN: + break + + if passes: + pass_count += 1 + + if pass_count == iterations: + asserts.explicit_pass( + 'Toggle SoftAP and client mode stress test passed %s/%s times.' + % (pass_count, iterations)) + else: + asserts.fail( + 'Toggle SoftAP and client mode stress test only passed %s/%s ' + 'times.' % (pass_count, iterations)) + +# Toggle Stress Test Helper Functions + + def run_toggle_stress_test(self, settings): + """Runner function for toggle stress tests. + + Repeats some test function through stress test iterations, logging + failures, tracking pass rate, managing states, etc. + + Args: + settings: dict, stress test settings + + Asserts: + PASS: if all iterations of the test function pass + FAIL: if any iteration of the test function fails + """ + test_runner_func = settings['test_runner_func'] + pre_test_func = settings.get('pre_test_func', None) + iterations = settings['iterations'] + if pre_test_func: + pre_test_func(settings) + + pass_count = 0 + current_state = STATE_DOWN + for iteration in range(iterations): + (current_state, + err) = self.run_toggle_iteration_func(test_runner_func, settings, + current_state) + if err: + self.log.error('Iteration %s failed. Err: %s' % + (str(iteration + 1), err)) + else: + pass_count += 1 + + if pass_count == iterations: + asserts.explicit_pass('Stress test passed %s/%s times.' % + (pass_count, iterations)) + else: + asserts.fail('Stress test only passed %s/%s ' + 'times.' % (pass_count, iterations)) + + def run_toggle_iteration_func(self, func, settings, current_state): + """Runs a toggle iteration function, updating the current state + based on what the toggle iteration function raises. + + Used for toggle stress tests. + + Note on EnvironmentError vs StressTestIterationFailure: + StressTestIterationFailure is raised by func when the toggle occurs + but connectivty or some other post-toggle check fails (i.e. the + next iteration should toggle to the next state.) + + EnvironmentError is raise by func when the toggle itself fails (i.e + the next iteration should retry the same toggle again.) + + Args: + func: toggle iteration func to run (e.g soft_ap_toggle_iteration) + settings: dict, stress test settings + current_state: bool, the current state of the mode being toggled + + Returns: + (new_state, err): + new_state: bool, state of the mode after toggle attempt + err: exception, if any are raise, else None + """ + try: + func(settings, current_state) + except EnvironmentError as err: + return (current_state, err) + except StressTestIterationFailure as err: + return (not current_state, err) + else: + return (not current_state, None) + +# Stress Test Toggle Functions + + def start_soft_ap_and_verify_connected(self, client, soft_ap_params): + """Sets up SoftAP, associates a client, then verifies connection. + + Args: + client: SoftApClient, client to use to verify SoftAP + soft_ap_params: dict, containing parameters to setup softap + + Raises: + StressTestIterationFailure, if toggle occurs, but connection + is not functioning as expected + """ + # Change SSID every time, to avoid client connection issues. + soft_ap_params['ssid'] = utils.rand_ascii_str( + hostapd_constants.AP_SSID_LENGTH_2G) + self.start_soft_ap(soft_ap_params) + associated = self.associate_with_soft_ap(client.w_device, + soft_ap_params) + if not associated: + raise StressTestIterationFailure( + 'Failed to associated client to DUT SoftAP. ' + 'Continuing with iterations.') + + if not self.verify_soft_ap_connectivity_from_state(STATE_UP, client): + raise StressTestIterationFailure( + 'Failed to ping between client and DUT. Continuing ' + 'with iterations.') + + def stop_soft_ap_and_verify_disconnected(self, client, soft_ap_params): + """Tears down SoftAP, and verifies connection is down. + + Args: + client: SoftApClient, client to use to verify SoftAP + soft_ap_params: dict, containing parameters of SoftAP to teardown + + Raise: + EnvironmentError, if client and AP can still communicate + """ + self.log.info('Stopping SoftAP on DUT.') + self.stop_soft_ap(soft_ap_params) + + if not self.verify_soft_ap_connectivity_from_state(STATE_DOWN, client): + raise EnvironmentError( + 'Client can still ping DUT. Continuing with ' + 'iterations.') + + def start_client_mode_and_verify_connected(self, ap_params): + """Connects DUT to AP in client mode and verifies connection + + Args: + ap_params: dict, containing parameters of the AP network + + Raises: + EnvironmentError, if DUT fails to associate altogether + StressTestIterationFailure, if DUT associates but connection is not + functioning as expected. + """ + ap_ssid = ap_params['ssid'] + ap_password = ap_params['password'] + ap_channel = ap_params['channel'] + self.log.info('Associating DUT with AP network: %s' % ap_ssid) + associated = self.dut.associate(target_ssid=ap_ssid, + target_pwd=ap_password) + if not associated: + raise EnvironmentError('Failed to associate DUT in client mode.') + else: + self.log.info('Association successful.') + + if not self.verify_client_mode_connectivity_from_state( + STATE_UP, ap_channel): + raise StressTestIterationFailure('Failed to ping AP from DUT.') + + def stop_client_mode_and_verify_disconnected(self, ap_params): + """Disconnects DUT from AP and verifies connection is down. + + Args: + ap_params: dict, containing parameters of the AP network + + Raises: + EnvironmentError, if DUT and AP can still communicate + """ + self.log.info('Disconnecting DUT from AP.') + self.dut.disconnect() + if not self.verify_client_mode_connectivity_from_state( + STATE_DOWN, ap_params['channel']): + raise EnvironmentError('DUT can still ping AP.') + +# Toggle Stress Test Iteration and Pre-Test Functions + +# SoftAP Toggle Stress Test Helper Functions + + def soft_ap_toggle_test_iteration(self, settings, current_state): + """Runs a single iteration of SoftAP toggle stress test + + Args: + settings: dict, containing test settings + current_state: bool, current state of SoftAP (True if up, + else False) + + Raises: + StressTestIterationFailure, if toggle occurs but mode isn't + functioning correctly. + EnvironmentError, if toggle fails to occur at all + """ + soft_ap_params = settings['soft_ap_params'] + self.log.info('Toggling SoftAP %s.' % + ('down' if current_state else 'up')) + + if current_state == STATE_DOWN: + self.start_soft_ap_and_verify_connected(self.primary_client, + soft_ap_params) + + else: + self.stop_soft_ap_and_verify_disconnected(self.primary_client, + soft_ap_params) + +# Client Mode Toggle Stress Test Helper Functions + + def client_mode_toggle_pre_test(self, settings): + """Prepares the AP before client mode toggle tests + + Args: + settings: dict, stress test settings + + Raises: + ConnectionError, if AP setup fails + """ + ap_params = settings['ap_params'] + ap_channel = ap_params['channel'] + ap_profile = ap_params.pop('profile') + self.log.info('Setting up AP with params: %s' % ap_params) + setup_ap(access_point=self.access_point, + profile_name=ap_profile, + **ap_params) + # Confirms AP assigned itself an address + self.get_ap_ipv4_address(ap_channel) + + def client_mode_toggle_test_iteration(self, settings, current_state): + """Runs a single iteration of client mode toggle stress test + + Args: + settings: dict, containing test settings + current_state: bool, current state of client mode (True if up, + else False) + + Raises: + StressTestIterationFailure, if toggle occurs but mode isn't + functioning correctly. + EnvironmentError, if toggle fails to occur at all + """ + # TODO(b/168054673): Use client connections and policy connect + ap_params = settings['ap_params'] + self.log.info('Toggling client mode %s' % + ('off' if current_state else 'on')) + + if current_state == STATE_DOWN: + self.start_client_mode_and_verify_connected(ap_params) + + else: + self.stop_client_mode_and_verify_disconnected(ap_params) + +# Toggle SoftAP with Client Mode Up Test Helper Functions + + def soft_ap_toggle_with_client_mode_pre_test(self, settings): + """Sets up and verifies client mode before SoftAP toggle test. + Args: + settings: dict, stress test settings + + Raises: + ConnectionError, if client mode setup fails + """ + self.client_mode_toggle_pre_test(settings) + try: + self.start_client_mode_and_verify_connected(settings['ap_params']) + except StressTestIterationFailure as err: + # This prevents it being treated as a routine error + raise ConnectionError( + 'Failed to set up DUT client mode before SoftAP toggle test.' + 'Err: %s' % err) + + def soft_ap_toggle_with_client_mode_iteration( + self, + settings, + current_state, + ): + """Runs single iteration of SoftAP toggle stress with client mode test. + + Args: + settings: dict, containing test settings + current_state: bool, current state of SoftAP (True if up, + else False) + + Raises: + StressTestIterationFailure, if toggle occurs but mode isn't + functioning correctly. + EnvironmentError, if toggle fails to occur at all + """ + ap_params = settings['ap_params'] + ap_channel = ap_params['channel'] + self.soft_ap_toggle_test_iteration(settings, current_state) + if not self.dut_is_connected_as_client(ap_channel): + raise StressTestIterationFailure( + 'DUT client mode is no longer functional after SoftAP toggle.') + +# Toggle Client Mode with SoftAP Up Test Helper Functions + + def client_mode_toggle_with_soft_ap_pre_test(self, settings): + """Sets up and verifies softap before client mode toggle test. + Args: + settings: dict, stress test settings + + Raises: + ConnectionError, if softap setup fails + """ + self.client_mode_toggle_pre_test(settings) + try: + self.start_soft_ap_and_verify_connected(self.primary_client, + settings['soft_ap_params']) + except StressTestIterationFailure as err: + # This prevents it being treated as a routine error + raise ConnectionError( + 'Failed to set up SoftAP before client mode toggle test. Err: %s' + % err) + + def client_mode_toggle_with_soft_ap_iteration(self, settings, + current_state): + """Runs single iteration of client mode toggle stress with SoftAP test. + + Args: + settings: dict, containing test settings + current_state: bool, current state of client mode (True if up, + else False) + + Raises: + StressTestIterationFailure, if toggle occurs but mode isn't + functioning correctly. + EnvironmentError, if toggle fails to occur at all + """ + self.client_mode_toggle_test_iteration(settings, current_state) + if not self.client_is_connected_to_soft_ap(self.primary_client): + raise StressTestIterationFailure( + 'SoftAP is no longer functional after client mode toggle.') + +# Toggle SoftAP and Client Mode Randomly + + def run_soft_ap_and_client_mode_random_toggle_stress_test(self, settings): + """Runner function for SoftAP and client mode random toggle tests. + + Each iteration, randomly chooses if a mode will be toggled or not. + + Args: + settings: dict, containing test settings + """ + iterations = settings['iterations'] + pass_count = 0 + current_soft_ap_state = STATE_DOWN + current_client_mode_state = STATE_DOWN + ap_channel = settings['ap_params']['channel'] + + self.client_mode_toggle_pre_test(settings) + for iteration in range(iterations): + self.log.info('Starting iteration %s out of %s.' % + (str(iteration + 1), iterations)) + passes = True + + # Randomly determine if softap, client mode, or both should + # be toggled. + rand_toggle_choice = random.randrange(0, 3) + if rand_toggle_choice <= 1: + (current_soft_ap_state, err) = self.run_toggle_iteration_func( + self.soft_ap_toggle_test_iteration, settings, + current_soft_ap_state) + if err: + self.log.error( + 'Iteration %s failed toggling SoftAP. Err: %s' % + (str(iteration + 1), err)) + passes = False + if rand_toggle_choice >= 1: + (current_client_mode_state, + err) = self.run_toggle_iteration_func( + self.client_mode_toggle_test_iteration, settings, + current_client_mode_state) + if err: + self.log.error( + 'Iteration %s failed toggling client mode. Err: %s' % + (str(iteration + 1), err)) + passes = False + + soft_ap_verified = self.verify_soft_ap_connectivity_from_state( + current_soft_ap_state, self.primary_client) + client_mode_verified = self.verify_client_mode_connectivity_from_state( + current_client_mode_state, ap_channel) + + if not soft_ap_verified or not client_mode_verified: + passes = False + if passes: + pass_count += 1 + + if pass_count == iterations: + asserts.explicit_pass('Stress test passed %s/%s times.' % + (pass_count, iterations)) + else: + asserts.fail('Stress test only passed %s/%s ' + 'times.' % (pass_count, iterations)) # Test Cases def test_soft_ap_2g_open_local(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': utils.rand_ascii_str( - hostapd_constants.AP_SSID_LENGTH_2G), - 'security_type': SECURITY_OPEN, - 'connectivity_mode': CONNECTIVITY_MODE_LOCAL, - 'operating_band': OPERATING_BAND_2G - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G), + 'security_type': SECURITY_OPEN, + 'connectivity_mode': CONNECTIVITY_MODE_LOCAL, + 'operating_band': OPERATING_BAND_2G + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_5g_open_local(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': utils.rand_ascii_str( - hostapd_constants.AP_SSID_LENGTH_5G), - 'security_type': SECURITY_OPEN, - 'connectivity_mode': CONNECTIVITY_MODE_LOCAL, - 'operating_band': OPERATING_BAND_5G - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), + 'security_type': SECURITY_OPEN, + 'connectivity_mode': CONNECTIVITY_MODE_LOCAL, + 'operating_band': OPERATING_BAND_5G + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_any_open_local(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': utils.rand_ascii_str( - hostapd_constants.AP_SSID_LENGTH_5G), - 'security_type': SECURITY_OPEN, - 'connectivity_mode': CONNECTIVITY_MODE_LOCAL, - 'operating_band': OPERATING_BAND_ANY - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), + 'security_type': SECURITY_OPEN, + 'connectivity_mode': CONNECTIVITY_MODE_LOCAL, + 'operating_band': OPERATING_BAND_ANY + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_2g_wep_local(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G), - 'security_type': - SECURITY_WEP, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_LOCAL, - 'operating_band': - OPERATING_BAND_2G - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G), + 'security_type': SECURITY_WEP, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_LOCAL, + 'operating_band': OPERATING_BAND_2G + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_5g_wep_local(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), - 'security_type': - SECURITY_WEP, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_LOCAL, - 'operating_band': - OPERATING_BAND_5G - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), + 'security_type': SECURITY_WEP, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_LOCAL, + 'operating_band': OPERATING_BAND_5G + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_any_wep_local(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), - 'security_type': - SECURITY_WEP, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_LOCAL, - 'operating_band': - OPERATING_BAND_ANY - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), + 'security_type': SECURITY_WEP, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_LOCAL, + 'operating_band': OPERATING_BAND_ANY + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, ) def test_soft_ap_2g_wpa_local(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G), - 'security_type': - SECURITY_WPA, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_LOCAL, - 'operating_band': - OPERATING_BAND_2G - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G), + 'security_type': SECURITY_WPA, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_LOCAL, + 'operating_band': OPERATING_BAND_2G + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_5g_wpa_local(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), - 'security_type': - SECURITY_WPA, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_LOCAL, - 'operating_band': - OPERATING_BAND_5G - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), + 'security_type': SECURITY_WPA, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_LOCAL, + 'operating_band': OPERATING_BAND_5G + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_any_wpa_local(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), - 'security_type': - SECURITY_WPA, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_LOCAL, - 'operating_band': - OPERATING_BAND_ANY - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), + 'security_type': SECURITY_WPA, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_LOCAL, + 'operating_band': OPERATING_BAND_ANY + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_2g_wpa2_local(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G), - 'security_type': - SECURITY_WPA2, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_LOCAL, - 'operating_band': - OPERATING_BAND_2G - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G), + 'security_type': SECURITY_WPA2, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_LOCAL, + 'operating_band': OPERATING_BAND_2G + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_5g_wpa2_local(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), - 'security_type': - SECURITY_WPA2, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_LOCAL, - 'operating_band': - OPERATING_BAND_5G - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), + 'security_type': SECURITY_WPA2, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_LOCAL, + 'operating_band': OPERATING_BAND_5G + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_any_wpa2_local(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), - 'security_type': - SECURITY_WPA2, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_LOCAL, - 'operating_band': - OPERATING_BAND_ANY - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), + 'security_type': SECURITY_WPA2, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_LOCAL, + 'operating_band': OPERATING_BAND_ANY + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_2g_wpa3_local(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G), - 'security_type': - SECURITY_WPA3, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_LOCAL, - 'operating_band': - OPERATING_BAND_2G - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G), + 'security_type': SECURITY_WPA3, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_LOCAL, + 'operating_band': OPERATING_BAND_2G + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_5g_wpa3_local(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), - 'security_type': - SECURITY_WPA3, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_LOCAL, - 'operating_band': - OPERATING_BAND_ANY - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), + 'security_type': SECURITY_WPA3, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_LOCAL, + 'operating_band': OPERATING_BAND_ANY + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_any_wpa3_local(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), - 'security_type': - SECURITY_WPA3, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_LOCAL, - 'operating_band': - OPERATING_BAND_ANY - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), + 'security_type': SECURITY_WPA3, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_LOCAL, + 'operating_band': OPERATING_BAND_ANY + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_2g_open_unrestricted(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': utils.rand_ascii_str( - hostapd_constants.AP_SSID_LENGTH_2G), - 'security_type': SECURITY_OPEN, - 'connectivity_mode': CONNECTIVITY_MODE_UNRESTRICTED, - 'operating_band': OPERATING_BAND_2G - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G), + 'security_type': SECURITY_OPEN, + 'connectivity_mode': CONNECTIVITY_MODE_UNRESTRICTED, + 'operating_band': OPERATING_BAND_2G + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_5g_open_unrestricted(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': utils.rand_ascii_str( - hostapd_constants.AP_SSID_LENGTH_5G), - 'security_type': SECURITY_OPEN, - 'connectivity_mode': CONNECTIVITY_MODE_UNRESTRICTED, - 'operating_band': OPERATING_BAND_5G - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), + 'security_type': SECURITY_OPEN, + 'connectivity_mode': CONNECTIVITY_MODE_UNRESTRICTED, + 'operating_band': OPERATING_BAND_5G + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_any_open_unrestricted(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': utils.rand_ascii_str( - hostapd_constants.AP_SSID_LENGTH_5G), - 'security_type': SECURITY_OPEN, - 'connectivity_mode': CONNECTIVITY_MODE_UNRESTRICTED, - 'operating_band': OPERATING_BAND_ANY - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), + 'security_type': SECURITY_OPEN, + 'connectivity_mode': CONNECTIVITY_MODE_UNRESTRICTED, + 'operating_band': OPERATING_BAND_ANY + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_2g_wep_unrestricted(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G), - 'security_type': - SECURITY_WEP, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_UNRESTRICTED, - 'operating_band': - OPERATING_BAND_2G - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G), + 'security_type': SECURITY_WEP, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_UNRESTRICTED, + 'operating_band': OPERATING_BAND_2G + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_5g_wep_unrestricted(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), - 'security_type': - SECURITY_WEP, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_UNRESTRICTED, - 'operating_band': - OPERATING_BAND_5G - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), + 'security_type': SECURITY_WEP, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_UNRESTRICTED, + 'operating_band': OPERATING_BAND_5G + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_any_wep_unrestricted(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), - 'security_type': - SECURITY_WEP, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_UNRESTRICTED, - 'operating_band': - OPERATING_BAND_ANY - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), + 'security_type': SECURITY_WEP, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_UNRESTRICTED, + 'operating_band': OPERATING_BAND_ANY + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_2g_wpa_unrestricted(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G), - 'security_type': - SECURITY_WPA, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_UNRESTRICTED, - 'operating_band': - OPERATING_BAND_2G - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G), + 'security_type': SECURITY_WPA, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_UNRESTRICTED, + 'operating_band': OPERATING_BAND_2G + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_5g_wpa_unrestricted(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), - 'security_type': - SECURITY_WPA, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_UNRESTRICTED, - 'operating_band': - OPERATING_BAND_5G - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), + 'security_type': SECURITY_WPA, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_UNRESTRICTED, + 'operating_band': OPERATING_BAND_5G + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_any_wpa_unrestricted(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), - 'security_type': - SECURITY_WPA, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_UNRESTRICTED, - 'operating_band': - OPERATING_BAND_ANY - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), + 'security_type': SECURITY_WPA, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_UNRESTRICTED, + 'operating_band': OPERATING_BAND_ANY + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_2g_wpa2_unrestricted(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G), - 'security_type': - SECURITY_WPA2, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_UNRESTRICTED, - 'operating_band': - OPERATING_BAND_2G - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G), + 'security_type': SECURITY_WPA2, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_UNRESTRICTED, + 'operating_band': OPERATING_BAND_2G + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_5g_wpa2_unrestricted(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), - 'security_type': - SECURITY_WPA2, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_UNRESTRICTED, - 'operating_band': - OPERATING_BAND_5G - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), + 'security_type': SECURITY_WPA2, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_UNRESTRICTED, + 'operating_band': OPERATING_BAND_5G + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_any_wpa2_unrestricted(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), - 'security_type': - SECURITY_WPA2, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_UNRESTRICTED, - 'operating_band': - OPERATING_BAND_ANY - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), + 'security_type': SECURITY_WPA2, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_UNRESTRICTED, + 'operating_band': OPERATING_BAND_ANY + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_2g_wpa3_unrestricted(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G), - 'security_type': - SECURITY_WPA3, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_UNRESTRICTED, - 'operating_band': - OPERATING_BAND_2G - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G), + 'security_type': SECURITY_WPA3, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_UNRESTRICTED, + 'operating_band': OPERATING_BAND_2G + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_5g_wpa3_unrestricted(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), - 'security_type': - SECURITY_WPA3, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_UNRESTRICTED, - 'operating_band': - OPERATING_BAND_ANY - }) + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), + 'security_type': SECURITY_WPA3, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_UNRESTRICTED, + 'operating_band': OPERATING_BAND_ANY + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) def test_soft_ap_any_wpa3_unrestricted(self): - self.verify_soft_ap_associate_and_pass_traffic( - self.primary_client, { - 'ssid': - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), - 'security_type': - SECURITY_WPA3, - 'password': - utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), - 'connectivity_mode': - CONNECTIVITY_MODE_UNRESTRICTED, - 'operating_band': - OPERATING_BAND_ANY - }) - - def test_multi_client_open(self): + soft_ap_params = { + 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_5G), + 'security_type': SECURITY_WPA3, + 'password': + utils.rand_ascii_str(hostapd_constants.MIN_WPA_PSK_LENGTH), + 'connectivity_mode': CONNECTIVITY_MODE_UNRESTRICTED, + 'operating_band': OPERATING_BAND_ANY + } + self.start_soft_ap(soft_ap_params) + self.verify_soft_ap_associate_and_pass_traffic(self.primary_client, + soft_ap_params) + + def test_multi_client(self): """Tests multi-client association with a single soft AP network. This tests associates a variable length list of clients, verfying it can @@ -917,27 +1576,37 @@ class SoftApTest(BaseTestClass): associated clients can still ping and pass traffic. The same occurs in reverse for disassocations. + + SoftAP parameters can be changed from default via ACTS config: + Example Config + "soft_ap_test_params" : { + "multi_client_test_params": { + "ssid": "testssid", + "security_type": "wpa2", + "password": "password", + "connectivity_mode": "local_only", + "operating_band": "only_2_4_ghz" + } + } """ + # TODO(fxb/59335): Validate clients on network can reach eachother. asserts.skip_if( len(self.clients) < 2, 'Test requires at least 2 SoftAPClients') - settings = { - 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G), - 'security_type': SECURITY_OPEN, - 'connectivity_mode': CONNECTIVITY_MODE_LOCAL, - 'operating_band': OPERATING_BAND_ANY - } - self.start_soft_ap(settings) + test_params = self.soft_ap_test_params.get('multi_client_test_params', + {}) + soft_ap_params = get_soft_ap_params_from_config_or_default(test_params) + + self.start_soft_ap(soft_ap_params) dut_ap_interface = self.get_dut_interface_by_role(INTERFACE_ROLE_AP) associated = [] for client in self.clients: # Associate new client - self.associate_with_soft_ap(client.w_device, settings) + self.verify_soft_ap_associate_and_ping(client, soft_ap_params) client_ipv4 = self.wait_for_ipv4_address( - client.w_device, ANDROID_DEFAULT_WLAN_PORT) - self.run_iperf_traffic(client.ip_client, dut_ap_interface.ipv4) + client.w_device, ANDROID_DEFAULT_WLAN_INTERFACE) # Verify previously associated clients still behave as expected for client_map in associated: @@ -946,18 +1615,13 @@ class SoftApTest(BaseTestClass): self.log.info( 'Verifying previously associated client %s still functions correctly.' % associated_client.w_device.device.serial) - try: - self.verify_ping(self.dut, associated_client_ipv4) - self.verify_ping(associated_client.w_device, - dut_ap_interface.ipv4) - self.run_iperf_traffic(associated_client.ip_client, - dut_ap_interface.ipv4) - except signals.TestFailure as err: + if not self.client_is_connected_to_soft_ap(associated_client, + check_traffic=True): asserts.fail( 'Previously associated client %s failed checks after ' - 'client %s associated. Error: %s' % + 'client %s associated.' % (associated_client.w_device.device.serial, - client.w_device.device.serial, err)) + client.w_device.device.serial)) associated.append({'client': client, 'ipv4': client_ipv4}) @@ -973,26 +1637,21 @@ class SoftApTest(BaseTestClass): for client_map in associated: associated_client = client_map['client'] associated_client_ipv4 = client_map['ipv4'] - try: - self.log.info( - 'Verifying still associated client %s still functions ' - 'correctly.' % - associated_client.w_device.device.serial) - self.verify_ping(self.dut, associated_client_ipv4) - self.verify_ping(associated_client.w_device, - dut_ap_interface.ipv4) - self.run_iperf_traffic(associated_client.ip_client, - dut_ap_interface.ipv4) - except signals.TestFailure as err: + + self.log.info( + 'Verifying still associated client %s still functions ' + 'correctly.' % associated_client.w_device.device.serial) + if not self.client_is_connected_to_soft_ap(associated_client, + check_traffic=True): asserts.fail( 'Previously associated client %s failed checks after' - ' client %s disassociated. Error: %s' % + ' client %s disassociated.' % (associated_client.w_device.device.serial, - client.w_device.device.serial, err)) + client.w_device.device.serial)) self.log.info('All disassociations occurred smoothly.') - def test_soft_ap_and_client(self): + def test_simultaneous_soft_ap_and_client(self): """ Tests FuchsiaDevice DUT can act as a client and a SoftAP simultaneously. @@ -1002,72 +1661,37 @@ class SoftApTest(BaseTestClass): TestFailure: if DUT fails to pass traffic as either a client or an AP """ + # TODO(fxb/59306): Fix flakey parallel streams. asserts.skip_if(not self.access_point, 'No access point provided.') self.log.info('Setting up AP using hostapd.') + test_params = self.soft_ap_test_params.get( + 'soft_ap_and_client_test_params', {}) # Configure AP - ap_params = self.user_params.get('soft_ap_test_params', - {}).get('ap_params', {}) - channel = ap_params.get('channel', 11) - ssid = ap_params.get('ssid', 'apnet') - security_mode = ap_params.get('security_mode', None) - password = ap_params.get('password', None) - if security_mode: - security = hostapd_security.Security(security_mode, password) - else: - security = None + ap_params = get_ap_params_from_config_or_default( + test_params.get('ap_params', {})) # Setup AP and associate DUT - if not setup_ap_and_associate(access_point=self.access_point, - client=self.dut, - profile_name='whirlwind', - channel=channel, - security=security, - password=password, - ssid=ssid): - raise ConnectionError( - 'FuchsiaDevice DUT failed to connect as client to AP.') - self.log.info('DUT successfully associated to AP network.') - - # Verify FuchsiaDevice's client interface has an ip address from AP - dut_client_interface = self.get_dut_interface_by_role( - INTERFACE_ROLE_CLIENT) - - # Verify FuchsiaDevice can ping AP - lowest_5ghz_channel = 36 - if channel < lowest_5ghz_channel: - ap_interface = self.access_point.wlan_2g - else: - ap_interface = self.access_point.wlan_5g - ap_ipv4 = utils.get_interface_ip_addresses( - self.access_point.ssh, ap_interface)['ipv4_private'][0] - - self.verify_ping(self.dut, ap_ipv4) + ap_profile = ap_params.pop('profile') + setup_ap(access_point=self.access_point, + profile_name=ap_profile, + **ap_params) + try: + self.start_client_mode_and_verify_connected(ap_params) + except Exception as err: + asserts.fail('Failed to set up client mode. Err: %s' % err) # Setup SoftAP - soft_ap_settings = { - 'ssid': utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G), - 'security_type': SECURITY_OPEN, - 'connectivity_mode': CONNECTIVITY_MODE_LOCAL, - 'operating_band': OPERATING_BAND_2G - } - self.start_soft_ap(soft_ap_settings) + soft_ap_params = get_soft_ap_params_from_config_or_default( + test_params.get('soft_ap_params', {})) + self.start_soft_ap_and_verify_connected(self.primary_client, + soft_ap_params) # Get FuchsiaDevice's AP interface info dut_ap_interface = self.get_dut_interface_by_role(INTERFACE_ROLE_AP) - - # Associate primary client with SoftAP - self.associate_with_soft_ap(self.primary_client.w_device, - soft_ap_settings) - - # Verify primary client has an ip address from SoftAP - client_ipv4 = self.wait_for_ipv4_address(self.primary_client.w_device, - ANDROID_DEFAULT_WLAN_PORT) - - # Verify primary client can ping SoftAP, and reverse - self.verify_ping(self.primary_client.w_device, dut_ap_interface.ipv4) - self.verify_ping(self.dut, client_ipv4) + dut_client_interface = self.get_dut_interface_by_role( + INTERFACE_ROLE_CLIENT) # Set up secondary iperf server of FuchsiaDevice self.log.info('Setting up second iperf server on FuchsiaDevice DUT.') @@ -1099,13 +1723,16 @@ class SoftApTest(BaseTestClass): # Run iperf processes simultaneously self.log.info('Running simultaneous iperf traffic: between AP and DUT ' 'client interface, and DUT AP interface and client.') + iperf_soft_ap.start() iperf_fuchsia_client.start() # Block until processes can join or timeout for proc in [iperf_soft_ap, iperf_fuchsia_client]: - proc.join(timeout=30) + proc.join(timeout=DEFAULT_IPERF_TIMEOUT) if proc.is_alive(): + proc.terminate() + proc.join() raise RuntimeError('Failed to join process %s' % proc) # Stop iperf server (also stopped in teardown class as failsafe) @@ -1124,54 +1751,294 @@ class SoftApTest(BaseTestClass): 'FuchsiaDevice failed to pass traffic as a client and an AP ' 'simultaneously.') - def test_soft_ap_stress_from_config(self): - """ Runs tests from ACTS config file. + def test_soft_ap_association_stress(self): + """ Sets up a single AP and repeatedly associate/disassociate + a client, verifying connection every time + + Each test creates 1 SoftAP and repeatedly associates/disassociates + client. Example Config "soft_ap_test_params" : { - "soft_ap_tests": [ + "soft_ap_association_stress_tests": [ { "ssid": "test_network", "security_type": "wpa2", "password": "password", "connectivity_mode": "local_only", "operating_band": "only_2_4_ghz", - "reconnect_loops": 10 + "iterations": 10 } ] } """ - tests = self.user_params.get('soft_ap_test_params', - {}).get('soft_ap_tests') - asserts.skip_if(not tests, 'No soft ap tests in the ACTS config.') + tests = self.soft_ap_test_params.get( + 'test_soft_ap_association_stress', + [dict(test_name='test_soft_ap_association_stress_default')]) test_settings_list = [] - for config_settings in self.user_params['soft_ap_test_params'][ - 'soft_ap_tests']: - ssid = config_settings.get( - 'ssid', - utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G)) - security_type = config_settings.get('security_type', SECURITY_OPEN) - password = config_settings.get('password', '') - connectivity_mode = config_settings.get('connectivity_mode', - CONNECTIVITY_MODE_LOCAL) - operating_band = config_settings.get('operating_band', - OPERATING_BAND_ANY) + for config_settings in tests: + soft_ap_params = get_soft_ap_params_from_config_or_default( + config_settings) test_type = config_settings.get('test_type', 'associate_and_pass_traffic') - reconnect_loops = config_settings.get('reconnect_loops', 1) + iterations = config_settings.get('iterations', + DEFAULT_STRESS_TEST_ITERATIONS) test_settings = { + 'test_name': config_settings['test_name'], 'client': self.primary_client, - 'ssid': ssid, - 'security_type': security_type, - 'password': password, - 'connectivity_mode': connectivity_mode, - 'operating_band': operating_band, + 'soft_ap_params': soft_ap_params, 'test_type': test_type, - 'reconnect_loops': reconnect_loops + 'iterations': iterations } test_settings_list.append(test_settings) - self.run_generated_testcases(self.run_config_stress_test, + self.run_generated_testcases(self.run_soft_ap_association_stress_test, test_settings_list, - name_func=generate_test_name)
\ No newline at end of file + name_func=get_test_name_from_settings) + + def test_soft_ap_and_client_mode_alternating_stress(self): + """ Runs tests that alternate between SoftAP and Client modes. + + Each tests sets up an AP. Then, for each iteration: + - DUT starts up SoftAP, client associates with SoftAP, + connection is verified, then disassociates + - DUT associates to the AP, connection is verified, then + disassociates + + Example Config: + "soft_ap_test_params": { + "toggle_soft_ap_and_client_tests": [ + { + "test_name": "test_wpa2_client_ap_toggle", + "ap_params": { + "channel": 6, + "ssid": "test-ap-network", + "security_mode": "wpa2", + "password": "password" + }, + "soft_ap_params": { + "ssid": "test-soft-ap-network", + "security_type": "wpa2", + "password": "other-password", + "connectivity_mode": "local_only", + "operating_band": "only_2_4_ghz" + }, + "iterations": 5 + } + ] + } + """ + asserts.skip_if(not self.access_point, 'No access point provided.') + tests = self.soft_ap_test_params.get( + 'test_soft_ap_and_client_mode_alternating_stress', [ + dict(test_name= + 'test_soft_ap_and_client_mode_alternating_stress_default') + ]) + + test_settings_list = [] + for config_settings in tests: + ap_params = get_ap_params_from_config_or_default( + config_settings.get('ap_params', {})) + soft_ap_params = get_soft_ap_params_from_config_or_default( + config_settings.get('soft_ap_params', {})) + iterations = config_settings.get('iterations', + DEFAULT_STRESS_TEST_ITERATIONS) + + test_settings = { + 'test_name': config_settings['test_name'], + 'iterations': iterations, + 'soft_ap_params': soft_ap_params, + 'ap_params': ap_params, + } + + test_settings_list.append(test_settings) + self.run_generated_testcases( + test_func=self.run_soft_ap_and_client_mode_alternating_test, + settings=test_settings_list, + name_func=get_test_name_from_settings) + + def test_soft_ap_toggle_stress(self): + """ Runs SoftAP toggling stress test. + + Each iteration toggles SoftAP to the opposite state (up or down). + + If toggled up, a client is associated and connection is verified + If toggled down, test verifies client is not connected + + Will run with default params, but custom tests can be provided in the + ACTS config. + + Example Config + "soft_ap_test_params" : { + "test_soft_ap_toggle_stress": [ + "soft_ap_params": { + "security_type": "wpa2", + "password": "password", + "connectivity_mode": "local_only", + "operating_band": "only_2_4_ghz", + }, + "iterations": 10 + ] + } + """ + tests = self.soft_ap_test_params.get( + 'test_soft_ap_toggle_stress', + [dict(test_name='test_soft_ap_toggle_stress_default')]) + + test_settings_list = [] + for config_settings in tests: + soft_ap_params = get_soft_ap_params_from_config_or_default( + config_settings) + iterations = config_settings.get('iterations', + DEFAULT_STRESS_TEST_ITERATIONS) + test_settings = { + 'test_name': config_settings['test_name'], + 'test_runner_func': self.soft_ap_toggle_test_iteration, + 'soft_ap_params': soft_ap_params, + 'iterations': iterations + } + test_settings_list.append(test_settings) + + self.run_generated_testcases(self.run_toggle_stress_test, + test_settings_list, + name_func=get_test_name_from_settings) + + def test_client_mode_toggle_stress(self): + """ Runs client mode toggling stress test. + + Each iteration toggles client mode to the opposite state (up or down). + + If toggled up, DUT associates to AP, and connection is verified + If toggled down, test verifies DUT is not connected to AP + + Will run with default params, but custom tests can be provided in the + ACTS config. + + Example Config + "soft_ap_test_params" : { + "test_client_mode_toggle_stress": [ + "soft_ap_params": { + 'ssid': ssid, + 'channel': channel, + 'security_mode': security, + 'password': password + }, + "iterations": 10 + ] + } + """ + asserts.skip_if(not self.access_point, 'No access point provided.') + tests = self.soft_ap_test_params.get( + 'test_client_mode_toggle_stress', + [dict(test_name='test_client_mode_toggle_stress_default')]) + + test_settings_list = [] + for config_settings in tests: + ap_params = get_ap_params_from_config_or_default(config_settings) + iterations = config_settings.get('iterations', + DEFAULT_STRESS_TEST_ITERATIONS) + test_settings = { + 'test_name': config_settings['test_name'], + 'test_runner_func': self.client_mode_toggle_test_iteration, + 'pre_test_func': self.client_mode_toggle_pre_test, + 'ap_params': ap_params, + 'iterations': iterations + } + test_settings_list.append(test_settings) + self.run_generated_testcases(self.run_toggle_stress_test, + test_settings_list, + name_func=get_test_name_from_settings) + + def test_soft_ap_toggle_stress_with_client_mode(self): + """Same as test_soft_ap_toggle_stress, but client mode is set up + at test start and verified after every toggle.""" + asserts.skip_if(not self.access_point, 'No access point provided.') + tests = self.soft_ap_test_params.get( + 'test_soft_ap_toggle_stress_with_client_mode', [ + dict(test_name= + 'test_soft_ap_toggle_stress_with_client_mode_default') + ]) + + test_settings_list = [] + for config_settings in tests: + soft_ap_params = get_soft_ap_params_from_config_or_default( + config_settings) + ap_params = get_ap_params_from_config_or_default(config_settings) + iterations = config_settings.get('iterations', + DEFAULT_STRESS_TEST_ITERATIONS) + test_settings = { + 'test_name': config_settings['test_name'], + 'test_runner_func': + self.soft_ap_toggle_with_client_mode_iteration, + 'pre_test_func': self.soft_ap_toggle_with_client_mode_pre_test, + 'soft_ap_params': soft_ap_params, + 'ap_params': ap_params, + 'iterations': iterations + } + test_settings_list.append(test_settings) + self.run_generated_testcases(self.run_toggle_stress_test, + test_settings_list, + name_func=get_test_name_from_settings) + + def test_client_mode_toggle_stress_with_soft_ap(self): + """Same as test_client_mode_toggle_stress, but softap is set up at + test start and verified after every toggle.""" + asserts.skip_if(not self.access_point, 'No access point provided.') + tests = self.soft_ap_test_params.get( + 'test_client_mode_toggle_stress_with_soft_ap', [ + dict(test_name= + 'test_client_mode_toggle_stress_with_soft_ap_default') + ]) + + test_settings_list = [] + for config_settings in tests: + soft_ap_params = get_soft_ap_params_from_config_or_default( + config_settings) + ap_params = get_ap_params_from_config_or_default(config_settings) + iterations = config_settings.get('iterations', + DEFAULT_STRESS_TEST_ITERATIONS) + test_settings = { + 'test_name': config_settings['test_name'], + 'test_runner_func': + self.client_mode_toggle_with_soft_ap_iteration, + 'pre_test_func': self.client_mode_toggle_with_soft_ap_pre_test, + 'soft_ap_params': soft_ap_params, + 'ap_params': ap_params, + 'iterations': iterations + } + test_settings_list.append(test_settings) + self.run_generated_testcases(self.run_toggle_stress_test, + test_settings_list, + name_func=get_test_name_from_settings) + + def test_soft_ap_and_client_mode_random_toggle_stress(self): + """Same as above toggle stres tests, but each iteration, either softap, + client mode, or both are toggled, then states are verified.""" + asserts.skip_if(not self.access_point, 'No access point provided.') + tests = self.soft_ap_test_params.get( + 'test_soft_ap_and_client_mode_random_toggle_stress', [ + dict( + test_name= + 'test_soft_ap_and_client_mode_random_toggle_stress_default' + ) + ]) + + test_settings_list = [] + for config_settings in tests: + soft_ap_params = get_soft_ap_params_from_config_or_default( + config_settings) + ap_params = get_ap_params_from_config_or_default(config_settings) + iterations = config_settings.get('iterations', + DEFAULT_STRESS_TEST_ITERATIONS) + test_settings = { + 'test_name': config_settings['test_name'], + 'soft_ap_params': soft_ap_params, + 'ap_params': ap_params, + 'iterations': iterations + } + test_settings_list.append(test_settings) + self.run_generated_testcases( + self.run_soft_ap_and_client_mode_random_toggle_stress_test, + test_settings_list, + name_func=get_test_name_from_settings) diff --git a/acts_tests/tests/google/fuchsia/wlan/WlanFacadeTest.py b/acts_tests/tests/google/fuchsia/wlan/WlanFacadeTest.py index 05e6cf18ea..ffe86faeee 100644 --- a/acts_tests/tests/google/fuchsia/wlan/WlanFacadeTest.py +++ b/acts_tests/tests/google/fuchsia/wlan/WlanFacadeTest.py @@ -29,6 +29,20 @@ class WlanFacadeTest(BaseTestClass): "Sorry, please try verifying FuchsiaDevice is in your " "config file and try again.") + def on_fail(self, test_name, begin_time): + for fd in self.fuchsia_devices: + try: + fd.take_bug_report(test_name, begin_time) + fd.get_log(test_name, begin_time) + except Exception: + pass + + try: + if fd.device.hard_reboot_on_fail: + fd.hard_power_cycle(self.pdu_devices) + except AttributeError: + pass + def test_get_phy_id_list(self): result = self.fuchsia_devices[0].wlan_lib.wlanPhyIdList() error = result['error'] diff --git a/acts_tests/tests/google/fuchsia/wlan/WlanInterfaceTest.py b/acts_tests/tests/google/fuchsia/wlan/WlanInterfaceTest.py index b8395f12c3..d470bf7c2f 100644 --- a/acts_tests/tests/google/fuchsia/wlan/WlanInterfaceTest.py +++ b/acts_tests/tests/google/fuchsia/wlan/WlanInterfaceTest.py @@ -18,22 +18,28 @@ from acts import signals from acts.base_test import BaseTestClass from acts.test_utils.abstract_devices.wlan_device import create_wlan_device +from acts.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest -class WlanInterfaceTest(BaseTestClass): +class WlanInterfaceTest(AbstractDeviceWlanDeviceBaseTest): def setup_class(self): + super().setup_class() dut = self.user_params.get('dut', None) if dut: - if dut == 'fuchsia_devices': - self.dut = create_wlan_device(self.fuchsia_devices[0]) - elif dut == 'android_devices': - self.dut = create_wlan_device(self.android_devices[0]) - else: - raise ValueError('Invalid DUT specified in config. (%s)' % self.user_params['dut']) + if dut == 'fuchsia_devices': + self.dut = create_wlan_device(self.fuchsia_devices[0]) + elif dut == 'android_devices': + self.dut = create_wlan_device(self.android_devices[0]) + else: + raise ValueError('Invalid DUT specified in config. (%s)' % + self.user_params['dut']) else: # Default is an Fuchsia device self.dut = create_wlan_device(self.fuchsia_devices[0]) + def on_fail(self, test_name, begin_time): + super().on_fail(test_name, begin_time) + def test_destroy_iface(self): """Test that we don't error out when destroying the WLAN interface. diff --git a/acts_tests/tests/google/fuchsia/wlan/WlanRebootTest.py b/acts_tests/tests/google/fuchsia/wlan/WlanRebootTest.py index cc744fa54e..a22622b123 100644 --- a/acts_tests/tests/google/fuchsia/wlan/WlanRebootTest.py +++ b/acts_tests/tests/google/fuchsia/wlan/WlanRebootTest.py @@ -14,18 +14,19 @@ # See the License for the specific language governing permissions and # limitations under the License. +import itertools import os import re import time + from multiprocessing import Process -import itertools from acts import asserts from acts import context from acts import utils -from acts.controllers import pdu from acts.controllers import iperf_client from acts.controllers import iperf_server +from acts.controllers import pdu from acts.controllers.ap_lib import hostapd_constants from acts.controllers.ap_lib.radvd import Radvd from acts.controllers.ap_lib import radvd_constants @@ -34,8 +35,6 @@ from acts.test_utils.abstract_devices.wlan_device import create_wlan_device from acts.test_utils.abstract_devices.utils_lib import wlan_utils from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest -# TODO(46633): Add in policy layer stuff once its implemented (see SetupTest) - # Constants, for readibility AP = 'ap' DUT = 'dut' @@ -58,6 +57,9 @@ IP_VERSIONS = [{ }] INTERRUPTS = [True, False] +DUT_NETWORK_CONNECTION_TIMEOUT = 60 +DUT_IP_ADDRESS_TIMEOUT = 15 + def get_test_name(settings): """Generates a test name from test settings. If a test_name is present @@ -91,20 +93,25 @@ class WlanRebootTest(WifiBaseTest): Testbed Requirement: * One ACTS compatible device (dut) * One Whirlwind Access Point (will also serve as iperf server) + * One PduDevice """ def __init__(self, controllers): WifiBaseTest.__init__(self, controllers) self.tests = [ - 'test_soft_reboot_ap_ipv4_ipv6_2g_5g', - 'test_hard_reboot_ap_ipv4_ipv6_2g_5g', 'test_soft_reboot_dut_ipv4_ipv6_2g_5g', - 'test_hard_reboot_dut_ipv4_ipv6_2g_5g' + 'test_hard_reboot_dut_ipv4_ipv6_2g_5g', + 'test_soft_reboot_ap_ipv4_ipv6_2g_5g', + 'test_hard_reboot_ap_ipv4_ipv6_2g_5g' ] if 'reboot_stress_tests' in self.user_params: self.tests.append('test_reboot_stress') def setup_class(self): super().setup_class() + + self.android_devices = getattr(self, 'android_devices', []) + self.fuchsia_devices = getattr(self, 'fuchsia_devices', []) + if 'dut' in self.user_params: if self.user_params['dut'] == 'fuchsia_devices': self.dut = create_wlan_device(self.fuchsia_devices[0]) @@ -117,85 +124,59 @@ class WlanRebootTest(WifiBaseTest): # Default is an android device, just like the other tests self.dut = create_wlan_device(self.android_devices[0]) - self.android_devices = getattr(self, 'android_devices', []) - self.fuchsia_devices = getattr(self, 'fuchsia_devices', []) - self.access_point = self.access_points[0] - self.pdus = self.register_controller(pdu) # IPerf Server is run on the AP and setup in the tests - self.iperf_server = None - self.iperf_client = self.iperf_clients[0] + self.iperf_server_on_ap = None + self.iperf_client_on_dut = self.iperf_clients[0] self.router_adv_daemon = None - # Times (in seconds) to retry different stages of the reboot/reconnect - # processes. - wlan_reboot_params = self.user_params.get('wlan_reboot_params', None) - if wlan_reboot_params: - self.timeout_for_unreachable_ap = wlan_reboot_params.get( - 'timeout_for_unreachable_ap', 3) - self.timeout_for_pingable_ap = wlan_reboot_params.get( - 'timeout_for_pingable_ap', 30) - self.timeout_for_sshable_ap = wlan_reboot_params.get( - 'timeout_for_sshable_ap', 30) - self.timeout_for_unreachable_dut = wlan_reboot_params.get( - 'timeout_for_unreachable_dut', 3) - self.timeout_for_pingable_dut = wlan_reboot_params.get( - 'timeout_for_pingable_dut', 30) - self.timeout_for_dut_network_connection = wlan_reboot_params.get( - 'timeout_for_dut_network_connection', 60) - self.timeout_for_dut_can_ping = wlan_reboot_params.get( - 'timeout_for_dut_can_ping', 3) - self.timeout_for_ip_address = wlan_reboot_params.get( - 'timeout_for_ip_address', 15) - self.timeout_for_reinitialize_services = wlan_reboot_params.get( - 'timeout_for_reinitialize_services', 3) - else: - self.timeout_for_unreachable_ap = 3 - self.timeout_for_pingable_ap = 60 - self.timeout_for_sshable_ap = 30 - self.timeout_for_unreachable_dut = 3 - self.timeout_for_pingable_dut = 30 - self.timeout_for_dut_network_connection = 60 - self.timeout_for_dut_can_ping = 3 - self.timeout_for_ip_address = 15 - self.timeout_for_reinitialize_services = 3 + # Times (in seconds) to wait for DUT network connection and assigning an + # ip address to the wlan interface. + wlan_reboot_params = self.user_params.get('wlan_reboot_params', {}) + self.dut_network_connection_timeout = wlan_reboot_params.get( + 'dut_network_connection_timeout', DUT_NETWORK_CONNECTION_TIMEOUT) + self.dut_ip_address_timeout = wlan_reboot_params.get( + 'dut_ip_address_timeout', DUT_IP_ADDRESS_TIMEOUT) def setup_test(self): self.access_point.stop_all_aps() if self.router_adv_daemon: self.router_adv_daemon.stop() + self.dut.wifi_toggle_state(True) for ad in self.android_devices: ad.droid.wakeLockAcquireBright() ad.droid.wakeUpNow() - self.dut.wifi_toggle_state(True) + for fd in self.fuchsia_devices: + fd.wlan_policy_lib.wlanCreateClientController() + fd.wlan_policy_lib.wlanStartClientConnections() + self.dut.clear_saved_networks() self.dut.disconnect() self.router_adv_daemon = None - if self.user_params['dut'] == 'fuchsia_devices': - self.dut.device.wlan_policy_lib.wlanCreateClientController() - # TODO(52319): Clear the saved networks list once - # removeSavedNetwork and clearSavedNetworks are implemented. - self.dut.device.wlan_policy_lib.wlanStartClientConnections() self.ssid = utils.rand_ascii_str(hostapd_constants.AP_SSID_LENGTH_2G) def teardown_test(self): + self.access_point.stop_all_aps() + self.dut.clear_saved_networks() + for fd in self.fuchsia_devices: + fd.wlan_policy_lib.wlanStopClientConnections() + self.dut.disconnect() for ad in self.android_devices: ad.droid.wakeLockRelease() ad.droid.goToSleepNow() self.dut.turn_location_off_and_scan_toggle_off() - self.dut.disconnect() self.dut.reset_wifi() - self.access_point.stop_all_aps() def on_fail(self, test_name, begin_time): self.dut.take_bug_report(test_name, begin_time) self.dut.get_log(test_name, begin_time) - def setup_ap(self, band, ipv4=True, ipv6=False): + def setup_ap(self, ssid, band, ipv4=True, ipv6=False): """Setup ap with basic config. Args: + ssid: string, ssid to setup on ap band: string ('2g' or '5g') of band to setup. ipv4: True if using ipv4 (dhcp), else False. ipv6: True if using ipv6 (radvd), else False. @@ -204,12 +185,12 @@ class WlanRebootTest(WifiBaseTest): wlan_utils.setup_ap(access_point=self.access_point, profile_name='whirlwind', channel=11, - ssid=self.ssid) + ssid=ssid) elif band == BAND_5G: wlan_utils.setup_ap(access_point=self.access_point, profile_name='whirlwind', channel=36, - ssid=self.ssid) + ssid=ssid) if not ipv4: self.access_point.stop_dhcp() @@ -228,48 +209,57 @@ class WlanRebootTest(WifiBaseTest): self.access_point.wlan_5g) self.router_adv_daemon.start(radvd_config) - self.log.info('Network (SSID: %s) is up.' % self.ssid) + self.log.info('Network (SSID: %s) is up.' % ssid) - def associate_and_save(self): + def save_and_connect(self, ssid): """Associates the dut with the network running on the AP and saves network to device. + + Args: + ssid: string, ssid to connect DUT to + + Raises: + EnvironmentError, if saving network fails + ConnectionError, if device fails to connect to network """ - wlan_utils.associate(client=self.dut, ssid=self.ssid) - if self.user_params['dut'] == 'fuchsia_devices': - response = self.dut.device.wlan_policy_lib.wlanSaveNetwork( - self.ssid, 'None') - if response.get('error'): - raise EnvironmentError( - 'Failed to save network %s for FuchsiaDevice %s: %s' % - (self.ssid, self.dut.device.ip, response.get('error'))) - - def setup_ap_associate_and_save(self, band, ipv4=True, ipv6=False): - """Setup ap with basic config and associates the dut with the network - running on the AP and saves network. + self.dut.save_network(self.ssid) + self.dut.associate(self.ssid) + + def setup_save_and_connect_to_network(self, + ssid, + band, + ipv4=True, + ipv6=False): + """Setup ap with passed params, saves network, and connects the dut with + the network running on the AP and saves network. Args: + ssid: string, ssid to setup and connect to band: string ('2g' or '5g') of band to setup. ipv4: True if using ipv4 (dhcp), else False. ipv6: True if using ipv6 (radvd), else False. """ - self.setup_ap(band, ipv4, ipv6) - self.associate_and_save() + self.setup_ap(ssid, band, ipv4, ipv6) + self.save_and_connect(ssid) - def wait_until_dut_gets_ipv4_addr(self): + def wait_until_dut_gets_ipv4_addr(self, interface): """Checks if device has an ipv4 private address. Sleeps 1 second between retries. + Args: + interface: string, name of interface from which to get ipv4 address. + Raises: ConnectionError, if DUT does not have an ipv4 address after all timeout. """ self.log.info( 'Checking if DUT has received an ipv4 addr. Will retry for %s ' - 'seconds.' % self.timeout_for_ip_address) - timeout = time.time() + self.timeout_for_ip_address + 'seconds.' % self.dut_ip_address_timeout) + timeout = time.time() + self.dut_ip_address_timeout while time.time() < timeout: - ip_addrs = self.dut.get_interface_ip_addresses( - self.iperf_client.test_interface) + ip_addrs = self.dut.get_interface_ip_addresses(interface) + if len(ip_addrs['ipv4_private']) > 0: self.log.info('DUT has an ipv4 address: %s' % ip_addrs['ipv4_private'][0]) @@ -282,21 +272,23 @@ class WlanRebootTest(WifiBaseTest): else: raise ConnectionError('DUT failed to get an ipv4 address.') - def wait_until_dut_gets_ipv6_addr(self): + def wait_until_dut_gets_ipv6_addr(self, interface): """Checks if device has an ipv6 private local address. Sleeps 1 second between retries. + Args: + interface: string, name of interface from which to get ipv6 address. + Raises: ConnectionError, if DUT does not have an ipv6 address after all timeout. """ self.log.info( 'Checking if DUT has received an ipv6 addr. Will retry for %s ' - 'seconds.' % self.timeout_for_ip_address) - timeout = time.time() + self.timeout_for_ip_address + 'seconds.' % self.dut_ip_address_timeout) + timeout = time.time() + self.dut_ip_address_timeout while time.time() < timeout: - ip_addrs = self.dut.get_interface_ip_addresses( - self.iperf_client.test_interface) + ip_addrs = self.dut.get_interface_ip_addresses(interface) if len(ip_addrs['ipv6_private_local']) > 0: self.log.info('DUT has an ipv6 private local address: %s' % ip_addrs['ipv6_private_local'][0]) @@ -309,36 +301,35 @@ class WlanRebootTest(WifiBaseTest): else: raise ConnectionError('DUT failed to get an ipv6 address.') - def setup_iperf_server(self, band): + def setup_iperf_server_on_ap(self, band): """Configures iperf server based on the tests band. Args: band: string ('2g' or '5g') of band to setup. """ - if self.iperf_server and self.iperf_server.started: - self.iperf_server.stop() if band == BAND_2G: - self.iperf_server = iperf_server.IPerfServerOverSsh( + return iperf_server.IPerfServerOverSsh( self.user_params['AccessPoint'][0]['ssh_config'], 5201, test_interface=self.access_point.wlan_2g) elif band == BAND_5G: - self.iperf_server = iperf_server.IPerfServerOverSsh( + return iperf_server.IPerfServerOverSsh( self.user_params['AccessPoint'][0]['ssh_config'], 5201, test_interface=self.access_point.wlan_5g) - def get_iperf_server_address(self, ip_version): + def get_iperf_server_address(self, iperf_server_on_ap, ip_version): """Retrieves the ip address of the iperf server. Args: + iperf_server_on_ap: IPerfServer object, linked to AP ip_version: string, the ip version (ipv4 or ipv6) Returns: String, the ip address of the iperf_server """ - iperf_server_addresses = self.iperf_server.get_interface_ip_addresses( - self.iperf_server.test_interface) + iperf_server_addresses = iperf_server_on_ap.get_interface_ip_addresses( + iperf_server_on_ap.test_interface) if ip_version == IPV4: iperf_server_ip_address = ( iperf_server_addresses['ipv4_private'][0]) @@ -349,18 +340,23 @@ class WlanRebootTest(WifiBaseTest): else: iperf_server_ip_address = ( '%s%%%s' % (iperf_server_addresses['ipv6_link_local'][0], - self.iperf_client.test_interface)) + self.iperf_client_on_dut.test_interface)) else: raise ValueError('Invalid IP version: %s' % ip_version) return iperf_server_ip_address - def verify_traffic(self, ip_version=IPV4): + def verify_traffic_between_dut_and_ap(self, + iperf_server_on_ap, + iperf_client_on_dut, + ip_version=IPV4): """Runs IPerf traffic from the iperf client (dut) and the iperf server (and vice versa) and verifies traffic was able to pass successfully. Args: + iperf_server_on_ap: IPerfServer object, linked to AP + iperf_client_on_dut: IPerfClient object, linked to DUT ip_version: string, the ip version (ipv4 or ipv6) Raises: @@ -369,15 +365,16 @@ class WlanRebootTest(WifiBaseTest): directions. """ dut_ip_addresses = self.dut.get_interface_ip_addresses( - self.iperf_client.test_interface) + iperf_client_on_dut.test_interface) - iperf_server_ip_address = self.get_iperf_server_address(ip_version) + iperf_server_ip_address = self.get_iperf_server_address( + iperf_server_on_ap, ip_version) self.log.info( 'Attempting to pass traffic from DUT to IPerf server (%s).' % iperf_server_ip_address) - tx_file = self.iperf_client.start(iperf_server_ip_address, - '-i 1 -t 10 -J', 'reboot_tx') + tx_file = iperf_client_on_dut.start(iperf_server_ip_address, + '-i 1 -t 3 -J', 'reboot_tx') tx_results = iperf_server.IPerfResult(tx_file) if not tx_results.avg_receive_rate or tx_results.avg_receive_rate == 0: raise ConnectionError( @@ -391,8 +388,8 @@ class WlanRebootTest(WifiBaseTest): self.log.info( 'Attempting to pass traffic from IPerf server (%s) to DUT.' % iperf_server_ip_address) - rx_file = self.iperf_client.start(iperf_server_ip_address, - '-i 1 -t 10 -R -J', 'reboot_rx') + rx_file = iperf_client_on_dut.start(iperf_server_ip_address, + '-i 1 -t 3 -R -J', 'reboot_rx') rx_results = iperf_server.IPerfResult(rx_file) if not rx_results.avg_receive_rate or rx_results.avg_receive_rate == 0: raise ConnectionError( @@ -404,16 +401,18 @@ class WlanRebootTest(WifiBaseTest): 'Success: Traffic passed from IPerf server (%s) to DUT.' % iperf_server_ip_address) - def start_dut_ping_process(self, ip_version=IPV4): + def start_dut_ping_process(self, iperf_server_on_ap, ip_version=IPV4): """Creates a process that pings the AP from the DUT. Runs in parallel for 15 seconds, so it can be interrupted by a reboot. Sleeps for a few seconds to ensure pings have started. Args: + iperf_server_on_ap: IPerfServer object, linked to AP ip_version: string, the ip version (ipv4 or ipv6) """ - ap_address = self.get_iperf_server_address(ip_version) + ap_address = self.get_iperf_server_address(iperf_server_on_ap, + ip_version) if ap_address: self.log.info( 'Starting ping process to %s in parallel. Logs from this ' @@ -430,223 +429,6 @@ class WlanRebootTest(WifiBaseTest): else: raise ConnectionError('Failed to retrieve APs iperf address.') - def wait_for_unreachable_dut(self): - """Checks if DUT is unreachable. Sleeps 1 second between retries. - - Raises: - ConnectionError, if DUT is still pingable after all timeout. - """ - self.log.info('Expecting unreachable DUT. Will retry for %s seconds.' % - self.timeout_for_unreachable_dut) - timeout = time.time() + self.timeout_for_unreachable_dut - while time.time() < timeout: - if not utils.is_pingable(self.dut.device.ip): - self.log.info('Success: DUT is unreachable.') - break - else: - self.log.debug('DUT still pingable...retrying in 1 second.') - time.sleep(1) - else: - raise ConnectionError('AP is still reachable.') - - def wait_for_pingable_dut(self): - """Checks if DUT is pingable. Sleeps 1 second between retries. - - Raises: - ConnectionError, if DUT is not pingable after all timeout. - """ - self.log.info('Attempting to ping DUT. Will retry for %s seconds.' % - self.timeout_for_pingable_dut) - timeout = time.time() + self.timeout_for_pingable_dut - while time.time() < timeout: - if utils.is_pingable(self.dut.device.ip): - self.log.info('Success: DUT is pingable.') - break - else: - self.log.debug('Could not ping DUT...retrying in 1 second.') - time.sleep(1) - else: - raise ConnectionError('Failed to ping DUT.') - - def prepare_dut_object_after_hard_reboot(self): - """Prepares DUT objects after a hard reboot has occurred. - - This essentially reinitializes SL4* on the DUT after it has been hard - rebooted and ensures the device wifi is on. This may require some device - specific logic. - """ - # start_services has a backoff loop, but not long enough to accommodate - # for a full boot time, so the additional loop is necessary. - timeout = time.time() + self.timeout_for_reinitialize_services - while time.time() < timeout: - try: - self.dut.device.reinitialize_services() - except Exception as err: - self.log.debug( - 'Failed to reinitialize services. Retrying. Error: %s' % - err) - else: - self.log.info('Services successfully reinitialized.') - break - else: - raise ConnectionError('Failed to reinitialize services, exiting.') - - self.dut.wifi_toggle_state(True) - - def prepare_dut_object_for_hard_reboot(self): - """Prepares DUT objects for hard reboot. - - This is not to be confused with clean, soft reboot functionality, and - should not prepare the device in any way, but can be used to clean up - device objects so ACTS behaves with the upcoming reboot. This may - require to have device specific logic. - """ - if self.user_params['dut'] == 'fuchsia_devices': - self.iperf_client.close_ssh() - self.dut.device.clean_up() - - def hard_reboot_dut(self): - """Hard reboots the DUT. - - prepares the DUT object (not the hardware itself) for the reboot. - - suppresses logs during reboot to allow for expected errors - - abruptly kills power to the DUT - - verifies the DUT is unreachable - - restores power to the DUT - - verifies the DUT comes back online - - If successful, prepare DUT object (i.e. reinitialize SL4*). - - If an exception occurs, still attempt to reinitialize SL4* so other - tests can continue. This is only possible if the exception occurred - before the power is killed. Otherwise, log that SL4* could not be reset. - """ - # Clean up *Device controllers (not the devices themselves) - self.prepare_dut_object_for_hard_reboot() - # Suppress logs so that disconnect errors don't fail the test. - self.log.info('Hard rebooting DUT, log output will be suppressed.') - with utils.SuppressLogOutput(): - try: - # Get PDU device and port for DUT - dut_pdu_config = self.dut.device.conf_data['PduDevice'] - dut_pdu, dut_pdu_port = pdu.get_pdu_port_for_device( - dut_pdu_config, self.pdus) - - # Kill power to DUT - self.log.info('Killing power to DUT...') - dut_pdu.off(str(dut_pdu_port)) - - # Verify DUT is unreachable - self.wait_for_unreachable_dut() - - # Restore power to DUT - self.log.info('Restoring power to DUT...') - dut_pdu.on(str(dut_pdu_port)) - - # Verify DUT is back online - self.wait_for_pingable_dut() - - finally: - # If something fails, attempt to restart services things so - # tests can continue. - try: - self.prepare_dut_object_after_hard_reboot() - except: - self.log.info('Failed to restart services.') - - self.log.info('DUT is back up.') - - def wait_for_unreachable_ap(self): - """Checks if AP is unreachable. Sleeps 1 second between retries. - - Raises: - ConnectionError, if AP is still reachable after all timeout. - """ - self.log.info('Expecting unreachable AP. Will retry for %s seconds.' % - self.timeout_for_unreachable_ap) - timeout = time.time() + self.timeout_for_unreachable_ap - while time.time() < timeout: - if not self.access_point.is_pingable(): - self.log.info('Success: AP is unreachable.') - break - else: - self.log.debug('AP is still pingable...retrying in 1 second.') - time.sleep(1) - else: - raise ConnectionError('AP is still reachable.') - - def wait_for_pingable_ap(self): - """Checks if AP is pingable. Sleeps 1 second between retries. - - Raises: - ConnectionError, if AP is not pingable after all timeout. - """ - self.log.info('Attempting to ping AP. Will retry for %s seconds.' % - self.timeout_for_pingable_ap) - timeout = time.time() + self.timeout_for_pingable_ap - while time.time() < timeout: - if self.access_point.is_pingable(): - self.log.info('Success: AP is pingable.') - break - else: - self.log.debug('Could not ping AP...retrying in 1 second.') - time.sleep(1) - else: - raise ConnectionError('Failed to ping AP.') - - def wait_for_sshable_ap(self): - """Checks if AP is sshable. Sleeps 1 second between retries. - - Raises: - ConnectionError, if could not ssh to AP after all timeout. - """ - self.log.info('Attempting to ssh to AP. Will retry for %s seconds.' % - self.timeout_for_sshable_ap) - timeout = time.time() + self.timeout_for_sshable_ap - while time.time() < timeout: - if self.access_point.is_sshable(): - self.log.info('Success: AP is online.') - break - else: - self.log.debug('Could not ssh to AP...retrying in 1 second.') - time.sleep(1) - else: - raise ConnectionError('Failed to ssh to AP.') - - def hard_reboot_ap(self): - """Hard reboot of AP. - - abruptly kills the power to AP - - verifies AP is unreachable - - restores power to the AP - - verifies AP comes back online - """ - # Get PDU device and port for AP - ap_pdu_config = self.user_params['AccessPoint'][0]['PduDevice'] - ap_pdu, ap_pdu_port = pdu.get_pdu_port_for_device( - ap_pdu_config, self.pdus) - - # Stop iperf server - self.iperf_server.close_ssh() - - # Kill power to AP - self.log.info('Killing power to AP...') - ap_pdu.off(str(ap_pdu_port)) - self.wait_for_unreachable_ap() - - # Clear AP settings - self.access_point._aps.clear() - - # Restore power to AP - self.log.info('Restoring power to AP...') - ap_pdu.on(str(ap_pdu_port)) - self.wait_for_pingable_ap() - self.wait_for_sshable_ap() - - # Restart hostapd stuff - # Allow 5 seconds for OS to get set up. - time.sleep(5) - self.access_point._initial_ap() - self.log.info('AP reboot successful.') - def prepare_dut_for_reconnection(self): """Perform any actions to ready DUT for reconnection. @@ -657,6 +439,8 @@ class WlanRebootTest(WifiBaseTest): self.dut.wifi_toggle_state(True) for ad in self.android_devices: ad.droid.wakeUpNow() + for fd in self.fuchsia_devices: + fd.wlan_policy_lib.wlanCreateClientController() def wait_for_dut_network_connection(self, ssid): """Checks if device is connected to given network. Sleeps 1 second @@ -669,13 +453,13 @@ class WlanRebootTest(WifiBaseTest): """ self.log.info( 'Checking if DUT is connected to %s network. Will retry for %s ' - 'seconds.' % (ssid, self.timeout_for_dut_network_connection)) - timeout = time.time() + self.timeout_for_dut_network_connection + 'seconds.' % (ssid, self.dut_network_connection_timeout)) + timeout = time.time() + self.dut_network_connection_timeout while time.time() < timeout: try: is_connected = self.dut.is_connected(ssid=ssid) except Exception as err: - self.log.info('SL4* call failed. Retrying in 1 second.') + self.log.debug('SL4* call failed. Retrying in 1 second.') is_connected = False finally: if is_connected: @@ -712,7 +496,8 @@ class WlanRebootTest(WifiBaseTest): Args: time_to_reconnect: the time from when the rebooted device came back ip to when reassociation occurred. - run: the run number in a looped stress tested. + run: the run number in a looped stress tested., + error: string, error message to log before continuing with the test """ if error: self.log.info( @@ -730,7 +515,7 @@ class WlanRebootTest(WifiBaseTest): def run_reboot_test(self, settings): """Runs a reboot test based on a given config. - 1. Setups up a network and associates the dut. + 1. Setups up a network, associates the dut, and saves the network. 2. Verifies the dut receives ip address(es). 3. Verifies traffic between DUT and AP (IPerf client and server). 4. Reboots (hard or soft) the device (dut or ap). @@ -785,20 +570,28 @@ class WlanRebootTest(WifiBaseTest): if band != BAND_2G and band != BAND_5G: raise ValueError('Invalid band: %s' % band) - self.setup_ap_associate_and_save(band, ipv4=ipv4, ipv6=ipv6) + self.setup_save_and_connect_to_network(self.ssid, + band, + ipv4=ipv4, + ipv6=ipv6) + self.wait_for_dut_network_connection(self.ssid) + dut_test_interface = self.iperf_client_on_dut.test_interface if ipv4: - self.wait_until_dut_gets_ipv4_addr() + self.wait_until_dut_gets_ipv4_addr(dut_test_interface) if ipv6: - self.wait_until_dut_gets_ipv6_addr() + self.wait_until_dut_gets_ipv6_addr(dut_test_interface) - self.setup_iperf_server(band) - self.iperf_server.start() + self.iperf_server_on_ap = self.setup_iperf_server_on_ap(band) + self.iperf_server_on_ap.start() if ipv4: - self.verify_traffic() + self.verify_traffic_between_dut_and_ap(self.iperf_server_on_ap, + self.iperf_client_on_dut) if ipv6: - self.verify_traffic(ip_version=IPV6) + self.verify_traffic_between_dut_and_ap(self.iperf_server_on_ap, + self.iperf_client_on_dut, + ip_version=IPV6) # Looping reboots for stress testing for run in range(loops): @@ -808,18 +601,20 @@ class WlanRebootTest(WifiBaseTest): # Ping from DUT to AP during AP reboot if interrupt: if ipv4: - self.start_dut_ping_process() + self.start_dut_ping_process(self.iperf_server_on_ap) if ipv6: - self.start_dut_ping_process(ip_version=IPV6) + self.start_dut_ping_process(self.iperf_server_on_ap, + ip_version=IPV6) # DUT reboots if reboot_device == DUT: - if type(self.iperf_client) == iperf_client.IPerfClientOverSsh: - self.iperf_client.close_ssh() + if type(self.iperf_client_on_dut + ) == iperf_client.IPerfClientOverSsh: + self.iperf_client_on_dut.close_ssh() if reboot_type == SOFT: self.dut.device.reboot() elif reboot_type == HARD: - self.hard_reboot_dut() + self.dut.hard_power_cycle(self.pdu_devices) # AP reboots elif reboot_device == AP: @@ -827,8 +622,9 @@ class WlanRebootTest(WifiBaseTest): self.log.info('Cleanly stopping ap.') self.access_point.stop_all_aps() elif reboot_type == HARD: - self.hard_reboot_ap() - self.setup_ap(band, ipv4=ipv4, ipv6=ipv6) + self.iperf_server_on_ap.close_ssh() + self.access_point.hard_power_cycle(self.pdu_devices) + self.setup_ap(self.ssid, band, ipv4=ipv4, ipv6=ipv6) self.prepare_dut_for_reconnection() uptime = time.time() @@ -836,18 +632,25 @@ class WlanRebootTest(WifiBaseTest): self.wait_for_dut_network_connection(self.ssid) time_to_reconnect = time.time() - uptime if ipv4: - self.wait_until_dut_gets_ipv4_addr() + self.wait_until_dut_gets_ipv4_addr(dut_test_interface) if ipv6: - self.wait_until_dut_gets_ipv6_addr() - self.iperf_server.start() + self.wait_until_dut_gets_ipv6_addr(dut_test_interface) + self.iperf_server_on_ap.start() + if ipv4: - self.verify_traffic() + self.verify_traffic_between_dut_and_ap( + self.iperf_server_on_ap, self.iperf_client_on_dut) if ipv6: - self.verify_traffic(ip_version=IPV6) + self.verify_traffic_between_dut_and_ap( + self.iperf_server_on_ap, + self.iperf_client_on_dut, + ip_version=IPV6) + except ConnectionError as err: self.log_and_continue(run, error=err) - passed_count += 1 - self.log_and_continue(run, time_to_reconnect=time_to_reconnect) + else: + passed_count += 1 + self.log_and_continue(run, time_to_reconnect=time_to_reconnect) if passed_count == loops: asserts.explicit_pass( @@ -856,9 +659,8 @@ class WlanRebootTest(WifiBaseTest): else: asserts.fail( - 'Test Summary: device failed stress test. Reconnected to ' - 'network %s %s/%s times.' % - (self.ssid, loops - passed_count, loops)) + 'Test Summary: device failed reconnection test. Reconnected to ' + 'network %s %s/%s times.' % (self.ssid, passed_count, loops)) # 12 test cases def test_soft_reboot_ap_ipv4_ipv6_2g_5g(self): diff --git a/acts_tests/tests/google/fuchsia/wlan/WlanRvrTest.py b/acts_tests/tests/google/fuchsia/wlan/WlanRvrTest.py index 8d80e2a944..87bf1a72a8 100644 --- a/acts_tests/tests/google/fuchsia/wlan/WlanRvrTest.py +++ b/acts_tests/tests/google/fuchsia/wlan/WlanRvrTest.py @@ -335,7 +335,7 @@ class WlanRvrTest(AbstractDeviceWlanDeviceBaseTest): else: raise ValueError('Invalid IP version: %s' % ip_version) if ip_address_checker_counter == ip_address_checker_max_attempts: - if self.dut.ping(iperf_server_ip_address): + if self.dut.can_ping(iperf_server_ip_address): self.log.error('IPerf server is pingable. Continuing with ' 'test. The missing IP address information ' 'should be marked as a bug.') @@ -477,7 +477,7 @@ class WlanRvrTest(AbstractDeviceWlanDeviceBaseTest): else: self.log.info('DUT has the following IPv6 address: "%s"' % dut_ip_addresses['ipv6_link_local'][0]) - server_pingable = self.dut.ping(iperf_server_ip_address) + server_pingable = self.dut.can_ping(iperf_server_ip_address) if not server_pingable: self.log.info('Iperf server "%s" is not pingable. Marking ' 'a 0 %s for throughput. Skipping running ' diff --git a/acts_tests/tests/google/fuchsia/wlan/WlanStatusTest.py b/acts_tests/tests/google/fuchsia/wlan/WlanStatusTest.py index e04b19a4bc..84a8805156 100644 --- a/acts_tests/tests/google/fuchsia/wlan/WlanStatusTest.py +++ b/acts_tests/tests/google/fuchsia/wlan/WlanStatusTest.py @@ -27,12 +27,26 @@ class WlanStatusTest(BaseTestClass): Test Bed Requirements: * One or more Fuchsia devices with WLAN client capabilities. """ - def setup_class(self): super().setup_class() for fd in self.fuchsia_devices: fd.wlan_policy_lib.wlanCreateClientController() + def on_fail(self, test_name, begin_time): + for fd in self.fuchsia_devices: + try: + fd.take_bug_report(test_name, begin_time) + fd.get_log(test_name, begin_time) + except Exception: + pass + + try: + if fd.device.hard_reboot_on_fail: + fd.hard_power_cycle(self.pdu_devices) + fd.wlan_policy_lib.wlanCreateClientController() + except AttributeError: + pass + def test_wlan_stopped_client_status(self): """Queries WLAN status on DUTs with no WLAN ifaces. diff --git a/acts_tests/tests/google/fuchsia/wlan_deprecated_configuration/WlanDeprecatedConfigurationTest.py b/acts_tests/tests/google/fuchsia/wlan_deprecated_configuration/WlanDeprecatedConfigurationTest.py index 7153f4375c..574a9c9aa1 100644 --- a/acts_tests/tests/google/fuchsia/wlan_deprecated_configuration/WlanDeprecatedConfigurationTest.py +++ b/acts_tests/tests/google/fuchsia/wlan_deprecated_configuration/WlanDeprecatedConfigurationTest.py @@ -40,6 +40,20 @@ class WlanDeprecatedConfigurationTest(BaseTestClass): def teardown_test(self): self._stop_soft_aps() + def on_fail(self, test_name, begin_time): + for fd in self.fuchsia_devices: + try: + fd.take_bug_report(test_name, begin_time) + fd.get_log(test_name, begin_time) + except Exception: + pass + + try: + if fd.device.hard_reboot_on_fail: + fd.hard_power_cycle(self.pdu_devices) + except AttributeError: + pass + def _get_ap_interface_mac_address(self): """Retrieves mac address from wlan interface with role ap diff --git a/acts_tests/tests/google/fuchsia/wlan_policy/PolicyScanTest.py b/acts_tests/tests/google/fuchsia/wlan_policy/PolicyScanTest.py index 685976fa5a..a1c0d7b223 100644 --- a/acts_tests/tests/google/fuchsia/wlan_policy/PolicyScanTest.py +++ b/acts_tests/tests/google/fuchsia/wlan_policy/PolicyScanTest.py @@ -31,7 +31,6 @@ class PolicyScanTest(WifiBaseTest): * One or more Fuchsia devices * One Whirlwind Access Point """ - def setup_class(self): super().setup_class() if len(self.fuchsia_devices) < 1: @@ -39,7 +38,6 @@ class PolicyScanTest(WifiBaseTest): for fd in self.fuchsia_devices: # Initialize the Policy client controller for each Fuchsia device fd.wlan_policy_lib.wlanCreateClientController() - if len(self.access_points) < 1: raise signals.TestFailure("No access points found.") # Prepare the AP @@ -104,6 +102,20 @@ class PolicyScanTest(WifiBaseTest): def teardown_class(self): pass + def on_fail(self, test_name, begin_time): + for fd in self.fuchsia_devices: + try: + fd.take_bug_report(test_name, begin_time) + fd.get_log(test_name, begin_time) + except Exception: + pass + + try: + if fd.device.hard_reboot_on_fail: + fd.hard_power_cycle(self.pdu_devices) + except AttributeError: + pass + """Helper Functions""" def perform_scan(self, fd): diff --git a/acts_tests/tests/google/gnss/GTWGnssPowerTest.py b/acts_tests/tests/google/gnss/GTWGnssPowerTest.py deleted file mode 100644 index 53d8dcfde7..0000000000 --- a/acts_tests/tests/google/gnss/GTWGnssPowerTest.py +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2020 - The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from acts import signals -from acts.test_utils.power.PowerBaseTest import PowerBaseTest -from acts.test_utils.gnss import dut_log_test_utils as diaglog -from acts.test_utils.gnss import gnss_test_utils as gutils -from acts.test_utils.wifi import wifi_test_utils as wutils - - -class GTWGnssPowerTest(PowerBaseTest): - """GTW Gnss Power test""" - - def setup_class(self): - super().setup_class() - self.ad = self.android_devices[0] - req_params = ['wifi_network', 'pixel_lab_location', 'qdsp6m_path'] - self.unpack_userparams(req_param_names=req_params) - - def setup_test(self): - super().setup_test() - # Enable GNSS setting for GNSS standalone mode - self.ad.adb.shell('settings put secure location_mode 3') - - def start_gnss_tracking_with_power_data(self, signal=True): - """Start GNSS tracking and collect power metrics. - Args: - signal: default True, False for no Gnss signal test. - """ - gutils.start_gnss_by_gtw_gpstool( - self.dut, state=True, type='gnss', bgdisplay=True) - self.ad.send_keycode('SLEEP') - result = self.collect_power_data() - gutils.start_gnss_by_gtw_gpstool(self.ad, False) - if signal: - gutils.parse_gtw_gpstool_log( - self.ad, self.pixel_lab_location, type='gnss') - self.pass_fail_check(result.average_current) - - # Test cases - def test_power_baseline(self): - """ - 1. Let DUT sleep. - 2. Mesuring the baseline after rockbottom DUT. - """ - self.ad.send_keycode('SLEEP') - result = self.collect_power_data() - self.pass_fail_check(result.average_current) - - def test_baseline_gnss_request_1Hz(self): - """ - 1. Attenuate signal to strong GNSS level. - 2. Open GPStool and tracking with DUT sleep. - 3. Collect power data. - """ - self.set_attenuation(self.atten_level[self.current_test_name]) - self.start_gnss_tracking_with_power_data() - - def test_DPO_on_gnss_request_1Hz(self): - """ - 1. Attenuate signal to strong GNSS level. - 2. Turn DPO ON. - 3. Open GPStool and tracking with DUT sleep. - 4. Collect power data. - """ - self.set_attenuation(self.atten_level[self.current_test_name]) - self.start_gnss_tracking_with_power_data() - - def test_L1_L5_weak_signal_gnss_request_1Hz(self): - """ - 1. Attenuate signal to weak GNSS level. - 3. Open GPStool and tracking with DUT sleep. - 4. Collect power data. - """ - self.set_attenuation(self.atten_level[self.current_test_name]) - self.start_gnss_tracking_with_power_data() - - def test_no_signal_gnss_request_1Hz(self): - """ - 1. Attenuate signal to no GNSS signal level. - 3. Open GPStool and tracking with DUT sleep. - 3. Collect power data. - """ - self.set_attenuation(self.atten_level[self.current_test_name]) - self.start_gnss_tracking_with_power_data(signal=False) diff --git a/acts_tests/tests/google/gnss/GnssSanityTest.py b/acts_tests/tests/google/gnss/GnssFunctionTest.py index 7f05774c25..eb8d23ec07 100644 --- a/acts_tests/tests/google/gnss/GnssSanityTest.py +++ b/acts_tests/tests/google/gnss/GnssFunctionTest.py @@ -78,8 +78,8 @@ from acts.test_utils.tel.tel_test_utils import stop_adb_tcpdump from acts.test_utils.tel.tel_test_utils import get_tcpdump_log -class GnssSanityTest(BaseTestClass): - """ GNSS Function Sanity Tests""" +class GnssFunctionTest(BaseTestClass): + """ GNSS Function Tests""" def setup_class(self): super().setup_class() self.ad = self.android_devices[0] @@ -97,7 +97,7 @@ class GnssSanityTest(BaseTestClass): "default_gnss_signal_attenuation", "weak_gnss_signal_attenuation", "no_gnss_signal_attenuation", "gnss_init_error_list", - "gnss_init_error_whitelist", "pixel_lab_location", + "gnss_init_error_allowlist", "pixel_lab_location", "legacy_wifi_xtra_cs_criteria", "legacy_projects", "qdsp6m_path", "supl_capabilities", "ttff_test_cycle", "collect_logs"] @@ -203,9 +203,9 @@ class GnssSanityTest(BaseTestClass): self.ad.log.info("There is no mcfg.version before push, " "unmatching device") return False - except: + except Exception as e: self.ad.log.info("There is no mcfg.version before push, " - "unmatching device") + "unmatching device %s" % e) return False get_baseband_and_gms_version(self.ad, "Before push mcfg") try: @@ -371,11 +371,11 @@ class GnssSanityTest(BaseTestClass): for attr in self.gnss_init_error_list: error = self.ad.adb.shell("logcat -d | grep -E '%s'" % attr) if error: - for whitelist in self.gnss_init_error_whitelist: - if whitelist in error: - error = re.sub(".*"+whitelist+".*\n?", "", error) - self.ad.log.info("\"%s\" is white-listed and removed " - "from error." % whitelist) + for allowlist in self.gnss_init_error_allowlist: + if allowlist in error: + error = re.sub(".*"+allowlist+".*\n?", "", error) + self.ad.log.info("\"%s\" is in allow-list and removed " + "from error." % allowlist) if error: error_mismatch = False self.ad.log.error("\n%s" % error) @@ -419,7 +419,7 @@ class GnssSanityTest(BaseTestClass): sap_state = str(self.ad.adb.shell("cat vendor/etc/izat.conf | grep " "SAP=")) self.ad.log.info("SAP Valid Modes - %s" % sap_state) - asserts.assert_true(sap_state == "SAP=PREMIUM", + asserts.assert_true("SAP=PREMIUM" in sap_state, "Wrong SAP Valid Modes is set") @test_tracker_info(uuid="14daaaba-35b4-42d9-8d2c-2a803dd746a6") @@ -1156,9 +1156,6 @@ class GnssSanityTest(BaseTestClass): start_gnss_by_gtw_gpstool(self.ad, False) self.ad.restart_runtime() self.ad.unlock_screen(password=None) - # in case restart android runtime and adb logcat didn't restart. - if not self.ad.is_adb_logcat_on: - self.ad.start_adb_logcat() test_result = process_gnss_by_gtw_gpstool(self.ad, self.supl_cs_criteria) start_gnss_by_gtw_gpstool(self.ad, False) @@ -1188,9 +1185,6 @@ class GnssSanityTest(BaseTestClass): start_gnss_by_gtw_gpstool(self.ad, False) self.ad.restart_runtime() self.ad.unlock_screen(password=None) - # in case restart android runtime and adb logcat didn't restart. - if not self.ad.is_adb_logcat_on: - self.ad.start_adb_logcat() test_result = process_gnss_by_gtw_gpstool(self.ad, self.xtra_cs_criteria) start_gnss_by_gtw_gpstool(self.ad, False) diff --git a/acts_tests/tests/google/gnss/GnssPowerAGPSTest.py b/acts_tests/tests/google/gnss/GnssPowerAGPSTest.py new file mode 100644 index 0000000000..f5a3dccc54 --- /dev/null +++ b/acts_tests/tests/google/gnss/GnssPowerAGPSTest.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# +# Copyright 2020 - The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from acts import utils +from acts.test_utils.power.PowerGTWGnssBaseTest import PowerGTWGnssBaseTest +from acts.test_utils.gnss import gnss_test_utils as gutils +from acts.test_utils.wifi import wifi_test_utils as wutils + + +class GnssPowerAGPSTest(PowerGTWGnssBaseTest): + """Gnss AGPS Power Test""" + + def turn_on_wifi_connection(self): + """Turn on wifi connection.""" + wutils.wifi_toggle_state(self.ad, True) + gutils.connect_to_wifi_network(self.ad, self.wifi_network) + + def set_cell_only(self): + """Turn off wifi connection, enable cell service.""" + wutils.wifi_toggle_state(self.ad, False) + utils.force_airplane_mode(self.ad, False) + + # Test cases + # Wifi only tests + def test_wifi_only_gps_power_baseline(self): + self.turn_on_wifi_connection() + self.baseline_test() + + def test_wifi_only_gps_strong_signal(self): + self.set_attenuation(self.atten_level['strong_signal']) + self.enable_DPO(True) + self.turn_on_wifi_connection() + self.start_gnss_tracking_with_power_data() + + def test_wifi_only_gps_weak_signal(self): + self.set_attenuation(self.atten_level['weak_signal']) + self.enable_DPO(True) + self.turn_on_wifi_connection() + self.start_gnss_tracking_with_power_data() + + def test_wifi_only_gps_no_signal(self): + self.set_attenuation(self.atten_level['no_signal']) + self.turn_on_wifi_connection() + self.start_gnss_tracking_with_power_data(is_signal=False) + + # Cell only tests + def test_cell_only_gps_power_baseline(self): + self.set_cell_only() + self.baseline_test() + + def test_cell_only_gps_strong_signal(self): + self.set_attenuation(self.atten_level['strong_signal']) + self.enable_DPO(True) + self.set_cell_only() + self.start_gnss_tracking_with_power_data() + + def test_cell_only_gps_weak_signal(self): + self.set_attenuation(self.atten_level['weak_signal']) + self.enable_DPO(True) + self.set_cell_only() + self.start_gnss_tracking_with_power_data() + + def test_cell_only_gps_no_signal(self): + self.set_attenuation(self.atten_level['no_signal']) + self.set_cell_only() + self.start_gnss_tracking_with_power_data(is_signal=False) diff --git a/acts_tests/tests/google/gnss/GnssPowerBasicTest.py b/acts_tests/tests/google/gnss/GnssPowerBasicTest.py new file mode 100644 index 0000000000..a6c0066e22 --- /dev/null +++ b/acts_tests/tests/google/gnss/GnssPowerBasicTest.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +# +# Copyright 2020 - The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from acts import utils +from acts.test_utils.power.PowerGTWGnssBaseTest import PowerGTWGnssBaseTest +from acts.test_utils.gnss import gnss_test_utils as gutils +from acts.test_utils.wifi import wifi_test_utils as wutils + + +class GnssPowerBasicTest(PowerGTWGnssBaseTest): + """Gnss Power Basic Test""" + + # Test cases + # Standalone tests + def test_standalone_gps_power_baseline(self): + """ + 1. Set DUT rockbottom. + 2. Collect power data. + """ + self.baseline_test() + + def test_standalone_DPO_on(self): + """ + 1. Attenuate signal to strong GNSS level. + 2. Turn DPO on. + 3. Open GPStool and tracking with DUT sleep. + 4. Collect power data. + """ + self.set_attenuation(self.atten_level['strong_signal']) + self.enable_DPO(True) + self.start_gnss_tracking_with_power_data(mode='standalone') + + def test_standalone_DPO_on_weak_signal(self): + """ + 1. Attenuate signal to strong GNSS level. + 2. Turn DPO on. + 3. Open GPStool and tracking with DUT sleep. + 4. Collect power data. + """ + self.set_attenuation(self.atten_level['weak_signal']) + self.enable_DPO(True) + self.start_gnss_tracking_with_power_data(mode='standalone') + + def test_standalone_DPO_off(self): + """ + 1. Attenuate signal to strong GNSS level. + 2. Turn DPO off. + 3. Open GPStool and tracking with DUT sleep. + 4. Collect power data. + """ + self.set_attenuation(self.atten_level['strong_signal']) + self.enable_DPO(False) + self.start_gnss_tracking_with_power_data(mode='standalone') + + def test_standalone_DPO_off_weak_signal(self): + """ + 1. Attenuate signal to strong GNSS level. + 2. Turn DPO off. + 3. Open GPStool and tracking with DUT sleep. + 4. Collect power data. + """ + self.set_attenuation(self.atten_level['weak_signal']) + self.enable_DPO(False) + self.start_gnss_tracking_with_power_data(mode='standalone') + + def test_standalone_no_signal(self): + """ + 1. Attenuate signal to strong GNSS level. + 2. Turn DPO on. + 3. Open GPStool and tracking with DUT sleep. + 4. Collect power data. + """ + self.set_attenuation(self.atten_level['no_signal']) + self.enable_DPO(True) + self.start_gnss_tracking_with_power_data(mode='standalone') + + def test_partial_wake_lock(self): + """ + 1. Attenuate signal to strong GNSS level. + 2. Trigger instrumentation to hold the partial wake lock. + 3. Collect power data. + """ + self.set_attenuation(self.atten_level['strong_signal']) + test_class = 'com.google.android.platform.powertests.IdleTestCase' + test_method = 'testPartialWakelock' + test_methods = {test_class: test_method} + options = {'IdleTestCase-testPartialWakelock': self.mon_duration} + instrument_cmd = gutils.build_instrumentation_call( + POWER_TEST_PACKAGE, DEFAULT_RUNNER, test_methods, options) + self.ad.adb.shell_nb(instrument_cmd) + self.baseline_test() diff --git a/acts_tests/tests/google/gnss/GnssPowerLongIntervalTest.py b/acts_tests/tests/google/gnss/GnssPowerLongIntervalTest.py new file mode 100644 index 0000000000..100b6bfb68 --- /dev/null +++ b/acts_tests/tests/google/gnss/GnssPowerLongIntervalTest.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# +# Copyright 2020 - The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from acts import utils +from acts.test_utils.power.PowerGTWGnssBaseTest import PowerGTWGnssBaseTest +from acts.test_utils.gnss import gnss_test_utils as gutils +from acts.test_utils.wifi import wifi_test_utils as wutils + + +class GnssPowerLongIntervalTest(PowerGTWGnssBaseTest): + """Gnss Power Long Interval Test""" + + def setup_class(self): + super().setup_class() + self.unpack_userparams(req_param_names=['interval']) + + # Test cases + def test_long_interval_DPO_on(self): + self.enable_DPO(True) + self.start_gnss_tracking_with_power_data( + mode='standalone', freq=self.interval) + + def test_long_interval_DPO_on_measurement_on(self): + self.enable_DPO(True) + self.start_gnss_tracking_with_power_data( + mode='standalone', freq=self.interval, meas=True) + + def test_long_interval_DPO_off(self): + self.enable_DPO(False) + self.start_gnss_tracking_with_power_data( + mode='standalone', freq=self.interval) + + def test_long_interval_DPO_off_measurement_on(self): + self.enable_DPO(False) + self.start_gnss_tracking_with_power_data( + mode='standalone', freq=self.interval, meas=True) diff --git a/acts_tests/tests/google/gnss/GnssPowerLowPowerTest.py b/acts_tests/tests/google/gnss/GnssPowerLowPowerTest.py new file mode 100644 index 0000000000..4e64be23e9 --- /dev/null +++ b/acts_tests/tests/google/gnss/GnssPowerLowPowerTest.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +# +# Copyright 2020 - The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from acts import utils +from acts.test_utils.power.PowerGTWGnssBaseTest import PowerGTWGnssBaseTest +from acts.test_utils.gnss import gnss_test_utils as gutils +from acts.test_utils.wifi import wifi_test_utils as wutils + + +class GnssPowerLowPowerTest(PowerGTWGnssBaseTest): + """Gnss Power Low Power Mode Test""" + + def setup_class(self): + super().setup_class() + self.unpack_userparams(req_param_names=['interval']) + + # Test cases + def test_low_power_mode_DPO_on(self): + self.set_attenuation(self.atten_level['strong_signal']) + self.enable_DPO(True) + self.start_gnss_tracking_with_power_data( + mode='standalone', lowpower=True) + + def test_low_power_mode_DPO_on_long_interval(self): + self.set_attenuation(self.atten_level['strong_signal']) + self.enable_DPO(True) + self.start_gnss_tracking_with_power_data( + mode='standalone', freq=self.interval, lowpower=True) + + def test_low_power_mode_DPO_off(self): + self.set_attenuation(self.atten_level['strong_signal']) + self.enable_DPO(False) + self.start_gnss_tracking_with_power_data( + mode='standalone', lowpower=True) + + def test_low_power_mode_DPO_off_long_interval(self): + self.set_attenuation(self.atten_level['strong_signal']) + self.enable_DPO(False) + self.start_gnss_tracking_with_power_data( + mode='standalone', freq=self.interval, lowpower=True) diff --git a/acts_tests/tests/google/gnss/GnssSimInventoryTest.py b/acts_tests/tests/google/gnss/GnssSimInventoryTest.py index 2daa598d52..8844cbc064 100644 --- a/acts_tests/tests/google/gnss/GnssSimInventoryTest.py +++ b/acts_tests/tests/google/gnss/GnssSimInventoryTest.py @@ -1,4 +1,7 @@ import time +import os +import tempfile + from acts import utils from acts import signals from acts.base_test import BaseTestClass @@ -22,25 +25,32 @@ class GnssSimInventoryTest(BaseTestClass): if not is_sim_ready_by_adb(self.ad.log, self.ad): raise signals.TestFailure("SIM card is not loaded and ready.") - def test_gnss_sim_inventory(self): - self.check_device_status() - android_version = int(self.ad.adb.getprop("ro.build.version.release")) - if android_version == 10: - imsi = str(self.ad.adb.shell("service call iphonesubinfo 7")) - elif android_version == 11: - imsi = str(self.ad.adb.shell("service call iphonesubinfo 8")) - else: - raise signals.TestFailure("Couldn't get imsi") + def get_imsi(self): + self.ad.log.info("Get imsi from netpolicy.xml") + tmp_path = tempfile.mkdtemp() + self.ad.pull_files("/data/system/netpolicy.xml", tmp_path) + netpolicy_path = os.path.join(tmp_path, "netpolicy.xml") + with open(netpolicy_path, "r", encoding="utf-8") as file: + for line in file.readlines(): + if "subscriberId" in line: + imsi = line.split(" ")[2].split("=")[-1].strip('"') + return imsi + raise signals.TestFailure("Fail to get imsi") + + def get_iccid(self): iccid = str(get_iccid_by_adb(self.ad)) if not isinstance(iccid, int): self.ad.log.info("Unable to get iccid via adb. Changed to isub.") iccid = str(self.ad.adb.shell( "dumpsys isub | grep iccid")).split(" ")[4].strip(",") - if not iccid: - raise signals.TestFailure("Couldn't get iccid") - sms_message = "imsi: %s, iccid: %s, ldap: %s, model: %s, sn: %s" % \ - (imsi, iccid, self.sim_inventory_ldap, self.ad.model, - self.ad.serial) + return iccid + raise signals.TestFailure("Fail to get iccid") + + def test_gnss_sim_inventory(self): + self.check_device_status() + sms_message = "imsi: %s, iccid: %s, ldap: %s, model: %s, sn: %s" % ( + self.get_imsi(), self.get_iccid(), self.sim_inventory_ldap, + self.ad.model, self.ad.serial) self.ad.log.info(sms_message) try: self.ad.log.info("Send SMS by SL4A.") diff --git a/acts_tests/tests/google/net/IKEv2VpnOverLTETest.py b/acts_tests/tests/google/net/IKEv2VpnOverLTETest.py new file mode 100644 index 0000000000..cd9c41ac53 --- /dev/null +++ b/acts_tests/tests/google/net/IKEv2VpnOverLTETest.py @@ -0,0 +1,99 @@ +# +# Copyright 2020 - The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from acts import base_test +from acts.test_decorators import test_tracker_info +from acts.test_utils.net import connectivity_const +from acts.test_utils.net import net_test_utils as nutils +from acts.test_utils.wifi import wifi_test_utils as wutils + +VPN_CONST = connectivity_const.VpnProfile +VPN_TYPE = connectivity_const.VpnProfileType +VPN_PARAMS = connectivity_const.VpnReqParams + + +class IKEv2VpnOverLTETest(base_test.BaseTestClass): + """IKEv2 VPN tests.""" + + def setup_class(self): + + required_params = dir(VPN_PARAMS) + required_params = [x for x in required_params if not x.startswith("__")] + self.unpack_userparams(req_param_names=required_params) + self.vpn_params = { + "vpn_username": self.vpn_username, + "vpn_password": self.vpn_password, + "psk_secret": self.psk_secret, + "client_pkcs_file_name": self.client_pkcs_file_name, + "cert_path_vpnserver": self.cert_path_vpnserver, + "cert_password": self.cert_password, + "vpn_identity": self.vpn_identity, + } + + for ad in self.android_devices: + wutils.wifi_test_device_init(ad) + nutils.verify_lte_data_and_tethering_supported(ad) + self.tmo_dut = self.android_devices[0] + self.vzw_dut = self.android_devices[1] + + def on_fail(self, test_name, begin_time): + for ad in self.android_devices: + ad.take_bug_report(test_name, begin_time) + + ### Helper methods ### + + def _test_ikev2_vpn(self, ad, vpn, hostname=None): + """Verify IKEv2 VPN connection. + + Args: + ad: android device to run the test. + vpn: type of VPN. + hostname: hostname or IP address of the server. + """ + server_addr = self.vpn_server_addresses[vpn.name][0] + self.vpn_params["server_addr"] = server_addr + if not hostname: + hostname = server_addr + vpn_addr = self.vpn_verify_addresses[vpn.name][0] + vpn_profile = nutils.generate_ikev2_vpn_profile( + ad, self.vpn_params, vpn, hostname, self.log_path) + nutils.legacy_vpn_connection_test_logic(ad, vpn_profile, vpn_addr) + + ### Test cases ### + + @test_tracker_info(uuid="31fac6c5-f76c-403c-8b76-29c01557a48a") + def test_ikev2_psk_vpn_tmo(self): + self._test_ikev2_vpn(self.tmo_dut, VPN_TYPE.IKEV2_IPSEC_PSK) + + @test_tracker_info(uuid="c28adef0-6578-4841-a833-e52a5b16a390") + def test_ikev2_mschapv2_vpn_tmo(self): + self._test_ikev2_vpn(self.tmo_dut, VPN_TYPE.IKEV2_IPSEC_USER_PASS) + + @test_tracker_info(uuid="6c7daad9-ae7a-493d-bbab-9001068f22c5") + def test_ikev2_rsa_vpn_tmo(self): + self._test_ikev2_vpn(self.tmo_dut, VPN_TYPE.IKEV2_IPSEC_RSA) + + @test_tracker_info(uuid="1275a2f-e939-4557-879d-fbbd9c5dbd93") + def test_ikev2_psk_vpn_vzw(self): + self._test_ikev2_vpn(self.vzw_dut, VPN_TYPE.IKEV2_IPSEC_PSK) + + @test_tracker_info(uuid="fd146163-f28d-4514-96a0-82f51b70e218") + def test_ikev2_mschapv2_vpn_vzw(self): + self._test_ikev2_vpn(self.vzw_dut, VPN_TYPE.IKEV2_IPSEC_USER_PASS) + + @test_tracker_info(uuid="722de9b5-834f-4854-b4a6-e31860628fe9") + def test_ikev2_rsa_vpn_vzw(self): + self._test_ikev2_vpn(self.vzw_dut, VPN_TYPE.IKEV2_IPSEC_RSA) diff --git a/acts_tests/tests/google/net/IKEv2VpnOverWifiTest.py b/acts_tests/tests/google/net/IKEv2VpnOverWifiTest.py new file mode 100644 index 0000000000..1f36e9a573 --- /dev/null +++ b/acts_tests/tests/google/net/IKEv2VpnOverWifiTest.py @@ -0,0 +1,98 @@ +# +# Copyright 2020 - The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from acts import base_test +from acts.test_decorators import test_tracker_info +from acts.test_utils.net import connectivity_const +from acts.test_utils.net import net_test_utils as nutils +from acts.test_utils.wifi import wifi_test_utils as wutils + +VPN_CONST = connectivity_const.VpnProfile +VPN_TYPE = connectivity_const.VpnProfileType +VPN_PARAMS = connectivity_const.VpnReqParams + + +class IKEv2VpnOverWifiTest(base_test.BaseTestClass): + """IKEv2 VPN tests.""" + + def setup_class(self): + self.dut = self.android_devices[0] + + required_params = dir(VPN_PARAMS) + required_params = [x for x in required_params if not x.startswith("__")] + self.unpack_userparams(req_param_names=required_params) + self.vpn_params = { + "vpn_username": self.vpn_username, + "vpn_password": self.vpn_password, + "psk_secret": self.psk_secret, + "client_pkcs_file_name": self.client_pkcs_file_name, + "cert_path_vpnserver": self.cert_path_vpnserver, + "cert_password": self.cert_password, + "vpn_identity": self.vpn_identity, + } + + wutils.wifi_test_device_init(self.dut) + wutils.connect_to_wifi_network(self.dut, self.wifi_network) + + def teardown_class(self): + wutils.reset_wifi(self.dut) + + def on_fail(self, test_name, begin_time): + self.dut.take_bug_report(test_name, begin_time) + + ### Helper methods ### + def _test_ikev2_vpn(self, vpn, hostname=None): + """Verify IKEv2 VPN connection. + + Args: + vpn: type of VPN. + hostname: hostname or IP address of the server. + """ + server_addr = self.vpn_server_addresses[vpn.name][0] + self.vpn_params["server_addr"] = server_addr + if not hostname: + hostname = server_addr + vpn_addr = self.vpn_verify_addresses[vpn.name][0] + vpn_profile = nutils.generate_ikev2_vpn_profile( + self.dut, self.vpn_params, vpn, hostname, self.log_path) + nutils.legacy_vpn_connection_test_logic(self.dut, vpn_profile, vpn_addr) + + ### Test cases ### + + @test_tracker_info(uuid="4991755c-321d-4e9a-ada9-fc821a35bb5b") + def test_ikev2_psk_vpn_wifi(self): + self._test_ikev2_vpn(VPN_TYPE.IKEV2_IPSEC_PSK) + + @test_tracker_info(uuid="04d88575-7b96-4746-bff8-a1d6841e202e") + def test_ikev2_mschapv2_vpn_wifi(self): + self._test_ikev2_vpn(VPN_TYPE.IKEV2_IPSEC_USER_PASS) + + @test_tracker_info(uuid="e65f8a3e-f807-4493-822e-377dd6fa89cd") + def test_ikev2_rsa_vpn_wifi(self): + self._test_ikev2_vpn(VPN_TYPE.IKEV2_IPSEC_RSA) + + @test_tracker_info(uuid="bdd8a967-8dac-4e48-87b7-2ce9f7d32158") + def test_ikev2_psk_vpn_wifi_with_hostname(self): + self._test_ikev2_vpn(VPN_TYPE.IKEV2_IPSEC_PSK, self.vpn_server_hostname) + + @test_tracker_info(uuid="19692520-c123-4b42-8549-08dda9c4873e") + def test_ikev2_mschapv2_vpn_wifi_with_hostname(self): + self._test_ikev2_vpn(VPN_TYPE.IKEV2_IPSEC_USER_PASS, + self.vpn_server_hostname) + + @test_tracker_info(uuid="bdaaf6e3-6671-4533-baba-2951009c7d69") + def test_ikev2_rsa_vpn_wifi_with_hostname(self): + self._test_ikev2_vpn(VPN_TYPE.IKEV2_IPSEC_RSA, self.vpn_server_hostname) diff --git a/acts_tests/tests/google/net/IpSecTest.py b/acts_tests/tests/google/net/IpSecTest.py index 42b508bdcd..1b4a14468c 100644 --- a/acts_tests/tests/google/net/IpSecTest.py +++ b/acts_tests/tests/google/net/IpSecTest.py @@ -25,8 +25,10 @@ from acts.test_utils.net.net_test_utils import stop_tcpdump from acts.test_utils.wifi import wifi_test_utils as wutils import random +import time WLAN = "wlan0" +WAIT_FOR_IP = 15 class IpSecTest(base_test.BaseTestClass): @@ -39,17 +41,21 @@ class IpSecTest(base_test.BaseTestClass): req_params = ("wifi_network",) self.unpack_userparams(req_params) - wutils.start_wifi_connection_scan_and_ensure_network_found( - self.dut_a, self.wifi_network['SSID']) - wutils.wifi_connect(self.dut_a, self.wifi_network) - wutils.start_wifi_connection_scan_and_ensure_network_found( - self.dut_b, self.wifi_network['SSID']) - wutils.wifi_connect(self.dut_b, self.wifi_network) - - self.ipv4_dut_a = self.dut_a.droid.connectivityGetIPv4Addresses(WLAN)[0] - self.ipv4_dut_b = self.dut_b.droid.connectivityGetIPv4Addresses(WLAN)[0] - self.ipv6_dut_a = self.dut_a.droid.connectivityGetIPv6Addresses(WLAN)[0] - self.ipv6_dut_b = self.dut_b.droid.connectivityGetIPv6Addresses(WLAN)[0] + wutils.connect_to_wifi_network(self.dut_a, self.wifi_network) + wutils.connect_to_wifi_network(self.dut_b, self.wifi_network) + time.sleep(WAIT_FOR_IP) + + try: + self.ipv4_dut_a = self.dut_a.droid.connectivityGetIPv4Addresses( + WLAN)[0] + self.ipv4_dut_b = self.dut_b.droid.connectivityGetIPv4Addresses( + WLAN)[0] + self.ipv6_dut_a = self.dut_a.droid.connectivityGetIPv6Addresses( + WLAN)[0] + self.ipv6_dut_b = self.dut_b.droid.connectivityGetIPv6Addresses( + WLAN)[0] + except Exception as e: + asserts.abort_class("Failed to get IPv4/IPv6 address: %s" % e) self.crypt_auth_combos = iutils.generate_random_crypt_auth_combo() diff --git a/acts_tests/tests/google/power/bt/PowerBLEadvertiseTest.py b/acts_tests/tests/google/power/bt/PowerBLEadvertiseTest.py index f5e24bd386..1933699fd3 100644 --- a/acts_tests/tests/google/power/bt/PowerBLEadvertiseTest.py +++ b/acts_tests/tests/google/power/bt/PowerBLEadvertiseTest.py @@ -20,34 +20,31 @@ import acts.test_utils.bt.bt_power_test_utils as btputils import acts.test_utils.power.PowerBTBaseTest as PBtBT BLE_LOCATION_SCAN_ENABLE = 'settings put secure location_mode 3' -EXTRA_ADV_TIME = 10 -MONSOON_TAIL_CUT = 5 +EXTRA_ADV_TIME = 3 +ADV_TAIL = 5 class PowerBLEadvertiseTest(PBtBT.PowerBTBaseTest): def __init__(self, configs): super().__init__(configs) - req_params = ['adv_modes', 'adv_power_levels', 'adv_duration'] + req_params = ['adv_modes', 'adv_power_levels'] self.unpack_userparams(req_params) # Loop all advertise modes and power levels for adv_mode in self.adv_modes: for adv_power_level in self.adv_power_levels: - self.generate_test_case(adv_mode, adv_power_level, - self.adv_duration) + self.generate_test_case(adv_mode, adv_power_level) def setup_class(self): super().setup_class() self.dut.adb.shell(BLE_LOCATION_SCAN_ENABLE) # Make sure during power measurement, advertisement is always on - self.mon_info.duration = (self.adv_duration - self.mon_offset - - EXTRA_ADV_TIME - MONSOON_TAIL_CUT) + self.adv_duration = self.mon_info.duration + self.mon_offset + ADV_TAIL + EXTRA_ADV_TIME - def generate_test_case(self, adv_mode, adv_power_level, adv_duration): + def generate_test_case(self, adv_mode, adv_power_level): def test_case_fn(): - self.measure_ble_advertise_power(adv_mode, adv_power_level, - adv_duration) + self.measure_ble_advertise_power(adv_mode, adv_power_level) adv_mode_str = bleenum.AdvertiseSettingsAdvertiseMode(adv_mode).name adv_txpl_str = bleenum.AdvertiseSettingsAdvertiseTxPower( @@ -55,10 +52,9 @@ class PowerBLEadvertiseTest(PBtBT.PowerBTBaseTest): test_case_name = ('test_BLE_{}_{}'.format(adv_mode_str, adv_txpl_str)) setattr(self, test_case_name, test_case_fn) - def measure_ble_advertise_power(self, adv_mode, adv_power_level, - adv_duration): + def measure_ble_advertise_power(self, adv_mode, adv_power_level): btputils.start_apk_ble_adv(self.dut, adv_mode, adv_power_level, - adv_duration) + self.adv_duration) time.sleep(EXTRA_ADV_TIME) self.measure_power_and_validate() diff --git a/acts_tests/tests/google/power/bt/PowerBLEscanTest.py b/acts_tests/tests/google/power/bt/PowerBLEscanTest.py index 91b11c3dd6..f859f0c567 100644 --- a/acts_tests/tests/google/power/bt/PowerBLEscanTest.py +++ b/acts_tests/tests/google/power/bt/PowerBLEscanTest.py @@ -20,39 +20,37 @@ import acts.test_utils.bt.bt_power_test_utils as btputils import acts.test_utils.power.PowerBTBaseTest as PBtBT BLE_LOCATION_SCAN_ENABLE = 'settings put secure location_mode 3' -EXTRA_SCAN_TIME = 10 -MONSOON_TAIL_CUT = 5 +EXTRA_SCAN_TIME = 3 +SCAN_TAIL = 5 class PowerBLEscanTest(PBtBT.PowerBTBaseTest): def __init__(self, configs): super().__init__(configs) - req_params = ['scan_modes', 'scan_duration'] + req_params = ['scan_modes'] self.unpack_userparams(req_params) for scan_mode in self.scan_modes: - self.generate_test_case_no_devices_around(scan_mode, - self.scan_duration) + self.generate_test_case_no_devices_around(scan_mode) def setup_class(self): super().setup_class() self.dut.adb.shell(BLE_LOCATION_SCAN_ENABLE) # Make sure during power measurement, scan is always on - self.mon_info.duration = (self.scan_duration - self.mon_offset - - EXTRA_SCAN_TIME - MONSOON_TAIL_CUT) + self.scan_duration = self.mon_info.duration + self.mon_offset + SCAN_TAIL + EXTRA_SCAN_TIME - def generate_test_case_no_devices_around(self, scan_mode, scan_duration): + def generate_test_case_no_devices_around(self, scan_mode): def test_case_fn(): - self.measure_ble_scan_power(scan_mode, scan_duration) + self.measure_ble_scan_power(scan_mode) test_case_name = ('test_BLE_{}_no_advertisers'.format( bleenum.ScanSettingsScanMode(scan_mode).name)) setattr(self, test_case_name, test_case_fn) - def measure_ble_scan_power(self, scan_mode, scan_duration): + def measure_ble_scan_power(self, scan_mode): - btputils.start_apk_ble_scan(self.dut, scan_mode, scan_duration) + btputils.start_apk_ble_scan(self.dut, scan_mode, self.scan_duration) time.sleep(EXTRA_SCAN_TIME) self.measure_power_and_validate() diff --git a/acts_tests/tests/google/power/bt/PowerBTa2dpTest.py b/acts_tests/tests/google/power/bt/PowerBTa2dpTest.py index 2473b9df47..b6d60f1330 100644 --- a/acts_tests/tests/google/power/bt/PowerBTa2dpTest.py +++ b/acts_tests/tests/google/power/bt/PowerBTa2dpTest.py @@ -60,18 +60,20 @@ class PowerBTa2dpTest(PBtBT.PowerBTBaseTest): else: self.log.info('Current Codec is {}, no need to change'.format( current_codec_type)) + # Start music playing + self.media.play() + time.sleep(EXTRA_PLAY_TIME) # Set attenuation so BT tx at desired power level self.log.info('Current Attenuation {} dB'.format( self.attenuator.get_atten())) tpl = 'PL' + str(tpl) - PBtBT.ramp_attenuation(self.attenuator, self.atten_pl_settings[tpl][0]) + PBtBT.ramp_attenuation(self.attenuator, self.atten_pl_settings[tpl][0], + attenuation_step_max=1, time_wait_in_between=1) self.log.info('Setting Attenuator to {} dB'.format( self.atten_pl_settings[tpl][0])) - self.media.play() self.log.info('Running A2DP with codec {} at {}'.format( codec_config['codec_type'], tpl)) self.dut.droid.goToSleepNow() - time.sleep(EXTRA_PLAY_TIME) self.measure_power_and_validate() diff --git a/acts_tests/tests/google/power/bt/PowerBTcalibrationTest.py b/acts_tests/tests/google/power/bt/PowerBTcalibrationTest.py index d6f029294c..4b14f1758c 100644 --- a/acts_tests/tests/google/power/bt/PowerBTcalibrationTest.py +++ b/acts_tests/tests/google/power/bt/PowerBTcalibrationTest.py @@ -52,14 +52,17 @@ class PowerBTcalibrationTest(PBtBT.PowerBTBaseTest): # Loop through attenuation in 1 dB step until reaching at PL10 self.log.info('Starting Calibration Process') + pl10_count = 0 for i in range(int(self.attenuator.get_max_atten())): self.attenuator.set_atten(i) bt_metrics_dict = btutils.get_bt_metric(self.dut) - pwl = int(bt_metrics_dict['pwlv'][self.dut.serial]) + pwl = bt_metrics_dict['pwlv'][self.dut.serial] self.log.info('Reach PW {} at attenuation {} dB'.format(pwl, i)) self.cal_matrix.append([i, pwl]) if pwl == 10: + pl10_count += 1 + if pl10_count > 5: break # Write cal results to csv diff --git a/acts_tests/tests/google/power/tel/PowerTelMac_Modem_Test.py b/acts_tests/tests/google/power/tel/PowerTelMac_Modem_Test.py index a633e6da22..0ceade672e 100644 --- a/acts_tests/tests/google/power/tel/PowerTelMac_Modem_Test.py +++ b/acts_tests/tests/google/power/tel/PowerTelMac_Modem_Test.py @@ -22,7 +22,6 @@ class PowerTelMac_Modem_Test(cppt.PowerTelPDCCHTest): def display_name_test_suite(self): return 'QComm dashboard - RB Test' - def test_lte_band_4_pul_low_bw_20_tm_4_dlmcs_28_mimo_2x2_direction_dlul_phich_16_cfi_1( - self): + def test_lte_band_4_pul_low_bw_20_tm_4_dlmcs_28_mimo_2x2_direction_dlul_phich_16_cfi_1_mac(self): self.display_name_test_case = 'LTE7E-RB - B4' self.power_pdcch_test() diff --git a/acts_tests/tests/google/tel/live/TelLiveDataTest.py b/acts_tests/tests/google/tel/live/TelLiveDataTest.py index 1a3d614a96..2b0b3e714f 100644..100755 --- a/acts_tests/tests/google/tel/live/TelLiveDataTest.py +++ b/acts_tests/tests/google/tel/live/TelLiveDataTest.py @@ -21,6 +21,7 @@ import collections import random import time import os +from queue import Empty from acts import signals from acts.test_decorators import test_tracker_info @@ -34,18 +35,21 @@ from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED from acts.test_utils.tel.tel_defines import DIRECTION_MOBILE_TERMINATED from acts.test_utils.tel.tel_defines import DATA_STATE_CONNECTED +from acts.test_utils.tel.tel_defines import FAKE_DATE_TIME +from acts.test_utils.tel.tel_defines import FAKE_YEAR +from acts.test_utils.tel.tel_defines import EventNetworkCallback +from acts.test_utils.tel.tel_defines import NetworkCallbackCapabilitiesChanged +from acts.test_utils.tel.tel_defines import NetworkCallbackLost from acts.test_utils.tel.tel_defines import GEN_2G from acts.test_utils.tel.tel_defines import GEN_3G from acts.test_utils.tel.tel_defines import GEN_4G from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA -from acts.test_utils.tel.tel_defines import NETWORK_SERVICE_VOICE from acts.test_utils.tel.tel_defines import RAT_2G from acts.test_utils.tel.tel_defines import RAT_3G from acts.test_utils.tel.tel_defines import RAT_4G from acts.test_utils.tel.tel_defines import RAT_5G from acts.test_utils.tel.tel_defines import NETWORK_MODE_WCDMA_ONLY from acts.test_utils.tel.tel_defines import NETWORK_MODE_NR_LTE_GSM_WCDMA -from acts.test_utils.tel.tel_defines import RAT_FAMILY_LTE from acts.test_utils.tel.tel_defines import SIM1_SLOT_INDEX from acts.test_utils.tel.tel_defines import SIM2_SLOT_INDEX from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_NW_SELECTION @@ -53,8 +57,9 @@ from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_TETHERING_ENTITLEMENT_ from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_WIFI_CONNECTION from acts.test_utils.tel.tel_defines import TETHERING_MODE_WIFI from acts.test_utils.tel.tel_defines import WAIT_TIME_AFTER_REBOOT -from acts.test_utils.tel.tel_defines import WAIT_TIME_AFTER_MODE_CHANGE +from acts.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_STATE_CHECK from acts.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING +from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_USER_PLANE_DATA from acts.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_REG_AND_CALL from acts.test_utils.tel.tel_defines import \ WAIT_TIME_DATA_STATUS_CHANGE_DURING_WIFI_TETHERING @@ -78,14 +83,11 @@ from acts.test_utils.tel.tel_test_utils import \ from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected from acts.test_utils.tel.tel_test_utils import get_mobile_data_usage from acts.test_utils.tel.tel_test_utils import get_slot_index_from_subid -from acts.test_utils.tel.tel_test_utils import get_network_rat_for_subscription from acts.test_utils.tel.tel_test_utils import hangup_call from acts.test_utils.tel.tel_test_utils import multithread_func from acts.test_utils.tel.tel_test_utils import reboot_device from acts.test_utils.tel.tel_test_utils import remove_mobile_data_usage_limit -from acts.test_utils.tel.tel_test_utils import set_call_state_listen_level from acts.test_utils.tel.tel_test_utils import set_mobile_data_usage_limit -from acts.test_utils.tel.tel_test_utils import setup_sim from acts.test_utils.tel.tel_test_utils import stop_wifi_tethering from acts.test_utils.tel.tel_test_utils import start_wifi_tethering from acts.test_utils.tel.tel_test_utils import is_current_network_5g_nsa @@ -93,7 +95,6 @@ from acts.test_utils.tel.tel_test_utils import set_preferred_mode_for_5g from acts.test_utils.tel.tel_test_utils import get_current_override_network_type from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb -from acts.test_utils.tel.tel_test_utils import toggle_volte from acts.test_utils.tel.tel_test_utils import verify_internet_connection from acts.test_utils.tel.tel_test_utils import verify_http_connection from acts.test_utils.tel.tel_test_utils import verify_incall_state @@ -111,7 +112,6 @@ from acts.test_utils.tel.tel_test_utils import wifi_toggle_state from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G from acts.test_utils.tel.tel_test_utils import WIFI_SSID_KEY -from acts.test_utils.tel.tel_test_utils import bring_up_sl4a from acts.test_utils.tel.tel_test_utils import check_data_stall_detection from acts.test_utils.tel.tel_test_utils import check_network_validation_fail from acts.test_utils.tel.tel_test_utils import break_internet_except_sl4a_port @@ -123,6 +123,8 @@ from acts.test_utils.tel.tel_test_utils import \ test_data_browsing_success_using_sl4a from acts.test_utils.tel.tel_test_utils import \ test_data_browsing_failure_using_sl4a +from acts.test_utils.tel.tel_test_utils import set_time_sync_from_network +from acts.test_utils.tel.tel_test_utils import datetime_handle from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte @@ -137,7 +139,6 @@ from acts.utils import enable_doze from acts.utils import rand_ascii_str from acts.utils import adb_shell_ping - class TelLiveDataTest(TelephonyBaseTest): def setup_class(self): super().setup_class() @@ -154,6 +155,43 @@ class TelLiveDataTest(TelephonyBaseTest): TelephonyBaseTest.teardown_class(self) + def _listen_for_network_callback(self, ad, event, apm_mode=False): + """Verify network callback for Meteredness + + Args: + ad: DUT to get the network callback for + event: Network callback event + + Returns: + True: if the expected network callback found, False if not + """ + key = ad.droid.connectivityRegisterDefaultNetworkCallback() + ad.droid.connectivityNetworkCallbackStartListeningForEvent(key, event) + if apm_mode: + ad.log.info("Turn on Airplane Mode") + toggle_airplane_mode(ad.log, ad, True) + curr_time = time.time() + status = False + while time.time() < curr_time + MAX_WAIT_TIME_USER_PLANE_DATA: + try: + nc_event = ad.ed.pop_event(EventNetworkCallback) + ad.log.info("Received: %s" % + nc_event["data"]["networkCallbackEvent"]) + if nc_event["data"]["networkCallbackEvent"] == event: + status = nc_event["data"]["metered"] + ad.log.info("Current state of Meteredness is %s", status) + break + except Empty: + pass + + ad.droid.connectivityNetworkCallbackStopListeningForEvent(key, event) + ad.droid.connectivityUnregisterNetworkCallback(key) + if apm_mode: + ad.log.info("Turn off Airplane Mode") + toggle_airplane_mode(ad.log, ad, False) + time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK) + return status + """ Tests Begin """ @@ -179,6 +217,53 @@ class TelLiveDataTest(TelephonyBaseTest): ad.log.info("Data Browsing test FAIL for all 3 iterations") return False + @test_tracker_info(uuid="7f9fee99-40ec-4770-9eb4-00befca9696d") + @TelephonyBaseTest.tel_test_wrap + def test_5g_nsa_data_browsing(self): + """ Verifying connectivity of internet and browsing websites on 5G NSA network. + + Ensure + 1. ping to IP of websites is successful. + 2. http ping to IP of websites is successful. + 3. browsing websites is successful. + Returns: + True if pass; False if fail. + """ + ad = self.android_devices[0] + wifi_toggle_state(ad.log, ad, False) + sub_id = ad.droid.subscriptionGetDefaultSubId() + if not set_preferred_network_mode_pref(ad.log, ad, sub_id, + NETWORK_MODE_NR_LTE_GSM_WCDMA): + ad.log.error("Failed to set network mode to NSA") + return False + ad.log.info("Set network mode to NSA successfully") + ad.log.info("Waiting for 5g NSA attach for 60 secs") + if is_current_network_5g_nsa(ad, timeout=60): + ad.log.info("Success! attached on 5g NSA") + else: + ad.log.error("Failure - expected NR_NSA, current %s", + get_current_override_network_type(ad)) + # Can't attach 5g NSA, exit test! + return False + for iteration in range(3): + connectivity = False + browsing = False + ad.log.info("Attempt %d", iteration + 1) + if not verify_internet_connection(self.log, ad): + ad.log.error("Failed to connect to internet!") + else: + ad.log.info("Connect to internet successfully!") + connectivity = True + if not browsing_test(ad.log, ad): + ad.log.error("Failed to browse websites!") + else: + ad.log.info("Successful to browse websites!") + browsing = True + if connectivity and browsing: + return True + time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING) + ad.log.error("5G NSA Connectivity and Data Browsing test FAIL for all 3 iterations") + return False @test_tracker_info(uuid="0679214b-9002-476d-83a7-3532b3cca209") @TelephonyBaseTest.tel_test_wrap @@ -327,6 +412,153 @@ class TelLiveDataTest(TelephonyBaseTest): return False + @test_tracker_info(uuid="a3229fbf-48d8-4b88-9a36-4875b55426de") + @TelephonyBaseTest.tel_test_wrap + def test_5g_nsa_data_stall_recovery(self): + """ Verifies 5G NSA data stall + + Set Mode to 5G + Wait for 5G attached on NSA + Browse websites for success + Trigger data stall and verify browsing fails + Resume data and verify browsing success + + Returns: + True if pass; False if fail. + """ + ad = self.android_devices[0] + result = True + wifi_toggle_state(ad.log, ad, False) + toggle_airplane_mode(ad.log, ad, False) + + set_preferred_mode_for_5g(ad) + if not is_current_network_5g_nsa(ad): + ad.log.error("Phone not attached on 5G NSA") + return False + + cmd = ('ss -l -p -n | grep "tcp.*droid_script" | tr -s " " ' + '| cut -d " " -f 5 | sed s/.*://g') + sl4a_port = ad.adb.shell(cmd) + + if not test_data_browsing_success_using_sl4a(ad.log, ad): + ad.log.error("Browsing failed before the test, aborting!") + return False + + begin_time = get_device_epoch_time(ad) + break_internet_except_sl4a_port(ad, sl4a_port) + + if not test_data_browsing_failure_using_sl4a(ad.log, ad): + ad.log.error("Browsing success even after breaking " \ + "the internet, aborting!") + result = False + + if not check_data_stall_detection(ad): + ad.log.warning("NetworkMonitor unable to detect Data Stall") + + if not check_network_validation_fail(ad, begin_time): + ad.log.warning("Unable to detect NW validation fail") + + if not check_data_stall_recovery(ad, begin_time): + ad.log.error("Recovery was not triggered") + result = False + + resume_internet_with_sl4a_port(ad, sl4a_port) + time.sleep(MAX_WAIT_TIME_USER_PLANE_DATA) + if not test_data_browsing_success_using_sl4a(ad.log, ad): + ad.log.error("Browsing failed after resuming internet") + result = False + if result: + ad.log.info("PASS - data stall over 5G NSA") + else: + ad.log.error("FAIL - data stall over 5G NSA") + return result + + + @test_tracker_info(uuid="754810e8-cd93-48e2-9c70-9d951ea416fe") + @TelephonyBaseTest.tel_test_wrap + def test_5g_nsa_metered_cellular(self): + """ Verifies 5G Meteredness API + + Set Mode to 5G + Wait for 5G attached on NSA + Register for Connectivity callback + Verify value of metered flag + + Returns: + True if pass; False if fail. + """ + try: + ad = self.android_devices[0] + wifi_toggle_state(ad.log, ad, False) + toggle_airplane_mode(ad.log, ad, False) + set_preferred_mode_for_5g(ad) + if not is_current_network_5g_nsa(ad): + ad.log.error("Phone not attached on 5G NSA") + return False + return self._listen_for_network_callback(ad, + NetworkCallbackCapabilitiesChanged) + except Exception as e: + ad.log.error(e) + return False + + + @test_tracker_info(uuid="5596d166-5543-4c55-8f65-84234ecb5ba7") + @TelephonyBaseTest.tel_test_wrap + def test_5g_nsa_metered_airplane(self): + """ Verifies 5G Meteredness API + + Set Mode to 5G, Turn on Airplane mode + Register for Connectivity callback + Verify value of metered flag + + Returns: + True if pass; False if fail. + """ + try: + ad = self.android_devices[0] + wifi_toggle_state(ad.log, ad, False) + set_preferred_mode_for_5g(ad) + return self._listen_for_network_callback(ad, + NetworkCallbackLost, apm_mode=True) + except Exception as e: + ad.log.error(e) + toggle_airplane_mode(ad.log, ad, False) + time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK) + return False + + @test_tracker_info(uuid="8fb6c4db-9424-4041-be28-5f4552b2afbf") + @TelephonyBaseTest.tel_test_wrap + def test_5g_nsa_metered_wifi(self): + """ Verifies 5G Meteredness API + + Set Mode to 5G, Wifi Connected + Register for Connectivity callback + Verify value of metered flag + + Returns: + True if pass; False if fail. + """ + ad = self.android_devices[0] + try: + toggle_airplane_mode(ad.log, ad, False) + set_preferred_mode_for_5g(ad) + if not is_current_network_5g_nsa(ad): + ad.log.error("Phone not attached on 5G NSA") + wifi_toggle_state(ad.log, ad, True) + if not ensure_wifi_connected(ad.log, ad, + self.wifi_network_ssid, + self.wifi_network_pass): + ad.log.error("WiFi connect fail.") + return False + return self._listen_for_network_callback(ad, + NetworkCallbackCapabilitiesChanged) + except Exception as e: + ad.log.error(e) + return False + finally: + wifi_toggle_state(ad.log, ad, False) + + @test_tracker_info(uuid="1b0354f3-8668-4a28-90a5-3b3d2b2756d3") @TelephonyBaseTest.tel_test_wrap def test_airplane_mode(self): @@ -3594,4 +3826,87 @@ class TelLiveDataTest(TelephonyBaseTest): return browsing_test(self.log, self.android_devices[0], wifi_ssid=self.wifi_network_ssid) + def _test_sync_time_from_network(self, ad, data_on=True): + """ Verifies time recovered by nitz service + + 1. Toggle mobile data + 2. Toggle off network time synchronization + 3. Change device time to FAKE_DATE_TIME: Jan,2,2019 03:04:05 + 4. Toggle on network time synchronization + 5. Verify the year is changed back. + 6. Recover mobile data + + Args: + ad: android device object + data_on: conditional mobile data on or off + + Returns: + True if pass; False if fail. + """ + if not(data_on): + ad.log.info("Disable mobile data.") + ad.droid.telephonyToggleDataConnection(False) + set_time_sync_from_network(ad, "disable") + ad.log.info("Set device time to Jan,2,2019 03:04:05") + datetime_handle(ad, 'set', set_datetime_value = FAKE_DATE_TIME) + datetime_before_sync = datetime_handle(ad, 'get') + ad.log.info("Got before sync datetime from device: %s", + datetime_before_sync) + device_year = datetime_handle(ad, 'get', get_year = True) + + if (device_year != FAKE_YEAR): + raise signals.TestSkip("Set device time failed, skip test.") + set_time_sync_from_network(ad, "enable") + datetime_after_sync = datetime_handle(ad, "get") + ad.log.info("Got after sync datetime from device: %s", + datetime_after_sync) + device_year = datetime_handle(ad, "get", get_year = True) + + if not(data_on): + ad.log.info("Enabling mobile data.") + ad.droid.telephonyToggleDataConnection(True) + + if (device_year != FAKE_YEAR): + self.record_data({ + "Test Name": "test_sync_time_from_network", + "sponge_properties": { + "result": "pass", + }, + }) + return True + else: + return False + + @test_tracker_info(uuid="1017b655-5d79-44d2-86ff-675c69aec26b") + @TelephonyBaseTest.tel_test_wrap + def test_sync_time_from_network(self): + """ Test device time recovered by nitz service + + 1. Toggle off network time synchronization + 2. Change device time to FAKE_DATE_TIME. + 3. Toggle on network time synchronization + 4. Verify the year is changed back. + + """ + ad = self.android_devices[0] + wifi_toggle_state(ad.log, ad, False) + return self._test_sync_time_from_network(ad) + + @test_tracker_info(uuid="46d170c6-a632-4124-889b-96caa0e641da") + @TelephonyBaseTest.tel_test_wrap + def test_sync_time_from_network_data_off(self): + """ Test device time recovered by nitz service + + 1. Toggle off mobile data + 2. Toggle off network time synchronization + 3. Change device time to FAKE_DATE_TIME. + 4. Toggle on network time synchronization + 5. Verify the year is changed back. + + """ + ad = self.android_devices[0] + wifi_toggle_state(ad.log, ad, False) + return self._test_sync_time_from_network(ad, data_on=False) + + """ Tests End """ diff --git a/acts_tests/tests/google/tel/live/TelLiveGFTDSDSDDSSwitchTest.py b/acts_tests/tests/google/tel/live/TelLiveGFTDSDSDDSSwitchTest.py new file mode 100644 index 0000000000..d188e28a99 --- /dev/null +++ b/acts_tests/tests/google/tel/live/TelLiveGFTDSDSDDSSwitchTest.py @@ -0,0 +1,2367 @@ +#!/usr/bin/env python3 +# +# Copyright 2020 - Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import re +import time + +from acts import asserts +from acts import signals +from acts.test_decorators import test_tracker_info +from acts.test_utils.tel.loggers.protos.telephony_metric_pb2 import \ + TelephonyVoiceTestResult +from acts.test_utils.tel.loggers.telephony_metric_logger import \ + TelephonyMetricLogger +from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest +from acts.test_utils.tel.tel_defines import MAX_WAIT_TIME_SMS_RECEIVE +from acts.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL +from acts.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING +from acts.test_utils.tel.tel_defines import INVALID_SUB_ID +from acts.test_utils.tel.tel_defines import WFC_MODE_DISABLED +from acts.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED +from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_ONLY +from acts.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED +from acts.test_utils.tel.tel_data_utils import reboot_test +from acts.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index +from acts.test_utils.tel.tel_subscription_utils import get_default_data_sub_id +from acts.test_utils.tel.tel_subscription_utils import set_subid_for_message +from acts.test_utils.tel.tel_subscription_utils import set_subid_for_data +from acts.test_utils.tel.tel_subscription_utils import set_incoming_voice_sub_id +from acts.test_utils.tel.tel_subscription_utils import set_dds_on_slot_0 +from acts.test_utils.tel.tel_subscription_utils import set_dds_on_slot_1 +from acts.test_utils.tel.tel_subscription_utils import \ + get_subid_on_same_network_of_host_ad +from acts.test_utils.tel.tel_test_utils import multithread_func +from acts.test_utils.tel.tel_test_utils import start_youtube_video +from acts.test_utils.tel.tel_test_utils import \ + wait_for_cell_data_connection_for_subscription +from acts.test_utils.tel.tel_test_utils import toggle_volte_for_subscription +from acts.test_utils.tel.tel_test_utils import toggle_wfc_for_subscription +from acts.test_utils.tel.tel_test_utils import set_wfc_mode_for_subscription +from acts.test_utils.tel.tel_test_utils import \ + sms_send_receive_verify_for_subscription +from acts.test_utils.tel.tel_test_utils import mms_send_receive_verify +from acts.test_utils.tel.tel_test_utils import verify_http_connection +from acts.test_utils.tel.tel_test_utils import verify_internet_connection +from acts.test_utils.tel.tel_test_utils import log_messaging_screen_shot +from acts.test_utils.tel.tel_test_utils import ensure_phones_idle +from acts.test_utils.tel.tel_test_utils import get_slot_index_from_subid +from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode +from acts.test_utils.tel.tel_test_utils import is_volte_enabled +from acts.test_utils.tel.tel_test_utils import check_is_wifi_connected +from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected +from acts.test_utils.tel.tel_test_utils import wait_for_wfc_enabled +from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_3g +from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb +from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_volte +from acts.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan +from acts.test_utils.tel.tel_voice_utils import phone_setup_iwlan_for_subscription +from acts.test_utils.tel.tel_voice_utils import \ + phone_setup_csfb_for_subscription +from acts.test_utils.tel.tel_voice_utils import \ + phone_setup_voice_3g_for_subscription +from acts.test_utils.tel.tel_voice_utils import \ + phone_setup_voice_general_for_subscription +from acts.test_utils.tel.tel_voice_utils import \ + phone_setup_volte_for_subscription +from acts.test_utils.tel.tel_voice_utils import two_phone_call_msim_for_slot +from acts.utils import rand_ascii_str + +CallResult = TelephonyVoiceTestResult.CallResult.Value + +class TelLiveGFTDSDSDDSSwitchTest(TelephonyBaseTest): + def setup_class(self): + TelephonyBaseTest.setup_class(self) + self.message_lengths = (50, 160, 180) + self.tel_logger = TelephonyMetricLogger.for_test_case() + + def teardown_test(self): + ensure_phones_idle(self.log, self.android_devices) + + def _msim_message_test( + self, + ad_mo, + ad_mt, + mo_sub_id, + mt_sub_id, msg="SMS", + max_wait_time=MAX_WAIT_TIME_SMS_RECEIVE, + expected_result=True): + """Make MO/MT SMS/MMS at specific slot. + + Args: + ad_mo: Android object of the device sending SMS/MMS + ad_mt: Android object of the device receiving SMS/MMS + mo_sub_id: Sub ID of MO device + mt_sub_id: Sub ID of MT device + max_wait_time: Max wait time before SMS/MMS is received. + expected_result: True for successful sending/receiving and False on + the contrary + + Returns: + True if the result matches expected_result and False on the + contrary. + """ + + if msg == "SMS": + for length in self.message_lengths: + message_array = [rand_ascii_str(length)] + if not sms_send_receive_verify_for_subscription( + self.log, + ad_mo, + ad_mt, + mo_sub_id, + mt_sub_id, + message_array, + max_wait_time): + ad_mo.log.warning( + "%s of length %s test failed", msg, length) + return False + else: + ad_mo.log.info( + "%s of length %s test succeeded", msg, length) + self.log.info("%s test of length %s characters succeeded.", + msg, self.message_lengths) + + elif msg == "MMS": + for length in self.message_lengths: + message_array = [("Test Message", rand_ascii_str(length), None)] + + if not mms_send_receive_verify( + self.log, + ad_mo, + ad_mt, + message_array, + max_wait_time, + expected_result): + self.log.warning("%s of body length %s test failed", + msg, length) + return False + else: + self.log.info( + "%s of body length %s test succeeded", msg, length) + self.log.info("%s test of body lengths %s succeeded", + msg, self.message_lengths) + return True + + def _test_dds_switch_during_data_transfer( + self, + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction=None, + call_or_sms_or_mms="call", + streaming=True, + is_airplane_mode=False, + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED]): + """Switch DDS and make voice call (VoLTE/WFC/CS call)/SMS/MMS together + with Youtube playing after each DDS switch at specific slot in specific + RAT. + + Test step: + 1. Get sub ID of each slot of the primary device. + 2. Set up phones in desired RAT. + 3. Switch DDS to slot 0. + 4. Check HTTP connection after DDS switch. + 5. Play Youtube. + 6. Make voice call (VoLTE/WFC/CS call)/SMS/MMS + 7. Switch DDS to slot 1 and repeat step 4-6. + 8. Switch DDS to slot 0 again and repeat step 4-6. + + Args: + 1, slot_0_nw_gen: Network generation of slot 0 on the primary device + 2, slot_1_nw_gen: Network generation of slot 1 on the primary device + 3. call_slot: Slot for making voice call + 4. call_direction: "mo" or "mt" or None to stoping making call. + 5. call_or_sms_or_mms: Voice call or SMS or MMS + 6. streaming: True for playing Youtube after DDS switch and False on + the contrary. + 7. is_airplane_mode: True of False for WFC setup + 8. wfc_mode: Cellular preferred or Wi-Fi preferred. + + Returns: + True or False + """ + ad = self.android_devices[0] + slot_0_subid = get_subid_from_slot_index(self.log, ad, 0) + slot_1_subid = get_subid_from_slot_index(self.log, ad, 1) + + if slot_0_subid == INVALID_SUB_ID or slot_1_subid == INVALID_SUB_ID: + ad.log.error("Not all slots have valid sub ID.") + raise signals.TestFailure("Failed", + extras={"fail_reason": "Not all slots have valid sub ID"}) + + ad.log.info( + "Step 0: Set up phone in desired RAT (slot 0: %s, slot 1: %s)", + slot_0_nw_gen, slot_1_nw_gen) + if slot_0_nw_gen == "volte": + slot0_phone_setup_func = phone_setup_volte_for_subscription + is_slot0_in_call = is_phone_in_call_volte + elif slot_0_nw_gen == "csfb": + slot0_phone_setup_func = phone_setup_csfb_for_subscription + is_slot0_in_call = is_phone_in_call_csfb + elif slot_0_nw_gen == "3g": + slot0_phone_setup_func = phone_setup_voice_3g_for_subscription + is_slot0_in_call = is_phone_in_call_3g + elif slot_0_nw_gen == "wfc": + slot0_phone_setup_func = phone_setup_iwlan_for_subscription + is_slot0_in_call = is_phone_in_call_iwlan + else: + slot0_phone_setup_func = phone_setup_voice_general_for_subscription + is_slot0_in_call = None + + if slot_0_nw_gen == "wfc": + tasks = [(slot0_phone_setup_func,( + self.log, + ad, + slot_0_subid, + is_airplane_mode, + wfc_mode[0], + self.wifi_network_ssid, + self.wifi_network_pass))] + else: + tasks = [(slot0_phone_setup_func, (self.log, ad, slot_0_subid))] + if not multithread_func(self.log, tasks): + self.log.error("Phone Failed to Set Up Properly.") + self.tel_logger.set_result(CallResult("CALL_SETUP_FAILURE")) + raise signals.TestFailure("Failed", + extras={"fail_reason": "Phone Failed to Set Up Properly."}) + + if slot_1_nw_gen == "volte": + slot1_phone_setup_func = phone_setup_volte_for_subscription + is_slot1_in_call = is_phone_in_call_volte + elif slot_1_nw_gen == "csfb": + slot1_phone_setup_func = phone_setup_csfb_for_subscription + is_slot1_in_call = is_phone_in_call_csfb + elif slot_1_nw_gen == "3g": + slot1_phone_setup_func = phone_setup_voice_3g_for_subscription + is_slot1_in_call = is_phone_in_call_3g + elif slot_1_nw_gen == "wfc": + slot1_phone_setup_func = phone_setup_iwlan_for_subscription + is_slot1_in_call = is_phone_in_call_iwlan + else: + slot1_phone_setup_func = phone_setup_voice_general_for_subscription + is_slot1_in_call = None + + if slot_1_nw_gen == "wfc": + tasks = [(slot1_phone_setup_func, ( + self.log, + ad, + slot_1_subid, + is_airplane_mode, + wfc_mode[1], + self.wifi_network_ssid, + self.wifi_network_pass))] + else: + tasks = [(slot1_phone_setup_func, (self.log, ad, slot_1_subid))] + if not multithread_func(self.log, tasks): + self.log.error("Phone Failed to Set Up Properly.") + self.tel_logger.set_result(CallResult("CALL_SETUP_FAILURE")) + raise signals.TestFailure("Failed", + extras={"fail_reason": "Phone Failed to Set Up Properly."}) + + for attempt in range(3): + if attempt != 0: + ad.log.info("Repeat step 1 to 4.") + + ad.log.info("Step 1: Switch DDS.") + if attempt % 2 == 0: + set_dds_on_slot_0(ad) + else: + set_dds_on_slot_1(ad) + + ad.log.info("Step 2: Check HTTP connection after DDS switch.") + if not verify_http_connection(self.log, ad): + ad.log.error("Failed to verify http connection.") + return False + else: + ad.log.info("Verify http connection successfully.") + + if streaming: + ad.log.info("Step 3: Start Youtube streaming.") + if not start_youtube_video(ad): + ad.log.warning("Fail to bring up youtube video") + time.sleep(10) + else: + ad.log.info("Step 3: Skip Youtube streaming.") + + if not call_direction: + return True + else: + expected_result = True + if call_direction == "mo": + ad_mo = self.android_devices[0] + ad_mt = self.android_devices[1] + mo_sub_id = get_subid_from_slot_index(self.log, ad, call_slot) + if call_or_sms_or_mms == "call": + set_incoming_voice_sub_id(ad_mo, mo_sub_id) + _, mt_sub_id, _ = get_subid_on_same_network_of_host_ad( + self.android_devices) + + if call_slot == 0: + is_mo_in_call = is_slot0_in_call + elif call_slot == 1: + is_mo_in_call = is_slot1_in_call + is_mt_in_call = None + + elif call_or_sms_or_mms == "sms": + set_subid_for_message(ad_mo, mo_sub_id) + _, mt_sub_id, _ = get_subid_on_same_network_of_host_ad( + self.android_devices, type="sms") + set_subid_for_message(ad_mt, mt_sub_id) + + elif call_or_sms_or_mms == "mms": + current_data_sub_id = get_default_data_sub_id(ad_mo) + if mo_sub_id != current_data_sub_id: + ad_mo.log.warning( + "Current data sub ID (%s) does not match" + " message sub ID (%s). MMS should NOT be sent.", + current_data_sub_id, mo_sub_id) + expected_result = False + set_subid_for_message(ad_mo, mo_sub_id) + _, mt_sub_id, _ = get_subid_on_same_network_of_host_ad( + self.android_devices, type="sms") + set_subid_for_message(ad_mt, mt_sub_id) + set_subid_for_data(ad_mt, mt_sub_id) + ad_mt.droid.telephonyToggleDataConnection(True) + + elif call_direction == "mt": + ad_mo = self.android_devices[1] + ad_mt = self.android_devices[0] + mt_sub_id = get_subid_from_slot_index(self.log, ad, call_slot) + if call_or_sms_or_mms == "call": + set_incoming_voice_sub_id(ad_mt, mt_sub_id) + _, mo_sub_id, _ = get_subid_on_same_network_of_host_ad( + self.android_devices) + + if call_slot == 0: + is_mt_in_call = is_slot0_in_call + elif call_slot == 1: + is_mt_in_call = is_slot1_in_call + is_mo_in_call = None + + elif call_or_sms_or_mms == "sms": + set_subid_for_message(ad_mt, mt_sub_id) + _, mo_sub_id, _ = get_subid_on_same_network_of_host_ad( + self.android_devices, type="sms") + set_subid_for_message(ad_mo, mo_sub_id) + + elif call_or_sms_or_mms == "mms": + current_data_sub_id = get_default_data_sub_id(ad_mt) + if mt_sub_id != current_data_sub_id: + ad_mt.log.warning( + "Current data sub ID (%s) does not match" + " message sub ID (%s). MMS should NOT be" + " received.", current_data_sub_id, mt_sub_id) + expected_result = False + set_subid_for_message(ad_mt, mt_sub_id) + _, mo_sub_id, _ = get_subid_on_same_network_of_host_ad( + self.android_devices, type="sms") + set_subid_for_message(ad_mo, mo_sub_id) + set_subid_for_data(ad_mo, mo_sub_id) + ad_mo.droid.telephonyToggleDataConnection(True) + + if call_or_sms_or_mms == "call": + self.log.info("Step 4: Make voice call.") + mo_slot = get_slot_index_from_subid( + self.log, ad_mo, mo_sub_id) + mt_slot = get_slot_index_from_subid( + self.log, ad_mt, mt_sub_id) + result = two_phone_call_msim_for_slot( + self.log, + ad_mo, + mo_slot, + None, + is_mo_in_call, + ad_mt, + mt_slot, + None, + is_mt_in_call) + self.tel_logger.set_result(result.result_value) + + if not result: + self.log.error( + "Failed to make MO call from %s slot %s to %s" + " slot %s", ad_mo.serial, mo_slot, ad_mt.serial, + mt_slot) + raise signals.TestFailure("Failed", + extras={"fail_reason": str(result.result_value)}) + else: + self.log.info("Step 4: Send %s.", call_or_sms_or_mms) + if call_or_sms_or_mms == "sms": + result = self._msim_message_test( + ad_mo, + ad_mt, + mo_sub_id, + mt_sub_id, + msg=call_or_sms_or_mms.upper()) + elif call_or_sms_or_mms == "mms": + result = self._msim_message_test( + ad_mo, + ad_mt, + mo_sub_id, + mt_sub_id, + msg=call_or_sms_or_mms.upper(), + expected_result=expected_result) + if not result: + log_messaging_screen_shot( + ad_mo, test_name="%s_tx" % call_or_sms_or_mms) + log_messaging_screen_shot( + ad_mt, test_name="%s_rx" % call_or_sms_or_mms) + + return False + return True + + def _test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + self, + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction=None, + streaming=True, + airplane_mode_cycling=False, + cellular_data_cycling=False, + wifi_cycling=False, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=False, + is_wifi_connected=False): + """Switch DDS and make VoLTE/WFC call together with Youtube playing + after each DDS switch at specific slot in specific RAT. + + Test step: + 1. Get sub ID of each slot of the primary device. + 2. Set up phones in desired RAT. + 3. Toggle on/off VoLTE/WFC and set WFC mode. + 4. Airplane mode or cellular data or Wi-Fi cycling. + 5. Switch DDS to slot 0. + 6. Check HTTP connection after DDS switch. + 7. Play Youtube. + 8. Make VoLTE or WFC call. + 9. Switch DDS to slot 1 and repeat step 6-8. + 10. Switch DDS to slot 0 again and repeat step 6-8. + + Args: + 1, slot_0_nw_gen: Network generation of slot 0 on the primary device + 2, slot_1_nw_gen: Network generation of slot 1 on the primary device + 3. call_slot: Slot for making voice call + 4. call_direction: "mo" or "mt" or None to stoping making call. + 5. streaming: True for playing Youtube after DDS switch and False on + the contrary. + 6. airplane_mode_cycling: True for cycling airplane + 7. cellular_data_cycling: True for cycling cellular data + 8. wifi_cycling: True for cycling Wi-Fi + 9. enable_volte: True for enabling and False for disabling VoLTE for + each slot on the primary device + 10. enable_wfc: True for enabling and False for disabling WFC for + each slot on the primary device + 11. wfc_mode: Cellular preferred or Wi-Fi preferred. + 12. is_airplane_mode: True of False for WFC setup + + Returns: + True or False + """ + ad = self.android_devices[0] + slot_0_subid = get_subid_from_slot_index(self.log, ad, 0) + slot_1_subid = get_subid_from_slot_index(self.log, ad, 1) + + if slot_0_subid == INVALID_SUB_ID or slot_1_subid == INVALID_SUB_ID: + ad.log.error("Not all slots have valid sub ID.") + raise signals.TestFailure("Failed", + extras={"fail_reason": "Not all slots have valid sub ID"}) + + ad.log.info( + "Step 0: Set up phone in desired RAT (slot 0: %s, slot 1: %s)", + slot_0_nw_gen, slot_1_nw_gen) + + if slot_0_nw_gen == "volte": + slot0_phone_setup_func = phone_setup_volte_for_subscription + is_slot0_in_call = is_phone_in_call_volte + elif slot_0_nw_gen == "wfc": + slot0_phone_setup_func = phone_setup_iwlan_for_subscription + is_slot0_in_call = is_phone_in_call_iwlan + else: + slot0_phone_setup_func = phone_setup_voice_general_for_subscription + is_slot0_in_call = None + + if slot_0_nw_gen == "wfc": + tasks = [(slot0_phone_setup_func, ( + self.log, + ad, + slot_0_subid, + is_airplane_mode, + wfc_mode[0], + self.wifi_network_ssid, + self.wifi_network_pass))] + else: + tasks = [(slot0_phone_setup_func, (self.log, ad, slot_0_subid))] + if not multithread_func(self.log, tasks): + self.log.error("Phone Failed to Set Up Properly.") + self.tel_logger.set_result(CallResult("CALL_SETUP_FAILURE")) + raise signals.TestFailure("Failed", + extras={"fail_reason": "Phone Failed to Set Up Properly."}) + + if slot_1_nw_gen == "volte": + slot1_phone_setup_func = phone_setup_volte_for_subscription + is_slot1_in_call = is_phone_in_call_volte + elif slot_1_nw_gen == "wfc": + slot1_phone_setup_func = phone_setup_iwlan_for_subscription + is_slot1_in_call = is_phone_in_call_iwlan + else: + slot1_phone_setup_func = phone_setup_voice_general_for_subscription + is_slot1_in_call = None + + if slot_1_nw_gen == "wfc": + tasks = [(slot1_phone_setup_func, ( + self.log, + ad, + slot_1_subid, + is_airplane_mode, + wfc_mode[1], + self.wifi_network_ssid, + self.wifi_network_pass))] + else: + tasks = [(slot1_phone_setup_func, (self.log, ad, slot_1_subid))] + if not multithread_func(self.log, tasks): + self.log.error("Phone Failed to Set Up Properly.") + self.tel_logger.set_result(CallResult("CALL_SETUP_FAILURE")) + raise signals.TestFailure("Failed", + extras={"fail_reason": "Phone Failed to Set Up Properly."}) + + if is_wifi_connected: + if not ensure_wifi_connected( + self.log, + ad, + self.wifi_network_ssid, + self.wifi_network_pass, + apm=False): + return False + time.sleep(5) + + ad.log.info("Step 1: Enable/disable VoLTE and WFC.") + for sub_id, volte in zip([slot_0_subid, slot_1_subid], enable_volte): + if not toggle_volte_for_subscription( + self.log, + ad, + sub_id, + new_state=volte): + return False + + for sub_id, wfc, mode in \ + zip([slot_0_subid, slot_1_subid], enable_wfc, wfc_mode): + if not toggle_wfc_for_subscription(self.log, ad, new_state=wfc, sub_id=sub_id): + return False + if not set_wfc_mode_for_subscription(ad, mode, sub_id=sub_id): + return False + + if airplane_mode_cycling: + ad.log.info("Step 2: Airplane mode cycling.") + ad.log.info("Step 2-1: Toggle on airplane mode.") + if not toggle_airplane_mode(self.log, ad, True): + ad.log.error("Failed to toggle on airplane mode.") + return False + time.sleep(5) + ad.log.info("Step 2-2: Toggle off airplane mode.") + if not toggle_airplane_mode(self.log, ad, False): + ad.log.error("Failed to toggle off airplane mode.") + return False + + if is_airplane_mode: + time.sleep(5) + ad.log.info("Step 2-3: Toggle on airplane mode again.") + if not toggle_airplane_mode(self.log, ad, True): + ad.log.error("Failed to toggle on airplane mode.") + return False + + if wfc_mode[0] or wfc_mode[1]: + time.sleep(5) + ad.log.info("Step 2-4: Toggle on Wi-Fi again.") + if not ensure_wifi_connected( + self.log, + ad, + self.wifi_network_ssid, + self.wifi_network_pass, + apm=is_airplane_mode): + return False + time.sleep(5) + + if cellular_data_cycling: + if call_slot == 0: + sub_id = slot_0_subid + elif call_slot == 1: + sub_id = slot_1_subid + ad.log.info("Step 2: Cellular data cycling") + ad.log.info("Step 2-1: Toggle off cellular data.") + ad.droid.telephonyToggleDataConnection(False) + if not check_is_wifi_connected( + self.log, + ad, + self.wifi_network_ssid): + if not wait_for_cell_data_connection_for_subscription( + self.log, ad, sub_id, False): + ad.log.error("Failed to disable cellular data") + return False + + if not verify_internet_connection( + self.log, + ad, + expected_state=False): + ad.log.error("Internet still accessible when cellular data" + " is disabled.") + return False + time.sleep(5) + ad.log.info("Step 2-2: Toggle on cellular data.") + ad.droid.telephonyToggleDataConnection(True) + if not check_is_wifi_connected( + self.log, + ad, + self.wifi_network_ssid): + if not wait_for_cell_data_connection_for_subscription( + self.log, ad, sub_id, True): + ad.log.error("Failed to enable cellular data") + return False + if not verify_internet_connection(self.log, ad, retries=3): + ad.log.error( + "Internet inaccessible when cellular data is enabled.") + return False + + if wifi_cycling: + ad.log.info("Step 2: Wi-Fi cycling") + ad.log.info("Step 2-1: Toggle on Wi-Fi.") + if not ensure_wifi_connected( + self.log, + ad, + self.wifi_network_ssid, + self.wifi_network_pass, + apm=is_airplane_mode): + return False + time.sleep(5) + ad.log.info("Step 2-2: Toggle off Wi-Fi.") + ad.droid.wifiToggleState(False) + time.sleep(5) + + if (call_slot == 0 and slot_0_nw_gen == "wfc") or \ + (call_slot == 1 and slot_1_nw_gen == "wfc") or is_wifi_connected: + if not ensure_wifi_connected( + self.log, + ad, + self.wifi_network_ssid, + self.wifi_network_pass, + apm=is_airplane_mode): + return False + + for attempt in range(3): + if attempt != 0: + ad.log.info("Repeat step 1 to 4.") + + ad.log.info("Step 3: Switch DDS.") + if attempt % 2 == 0: + set_dds_on_slot_0(ad) + else: + set_dds_on_slot_1(ad) + + ad.log.info("Step 4: Check HTTP connection after DDS switch.") + if not verify_http_connection(self.log, ad): + ad.log.error("Failed to verify http connection.") + return False + else: + ad.log.info("Verify http connection successfully.") + + if streaming: + ad.log.info("Step 5: Start Youtube streaming.") + if not start_youtube_video(ad): + ad.log.warning("Fail to bring up youtube video") + time.sleep(10) + else: + ad.log.info("Step 5: Skip Youtube streaming.") + + if not call_direction: + return True + else: + expected_result = True + if call_direction == "mo": + ad_mo = self.android_devices[0] + ad_mt = self.android_devices[1] + mo_sub_id = get_subid_from_slot_index(self.log, ad, call_slot) + + set_incoming_voice_sub_id(ad_mo, mo_sub_id) + _, mt_sub_id, _ = get_subid_on_same_network_of_host_ad( + self.android_devices) + + if call_slot == 0: + is_mo_in_call = is_slot0_in_call + elif call_slot == 1: + is_mo_in_call = is_slot1_in_call + is_mt_in_call = None + + elif call_direction == "mt": + ad_mo = self.android_devices[1] + ad_mt = self.android_devices[0] + mt_sub_id = get_subid_from_slot_index(self.log, ad, call_slot) + + set_incoming_voice_sub_id(ad_mt, mt_sub_id) + _, mo_sub_id, _ = get_subid_on_same_network_of_host_ad( + self.android_devices) + + if call_slot == 0: + is_mt_in_call = is_slot0_in_call + elif call_slot == 1: + is_mt_in_call = is_slot1_in_call + is_mo_in_call = None + + if (call_slot == 0 and slot_0_nw_gen == "wfc") or \ + (call_slot == 1 and slot_1_nw_gen == "wfc"): + if not wait_for_wfc_enabled(self.log, ad): + return False + + self.log.info("Step 6: Make voice call.") + mo_slot = get_slot_index_from_subid(self.log, ad_mo, mo_sub_id) + mt_slot = get_slot_index_from_subid(self.log, ad_mt, mt_sub_id) + result = two_phone_call_msim_for_slot( + self.log, + ad_mo, + mo_slot, + None, + is_mo_in_call, + ad_mt, + mt_slot, + None, + is_mt_in_call) + self.tel_logger.set_result(result.result_value) + + if not result: + self.log.error( + "Failed to make MO call from %s slot %s to %s slot %s", + ad_mo.serial, mo_slot, ad_mt.serial, mt_slot) + raise signals.TestFailure("Failed", + extras={"fail_reason": str(result.result_value)}) + + return True + + def _test_dds_switch_volte_cycling(self, slot=0): + """ VoLTE cycling after DDS switch. + + Test steps: + 1. Enable VoLTE. + 2. Disable VoLTE. + 3. Switch DDS to slot 0. + 4. Check HTTP connection after DDS switch. + 5. Enable VoLTE again. + 6. Check if IMS can be registered successfully and if VoLTE is + available. + 7. Repeat steps 2-6 for 2 times and each time DDS should be switched + to another slot. + + Args: + slot: slot to be tested + + Returns: + True or False + """ + ad = self.android_devices[0] + slot_0_subid = get_subid_from_slot_index(self.log, ad, 0) + slot_1_subid = get_subid_from_slot_index(self.log, ad, 1) + + if slot == 0: + sub_id = slot_0_subid + elif slot == 1: + sub_id = slot_1_subid + + ad.log.info("Step 1: Enable VoLTE for sub ID %s.", sub_id) + if not phone_setup_volte_for_subscription(self.log, ad, sub_id): + return False + + for attempt in range(3): + if attempt != 0: + ad.log.info("Repeat step 2 to 4.") + + ad.log.info("Step 2-1: Disable VoLTE for sub ID %s.", sub_id) + if not toggle_volte_for_subscription( + self.log, ad, sub_id, new_state=False): + return False + + ad.log.info( + "Step 2-2: Ensure VoLTE is disabled for sub ID %s.", sub_id) + if is_volte_enabled(self.log, ad, sub_id): + return False + + ad.log.info("Step 3: Switch DDS.") + if attempt % 2 == 0: + set_dds_on_slot_0(ad) + else: + set_dds_on_slot_1(ad) + + ad.log.info("Step 4: Check HTTP connection after DDS switch.") + if not verify_http_connection(self.log, ad): + ad.log.error("Failed to verify http connection.") + return False + else: + ad.log.info("Verify http connection successfully.") + + ad.log.info( + "Step 5: Enable VoLTE again after DDS switch for sub ID %s.", + sub_id) + if not phone_setup_volte_for_subscription(self.log, ad, sub_id): + return False + + return True + + @test_tracker_info(uuid="06908fb0-aaaa-4c95-b073-ea5ba8977050") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_when_volte_enabled(self): + ad = self.android_devices[0] + slot_0_subid = get_subid_from_slot_index(self.log, ad, 0) + slot_1_subid = get_subid_from_slot_index(self.log, ad, 1) + + ad.log.info("Step 0: Ensure VoLTE is enabled as initial condition.") + if not phone_setup_volte_for_subscription(self.log, ad, slot_0_subid): + return False + if not phone_setup_volte_for_subscription(self.log, ad, slot_1_subid): + return False + + for attempt in range(3): + ad.log.info("Step 1: Switch DDS.") + if attempt % 2 == 0: + set_dds_on_slot_0(ad) + else: + set_dds_on_slot_1(ad) + + ad.log.info("Step 2: Check HTTP connection after DDS switch.") + if not verify_http_connection(self.log, ad): + ad.log.error("Failed to verify http connection.") + return False + else: + ad.log.info("Verify http connection successfully.") + + ad.log.info("Step 3: Ensure VoLTE is still enabled after DDS" + " switch.") + if not phone_setup_volte_for_subscription( + self.log, ad, slot_0_subid): + return False + if not phone_setup_volte_for_subscription( + self.log, ad, slot_1_subid): + return False + return True + + @test_tracker_info(uuid="6b41d84c-4485-47b0-a5d8-eac16ed36258") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_and_reboot_when_volte_enabled(self): + ad = self.android_devices[0] + slot_0_subid = get_subid_from_slot_index(self.log, ad, 0) + slot_1_subid = get_subid_from_slot_index(self.log, ad, 1) + + ad.log.info("Step 0: Ensure VoLTE is enabled as initial condition.") + if not phone_setup_volte_for_subscription(self.log, ad, slot_0_subid): + return False + if not phone_setup_volte_for_subscription(self.log, ad, slot_1_subid): + return False + + for attempt in range(3): + ad.log.info("Step 1: Switch DDS.") + if attempt % 2 == 0: + set_dds_on_slot_0(ad) + else: + set_dds_on_slot_1(ad) + + ad.log.info("Step 2: Check HTTP connection after DDS switch.") + if not verify_http_connection(self.log, ad): + ad.log.error("Failed to verify http connection.") + return False + else: + ad.log.info("Verify http connection successfully.") + + ad.log.info("Step 3: Reboot.") + if not reboot_test(self.log, ad): + return False + + ad.log.info("Step 4: Ensure VoLTE is still enabled after DDS" + " switch.") + if not phone_setup_volte_for_subscription( + self.log, ad, slot_0_subid): + return False + if not phone_setup_volte_for_subscription( + self.log, ad, slot_1_subid): + return False + return True + + @test_tracker_info(uuid="bb440f33-ab0d-4999-885c-5c1f933430c4") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_when_volte_cycling_psim(self): + return self._test_dds_switch_volte_cycling(slot=0) + + @test_tracker_info(uuid="cbd5a4ae-be37-4435-b9db-fe58e8fdac5f") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_when_volte_cycling_esim(self): + return self._test_dds_switch_volte_cycling(slot=1) + + + @test_tracker_info(uuid="2df5faf9-8318-4acb-9068-e6ec0481c2ca") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_youtube_volte(self): + return self._test_dds_switch_during_data_transfer( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte") + + @test_tracker_info(uuid="eb73506e-c604-48df-be04-9b602a801afc") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_youtube_and_voice_call_mo_volte_psim(self): + return self._test_dds_switch_during_data_transfer( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mo") + + @test_tracker_info(uuid="2e2feab1-65a8-40a7-8666-0c46cb3411a4") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_youtube_and_voice_call_mo_volte_esim(self): + return self._test_dds_switch_during_data_transfer( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mo") + + @test_tracker_info(uuid="f2a7d62c-1f54-4081-b7bb-4782c5482b41") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_youtube_and_voice_call_mt_volte_psim(self): + return self._test_dds_switch_during_data_transfer( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mt") + + @test_tracker_info(uuid="df211078-b260-499d-8f7e-86cad039c5f5") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_youtube_and_voice_call_mt_volte_esim(self): + return self._test_dds_switch_during_data_transfer( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mt") + + @test_tracker_info(uuid="bd77782d-f43d-40c6-9982-47cd452d980f") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_youtube_and_voice_call_mo_csfb_psim(self): + return self._test_dds_switch_during_data_transfer( + slot_0_nw_gen="csfb", + slot_1_nw_gen="csfb", + call_slot=0, + call_direction="mo") + + @test_tracker_info(uuid="361a6f69-e6ea-4013-960d-732931fcd130") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_youtube_and_voice_call_mo_csfb_esim(self): + return self._test_dds_switch_during_data_transfer( + slot_0_nw_gen="csfb", + slot_1_nw_gen="csfb", + call_slot=1, + call_direction="mo") + + @test_tracker_info(uuid="26186d4f-0b0d-41c5-ab91-04e9851461f0") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_youtube_and_voice_call_mt_csfb_psim(self): + return self._test_dds_switch_during_data_transfer( + slot_0_nw_gen="csfb", + slot_1_nw_gen="csfb", + call_slot=0, + call_direction="mt") + + @test_tracker_info(uuid="7d31c644-a470-4eb9-b272-f0cfc34d23cb") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_youtube_and_voice_call_mt_csfb_esim(self): + return self._test_dds_switch_during_data_transfer( + slot_0_nw_gen="csfb", + slot_1_nw_gen="csfb", + call_slot=1, + call_direction="mt") + + @test_tracker_info(uuid="614076a6-b042-45f3-84fe-c84591e02f78") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_youtube_and_sms_volte(self): + result = True + self.log.info("Scenario 1: MO SMS at slot 0") + if not self._test_dds_switch_during_data_transfer( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mo", + call_or_sms_or_mms="sms"): + result = False + self.log.info("Scenario 2: MO SMS at slot 1") + if not self._test_dds_switch_during_data_transfer( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mo", + call_or_sms_or_mms="sms"): + result = False + self.log.info("Scenario 3: MT SMS at slot 0") + if not self._test_dds_switch_during_data_transfer( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mt", + call_or_sms_or_mms="sms"): + result = False + self.log.info("Scenario 4: MT SMS at slot 1") + if not self._test_dds_switch_during_data_transfer( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mt", + call_or_sms_or_mms="sms"): + result = False + return result + + @test_tracker_info(uuid="5e61f007-7b01-4dee-ac2d-fd2225ac3dbd") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_youtube_and_mms_volte(self): + result = True + self.log.info("Scenario 1: MO MMS at slot 0") + if not self._test_dds_switch_during_data_transfer( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mo", + call_or_sms_or_mms="mms"): + result = False + self.log.info("Scenario 2: MO MMS at slot 1") + if not self._test_dds_switch_during_data_transfer( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mo", + call_or_sms_or_mms="mms"): + result = False + self.log.info("Scenario 3: MT MMS at slot 0") + if not self._test_dds_switch_during_data_transfer( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mt", + call_or_sms_or_mms="mms"): + result = False + self.log.info("Scenario 4: MT MMS at slot 1") + if not self._test_dds_switch_during_data_transfer( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mt", + call_or_sms_or_mms="mms"): + result = False + return result + + @test_tracker_info(uuid="47b9bf08-2c17-4646-b1d3-3d191318bc0d") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_volte_psim(self): + return self._test_dds_switch_during_data_transfer( + "volte", "volte", 0, "mo", "call", False) + + @test_tracker_info(uuid="eef31675-f0a3-4086-8474-d67614ede507") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_volte_esim(self): + return self._test_dds_switch_during_data_transfer( + "volte", "volte", 1, "mo", "call", False) + + @test_tracker_info(uuid="ce8b6ce8-d314-49ca-bead-391c15809235") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_volte_psim(self): + return self._test_dds_switch_during_data_transfer( + "volte", "volte", 0, "mt", "call", False) + + @test_tracker_info(uuid="64c941e0-fcab-43ca-a988-f667398f1997") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_volte_esim(self): + return self._test_dds_switch_during_data_transfer( + "volte", "volte", 1, "mt", "call", False) + + @test_tracker_info(uuid="28963e86-f8ce-4324-8ce8-8e6628fd2d99") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_sms_volte(self): + result = True + self.log.info("Scenario 1: MO SMS at slot 0") + if not self._test_dds_switch_during_data_transfer( + "volte", "volte", 0, "mo", "sms", False): + result = False + self.log.info("Scenario 2: MO SMS at slot 1") + if not self._test_dds_switch_during_data_transfer( + "volte", "volte", 1, "mo", "sms", False): + result = False + self.log.info("Scenario 3: MT SMS at slot 0") + if not self._test_dds_switch_during_data_transfer( + "volte", "volte", 0, "mt", "sms", False): + result = False + self.log.info("Scenario 4: MT SMS at slot 1") + if not self._test_dds_switch_during_data_transfer( + "volte", "volte", 1, "mt", "sms", False): + result = False + return result + + @test_tracker_info(uuid="915c0eb3-1a84-4eb0-8cba-cafe32c0d604") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_mms_volte(self): + result = True + self.log.info("Scenario 1: MO MMS at slot 0") + if not self._test_dds_switch_during_data_transfer( + "volte", "volte", 0, "mo", "mms", False): + result = False + self.log.info("Scenario 2: MO MMS at slot 1") + if not self._test_dds_switch_during_data_transfer( + "volte", "volte", 1, "mo", "mms", False): + result = False + self.log.info("Scenario 3: MT MMS at slot 0") + if not self._test_dds_switch_during_data_transfer( + "volte", "volte", 0, "mt", "mms", False): + result = False + self.log.info("Scenario 4: MT MMS at slot 1") + if not self._test_dds_switch_during_data_transfer( + "volte", "volte", 1, "mt", "mms", False): + result = False + return result + + @test_tracker_info(uuid="d58939c0-d246-453e-9eac-54152d6dc70c") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_volte_psim_with_wfc_on_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mo", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED]) + + @test_tracker_info(uuid="61dfb957-6349-4190-8e63-973558b1292b") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_volte_esim_with_wfc_on_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mo", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED]) + + @test_tracker_info(uuid="127377f2-973f-4758-b138-4c0dd276f893") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_volte_psim_with_wfc_on_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mt", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED]) + + @test_tracker_info(uuid="a149d357-27a7-490d-a30b-70f44cd43ac7") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_volte_esim_with_wfc_on_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mt", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED]) + + @test_tracker_info(uuid="8823e87c-0e4d-435d-a17f-84e1b65c1012") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_volte_psim_with_wfc_on_wifi_on_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mo", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_wifi_connected=True) + + @test_tracker_info(uuid="bd038a77-baa6-483d-9af0-fe18d50d7f1f") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_volte_esim_with_wfc_on_wifi_on_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mo", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_wifi_connected=True) + + @test_tracker_info(uuid="3f96fb8a-d4ee-49b8-8958-45cd509ed7b8") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_volte_psim_with_wfc_on_wifi_on_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mt", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_wifi_connected=True) + + @test_tracker_info(uuid="1f89da8b-e559-4e96-afc7-0d2127616c56") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_volte_esim_with_wfc_on_wifi_on_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mt", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_wifi_connected=True) + + @test_tracker_info(uuid="bc522b4d-2d26-4b5f-b82c-f92356f103d0") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_psim_cellular_preferred_apm_on_with_volte_on_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mo", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="970ccdb4-c7b3-4b56-b93b-f811407c82cb") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_esim_cellular_preferred_apm_on_with_volte_on_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mo", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="26c8b63e-631c-42d0-868b-03c2db6181b7") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_psim_wifi_preferred_apm_off_with_volte_on_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mo", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="efd73091-8667-42a3-8551-eafa497fc383") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_esim_wifi_preferred_apm_off_with_volte_on_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mo", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="618768b9-83c2-4ab7-b1fb-10a4037c5834") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_wfc_psim_cellular_preferred_apm_on_with_volte_on_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mt", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="24d695a1-7421-4038-bb07-4d81f3f6d05b") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_wfc_esim_cellular_preferred_apm_on_with_volte_on_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mt", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="2f28db8f-c0c3-4cf6-9f6f-439c9e32d9f3") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_wfc_psim_wifi_preferred_apm_off_with_volte_on_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mt", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="60d851c5-f79d-46e7-b921-b510bcdc9024") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_wfc_esim_wifi_preferred_apm_off_with_volte_on_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mt", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="092b1e43-3de4-4b08-b526-4f3f1e71a47a") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_volte_psim_with_wfc_on_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mo", + streaming=True, + cellular_data_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED]) + + @test_tracker_info(uuid="3b876b39-1cfb-4adb-a45c-11a02890f8e1") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_volte_esim_with_wfc_on_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mo", + streaming=True, + cellular_data_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED]) + + @test_tracker_info(uuid="96c42ffb-5b4e-4ab0-b52a-8b498a25f759") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_volte_psim_with_wfc_on_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mt", + streaming=True, + cellular_data_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED]) + + @test_tracker_info(uuid="fbd4c30c-fef9-4100-b704-eb27d3bcb7ae") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_volte_esim_with_wfc_on_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mt", + streaming=True, + cellular_data_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED]) + + @test_tracker_info(uuid="fbd88b9d-e27b-4c06-a983-a780d0c00623") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_volte_psim_with_wfc_on_wifi_on_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mo", + streaming=True, + cellular_data_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_wifi_connected=True) + + @test_tracker_info(uuid="0fabca6f-28c4-4843-af70-f33d806d8dc1") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_volte_esim_with_wfc_on_wifi_on_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mo", + streaming=True, + cellular_data_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_wifi_connected=True) + + @test_tracker_info(uuid="9b060264-69d8-437e-9981-b86e213952c5") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_volte_psim_with_wfc_on_wifi_on_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mt", + streaming=True, + cellular_data_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_wifi_connected=True) + + @test_tracker_info(uuid="3a2effa4-728a-4160-9e0c-2aeafc7ba153") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_volte_esim_with_wfc_on_wifi_on_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mt", + streaming=True, + cellular_data_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_wifi_connected=True) + + @test_tracker_info(uuid="ad491362-8bcc-4097-84af-932878002ce6") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_psim_cellular_preferred_apm_on_with_volte_on_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mo", + streaming=True, + cellular_data_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="c137fe4e-380b-4dc5-8996-c8c5654596f7") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_esim_cellular_preferred_apm_on_with_volte_on_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mo", + streaming=True, + cellular_data_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="e7936ce8-1652-4b21-b3f0-5327084b823c") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_psim_wifi_preferred_apm_off_with_volte_on_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mo", + streaming=True, + cellular_data_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="86db06b4-907f-4085-af8e-75c983831bb0") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_esim_wifi_preferred_apm_off_with_volte_on_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mo", + streaming=True, + cellular_data_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="e43b23b5-022a-4830-9110-839ece333f6f") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_wfc_psim_cellular_preferred_apm_on_with_volte_on_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mt", + streaming=True, + cellular_data_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="00d0bfc2-2121-4ba9-9dd7-72bf78380121") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_wfc_esim_cellular_preferred_apm_on_with_volte_on_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mt", + streaming=True, + cellular_data_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="4921a948-54d4-4945-81ea-02893d10b6e6") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_wfc_psim_wifi_preferred_apm_off_with_volte_on_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mt", + streaming=True, + cellular_data_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="ed4b8ba4-1b31-4e3c-9be3-0e184e324523") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_wfc_esim_wifi_preferred_apm_off_with_volte_on_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mt", + streaming=True, + cellular_data_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="1fb43960-51dd-4be9-adf1-51c84cb8d85a") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_volte_psim_with_wfc_on_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mo", + streaming=True, + wifi_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED]) + + @test_tracker_info(uuid="1052e02f-5a4b-4826-9c47-9ab6d142f300") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_volte_esim_with_wfc_on_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mo", + streaming=True, + wifi_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED]) + + @test_tracker_info(uuid="23bb1991-6ff1-4528-aeee-1ec0c7b525be") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_volte_psim_with_wfc_on_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mt", + streaming=True, + wifi_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED]) + + @test_tracker_info(uuid="1d5842c5-91f5-4c87-9f65-67891d255d43") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_volte_esim_with_wfc_on_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mt", + streaming=True, + wifi_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED]) + + @test_tracker_info(uuid="380bd592-5437-4e16-9564-5f47b066cab2") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_volte_psim_with_wfc_on_wifi_on_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mo", + streaming=True, + wifi_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_wifi_connected=True) + + @test_tracker_info(uuid="90bb2647-71f1-44cd-bff3-5bbb720e59b7") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_volte_esim_with_wfc_on_wifi_on_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mo", + streaming=True, + wifi_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_wifi_connected=True) + + @test_tracker_info(uuid="5bca72c8-62d0-41bf-8888-310cd235dac4") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_volte_psim_with_wfc_on_wifi_on_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mt", + streaming=True, + wifi_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_wifi_connected=True) + + @test_tracker_info(uuid="13805ecf-3cf9-44c8-98bc-a099edb36340") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_volte_esim_with_wfc_on_wifi_on_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mt", + streaming=True, + wifi_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_wifi_connected=True) + + @test_tracker_info(uuid="33ed3dee-581f-4ae8-b236-1317377a6ca1") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_psim_cellular_preferred_apm_on_with_with_volte_on_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mo", + streaming=True, + wifi_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="88391458-6886-483f-a997-c62fd6dfd0b8") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_esim_cellular_preferred_apm_on_with_with_volte_on_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mo", + streaming=True, + wifi_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="966bcb75-dd2d-4400-a880-e7407989ee52") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_psim_wifi_preferred_apm_off_with_with_volte_on_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mo", + streaming=True, + wifi_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="7ff48189-5b4b-4b2d-a96a-fa66e86d0596") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_esim_wifi_preferred_apm_off_with_with_volte_on_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mo", + streaming=True, + wifi_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="ab503377-7150-4a6d-a7c1-b21009a69402") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_psim_wfc_cellular_preferred_apm_on_with_with_volte_on_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mt", + streaming=True, + wifi_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="7f02ee60-19e9-4602-8f6d-a13b976a6bba") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_esim_wfc_cellular_preferred_apm_on_with_with_volte_on_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mt", + streaming=True, + wifi_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="e93fa3ac-c5cd-4e21-b872-5172aa22d030") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_wfc_psim_wifi_preferred_apm_off_with_with_volte_on_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mt", + streaming=True, + wifi_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="c2af6998-f702-4e36-bbaa-f099a307b21a") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_wfc_esim_wifi_preferred_apm_off_with_with_volte_on_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mt", + streaming=True, + wifi_cycling=True, + enable_volte=[True, True], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="45ba6e90-bdaa-4dc0-a504-c596bafdfaad") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_volte_psim_with_wfc_off_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mo", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[True, True], + enable_wfc=[False, False], + wfc_mode=[WFC_MODE_DISABLED, WFC_MODE_DISABLED]) + + @test_tracker_info(uuid="1b573f40-3eaf-4149-baad-2e73e5bf15f4") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_volte_esim_with_wfc_off_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mo", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[True, True], + enable_wfc=[False, False], + wfc_mode=[WFC_MODE_DISABLED, WFC_MODE_DISABLED]) + + @test_tracker_info(uuid="13511fb6-2984-40c3-b1b9-22f27f241c07") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_volte_psim_with_wfc_off_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mt", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[True, True], + enable_wfc=[False, False], + wfc_mode=[WFC_MODE_DISABLED, WFC_MODE_DISABLED]) + + @test_tracker_info(uuid="61cf33d1-e1b2-427a-bb38-29a4c7566947") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_volte_esim_with_wfc_off_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mt", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[True, True], + enable_wfc=[False, False], + wfc_mode=[WFC_MODE_DISABLED, WFC_MODE_DISABLED]) + + @test_tracker_info(uuid="31a2a741-c825-46f8-8e0a-8487fab9940e") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_volte_psim_with_wfc_off_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mo", + streaming=True, + cellular_data_cycling=True, + enable_volte=[True, True], + enable_wfc=[False, False], + wfc_mode=[WFC_MODE_DISABLED, WFC_MODE_DISABLED]) + + @test_tracker_info(uuid="9fb2a85f-08b3-4d5d-9e03-3f7f67039148") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_volte_esim_with_wfc_off_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mo", + streaming=True, + cellular_data_cycling=True, + enable_volte=[True, True], + enable_wfc=[False, False], + wfc_mode=[WFC_MODE_DISABLED, WFC_MODE_DISABLED]) + + @test_tracker_info(uuid="30eba519-b349-4a75-9f31-3fea0d1a8447") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_volte_psim_with_wfc_off_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mt", + streaming=True, + cellular_data_cycling=True, + enable_volte=[True, True], + enable_wfc=[False, False], + wfc_mode=[WFC_MODE_DISABLED, WFC_MODE_DISABLED]) + + @test_tracker_info(uuid="37240938-3ce1-4ad2-b35a-a8862dc2c70f") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_volte_esim_with_wfc_off_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mt", + streaming=True, + cellular_data_cycling=True, + enable_volte=[True, True], + enable_wfc=[False, False], + wfc_mode=[WFC_MODE_DISABLED, WFC_MODE_DISABLED]) + + @test_tracker_info(uuid="a7321b9c-fb2c-4a03-9566-05e4244ae6fd") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_volte_psim_with_wfc_off_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mo", + streaming=True, + wifi_cycling=True, + enable_volte=[True, True], + enable_wfc=[False, False], + wfc_mode=[WFC_MODE_DISABLED, WFC_MODE_DISABLED]) + + @test_tracker_info(uuid="6de8d678-2f72-41ea-9ed9-47b27afee038") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_volte_esim_with_wfc_off_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mo", + streaming=True, + wifi_cycling=True, + enable_volte=[True, True], + enable_wfc=[False, False], + wfc_mode=[WFC_MODE_DISABLED, WFC_MODE_DISABLED]) + + @test_tracker_info(uuid="7bc16dcf-6dab-4eec-931d-9b342caa7a32") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_volte_psim_with_wfc_off_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=0, + call_direction="mt", + streaming=True, + wifi_cycling=True, + enable_volte=[True, True], + enable_wfc=[False, False], + wfc_mode=[WFC_MODE_DISABLED, WFC_MODE_DISABLED]) + + @test_tracker_info(uuid="9c13e51b-385d-4df6-90b7-33b5e185f225") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_volte_esim_with_wfc_off_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="volte", + slot_1_nw_gen="volte", + call_slot=1, + call_direction="mt", + streaming=True, + wifi_cycling=True, + enable_volte=[True, True], + enable_wfc=[False, False], + wfc_mode=[WFC_MODE_DISABLED, WFC_MODE_DISABLED]) + + @test_tracker_info(uuid="161341e7-5c2d-45f9-9b11-4f44d542cd01") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_psim_cellular_preferred_apm_on_with_volte_off_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mo", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="4d43f92f-562f-4bf1-bc25-71410f14425c") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_esim_cellular_preferred_apm_on_with_volte_off_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mo", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="fe83e92d-c554-4f81-a447-d58300426da7") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_psim_wifi_preferred_apm_off_with_volte_off_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mo", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="76ba6834-1523-4ce8-80b9-079f2428da67") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_esim_wifi_preferred_apm_off_with_volte_off_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mo", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="15289348-8e09-4997-b5a3-f66bb7e7dca1") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_wfc_psim_cellular_preferred_apm_on_with_volte_off_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mt", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="1ae4fa98-1ac3-4194-a483-097b7262415b") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_wfc_esim_cellular_preferred_apm_on_with_volte_off_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mt", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="13d0af2c-2870-4cbc-8a38-311f93cd4bd7") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_wfc_psim_wifi_preferred_apm_off_with_volte_off_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mt", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="5fbe2002-a02f-4750-81e5-4a06d7b62740") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_wfc_esim_wifi_preferred_apm_off_with_volte_off_apm_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mt", + streaming=True, + airplane_mode_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="f8f523d2-e432-45d4-a850-469a22894bc7") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_psim_cellular_preferred_apm_on_with_volte_off_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mo", + streaming=True, + cellular_data_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="750a8690-f9dd-4779-b13f-4011f478f194") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_esim_cellular_preferred_apm_on_with_volte_off_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mo", + streaming=True, + cellular_data_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="9fcda7e5-1705-4bbe-8f18-f005437c71f2") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_psim_wifi_preferred_apm_off_with_volte_off_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mo", + streaming=True, + cellular_data_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="0e054729-945a-4f6f-ad29-4d832f0b11ed") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_esim_wifi_preferred_apm_off_with_volte_off_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mo", + streaming=True, + cellular_data_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="2e387739-cc4a-4d48-aa56-83bf621835b1") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_wfc_psim_cellular_preferred_apm_on_with_volte_off_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mt", + streaming=True, + cellular_data_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="a0e2deda-9148-4665-abc8-7665e3818d06") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_wfc_esim_cellular_preferred_apm_on_with_volte_off_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mt", + streaming=True, + cellular_data_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="6a43acef-aaa1-4fe8-ae7e-c8e045bf8814") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_wfc_psim_wifi_preferred_apm_off_with_volte_off_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mt", + streaming=True, + cellular_data_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="95524166-212f-4e82-9e02-2f9b58d92a9f") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_wfc_esim_wifi_preferred_apm_off_with_volte_off_cellular_data_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mt", + streaming=True, + cellular_data_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="828ae64c-41b3-4974-a412-342d3ca16ce3") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_psim_cellular_preferred_apm_on_with_volte_off_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mo", + streaming=True, + wifi_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="343cf9dc-4f4c-4c0c-bd7b-ba664381f6bd") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_esim_cellular_preferred_apm_on_with_volte_off_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mo", + streaming=True, + wifi_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="6ef18605-bf4c-43d8-83fd-0bf71311973e") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_psim_wifi_preferred_apm_off_with_volte_off_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mo", + streaming=True, + wifi_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="9bddfe69-68e1-4d04-aeaa-75fb0f9ed9aa") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mo_wfc_esim_wifi_preferred_apm_off_with_volte_off_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mo", + streaming=True, + wifi_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="81e7d2b6-e3eb-4651-807f-66bf8eeeea93") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_wfc_psim_cellular_preferred_apm_on_with_volte_off_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mt", + streaming=True, + wifi_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="8c6da88b-be3a-4c0c-a239-255faf03a28b") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_wfc_esim_cellular_preferred_apm_on_with_volte_off_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mt", + streaming=True, + wifi_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_CELLULAR_PREFERRED, WFC_MODE_CELLULAR_PREFERRED], + is_airplane_mode=True) + + @test_tracker_info(uuid="1b9ef6b4-c0c0-4375-b1a5-d569b946491e") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_wfc_psim_wifi_preferred_apm_off_with_volte_off_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=0, + call_direction="mt", + streaming=True, + wifi_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False) + + @test_tracker_info(uuid="5771fedb-5eed-4868-84a3-0d7a01474dcf") + @TelephonyBaseTest.tel_test_wrap + def test_dds_switch_voice_call_mt_wfc_esim_wifi_preferred_apm_off_with_volte_off_wifi_cycling(self): + return self._test_dds_switch_during_data_transfer_with_apm_cycling_and_ims_setting( + slot_0_nw_gen="wfc", + slot_1_nw_gen="wfc", + call_slot=1, + call_direction="mt", + streaming=True, + wifi_cycling=True, + enable_volte=[False, False], + enable_wfc=[True, True], + wfc_mode=[WFC_MODE_WIFI_PREFERRED, WFC_MODE_WIFI_PREFERRED], + is_airplane_mode=False)
\ No newline at end of file diff --git a/acts_tests/tests/google/tel/live/TelLiveRebootStressTest.py b/acts_tests/tests/google/tel/live/TelLiveRebootStressTest.py index 3b6482af5a..1fd39eb8c7 100644 --- a/acts_tests/tests/google/tel/live/TelLiveRebootStressTest.py +++ b/acts_tests/tests/google/tel/live/TelLiveRebootStressTest.py @@ -123,6 +123,10 @@ class TelLiveRebootStressTest(TelephonyBaseTest): if not func(): self.log.error("%s failed", method) failed_tests.append(method) + self.log.info("Wait 5s before each function check.") + time.sleep(5) + self.log.info("Wait 30s before NW mode switch.") + time.sleep(30) for method in args: func = getattr(self, method) try: @@ -936,7 +940,7 @@ class TelLiveRebootStressTest(TelephonyBaseTest): Returns: True is pass, False if fail. """ - return self._crash_recovery_test("qtidataservice", + return self._crash_recovery_test(".qtidataservices", *self.default_testing_func_names) @test_tracker_info(uuid="fa34f994-bc49-4444-9187-87691c94b4f4") diff --git a/acts_tests/tests/google/tel/live/TelLiveSmsTest.py b/acts_tests/tests/google/tel/live/TelLiveSmsTest.py index 2529aea63e..43302ede0e 100644 --- a/acts_tests/tests/google/tel/live/TelLiveSmsTest.py +++ b/acts_tests/tests/google/tel/live/TelLiveSmsTest.py @@ -49,6 +49,7 @@ from acts.test_utils.tel.tel_test_utils import set_mobile_data_usage_limit from acts.test_utils.tel.tel_test_utils import setup_sim from acts.test_utils.tel.tel_test_utils import sms_send_receive_verify from acts.test_utils.tel.tel_test_utils import set_wfc_mode +from acts.test_utils.tel.tel_test_utils import toggle_airplane_mode from acts.test_utils.tel.tel_test_utils import \ sms_in_collision_send_receive_verify from acts.test_utils.tel.tel_test_utils import \ @@ -94,7 +95,7 @@ class TelLiveSmsTest(TelephonyBaseTest): is_roaming = False for ad in self.android_devices: ad.sms_over_wifi = False - #verizon supports sms over wifi. will add more carriers later + # verizon supports sms over wifi. will add more carriers later for sub in ad.telephony["subscription"].values(): if sub["operator"] in SMS_OVER_WIFI_PROVIDERS: ad.sms_over_wifi = True @@ -456,13 +457,13 @@ class TelLiveSmsTest(TelephonyBaseTest): message_array = [rand_ascii_str(length)] message_array2 = [rand_ascii_str(length)] if not sms_in_collision_send_receive_verify( - self.log, - ads[0], - ads[0], - ads[1], - ads[2], - message_array, - message_array2): + self.log, + ads[0], + ads[0], + ads[1], + ads[2], + message_array, + message_array2): ads[0].log.warning( "Test of SMS collision with length %s failed", length) return False @@ -477,14 +478,14 @@ class TelLiveSmsTest(TelephonyBaseTest): def _sms_in_collision_when_power_off_test(self, ads): for length in self.message_lengths: if not sms_rx_power_off_multiple_send_receive_verify( - self.log, - ads[0], - ads[1], - ads[2], - length, - length, - 5, - 5): + self.log, + ads[0], + ads[1], + ads[2], + length, + length, + 5, + 5): ads[0].log.warning( "Test of SMS collision when power off with length %s failed", length) @@ -614,6 +615,49 @@ class TelLiveSmsTest(TelephonyBaseTest): self.log.info("PASS - SMS test over 5G NSA validated") return True + @test_tracker_info(uuid="2bb5b60f-7269-44a4-9cbd-e1c7d00e53c9") + @TelephonyBaseTest.tel_test_wrap + def test_mms_mo_mt_5g_nsa(self): + """Test MMS basic function between two phones. Phones in 5G NSA. + + Airplane mode is off. + Send MMS from PhoneA to PhoneB. + Verify received message on PhoneB is correct. + + Returns: + True if success. + False if failed. + """ + ads = self.android_devices + + # Mode Pref + tasks = [(set_preferred_mode_for_5g, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Failed to set preferred network mode.") + return False + + # Attach 5g + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not on 5G NSA before sending MMS.") + return False + + if not self._mms_test_mo(ads): + self.log.error("Failed to send receive MMS over 5G NSA.") + return False + + # Attach 5g + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not on 5G NSA after sending MMS.") + return False + + self.log.info("PASS - MMS test over 5G NSA validated") + return True + @test_tracker_info(uuid="f2779e1e-7d09-43f0-8b5c-87eae5d146be") @TelephonyBaseTest.tel_test_wrap def test_mms_mt_general(self): @@ -661,7 +705,6 @@ class TelLiveSmsTest(TelephonyBaseTest): return self._sms_test_mo(ads) - @test_tracker_info(uuid="17fafc41-7e12-47ab-a4cc-fb9bd94e79b9") @TelephonyBaseTest.tel_test_wrap def test_sms_mt_2g(self): @@ -1037,6 +1080,106 @@ class TelLiveSmsTest(TelephonyBaseTest): return self._mms_test_mt(ads) + @test_tracker_info(uuid="2bef0029-bbed-423b-8745-f3f678a61fc8") + @TelephonyBaseTest.tel_test_wrap + def test_sms_mo_mt_volte_5g_nsa(self): + """Test SMS text function between two phones. Phones with VoLTE in 5G NSA network. + + Airplane mode is off. VoLTE is enabled on Phones. + Send SMS from PhoneA to PhoneB. + Verify received message on PhoneB is correct. + + Returns: + True if success. + False if failed. + """ + + ads = self.android_devices + # LTE attach and enable VoLTE on both phones + tasks = [(phone_setup_volte, (self.log, ads[0])), + (phone_setup_volte, (self.log, ads[1]))] + if not multithread_func(self.log, tasks): + self.log.error("Phone Failed to Set Up in VoLTE.") + return False + + # Mode Pref + tasks = [(set_preferred_mode_for_5g, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Failed to set preferred network mode.") + return False + + # Attach 5g + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not attached on 5G NSA before call.") + return False + + if not self._sms_test_mo(ads): + self.log.error("Failed to send receive SMS over 5G NSA.") + return False + + # Attach 5g + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not attached on 5G NSA after call end.") + return False + + self.log.info("PASS - VoLTE SMS test over 5G NSA validated") + return True + + @test_tracker_info(uuid="11960768-2e8f-4306-9698-150d5050f961") + @TelephonyBaseTest.tel_test_wrap + def test_mms_mo_mt_volte_5g_nsa(self): + """Test MMS text function between two phones. Phones with VoLTE in 5G NSA network. + + Airplane mode is off. VoLTE is enabled on Phones. + Send MMS from PhoneA to PhoneB. + Verify received message on PhoneB is correct. + + Returns: + True if success. + False if failed. + """ + + ads = self.android_devices + # LTE attach and enable VoLTE on both phones + tasks = [(phone_setup_volte, (self.log, ads[0])), + (phone_setup_volte, (self.log, ads[1]))] + if not multithread_func(self.log, tasks): + self.log.error("Phone Failed to Set Up in VoLTE.") + return False + + # Mode Pref + tasks = [(set_preferred_mode_for_5g, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Failed to set preferred network mode.") + return False + + # Attach 5g + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not attached on 5G NSA before call.") + return False + + if not self._mms_test_mo(ads): + self.log.error("Failed to send receive MMS over 5G NSA.") + return False + + # Attach 5g + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not attached on 5G NSA after call end.") + return False + + self.log.info("PASS - VoLTE MMS test over 5G NSA validated") + return True + @test_tracker_info(uuid="54a68d6a-dae7-4fe6-b2bb-7c73151a4a73") @TelephonyBaseTest.tel_test_wrap def test_sms_mo_4g_volte(self): @@ -1558,6 +1701,65 @@ class TelLiveSmsTest(TelephonyBaseTest): return True + @test_tracker_info(uuid="fe862c8a-9fe0-4563-885e-9a819f5c8ba6") + @TelephonyBaseTest.tel_test_wrap + def test_sms_mo_mt_in_call_volte_5g_nsa(self): + """ Test MO SMS during a MO VoLTE call over 5G NSA. + + Make sure PhoneA/B are in 5G NSA (with VoLTE). + Make sure PhoneA/B is able to make/receive call. + Call from PhoneA to PhoneB, accept on PhoneB, send SMS on PhoneA. + Make sure PhoneA/B are in 5G NSA + + Returns: + True if pass; False if fail. + """ + ads = self.android_devices + + tasks = [(phone_setup_volte, (self.log, ads[0])), (phone_setup_volte, + (self.log, ads[1]))] + if not multithread_func(self.log, tasks): + self.log.error("Phone Failed to Set Up Properly.") + return False + time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING) + + # Mode Pref + tasks = [(set_preferred_mode_for_5g, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Failed to set preferred network mode.") + return False + + # Attach 5g + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not attached on 5G NSA before call.") + return False + + self.log.info("Begin In Call SMS Test.") + if not call_setup_teardown( + self.log, + ads[0], + ads[1], + ad_hangup=None, + verify_caller_func=is_phone_in_call_volte, + verify_callee_func=None): + return False + + if not self._sms_test_mo(ads): + self.log.error("SMS test fail.") + return False + + # Check if phoneA/B are attached to 5g after sending mms. + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not attached on 5G NSA after call.") + return False + + return True + @test_tracker_info(uuid="3bf8ff74-baa6-4dc6-86eb-c13816fa9bc8") @TelephonyBaseTest.tel_test_wrap def test_mms_mo_in_call_volte(self): @@ -1630,6 +1832,65 @@ class TelLiveSmsTest(TelephonyBaseTest): return True + @test_tracker_info(uuid="ffcb1afe-0118-40f8-9830-35847826d405") + @TelephonyBaseTest.tel_test_wrap + def test_mms_mo_mt_in_call_volte_5g_nsa(self): + """ Test MO MMS during a MO VoLTE call over 5G NSA. + + Make sure PhoneA/B are in 5G NSA (with VoLTE). + Make sure PhoneA/B is able to make/receive call. + Call from PhoneA to PhoneB, accept on PhoneB, send SMS on PhoneA. + Make sure PhoneA/B are in 5G NSA. + + Returns: + True if pass; False if fail. + """ + ads = self.android_devices + + tasks = [(phone_setup_volte, (self.log, ads[0])), (phone_setup_volte, + (self.log, ads[1]))] + if not multithread_func(self.log, tasks): + self.log.error("Phone Failed to Set Up Properly.") + return False + time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING) + + # Mode Pref + tasks = [(set_preferred_mode_for_5g, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Failed to set preferred network mode.") + return False + + # Attach 5g + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not attached on 5G NSA before call.") + return False + + self.log.info("Begin In Call MMS Test.") + if not call_setup_teardown( + self.log, + ads[0], + ads[1], + ad_hangup=None, + verify_caller_func=is_phone_in_call_volte, + verify_callee_func=None): + return False + + if not self._mms_test_mo(ads): + self.log.error("MMS test fail.") + return False + + # Check if phoneA/B are attached to 5g after sending mms. + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not attached on 5G NSA after call.") + return False + + return True + @test_tracker_info(uuid="5654d974-3c32-4cce-9d07-0c96213dacc5") @TelephonyBaseTest.tel_test_wrap def test_mms_mo_in_call_volte_wifi(self): @@ -1708,6 +1969,72 @@ class TelLiveSmsTest(TelephonyBaseTest): return True + @test_tracker_info(uuid="a5b95867-133f-4576-8a44-bd057ee0188f") + @TelephonyBaseTest.tel_test_wrap + def test_mms_mo_mt_in_call_volte_wifi_5g_nsa(self): + """ Test MO MMS during a MO VoLTE call over 5G NSA. + + Make sure PhoneA/B are in 5G NSA (with VoLTE). + Make sure PhoneA/B are able to make/receive call. + Connect PhoneA/B to Wifi. + Call from PhoneA to PhoneB, accept on PhoneB, send MMS on PhoneA. + Make sure PhoneA/B are in 5G NSA. + + Returns: + True if pass; False if fail. + """ + ads = self.android_devices + + tasks = [(phone_setup_volte, (self.log, ads[0])), (phone_setup_volte, + (self.log, ads[1]))] + if not multithread_func(self.log, tasks): + self.log.error("Phone Failed to Set Up Properly.") + return False + time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING) + + # Mode Pref + tasks = [(set_preferred_mode_for_5g, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Failed to set preferred network mode.") + return False + + # Attach 5g + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not attached on 5G NSA before call.") + return False + + tasks = [(ensure_wifi_connected, (self.log, ad, self.wifi_network_ssid, + self.wifi_network_pass)) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone Failed to connect to wifi.") + return False + self.log.info("Begin In Call MMS Test.") + if not call_setup_teardown( + self.log, + ads[0], + ads[1], + ad_hangup=None, + verify_caller_func=is_phone_in_call_volte, + verify_callee_func=None): + return False + + if not self._mms_test_mo(ads): + self.log.error("MMS test fail.") + return False + + # Check if phoneA/B are attached to 5g after sending mms. + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not attached on 5G NSA after call.") + return False + + return True + @test_tracker_info(uuid="b6e9ce80-8577-48e5-baa7-92780932f278") @TelephonyBaseTest.tel_test_wrap def test_sms_mo_in_call_csfb(self): @@ -1996,6 +2323,124 @@ class TelLiveSmsTest(TelephonyBaseTest): return self._mt_mms_in_3g_call(ads, wifi=True) + @test_tracker_info(uuid="f23cd374-1502-4bcb-a636-d2a873499e17") + @TelephonyBaseTest.tel_test_wrap + def test_sms_mo_mt_iwlan_5g_nsa(self): + """ Test SMS text function between two phones, Phones in APM, WiFi connected, WFC Cell Preferred mode. + + Make sure airplane mode is off + Set Phones are operated at 5G NSA + Make sure Phones are operated at 5G NSA + Make sure PhoneA/B APM, WiFi connected, WFC wifi preferred mode. + Make sure PhoneA/B report iwlan as data rat. + Make sure PhoneB is able to receive sms. + Send SMS on PhoneA. + + Returns: + True if pass; False if fail. + """ + + ads = self.android_devices + + # Turn off airplane mode to ensure attaching to 5g nsa is working + self.log.info("Turn off APM mode before starting testing.") + tasks = [(toggle_airplane_mode, (self.log, ads[0], False)), + (toggle_airplane_mode, (self.log, ads[1], False))] + if not multithread_func(self.log, tasks): + self.log.error("Failed to turn off airplane mode") + return False + + # Mode Pref + tasks = [(set_preferred_mode_for_5g, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Failed to set preferred network mode.") + return False + + # Attach 5g + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not attached on 5G NSA before call.") + return False + + tasks = [(phone_setup_iwlan, + (self.log, ads[0], True, WFC_MODE_CELLULAR_PREFERRED, + self.wifi_network_ssid, self.wifi_network_pass)), + (phone_setup_iwlan, + (self.log, ads[1], True, WFC_MODE_CELLULAR_PREFERRED, + self.wifi_network_ssid, self.wifi_network_pass))] + if not multithread_func(self.log, tasks): + self.log.error("Phone Failed to Set Up Properly.") + return False + time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING) + + if not self._sms_test_mo(ads): + self.log.error("Failed to send receive SMS over 5G NSA.") + return False + + self.log.info("PASS - iwlan SMS test over 5G NSA validated") + return True + + @test_tracker_info(uuid="3ecf3fb0-0c76-4ff7-b91b-842de97bdc50") + @TelephonyBaseTest.tel_test_wrap + def test_mms_mo_mt_iwlan_5g_nsa(self): + """ Test MMS text function between two phones, Phones in APM, WiFi connected, WFC Cell Preferred mode. + + Make sure airplane mode is off + Set Phones are operated at 5G NSA + Make sure Phones are operated at 5G NSA + Make sure PhoneA/B APM, WiFi connected, WFC wifi preferred mode. + Make sure PhoneA/B report iwlan as data rat. + Make sure PhoneB is able to receive sms. + Send MMS on PhoneA. + + Returns: + True if pass; False if fail. + """ + + ads = self.android_devices + + # Turn off airplane mode to ensure attaching to 5g nsa is working + self.log.info("Turn off APM mode before starting testing.") + tasks = [(toggle_airplane_mode, (self.log, ads[0], False)), + (toggle_airplane_mode, (self.log, ads[1], False))] + if not multithread_func(self.log, tasks): + self.log.error("Failed to turn off airplane mode") + return False + + # Mode Pref + tasks = [(set_preferred_mode_for_5g, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Failed to set preferred network mode.") + return False + + # Attach 5g + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not attached on 5G NSA before call.") + return False + + tasks = [(phone_setup_iwlan, + (self.log, ads[0], True, WFC_MODE_CELLULAR_PREFERRED, + self.wifi_network_ssid, self.wifi_network_pass)), + (phone_setup_iwlan, + (self.log, ads[1], True, WFC_MODE_CELLULAR_PREFERRED, + self.wifi_network_ssid, self.wifi_network_pass))] + if not multithread_func(self.log, tasks): + self.log.error("Phone Failed to Set Up Properly.") + return False + time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING) + + if not self._mms_test_mo(ads): + self.log.error("Failed to send receive MMS over 5G NSA.") + return False + + self.log.info("PASS - iwlan MMS test over 5G NSA validated") + return True + @test_tracker_info(uuid="ed720013-e366-448b-8901-bb09d26cea05") @TelephonyBaseTest.tel_test_wrap def test_sms_mo_iwlan(self): @@ -2320,6 +2765,142 @@ class TelLiveSmsTest(TelephonyBaseTest): return self._mms_test_mt(ads) + @test_tracker_info(uuid="68366dd1-d544-43ff-9023-c300b8a19bdd") + @TelephonyBaseTest.tel_test_wrap + def test_sms_mo_mt_iwlan_apm_off_5g_nsa(self): + """ Test MO SMS, Phone in APM off, WiFi connected, WFC WiFi Preferred mode. + + Turn off APM always + Set PhoneA/B are operated at 5G NSA + Make sure PhoneA/B are operated at 5G NSA + Make sure PhoneA/B APM off, WiFi connected, WFC WiFi preferred mode. + Make sure PhoneA/B report iwlan as data rat. + Make sure PhoneA/B is able to make/receive call/sms. + Send SMS on PhoneA. + Make sure PhoneA/B are operated at 5G NSA + + Returns: + True if pass; False if fail. + """ + + ads = self.android_devices + + # Turn off airplane mode + self.log.info("Turn off APM mode before starting testing.") + tasks = [(toggle_airplane_mode, (self.log, ads[0], False)), + (toggle_airplane_mode, (self.log, ads[1], False))] + if not multithread_func(self.log, tasks): + self.log.error("Failed to turn off airplane mode") + return False + + # Mode Pref + tasks = [(set_preferred_mode_for_5g, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Failed to set preferred network mode.") + return False + + # Attach 5g + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not attached on 5G NSA before sending SMS.") + return False + + tasks = [(phone_setup_iwlan, + (self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED, + self.wifi_network_ssid, self.wifi_network_pass)), + (phone_setup_iwlan, + (self.log, ads[1], False, WFC_MODE_WIFI_PREFERRED, + self.wifi_network_ssid, self.wifi_network_pass))] + if not multithread_func(self.log, tasks): + self.log.error("Phone Failed to Set Up Properly.") + return False + time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING) + + if not self._sms_test_mo(ads): + self.log.error("Failed to send receive SMS over 5G NSA.") + return False + + self.log.info("PASS - iwlan SMS test over 5G NSA validated") + + # Attach 5g + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not attached on 5G NSA after sending SMS.") + return False + + return True + + @test_tracker_info(uuid="ab393c17-f1b0-47db-84c3-69a0ebdc9fba") + @TelephonyBaseTest.tel_test_wrap + def test_mms_mo_mt_iwlan_apm_off_5g_nsa(self): + """ Test MO MMS, Phone in APM off, WiFi connected, WFC WiFi Preferred mode. + + Turn off APM always + Set PhoneA/B are operated at 5G NSA + Make sure PhoneA/B are operated at 5G NSA + Make sure PhoneA/B APM off, WiFi connected, WFC WiFi preferred mode. + Make sure PhoneA/B report iwlan as data rat. + Make sure PhoneA/B is able to make/receive call/mms. + Send MMS on PhoneA. + Make sure PhoneA/B are operated at 5G NSA + + Returns: + True if pass; False if fail. + """ + + ads = self.android_devices + + # Turn off airplane mode + self.log.info("Turn off APM mode before starting testing.") + tasks = [(toggle_airplane_mode, (self.log, ads[0], False)), + (toggle_airplane_mode, (self.log, ads[1], False))] + if not multithread_func(self.log, tasks): + self.log.error("Failed to turn off airplane mode") + return False + + # Mode Pref + tasks = [(set_preferred_mode_for_5g, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Failed to set preferred network mode.") + return False + + # Attach 5g + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not attached on 5G NSA before sending MMS.") + return False + + tasks = [(phone_setup_iwlan, + (self.log, ads[0], False, WFC_MODE_WIFI_PREFERRED, + self.wifi_network_ssid, self.wifi_network_pass)), + (phone_setup_iwlan, + (self.log, ads[1], False, WFC_MODE_WIFI_PREFERRED, + self.wifi_network_ssid, self.wifi_network_pass))] + if not multithread_func(self.log, tasks): + self.log.error("Phone Failed to Set Up Properly.") + return False + time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING) + + if not self._mms_test_mo(ads): + self.log.error("Failed to send receive MMS over 5G NSA.") + return False + + self.log.info("PASS - iwlan MMS test over 5G NSA validated") + + # Attach 5g + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not attached on 5G NSA after sending MMS.") + return False + + return True + @test_tracker_info(uuid="075933a2-df7f-4374-a405-92f96bcc7770") @TelephonyBaseTest.tel_test_wrap def test_sms_mo_apm_wifi_wfc_off(self): @@ -2340,7 +2921,7 @@ class TelLiveSmsTest(TelephonyBaseTest): tasks = [(ensure_wifi_connected, (self.log, ads[0], self.wifi_network_ssid, self.wifi_network_pass, 3, True)), (phone_setup_voice_general, - (self.log, ads[1]))] + (self.log, ads[1]))] if not multithread_func(self.log, tasks): self.log.error("Phone Failed to Set Up Properly.") return False @@ -2368,7 +2949,7 @@ class TelLiveSmsTest(TelephonyBaseTest): tasks = [(ensure_wifi_connected, (self.log, ads[0], self.wifi_network_ssid, self.wifi_network_pass, 3, True)), (phone_setup_voice_general, - (self.log, ads[1]))] + (self.log, ads[1]))] if not multithread_func(self.log, tasks): self.log.error("Phone Failed to Set Up Properly.") return False @@ -2396,7 +2977,7 @@ class TelLiveSmsTest(TelephonyBaseTest): tasks = [(ensure_wifi_connected, (self.log, ads[0], self.wifi_network_ssid, self.wifi_network_pass, 3, True)), (phone_setup_voice_general, - (self.log, ads[1]))] + (self.log, ads[1]))] if not multithread_func(self.log, tasks): self.log.error("Phone Failed to Set Up Properly.") return False @@ -2424,7 +3005,7 @@ class TelLiveSmsTest(TelephonyBaseTest): tasks = [(ensure_wifi_connected, (self.log, ads[0], self.wifi_network_ssid, self.wifi_network_pass, 3, True)), (phone_setup_voice_general, - (self.log, ads[1]))] + (self.log, ads[1]))] if not multithread_func(self.log, tasks): self.log.error("Phone Failed to Set Up Properly.") return False @@ -2432,6 +3013,136 @@ class TelLiveSmsTest(TelephonyBaseTest): return self._mms_test_mt(ads) + @test_tracker_info(uuid="27db81bc-10bc-4c75-ad9e-60f746ee148b") + @TelephonyBaseTest.tel_test_wrap + def test_sms_mo_mt_in_call_iwlan_5g_nsa(self): + """ Test MO SMS, Phone in APM, WiFi connected, WFC WiFi Preferred mode. + + Turn off APM always + Set PhoneA/B are operated at 5G NSA + Make sure PhoneA/B are operated at 5G NSA + Make sure PhoneA/B APM, WiFi connected, WFC WiFi preferred mode. + Make sure PhoneA/B report iwlan as data rat. + Make sure PhoneA/B is able to make/receive call/sms. + Call from PhoneA to PhoneB, accept on PhoneB. + Send SMS on PhoneA. + + Returns: + True if pass; False if fail. + """ + + ads = self.android_devices + + # Turn off airplane mode + self.log.info("Turn off APM mode before starting testing.") + tasks = [(toggle_airplane_mode, (self.log, ads[0], False)), + (toggle_airplane_mode, (self.log, ads[1], False))] + if not multithread_func(self.log, tasks): + self.log.error("Failed to turn off airplane mode") + return False + + # Mode Pref + tasks = [(set_preferred_mode_for_5g, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Failed to set preferred network mode.") + return False + + # Attach 5g + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not attached on 5G NSA before sending SMS.") + return False + + tasks = [(phone_setup_iwlan, + (self.log, ads[0], True, WFC_MODE_WIFI_PREFERRED, + self.wifi_network_ssid, self.wifi_network_pass)), + (phone_setup_iwlan, + (self.log, ads[1], True, WFC_MODE_WIFI_PREFERRED, + self.wifi_network_ssid, self.wifi_network_pass))] + if not multithread_func(self.log, tasks): + self.log.error("Phone Failed to Set Up Properly.") + return False + time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING) + + self.log.info("Begin In Call SMS Test.") + if not call_setup_teardown( + self.log, + ads[0], + ads[1], + ad_hangup=None, + verify_caller_func=is_phone_in_call_iwlan, + verify_callee_func=None): + return False + + return self._sms_test_mo(ads) + + @test_tracker_info(uuid="36ce7bd8-f7b7-4c8e-a1a1-bcf8346a1b8e") + @TelephonyBaseTest.tel_test_wrap + def test_mms_mo_mt_in_call_iwlan_5g_nsa(self): + """ Test MO MMS, Phone in APM, WiFi connected, WFC WiFi Preferred mode. + + Turn off APM always + Set PhoneA/B are operated at 5G NSA + Make sure PhoneA/B are operated at 5G NSA + Make sure PhoneA/B APM, WiFi connected, WFC WiFi preferred mode. + Make sure PhoneA/B report iwlan as data rat. + Make sure PhoneA/B is able to make/receive call/sms. + Call from PhoneA to PhoneB, accept on PhoneB. + Send MMS on PhoneA. + + Returns: + True if pass; False if fail. + """ + + ads = self.android_devices + + # Turn off airplane mode + self.log.info("Turn off APM mode before starting testing.") + tasks = [(toggle_airplane_mode, (self.log, ads[0], False)), + (toggle_airplane_mode, (self.log, ads[1], False))] + if not multithread_func(self.log, tasks): + self.log.error("Failed to turn off airplane mode") + return False + + # Mode Pref + tasks = [(set_preferred_mode_for_5g, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Failed to set preferred network mode.") + return False + + # Attach 5g + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not attached on 5G NSA before sending MMS.") + return False + + tasks = [(phone_setup_iwlan, + (self.log, ads[0], True, WFC_MODE_WIFI_PREFERRED, + self.wifi_network_ssid, self.wifi_network_pass)), + (phone_setup_iwlan, + (self.log, ads[1], True, WFC_MODE_WIFI_PREFERRED, + self.wifi_network_ssid, self.wifi_network_pass))] + if not multithread_func(self.log, tasks): + self.log.error("Phone Failed to Set Up Properly.") + return False + time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING) + + self.log.info("Begin In Call MMS Test.") + if not call_setup_teardown( + self.log, + ads[0], + ads[1], + ad_hangup=None, + verify_caller_func=is_phone_in_call_iwlan, + verify_callee_func=None): + return False + + return self._sms_test_mo(ads) + @test_tracker_info(uuid="e5a31b94-1cb6-4770-a2bc-5a0ddba51502") @TelephonyBaseTest.tel_test_wrap def test_sms_mo_in_call_iwlan(self): @@ -2584,6 +3295,158 @@ class TelLiveSmsTest(TelephonyBaseTest): return self._mms_test_mt(ads) + @test_tracker_info(uuid="029e05cd-df6b-4a82-8402-77fc6eadf66f") + @TelephonyBaseTest.tel_test_wrap + def test_sms_mo_in_call_iwlan_cellular(self): + """ Test MO SMS, Phone in APM, WiFi connected, Cellular Preferred mode. + + Make sure PhoneA APM, WiFi connected, Cellular preferred mode. + Make sure PhoneA report iwlan as data rat. + Make sure PhoneB is able to make/receive call/sms. + Call from PhoneA to PhoneB, accept on PhoneB. + Send SMS on PhoneA. + + Returns: + True if pass; False if fail. + """ + + ads = self.android_devices + + tasks = [(phone_setup_iwlan_cellular_preferred, + (self.log, ads[0], + self.wifi_network_ssid, self.wifi_network_pass)), + (phone_setup_voice_general, (self.log, ads[1]))] + if not multithread_func(self.log, tasks): + self.log.error("Phone Failed to Set Up Properly.") + return False + time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING) + + self.log.info("Begin In Call SMS Test.") + if not call_setup_teardown( + self.log, + ads[0], + ads[1], + ad_hangup=None, + verify_caller_func=is_phone_in_call_iwlan, + verify_callee_func=None): + return False + + return self._sms_test_mo(ads) + + @test_tracker_info(uuid="c3c47a68-a839-4470-87f6-e85496cfab23") + @TelephonyBaseTest.tel_test_wrap + def test_sms_mt_in_call_iwlan_cellular(self): + """ Test MT SMS, Phone in APM, WiFi connected, Cellular Preferred mode. + + Make sure PhoneA APM, WiFi connected, Cellular Preferred mode. + Make sure PhoneA report iwlan as data rat. + Make sure PhoneB is able to make/receive call/sms. + Call from PhoneA to PhoneB, accept on PhoneB. + Receive SMS on PhoneA. + + Returns: + True if pass; False if fail. + """ + + ads = self.android_devices + + tasks = [(phone_setup_iwlan_cellular_preferred, + (self.log, ads[0], + self.wifi_network_ssid, self.wifi_network_pass)), + (phone_setup_voice_general, (self.log, ads[1]))] + if not multithread_func(self.log, tasks): + self.log.error("Phone Failed to Set Up Properly.") + return False + time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING) + + self.log.info("Begin In Call SMS Test.") + if not call_setup_teardown( + self.log, + ads[0], + ads[1], + ad_hangup=None, + verify_caller_func=is_phone_in_call_iwlan, + verify_callee_func=None): + return False + + return self._sms_test_mt(ads) + + @test_tracker_info(uuid="4c6cd913-4aca-4f2b-b33b-1efe0a7dc11d") + @TelephonyBaseTest.tel_test_wrap + def test_mms_mo_in_call_iwlan_cellular(self): + """ Test MO MMS, Phone in APM, WiFi connected, Cellular Preferred mode. + + Make sure PhoneA APM, WiFi connected, Cellular mode. + Make sure PhoneA report iwlan as data rat. + Make sure PhoneB is able to make/receive call/sms. + Call from PhoneA to PhoneB, accept on PhoneB. + Send MMS on PhoneA. + + Returns: + True if pass; False if fail. + """ + + ads = self.android_devices + + tasks = [(phone_setup_iwlan_cellular_preferred, + (self.log, ads[0], + self.wifi_network_ssid, self.wifi_network_pass)), + (phone_setup_voice_general, (self.log, ads[1]))] + if not multithread_func(self.log, tasks): + self.log.error("Phone Failed to Set Up Properly.") + return False + time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING) + + self.log.info("Begin In Call MMS Test.") + if not call_setup_teardown( + self.log, + ads[0], + ads[1], + ad_hangup=None, + verify_caller_func=is_phone_in_call_iwlan, + verify_callee_func=None): + return False + + return self._mms_test_mo(ads) + + @test_tracker_info(uuid="5b667ca1-cafd-47d4-86dc-8b87232ddcfa") + @TelephonyBaseTest.tel_test_wrap + def test_mms_mt_in_call_iwlan_cellular(self): + """ Test MT MMS, Phone in APM, WiFi connected, Cellular Preferred mode. + + Make sure PhoneA APM, WiFi connected, Cellular preferred mode. + Make sure PhoneA report iwlan as data rat. + Make sure PhoneB is able to make/receive call/sms. + Call from PhoneA to PhoneB, accept on PhoneB. + Receive MMS on PhoneA. + + Returns: + True if pass; False if fail. + """ + + ads = self.android_devices + + tasks = [(phone_setup_iwlan_cellular_preferred, + (self.log, ads[0], + self.wifi_network_ssid, self.wifi_network_pass)), + (phone_setup_voice_general, (self.log, ads[1]))] + if not multithread_func(self.log, tasks): + self.log.error("Phone Failed to Set Up Properly.") + return False + time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING) + + self.log.info("Begin In Call MMS Test.") + if not call_setup_teardown( + self.log, + ads[0], + ads[1], + ad_hangup=None, + verify_caller_func=is_phone_in_call_iwlan, + verify_callee_func=None): + return False + + return self._mms_test_mt(ads) + @test_tracker_info(uuid="9f1933bb-c4cb-4655-8655-327c1f38e8ee") @TelephonyBaseTest.tel_test_wrap def test_sms_mo_in_call_vt(self): diff --git a/acts_tests/tests/google/tel/live/TelLiveStressTest.py b/acts_tests/tests/google/tel/live/TelLiveStressTest.py index 9677ed6717..0bde255e96 100644 --- a/acts_tests/tests/google/tel/live/TelLiveStressTest.py +++ b/acts_tests/tests/google/tel/live/TelLiveStressTest.py @@ -27,6 +27,7 @@ from acts import context from acts import signals from acts.libs.proc import job from acts.test_decorators import test_tracker_info +from acts.test_utils.tel.loggers.telephony_metric_logger import TelephonyMetricLogger from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest from acts.test_utils.tel.tel_defines import CAPABILITY_VOLTE from acts.test_utils.tel.tel_defines import CAPABILITY_WFC @@ -103,6 +104,7 @@ from acts.utils import rand_ascii_str EXCEPTION_TOLERANCE = 5 BINDER_LOGS = ["/sys/kernel/debug/binder"] +DEFAULT_FILE_DOWNLOADS = ["1MB", "5MB", "10MB", "20MB", "50MB"] class TelLiveStressTest(TelephonyBaseTest): @@ -158,6 +160,8 @@ class TelLiveStressTest(TelephonyBaseTest): self.dut_capabilities = telephony_info.get("capabilities", []) self.dut_wfc_modes = telephony_info.get("wfc_modes", []) self.gps_log_file = self.user_params.get("gps_log_file", None) + self.file_name_list = self.user_params.get("file_downloads", DEFAULT_FILE_DOWNLOADS) + self.tel_logger = TelephonyMetricLogger.for_test_case() return True def setup_test(self): @@ -367,11 +371,12 @@ class TelLiveStressTest(TelephonyBaseTest): self.result_info["%s Success" % message_type] += 1 return True - def _make_phone_call(self, call_verification_func=None): + def _make_phone_call(self, call_verification_func=None, voice_stress_only = False): ads = self.android_devices[:] slot_id_callee = None - if not self.single_phone_test: - random.shuffle(ads) + if not voice_stress_only: + if not self.single_phone_test: + random.shuffle(ads) if self.dsds_esim: slot_id = random.randint(0,1) sub_id = get_subid_from_slot_index(self.log, ads[0], slot_id) @@ -381,7 +386,10 @@ class TelLiveStressTest(TelephonyBaseTest): slot_id_callee = random.randint(0,1) ads[1].log.info("Voice - MT - slot_id %d", slot_id_callee) the_number = self.result_info["Call Total"] + 1 - duration = random.randrange(self.min_phone_call_duration, + if voice_stress_only: + duration = 30 + else: + duration = random.randrange(self.min_phone_call_duration, self.max_phone_call_duration) result = True test_name = "%s_No_%s_phone_call" % (self.test_name, the_number) @@ -436,6 +444,8 @@ class TelLiveStressTest(TelephonyBaseTest): wait_time_in_call=0, incall_ui_display=INCALL_UI_DISPLAY_BACKGROUND, slot_id_callee=slot_id_callee) + + self.tel_logger.set_result(call_setup_result.result_value) if not call_setup_result: get_telephony_signal_strength(ads[0]) if not self.single_phone_test: @@ -770,7 +780,7 @@ class TelLiveStressTest(TelephonyBaseTest): else: return True - def _data_download(self, file_names=["5MB", "10MB", "20MB", "50MB"]): + def _data_download(self, file_names=[]): begin_time = get_current_epoch_time() slot_id = random.randint(0,1) if self.dsds_esim: @@ -831,11 +841,7 @@ class TelLiveStressTest(TelephonyBaseTest): def data_test(self): while time.time() < self.finishing_time: try: - operator_name = self.dut.adb.getprop("gsm.sim.operator.alpha") - if CARRIER_SING in operator_name: - self._data_download(file_names=["1MB", "5MB"]) - else: - self._data_download() + self._data_download(self.file_name_list) except Exception as e: self.log.error("Exception error %s", str(e)) self.result_info["Exception Errors"] += 1 @@ -1098,6 +1104,69 @@ class TelLiveStressTest(TelephonyBaseTest): ad.log.info("Phone WIFI is connected successfully.") return True + def performance_tests(self, setup_func=None, call_verification_func=None): + self.log.info(self._get_result_message()) + if setup_func and not setup_func(): + msg = "%s setup %s failed" % (self.test_name, setup_func.__name__) + self.log.error(msg) + self._take_bug_report("%s%s" % (self.test_name, + setup_func.__name__), + self.begin_time) + return False + if not call_verification_func: + call_verification_func = is_phone_in_call + self.finishing_time = time.time() + self.max_run_time + + self.log.info( + "==== Start voice stress test ====") + self.perf_data["testing method"] = "parallel" + results = self.call_performance_test(call_verification_func) + + result_message = self._get_result_message() + self.log.info(result_message) + self._update_perf_json() + self.result_detail = result_message + total_call = self.result_info["Call Total"] + success_call = self.result_info["Call Success Total"] + call_fail = total_call - success_call + if call_fail != 0: + call_fail_rate = ( call_fail / total_call ) * 100 + else: + call_fail_rate = 0 + call_success_rate = (success_call / total_call) * 100 + + self.log.info("Call Success Rate is %s", call_success_rate) + self.log.info("Call Drop Rate is %s", call_success_rate) + + return results + + def _update_initiate_call_fail_count(self): + self.result_info["Call Initiate Fail"] += 1 + + def call_performance_test(self, call_verification_func=None): + count = 0 + while count < self.phone_call_iteration: + time.sleep(15) + count += 1 + try: + self._make_phone_call(call_verification_func, True) + except Exception as e: + self.log.exception("Exception error %s", str(e)) + self.result_info["Exception Errors"] += 1 + if self.result_info["Exception Errors"] >= EXCEPTION_TOLERANCE: + self.log.error("Too many exception errors, quit test") + return False + self.log.info("%s", dict(self.result_info)) + self.result_info["Call Success Total"] += 1 + if any([ + self.result_info["Call Setup Failure"], + self.result_info["Call Maintenance Failure"], + self.result_info["Call Teardown Failure"] + ]): + return False + else: + return True + """ Tests Begin """ @test_tracker_info(uuid="d035e5b9-476a-4e3d-b4e9-6fd86c51a68d") @@ -1184,4 +1253,10 @@ class TelLiveStressTest(TelephonyBaseTest): self.result_detail = result_message return all(results) + @test_tracker_info(uuid="4212d0e0-fb87-47e5-ba48-9df9a4a6bb9b") + @TelephonyBaseTest.tel_test_wrap + def test_voice_performance_stress(self): + """ Vocie Performance stress test""" + return self.performance_tests() + """ Tests End """ diff --git a/acts_tests/tests/google/tel/live/TelLiveVoiceTest.py b/acts_tests/tests/google/tel/live/TelLiveVoiceTest.py index 95adc5f325..f91f8cc87b 100644 --- a/acts_tests/tests/google/tel/live/TelLiveVoiceTest.py +++ b/acts_tests/tests/google/tel/live/TelLiveVoiceTest.py @@ -113,6 +113,8 @@ class TelLiveVoiceTest(TelephonyBaseTest): "long_duration_call_total_duration", DEFAULT_LONG_DURATION_CALL_TOTAL_DURATION) self.number_of_devices = 2 + self.call_server_number = self.user_params.get( + "call_server_number", STORY_LINE) self.tel_logger = TelephonyMetricLogger.for_test_case() @@ -139,7 +141,7 @@ class TelLiveVoiceTest(TelephonyBaseTest): for iteration in range(3): result = True ad.log.info("Attempt %d", iteration + 1) - if not initiate_call(ad.log, ad, STORY_LINE): + if not initiate_call(ad.log, ad, self.call_server_number): ad.log.error("Call Failed to Initiate") result = False continue @@ -307,7 +309,7 @@ class TelLiveVoiceTest(TelephonyBaseTest): self.log.error("Phone Failed to Set Up Properly.") return False - return call_setup_teardown( + if not call_setup_teardown( self.log, ads[0], ads[1], @@ -315,7 +317,10 @@ class TelLiveVoiceTest(TelephonyBaseTest): is_phone_in_call_volte, is_phone_in_call_volte, WAIT_TIME_IN_CALL_FOR_IMS, - dialing_number_length=10) + dialing_number_length=10): + return False + + return True @test_tracker_info(uuid="4fd3aa62-2398-4cee-994e-7fc5cadbcbc1") @TelephonyBaseTest.tel_test_wrap @@ -2582,6 +2587,146 @@ class TelLiveVoiceTest(TelephonyBaseTest): return True + @test_tracker_info(uuid="b5475061-30b4-4543-85c4-0ef2ecb2c0ef") + @TelephonyBaseTest.tel_test_wrap + def test_call_volte_mo_hold_unhold_5g_nsa(self): + """ VoLTE MO call hold/unhold test in 5G NSA network. + + 1. Attach PhoneA/B to 5G NSA. + 2. Make Sure PhoneA/B is in 5G NSA (with VoLTE). + 3. Make Sure PhoneA/B is able to make/receive call. + 4. Call from PhoneA to PhoneB, accept on PhoneB. + 5. Make sure PhoneA/B are in call. + 6. Hold and unhold on PhoneA. + 7. Make sure PhoneA/B are in 5G NSA. + + Returns: + True if pass; False if fail. + """ + ads = self.android_devices + + tasks = [(phone_setup_volte, (self.log, ads[0])), + (phone_setup_volte, (self.log, ads[1]))] + if not multithread_func(self.log, tasks): + self.log.error("Phone Failed to Set Up Properly.") + return False + + # Mode Pref + tasks = [(set_preferred_mode_for_5g, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Failed to set preferred network mode.") + return False + + # Attach 5g + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not attached on 5G NSA before call.") + return False + + ads[0].droid.telecomCallClearCallList() + if num_active_calls(self.log, ads[0]) != 0: + ads[0].log.error("Call List is not empty.") + return False + + self.log.info("Begin MO Call Hold/Unhold Test.") + if not call_setup_teardown( + self.log, + ads[0], + ads[1], + ad_hangup=None, + verify_caller_func=is_phone_in_call_volte, + verify_callee_func=None): + return False + + if not self._hold_unhold_test(ads): + self.log.error("Hold/Unhold test fail.") + return False + + if not hangup_call(self.log, ads[0]): + self.log.error("Call Hangup Failed") + return False + + # Check if phoneA are attached to 5g after Hold/Unhold test. + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not attached on 5G NSA after call.") + return False + + return True + + @test_tracker_info(uuid="ecbc7ea3-a591-4b81-930e-39598c7ee5b8") + @TelephonyBaseTest.tel_test_wrap + def test_call_volte_mt_hold_unhold_5g_nsa(self): + """ VoLTE MT call hold/unhold test in 5G NSA network. + + 1. Attach PhoneA/B to 5G NSA. + 2. Make Sure PhoneA/B is in 5G NSA (with VoLTE). + 3. Make Sure PhoneB/A is able to make/receive call. + 4. Call from PhoneB to PhoneA, accept on PhoneA. + 5. Make sure PhoneA/B are in call. + 6. Hold and unhold on PhoneA. + 7. Make sure PhoneA/B are in 5G NSA. + + Returns: + True if pass; False if fail. + """ + ads = self.android_devices + + tasks = [(phone_setup_volte, (self.log, ads[0])), + (phone_setup_volte, (self.log, ads[1]))] + if not multithread_func(self.log, tasks): + self.log.error("Phone Failed to Set Up Properly.") + return False + + # Mode Pref + tasks = [(set_preferred_mode_for_5g, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Failed to set preferred network mode.") + return False + + # Attach 5g + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not attached on 5G NSA before call.") + return False + + ads[0].droid.telecomCallClearCallList() + if num_active_calls(self.log, ads[0]) != 0: + ads[0].log.error("Call List is not empty.") + return False + + self.log.info("Begin MT Call Hold/Unhold Test.") + if not call_setup_teardown( + self.log, + ads[1], + ads[0], + ad_hangup=None, + verify_caller_func=None, + verify_callee_func=is_phone_in_call_volte): + return False + + if not self._hold_unhold_test(ads): + self.log.error("Hold/Unhold test fail.") + return False + + if not hangup_call(self.log, ads[0]): + self.log.error("Call Hangup Failed") + return False + + # Check if phoneA are attached to 5g after Hold/Unhold test. + tasks = [(is_current_network_5g_nsa, [ad]) + for ad in self.android_devices] + if not multithread_func(self.log, tasks): + self.log.error("Phone not attached on 5G NSA after call.") + return False + + return True + @test_tracker_info(uuid="ffe724ae-4223-4c15-9fed-9aba17de9a63") @TelephonyBaseTest.tel_test_wrap def test_call_wcdma_mo_hold_unhold(self): @@ -3741,6 +3886,54 @@ class TelLiveVoiceTest(TelephonyBaseTest): return self._test_call_setup_in_active_data_transfer( None, DIRECTION_MOBILE_TERMINATED) + @test_tracker_info(uuid="d1bf0739-ffb7-4bf8-ab94-570619f812a8") + @TelephonyBaseTest.tel_test_wrap + def test_call_mo_voice_apm_wifi_in_active_data_transfer_cellular(self): + """Test call can be established during active data connection. + + Turn on wifi, airplane mode and wifi. + Starting downloading file from Internet. + Initiate a MO voice call. Verify call can be established. + Hangup Voice Call, verify file is downloaded successfully. + + Returns: + True if success. + False if failed. + """ + if not phone_setup_iwlan_cellular_preferred(self.log, + self.android_devices[0], + self.wifi_network_ssid, + self.wifi_network_pass): + self.android_devices[0].log.error( + "Failed to setup iwlan with APM, WIFI") + return False + return self._test_call_setup_in_active_data_transfer( + None, DIRECTION_MOBILE_ORIGINATED) + + @test_tracker_info(uuid="76b2cdaf-b783-4c1a-b91b-207f82ffa816") + @TelephonyBaseTest.tel_test_wrap + def test_call_mt_voice_apm_wifi_in_active_data_transfer_cellular(self): + """Test call can be established during active data connection. + + Turn on wifi, airplane mode and wifi. + Starting downloading file from Internet. + Initiate a MT voice call. Verify call can be established. + Hangup Voice Call, verify file is downloaded successfully. + + Returns: + True if success. + False if failed. + """ + if not phone_setup_iwlan_cellular_preferred(self.log, + self.android_devices[0], + self.wifi_network_ssid, + self.wifi_network_pass): + self.android_devices[0].log.error( + "Failed to setup iwlan with APM, WIFI and WFC on") + return False + return self._test_call_setup_in_active_data_transfer( + None, DIRECTION_MOBILE_TERMINATED) + def _test_call_setup_in_active_youtube_video( self, nw_gen=None, @@ -4101,6 +4294,52 @@ class TelLiveVoiceTest(TelephonyBaseTest): return self._test_call_setup_in_active_youtube_video( None, DIRECTION_MOBILE_TERMINATED) + @test_tracker_info(uuid="88822edf-4c4a-4bc4-9280-2f27ee9e28d5") + @TelephonyBaseTest.tel_test_wrap + def test_call_mo_voice_apm_wifi_in_active_youtube_video_cellular(self): + """Test call can be established during active youtube video. + + Turn on wifi, Cellular Preferred, airplane mode and wifi. + Starting an youtube video. + Initiate a MO voice call. Verify call can be established. + + Returns: + True if success. + False if failed. + """ + if not phone_setup_iwlan_cellular_preferred(self.log, + self.android_devices[0], + self.wifi_network_ssid, + self.wifi_network_pass): + self.android_devices[0].log.error( + "Failed to setup iwlan with APM, WIFI and WFC on") + return False + return self._test_call_setup_in_active_youtube_video( + None, DIRECTION_MOBILE_ORIGINATED) + + @test_tracker_info(uuid="c4b066b0-3cfd-4831-9c61-5d6b132648c4") + @TelephonyBaseTest.tel_test_wrap + def test_call_mt_voice_apm_wifi_in_active_youtube_video_cellular(self): + """Test call can be established during active youtube video. + + Turn on cellular calling, airplane mode and wifi. + Starting youtube video. + Initiate a MT voice call. Verify call can be established. + + Returns: + True if success. + False if failed. + """ + if not phone_setup_iwlan_cellular_preferred(self.log, + self.android_devices[0], + self.wifi_network_ssid, + self.wifi_network_pass): + self.android_devices[0].log.error( + "Failed to setup iwlan with APM, WIFI and WFC on") + return False + return self._test_call_setup_in_active_youtube_video( + None, DIRECTION_MOBILE_TERMINATED) + @test_tracker_info(uuid="f367de12-1fd8-488d-816f-091deaacb791") @TelephonyBaseTest.tel_test_wrap def test_call_wfc_wifi_preferred_after_mobile_data_usage_limit_reached( diff --git a/acts_tests/tests/google/tel/live/TelWifiVoiceTest.py b/acts_tests/tests/google/tel/live/TelWifiVoiceTest.py index b254f865bb..9d0e4c53fe 100644 --- a/acts_tests/tests/google/tel/live/TelWifiVoiceTest.py +++ b/acts_tests/tests/google/tel/live/TelWifiVoiceTest.py @@ -3248,6 +3248,45 @@ class TelWifiVoiceTest(TelephonyBaseTest): self._is_phone_in_call_iwlan, self._increase_lte_decrease_wifi_rssi_check_phone_hand_out, True) + def _decrease_then_increase_cellular_rssi_check_phone_hand_out(self): + """Private Test utility for hand_out test. + Step1 + Decrease Cellular RSSI to MIN_RSSI_RESERVED_VALUE 5db per sec + PhoneA should still be in call. PhoneA should hand-out to iWLAN. + Step2 + Increase Cellular RSSI to MAX_RSSI_RESERVED_VALUE + PhoneA should still be in call. PhoneA should hand-out to LTE. + """ + self._decrease_cellular_rssi_check_phone_hand_out() + self._increase_lte_decrease_wifi_rssi_check_phone_hand_out() + + return True + + @TelephonyBaseTest.tel_test_wrap + def test_lte_iwlan_lte_handoff_cellular_preferred(self): + """VoLET to VoWiFi then back to VoLTE In Call Handover Test + Step1 + VoLTE to VoWiFi in call handover + PhoneA on LTE, VoLTE enabled, WFC WiFi preferred, WiFi associated. + Cellular strong, WiFi signal strong. + Call from PhoneA to PhoneB, PhoneA should be on LTE. + Attenuate LTE + PhoneA should still be in call. PhoneA should handover to iWLAN. + + Step2 + PhoneA on LTE, VoLTE enabled, WFC Cellular preferred, WiFi associated. + Cellular absent, WiFi signal strong. + Call from PhoneA to PhoneB, PhoneA should be on iwlan. + Attenuate WiFi and Bring up LTE + PhoneA should still be in call. PhoneA should handover to LTE. + """ + return self._wfc_call_sequence( + [self.android_devices[0], self.android_devices[1]], + DIRECTION_MOBILE_ORIGINATED, self._wfc_set_wifi_strong_cell_strong, + self._wfc_phone_setup_cellular_preferred, self._phone_idle_volte, + self._is_phone_in_call_volte, + self._decrease_then_increase_cellular_rssi_check_phone_hand_out, True) + def _decrease_wifi_rssi_hand_out_and_increase_wifi_rssi_hand_in(self): if not self._decrease_wifi_rssi_check_phone_hand_out(): return False diff --git a/acts_tests/tests/google/wifi/OWNERS b/acts_tests/tests/google/wifi/OWNERS new file mode 100644 index 0000000000..edb3e3ee80 --- /dev/null +++ b/acts_tests/tests/google/wifi/OWNERS @@ -0,0 +1,6 @@ +bkleung@google.com +dysu@google.com +etancohen@google.com +gmoturu@google.com +rpius@google.com +satk@google.com diff --git a/acts/tests/google/wifi/SetupWifiNetworkTest.py b/acts_tests/tests/google/wifi/SetupWifiNetworkTest.py index 26026ff193..26026ff193 100644 --- a/acts/tests/google/wifi/SetupWifiNetworkTest.py +++ b/acts_tests/tests/google/wifi/SetupWifiNetworkTest.py diff --git a/acts/tests/google/wifi/WifiAutoJoinTest.py b/acts_tests/tests/google/wifi/WifiAutoJoinTest.py index 829fc6c0d3..829fc6c0d3 100755 --- a/acts/tests/google/wifi/WifiAutoJoinTest.py +++ b/acts_tests/tests/google/wifi/WifiAutoJoinTest.py diff --git a/acts_tests/tests/google/wifi/WifiAutoUpdateTest.py b/acts_tests/tests/google/wifi/WifiAutoUpdateTest.py new file mode 100755 index 0000000000..8e8c163bf1 --- /dev/null +++ b/acts_tests/tests/google/wifi/WifiAutoUpdateTest.py @@ -0,0 +1,401 @@ +# !/usr/bin/env python3.4 +# +# Copyright 2017 - The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import re +from acts import asserts +from acts.controllers.android_device import SL4A_APK_NAME +from acts.libs.ota import ota_updater +import acts.signals as signals +from acts.test_decorators import test_tracker_info +from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G +import acts.test_utils.wifi.wifi_test_utils as wutils +from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest +import acts.utils as utils + +WifiEnums = wutils.WifiEnums +SSID = WifiEnums.SSID_KEY +PWD = WifiEnums.PWD_KEY +NETID = WifiEnums.NETID_KEY +# Default timeout used for reboot, toggle WiFi and Airplane mode, +# for the system to settle down after the operation. +DEFAULT_TIMEOUT = 10 +BAND_2GHZ = 0 +BAND_5GHZ = 1 + + +class WifiAutoUpdateTest(WifiBaseTest): + """Tests for APIs in Android's WifiManager class. + + Test Bed Requirement: + * One Android device + * Several Wi-Fi networks visible to the device, including an open Wi-Fi + network. + """ + + def __init__(self, controllers): + WifiBaseTest.__init__(self, controllers) + self.tests = ( + "test_check_wifi_state_after_au", + "test_verify_networks_after_au", + "test_configstore_after_au", + "test_mac_randomization_after_au", + "test_wifi_hotspot_5g_psk_after_au", + "test_all_networks_connectable_after_au", + "test_connect_to_network_suggestion_after_au", + "test_check_wifi_toggling_after_au", + "test_connection_to_new_networks", + "test_reset_wifi_after_au") + + def setup_class(self): + super(WifiAutoUpdateTest, self).setup_class() + ota_updater.initialize(self.user_params, self.android_devices) + self.dut = self.android_devices[0] + self.dut_client = self.android_devices[1] + wutils.wifi_test_device_init(self.dut) + wutils.wifi_toggle_state(self.dut, True) + + # configure APs + self.legacy_configure_ap_and_start(wpa_network=True) + self.wpapsk_2g = self.reference_networks[0]["2g"] + self.wpapsk_5g = self.reference_networks[0]["5g"] + self.open_2g = self.open_network[0]["2g"] + self.open_5g = self.open_network[0]["5g"] + + # saved & connected networks, network suggestions + # and new networks + self.saved_networks = [self.open_2g] + self.network_suggestions = [self.wpapsk_5g] + self.connected_networks = [self.wpapsk_2g, self.open_5g] + self.new_networks = [self.reference_networks[1]["2g"], + self.open_network[1]["5g"]] + + # add pre ota upgrade configuration + self.wifi_config_list = [] + self.pre_default_mac = {} + self.pre_random_mac = {} + self.pst_default_mac = {} + self.pst_random_mac = {} + self.add_pre_update_configuration() + + # Run OTA below, if ota fails then abort all tests. + try: + ota_updater.update(self.dut) + except Exception as e: + raise signals.TestAbortClass( + "Failed up apply OTA update. Aborting tests: %s" % e) + + def setup_test(self): + self.dut.droid.wakeLockAcquireBright() + self.dut.droid.wakeUpNow() + + def teardown_test(self): + self.dut.droid.wakeLockRelease() + self.dut.droid.goToSleepNow() + + def on_fail(self, test_name, begin_time): + self.dut.take_bug_report(test_name, begin_time) + self.dut.cat_adb_log(test_name, begin_time) + + def teardown_class(self): + if "AccessPoint" in self.user_params: + del self.user_params["reference_networks"] + del self.user_params["open_network"] + + ### Helper Methods + + def add_pre_update_configuration(self): + self.add_network_suggestions(self.network_suggestions) + self.add_network_and_enable(self.saved_networks[0]) + self.add_wifi_hotspot() + self.connect_to_multiple_networks(self.connected_networks) + + def add_wifi_hotspot(self): + self.wifi_hotspot = {"SSID": "hotspot_%s" % utils.rand_ascii_str(6), + "password": "pass_%s" % utils.rand_ascii_str(6)} + band = WIFI_CONFIG_APBAND_5G + if self.dut.build_info["build_id"].startswith("Q"): + band = WifiEnums.WIFI_CONFIG_APBAND_5G_OLD + self.wifi_hotspot[WifiEnums.AP_BAND_KEY] = band + asserts.assert_true( + self.dut.droid.wifiSetWifiApConfiguration(self.wifi_hotspot), + "Failed to set WifiAp Configuration") + wifi_ap = self.dut.droid.wifiGetApConfiguration() + asserts.assert_true( + wifi_ap[WifiEnums.SSID_KEY] == self.wifi_hotspot[WifiEnums.SSID_KEY], + "Hotspot SSID doesn't match with expected SSID") + return + wutils.save_wifi_soft_ap_config(self.dut, self.wifi_hotspot, band) + + def verify_wifi_hotspot(self): + """Verify wifi tethering.""" + wutils.start_wifi_tethering_saved_config(self.dut) + wutils.connect_to_wifi_network(self.dut_client, + self.wifi_hotspot, + check_connectivity=False) + wutils.stop_wifi_tethering(self.dut) + + def connect_to_multiple_networks(self, networks): + """Connect to a list of wifi networks. + + Args: + networks : list of wifi networks. + """ + self.log.info("Connect to multiple wifi networks") + for network in networks: + ssid = network[SSID] + wutils.start_wifi_connection_scan_and_ensure_network_found( + self.dut, ssid) + wutils.wifi_connect(self.dut, network, num_of_tries=6) + self.wifi_config_list.append(network) + self.pre_default_mac[network[SSID]] = self.get_sta_mac_address() + self.pre_random_mac[network[SSID]] = \ + self.dut.droid.wifigetRandomizedMacAddress(network) + + def get_sta_mac_address(self): + """Gets the current MAC address being used for client mode.""" + out = self.dut.adb.shell("ifconfig wlan0") + res = re.match(".* HWaddr (\S+).*", out, re.S) + return res.group(1) + + def add_network_suggestions(self, network_suggestions): + """Add wifi network suggestions to DUT. + + Args: + network_suggestions : suggestions to add. + """ + self.dut.log.info("Adding network suggestions") + asserts.assert_true( + self.dut.droid.wifiAddNetworkSuggestions(network_suggestions), + "Failed to add suggestions") + + # Enable suggestions by the app. + self.dut.log.debug("Enabling suggestions from test") + self.dut.adb.shell( + "cmd wifi network-suggestions-set-user-approved %s yes" % \ + SL4A_APK_NAME) + + def remove_suggestions_and_ensure_no_connection(self, + network_suggestions, + expected_ssid): + """Remove network suggestions. + + Args: + network_suggestions : suggestions to remove. + expected_ssid : SSID to verify that DUT is not connected. + """ + # remove network suggestion and verify disconnect + self.dut.log.info("Removing network suggestions") + asserts.assert_true( + self.dut.droid.wifiRemoveNetworkSuggestions(network_suggestions), + "Failed to remove suggestions") + + wutils.wait_for_disconnect(self.dut) + self.dut.ed.clear_all_events() + + # Now ensure that we didn't connect back. + asserts.assert_false( + wutils.wait_for_connect(self.dut, + expected_ssid, + assert_on_fail=False), + "Device should not connect back") + + def add_network_and_enable(self, network): + """Add a network and enable it. + + Args: + network : Network details for the network to be added. + """ + self.log.info("Add a wifi network and enable it") + ret = self.dut.droid.wifiAddNetwork(network) + asserts.assert_true(ret != -1, "Add network %r failed" % network) + self.wifi_config_list.append({SSID: network[SSID], NETID: ret}) + self.dut.droid.wifiEnableNetwork(ret, 0) + + def check_networks_after_autoupdate(self, networks): + """Verify that all previously configured networks are persistent. + + Args: + networks: List of network dicts. + """ + network_info = self.dut.droid.wifiGetConfiguredNetworks() + if len(network_info) != len(networks): + msg = ( + "Number of configured networks before and after Auto-update " + "don't match. \nBefore reboot = %s \n After reboot = %s" % + (networks, network_info)) + raise signals.TestFailure(msg) + + # For each network, check if it exists in configured list after Auto- + # update. + for network in networks: + exists = wutils.match_networks({SSID: network[SSID]}, network_info) + if not exists: + raise signals.TestFailure("%s network is not present in the" + " configured list after Auto-update" % + network[SSID]) + # Get the new network id for each network after reboot. + network[NETID] = exists[0]["networkId"] + + def get_enabled_network(self, network1, network2): + """Check network status and return currently unconnected network. + + Args: + network1: dict representing a network. + network2: dict representing a network. + + Returns: + Network dict of the unconnected network. + """ + wifi_info = self.dut.droid.wifiGetConnectionInfo() + enabled = network1 + if wifi_info[SSID] == network1[SSID]: + enabled = network2 + return enabled + + ### Tests + + @test_tracker_info(uuid="9ff1f01e-e5ff-408b-9a95-29e87a2df2d8") + def test_check_wifi_state_after_au(self): + """Check if the state of WiFi is enabled after Auto-update.""" + if not self.dut.droid.wifiCheckState(): + raise signals.TestFailure("WiFi is disabled after Auto-update!!!") + + @test_tracker_info(uuid="e3ebdbba-71dd-4281-aef8-5b3d42b88770") + def test_verify_networks_after_au(self): + """Check if the previously added networks are intact. + + Steps: + Number of networs should be the same and match each network. + + """ + self.check_networks_after_autoupdate(self.wifi_config_list) + + @test_tracker_info(uuid="799e83c2-305d-4510-921e-dac3c0dbb6c5") + def test_configstore_after_au(self): + """Verify DUT automatically connects to wifi networks after ota. + + Steps: + 1. Connect to two wifi networks pre ota. + 2. Verify DUT automatically connects to 1 after ota. + 3. Re-connect to the other wifi network. + """ + wifi_info = self.dut.droid.wifiGetConnectionInfo() + self.pst_default_mac[wifi_info[SSID]] = self.get_sta_mac_address() + self.pst_random_mac[wifi_info[SSID]] = \ + self.dut.droid.wifigetRandomizedMacAddress(wifi_info) + reconnect_to = self.get_enabled_network(self.wifi_config_list[1], + self.wifi_config_list[2]) + wutils.start_wifi_connection_scan_and_ensure_network_found( + self.dut, reconnect_to[SSID]) + wutils.wifi_connect_by_id(self.dut, reconnect_to[NETID]) + connect_data = self.dut.droid.wifiGetConnectionInfo() + connect_ssid = connect_data[SSID] + self.log.info("Expected SSID = %s" % reconnect_to[SSID]) + self.log.info("Connected SSID = %s" % connect_ssid) + if connect_ssid != reconnect_to[SSID]: + raise signals.TestFailure( + "Device failed to reconnect to the correct" + " network after reboot.") + self.pst_default_mac[wifi_info[SSID]] = self.get_sta_mac_address() + self.pst_random_mac[wifi_info[SSID]] = \ + self.dut.droid.wifigetRandomizedMacAddress(wifi_info) + + for network in self.connected_networks: + wutils.wifi_forget_network(self.dut, network[SSID]) + + @test_tracker_info(uuid="e26d0ed9-9457-4a95-a962-4d43b0032bac") + def test_mac_randomization_after_au(self): + """Verify randomized MAC addrs are persistent after ota. + + Steps: + 1. Reconnect to the wifi networks configured pre ota. + 2. Get the randomized MAC addrs. + """ + for ssid, mac in self.pst_random_mac.items(): + asserts.assert_true( + self.pre_random_mac[ssid] == mac, + "MAC addr of %s is %s after ota. Expected %s" % + (ssid, mac, self.pre_random_mac[ssid])) + + @test_tracker_info(uuid="f68a65e6-97b7-4746-bad8-4c206551d87e") + def test_wifi_hotspot_5g_psk_after_au(self): + """Verify hotspot after ota upgrade. + + Steps: + 1. Start wifi hotspot on the saved config. + 2. Verify DUT client connects to it. + """ + self.verify_wifi_hotspot() + + @test_tracker_info(uuid="21f91372-88a6-44b9-a4e8-d4664823dffb") + def test_connect_to_network_suggestion_after_au(self): + """Verify connection to network suggestion after ota. + + Steps: + 1. DUT has network suggestion added before OTA. + 2. Wait for the device to connect to it. + 3. Remove the suggestions and ensure the device does not + connect back. + """ + wutils.start_wifi_connection_scan_and_return_status(self.dut) + wutils.wait_for_connect(self.dut, self.network_suggestions[0][SSID]) + self.remove_suggestions_and_ensure_no_connection( + self.network_suggestions, self.network_suggestions[0][SSID]) + + @test_tracker_info(uuid="b8e47a4f-62fe-4a0e-b999-27ae1ebf4d19") + def test_connection_to_new_networks(self): + """Check if we can connect to new networks after Auto-update. + + Steps: + 1. Connect to a PSK network. + 2. Connect to an open network. + 3. Forget ntworks added in 1 & 2. + TODO: (@bmahadev) Add WEP network once it's ready. + """ + for network in self.new_networks: + wutils.connect_to_wifi_network(self.dut, network) + for network in self.new_networks: + wutils.wifi_forget_network(self.dut, network[SSID]) + + @test_tracker_info(uuid="1d8309e4-d5a2-4f48-ba3b-895a58c9bf3a") + def test_all_networks_connectable_after_au(self): + """Check if previously added networks are connectable. + + Steps: + 1. Connect to previously added PSK network using network id. + 2. Connect to previously added open network using network id. + TODO: (@bmahadev) Add WEP network once it's ready. + """ + network = self.wifi_config_list[0] + if not wutils.connect_to_wifi_network_with_id(self.dut, + network[NETID], + network[SSID]): + raise signals.TestFailure("Failed to connect to %s after OTA" % + network[SSID]) + wutils.wifi_forget_network(self.dut, network[SSID]) + + @test_tracker_info(uuid="05671859-38b1-4dbf-930c-18048971d075") + def test_check_wifi_toggling_after_au(self): + """Check if WiFi can be toggled ON/OFF after auto-update.""" + self.log.debug("Going from on to off.") + wutils.wifi_toggle_state(self.dut, False) + self.log.debug("Going from off to on.") + wutils.wifi_toggle_state(self.dut, True) + + @test_tracker_info(uuid="440edf32-4b00-42b0-9811-9f2bc4a83efb") + def test_reset_wifi_after_au(self): + """"Check if WiFi can be reset after auto-update.""" + wutils.reset_wifi(self.dut) diff --git a/acts/tests/google/wifi/WifiChaosTest.py b/acts_tests/tests/google/wifi/WifiChaosTest.py index d0a3722f9f..d0a3722f9f 100755 --- a/acts/tests/google/wifi/WifiChaosTest.py +++ b/acts_tests/tests/google/wifi/WifiChaosTest.py diff --git a/acts/tests/google/wifi/WifiConnectedMacRandomizationTest.py b/acts_tests/tests/google/wifi/WifiConnectedMacRandomizationTest.py index bd10a8b9a7..bd10a8b9a7 100644 --- a/acts/tests/google/wifi/WifiConnectedMacRandomizationTest.py +++ b/acts_tests/tests/google/wifi/WifiConnectedMacRandomizationTest.py diff --git a/acts/tests/google/wifi/WifiCrashStressTest.py b/acts_tests/tests/google/wifi/WifiCrashStressTest.py index 837112a62c..837112a62c 100644 --- a/acts/tests/google/wifi/WifiCrashStressTest.py +++ b/acts_tests/tests/google/wifi/WifiCrashStressTest.py diff --git a/acts/tests/google/wifi/WifiCrashTest.py b/acts_tests/tests/google/wifi/WifiCrashTest.py index d43a0b9c0c..f76b1ed0ac 100644 --- a/acts/tests/google/wifi/WifiCrashTest.py +++ b/acts_tests/tests/google/wifi/WifiCrashTest.py @@ -29,9 +29,8 @@ from acts.test_decorators import test_tracker_info from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest WifiEnums = wutils.WifiEnums -# Default timeout used for reboot, toggle WiFi and Airplane mode, -# for the system to settle down after the operation. -DEFAULT_TIMEOUT = 10 +# Timeout used for crash recovery. +RECOVERY_TIMEOUT = 15 WIFICOND_KILL_SHELL_COMMAND = "killall wificond" WIFI_VENDOR_HAL_KILL_SHELL_COMMAND = "killall android.hardware.wifi@1.0-service vendor.google.wifi_ext@1.0-service-vendor" SUPPLICANT_KILL_SHELL_COMMAND = "killall wpa_supplicant" @@ -56,6 +55,8 @@ class WifiCrashTest(WifiBaseTest): if "AccessPoint" in self.user_params: self.legacy_configure_ap_and_start() + elif "OpenWrtAP" in self.user_params: + self.configure_openwrt_ap_and_start(wpa_network=True) asserts.assert_true( len(self.reference_networks) > 0, @@ -100,7 +101,7 @@ class WifiCrashTest(WifiBaseTest): self.dut.restart_runtime() # We won't get the disconnect broadcast because framework crashed. # wutils.wait_for_disconnect(self.dut) - time.sleep(DEFAULT_TIMEOUT) + time.sleep(RECOVERY_TIMEOUT) wifi_info = self.dut.droid.wifiGetConnectionInfo() if wifi_info[WifiEnums.SSID_KEY] != self.network[WifiEnums.SSID_KEY]: raise signals.TestFailure("Device did not connect to the" @@ -123,7 +124,7 @@ class WifiCrashTest(WifiBaseTest): self.log.info("Crashing wificond") self.dut.adb.shell(WIFICOND_KILL_SHELL_COMMAND) wutils.wait_for_disconnect(self.dut) - time.sleep(DEFAULT_TIMEOUT) + time.sleep(RECOVERY_TIMEOUT) wifi_info = self.dut.droid.wifiGetConnectionInfo() if wifi_info[WifiEnums.SSID_KEY] != self.network[WifiEnums.SSID_KEY]: raise signals.TestFailure("Device did not connect to the" @@ -146,7 +147,7 @@ class WifiCrashTest(WifiBaseTest): self.log.info("Crashing wifi HAL") self.dut.adb.shell(WIFI_VENDOR_HAL_KILL_SHELL_COMMAND) wutils.wait_for_disconnect(self.dut) - time.sleep(DEFAULT_TIMEOUT) + time.sleep(RECOVERY_TIMEOUT) wifi_info = self.dut.droid.wifiGetConnectionInfo() if wifi_info[WifiEnums.SSID_KEY] != self.network[WifiEnums.SSID_KEY]: raise signals.TestFailure("Device did not connect to the" @@ -169,7 +170,7 @@ class WifiCrashTest(WifiBaseTest): self.log.info("Crashing wpa_supplicant") self.dut.adb.shell(SUPPLICANT_KILL_SHELL_COMMAND) wutils.wait_for_disconnect(self.dut) - time.sleep(DEFAULT_TIMEOUT) + time.sleep(RECOVERY_TIMEOUT) wifi_info = self.dut.droid.wifiGetConnectionInfo() if wifi_info[WifiEnums.SSID_KEY] != self.network[WifiEnums.SSID_KEY]: raise signals.TestFailure("Device did not connect to the" diff --git a/acts/tests/google/wifi/WifiDiagnosticsTest.py b/acts_tests/tests/google/wifi/WifiDiagnosticsTest.py index 8ef24bd24f..8ef24bd24f 100644 --- a/acts/tests/google/wifi/WifiDiagnosticsTest.py +++ b/acts_tests/tests/google/wifi/WifiDiagnosticsTest.py diff --git a/acts/tests/google/wifi/WifiDppTest.py b/acts_tests/tests/google/wifi/WifiDppTest.py index 42591b03f2..85d25239fa 100644 --- a/acts/tests/google/wifi/WifiDppTest.py +++ b/acts_tests/tests/google/wifi/WifiDppTest.py @@ -49,19 +49,33 @@ class WifiDppTest(WifiBaseTest): DPP_TEST_MESSAGE_TYPE = "Type" DPP_TEST_MESSAGE_STATUS = "Status" DPP_TEST_MESSAGE_NETWORK_ID = "NetworkId" + DPP_TEST_MESSAGE_FAILURE_SSID = "onFailureSsid" + DPP_TEST_MESSAGE_FAILURE_CHANNEL_LIST = "onFailureChannelList" + DPP_TEST_MESSAGE_FAILURE_BAND_LIST = "onFailureBandList" DPP_TEST_NETWORK_ROLE_STA = "sta" DPP_TEST_NETWORK_ROLE_AP = "ap" + DPP_TEST_PARAM_SSID = "SSID" + DPP_TEST_PARAM_PASSWORD = "Password" + WPA_SUPPLICANT_SECURITY_SAE = "sae" WPA_SUPPLICANT_SECURITY_PSK = "psk" + DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS = 0 + DPP_EVENT_PROGRESS_RESPONSE_PENDING = 1 + DPP_EVENT_PROGRESS_CONFIGURATION_SENT_WAITING_RESPONSE = 2 + DPP_EVENT_PROGRESS_CONFIGURATION_ACCEPTED = 3 + + DPP_EVENT_SUCCESS_CONFIGURATION_SENT = 0 + DPP_EVENT_SUCCESS_CONFIGURATION_APPLIED = 1 + def setup_class(self): """ Sets up the required dependencies from the config file and configures the device for WifiService API tests. Returns: - True is successfully configured the requirements for testig. + True is successfully configured the requirements for testing. """ # Device 0 is under test. Device 1 performs the responder role @@ -75,6 +89,41 @@ class WifiDppTest(WifiBaseTest): utils.require_sl4a((self.dut,)) utils.sync_device_time(self.dut) + req_params = ["dpp_r1_test_only"] + opt_param = ["wifi_psk_network", "wifi_sae_network"] + self.unpack_userparams( + req_param_names=req_params, opt_param_names=opt_param) + + self.dut.log.info( + "Parsed configs: %s %s" % (self.wifi_psk_network, self.wifi_sae_network)) + + # Set up the networks. This is optional. In case these networks are not initialized, + # the script will create random ones. However, a real AP is required to pass DPP R2 test. + # Most efficient setup would be to use an AP in WPA2/WPA3 transition mode. + if self.DPP_TEST_PARAM_SSID in self.wifi_psk_network: + self.psk_network_ssid = self.wifi_psk_network[self.DPP_TEST_PARAM_SSID] + else: + self.psk_network_ssid = None + + if self.DPP_TEST_PARAM_PASSWORD in self.wifi_psk_network: + self.psk_network_password = self.wifi_psk_network[self.DPP_TEST_PARAM_PASSWORD] + else: + self.psk_network_ssid = None + + if self.DPP_TEST_PARAM_SSID in self.wifi_sae_network: + self.sae_network_ssid = self.wifi_sae_network[self.DPP_TEST_PARAM_SSID] + else: + self.sae_network_ssid = None + + if self.DPP_TEST_PARAM_PASSWORD in self.wifi_sae_network: + self.sae_network_password = self.wifi_sae_network[self.DPP_TEST_PARAM_PASSWORD] + else: + self.sae_network_ssid = None + + if self.dpp_r1_test_only == "False": + if not self.wifi_psk_network or not self.wifi_sae_network: + asserts.fail("Must specify wifi_psk_network and wifi_sae_network for DPP R2 tests") + # Enable verbose logging on the dut self.dut.droid.wifiEnableVerboseLogging(1) asserts.assert_true(self.dut.droid.wifiGetVerboseLoggingLevel() == 1, @@ -87,18 +136,42 @@ class WifiDppTest(WifiBaseTest): self.dut.take_bug_report(test_name, begin_time) self.dut.cat_adb_log(test_name, begin_time) - def create_and_save_wifi_network_config(self, security): + def create_and_save_wifi_network_config(self, security, random_network=False, + r2_auth_error=False): """ Create a config with random SSID and password. Args: security: Security type: PSK or SAE + random_network: A boolean that indicates if to create a random network + r2_auth_error: A boolean that indicates if to create a network with a bad password Returns: A tuple with the config and networkId for the newly created and saved network. """ - config_ssid = self.DPP_TEST_SSID_PREFIX + utils.rand_ascii_str(8) - config_password = utils.rand_ascii_str(8) + if security == self.DPP_TEST_SECURITY_PSK: + if self.psk_network_ssid is None or self.psk_network_password is None or \ + random_network is True: + config_ssid = self.DPP_TEST_SSID_PREFIX + utils.rand_ascii_str(8) + config_password = utils.rand_ascii_str(8) + else: + config_ssid = self.psk_network_ssid + if r2_auth_error: + config_password = utils.rand_ascii_str(8) + else: + config_password = self.psk_network_password + else: + if self.sae_network_ssid is None or self.sae_network_password is None or \ + random_network is True: + config_ssid = self.DPP_TEST_SSID_PREFIX + utils.rand_ascii_str(8) + config_password = utils.rand_ascii_str(8) + else: + config_ssid = self.sae_network_ssid + if r2_auth_error: + config_password = utils.rand_ascii_str(8) + else: + config_password = self.sae_network_password + self.dut.log.info( "creating config: %s %s %s" % (config_ssid, config_password, security)) config = { @@ -231,9 +304,9 @@ class WifiDppTest(WifiBaseTest): cmd = "wpa_cli DPP_BOOTSTRAP_REMOVE %s" % uri_id result = device.adb.shell(cmd) - if "FAIL" in result: - asserts.fail("del_uri: Failed to delete URI. Command used: %s" % cmd) - device.log.info("Deleted URI, id = %s" % uri_id) + # If URI was already flushed, ignore a failure here + if "FAIL" not in result: + device.log.info("Deleted URI, id = %s" % uri_id) def start_responder_configurator(self, device, @@ -269,15 +342,23 @@ class WifiDppTest(WifiBaseTest): self.log.warning("SAE not supported on device! reverting to PSK") security = self.DPP_TEST_SECURITY_PSK_PASSPHRASE + ssid = self.DPP_TEST_SSID_PREFIX + utils.rand_ascii_str(8) + password = utils.rand_ascii_str(8) + if security == self.DPP_TEST_SECURITY_SAE: conf += self.WPA_SUPPLICANT_SECURITY_SAE + if not self.sae_network_ssid is None: + ssid = self.sae_network_ssid + password = self.sae_network_password elif security == self.DPP_TEST_SECURITY_PSK_PASSPHRASE: conf += self.WPA_SUPPLICANT_SECURITY_PSK + if not self.psk_network_ssid is None: + ssid = self.psk_network_ssid + password = self.psk_network_password else: conf += self.WPA_SUPPLICANT_SECURITY_PSK use_psk = True - ssid = self.DPP_TEST_SSID_PREFIX + utils.rand_ascii_str(8) self.log.debug("SSID = %s" % ssid) ssid_encoded = binascii.hexlify(ssid.encode()).decode() @@ -291,7 +372,6 @@ class WifiDppTest(WifiBaseTest): psk_encoded = psk self.log.debug("PSK = %s" % psk) else: - password = utils.rand_ascii_str(8) if not invalid_config: password_encoded = binascii.b2a_hex(password.encode()).decode() else: @@ -356,9 +436,11 @@ class WifiDppTest(WifiBaseTest): if "FAIL" in result: asserts.fail("start_responder_enrollee: Failure. Command used: %s" % cmd) + device.adb.shell("wpa_cli set dpp_config_processing 2") + device.log.info("Started responder in enrollee mode") - def stop_responder(self, device): + def stop_responder(self, device, flush=False): """Stop responder on helper device Args: @@ -368,7 +450,9 @@ class WifiDppTest(WifiBaseTest): if "FAIL" in result: asserts.fail("stop_responder: Failed to stop responder") device.adb.shell("wpa_cli set dpp_configurator_params") - + device.adb.shell("wpa_cli set dpp_config_processing 0") + if flush: + device.adb.shell("wpa_cli flush") device.log.info("Stopped responder") def start_dpp_as_initiator_configurator(self, @@ -379,7 +463,9 @@ class WifiDppTest(WifiBaseTest): net_role=DPP_TEST_NETWORK_ROLE_STA, cause_timeout=False, fail_authentication=False, - invalid_uri=False): + invalid_uri=False, + r2_no_ap=False, + r2_auth_error=False): """ Test Easy Connect (DPP) as initiator configurator. 1. Enable wifi, if needed @@ -406,13 +492,16 @@ class WifiDppTest(WifiBaseTest): fail_authentication: Fail authentication by corrupting the responder's key invalid_uri: Use garbage string instead of a URI + r2_no_ap: Indicates if to test DPP R2 no AP failure event + r2_auth_error: Indicates if to test DPP R2 authentication failure """ if not self.dut.droid.wifiIsEasyConnectSupported(): self.log.warning("Easy Connect is not supported on device!") return wutils.wifi_toggle_state(self.dut, True) - test_network_id = self.create_and_save_wifi_network_config(security) + test_network_id = self.create_and_save_wifi_network_config(security, random_network=r2_no_ap, + r2_auth_error=r2_auth_error) if use_mac: mac = autils.get_mac_addr(self.helper_dev, "wlan0") @@ -454,34 +543,57 @@ class WifiDppTest(WifiBaseTest): == self.DPP_TEST_EVENT_ENROLLEE_SUCCESS: asserts.fail("DPP failure, unexpected result!") break - if dut_event[self.DPP_TEST_EVENT_DATA][ - self - .DPP_TEST_MESSAGE_TYPE] == self.DPP_TEST_EVENT_CONFIGURATOR_SUCCESS: - if cause_timeout or fail_authentication or invalid_uri: + if dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_TYPE] \ + == self.DPP_TEST_EVENT_CONFIGURATOR_SUCCESS: + if cause_timeout or fail_authentication or invalid_uri or r2_no_ap or r2_auth_error: asserts.fail( "Unexpected DPP success, status code: %s" % dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_STATUS]) else: val = dut_event[self.DPP_TEST_EVENT_DATA][ self.DPP_TEST_MESSAGE_STATUS] - if val == 0: + if val == self.DPP_EVENT_SUCCESS_CONFIGURATION_SENT: self.dut.log.info("DPP Configuration sent success") + if val == self.DPP_EVENT_SUCCESS_CONFIGURATION_APPLIED: + self.dut.log.info("DPP Configuration applied by enrollee") break - if dut_event[self.DPP_TEST_EVENT_DATA][ - self.DPP_TEST_MESSAGE_TYPE] == self.DPP_TEST_EVENT_PROGRESS: + if dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_TYPE] \ + == self.DPP_TEST_EVENT_PROGRESS: self.dut.log.info("DPP progress event") val = dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_STATUS] - if val == 0: + if val == self.DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS: self.dut.log.info("DPP Authentication success") - elif val == 1: + elif val == self.DPP_EVENT_PROGRESS_RESPONSE_PENDING: self.dut.log.info("DPP Response pending") + elif val == self.DPP_EVENT_PROGRESS_CONFIGURATION_SENT_WAITING_RESPONSE: + self.dut.log.info("DPP Configuration sent, waiting response") + elif val == self.DPP_EVENT_PROGRESS_CONFIGURATION_ACCEPTED: + self.dut.log.info("Configuration accepted") continue if dut_event[self.DPP_TEST_EVENT_DATA][ self.DPP_TEST_MESSAGE_TYPE] == self.DPP_TEST_EVENT_FAILURE: - if cause_timeout or fail_authentication or invalid_uri: + if cause_timeout or fail_authentication or invalid_uri or r2_no_ap or r2_auth_error: self.dut.log.info( "Error %s occurred, as expected" % dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_STATUS]) + if r2_no_ap or r2_auth_error: + if not dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_FAILURE_SSID]: + asserts.fail("Expected SSID value in DPP R2 onFailure event") + self.dut.log.info( + "Enrollee searched for SSID %s" % + dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_FAILURE_SSID]) + if r2_no_ap: + if not dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_FAILURE_CHANNEL_LIST]: + asserts.fail("Expected Channel list value in DPP R2 onFailure event") + self.dut.log.info( + "Enrollee scanned the following channels: %s" % + dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_FAILURE_CHANNEL_LIST]) + if r2_no_ap or r2_auth_error: + if not dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_FAILURE_BAND_LIST]: + asserts.fail("Expected Band Support list value in DPP R2 onFailure event") + self.dut.log.info( + "Enrollee supports the following bands: %s" % + dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_FAILURE_BAND_LIST]) else: asserts.fail( "DPP failure, status code: %s" % @@ -492,7 +604,7 @@ class WifiDppTest(WifiBaseTest): self.dut.ed.clear_all_events() # Stop responder - self.stop_responder(self.helper_dev) + self.stop_responder(self.helper_dev, flush=True) if not invalid_uri: # Delete URI @@ -607,7 +719,7 @@ class WifiDppTest(WifiBaseTest): self.dut.ed.clear_all_events() # Stop responder - self.stop_responder(self.helper_dev) + self.stop_responder(self.helper_dev, flush=True) # Delete URI self.del_uri(self.helper_dev, uri_id) @@ -792,4 +904,18 @@ class WifiDppTest(WifiBaseTest): use_mac=True, cause_timeout=True) - """ Tests End """ + @test_tracker_info(uuid="23601af8-118e-4ba8-89e3-5da2e37bbd7d") + def test_dpp_as_initiator_configurator_fail_r2_no_ap(self): + asserts.skip_if(self.dpp_r1_test_only == "True", + "DPP R1 test, skipping this test for DPP R2 only") + self.start_dpp_as_initiator_configurator( + security=self.DPP_TEST_SECURITY_PSK, use_mac=True, r2_no_ap=True) + + @test_tracker_info(uuid="7f9756d3-f28f-498e-8dcf-ac3816303998") + def test_dpp_as_initiator_configurator_fail_r2_auth_error(self): + asserts.skip_if(self.dpp_r1_test_only == "True", + "DPP R1 test, skipping this test for DPP R2 only") + self.start_dpp_as_initiator_configurator( + security=self.DPP_TEST_SECURITY_PSK, use_mac=True, r2_auth_error=True) + +""" Tests End """ diff --git a/acts/tests/google/wifi/WifiEnterpriseRoamingTest.py b/acts_tests/tests/google/wifi/WifiEnterpriseRoamingTest.py index 0cebd42e28..b9b7efd207 100644 --- a/acts/tests/google/wifi/WifiEnterpriseRoamingTest.py +++ b/acts_tests/tests/google/wifi/WifiEnterpriseRoamingTest.py @@ -59,13 +59,22 @@ class WifiEnterpriseRoamingTest(WifiBaseTest): ap_count=2, radius_conf_2g=self.radius_conf_2g, radius_conf_5g=self.radius_conf_5g,) + elif "OpenWrtAP" in self.user_params: + self.configure_openwrt_ap_and_start( + mirror_ap=True, + ent_network=True, + ap_count=2, + radius_conf_2g=self.radius_conf_2g, + radius_conf_5g=self.radius_conf_5g,) self.ent_network_2g_a = self.ent_networks[0]["2g"] self.ent_network_2g_b = self.ent_networks[1]["2g"] - self.bssid_2g_a = self.ent_network_2g_a[WifiEnums.BSSID_KEY.lower()] - self.bssid_2g_b = self.ent_network_2g_b[WifiEnums.BSSID_KEY.lower()] self.ent_roaming_ssid = self.ent_network_2g_a[WifiEnums.SSID_KEY] - self.bssid_a = self.bssid_2g_a - self.bssid_b = self.bssid_2g_b + if "AccessPoint" in self.user_params: + self.bssid_a = self.ent_network_2g_a[WifiEnums.BSSID_KEY.lower()] + self.bssid_b = self.ent_network_2g_b[WifiEnums.BSSID_KEY.lower()] + elif "OpenWrtAP" in self.user_params: + self.bssid_a = self.bssid_map[0]["2g"][self.ent_roaming_ssid] + self.bssid_b = self.bssid_map[1]["2g"][self.ent_roaming_ssid] self.config_peap = { Ent.EAP: int(EAP.PEAP), @@ -97,6 +106,8 @@ class WifiEnterpriseRoamingTest(WifiBaseTest): } self.attn_a = self.attenuators[0] self.attn_b = self.attenuators[2] + if "OpenWrtAP" in self.user_params: + self.attn_b = self.attenuators[1] # Set screen lock password so ConfigStore is unlocked. self.dut.droid.setDevicePassword(self.device_password) self.set_attns("default") diff --git a/acts/tests/google/wifi/WifiEnterpriseTest.py b/acts_tests/tests/google/wifi/WifiEnterpriseTest.py index 6ba5eb4fb5..20f5e9f043 100644 --- a/acts/tests/google/wifi/WifiEnterpriseTest.py +++ b/acts_tests/tests/google/wifi/WifiEnterpriseTest.py @@ -55,7 +55,8 @@ class WifiEnterpriseTest(WifiBaseTest): "radius_conf_pwd") self.unpack_userparams(required_userparam_names, roaming_consortium_ids=None, - plmn=None) + plmn=None, + ocsp=0) if "AccessPoint" in self.user_params: self.legacy_configure_ap_and_start( @@ -64,6 +65,13 @@ class WifiEnterpriseTest(WifiBaseTest): radius_conf_5g=self.radius_conf_5g, ent_network_pwd=True, radius_conf_pwd=self.radius_conf_pwd,) + elif "OpenWrtAP" in self.user_params: + self.configure_openwrt_ap_and_start( + ent_network=True, + radius_conf_2g=self.radius_conf_2g, + radius_conf_5g=self.radius_conf_5g, + ent_network_pwd=True, + radius_conf_pwd=self.radius_conf_pwd,) self.ent_network_2g = self.ent_networks[0]["2g"] self.ent_network_5g = self.ent_networks[0]["5g"] self.ent_network_pwd = self.ent_networks_pwd[0]["2g"] @@ -76,6 +84,7 @@ class WifiEnterpriseTest(WifiBaseTest): Ent.PASSWORD: self.eap_password, Ent.PHASE2: int(EapPhase2.MSCHAPV2), WifiEnums.SSID_KEY: self.ent_network_5g[WifiEnums.SSID_KEY], + Ent.OCSP: self.ocsp, } self.config_peap1 = dict(self.config_peap0) self.config_peap1[WifiEnums.SSID_KEY] = \ @@ -87,6 +96,7 @@ class WifiEnterpriseTest(WifiBaseTest): Ent.CLIENT_CERT: self.client_cert, Ent.PRIVATE_KEY_ID: self.client_key, Ent.IDENTITY: self.eap_identity, + Ent.OCSP: self.ocsp, } self.config_ttls = { Ent.EAP: int(EAP.TTLS), @@ -95,6 +105,7 @@ class WifiEnterpriseTest(WifiBaseTest): Ent.PASSWORD: self.eap_password, Ent.PHASE2: int(EapPhase2.MSCHAPV2), WifiEnums.SSID_KEY: self.ent_network_2g[WifiEnums.SSID_KEY], + Ent.OCSP: self.ocsp, } self.config_pwd = { Ent.EAP: int(EAP.PWD), diff --git a/acts/tests/google/wifi/WifiHiddenSSIDTest.py b/acts_tests/tests/google/wifi/WifiHiddenSSIDTest.py index a68144d35a..38f3442dd8 100644 --- a/acts/tests/google/wifi/WifiHiddenSSIDTest.py +++ b/acts_tests/tests/google/wifi/WifiHiddenSSIDTest.py @@ -51,6 +51,10 @@ class WifiHiddenSSIDTest(WifiBaseTest): if "AccessPoint" in self.user_params: self.legacy_configure_ap_and_start(hidden=True) + elif "OpenWrtAP" in self.user_params: + self.configure_openwrt_ap_and_start(open_network=True, + wpa_network=True, + hidden=True) asserts.assert_true( len(self.reference_networks) > 0, diff --git a/acts/tests/google/wifi/WifiIFSTwTest.py b/acts_tests/tests/google/wifi/WifiIFSTwTest.py index 5b1603dd4a..5b1603dd4a 100644 --- a/acts/tests/google/wifi/WifiIFSTwTest.py +++ b/acts_tests/tests/google/wifi/WifiIFSTwTest.py diff --git a/acts/tests/google/wifi/WifiIOTTest.py b/acts_tests/tests/google/wifi/WifiIOTTest.py index 1daf346dca..1daf346dca 100644 --- a/acts/tests/google/wifi/WifiIOTTest.py +++ b/acts_tests/tests/google/wifi/WifiIOTTest.py diff --git a/acts/tests/google/wifi/WifiIOTTwPkg1Test.py b/acts_tests/tests/google/wifi/WifiIOTTwPkg1Test.py index c6f8c3dc98..c6f8c3dc98 100644 --- a/acts/tests/google/wifi/WifiIOTTwPkg1Test.py +++ b/acts_tests/tests/google/wifi/WifiIOTTwPkg1Test.py diff --git a/acts/tests/google/wifi/WifiIOTtpeTest.py b/acts_tests/tests/google/wifi/WifiIOTtpeTest.py index fd141ffea6..fd141ffea6 100644 --- a/acts/tests/google/wifi/WifiIOTtpeTest.py +++ b/acts_tests/tests/google/wifi/WifiIOTtpeTest.py diff --git a/acts/tests/google/wifi/WifiLinkProbeTest.py b/acts_tests/tests/google/wifi/WifiLinkProbeTest.py index 7e6d58ffd7..5f00e6c3e2 100644 --- a/acts/tests/google/wifi/WifiLinkProbeTest.py +++ b/acts_tests/tests/google/wifi/WifiLinkProbeTest.py @@ -47,6 +47,8 @@ class WifiLinkProbeTest(WifiBaseTest): if "AccessPoint" in self.user_params: self.legacy_configure_ap_and_start() + elif "OpenWrtAP" in self.user_params: + self.configure_openwrt_ap_and_start(wpa_network=True) asserts.assert_true(len(self.reference_networks) > 0, "Need at least one reference network with psk.") @@ -58,13 +60,19 @@ class WifiLinkProbeTest(WifiBaseTest): wutils.wifi_toggle_state(self.dut, True) self.attenuators[0].set_atten(0) self.attenuators[1].set_atten(0) + self.pcap_procs = wutils.start_pcap( + self.packet_capture, 'dual', self.test_name) def teardown_test(self): self.dut.droid.wakeLockRelease() self.dut.droid.goToSleepNow() wutils.reset_wifi(self.dut) + def on_pass(self, test_name, begin_time): + wutils.stop_pcap(self.packet_capture, self.pcap_procs, True) + def on_fail(self, test_name, begin_time): + wutils.stop_pcap(self.packet_capture, self.pcap_procs, False) self.dut.take_bug_report(test_name, begin_time) self.dut.cat_adb_log(test_name, begin_time) diff --git a/acts/tests/google/wifi/WifiMacRandomizationTest.py b/acts_tests/tests/google/wifi/WifiMacRandomizationTest.py index 68cf85417c..64990d3ce1 100644 --- a/acts/tests/google/wifi/WifiMacRandomizationTest.py +++ b/acts_tests/tests/google/wifi/WifiMacRandomizationTest.py @@ -66,7 +66,7 @@ class WifiMacRandomizationTest(WifiBaseTest): self.dut_client = self.android_devices[1] wutils.wifi_test_device_init(self.dut) wutils.wifi_test_device_init(self.dut_client) - req_params = ["dbs_supported_models"] + req_params = ["dbs_supported_models", "roaming_attn"] opt_param = [ "open_network", "reference_networks", "wep_networks" ] @@ -79,8 +79,12 @@ class WifiMacRandomizationTest(WifiBaseTest): self.configure_packet_capture() if "AccessPoint" in self.user_params: - if "AccessPoint" in self.user_params: - self.legacy_configure_ap_and_start(wep_network=True, ap_count=2) + self.legacy_configure_ap_and_start(wep_network=True, + ap_count=2) + elif "OpenWrtAP" in self.user_params: + self.configure_openwrt_ap_and_start(open_network=True, + wpa_network=True, + wep_network=True) asserts.assert_true( len(self.reference_networks) > 0, @@ -242,8 +246,9 @@ class WifiMacRandomizationTest(WifiBaseTest): for pkt in packets: self.log.debug("Packet Summary = %s" % pkt.summary()) if mac in pkt.summary(): - raise signals.TestFailure("Caught Factory MAC in packet sniffer." - "Packet = %s" % pkt.show()) + raise signals.TestFailure("Caught Factory MAC in packet sniffer" + "Packet = %s Device = %s" + % (pkt.show(), self.dut)) def verify_mac_is_found_in_pcap(self, mac, packets): for pkt in packets: @@ -251,7 +256,7 @@ class WifiMacRandomizationTest(WifiBaseTest): if mac in pkt.summary(): return raise signals.TestFailure("Did not find MAC = %s in packet sniffer." - % mac) + "for device %s" % (mac, self.dut)) def get_sta_mac_address(self): """Gets the current MAC address being used for client mode.""" @@ -348,7 +353,7 @@ class WifiMacRandomizationTest(WifiBaseTest): """ self.check_mac_persistence(self.wpapsk_2g, TOGGLE) - @test_tracker_info(uuid="a514f-8562-44e8-bfe0-4ecab9af165b") + @test_tracker_info(uuid="b3aa514f-8562-44e8-bfe0-4ecab9af165b") def test_persistent_mac_after_device_reboot(self): """Check if MAC is persistent after a device reboot. @@ -490,18 +495,18 @@ class WifiMacRandomizationTest(WifiBaseTest): """ AP1_network = self.reference_networks[0]["5g"] AP2_network = self.reference_networks[1]["5g"] - wutils.set_attns(self.attenuators, "AP1_on_AP2_off") + wutils.set_attns(self.attenuators, "AP1_on_AP2_off", self.roaming_attn) mac_before_roam = self.connect_to_network_and_verify_mac_randomization( AP1_network) wutils.trigger_roaming_and_validate(self.dut, self.attenuators, - "AP1_off_AP2_on", AP2_network) + "AP1_off_AP2_on", AP2_network, self.roaming_attn) mac_after_roam = self.get_randomized_mac(AP2_network) if mac_after_roam != mac_before_roam: raise signals.TestFailure("Randomized MAC address changed after " "roaming from AP1 to AP2.\nMAC before roam = %s\nMAC after " "roam = %s" %(mac_before_roam, mac_after_roam)) wutils.trigger_roaming_and_validate(self.dut, self.attenuators, - "AP1_on_AP2_off", AP1_network) + "AP1_on_AP2_off", AP1_network, self.roaming_attn) mac_after_roam = self.get_randomized_mac(AP1_network) if mac_after_roam != mac_before_roam: raise signals.TestFailure("Randomized MAC address changed after " diff --git a/acts/tests/google/wifi/WifiManagerTest.py b/acts_tests/tests/google/wifi/WifiManagerTest.py index a4ef011f35..9d7f8a0a0f 100644 --- a/acts/tests/google/wifi/WifiManagerTest.py +++ b/acts_tests/tests/google/wifi/WifiManagerTest.py @@ -70,6 +70,10 @@ class WifiManagerTest(WifiBaseTest): if "AccessPoint" in self.user_params: self.legacy_configure_ap_and_start(wpa_network=True, wep_network=True) + elif "OpenWrtAP" in self.user_params: + self.configure_openwrt_ap_and_start(open_network=True, + wpa_network=True, + wep_network=True) asserts.assert_true( len(self.reference_networks) > 0, @@ -90,6 +94,8 @@ class WifiManagerTest(WifiBaseTest): ad.droid.wakeLockRelease() ad.droid.goToSleepNow() self.turn_location_off_and_scan_toggle_off() + if self.dut.droid.wifiIsApEnabled(): + wutils.stop_wifi_tethering(self.dut) wutils.reset_wifi(self.dut) if self.dut_client: wutils.reset_wifi(self.dut_client) @@ -97,6 +103,8 @@ class WifiManagerTest(WifiBaseTest): def on_fail(self, test_name, begin_time): self.dut.take_bug_report(test_name, begin_time) self.dut.cat_adb_log(test_name, begin_time) + if self.dut_client: + self.dut_client.take_bug_report(test_name, begin_time) def teardown_class(self): if "AccessPoint" in self.user_params: @@ -568,6 +576,24 @@ class WifiManagerTest(WifiBaseTest): wutils.start_wifi_connection_scan_and_ensure_network_found( self.dut, ssid) + @test_tracker_info(uuid="558652de-c802-405f-b9dc-b7fcc9237673") + def test_scan_after_reboot_with_wifi_off_and_location_scan_on(self): + """Put wifi in scan only mode""" + self.turn_location_on_and_scan_toggle_on() + wutils.wifi_toggle_state(self.dut, False) + + # Reboot the device. + self.dut.reboot() + time.sleep(DEFAULT_TIMEOUT) + + """Test wifi connection scan can start and find expected networks.""" + ssid = self.open_network_2g[WifiEnums.SSID_KEY] + wutils.start_wifi_connection_scan_and_ensure_network_found( + self.dut, ssid) + ssid = self.open_network_5g[WifiEnums.SSID_KEY] + wutils.start_wifi_connection_scan_and_ensure_network_found( + self.dut, ssid) + @test_tracker_info(uuid="770caebe-bcb1-43ac-95b6-5dd52dd90e80") def test_scan_with_wifi_off_and_location_scan_off(self): """Turn off wifi and location scan""" @@ -614,32 +640,6 @@ class WifiManagerTest(WifiBaseTest): nw[WifiEnums.BSSID_KEY] != ssid, "Found forgotten network %s in configured networks." % ssid) - @test_tracker_info(uuid="b306d65c-6df3-4eb5-a178-6278bdc76c3e") - def test_reconnect_to_connected_network(self): - """Connect to a network and immediately issue reconnect. - - Steps: - 1. Connect to a 2GHz network. - 2. Reconnect to the network using its network id. - 3. Connect to a 5GHz network. - 4. Reconnect to the network using its network id. - - """ - connect_2g_data = self.get_connection_data(self.dut, self.wpapsk_2g) - reconnect_2g = self.connect_to_wifi_network_with_id( - connect_2g_data[WifiEnums.NETID_KEY], - connect_2g_data[WifiEnums.SSID_KEY]) - if not reconnect_2g: - raise signals.TestFailure("Device did not connect to the correct" - " 2GHz network.") - connect_5g_data = self.get_connection_data(self.dut, self.wpapsk_5g) - reconnect_5g = self.connect_to_wifi_network_with_id( - connect_5g_data[WifiEnums.NETID_KEY], - connect_5g_data[WifiEnums.SSID_KEY]) - if not reconnect_5g: - raise signals.TestFailure("Device did not connect to the correct" - " 5GHz network.") - @test_tracker_info(uuid="3cff17f6-b684-4a95-a438-8272c2ad441d") def test_reconnect_to_previously_connected(self): """Connect to multiple networks and reconnect to the previous network. @@ -943,12 +943,33 @@ class WifiManagerTest(WifiBaseTest): 3. Let DUT sleep for 5 minutes 4. Check DUT can be pinged by DUT_Client """ + asserts.skip_if(len(self.android_devices) < 3, "Need 3 devices") + self.dut_client_a = self.android_devices[1] + self.dut_client_b = self.android_devices[2] + + # enable hotspot on dut and connect client devices to it + ap_ssid = "softap_" + acts.utils.rand_ascii_str(8) + ap_password = acts.utils.rand_ascii_str(8) + self.dut.log.info("softap setup: %s %s", ap_ssid, ap_password) + config = {wutils.WifiEnums.SSID_KEY: ap_ssid} + config[wutils.WifiEnums.PWD_KEY] = ap_password + wutils.start_wifi_tethering( + self.dut, + config[wutils.WifiEnums.SSID_KEY], + config[wutils.WifiEnums.PWD_KEY], + wutils.WifiEnums.WIFI_CONFIG_APBAND_AUTO) + # DUT connect to AP - wutils.connect_to_wifi_network(self.dut, self.wpa_networks[0]["2g"]) - wutils.connect_to_wifi_network(self.dut_client, self.wpa_networks[0]["2g"]) + wutils.connect_to_wifi_network( + self.dut_client_a, config, check_connectivity=False) + wutils.connect_to_wifi_network( + self.dut_client_b, config, check_connectivity=False) # Check DUT and DUT_Client can ping each other successfully - self.verify_traffic_between_devices(self.dut,self.dut_client) - self.verify_traffic_between_devices(self.dut_client,self.dut) + self.verify_traffic_between_devices(self.dut_client_a, + self.dut_client_b) + self.verify_traffic_between_devices(self.dut_client_a, + self.dut_client_b) + # DUT turn off screen and go sleep for 5 mins self.dut.droid.wakeLockRelease() self.dut.droid.goToSleepNow() @@ -957,7 +978,8 @@ class WifiManagerTest(WifiBaseTest): self.log.info("Sleep for 5 minutes") time.sleep(300) # Verify DUT_Client can ping DUT when DUT sleeps - self.verify_traffic_between_devices(self.dut,self.dut_client) + self.verify_traffic_between_devices(self.dut_client_a, + self.dut_client_b) self.dut.droid.wakeLockAcquireBright() self.dut.droid.wakeUpNow() @@ -976,3 +998,42 @@ class WifiManagerTest(WifiBaseTest): "wifi state changed after reboot") disable_bluetooth(self.dut.droid) + + @test_tracker_info(uuid="d0e14a2d-a28f-4551-8988-1e15d9d8bb1a") + def test_scan_result_api(self): + """Register scan result callback, start scan and wait for event""" + self.dut.ed.clear_all_events() + self.dut.droid.wifiStartScanWithListener() + try: + events = self.dut.ed.pop_events( + "WifiManagerScanResultsCallbackOnSuccess", 60) + except queue.Empty: + asserts.fail( + "Wi-Fi scan results did not become available within 60s.") + + @test_tracker_info(uuid="03cfbc86-7fcc-48d8-ab0f-1f6f3523e596") + def test_enable_disable_auto_join_saved_network(self): + """ + Add a saved network, simulate user change the auto join to false, ensure the device doesn't + auto connect to this network + + Steps: + 1. Create a saved network. + 2. Add this saved network, and ensure we connect to this network + 3. Simulate user change the auto join to false. + 4. Toggle the Wifi off and on + 4. Ensure device doesn't connect to his network + """ + network = self.open_network_5g + wutils.connect_to_wifi_network(self.dut, network) + info = self.dut.droid.wifiGetConnectionInfo() + network_id = info[WifiEnums.NETID_KEY] + self.dut.log.info("Disable auto join on network") + self.dut.droid.wifiEnableAutojoin(network_id, False) + wutils.wifi_toggle_state(self.dut, False) + wutils.wifi_toggle_state(self.dut, True) + asserts.assert_false( + wutils.wait_for_connect(self.dut, network[WifiEnums.SSID_KEY], + assert_on_fail=False), "Device should not connect.") + self.dut.droid.wifiEnableAutojoin(network_id, True) + wutils.wait_for_connect(self.dut, network[WifiEnums.SSID_KEY], assert_on_fail=False) diff --git a/acts/tests/google/wifi/WifiNativeTest.py b/acts_tests/tests/google/wifi/WifiNativeTest.py index 56e21955a7..56e21955a7 100644 --- a/acts/tests/google/wifi/WifiNativeTest.py +++ b/acts_tests/tests/google/wifi/WifiNativeTest.py diff --git a/acts/tests/google/wifi/WifiNetworkRequestTest.py b/acts_tests/tests/google/wifi/WifiNetworkRequestTest.py index 7fed0ad1c7..7a1d8bcc32 100644 --- a/acts/tests/google/wifi/WifiNetworkRequestTest.py +++ b/acts_tests/tests/google/wifi/WifiNetworkRequestTest.py @@ -61,6 +61,10 @@ class WifiNetworkRequestTest(WifiBaseTest): if "AccessPoint" in self.user_params: self.legacy_configure_ap_and_start(wpa_network=True, wep_network=True) + elif "OpenWrtAP" in self.user_params: + self.configure_openwrt_ap_and_start(open_network=True, + wpa_network=True, + wep_network=True) asserts.assert_true( len(self.reference_networks) > 0, @@ -84,6 +88,10 @@ class WifiNetworkRequestTest(WifiBaseTest): self.dut.droid.wifiReleaseNetworkAll() self.dut.droid.wifiDisconnect() wutils.reset_wifi(self.dut) + # Ensure we disconnected from the current network before the next test. + if self.dut.droid.wifiGetConnectionInfo()["supplicant_state"] != "disconnected": + wutils.wait_for_disconnect(self.dut) + wutils.wifi_toggle_state(self.dut, False) self.dut.ed.clear_all_events() def on_fail(self, test_name, begin_time): @@ -200,6 +208,7 @@ class WifiNetworkRequestTest(WifiBaseTest): # Ensure we disconnected from the previous network. wutils.wait_for_disconnect(self.dut) self.dut.log.info("Disconnected from network %s", self.wpa_psk_2g) + self.dut.ed.clear_all_events() # Complete flow for the second request. wutils.wifi_connect_using_network_request(self.dut, self.open_5g, self.open_5g) @@ -333,8 +342,7 @@ class WifiNetworkRequestTest(WifiBaseTest): self.wpa_psk_2g) # Simulate user forgeting the ephemeral network. - self.dut.droid.wifiDisableEphemeralNetwork( - self.wpa_psk_2g[WifiEnums.SSID_KEY]) + self.dut.droid.wifiUserDisconnectNetwork(self.wpa_psk_2g[WifiEnums.SSID_KEY]) # Ensure we disconnected from the network. wutils.wait_for_disconnect(self.dut) self.dut.log.info("Disconnected from network %s", self.wpa_psk_2g) @@ -385,11 +393,17 @@ class WifiNetworkRequestTest(WifiBaseTest): that does not match any networks. Steps: - 1. Send a network specifier with the non-matching SSID pattern. - 2. Ensure that the platform does not retrun any matching networks. - 3. Wait for the request to timeout. + 1. Trigger a connect to one of the networks (as a saved network). + 2. Send a network specifier with the non-matching SSID pattern. + 3. Ensure that the platform does not return any matching networks. + 4. Wait for the request to timeout. """ network = self.wpa_psk_5g + + # Trigger a connection to a network as a saved network before the + # request and ensure that this does not change the behavior. + wutils.connect_to_wifi_network(self.dut, network, check_connectivity=False) + network_specifier = self.wpa_psk_5g.copy(); # Remove ssid & replace with invalid ssid pattern. network_ssid = network_specifier.pop(WifiEnums.SSID_KEY) @@ -406,19 +420,17 @@ class WifiNetworkRequestTest(WifiBaseTest): time.sleep(wifi_constants.NETWORK_REQUEST_CB_REGISTER_DELAY_SEC) self.dut.droid.wifiRegisterNetworkRequestMatchCallback() # Wait for the request to timeout. - timeout_secs = \ - NETWORK_REQUEST_TIMEOUT_MS / 1000 + NETWORK_REQUEST_INSTANT_FAILURE_TIMEOUT_SEC + timeout_secs = NETWORK_REQUEST_TIMEOUT_MS * 2 / 1000 try: on_unavailable_event = self.dut.ed.pop_event( wifi_constants.WIFI_NETWORK_CB_ON_UNAVAILABLE, timeout_secs) asserts.assert_true(on_unavailable_event, "Network request did not timeout") - except queue.Empty: asserts.fail("No events returned") finally: self.dut.droid.wifiStopTrackingStateChange() - @test_tracker_info(uuid="760c3768-697d-442b-8d61-cfe02f10ceff") + @test_tracker_info(uuid="caa96f57-840e-4997-9280-655edd3b76ee") def test_connect_failure_user_rejected(self): """ Initiates a connection to network via network request with specific SSID diff --git a/acts/tests/google/wifi/WifiNetworkSelectorTest.py b/acts_tests/tests/google/wifi/WifiNetworkSelectorTest.py index 5af4ad9584..41f723c599 100644 --- a/acts/tests/google/wifi/WifiNetworkSelectorTest.py +++ b/acts_tests/tests/google/wifi/WifiNetworkSelectorTest.py @@ -35,9 +35,13 @@ AP_1_2G_ATTENUATOR = 0 AP_1_5G_ATTENUATOR = 1 AP_2_2G_ATTENUATOR = 2 AP_2_5G_ATTENUATOR = 3 -ATTENUATOR_INITIAL_SETTING = 60 # WifiNetworkSelector imposes a 10 seconds gap between two selections NETWORK_SELECTION_TIME_GAP = 12 +LVL1_ATTN = 15 +LVL2_ATTN = 30 +MIN_ATTN = 0 +MAX_ATTN = 95 +ATTN_SLEEP = 12 class WifiNetworkSelectorTest(WifiBaseTest): @@ -50,44 +54,31 @@ class WifiNetworkSelectorTest(WifiBaseTest): self.dut = self.android_devices[0] wutils.wifi_test_device_init(self.dut) - req_params = [] - opt_param = ["open_network", "reference_networks"] - self.unpack_userparams( - req_param_names=req_params, opt_param_names=opt_param) - - if hasattr(self, 'access_points'): - self.legacy_configure_ap_and_start(ap_count=2) - - if hasattr(self, 'packet_capture'): - self.configure_packet_capture() + self.legacy_configure_ap_and_start(ap_count=2, mirror_ap=False) + self.configure_packet_capture() def setup_test(self): - #reset and clear all saved networks on the DUT - wutils.reset_wifi(self.dut) - #move the APs out of range - for attenuator in self.attenuators: - attenuator.set_atten(ATTENUATOR_INITIAL_SETTING) - #turn on the screen self.dut.droid.wakeLockAcquireBright() self.dut.droid.wakeUpNow() self.dut.ed.clear_all_events() - - if hasattr(self, 'packet_capture'): - self.pcap_procs = wutils.start_pcap( - self.packet_capture, 'dual', self.test_name) + self.pcap_procs = wutils.start_pcap( + self.packet_capture, 'dual', self.test_name) + for a in self.attenuators: + a.set_atten(MAX_ATTN) + time.sleep(ATTN_SLEEP) def teardown_test(self): - #turn off the screen + for a in self.attenuators: + a.set_atten(MIN_ATTN) + wutils.reset_wifi(self.dut) self.dut.droid.wakeLockRelease() self.dut.droid.goToSleepNow() def on_pass(self, test_name, begin_time): - if hasattr(self, 'packet_capture'): - wutils.stop_pcap(self.packet_capture, self.pcap_procs, True) + wutils.stop_pcap(self.packet_capture, self.pcap_procs, True) def on_fail(self, test_name, begin_time): - if hasattr(self, 'packet_capture'): - wutils.stop_pcap(self.packet_capture, self.pcap_procs, False) + wutils.stop_pcap(self.packet_capture, self.pcap_procs, False) self.dut.take_bug_report(test_name, begin_time) self.dut.cat_adb_log(test_name, begin_time) @@ -108,13 +99,14 @@ class WifiNetworkSelectorTest(WifiBaseTest): """ for network in networks: ret = ad.droid.wifiAddNetwork(network) - asserts.assert_true(ret != -1, "Failed to add network %s" % - network) + asserts.assert_true(ret != -1, + "Failed to add network %s" % network) ad.droid.wifiEnableNetwork(ret, 0) + configured_networks = ad.droid.wifiGetConfiguredNetworks() - logging.debug("Configured networks: %s", configured_networks) + self.log.info("Configured networks: %s", configured_networks) - def connect_and_verify_connected_bssid(self, expected_bssid): + def connect_and_verify_connected_bssid(self, network): """Start a scan to get the DUT connected to an AP and verify the DUT is connected to the correct BSSID. @@ -124,22 +116,19 @@ class WifiNetworkSelectorTest(WifiBaseTest): Returns: True if connection to given network happen, else return False. """ - #wait for the attenuator to stablize - time.sleep(10) - #force start a single scan so we don't have to wait for the - #WCM scheduled scan. - wutils.start_wifi_connection_scan(self.dut) - #wait for connection + expected_ssid = network['SSID'] + expected_bssid = network['bssid'] + wutils.start_wifi_connection_scan_and_ensure_network_found( + self.dut, expected_ssid) time.sleep(20) - #verify connection actual_network = self.dut.droid.wifiGetConnectionInfo() - logging.info("Actual network: %s", actual_network) - try: - asserts.assert_equal(expected_bssid, - actual_network[WifiEnums.BSSID_KEY]) - except: - msg = "Device did not connect to any network." - raise signals.TestFailure(msg) + self.log.info("Actual network: %s", actual_network) + asserts.assert_true( + actual_network and WifiEnums.BSSID_KEY in actual_network and \ + expected_bssid == actual_network[WifiEnums.BSSID_KEY], + "Expected BSSID: %s, Actual BSSID: %s" % + (expected_bssid, actual_network[WifiEnums.BSSID_KEY])) + self.log.info("DUT connected to valid network: %s" % expected_bssid) """ Tests Begin """ @@ -150,14 +139,17 @@ class WifiNetworkSelectorTest(WifiBaseTest): 2. Move the DUT in range. 3. Verify the DUT is connected to the network. """ - #add a saved network to DUT + # add a saved network to DUT networks = [self.reference_networks[AP_1]['5g']] self.add_networks(self.dut, networks) - #move the DUT in range - self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0) - #verify - self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][ - '5g']['bssid']) + + # move the DUT in range + self.attenuators[AP_1_5G_ATTENUATOR].set_atten(MIN_ATTN) + time.sleep(ATTN_SLEEP) + + # verify DUT is connected to AP_1 5g network + self.connect_and_verify_connected_bssid( + self.reference_networks[AP_1]['5g']) @test_tracker_info(uuid="3ea818f2-10d7-4aad-bfab-7d8fb25aae78") def test_network_selector_basic_connection_prefer_5g(self): @@ -166,18 +158,19 @@ class WifiNetworkSelectorTest(WifiBaseTest): 2. Move the DUT in range. 3. Verify the DUT is connected to the 5G BSSID. """ - #add a saved network with both 2G and 5G BSSIDs to DUT - # TODO: bmahadev Change this to a single SSID once we migrate tests to - # use dynamic AP. + # add a saved network with both 2G and 5G BSSIDs to DUT networks = [self.reference_networks[AP_1]['2g'], self.reference_networks[AP_1]['5g']] self.add_networks(self.dut, networks) - #move the DUT in range - self.attenuators[AP_1_2G_ATTENUATOR].set_atten(0) - self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0) - #verify - self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][ - '5g']['bssid']) + + # Move DUT in range + self.attenuators[AP_1_2G_ATTENUATOR].set_atten(MIN_ATTN) + self.attenuators[AP_1_5G_ATTENUATOR].set_atten(MIN_ATTN) + time.sleep(ATTN_SLEEP) + + # verify DUT is connected to 5G network + self.connect_and_verify_connected_bssid( + self.reference_networks[AP_1]['5g']) @test_tracker_info(uuid="bebb29ca-4486-4cde-b390-c5f8f2e1580c") def test_network_selector_prefer_stronger_rssi(self): @@ -187,18 +180,19 @@ class WifiNetworkSelectorTest(WifiBaseTest): 2. Move the DUT in range. 3. Verify the DUT is connected to the SSID with stronger RSSI. """ - #add a 2G and a 5G saved network to DUT - networks = [ - self.reference_networks[AP_1]['2g'], self.reference_networks[AP_2][ - '2g'] - ] + # add a 2G and a 5G saved network to DUT + networks = [self.reference_networks[AP_1]['2g'], + self.reference_networks[AP_2]['2g']] self.add_networks(self.dut, networks) - #move the DUT in range - self.attenuators[AP_1_2G_ATTENUATOR].set_atten(20) - self.attenuators[AP_2_2G_ATTENUATOR].set_atten(40) - #verify - self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][ - '2g']['bssid']) + + # move the DUT in range + self.attenuators[AP_1_2G_ATTENUATOR].set_atten(LVL1_ATTN) + self.attenuators[AP_2_2G_ATTENUATOR].set_atten(LVL2_ATTN) + time.sleep(ATTN_SLEEP) + + # verify DUT is connected AP_1 + self.connect_and_verify_connected_bssid( + self.reference_networks[AP_1]['2g']) @test_tracker_info(uuid="f9f72dc5-034f-4fe2-a27d-df1b6cae76cd") def test_network_selector_prefer_secure_over_open_network(self): @@ -208,17 +202,18 @@ class WifiNetworkSelectorTest(WifiBaseTest): 2. Move the DUT in range. 3. Verify the DUT is connected to the secure network that uses WPA2. """ - #add a open network and a secure saved network to DUT - networks = [ - self.open_network[AP_1]['5g'], self.reference_networks[AP_1]['5g'] - ] + # add a open network and a secure saved network to DUT + networks = [self.open_network[AP_1]['5g'], + self.reference_networks[AP_1]['5g']] self.add_networks(self.dut, networks) - #move the DUT in range - #TODO: control open network attenuator - self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0) - #verify - self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][ - '5g']['bssid']) + + # Move DUT in range + self.attenuators[AP_1_5G_ATTENUATOR].set_atten(MIN_ATTN) + time.sleep(ATTN_SLEEP) + + # verify DUT connects to secure network + self.connect_and_verify_connected_bssid( + self.reference_networks[AP_1]['5g']) @test_tracker_info(uuid="ab2c527c-0f9c-4f09-a13f-e3f461b7da52") def test_network_selector_blacklist_by_connection_failure(self): @@ -228,26 +223,30 @@ class WifiNetworkSelectorTest(WifiBaseTest): 2. Move the DUT in range. 3. Verify the DUT is connected to network Y. """ - #add two saved networks to DUT, and one of them is configured with incorrect password + # add two saved networks to DUT, and one of them is configured with + # incorrect password wrong_passwd_network = self.reference_networks[AP_1]['5g'].copy() wrong_passwd_network['password'] += 'haha' networks = [wrong_passwd_network, self.reference_networks[AP_2]['5g']] self.add_networks(self.dut, networks) - #make both AP_1 5G and AP_2 5G in range, and AP_1 5G has stronger RSSI than AP_2 5G - self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0) - self.attenuators[AP_2_5G_ATTENUATOR].set_atten(10) - #start 3 scans to get AP_1 5G blacklisted because of the incorrect password - count = 0 - while count < 3: - wutils.start_wifi_connection_scan(self.dut) + + # make AP_1 5G has stronger RSSI than AP_2 5G + self.attenuators[AP_1_5G_ATTENUATOR].set_atten(MIN_ATTN) + self.attenuators[AP_2_5G_ATTENUATOR].set_atten(LVL1_ATTN) + time.sleep(ATTN_SLEEP) + + # start 3 scans to get AP_1 5G blacklisted because of the incorrect + # password + for _ in range(3): + wutils.start_wifi_connection_scan_and_return_status(self.dut) time.sleep(NETWORK_SELECTION_TIME_GAP) - count += 1 - #verify - self.connect_and_verify_connected_bssid(self.reference_networks[AP_2][ - '5g']['bssid']) + + # verify DUT is connect AP_2 5G + self.connect_and_verify_connected_bssid( + self.reference_networks[AP_2]['5g']) @test_tracker_info(uuid="71d88fcf-c7b8-4fd2-a7cb-84ac4a130ecf") - def test_network_selector_2g_to_5g_prefer_same_SSID(self): + def network_selector_2g_to_5g_prefer_same_SSID(self): """ 1. Add SSID_A and SSID_B to DUT. Both SSIDs have both 2G and 5G BSSIDs. @@ -277,7 +276,7 @@ class WifiNetworkSelectorTest(WifiBaseTest): '5g']['bssid']) @test_tracker_info(uuid="c1243cf4-d96e-427e-869e-3d640bee3f28") - def test_network_selector_2g_to_5g_different_ssid(self): + def network_selector_2g_to_5g_different_ssid(self): """ 1. Add SSID_A and SSID_B to DUT. Both SSIDs have both 2G and 5G BSSIDs. @@ -287,14 +286,13 @@ class WifiNetworkSelectorTest(WifiBaseTest): 2G RSSI. 4. Verify the DUT switches to SSID_B's 5G. """ - #add two saved networks to DUT - networks = [ - self.reference_networks[AP_1]['2g'], self.reference_networks[AP_2][ - '2g'] - ] + # add two saved networks to DUT + networks = [self.reference_networks[AP_1]['2g'], + self.reference_networks[AP_2]['2g']] self.add_networks(self.dut, networks) - #make both AP_1 2G and AP_2 5G in range, and AP_1 2G - #has much stronger RSSI than AP_2 5G + + # make both AP_1 2G and AP_2 5G in range, and AP_1 2G + # has much stronger RSSI than AP_2 5G self.attenuators[AP_1_2G_ATTENUATOR].set_atten(0) self.attenuators[AP_2_5G_ATTENUATOR].set_atten(20) #verify @@ -310,7 +308,7 @@ class WifiNetworkSelectorTest(WifiBaseTest): '5g']['bssid']) @test_tracker_info(uuid="10da95df-83ed-4447-89f8-735b08dbe2eb") - def test_network_selector_5g_to_2g_same_ssid(self): + def network_selector_5g_to_2g_same_ssid(self): """ 1. Add one SSID that has both 2G and 5G to the DUT. 2. Attenuate down the 2G RSSI. @@ -346,26 +344,30 @@ class WifiNetworkSelectorTest(WifiBaseTest): 3. Change attenuation so that Y's RSSI goes above X's. 4. Verify the DUT stays on X. """ - #add two saved networks to DUT - networks = [ - self.reference_networks[AP_1]['5g'], self.reference_networks[AP_2][ - '5g'] - ] + # add two saved networks to DUT + networks = [self.reference_networks[AP_1]['5g'], + self.reference_networks[AP_2]['5g']] self.add_networks(self.dut, networks) - #make both AP_1 5G and AP_2 5G in range, and AP_1 5G - #has stronger RSSI than AP_2 5G - self.attenuators[AP_1_5G_ATTENUATOR].set_atten(10) - self.attenuators[AP_2_5G_ATTENUATOR].set_atten(20) - #verify - self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][ - '5g']['bssid']) - #bump up AP_2 5G RSSI over AP_1 5G RSSI - self.attenuators[AP_2_5G_ATTENUATOR].set_atten(0) - #ensure the time gap between two network selections + + # make both AP_1 5G and AP_2 5G in range, and AP_1 5G + # has stronger RSSI than AP_2 5G + self.attenuators[AP_1_5G_ATTENUATOR].set_atten(LVL1_ATTN) + self.attenuators[AP_2_5G_ATTENUATOR].set_atten(LVL2_ATTN) + time.sleep(ATTN_SLEEP) + + # verify DUT is connected to AP_1 + self.connect_and_verify_connected_bssid( + self.reference_networks[AP_1]['5g']) + + # bump up AP_2 5G RSSI over AP_1 5G RSSI + self.attenuators[AP_2_5G_ATTENUATOR].set_atten(MIN_ATTN) + + # ensure the time gap between two network selections time.sleep(NETWORK_SELECTION_TIME_GAP) - #verify - self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][ - '5g']['bssid']) + + # verify DUT is still connected to AP_1 + self.connect_and_verify_connected_bssid( + self.reference_networks[AP_1]['5g']) @test_tracker_info(uuid="5470010f-8b62-4b1c-8b83-1f91422eced0") def test_network_selector_stay_on_user_selected_network(self): @@ -375,23 +377,23 @@ class WifiNetworkSelectorTest(WifiBaseTest): 3. Start a scan and network selection. 4. Verify DUT stays on SSID_A. """ - #make AP_1 5G in range with a low RSSI - self.attenuators[AP_1_5G_ATTENUATOR].set_atten(10) - #connect to AP_1 via user selection - wutils.wifi_connect(self.dut, self.reference_networks[AP_1]['5g']) - #verify - self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][ - '5g']['bssid']) - #make AP_2 5G in range with a strong RSSI - self.attenuators[AP_2_5G_ATTENUATOR].set_atten(0) - #add AP_2 as a saved network to DUT + # set max attenuation on AP_2 and make AP_1 5G in range with low RSSI + self.attenuators[AP_2_5G_ATTENUATOR].set_atten(MIN_ATTN) + self.attenuators[AP_1_5G_ATTENUATOR].set_atten(LVL1_ATTN) + time.sleep(ATTN_SLEEP) + + # connect to AP_1 via user selection and add, save AP_2 + wutils.connect_to_wifi_network( + self.dut, self.reference_networks[AP_1]['5g']) networks = [self.reference_networks[AP_2]['5g']] self.add_networks(self.dut, networks) - #ensure the time gap between two network selections + + # ensure the time gap between two network selections time.sleep(NETWORK_SELECTION_TIME_GAP) - #verify we are still connected to AP_1 5G - self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][ - '5g']['bssid']) + + # verify we are still connected to AP_1 5G + self.connect_and_verify_connected_bssid( + self.reference_networks[AP_1]['5g']) @test_tracker_info(uuid="f08d8f73-8c94-42af-bba9-4c49bbf16420") def test_network_selector_reselect_after_forget_network(self): @@ -402,22 +404,25 @@ class WifiNetworkSelectorTest(WifiBaseTest): 3. Forget X. 5. Verify the DUT reselect and connect to Y. """ - #add two saved networks to DUT - networks = [ - self.reference_networks[AP_1]['5g'], self.reference_networks[AP_2][ - '5g'] - ] + # add two networks to DUT + networks = [self.reference_networks[AP_1]['5g'], + self.reference_networks[AP_2]['5g']] self.add_networks(self.dut, networks) - #make both AP_1 5G and AP_2 5G in range. AP_1 5G has stronger - #RSSI than AP_2 5G - self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0) - self.attenuators[AP_2_5G_ATTENUATOR].set_atten(10) - #verify - self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][ - '5g']['bssid']) - #forget AP_1 - wutils.wifi_forget_network(self.dut, - self.reference_networks[AP_1]['5g']['SSID']) - #verify - self.connect_and_verify_connected_bssid(self.reference_networks[AP_2][ - '5g']['bssid']) + + # make both AP_1 5G and AP_2 5G in range. AP_1 5G has stronger + # RSSI than AP_2 5G + self.attenuators[AP_1_5G_ATTENUATOR].set_atten(MIN_ATTN) + self.attenuators[AP_2_5G_ATTENUATOR].set_atten(LVL1_ATTN) + time.sleep(ATTN_SLEEP) + + # verify DUT connected to AP1 + self.connect_and_verify_connected_bssid( + self.reference_networks[AP_1]['5g']) + + # forget AP_1 + wutils.wifi_forget_network( + self.dut, self.reference_networks[AP_1]['5g']['SSID']) + + # verify DUT connected to AP2 + self.connect_and_verify_connected_bssid( + self.reference_networks[AP_2]['5g']) diff --git a/acts_tests/tests/google/wifi/WifiNetworkSuggestionTest.py b/acts_tests/tests/google/wifi/WifiNetworkSuggestionTest.py new file mode 100644 index 0000000000..f62ba56526 --- /dev/null +++ b/acts_tests/tests/google/wifi/WifiNetworkSuggestionTest.py @@ -0,0 +1,873 @@ +#!/usr/bin/env python3.4 +# +# Copyright 2018 - The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import itertools +import pprint +import queue +import time + +import acts.base_test +import acts.signals as signals +import acts.test_utils.wifi.wifi_test_utils as wutils +import acts.utils + +from acts import asserts +from acts.controllers.android_device import SL4A_APK_NAME +from acts.test_decorators import test_tracker_info +from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest +from acts.test_utils.wifi import wifi_constants + +WifiEnums = wutils.WifiEnums +# EAP Macros +EAP = WifiEnums.Eap +EapPhase2 = WifiEnums.EapPhase2 +# Enterprise Config Macros +Ent = WifiEnums.Enterprise +ATT = 2 +# Suggestion network Macros +Untrusted = "untrusted" +AutoJoin = "enableAutojoin" +# Network request Macros +ClearCapabilities = "ClearCapabilities" +TransportType = "TransportType" + + +# Default timeout used for reboot, toggle WiFi and Airplane mode, +# for the system to settle down after the operation. +DEFAULT_TIMEOUT = 10 +PASSPOINT_TIMEOUT = 30 + + +class WifiNetworkSuggestionTest(WifiBaseTest): + """Tests for WifiNetworkSuggestion API surface. + + Test Bed Requirement: + * one Android device + * Several Wi-Fi networks visible to the device, including an open Wi-Fi + network. + """ + + def setup_class(self): + super().setup_class() + + self.dut = self.android_devices[0] + wutils.wifi_test_device_init(self.dut) + opt_param = [ + "open_network", "reference_networks", "hidden_networks", "radius_conf_2g", + "radius_conf_5g", "ca_cert", "eap_identity", "eap_password", "passpoint_networks", + "altsubject_match"] + self.unpack_userparams(opt_param_names=opt_param,) + + if "AccessPoint" in self.user_params: + self.legacy_configure_ap_and_start( + wpa_network=True, ent_network=True, + radius_conf_2g=self.radius_conf_2g, + radius_conf_5g=self.radius_conf_5g,) + elif "OpenWrtAP" in self.user_params: + self.configure_openwrt_ap_and_start(open_network=True, + wpa_network=True,) + if hasattr(self, "reference_networks") and \ + isinstance(self.reference_networks, list): + self.wpa_psk_2g = self.reference_networks[0]["2g"] + self.wpa_psk_5g = self.reference_networks[0]["5g"] + if hasattr(self, "open_network") and isinstance(self.open_network,list): + self.open_2g = self.open_network[0]["2g"] + self.open_5g = self.open_network[0]["5g"] + if hasattr(self, "hidden_networks") and \ + isinstance(self.hidden_networks, list): + self.hidden_network = self.hidden_networks[0] + self.dut.droid.wifiRemoveNetworkSuggestions([]) + self.dut.adb.shell( + "pm disable com.google.android.apps.carrier.carrierwifi") + + def setup_test(self): + self.dut.droid.wakeLockAcquireBright() + self.dut.droid.wakeUpNow() + self.dut.unlock_screen() + self.clear_user_disabled_networks() + wutils.wifi_toggle_state(self.dut, True) + self.dut.ed.clear_all_events() + self.clear_carrier_approved(str(self.dut.droid.telephonyGetSimCarrierId())) + if "_ent_" in self.test_name: + if "OpenWrtAP" in self.user_params: + self.access_points[0].close() + self.configure_openwrt_ap_and_start( + ent_network=True, + radius_conf_2g=self.radius_conf_2g, + radius_conf_5g=self.radius_conf_5g,) + self.ent_network_2g = self.ent_networks[0]["2g"] + self.ent_network_5g = self.ent_networks[0]["5g"] + + def teardown_test(self): + self.dut.droid.wakeLockRelease() + self.dut.droid.goToSleepNow() + self.dut.droid.wifiRemoveNetworkSuggestions([]) + self.dut.droid.wifiDisconnect() + wutils.reset_wifi(self.dut) + wutils.wifi_toggle_state(self.dut, False) + self.dut.ed.clear_all_events() + self.clear_carrier_approved(str(self.dut.droid.telephonyGetSimCarrierId())) + + def on_fail(self, test_name, begin_time): + self.dut.take_bug_report(test_name, begin_time) + self.dut.cat_adb_log(test_name, begin_time) + + def teardown_class(self): + self.dut.adb.shell( + "pm enable com.google.android.apps.carrier.carrierwifi") + if "AccessPoint" in self.user_params: + del self.user_params["reference_networks"] + del self.user_params["open_network"] + + """Helper Functions""" + def set_approved(self, approved): + self.dut.log.debug("Setting suggestions from sl4a app " + + "approved" if approved else "not approved") + self.dut.adb.shell("cmd wifi network-suggestions-set-user-approved" + + " " + SL4A_APK_NAME + + " " + ("yes" if approved else "no")) + + def is_approved(self): + is_approved_str = self.dut.adb.shell( + "cmd wifi network-suggestions-has-user-approved" + + " " + SL4A_APK_NAME) + return True if (is_approved_str == "yes") else False + + def set_carrier_approved(self, carrier_id, approved): + self.dut.log.debug(("Setting IMSI protection exemption for carrier: " + carrier_id + + "approved" if approved else "not approved")) + self.dut.adb.shell("cmd wifi imsi-protection-exemption-set-user-approved-for-carrier" + + " " + carrier_id + + " " + ("yes" if approved else "no")) + + def is_carrier_approved(self, carrier_id): + is_approved_str = self.dut.adb.shell( + "cmd wifi imsi-protection-exemption-has-user-approved-for-carrier" + + " " + carrier_id) + return True if (is_approved_str == "yes") else False + + def clear_carrier_approved(self, carrier_id): + self.dut.adb.shell( + "cmd wifi imsi-protection-exemption-clear-user-approved-for-carrier" + + " " + carrier_id) + + def clear_user_disabled_networks(self): + self.dut.log.debug("Clearing user disabled networks") + self.dut.adb.shell( + "cmd wifi clear-user-disabled-networks") + + def add_suggestions_and_ensure_connection(self, network_suggestions, + expected_ssid, + expect_post_connection_broadcast): + if expect_post_connection_broadcast is not None: + self.dut.droid.wifiStartTrackingNetworkSuggestionStateChange() + + self.dut.log.info("Adding network suggestions") + asserts.assert_true( + self.dut.droid.wifiAddNetworkSuggestions(network_suggestions), + "Failed to add suggestions") + # Enable suggestions by the app. + self.dut.log.debug("Enabling suggestions from test") + self.set_approved(True) + wutils.start_wifi_connection_scan_and_return_status(self.dut) + # if suggestion is passpoint wait longer for connection. + if "profile" in network_suggestions: + time.sleep(PASSPOINT_TIMEOUT) + wutils.wait_for_connect(self.dut, expected_ssid) + + if expect_post_connection_broadcast is None: + return + + # Check if we expected to get the broadcast. + try: + event = self.dut.ed.pop_event( + wifi_constants.WIFI_NETWORK_SUGGESTION_POST_CONNECTION, 60) + except queue.Empty: + if expect_post_connection_broadcast: + raise signals.TestFailure( + "Did not receive post connection broadcast") + else: + if not expect_post_connection_broadcast: + raise signals.TestFailure( + "Received post connection broadcast") + finally: + self.dut.droid.wifiStopTrackingNetworkSuggestionStateChange() + self.dut.ed.clear_all_events() + + def remove_suggestions_disconnect_and_ensure_no_connection_back(self, + network_suggestions, + expected_ssid): + # Remove suggestion trigger disconnect and wait for the disconnect. + self.dut.log.info("Removing network suggestions") + asserts.assert_true( + self.dut.droid.wifiRemoveNetworkSuggestions(network_suggestions), + "Failed to remove suggestions") + wutils.wait_for_disconnect(self.dut) + self.dut.ed.clear_all_events() + + # Now ensure that we didn't connect back. + asserts.assert_false( + wutils.wait_for_connect(self.dut, expected_ssid, assert_on_fail=False), + "Device should not connect back") + + def _test_connect_to_wifi_network_reboot_config_store(self, + network_suggestions, + wifi_network): + """ Test network suggestion with reboot config store + + Args: + 1. network_suggestions: network suggestions in list to add to the device. + 2. wifi_network: expected wifi network to connect to + """ + + self.add_suggestions_and_ensure_connection( + network_suggestions, wifi_network[WifiEnums.SSID_KEY], None) + + # Reboot and wait for connection back to the same suggestion. + self.dut.reboot() + time.sleep(DEFAULT_TIMEOUT) + + wutils.wait_for_connect(self.dut, wifi_network[WifiEnums.SSID_KEY]) + + self.remove_suggestions_disconnect_and_ensure_no_connection_back( + network_suggestions, wifi_network[WifiEnums.SSID_KEY]) + + # Reboot with empty suggestion, verify user approval is kept. + self.dut.reboot() + time.sleep(DEFAULT_TIMEOUT) + asserts.assert_true(self.is_approved(), "User approval should be kept") + + @test_tracker_info(uuid="bda8ed20-4382-4380-831a-64cf77eca108") + def test_connect_to_wpa_psk_2g(self): + """ Adds a network suggestion and ensure that the device connected. + + Steps: + 1. Send a network suggestion to the device. + 2. Wait for the device to connect to it. + 3. Ensure that we did not receive the post connection broadcast + (isAppInteractionRequired = False). + 4. Remove the suggestions and ensure the device does not connect back. + """ + self.add_suggestions_and_ensure_connection( + [self.wpa_psk_2g], self.wpa_psk_2g[WifiEnums.SSID_KEY], + False) + + self.remove_suggestions_disconnect_and_ensure_no_connection_back( + [self.wpa_psk_2g], self.wpa_psk_2g[WifiEnums.SSID_KEY]) + + @test_tracker_info(uuid="b2df6ebe-9c5b-4e84-906a-e76f96fcef56") + def test_connect_to_wpa_psk_2g_with_screen_off(self): + """ Adds a network suggestion and ensure that the device connected + when the screen is off. + + Steps: + 1. Send an invalid suggestion to the device (Needed for PNO scan to start). + 2. Toggle screen off. + 3. Send a valid network suggestion to the device. + 4. Wait for the device to connect to it. + 5. Ensure that we did not receive the post connection broadcast + (isAppInteractionRequired = False). + 6. Remove the suggestions and ensure the device does not connect back. + """ + invalid_suggestion = self.wpa_psk_5g + network_ssid = invalid_suggestion.pop(WifiEnums.SSID_KEY) + invalid_suggestion[WifiEnums.SSID_KEY] = network_ssid + "blah" + + self.dut.log.info("Adding invalid suggestions") + asserts.assert_true( + self.dut.droid.wifiAddNetworkSuggestions([invalid_suggestion]), + "Failed to add suggestions") + + # Approve suggestions by the app. + self.set_approved(True) + + # Turn screen off to ensure PNO kicks-in. + self.dut.droid.wakeLockRelease() + self.dut.droid.goToSleepNow() + time.sleep(10) + + # Add valid suggestions & ensure we restart PNO and connect to it. + self.add_suggestions_and_ensure_connection( + [self.wpa_psk_2g], self.wpa_psk_2g[WifiEnums.SSID_KEY], + False) + + self.remove_suggestions_disconnect_and_ensure_no_connection_back( + [self.wpa_psk_2g], self.wpa_psk_2g[WifiEnums.SSID_KEY]) + + @test_tracker_info(uuid="f18bf994-ef3b-45d6-aba0-dd6338b07979") + def test_connect_to_wpa_psk_2g_modify_meteredness(self): + """ Adds a network suggestion and ensure that the device connected. + Change the meteredness of the network after the connection. + + Steps: + 1. Send a network suggestion to the device. + 2. Wait for the device to connect to it. + 3. Ensure that we did not receive the post connection broadcast + (isAppInteractionRequired = False). + 4. Mark the network suggestion metered. + 5. Ensure that the device disconnected and reconnected back to the + suggestion. + 6. Mark the network suggestion unmetered. + 7. Ensure that the device did not disconnect. + 8. Remove the suggestions and ensure the device does not connect back. + """ + self.add_suggestions_and_ensure_connection( + [self.wpa_psk_2g], self.wpa_psk_2g[WifiEnums.SSID_KEY], + False) + + mod_suggestion = self.wpa_psk_2g + + # Mark the network metered. + self.dut.log.debug("Marking suggestion as metered") + mod_suggestion[WifiEnums.IS_SUGGESTION_METERED] = True + asserts.assert_true( + self.dut.droid.wifiAddNetworkSuggestions([mod_suggestion]), + "Failed to add suggestions") + # Wait for disconnect. + wutils.wait_for_disconnect(self.dut) + self.dut.log.info("Disconnected from network %s", mod_suggestion) + self.dut.ed.clear_all_events() + # Wait for reconnect. + wutils.wait_for_connect(self.dut, mod_suggestion[WifiEnums.SSID_KEY]) + + # Mark the network unmetered. + self.dut.log.debug("Marking suggestion as unmetered") + mod_suggestion[WifiEnums.IS_SUGGESTION_METERED] = False + asserts.assert_true( + self.dut.droid.wifiAddNetworkSuggestions([mod_suggestion]), + "Failed to add suggestions") + # Ensure there is no disconnect. + wutils.ensure_no_disconnect(self.dut) + self.dut.ed.clear_all_events() + + self.remove_suggestions_disconnect_and_ensure_no_connection_back( + [mod_suggestion], mod_suggestion[WifiEnums.SSID_KEY]) + + + @test_tracker_info(uuid="f54bc250-d9e9-4f00-8b5b-b866e8550b43") + def test_connect_to_highest_priority(self): + """ + Adds network suggestions and ensures that device connects to + the suggestion with the highest priority. + + Steps: + 1. Send 2 network suggestions to the device (with different priorities). + 2. Wait for the device to connect to the network with the highest + priority. + 3. In-place modify network suggestions with priorities reversed + 4. Restart wifi, wait for the device to connect to the network with the highest + priority. + 5. Re-add the suggestions with the priorities reversed again. + 6. Again wait for the device to connect to the network with the highest + priority. + """ + network_suggestion_2g = self.wpa_psk_2g + network_suggestion_5g = self.wpa_psk_5g + + # Add suggestions & wait for the connection event. + network_suggestion_2g[WifiEnums.PRIORITY] = 5 + network_suggestion_5g[WifiEnums.PRIORITY] = 2 + self.add_suggestions_and_ensure_connection( + [network_suggestion_2g, network_suggestion_5g], + self.wpa_psk_2g[WifiEnums.SSID_KEY], + None) + + # In-place modify Reverse the priority, should be no disconnect + network_suggestion_2g[WifiEnums.PRIORITY] = 2 + network_suggestion_5g[WifiEnums.PRIORITY] = 5 + self.dut.log.info("Modifying network suggestions") + asserts.assert_true( + self.dut.droid.wifiAddNetworkSuggestions([network_suggestion_2g, + network_suggestion_5g]), + "Failed to add suggestions") + wutils.ensure_no_disconnect(self.dut) + + # Disable and re-enable wifi, should connect to higher priority + wutils.wifi_toggle_state(self.dut, False) + time.sleep(DEFAULT_TIMEOUT) + wutils.wifi_toggle_state(self.dut, True) + wutils.start_wifi_connection_scan_and_return_status(self.dut) + wutils.wait_for_connect(self.dut, self.wpa_psk_5g[WifiEnums.SSID_KEY]) + + self.remove_suggestions_disconnect_and_ensure_no_connection_back( + [], self.wpa_psk_5g[WifiEnums.SSID_KEY]) + + # Reverse the priority. + # Add suggestions & wait for the connection event. + network_suggestion_2g[WifiEnums.PRIORITY] = 5 + network_suggestion_5g[WifiEnums.PRIORITY] = 2 + self.add_suggestions_and_ensure_connection( + [network_suggestion_2g, network_suggestion_5g], + self.wpa_psk_2g[WifiEnums.SSID_KEY], + None) + + @test_tracker_info(uuid="b1d27eea-23c8-4c4f-b944-ef118e4cc35f") + def test_connect_to_wpa_psk_2g_with_post_connection_broadcast(self): + """ Adds a network suggestion and ensure that the device connected. + + Steps: + 1. Send a network suggestion to the device with + isAppInteractionRequired set. + 2. Wait for the device to connect to it. + 3. Ensure that we did receive the post connection broadcast + (isAppInteractionRequired = True). + 4. Remove the suggestions and ensure the device does not connect back. + """ + network_suggestion = self.wpa_psk_2g + network_suggestion[WifiEnums.IS_APP_INTERACTION_REQUIRED] = True + self.add_suggestions_and_ensure_connection( + [network_suggestion], self.wpa_psk_2g[WifiEnums.SSID_KEY], + True) + self.remove_suggestions_disconnect_and_ensure_no_connection_back( + [self.wpa_psk_2g], self.wpa_psk_2g[WifiEnums.SSID_KEY]) + + @test_tracker_info(uuid="a036a24d-29c0-456d-ae6a-afdde34da710") + def test_connect_to_wpa_psk_5g_reboot_config_store(self): + """ + Adds a network suggestion and ensure that the device connects to it + after reboot. + + Steps: + 1. Send a network suggestion to the device. + 2. Wait for the device to connect to it. + 3. Ensure that we did not receive the post connection broadcast + (isAppInteractionRequired = False). + 4. Reboot the device. + 5. Wait for the device to connect to back to it. + 6. Remove the suggestions and ensure the device does not connect back. + 7. Reboot the device again, ensure user approval is kept + """ + self._test_connect_to_wifi_network_reboot_config_store( + [self.wpa_psk_5g], self.wpa_psk_5g) + + @test_tracker_info(uuid="61649a2b-0f00-4272-9b9b-40ad5944da31") + def test_connect_to_wpa_ent_config_aka_reboot_config_store(self): + """ + Adds a network suggestion and ensure that the device connects to it + after reboot. + + Steps: + 1. Send a Enterprise AKA network suggestion to the device. + 2. Wait for the device to connect to it. + 3. Ensure that we did not receive the post connection broadcast. + 4. Reboot the device. + 5. Wait for the device to connect to the wifi network. + 6. Remove suggestions and ensure device doesn't connect back to it. + 7. Reboot the device again, ensure user approval is kept + """ + self.config_aka = { + Ent.EAP: int(EAP.AKA), + WifiEnums.SSID_KEY: self.ent_network_2g[WifiEnums.SSID_KEY], + } + if "carrierId" in self.config_aka: + self.set_carrier_approved(self.config_aka["carrierId"], True) + self._test_connect_to_wifi_network_reboot_config_store( + [self.config_aka], self.ent_network_2g) + if "carrierId" in self.config_aka: + self.clear_carrier_approved(self.config_aka["carrierId"]) + + @test_tracker_info(uuid="98b2d40a-acb4-4a2f-aba1-b069e2a1d09d") + def test_connect_to_wpa_ent_config_ttls_pap_reboot_config_store(self): + """ + Adds a network suggestion and ensure that the device connects to it + after reboot. + + Steps: + 1. Send a Enterprise TTLS PAP network suggestion to the device. + 2. Wait for the device to connect to it. + 3. Ensure that we did not receive the post connection broadcast. + 4. Reboot the device. + 5. Wait for the device to connect to the wifi network. + 6. Remove suggestions and ensure device doesn't connect back to it. + 7. Reboot the device again, ensure user approval is kept + """ + self.config_ttls = { + Ent.EAP: int(EAP.TTLS), + Ent.CA_CERT: self.ca_cert, + Ent.IDENTITY: self.eap_identity, + Ent.PASSWORD: self.eap_password, + Ent.PHASE2: int(EapPhase2.MSCHAPV2), + WifiEnums.SSID_KEY: self.ent_network_2g[WifiEnums.SSID_KEY], + } + config = dict(self.config_ttls) + config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.PAP.value + + self._test_connect_to_wifi_network_reboot_config_store( + [config], self.ent_network_2g) + + @test_tracker_info(uuid="554b5861-22d0-4922-a5f4-712b4cf564eb") + def test_fail_to_connect_to_wpa_psk_5g_when_not_approved(self): + """ + Adds a network suggestion and ensure that the device does not + connect to it until we approve the app. + + Steps: + 1. Send a network suggestion to the device with the app not approved. + 2. Ensure the network is present in scan results, but we don't connect + to it. + 3. Now approve the app. + 4. Wait for the device to connect to it. + """ + self.dut.log.info("Adding network suggestions") + asserts.assert_true( + self.dut.droid.wifiAddNetworkSuggestions([self.wpa_psk_5g]), + "Failed to add suggestions") + + # Disable suggestions by the app. + self.set_approved(False) + + # Ensure the app is not approved. + asserts.assert_false( + self.is_approved(), + "Suggestions should be disabled") + + # Start a new scan to trigger auto-join. + wutils.start_wifi_connection_scan_and_ensure_network_found( + self.dut, self.wpa_psk_5g[WifiEnums.SSID_KEY]) + + # Ensure we don't connect to the network. + asserts.assert_false( + wutils.wait_for_connect( + self.dut, self.wpa_psk_5g[WifiEnums.SSID_KEY], assert_on_fail=False), + "Should not connect to network suggestions from unapproved app") + + self.dut.log.info("Enabling suggestions from test") + # Now Enable suggestions by the app & ensure we connect to the network. + self.set_approved(True) + + # Ensure the app is approved. + asserts.assert_true( + self.is_approved(), + "Suggestions should be enabled") + + # Start a new scan to trigger auto-join. + wutils.start_wifi_connection_scan_and_ensure_network_found( + self.dut, self.wpa_psk_5g[WifiEnums.SSID_KEY]) + + wutils.wait_for_connect(self.dut, self.wpa_psk_5g[WifiEnums.SSID_KEY]) + + @test_tracker_info(uuid="98400dea-776e-4a0a-9024-18845b27331c") + def test_fail_to_connect_to_wpa_psk_2g_after_user_forgot_network(self): + """ + Adds a network suggestion and ensures that the device does not + connect to it after the user forgot the network previously. + + Steps: + 1. Send a network suggestion to the device with + isAppInteractionRequired set. + 2. Wait for the device to connect to it. + 3. Ensure that we did receive the post connection broadcast + (isAppInteractionRequired = True). + 4. Simulate user forgetting the network and the device does not + connecting back even though the suggestion is active from the app. + """ + network_suggestion = self.wpa_psk_2g + network_suggestion[WifiEnums.IS_APP_INTERACTION_REQUIRED] = True + self.add_suggestions_and_ensure_connection( + [network_suggestion], self.wpa_psk_2g[WifiEnums.SSID_KEY], + True) + + # Simulate user disconnect the network. + self.dut.droid.wifiUserDisconnectNetwork( + self.wpa_psk_2g[WifiEnums.SSID_KEY]) + wutils.wait_for_disconnect(self.dut) + self.dut.log.info("Disconnected from network %s", self.wpa_psk_2g) + self.dut.ed.clear_all_events() + + # Now ensure that we don't connect back even though the suggestion + # is still active. + asserts.assert_false( + wutils.wait_for_connect(self.dut, + self.wpa_psk_2g[WifiEnums.SSID_KEY], + assert_on_fail=False), + "Device should not connect back") + + @test_tracker_info(uuid="93c86b05-fa56-4d79-ad27-009a16f691b1") + def test_connect_to_hidden_network(self): + """ + Adds a network suggestion with hidden SSID config, ensure device can scan + and connect to this network. + + Steps: + 1. Send a hidden network suggestion to the device. + 2. Wait for the device to connect to it. + 3. Ensure that we did not receive the post connection broadcast + (isAppInteractionRequired = False). + 4. Remove the suggestions and ensure the device does not connect back. + """ + asserts.skip_if(not hasattr(self, "hidden_networks"), "No hidden networks, skip this test") + + network_suggestion = self.hidden_network + self.add_suggestions_and_ensure_connection( + [network_suggestion], network_suggestion[WifiEnums.SSID_KEY], False) + self.remove_suggestions_disconnect_and_ensure_no_connection_back( + [network_suggestion], network_suggestion[WifiEnums.SSID_KEY]) + + @test_tracker_info(uuid="806dff14-7543-482b-bd0a-598de59374b3") + def test_connect_to_passpoint_network_with_post_connection_broadcast(self): + """ Adds a passpoint network suggestion and ensure that the device connected. + + Steps: + 1. Send a network suggestion to the device. + 2. Wait for the device to connect to it. + 3. Ensure that we did receive the post connection broadcast + (isAppInteractionRequired = true). + 4. Remove the suggestions and ensure the device does not connect back. + """ + asserts.skip_if(not hasattr(self, "passpoint_networks"), + "No passpoint networks, skip this test") + passpoint_config = self.passpoint_networks[ATT] + passpoint_config[WifiEnums.IS_APP_INTERACTION_REQUIRED] = True + if "carrierId" in passpoint_config: + self.set_carrier_approved(passpoint_config["carrierId"], True) + self.add_suggestions_and_ensure_connection([passpoint_config], + passpoint_config[WifiEnums.SSID_KEY], True) + self.remove_suggestions_disconnect_and_ensure_no_connection_back( + [passpoint_config], passpoint_config[WifiEnums.SSID_KEY]) + if "carrierId" in passpoint_config: + self.clear_carrier_approved(passpoint_config["carrierId"]) + + @test_tracker_info(uuid="159b8b8c-fb00-4d4e-a29f-606881dcbf44") + def test_connect_to_passpoint_network_reboot_config_store(self): + """ + Adds a passpoint network suggestion and ensure that the device connects to it + after reboot. + + Steps: + 1. Send a network suggestion to the device. + 2. Wait for the device to connect to it. + 3. Ensure that we did not receive the post connection broadcast + (isAppInteractionRequired = False). + 4. Reboot the device. + 5. Wait for the device to connect to back to it. + 6. Remove the suggestions and ensure the device does not connect back. + 7. Reboot the device again, ensure user approval is kept + """ + asserts.skip_if(not hasattr(self, "passpoint_networks"), + "No passpoint networks, skip this test") + passpoint_config = self.passpoint_networks[ATT] + if "carrierId" in passpoint_config: + self.set_carrier_approved(passpoint_config["carrierId"], True) + self._test_connect_to_wifi_network_reboot_config_store([passpoint_config], + passpoint_config) + if "carrierId" in passpoint_config: + self.clear_carrier_approved(passpoint_config["carrierId"]) + + @test_tracker_info(uuid="34f3d28a-bedf-43fe-a12d-2cfadf6bc6eb") + def test_fail_to_connect_to_passpoint_network_when_not_approved(self): + """ + Adds a passpoint network suggestion and ensure that the device does not + connect to it until we approve the app. + + Steps: + 1. Send a network suggestion to the device with the app not approved. + 2. Ensure the network is present in scan results, but we don't connect + to it. + 3. Now approve the app. + 4. Wait for the device to connect to it. + """ + asserts.skip_if(not hasattr(self, "passpoint_networks"), + "No passpoint networks, skip this test") + passpoint_config = self.passpoint_networks[ATT] + if "carrierId" in passpoint_config: + self.set_carrier_approved(passpoint_config["carrierId"], True) + self.dut.log.info("Adding network suggestions") + asserts.assert_true( + self.dut.droid.wifiAddNetworkSuggestions([passpoint_config]), + "Failed to add suggestions") + + # Disable suggestions by the app. + self.set_approved(False) + + # Ensure the app is not approved. + asserts.assert_false( + self.is_approved(), + "Suggestions should be disabled") + + # Start a new scan to trigger auto-join. + wutils.start_wifi_connection_scan_and_ensure_network_found( + self.dut, passpoint_config[WifiEnums.SSID_KEY]) + + # Ensure we don't connect to the network. + asserts.assert_false( + wutils.wait_for_connect( + self.dut, passpoint_config[WifiEnums.SSID_KEY], assert_on_fail=False), + "Should not connect to network suggestions from unapproved app") + + self.dut.log.info("Enabling suggestions from test") + # Now Enable suggestions by the app & ensure we connect to the network. + self.set_approved(True) + + # Ensure the app is approved. + asserts.assert_true( + self.is_approved(), + "Suggestions should be enabled") + + # Start a new scan to trigger auto-join. + wutils.start_wifi_connection_scan_and_ensure_network_found( + self.dut, passpoint_config[WifiEnums.SSID_KEY]) + time.sleep(PASSPOINT_TIMEOUT) + wutils.wait_for_connect(self.dut, passpoint_config[WifiEnums.SSID_KEY]) + if "carrierId" in passpoint_config: + self.clear_carrier_approved(passpoint_config["carrierId"]) + + @test_tracker_info(uuid="cf624cda-4d25-42f1-80eb-6c717fb08338") + def test_fail_to_connect_to_passpoint_network_when_imsi_protection_exemption_not_approved(self): + """ + Adds a passpoint network suggestion using SIM credential without IMSI privacy protection. + Before user approves the exemption, ensure that the device does noconnect to it until we + approve the carrier exemption. + + Steps: + 1. Send a network suggestion to the device with IMSI protection exemption not approved. + 2. Ensure the network is present in scan results, but we don't connect + to it. + 3. Now approve the carrier. + 4. Wait for the device to connect to it. + """ + asserts.skip_if(not hasattr(self, "passpoint_networks"), + "No passpoint networks, skip this test") + passpoint_config = self.passpoint_networks[ATT] + asserts.skip_if("carrierId" not in passpoint_config, + "Not a SIM based passpoint network, skip this test") + + # Ensure the carrier is not approved. + asserts.assert_false( + self.is_carrier_approved(passpoint_config["carrierId"]), + "Carrier shouldn't be approved") + + self.dut.log.info("Adding network suggestions") + asserts.assert_true( + self.dut.droid.wifiAddNetworkSuggestions([passpoint_config]), + "Failed to add suggestions") + + # Start a new scan to trigger auto-join. + wutils.start_wifi_connection_scan_and_ensure_network_found( + self.dut, passpoint_config[WifiEnums.SSID_KEY]) + + # Ensure we don't connect to the network. + asserts.assert_false( + wutils.wait_for_connect( + self.dut, passpoint_config[WifiEnums.SSID_KEY], assert_on_fail=False), + "Should not connect to network suggestions from unapproved app") + + self.dut.log.info("Enabling suggestions from test") + # Now approve IMSI protection exemption by carrier & ensure we connect to the network. + self.set_carrier_approved(passpoint_config["carrierId"], True) + + # Ensure the carrier is approved. + asserts.assert_true( + self.is_carrier_approved(passpoint_config["carrierId"]), + "Carrier should be approved") + + # Start a new scan to trigger auto-join. + wutils.start_wifi_connection_scan_and_ensure_network_found( + self.dut, passpoint_config[WifiEnums.SSID_KEY]) + time.sleep(PASSPOINT_TIMEOUT) + wutils.wait_for_connect(self.dut, passpoint_config[WifiEnums.SSID_KEY]) + self.clear_carrier_approved(passpoint_config["carrierId"]) + + @test_tracker_info(uuid="e35f99c8-78a4-4b96-9258-f9834b6ddd33") + def test_initial_auto_join_on_network_suggestion(self): + """ + Add a network suggestion with enableAutojoin bit set to false, ensure the device doesn't + auto connect to this network + + Steps: + 1. Create a network suggestion. + 2. Set EnableAutojoin to false. + 3. Add this suggestion + 4. Ensure device doesn't connect to his network + """ + network_suggestion = self.wpa_psk_5g + # Set suggestion auto join initial to false. + network_suggestion[AutoJoin] = False + self.dut.log.info("Adding network suggestions") + asserts.assert_true( + self.dut.droid.wifiAddNetworkSuggestions([network_suggestion]), + "Failed to add suggestions") + # Enable suggestions by the app. + self.dut.log.debug("Enabling suggestions from test") + self.set_approved(True) + wutils.start_wifi_connection_scan_and_return_status(self.dut) + asserts.assert_false( + wutils.wait_for_connect(self.dut, network_suggestion[WifiEnums.SSID_KEY], + assert_on_fail=False), "Device should not connect.") + + @test_tracker_info(uuid="ff4e451f-a380-4ff5-a5c2-dd9b1633d5e5") + def test_user_override_auto_join_on_network_suggestion(self): + """ + Add a network suggestion, user change the auto join to false, ensure the device doesn't + auto connect to this network + + Steps: + 1. Create a network suggestion. + 2. Add this suggestion, and ensure we connect to this network + 3. Simulate user change the auto join to false. + 4. Toggle the Wifi off and on + 4. Ensure device doesn't connect to his network + """ + network_suggestion = self.wpa_psk_5g + self.add_suggestions_and_ensure_connection([network_suggestion], + network_suggestion[WifiEnums.SSID_KEY], False) + wifi_info = self.dut.droid.wifiGetConnectionInfo() + self.dut.log.info(wifi_info) + network_id = wifi_info[WifiEnums.NETID_KEY] + # Simulate user disable auto join through Settings. + self.dut.log.info("Disable auto join on suggestion") + self.dut.droid.wifiEnableAutojoin(network_id, False) + wutils.wifi_toggle_state(self.dut, False) + wutils.wifi_toggle_state(self.dut, True) + asserts.assert_false( + wutils.wait_for_connect(self.dut, network_suggestion[WifiEnums.SSID_KEY], + assert_on_fail=False), "Device should not connect.") + + @test_tracker_info(uuid="32201b1c-76a0-46dc-9983-2cd24312a783") + def test_untrusted_suggestion_without_untrusted_request(self): + """ + Add an untrusted network suggestion, when no untrusted request, will not connect to it. + Steps: + 1. Create a untrusted network suggestion. + 2. Add this suggestion, and ensure device do not connect to this network + 3. Request untrusted network and ensure device connect to this network + """ + network_suggestion = self.open_5g + network_suggestion[Untrusted] = True + self.dut.log.info("Adding network suggestions") + asserts.assert_true( + self.dut.droid.wifiAddNetworkSuggestions([network_suggestion]), + "Failed to add suggestions") + # Start a new scan to trigger auto-join. + wutils.start_wifi_connection_scan_and_ensure_network_found( + self.dut, network_suggestion[WifiEnums.SSID_KEY]) + + # Ensure we don't connect to the network. + asserts.assert_false( + wutils.wait_for_connect( + self.dut, network_suggestion[WifiEnums.SSID_KEY], assert_on_fail=False), + "Should not connect to untrusted network suggestions with no request") + network_request = {ClearCapabilities: True, TransportType: 1} + req_key = self.dut.droid.connectivityRequestNetwork(network_request) + + # Start a new scan to trigger auto-join. + wutils.start_wifi_connection_scan_and_ensure_network_found( + self.dut, network_suggestion[WifiEnums.SSID_KEY]) + + wutils.wait_for_connect( + self.dut, network_suggestion[WifiEnums.SSID_KEY], assert_on_fail=False) + + self.dut.droid.connectivityUnregisterNetworkCallback(req_key) + diff --git a/acts/tests/google/wifi/WifiNewSetupAutoJoinTest.py b/acts_tests/tests/google/wifi/WifiNewSetupAutoJoinTest.py index b5ba8997d7..b5ba8997d7 100644 --- a/acts/tests/google/wifi/WifiNewSetupAutoJoinTest.py +++ b/acts_tests/tests/google/wifi/WifiNewSetupAutoJoinTest.py diff --git a/acts/tests/google/wifi/WifiPasspointTest.py b/acts_tests/tests/google/wifi/WifiPasspointTest.py index b867faad98..962d9dba2f 100755 --- a/acts/tests/google/wifi/WifiPasspointTest.py +++ b/acts_tests/tests/google/wifi/WifiPasspointTest.py @@ -85,6 +85,9 @@ class WifiPasspointTest(acts.base_test.BaseTestClass): def teardown_test(self): self.dut.droid.wakeLockRelease() self.dut.droid.goToSleepNow() + passpoint_configs = self.dut.droid.getPasspointConfigs() + for config in passpoint_configs: + wutils.delete_passpoint(self.dut, config) wutils.reset_wifi(self.dut) @@ -119,20 +122,18 @@ class WifiPasspointTest(acts.base_test.BaseTestClass): """ ad = self.dut ad.ed.clear_all_events() - wutils.start_wifi_connection_scan(ad) - scan_results = ad.droid.wifiGetScanResults() - # Wait for scan to complete. - time.sleep(5) - ssid = passpoint_network - wutils.assert_network_in_list({WifiEnums.SSID_KEY: ssid}, scan_results) - # Passpoint network takes longer time to connect than normal networks. - # Every try comes with a timeout of 30s. Setting total timeout to 120s. - wutils.wifi_passpoint_connect(self.dut, passpoint_network, num_of_tries=4) + try: + wutils.start_wifi_connection_scan_and_return_status(ad) + wutils.wait_for_connect(ad) + except: + pass # Re-verify we are connected to the correct network. network_info = self.dut.droid.wifiGetConnectionInfo() - if network_info[WifiEnums.SSID_KEY] != passpoint_network: - raise signals.TestFailure("Device did not connect to the passpoint" - " network.") + self.log.info("Network Info: %s" % network_info) + if not network_info or not network_info[WifiEnums.SSID_KEY] or \ + network_info[WifiEnums.SSID_KEY] not in passpoint_network: + raise signals.TestFailure( + "Device did not connect to passpoint network.") def get_configured_passpoint_and_delete(self): @@ -342,6 +343,7 @@ class WifiPasspointTest(acts.base_test.BaseTestClass): wutils.wait_for_disconnect(self.dut) + @test_tracker_info(uuid="37ae0223-0cb7-43f3-8ba8-474fad6e4b71") def test_install_att_passpoint_profile(self): """Add an AT&T Passpoint profile. @@ -395,3 +397,26 @@ class WifiPasspointTest(acts.base_test.BaseTestClass): @test_tracker_info(uuid="f43ea759-673f-4567-aa11-da3bc2cabf08") def test_start_subscription_provisioning_and_toggle_wifi(self): self.start_subscription_provisioning(TOGGLE) + + @test_tracker_info(uuid="ad6d5eb8-a3c5-4ce0-9e10-d0f201cd0f40") + def test_user_override_auto_join_on_passpoint_network(self): + """Add a Passpoint network, simulate user change the auto join to false, ensure the device + doesn't auto connect to this passponit network + + Steps: + 1. Install a Passpoint Profile. + 2. Verify the device connects to the required Passpoint SSID. + 3. Disable auto join Passpoint configuration using its FQDN. + 4. disable and enable Wifi toggle, ensure we don't connect back + """ + passpoint_config = self.passpoint_networks[BOINGO] + self.install_passpoint_profile(passpoint_config) + ssid = passpoint_config[WifiEnums.SSID_KEY] + self.check_passpoint_connection(ssid) + self.dut.log.info("Disable auto join on passpoint") + self.dut.droid.wifiEnableAutojoinPasspoint(passpoint_config['fqdn'], False) + wutils.wifi_toggle_state(self.dut, False) + wutils.wifi_toggle_state(self.dut, True) + asserts.assert_false( + wutils.wait_for_connect(self.dut, ssid, assert_on_fail=False), + "Device should not connect.") diff --git a/acts/tests/google/wifi/WifiPerformancePreflightTest.py b/acts_tests/tests/google/wifi/WifiPerformancePreflightTest.py index 35f29666da..35f29666da 100644 --- a/acts/tests/google/wifi/WifiPerformancePreflightTest.py +++ b/acts_tests/tests/google/wifi/WifiPerformancePreflightTest.py diff --git a/acts/tests/google/wifi/WifiPingTest.py b/acts_tests/tests/google/wifi/WifiPingTest.py index cd9c52c39d..181d04d73f 100644 --- a/acts/tests/google/wifi/WifiPingTest.py +++ b/acts_tests/tests/google/wifi/WifiPingTest.py @@ -188,20 +188,18 @@ class WifiPingTest(base_test.BaseTestClass): Args: result: dict containing ping results and meta data """ - # Get target range - #rvr_range = self.get_range_from_rvr() - # Set Blackbox metric - if self.publish_testcase_metrics: - self.testcase_metric_logger.add_metric('ping_range', - result['range']) # Evaluate test pass/fail test_message = ('Attenuation at range is {}dB. ' 'LLStats at Range: {}'.format( result['range'], result['llstats_at_range'])) if result['peak_throughput_pct'] < 95: asserts.fail("(RESULT NOT RELIABLE) {}".format(test_message)) - else: - asserts.explicit_pass(test_message) + + # If pass, set Blackbox metric + if self.publish_testcase_metrics: + self.testcase_metric_logger.add_metric('ping_range', + result['range']) + asserts.explicit_pass(test_message) def pass_fail_check(self, result): if 'range' in result['testcase_params']['test_type']: diff --git a/acts/tests/google/wifi/WifiPnoTest.py b/acts_tests/tests/google/wifi/WifiPnoTest.py index 4bfa1d7c49..ba2eb4ec81 100644 --- a/acts/tests/google/wifi/WifiPnoTest.py +++ b/acts_tests/tests/google/wifi/WifiPnoTest.py @@ -115,17 +115,17 @@ class WifiPnoTest(WifiBaseTest): finally: pass - def add_and_enable_dummy_networks(self, num_networks): - """Add some dummy networks to the device and enable them. + def add_and_enable_test_networks(self, num_networks): + """Add some test networks to the device and enable them. Args: num_networks: Number of networks to add. """ - ssid_name_base = "pno_dummy_network_" + ssid_name_base = "pno_test_network_" for i in range(0, num_networks): network = {} network[WifiEnums.SSID_KEY] = ssid_name_base + str(i) - network[WifiEnums.PWD_KEY] = "pno_dummy" + network[WifiEnums.PWD_KEY] = "pno_test" self.add_network_and_enable(network) def add_network_and_enable(self, network): @@ -178,13 +178,13 @@ class WifiPnoTest(WifiBaseTest): 16 is the max list size of PNO watch list for most devices. The device should automatically pick the 16 latest added networks in the list. - So add 16 dummy networks and then add 2 valid networks. + So add 16 test networks and then add 2 valid networks. Steps: - 1. Save 16 dummy network configurations in the device. + 1. Save 16 test network configurations in the device. 2. Run the simple pno test. """ - self.add_and_enable_dummy_networks(16) + self.add_and_enable_test_networks(16) self.add_network_and_enable(self.pno_network_a) self.add_network_and_enable(self.pno_network_b) # Force single scan so that both networks become preferred before PNO. diff --git a/acts/tests/google/wifi/WifiPreFlightTest.py b/acts_tests/tests/google/wifi/WifiPreFlightTest.py index 14a4190c85..14a4190c85 100644 --- a/acts/tests/google/wifi/WifiPreFlightTest.py +++ b/acts_tests/tests/google/wifi/WifiPreFlightTest.py diff --git a/acts/tests/google/wifi/WifiRoamingPerformanceTest.py b/acts_tests/tests/google/wifi/WifiRoamingPerformanceTest.py index bfc96d5656..bfc96d5656 100644 --- a/acts/tests/google/wifi/WifiRoamingPerformanceTest.py +++ b/acts_tests/tests/google/wifi/WifiRoamingPerformanceTest.py diff --git a/acts/tests/google/wifi/WifiRoamingTest.py b/acts_tests/tests/google/wifi/WifiRoamingTest.py index 4a64ec1318..b0f1a71f7f 100644 --- a/acts/tests/google/wifi/WifiRoamingTest.py +++ b/acts_tests/tests/google/wifi/WifiRoamingTest.py @@ -16,6 +16,8 @@ import pprint import random import time +from acts import context +from scapy.all import * from acts import asserts from acts import base_test @@ -25,6 +27,11 @@ from acts.test_utils.wifi import wifi_test_utils as wutils from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest WifiEnums = wutils.WifiEnums +DEF_ATTN = 60 +MAX_ATTN = 95 +ROAM_DBM = -75 +WAIT_AFTER_ATTN = 12 +ATTN_STEP = 5 class WifiRoamingTest(WifiBaseTest): @@ -54,7 +61,9 @@ class WifiRoamingTest(WifiBaseTest): asserts.assert_true( len(self.open_network) > 1, "Need at least two open networks for roaming") - wutils.wifi_toggle_state(self.dut, True) + + + self.configure_packet_capture() def teardown_class(self): self.dut.ed.clear_all_events() @@ -71,6 +80,8 @@ class WifiRoamingTest(WifiBaseTest): self.dut.droid.wakeLockRelease() self.dut.droid.goToSleepNow() wutils.reset_wifi(self.dut) + for a in self.attenuators: + a.set_atten(0) def on_fail(self, test_name, begin_time): self.dut.cat_adb_log(test_name, begin_time) @@ -91,10 +102,89 @@ class WifiRoamingTest(WifiBaseTest): 5. Validate connection information and ping. """ wutils.set_attns(self.attenuators, "AP1_on_AP2_off") - wutils.wifi_connect(self.dut, AP1_network) + wifi_config = AP1_network.copy() + wifi_config.pop("bssid") + wutils.connect_to_wifi_network(self.dut, wifi_config) self.log.info("Roaming from %s to %s", AP1_network, AP2_network) - wutils.trigger_roaming_and_validate(self.dut, self.attenuators, - "AP1_off_AP2_on", AP2_network) + wutils.trigger_roaming_and_validate( + self.dut, self.attenuators, "AP1_off_AP2_on", AP2_network) + + def get_rssi(self, pcap_file, expected_bssid): + """Get signal strength of the wifi network attenuated. + + Args: + pcap_file: PCAP file path. + expected_bssid: BSSID of the wifi network attenuated. + """ + packets = [] + try: + packets = rdpcap(pcap_file) + except Scapy_Exception: + self.log.error("Failed to read pcap file") + if not packets: + return 0 + + dbm = -100 + for pkt in packets: + if pkt and hasattr(pkt, 'type') and pkt.type == 0 and \ + pkt.subtype == 8 and hasattr(pkt, 'info'): + bssid = pkt.addr3 + if expected_bssid == bssid: + dbm = int(pkt.dBm_AntSignal) + self.log.info("RSSI: %s" % dbm) + return dbm + + def trigger_roaming_and_verify_attenuation(self, network): + """Trigger roaming and verify signal strength is below roaming limit. + + Args: + network: Wifi network that is being attenuated. + """ + wutils.set_attns_steps(self.attenuators, "AP1_off_AP2_on") + band = '5G' if network['SSID'].startswith('5g_') else '2G' + attn = DEF_ATTN + ATTN_STEP + while attn <= MAX_ATTN: + self.pcap_procs = wutils.start_pcap( + self.packet_capture, 'dual', self.test_name) + time.sleep(WAIT_AFTER_ATTN/3) + wutils.stop_pcap(self.packet_capture, self.pcap_procs, False) + pcap_file = os.path.join( + context.get_current_context().get_full_output_path(), + 'PacketCapture', + '%s_%s.pcap' % (self.test_name, band)) + + rssi = self.get_rssi(pcap_file, network["bssid"]) + if rssi == 0: + self.log.error("Failed to verify signal strength") + break + if self.get_rssi(pcap_file, network["bssid"]) < ROAM_DBM: + break + + self.attenuators[0].set_atten(attn) + self.attenuators[1].set_atten(attn) + time.sleep(WAIT_AFTER_ATTN) # allow some time for attenuation + attn += 5 + + def validate_roaming(self, expected_con): + """Validate roaming. + + Args: + expected_con: Expected wifi network after roaming. + """ + expected_con = { + WifiEnums.SSID_KEY: expected_con[WifiEnums.SSID_KEY], + WifiEnums.BSSID_KEY: expected_con["bssid"], + } + curr_con = self.dut.droid.wifiGetConnectionInfo() + for key in expected_con: + if expected_con[key] != curr_con[key]: + asserts.fail("Expected '%s' to be %s, actual is %s." % + (key, expected_con[key], curr_con[key])) + self.log.info("Roamed to %s successfully", + expected_con[WifiEnums.BSSID_KEY]) + if not wutils.validate_connection(self.dut): + raise signals.TestFailure("Fail to connect to internet on %s" % + expected_con[WifiEnums.BSSID_KEY]) """ Tests Begin. diff --git a/acts/tests/google/wifi/WifiRssiTest.py b/acts_tests/tests/google/wifi/WifiRssiTest.py index 9ff9afa678..9ff9afa678 100644 --- a/acts/tests/google/wifi/WifiRssiTest.py +++ b/acts_tests/tests/google/wifi/WifiRssiTest.py diff --git a/acts/tests/google/wifi/WifiRttManagerTest.py b/acts_tests/tests/google/wifi/WifiRttManagerTest.py index 743d89579d..f0985dec44 100644 --- a/acts/tests/google/wifi/WifiRttManagerTest.py +++ b/acts_tests/tests/google/wifi/WifiRttManagerTest.py @@ -405,7 +405,7 @@ class WifiRttManagerTest(acts.base_test.BaseTestClass): """Tests""" def test_invalid_params(self): - """Tests the sanity check function in RttManager. + """Tests the check function in RttManager. """ param_list = [{ RttParam.device_type: 3 diff --git a/acts/tests/google/wifi/WifiRvrTest.py b/acts_tests/tests/google/wifi/WifiRvrTest.py index 8a8c8431f4..3033c59360 100644 --- a/acts/tests/google/wifi/WifiRvrTest.py +++ b/acts_tests/tests/google/wifi/WifiRvrTest.py @@ -62,7 +62,6 @@ class WifiRvrTest(base_test.BaseTestClass): This function initializes hardwares and compiles parameters that are common to all tests in this class. """ - self.dut = self.android_devices[-1] req_params = [ 'RetailAccessPoints', 'rvr_test_params', 'testbed_params', 'RemoteServer', 'main_network' @@ -97,10 +96,11 @@ class WifiRvrTest(base_test.BaseTestClass): # Turn WiFi ON if self.testclass_params.get('airplane_mode', 1): - self.log.info('Turning on airplane mode.') - asserts.assert_true(utils.force_airplane_mode(self.dut, True), - "Can not turn on airplane mode.") - wutils.wifi_toggle_state(self.dut, True) + for dev in self.android_devices: + self.log.info('Turning on airplane mode.') + asserts.assert_true(utils.force_airplane_mode(dev, True), + "Can not turn on airplane mode.") + wutils.wifi_toggle_state(dev, True) def teardown_test(self): self.iperf_server.stop() @@ -150,7 +150,8 @@ class WifiRvrTest(base_test.BaseTestClass): try: throughput_limits = self.compute_throughput_limits(rvr_result) except: - asserts.fail('Test failed: Golden file not found') + asserts.explicit_pass( + 'Test passed by default. Golden file not found') failure_count = 0 for idx, current_throughput in enumerate( @@ -352,7 +353,9 @@ class WifiRvrTest(base_test.BaseTestClass): """ self.log.info('Start running RvR') # Refresh link layer stats before test - llstats_obj = wputils.LinkLayerStats(self.dut) + llstats_obj = wputils.LinkLayerStats( + self.monitored_dut, + self.testclass_params.get('monitor_llstats', 1)) zero_counter = 0 throughput = [] llstats = [] @@ -374,20 +377,33 @@ class WifiRvrTest(base_test.BaseTestClass): bw=int(testcase_params['mode'][3:]), duration=self.testclass_params['iperf_duration'] / 5) # Start iperf session + if self.testclass_params.get('monitor_rssi', 1): + rssi_future = wputils.get_connected_rssi_nb( + self.monitored_dut, + self.testclass_params['iperf_duration'] - 1, + 1, + 1, + interface=self.monitored_interface) self.iperf_server.start(tag=str(atten)) - rssi_future = wputils.get_connected_rssi_nb( - self.dut, self.testclass_params['iperf_duration'] - 1, 1, 1) client_output_path = self.iperf_client.start( testcase_params['iperf_server_address'], testcase_params['iperf_args'], str(atten), self.testclass_params['iperf_duration'] + self.TEST_TIMEOUT) server_output_path = self.iperf_server.stop() - rssi_result = rssi_future.result() - current_rssi = { - 'signal_poll_rssi': rssi_result['signal_poll_rssi']['mean'], - 'chain_0_rssi': rssi_result['chain_0_rssi']['mean'], - 'chain_1_rssi': rssi_result['chain_1_rssi']['mean'] - } + if self.testclass_params.get('monitor_rssi', 1): + rssi_result = rssi_future.result() + current_rssi = { + 'signal_poll_rssi': + rssi_result['signal_poll_rssi']['mean'], + 'chain_0_rssi': rssi_result['chain_0_rssi']['mean'], + 'chain_1_rssi': rssi_result['chain_1_rssi']['mean'] + } + else: + current_rssi = { + 'signal_poll_rssi': float('nan'), + 'chain_0_rssi': float('nan'), + 'chain_1_rssi': float('nan') + } rssi.append(current_rssi) # Stop sniffer if self.testbed_params['sniffer_enable']: @@ -477,26 +493,33 @@ class WifiRvrTest(base_test.BaseTestClass): Args: testcase_params: dict containing AP and other test params """ + self.sta_dut = self.android_devices[0] # Check battery level before test if not wputils.health_check( - self.dut, 20) and testcase_params['traffic_direction'] == 'UL': + self.sta_dut, + 20) and testcase_params['traffic_direction'] == 'UL': asserts.skip('Overheating or Battery level low. Skipping test.') # Turn screen off to preserve battery - self.dut.go_to_sleep() - if wputils.validate_network(self.dut, + self.sta_dut.go_to_sleep() + if wputils.validate_network(self.sta_dut, testcase_params['test_network']['SSID']): self.log.info('Already connected to desired network') else: - wutils.reset_wifi(self.dut) - wutils.set_wifi_country_code(self.dut, + wutils.reset_wifi(self.sta_dut) + wutils.set_wifi_country_code(self.sta_dut, self.testclass_params['country_code']) - testcase_params['test_network']['channel'] = testcase_params[ - 'channel'] - wutils.wifi_connect(self.dut, + if self.testbed_params['sniffer_enable']: + self.sniffer.start_capture( + network={'SSID': testcase_params['test_network']['SSID']}, + chan=testcase_params['channel'], + bw=testcase_params['mode'][3:], + duration=180) + wutils.wifi_connect(self.sta_dut, testcase_params['test_network'], num_of_tries=5, check_connectivity=True) - self.dut_ip = self.dut.droid.connectivityGetIPv4Addresses('wlan0')[0] + if self.testbed_params['sniffer_enable']: + self.sniffer.stop_capture(tag='connection_setup') def setup_rvr_test(self, testcase_params): """Function that gets devices ready for the test. @@ -518,12 +541,17 @@ class WifiRvrTest(base_test.BaseTestClass): time.sleep(first_test_delay) self.setup_dut(testcase_params) # Get iperf_server address + sta_dut_ip = self.sta_dut.droid.connectivityGetIPv4Addresses( + 'wlan0')[0] if isinstance(self.iperf_server, ipf.IPerfServerOverAdb): - testcase_params['iperf_server_address'] = self.dut_ip + testcase_params['iperf_server_address'] = sta_dut_ip else: testcase_params[ 'iperf_server_address'] = wputils.get_server_address( - self.remote_server, self.dut_ip, '255.255.255.0') + self.remote_server, sta_dut_ip, '255.255.255.0') + # Set DUT to monitor RSSI and LLStats on + self.monitored_dut = self.sta_dut + self.monitored_interface = None def compile_test_params(self, testcase_params): """Function that completes all test params based on the test name. @@ -542,6 +570,16 @@ class WifiRvrTest(base_test.BaseTestClass): band = self.access_point.band_lookup_by_channel( testcase_params['channel']) testcase_params['test_network'] = self.main_network[band] + if testcase_params['traffic_type'] == 'TCP': + testcase_params['iperf_socket_size'] = self.testclass_params.get( + 'tcp_socket_size', None) + testcase_params['iperf_processes'] = self.testclass_params.get( + 'tcp_processes', 1) + elif testcase_params['traffic_type'] == 'UDP': + testcase_params['iperf_socket_size'] = self.testclass_params.get( + 'udp_socket_size', None) + testcase_params['iperf_processes'] = self.testclass_params.get( + 'udp_processes', 1) if (testcase_params['traffic_direction'] == 'DL' and not isinstance(self.iperf_server, ipf.IPerfServerOverAdb) ) or (testcase_params['traffic_direction'] == 'UL' @@ -549,13 +587,17 @@ class WifiRvrTest(base_test.BaseTestClass): testcase_params['iperf_args'] = wputils.get_iperf_arg_string( duration=self.testclass_params['iperf_duration'], reverse_direction=1, - traffic_type=testcase_params['traffic_type']) + traffic_type=testcase_params['traffic_type'], + socket_size=testcase_params['iperf_socket_size'], + num_processes=testcase_params['iperf_processes']) testcase_params['use_client_output'] = True else: testcase_params['iperf_args'] = wputils.get_iperf_arg_string( duration=self.testclass_params['iperf_duration'], reverse_direction=0, - traffic_type=testcase_params['traffic_type']) + traffic_type=testcase_params['traffic_type'], + socket_size=testcase_params['iperf_socket_size'], + num_processes=testcase_params['iperf_processes']) testcase_params['use_client_output'] = False return testcase_params diff --git a/acts/tests/google/wifi/WifiRvrTwTest.py b/acts_tests/tests/google/wifi/WifiRvrTwTest.py index 2f9dc12fdd..2f9dc12fdd 100644 --- a/acts/tests/google/wifi/WifiRvrTwTest.py +++ b/acts_tests/tests/google/wifi/WifiRvrTwTest.py diff --git a/acts/tests/google/wifi/WifiScannerBssidTest.py b/acts_tests/tests/google/wifi/WifiScannerBssidTest.py index e91c449924..e91c449924 100644 --- a/acts/tests/google/wifi/WifiScannerBssidTest.py +++ b/acts_tests/tests/google/wifi/WifiScannerBssidTest.py diff --git a/acts/tests/google/wifi/WifiScannerMultiScanTest.py b/acts_tests/tests/google/wifi/WifiScannerMultiScanTest.py index f8804bb887..4ee786267e 100755 --- a/acts/tests/google/wifi/WifiScannerMultiScanTest.py +++ b/acts_tests/tests/google/wifi/WifiScannerMultiScanTest.py @@ -263,6 +263,8 @@ class WifiScannerMultiScanTest(WifiBaseTest): if "AccessPoint" in self.user_params: self.legacy_configure_ap_and_start() + elif "OpenWrtAP" in self.user_params: + self.configure_openwrt_ap_and_start(open_network=True) self.wifi_chs = WifiChannelUS(self.dut.model) @@ -296,7 +298,7 @@ class WifiScannerMultiScanTest(WifiBaseTest): return idx, wait_time, scan_channels def validate_scan_results(self, scan_results_dict): - # Sanity check to make sure the dict is not empty + # Check to make sure the dict is not empty asserts.assert_true(scan_results_dict, "Scan result dict is empty.") for scan_result_obj in scan_results_dict.values(): # Validate the results received for each scan setting diff --git a/acts/tests/google/wifi/WifiScannerScanTest.py b/acts_tests/tests/google/wifi/WifiScannerScanTest.py index 05945dcbc3..89ce0d69c6 100755 --- a/acts/tests/google/wifi/WifiScannerScanTest.py +++ b/acts_tests/tests/google/wifi/WifiScannerScanTest.py @@ -95,9 +95,6 @@ class WifiScannerScanTest(WifiBaseTest): } self.log.debug("Run extended test: {}".format(self.run_extended_test)) self.wifi_chs = wutils.WifiChannelUS(self.dut.model) - asserts.assert_true(self.dut.droid.wifiIsScannerSupported(), - "Device %s doesn't support WifiScanner, abort." % - self.dut.model) self.attenuators = wutils.group_attenuators(self.attenuators) self.attenuators[0].set_atten(0) self.attenuators[1].set_atten(0) @@ -548,9 +545,10 @@ class WifiScannerScanTest(WifiBaseTest): band: wifi band.""" r = self.dut.droid.wifiScannerGetAvailableChannels(band) - self.log.debug(band) - self.log.debug(r) + self.log.info("Band: %s" % band) + self.log.info("Available channels: %s" % r) expected = self.wifi_chs.band_to_freq(band) + self.log.info("Expected channels: %s" % expected) asserts.assert_equal(set(r), set(expected), "Band %s failed." % band) def connect_to_reference_network(self): diff --git a/acts/tests/google/wifi/WifiScannerTests.config b/acts_tests/tests/google/wifi/WifiScannerTests.config index 85d599c2a8..85d599c2a8 100755 --- a/acts/tests/google/wifi/WifiScannerTests.config +++ b/acts_tests/tests/google/wifi/WifiScannerTests.config diff --git a/acts/tests/google/wifi/WifiSensitivityTest.py b/acts_tests/tests/google/wifi/WifiSensitivityTest.py index 73078501f2..73078501f2 100644 --- a/acts/tests/google/wifi/WifiSensitivityTest.py +++ b/acts_tests/tests/google/wifi/WifiSensitivityTest.py diff --git a/acts/tests/google/wifi/WifiServiceApiTest.py b/acts_tests/tests/google/wifi/WifiServiceApiTest.py index b5eed89b90..b5eed89b90 100644 --- a/acts/tests/google/wifi/WifiServiceApiTest.py +++ b/acts_tests/tests/google/wifi/WifiServiceApiTest.py diff --git a/acts/tests/google/wifi/WifiSoftApAcsTest.py b/acts_tests/tests/google/wifi/WifiSoftApAcsTest.py index edd76368ee..22d08b1a21 100644 --- a/acts/tests/google/wifi/WifiSoftApAcsTest.py +++ b/acts_tests/tests/google/wifi/WifiSoftApAcsTest.py @@ -34,7 +34,8 @@ from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest from threading import Thread WifiEnums = wutils.WifiEnums -WIFI_CONFIG_APBAND_AUTO = -1 +WIFI_CONFIG_APBAND_AUTO = WifiEnums.WIFI_CONFIG_SOFTAP_BAND_2G_5G +GET_FREQUENCY_NUM_RETRIES = 3 class WifiSoftApAcsTest(WifiBaseTest): """Tests for Automatic Channel Selection. @@ -103,9 +104,8 @@ class WifiSoftApAcsTest(WifiBaseTest): self.access_points[0].close() def on_fail(self, test_name, begin_time): - self.dut.take_bug_report(test_name, begin_time) - self.dut.cat_adb_log(test_name, begin_time) for ad in self.android_devices: + ad.take_bug_report(test_name, begin_time) wutils.get_cnss_diag_log(ad, test_name) """Helper Functions""" @@ -155,10 +155,15 @@ class WifiSoftApAcsTest(WifiBaseTest): """ wutils.connect_to_wifi_network(self.dut_client, softap, check_connectivity=False) - softap_info = self.dut_client.droid.wifiGetConnectionInfo() - self.log.debug("DUT is connected to softAP %s with details: %s" % - (softap[wutils.WifiEnums.SSID_KEY], softap_info)) - frequency = softap_info['frequency'] + for _ in range(GET_FREQUENCY_NUM_RETRIES): + softap_info = self.dut_client.droid.wifiGetConnectionInfo() + self.log.debug("DUT is connected to softAP %s with details: %s" % + (softap[wutils.WifiEnums.SSID_KEY], softap_info)) + frequency = softap_info['frequency'] + if frequency > 0: + break + time.sleep(1) # frequency not updated yet, try again after a delay + return hostapd_constants.CHANNEL_MAP[frequency] def configure_ap(self, channel_2g=None, channel_5g=None): @@ -169,15 +174,20 @@ class WifiSoftApAcsTest(WifiBaseTest): channel_5g: The channel number to use for 5GHz network. """ + if not channel_2g: + channel_2g = hostapd_constants.AP_DEFAULT_CHANNEL_2G + if not channel_5g: + channel_5g = hostapd_constants.AP_DEFAULT_CHANNEL_5G if "AccessPoint" in self.user_params: - if not channel_2g: - channel_2g = hostapd_constants.AP_DEFAULT_CHANNEL_2G - if not channel_5g: - channel_5g = hostapd_constants.AP_DEFAULT_CHANNEL_5G self.legacy_configure_ap_and_start(wpa_network=True, wep_network=True, channel_2g=channel_2g, channel_5g=channel_5g) + elif "OpenWrtAP" in self.user_params: + self.configure_openwrt_ap_and_start(wpa_network=True, + wep_network=True, + channel_2g=channel_2g, + channel_5g=channel_5g) def start_traffic_and_softap(self, network, softap_band): """Start iPerf traffic on client dut, during softAP bring-up on dut. diff --git a/acts/tests/google/wifi/WifiSoftApPerformanceTest.py b/acts_tests/tests/google/wifi/WifiSoftApPerformanceTest.py index 489ab61b66..375ae937f9 100644 --- a/acts/tests/google/wifi/WifiSoftApPerformanceTest.py +++ b/acts_tests/tests/google/wifi/WifiSoftApPerformanceTest.py @@ -25,6 +25,7 @@ from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger from acts.test_utils.wifi import ota_sniffer from acts.test_utils.wifi import wifi_test_utils as wutils from acts.test_utils.wifi import wifi_performance_test_utils as wputils +from acts.test_utils.wifi import wifi_retail_ap as retail_ap from WifiRvrTest import WifiRvrTest AccessPointTuple = collections.namedtuple(('AccessPointTuple'), @@ -35,7 +36,15 @@ class WifiSoftApRvrTest(WifiRvrTest): def __init__(self, controllers): base_test.BaseTestClass.__init__(self, controllers) self.tests = ('test_rvr_TCP_DL_2GHz', 'test_rvr_TCP_UL_2GHz', - 'test_rvr_TCP_DL_5GHz', 'test_rvr_TCP_UL_5GHz') + 'test_rvr_TCP_DL_5GHz', 'test_rvr_TCP_UL_5GHz', + 'test_rvr_TCP_DL_2GHz_backhaul_2GHz', + 'test_rvr_TCP_UL_2GHz_backhaul_2GHz', + 'test_rvr_TCP_DL_5GHz_backhaul_2GHz', + 'test_rvr_TCP_UL_5GHz_backhaul_2GHz', + 'test_rvr_TCP_DL_2GHz_backhaul_5GHz', + 'test_rvr_TCP_UL_2GHz_backhaul_5GHz', + 'test_rvr_TCP_DL_5GHz_backhaul_5GHz', + 'test_rvr_TCP_UL_5GHz_backhaul_5GHz') self.testcase_metric_logger = ( BlackboxMappedMetricLogger.for_test_case()) self.testclass_metric_logger = ( @@ -48,10 +57,14 @@ class WifiSoftApRvrTest(WifiRvrTest): This function initializes hardwares and compiles parameters that are common to all tests in this class. """ - self.dut = self.android_devices[-1] - req_params = ['sap_test_params', 'testbed_params'] + req_params = [ + 'sap_test_params', 'testbed_params', 'RetailAccessPoints', + 'ap_networks' + ] opt_params = ['golden_files_list', 'OTASniffer'] self.unpack_userparams(req_params, opt_params) + self.access_points = retail_ap.create(self.RetailAccessPoints) + self.access_point = self.access_points[0] self.testclass_params = self.sap_test_params self.num_atten = self.attenuators[0].instrument.num_atten self.iperf_server = ipf.create([{ @@ -94,6 +107,8 @@ class WifiSoftApRvrTest(WifiRvrTest): for dev in self.android_devices: wutils.wifi_toggle_state(dev, False) self.process_testclass_results() + # Teardown AP and release it's lockfile + self.access_point.teardown() def teardown_test(self): self.iperf_server.stop() @@ -109,23 +124,45 @@ class WifiSoftApRvrTest(WifiRvrTest): 'wpa_cli status | grep freq').split('=')[1] info['channel'] = wutils.WifiEnums.freq_to_channel[int( info['frequency'])] + info['mode'] = 'VHT20' if info['channel'] < 13 else 'VHT80' return info - def setup_sap_rvr_test(self, testcase_params): + def setup_aps(self, testcase_params): + for network in testcase_params['ap_networks']: + self.log.info('Setting AP {} {} interface on channel {}'.format( + network['ap_id'], network['interface_id'], network['channel'])) + self.access_points[network['ap_id']].set_channel( + network['interface_id'], network['channel']) + + def setup_duts(self, testcase_params): """Function that gets devices ready for the test. Args: testcase_params: dict containing test-specific parameters """ + self.ap_dut = self.android_devices[0] + self.sta_dut = self.android_devices[1] for dev in self.android_devices: if not wputils.health_check(dev, 20): asserts.skip('DUT health check failed. Skipping test.') # Reset WiFi on all devices for dev in self.android_devices: - self.dut.go_to_sleep() + dev.go_to_sleep() wutils.reset_wifi(dev) wutils.set_wifi_country_code(dev, wutils.WifiEnums.CountryCode.US) + for network in testcase_params['ap_networks']: + for connected_dut in network['connected_dut']: + self.log.info("Connecting DUT {} to {}".format( + connected_dut, self.ap_networks[network['ap_id']][ + network['interface_id']])) + wutils.wifi_connect(self.android_devices[connected_dut], + self.ap_networks[network['ap_id']][ + network['interface_id']], + num_of_tries=5, + check_connectivity=True) + + def setup_sap_connection(self, testcase_params): # Setup Soft AP sap_config = wutils.create_softap_config() self.log.info('SoftAP Config: {}'.format(sap_config)) @@ -133,31 +170,39 @@ class WifiSoftApRvrTest(WifiRvrTest): sap_config[wutils.WifiEnums.SSID_KEY], sap_config[wutils.WifiEnums.PWD_KEY], testcase_params['sap_band_enum']) - # Set attenuator to 0 dB - for attenuator in self.attenuators: - attenuator.set_atten(0, strict=False) # Connect DUT to Network testcase_params['test_network'] = { 'SSID': sap_config[wutils.WifiEnums.SSID_KEY], 'password': sap_config[wutils.WifiEnums.PWD_KEY] } - wutils.wifi_connect(self.android_devices[1], + wutils.wifi_connect(self.sta_dut, testcase_params['test_network'], num_of_tries=5, check_connectivity=False) # Compile meta data - self.access_point = AccessPointTuple(sap_config) - testcase_params['connection_info'] = self.get_sap_connection_info() - testcase_params['channel'] = testcase_params['connection_info'][ - 'channel'] - testcase_params['test_network']['channel'] = testcase_params[ - 'connection_info']['channel'] - if testcase_params['channel'] < 13: - testcase_params['mode'] = 'VHT20' - else: - testcase_params['mode'] = 'VHT80' - testcase_params['iperf_server_address'] = testcase_params[ - 'connection_info']['ap_ip_address'] + #self.access_point = AccessPointTuple(sap_config) + sap_info = self.get_sap_connection_info() + testcase_params['channel'] = sap_info['channel'] + testcase_params['mode'] = sap_info['mode'] + testcase_params['iperf_server_address'] = sap_info['ap_ip_address'] + + def setup_sap_rvr_test(self, testcase_params): + """Function that gets devices ready for the test. + + Args: + testcase_params: dict containing test-specific parameters + """ + # Configure DUTs + self.setup_aps(testcase_params) + # Set attenuator to 0 dB + for attenuator in self.attenuators: + attenuator.set_atten(0, strict=False) + # Configure DUTs + self.setup_duts(testcase_params) + # Setup sap connection + self.setup_sap_connection(testcase_params) + # Set DUT to monitor RSSI and LLStats on + self.monitored_dut = self.sta_dut def compile_test_params(self, testcase_params): """Function that completes all test params based on the test name. @@ -186,6 +231,40 @@ class WifiSoftApRvrTest(WifiRvrTest): reverse_direction=0, traffic_type=testcase_params['traffic_type']) testcase_params['use_client_output'] = False + + # Compile AP and infrastructure connection parameters + ap_networks = [] + if testcase_params['dut_connected'][0]: + band = testcase_params['dut_connected'][0].split('_')[0] + ap_networks.append({ + 'ap_id': 0, + 'interface_id': band if band == '2G' else band + '_1', + 'band': band, + 'channel': 1 if band == '2G' else 36, + 'connected_dut': [0] + }) + + if testcase_params['dut_connected'][1]: + if testcase_params['dut_connected'][0] == testcase_params[ + 'dut_connected'][1]: + # if connected to same network, add it to the above + ap_networks[0]['connected_dut'].append(1) + else: + band = testcase_params['dut_connected'][1].split('_')[0] + if not testcase_params['dut_connected'][0]: + # if it's the only dut connected, assign it to ap 0 + ap_id = 0 + else: + ap_id = 1 + ap_networks.append({ + 'ap_id': ap_id, + 'interface_id': band if band == '2G' else band + '_1', + 'band': band, + 'channel': 11 if band == '2G' else 149, + 'connected_dut': [1] + }) + testcase_params['ap_networks'] = ap_networks + return testcase_params def _test_sap_rvr(self, testcase_params): @@ -209,7 +288,8 @@ class WifiSoftApRvrTest(WifiRvrTest): sap_band='2GHz', sap_band_enum=wutils.WifiEnums.WIFI_CONFIG_APBAND_2G, traffic_type='TCP', - traffic_direction='DL') + traffic_direction='DL', + dut_connected=[False, False]) self._test_sap_rvr(testcase_params) def test_rvr_TCP_UL_2GHz(self): @@ -217,7 +297,8 @@ class WifiSoftApRvrTest(WifiRvrTest): sap_band='2GHz', sap_band_enum=wutils.WifiEnums.WIFI_CONFIG_APBAND_2G, traffic_type='TCP', - traffic_direction='UL') + traffic_direction='UL', + dut_connected=[False, False]) self._test_sap_rvr(testcase_params) def test_rvr_TCP_DL_5GHz(self): @@ -225,7 +306,8 @@ class WifiSoftApRvrTest(WifiRvrTest): sap_band='5GHz', sap_band_enum=wutils.WifiEnums.WIFI_CONFIG_APBAND_5G, traffic_type='TCP', - traffic_direction='DL') + traffic_direction='DL', + dut_connected=[False, False]) self._test_sap_rvr(testcase_params) def test_rvr_TCP_UL_5GHz(self): @@ -233,5 +315,78 @@ class WifiSoftApRvrTest(WifiRvrTest): sap_band='5GHz', sap_band_enum=wutils.WifiEnums.WIFI_CONFIG_APBAND_5G, traffic_type='TCP', - traffic_direction='UL') + traffic_direction='UL', + dut_connected=[False, False]) + self._test_sap_rvr(testcase_params) + + def test_rvr_TCP_DL_2GHz_backhaul_2GHz(self): + testcase_params = collections.OrderedDict( + sap_band='2GHz', + sap_band_enum=wutils.WifiEnums.WIFI_CONFIG_APBAND_2G, + traffic_type='TCP', + traffic_direction='DL', + dut_connected=['2G_1', False]) + self._test_sap_rvr(testcase_params) + + def test_rvr_TCP_UL_2GHz_backhaul_2GHz(self): + testcase_params = collections.OrderedDict( + sap_band='2GHz', + sap_band_enum=wutils.WifiEnums.WIFI_CONFIG_APBAND_2G, + traffic_type='TCP', + traffic_direction='UL', + dut_connected=['2G_1', False]) + self._test_sap_rvr(testcase_params) + + def test_rvr_TCP_DL_5GHz_backhaul_2GHz(self): + testcase_params = collections.OrderedDict( + sap_band='5GHz', + sap_band_enum=wutils.WifiEnums.WIFI_CONFIG_APBAND_5G, + traffic_type='TCP', + traffic_direction='DL', + dut_connected=['2G_1', False]) + self._test_sap_rvr(testcase_params) + + def test_rvr_TCP_UL_5GHz_backhaul_2GHz(self): + testcase_params = collections.OrderedDict( + sap_band='5GHz', + sap_band_enum=wutils.WifiEnums.WIFI_CONFIG_APBAND_5G, + traffic_type='TCP', + traffic_direction='UL', + dut_connected=['2G_1', False]) + self._test_sap_rvr(testcase_params) + + def test_rvr_TCP_DL_2GHz_backhaul_5GHz(self): + testcase_params = collections.OrderedDict( + sap_band='2GHz', + sap_band_enum=wutils.WifiEnums.WIFI_CONFIG_APBAND_2G, + traffic_type='TCP', + traffic_direction='DL', + dut_connected=['5G_1', False]) + self._test_sap_rvr(testcase_params) + + def test_rvr_TCP_UL_2GHz_backhaul_5GHz(self): + testcase_params = collections.OrderedDict( + sap_band='2GHz', + sap_band_enum=wutils.WifiEnums.WIFI_CONFIG_APBAND_2G, + traffic_type='TCP', + traffic_direction='UL', + dut_connected=['5G_1', False]) + self._test_sap_rvr(testcase_params) + + def test_rvr_TCP_DL_5GHz_backhaul_5GHz(self): + testcase_params = collections.OrderedDict( + sap_band='5GHz', + sap_band_enum=wutils.WifiEnums.WIFI_CONFIG_APBAND_5G, + traffic_type='TCP', + traffic_direction='DL', + dut_connected=['5G_1', False]) + self._test_sap_rvr(testcase_params) + + def test_rvr_TCP_UL_5GHz_backhaul_5GHz(self): + testcase_params = collections.OrderedDict( + sap_band='5GHz', + sap_band_enum=wutils.WifiEnums.WIFI_CONFIG_APBAND_5G, + traffic_type='TCP', + traffic_direction='UL', + dut_connected=['5G_1', False]) self._test_sap_rvr(testcase_params) diff --git a/acts/tests/google/wifi/WifiSoftApTest.py b/acts_tests/tests/google/wifi/WifiSoftApTest.py index de14eb3938..7c7840d5d1 100644 --- a/acts/tests/google/wifi/WifiSoftApTest.py +++ b/acts_tests/tests/google/wifi/WifiSoftApTest.py @@ -33,6 +33,8 @@ from acts.test_utils.wifi import wifi_constants from acts.test_utils.wifi import wifi_test_utils as wutils from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest +WifiEnums = wutils.WifiEnums + class WifiSoftApTest(WifiBaseTest): def setup_class(self): @@ -50,6 +52,8 @@ class WifiSoftApTest(WifiBaseTest): req_param_names=req_params, opt_param_names=opt_param) if "AccessPoint" in self.user_params: self.legacy_configure_ap_and_start() + elif "OpenWrtAP" in self.user_params: + self.configure_openwrt_ap_and_start(open_network=True) self.open_network = self.open_network[0]["2g"] # Do a simple version of init - mainly just sync the time and enable # verbose logging. This test will fail if the DUT has a sim and cell @@ -80,9 +84,11 @@ class WifiSoftApTest(WifiBaseTest): self.android_devices[2].droid.wifiEnableVerboseLogging(1) asserts.assert_equal(self.android_devices[2].droid.wifiGetVerboseLoggingLevel(), 1, "Failed to enable WiFi verbose logging on the client dut.") + self.dut_client_2 = self.android_devices[2] def teardown_class(self): - wutils.stop_wifi_tethering(self.dut) + if self.dut.droid.wifiIsApEnabled(): + wutils.stop_wifi_tethering(self.dut) wutils.reset_wifi(self.dut) wutils.reset_wifi(self.dut_client) if "AccessPoint" in self.user_params: @@ -499,7 +505,7 @@ class WifiSoftApTest(WifiBaseTest): 6. Unregister second softap callback 7. Force second client connect to hotspot (if supported) 8. Turn off hotspot - 9. Verify second softap callback doesn't respond after unresister + 9. Verify second softap callback doesn't respond after unregister """ config = wutils.start_softap_and_verify(self, WIFI_CONFIG_APBAND_AUTO) # Register callback after softap enabled to avoid unnecessary callback @@ -620,6 +626,265 @@ class WifiSoftApTest(WifiBaseTest): "SoftAp is not reported as running") self.dut.droid.unregisterSoftApCallback(callbackId) + @test_tracker_info(uuid="3a10c7fd-cd8d-4d46-9d12-88a68640e060") + def test_softap_auto_shut_off_with_customized_timeout(self): + """Test for softap auto shut off + 1. Turn on hotspot + 2. Register softap callback + 3. Backup original shutdown timeout value + 4. Set up test_shutdown_timeout_value + 5. Let client connect to the hotspot + 6. Start wait test_shutdown_timeout_value * 1.1 seconds + 7. Check hotspot doesn't shut off + 8. Let client disconnect to the hotspot + 9. Start wait test_shutdown_timeout_value seconds + 10. Check hotspot auto shut off + """ + # Backup config + original_softap_config = self.dut.droid.wifiGetApConfiguration() + # This config only included SSID and Password which used for connection + # only. + config = wutils.start_softap_and_verify(self, WIFI_CONFIG_APBAND_AUTO) + + # Get current configuration to use for update configuration + current_softap_config = self.dut.droid.wifiGetApConfiguration() + # Register callback after softap enabled to avoid unnecessary callback + # impact the test + callbackId = self.dut.droid.registerSoftApCallback() + # Verify clients will update immediately after register callback + wutils.wait_for_expected_number_of_softap_clients(self.dut, + callbackId, 0) + wutils.wait_for_expected_softap_state(self.dut, callbackId, + wifi_constants.WIFI_AP_ENABLED_STATE) + + # Setup shutdown timeout value + test_shutdown_timeout_value_s = 10 + wutils.save_wifi_soft_ap_config(self.dut, current_softap_config, + shutdown_timeout_millis=test_shutdown_timeout_value_s * 1000) + # Force DUTs connect to Network + wutils.wifi_connect(self.dut_client, config, check_connectivity=False) + wutils.wait_for_expected_number_of_softap_clients( + self.dut, callbackId, 1) + + self.dut.log.info("Start waiting %s seconds with 1 clients ", + test_shutdown_timeout_value_s * 1.1) + time.sleep(test_shutdown_timeout_value_s * 1.1) + + # When client connected, softap should keep as enabled + asserts.assert_true(self.dut.droid.wifiIsApEnabled(), + "SoftAp is not reported as running") + + wutils.wifi_toggle_state(self.dut_client, False) + wutils.wait_for_expected_number_of_softap_clients(self.dut, + callbackId, 0) + self.dut.log.info("Start waiting %s seconds with 0 client", + test_shutdown_timeout_value_s * 1.1) + time.sleep(test_shutdown_timeout_value_s * 1.1) + # Softap should stop since no client connected + # doesn't disconnect before softap stop + wutils.wait_for_expected_softap_state(self.dut, callbackId, + wifi_constants.WIFI_AP_DISABLING_STATE) + wutils.wait_for_expected_softap_state(self.dut, callbackId, + wifi_constants.WIFI_AP_DISABLED_STATE) + asserts.assert_false(self.dut.droid.wifiIsApEnabled(), + "SoftAp is not reported as running") + self.dut.droid.unregisterSoftApCallback(callbackId) + + # Restore config + wutils.save_wifi_soft_ap_config(self.dut, original_softap_config) + + @test_tracker_info(uuid="a9444699-f0d3-4ac3-922b-05e9d4f67968") + def test_softap_configuration_update(self): + """Test for softap configuration update + 1. Get current softap configuration + 2. Update to Open Security configuration + 3. Update to WPA2_PSK configuration + 4. Restore the configuration + """ + # Backup config + original_softap_config = self.dut.droid.wifiGetApConfiguration() + wutils.save_wifi_soft_ap_config(self.dut, {"SSID":"ACTS_TEST"}, + band=WifiEnums.WIFI_CONFIG_SOFTAP_BAND_2G, hidden=False, + security=WifiEnums.SoftApSecurityType.OPEN, password="", + channel=11, max_clients=0, shutdown_timeout_enable=False, + shutdown_timeout_millis=0, client_control_enable=True, + allowedList=[], blockedList=[]) + + wutils.save_wifi_soft_ap_config(self.dut, {"SSID":"ACTS_TEST"}, + band=WifiEnums.WIFI_CONFIG_SOFTAP_BAND_2G_5G, hidden=True, + security=WifiEnums.SoftApSecurityType.WPA2, password="12345678", + channel=0, max_clients=1, shutdown_timeout_enable=True, + shutdown_timeout_millis=10000, client_control_enable=False, + allowedList=["aa:bb:cc:dd:ee:ff"], blockedList=["11:22:33:44:55:66"]) + + # Restore config + wutils.save_wifi_soft_ap_config(self.dut, original_softap_config) + + @test_tracker_info(uuid="8a5d81fa-649c-4679-a823-5cef50828a94") + def test_softap_client_control(self): + """Test Client Control feature + 1. Check SoftApCapability to make sure feature is supported + 2. Backup config + 3. Setup configuration which used to start softap + 4. Register callback after softap enabled + 5. Trigger client connect to softap + 6. Verify blocking event + 7. Add client into allowed list + 8. Verify client connected + 9. Restore Config + """ + # Register callback to check capability first + callbackId = self.dut.droid.registerSoftApCallback() + # Check capability first to make sure DUT support this test. + capabilityEventStr = wifi_constants.SOFTAP_CALLBACK_EVENT + str( + callbackId) + wifi_constants.SOFTAP_CAPABILITY_CHANGED + capability = self.dut.ed.pop_event(capabilityEventStr, 10) + asserts.skip_if(not capability['data'][wifi_constants + .SOFTAP_CAPABILITY_FEATURE_CLIENT_CONTROL], + "Client control isn't supported, ignore test") + + # Unregister callback before start test to avoid + # unnecessary callback impact the test + self.dut.droid.unregisterSoftApCallback(callbackId) + + # start the test + + # Backup config + original_softap_config = self.dut.droid.wifiGetApConfiguration() + # Setup configuration which used to start softap + wutils.save_wifi_soft_ap_config(self.dut, {"SSID":"ACTS_TEST"}, + band=WifiEnums.WIFI_CONFIG_SOFTAP_BAND_2G_5G, hidden=False, + security=WifiEnums.SoftApSecurityType.WPA2, password="12345678", + client_control_enable=True) + + wutils.start_wifi_tethering_saved_config(self.dut) + current_softap_config = self.dut.droid.wifiGetApConfiguration() + # Register callback after softap enabled to avoid unnecessary callback + # impact the test + callbackId = self.dut.droid.registerSoftApCallback() + + # Verify clients will update immediately after register callback + wutils.wait_for_expected_number_of_softap_clients(self.dut, + callbackId, 0) + wutils.wait_for_expected_softap_state(self.dut, callbackId, + wifi_constants.WIFI_AP_ENABLED_STATE) + + # Trigger client connection + self.dut_client.droid.wifiConnectByConfig(current_softap_config) + + eventStr = wifi_constants.SOFTAP_CALLBACK_EVENT + str( + callbackId) + wifi_constants.SOFTAP_BLOCKING_CLIENT_CONNECTING + blockedClient = self.dut.ed.pop_event(eventStr, 10) + asserts.assert_equal(blockedClient['data'][wifi_constants. + SOFTAP_BLOCKING_CLIENT_REASON_KEY], + wifi_constants.SAP_CLIENT_BLOCK_REASON_CODE_BLOCKED_BY_USER, + "Blocked reason code doesn't match") + + # Update configuration, add client into allowed list + wutils.save_wifi_soft_ap_config(self.dut, current_softap_config, + allowedList=[blockedClient['data'][wifi_constants. + SOFTAP_BLOCKING_CLIENT_WIFICLIENT_KEY]]) + + # Wait configuration updated + time.sleep(3) + # Trigger connection again + self.dut_client.droid.wifiConnectByConfig(current_softap_config) + + # Verify client connected + wutils.wait_for_expected_number_of_softap_clients(self.dut, + callbackId, 1) + + # Restore config + wutils.save_wifi_soft_ap_config(self.dut, original_softap_config) + + # Unregister callback + self.dut.droid.unregisterSoftApCallback(callbackId) + + @test_tracker_info(uuid="d0b61b58-fa2b-4ced-bc52-3366cb826e79") + def test_softap_max_client_setting(self): + """Test Client Control feature + 1. Check device number and capability to make sure feature is supported + 2. Backup config + 3. Setup configuration which used to start softap + 4. Register callback after softap enabled + 5. Trigger client connect to softap + 6. Verify blocking event + 7. Extend max client setting + 8. Verify client connected + 9. Restore Config + """ + asserts.skip_if(len(self.android_devices) < 3, + "Device less than 3, skip the test.") + # Register callback to check capability first + callbackId = self.dut.droid.registerSoftApCallback() + # Check capability first to make sure DUT support this test. + capabilityEventStr = wifi_constants.SOFTAP_CALLBACK_EVENT + str( + callbackId) + wifi_constants.SOFTAP_CAPABILITY_CHANGED + capability = self.dut.ed.pop_event(capabilityEventStr, 10) + asserts.skip_if(not capability['data'][wifi_constants + .SOFTAP_CAPABILITY_FEATURE_CLIENT_CONTROL], + "Client control isn't supported, ignore test") + + # Unregister callback before start test to avoid + # unnecessary callback impact the test + self.dut.droid.unregisterSoftApCallback(callbackId) + + # Backup config + original_softap_config = self.dut.droid.wifiGetApConfiguration() + # Setup configuration which used to start softap + wutils.save_wifi_soft_ap_config(self.dut, {"SSID":"ACTS_TEST"}, + band=WifiEnums.WIFI_CONFIG_SOFTAP_BAND_2G_5G, hidden=False, + security=WifiEnums.SoftApSecurityType.WPA2, password="12345678", + max_clients=1) + + wutils.start_wifi_tethering_saved_config(self.dut) + current_softap_config = self.dut.droid.wifiGetApConfiguration() + # Register callback again after softap enabled to avoid + # unnecessary callback impact the test + callbackId = self.dut.droid.registerSoftApCallback() + + # Verify clients will update immediately after register calliback + wutils.wait_for_expected_number_of_softap_clients(self.dut, + callbackId, 0) + wutils.wait_for_expected_softap_state(self.dut, callbackId, + wifi_constants.WIFI_AP_ENABLED_STATE) + + # Trigger client connection + self.dut_client.droid.wifiConnectByConfig(current_softap_config) + self.dut_client_2.droid.wifiConnectByConfig(current_softap_config) + # Wait client connect + time.sleep(3) + + # Verify one client connected and one blocked due to max client + eventStr = wifi_constants.SOFTAP_CALLBACK_EVENT + str( + callbackId) + wifi_constants.SOFTAP_BLOCKING_CLIENT_CONNECTING + blockedClient = self.dut.ed.pop_event(eventStr, 10) + asserts.assert_equal(blockedClient['data'][wifi_constants. + SOFTAP_BLOCKING_CLIENT_REASON_KEY], + wifi_constants.SAP_CLIENT_BLOCK_REASON_CODE_NO_MORE_STAS, + "Blocked reason code doesn't match") + wutils.wait_for_expected_number_of_softap_clients(self.dut, + callbackId, 1) + + # Update configuration, extend client to 2 + wutils.save_wifi_soft_ap_config(self.dut, current_softap_config, + max_clients=2) + + # Wait configuration updated + time.sleep(3) + # Trigger connection again + self.dut_client_2.droid.wifiConnectByConfig(current_softap_config) + # Wait client connect + time.sleep(3) + # Verify client connected + wutils.wait_for_expected_number_of_softap_clients(self.dut, + callbackId, 2) + + # Restore config + wutils.save_wifi_soft_ap_config(self.dut, original_softap_config) + + # Unregister callback + self.dut.droid.unregisterSoftApCallback(callbackId) """ Tests End """ diff --git a/acts/tests/google/wifi/WifiStaApConcurrencyStressTest.py b/acts_tests/tests/google/wifi/WifiStaApConcurrencyStressTest.py index 6dda5ddae5..f84c06c21e 100755 --- a/acts/tests/google/wifi/WifiStaApConcurrencyStressTest.py +++ b/acts_tests/tests/google/wifi/WifiStaApConcurrencyStressTest.py @@ -59,76 +59,38 @@ class WifiStaApConcurrencyStressTest(WifiStaApConcurrencyTest): "test_stress_softap_5G_wifi_connection_2G_with_location_scan_on") def setup_class(self): - self.dut = self.android_devices[0] - self.dut_client = self.android_devices[1] - wutils.wifi_test_device_init(self.dut) - wutils.wifi_test_device_init(self.dut_client) - # Do a simple version of init - mainly just sync the time and enable - # verbose logging. This test will fail if the DUT has a sim and cell - # data is disabled. We would also like to test with phones in less - # constrained states (or add variations where we specifically - # constrain). - utils.require_sl4a((self.dut, self.dut_client)) - utils.sync_device_time(self.dut) - utils.sync_device_time(self.dut_client) - # Set country code explicitly to "US". - wutils.set_wifi_country_code(self.dut, wutils.WifiEnums.CountryCode.US) - wutils.set_wifi_country_code(self.dut_client, wutils.WifiEnums.CountryCode.US) - # Enable verbose logging on the duts - self.dut.droid.wifiEnableVerboseLogging(1) - asserts.assert_equal(self.dut.droid.wifiGetVerboseLoggingLevel(), 1, - "Failed to enable WiFi verbose logging on the softap dut.") - self.dut_client.droid.wifiEnableVerboseLogging(1) - asserts.assert_equal(self.dut_client.droid.wifiGetVerboseLoggingLevel(), 1, - "Failed to enable WiFi verbose logging on the client dut.") - - req_params = ["AccessPoint", "dbs_supported_models", "stress_count"] - opt_param = ["iperf_server_address"] - self.unpack_userparams( - req_param_names=req_params, opt_param_names=opt_param) - - if self.dut.model not in self.dbs_supported_models: - asserts.skip( - ("Device %s does not support dual interfaces.") - % self.dut.model) - - if "iperf_server_address" in self.user_params: - self.iperf_server = self.iperf_servers[0] - if hasattr(self, 'iperf_server'): - self.iperf_server.start() - - # Set the client wifi state to on before the test begins. - wutils.wifi_toggle_state(self.dut_client, True) - - # Init extra devices - if len(self.android_devices) > 2: - wutils.wifi_test_device_init(self.android_devices[2]) - utils.sync_device_time(self.android_devices[2]) - wutils.set_wifi_country_code(self.android_devices[2], wutils.WifiEnums.CountryCode.US) - self.android_devices[2].droid.wifiEnableVerboseLogging(1) - asserts.assert_equal(self.android_devices[2].droid.wifiGetVerboseLoggingLevel(), 1, - "Failed to enable WiFi verbose logging on the client dut.") + super().setup_class() + opt_param = ["stress_count"] + self.unpack_userparams(opt_param_names=opt_param) """Helper Functions""" + def connect_to_wifi_network_and_verify(self, params): + """Connection logic for open and psk wifi networks. + Args: + params: A tuple of network info and AndroidDevice object. + """ + network, ad = params + SSID = network[WifiEnums.SSID_KEY] + wutils.reset_wifi(ad) + wutils.connect_to_wifi_network(ad, network) + if len(self.android_devices) > 2: + wutils.reset_wifi(self.android_devices[2]) + wutils.connect_to_wifi_network(self.android_devices[2], network) def verify_wifi_full_on_off(self, network, softap_config): wutils.wifi_toggle_state(self.dut, True) self.connect_to_wifi_network_and_verify((network, self.dut)) - self.run_iperf_client((network, self.dut)) - self.run_iperf_client((softap_config, self.dut_client)) if len(self.android_devices) > 2: self.log.info("Testbed has extra android devices, do more validation") - self.verify_traffic_between_ap_clients( + self.verify_traffic_between_dut_clients( self.dut, self.android_devices[2]) wutils.wifi_toggle_state(self.dut, False) def verify_softap_full_on_off(self, network, softap_band): softap_config = self.start_softap_and_verify(softap_band) - self.run_iperf_client((network, self.dut)) - self.run_iperf_client((softap_config, self.dut_client)) if len(self.android_devices) > 2: self.log.info("Testbed has extra android devices, do more validation") - self.verify_traffic_between_softap_clients( + self.verify_traffic_between_dut_clients( self.dut_client, self.android_devices[2]) wutils.reset_wifi(self.dut_client) if len(self.android_devices) > 2: @@ -142,12 +104,10 @@ class WifiStaApConcurrencyStressTest(WifiStaApConcurrencyTest): """ self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G) wutils.wifi_toggle_state(self.dut, True) - self.connect_to_wifi_network_and_verify((self.wpapsk_2g, self.dut)) + self.connect_to_wifi_network_and_verify((self.open_2g, self.dut)) for count in range(self.stress_count): self.log.info("Iteration %d", count+1) - self.verify_softap_full_on_off(self.wpapsk_2g, WIFI_CONFIG_APBAND_2G) - raise signals.TestPass(details="", extras={"Iterations":"%d" % - self.stress_count, "Pass":"%d" %(count+1)}) + self.verify_softap_full_on_off(self.open_2g, WIFI_CONFIG_APBAND_2G) @test_tracker_info(uuid="03362d54-a624-4fb8-ad97-7abb9e6f655c") def test_stress_wifi_connection_5G_softap_5G(self): @@ -155,12 +115,10 @@ class WifiStaApConcurrencyStressTest(WifiStaApConcurrencyTest): """ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G) wutils.wifi_toggle_state(self.dut, True) - self.connect_to_wifi_network_and_verify((self.wpapsk_5g, self.dut)) + self.connect_to_wifi_network_and_verify((self.open_5g, self.dut)) for count in range(self.stress_count): self.log.info("Iteration %d", count+1) - self.verify_softap_full_on_off(self.wpapsk_5g, WIFI_CONFIG_APBAND_5G) - raise signals.TestPass(details="", extras={"Iterations":"%d" % - self.stress_count, "Pass":"%d" %(count+1)}) + self.verify_softap_full_on_off(self.open_5g, WIFI_CONFIG_APBAND_5G) @test_tracker_info(uuid="fdda4ff2-38d5-4398-9a59-c7cee407a2b3") def test_stress_wifi_connection_5G_DFS_softap_5G(self): @@ -168,12 +126,10 @@ class WifiStaApConcurrencyStressTest(WifiStaApConcurrencyTest): """ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS) wutils.wifi_toggle_state(self.dut, True) - self.connect_to_wifi_network_and_verify((self.wpapsk_5g, self.dut)) + self.connect_to_wifi_network_and_verify((self.open_5g, self.dut)) for count in range(self.stress_count): self.log.info("Iteration %d", count+1) - self.verify_softap_full_on_off(self.wpapsk_5g, WIFI_CONFIG_APBAND_5G) - raise signals.TestPass(details="", extras={"Iterations":"%d" % - self.stress_count, "Pass":"%d" %(count+1)}) + self.verify_softap_full_on_off(self.open_5g, WIFI_CONFIG_APBAND_5G) @test_tracker_info(uuid="b3621721-7714-43eb-8438-b578164b9194") def test_stress_wifi_connection_5G_softap_2G(self): @@ -181,12 +137,10 @@ class WifiStaApConcurrencyStressTest(WifiStaApConcurrencyTest): """ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G) wutils.wifi_toggle_state(self.dut, True) - self.connect_to_wifi_network_and_verify((self.wpapsk_5g, self.dut)) + self.connect_to_wifi_network_and_verify((self.open_5g, self.dut)) for count in range(self.stress_count): self.log.info("Iteration %d", count+1) - self.verify_softap_full_on_off(self.wpapsk_5g, WIFI_CONFIG_APBAND_2G) - raise signals.TestPass(details="", extras={"Iterations":"%d" % - self.stress_count, "Pass":"%d" %(count+1)}) + self.verify_softap_full_on_off(self.open_5g, WIFI_CONFIG_APBAND_2G) @test_tracker_info(uuid="bde1443f-f912-408e-b01a-537548dd023c") def test_stress_wifi_connection_5G_DFS_softap_2G(self): @@ -194,11 +148,9 @@ class WifiStaApConcurrencyStressTest(WifiStaApConcurrencyTest): """ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS) wutils.wifi_toggle_state(self.dut, True) - self.connect_to_wifi_network_and_verify((self.wpapsk_5g, self.dut)) + self.connect_to_wifi_network_and_verify((self.open_5g, self.dut)) for count in range(self.stress_count): - self.verify_softap_full_on_off(self.wpapsk_5g, WIFI_CONFIG_APBAND_2G) - raise signals.TestPass(details="", extras={"Iterations":"%d" % - self.stress_count, "Pass":"%d" %(count+1)}) + self.verify_softap_full_on_off(self.open_5g, WIFI_CONFIG_APBAND_2G) @test_tracker_info(uuid="2b6a891a-e0d6-4660-abf6-579099ce6924") def test_stress_wifi_connection_2G_softap_5G(self): @@ -206,12 +158,10 @@ class WifiStaApConcurrencyStressTest(WifiStaApConcurrencyTest): """ self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G) wutils.wifi_toggle_state(self.dut, True) - self.connect_to_wifi_network_and_verify((self.wpapsk_2g, self.dut)) + self.connect_to_wifi_network_and_verify((self.open_2g, self.dut)) for count in range(self.stress_count): self.log.info("Iteration %d", count+1) - self.verify_softap_full_on_off(self.wpapsk_2g, WIFI_CONFIG_APBAND_5G) - raise signals.TestPass(details="", extras={"Iterations":"%d" % - self.stress_count, "Pass":"%d" %(count+1)}) + self.verify_softap_full_on_off(self.open_2g, WIFI_CONFIG_APBAND_5G) @test_tracker_info(uuid="f28abf22-9df0-4500-b342-6682ca305e60") def test_stress_wifi_connection_5G_softap_2G_with_location_scan_on(self): @@ -221,12 +171,10 @@ class WifiStaApConcurrencyStressTest(WifiStaApConcurrencyTest): self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G) self.turn_location_on_and_scan_toggle_on() wutils.wifi_toggle_state(self.dut, True) - self.connect_to_wifi_network_and_verify((self.wpapsk_5g, self.dut)) + self.connect_to_wifi_network_and_verify((self.open_5g, self.dut)) for count in range(self.stress_count): self.log.info("Iteration %d", count+1) - self.verify_softap_full_on_off(self.wpapsk_5g, WIFI_CONFIG_APBAND_2G) - raise signals.TestPass(details="", extras={"Iterations":"%d" % - self.stress_count, "Pass":"%d" %(count+1)}) + self.verify_softap_full_on_off(self.open_5g, WIFI_CONFIG_APBAND_2G) @test_tracker_info(uuid="0edb1500-6c60-442e-9268-a2ad9ee2b55c") def test_stress_softap_2G_wifi_connection_2G(self): @@ -237,9 +185,7 @@ class WifiStaApConcurrencyStressTest(WifiStaApConcurrencyTest): WIFI_CONFIG_APBAND_2G, check_connectivity=False) for count in range(self.stress_count): self.log.info("Iteration %d", count+1) - self.verify_wifi_full_on_off(self.wpapsk_2g, softap_config) - raise signals.TestPass(details="", extras={"Iterations":"%d" % - self.stress_count, "Pass":"%d" %(count+1)}) + self.verify_wifi_full_on_off(self.open_2g, softap_config) @test_tracker_info(uuid="162a6679-edd5-4daa-9f25-75d79cf4bb4a") def test_stress_softap_5G_wifi_connection_5G(self): @@ -250,9 +196,7 @@ class WifiStaApConcurrencyStressTest(WifiStaApConcurrencyTest): WIFI_CONFIG_APBAND_5G, check_connectivity=False) for count in range(self.stress_count): self.log.info("Iteration %d", count+1) - self.verify_wifi_full_on_off(self.wpapsk_5g, softap_config) - raise signals.TestPass(details="", extras={"Iterations":"%d" % - self.stress_count, "Pass":"%d" %(count+1)}) + self.verify_wifi_full_on_off(self.open_5g, softap_config) @test_tracker_info(uuid="ee98f2dd-c4f9-4f48-ab59-f577267760d5") def test_stress_softap_5G_wifi_connection_5G_DFS(self): @@ -263,9 +207,7 @@ class WifiStaApConcurrencyStressTest(WifiStaApConcurrencyTest): WIFI_CONFIG_APBAND_5G, check_connectivity=False) for count in range(self.stress_count): self.log.info("Iteration %d", count+1) - self.verify_wifi_full_on_off(self.wpapsk_5g, softap_config) - raise signals.TestPass(details="", extras={"Iterations":"%d" % - self.stress_count, "Pass":"%d" %(count+1)}) + self.verify_wifi_full_on_off(self.open_5g, softap_config) @test_tracker_info(uuid="b50750b5-d5b9-4687-b9e7-9fb15f54b428") def test_stress_softap_5G_wifi_connection_2G(self): @@ -276,9 +218,7 @@ class WifiStaApConcurrencyStressTest(WifiStaApConcurrencyTest): WIFI_CONFIG_APBAND_5G, check_connectivity=False) for count in range(self.stress_count): self.log.info("Iteration %d", count+1) - self.verify_wifi_full_on_off(self.wpapsk_2g, softap_config) - raise signals.TestPass(details="", extras={"Iterations":"%d" % - self.stress_count, "Pass":"%d" %(count+1)}) + self.verify_wifi_full_on_off(self.open_2g, softap_config) @test_tracker_info(uuid="9a2865db-8e4b-4339-9999-000ce9b6970b") def test_stress_softap_2G_wifi_connection_5G(self): @@ -289,9 +229,7 @@ class WifiStaApConcurrencyStressTest(WifiStaApConcurrencyTest): WIFI_CONFIG_APBAND_2G, check_connectivity=False) for count in range(self.stress_count): self.log.info("Iteration %d", count+1) - self.verify_wifi_full_on_off(self.wpapsk_5g, softap_config) - raise signals.TestPass(details="", extras={"Iterations":"%d" % - self.stress_count, "Pass":"%d" %(count+1)}) + self.verify_wifi_full_on_off(self.open_5g, softap_config) @test_tracker_info(uuid="add6609d-91d6-4b89-94c5-0ad8b941e3d1") def test_stress_softap_2G_wifi_connection_5G_DFS(self): @@ -302,9 +240,7 @@ class WifiStaApConcurrencyStressTest(WifiStaApConcurrencyTest): WIFI_CONFIG_APBAND_2G, check_connectivity=False) for count in range(self.stress_count): self.log.info("Iteration %d", count+1) - self.verify_wifi_full_on_off(self.wpapsk_5g, softap_config) - raise signals.TestPass(details="", extras={"Iterations":"%d" % - self.stress_count, "Pass":"%d" %(count+1)}) + self.verify_wifi_full_on_off(self.open_5g, softap_config) @test_tracker_info(uuid="ee42afb6-99d0-4330-933f-d4dd8c3626c6") def test_stress_softap_5G_wifi_connection_2G_with_location_scan_on(self): @@ -317,6 +253,4 @@ class WifiStaApConcurrencyStressTest(WifiStaApConcurrencyTest): WIFI_CONFIG_APBAND_5G, check_connectivity=False) for count in range(self.stress_count): self.log.info("Iteration %d", count+1) - self.verify_wifi_full_on_off(self.wpapsk_2g, softap_config) - raise signals.TestPass(details="", extras={"Iterations":"%d" % - self.stress_count, "Pass":"%d" %(count+1)}) + self.verify_wifi_full_on_off(self.open_2g, softap_config) diff --git a/acts/tests/google/wifi/WifiStaApConcurrencyTest.py b/acts_tests/tests/google/wifi/WifiStaApConcurrencyTest.py index c5d74249f1..6757be6e09 100644 --- a/acts/tests/google/wifi/WifiStaApConcurrencyTest.py +++ b/acts_tests/tests/google/wifi/WifiStaApConcurrencyTest.py @@ -92,8 +92,13 @@ class WifiStaApConcurrencyTest(WifiBaseTest): self.turn_location_on_and_scan_toggle_on() wutils.wifi_toggle_state(self.dut, True) self.access_points[0].close() - del self.user_params["reference_networks"] - del self.user_params["open_network"] + if "AccessPoint" in self.user_params: + try: + del self.user_params["reference_networks"] + del self.user_params["open_network"] + except KeyError as e: + self.log.warn("There is no 'reference_network' or " + "'open_network' to delete") def on_fail(self, test_name, begin_time): for ad in self.android_devices: @@ -114,8 +119,13 @@ class WifiStaApConcurrencyTest(WifiBaseTest): channel_2g = hostapd_constants.AP_DEFAULT_CHANNEL_2G if not channel_5g: channel_5g = hostapd_constants.AP_DEFAULT_CHANNEL_5G - self.legacy_configure_ap_and_start(channel_2g=channel_2g, - channel_5g=channel_5g) + if "AccessPoint" in self.user_params: + self.legacy_configure_ap_and_start(channel_2g=channel_2g, + channel_5g=channel_5g) + elif "OpenWrtAP" in self.user_params: + self.configure_openwrt_ap_and_start(open_network=True, + channel_2g=channel_2g, + channel_5g=channel_5g) self.open_2g = self.open_network[0]["2g"] self.open_5g = self.open_network[0]["5g"] diff --git a/acts/tests/google/wifi/WifiStressTest.py b/acts_tests/tests/google/wifi/WifiStressTest.py index e31adbb792..e31adbb792 100644 --- a/acts/tests/google/wifi/WifiStressTest.py +++ b/acts_tests/tests/google/wifi/WifiStressTest.py diff --git a/acts/tests/google/wifi/WifiTeleCoexTest.py b/acts_tests/tests/google/wifi/WifiTeleCoexTest.py index 6d5fef8708..6d5fef8708 100644 --- a/acts/tests/google/wifi/WifiTeleCoexTest.py +++ b/acts_tests/tests/google/wifi/WifiTeleCoexTest.py diff --git a/acts/tests/google/wifi/WifiTethering2GOpenOTATest.py b/acts_tests/tests/google/wifi/WifiTethering2GOpenOTATest.py index 0716158327..0716158327 100755 --- a/acts/tests/google/wifi/WifiTethering2GOpenOTATest.py +++ b/acts_tests/tests/google/wifi/WifiTethering2GOpenOTATest.py diff --git a/acts/tests/google/wifi/WifiTethering2GPskOTATest.py b/acts_tests/tests/google/wifi/WifiTethering2GPskOTATest.py index 7399e32879..7399e32879 100755 --- a/acts/tests/google/wifi/WifiTethering2GPskOTATest.py +++ b/acts_tests/tests/google/wifi/WifiTethering2GPskOTATest.py diff --git a/acts/tests/google/wifi/WifiTethering5GOpenOTATest.py b/acts_tests/tests/google/wifi/WifiTethering5GOpenOTATest.py index 985e7a759a..985e7a759a 100755 --- a/acts/tests/google/wifi/WifiTethering5GOpenOTATest.py +++ b/acts_tests/tests/google/wifi/WifiTethering5GOpenOTATest.py diff --git a/acts/tests/google/wifi/WifiTethering5GPskOTATest.py b/acts_tests/tests/google/wifi/WifiTethering5GPskOTATest.py index 9e68f2200a..9e68f2200a 100755 --- a/acts/tests/google/wifi/WifiTethering5GPskOTATest.py +++ b/acts_tests/tests/google/wifi/WifiTethering5GPskOTATest.py diff --git a/acts/tests/google/wifi/WifiTetheringPowerTest.py b/acts_tests/tests/google/wifi/WifiTetheringPowerTest.py index dd3cf7435f..dd3cf7435f 100644 --- a/acts/tests/google/wifi/WifiTetheringPowerTest.py +++ b/acts_tests/tests/google/wifi/WifiTetheringPowerTest.py diff --git a/acts/tests/google/wifi/WifiTetheringTest.py b/acts_tests/tests/google/wifi/WifiTetheringTest.py index d5d197a0a6..2ad29e4976 100644 --- a/acts/tests/google/wifi/WifiTetheringTest.py +++ b/acts_tests/tests/google/wifi/WifiTetheringTest.py @@ -24,19 +24,21 @@ from acts import test_runner from acts import utils from acts.controllers import adb from acts.test_decorators import test_tracker_info +from acts.test_utils.net import arduino_test_utils as dutils +from acts.test_utils.net import net_test_utils as nutils +from acts.test_utils.net import socket_test_utils as sutils from acts.test_utils.tel import tel_defines from acts.test_utils.tel.tel_data_utils import wait_for_cell_data_connection from acts.test_utils.tel.tel_test_utils import get_operator_name from acts.test_utils.tel.tel_test_utils import verify_http_connection from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G -from acts.test_utils.net import socket_test_utils as sutils -from acts.test_utils.net import arduino_test_utils as dutils -from acts.test_utils.net import net_test_utils as nutils from acts.test_utils.wifi import wifi_test_utils as wutils + WAIT_TIME = 5 + class WifiTetheringTest(base_test.BaseTestClass): """ Tests for Wifi Tethering """ @@ -45,11 +47,13 @@ class WifiTetheringTest(base_test.BaseTestClass): self.hotspot_device = self.android_devices[0] self.tethered_devices = self.android_devices[1:] - req_params = ("url", "open_network") + req_params = ("url", "open_network", "carrier_supports_ipv6", + "carrier_supports_tethering") self.unpack_userparams(req_params) self.network = {"SSID": "hotspot_%s" % utils.rand_ascii_str(6), "password": "pass_%s" % utils.rand_ascii_str(6)} - self.new_ssid = "wifi_tethering_test2" + + self.new_ssid = "hs_%s" % utils.rand_ascii_str(6) self.tcpdump_pid=[] nutils.verify_lte_data_and_tethering_supported(self.hotspot_device) @@ -59,10 +63,12 @@ class WifiTetheringTest(base_test.BaseTestClass): def setup_test(self): for ad in self.android_devices: self.tcpdump_pid.append(nutils.start_tcpdump(ad, self.test_name)) + self.tethered_devices[0].droid.telephonyToggleDataConnection(False) def teardown_test(self): if self.hotspot_device.droid.wifiIsApEnabled(): wutils.stop_wifi_tethering(self.hotspot_device) + self.tethered_devices[0].droid.telephonyToggleDataConnection(True) for ad, pid in zip(self.android_devices, self.tcpdump_pid): nutils.stop_tcpdump(ad, pid, self.test_name) self.tcpdump_pid = [] @@ -104,10 +110,8 @@ class WifiTetheringTest(base_test.BaseTestClass): True: if provider supports IPv6 tethering False: if not """ - # Currently only Verizon support IPv6 tethering - carrier_supports_tethering = ["vzw", "tmo", "Far EasTone", "Chunghwa Telecom"] operator = get_operator_name(self.log, dut) - return operator in carrier_supports_tethering + return operator in self.carrier_supports_tethering def _carrier_supports_ipv6(self,dut): """ Verify if carrier supports ipv6 @@ -117,10 +121,9 @@ class WifiTetheringTest(base_test.BaseTestClass): True: if carrier supports ipv6 False: if not """ - carrier_supports_ipv6 = ["vzw", "tmo", "Far EasTone", "Chunghwa Telecom"] operator = get_operator_name(self.log, dut) self.log.info("Carrier is %s" % operator) - return operator in carrier_supports_ipv6 + return operator in self.carrier_supports_ipv6 def _verify_ipv6_tethering(self, dut): """ Verify IPv6 tethering """ @@ -444,38 +447,30 @@ class WifiTetheringTest(base_test.BaseTestClass): wutils.stop_wifi_tethering(self.hotspot_device) @test_tracker_info(uuid="7edfb220-37f8-42ea-8d7c-39712fbe9be5") - def test_wifi_tethering_2ghz_ping_hotspot_interfaces(self): + def test_wifi_tethering_wpapsk_network_2g(self): """ Steps: - 1. Start wifi hotspot with 2ghz band - 2. Connect tethered device to hotspot device - 3. Ping 'wlan0' and 'rmnet_data' interface's IPv4 - and IPv6 interfaces on hotspot device from tethered device + 1. Start wifi tethering with wpapsk network 2G band + 2. Connect tethered device to the SSID + 3. Verify internet connectivity """ - wutils.toggle_wifi_off_and_on(self.hotspot_device) - self._start_wifi_tethering(WIFI_CONFIG_APBAND_2G) - wutils.wifi_connect(self.tethered_devices[0], self.network) - result = self._ping_hotspot_interfaces_from_tethered_device( - self.tethered_devices[0]) - wutils.stop_wifi_tethering(self.hotspot_device) - return result + self._start_wifi_tethering() + wutils.connect_to_wifi_network(self.tethered_devices[0], + self.network, + check_connectivity=True) @test_tracker_info(uuid="17e450f4-795f-4e67-adab-984940dffedc") - def test_wifi_tethering_5ghz_ping_hotspot_interfaces(self): + def test_wifi_tethering_wpapsk_network_5g(self): """ Steps: - 1. Start wifi hotspot with 5ghz band - 2. Connect tethered device to hotspot device - 3. Ping 'wlan0' and 'rmnet_data' interface's IPv4 - and IPv6 interfaces on hotspot device from tethered device + 1. Start wifi tethering with wpapsk network 5G band + 2. Connect tethered device to the SSID + 3. Verify internet connectivity """ - wutils.toggle_wifi_off_and_on(self.hotspot_device) self._start_wifi_tethering(WIFI_CONFIG_APBAND_5G) - wutils.wifi_connect(self.tethered_devices[0], self.network) - result = self._ping_hotspot_interfaces_from_tethered_device( - self.tethered_devices[0]) - wutils.stop_wifi_tethering(self.hotspot_device) - return result + wutils.connect_to_wifi_network(self.tethered_devices[0], + self.network, + check_connectivity=True) @test_tracker_info(uuid="2bc344cb-0277-4f06-b6cc-65b3972086ed") def test_change_wifi_hotspot_ssid_when_hotspot_enabled(self): @@ -488,7 +483,6 @@ class WifiTetheringTest(base_test.BaseTestClass): 5. Restart tethering and verify that the tethered device is able to connect to the new SSID """ - wutils.toggle_wifi_off_and_on(self.hotspot_device) dut = self.hotspot_device # start tethering and verify the wifi ap configuration settings @@ -498,12 +492,12 @@ class WifiTetheringTest(base_test.BaseTestClass): wifi_ap[wutils.WifiEnums.SSID_KEY] == \ self.network[wutils.WifiEnums.SSID_KEY], "Configured wifi hotspot SSID did not match with the expected SSID") - wutils.wifi_connect(self.tethered_devices[0], self.network) + wutils.connect_to_wifi_network(self.tethered_devices[0], self.network) # update the wifi ap configuration with new ssid config = {wutils.WifiEnums.SSID_KEY: self.new_ssid} config[wutils.WifiEnums.PWD_KEY] = self.network[wutils.WifiEnums.PWD_KEY] - config[wutils.WifiEnums.APBAND_KEY] = WIFI_CONFIG_APBAND_2G + config[wutils.WifiEnums.AP_BAND_KEY] = WIFI_CONFIG_APBAND_2G self._save_wifi_softap_configuration(dut, config) # start wifi tethering with new wifi ap configuration @@ -514,8 +508,7 @@ class WifiTetheringTest(base_test.BaseTestClass): new_network = {wutils.WifiEnums.SSID_KEY: self.new_ssid, wutils.WifiEnums.PWD_KEY: \ self.network[wutils.WifiEnums.PWD_KEY]} - wutils.wifi_connect(self.tethered_devices[0], new_network) - wutils.stop_wifi_tethering(self.hotspot_device) + wutils.connect_to_wifi_network(self.tethered_devices[0], new_network) @test_tracker_info(uuid="4cf7ab26-ca2d-46f6-9d3f-a935c7e04c97") def test_wifi_tethering_open_network_2g(self): @@ -526,11 +519,17 @@ class WifiTetheringTest(base_test.BaseTestClass): 2. Connect tethered device to the SSID 3. Verify internet connectivity """ + open_network = { + wutils.WifiEnums.SSID_KEY: "hs_2g_%s" % utils.rand_ascii_str(6) + } wutils.start_wifi_tethering( - self.hotspot_device, self.open_network[wutils.WifiEnums.SSID_KEY], - None, WIFI_CONFIG_APBAND_2G) - wutils.wifi_connect(self.tethered_devices[0], self.open_network) - wutils.stop_wifi_tethering(self.hotspot_device) + self.hotspot_device, + open_network[wutils.WifiEnums.SSID_KEY], + None, + WIFI_CONFIG_APBAND_2G) + wutils.connect_to_wifi_network(self.tethered_devices[0], + open_network, + check_connectivity=True) @test_tracker_info(uuid="f407049b-1324-40ea-a8d1-f90587933310") def test_wifi_tethering_open_network_5g(self): @@ -541,11 +540,17 @@ class WifiTetheringTest(base_test.BaseTestClass): 2. Connect tethered device to the SSID 3. Verify internet connectivity """ + open_network = { + wutils.WifiEnums.SSID_KEY: "hs_5g_%s" % utils.rand_ascii_str(6) + } wutils.start_wifi_tethering( - self.hotspot_device, self.open_network[wutils.WifiEnums.SSID_KEY], - None, WIFI_CONFIG_APBAND_5G) - wutils.wifi_connect(self.tethered_devices[0], self.open_network) - wutils.stop_wifi_tethering(self.hotspot_device) + self.hotspot_device, + open_network[wutils.WifiEnums.SSID_KEY], + None, + WIFI_CONFIG_APBAND_5G) + wutils.connect_to_wifi_network(self.tethered_devices[0], + open_network, + check_connectivity=True) @test_tracker_info(uuid="d964f2e6-0acb-417c-ada9-eb9fc5a470e4") def test_wifi_tethering_open_network_2g_stress(self): @@ -561,7 +566,7 @@ class WifiTetheringTest(base_test.BaseTestClass): # save open network wifi ap configuration with 2G band config = {wutils.WifiEnums.SSID_KEY: self.open_network[wutils.WifiEnums.SSID_KEY]} - config[wutils.WifiEnums.APBAND_KEY] = WIFI_CONFIG_APBAND_2G + config[wutils.WifiEnums.AP_BAND_KEY] = WIFI_CONFIG_APBAND_2G self._save_wifi_softap_configuration(self.hotspot_device, config) # turn on/off wifi hotspot, connect device @@ -585,7 +590,7 @@ class WifiTetheringTest(base_test.BaseTestClass): # save open network wifi ap configuration with 5G band config = {wutils.WifiEnums.SSID_KEY: self.open_network[wutils.WifiEnums.SSID_KEY]} - config[wutils.WifiEnums.APBAND_KEY] = WIFI_CONFIG_APBAND_5G + config[wutils.WifiEnums.AP_BAND_KEY] = WIFI_CONFIG_APBAND_5G self._save_wifi_softap_configuration(self.hotspot_device, config) # turn on/off wifi hotspot, connect device diff --git a/acts/tests/google/wifi/WifiThroughputStabilityTest.py b/acts_tests/tests/google/wifi/WifiThroughputStabilityTest.py index 7e2c3ef38a..af2bb76595 100644 --- a/acts/tests/google/wifi/WifiThroughputStabilityTest.py +++ b/acts_tests/tests/google/wifi/WifiThroughputStabilityTest.py @@ -400,6 +400,16 @@ class WifiThroughputStabilityTest(base_test.BaseTestClass): testcase_params['channel']) testcase_params['test_network'] = self.main_network[band] + if testcase_params['traffic_type'] == 'TCP': + testcase_params['iperf_socket_size'] = self.testclass_params.get( + 'tcp_socket_size', None) + testcase_params['iperf_processes'] = self.testclass_params.get( + 'tcp_processes', 1) + elif testcase_params['traffic_type'] == 'UDP': + testcase_params['iperf_socket_size'] = self.testclass_params.get( + 'udp_socket_size', None) + testcase_params['iperf_processes'] = self.testclass_params.get( + 'udp_processes', 1) if (testcase_params['traffic_direction'] == 'DL' and not isinstance(self.iperf_server, ipf.IPerfServerOverAdb) ) or (testcase_params['traffic_direction'] == 'UL' @@ -407,13 +417,17 @@ class WifiThroughputStabilityTest(base_test.BaseTestClass): testcase_params['iperf_args'] = wputils.get_iperf_arg_string( duration=self.testclass_params['iperf_duration'], reverse_direction=1, - traffic_type=testcase_params['traffic_type']) + traffic_type=testcase_params['traffic_type'], + socket_size=testcase_params['iperf_socket_size'], + num_processes=testcase_params['iperf_processes']) testcase_params['use_client_output'] = True else: testcase_params['iperf_args'] = wputils.get_iperf_arg_string( duration=self.testclass_params['iperf_duration'], reverse_direction=0, - traffic_type=testcase_params['traffic_type']) + traffic_type=testcase_params['traffic_type'], + socket_size=testcase_params['iperf_socket_size'], + num_processes=testcase_params['iperf_processes']) testcase_params['use_client_output'] = False return testcase_params diff --git a/acts/tests/google/wifi/WifiWakeTest.py b/acts_tests/tests/google/wifi/WifiWakeTest.py index 13c6b2dc0b..5fffb9088a 100644 --- a/acts/tests/google/wifi/WifiWakeTest.py +++ b/acts_tests/tests/google/wifi/WifiWakeTest.py @@ -24,10 +24,14 @@ from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest import acts.test_utils.wifi.wifi_test_utils as wutils import acts.utils +WifiEnums = wutils.WifiEnums +SSID = WifiEnums.SSID_KEY CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT = 5 +SCANS_REQUIRED_TO_FIND_SSID = 5 LAST_DISCONNECT_TIMEOUT_MILLIS = 5000 LAST_DISCONNECT_TIMEOUT_SEC = LAST_DISCONNECT_TIMEOUT_MILLIS / 1000 PRESCAN_DELAY_SEC = 5 +WIFI_TOGGLE_DELAY_SEC = 3 class WifiWakeTest(WifiBaseTest): @@ -53,6 +57,9 @@ class WifiWakeTest(WifiBaseTest): if "AccessPoint" in self.user_params: self.legacy_configure_ap_and_start(mirror_ap=False, ap_count=2) + elif "OpenWrtAP" in self.user_params: + self.configure_openwrt_ap_and_start(wpa_network=True, + ap_count=2) # use 2G since Wifi Wake does not work if an AP is on a 5G DFS channel self.ap_a = self.reference_networks[0]["2g"] @@ -60,28 +67,46 @@ class WifiWakeTest(WifiBaseTest): self.ap_a_atten = self.attenuators[0] self.ap_b_atten = self.attenuators[2] + if "OpenWrtAP" in self.user_params: + self.ap_b_atten = self.attenuators[1] # TODO(b/119040540): this method of disabling/re-enabling Wifi on APs is # hacky, switch to using public methods when they are implemented def ap_a_off(self): + if "OpenWrtAP" in self.user_params: + self.access_points[0].stop_ap() + self.log.info('Turned AP A off') + return ap_a_hostapd = self.access_points[0]._aps['wlan0'].hostapd if ap_a_hostapd.is_alive(): ap_a_hostapd.stop() self.log.info('Turned AP A off') def ap_a_on(self): + if "OpenWrtAP" in self.user_params: + self.access_points[0].start_ap() + self.log.info('Turned AP A on') + return ap_a_hostapd = self.access_points[0]._aps['wlan0'].hostapd if not ap_a_hostapd.is_alive(): ap_a_hostapd.start(ap_a_hostapd.config) self.log.info('Turned AP A on') def ap_b_off(self): + if "OpenWrtAP" in self.user_params: + self.access_points[1].stop_ap() + self.log.info('Turned AP B off') + return ap_b_hostapd = self.access_points[1]._aps['wlan0'].hostapd if ap_b_hostapd.is_alive(): ap_b_hostapd.stop() self.log.info('Turned AP B off') def ap_b_on(self): + if "OpenWrtAP" in self.user_params: + self.access_points[1].start_ap() + self.log.info('Turned AP B on') + return ap_b_hostapd = self.access_points[1]._aps['wlan0'].hostapd if not ap_b_hostapd.is_alive(): ap_b_hostapd.start(ap_b_hostapd.config) @@ -109,7 +134,15 @@ class WifiWakeTest(WifiBaseTest): self.dut.take_bug_report(test_name, begin_time) self.dut.cat_adb_log(test_name, begin_time) - def do_location_scan(self, num_times=1): + def find_ssid_in_scan_results(self, scan_results_batches, ssid): + scan_results_batch = scan_results_batches[0] + scan_results = scan_results_batch["ScanResults"] + for scan_result in scan_results: + if ssid == scan_result["SSID"]: + return True + return False + + def do_location_scan(self, num_times=1, ssid_to_find=None): scan_settings = { "band": wutils.WifiEnums.WIFI_BAND_BOTH, "periodInMs": 0, @@ -142,6 +175,10 @@ class WifiWakeTest(WifiBaseTest): event = self.dut.ed.pop_event(event_name, wait_time) self.log.debug("Event received: %s", event) result_received += 1 + scan_results_batches = event["data"]["Results"] + if ssid_to_find and self.find_ssid_in_scan_results( + scan_results_batches, ssid_to_find): + return except queue.Empty as error: asserts.assert_true( result_received >= 1, @@ -187,15 +224,17 @@ class WifiWakeTest(WifiBaseTest): self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2) self.ap_a_on() - self.do_location_scan() + self.do_location_scan( + SCANS_REQUIRED_TO_FIND_SSID, self.ap_a[wutils.WifiEnums.SSID_KEY]) + time.sleep(WIFI_TOGGLE_DELAY_SEC) asserts.assert_true( self.dut.droid.wifiCheckState(), "Expect Wifi Wake to enable Wifi, but Wifi is disabled.") - @test_tracker_info(uuid="") + @test_tracker_info(uuid="3cecd1c5-54bc-44a2-86f7-ad84625bf094") def test_reconnect_wifi_network_suggestion(self): """Tests that Wifi Wake re-enables Wifi for app provided suggestion.""" - self.dut.log.info("Adding network suggestions"); + self.dut.log.info("Adding network suggestions") asserts.assert_true( self.dut.droid.wifiAddNetworkSuggestions([self.ap_a]), "Failed to add suggestions") @@ -203,15 +242,23 @@ class WifiWakeTest(WifiBaseTest): self.dut.droid.wifiAddNetworkSuggestions([self.ap_b]), "Failed to add suggestions") # Enable suggestions by the app. - self.dut.log.debug("Enabling suggestions from test"); + self.dut.log.debug("Enabling suggestions from test") self.dut.adb.shell("cmd wifi network-suggestions-set-user-approved" + " " + SL4A_APK_NAME + " yes") # Ensure network is seen in scan results & auto-connected to. self.do_location_scan(2) wutils.wait_for_connect(self.dut) + current_network = self.dut.droid.wifiGetConnectionInfo() self.dut.ed.clear_all_events() - self.ap_a_off() - self.ap_b_off() + if current_network[SSID] == self.ap_a[SSID]: + # connected to AP A, so turn AP B off first to prevent the + # device from immediately reconnecting to AP B + self.ap_b_off() + self.ap_a_off() + else: + self.ap_a_off() + self.ap_b_off() + wutils.wait_for_disconnect(self.dut) self.log.info("Wifi Disconnected") time.sleep(LAST_DISCONNECT_TIMEOUT_SEC * 1.2) @@ -220,7 +267,9 @@ class WifiWakeTest(WifiBaseTest): self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2) self.ap_a_on() - self.do_location_scan() + self.do_location_scan( + SCANS_REQUIRED_TO_FIND_SSID, self.ap_a[wutils.WifiEnums.SSID_KEY]) + time.sleep(WIFI_TOGGLE_DELAY_SEC) asserts.assert_true( self.dut.droid.wifiCheckState(), "Expect Wifi Wake to enable Wifi, but Wifi is disabled.") @@ -240,7 +289,9 @@ class WifiWakeTest(WifiBaseTest): # evict AP A from Wakeup Lock self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2) self.ap_a_on() - self.do_location_scan() + self.do_location_scan( + SCANS_REQUIRED_TO_FIND_SSID, self.ap_a[wutils.WifiEnums.SSID_KEY]) + time.sleep(WIFI_TOGGLE_DELAY_SEC) asserts.assert_true( self.dut.droid.wifiCheckState(), "Expect Wifi Wake to enable Wifi, but Wifi is disabled.") @@ -282,7 +333,9 @@ class WifiWakeTest(WifiBaseTest): time.sleep(PRESCAN_DELAY_SEC) self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2) self.ap_a_on() - self.do_location_scan() + self.do_location_scan( + SCANS_REQUIRED_TO_FIND_SSID, self.ap_a[wutils.WifiEnums.SSID_KEY]) + time.sleep(WIFI_TOGGLE_DELAY_SEC) asserts.assert_true( self.dut.droid.wifiCheckState(), "Expect Wifi Wake to enable Wifi, but Wifi is disabled.") @@ -322,7 +375,9 @@ class WifiWakeTest(WifiBaseTest): self.ap_b_off() self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2) self.ap_a_on() - self.do_location_scan() + self.do_location_scan( + SCANS_REQUIRED_TO_FIND_SSID, self.ap_a[wutils.WifiEnums.SSID_KEY]) + time.sleep(WIFI_TOGGLE_DELAY_SEC) asserts.assert_true( self.dut.droid.wifiCheckState(), "Expect Wifi Wake to enable Wifi, but Wifi is disabled.") @@ -350,14 +405,11 @@ class WifiWakeTest(WifiBaseTest): self.ap_a_atten.set_atten(30) self.ap_b_atten.set_atten(0) - self.do_location_scan() + self.do_location_scan( + SCANS_REQUIRED_TO_FIND_SSID, self.ap_b[wutils.WifiEnums.SSID_KEY]) + time.sleep(WIFI_TOGGLE_DELAY_SEC) asserts.assert_true( self.dut.droid.wifiCheckState(), "Expect Wifi Wake to enable Wifi, but Wifi is disabled.") expected_ssid = self.ap_b[wutils.WifiEnums.SSID_KEY] - actual_ssid = self.dut.droid.wifiGetConnectionInfo()[ - wutils.WifiEnums.SSID_KEY] - asserts.assert_equal( - expected_ssid, actual_ssid, - ("Expected to connect to SSID '{}', but actually connected to " - "'{}' instead.").format(expected_ssid, actual_ssid)) + wutils.wait_for_connect(self.dut, expected_ssid) diff --git a/acts/tests/google/wifi/WifiWpa3OweTest.py b/acts_tests/tests/google/wifi/WifiWpa3OweTest.py index fbe939c216..ab0f23db2b 100644 --- a/acts/tests/google/wifi/WifiWpa3OweTest.py +++ b/acts_tests/tests/google/wifi/WifiWpa3OweTest.py @@ -100,4 +100,23 @@ class WifiWpa3OweTest(WifiBaseTest): def test_connect_to_wpa3_personal_5g(self): wutils.start_wifi_connection_scan_and_ensure_network_found(self.dut, self.wpa3_personal_5g[WifiEnums.SSID_KEY]) - wutils.connect_to_wifi_network(self.dut, self.owe_5g) + wutils.connect_to_wifi_network(self.dut, self.wpa3_personal_5g) + + @test_tracker_info(uuid="a8fb46be-3487-4dc8-a393-5af992b27f45") + def test_connect_to_wpa3_personal_reconnection(self): + """ This is to catch auth reject which is caused by PMKSA cache. + Steps: + ------------------------------ + 1. Connect STA to WPA3 AP + 2. Turn off the WiFi or connect to a different AP + 3. Turn on the WiFi or connect back to WPA3 AP. + 4. Initial connect request fails + Second connect request from framework succeeds. + """ + wutils.start_wifi_connection_scan_and_ensure_network_found(self.dut, + self.wpa3_personal_2g[WifiEnums.SSID_KEY]) + wutils.connect_to_wifi_network(self.dut, self.wpa3_personal_2g) + wutils.toggle_wifi_off_and_on(self.dut) + wutils.start_wifi_connection_scan_and_ensure_network_found(self.dut, + self.wpa3_personal_2g[WifiEnums.SSID_KEY]) + wutils.connect_to_wifi_network(self.dut, self.wpa3_personal_2g) diff --git a/acts_tests/tests/google/wifi/__init__.py b/acts_tests/tests/google/wifi/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/acts_tests/tests/google/wifi/__init__.py diff --git a/acts/tests/google/wifi/aware/README.md b/acts_tests/tests/google/wifi/aware/README.md index 6b969141ab..11a8296394 100644 --- a/acts/tests/google/wifi/aware/README.md +++ b/acts_tests/tests/google/wifi/aware/README.md @@ -33,7 +33,7 @@ group sub-directories contains a file with the same name as that of the directory which lists all tests in the directory. E.g. to execute all functional tests execute: -`act.py -c <config> -tf ./tools/test/connectivity/acts_tests/tests/google/wifi/aware/functional/functional` +`act.py -c <config> -tf ./tools/test/connectivity/acts/tests/google/wifi/aware/functional/functional` ## Test Configurations The test configurations, the `<config>` in the commands above, are stored in diff --git a/acts/tests/google/wifi/aware/config/wifi_aware.json b/acts_tests/tests/google/wifi/aware/config/wifi_aware.json index f1bd907ff1..97323fc6fa 100644 --- a/acts/tests/google/wifi/aware/config/wifi_aware.json +++ b/acts_tests/tests/google/wifi/aware/config/wifi_aware.json @@ -9,7 +9,7 @@ } ], "logpath": "~/logs", - "testpaths": ["./tools/test/connectivity/acts_tests/tests/google/wifi"], + "testpaths": ["./tools/test/connectivity/acts/tests/google/wifi"], "adb_logcat_param": "-b all", "aware_default_power_mode": "INTERACTIVE" } diff --git a/acts/tests/google/wifi/aware/config/wifi_aware_non_interactive.json b/acts_tests/tests/google/wifi/aware/config/wifi_aware_non_interactive.json index 391775689f..f2e6e79b53 100644 --- a/acts/tests/google/wifi/aware/config/wifi_aware_non_interactive.json +++ b/acts_tests/tests/google/wifi/aware/config/wifi_aware_non_interactive.json @@ -9,7 +9,7 @@ } ], "logpath": "~/logs", - "testpaths": ["./tools/test/connectivity/acts_tests/tests/google/wifi"], + "testpaths": ["./tools/test/connectivity/acts/tests/google/wifi"], "adb_logcat_param": "-b all", "aware_default_power_mode": "NON_INTERACTIVE" } diff --git a/acts/tests/google/wifi/aware/functional/AttachTest.py b/acts_tests/tests/google/wifi/aware/functional/AttachTest.py index 0df82a1c77..0df82a1c77 100644 --- a/acts/tests/google/wifi/aware/functional/AttachTest.py +++ b/acts_tests/tests/google/wifi/aware/functional/AttachTest.py diff --git a/acts/tests/google/wifi/aware/functional/CapabilitiesTest.py b/acts_tests/tests/google/wifi/aware/functional/CapabilitiesTest.py index 3bed77fdc6..3bed77fdc6 100644 --- a/acts/tests/google/wifi/aware/functional/CapabilitiesTest.py +++ b/acts_tests/tests/google/wifi/aware/functional/CapabilitiesTest.py diff --git a/acts/tests/google/wifi/aware/functional/DataPathTest.py b/acts_tests/tests/google/wifi/aware/functional/DataPathTest.py index 3f68376831..b8ac693c47 100644 --- a/acts/tests/google/wifi/aware/functional/DataPathTest.py +++ b/acts_tests/tests/google/wifi/aware/functional/DataPathTest.py @@ -20,6 +20,7 @@ from acts import asserts from acts.test_decorators import test_tracker_info from acts.test_utils.net import connectivity_const as cconsts from acts.test_utils.wifi import wifi_test_utils as wutils +from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest from acts.test_utils.wifi.aware import aware_const as aconsts from acts.test_utils.wifi.aware import aware_test_utils as autils from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest @@ -596,15 +597,22 @@ class DataPathTest(AwareBaseTest): # Initiator & Responder: # - expect unavailable on the Initiator party if the - # Initiator or Responder has a bad ID - # - but a Responder will keep waiting ... + # Initiator and Responder with mac or encryption mismatch + # - For responder: + # - If mac mismatch, responder will keep waiting ... + # - If encryption mismatch, responder expect unavailable autils.wait_for_event_with_keys( init_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT, (cconsts.NETWORK_CB_KEY_EVENT, cconsts.NETWORK_CB_UNAVAILABLE)) time.sleep(autils.EVENT_NDP_TIMEOUT) - autils.fail_on_event_with_keys( - resp_dut, cconsts.EVENT_NETWORK_CALLBACK, 0, - (cconsts.NETWORK_CB_KEY_ID, init_req_key)) + if init_mismatch_mac or resp_mismatch_mac: + autils.fail_on_event_with_keys( + resp_dut, cconsts.EVENT_NETWORK_CALLBACK, 0, + (cconsts.NETWORK_CB_KEY_ID, resp_req_key)) + else: + autils.wait_for_event_with_keys( + resp_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT, + (cconsts.NETWORK_CB_KEY_EVENT, cconsts.NETWORK_CB_UNAVAILABLE)) # clean-up resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key) @@ -656,6 +664,7 @@ class DataPathTest(AwareBaseTest): ####################################### @test_tracker_info(uuid="fa30bedc-d1de-4440-bf25-ec00d10555af") + @WifiBaseTest.wifi_test_wrap def test_ib_unsolicited_passive_open_specific(self): """Data-path: in-band, unsolicited/passive, open encryption, specific peer @@ -668,6 +677,7 @@ class DataPathTest(AwareBaseTest): use_peer_id=True) @test_tracker_info(uuid="57fc9d53-32ae-470f-a8b1-2fe37893687d") + @WifiBaseTest.wifi_test_wrap def test_ib_unsolicited_passive_open_any(self): """Data-path: in-band, unsolicited/passive, open encryption, any peer @@ -680,6 +690,7 @@ class DataPathTest(AwareBaseTest): use_peer_id=False) @test_tracker_info(uuid="93b2a23d-8579-448a-936c-7812929464cf") + @WifiBaseTest.wifi_test_wrap def test_ib_unsolicited_passive_passphrase_specific(self): """Data-path: in-band, unsolicited/passive, passphrase, specific peer @@ -692,6 +703,7 @@ class DataPathTest(AwareBaseTest): use_peer_id=True) @test_tracker_info(uuid="1736126f-a0ff-4712-acc4-f89b4eef5716") + @WifiBaseTest.wifi_test_wrap def test_ib_unsolicited_passive_passphrase_any(self): """Data-path: in-band, unsolicited/passive, passphrase, any peer @@ -704,6 +716,7 @@ class DataPathTest(AwareBaseTest): use_peer_id=False) @test_tracker_info(uuid="b9353d5b-3f77-46bf-bfd9-65d56a7c939a") + @WifiBaseTest.wifi_test_wrap def test_ib_unsolicited_passive_pmk_specific(self): """Data-path: in-band, unsolicited/passive, PMK, specific peer @@ -716,6 +729,7 @@ class DataPathTest(AwareBaseTest): use_peer_id=True) @test_tracker_info(uuid="06f3b2ab-4a10-4398-83a4-6a23851b1662") + @WifiBaseTest.wifi_test_wrap def test_ib_unsolicited_passive_pmk_any(self): """Data-path: in-band, unsolicited/passive, PMK, any peer @@ -728,6 +742,7 @@ class DataPathTest(AwareBaseTest): use_peer_id=False) @test_tracker_info(uuid="0ed7d8b3-a69e-46ba-aeb7-13e507ecf290") + @WifiBaseTest.wifi_test_wrap def test_ib_solicited_active_open_specific(self): """Data-path: in-band, solicited/active, open encryption, specific peer @@ -740,6 +755,7 @@ class DataPathTest(AwareBaseTest): use_peer_id=True) @test_tracker_info(uuid="c7ba6d28-5ef6-45d9-95d5-583ad6d981f3") + @WifiBaseTest.wifi_test_wrap def test_ib_solicited_active_open_any(self): """Data-path: in-band, solicited/active, open encryption, any peer @@ -752,6 +768,7 @@ class DataPathTest(AwareBaseTest): use_peer_id=False) @test_tracker_info(uuid="388cea99-0e2e-49ea-b00e-f3e56b6236e5") + @WifiBaseTest.wifi_test_wrap def test_ib_solicited_active_passphrase_specific(self): """Data-path: in-band, solicited/active, passphrase, specific peer @@ -764,6 +781,7 @@ class DataPathTest(AwareBaseTest): use_peer_id=True) @test_tracker_info(uuid="fcd3e28a-5eab-4169-8a0c-dc7204dcdc13") + @WifiBaseTest.wifi_test_wrap def test_ib_solicited_active_passphrase_any(self): """Data-path: in-band, solicited/active, passphrase, any peer @@ -776,6 +794,7 @@ class DataPathTest(AwareBaseTest): use_peer_id=False) @test_tracker_info(uuid="9d4eaad7-ba53-4a06-8ce0-e308daea3309") + @WifiBaseTest.wifi_test_wrap def test_ib_solicited_active_pmk_specific(self): """Data-path: in-band, solicited/active, PMK, specific peer @@ -788,6 +807,7 @@ class DataPathTest(AwareBaseTest): use_peer_id=True) @test_tracker_info(uuid="129d850e-c312-4137-a67b-05ae95fe66cc") + @WifiBaseTest.wifi_test_wrap def test_ib_solicited_active_pmk_any(self): """Data-path: in-band, solicited/active, PMK, any peer @@ -819,6 +839,7 @@ class DataPathTest(AwareBaseTest): ####################################### @test_tracker_info(uuid="e855dd81-45c8-4bb2-a204-7687c48ff843") + @WifiBaseTest.wifi_test_wrap def test_ib_extra_pub_same_unsolicited_passive_open_specific(self): """Data-path: in-band, unsolicited/passive, open encryption, specific peer. @@ -835,7 +856,8 @@ class DataPathTest(AwareBaseTest): pub_on_both=True, pub_on_both_same=True) - @test_tracker_info(uuid="57fc9d53-32ae-470f-a8b1-2fe37893687d") + @test_tracker_info(uuid="228ea657-82e6-44bc-8369-a2c719a5e252") + @WifiBaseTest.wifi_test_wrap def test_ib_extra_pub_same_unsolicited_passive_open_any(self): """Data-path: in-band, unsolicited/passive, open encryption, any peer. @@ -853,6 +875,7 @@ class DataPathTest(AwareBaseTest): pub_on_both_same=True) @test_tracker_info(uuid="7a32f439-d745-4716-a75e-b54109aaaf82") + @WifiBaseTest.wifi_test_wrap def test_ib_extra_pub_diff_unsolicited_passive_open_specific(self): """Data-path: in-band, unsolicited/passive, open encryption, specific peer. @@ -870,6 +893,7 @@ class DataPathTest(AwareBaseTest): pub_on_both_same=False) @test_tracker_info(uuid="a14ddc66-88fd-4b49-ab37-225533867c63") + @WifiBaseTest.wifi_test_wrap def test_ib_extra_pub_diff_unsolicited_passive_open_any(self): """Data-path: in-band, unsolicited/passive, open encryption, any peer. @@ -903,6 +927,7 @@ class DataPathTest(AwareBaseTest): ####################################### @test_tracker_info(uuid="7db17d8c-1dce-4084-b695-215bbcfe7d41") + @WifiBaseTest.wifi_test_wrap def test_oob_open_specific(self): """Data-path: out-of-band, open encryption, specific peer @@ -912,6 +937,7 @@ class DataPathTest(AwareBaseTest): encr_type=self.ENCR_TYPE_OPEN, use_peer_id=True) @test_tracker_info(uuid="ad416d89-cb95-4a07-8d29-ee213117450b") + @WifiBaseTest.wifi_test_wrap def test_oob_open_any(self): """Data-path: out-of-band, open encryption, any peer @@ -921,6 +947,7 @@ class DataPathTest(AwareBaseTest): encr_type=self.ENCR_TYPE_OPEN, use_peer_id=False) @test_tracker_info(uuid="74937a3a-d524-43e2-8979-4449271cab52") + @WifiBaseTest.wifi_test_wrap def test_oob_passphrase_specific(self): """Data-path: out-of-band, passphrase, specific peer @@ -930,6 +957,7 @@ class DataPathTest(AwareBaseTest): encr_type=self.ENCR_TYPE_PASSPHRASE, use_peer_id=True) @test_tracker_info(uuid="afcbdc7e-d3a9-465b-b1da-ce2e42e3941e") + @WifiBaseTest.wifi_test_wrap def test_oob_passphrase_any(self): """Data-path: out-of-band, passphrase, any peer @@ -939,6 +967,7 @@ class DataPathTest(AwareBaseTest): encr_type=self.ENCR_TYPE_PASSPHRASE, use_peer_id=False) @test_tracker_info(uuid="0d095031-160a-4537-aab5-41b6ad5d55f8") + @WifiBaseTest.wifi_test_wrap def test_oob_pmk_specific(self): """Data-path: out-of-band, PMK, specific peer @@ -948,6 +977,7 @@ class DataPathTest(AwareBaseTest): encr_type=self.ENCR_TYPE_PMK, use_peer_id=True) @test_tracker_info(uuid="e45477bd-66cc-4eb7-88dd-4518c8aa2a74") + @WifiBaseTest.wifi_test_wrap def test_oob_pmk_any(self): """Data-path: out-of-band, PMK, any peer @@ -957,6 +987,7 @@ class DataPathTest(AwareBaseTest): encr_type=self.ENCR_TYPE_PMK, use_peer_id=False) @test_tracker_info(uuid="dd464f24-b404-4eea-955c-d10c9e8adefc") + @WifiBaseTest.wifi_test_wrap def test_oob_ib_coex_open_specific(self): """Data-path: out-of-band, open encryption, specific peer - in-band coex: set up a concurrent discovery session to verify no impact. The session @@ -970,6 +1001,7 @@ class DataPathTest(AwareBaseTest): setup_discovery_sessions=True) @test_tracker_info(uuid="088fcd3a-b015-4179-a9a5-91f782b03e3b") + @WifiBaseTest.wifi_test_wrap def test_oob_ib_coex_open_any(self): """Data-path: out-of-band, open encryption, any peer - in-band coex: set up a concurrent discovery session to verify no impact. The session @@ -985,6 +1017,7 @@ class DataPathTest(AwareBaseTest): ############################################################## @test_tracker_info(uuid="1c2c9805-dc1e-43b5-a1b8-315e8c9a4337") + @WifiBaseTest.wifi_test_wrap def test_passphrase_min(self): """Data-path: minimum passphrase length @@ -998,6 +1031,7 @@ class DataPathTest(AwareBaseTest): passphrase_to_use=self.PASSPHRASE_MIN) @test_tracker_info(uuid="e696e2b9-87a9-4521-b337-61b9efaa2057") + @WifiBaseTest.wifi_test_wrap def test_passphrase_max(self): """Data-path: maximum passphrase length @@ -1011,30 +1045,35 @@ class DataPathTest(AwareBaseTest): passphrase_to_use=self.PASSPHRASE_MAX) @test_tracker_info(uuid="533cd44c-ff30-4283-ac28-f71fd7b4f02d") + @WifiBaseTest.wifi_test_wrap def test_negative_mismatch_publisher_peer_id(self): """Data-path: failure when publisher peer ID is mismatched""" self.run_mismatched_ib_data_path_test( pub_mismatch=True, sub_mismatch=False) @test_tracker_info(uuid="682f275e-722a-4f8b-85e7-0dcea9d25532") + @WifiBaseTest.wifi_test_wrap def test_negative_mismatch_subscriber_peer_id(self): """Data-path: failure when subscriber peer ID is mismatched""" self.run_mismatched_ib_data_path_test( pub_mismatch=False, sub_mismatch=True) @test_tracker_info(uuid="7fa82796-7fc9-4d9e-bbbb-84b751788943") + @WifiBaseTest.wifi_test_wrap def test_negative_mismatch_init_mac(self): """Data-path: failure when Initiator MAC address mismatch""" self.run_mismatched_oob_data_path_test( init_mismatch_mac=True, resp_mismatch_mac=False) @test_tracker_info(uuid="edeae959-4644-44f9-8d41-bdeb5216954e") + @WifiBaseTest.wifi_test_wrap def test_negative_mismatch_resp_mac(self): """Data-path: failure when Responder MAC address mismatch""" self.run_mismatched_oob_data_path_test( init_mismatch_mac=False, resp_mismatch_mac=True) @test_tracker_info(uuid="91f46949-c47f-49f9-a90f-6fae699613a7") + @WifiBaseTest.wifi_test_wrap def test_negative_mismatch_passphrase(self): """Data-path: failure when passphrases mismatch""" self.run_mismatched_oob_data_path_test( @@ -1042,6 +1081,7 @@ class DataPathTest(AwareBaseTest): resp_encr_type=self.ENCR_TYPE_PASSPHRASE) @test_tracker_info(uuid="01c49c2e-dc92-4a27-bb47-c4fc67617c23") + @WifiBaseTest.wifi_test_wrap def test_negative_mismatch_pmk(self): """Data-path: failure when PMK mismatch""" self.run_mismatched_oob_data_path_test( @@ -1049,6 +1089,7 @@ class DataPathTest(AwareBaseTest): resp_encr_type=self.ENCR_TYPE_PMK) @test_tracker_info(uuid="4d651797-5fbb-408e-a4b6-a6e1944136da") + @WifiBaseTest.wifi_test_wrap def test_negative_mismatch_open_passphrase(self): """Data-path: failure when initiator is open, and responder passphrase""" self.run_mismatched_oob_data_path_test( @@ -1056,6 +1097,7 @@ class DataPathTest(AwareBaseTest): resp_encr_type=self.ENCR_TYPE_PASSPHRASE) @test_tracker_info(uuid="1ae697f4-5987-4187-aeef-1e22d07d4a7c") + @WifiBaseTest.wifi_test_wrap def test_negative_mismatch_open_pmk(self): """Data-path: failure when initiator is open, and responder PMK""" self.run_mismatched_oob_data_path_test( @@ -1063,6 +1105,7 @@ class DataPathTest(AwareBaseTest): resp_encr_type=self.ENCR_TYPE_PMK) @test_tracker_info(uuid="f027b1cc-0e7a-4075-b880-5e64b288afbd") + @WifiBaseTest.wifi_test_wrap def test_negative_mismatch_pmk_passphrase(self): """Data-path: failure when initiator is pmk, and responder passphrase""" self.run_mismatched_oob_data_path_test( @@ -1070,6 +1113,7 @@ class DataPathTest(AwareBaseTest): resp_encr_type=self.ENCR_TYPE_PASSPHRASE) @test_tracker_info(uuid="0819bbd4-72ae-49c4-bd46-5448db2b0a06") + @WifiBaseTest.wifi_test_wrap def test_negative_mismatch_passphrase_open(self): """Data-path: failure when initiator is passphrase, and responder open""" self.run_mismatched_oob_data_path_test( @@ -1077,6 +1121,7 @@ class DataPathTest(AwareBaseTest): resp_encr_type=self.ENCR_TYPE_OPEN) @test_tracker_info(uuid="7ef24f62-8e6b-4732-88a3-80a43584dda4") + @WifiBaseTest.wifi_test_wrap def test_negative_mismatch_pmk_open(self): """Data-path: failure when initiator is PMK, and responder open""" self.run_mismatched_oob_data_path_test( @@ -1084,6 +1129,7 @@ class DataPathTest(AwareBaseTest): resp_encr_type=self.ENCR_TYPE_OPEN) @test_tracker_info(uuid="7b9c9efc-1c06-465e-8a5e-d6a22ac1da97") + @WifiBaseTest.wifi_test_wrap def test_negative_mismatch_passphrase_pmk(self): """Data-path: failure when initiator is passphrase, and responder pmk""" self.run_mismatched_oob_data_path_test( @@ -1132,6 +1178,7 @@ class DataPathTest(AwareBaseTest): "Network specifier leak!") @test_tracker_info(uuid="2e325e2b-d552-4890-b470-20b40284395d") + @WifiBaseTest.wifi_test_wrap def test_multiple_identical_networks(self): """Validate that creating multiple networks between 2 devices, each network with identical configuration is supported over a single NDP. @@ -1272,6 +1319,8 @@ class DataPathTest(AwareBaseTest): for init_req_key in init_req_keys: init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key) + @test_tracker_info(uuid="34cf12e8-5df6-49bd-b384-e9935d89a5b7") + @WifiBaseTest.wifi_test_wrap def test_identical_network_from_both_sides(self): """Validate that requesting two identical NDPs (Open) each being initiated from a different side, results in the same/single NDP. @@ -1587,6 +1636,7 @@ class DataPathTest(AwareBaseTest): dut1.droid.connectivityUnregisterNetworkCallback(dut1_req_key) @test_tracker_info(uuid="2d728163-11cc-46ba-a973-c8e1e71397fc") + @WifiBaseTest.wifi_test_wrap def test_multiple_ndi_open_passphrase(self): """Verify that between 2 DUTs can create 2 NDPs with different security configuration (one open, one using passphrase). The result should use two @@ -1594,6 +1644,7 @@ class DataPathTest(AwareBaseTest): self.run_multiple_ndi([None, self.PASSPHRASE]) @test_tracker_info(uuid="5f2c32aa-20b2-41f0-8b1e-d0b68df73ada") + @WifiBaseTest.wifi_test_wrap def test_multiple_ndi_open_pmk(self): """Verify that between 2 DUTs can create 2 NDPs with different security configuration (one open, one using pmk). The result should use two @@ -1601,6 +1652,7 @@ class DataPathTest(AwareBaseTest): self.run_multiple_ndi([None, self.PMK]) @test_tracker_info(uuid="34467659-bcfb-40cd-ba25-7e50560fca63") + @WifiBaseTest.wifi_test_wrap def test_multiple_ndi_passphrase_pmk(self): """Verify that between 2 DUTs can create 2 NDPs with different security configuration (one using passphrase, one using pmk). The result should use @@ -1608,6 +1660,7 @@ class DataPathTest(AwareBaseTest): self.run_multiple_ndi([self.PASSPHRASE, self.PMK]) @test_tracker_info(uuid="d9194ce6-45b6-41b1-9cc8-ada79968966d") + @WifiBaseTest.wifi_test_wrap def test_multiple_ndi_passphrases(self): """Verify that between 2 DUTs can create 2 NDPs with different security configuration (using different passphrases). The result should use two @@ -1615,6 +1668,7 @@ class DataPathTest(AwareBaseTest): self.run_multiple_ndi([self.PASSPHRASE, self.PASSPHRASE2]) @test_tracker_info(uuid="879df795-62d2-40d4-a862-bd46d8f7e67f") + @WifiBaseTest.wifi_test_wrap def test_multiple_ndi_pmks(self): """Verify that between 2 DUTs can create 2 NDPs with different security configuration (using different PMKS). The result should use two different @@ -1622,6 +1676,7 @@ class DataPathTest(AwareBaseTest): self.run_multiple_ndi([self.PMK, self.PMK2]) @test_tracker_info(uuid="397d380a-8e41-466e-9ccb-cf8f413d83ba") + @WifiBaseTest.wifi_test_wrap def test_multiple_ndi_open_passphrase_flip(self): """Verify that between 2 DUTs can create 2 NDPs with different security configuration (one open, one using passphrase). The result should use two @@ -1632,6 +1687,7 @@ class DataPathTest(AwareBaseTest): self.run_multiple_ndi([None, self.PASSPHRASE], flip_init_resp=True) @test_tracker_info(uuid="b3a4300b-1514-4cb8-a814-9c2baa449700") + @WifiBaseTest.wifi_test_wrap def test_multiple_ndi_open_pmk_flip(self): """Verify that between 2 DUTs can create 2 NDPs with different security configuration (one open, one using pmk). The result should use two @@ -1642,6 +1698,7 @@ class DataPathTest(AwareBaseTest): self.run_multiple_ndi([None, self.PMK], flip_init_resp=True) @test_tracker_info(uuid="0bfea9e4-e57d-417f-8db4-245741e9bbd5") + @WifiBaseTest.wifi_test_wrap def test_multiple_ndi_passphrase_pmk_flip(self): """Verify that between 2 DUTs can create 2 NDPs with different security configuration (one using passphrase, one using pmk). The result should use @@ -1652,6 +1709,7 @@ class DataPathTest(AwareBaseTest): self.run_multiple_ndi([self.PASSPHRASE, self.PMK], flip_init_resp=True) @test_tracker_info(uuid="74023483-5417-431b-a362-991ad4a03ab8") + @WifiBaseTest.wifi_test_wrap def test_multiple_ndi_passphrases_flip(self): """Verify that between 2 DUTs can create 2 NDPs with different security configuration (using different passphrases). The result should use two @@ -1663,6 +1721,7 @@ class DataPathTest(AwareBaseTest): [self.PASSPHRASE, self.PASSPHRASE2], flip_init_resp=True) @test_tracker_info(uuid="873b2d91-28a1-403f-ae9c-d756bb2f59ee") + @WifiBaseTest.wifi_test_wrap def test_multiple_ndi_pmks_flip(self): """Verify that between 2 DUTs can create 2 NDPs with different security configuration (using different PMKS). The result should use two different @@ -1675,6 +1734,7 @@ class DataPathTest(AwareBaseTest): ####################################### @test_tracker_info(uuid="2f10a9df-7fbd-490d-a238-3523f47ab54c") + @WifiBaseTest.wifi_test_wrap def test_ib_responder_any_usage(self): """Verify that configuring an in-band (Aware discovery) Responder to receive an NDP request from any peer is not permitted by current API level. Override @@ -1703,6 +1763,7 @@ class DataPathTest(AwareBaseTest): expect_failure=True) @test_tracker_info(uuid="5889cd41-0a72-4b7b-ab82-5b9168b9b5b8") + @WifiBaseTest.wifi_test_wrap def test_oob_responder_any_usage(self): """Verify that configuring an out-of-band (Aware discovery) Responder to receive an NDP request from any peer is not permitted by current API level. @@ -1766,6 +1827,7 @@ class DataPathTest(AwareBaseTest): init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key) @test_tracker_info(uuid="eff53739-35c5-47a6-81f0-d70b51d89c3b") + @WifiBaseTest.wifi_test_wrap def test_multiple_regulator_domains_ib_us_jp(self): """Verify data-path setup across multiple regulator domains. @@ -1778,6 +1840,7 @@ class DataPathTest(AwareBaseTest): resp_domain=wutils.WifiEnums.CountryCode.JAPAN) @test_tracker_info(uuid="19af47cc-3204-40ef-b50f-14cf7b89cf4a") + @WifiBaseTest.wifi_test_wrap def test_multiple_regulator_domains_ib_jp_us(self): """Verify data-path setup across multiple regulator domains. @@ -1790,6 +1853,7 @@ class DataPathTest(AwareBaseTest): resp_domain=wutils.WifiEnums.CountryCode.US) @test_tracker_info(uuid="65285ab3-977f-4dbd-b663-d5a02f4fc663") + @WifiBaseTest.wifi_test_wrap def test_multiple_regulator_domains_oob_us_jp(self): """Verify data-path setup across multiple regulator domains. @@ -1802,6 +1866,7 @@ class DataPathTest(AwareBaseTest): resp_domain=wutils.WifiEnums.CountryCode.JAPAN) @test_tracker_info(uuid="8a417e24-aaf6-44b9-a089-a07c3ba8d954") + @WifiBaseTest.wifi_test_wrap def test_multiple_regulator_domains_oob_jp_us(self): """Verify data-path setup across multiple regulator domains. @@ -2056,6 +2121,7 @@ class DataPathTest(AwareBaseTest): init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key) @test_tracker_info(uuid="d8a0839d-4ba0-43f2-af93-3cf1382f9f16") + @WifiBaseTest.wifi_test_wrap def test_identical_ndps_mix_ib_oob_ib_first_same_polarity(self): """Validate that a single NDP is created for multiple identical requests which are issued through either in-band (ib) or out-of-band (oob) APIs. @@ -2067,6 +2133,7 @@ class DataPathTest(AwareBaseTest): same_request=True, ib_first=True, inits_on_same_dut=True) @test_tracker_info(uuid="70bbb811-0bed-4a19-96b3-f2446e777c8a") + @WifiBaseTest.wifi_test_wrap def test_identical_ndps_mix_ib_oob_oob_first_same_polarity(self): """Validate that a single NDP is created for multiple identical requests which are issued through either in-band (ib) or out-of-band (oob) APIs. @@ -2078,6 +2145,7 @@ class DataPathTest(AwareBaseTest): same_request=True, ib_first=False, inits_on_same_dut=True) @test_tracker_info(uuid="d9796da5-f96a-4a51-be0f-89d6f5bfe3ad") + @WifiBaseTest.wifi_test_wrap def test_identical_ndps_mix_ib_oob_ib_first_diff_polarity(self): """Validate that a single NDP is created for multiple identical requests which are issued through either in-band (ib) or out-of-band (oob) APIs. @@ -2088,7 +2156,8 @@ class DataPathTest(AwareBaseTest): self.run_mix_ib_oob( same_request=True, ib_first=True, inits_on_same_dut=False) - @test_tracker_info(uuid="72b16cbf-53ad-4f98-8dcf-a8cc5fa812e3") + @test_tracker_info(uuid="48b9005b-7851-4222-b41c-1fcbefbc704d") + @WifiBaseTest.wifi_test_wrap def test_identical_ndps_mix_ib_oob_oob_first_diff_polarity(self): """Validate that a single NDP is created for multiple identical requests which are issued through either in-band (ib) or out-of-band (oob) APIs. @@ -2100,6 +2169,7 @@ class DataPathTest(AwareBaseTest): same_request=True, ib_first=False, inits_on_same_dut=False) @test_tracker_info(uuid="51f9581e-c5ee-48a7-84d2-adff4876c3d7") + @WifiBaseTest.wifi_test_wrap def test_multiple_ndis_mix_ib_oob_ib_first_same_polarity(self): """Validate that multiple NDIs are created for NDPs which are requested with different security configurations. Use a mix of in-band and out-of-band APIs @@ -2112,6 +2182,7 @@ class DataPathTest(AwareBaseTest): same_request=False, ib_first=True, inits_on_same_dut=True) @test_tracker_info(uuid="b1e3070e-4d38-4b31-862d-39b82e0f2853") + @WifiBaseTest.wifi_test_wrap def test_multiple_ndis_mix_ib_oob_oob_first_same_polarity(self): """Validate that multiple NDIs are created for NDPs which are requested with different security configurations. Use a mix of in-band and out-of-band APIs @@ -2124,6 +2195,7 @@ class DataPathTest(AwareBaseTest): same_request=False, ib_first=False, inits_on_same_dut=True) @test_tracker_info(uuid="b1e3070e-4d38-4b31-862d-39b82e0f2853") + @WifiBaseTest.wifi_test_wrap def test_multiple_ndis_mix_ib_oob_ib_first_diff_polarity(self): """Validate that multiple NDIs are created for NDPs which are requested with different security configurations. Use a mix of in-band and out-of-band APIs @@ -2136,6 +2208,7 @@ class DataPathTest(AwareBaseTest): same_request=False, ib_first=True, inits_on_same_dut=False) @test_tracker_info(uuid="596caadf-028e-494b-bbce-8304ccec2cbb") + @WifiBaseTest.wifi_test_wrap def test_multiple_ndis_mix_ib_oob_oob_first_diff_polarity(self): """Validate that multiple NDIs are created for NDPs which are requested with different security configurations. Use a mix of in-band and out-of-band APIs @@ -2149,6 +2222,7 @@ class DataPathTest(AwareBaseTest): ######################################################################## + @test_tracker_info(uuid="5ec10bf9-bfda-4093-8344-7ccc7764737e") def test_ndp_loop(self): """Validate that can create a loop (chain) of N NDPs between N devices, where N >= 3, e.g. @@ -2159,8 +2233,8 @@ class DataPathTest(AwareBaseTest): The NDPs are all OPEN (no encryption). """ - asserts.assert_true( - len(self.android_devices) >= 3, + asserts.skip_if( + len(self.android_devices) < 3, 'A minimum of 3 devices is needed to run the test, have %d' % len( self.android_devices)) diff --git a/acts/tests/google/wifi/aware/functional/DiscoveryTest.py b/acts_tests/tests/google/wifi/aware/functional/DiscoveryTest.py index 5f01c80eb7..31255afa04 100644 --- a/acts/tests/google/wifi/aware/functional/DiscoveryTest.py +++ b/acts_tests/tests/google/wifi/aware/functional/DiscoveryTest.py @@ -22,6 +22,7 @@ from acts.test_decorators import test_tracker_info from acts.test_utils.wifi.aware import aware_const as aconsts from acts.test_utils.wifi.aware import aware_test_utils as autils from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest +from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest class DiscoveryTest(AwareBaseTest): @@ -561,6 +562,7 @@ class DiscoveryTest(AwareBaseTest): ####################################### @test_tracker_info(uuid="954ebbde-ed2b-4f04-9e68-88239187d69d") + @WifiBaseTest.wifi_test_wrap def test_positive_unsolicited_passive_typical(self): """Functional test case / Discovery test cases / positive test case: - Solicited publish + passive subscribe @@ -1068,3 +1070,194 @@ class DiscoveryTest(AwareBaseTest): s_config=autils.create_discovery_config( sub_service_name, aconsts.SUBSCRIBE_TYPE_PASSIVE), device_startup_offset=self.device_startup_offset) + + ########################################################## + + def exchange_messages(self, p_dut, p_disc_id, s_dut, s_disc_id, peer_id_on_sub, session_name): + """ + Exchange message between Publisher and Subscriber on target discovery session + + Args: + p_dut: Publisher device + p_disc_id: Publish discovery session id + s_dut: Subscriber device + s_disc_id: Subscribe discovery session id + peer_id_on_sub: Peer ID of the Publisher as seen on the Subscriber + session_name: dictionary of discovery session name base on role("pub" or "sub") + {role: {disc_id: name}} + """ + msg_template = "Hello {} from {} !" + + # Message send from Subscriber to Publisher + s_to_p_msg = msg_template.format(session_name["pub"][p_disc_id], + session_name["sub"][s_disc_id]) + s_dut.droid.wifiAwareSendMessage(s_disc_id, + peer_id_on_sub, + self.get_next_msg_id(), + s_to_p_msg, + self.msg_retx_count) + autils.wait_for_event(s_dut, + autils.decorate_event(aconsts.SESSION_CB_ON_MESSAGE_SENT, s_disc_id)) + event = autils.wait_for_event(p_dut, + autils.decorate_event(aconsts.SESSION_CB_ON_MESSAGE_RECEIVED, + p_disc_id)) + asserts.assert_equal( + event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING], s_to_p_msg, + "Message on service %s from Subscriber to Publisher " + "not received correctly" % session_name["pub"][p_disc_id]) + peer_id_on_pub = event["data"][aconsts.SESSION_CB_KEY_PEER_ID] + + # Message send from Publisher to Subscriber + p_to_s_msg = msg_template.format(session_name["sub"][s_disc_id], + session_name["pub"][p_disc_id]) + p_dut.droid.wifiAwareSendMessage(p_disc_id, + peer_id_on_pub, + self.get_next_msg_id(), p_to_s_msg, + self.msg_retx_count) + autils.wait_for_event( + p_dut, autils.decorate_event(aconsts.SESSION_CB_ON_MESSAGE_SENT, p_disc_id)) + event = autils.wait_for_event(s_dut, + autils.decorate_event(aconsts.SESSION_CB_ON_MESSAGE_RECEIVED, + s_disc_id)) + asserts.assert_equal( + event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING], p_to_s_msg, + "Message on service %s from Publisher to Subscriber" + "not received correctly" % session_name["sub"][s_disc_id]) + + def run_multiple_concurrent_services_same_name_diff_ssi(self, type_x, type_y): + """Validate same service name with multiple service specific info on publisher + and subscriber can see all service + + - p_dut running Publish X and Y + - s_dut running subscribe A and B + - subscribe A find X and Y + - subscribe B find X and Y + + Message exchanges: + - A to X and X to A + - B to X and X to B + - A to Y and Y to A + - B to Y and Y to B + + Note: test requires that publisher device support 2 publish sessions concurrently, + and subscriber device support 2 subscribe sessions concurrently. + The test will be skipped if the devices are not capable. + + Args: + type_x, type_y: A list of [ptype, stype] of the publish and subscribe + types for services X and Y respectively. + """ + p_dut = self.android_devices[0] + s_dut = self.android_devices[1] + + asserts.skip_if( + p_dut.aware_capabilities[aconsts.CAP_MAX_PUBLISHES] < 2 + or s_dut.aware_capabilities[aconsts.CAP_MAX_SUBSCRIBES] < 2, + "Devices do not support 2 publish sessions or 2 subscribe sessions") + + SERVICE_NAME = "ServiceName" + X_SERVICE_SSI = "ServiceSpecificInfoXXX" + Y_SERVICE_SSI = "ServiceSpecificInfoYYY" + use_id = True + + # attach and wait for confirmation + p_id = p_dut.droid.wifiAwareAttach(False, None, use_id) + autils.wait_for_event(p_dut, autils.decorate_event(aconsts.EVENT_CB_ON_ATTACHED, p_id)) + time.sleep(self.device_startup_offset) + s_id = s_dut.droid.wifiAwareAttach(False, None, use_id) + autils.wait_for_event(s_dut, autils.decorate_event(aconsts.EVENT_CB_ON_ATTACHED, s_id)) + + # Publisher: start publishing both X & Y services and wait for confirmations + p_disc_id_x = p_dut.droid.wifiAwarePublish( + p_id, autils.create_discovery_config(SERVICE_NAME, type_x[0], X_SERVICE_SSI), use_id) + event = autils.wait_for_event(p_dut, + autils.decorate_event( + aconsts.SESSION_CB_ON_PUBLISH_STARTED, p_disc_id_x)) + + p_disc_id_y = p_dut.droid.wifiAwarePublish( + p_id, autils.create_discovery_config(SERVICE_NAME, type_x[0], Y_SERVICE_SSI), use_id) + event = autils.wait_for_event(p_dut, + autils.decorate_event( + aconsts.SESSION_CB_ON_PUBLISH_STARTED, p_disc_id_y)) + + # Subscriber: start subscribe session A + s_disc_id_a = s_dut.droid.wifiAwareSubscribe( + s_id, autils.create_discovery_config(SERVICE_NAME, type_x[1]), use_id) + autils.wait_for_event(s_dut, autils.decorate_event( + aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, s_disc_id_a)) + + # Subscriber: start subscribe session B + s_disc_id_b = s_dut.droid.wifiAwareSubscribe( + p_id, autils.create_discovery_config(SERVICE_NAME, type_y[1]), use_id) + autils.wait_for_event(s_dut, autils.decorate_event( + aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, s_disc_id_b)) + + session_name = {"pub": {p_disc_id_x: "X", p_disc_id_y: "Y"}, + "sub": {s_disc_id_a: "A", s_disc_id_b: "B"}} + + # Subscriber: subscribe session A & B wait for service discovery + # Number of results on each session should be exactly 2 + results_a = {} + for i in range(2): + event = autils.wait_for_event(s_dut, autils.decorate_event( + aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, s_disc_id_a)) + results_a[ + bytes(event["data"][ + aconsts.SESSION_CB_KEY_SERVICE_SPECIFIC_INFO]).decode('utf-8')] = event + autils.fail_on_event(s_dut, autils.decorate_event( + aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, s_disc_id_a)) + + results_b = {} + for i in range(2): + event = autils.wait_for_event(s_dut, autils.decorate_event( + aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, s_disc_id_b)) + results_b[ + bytes(event["data"][ + aconsts.SESSION_CB_KEY_SERVICE_SPECIFIC_INFO]).decode('utf-8')] = event + autils.fail_on_event(s_dut, autils.decorate_event( + aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, s_disc_id_b)) + + s_a_peer_id_for_p_x = results_a[X_SERVICE_SSI]["data"][aconsts.SESSION_CB_KEY_PEER_ID] + s_a_peer_id_for_p_y = results_a[Y_SERVICE_SSI]["data"][aconsts.SESSION_CB_KEY_PEER_ID] + s_b_peer_id_for_p_x = results_b[X_SERVICE_SSI]["data"][aconsts.SESSION_CB_KEY_PEER_ID] + s_b_peer_id_for_p_y = results_b[Y_SERVICE_SSI]["data"][aconsts.SESSION_CB_KEY_PEER_ID] + + # Message exchange between Publisher and Subscribe + self.exchange_messages(p_dut, p_disc_id_x, + s_dut, s_disc_id_a, s_a_peer_id_for_p_x, session_name) + + self.exchange_messages(p_dut, p_disc_id_x, + s_dut, s_disc_id_b, s_b_peer_id_for_p_x, session_name) + + self.exchange_messages(p_dut, p_disc_id_y, + s_dut, s_disc_id_a, s_a_peer_id_for_p_y, session_name) + + self.exchange_messages(p_dut, p_disc_id_y, + s_dut, s_disc_id_b, s_b_peer_id_for_p_y, session_name) + + # Check no more messages + time.sleep(autils.EVENT_TIMEOUT) + autils.verify_no_more_events(p_dut, timeout=0) + autils.verify_no_more_events(s_dut, timeout=0) + + ########################################################## + + @test_tracker_info(uuid="78d89d63-1cbc-47f6-a8fc-74057fea655e") + def test_multiple_concurrent_services_diff_ssi_unsolicited_passive(self): + """Multi service test on same service name but different Service Specific Info + - Unsolicited publish + - Passive subscribe + """ + self.run_multiple_concurrent_services_same_name_diff_ssi( + type_x=[aconsts.PUBLISH_TYPE_UNSOLICITED, aconsts.SUBSCRIBE_TYPE_PASSIVE], + type_y=[aconsts.PUBLISH_TYPE_UNSOLICITED, aconsts.SUBSCRIBE_TYPE_PASSIVE]) + + @test_tracker_info(uuid="5d349491-48e4-4ca1-a8af-7afb44e7bcbc") + def test_multiple_concurrent_services_diff_ssi_solicited_active(self): + """Multi service test on same service name but different Service Specific Info + - Solicited publish + - Active subscribe + """ + self.run_multiple_concurrent_services_same_name_diff_ssi( + type_x=[aconsts.PUBLISH_TYPE_SOLICITED, aconsts.SUBSCRIBE_TYPE_ACTIVE], + type_y=[aconsts.PUBLISH_TYPE_SOLICITED, aconsts.SUBSCRIBE_TYPE_ACTIVE]) diff --git a/acts/tests/google/wifi/aware/functional/MacRandomNoLeakageTest.py b/acts_tests/tests/google/wifi/aware/functional/MacRandomNoLeakageTest.py index 074ca48826..3898832026 100644 --- a/acts/tests/google/wifi/aware/functional/MacRandomNoLeakageTest.py +++ b/acts_tests/tests/google/wifi/aware/functional/MacRandomNoLeakageTest.py @@ -70,12 +70,13 @@ class MacRandomNoLeakageTest(AwareBaseTest, WifiBaseTest): pcaps = pcap_5g + pcap_2g # Verify factory MAC is not leaked in both 2G and 5G pcaps - for mac in factory_mac_addresses: - wutils.verify_mac_not_found_in_pcap(mac, pcaps) + ads = [self.android_devices[0], self.android_devices[1]] + for i, mac in enumerate(factory_mac_addresses): + wutils.verify_mac_not_found_in_pcap(ads[i], mac, pcaps) # Verify random MACs are being used and in pcaps - for mac in mac_addresses: - wutils.verify_mac_is_found_in_pcap(mac, pcaps) + for i, mac in enumerate(mac_addresses): + wutils.verify_mac_is_found_in_pcap(ads[i], mac, pcaps) def transfer_mac_format(self, mac): """add ':' to mac String, and transfer to lower case diff --git a/acts/tests/google/wifi/aware/functional/MacRandomTest.py b/acts_tests/tests/google/wifi/aware/functional/MacRandomTest.py index 6c8d5b1c8a..6c8d5b1c8a 100644 --- a/acts/tests/google/wifi/aware/functional/MacRandomTest.py +++ b/acts_tests/tests/google/wifi/aware/functional/MacRandomTest.py diff --git a/acts/tests/google/wifi/aware/functional/MatchFilterTest.py b/acts_tests/tests/google/wifi/aware/functional/MatchFilterTest.py index e008e12ae0..e008e12ae0 100644 --- a/acts/tests/google/wifi/aware/functional/MatchFilterTest.py +++ b/acts_tests/tests/google/wifi/aware/functional/MatchFilterTest.py diff --git a/acts/tests/google/wifi/aware/functional/MessageTest.py b/acts_tests/tests/google/wifi/aware/functional/MessageTest.py index 9bfb932b31..040f4e4615 100644 --- a/acts/tests/google/wifi/aware/functional/MessageTest.py +++ b/acts_tests/tests/google/wifi/aware/functional/MessageTest.py @@ -19,6 +19,7 @@ import time from acts import asserts from acts.test_decorators import test_tracker_info +from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest from acts.test_utils.wifi.aware import aware_const as aconsts from acts.test_utils.wifi.aware import aware_test_utils as autils from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest @@ -407,6 +408,7 @@ class MessageTest(AwareBaseTest): ############################################################################ @test_tracker_info(uuid="a8cd0512-b279-425f-93cf-949ddba22c7a") + @WifiBaseTest.wifi_test_wrap def test_message_no_queue_min(self): """Functional / Message / No queue - Minimal payload size (None or "") @@ -421,6 +423,7 @@ class MessageTest(AwareBaseTest): self.run_message_no_queue(self.PAYLOAD_SIZE_TYPICAL) @test_tracker_info(uuid="c984860c-b62d-4d9b-8bce-4d894ea3bfbe") + @WifiBaseTest.wifi_test_wrap def test_message_no_queue_max(self): """Functional / Message / No queue - Max payload size (based on device capabilities) diff --git a/acts/tests/google/wifi/aware/functional/NonConcurrencyTest.py b/acts_tests/tests/google/wifi/aware/functional/NonConcurrencyTest.py index f0286f25a3..51f4caedd4 100644 --- a/acts/tests/google/wifi/aware/functional/NonConcurrencyTest.py +++ b/acts_tests/tests/google/wifi/aware/functional/NonConcurrencyTest.py @@ -14,9 +14,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -from acts import asserts import queue +import time + +from acts import asserts from acts import utils +from acts.test_decorators import test_tracker_info from acts.test_utils.wifi import wifi_test_utils as wutils from acts.test_utils.wifi import wifi_constants as wconsts from acts.test_utils.wifi.aware import aware_const as aconsts @@ -40,10 +43,13 @@ class NonConcurrencyTest(AwareBaseTest): AwareBaseTest.teardown_test(self) for ad in self.android_devices: ad.droid.wifiP2pClose() + ad.droid.connectivityStopTethering(0) def run_aware_then_incompat_service(self, is_p2p): """Run test to validate that a running Aware session terminates when an Aware-incompatible service is started. + P2P: has same priority, will bring down Aware, then Aware will become available again. + SoftAp: has higher priority, will bring down Aware, Aware will keep unavailable. Args: is_p2p: True for p2p, False for SoftAP @@ -54,6 +60,8 @@ class NonConcurrencyTest(AwareBaseTest): id = dut.droid.wifiAwareAttach() autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED) + time.sleep(EVENT_TIMEOUT) + # start other service if is_p2p: dut.droid.wifiP2pInitialize() @@ -63,40 +71,68 @@ class NonConcurrencyTest(AwareBaseTest): # expect an announcement about Aware non-availability autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_NOT_AVAILABLE) - # local clean-up - if not is_p2p: + if is_p2p: + # P2P has same priority, aware will be available + autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_AVAILABLE) + else: + # SoftAp has higher priority, aware will keep unavailable + autils.fail_on_event(dut, aconsts.BROADCAST_WIFI_AWARE_AVAILABLE) + # local clean-up wutils.stop_wifi_tethering(dut) def run_incompat_service_then_aware(self, is_p2p): - """Validate that if an Aware-incompatible service is already up then any - Aware operation fails""" + """Validate that if an Aware-incompatible service is already up then try to start Aware. + P2P: has same priority, Aware can bring it down. + SoftAp: has higher priority, Aware will be unavailable, any Aware operation will fail. + + Args: + is_p2p: True for p2p, False for SoftAP + """ dut = self.android_devices[0] # start other service if is_p2p: dut.droid.wifiP2pInitialize() + # expect no announcement about Aware non-availability + autils.fail_on_event(dut, aconsts.BROADCAST_WIFI_AWARE_NOT_AVAILABLE) else: wutils.start_wifi_tethering(dut, self.TETHER_SSID, password=None) + # expect an announcement about Aware non-availability + autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_NOT_AVAILABLE) - # expect an announcement about Aware non-availability - autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_NOT_AVAILABLE) + # Change Wifi state and location mode to check if aware became available. + wutils.wifi_toggle_state(dut, False) + utils.set_location_service(dut, False) + wutils.wifi_toggle_state(dut, True) + utils.set_location_service(dut, True) - # try starting anyway (expect failure) - dut.droid.wifiAwareAttach() - autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACH_FAILED) + if is_p2p: + # P2P has same priority, aware will be available + autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_AVAILABLE) + asserts.assert_true(dut.droid.wifiIsAwareAvailable(), "Aware should be available") + else: + # SoftAp has higher priority, aware will keep unavailable + autils.fail_on_event(dut, aconsts.BROADCAST_WIFI_AWARE_AVAILABLE) + asserts.assert_false(dut.droid.wifiIsAwareAvailable(), + "Aware is available (it shouldn't be)") - # stop other service + dut.droid.wifiAwareAttach() if is_p2p: - dut.droid.wifiP2pClose() + # P2P has same priority, Aware attach should success. + autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED) else: + # SoftAp has higher priority, Aware attach should fail. + autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACH_FAILED) + + if not is_p2p: wutils.stop_wifi_tethering(dut) - # expect an announcement about Aware availability - autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_AVAILABLE) + # expect an announcement about Aware availability + autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_AVAILABLE) - # try starting Aware - dut.droid.wifiAwareAttach() - autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED) + # try starting Aware + dut.droid.wifiAwareAttach() + autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED) def run_aware_then_connect_to_new_ap(self): """Validate interaction of Wi-Fi Aware and infra (STA) association with randomized MAC @@ -133,6 +169,7 @@ class NonConcurrencyTest(AwareBaseTest): p_id = dut.droid.wifiAwareAttach() autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED) + wutils.start_wifi_connection_scan_and_ensure_network_found(dut, ap_ssid) wutils.wifi_connect(dut, config, check_connectivity=False) autils.wait_for_event(dut, wconsts.WIFI_STATE_CHANGED) @@ -162,23 +199,31 @@ class NonConcurrencyTest(AwareBaseTest): ########################################################################## + @test_tracker_info(uuid="b7c84cbe-d744-440a-9279-a0133e88e8cb") def test_run_p2p_then_aware(self): """Validate that if p2p is already up then any Aware operation fails""" self.run_incompat_service_then_aware(is_p2p=True) + @test_tracker_info(uuid="1e7b3a6d-575d-4911-80bb-6fcf1157ee9f") def test_run_aware_then_p2p(self): """Validate that a running Aware session terminates when p2p is started""" self.run_aware_then_incompat_service(is_p2p=True) + @test_tracker_info(uuid="82a0bd98-3022-4831-ac9e-d81f58c742d2") def test_run_softap_then_aware(self): """Validate that if SoftAp is already up then any Aware operation fails""" + asserts.skip_if( + self.android_devices[0].model not in self.dbs_supported_models, + "Device %s doesn't support STA+AP." % self.android_devices[0].model) self.run_incompat_service_then_aware(is_p2p=False) + @test_tracker_info(uuid="0da7661e-8ac2-4f68-b6d3-b3f612369d03") def test_run_aware_then_softap(self): """Validate that a running Aware session terminates when softAp is started""" self.run_aware_then_incompat_service(is_p2p=False) + @test_tracker_info(uuid="2ac27ac6-8010-4d05-b892-00242420b075") def test_run_aware_then_connect_new_ap(self): """Validate connect new ap during Aware session""" self.run_aware_then_connect_to_new_ap() diff --git a/acts/tests/google/wifi/aware/functional/ProtocolsTest.py b/acts_tests/tests/google/wifi/aware/functional/ProtocolsTest.py index 15e84ff1f6..15e84ff1f6 100644 --- a/acts/tests/google/wifi/aware/functional/ProtocolsTest.py +++ b/acts_tests/tests/google/wifi/aware/functional/ProtocolsTest.py diff --git a/acts/tests/google/wifi/aware/functional/functional b/acts_tests/tests/google/wifi/aware/functional/functional index e13a4705e6..e13a4705e6 100644 --- a/acts/tests/google/wifi/aware/functional/functional +++ b/acts_tests/tests/google/wifi/aware/functional/functional diff --git a/acts/tests/google/wifi/aware/ota/ServiceIdsTest.py b/acts_tests/tests/google/wifi/aware/ota/ServiceIdsTest.py index e29cd713f4..e29cd713f4 100644 --- a/acts/tests/google/wifi/aware/ota/ServiceIdsTest.py +++ b/acts_tests/tests/google/wifi/aware/ota/ServiceIdsTest.py diff --git a/acts/tests/google/wifi/aware/ota/ota b/acts_tests/tests/google/wifi/aware/ota/ota index 27f6724681..27f6724681 100644 --- a/acts/tests/google/wifi/aware/ota/ota +++ b/acts_tests/tests/google/wifi/aware/ota/ota diff --git a/acts/tests/google/wifi/aware/performance/LatencyTest.py b/acts_tests/tests/google/wifi/aware/performance/LatencyTest.py index 8ebff89ab6..8ebff89ab6 100644 --- a/acts/tests/google/wifi/aware/performance/LatencyTest.py +++ b/acts_tests/tests/google/wifi/aware/performance/LatencyTest.py diff --git a/acts/tests/google/wifi/aware/performance/ThroughputTest.py b/acts_tests/tests/google/wifi/aware/performance/ThroughputTest.py index a90734d7d7..2dab2760a9 100644 --- a/acts/tests/google/wifi/aware/performance/ThroughputTest.py +++ b/acts_tests/tests/google/wifi/aware/performance/ThroughputTest.py @@ -406,3 +406,35 @@ class ThroughputTest(AwareBaseTest): [self.PASSPHRASE, self.PASSPHRASE2], results=results) asserts.explicit_pass( "test_iperf_max_ndi_aware_only_passphrases passes", extras=results) + + def run_test_traffic_latency_single_ndp_ib_aware_only_open(self): + """Measure IPv6 traffic latency performance(ping) on NDP between 2 devices. + Security config is open. + """ + p_dut = self.android_devices[0] + p_dut.pretty_name = "publisher" + s_dut = self.android_devices[1] + s_dut.pretty_name = "subscriber" + ndp_info = autils.create_ib_ndp(p_dut, + s_dut, + autils.create_discovery_config( + self.SERVICE_NAME, aconsts.PUBLISH_TYPE_UNSOLICITED), + autils.create_discovery_config( + self.SERVICE_NAME, aconsts.SUBSCRIBE_TYPE_PASSIVE), + self.device_startup_offset) + p_req_key = ndp_info[0] + s_req_key = ndp_info[1] + p_aware_if = ndp_info[2] + s_aware_if = ndp_info[3] + p_ipv6 = ndp_info[4] + s_ipv6 = ndp_info[5] + self.log.info("Interface names: P=%s, S=%s", p_aware_if, s_aware_if) + self.log.info("Interface addresses (IPv6): P=%s, S=%s", p_ipv6, s_ipv6) + self.log.info("Start ping %s from %s", s_ipv6, p_ipv6) + latency_result = autils.run_ping6(p_dut, s_ipv6) + self.log.info("The latency results are %s", latency_result) + + def test_traffic_latency_single_ndp_ib_aware_only_open(self): + """Test IPv6 traffic latency performance on NDP with security config is open. + """ + self.run_test_traffic_latency_single_ndp_ib_aware_only_open() diff --git a/acts/tests/google/wifi/aware/performance/performance b/acts_tests/tests/google/wifi/aware/performance/performance index 17e39639ed..17e39639ed 100644 --- a/acts/tests/google/wifi/aware/performance/performance +++ b/acts_tests/tests/google/wifi/aware/performance/performance diff --git a/acts/tests/google/wifi/aware/stress/DataPathStressTest.py b/acts_tests/tests/google/wifi/aware/stress/DataPathStressTest.py index d2e95df561..d2e95df561 100644 --- a/acts/tests/google/wifi/aware/stress/DataPathStressTest.py +++ b/acts_tests/tests/google/wifi/aware/stress/DataPathStressTest.py diff --git a/acts/tests/google/wifi/aware/stress/DiscoveryStressTest.py b/acts_tests/tests/google/wifi/aware/stress/DiscoveryStressTest.py index 55545ea654..55545ea654 100644 --- a/acts/tests/google/wifi/aware/stress/DiscoveryStressTest.py +++ b/acts_tests/tests/google/wifi/aware/stress/DiscoveryStressTest.py diff --git a/acts/tests/google/wifi/aware/stress/InfraAssociationStressTest.py b/acts_tests/tests/google/wifi/aware/stress/InfraAssociationStressTest.py index 58e2c84e07..58e2c84e07 100644 --- a/acts/tests/google/wifi/aware/stress/InfraAssociationStressTest.py +++ b/acts_tests/tests/google/wifi/aware/stress/InfraAssociationStressTest.py diff --git a/acts/tests/google/wifi/aware/stress/MessagesStressTest.py b/acts_tests/tests/google/wifi/aware/stress/MessagesStressTest.py index fbe95d4df5..153a81fde9 100644 --- a/acts/tests/google/wifi/aware/stress/MessagesStressTest.py +++ b/acts_tests/tests/google/wifi/aware/stress/MessagesStressTest.py @@ -30,10 +30,18 @@ KEY_RX_COUNT = "rx_count" class MessagesStressTest(AwareBaseTest): """Set of stress tests for Wi-Fi Aware L2 (layer 2) message exchanges.""" + # Number of the message queue depth per Uid from framework + MESSAGE_QUEUE_DEPTH_PER_UID = 50 # Number of iterations in the stress test (number of messages) + # Should be larger than MESSAGE_QUEUE_DEPTH_PER_UID NUM_ITERATIONS = 100 + # Number of message to send per round to avoid exceed message queue depth limit + # Should be less than or equal to 1/2 of MESSAGE_QUEUE_DEPTH_PER_UID + NUM_PER_ROUND = 20 + NUM_ROUNDS = 5 + # Maximum permitted percentage of messages which fail to be transmitted # correctly MAX_TX_FAILURE_PERCENTAGE = 2 @@ -172,39 +180,166 @@ class MessagesStressTest(AwareBaseTest): if data[KEY_TX_OK_COUNT] > 0: results["tx_count_success"] = results["tx_count_success"] + 1 if data[KEY_TX_OK_COUNT] > 1: - results["tx_count_duplicate_success"] = ( - results["tx_count_duplicate_success"] + 1) + results["tx_count_duplicate_success"] += 1 if data[KEY_TX_FAIL_COUNT] > 0: - results["tx_count_fail"] = results["tx_count_fail"] + 1 + results["tx_count_fail"] += 1 if data[KEY_TX_FAIL_COUNT] > 1: - results[ - "tx_count_duplicate_fail"] = results["tx_count_duplicate_fail"] + 1 + results["tx_count_duplicate_fail"] += 1 if (data[KEY_TX_OK_COUNT] == 0 and data[KEY_TX_FAIL_COUNT] == 0 and data[KEY_ID] != -1): - results["tx_count_neither"] = results["tx_count_neither"] + 1 + results["tx_count_neither"] += 1 if data[KEY_TX_OK_COUNT] > 0 and data[KEY_RX_COUNT] == 0: - results["tx_count_tx_ok_but_no_rx"] = ( - results["tx_count_tx_ok_but_no_rx"] + 1) + results["tx_count_tx_ok_but_no_rx"] += 1 if data[KEY_RX_COUNT] > 0: - results["rx_count"] = results["rx_count"] + 1 + results["rx_count"] += 1 if data[KEY_RX_COUNT] > 1: - results[ - "rx_count_duplicate"] = results["rx_count_duplicate"] + 1 + results["rx_count_duplicate"] += 1 if data[KEY_RX_COUNT] > 0 and data[KEY_TX_OK_COUNT] == 0: - results["rx_count_no_ok_tx_indication"] = ( - results["rx_count_no_ok_tx_indication"] + 1) + results["rx_count_no_ok_tx_indication"] += 1 if data[KEY_RX_COUNT] > 0 and data[KEY_TX_FAIL_COUNT] > 0: - results["rx_count_fail_tx_indication"] = ( - results["rx_count_fail_tx_indication"] + 1) + results["rx_count_fail_tx_indication"] += 1 if data[KEY_RX_COUNT] > 0 and data[KEY_ID] == -1: - results[ - "rx_count_no_tx_message"] = results["rx_count_no_tx_message"] + 1 + results["rx_count_no_tx_message"] += 1 ####################################################################### @test_tracker_info(uuid="e88c060f-4ca7-41c1-935a-d3d62878ec0b") - def test_stress_message(self): - """Stress test for bi-directional message transmission and reception.""" + def test_stress_message_no_throttling(self): + """Stress test for bi-directional message transmission and reception no throttling""" + p_dut = self.android_devices[0] + s_dut = self.android_devices[1] + + # Start up a discovery session + discovery_data = autils.create_discovery_pair( + p_dut, + s_dut, + p_config=autils.create_discovery_config( + self.SERVICE_NAME, aconsts.PUBLISH_TYPE_UNSOLICITED), + s_config=autils.create_discovery_config( + self.SERVICE_NAME, aconsts.SUBSCRIBE_TYPE_PASSIVE), + device_startup_offset=self.device_startup_offset, + msg_id=self.get_next_msg_id()) + p_id = discovery_data[0] + s_id = discovery_data[1] + p_disc_id = discovery_data[2] + s_disc_id = discovery_data[3] + peer_id_on_sub = discovery_data[4] + peer_id_on_pub = discovery_data[5] + + # Store information on Tx & Rx messages + messages_by_msg = {} # keyed by message text + # {text -> {id, tx_ok_count, tx_fail_count, rx_count}} + messages_by_id = {} # keyed by message ID {id -> text} + iterations = 0 + p_tx_ok_count_total = 0 + p_tx_fail_count_total = 0 + p_tx_unknown_id_total = 0 + s_tx_ok_count_total = 0 + s_tx_fail_count_total = 0 + s_tx_unknown_id_total = 0 + + # First round will fill up the message queue + num_of_messages_this_round = self.MESSAGE_QUEUE_DEPTH_PER_UID + + # send messages (one in each direction) in rounds to avoid exceed the queue limit + for j in range(self.NUM_ROUNDS): + for k in range(num_of_messages_this_round): + msg_p2s = "Message Publisher -> Subscriber #%d" % iterations + next_msg_id = self.get_next_msg_id() + self.init_info(msg_p2s, next_msg_id, messages_by_msg, + messages_by_id) + p_dut.droid.wifiAwareSendMessage(p_disc_id, peer_id_on_pub, + next_msg_id, msg_p2s, 0) + + msg_s2p = "Message Subscriber -> Publisher #%d" % iterations + next_msg_id = self.get_next_msg_id() + self.init_info(msg_s2p, next_msg_id, messages_by_msg, + messages_by_id) + s_dut.droid.wifiAwareSendMessage(s_disc_id, peer_id_on_sub, + next_msg_id, msg_s2p, 0) + iterations += 1 + + # wait for message tx confirmation + (p_tx_ok_count, p_tx_fail_count, p_tx_unknown_id) = self.wait_for_tx_events( + p_dut, self.NUM_PER_ROUND, messages_by_msg, messages_by_id) + p_tx_ok_count_total += p_tx_ok_count + p_tx_fail_count_total += p_tx_fail_count + p_tx_unknown_id_total += p_tx_unknown_id + (s_tx_ok_count, s_tx_fail_count, s_tx_unknown_id) = self.wait_for_tx_events( + s_dut, self.NUM_PER_ROUND, messages_by_msg, messages_by_id) + s_tx_ok_count_total += s_tx_ok_count + s_tx_fail_count_total += s_tx_fail_count + s_tx_unknown_id_total += s_tx_unknown_id + + num_of_messages_this_round = self.NUM_PER_ROUND + + # wait for the rest message tx confirmation + p_tx_total = p_tx_ok_count_total + p_tx_fail_count_total + p_tx_unknown_id_total + s_tx_total = s_tx_ok_count_total + s_tx_fail_count_total + s_tx_unknown_id_total + (p_tx_ok_count, p_tx_fail_count, p_tx_unknown_id) = self.wait_for_tx_events( + p_dut, iterations - p_tx_total, messages_by_msg, messages_by_id) + (s_tx_ok_count, s_tx_fail_count, s_tx_unknown_id) = self.wait_for_tx_events( + s_dut, iterations - s_tx_total, messages_by_msg, messages_by_id) + p_tx_ok_count_total += p_tx_ok_count + p_tx_fail_count_total += p_tx_fail_count + p_tx_unknown_id_total += p_tx_unknown_id + s_tx_ok_count_total += s_tx_ok_count + s_tx_fail_count_total += s_tx_fail_count + s_tx_unknown_id_total += s_tx_unknown_id + self.log.info( + "Transmission done: pub=%d, sub=%d transmitted successfully", + p_tx_ok_count_total, s_tx_ok_count_total) + + # wait for message rx confirmation (giving it the total number of messages + # transmitted rather than just those transmitted correctly since sometimes + # the Tx doesn't get that information correctly. I.e. a message the Tx + # thought was not transmitted correctly is actually received - missing ACK? + # bug?) + self.wait_for_rx_events(p_dut, iterations, messages_by_msg) + self.wait_for_rx_events(s_dut, iterations, messages_by_msg) + + # analyze results + results = {} + results["tx_count"] = 2 * iterations + results["tx_unknown_ids"] = p_tx_unknown_id_total + s_tx_unknown_id_total + self.analyze_results(results, messages_by_msg) + + # clear errors + asserts.assert_equal(results["tx_unknown_ids"], 0, + "Message ID corruption", results) + asserts.assert_equal(results["tx_count_neither"], 0, + "Tx message with no success or fail indication", + results) + asserts.assert_equal(results["tx_count_duplicate_fail"], 0, + "Duplicate Tx fail messages", results) + asserts.assert_equal(results["tx_count_duplicate_success"], 0, + "Duplicate Tx success messages", results) + asserts.assert_equal(results["rx_count_no_tx_message"], 0, + "Rx message which wasn't sent - message corruption?", results) + asserts.assert_equal(results["tx_count_tx_ok_but_no_rx"], 0, + "Tx got ACK but Rx didn't get message", results) + + # possibly ok - but flag since most frequently a bug + asserts.assert_equal(results["rx_count_no_ok_tx_indication"], 0, + "Message received but Tx didn't get ACK", results) + asserts.assert_equal(results["rx_count_fail_tx_indication"], 0, + "Message received but Tx didn't get ACK", results) + + # permissible failures based on thresholds + asserts.assert_true( + results["tx_count_fail"] <= + (self.MAX_TX_FAILURE_PERCENTAGE * iterations * 2 / 100), + "Number of Tx failures exceeds threshold", extras=results) + asserts.assert_true( + results["rx_count_duplicate"] <= + (self.MAX_DUPLICATE_RX_PERCENTAGE * iterations * 2 / 100), + "Number of duplicate Rx exceeds threshold", extras=results) + + asserts.explicit_pass("test_stress_message_no_throttling done", extras=results) + + @test_tracker_info(uuid="546b0c6f-3071-4330-8e23-842ecbd07018") + def test_stress_message_throttling(self): + """Stress test for bi-directional message transmission and reception with throttling""" p_dut = self.android_devices[0] s_dut = self.android_devices[1] @@ -295,14 +430,16 @@ class MessagesStressTest(AwareBaseTest): # permissible failures based on thresholds asserts.assert_true( - results["tx_count_fail"] <= - (self.MAX_TX_FAILURE_PERCENTAGE * self.NUM_ITERATIONS / 100), - "Number of Tx failures exceeds threshold", - extras=results) - asserts.assert_true( results["rx_count_duplicate"] <= - (self.MAX_DUPLICATE_RX_PERCENTAGE * self.NUM_ITERATIONS / 100), - "Number of duplicate Rx exceeds threshold", - extras=results) + (self.MAX_DUPLICATE_RX_PERCENTAGE * results["tx_count_success"] / 100), + "Number of duplicate Rx exceeds threshold", extras=results) + + # check working status message queue limit per UID + asserts.assert_true( + results["tx_count_success"] >= self.MESSAGE_QUEUE_DEPTH_PER_UID * 2, + "Number of messages did not reach uid message queue limit", extras=results) + asserts.assert_true( + results["tx_count_success"] < self.NUM_ITERATIONS * 2, + "Seems uid message queue limit is not working, Tx all message", extras=results) - asserts.explicit_pass("test_stress_message done", extras=results) + asserts.explicit_pass("test_stress_message_throttling done", extras=results) diff --git a/acts/tests/google/wifi/aware/stress/stress b/acts_tests/tests/google/wifi/aware/stress/stress index f79b15885c..f79b15885c 100644 --- a/acts/tests/google/wifi/aware/stress/stress +++ b/acts_tests/tests/google/wifi/aware/stress/stress diff --git a/acts/tests/google/wifi/example_config_iot.json b/acts_tests/tests/google/wifi/example_config_iot.json index 133cbe3625..42b0be7531 100644 --- a/acts/tests/google/wifi/example_config_iot.json +++ b/acts_tests/tests/google/wifi/example_config_iot.json @@ -14,7 +14,7 @@ ], "logpath": "/tmp/ACTS_logs", "testpaths": [ - "<path to acts root>/tools/test/connectivity/acts_tests/tests/google/wifi" + "<path to acts root>/tools/test/connectivity/acts/tests/google/wifi" ], "iot_networks": [ { diff --git a/acts/tests/google/wifi/example_config_sanity.json b/acts_tests/tests/google/wifi/example_config_sanity.json index b23d3bff83..b23d3bff83 100644 --- a/acts/tests/google/wifi/example_config_sanity.json +++ b/acts_tests/tests/google/wifi/example_config_sanity.json diff --git a/acts/tests/google/wifi/example_connectivity_performance_ap_sta.json b/acts_tests/tests/google/wifi/example_connectivity_performance_ap_sta.json index 6ec05017a5..234df4a52b 100644 --- a/acts/tests/google/wifi/example_connectivity_performance_ap_sta.json +++ b/acts_tests/tests/google/wifi/example_connectivity_performance_ap_sta.json @@ -85,5 +85,5 @@ "rtt_std_deviation_threshold": 5 }, "logpath": "<path to logs>", - "testpaths": ["<path to ACTS root folder>/tools/test/connectivity/acts_tests/tests/google/wifi"] + "testpaths": ["<path to ACTS root folder>/tools/test/connectivity/acts/tests/google/wifi"] } diff --git a/acts/tests/google/wifi/p2p/config/wifi_p2p.json b/acts_tests/tests/google/wifi/p2p/config/wifi_p2p.json index 9a8c841ed6..31af5313af 100644 --- a/acts/tests/google/wifi/p2p/config/wifi_p2p.json +++ b/acts_tests/tests/google/wifi/p2p/config/wifi_p2p.json @@ -8,7 +8,8 @@ "AndroidDevice": "*" } ], + "skip_read_factory_mac": 1, "logpath": "~/logs", - "testpaths": ["./tools/test/connectivity/acts_tests/tests/google/wifi/p2p"], + "testpaths": ["./tools/test/connectivity/acts/tests/google/wifi/p2p"], "adb_logcat_param": "-b all" } diff --git a/acts/tests/google/wifi/p2p/config/wifi_p2p_group.json b/acts_tests/tests/google/wifi/p2p/config/wifi_p2p_group.json index e4f7d8b3a7..5ca412d404 100644 --- a/acts/tests/google/wifi/p2p/config/wifi_p2p_group.json +++ b/acts_tests/tests/google/wifi/p2p/config/wifi_p2p_group.json @@ -8,10 +8,11 @@ "AndroidDevice": "*" } ], + "skip_read_factory_mac": 1, "network_name": "DIRECT-xy-Hello", "passphrase": "P2pWorld1234", "group_band": "2", "logpath": "~/logs", - "testpaths": ["./tools/test/connectivity/acts_tests/tests/google/wifi/p2p"], + "testpaths": ["./tools/test/connectivity/acts/tests/google/wifi/p2p"], "adb_logcat_param": "-b all" } diff --git a/acts/tests/google/wifi/p2p/functional/WifiP2pGroupTest.py b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pGroupTest.py index 165391fe35..dd27f210c9 100644 --- a/acts/tests/google/wifi/p2p/functional/WifiP2pGroupTest.py +++ b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pGroupTest.py @@ -30,13 +30,13 @@ WPS_PBC = wp2putils.WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_PBC WPS_DISPLAY = wp2putils.WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_DISPLAY WPS_KEYPAD = wp2putils.WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_KEYPAD + class WifiP2pGroupTest(WifiP2pBaseTest): """Tests for APIs in Android's WifiP2pManager class. Test Bed Requirement: * At least two Android devices """ - def __init__(self, controllers): WifiP2pBaseTest.__init__(self, controllers) @@ -76,10 +76,15 @@ class WifiP2pGroupTest(WifiP2pBaseTest): gc_dut = self.dut2 # Create a group wp2putils.p2p_create_group(go_dut) - go_dut.ed.pop_event(p2pconsts.CONNECTED_EVENT, p2pconsts.DEFAULT_TIMEOUT) + go_dut.ed.pop_event(p2pconsts.CONNECTED_EVENT, + p2pconsts.DEFAULT_TIMEOUT) time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) # Request the connection - wp2putils.p2p_connect(gc_dut, go_dut, False, wps_type, True) + wp2putils.p2p_connect(gc_dut, + go_dut, + False, + wps_type, + p2p_connect_type=p2pconsts.P2P_CONNECT_JOIN) go_ip = wp2putils.p2p_go_ip(gc_dut) wp2putils.p2p_connection_ping_test(gc_dut, go_ip) @@ -88,7 +93,6 @@ class WifiP2pGroupTest(WifiP2pBaseTest): wp2putils.check_disconnect(gc_dut) time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) - """Test Cases""" @test_tracker_info(uuid="c41f8293-5225-430d-917e-c294ddff7c2a") def test_p2p_group_join_via_pbc(self): @@ -114,15 +118,13 @@ class WifiP2pGroupTest(WifiP2pBaseTest): go_dut = self.dut1 gc_dut = self.dut2 # Create a group - wp2putils.p2p_create_group_with_config(go_dut, - self.network_name, + wp2putils.p2p_create_group_with_config(go_dut, self.network_name, self.passphrase, self.group_band) time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) # Request the connection. Since config is known, this is reconnection. - wp2putils.p2p_connect_with_config(gc_dut, go_dut, - self.network_name, self.passphrase, - self.group_band) + wp2putils.p2p_connect_with_config(gc_dut, go_dut, self.network_name, + self.passphrase, self.group_band) go_ip = wp2putils.p2p_go_ip(gc_dut) wp2putils.p2p_connection_ping_test(gc_dut, go_ip) diff --git a/acts/tests/google/wifi/p2p/functional/WifiP2pLocalServiceTest.py b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pLocalServiceTest.py index baa21847b8..b043eb9f13 100644 --- a/acts/tests/google/wifi/p2p/functional/WifiP2pLocalServiceTest.py +++ b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pLocalServiceTest.py @@ -21,6 +21,7 @@ from acts.test_utils.wifi.p2p.WifiP2pBaseTest import WifiP2pBaseTest from acts.test_utils.wifi.p2p import wifi_p2p_test_utils as wp2putils from acts.test_utils.wifi.p2p import wifi_p2p_const as p2pconsts + class WifiP2pLocalServiceTest(WifiP2pBaseTest): """Tests for APIs in Android's WifiP2pManager and p2p local service class. @@ -28,7 +29,6 @@ class WifiP2pLocalServiceTest(WifiP2pBaseTest): * At least two Android devices * 3 Android devices for WifiP2pMultiPeersTest.py """ - def __init__(self, controllers): WifiP2pBaseTest.__init__(self, controllers) @@ -46,19 +46,20 @@ class WifiP2pLocalServiceTest(WifiP2pBaseTest): Note: Step 2 - Step 5 should reference function requestServiceAndCheckResult """ self.log.info("Add local Upnp Service") - wp2putils.createP2pLocalService(self.dut1, p2pconsts.P2P_LOCAL_SERVICE_UPNP) + wp2putils.createP2pLocalService(self.dut1, + p2pconsts.P2P_LOCAL_SERVICE_UPNP) - wp2putils.requestServiceAndCheckResult(self.dut1, self.dut2, - wp2putils.WifiP2PEnums.WifiP2pServiceInfo.WIFI_P2P_SERVICE_TYPE_UPNP, - None, None) + wp2putils.requestServiceAndCheckResult( + self.dut1, self.dut2, wp2putils.WifiP2PEnums.WifiP2pServiceInfo. + WIFI_P2P_SERVICE_TYPE_UPNP, None, None) time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) - wp2putils.requestServiceAndCheckResult(self.dut1, self.dut2, - wp2putils.WifiP2PEnums.WifiP2pServiceInfo.WIFI_P2P_SERVICE_TYPE_UPNP, - "ssdp:all", None) + wp2putils.requestServiceAndCheckResult( + self.dut1, self.dut2, wp2putils.WifiP2PEnums.WifiP2pServiceInfo. + WIFI_P2P_SERVICE_TYPE_UPNP, "ssdp:all", None) time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) - wp2putils.requestServiceAndCheckResult(self.dut1, self.dut2, - wp2putils.WifiP2PEnums.WifiP2pServiceInfo.WIFI_P2P_SERVICE_TYPE_UPNP, - "upnp:rootdevice", None) + wp2putils.requestServiceAndCheckResult( + self.dut1, self.dut2, wp2putils.WifiP2PEnums.WifiP2pServiceInfo. + WIFI_P2P_SERVICE_TYPE_UPNP, "upnp:rootdevice", None) time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) """Test Cases""" @@ -75,21 +76,23 @@ class WifiP2pLocalServiceTest(WifiP2pBaseTest): Note: Step 2 - Step 5 should reference function requestServiceAndCheckResult """ self.log.info("Add local bonjour service to %s" % (self.dut1.name)) - wp2putils.createP2pLocalService(self.dut1, p2pconsts.P2P_LOCAL_SERVICE_IPP) - wp2putils.createP2pLocalService(self.dut1, p2pconsts.P2P_LOCAL_SERVICE_AFP) + wp2putils.createP2pLocalService(self.dut1, + p2pconsts.P2P_LOCAL_SERVICE_IPP) + wp2putils.createP2pLocalService(self.dut1, + p2pconsts.P2P_LOCAL_SERVICE_AFP) - wp2putils.requestServiceAndCheckResultWithRetry(self.dut1, self.dut2, - wp2putils.WifiP2PEnums.WifiP2pServiceInfo.WIFI_P2P_SERVICE_TYPE_BONJOUR, - None, None) + wp2putils.requestServiceAndCheckResultWithRetry( + self.dut1, self.dut2, wp2putils.WifiP2PEnums.WifiP2pServiceInfo. + WIFI_P2P_SERVICE_TYPE_BONJOUR, None, None) time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) - wp2putils.requestServiceAndCheckResultWithRetry(self.dut1,self.dut2, - wp2putils.WifiP2PEnums.WifiP2pServiceInfo.WIFI_P2P_SERVICE_TYPE_BONJOUR, - "_ipp._tcp", None) + wp2putils.requestServiceAndCheckResultWithRetry( + self.dut1, self.dut2, wp2putils.WifiP2PEnums.WifiP2pServiceInfo. + WIFI_P2P_SERVICE_TYPE_BONJOUR, "_ipp._tcp", None) time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) - wp2putils.requestServiceAndCheckResultWithRetry(self.dut1, self.dut2, - wp2putils.WifiP2PEnums.WifiP2pServiceInfo.WIFI_P2P_SERVICE_TYPE_BONJOUR, - "_ipp._tcp", "MyPrinter") + wp2putils.requestServiceAndCheckResultWithRetry( + self.dut1, self.dut2, wp2putils.WifiP2PEnums.WifiP2pServiceInfo. + WIFI_P2P_SERVICE_TYPE_BONJOUR, "_ipp._tcp", "MyPrinter") time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) - wp2putils.requestServiceAndCheckResultWithRetry(self.dut1, self.dut2, - wp2putils.WifiP2PEnums.WifiP2pServiceInfo.WIFI_P2P_SERVICE_TYPE_BONJOUR, - "_afpovertcp._tcp", "Example") + wp2putils.requestServiceAndCheckResultWithRetry( + self.dut1, self.dut2, wp2putils.WifiP2PEnums.WifiP2pServiceInfo. + WIFI_P2P_SERVICE_TYPE_BONJOUR, "_afpovertcp._tcp", "Example") diff --git a/acts/tests/google/wifi/p2p/functional/WifiP2pManagerTest.py b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pManagerTest.py index 1f5a8628bd..6bda400dd6 100644 --- a/acts/tests/google/wifi/p2p/functional/WifiP2pManagerTest.py +++ b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pManagerTest.py @@ -30,6 +30,7 @@ WPS_PBC = wp2putils.WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_PBC WPS_DISPLAY = wp2putils.WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_DISPLAY WPS_KEYPAD = wp2putils.WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_KEYPAD + class WifiP2pManagerTest(WifiP2pBaseTest): """Tests for APIs in Android's WifiP2pManager class. @@ -37,7 +38,6 @@ class WifiP2pManagerTest(WifiP2pBaseTest): * At least two Android devices * 3 Android devices for WifiP2pMultiPeersTest.py """ - def __init__(self, controllers): WifiP2pBaseTest.__init__(self, controllers) @@ -100,7 +100,8 @@ class WifiP2pManagerTest(WifiP2pBaseTest): gc_dut.ed.clear_all_events() wp2putils.p2p_connect(gc_dut, go_dut, True, WPS_PBC) wp2putils.p2p_disconnect(gc_dut) - wp2putils.check_disconnect(go_dut) + wp2putils.check_disconnect( + go_dut, timeout=p2pconsts.DEFAULT_GROUP_CLIENT_LOST_TIME) time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) @test_tracker_info(uuid="12bbe73a-5a6c-4307-9797-c77c7efdc4b5") @@ -150,7 +151,8 @@ class WifiP2pManagerTest(WifiP2pBaseTest): gc_dut.ed.clear_all_events() wp2putils.p2p_connect(gc_dut, go_dut, True, WPS_DISPLAY) wp2putils.p2p_disconnect(gc_dut) - wp2putils.check_disconnect(go_dut) + wp2putils.check_disconnect( + go_dut, timeout=p2pconsts.DEFAULT_GROUP_CLIENT_LOST_TIME) time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) @test_tracker_info(uuid="efe88f57-5a08-4195-9592-2f6945a9d18a") @@ -200,5 +202,6 @@ class WifiP2pManagerTest(WifiP2pBaseTest): gc_dut.ed.clear_all_events() wp2putils.p2p_connect(gc_dut, go_dut, True, WPS_KEYPAD) wp2putils.p2p_disconnect(gc_dut) - wp2putils.check_disconnect(go_dut) + wp2putils.check_disconnect( + go_dut, timeout=p2pconsts.DEFAULT_GROUP_CLIENT_LOST_TIME) time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) diff --git a/acts_tests/tests/google/wifi/p2p/functional/WifiP2pMultiPeersTest.py b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pMultiPeersTest.py new file mode 100644 index 0000000000..2951be6355 --- /dev/null +++ b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pMultiPeersTest.py @@ -0,0 +1,266 @@ +#!/usr/bin/env python3 +# +# Copyright 2020 - The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import acts.test_utils.wifi.wifi_test_utils as wutils +import acts.utils +import time + +from acts import asserts +from acts import utils + +from acts.test_decorators import test_tracker_info +from acts.test_utils.wifi import wifi_test_utils as wutils +from acts.test_utils.wifi.p2p.WifiP2pBaseTest import WifiP2pBaseTest +from acts.test_utils.wifi.p2p import wifi_p2p_test_utils as wp2putils +from acts.test_utils.wifi.p2p import wifi_p2p_const as p2pconsts + +WPS_PBC = wp2putils.WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_PBC +WPS_DISPLAY = wp2putils.WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_DISPLAY +WPS_KEYPAD = wp2putils.WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_KEYPAD + +WifiEnums = wutils.WifiEnums + + +class WifiP2pMultiPeersTest(WifiP2pBaseTest): + """Tests for multiple clients. + + Test Bed Requirement: + * 3 Android devices for each test in this class. + """ + def __init__(self, controllers): + WifiP2pBaseTest.__init__(self, controllers) + + def setup_test(self): + WifiP2pBaseTest.setup_test(self) + asserts.skip_if( + len(self.android_devices) < 3, + "No enough android devices. Skip this test") + + def form_group(self, dut1, dut2, isReconnect=False, wpsType=WPS_PBC): + # Request the connection to create a group + wp2putils.p2p_connect(dut1, dut2, isReconnect, wpsType) + + if wp2putils.is_go(dut1): + go_dut = dut1 + gc_dut = dut2 + elif wp2putils.is_go(dut2): + go_dut = dut2 + gc_dut = dut1 + return (go_dut, gc_dut) + + def verify_group_connection(self, group_clients): + for gc in group_clients: + go_ip = wp2putils.p2p_go_ip(gc) + wp2putils.p2p_connection_ping_test(gc, go_ip) + + def clear_all_events(self, duts): + for dut in duts: + dut.ed.clear_all_events() + + def check_disconnection(self, duts): + for dut in duts: + wp2putils.check_disconnect(dut) + + """Test Cases""" + @test_tracker_info(uuid="20cd4f4d-fe7d-4ee2-a832-33caa5b9700b") + def test_p2p_multi_clients_group_removal_behavior(self): + """Verify the p2p group removal behavior + + Steps: + 1. form a group between 3 peers + 2. verify their connection + 3. disconnect + 4. form a group between 3 peers + 5. trigger disconnect from GO + 6. check the group is removed + 7. form a group between 3 peers + 8. trigger disconnect from a GC + 9. check the group is still alive + 10. disconnect + """ + all_duts = [self.dut1, self.dut2, self.dut3] + + # the 1st round + (go_dut, gc_dut) = self.form_group(self.dut1, self.dut2) + gc2_dut = self.dut3 + wp2putils.p2p_connect(gc2_dut, + go_dut, + False, + WPS_PBC, + p2p_connect_type=p2pconsts.P2P_CONNECT_JOIN) + + self.verify_group_connection([gc_dut, gc2_dut]) + + go_dut.log.info("Trigger disconnection") + wp2putils.p2p_disconnect(go_dut) + self.check_disconnection([gc_dut, gc2_dut]) + time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) + + # the 2nd round + self.log.info("Reconnect test, triggered by GO") + self.clear_all_events(all_duts) + + self.form_group(go_dut, gc_dut, isReconnect=True) + wp2putils.p2p_connect(gc2_dut, + go_dut, + True, + WPS_PBC, + p2p_connect_type=p2pconsts.P2P_CONNECT_JOIN) + + # trigger disconnect from GO, the group is destroyed and all + # client are disconnected. + go_dut.log.info("Trigger disconnection") + wp2putils.p2p_disconnect(go_dut) + self.check_disconnection([gc_dut, gc2_dut]) + time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) + + # the 3rd round + self.log.info("Reconnect test, triggered by GC") + self.clear_all_events(all_duts) + + self.form_group(go_dut, gc_dut, isReconnect=True) + wp2putils.p2p_connect(gc2_dut, + go_dut, + True, + WPS_PBC, + p2p_connect_type=p2pconsts.P2P_CONNECT_JOIN) + + # trigger disconnect from GC, the group is still there. + gc_dut.log.info("Trigger disconnection") + wp2putils.p2p_disconnect(gc_dut) + self.verify_group_connection([ + gc2_dut, + ]) + + # all clients are disconnected, the group is removed. + wp2putils.p2p_disconnect(gc2_dut) + self.check_disconnection([ + go_dut, + ]) + time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) + + @test_tracker_info(uuid="6ea6e802-df62-4ae2-aa15-44c3267fd99b") + def test_p2p_connect_with_p2p_and_join_go(self): + """Verify the invitation from GC + + Steps: + 1. form a group between 2 peers + 2. gc joins the group via go + 2. verify their connection + 3. disconnect + """ + all_duts = [self.dut1, self.dut2, self.dut3] + + (go_dut, gc_dut) = self.form_group(self.dut1, self.dut2) + gc2_dut = self.dut3 + wp2putils.p2p_connect(gc2_dut, + go_dut, + False, + WPS_PBC, + p2p_connect_type=p2pconsts.P2P_CONNECT_JOIN) + + self.verify_group_connection([gc_dut, gc2_dut]) + + go_dut.log.info("Trigger disconnection") + wp2putils.p2p_disconnect(go_dut) + self.check_disconnection([gc_dut, gc2_dut]) + time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) + + @test_tracker_info(uuid="e00469a4-93b7-44dd-8a5e-5d317e0e9333") + def test_p2p_connect_with_p2p_and_legacy_client(self): + """Verify the invitation from GC + + Steps: + 1. form a group between 2 peers + 2. gc joins the group via go + 2. verify their connection + 3. disconnect + """ + all_duts = [self.dut1, self.dut2, self.dut3] + + (go_dut, gc_dut) = self.form_group(self.dut1, self.dut2) + gc2_dut = self.dut3 + + group = wp2putils.p2p_get_current_group(go_dut) + network = { + WifiEnums.SSID_KEY: group['NetworkName'], + WifiEnums.PWD_KEY: group['Passphrase'] + } + wutils.start_wifi_connection_scan_and_ensure_network_found( + gc2_dut, group['NetworkName']) + wutils.wifi_connect(gc2_dut, network, num_of_tries=3) + + go_dut.log.info("Trigger disconnection") + wp2putils.p2p_disconnect(go_dut) + time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) + + @test_tracker_info(uuid="bd53cc18-bcc7-4e27-b78b-1506f5c098c5") + def test_p2p_connect_with_p2p_and_go_invite_peer(self): + """Verify the invitation from GC + + Steps: + 1. form a group between 2 peers + 2. gc joins the group via go + 2. verify their connection + 3. disconnect + """ + all_duts = [self.dut1, self.dut2, self.dut3] + + (go_dut, gc_dut) = self.form_group(self.dut1, self.dut2) + gc2_dut = self.dut3 + wp2putils.p2p_connect( + go_dut, + gc2_dut, + False, + WPS_PBC, + p2p_connect_type=p2pconsts.P2P_CONNECT_INVITATION, + go_ad=go_dut) + + self.verify_group_connection([gc_dut, gc2_dut]) + + go_dut.log.info("Trigger disconnection") + wp2putils.p2p_disconnect(go_dut) + self.check_disconnection([gc_dut, gc2_dut]) + time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) + + @test_tracker_info(uuid="4d6e666d-dc48-4881-86c1-5d7cec5e2571") + def test_p2p_connect_with_p2p_and_gc_invite_peer(self): + """Verify the invitation from GC + + Steps: + 1. form a group between 2 peers + 2. gc joins the group via go + 2. verify their connection + 3. disconnect + """ + all_duts = [self.dut1, self.dut2, self.dut3] + + (go_dut, gc_dut) = self.form_group(self.dut1, self.dut2) + gc2_dut = self.dut3 + wp2putils.p2p_connect( + gc_dut, + gc2_dut, + False, + WPS_PBC, + p2p_connect_type=p2pconsts.P2P_CONNECT_INVITATION, + go_ad=go_dut) + + self.verify_group_connection([gc_dut, gc2_dut]) + + go_dut.log.info("Trigger disconnection") + wp2putils.p2p_disconnect(go_dut) + self.check_disconnection([gc_dut, gc2_dut]) + time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) diff --git a/acts/tests/google/wifi/p2p/functional/WifiP2pSnifferTest.py b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pSnifferTest.py index 310eab2b50..2db52730d5 100644 --- a/acts/tests/google/wifi/p2p/functional/WifiP2pSnifferTest.py +++ b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pSnifferTest.py @@ -17,6 +17,7 @@ import acts.test_utils.wifi.wifi_test_utils as wutils import acts.utils import time +import re from acts import asserts from acts import utils @@ -33,6 +34,7 @@ WPS_DISPLAY = wp2putils.WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_DISPLAY WPS_KEYPAD = wp2putils.WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_KEYPAD DEFAULT_TIMEOUT = 10 + class WifiP2pSnifferTest(WifiP2pBaseTest): """Tests factory MAC is not leaked for p2p discovery and associated cases. @@ -40,7 +42,6 @@ class WifiP2pSnifferTest(WifiP2pBaseTest): * At least two Android devices * An access point as sniffer """ - def __init__(self, controllers): WifiP2pBaseTest.__init__(self, controllers) @@ -52,11 +53,12 @@ class WifiP2pSnifferTest(WifiP2pBaseTest): def setup_test(self): super(WifiP2pSnifferTest, self).setup_test() - self.pcap_procs = wutils.start_pcap( - self.packet_capture, '2g', self.test_name) + self.pcap_procs = wutils.start_pcap(self.packet_capture, '2g', + self.test_name) def teardown_test(self): - self.verify_mac_no_leakage() + if self.pcap_procs: + wutils.stop_pcap(self.packet_capture, self.pcap_procs, False) super(WifiP2pSnifferTest, self).teardown_test() def configure_packet_capture(self): @@ -73,12 +75,13 @@ class WifiP2pSnifferTest(WifiP2pBaseTest): # Verify factory MAC is not leaked in 2G pcaps pcap_fname = '%s_%s.pcap' % (self.pcap_procs[BAND_2G][1], BAND_2G.upper()) + self.pcap_procs = None packets = rdpcap(pcap_fname) - wutils.verify_mac_not_found_in_pcap(self.dut1_mac, packets) - wutils.verify_mac_not_found_in_pcap(self.dut2_mac, packets) + wutils.verify_mac_not_found_in_pcap(self.dut1, self.dut1_mac, packets) + wutils.verify_mac_not_found_in_pcap(self.dut2, self.dut2_mac, packets) """Test Cases""" - @test_tracker_info(uuid=" d04e62dc-e1ef-4cea-86e6-39f0dd08fb6b") + @test_tracker_info(uuid="d04e62dc-e1ef-4cea-86e6-39f0dd08fb6b") def test_p2p_discovery_sniffer(self): """Verify the p2p discovery functionality Steps: @@ -88,6 +91,7 @@ class WifiP2pSnifferTest(WifiP2pBaseTest): self.log.info("Device discovery") wp2putils.find_p2p_device(self.dut1, self.dut2) wp2putils.find_p2p_device(self.dut2, self.dut1) + self.verify_mac_no_leakage() @test_tracker_info(uuid="6a02be84-912d-4b5b-8dfa-fd80d2554c55") def test_p2p_connect_via_pbc_and_ping_and_reconnect_sniffer(self): @@ -138,3 +142,6 @@ class WifiP2pSnifferTest(WifiP2pBaseTest): wp2putils.p2p_disconnect(gc_dut) wp2putils.check_disconnect(go_dut) time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME) + + # teardown + self.verify_mac_no_leakage() diff --git a/acts/tests/google/wifi/rtt/README.md b/acts_tests/tests/google/wifi/rtt/README.md index 1e435eb379..1e435eb379 100644 --- a/acts/tests/google/wifi/rtt/README.md +++ b/acts_tests/tests/google/wifi/rtt/README.md diff --git a/acts/tests/google/wifi/rtt/config/wifi_rtt.json b/acts_tests/tests/google/wifi/rtt/config/wifi_rtt.json index 1870fe97f2..315beacc26 100644 --- a/acts/tests/google/wifi/rtt/config/wifi_rtt.json +++ b/acts_tests/tests/google/wifi/rtt/config/wifi_rtt.json @@ -9,7 +9,7 @@ } ], "logpath": "~/logs", - "testpaths": ["./tools/test/connectivity/acts_tests/tests/google/wifi"], + "testpaths": ["./tools/test/connectivity/acts/tests/google/wifi"], "adb_logcat_param": "-b all", "aware_default_power_mode": "INTERACTIVE", "lci_reference": [], diff --git a/acts/tests/google/wifi/rtt/functional/AwareDiscoveryWithRangingTest.py b/acts_tests/tests/google/wifi/rtt/functional/AwareDiscoveryWithRangingTest.py index e19993db13..da0ba4be86 100644 --- a/acts/tests/google/wifi/rtt/functional/AwareDiscoveryWithRangingTest.py +++ b/acts_tests/tests/google/wifi/rtt/functional/AwareDiscoveryWithRangingTest.py @@ -1044,7 +1044,7 @@ class AwareDiscoveryWithRangingTest(AwareBaseTest, RttBaseTest): s_config=None, # no updates expect_discovery=False) - @test_tracker_info(uuid="ec6ca57b-f115-4516-813a-4572b930c8d3") + @test_tracker_info(uuid="3938a3dc-8032-4096-b184-b528e4564b5e") def test_ranged_updated_discovery_solicited_active_multi_step(self): """Verify discovery with ranging operation with updated configuration: - Unsolicited Publish/Passive Subscribe diff --git a/acts/tests/google/wifi/rtt/functional/RangeApMiscTest.py b/acts_tests/tests/google/wifi/rtt/functional/RangeApMiscTest.py index c68ca92f9f..c68ca92f9f 100644 --- a/acts/tests/google/wifi/rtt/functional/RangeApMiscTest.py +++ b/acts_tests/tests/google/wifi/rtt/functional/RangeApMiscTest.py diff --git a/acts/tests/google/wifi/rtt/functional/RangeApNonSupporting11McTest.py b/acts_tests/tests/google/wifi/rtt/functional/RangeApNonSupporting11McTest.py index 1ffbb0278d..1ffbb0278d 100644 --- a/acts/tests/google/wifi/rtt/functional/RangeApNonSupporting11McTest.py +++ b/acts_tests/tests/google/wifi/rtt/functional/RangeApNonSupporting11McTest.py diff --git a/acts/tests/google/wifi/rtt/functional/RangeApSupporting11McTest.py b/acts_tests/tests/google/wifi/rtt/functional/RangeApSupporting11McTest.py index c218898fba..c218898fba 100644 --- a/acts/tests/google/wifi/rtt/functional/RangeApSupporting11McTest.py +++ b/acts_tests/tests/google/wifi/rtt/functional/RangeApSupporting11McTest.py diff --git a/acts/tests/google/wifi/rtt/functional/RangeAwareTest.py b/acts_tests/tests/google/wifi/rtt/functional/RangeAwareTest.py index 2e53c6db6a..2e53c6db6a 100644 --- a/acts/tests/google/wifi/rtt/functional/RangeAwareTest.py +++ b/acts_tests/tests/google/wifi/rtt/functional/RangeAwareTest.py diff --git a/acts/tests/google/wifi/rtt/functional/RangeSoftApTest.py b/acts_tests/tests/google/wifi/rtt/functional/RangeSoftApTest.py index c23b5b0ed5..c23b5b0ed5 100644 --- a/acts/tests/google/wifi/rtt/functional/RangeSoftApTest.py +++ b/acts_tests/tests/google/wifi/rtt/functional/RangeSoftApTest.py diff --git a/acts/tests/google/wifi/rtt/functional/RttDisableTest.py b/acts_tests/tests/google/wifi/rtt/functional/RttDisableTest.py index 635837c950..e3219d4158 100644 --- a/acts/tests/google/wifi/rtt/functional/RttDisableTest.py +++ b/acts_tests/tests/google/wifi/rtt/functional/RttDisableTest.py @@ -34,6 +34,8 @@ class RttDisableTest(WifiBaseTest, RttBaseTest): super().setup_class() if "AccessPoint" in self.user_params: self.legacy_configure_ap_and_start() + elif "OpenWrtAP" in self.user_params: + self.configure_openwrt_ap_and_start(open_network=True) def run_disable_rtt(self, disable_mode): """Validate the RTT disabled flows: whether by disabling Wi-Fi or entering diff --git a/acts/tests/google/wifi/rtt/functional/RttRequestManagementTest.py b/acts_tests/tests/google/wifi/rtt/functional/RttRequestManagementTest.py index c91521df0d..c91521df0d 100644 --- a/acts/tests/google/wifi/rtt/functional/RttRequestManagementTest.py +++ b/acts_tests/tests/google/wifi/rtt/functional/RttRequestManagementTest.py diff --git a/acts/tests/google/wifi/rtt/stress/StressRangeApTest.py b/acts_tests/tests/google/wifi/rtt/stress/StressRangeApTest.py index d0e9fe9bb6..d0e9fe9bb6 100644 --- a/acts/tests/google/wifi/rtt/stress/StressRangeApTest.py +++ b/acts_tests/tests/google/wifi/rtt/stress/StressRangeApTest.py diff --git a/acts/tests/google/wifi/rtt/stress/StressRangeAwareTest.py b/acts_tests/tests/google/wifi/rtt/stress/StressRangeAwareTest.py index ccf8b9d0c5..ccf8b9d0c5 100644 --- a/acts/tests/google/wifi/rtt/stress/StressRangeAwareTest.py +++ b/acts_tests/tests/google/wifi/rtt/stress/StressRangeAwareTest.py diff --git a/acts_tests/tests/sample/RepeatedTest.py b/acts_tests/tests/sample/RepeatedTest.py index 889132e0af..0266e24ba7 100644 --- a/acts_tests/tests/sample/RepeatedTest.py +++ b/acts_tests/tests/sample/RepeatedTest.py @@ -40,18 +40,14 @@ def get_median_current(test_results): class RepeatedTest(BaseTestClass): - def __init__(self, controllers): - super().__init__(controllers) - self.count_for_passing_testcase = 0 - self.count_for_failing_testcase = 0 @repeated_test(num_passes=3, acceptable_failures=0) - def test_repeated_case(self): + def test_repeated_case(self, _): self.log.info('This logic executes three times.') @repeated_test(num_passes=3, acceptable_failures=2, result_selector=get_median_current) - def test_repeated_case_pass(self): + def test_repeated_case_pass(self, attempt_number): """The end result of this test is a pass with current=3.5""" returned_results = [ signals.TestPass('0Pass msg!', extras={'current': 3.5}), @@ -59,13 +55,11 @@ class RepeatedTest(BaseTestClass): signals.TestPass('1Pass msg!', extras={'current': 3.2}), signals.TestPass('2Pass msg!', extras={'current': 3.6}) ] - # Every time this function runs, we return a different signal. - self.count_for_passing_testcase += 1 - raise returned_results[self.count_for_passing_testcase - 1] + raise returned_results[attempt_number - 1] @repeated_test(num_passes=3, acceptable_failures=2, result_selector=get_median_current) - def test_repeated_case_with_failures(self): + def test_repeated_case_with_failures(self, attempt_number): """The end result of this test is the last failure to occur.""" returned_results = [ signals.TestPass('Pass msg!', extras={'current': 3.5}), @@ -73,6 +67,4 @@ class RepeatedTest(BaseTestClass): signals.TestFailure('Fail msg!', extras={'current': 58.1}), signals.TestFailure('Fail msg!', extras={'current': 74.2}), ] - # Every time this function runs, we return a different signal. - self.count_for_failing_testcase += 1 - raise returned_results[(self.count_for_failing_testcase - 1) % 4] + raise returned_results[(attempt_number - 1) % 4] diff --git a/acts_tests/tests/sample/SampleTest.py b/acts_tests/tests/sample/SampleTest.py index d21e3044ed..eb4481175f 100755 --- a/acts_tests/tests/sample/SampleTest.py +++ b/acts_tests/tests/sample/SampleTest.py @@ -1,6 +1,6 @@ -#!/usr/bin/env python3.4 +#!/usr/bin/env python3 # -# Copyright 2016 - The Android Open Source Project +# Copyright 2020 - The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,14 +13,21 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - from acts.base_test import BaseTestClass class SampleTest(BaseTestClass): - def __init__(self, controllers): - BaseTestClass.__init__(self, controllers) - self.tests = ("test_make_toast", ) + def setup_class(self): + self.log.info('Called when the test class is started.') + + def teardown_class(self): + self.log.info('Called when the test class has finished.') + + def setup_test(self): + self.log.info('Called before each test case starts.') + + def teardown_test(self): + self.log.info('Called after each test case completes.') """Tests""" diff --git a/tools/create_virtualenv.sh b/tools/create_virtualenv.sh index 3746ba2936..197282a306 100755 --- a/tools/create_virtualenv.sh +++ b/tools/create_virtualenv.sh @@ -10,8 +10,11 @@ fi virtualenv='/tmp/acts_preupload_virtualenv' -python3 -m virtualenv -p python3 $virtualenv +echo "preparing virtual env" > "${virtualenv}.log" +python3 -m virtualenv -p python3 $virtualenv &>> "${virtualenv}.log" cp -r acts/framework $virtualenv/ cd $virtualenv/framework -$virtualenv/bin/python3 setup.py develop +echo "installing acts in virtual env" >> "${virtualenv}.log" +$virtualenv/bin/python3 setup.py develop &>> "${virtualenv}.log" cd - +echo "done" >> "${virtualenv}.log" |