Revert "Merge sc-d1-dev 6958804 into master."
This reverts commit 3aed9d58ff5cfdbbafc5f8ec8bedd6a6d1ed2221.
Reason for revert: Certain changes were originally not merged from master to sc-d1-dev. By merging sc-d1-dev back to master, those changes would be lost. In addition, sc-d1-dev has never been used for development for platform/tools/test/connectivity.
Bug: 172861149
Change-Id: Ie2d73b100cebfc098a15021c2ed122af165fd869
diff --git a/acts_tests/tests/google/wifi b/acts_tests/tests/google/wifi
deleted file mode 120000
index 48192ea..0000000
--- a/acts_tests/tests/google/wifi
+++ /dev/null
@@ -1 +0,0 @@
-../../../acts/tests/google/wifi
\ No newline at end of file
diff --git a/acts_tests/tests/google/wifi/OWNERS b/acts_tests/tests/google/wifi/OWNERS
new file mode 100644
index 0000000..edb3e3e
--- /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/tests/google/wifi/SetupWifiNetworkTest.py b/acts_tests/tests/google/wifi/SetupWifiNetworkTest.py
new file mode 100644
index 0000000..26026ff
--- /dev/null
+++ b/acts_tests/tests/google/wifi/SetupWifiNetworkTest.py
@@ -0,0 +1,107 @@
+#!/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 logging
+import socket
+import sys
+
+from acts import base_test
+from acts.controllers.ap_lib import hostapd_ap_preset
+from acts.controllers.ap_lib import hostapd_bss_settings
+from acts.controllers.ap_lib import hostapd_constants
+from acts.controllers.ap_lib import hostapd_config
+from acts.controllers.ap_lib import hostapd_security
+
+
+class SetupWifiNetworkTest(base_test.BaseTestClass):
+ def wait_for_test_completion(self):
+ port = int(self.user_params["socket_port"])
+ timeout = float(self.user_params["socket_timeout_secs"])
+
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+
+ server_address = ('localhost', port)
+ logging.info("Starting server socket on localhost port %s", port)
+ sock.bind(('localhost', port))
+ sock.settimeout(timeout)
+ sock.listen(1)
+ logging.info("Waiting for client socket connection")
+ try:
+ connection, client_address = sock.accept()
+ except socket.timeout:
+ logging.error("Did not receive signal. Shutting down AP")
+ except socket.error:
+ logging.error("Socket connection errored out. Shutting down AP")
+ finally:
+ connection.close()
+ sock.shutdown(socket.SHUT_RDWR)
+ sock.close()
+
+ def setup_ap(self):
+ bss_settings = []
+ self.access_point = self.access_points[0]
+ network_type = self.user_params["network_type"]
+ if (network_type == "2G"):
+ self.channel = hostapd_constants.AP_DEFAULT_CHANNEL_2G
+ else:
+ self.channel = hostapd_constants.AP_DEFAULT_CHANNEL_5G
+
+ self.ssid = self.user_params["ssid"]
+ self.security = self.user_params["security"]
+ if self.security == "none":
+ self.config = hostapd_ap_preset.create_ap_preset(
+ channel=self.channel,
+ ssid=self.ssid,
+ bss_settings=bss_settings,
+ profile_name='whirlwind')
+ else:
+ self.passphrase = self.user_params["passphrase"]
+ self.hostapd_security = hostapd_security.Security(
+ security_mode=self.security, password=self.passphrase)
+ self.config = hostapd_ap_preset.create_ap_preset(
+ channel=self.channel,
+ ssid=self.ssid,
+ security=self.hostapd_security,
+ bss_settings=bss_settings,
+ profile_name='whirlwind')
+ self.access_point.start_ap(self.config)
+
+ def test_set_up_single_ap(self):
+ req_params = [
+ "AccessPoint", "network_type", "ssid", "passphrase", "security",
+ "socket_port", "socket_timeout_secs"
+ ]
+ opt_params = []
+ self.unpack_userparams(
+ req_param_names=req_params, opt_param_names=opt_params)
+ # Setup the AP environment
+ self.setup_ap()
+ # AP enviroment created. Wait for client to teardown the environment
+ self.wait_for_test_completion()
+
+ def test_set_up_open_ap(self):
+ req_params = [
+ "AccessPoint", "network_type", "ssid", "security", "socket_port",
+ "socket_timeout_secs"
+ ]
+ opt_params = []
+ self.unpack_userparams(
+ req_param_names=req_params, opt_param_names=opt_params)
+ # Setup the AP environment
+ self.setup_ap()
+ # AP enviroment created. Wait for client to teardown the environment
+ self.wait_for_test_completion()
diff --git a/acts_tests/tests/google/wifi/WifiAutoJoinTest.py b/acts_tests/tests/google/wifi/WifiAutoJoinTest.py
new file mode 100755
index 0000000..829fc6c
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiAutoJoinTest.py
@@ -0,0 +1,491 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2016 - 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 asserts
+from acts import base_test
+from acts.test_utils.wifi import wifi_constants
+from acts.test_utils.wifi import wifi_test_utils as wutils
+
+WifiEnums = wutils.WifiEnums
+NETWORK_ID_ERROR = "Network don't have ID"
+NETWORK_ERROR = "Device is not connected to reference network"
+
+
+class WifiAutoJoinTest(base_test.BaseTestClass):
+ def __init__(self, controllers):
+ base_test.BaseTestClass.__init__(self, controllers)
+ self.tests = ("test_autojoin_out_of_range",
+ "test_autojoin_Ap1_2g",
+ "test_autojoin_Ap1_2gto5g",
+ "test_autojoin_in_AP1_5gto2g",
+ "test_autojoin_swtich_AP1toAp2",
+ "test_autojoin_Ap2_2gto5g",
+ "test_autojoin_Ap2_5gto2g",
+ "test_autojoin_out_of_range",
+ "test_autojoin_Ap2_2g",
+ "test_autojoin_Ap2_2gto5g",
+ "test_autojoin_in_Ap2_5gto2g",
+ "test_autojoin_swtich_AP2toAp1",
+ "test_autojoin_Ap1_2gto5g",
+ "test_autojoin_Ap1_5gto2g",
+ "test_autojoin_swtich_to_blacklist_AP",
+ "test_autojoin_in_blacklist_AP",
+ "test_autojoin_back_from_blacklist_AP", )
+
+ def setup_class(self):
+ """It will setup the required dependencies from config file and configure
+ the required networks for auto-join testing. Configured networks will
+ not be removed. If networks are already configured it will skip
+ configuring the networks
+
+ Returns:
+ True if successfully configured the requirements for testing.
+ """
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ req_params = ("reference_networks", "other_network", "atten_val",
+ "ping_addr", "max_bugreports")
+ self.unpack_userparams(req_params)
+ self.log.debug("Connect networks :: {}".format(self.other_network))
+ configured_networks = self.dut.droid.wifiGetConfiguredNetworks()
+ self.log.debug("Configured networks :: {}".format(configured_networks))
+ count_confnet = 0
+ result = False
+ if self.reference_networks[0]['2g']['ssid'] == self.reference_networks[
+ 0]['5g']['ssid']:
+ self.ref_ssid_count = 1
+ else:
+ self.ref_ssid_count = 2 # Different SSID for 2g and 5g
+ for confnet in configured_networks:
+ if confnet[WifiEnums.SSID_KEY] == self.reference_networks[0]['2g'][
+ 'ssid']:
+ count_confnet += 1
+ elif confnet[WifiEnums.SSID_KEY] == self.reference_networks[0][
+ '5g']['ssid']:
+ count_confnet += 1
+ self.log.info("count_confnet {}".format(count_confnet))
+ if count_confnet == self.ref_ssid_count:
+ return
+ else:
+ self.log.info("Configured networks for testing")
+ self.attenuators[0].set_atten(0)
+ self.attenuators[1].set_atten(90)
+ self.attenuators[2].set_atten(90)
+ wait_time = 15
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ try:
+ self.dut.droid.wifiConnectByConfig(self.reference_networks[0][
+ '2g'])
+ connect_result = self.dut.ed.pop_event(
+ wifi_constants.CONNECT_BY_CONFIG_SUCCESS, 1)
+ self.log.info(connect_result)
+ time.sleep(wait_time)
+ if self.ref_ssid_count == 2: #add 5g network as well
+ self.dut.droid.wifiConnectByConfig(self.reference_networks[
+ 0]['5g'])
+ connect_result = self.dut.ed.pop_event(
+ wifi_constants.CONNECT_BY_CONFIG_SUCCESS, 1)
+ self.log.info(connect_result)
+ time.sleep(wait_time)
+ self.dut.droid.wifiConnectByConfig(self.other_network)
+ connect_result = self.dut.ed.pop_event(
+ wifi_constants.CONNECT_BY_CONFIG_SUCCESS)
+ self.log.info(connect_result)
+ wutils.track_connection(self.dut, self.other_network["ssid"], 1)
+ wutils.wifi_forget_network(self.dut, self.other_network["ssid"])
+ time.sleep(wait_time)
+ current_network = self.dut.droid.wifiGetConnectionInfo()
+ self.log.info("Current network: {}".format(current_network))
+ asserts.assert_true('network_id' in current_network,
+ NETWORK_ID_ERROR)
+ asserts.assert_true(current_network['network_id'] >= 0,
+ NETWORK_ERROR)
+ finally:
+ self.dut.droid.wifiLockRelease()
+ self.dut.droid.goToSleepNow()
+
+ def check_connection(self, network_bssid):
+ """Check current wifi connection networks.
+ Args:
+ network_bssid: Network bssid to which connection.
+ Returns:
+ True if connection to given network happen, else return False.
+ """
+ time.sleep(40) #time for connection state to be updated
+ self.log.info("Check network for {}".format(network_bssid))
+ current_network = self.dut.droid.wifiGetConnectionInfo()
+ self.log.debug("Current network: {}".format(current_network))
+ if WifiEnums.BSSID_KEY in current_network:
+ return current_network[WifiEnums.BSSID_KEY] == network_bssid
+ return False
+
+ def set_attn_and_validate_connection(self, attn_value, bssid):
+ """Validate wifi connection status on different attenuation setting.
+
+ Args:
+ attn_value: Attenuation value for different APs signal.
+ bssid: Bssid of excepted network.
+
+ Returns:
+ True if bssid of current network match, else false.
+ """
+ self.attenuators[0].set_atten(attn_value[0])
+ self.attenuators[1].set_atten(attn_value[1])
+ self.attenuators[2].set_atten(attn_value[2])
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ try:
+ asserts.assert_true(
+ self.check_connection(bssid),
+ "Device is not connected to required bssid {}".format(bssid))
+ time.sleep(10) #wait for connection to be active
+ asserts.assert_true(
+ wutils.validate_connection(self.dut, self.ping_addr),
+ "Error, No Internet connection for current bssid {}".format(
+ bssid))
+ finally:
+ self.dut.droid.wifiLockRelease()
+ self.dut.droid.goToSleepNow()
+
+ def on_fail(self, test_name, begin_time):
+ if self.max_bugreports > 0:
+ self.dut.take_bug_report(test_name, begin_time)
+ self.max_bugreports -= 1
+ self.dut.cat_adb_log(test_name, begin_time)
+
+ """ Tests Begin """
+
+ def test_autojoin_Ap1_2g(self):
+ """Test wifi auto join functionality move in range of AP1.
+
+ 1. Attenuate the signal to low range of AP1 and Ap2 not visible at all.
+ 2. Wake up the device.
+ 3. Check that device is connected to right BSSID and maintain stable
+ connection to BSSID in range.
+ """
+ att0, att1, att2 = self.atten_val["Ap1_2g"]
+ variance = 5
+ attenuations = ([att0 + variance * 2, att1, att2],
+ [att0 + variance, att1, att2], [att0, att1, att2],
+ [att0 - variance, att1, att2])
+ name_func = lambda att_value, bssid: ("test_autojoin_Ap1_2g_AP1_{}_AP2"
+ "_{}_AP3_{}").format(att_value[0], att_value[1], att_value[2])
+ failed = self.run_generated_testcases(
+ self.set_attn_and_validate_connection,
+ attenuations,
+ args=(self.reference_networks[0]["2g"]['bssid'], ),
+ name_func=name_func)
+ asserts.assert_false(
+ failed,
+ "Number of test_autojoin_Ap1_2g failed {}".format(len(failed)))
+
+ def test_autojoin_Ap1_2gto5g(self):
+ """Test wifi auto join functionality move to high range.
+
+ 1. Attenuate the signal to high range of AP1.
+ 2. Wake up the device.
+ 3. Check that device is connected to right BSSID and maintain stable
+ connection to BSSID in range.
+ """
+ att0, att1, att2 = self.atten_val["Ap1_2gto5g"]
+ variance = 5
+ attenuations = ([att0 + variance * 2, att1, att2],
+ [att0 + variance, att1, att2], [att0, att1, att2])
+ name_func = lambda att_value, bssid: ("test_autojoin_Ap1_2gto5g_AP1_{}_AP2"
+ "_{}_AP3_{}").format(att_value[0], att_value[1], att_value[2])
+ failed = self.run_generated_testcases(
+ self.set_attn_and_validate_connection,
+ attenuations,
+ args=(self.reference_networks[0]["5g"]['bssid'], ),
+ name_func=name_func)
+ asserts.assert_false(
+ failed,
+ "Number of test_autojoin_Ap1_2gto5g failed {}".format(len(failed)))
+
+ def test_autojoin_in_AP1_5gto2g(self):
+ """Test wifi auto join functionality move to low range toward AP2.
+
+ 1. Attenuate the signal to medium range of AP1 and low range of AP2.
+ 2. Wake up the device.
+ 3. Check that device is connected to right BSSID and maintain stable
+ connection to BSSID in range.
+ """
+ att0, att1, att2 = self.atten_val["In_AP1_5gto2g"]
+ variance = 5
+ attenuations = ([att0 - variance, att1 + variance, att2],
+ [att0, att1, att2],
+ [att0 + variance, att1 - variance, att2])
+ name_func = lambda att_value, bssid: ("test_autojoin_in_AP1_5gto2g_AP1_{}_AP2"
+ "_{}_AP3_{}").format(att_value[0], att_value[1], att_value[2])
+ failed = self.run_generated_testcases(
+ self.set_attn_and_validate_connection,
+ attenuations,
+ args=(self.reference_networks[0]["2g"]['bssid'], ),
+ name_func=name_func)
+ asserts.assert_false(
+ failed, "Number of test_autojoin_in_AP1_5gto2g failed {}".format(
+ len(failed)))
+
+ def test_autojoin_swtich_AP1toAp2(self):
+ """Test wifi auto join functionality move from low range of AP1 to better
+ range of AP2.
+
+ 1. Attenuate the signal to low range of AP1 and medium range of AP2.
+ 2. Wake up the device.
+ 3. Check that device is connected to right BSSID and maintain stable
+ connection to BSSID in range.
+ """
+ att0, att1, att2 = self.atten_val["Swtich_AP1toAp2"]
+ variance = 5
+ attenuations = ([att0 - variance, att1 + variance, att2],
+ [att0, att1, att2],
+ [att0 + variance, att1 - variance, att2])
+ name_func = lambda att_value, bssid: ("test_autojoin_swtich_AP1toAp2_AP1_{}_AP2"
+ "_{}_AP3_{}").format(att_value[0], att_value[1], att_value[2])
+ failed = self.run_generated_testcases(
+ self.set_attn_and_validate_connection,
+ attenuations,
+ args=(self.reference_networks[1]["2g"]['bssid'], ),
+ name_func=name_func)
+ asserts.assert_false(
+ failed, "Number of test_autojoin_swtich_AP1toAp2 failed {}".format(
+ len(failed)))
+
+ def test_autojoin_Ap2_2gto5g(self):
+ """Test wifi auto join functionality move to high range of AP2.
+
+ 1. Attenuate the signal to out range of AP1 and high range of AP2.
+ 2. Wake up the device.
+ 3. Check that device is connected to right BSSID and maintain stable
+ connection to BSSID in range.
+ """
+ att0, att1, att2 = self.atten_val["Ap2_2gto5g"]
+ variance = 5
+ attenuations = ([att0 - variance, att1 + variance * 2, att2],
+ [att0, att1 + variance, att2], [att0, att1, att2])
+ name_func = lambda att_value, bssid: ("test_autojoin_Ap2_2gto5g_AP1_{}_AP2"
+ "_{}_AP3_{}").format(att_value[0], att_value[1], att_value[2])
+ failed = self.run_generated_testcases(
+ self.set_attn_and_validate_connection,
+ attenuations,
+ args=(self.reference_networks[1]["5g"]['bssid'], ),
+ name_func=name_func)
+ asserts.assert_false(
+ failed,
+ "Number of test_autojoin_Ap2_2gto5g failed {}".format(len(failed)))
+
+ def test_autojoin_Ap2_5gto2g(self):
+ """Test wifi auto join functionality move to low range of AP2.
+
+ 1. Attenuate the signal to low range of AP2.
+ 2. Wake up the device.
+ 3. Check that device is connected to right BSSID and maintain stable.
+ """
+ att0, att1, att2 = self.atten_val["Ap2_5gto2g"]
+ variance = 5
+ attenuations = ([att0, att1 - variance, att2], [att0, att1, att2],
+ [att0, att1 + variance, att2])
+ name_func = lambda att_value, bssid: ("test_autojoin_Ap2_5gto2g_AP1_{}_AP2"
+ "_{}_AP3_{}").format(att_value[0], att_value[1], att_value[2])
+ failed = self.run_generated_testcases(
+ self.set_attn_and_validate_connection,
+ attenuations,
+ args=(self.reference_networks[1]["2g"]['bssid'], ),
+ name_func=name_func)
+ asserts.assert_false(
+ failed,
+ "Number of test_autojoin_Ap2_5gto2g failed {}".format(len(failed)))
+
+ def test_autojoin_out_of_range(self):
+ """Test wifi auto join functionality move to low range.
+
+ 1. Attenuate the signal to out of range.
+ 2. Wake up the device.
+ 3. Start the scan.
+ 4. Check that device is not connected to any network.
+ """
+ self.attenuators[0].set_atten(90)
+ self.attenuators[1].set_atten(90)
+ self.attenuators[2].set_atten(90)
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ try:
+ wutils.start_wifi_connection_scan(self.dut)
+ wifi_results = self.dut.droid.wifiGetScanResults()
+ self.log.debug("Scan result {}".format(wifi_results))
+ time.sleep(20)
+ current_network = self.dut.droid.wifiGetConnectionInfo()
+ self.log.info("Current network: {}".format(current_network))
+ asserts.assert_true(
+ ('network_id' in current_network and
+ current_network['network_id'] == -1),
+ "Device is connected to network {}".format(current_network))
+ finally:
+ self.dut.droid.wifiLockRelease()
+ self.dut.droid.goToSleepNow()
+
+ def test_autojoin_Ap2_2g(self):
+ """Test wifi auto join functionality move in low range of AP2.
+
+ 1. Attenuate the signal to move in range of AP2 and Ap1 not visible at all.
+ 2. Wake up the device.
+ 3. Check that device is connected to right BSSID and maintain stable
+ connection to BSSID in range.
+ """
+ att0, att1, att2 = self.atten_val["Ap2_2g"]
+ variance = 5
+ attenuations = ([att0, att1 + variance * 2, att2],
+ [att0, att1 + variance, att2], [att0, att1, att2],
+ [att0, att1 - variance, att2])
+ name_func = lambda att_value, bssid: ("test_autojoin_Ap2_2g_AP1_{}_AP2"
+ "_{}_AP3_{}").format(att_value[0], att_value[1], att_value[2])
+ failed = self.run_generated_testcases(
+ self.set_attn_and_validate_connection,
+ attenuations,
+ args=(self.reference_networks[1]["2g"]['bssid'], ),
+ name_func=name_func)
+ asserts.assert_false(
+ failed,
+ "Number of test_autojoin_Ap2_2g failed {}".format(len(failed)))
+
+ def test_autojoin_in_Ap2_5gto2g(self):
+ """Test wifi auto join functionality move to medium range of Ap2 and
+ low range of AP1.
+
+ 1. Attenuate the signal to move in medium range of AP2 and low range of AP1.
+ 2. Wake up the device.
+ 3. Check that device is connected to right BSSID and maintain stable
+ connection to BSSID in range.
+ """
+ att0, att1, att2 = self.atten_val["In_Ap2_5gto2g"]
+ variance = 5
+ attenuations = ([att0, att1 - variance, att2], [att0, att1, att2],
+ [att0, att1 + variance, att2])
+ name_func = lambda att_value, bssid: ("test_autojoin_in_Ap2_5gto2g_AP1_{}_AP2"
+ "_{}_AP3_{}").format(att_value[0], att_value[1], att_value[2])
+ failed = self.run_generated_testcases(
+ self.set_attn_and_validate_connection,
+ attenuations,
+ args=(self.reference_networks[1]["2g"]['bssid'], ),
+ name_func=name_func)
+ asserts.assert_false(
+ failed, "Number of test_autojoin_in_Ap2_5gto2g failed {}".format(
+ len(failed)))
+
+ def test_autojoin_swtich_AP2toAp1(self):
+ """Test wifi auto join functionality move from low range of AP2 to better
+ range of AP1.
+
+ 1. Attenuate the signal to low range of AP2 and medium range of AP1.
+ 2. Wake up the device.
+ 3. Check that device is connected to right BSSID and maintain stable
+ connection to BSSID in range.
+ """
+ att0, att1, att2 = self.atten_val["Swtich_AP2toAp1"]
+ variance = 5
+ attenuations = ([att0 + variance, att1 - variance, att2],
+ [att0, att1, att2],
+ [att0 - variance, att1 + variance, att2])
+ name_func = lambda att_value, bssid: ("test_autojoin_swtich_AP2toAp1_AP1_{}_AP2"
+ "_{}_AP3_{}").format(att_value[0], att_value[1], att_value[2])
+ failed = self.run_generated_testcases(
+ self.set_attn_and_validate_connection,
+ attenuations,
+ args=(self.reference_networks[0]["2g"]['bssid'], ),
+ name_func=name_func)
+ asserts.assert_false(
+ failed, "Number of test_autojoin_swtich_AP2toAp1 failed {}".format(
+ len(failed)))
+
+ def test_autojoin_Ap1_5gto2g(self):
+ """Test wifi auto join functionality move to medium range of AP1.
+
+ 1. Attenuate the signal to medium range of AP1.
+ 2. Wake up the device.
+ 3. Check that device is connected to right BSSID and maintain stable
+ connection to BSSID in range.
+ """
+ att0, att1, att2 = self.atten_val["Ap1_5gto2g"]
+ variance = 5
+ attenuations = ([att0, att1, att2], [att0 + variance, att1, att2],
+ [att0 + variance * 2, att1, att2])
+ name_func = lambda att_value, bssid: ("test_autojoin_Ap1_5gto2g_AP1_{}_AP2"
+ "_{}_AP3_{}").format(att_value[0], att_value[1], att_value[2])
+ failed = self.run_generated_testcases(
+ self.set_attn_and_validate_connection,
+ attenuations,
+ args=(self.reference_networks[0]["2g"]['bssid'], ),
+ name_func=name_func)
+ asserts.assert_false(
+ failed,
+ "Number of test_autojoin_Ap1_5gto2g failed {}".format(len(failed)))
+
+ def test_autojoin_swtich_to_blacklist_AP(self):
+ """Test wifi auto join functionality in medium range of blacklist BSSID.
+
+ 1. Attenuate the signal to low range of AP1 and medium range of AP3.
+ 2. Wake up the device.
+ 3. Check that device is connected to AP1 BSSID and maintain stable
+ connection to BSSID.
+ """
+ self.set_attn_and_validate_connection(
+ self.atten_val["Swtich_to_blacklist"],
+ self.reference_networks[0]["2g"]['bssid'])
+
+ def test_autojoin_in_blacklist_AP(self):
+ """Test wifi auto join functionality in high range of blacklist BSSID.
+
+ 1. Attenuate the signal to out of range of AP1 and full range of AP3.
+ 2. Wake up the device.
+ 3. Check that device is disconnected form all AP.
+ """
+ attn0, attn1, attn2 = self.atten_val["In_blacklist"]
+ self.attenuators[0].set_atten(attn0)
+ self.attenuators[1].set_atten(attn1)
+ self.attenuators[2].set_atten(attn2)
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ try:
+ wutils.start_wifi_connection_scan(self.dut)
+ wifi_results = self.dut.droid.wifiGetScanResults()
+ self.log.debug("Scan result {}".format(wifi_results))
+ time.sleep(20)
+ current_network = self.dut.droid.wifiGetConnectionInfo()
+ self.log.info("Current network: {}".format(current_network))
+ asserts.assert_true(
+ ('network_id' in current_network and
+ current_network['network_id'] == -1),
+ "Device is still connected to blacklisted network {}".format(
+ current_network))
+ finally:
+ self.dut.droid.wifiLockRelease()
+ self.dut.droid.goToSleepNow()
+
+ def test_autojoin_back_from_blacklist_AP(self):
+ """Test wifi auto join functionality in medium range of blacklist BSSID.
+
+ 1. Attenuate the signal to medium of range of AP1 and low range of AP3.
+ 2. Wake up the device.
+ 3. Check that device is disconnected form all AP.
+ """
+ self.set_attn_and_validate_connection(
+ self.atten_val["Back_from_blacklist"],
+ self.reference_networks[0]["2g"]['bssid'])
+
+ """ Tests End """
diff --git a/acts_tests/tests/google/wifi/WifiAutoUpdateTest.py b/acts_tests/tests/google/wifi/WifiAutoUpdateTest.py
new file mode 100755
index 0000000..ae69271
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiAutoUpdateTest.py
@@ -0,0 +1,410 @@
+# !/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):
+ super().setup_test()
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+
+ def teardown_test(self):
+ super().teardown_test()
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+
+ 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")
+ @WifiBaseTest.wifi_test_wrap
+ 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")
+ @WifiBaseTest.wifi_test_wrap
+ 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")
+ @WifiBaseTest.wifi_test_wrap
+ 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")
+ @WifiBaseTest.wifi_test_wrap
+ 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")
+ @WifiBaseTest.wifi_test_wrap
+ 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")
+ @WifiBaseTest.wifi_test_wrap
+ 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.reset_wifi(self.dut)
+ 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")
+ @WifiBaseTest.wifi_test_wrap
+ 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")
+ @WifiBaseTest.wifi_test_wrap
+ 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")
+ @WifiBaseTest.wifi_test_wrap
+ 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")
+ @WifiBaseTest.wifi_test_wrap
+ 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/tests/google/wifi/WifiChannelSwitchStressTest.py b/acts_tests/tests/google/wifi/WifiChannelSwitchStressTest.py
new file mode 100644
index 0000000..bdba6e1
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiChannelSwitchStressTest.py
@@ -0,0 +1,308 @@
+#!/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
+import random
+import re
+import logging
+import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts.test_utils.tel.tel_test_utils as tel_utils
+import acts.utils as utils
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts import signals
+from acts.controllers import packet_capture
+from acts.controllers.ap_lib.hostapd_constants import BAND_2G
+from acts.controllers.ap_lib.hostapd_constants import BAND_5G
+
+
+WifiEnums = wutils.WifiEnums
+
+
+class WifiChannelSwitchStressTest(WifiBaseTest):
+
+ def setup_class(self):
+ super().setup_class()
+ self.dut = self.android_devices[0]
+ self.dut_client = self.android_devices[1]
+ utils.require_sl4a((self.dut, self.dut_client))
+
+ if hasattr(self, 'packet_capture'):
+ self.packet_capture = self.packet_capture[0]
+
+ req_params = ["dbs_supported_models"]
+ opt_param = ["stress_count", "cs_count"]
+ self.unpack_userparams(
+ req_param_names=req_params, opt_param_names=opt_param)
+
+ self.AP_IFACE = 'wlan0'
+ if self.dut.model in self.dbs_supported_models:
+ self.AP_IFACE = 'wlan1'
+
+ for ad in self.android_devices:
+ wutils.wifi_test_device_init(ad)
+ utils.sync_device_time(ad)
+ wutils.set_wifi_country_code(ad, WifiEnums.CountryCode.US)
+
+ def setup_test(self):
+ super().setup_test()
+ for ad in self.android_devices:
+ ad.droid.wakeLockAcquireBright()
+ ad.droid.wakeUpNow()
+ try:
+ if self.dut.droid.wifiIsApEnabled():
+ wutils.stop_wifi_tethering(self.dut)
+ except signals.TestFailure:
+ pass
+ wutils.wifi_toggle_state(self.dut_client, True)
+ init_sim_state = tel_utils.is_sim_ready(self.log, self.dut)
+ if init_sim_state:
+ self.check_cell_data_and_enable()
+ self.config = wutils.create_softap_config()
+ self.channel_list_2g = self.generate_random_list(
+ WifiEnums.ALL_2G_FREQUENCIES)
+ self.channel_list_5g = self.generate_random_list(
+ WifiEnums.NONE_DFS_5G_FREQUENCIES)
+
+ def teardown_test(self):
+ super().teardown_test()
+ for ad in self.android_devices:
+ ad.droid.wakeLockRelease()
+ ad.droid.goToSleepNow()
+ wutils.reset_wifi(ad)
+ try:
+ wutils.stop_wifi_tethering(self.dut)
+ except signals.TestFailure:
+ pass
+
+ def on_fail(self, test_name, begin_time):
+ try:
+ wutils.stop_pcap(self.packet_capture, self.pcap_procs, False)
+ except signals.TestFailure:
+ pass
+ super().on_fail(test_name, begin_time)
+
+ def check_cell_data_and_enable(self):
+ """Make sure that cell data is enabled if there is a sim present.
+
+ If a sim is active, cell data needs to be enabled to allow provisioning
+ checks through (when applicable). This is done to relax hardware
+ requirements on DUTs - without this check, running this set of tests
+ after other wifi tests may cause failures.
+ """
+ if not self.dut.droid.telephonyIsDataEnabled():
+ self.dut.log.info("need to enable data")
+ self.dut.droid.telephonyToggleDataConnection(True)
+ asserts.assert_true(self.dut.droid.telephonyIsDataEnabled(),
+ "Failed to enable cell data for dut.")
+
+ def get_wlan0_link(self, dut):
+ """ get wlan0 interface status"""
+ get_wlan0 = 'wpa_cli -iwlan0 -g@android:wpa_wlan0 IFNAME=wlan0 status'
+ out = dut.adb.shell(get_wlan0)
+ out = dict(re.findall(r'(\S+)=(".*?"|\S+)', out))
+ asserts.assert_true("ssid" in out,
+ "Client doesn't connect to any network")
+ return out
+
+ def get_wlan1_status(self, dut):
+ """ get wlan1 interface status"""
+ get_wlan1 = 'hostapd_cli status'
+ out_wlan1 = dut.adb.shell(get_wlan1)
+ out_wlan1 = dict(re.findall(r'(\S+)=(".*?"|\S+)', out_wlan1))
+ return out_wlan1
+
+ def generate_random_list(self, lst):
+ """Generate a list where
+ the previous and subsequent items
+ do not repeat"""
+ channel_list = []
+ num = random.choice(lst)
+ channel_list.append(num)
+ for i in range(1, self.stress_count):
+ num = random.choice(lst)
+ while num == channel_list[-1]:
+ num = random.choice(lst)
+ channel_list.append(num)
+ return channel_list
+
+ def conf_packet_capture(self, band, channel):
+ """Configure packet capture on necessary channels."""
+ freq_to_chan = wutils.WifiEnums.freq_to_channel[int(channel)]
+ logging.info("Capturing packets from "
+ "frequency:{}, Channel:{}".format(channel, freq_to_chan))
+ result = self.packet_capture.configure_monitor_mode(band, freq_to_chan)
+ if not result:
+ logging.error("Failed to configure channel "
+ "for {} band".format(band))
+ self.pcap_procs = wutils.start_pcap(
+ self.packet_capture, band, self.test_name)
+
+ time.sleep(5)
+
+ @test_tracker_info(uuid="b1a8b00e-eca8-4eba-99e9-c7a3beb2a009")
+ def test_softap_channel_switch_stress_2g(self):
+ """
+ 1. Disable DUT's Wi-Fi
+ 2. Enable CLIENT's Wi-Fi
+ 3. Check DUT's sim is ready or not
+ 4. Enable DUT's mobile data
+ 5. Bring up DUT's softap in 2g
+ 6. CLIENT connect to DUT
+ 7. DUT switch to different 2g channel
+ 8. Verify CLIENT follow the change
+ 9, Repeat step 7 and 8
+ """
+ wutils.start_wifi_tethering(self.dut,
+ self.config[wutils.WifiEnums.SSID_KEY],
+ self.config[wutils.WifiEnums.PWD_KEY],
+ WifiEnums.WIFI_CONFIG_APBAND_2G)
+ wutils.connect_to_wifi_network(self.dut_client, self.config)
+ time.sleep(10)
+ for count in range(len(self.channel_list_2g)):
+ self.dut.log.info("2g channel switch iteration : {}".format(count+1))
+ channel_2g = str(self.channel_list_2g[count])
+ # Configure sniffer before set SoftAP channel
+ if hasattr(self, 'packet_capture'):
+ self.conf_packet_capture(BAND_2G, channel_2g)
+ # Set SoftAP channel
+ wutils.set_softap_channel(self.dut,
+ self.AP_IFACE,
+ self.cs_count, channel_2g)
+ time.sleep(10)
+ softap_frequency = int(self.get_wlan1_status(self.dut)['freq'])
+ self.dut.log.info('softap frequency : {}'.format(softap_frequency))
+ client_frequency = int(self.get_wlan0_link(self.dut_client)["freq"])
+ self.dut_client.log.info(
+ "client frequency : {}".format(client_frequency))
+ asserts.assert_true(
+ softap_frequency == client_frequency,
+ "hotspot frequency != client frequency")
+ if hasattr(self, 'packet_capture'):
+ wutils.stop_pcap(self.packet_capture, self.pcap_procs, False)
+
+ @test_tracker_info(uuid="3411cb7c-2609-433a-97b6-202a096dc71b")
+ def test_softap_channel_switch_stress_5g(self):
+ """
+ 1. Disable DUT's Wi-Fi
+ 2. Enable CLIENT's Wi-Fi
+ 3. Check DUT's sim is ready or not
+ 4. Enable DUT's mobile data
+ 5. Bring up DUT's softap in 5g
+ 6. CLIENT connect to DUT
+ 7. DUT switch to different 5g channel
+ 8. Verify CLIENT follow the change
+ 9, Repeat step 7 and 8
+ """
+ wutils.start_wifi_tethering(self.dut,
+ self.config[wutils.WifiEnums.SSID_KEY],
+ self.config[wutils.WifiEnums.PWD_KEY],
+ WifiEnums.WIFI_CONFIG_APBAND_5G)
+ wutils.connect_to_wifi_network(self.dut_client, self.config)
+ time.sleep(10)
+ for count in range(len(self.channel_list_5g)):
+ self.dut.log.info("5g channel switch iteration : {}".format(count+1))
+ channel_5g = str(self.channel_list_5g[count])
+ # Configure sniffer before set SoftAP channel
+ if hasattr(self, 'packet_capture'):
+ self.conf_packet_capture(BAND_5G, channel_5g)
+ # Set SoftAP channel
+ wutils.set_softap_channel(self.dut,
+ self.AP_IFACE,
+ self.cs_count, channel_5g)
+ time.sleep(10)
+ softap_frequency = int(self.get_wlan1_status(self.dut)['freq'])
+ self.dut.log.info('softap frequency : {}'.format(softap_frequency))
+ client_frequency = int(self.get_wlan0_link(self.dut_client)["freq"])
+ self.dut_client.log.info(
+ "client frequency : {}".format(client_frequency))
+ asserts.assert_true(
+ softap_frequency == client_frequency,
+ "hotspot frequency != client frequency")
+ if hasattr(self, 'packet_capture'):
+ wutils.stop_pcap(self.packet_capture, self.pcap_procs, False)
+
+ @test_tracker_info(uuid="0f279058-119f-49fc-b8d6-fb2991cc35aa")
+ def test_softap_channel_switch_stress_2g_5g(self):
+ """
+ 1. Disable DUT's Wi-Fi
+ 2. Enable CLIENT's Wi-Fi
+ 3. Check DUT's sim is ready or not
+ 4. Enable DUT's mobile data
+ 5. Bring up DUT's softap in 2g
+ 6. CLIENT connect to DUT
+ 7. DUT switch to different 2g channel
+ 8. Verify CLIENT follow the change
+ 9. DUT switch to 5g channel
+ 10. Verify CLIENT follow the change
+ 11. Repeat step 7 to 10
+ """
+ for count in range(self.stress_count):
+ self.log.info("2g 5g channel switch iteration : {}".format(count+1))
+ wutils.start_wifi_tethering(self.dut,
+ self.config[wutils.WifiEnums.SSID_KEY],
+ self.config[wutils.WifiEnums.PWD_KEY],
+ WifiEnums.WIFI_CONFIG_APBAND_2G)
+ wutils.connect_to_wifi_network(self.dut_client, self.config)
+ self.log.info("wait 10 secs for client reconnect to dut")
+ time.sleep(10)
+ channel_2g = self.channel_list_2g[count]
+ if hasattr(self, 'packet_capture'):
+ self.conf_packet_capture(BAND_2G, channel_2g)
+ wutils.set_softap_channel(self.dut,
+ self.AP_IFACE,
+ self.cs_count, channel_2g)
+ time.sleep(10)
+ softap_frequency = int(self.get_wlan1_status(self.dut)['freq'])
+ self.dut.log.info('softap frequency : {}'.format(softap_frequency))
+ client_frequency = int(self.get_wlan0_link(self.dut_client)["freq"])
+ self.dut_client.log.info(
+ "client frequency : {}".format(client_frequency))
+ asserts.assert_true(
+ softap_frequency == client_frequency,
+ "hotspot frequency != client frequency")
+ wutils.stop_wifi_tethering(self.dut)
+ if hasattr(self, 'packet_capture'):
+ wutils.stop_pcap(self.packet_capture, self.pcap_procs, False)
+ self.dut.log.info('switch to SoftAP 5g')
+
+ # switch to SoftAp 5g
+ wutils.start_wifi_tethering(self.dut,
+ self.config[wutils.WifiEnums.SSID_KEY],
+ self.config[wutils.WifiEnums.PWD_KEY],
+ WifiEnums.WIFI_CONFIG_APBAND_5G)
+ wutils.connect_to_wifi_network(self.dut_client, self.config)
+ self.log.info("wait 10 secs for client reconnect to dut")
+ time.sleep(10)
+ channel_5g = self.channel_list_5g[count]
+ if hasattr(self, 'packet_capture'):
+ self.conf_packet_capture(BAND_5G, channel_5g)
+ wutils.set_softap_channel(self.dut,
+ self.AP_IFACE,
+ self.cs_count, channel_5g)
+ time.sleep(10)
+ softap_frequency = int(self.get_wlan1_status(self.dut)['freq'])
+ self.dut.log.info('softap frequency : {}'.format(softap_frequency))
+ client_frequency = int(self.get_wlan0_link(self.dut_client)["freq"])
+ self.dut_client.log.info(
+ "client frequency : {}".format(client_frequency))
+ asserts.assert_true(
+ softap_frequency == client_frequency,
+ "hotspot frequency != client frequency")
+ wutils.stop_wifi_tethering(self.dut)
+ if hasattr(self, 'packet_capture'):
+ wutils.stop_pcap(self.packet_capture, self.pcap_procs, False)
diff --git a/acts_tests/tests/google/wifi/WifiChaosTest.py b/acts_tests/tests/google/wifi/WifiChaosTest.py
new file mode 100755
index 0000000..77a83ec
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiChaosTest.py
@@ -0,0 +1,366 @@
+#!/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 re
+import sys
+import random
+import time
+
+import acts.controllers.packet_capture as packet_capture
+import acts.signals as signals
+import acts.test_utils.wifi.rpm_controller_utils as rutils
+import acts.test_utils.wifi.wifi_datastore_utils as dutils
+import acts.test_utils.wifi.wifi_test_utils as wutils
+
+from acts import asserts
+from acts.base_test import BaseTestClass
+from acts.controllers.ap_lib import hostapd_constants
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+WifiEnums = wutils.WifiEnums
+
+WAIT_BEFORE_CONNECTION = 1
+SINGLE_BAND = 1
+DUAL_BAND = 2
+
+TIMEOUT = 60
+TEST = 'test_'
+PING_ADDR = 'www.google.com'
+
+NUM_LINK_PROBES = 3
+PROBE_DELAY_SEC = 1
+
+
+class WifiChaosTest(WifiBaseTest):
+ """ Tests for wifi IOT
+
+ Test Bed Requirement:
+ * One Android device
+ * Wi-Fi IOT networks visible to the device
+ """
+
+ def __init__(self, configs):
+ BaseTestClass.__init__(self, configs)
+ self.generate_interop_tests()
+
+ def randomize_testcases(self):
+ """Randomize the list of hosts and build a random order of tests,
+ based on SSIDs, keeping all the relevant bands of an AP together.
+
+ """
+ temp_tests = list()
+ hosts = self.user_params['interop_host']
+
+ random.shuffle(hosts)
+
+ for host in hosts:
+ ssid_2g = None
+ ssid_5g = None
+ info = dutils.show_device(host)
+
+ # Based on the information in datastore, add to test list if
+ # AP has 2G band.
+ if 'ssid_2g' in info:
+ ssid_2g = info['ssid_2g']
+ temp_tests.append(TEST + ssid_2g)
+
+ # Based on the information in datastore, add to test list if
+ # AP has 5G band.
+ if 'ssid_5g' in info:
+ ssid_5g = info['ssid_5g']
+ temp_tests.append(TEST + ssid_5g)
+
+ self.tests = temp_tests
+
+ def generate_interop_testcase(self, base_test, testcase_name, ssid_dict):
+ """Generates a single test case from the given data.
+
+ Args:
+ base_test: The base test case function to run.
+ testcase_name: The name of the test case.
+ ssid_dict: The information about the network under test.
+ """
+ ssid = testcase_name
+ test_tracker_uuid = ssid_dict[testcase_name]['uuid']
+ hostname = ssid_dict[testcase_name]['host']
+ if not testcase_name.startswith('test_'):
+ testcase_name = 'test_%s' % testcase_name
+ test_case = test_tracker_info(uuid=test_tracker_uuid)(
+ lambda: base_test(ssid, hostname))
+ setattr(self, testcase_name, test_case)
+ self.tests.append(testcase_name)
+
+ def generate_interop_tests(self):
+ for ssid_dict in self.user_params['interop_ssid']:
+ testcase_name = list(ssid_dict)[0]
+ self.generate_interop_testcase(self.interop_base_test,
+ testcase_name, ssid_dict)
+ self.randomize_testcases()
+
+ def setup_class(self):
+ super().setup_class()
+ self.dut = self.android_devices[0]
+ self.admin = 'admin' + str(random.randint(1000001, 12345678))
+ wutils.wifi_test_device_init(self.dut)
+ # Set country code explicitly to "US".
+ wutils.set_wifi_country_code(self.dut, wutils.WifiEnums.CountryCode.US)
+
+ asserts.assert_true(
+ self.lock_pcap(),
+ "Could not lock a Packet Capture. Aborting Interop test.")
+
+ wutils.wifi_toggle_state(self.dut, True)
+
+ def lock_pcap(self):
+ """Lock a Packet Capturere to use for the test."""
+
+ # Get list of devices from the datastore.
+ locked_pcap = False
+ devices = dutils.get_devices()
+
+ for device in devices:
+
+ device_name = device['hostname']
+ device_type = device['ap_label']
+ if device_type == 'PCAP' and not device['lock_status']:
+ if dutils.lock_device(device_name, self.admin):
+ self.pcap_host = device_name
+ host = device['ip_address']
+ self.log.info("Locked Packet Capture device: %s" % device_name)
+ locked_pcap = True
+ break
+ else:
+ self.log.warning("Failed to lock %s PCAP." % device_name)
+
+ if not locked_pcap:
+ return False
+
+ pcap_config = {'ssh_config':{'user':'root'} }
+ pcap_config['ssh_config']['host'] = host
+
+ self.pcap = packet_capture.PacketCapture(pcap_config)
+ return True
+
+ def setup_test(self):
+ super().setup_test()
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+
+ def on_pass(self, test_name, begin_time):
+ wutils.stop_pcap(self.pcap, self.pcap_procs, True)
+
+ def on_fail(self, test_name, begin_time):
+ # Sleep to ensure all failed packets are captured.
+ time.sleep(5)
+ wutils.stop_pcap(self.pcap, self.pcap_procs, False)
+ super().on_fail(test_name, begin_time)
+
+ def teardown_test(self):
+ super().teardown_test()
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+
+ def teardown_class(self):
+ # Unlock the PCAP.
+ if not dutils.unlock_device(self.pcap_host):
+ self.log.warning("Failed to unlock %s PCAP. Check in datastore.")
+
+
+ """Helper Functions"""
+
+ def scan_and_connect_by_id(self, network, net_id):
+ """Scan for network and connect using network id.
+
+ Args:
+ net_id: Integer specifying the network id of the network.
+
+ """
+ ssid = network[WifiEnums.SSID_KEY]
+ wutils.start_wifi_connection_scan_and_ensure_network_found(self.dut,
+ ssid)
+ wutils.wifi_connect_by_id(self.dut, net_id)
+
+ def run_ping(self, sec):
+ """Run ping for given number of seconds.
+
+ Args:
+ sec: Time in seconds to run teh ping traffic.
+
+ """
+ self.log.info("Finding Gateway...")
+ route_response = self.dut.adb.shell("ip route get 8.8.8.8")
+ gateway_ip = re.search('via (.*) dev', str(route_response)).group(1)
+ self.log.info("Gateway IP = %s" % gateway_ip)
+ self.log.info("Running ping for %d seconds" % sec)
+ result = self.dut.adb.shell("ping -w %d %s" % (sec, gateway_ip),
+ timeout=sec + 1)
+ self.log.debug("Ping Result = %s" % result)
+ if "100% packet loss" in result:
+ raise signals.TestFailure("100% packet loss during ping")
+
+ def send_link_probes(self, network):
+ """
+ Send link probes, and verify that the device and AP did not crash.
+ Also verify that at least one link probe succeeded.
+
+ Steps:
+ 1. Send a few link probes.
+ 2. Ensure that the device and AP did not crash (by checking that the
+ device remains connected to the expected network).
+ """
+ results = wutils.send_link_probes(
+ self.dut, NUM_LINK_PROBES, PROBE_DELAY_SEC)
+
+ self.log.info("Link Probe results: %s" % (results,))
+
+ wifi_info = self.dut.droid.wifiGetConnectionInfo()
+ expected = network[WifiEnums.SSID_KEY]
+ actual = wifi_info[WifiEnums.SSID_KEY]
+ asserts.assert_equal(
+ expected, actual,
+ "Device did not remain connected after sending link probes!")
+
+ def unlock_and_turn_off_ap(self, hostname, rpm_port, rpm_ip):
+ """UNlock the AP in datastore and turn off the AP.
+
+ Args:
+ hostname: Hostname of the AP.
+ rpm_port: Port number on the RPM for the AP.
+ rpm_ip: RPM's IP address.
+
+ """
+ # Un-Lock AP in datastore.
+ self.log.debug("Un-lock AP in datastore")
+ if not dutils.unlock_device(hostname):
+ self.log.warning("Failed to unlock %s AP. Check AP in datastore.")
+ # Turn OFF AP from the RPM port.
+ rutils.turn_off_ap(rpm_port, rpm_ip)
+
+ def run_connect_disconnect(self, network, hostname, rpm_port, rpm_ip,
+ release_ap):
+ """Run connect/disconnect to a given network in loop.
+
+ Args:
+ network: Dict, network information.
+ hostname: Hostanme of the AP to connect to.
+ rpm_port: Port number on the RPM for the AP.
+ rpm_ip: Port number on the RPM for the AP.
+ release_ap: Flag to determine if we should turn off the AP yet.
+
+ Raises: TestFailure if the network connection fails.
+
+ """
+ for attempt in range(5):
+ try:
+ begin_time = time.time()
+ ssid = network[WifiEnums.SSID_KEY]
+ net_id = self.dut.droid.wifiAddNetwork(network)
+ asserts.assert_true(net_id != -1, "Add network %s failed" % network)
+ self.log.info("Connecting to %s" % ssid)
+ self.scan_and_connect_by_id(network, net_id)
+ self.run_ping(10)
+ # TODO(b/133369482): uncomment once bug is resolved
+ # self.send_link_probes(network)
+ wutils.wifi_forget_network(self.dut, ssid)
+ time.sleep(WAIT_BEFORE_CONNECTION)
+ except Exception as e:
+ self.log.error("Connection to %s network failed on the %d "
+ "attempt with exception %s." % (ssid, attempt, e))
+ # TODO:(bmahadev) Uncomment after scan issue is fixed.
+ # self.dut.take_bug_report(ssid, begin_time)
+ # self.dut.cat_adb_log(ssid, begin_time)
+ if release_ap:
+ self.unlock_and_turn_off_ap(hostname, rpm_port, rpm_ip)
+ raise signals.TestFailure("Failed to connect to %s" % ssid)
+
+ def get_band_and_chan(self, ssid):
+ """Get the band and channel information from SSID.
+
+ Args:
+ ssid: SSID of the network.
+
+ """
+ ssid_info = ssid.split('_')
+ self.band = ssid_info[-1]
+ for item in ssid_info:
+ # Skip over the router model part.
+ if 'ch' in item and item != ssid_info[0]:
+ self.chan = re.search(r'(\d+)',item).group(0)
+ return
+ raise signals.TestFailure("Channel information not found in SSID.")
+
+ def interop_base_test(self, ssid, hostname):
+ """Base test for all the connect-disconnect interop tests.
+
+ Args:
+ ssid: string, SSID of the network to connect to.
+ hostname: string, hostname of the AP.
+
+ Steps:
+ 1. Lock AP in datstore.
+ 2. Turn on AP on the rpm switch.
+ 3. Run connect-disconnect in loop.
+ 4. Turn off AP on the rpm switch.
+ 5. Unlock AP in datastore.
+
+ """
+ network = {}
+ network['password'] = 'password'
+ network['SSID'] = ssid
+ release_ap = False
+ wutils.reset_wifi(self.dut)
+
+ # Lock AP in datastore.
+ self.log.info("Lock AP in datastore")
+
+ ap_info = dutils.show_device(hostname)
+
+ # If AP is locked by a different test admin, then we skip.
+ if ap_info['lock_status'] and ap_info['locked_by'] != self.admin:
+ raise signals.TestSkip("AP %s is locked, skipping test" % hostname)
+
+ if not dutils.lock_device(hostname, self.admin):
+ self.log.warning("Failed to lock %s AP. Unlock AP in datastore"
+ " and try again.")
+ raise signals.TestFailure("Failed to lock AP")
+
+ band = SINGLE_BAND
+ if ('ssid_2g' in ap_info) and ('ssid_5g' in ap_info):
+ band = DUAL_BAND
+ if (band == SINGLE_BAND) or (
+ band == DUAL_BAND and '5G' in ssid):
+ release_ap = True
+
+ # Get AP RPM attributes and Turn ON AP.
+ rpm_ip = ap_info['rpm_ip']
+ rpm_port = ap_info['rpm_port']
+
+ rutils.turn_on_ap(self.pcap, ssid, rpm_port, rpm_ip=rpm_ip)
+ self.log.info("Finished turning ON AP.")
+ # Experimental. Some APs take upto a min to come online.
+ time.sleep(60)
+
+ self.get_band_and_chan(ssid)
+ self.pcap.configure_monitor_mode(self.band, self.chan)
+ self.pcap_procs = wutils.start_pcap(
+ self.pcap, self.band.lower(), self.test_name)
+ self.run_connect_disconnect(network, hostname, rpm_port, rpm_ip,
+ release_ap)
+
+ # Un-lock only if it's a single band AP or we are running the last band.
+ if release_ap:
+ self.unlock_and_turn_off_ap(hostname, rpm_port, rpm_ip)
diff --git a/acts_tests/tests/google/wifi/WifiConnectedMacRandomizationTest.py b/acts_tests/tests/google/wifi/WifiConnectedMacRandomizationTest.py
new file mode 100644
index 0000000..14eafab
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiConnectedMacRandomizationTest.py
@@ -0,0 +1,256 @@
+#!/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
+from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts.utils as utils
+
+from acts import asserts
+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
+GET_MAC_ADDRESS= ("ip addr show wlan0"
+ "| grep 'link/ether'"
+ "| cut -d ' ' -f6")
+MAC_SETTING = "wifi_connected_mac_randomization_enabled"
+GET_MAC_RANDOMIZATION_STATUS = "settings get global {}".format(MAC_SETTING)
+TURN_ON_MAC_RANDOMIZATION = "settings put global {} 1".format(MAC_SETTING)
+TURN_OFF_MAC_RANDOMIZATION = "settings put global {} 0".format(MAC_SETTING)
+LOG_CLEAR = "logcat -c"
+LOG_GREP = "logcat -d | grep {}"
+
+class WifiConnectedMacRandomizationTest(WifiBaseTest):
+ """Tests for Connected MAC Randomization.
+
+ Test Bed Requirement:
+ * Two Android devices with the first one supporting MAC randomization.
+ * At least two Wi-Fi networks to connect to.
+ """
+
+ def setup_class(self):
+ super().setup_class()
+
+ self.dut = self.android_devices[0]
+ self.dut_softap = self.android_devices[1]
+ wutils.wifi_test_device_init(self.dut)
+ wutils.wifi_test_device_init(self.dut_softap)
+
+ self.reset_mac_address_to_factory_mac()
+ self.dut.adb.shell(TURN_ON_MAC_RANDOMIZATION)
+ asserts.assert_equal(
+ self.dut.adb.shell(GET_MAC_RANDOMIZATION_STATUS), "1",
+ "Failed to enable Connected MAC Randomization on dut.")
+
+ req_params = ["reference_networks"]
+ opt_param = []
+ 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(
+ self.reference_networks[0]["2g"],
+ "Need at least 1 2.4Ghz reference network with psk.")
+ asserts.assert_true(
+ self.reference_networks[0]["5g"],
+ "Need at least 1 5Ghz reference network with psk.")
+ self.wpapsk_2g = self.reference_networks[0]["2g"]
+ self.wpapsk_5g = self.reference_networks[0]["5g"]
+
+ def setup_test(self):
+ super().setup_test()
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ wutils.wifi_toggle_state(self.dut, True)
+ wutils.wifi_toggle_state(self.dut_softap, False)
+
+ def teardown_test(self):
+ super().teardown_test()
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ wutils.reset_wifi(self.dut)
+ wutils.reset_wifi(self.dut_softap)
+
+ def teardown_class(self):
+ wutils.stop_wifi_tethering(self.dut_softap)
+ self.reset_mac_address_to_factory_mac()
+ if "AccessPoint" in self.user_params:
+ del self.user_params["reference_networks"]
+ del self.user_params["open_network"]
+
+ """Helper Functions"""
+ def get_current_mac_address(self, ad):
+ """Get the device's wlan0 MAC address.
+
+ Args:
+ ad: AndroidDevice to get MAC address of.
+
+ Returns:
+ A MAC address string in the format of "12:34:56:78:90:12".
+ """
+ return ad.adb.shell(GET_MAC_ADDRESS)
+
+ def is_valid_randomized_mac_address(self, mac):
+ """Check if the given MAC address is a valid randomized MAC address.
+
+ Args:
+ mac: MAC address to check in the format of "12:34:56:78:90:12".
+ """
+ asserts.assert_true(
+ mac != self.dut_factory_mac,
+ "Randomized MAC address is same as factory MAC address.")
+ first_byte = int(mac[:2], 16)
+ asserts.assert_equal(first_byte & 1, 0, "MAC address is not unicast.")
+ asserts.assert_equal(first_byte & 2, 2, "MAC address is not local.")
+
+ def reset_mac_address_to_factory_mac(self):
+ """Reset dut to and store factory MAC address by turning off
+ Connected MAC Randomization and rebooting dut.
+ """
+ self.dut.adb.shell(TURN_OFF_MAC_RANDOMIZATION)
+ asserts.assert_equal(
+ self.dut.adb.shell(GET_MAC_RANDOMIZATION_STATUS), "0",
+ "Failed to disable Connected MAC Randomization on dut.")
+ self.dut.reboot()
+ time.sleep(DEFAULT_TIMEOUT)
+ self.dut_factory_mac = self.get_current_mac_address(self.dut)
+
+ def get_connection_data(self, ad, network):
+ """Connect and get network id and ssid info from connection data.
+
+ Args:
+ ad: AndroidDevice to use for connection
+ network: network info of the network to connect to
+
+ Returns:
+ A convenience dict with the connected network's ID and SSID.
+ """
+ wutils.connect_to_wifi_network(ad, network)
+ connect_data = ad.droid.wifiGetConnectionInfo()
+ ssid_id_dict = dict()
+ ssid_id_dict[WifiEnums.NETID_KEY] = connect_data[WifiEnums.NETID_KEY]
+ ssid_id_dict[WifiEnums.SSID_KEY] = connect_data[WifiEnums.SSID_KEY]
+ return ssid_id_dict
+
+ """Tests"""
+ @test_tracker_info(uuid="")
+ def test_wifi_connection_2G_with_mac_randomization(self):
+ """Tests connection to 2G network with Connected MAC Randomization.
+ """
+ wutils.connect_to_wifi_network(self.dut, self.wpapsk_2g)
+ mac = self.get_current_mac_address(self.dut)
+ self.is_valid_randomized_mac_address(mac)
+
+ @test_tracker_info(uuid="")
+ def test_wifi_connection_5G_with_mac_randomization(self):
+ """Tests connection to 5G network with Connected MAC Randomization.
+ """
+ wutils.connect_to_wifi_network(self.dut, self.wpapsk_5g)
+ mac = self.get_current_mac_address(self.dut)
+ self.is_valid_randomized_mac_address(mac)
+
+ @test_tracker_info(uuid="")
+ def test_randomized_mac_persistent_between_connections(self):
+ """Tests that randomized MAC address assigned to each network is
+ persistent between connections.
+
+ Steps:
+ 1. Connect to a 2GHz network.
+ 2. Connect to a 5GHz network.
+ 3. Reconnect to the 2GHz network using its network id.
+ 4. Verify that MAC addresses in Steps 1 and 3 are equal.
+ 5. Reconnect to the 5GHz network using its network id.
+ 6. Verify that MAC addresses in Steps 2 and 5 are equal.
+ """
+ connect_data_2g = self.get_connection_data(self.dut, self.wpapsk_2g)
+ old_mac_2g = self.get_current_mac_address(self.dut)
+ self.is_valid_randomized_mac_address(old_mac_2g)
+
+ connect_data_5g = self.get_connection_data(self.dut, self.wpapsk_5g)
+ old_mac_5g = self.get_current_mac_address(self.dut)
+ self.is_valid_randomized_mac_address(old_mac_5g)
+
+ asserts.assert_true(
+ old_mac_2g != old_mac_5g,
+ "Randomized MAC addresses for 2G and 5G networks are equal.")
+
+ reconnect_2g = wutils.connect_to_wifi_network_with_id(
+ self.dut,
+ connect_data_2g[WifiEnums.NETID_KEY],
+ connect_data_2g[WifiEnums.SSID_KEY])
+ if not reconnect_2g:
+ raise signals.TestFailure("Device did not connect to the correct"
+ " 2GHz network.")
+ new_mac_2g = self.get_current_mac_address(self.dut)
+ asserts.assert_equal(
+ old_mac_2g,
+ new_mac_2g,
+ "Randomized MAC for 2G is not persistent between connections.")
+
+ reconnect_5g = wutils.connect_to_wifi_network_with_id(
+ self.dut,
+ connect_data_5g[WifiEnums.NETID_KEY],
+ connect_data_5g[WifiEnums.SSID_KEY])
+ if not reconnect_5g:
+ raise signals.TestFailure("Device did not connect to the correct"
+ " 5GHz network.")
+ new_mac_5g = self.get_current_mac_address(self.dut)
+ asserts.assert_equal(
+ old_mac_5g,
+ new_mac_5g,
+ "Randomized MAC for 5G is not persistent between connections.")
+
+ @test_tracker_info(uuid="")
+ def test_randomized_mac_used_during_connection(self):
+ """Verify that the randomized MAC address and not the factory
+ MAC address is used during connection by checking the softap logs.
+
+ Steps:
+ 1. Set up softAP on dut_softap.
+ 2. Have dut connect to the softAp.
+ 3. Verify that only randomized MAC appears in softAp logs.
+ """
+ self.dut_softap.adb.shell(LOG_CLEAR)
+ config = wutils.create_softap_config()
+ wutils.start_wifi_tethering(self.dut_softap,
+ config[wutils.WifiEnums.SSID_KEY],
+ config[wutils.WifiEnums.PWD_KEY],
+ WIFI_CONFIG_APBAND_2G)
+
+ # Internet validation fails when dut_softap does not have a valid sim
+ # supporting softap. Since this test is not checking for internet
+ # validation, we suppress failure signals.
+ wutils.connect_to_wifi_network(self.dut, config, assert_on_fail=False)
+ mac = self.get_current_mac_address(self.dut)
+ wutils.stop_wifi_tethering(self.dut_softap)
+
+ self.is_valid_randomized_mac_address(mac)
+ log = self.dut_softap.adb.shell(LOG_GREP.format(mac))
+ asserts.assert_true(len(log) > 0, "Randomized MAC not in log.")
+ log = self.dut_softap.adb.shell(LOG_GREP.format(self.dut_factory_mac))
+ asserts.assert_true(len(log) == 0, "Factory MAC is in log.")
diff --git a/acts_tests/tests/google/wifi/WifiCrashStressTest.py b/acts_tests/tests/google/wifi/WifiCrashStressTest.py
new file mode 100644
index 0000000..503583c
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiCrashStressTest.py
@@ -0,0 +1,209 @@
+#!/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 time
+import acts.signals as signals
+import acts.test_utils.wifi.wifi_test_utils as wutils
+from acts import asserts
+from acts import utils
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts.test_utils.tel.tel_test_utils import disable_qxdm_logger
+
+WifiEnums = wutils.WifiEnums
+
+class WifiCrashStressTest(WifiBaseTest):
+ """Crash Tests for wifi stack.
+
+ Test Bed Requirement:
+ * Two Android device
+ * One Wi-Fi network visible to the device.
+ """
+
+ def __init__(self, configs):
+ super().__init__(configs)
+ self.enable_packet_log = True
+
+ def setup_class(self):
+ super().setup_class()
+
+ 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)
+ req_params = ["dbs_supported_models", "stress_count"]
+ opt_param = ["reference_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()
+
+ asserts.assert_true(
+ len(self.reference_networks) > 0,
+ "Need at least one reference network with psk.")
+ self.network = self.reference_networks[0]["2g"]
+ self.ap_iface = 'wlan0'
+ if self.dut.model in self.dbs_supported_models:
+ self.ap_iface = 'wlan1'
+
+ def setup_test(self):
+ super().setup_test()
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ wutils.wifi_toggle_state(self.dut, True)
+ self.dut_client.droid.wakeLockAcquireBright()
+ self.dut_client.droid.wakeUpNow()
+ wutils.wifi_toggle_state(self.dut_client, True)
+
+ def teardown_test(self):
+ super().teardown_test()
+ if self.dut.droid.wifiIsApEnabled():
+ wutils.stop_wifi_tethering(self.dut)
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ wutils.reset_wifi(self.dut)
+ self.dut_client.droid.wakeLockRelease()
+ self.dut_client.droid.goToSleepNow()
+ wutils.reset_wifi(self.dut_client)
+
+ def teardown_class(self):
+ if "AccessPoint" in self.user_params:
+ del self.user_params["reference_networks"]
+
+ """Helper Functions"""
+ def trigger_wifi_firmware_crash(self, ad, timeout=15):
+ pre_timestamp = ad.adb.getprop("vendor.debug.ssrdump.timestamp")
+ disable_qxdm_logger(ad)
+ cmd = ('wlanSSR')
+ ad.log.info("Crash wifi firmware by %s", cmd)
+ ad.adb.shell(cmd, ignore_status=True)
+ time.sleep(timeout) # sleep time for firmware restart
+ subsystem = ad.adb.getprop("vendor.debug.ssrdump.subsys")
+ timestamp = ad.adb.getprop("vendor.debug.ssrdump.timestamp")
+ asserts.assert_true(timestamp != pre_timestamp,
+ "SSR didn't happened %s %s" % (subsystem, timestamp))
+
+ """Tests"""
+ @test_tracker_info(uuid="b5a982ef-10ef-4f36-a1b5-1e5d1fec06a4")
+ def test_firmware_crash_wifi_reconnect_stress(self):
+ """Firmware crash stress test for station mode
+
+ 1. Turn on Wi-Fi and connect to access point
+ 2. Trigger firmware crash
+ 3. Check ssr happened
+ 4. Check dut can connect to access point
+ 5. Repeat step 2~4
+ """
+ wutils.wifi_toggle_state(self.dut, True)
+ wutils.connect_to_wifi_network(self.dut, self.network)
+ for count in range(self.stress_count):
+ self.log.info("%s: %d/%d" %
+ (self.current_test_name, count + 1, self.stress_count))
+ wutils.reset_wifi(self.dut)
+ self.trigger_wifi_firmware_crash(self.dut)
+ wutils.connect_to_wifi_network(self.dut, self.network)
+
+ @test_tracker_info(uuid="204a921b-b0de-47f7-9b70-9384317051c8")
+ def test_firmware_crash_softap_reconnect_stress(self):
+ """Firmware crash stress test for softap mode
+
+ 1. Turn off dut's Wi-Fi
+ 2. Turn on dut's hotspot and connected by dut client
+ 3. Trigger firmware crash
+ 4. Check ssr happened
+ 5. Check the connectivity of hotspot's client
+ 6. Repeat step 3~5
+ """
+ wutils.wifi_toggle_state(self.dut, False)
+ # Setup Soft AP
+ sap_config = wutils.create_softap_config()
+ wutils.start_wifi_tethering(
+ self.dut, sap_config[wutils.WifiEnums.SSID_KEY],
+ sap_config[wutils.WifiEnums.PWD_KEY], wutils.WifiEnums.WIFI_CONFIG_APBAND_2G)
+ config = {
+ "SSID": sap_config[wutils.WifiEnums.SSID_KEY],
+ "password": sap_config[wutils.WifiEnums.PWD_KEY]
+ }
+ # DUT client connects to softap
+ wutils.wifi_toggle_state(self.dut_client, True)
+ wutils.connect_to_wifi_network(self.dut_client, config, check_connectivity=False)
+ # Ping the DUT
+ dut_addr = self.dut.droid.connectivityGetIPv4Addresses(
+ self.ap_iface)[0]
+ asserts.assert_true(
+ utils.adb_shell_ping(self.dut_client, count=10, dest_ip=dut_addr, timeout=20),
+ "%s ping %s failed" % (self.dut_client.serial, dut_addr))
+ for count in range(self.stress_count):
+ self.log.info("%s: %d/%d" %
+ (self.current_test_name, count + 1, self.stress_count))
+ wutils.reset_wifi(self.dut_client)
+ # Trigger firmware crash
+ self.trigger_wifi_firmware_crash(self.dut)
+ # Connect DUT to Network
+ wutils.connect_to_wifi_network(self.dut_client, config, check_connectivity=False)
+ # Ping the DUT
+ server_addr = self.dut.droid.connectivityGetIPv4Addresses(
+ self.ap_iface)[0]
+ asserts.assert_true(
+ utils.adb_shell_ping(
+ self.dut_client,
+ count=10,
+ dest_ip=server_addr,
+ timeout=20),
+ "%s ping %s failed" % (self.dut_client.serial, server_addr))
+ wutils.stop_wifi_tethering(self.dut)
+
+ @test_tracker_info(uuid="4b7f2d89-82be-41de-9277-e938cc1c318b")
+ def test_firmware_crash_concurrent_reconnect_stress(self):
+ """Firmware crash stress test for concurrent mode
+
+ 1. Turn on dut's Wi-Fi and connect to access point
+ 2. Turn on dut's hotspot and connected by dut client
+ 3. Trigger firmware crash
+ 4. Check ssr happened
+ 5. Check dut can connect to access point
+ 6. Check the connectivity of hotspot's client
+ 7. Repeat step 3~6
+ """
+ if self.dut.model not in self.dbs_supported_models:
+ raise signals.TestSkip("%s does not support dual interfaces" % self.dut.model)
+
+ # Connect DUT to Network
+ wutils.wifi_toggle_state(self.dut, True)
+ wutils.connect_to_wifi_network(self.dut, self.network)
+ # Setup Soft AP
+ sap_config = wutils.create_softap_config()
+ wutils.start_wifi_tethering(
+ self.dut, sap_config[wutils.WifiEnums.SSID_KEY],
+ sap_config[wutils.WifiEnums.PWD_KEY], wutils.WifiEnums.WIFI_CONFIG_APBAND_2G)
+ config = {
+ "SSID": sap_config[wutils.WifiEnums.SSID_KEY],
+ "password": sap_config[wutils.WifiEnums.PWD_KEY]
+ }
+ # Client connects to Softap
+ wutils.wifi_toggle_state(self.dut_client, True)
+ wutils.connect_to_wifi_network(self.dut_client, config)
+ for count in range(self.stress_count):
+ self.log.info("%s: %d/%d" %
+ (self.current_test_name, count + 1, self.stress_count))
+ wutils.reset_wifi(self.dut_client)
+ wutils.reset_wifi(self.dut)
+ # Trigger firmware crash
+ self.trigger_wifi_firmware_crash(self.dut)
+ wutils.connect_to_wifi_network(self.dut, self.network)
+ wutils.connect_to_wifi_network(self.dut_client, config)
+ wutils.stop_wifi_tethering(self.dut)
diff --git a/acts_tests/tests/google/wifi/WifiCrashTest.py b/acts_tests/tests/google/wifi/WifiCrashTest.py
new file mode 100644
index 0000000..7f2ad68
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiCrashTest.py
@@ -0,0 +1,178 @@
+#!/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.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+WifiEnums = wutils.WifiEnums
+# 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"
+
+class WifiCrashTest(WifiBaseTest):
+ """Crash Tests for wifi stack.
+
+ Test Bed Requirement:
+ * One Android device
+ * One Wi-Fi network visible to the device.
+ """
+ def __init__(self, configs):
+ super().__init__(configs)
+ self.enable_packet_log = True
+
+ def setup_class(self):
+ super().setup_class()
+
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ req_params = []
+ opt_param = ["reference_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()
+ 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.")
+ self.network = self.reference_networks[0]["2g"]
+
+ def setup_test(self):
+ super().setup_test()
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ wutils.wifi_toggle_state(self.dut, True)
+
+ def teardown_test(self):
+ super().teardown_test()
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ wutils.reset_wifi(self.dut)
+
+ def teardown_class(self):
+ if "AccessPoint" in self.user_params:
+ del self.user_params["reference_networks"]
+
+ """Helper Functions"""
+
+ """Tests"""
+ @test_tracker_info(uuid="b87fd23f-9bfc-406b-a5b2-17ce6be6c780")
+ def test_wifi_framework_crash_reconnect(self):
+ """Connect to a network, crash framework, then ensure
+ we connect back to the previously connected network.
+
+ Steps:
+ 1. Connect to a network.
+ 2. Restart framework.
+ 3. Reconnect to the previous network.
+
+ """
+ wutils.wifi_connect(self.dut, self.network, num_of_tries=3)
+ # Restart framework
+ self.log.info("Crashing framework")
+ self.dut.restart_runtime()
+ # We won't get the disconnect broadcast because framework crashed.
+ # wutils.wait_for_disconnect(self.dut)
+ 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"
+ " network after crashing framework.")
+
+ @test_tracker_info(uuid="33f9e4f6-29b8-4116-8f9b-5b13d93b4bcb")
+ def test_wifi_cond_crash_reconnect(self):
+ """Connect to a network, crash wificond, then ensure
+ we connect back to the previously connected network.
+
+ Steps:
+ 1. Connect to a network.
+ 2. Crash wificond.
+ 3. Ensure we get a disconnect.
+ 4. Ensure we reconnect to the previous network.
+
+ """
+ wutils.wifi_connect(self.dut, self.network, num_of_tries=3)
+ # Restart wificond
+ self.log.info("Crashing wificond")
+ self.dut.adb.shell(WIFICOND_KILL_SHELL_COMMAND)
+ wutils.wait_for_disconnect(self.dut)
+ 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"
+ " network after crashing wificond.")
+
+ @test_tracker_info(uuid="463e3d7b-b0b7-4843-b83b-5613a71ae2ac")
+ def test_wifi_vendorhal_crash_reconnect(self):
+ """Connect to a network, crash wifi HAL, then ensure
+ we connect back to the previously connected network.
+
+ Steps:
+ 1. Connect to a network.
+ 2. Crash wifi HAL.
+ 3. Ensure we get a disconnect.
+ 4. Ensure we reconnect to the previous network.
+
+ """
+ wutils.wifi_connect(self.dut, self.network, num_of_tries=3)
+ # Restart wificond
+ self.log.info("Crashing wifi HAL")
+ self.dut.adb.shell(WIFI_VENDOR_HAL_KILL_SHELL_COMMAND)
+ wutils.wait_for_disconnect(self.dut)
+ 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"
+ " network after crashing wifi HAL.")
+
+ @test_tracker_info(uuid="7c5cd1fc-8f8d-494c-beaf-4eb61b48917b")
+ def test_wpa_supplicant_crash_reconnect(self):
+ """Connect to a network, crash wpa_supplicant, then ensure
+ we connect back to the previously connected network.
+
+ Steps:
+ 1. Connect to a network.
+ 2. Crash wpa_supplicant.
+ 3. Ensure we get a disconnect.
+ 4. Ensure we reconnect to the previous network.
+
+ """
+ wutils.wifi_connect(self.dut, self.network, num_of_tries=3)
+ # Restart wificond
+ self.log.info("Crashing wpa_supplicant")
+ self.dut.adb.shell(SUPPLICANT_KILL_SHELL_COMMAND)
+ wutils.wait_for_disconnect(self.dut)
+ 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"
+ " network after crashing wpa_supplicant.")
diff --git a/acts_tests/tests/google/wifi/WifiDiagnosticsTest.py b/acts_tests/tests/google/wifi/WifiDiagnosticsTest.py
new file mode 100644
index 0000000..9e5a46f
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiDiagnosticsTest.py
@@ -0,0 +1,98 @@
+#!/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.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+WifiEnums = wutils.WifiEnums
+
+DEFAULT_WAIT_TIME = 2
+
+
+class WifiDiagnosticsTest(WifiBaseTest):
+ """
+ Test Bed Requirement:
+ * One Android device
+ * An open Wi-Fi network.
+ * Verbose logging is on.
+ """
+
+ 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"]
+ 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()
+ wutils.wifi_toggle_state(self.dut, True)
+ asserts.assert_true(
+ len(self.open_network) > 0,
+ "Need at least one open network.")
+ self.open_network = self.open_network[0]["2g"]
+
+ def setup_test(self):
+ super().setup_test()
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+
+ def teardown_test(self):
+ super().teardown_test()
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ wutils.reset_wifi(self.dut)
+
+ def teardown_class(self):
+ if "AccessPoint" in self.user_params:
+ del self.user_params["open_network"]
+
+ """Tests"""
+
+ @test_tracker_info(uuid="d6f1661b-6732-4939-8c28-f20917774ec0")
+ def test_ringbuffers_are_dumped_during_lsdebug(self):
+ """Steps:
+ 1. Connect to a open network.
+ 2. Delete old files under data/vendor/tombstones/wifi
+ 3. Call lshal debug on wifi hal component
+ 4. Verify that files are created under data/vender/tombstones/wifi
+ """
+ wutils.connect_to_wifi_network(self.dut, self.open_network)
+ time.sleep(DEFAULT_WAIT_TIME)
+ self.dut.adb.shell("rm data/vendor/tombstones/wifi/*")
+ try:
+ self.dut.adb.shell("lshal debug android.hardware.wifi@1.2::IWifi")
+ except UnicodeDecodeError:
+ """ Gets this error because adb.shell trys to parse the output to a string
+ but ringbuffer dumps should already be generated """
+ self.log.info("Unicode decode error occurred, but this is ok")
+ file_count_plus_one = self.dut.adb.shell("ls -l data/vendor/tombstones/wifi | wc -l")
+ if int(file_count_plus_one) <= 1:
+ raise signals.TestFailure("Failed to create ringbuffer debug files.")
\ No newline at end of file
diff --git a/acts_tests/tests/google/wifi/WifiDppTest.py b/acts_tests/tests/google/wifi/WifiDppTest.py
new file mode 100644
index 0000000..64f7f25
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiDppTest.py
@@ -0,0 +1,918 @@
+#!/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 binascii
+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_constants
+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_test_utils as autils
+
+class WifiDppTest(WifiBaseTest):
+ """This class tests the DPP API surface.
+
+ Attributes: The tests in this class require one DUT and one helper phone
+ device.
+ The tests in this class do not require a SIM.
+ """
+
+ DPP_TEST_TIMEOUT = 60
+ DPP_TEST_SSID_PREFIX = "dpp_test_ssid_"
+ DPP_TEST_SECURITY_SAE = "SAE"
+ DPP_TEST_SECURITY_PSK_PASSPHRASE = "PSK_PASSPHRASE"
+ DPP_TEST_SECURITY_PSK = "PSK"
+
+ DPP_TEST_EVENT_DPP_CALLBACK = "onDppCallback"
+ DPP_TEST_EVENT_DATA = "data"
+ DPP_TEST_EVENT_ENROLLEE_SUCCESS = "onEnrolleeSuccess"
+ DPP_TEST_EVENT_CONFIGURATOR_SUCCESS = "onConfiguratorSuccess"
+ DPP_TEST_EVENT_PROGRESS = "onProgress"
+ DPP_TEST_EVENT_FAILURE = "onFailure"
+ 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 testing.
+ """
+ super().setup_class()
+ # Device 0 is under test. Device 1 performs the responder role
+ self.dut = self.android_devices[0]
+ self.helper_dev = self.android_devices[1]
+
+ # Do a simple version of init - mainly just sync the time and enable
+ # verbose logging. We would also like to test with phones in less
+ # constrained states (or add variations where we specifically
+ # constrain).
+ 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,
+ "Failed to enable WiFi verbose logging on the dut.")
+
+ def teardown_class(self):
+ wutils.reset_wifi(self.dut)
+
+ 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.
+ """
+ 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 = {
+ wutils.WifiEnums.SSID_KEY: config_ssid,
+ wutils.WifiEnums.PWD_KEY: config_password,
+ wutils.WifiEnums.SECURITY: security
+ }
+
+ # Now save the config.
+ network_id = self.dut.droid.wifiAddNetwork(config)
+ self.dut.log.info("saved config: network_id = %d" % network_id)
+ return network_id
+
+ def check_network_config_saved(self, expected_ssid, security, network_id):
+ """ Get the configured networks and check if the provided network ID is present.
+
+ Args:
+ expected_ssid: Expected SSID to match with received configuration.
+ security: Security type to match, PSK or SAE
+
+ Returns:
+ True if the WifiConfig is present.
+ """
+ networks = self.dut.droid.wifiGetConfiguredNetworks()
+ if not networks:
+ return False
+
+ # Normalize PSK and PSK Passphrase to PSK
+ if security == self.DPP_TEST_SECURITY_PSK_PASSPHRASE:
+ security = self.DPP_TEST_SECURITY_PSK
+
+ # If the device doesn't support SAE, then the test fallbacks to PSK
+ if not self.dut.droid.wifiIsWpa3SaeSupported() and \
+ security == self.DPP_TEST_SECURITY_SAE:
+ security = self.DPP_TEST_SECURITY_PSK
+
+ for network in networks:
+ if network_id == network['networkId'] and \
+ security == network[wutils.WifiEnums.SECURITY] and \
+ expected_ssid == network[wutils.WifiEnums.SSID_KEY]:
+ self.log.info("Found SSID %s" % network[wutils.WifiEnums.SSID_KEY])
+ return True
+ return False
+
+ def forget_network(self, network_id):
+ """ Simple method to call wifiForgetNetwork and wait for confirmation callback.
+
+ Returns:
+ True if network was successfully deleted.
+ """
+ self.dut.log.info("Deleting config: networkId = %s" % network_id)
+ self.dut.droid.wifiForgetNetwork(network_id)
+ try:
+ event = self.dut.ed.pop_event(wifi_constants.WIFI_FORGET_NW_SUCCESS, 10)
+ return True
+ except queue.Empty:
+ self.dut.log.error("Failed to forget network")
+ return False
+
+ def gen_uri(self, device, info="DPP_TESTER", chan="81/1", mac=None):
+ """Generate a URI on a device
+
+ Args:
+ device: Device object
+ mac: MAC address to use
+ info: Optional info to be embedded in URI
+ chan: Optional channel info
+
+ Returns:
+ URI ID to be used later
+ """
+
+ # Clean up any previous URIs
+ self.del_uri(device, "'*'")
+
+ self.log.info("Generating a URI for the Responder")
+ cmd = "wpa_cli DPP_BOOTSTRAP_GEN type=qrcode info=%s" % info
+
+ if mac:
+ cmd += " mac=%s" % mac
+
+ if chan:
+ cmd += " chan=%s" % chan
+
+ result = device.adb.shell(cmd)
+
+ if "FAIL" in result:
+ asserts.fail("gen_uri: Failed to generate a URI. Command used: %s" % cmd)
+
+ if "\n" not in result:
+ asserts.fail(
+ "gen_uri: Helper device not responding correctly, "
+ "may need to restart it. Command used: %s" % cmd)
+
+ result = result[result.index("\n") + 1:]
+ device.log.info("Generated URI, id = %s" % result)
+
+ return result
+
+ def get_uri(self, device, uri_id):
+ """Get a previously generated URI from a device
+
+ Args:
+ device: Device object
+ uri_id: URI ID returned by gen_uri method
+
+ Returns:
+ URI string
+
+ """
+ self.log.info("Reading the contents of the URI of the Responder")
+ cmd = "wpa_cli DPP_BOOTSTRAP_GET_URI %s" % uri_id
+ result = device.adb.shell(cmd)
+
+ if "FAIL" in result:
+ asserts.fail("get_uri: Failed to read URI. Command used: %s" % cmd)
+
+ result = result[result.index("\n") + 1:]
+ device.log.info("URI contents = %s" % result)
+
+ return result
+
+ def del_uri(self, device, uri_id):
+ """Delete a previously generated URI
+
+ Args:
+ device: Device object
+ uri_id: URI ID returned by gen_uri method
+ """
+ self.log.info("Deleting the Responder URI")
+ cmd = "wpa_cli DPP_BOOTSTRAP_REMOVE %s" % uri_id
+ result = device.adb.shell(cmd)
+
+ # 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,
+ freq=2412,
+ net_role=DPP_TEST_NETWORK_ROLE_STA,
+ security=DPP_TEST_SECURITY_SAE,
+ invalid_config=False):
+ """Start a responder on helper device
+
+ Args:
+ device: Device object
+ freq: Frequency to listen on
+ net_role: Network role to configure
+ security: Security type: SAE or PSK
+ invalid_config: Send invalid configuration (negative test)
+
+ Returns:
+ ssid: SSID name of the network to be configured
+
+ """
+ if not net_role or (net_role != self.DPP_TEST_NETWORK_ROLE_STA and
+ net_role != self.DPP_TEST_NETWORK_ROLE_AP):
+ asserts.fail("start_responder: Must specify net_role sta or ap")
+
+ self.log.info("Starting Responder in Configurator mode, frequency %sMHz" % freq)
+
+ conf = "conf=%s-" % net_role
+
+ use_psk = False
+
+ if security == self.DPP_TEST_SECURITY_SAE:
+ if not self.dut.droid.wifiIsWpa3SaeSupported():
+ 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
+
+ self.log.debug("SSID = %s" % ssid)
+
+ ssid_encoded = binascii.hexlify(ssid.encode()).decode()
+
+ if use_psk:
+ psk = utils.rand_ascii_str(16)
+ if not invalid_config:
+ psk_encoded = binascii.b2a_hex(psk.encode()).decode()
+ else:
+ # Use the psk as is without hex encoding, will make it invalid
+ psk_encoded = psk
+ self.log.debug("PSK = %s" % psk)
+ else:
+ if not invalid_config:
+ password_encoded = binascii.b2a_hex(password.encode()).decode()
+ else:
+ # Use the password as is without hex encoding, will make it invalid
+ password_encoded = password
+ self.log.debug("Password = %s" % password)
+
+ conf += " ssid=%s" % ssid_encoded
+
+ if password: # SAE password or PSK passphrase
+ conf += " pass=%s" % password_encoded
+ else: # PSK
+ conf += " psk=%s" % psk_encoded
+
+ # Stop responder first
+ self.stop_responder(device)
+
+ cmd = "wpa_cli set dpp_configurator_params guard=1 %s" % conf
+ device.log.debug("Command used: %s" % cmd)
+ result = self.helper_dev.adb.shell(cmd)
+ if "FAIL" in result:
+ asserts.fail(
+ "start_responder_configurator: Failure. Command used: %s" % cmd)
+
+ cmd = "wpa_cli DPP_LISTEN %d role=configurator netrole=%s" % (freq,
+ net_role)
+ device.log.debug("Command used: %s" % cmd)
+ result = self.helper_dev.adb.shell(cmd)
+ if "FAIL" in result:
+ asserts.fail(
+ "start_responder_configurator: Failure. Command used: %s" % cmd)
+
+ device.log.info("Started responder in configurator mode")
+ return ssid
+
+ def start_responder_enrollee(self,
+ device,
+ freq=2412,
+ net_role=DPP_TEST_NETWORK_ROLE_STA):
+ """Start a responder-enrollee on helper device
+
+ Args:
+ device: Device object
+ freq: Frequency to listen on
+ net_role: Network role to request
+
+ Returns:
+ ssid: SSID name of the network to be configured
+
+ """
+ if not net_role or (net_role != self.DPP_TEST_NETWORK_ROLE_STA and
+ net_role != self.DPP_TEST_NETWORK_ROLE_AP):
+ asserts.fail("start_responder: Must specify net_role sta or ap")
+
+ # Stop responder first
+ self.stop_responder(device)
+ self.log.info("Starting Responder in Enrollee mode, frequency %sMHz" % freq)
+
+ cmd = "wpa_cli DPP_LISTEN %d role=enrollee netrole=%s" % (freq, net_role)
+ result = device.adb.shell(cmd)
+
+ 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, flush=False):
+ """Stop responder on helper device
+
+ Args:
+ device: Device object
+ """
+ result = device.adb.shell("wpa_cli DPP_STOP_LISTEN")
+ 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,
+ security,
+ use_mac,
+ responder_chan="81/1",
+ responder_freq=2412,
+ net_role=DPP_TEST_NETWORK_ROLE_STA,
+ cause_timeout=False,
+ fail_authentication=False,
+ invalid_uri=False,
+ r2_no_ap=False,
+ r2_auth_error=False):
+ """ Test Easy Connect (DPP) as initiator configurator.
+
+ 1. Enable wifi, if needed
+ 2. Create and save a random config.
+ 3. Generate a URI using the helper device
+ 4. Start DPP as responder-enrollee on helper device
+ 5. Start DPP as initiator configurator on dut
+ 6. Check if configurator sent successfully
+ 7. Delete the URI from helper device
+ 8. Remove the config.
+
+ Args:
+ security: Security type, a string "SAE" or "PSK"
+ use_mac: A boolean indicating whether to use the device's MAC
+ address (if True) or use a Broadcast (if False).
+ responder_chan: Responder channel to specify in the URI
+ responder_freq: Frequency that the Responder would actually listen on.
+ Note: To succeed, there must be a correlation between responder_chan, which is what
+ the URI advertises, and responder_freq which is the actual frequency. See:
+ https://en.wikipedia.org/wiki/List_of_WLAN_channels
+ net_role: Network role, a string "sta" or "ap"
+ cause_timeout: Intentionally don't start the responder to cause a
+ timeout
+ 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, random_network=r2_no_ap,
+ r2_auth_error=r2_auth_error)
+
+ if use_mac:
+ mac = autils.get_mac_addr(self.helper_dev, "wlan0")
+ else:
+ mac = None
+
+ if invalid_uri:
+ enrollee_uri = "dskjgnkdjfgnkdsjfgnsDFGDIFGKDSJFGFDbgjdsnbkjdfnkbgsdfgFDSGSDfgesouhgureho" \
+ "iu3ht98368903434089ut4958763094u0934ujg094j5oifegjfds"
+ else:
+ # Generate a URI with default info and channel
+ uri_id = self.gen_uri(self.helper_dev, chan=responder_chan, mac=mac)
+
+ # Get the URI. This is equal to scanning a QR code
+ enrollee_uri = self.get_uri(self.helper_dev, uri_id)
+
+ # Corrupt the responder key if required
+ if fail_authentication:
+ enrollee_uri = enrollee_uri[:80] + "DeAdBeeF" + enrollee_uri[88:]
+ self.log.info("Corrupted enrollee URI: %s" % enrollee_uri)
+
+ if not cause_timeout:
+ # Start DPP as an enrolle-responder for STA on helper device
+ self.start_responder_enrollee(self.helper_dev, freq=responder_freq, net_role=net_role)
+ else:
+ self.log.info("Not starting DPP responder on purpose")
+
+ self.log.info("Starting DPP in Configurator-Initiator mode")
+
+ # Start DPP as configurator-initiator on dut
+ self.dut.droid.startEasyConnectAsConfiguratorInitiator(enrollee_uri,
+ test_network_id, net_role)
+
+ start_time = time.time()
+ while time.time() < start_time + self.DPP_TEST_TIMEOUT:
+ dut_event = self.dut.ed.pop_event(self.DPP_TEST_EVENT_DPP_CALLBACK,
+ self.DPP_TEST_TIMEOUT)
+ if dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_TYPE] \
+ == 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 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 == 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:
+ self.dut.log.info("DPP progress event")
+ val = dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_STATUS]
+ if val == self.DPP_EVENT_PROGRESS_AUTHENTICATION_SUCCESS:
+ self.dut.log.info("DPP Authentication success")
+ 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 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" %
+ dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_STATUS])
+ break
+
+ # Clear all pending events.
+ self.dut.ed.clear_all_events()
+
+ # Stop responder
+ self.stop_responder(self.helper_dev, flush=True)
+
+ if not invalid_uri:
+ # Delete URI
+ self.del_uri(self.helper_dev, uri_id)
+
+ asserts.assert_true(
+ self.forget_network(test_network_id),
+ "Test network not deleted from configured networks.")
+
+ def start_dpp_as_initiator_enrollee(self,
+ security,
+ use_mac,
+ cause_timeout=False,
+ invalid_config=False):
+ """ Test Easy Connect (DPP) as initiator enrollee.
+
+ 1. Enable wifi, if needed
+ 2. Start DPP as responder-configurator on helper device
+ 3. Start DPP as initiator enrollee on dut
+ 4. Check if configuration received successfully
+ 5. Delete the URI from helper device
+ 6. Remove the config.
+
+ Args:
+ security: Security type, a string "SAE" or "PSK"
+ use_mac: A boolean indicating whether to use the device's MAC
+ address (if True) or use a Broadcast (if False).
+ cause_timeout: Intentionally don't start the responder to cause a
+ timeout
+ invalid_config: Responder to intentionally send malformed
+ configuration
+ """
+ if not self.dut.droid.wifiIsEasyConnectSupported():
+ self.log.warning("Easy Connect is not supported on device!")
+ return
+
+ wutils.wifi_toggle_state(self.dut, True)
+
+ if use_mac:
+ mac = autils.get_mac_addr(self.helper_dev, "wlan0")
+ else:
+ mac = None
+
+ # Generate a URI with default info and channel
+ uri_id = self.gen_uri(self.helper_dev, mac=mac)
+
+ # Get the URI. This is equal to scanning a QR code
+ configurator_uri = self.get_uri(self.helper_dev, uri_id)
+
+ if not cause_timeout:
+ # Start DPP as an configurator-responder for STA on helper device
+ ssid = self.start_responder_configurator(
+ self.helper_dev, security=security, invalid_config=invalid_config)
+ else:
+ self.log.info(
+ "Not starting a responder configurator on helper device, on purpose")
+ ssid = self.DPP_TEST_SSID_PREFIX + utils.rand_ascii_str(8)
+
+ self.log.info("Starting DPP in Enrollee-Initiator mode")
+
+ # Start DPP as enrollee-initiator on dut
+ self.dut.droid.startEasyConnectAsEnrolleeInitiator(configurator_uri)
+
+ network_id = 0
+
+ start_time = time.time()
+ while time.time() < start_time + self.DPP_TEST_TIMEOUT:
+ dut_event = self.dut.ed.pop_event(self.DPP_TEST_EVENT_DPP_CALLBACK,
+ self.DPP_TEST_TIMEOUT)
+ if dut_event[self.DPP_TEST_EVENT_DATA][
+ self.DPP_TEST_MESSAGE_TYPE] == self.DPP_TEST_EVENT_ENROLLEE_SUCCESS:
+ if cause_timeout or invalid_config:
+ asserts.fail(
+ "Unexpected DPP success, status code: %s" %
+ dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_STATUS])
+ else:
+ self.dut.log.info("DPP Configuration received success")
+ network_id = dut_event[self.DPP_TEST_EVENT_DATA][
+ self.DPP_TEST_MESSAGE_NETWORK_ID]
+ self.dut.log.info("NetworkID: %d" % network_id)
+ break
+ if dut_event[self.DPP_TEST_EVENT_DATA][
+ self
+ .DPP_TEST_MESSAGE_TYPE] == self.DPP_TEST_EVENT_CONFIGURATOR_SUCCESS:
+ asserts.fail(
+ "DPP failure, unexpected result: %s" %
+ dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_STATUS])
+ break
+ 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:
+ self.dut.log.info("DPP Authentication success")
+ elif val == 1:
+ self.dut.log.info("DPP Response pending")
+ continue
+ if dut_event[self.DPP_TEST_EVENT_DATA][
+ self.DPP_TEST_MESSAGE_TYPE] == self.DPP_TEST_EVENT_FAILURE:
+ if cause_timeout or invalid_config:
+ self.dut.log.info(
+ "Error %s occurred, as expected" %
+ dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_STATUS])
+ else:
+ asserts.fail(
+ "DPP failure, status code: %s" %
+ dut_event[self.DPP_TEST_EVENT_DATA][self.DPP_TEST_MESSAGE_STATUS])
+ break
+ asserts.fail("Unknown message received")
+
+ # Clear all pending events.
+ self.dut.ed.clear_all_events()
+
+ # Stop responder
+ self.stop_responder(self.helper_dev, flush=True)
+
+ # Delete URI
+ self.del_uri(self.helper_dev, uri_id)
+
+ if not (invalid_config or cause_timeout):
+ # Check that the saved network is what we expect
+ asserts.assert_true(
+ self.check_network_config_saved(ssid, security, network_id),
+ "Could not find the expected network: %s" % ssid)
+
+ asserts.assert_true(
+ self.forget_network(network_id),
+ "Test network not deleted from configured networks.")
+
+ """ Tests Begin """
+
+ @test_tracker_info(uuid="30893d51-2069-4e1c-8917-c8a840f91b59")
+ @WifiBaseTest.wifi_test_wrap
+ def test_dpp_as_initiator_configurator_with_psk_5G(self):
+ asserts.skip_if(not self.dut.droid.wifiIs5GHzBandSupported() or
+ not self.helper_dev.droid.wifiIs5GHzBandSupported(),
+ "5G not supported on at least on test device")
+ self.start_dpp_as_initiator_configurator(
+ security=self.DPP_TEST_SECURITY_PSK, responder_chan="126/149", responder_freq=5745,
+ use_mac=True)
+
+ @test_tracker_info(uuid="54d1d19a-aece-459c-b819-9d4b1ae63f77")
+ @WifiBaseTest.wifi_test_wrap
+ def test_dpp_as_initiator_configurator_with_psk_5G_broadcast(self):
+ asserts.skip_if(not self.dut.droid.wifiIs5GHzBandSupported() or
+ not self.helper_dev.droid.wifiIs5GHzBandSupported(),
+ "5G not supported on at least on test device")
+ self.start_dpp_as_initiator_configurator(
+ security=self.DPP_TEST_SECURITY_PSK, responder_chan="126/149", responder_freq=5745,
+ use_mac=False)
+
+ @test_tracker_info(uuid="18270a69-300c-4f54-87fd-c19073a2854e ")
+ @WifiBaseTest.wifi_test_wrap
+ def test_dpp_as_initiator_configurator_with_psk_no_chan_in_uri_listen_on_5745_broadcast(self):
+ asserts.skip_if(not self.dut.droid.wifiIs5GHzBandSupported() or
+ not self.helper_dev.droid.wifiIs5GHzBandSupported(),
+ "5G not supported on at least on test device")
+ self.start_dpp_as_initiator_configurator(
+ security=self.DPP_TEST_SECURITY_PSK, responder_chan=None, responder_freq=5745, use_mac=False)
+
+ @test_tracker_info(uuid="fbdd687c-954a-400b-9da3-2d17e28b0798")
+ @WifiBaseTest.wifi_test_wrap
+ def test_dpp_as_initiator_configurator_with_psk_no_chan_in_uri_listen_on_5745(self):
+ asserts.skip_if(not self.dut.droid.wifiIs5GHzBandSupported() or
+ not self.helper_dev.droid.wifiIs5GHzBandSupported(),
+ "5G not supported on at least on test device")
+ self.start_dpp_as_initiator_configurator(
+ security=self.DPP_TEST_SECURITY_PSK, responder_chan=None, responder_freq=5745, use_mac=True)
+
+ @test_tracker_info(uuid="570f499f-ab12-4405-af14-c9ed36da2e01")
+ @WifiBaseTest.wifi_test_wrap
+ def test_dpp_as_initiator_configurator_with_psk_no_chan_in_uri_listen_on_2462_broadcast(self):
+ self.start_dpp_as_initiator_configurator(
+ security=self.DPP_TEST_SECURITY_PSK, responder_chan=None, responder_freq=2462, use_mac=False)
+
+ @test_tracker_info(uuid="e1f083e0-0878-4c49-8ac5-d7c6bba24625")
+ @WifiBaseTest.wifi_test_wrap
+ def test_dpp_as_initiator_configurator_with_psk_no_chan_in_uri_listen_on_2462(self):
+ self.start_dpp_as_initiator_configurator(
+ security=self.DPP_TEST_SECURITY_PSK, responder_chan=None, responder_freq=2462, use_mac=True)
+
+ @test_tracker_info(uuid="d2a526f5-4269-493d-bd79-4e6d1b7b00f0")
+ @WifiBaseTest.wifi_test_wrap
+ def test_dpp_as_initiator_configurator_with_psk(self):
+ self.start_dpp_as_initiator_configurator(
+ security=self.DPP_TEST_SECURITY_PSK, use_mac=True)
+
+ @test_tracker_info(uuid="6ead218c-222b-45b8-8aad-fe7d883ed631")
+ @WifiBaseTest.wifi_test_wrap
+ def test_dpp_as_initiator_configurator_with_sae(self):
+ self.start_dpp_as_initiator_configurator(
+ security=self.DPP_TEST_SECURITY_SAE, use_mac=True)
+
+ @test_tracker_info(uuid="1686adb5-1b3c-4e6d-a969-6b007bdd990d")
+ @WifiBaseTest.wifi_test_wrap
+ def test_dpp_as_initiator_configurator_with_psk_passphrase(self):
+ self.start_dpp_as_initiator_configurator(
+ security=self.DPP_TEST_SECURITY_PSK_PASSPHRASE, use_mac=True)
+
+ @test_tracker_info(uuid="3958feb5-1a0c-4487-9741-ac06f04c55a2")
+ @WifiBaseTest.wifi_test_wrap
+ def test_dpp_as_initiator_configurator_with_sae_broadcast(self):
+ self.start_dpp_as_initiator_configurator(
+ security=self.DPP_TEST_SECURITY_SAE, use_mac=False)
+
+ @test_tracker_info(uuid="fe6d66f5-73a1-46e9-8f49-73b8f332cc8c")
+ @WifiBaseTest.wifi_test_wrap
+ def test_dpp_as_initiator_configurator_with_psk_passphrase_broadcast(self):
+ self.start_dpp_as_initiator_configurator(
+ security=self.DPP_TEST_SECURITY_PSK_PASSPHRASE, use_mac=False)
+
+ @test_tracker_info(uuid="9edd372d-e2f1-4545-8d04-6a1636fcbc4b")
+ @WifiBaseTest.wifi_test_wrap
+ def test_dpp_as_initiator_configurator_with_sae_for_ap(self):
+ self.start_dpp_as_initiator_configurator(
+ security=self.DPP_TEST_SECURITY_SAE,
+ use_mac=True,
+ net_role=self.DPP_TEST_NETWORK_ROLE_AP)
+
+ @test_tracker_info(uuid="e9eec912-d665-4926-beac-859cb13dc17b")
+ @WifiBaseTest.wifi_test_wrap
+ def test_dpp_as_initiator_configurator_with_psk_passphrase_for_ap(self):
+ self.start_dpp_as_initiator_configurator(
+ security=self.DPP_TEST_SECURITY_PSK_PASSPHRASE,
+ use_mac=True,
+ net_role=self.DPP_TEST_NETWORK_ROLE_AP)
+
+ @test_tracker_info(uuid="8055694f-606f-41dd-9826-3ea1e9b007f8")
+ @WifiBaseTest.wifi_test_wrap
+ def test_dpp_as_initiator_enrollee_with_sae(self):
+ self.start_dpp_as_initiator_enrollee(
+ security=self.DPP_TEST_SECURITY_SAE, use_mac=True)
+
+ @test_tracker_info(uuid="c1e9f605-b5c0-4e53-8a08-1b0087a667fa")
+ @WifiBaseTest.wifi_test_wrap
+ def test_dpp_as_initiator_enrollee_with_psk_passphrase(self):
+ self.start_dpp_as_initiator_enrollee(
+ security=self.DPP_TEST_SECURITY_PSK_PASSPHRASE, use_mac=True)
+
+ @test_tracker_info(uuid="1d7f30ad-2f9a-427a-8059-651dc8827ae2")
+ @WifiBaseTest.wifi_test_wrap
+ def test_dpp_as_initiator_enrollee_with_sae_broadcast(self):
+ self.start_dpp_as_initiator_enrollee(
+ security=self.DPP_TEST_SECURITY_SAE, use_mac=False)
+
+ @test_tracker_info(uuid="0cfc2645-600e-4f2b-ab5c-fcee6d363a9a")
+ @WifiBaseTest.wifi_test_wrap
+ def test_dpp_as_initiator_enrollee_with_psk_passphrase_broadcast(self):
+ self.start_dpp_as_initiator_enrollee(
+ security=self.DPP_TEST_SECURITY_PSK_PASSPHRASE, use_mac=False)
+
+ @test_tracker_info(uuid="2e26b248-65dd-41f6-977b-e223d72b2de9")
+ @WifiBaseTest.wifi_test_wrap
+ def test_start_dpp_as_initiator_enrollee_receive_invalid_config(self):
+ self.start_dpp_as_initiator_enrollee(
+ security=self.DPP_TEST_SECURITY_PSK_PASSPHRASE,
+ use_mac=True,
+ invalid_config=True)
+
+ @test_tracker_info(uuid="ed189661-d1c1-4626-9f01-3b7bb8a417fe")
+ @WifiBaseTest.wifi_test_wrap
+ def test_dpp_as_initiator_configurator_fail_authentication(self):
+ self.start_dpp_as_initiator_configurator(
+ security=self.DPP_TEST_SECURITY_PSK_PASSPHRASE,
+ use_mac=True,
+ fail_authentication=True)
+
+ @test_tracker_info(uuid="5a8c6587-fbb4-4a27-9cba-af6f8935833a")
+ @WifiBaseTest.wifi_test_wrap
+ def test_dpp_as_initiator_configurator_fail_unicast_timeout(self):
+ self.start_dpp_as_initiator_configurator(
+ security=self.DPP_TEST_SECURITY_PSK_PASSPHRASE,
+ use_mac=True,
+ cause_timeout=True)
+
+ @test_tracker_info(uuid="b12353ac-1a04-4036-81a4-2d2d0c653dbb")
+ @WifiBaseTest.wifi_test_wrap
+ def test_dpp_as_initiator_configurator_fail_broadcast_timeout(self):
+ self.start_dpp_as_initiator_configurator(
+ security=self.DPP_TEST_SECURITY_PSK_PASSPHRASE,
+ use_mac=False,
+ cause_timeout=True)
+
+ @test_tracker_info(uuid="eeff91be-09ce-4a33-8b4f-ece40eb51c76")
+ @WifiBaseTest.wifi_test_wrap
+ def test_dpp_as_initiator_configurator_invalid_uri(self):
+ self.start_dpp_as_initiator_configurator(
+ security=self.DPP_TEST_SECURITY_PSK_PASSPHRASE,
+ use_mac=True,
+ invalid_uri=True)
+
+ @test_tracker_info(uuid="1fa25f58-0d0e-40bd-8714-ab78957514d9")
+ @WifiBaseTest.wifi_test_wrap
+ def test_start_dpp_as_initiator_enrollee_fail_timeout(self):
+ self.start_dpp_as_initiator_enrollee(
+ security=self.DPP_TEST_SECURITY_PSK_PASSPHRASE,
+ use_mac=True,
+ cause_timeout=True)
+
+ @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/tests/google/wifi/WifiEnterpriseRoamingTest.py b/acts_tests/tests/google/wifi/WifiEnterpriseRoamingTest.py
new file mode 100644
index 0000000..05b3549
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiEnterpriseRoamingTest.py
@@ -0,0 +1,254 @@
+#
+# Copyright 2016 - 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 pprint
+import random
+import time
+
+from acts import asserts
+from acts import signals
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+WifiEnums = wutils.WifiEnums
+
+# EAP Macros
+EAP = WifiEnums.Eap
+EapPhase2 = WifiEnums.EapPhase2
+
+# Enterprise Config Macros
+Ent = WifiEnums.Enterprise
+
+
+class WifiEnterpriseRoamingTest(WifiBaseTest):
+
+ def __init__(self, configs):
+ super().__init__(configs)
+ self.enable_packet_log = True
+
+ def setup_class(self):
+ super().setup_class()
+
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ req_params = (
+ "attn_vals",
+ # Expected time within which roaming should finish, in seconds.
+ "roam_interval",
+ "ca_cert",
+ "client_cert",
+ "client_key",
+ "eap_identity",
+ "eap_password",
+ "device_password",
+ "radius_conf_2g",
+ "radius_conf_5g")
+ self.unpack_userparams(req_params)
+ if "AccessPoint" in self.user_params:
+ self.legacy_configure_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,)
+ 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.ent_roaming_ssid = self.ent_network_2g_a[WifiEnums.SSID_KEY]
+ 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),
+ 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_roaming_ssid
+ }
+ self.config_tls = {
+ Ent.EAP: int(EAP.TLS),
+ Ent.CA_CERT: self.ca_cert,
+ WifiEnums.SSID_KEY: self.ent_roaming_ssid,
+ Ent.CLIENT_CERT: self.client_cert,
+ Ent.PRIVATE_KEY_ID: self.client_key,
+ Ent.IDENTITY: self.eap_identity,
+ }
+ 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_roaming_ssid
+ }
+ self.config_sim = {
+ Ent.EAP: int(EAP.SIM),
+ WifiEnums.SSID_KEY: self.ent_roaming_ssid,
+ }
+ 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")
+
+ def teardown_class(self):
+ wutils.reset_wifi(self.dut)
+ self.dut.droid.disableDevicePassword(self.device_password)
+ self.dut.ed.clear_all_events()
+ self.set_attns("default")
+
+ def setup_test(self):
+ super().setup_test()
+ self.dut.droid.wifiStartTrackingStateChange()
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ wutils.reset_wifi(self.dut)
+ self.dut.ed.clear_all_events()
+
+ def teardown_test(self):
+ super().teardown_test()
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ self.dut.droid.wifiStopTrackingStateChange()
+ self.set_attns("default")
+
+ def set_attns(self, attn_val_name):
+ """Sets attenuation values on attenuators used in this test.
+
+ Args:
+ attn_val_name: Name of the attenuation value pair to use.
+ """
+ self.log.info("Set attenuation values to %s",
+ self.attn_vals[attn_val_name])
+ try:
+ self.attn_a.set_atten(self.attn_vals[attn_val_name][0])
+ self.attn_b.set_atten(self.attn_vals[attn_val_name][1])
+ except:
+ self.log.exception("Failed to set attenuation values %s.",
+ attn_val_name)
+ raise
+
+ def trigger_roaming_and_validate(self, attn_val_name, expected_con):
+ """Sets attenuators to trigger roaming and validate the DUT connected
+ to the BSSID expected.
+
+ Args:
+ attn_val_name: Name of the attenuation value pair to use.
+ expected_con: The expected info of the network to we expect the DUT
+ to roam to.
+ """
+ self.set_attns(attn_val_name)
+ self.log.info("Wait %ss for roaming to finish.", self.roam_interval)
+ time.sleep(self.roam_interval)
+ try:
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ wutils.verify_wifi_connection_info(self.dut, expected_con)
+ expected_bssid = expected_con[WifiEnums.BSSID_KEY]
+ self.log.info("Roamed to %s successfully", expected_bssid)
+ finally:
+ self.dut.droid.wifiLockRelease()
+ self.dut.droid.goToSleepNow()
+
+ def roaming_between_a_and_b_logic(self, config):
+ """Test roaming between two enterprise APs.
+
+ Steps:
+ 1. Make bssid_a visible, bssid_b not visible.
+ 2. Connect to ent_roaming_ssid. Expect DUT to connect to bssid_a.
+ 3. Make bssid_a not visible, bssid_b visible.
+ 4. Expect DUT to roam to bssid_b.
+ 5. Make bssid_a visible, bssid_b not visible.
+ 6. Expect DUT to roam back to bssid_a.
+ """
+ expected_con_to_a = {
+ WifiEnums.SSID_KEY: self.ent_roaming_ssid,
+ WifiEnums.BSSID_KEY: self.bssid_a,
+ }
+ expected_con_to_b = {
+ WifiEnums.SSID_KEY: self.ent_roaming_ssid,
+ WifiEnums.BSSID_KEY: self.bssid_b,
+ }
+ self.set_attns("a_on_b_off")
+ wutils.wifi_connect(self.dut, config)
+ wutils.verify_wifi_connection_info(self.dut, expected_con_to_a)
+ self.log.info("Roaming from %s to %s", self.bssid_a, self.bssid_b)
+ self.trigger_roaming_and_validate("b_on_a_off", expected_con_to_b)
+ self.log.info("Roaming from %s to %s", self.bssid_b, self.bssid_a)
+ self.trigger_roaming_and_validate("a_on_b_off", expected_con_to_a)
+
+ """ Tests Begin """
+
+ @test_tracker_info(uuid="b15e4b3f-841d-428d-87ac-272f29f06e14")
+ def test_roaming_with_config_tls(self):
+ self.roaming_between_a_and_b_logic(self.config_tls)
+
+ @test_tracker_info(uuid="d349cfec-b4af-48b2-88b7-744f5de25d43")
+ def test_roaming_with_config_ttls_none(self):
+ config = dict(self.config_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.NONE.value
+ self.roaming_between_a_and_b_logic(config)
+
+ @test_tracker_info(uuid="89b8161c-754e-4138-831d-5fe40f521ce4")
+ def test_roaming_with_config_ttls_pap(self):
+ config = dict(self.config_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.PAP.value
+ self.roaming_between_a_and_b_logic(config)
+
+ @test_tracker_info(uuid="d4925470-924b-4d03-8437-83e26b5f2df3")
+ def test_roaming_with_config_ttls_mschap(self):
+ config = dict(self.config_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.MSCHAP.value
+ self.roaming_between_a_and_b_logic(config)
+
+ @test_tracker_info(uuid="206b1327-dd9c-4742-8717-e7bf2a04eed6")
+ def test_roaming_with_config_ttls_mschapv2(self):
+ config = dict(self.config_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.MSCHAPV2.value
+ self.roaming_between_a_and_b_logic(config)
+
+ @test_tracker_info(uuid="c2c0168b-2933-4954-af62-fb41f42dc45a")
+ def test_roaming_with_config_ttls_gtc(self):
+ config = dict(self.config_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.GTC.value
+ self.roaming_between_a_and_b_logic(config)
+
+ @test_tracker_info(uuid="481c4102-8f5b-4fcd-95cc-5e3285f47985")
+ def test_roaming_with_config_peap_mschapv2(self):
+ config = dict(self.config_peap)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.MSCHAPV2.value
+ self.roaming_between_a_and_b_logic(config)
+
+ @test_tracker_info(uuid="404155d4-33a7-42b3-b369-5f2d63d19f16")
+ def test_roaming_with_config_peap_gtc(self):
+ config = dict(self.config_peap)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.GTC.value
+ self.roaming_between_a_and_b_logic(config)
+
+ """ Tests End """
diff --git a/acts_tests/tests/google/wifi/WifiEnterpriseTest.py b/acts_tests/tests/google/wifi/WifiEnterpriseTest.py
new file mode 100644
index 0000000..aee773c
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiEnterpriseTest.py
@@ -0,0 +1,765 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2016 - 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 pprint
+import random
+import time
+
+from acts import asserts
+from acts import signals
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+WifiEnums = wutils.WifiEnums
+
+# EAP Macros
+EAP = WifiEnums.Eap
+EapPhase2 = WifiEnums.EapPhase2
+# Enterprise Config Macros
+Ent = WifiEnums.Enterprise
+
+
+class WifiEnterpriseTest(WifiBaseTest):
+
+ def __init__(self, configs):
+ super().__init__(configs)
+ self.enable_packet_log = True
+
+ def setup_class(self):
+ super().setup_class()
+
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ # If running in a setup with attenuators, set attenuation on all
+ # channels to zero.
+ if getattr(self, "attenuators", []):
+ for a in self.attenuators:
+ a.set_atten(0)
+ required_userparam_names = (
+ "ca_cert", "client_cert", "client_key", "passpoint_ca_cert",
+ "passpoint_client_cert", "passpoint_client_key", "eap_identity",
+ "eap_password", "invalid_ca_cert", "invalid_client_cert",
+ "invalid_client_key", "fqdn", "provider_friendly_name", "realm",
+ "device_password", "ping_addr", "radius_conf_2g", "radius_conf_5g",
+ "radius_conf_pwd")
+ self.unpack_userparams(required_userparam_names,
+ roaming_consortium_ids=None,
+ plmn=None,
+ ocsp=0)
+
+ if "AccessPoint" in self.user_params:
+ self.legacy_configure_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,)
+ 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"]
+
+ # Default configs for EAP networks.
+ self.config_peap0 = {
+ Ent.EAP: int(EAP.PEAP),
+ 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_5g[WifiEnums.SSID_KEY],
+ Ent.OCSP: self.ocsp,
+ }
+ self.config_peap1 = dict(self.config_peap0)
+ self.config_peap1[WifiEnums.SSID_KEY] = \
+ self.ent_network_2g[WifiEnums.SSID_KEY]
+ self.config_tls = {
+ Ent.EAP: int(EAP.TLS),
+ Ent.CA_CERT: self.ca_cert,
+ WifiEnums.SSID_KEY: self.ent_network_2g[WifiEnums.SSID_KEY],
+ 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),
+ 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],
+ Ent.OCSP: self.ocsp,
+ }
+ self.config_pwd = {
+ Ent.EAP: int(EAP.PWD),
+ Ent.IDENTITY: self.eap_identity,
+ Ent.PASSWORD: self.eap_password,
+ WifiEnums.SSID_KEY: self.ent_network_pwd[WifiEnums.SSID_KEY],
+ }
+ self.config_sim = {
+ Ent.EAP: int(EAP.SIM),
+ WifiEnums.SSID_KEY: self.ent_network_2g[WifiEnums.SSID_KEY],
+ }
+ self.config_aka = {
+ Ent.EAP: int(EAP.AKA),
+ WifiEnums.SSID_KEY: self.ent_network_2g[WifiEnums.SSID_KEY],
+ }
+ self.config_aka_prime = {
+ Ent.EAP: int(EAP.AKA_PRIME),
+ WifiEnums.SSID_KEY: self.ent_network_2g[WifiEnums.SSID_KEY],
+ }
+
+ # Base config for passpoint networks.
+ self.config_passpoint = {
+ Ent.FQDN: self.fqdn,
+ Ent.FRIENDLY_NAME: self.provider_friendly_name,
+ Ent.REALM: self.realm,
+ Ent.CA_CERT: self.passpoint_ca_cert
+ }
+ if self.plmn:
+ self.config_passpoint[Ent.PLMN] = self.plmn
+ if self.roaming_consortium_ids:
+ self.config_passpoint[
+ Ent.ROAMING_IDS] = self.roaming_consortium_ids
+
+ # Default configs for passpoint networks.
+ self.config_passpoint_tls = dict(self.config_tls)
+ self.config_passpoint_tls.update(self.config_passpoint)
+ self.config_passpoint_tls[Ent.CLIENT_CERT] = self.passpoint_client_cert
+ self.config_passpoint_tls[
+ Ent.PRIVATE_KEY_ID] = self.passpoint_client_key
+ del self.config_passpoint_tls[WifiEnums.SSID_KEY]
+ self.config_passpoint_ttls = dict(self.config_ttls)
+ self.config_passpoint_ttls.update(self.config_passpoint)
+ del self.config_passpoint_ttls[WifiEnums.SSID_KEY]
+ # Set screen lock password so ConfigStore is unlocked.
+ self.dut.droid.setDevicePassword(self.device_password)
+
+ def teardown_class(self):
+ wutils.reset_wifi(self.dut)
+ self.dut.droid.disableDevicePassword(self.device_password)
+ self.dut.ed.clear_all_events()
+
+ def setup_test(self):
+ super().setup_test()
+ self.dut.droid.wifiStartTrackingStateChange()
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ wutils.reset_wifi(self.dut)
+ self.dut.ed.clear_all_events()
+
+ def teardown_test(self):
+ super().teardown_test()
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ self.dut.droid.wifiStopTrackingStateChange()
+
+ """Helper Functions"""
+
+ def eap_negative_connect_logic(self, config, ad):
+ """Tries to connect to an enterprise network with invalid credentials
+ and expect a failure.
+
+ Args:
+ config: A dict representing an invalid EAP credential.
+
+ Returns:
+ True if connection failed as expected, False otherwise.
+ """
+ with asserts.assert_raises(signals.TestFailure, extras=config):
+ verdict = wutils.wifi_connect(ad, config)
+ asserts.explicit_pass("Connection failed as expected.")
+
+ def gen_negative_configs(self, config, neg_params):
+ """Generic function used to generate negative configs.
+
+ For all the valid configurations, if a param in the neg_params also
+ exists in a config, a copy of the config is made with an invalid value
+ of the param.
+
+ Args:
+ config: A valid configuration.
+ neg_params: A dict that has all the invalid values.
+
+ Returns:
+ An invalid configurations generated based on the valid
+ configuration. Each invalid configuration has a different invalid
+ field.
+ """
+ negative_config = dict(config)
+ if negative_config in [self.config_sim, self.config_aka,
+ self.config_aka_prime]:
+ negative_config[WifiEnums.SSID_KEY] = 'wrong_hostapd_ssid'
+ for k, v in neg_params.items():
+ # Skip negative test for TLS's identity field since it's not
+ # used for auth.
+ if config[Ent.EAP] == EAP.TLS and k == Ent.IDENTITY:
+ continue
+ if k in config:
+ negative_config[k] = v
+ negative_config["invalid_field"] = k
+ return negative_config
+
+ def gen_negative_eap_configs(self, config):
+ """Generates invalid configurations for different EAP authentication
+ types.
+
+ For all the valid EAP configurations, if a param that is part of the
+ authentication info exists in a config, a copy of the config is made
+ with an invalid value of the param.
+
+ Args:
+ A valid network configration
+
+ Returns:
+ An invalid EAP configuration.
+ """
+ neg_params = {
+ Ent.CLIENT_CERT: self.invalid_client_cert,
+ Ent.CA_CERT: self.invalid_ca_cert,
+ Ent.PRIVATE_KEY_ID: self.invalid_client_key,
+ Ent.IDENTITY: "fake_identity",
+ Ent.PASSWORD: "wrong_password"
+ }
+ return self.gen_negative_configs(config, neg_params)
+
+ def gen_negative_passpoint_configs(self, config):
+ """Generates invalid configurations for different EAP authentication
+ types with passpoint support.
+
+ Args:
+ A valid network configration
+
+ Returns:
+ An invalid EAP configuration with passpoint fields.
+ """
+ neg_params = {
+ Ent.CLIENT_CERT: self.invalid_client_cert,
+ Ent.CA_CERT: self.invalid_ca_cert,
+ Ent.PRIVATE_KEY_ID: self.invalid_client_key,
+ Ent.IDENTITY: "fake_identity",
+ Ent.PASSWORD: "wrong_password",
+ Ent.FQDN: "fake_fqdn",
+ Ent.REALM: "where_no_one_has_gone_before",
+ Ent.PLMN: "fake_plmn",
+ Ent.ROAMING_IDS: [1234567890, 9876543210]
+ }
+ return self.gen_negative_configs(config, neg_params)
+
+ def eap_connect_toggle_wifi(self,
+ config,
+ *args):
+ """Connects to an enterprise network, toggles wifi state and ensures
+ that the device reconnects.
+
+ This logic expect the enterprise network to have Internet access.
+
+ Args:
+ config: A dict representing a wifi enterprise configuration.
+ args: args to be passed to |wutils.eap_connect|.
+
+ Returns:
+ True if the connection is successful and Internet access works.
+ """
+ ad = args[0]
+ wutils.wifi_connect(ad, config)
+ wutils.toggle_wifi_and_wait_for_reconnection(ad, config, num_of_tries=5)
+
+ """ Tests """
+
+ # EAP connect tests
+ """ Test connecting to enterprise networks of different authentication
+ types.
+
+ The authentication types tested are:
+ EAP-TLS
+ EAP-PEAP with different phase2 types.
+ EAP-TTLS with different phase2 types.
+
+ Procedures:
+ For each enterprise wifi network
+ 1. Connect to the network.
+ 2. Send a GET request to a website and check response.
+
+ Expect:
+ Successful connection and Internet access through the enterprise
+ networks.
+ """
+ @test_tracker_info(uuid="4e720cac-ea17-4de7-a540-8dc7c49f9713")
+ def test_eap_connect_with_config_tls(self):
+ wutils.wifi_connect(self.dut, self.config_tls)
+
+ @test_tracker_info(uuid="10e3a5e9-0018-4162-a9fa-b41500f13340")
+ def test_eap_connect_with_config_pwd(self):
+ wutils.wifi_connect(self.dut, self.config_pwd)
+
+ @test_tracker_info(uuid="b4513f78-a1c4-427f-bfc7-2a6b3da714b5")
+ def test_eap_connect_with_config_sim(self):
+ wutils.wifi_connect(self.dut, self.config_sim)
+
+ @test_tracker_info(uuid="7d390e30-cb67-4b55-bf00-567adad2d9b0")
+ def test_eap_connect_with_config_aka(self):
+ wutils.wifi_connect(self.dut, self.config_aka)
+
+ @test_tracker_info(uuid="742f921b-27c3-4b68-a3ca-88e64fe79c1d")
+ def test_eap_connect_with_config_aka_prime(self):
+ wutils.wifi_connect(self.dut, self.config_aka_prime)
+
+ @test_tracker_info(uuid="d34e30f3-6ef6-459f-b47a-e78ed90ce4c6")
+ def test_eap_connect_with_config_ttls_none(self):
+ config = dict(self.config_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.NONE.value
+ wutils.wifi_connect(self.dut, config)
+
+ @test_tracker_info(uuid="0dca3a15-472e-427c-8e06-4e38088ee973")
+ def test_eap_connect_with_config_ttls_pap(self):
+ config = dict(self.config_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.PAP.value
+ wutils.wifi_connect(self.dut, config)
+
+ @test_tracker_info(uuid="47c4b459-2cb1-4fc7-b4e7-82534e8e090e")
+ def test_eap_connect_with_config_ttls_mschap(self):
+ config = dict(self.config_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.MSCHAP.value
+ wutils.wifi_connect(self.dut, config)
+
+ @test_tracker_info(uuid="fdb286c7-8069-481d-baf0-c5dd7a31ff03")
+ def test_eap_connect_with_config_ttls_mschapv2(self):
+ config = dict(self.config_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.MSCHAPV2.value
+ wutils.wifi_connect(self.dut, config)
+
+ @test_tracker_info(uuid="d9315962-7987-4ee7-905d-6972c78ce8a1")
+ def test_eap_connect_with_config_ttls_gtc(self):
+ config = dict(self.config_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.GTC.value
+ wutils.wifi_connect(self.dut, config)
+
+ @test_tracker_info(uuid="90a67bd3-30da-4daf-8ab0-d964d7ad19be")
+ def test_eap_connect_with_config_peap0_mschapv2(self):
+ config = dict(self.config_peap0)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.MSCHAPV2.value
+ wutils.wifi_connect(self.dut, config)
+
+ @test_tracker_info(uuid="3c451ba4-0c83-4eef-bc95-db4c21893008")
+ def test_eap_connect_with_config_peap0_gtc(self):
+ config = dict(self.config_peap0)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.GTC.value
+ wutils.wifi_connect(self.dut, config)
+
+ @test_tracker_info(uuid="6b45157d-0325-417a-af18-11af5d240d79")
+ def test_eap_connect_with_config_peap1_mschapv2(self):
+ config = dict(self.config_peap1)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.MSCHAPV2.value
+ wutils.wifi_connect(self.dut, config)
+
+ @test_tracker_info(uuid="1663decc-71ae-4f95-a027-8a6dbf9c337f")
+ def test_eap_connect_with_config_peap1_gtc(self):
+ config = dict(self.config_peap1)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.GTC.value
+ wutils.wifi_connect(self.dut, config)
+
+ # EAP connect negative tests
+ """ Test connecting to enterprise networks.
+
+ Procedures:
+ For each enterprise wifi network
+ 1. Connect to the network with invalid credentials.
+
+ Expect:
+ Fail to establish connection.
+ """
+ @test_tracker_info(uuid="b2a91f1f-ccd7-4bd1-ab81-19aab3d8ee38")
+ def test_eap_connect_negative_with_config_tls(self):
+ config = self.gen_negative_eap_configs(self.config_tls)
+ self.eap_negative_connect_logic(config, self.dut)
+
+ @test_tracker_info(uuid="6466abde-1d16-4168-9dd8-1e7a0a19889b")
+ def test_eap_connect_negative_with_config_pwd(self):
+ config = self.gen_negative_eap_configs(self.config_pwd)
+ self.eap_negative_connect_logic(config, self.dut)
+
+ @test_tracker_info(uuid="d7742a2a-85b0-409a-99d8-47711ddc5612")
+ def test_eap_connect_negative_with_config_sim(self):
+ config = self.gen_negative_eap_configs(self.config_sim)
+ self.eap_negative_connect_logic(config, self.dut)
+
+ @test_tracker_info(uuid="0ec0de93-cab3-4f41-960b-c0af64ff48c4")
+ def test_eap_connect_negative_with_config_aka(self):
+ config = self.gen_negative_eap_configs(self.config_aka)
+ self.eap_negative_connect_logic(config, self.dut)
+
+ @test_tracker_info(uuid="bb640ea4-32a6-48ea-87c9-f7128fffbbf6")
+ def test_eap_connect_negative_with_config_aka_prime(self):
+ config = self.gen_negative_eap_configs(self.config_aka_prime)
+ self.eap_negative_connect_logic(config, self.dut)
+
+ @test_tracker_info(uuid="86336ada-0ced-45a4-8a22-c4aa23c81111")
+ def test_eap_connect_negative_with_config_ttls_none(self):
+ config = dict(self.config_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.NONE.value
+ config = self.gen_negative_eap_configs(config)
+ self.eap_negative_connect_logic(config, self.dut)
+
+ @test_tracker_info(uuid="71e0498d-9973-4958-94bd-79051c328920")
+ def test_eap_connect_negative_with_config_ttls_pap(self):
+ config = dict(self.config_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.PAP.value
+ config = self.gen_negative_eap_configs(config)
+ self.eap_negative_connect_logic(config, self.dut)
+
+ @test_tracker_info(uuid="c04142a8-b204-4d2d-98dc-150b16c8397e")
+ def test_eap_connect_negative_with_config_ttls_mschap(self):
+ config = dict(self.config_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.MSCHAP.value
+ config = self.gen_negative_eap_configs(config)
+ self.eap_negative_connect_logic(config, self.dut)
+
+ @test_tracker_info(uuid="625e7aa5-e3e6-4bbe-98c0-5aad8ca1555b")
+ def test_eap_connect_negative_with_config_ttls_mschapv2(self):
+ config = dict(self.config_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.MSCHAPV2.value
+ config = self.gen_negative_eap_configs(config)
+ self.eap_negative_connect_logic(config, self.dut)
+
+ @test_tracker_info(uuid="24ea0d80-0a3f-41c2-8e05-d6387e589058")
+ def test_eap_connect_negative_with_config_ttls_gtc(self):
+ config = dict(self.config_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.GTC.value
+ config = self.gen_negative_eap_configs(config)
+ self.eap_negative_connect_logic(config, self.dut)
+
+ @test_tracker_info(uuid="b7c1f0f8-6338-4501-8e1d-c9b136aaba88")
+ def test_eap_connect_negative_with_config_peap0_mschapv2(self):
+ config = dict(self.config_peap0)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.MSCHAPV2.value
+ config = self.gen_negative_eap_configs(config)
+ self.eap_negative_connect_logic(config, self.dut)
+
+ @test_tracker_info(uuid="9cf83dcb-38ad-4f75-9ea9-98de1cfaf7f3")
+ def test_eap_connect_negative_with_config_peap0_gtc(self):
+ config = dict(self.config_peap0)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.GTC.value
+ config = self.gen_negative_eap_configs(config)
+ self.eap_negative_connect_logic(config, self.dut)
+
+ @test_tracker_info(uuid="89bb2b6b-d073-402a-bdc1-68ac5f8752a3")
+ def test_eap_connect_negative_with_config_peap1_mschapv2(self):
+ config = dict(self.config_peap1)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.MSCHAPV2.value
+ config = self.gen_negative_eap_configs(config)
+ self.eap_negative_connect_logic(config, self.dut)
+
+ @test_tracker_info(uuid="2252a864-9ff7-43b5-82d9-afe57d1f5e5f")
+ def test_eap_connect_negative_with_config_peap1_gtc(self):
+ config = dict(self.config_peap1)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.GTC.value
+ config = self.gen_negative_eap_configs(config)
+ self.eap_negative_connect_logic(config, self.dut)
+
+ # EAP connect config store tests
+ """ Test connecting to enterprise networks of different authentication
+ types after wifi toggle.
+
+ The authentication types tested are:
+ EAP-TLS
+ EAP-PEAP with different phase2 types.
+ EAP-TTLS with different phase2 types.
+
+ Procedures:
+ For each enterprise wifi network
+ 1. Connect to the network.
+ 2. Send a GET request to a website and check response.
+ 3. Toggle wifi.
+ 4. Ensure that the device reconnects to the same network.
+
+ Expect:
+ Successful connection and Internet access through the enterprise
+ networks.
+ """
+ @test_tracker_info(uuid="2a933b7f-27d7-4201-a34f-25b9d8072a8c")
+ def test_eap_connect_config_store_with_config_tls(self):
+ self.eap_connect_toggle_wifi(self.config_tls, self.dut)
+
+ @test_tracker_info(uuid="08dc071b-9fea-408a-a3f6-d3493869f6d4")
+ def test_eap_connect_config_store_with_config_pwd(self):
+ self.eap_connect_toggle_wifi(self.config_pwd, self.dut)
+
+ @test_tracker_info(uuid="230cb03e-58bc-41cb-b9b3-7215c2ab2325")
+ def test_eap_connect_config_store_with_config_sim(self):
+ self.eap_connect_toggle_wifi(self.config_sim, self.dut)
+
+ @test_tracker_info(uuid="dfc3e59c-2309-4598-8c23-bb3fe95ef89f")
+ def test_eap_connect_config_store_with_config_aka(self):
+ self.eap_connect_toggle_wifi(self.config_aka, self.dut)
+
+ @test_tracker_info(uuid="6050a1d1-4f3a-476d-bf93-638abd066790")
+ def test_eap_connect_config_store_with_config_aka_prime(self):
+ self.eap_connect_toggle_wifi(self.config_aka_prime, self.dut)
+
+ @test_tracker_info(uuid="03108057-cc44-4a80-8331-77c93694099c")
+ def test_eap_connect_config_store_with_config_ttls_none(self):
+ config = dict(self.config_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.NONE.value
+ self.eap_connect_toggle_wifi(config, self.dut)
+
+ @test_tracker_info(uuid="53dd8195-e272-4589-a261-b8fa3607ad8d")
+ def test_eap_connect_config_store_with_config_ttls_pap(self):
+ config = dict(self.config_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.PAP.value
+ self.eap_connect_toggle_wifi(config, self.dut)
+
+ @test_tracker_info(uuid="640f697b-9c62-4b19-bd76-53b236a152e0")
+ def test_eap_connect_config_store_with_config_ttls_mschap(self):
+ config = dict(self.config_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.MSCHAP.value
+ self.eap_connect_toggle_wifi(config, self.dut)
+
+ @test_tracker_info(uuid="f0243684-fae0-46f3-afbd-bf525fc712e2")
+ def test_eap_connect_config_store_with_config_ttls_mschapv2(self):
+ config = dict(self.config_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.MSCHAPV2.value
+ self.eap_connect_toggle_wifi(config, self.dut)
+
+ @test_tracker_info(uuid="49ec7202-3b00-49c3-970a-201360888c74")
+ def test_eap_connect_config_store_with_config_ttls_gtc(self):
+ config = dict(self.config_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.GTC.value
+ self.eap_connect_toggle_wifi(config, self.dut)
+
+ @test_tracker_info(uuid="1c6abfa3-f344-4e28-b891-5481ab79efcf")
+ def test_eap_connect_config_store_with_config_peap0_mschapv2(self):
+ config = dict(self.config_peap0)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.MSCHAPV2.value
+ self.eap_connect_toggle_wifi(config, self.dut)
+
+ @test_tracker_info(uuid="2815bc76-49fa-43a5-a4b6-84788f9809d5")
+ def test_eap_connect_config_store_with_config_peap0_gtc(self):
+ config = dict(self.config_peap0)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.GTC.value
+ self.eap_connect_toggle_wifi(config, self.dut)
+
+ @test_tracker_info(uuid="e93f7472-6895-4e36-bff2-9b2dcfd07ad0")
+ def test_eap_connect_config_store_with_config_peap1_mschapv2(self):
+ config = dict(self.config_peap1)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.MSCHAPV2.value
+ self.eap_connect_toggle_wifi(config, self.dut)
+
+ @test_tracker_info(uuid="6da72fa0-b858-4475-9559-46fe052d0d64")
+ def test_eap_connect_config_store_with_config_peap1_gtc(self):
+ config = dict(self.config_peap1)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.GTC.value
+ self.eap_connect_toggle_wifi(config, self.dut)
+
+ # Removing 'test_' for all passpoint based testcases as we want to disable
+ # them. Adding the valid test cases to self.tests make them run in serial
+ # (TODO): gmoturu - Update the passpoint tests to test the valid scenario
+ # Passpoint connect tests
+ """ Test connecting to enterprise networks of different authentication
+ types with passpoint support.
+
+ The authentication types tested are:
+ EAP-TLS
+ EAP-TTLS with MSCHAPV2 as phase2.
+
+ Procedures:
+ For each enterprise wifi network
+ 1. Connect to the network.
+ 2. Send a GET request to a website and check response.
+
+ Expect:
+ Successful connection and Internet access through the enterprise
+ networks with passpoint support.
+ """
+ @test_tracker_info(uuid="0b942524-bde9-4fc6-ac6a-fef1c247cb8e")
+ def passpoint_connect_with_config_passpoint_tls(self):
+ asserts.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
+ "Passpoint is not supported on %s" % self.dut.model)
+ wutils.wifi_connect(self.dut, self.config_passpoint_tls)
+
+ @test_tracker_info(uuid="33a014aa-99e7-4612-b732-54fabf1bf922")
+ def passpoint_connect_with_config_passpoint_ttls_none(self):
+ asserts.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
+ "Passpoint is not supported on %s" % self.dut.model)
+ config = dict(self.config_passpoint_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.NONE.value
+ wutils.wifi_connect(self.dut, config)
+
+ @test_tracker_info(uuid="1aba8bf9-2b09-4956-b418-c3f4dadab330")
+ def passpoint_connect_with_config_passpoint_ttls_pap(self):
+ asserts.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
+ "Passpoint is not supported on %s" % self.dut.model)
+ config = dict(self.config_passpoint_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.PAP.value
+ wutils.wifi_connect(self.dut, config)
+
+ @test_tracker_info(uuid="cd978fc9-a393-4b1e-bba3-1efc52224500")
+ def passpoint_connect_with_config_passpoint_ttls_mschap(self):
+ asserts.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
+ "Passpoint is not supported on %s" % self.dut.model)
+ config = dict(self.config_passpoint_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.MSCHAP.value
+ wutils.wifi_connect(self.dut, config)
+
+ @test_tracker_info(uuid="bc311ee7-ba64-4c76-a629-b916701bf6a5")
+ def passpoint_connect_with_config_passpoint_ttls_mschapv2(self):
+ asserts.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
+ "Passpoint is not supported on %s" % self.dut.model)
+ config = dict(self.config_passpoint_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.MSCHAPV2.value
+ wutils.wifi_connect(self.dut, config)
+
+ @test_tracker_info(uuid="357e5162-5081-4149-bedd-ef2c0f88b97e")
+ def passpoint_connect_with_config_passpoint_ttls_gtc(self):
+ asserts.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
+ "Passpoint is not supported on %s" % self.dut.model)
+ config = dict(self.config_passpoint_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.GTC.value
+ wutils.wifi_connect(self.dut, config)
+
+ # Passpoint connect negative tests
+ """ Test connecting to enterprise networks.
+
+ Procedures:
+ For each enterprise wifi network
+ 1. Connect to the network with invalid credentials.
+
+ Expect:
+ Fail to establish connection.
+ """
+ @test_tracker_info(uuid="7b6b44a0-ff70-49b4-94ca-a98bedc18f92")
+ def passpoint_connect_negative_with_config_passpoint_tls(self):
+ asserts.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
+ "Passpoint is not supported on %s" % self.dut.model)
+ config = self.gen_negative_passpoint_configs(self.config_passpoint_tls)
+ self.eap_negative_connect_logic(config, self.dut)
+
+ @test_tracker_info(uuid="3dbde40a-e88c-4166-b932-163663a10a41")
+ def passpoint_connect_negative_with_config_passpoint_ttls_none(self):
+ asserts.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
+ "Passpoint is not supported on %s" % self.dut.model)
+ config = dict(self.config_passpoint_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.NONE.value
+ config = self.gen_negative_passpoint_configs(config)
+ self.eap_negative_connect_logic(config, self.dut)
+
+ @test_tracker_info(uuid="8ee22ad6-d561-4ca2-a808-9f372fce56b4")
+ def passpoint_connect_negative_with_config_passpoint_ttls_pap(self):
+ asserts.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
+ "Passpoint is not supported on %s" % self.dut.model)
+ config = dict(self.config_passpoint_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.PAP.value
+ config = self.gen_negative_passpoint_configs(config)
+ self.eap_negative_connect_logic(config, self.dut)
+
+ @test_tracker_info(uuid="db5cefe7-9cb8-47a6-8635-006c80b97012")
+ def passpoint_connect_negative_with_config_passpoint_ttls_mschap(self):
+ asserts.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
+ "Passpoint is not supported on %s" % self.dut.model)
+ config = dict(self.config_passpoint_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.MSCHAP.value
+ config = self.gen_negative_passpoint_configs(config)
+ self.eap_negative_connect_logic(config, self.dut)
+
+ @test_tracker_info(uuid="8f49496e-80df-48ce-9c51-42f0c6b81aff")
+ def passpoint_connect_negative_with_config_passpoint_ttls_mschapv2(self):
+ asserts.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
+ "Passpoint is not supported on %s" % self.dut.model)
+ config = dict(self.config_passpoint_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.MSCHAPV2.value
+ config = self.gen_negative_passpoint_configs(config)
+ self.eap_negative_connect_logic(config, self.dut)
+
+ @test_tracker_info(uuid="6561508f-598e-408d-96b6-15b631664be6")
+ def passpoint_connect_negative_with_config_passpoint_ttls_gtc(self):
+ asserts.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
+ "Passpoint is not supported on %s" % self.dut.model)
+ config = dict(self.config_passpoint_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.GTC.value
+ config = self.gen_negative_passpoint_configs(config)
+ self.eap_negative_connect_logic(config, self.dut)
+
+ # Passpoint connect config store tests
+ """ Test connecting to enterprise networks of different authentication
+ types with passpoint support after wifi toggle.
+
+ The authentication types tested are:
+ EAP-TLS
+ EAP-TTLS with MSCHAPV2 as phase2.
+
+ Procedures:
+ For each enterprise wifi network
+ 1. Connect to the network.
+ 2. Send a GET request to a website and check response.
+ 3. Toggle wifi.
+ 4. Ensure that the device reconnects to the same network.
+
+ Expect:
+ Successful connection and Internet access through the enterprise
+ networks with passpoint support.
+ """
+ @test_tracker_info(uuid="5d5e6bb0-faea-4a6e-a6bc-c87de997a4fd")
+ def passpoint_connect_config_store_with_config_passpoint_tls(self):
+ asserts.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
+ "Passpoint is not supported on %s" % self.dut.model)
+ self.eap_connect_toggle_wifi(self.config_passpoint_tls, self.dut)
+
+ @test_tracker_info(uuid="0c80262d-23c1-439f-ad64-7b8ada5d1962")
+ def passpoint_connect_config_store_with_config_passpoint_ttls_none(self):
+ asserts.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
+ "Passpoint is not supported on %s" % self.dut.model)
+ config = dict(self.config_passpoint_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.NONE.value
+ self.eap_connect_toggle_wifi(config, self.dut)
+
+ @test_tracker_info(uuid="786e424c-b5a6-4fe9-a951-b3de16ebb6db")
+ def passpoint_connect_config_store_with_config_passpoint_ttls_pap(self):
+ asserts.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
+ "Passpoint is not supported on %s" % self.dut.model)
+ config = dict(self.config_passpoint_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.PAP.value
+ self.eap_connect_toggle_wifi(config, self.dut)
+
+ @test_tracker_info(uuid="22fd61bf-722a-4016-a778-fc33e94ed211")
+ def passpoint_connect_config_store_with_config_passpoint_ttls_mschap(self):
+ asserts.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
+ "Passpoint is not supported on %s" % self.dut.model)
+ config = dict(self.config_passpoint_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.MSCHAP.value
+ self.eap_connect_toggle_wifi(config, self.dut)
+
+ @test_tracker_info(uuid="2abd348c-9c66-456b-88ad-55f971717620")
+ def passpoint_connect_config_store_with_config_passpoint_ttls_mschapv2(self):
+ asserts.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
+ "Passpoint is not supported on %s" % self.dut.model)
+ config = dict(self.config_passpoint_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.MSCHAPV2.value
+ self.eap_connect_toggle_wifi(config, self.dut)
+
+ @test_tracker_info(uuid="043e8cdd-db95-4f03-b308-3c8cecf874b1")
+ def passpoint_connect_config_store_with_config_passpoint_ttls_gtc(self):
+ asserts.skip_if(not self.dut.droid.wifiIsPasspointSupported(),
+ "Passpoint is not supported on %s" % self.dut.model)
+ config = dict(self.config_passpoint_ttls)
+ config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.GTC.value
+ self.eap_connect_toggle_wifi(config, self.dut)
diff --git a/acts_tests/tests/google/wifi/WifiHiddenSSIDTest.py b/acts_tests/tests/google/wifi/WifiHiddenSSIDTest.py
new file mode 100644
index 0000000..da2c066
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiHiddenSSIDTest.py
@@ -0,0 +1,173 @@
+#!/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 acts.signals
+import acts.test_utils.wifi.wifi_test_utils as wutils
+
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+
+class WifiHiddenSSIDTest(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, configs):
+ super().__init__(configs)
+ self.enable_packet_log = True
+
+ def setup_class(self):
+ super().setup_class()
+
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+
+ 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,
+ owe_network=True,
+ sae_network=True,
+ hidden=True)
+
+ self.open_hidden_2g = self.open_network[0]["2g"]
+ self.open_hidden_5g = self.open_network[0]["5g"]
+ self.wpa_hidden_2g = self.reference_networks[0]["2g"]
+ self.wpa_hidden_5g = self.reference_networks[0]["5g"]
+
+ def setup_test(self):
+ super().setup_test()
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+
+ def teardown_test(self):
+ super().teardown_test()
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+
+ def teardown_class(self):
+ wutils.reset_wifi(self.dut)
+ if "AccessPoint" in self.user_params:
+ del self.user_params["reference_networks"]
+ del self.user_params["open_network"]
+
+ """Helper Functions"""
+
+ def check_hiddenSSID_in_scan(self, ap_ssid, max_tries=2):
+ """Check if the ap started by wifi tethering is seen in scan results.
+
+ Args:
+ ap_ssid: SSID of the ap we are looking for.
+ max_tries: Number of scans to try.
+ Returns:
+ True: if ap_ssid is found in scan results.
+ False: if ap_ssid is not found in scan results.
+ """
+ for num_tries in range(max_tries):
+ wutils.start_wifi_connection_scan(self.dut)
+ scan_results = self.dut.droid.wifiGetScanResults()
+ match_results = wutils.match_networks(
+ {wutils.WifiEnums.SSID_KEY: ap_ssid}, scan_results)
+ if len(match_results) > 0:
+ return True
+ return False
+
+ def add_hiddenSSID_and_connect(self, hidden_network):
+ """Add the hidden network and connect to it.
+
+ Args:
+ hidden_network: The hidden network config to connect to.
+
+ """
+ wutils.connect_to_wifi_network(self.dut, hidden_network, hidden=True)
+ if not wutils.validate_connection(self.dut):
+ raise signals.TestFailure("Fail to connect to internet on %s" %
+ hidden_network)
+
+ """Tests"""
+
+ @test_tracker_info(uuid="d0871f98-6049-4937-a288-ec4a2746c771")
+ def test_connect_to_wpa_hidden_2g(self):
+ """Connect to a WPA, 2G network.
+
+ Steps:
+ 1. Add a WPA, 2G hidden network.
+ 2. Ensure the network is visible in scan.
+ 3. Connect and run ping.
+
+ """
+ self.add_hiddenSSID_and_connect(self.wpa_hidden_2g)
+
+ @test_tracker_info(uuid="c558b31a-549a-4012-9052-275623992187")
+ def test_connect_to_wpa_hidden_5g(self):
+ """Connect to a WPA, 5G hidden network.
+
+ Steps:
+ 1. Add a WPA, 5G hidden network.
+ 2. Ensure the network is visible in scan.
+ 3. Connect and run ping.
+
+ """
+ self.add_hiddenSSID_and_connect(self.wpa_hidden_5g)
+
+ @test_tracker_info(uuid="cdfce76f-6374-439d-aa1d-e920508269d2")
+ def test_connect_to_open_hidden_2g(self):
+ """Connect to a Open, 2G hidden network.
+
+ Steps:
+ 1. Add a Open, 2G hidden network.
+ 2. Ensure the network is visible in scan.
+ 3. Connect and run ping.
+
+ """
+ self.add_hiddenSSID_and_connect(self.open_hidden_2g)
+
+ @test_tracker_info(uuid="29ccbae4-4382-4df8-8fc5-00e3104230d0")
+ def test_connect_to_open_hidden_5g(self):
+ """Connect to a Open, 5G hidden network.
+
+ Steps:
+ 1. Add a Open, 5G hidden network.
+ 2. Ensure the network is visible in scan.
+ 3. Connect and run ping.
+
+ """
+ self.add_hiddenSSID_and_connect(self.open_hidden_5g)
+
+ @test_tracker_info(uuid="62b664df-6397-4360-97bf-a8095c23a878")
+ def test_connect_to_wpa3_owe_hidden_2g(self):
+ """Connect to WPA3 OWE 2G hidden wifi network."""
+ self.add_hiddenSSID_and_connect(self.owe_networks[0]["2g"])
+
+ @test_tracker_info(uuid="dd7b029d-c008-4288-a91c-0820b0b3f29d")
+ def test_connect_to_wpa3_owe_hidden_5g(self):
+ """Connect to WPA3 OWE 5G hidden wifi network."""
+ self.add_hiddenSSID_and_connect(self.owe_networks[0]["5g"])
+
+ @test_tracker_info(uuid="1a9f3ee8-3db0-4f07-a604-66c14a897f94")
+ def test_connect_to_wpa3_sae_hidden_2g(self):
+ """Connect to WPA3 SAE 2G hidden wifi network."""
+ self.add_hiddenSSID_and_connect(self.sae_networks[0]["2g"])
+
+ @test_tracker_info(uuid="6c75618b-9c9b-4eb6-a922-ef1719704a9c")
+ def test_connect_to_wpa3_sae_hidden_5g(self):
+ """Connect to WPA3 SAE 5G hidden wifi network."""
+ self.add_hiddenSSID_and_connect(self.sae_networks[0]["5g"])
diff --git a/acts_tests/tests/google/wifi/WifiIFSTwTest.py b/acts_tests/tests/google/wifi/WifiIFSTwTest.py
new file mode 100644
index 0000000..ac2022f
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiIFSTwTest.py
@@ -0,0 +1,297 @@
+#
+# 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
+import os
+
+import time
+
+import threading
+from acts import utils
+from acts import signals
+from acts import asserts
+from acts.controllers import attenuator
+from acts.controllers.sl4a_lib import rpc_client
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.net.net_test_utils import start_tcpdump, stop_tcpdump
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi.wifi_test_utils import WifiEnums
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts.utils import stop_standing_subprocess
+
+TCPDUMP_PATH = '/data/local/tmp/tcpdump'
+
+
+class WifiIFSTwTest(WifiBaseTest):
+ """Tests for wifi IFS
+
+ Test Bed Requirement:
+ *One Android device
+ *Two Visible Wi-Fi Access Points
+ *One attenuator with 4 ports
+ """
+
+ def setup_class(self):
+ """Setup required dependencies from config file and configure
+ the required networks for testing roaming.
+
+ Returns:
+ True if successfully configured the requirements for testing.
+ """
+ super().setup_class()
+ self.simulation_thread_running = False
+ self.atten_roaming_count = 0
+ self.start_db = 30
+ self.roaming_cycle_seconds = 20
+ self.fail_count = 0
+ self.retry_pass_count = 0
+ self.ping_count = 0
+
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ req_params = ["attenuators", "ifs_params"]
+ opt_param = []
+
+ 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(ap_count=2, same_ssid=True)
+
+ wutils.wifi_toggle_state(self.dut, True)
+ if "ifs_params" in self.user_params:
+ self.attn_start_db = self.ifs_params[0]["start_db"]
+ self.gateway = self.ifs_params[0]["gateway"]
+ self.roaming_cycle_seconds = self.ifs_params[0][
+ "roaming_cycle_seconds"]
+ self.total_test_hour = self.ifs_params[0]["total_test_hour"]
+ self.log_capture_period_hour = self.ifs_params[0][
+ "log_capture_period_hour"]
+ self.on_active_port = self.ifs_params[0]["on_active_port"]
+ asserts.assert_true(
+ len(self.on_active_port) == 2, "Need setup 2 port.")
+
+ self.tcpdump_pid = None
+ utils.set_location_service(self.dut, True)
+
+ def setup_test(self):
+ super().setup_test()
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ self.dut.unlock_screen()
+ self.tcpdump_pid = start_tcpdump(self.dut, self.test_name)
+
+ def teardown_class(self):
+ self.dut.ed.clear_all_events()
+
+ def teardown_test(self):
+ super().teardown_test()
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ wutils.reset_wifi(self.dut)
+
+ def simulate_roaming(self):
+ """
+ To simulate user move between ap1 and ap2:
+
+ 1. Move to ap2:
+ Set ap1's signal attenuation gradually changed from 0 to max_db
+ Set ap2's signal attenuation gradually changed from start_db to 0
+
+ 2. Then move to ap1:
+ Set ap1's signal attenuation gradually changed from start_db to 0
+ Set ap2's signal attenuation gradually changed from 0 to max_db
+
+ * 0<start_db<max_db
+ """
+ attn_max = 95
+ attn_min = 0
+
+ #on_active_port value should between [0-1,2-3]
+ active_attenuator = {
+ "1": self.attenuators[self.on_active_port[0]],
+ "2": self.attenuators[self.on_active_port[1]]
+ }
+
+ for attenuator in self.attenuators:
+ attenuator.set_atten(attn_max)
+
+ self.simulation_thread_running = True
+ while self.simulation_thread_running:
+ active_attenuator["1"].set_atten(attn_min)
+ active_attenuator["2"].set_atten(attn_max)
+ self.log_attens()
+ time.sleep(10)
+
+ active_attenuator["2"].set_atten(self.start_db)
+ self.log_attens()
+ time.sleep(5)
+ for i in range(self.roaming_cycle_seconds):
+ db1 = math.ceil(attn_max / self.roaming_cycle_seconds *
+ (i + 1))
+ db2 = self.start_db - math.ceil(
+ self.start_db / self.roaming_cycle_seconds * (i + 1))
+ active_attenuator["1"].set_atten(db1)
+ active_attenuator["2"].set_atten(db2)
+ self.log_attens()
+ time.sleep(1)
+
+ active_attenuator["1"].set_atten(self.start_db)
+ self.log_attens()
+ time.sleep(5)
+ for i in range(self.roaming_cycle_seconds):
+ db1 = math.ceil(attn_max / self.roaming_cycle_seconds *
+ (i + 1))
+ db2 = self.start_db - math.ceil(
+ self.start_db / self.roaming_cycle_seconds * (i + 1))
+ active_attenuator["1"].set_atten(db2)
+ active_attenuator["2"].set_atten(db1)
+ self.log_attens()
+ time.sleep(1)
+ self.atten_roaming_count += 1
+
+ def catch_log(self):
+ """Capture logs include bugreport, ANR, mount,ps,vendor,tcpdump"""
+
+ self.log.info("Get log for regular capture.")
+ file_name = time.strftime("%Y-%m-%d_%H:%M:%S", time.localtime())
+ current_path = os.path.join(self.dut.log_path, file_name)
+ os.makedirs(current_path, exist_ok=True)
+ serial_number = self.dut.serial
+
+ try:
+ out = self.dut.adb.shell("bugreportz", timeout=240)
+ if not out.startswith("OK"):
+ raise AndroidDeviceError(
+ 'Failed to take bugreport on %s: %s' % (serial_number,
+ out),
+ serial=serial_number)
+ br_out_path = out.split(':')[1].strip().split()[0]
+ self.dut.adb.pull("%s %s" % (br_out_path, self.dut.log_path))
+ self.dut.adb.pull("/data/anr {}".format(current_path), timeout=600)
+ self.dut.adb._exec_adb_cmd("shell", "mount > {}".format(
+ os.path.join(current_path, "mount.txt")))
+ self.dut.adb._exec_adb_cmd("shell", "ps > {}".format(
+ os.path.join(current_path, "ps.txt")))
+ self.dut.adb.pull("/data/misc/logd {}".format(current_path))
+ self.dut.adb.pull(
+ "/data/vendor {}".format(current_path), timeout=800)
+ stop_tcpdump(
+ self.dut, self.tcpdump_pid, file_name, adb_pull_timeout=600)
+ self.tcpdump_pid = start_tcpdump(self.dut, file_name)
+ except TimeoutError as e:
+ self.log.error(e)
+
+ def http_request(self, url="https://www.google.com/"):
+ """Get the result via string from target url
+
+ Args:
+ url: target url to loading
+
+ Returns:
+ True if http_request pass
+ """
+
+ self.ping_count += 1
+ try:
+ self.dut.droid.httpRequestString(url)
+ self.log.info("httpRequest Finish")
+ time.sleep(1)
+ return True
+ except rpc_client.Sl4aApiError as e:
+ self.log.warning("httpRequest Fail.")
+ self.log.warning(e)
+ # Set check delay if http request fail during device roaming.
+ # Except finish roaming within 10s.
+ time.sleep(10)
+ self.log.warning("Ping Google DNS response : {}".format(
+ self.can_ping("8.8.8.8")))
+ for gate in self.gateway:
+ ping_result = self.can_ping(gate)
+ self.log.warning("Ping AP Gateway[{}] response : {}".format(
+ gate, ping_result))
+ if ping_result:
+ self.retry_pass_count += 1
+ return True
+ self.fail_count += 1
+ return False
+
+ def log_attens(self):
+ """Log DB from channels"""
+
+ attenuation = ', '.join('{:>5.2f}dB '.format(atten.get_atten())
+ for atten in self.attenuators)
+ self.log.debug('[Attenuation] %s', attenuation)
+
+ def can_ping(self, ip_addr):
+ """A function to check ping pass.
+
+ Args:
+ ip_addr: target ip address to ping
+
+ Returns:
+ True if ping pass
+ """
+ ping_result = self.dut.adb.shell("ping -c 1 {}".format(ip_addr))
+ return '0%' in ping_result.split(' ')
+
+ def browsing_test(self, stress_hour_time):
+ """Continue stress http_request and capture log if any fail
+
+ Args:
+ stress_hour_time: hour of time to stress http_request
+ """
+ t = threading.Thread(target=self.simulate_roaming)
+ t.start()
+ start_time = time.time()
+ http_request_failed = False
+ while time.time() < start_time + stress_hour_time * 3600:
+ if not self.http_request():
+ http_request_failed = True
+ self.simulation_thread_running = False
+ t.join()
+ if http_request_failed:
+ self.catch_log()
+ else:
+ stop_standing_subprocess(self.tcpdump_pid)
+ file_name = time.strftime("%Y-%m-%d_%H:%M:%S", time.localtime())
+ self.tcpdump_pid = start_tcpdump(self.dut, file_name)
+
+ def test_roaming(self):
+ network = self.reference_networks[0]["2g"]
+ wutils.connect_to_wifi_network(self.dut, network)
+
+ time.sleep(10)
+ test_time_slot = int(
+ self.total_test_hour / self.log_capture_period_hour)
+ edge_time_slot = int(
+ self.total_test_hour % self.log_capture_period_hour)
+
+ for i in range(test_time_slot):
+ self.browsing_test(self.log_capture_period_hour)
+ if edge_time_slot:
+ self.browsing_test(edge_time_slot)
+
+ self.log.info("Total roaming times: {}".format(
+ self.atten_roaming_count))
+ self.log.info("Total ping times: {}".format(self.ping_count))
+ self.log.info("Retry pass times: {}".format(self.retry_pass_count))
+ self.log.info("Total fail times: {}".format(self.fail_count))
+ if self.fail_count:
+ signals.TestFailure(
+ 'Find roaming fail condition',
+ extras={
+ 'Roaming fail times': self.fail_count
+ })
diff --git a/acts_tests/tests/google/wifi/WifiIOTTest.py b/acts_tests/tests/google/wifi/WifiIOTTest.py
new file mode 100644
index 0000000..3ea3b3b
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiIOTTest.py
@@ -0,0 +1,374 @@
+#!/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 time
+
+import acts.signals
+import acts.test_utils.wifi.wifi_test_utils as wutils
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+WifiEnums = wutils.WifiEnums
+
+
+class WifiIOTTest(WifiBaseTest):
+ """ Tests for wifi IOT
+
+ Test Bed Requirement:
+ * One Android device
+ * Wi-Fi IOT networks visible to the device
+ """
+
+ def setup_class(self):
+ super().setup_class()
+
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+
+ req_params = [ "iot_networks", ]
+ opt_params = [ "open_network", "iperf_server_address" ]
+ self.unpack_userparams(req_param_names=req_params,
+ opt_param_names=opt_params)
+
+ asserts.assert_true(
+ len(self.iot_networks) > 0,
+ "Need at least one iot network with psk.")
+
+ if getattr(self, 'open_network', False):
+ self.iot_networks.append(self.open_network)
+
+ wutils.wifi_toggle_state(self.dut, True)
+ if "iperf_server_address" in self.user_params:
+ self.iperf_server = self.iperf_servers[0]
+ self.iperf_server.start()
+
+ # create hashmap for testcase name and SSIDs
+ self.iot_test_prefix = "test_iot_connection_to_"
+ self.ssid_map = {}
+ for network in self.iot_networks:
+ SSID = network['SSID'].replace('-','_')
+ self.ssid_map[SSID] = network
+
+ def setup_test(self):
+ super().setup_test()
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+
+ def teardown_test(self):
+ super().teardown_test()
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ wutils.reset_wifi(self.dut)
+
+ def teardown_class(self):
+ if "iperf_server_address" in self.user_params:
+ self.iperf_server.stop()
+
+ """Helper Functions"""
+
+ def connect_to_wifi_network(self, network):
+ """Connection logic for open and psk wifi networks.
+
+ Args:
+ params: Dictionary with network info.
+ """
+ SSID = network[WifiEnums.SSID_KEY]
+ self.dut.ed.clear_all_events()
+ wutils.start_wifi_connection_scan(self.dut)
+ scan_results = self.dut.droid.wifiGetScanResults()
+ wutils.assert_network_in_list({WifiEnums.SSID_KEY: SSID}, scan_results)
+ wutils.wifi_connect(self.dut, network, num_of_tries=3)
+
+ def run_iperf_client(self, network):
+ """Run iperf traffic after connection.
+
+ Args:
+ params: Dictionary with network info.
+ """
+ if "iperf_server_address" in self.user_params:
+ wait_time = 5
+ SSID = network[WifiEnums.SSID_KEY]
+ self.log.info("Starting iperf traffic through {}".format(SSID))
+ time.sleep(wait_time)
+ port_arg = "-p {}".format(self.iperf_server.port)
+ success, data = self.dut.run_iperf_client(self.iperf_server_address,
+ port_arg)
+ self.log.debug(pprint.pformat(data))
+ asserts.assert_true(success, "Error occurred in iPerf traffic.")
+
+ def connect_to_wifi_network_and_run_iperf(self, network):
+ """Connection logic for open and psk wifi networks.
+
+ Logic steps are
+ 1. Connect to the network.
+ 2. Run iperf traffic.
+
+ Args:
+ params: A dictionary with network info.
+ """
+ self.connect_to_wifi_network(network)
+ self.run_iperf_client(network)
+
+ """Tests"""
+
+ @test_tracker_info(uuid="a57cc861-b6c2-47e4-9db6-7a3ab32c6e20")
+ def test_iot_connection_to_ubiquity_ap1_2g(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="2065c2f7-2b89-4da7-a15d-e5dc17b88d52")
+ def test_iot_connection_to_ubiquity_ap1_5g(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="6870e35b-f7a7-45bf-b021-fea049ae53de")
+ def test_iot_connection_to_AirportExpress_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="95f4b405-79d7-4873-a152-4384acc88f41")
+ def test_iot_connection_to_AirportExpress_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="02a8cc75-6781-4153-8d90-bed7568a1e78")
+ def test_iot_connection_to_AirportExtreme_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="83a42c97-1358-4ba7-bdb2-238fdb1c945e")
+ def test_iot_connection_to_AirportExtreme_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="d56cc46a-f772-4c96-b84e-4e05c82f5f9d")
+ def test_iot_connection_to_AirportExtremeOld_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="4b57277d-ea96-4379-bd71-8b4f03253ec8")
+ def test_iot_connection_to_AirportExtremeOld_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="2503d9ed-35df-4be0-b838-590324cecaee")
+ def iot_connection_to_Dlink_AC1200_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="0a44e148-a4bf-43f4-88eb-e4c1ffa850ce")
+ def iot_connection_to_Dlink_AC1200_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="6bd77417-089f-4fb1-b4c2-2cd673c64bcb")
+ def test_iot_connection_to_Dlink_AC3200_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="9fbff6e7-36c8-4342-9c29-bce6a8ef04ec")
+ def test_iot_connection_to_Dlink_AC3200_5G_1(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="bfccdaa9-8e01-488c-9768-8c71ab5ec157")
+ def test_iot_connection_to_Dlink_AC3200_5G_2(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="0e4978de-0435-4856-ae5a-c39cc64e375b")
+ def test_iot_connection_to_Dlink_N750_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="cdb82797-9981-4ba6-8958-025f59c60e83")
+ def test_iot_connection_to_Dlink_N750_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="0bf8f129-eb96-4b1e-94bd-8dd93e8731e3")
+ def iot_connection_to_Linksys_E800_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="f231216d-6ab6-46b7-a0a5-1ac15935e412")
+ def test_iot_connection_to_Linksys_AC1900_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="5acd4bec-b210-4b4c-8b2c-60f3f67798a9")
+ def test_iot_connection_to_Linksys_AC1900_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="f4fd9877-b13f-47b0-9523-1ce363200c2f")
+ def iot_connection_to_Linksys_AC2400_2g(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="438d679a-4f6c-476d-9eba-63b6f1f2bef4")
+ def iot_connection_to_Linksys_AC2400_5g(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="b9bc00d8-46c5-4c5e-bd58-93ab1ca8d53b")
+ def iot_connection_to_NETGEAR_AC1900_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="fb4c7d80-4c12-4b08-a40a-2745e2bd167b")
+ def iot_connection_to_NETGEAR_AC1900_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="054d2ffc-97fd-4613-bf47-acedd0fa4701")
+ def test_iot_connection_to_NETGEAR_AC3200_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="d15a789a-def5-4c6a-b59e-1a75f73cc6a9")
+ def test_iot_connection_to_NETGEAR_AC3200_5G_1(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="1de6369e-97da-479f-b17c-9144bb814f51")
+ def test_iot_connection_to_NETGEAR_AC3200_5G_2(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="008ec18e-fd48-4359-8a0d-223c921a1faa")
+ def iot_connection_to_NETGEAR_N300_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="c61eeaf0-af02-46bf-bcec-871e2f9dee71")
+ def iot_connection_to_WNDR4500v2_AES_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="dcad3474-4022-48bc-8529-07321611b616")
+ def iot_connection_to_WNDR4500v2_WEP_SHARED128_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="3573a880-4542-4dea-9909-aa2f9865a059")
+ def iot_connection_to_ARCHER_WEP_OPEN_64_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="9c15c52e-945a-4b9b-bf0e-5bd6293dad1c")
+ def iot_connection_to_ARCHER_WEP_OPEN_128_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="e5517b82-c225-449d-83ac-055a561a764f")
+ def test_iot_connection_to_TP_LINK_AC1700_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="9531d3cc-129d-4501-a5e3-d7502120cd8b")
+ def test_iot_connection_to_TP_LINK_AC1700_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="eab810d4-8e07-49c9-86c1-cb8d1a0285d0")
+ def iot_connection_to_TP_LINK_N300_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="05d4cb25-a58d-46b4-a5ff-6e3fe28f2b16")
+ def iot_connection_to_fritz_7490_5g(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="8333e5e6-72fd-4957-bab0-fa45ce1d765a")
+ def iot_connection_to_NETGEAR_R8500_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="c88053fb-730f-4447-a802-1fb9721f69df")
+ def iot_connection_to_NETGEAR_R8500_5G1(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="f5d1e44b-396b-4976-bb0c-160bdce89a59")
+ def iot_connection_to_NETGEAR_R8500_5G2(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="7c12f943-d9e2-45b1-aa84-fcb43efbbb04")
+ def test_iot_connection_to_TP_LINK_5504_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="52be6b76-5e43-4289-83e1-4cd0d995d39b")
+ def test_iot_connection_to_TP_LINK_5504_5G_1(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="0b43d1da-e207-443d-b16c-c4ee3e924036")
+ def test_iot_connection_to_TP_LINK_5504_5G_2(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="4adcef5c-589a-4398-a28c-28a56d762f72")
+ def test_iot_connection_to_TP_LINK_C2600_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="3955a443-505b-4015-9daa-f52abbad8377")
+ def test_iot_connection_to_TP_LINK_C2600_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="3e9115dd-adb6-40a4-9831-dca8f1f32abe")
+ def test_iot_connection_to_Linksys06832_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="5dca028a-7384-444f-b231-973054afe215")
+ def test_iot_connection_to_Linksys06832_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="e639f6db-ad8e-4b4f-91f3-10acdf93142a")
+ def test_iot_connection_to_AmpedAthena_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="3dd90d80-952f-4f17-a48a-fe42e7d6e1ff")
+ def test_iot_connection_to_AmpedAthena_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="b9babe3a-ecba-4c5c-bc6b-0ba48c744e66")
+ def test_iot_connection_to_ASUS_AC3100_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="f8f06f92-821d-4e80-8f1e-efb6c6adc12a")
+ def test_iot_connection_to_ASUS_AC3100_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="f4d227df-1151-469a-b01c-f4b1c1f7a84b")
+ def iot_connection_to_NETGEAR_WGR614_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
diff --git a/acts_tests/tests/google/wifi/WifiIOTTwPkg1Test.py b/acts_tests/tests/google/wifi/WifiIOTTwPkg1Test.py
new file mode 100644
index 0000000..ed0d8d7
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiIOTTwPkg1Test.py
@@ -0,0 +1,360 @@
+#!/usr/bin/env python3
+#
+# 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 time
+
+import acts.signals
+import acts.test_utils.wifi.wifi_test_utils as wutils
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts.controllers import iperf_server as ipf
+
+import json
+import logging
+import math
+import os
+from acts import utils
+import csv
+
+WifiEnums = wutils.WifiEnums
+
+
+class WifiIOTTwPkg1Test(WifiBaseTest):
+ """ Tests for wifi IOT
+
+ Test Bed Requirement:
+ * One Android device
+ * Wi-Fi IOT networks visible to the device
+ """
+
+ def setup_class(self):
+ super().setup_class()
+
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+
+ req_params = [ "iot_networks", ]
+ opt_params = [ "open_network",
+ "iperf_server_address","iperf_port_arg",
+ "pdu_address" , "pduon_wait_time","pduon_address"
+ ]
+ self.unpack_userparams(req_param_names=req_params,
+ opt_param_names=opt_params)
+
+ asserts.assert_true(
+ len(self.iot_networks) > 0,
+ "Need at least one iot network with psk.")
+
+ if getattr(self, 'open_network', False):
+ self.iot_networks.append(self.open_network)
+
+ wutils.wifi_toggle_state(self.dut, True)
+ if "iperf_server_address" in self.user_params:
+ self.iperf_server = self.iperf_servers[0]
+
+ # create hashmap for testcase name and SSIDs
+ self.iot_test_prefix = "test_iot_connection_to_"
+ self.ssid_map = {}
+ for network in self.iot_networks:
+ SSID = network['SSID'].replace('-','_')
+ self.ssid_map[SSID] = network
+
+ # create folder for IOT test result
+ self.log_path = os.path.join(logging.log_path, "IOT_results")
+ os.makedirs(self.log_path, exist_ok=True)
+
+ Header=("test_name","throughput_TX","throughput_RX")
+ self.csv_write(Header)
+
+ # check pdu_address
+ if "pdu_address" and "pduon_wait_time" in self.user_params:
+ self.pdu_func()
+
+ def setup_test(self):
+ super().setup_test()
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+
+ def teardown_test(self):
+ super().teardown_test()
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ wutils.reset_wifi(self.dut)
+
+ def teardown_class(self):
+ if "iperf_server_address" in self.user_params:
+ self.iperf_server.stop()
+
+ """Helper Functions"""
+
+ def connect_to_wifi_network(self, network):
+ """Connection logic for open and psk wifi networks.
+
+ Args:
+ params: Dictionary with network info.
+ """
+ SSID = network[WifiEnums.SSID_KEY]
+ self.dut.ed.clear_all_events()
+ wutils.start_wifi_connection_scan(self.dut)
+ scan_results = self.dut.droid.wifiGetScanResults()
+ wutils.assert_network_in_list({WifiEnums.SSID_KEY: SSID}, scan_results)
+ wutils.wifi_connect(self.dut, network, num_of_tries=3)
+
+ def run_iperf_client(self, network):
+ """Run iperf TX throughput after connection.
+
+ Args:
+ params: Dictionary with network info.
+ """
+ if "iperf_server_address" in self.user_params:
+
+ # Add iot_result
+ iot_result = []
+ self.iperf_server.start(tag="TX_server_{}".format(
+ self.current_test_name))
+ wait_time = 5
+ SSID = network[WifiEnums.SSID_KEY]
+ self.log.info("Starting iperf traffic TX through {}".format(SSID))
+ time.sleep(wait_time)
+ port_arg = "-p {} -J {}".format(self.iperf_server.port,self.iperf_port_arg)
+ success, data = self.dut.run_iperf_client(self.iperf_server_address,
+ port_arg)
+ # Parse and log result
+ client_output_path = os.path.join(
+ self.iperf_server.log_path, "IperfDUT,{},TX_client_{}".format(
+ self.iperf_server.port,self.current_test_name))
+ with open(client_output_path, 'w') as out_file:
+ out_file.write("\n".join(data))
+ self.iperf_server.stop()
+
+ iperf_file = self.iperf_server.log_files[-1]
+ try:
+ iperf_result = ipf.IPerfResult(iperf_file)
+ curr_throughput = math.fsum(iperf_result.instantaneous_rates)
+ except:
+ self.log.warning(
+ "ValueError: Cannot get iperf result. Setting to 0")
+ curr_throughput = 0
+ iot_result.append(curr_throughput)
+ self.log.info("Throughput is {0:.2f} Mbps".format(curr_throughput))
+
+ self.log.debug(pprint.pformat(data))
+ asserts.assert_true(success, "Error occurred in iPerf traffic.")
+ return iot_result
+
+ def run_iperf_server(self, network):
+ """Run iperf RX throughput after connection.
+
+ Args:
+ params: Dictionary with network info.
+
+ Returns:
+ iot_result: dict containing iot_results
+ """
+ if "iperf_server_address" in self.user_params:
+
+ # Add iot_result
+ iot_result = []
+ self.iperf_server.start(tag="RX_client_{}".format(
+ self.current_test_name))
+ wait_time = 5
+ SSID = network[WifiEnums.SSID_KEY]
+ self.log.info("Starting iperf traffic RX through {}".format(SSID))
+ time.sleep(wait_time)
+ port_arg = "-p {} -J -R {}".format(self.iperf_server.port,self.iperf_port_arg)
+ success, data = self.dut.run_iperf_client(self.iperf_server_address,
+ port_arg)
+ client_output_path = os.path.join(
+ self.iperf_server.log_path, "IperfDUT,{},RX_server_{}".format(
+ self.iperf_server.port,self.current_test_name))
+ with open(client_output_path, 'w') as out_file:
+ out_file.write("\n".join(data))
+ self.iperf_server.stop()
+
+ iperf_file = client_output_path
+ try:
+ iperf_result = ipf.IPerfResult(iperf_file)
+ curr_throughput = math.fsum(iperf_result.instantaneous_rates)
+ except:
+ self.log.warning(
+ "ValueError: Cannot get iperf result. Setting to 0")
+ curr_throughput = 0
+ iot_result.append(curr_throughput)
+ self.log.info("Throughput is {0:.2f} Mbps".format(curr_throughput))
+
+ self.log.debug(pprint.pformat(data))
+ asserts.assert_true(success, "Error occurred in iPerf traffic.")
+ return iot_result
+
+ def iperf_test_func(self,network):
+ """Main function to test iperf TX/RX.
+
+ Args:
+ params: Dictionary with network info
+ """
+ # Initialize
+ iot_result = {}
+
+ # Run RvR and log result
+ iot_result["throughput_TX"] = self.run_iperf_client(network)
+ iot_result["throughput_RX"] = self.run_iperf_server(network)
+ iot_result["test_name"] = self.current_test_name
+
+ # Save output as text file
+ results_file_path = "{}/{}.json".format(self.log_path,
+ self.current_test_name)
+ with open(results_file_path, 'w') as results_file:
+ json.dump(iot_result, results_file, indent=4)
+
+ data=(iot_result["test_name"],iot_result["throughput_TX"][0],
+ iot_result["throughput_RX"][0])
+ self.csv_write(data)
+
+ def csv_write(self,data):
+ with open("{}/Result.csv".format(self.log_path), "a", newline="") as csv_file:
+ csv_writer = csv.writer(csv_file,delimiter=',')
+ csv_writer.writerow(data)
+ csv_file.close()
+
+ def pdu_func(self):
+ """control Power Distribution Units on local machine.
+
+ Logic steps are
+ 1. Turn off PDU for all port.
+ 2. Turn on PDU for specified port.
+ """
+ out_file_name = "PDU.log"
+ self.full_out_path = os.path.join(self.log_path, out_file_name)
+ cmd = "curl http://snmp:1234@{}/offs.cgi?led=11111111> {}".format(self.pdu_address,
+ self.full_out_path)
+ self.pdu_process = utils.start_standing_subprocess(cmd)
+ wait_time = 10
+ self.log.info("Starting set PDU to OFF")
+ time.sleep(wait_time)
+ self.full_out_path = os.path.join(self.log_path, out_file_name)
+ cmd = "curl http://snmp:1234@{}/ons.cgi?led={}> {}".format(self.pdu_address,
+ self.pduon_address,
+ self.full_out_path)
+ self.pdu_process = utils.start_standing_subprocess(cmd)
+ wait_time = int("{}".format(self.pduon_wait_time))
+ self.log.info("Starting set PDU to ON for port1,"
+ "wait for {}s".format(self.pduon_wait_time))
+ time.sleep(wait_time)
+ self.log.info("PDU setup complete")
+
+ def connect_to_wifi_network_and_run_iperf(self, network):
+ """Connection logic for open and psk wifi networks.
+
+ Logic steps are
+ 1. Connect to the network.
+ 2. Run iperf throghput.
+
+ Args:
+ params: A dictionary with network info.
+ """
+ self.connect_to_wifi_network(network)
+ self.iperf_test_func(network)
+
+ """Tests"""
+
+ @test_tracker_info(uuid="0e4ad6ed-595c-4629-a4c9-c6be9c3c58e0")
+ def test_iot_connection_to_ASUS_RT_AC68U_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="a76d8acc-808e-4a5d-a52b-5ba07d07b810")
+ def test_iot_connection_to_ASUS_RT_AC68U_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="659a3e5e-07eb-4905-9cda-92e959c7b674")
+ def test_iot_connection_to_D_Link_DIR_868L_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="6bcfd736-30fc-48a8-b4fb-723d1d113f3c")
+ def test_iot_connection_to_D_Link_DIR_868L_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="c9da945a-2c4a-44e1-881d-adf307b39b21")
+ def test_iot_connection_to_TP_LINK_WR940N_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="db0d224d-df81-401f-bf35-08ad02e41a71")
+ def test_iot_connection_to_ASUS_RT_N66U_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="845ff1d6-618d-40f3-81c3-6ed3a0751fde")
+ def test_iot_connection_to_ASUS_RT_N66U_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="6908039b-ccc9-4777-a0f1-3494ce642014")
+ def test_iot_connection_to_ASUS_RT_AC54U_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="2647c15f-2aad-47d7-8dee-b2ee1ac4cef6")
+ def test_iot_connection_to_ASUS_RT_AC54U_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="99678f66-ddf1-454d-87e4-e55177ec380d")
+ def test_iot_connection_to_ASUS_RT_N56U_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="4dd75e81-9a8e-44fd-9449-09f5ab8a63c3")
+ def test_iot_connection_to_ASUS_RT_N56U_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="315397ce-50d5-4abf-a11c-1abcaef832d3")
+ def test_iot_connection_to_BELKIN_F9K1002v1_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="05ba464a-b1ef-4ac1-a32f-c919ec4aa1dd")
+ def test_iot_connection_to_CISCO_E1200_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="04912868-4a47-40ce-877e-4e4c89849557")
+ def test_iot_connection_to_TP_LINK_C2_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="53517a21-3802-4185-b8bb-6eaace063a42")
+ def test_iot_connection_to_TP_LINK_C2_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="71c08c1c-415d-4da4-a151-feef43fb6ad8")
+ def test_iot_connection_to_ASUS_RT_AC66U_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="2322c155-07d1-47c9-bd21-2e358e3df6ee")
+ def test_iot_connection_to_ASUS_RT_AC66U_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
diff --git a/acts_tests/tests/google/wifi/WifiIOTtpeTest.py b/acts_tests/tests/google/wifi/WifiIOTtpeTest.py
new file mode 100644
index 0000000..3a00d0c
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiIOTtpeTest.py
@@ -0,0 +1,214 @@
+#!/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 time
+
+import acts.signals
+import acts.test_utils.wifi.wifi_test_utils as wutils
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+WifiEnums = wutils.WifiEnums
+
+
+class WifiIOTtpeTest(WifiBaseTest):
+ """ Tests for wifi IOT
+
+ Test Bed Requirement:
+ * One Android device
+ * Wi-Fi IOT networks visible to the device
+ """
+
+ def setup_class(self):
+ super().setup_class()
+
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+
+ req_params = [ "iot_networks", ]
+ opt_params = [ "open_network", "iperf_server_address" ]
+ self.unpack_userparams(req_param_names=req_params,
+ opt_param_names=opt_params)
+
+ asserts.assert_true(
+ len(self.iot_networks) > 0,
+ "Need at least one iot network with psk.")
+
+ if getattr(self, 'open_network', False):
+ self.iot_networks.append(self.open_network)
+
+ wutils.wifi_toggle_state(self.dut, True)
+ if "iperf_server_address" in self.user_params:
+ self.iperf_server = self.iperf_servers[0]
+ self.iperf_server.start()
+
+ # create hashmap for testcase name and SSIDs
+ self.iot_test_prefix = "test_iot_connection_to_"
+ self.ssid_map = {}
+ for network in self.iot_networks:
+ SSID = network['SSID'].replace('-','_')
+ self.ssid_map[SSID] = network
+
+ def setup_test(self):
+ super().setup_test()
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+
+ def teardown_test(self):
+ super().teardown_test()
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ wutils.reset_wifi(self.dut)
+
+ def teardown_class(self):
+ if "iperf_server_address" in self.user_params:
+ self.iperf_server.stop()
+
+ """Helper Functions"""
+
+ def connect_to_wifi_network(self, network):
+ """Connection logic for open and psk wifi networks.
+
+ Args:
+ params: Dictionary with network info.
+ """
+ SSID = network[WifiEnums.SSID_KEY]
+ self.dut.ed.clear_all_events()
+ wutils.start_wifi_connection_scan(self.dut)
+ scan_results = self.dut.droid.wifiGetScanResults()
+ wutils.assert_network_in_list({WifiEnums.SSID_KEY: SSID}, scan_results)
+ wutils.wifi_connect(self.dut, network, num_of_tries=3)
+
+ def run_iperf_client(self, network):
+ """Run iperf traffic after connection.
+
+ Args:
+ params: Dictionary with network info.
+ """
+ if "iperf_server_address" in self.user_params:
+ wait_time = 5
+ SSID = network[WifiEnums.SSID_KEY]
+ self.log.info("Starting iperf traffic through {}".format(SSID))
+ time.sleep(wait_time)
+ port_arg = "-p {}".format(self.iperf_server.port)
+ success, data = self.dut.run_iperf_client(self.iperf_server_address,
+ port_arg)
+ self.log.debug(pprint.pformat(data))
+ asserts.assert_true(success, "Error occurred in iPerf traffic.")
+
+ def connect_to_wifi_network_and_run_iperf(self, network):
+ """Connection logic for open and psk wifi networks.
+
+ Logic steps are
+ 1. Connect to the network.
+ 2. Run iperf traffic.
+
+ Args:
+ params: A dictionary with network info.
+ """
+ self.connect_to_wifi_network(network)
+ self.run_iperf_client(network)
+
+ """Tests"""
+
+ @test_tracker_info(uuid="0e4ad6ed-595c-4629-a4c9-c6be9c3c58e0")
+ def test_iot_connection_to_ASUS_RT_AC68U_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="a76d8acc-808e-4a5d-a52b-5ba07d07b810")
+ def test_iot_connection_to_ASUS_RT_AC68U_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="659a3e5e-07eb-4905-9cda-92e959c7b674")
+ def test_iot_connection_to_D_Link_DIR_868L_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="6bcfd736-30fc-48a8-b4fb-723d1d113f3c")
+ def test_iot_connection_to_D_Link_DIR_868L_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="c9da945a-2c4a-44e1-881d-adf307b39b21")
+ def test_iot_connection_to_TP_LINK_WR940N_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="db0d224d-df81-401f-bf35-08ad02e41a71")
+ def test_iot_connection_to_ASUS_RT_N66U_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="845ff1d6-618d-40f3-81c3-6ed3a0751fde")
+ def test_iot_connection_to_ASUS_RT_N66U_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="6908039b-ccc9-4777-a0f1-3494ce642014")
+ def test_iot_connection_to_ASUS_RT_AC54U_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="2647c15f-2aad-47d7-8dee-b2ee1ac4cef6")
+ def test_iot_connection_to_ASUS_RT_AC54U_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="99678f66-ddf1-454d-87e4-e55177ec380d")
+ def test_iot_connection_to_ASUS_RT_N56U_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="4dd75e81-9a8e-44fd-9449-09f5ab8a63c3")
+ def test_iot_connection_to_ASUS_RT_N56U_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="315397ce-50d5-4abf-a11c-1abcaef832d3")
+ def test_iot_connection_to_BELKIN_F9K1002v1_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="05ba464a-b1ef-4ac1-a32f-c919ec4aa1dd")
+ def test_iot_connection_to_CISCO_E1200_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="04912868-4a47-40ce-877e-4e4c89849557")
+ def test_iot_connection_to_TP_LINK_C2_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="53517a21-3802-4185-b8bb-6eaace063a42")
+ def test_iot_connection_to_TP_LINK_C2_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="71c08c1c-415d-4da4-a151-feef43fb6ad8")
+ def test_iot_connection_to_ASUS_RT_AC66U_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="2322c155-07d1-47c9-bd21-2e358e3df6ee")
+ def test_iot_connection_to_ASUS_RT_AC66U_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.connect_to_wifi_network_and_run_iperf(self.ssid_map[ssid_key])
diff --git a/acts_tests/tests/google/wifi/WifiLinkProbeTest.py b/acts_tests/tests/google/wifi/WifiLinkProbeTest.py
new file mode 100644
index 0000000..de202b8
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiLinkProbeTest.py
@@ -0,0 +1,155 @@
+#!/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 time
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+import acts.test_utils.wifi.wifi_test_utils as wutils
+
+WifiEnums = wutils.WifiEnums
+NUM_LINK_PROBES = 8
+PROBE_DELAY_SEC = 3
+ATTENUATION = 40
+
+
+class WifiLinkProbeTest(WifiBaseTest):
+ """
+ Tests sending 802.11 Probe Request frames to the currently connected AP to
+ determine if the uplink to the AP is working.
+
+ Test Bed Requirements:
+ * One Android Device
+ * One Wi-Fi network visible to the device, with an attenuator
+ """
+
+ def setup_class(self):
+ super().setup_class()
+
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ self.unpack_userparams(req_param_names=[],
+ opt_param_names=["reference_networks"])
+
+ 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.")
+ self.attenuators = wutils.group_attenuators(self.attenuators)
+
+ def setup_test(self):
+ super().setup_test()
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ wutils.wifi_toggle_state(self.dut, True)
+ self.attenuators[0].set_atten(0)
+ self.attenuators[1].set_atten(0)
+
+ def teardown_test(self):
+ super().teardown_test()
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ wutils.reset_wifi(self.dut)
+
+ def teardown_class(self):
+ if "AccessPoint" in self.user_params:
+ del self.user_params["reference_networks"]
+
+ # HELPER METHODS
+
+ def _test_link_probe_does_not_crash_device(self, network):
+ """
+ Connect to a network, send link probes, and verify that the device did
+ not crash. Also verify that at least one link probe succeeded.
+
+ Steps:
+ 1. Connect to a network.
+ 2. Send a few link probes.
+ 3. Verify that at least one link probe succeeded.
+ 4. Ensure that the device did not crash (by checking that it is
+ connected to the expected network).
+ """
+ wutils.wifi_connect(self.dut, network, num_of_tries=3)
+
+ results = wutils.send_link_probes(
+ self.dut, NUM_LINK_PROBES, PROBE_DELAY_SEC)
+
+ asserts.assert_true(any(result.is_success for result in results),
+ "Expect at least 1 probe success: " + str(results))
+
+ wifi_info = self.dut.droid.wifiGetConnectionInfo()
+ expected = network[WifiEnums.SSID_KEY]
+ actual = wifi_info[WifiEnums.SSID_KEY]
+ asserts.assert_equal(
+ expected, actual,
+ "Device did not remain connected after sending link probes!")
+
+ def _test_link_probe_ap_attenuated(self, network):
+ """
+ Connect to a network, significantly attenuate the signal, and verify
+ that the device did not crash.
+
+ Steps:
+ 1. Connect to a network.
+ 2. Attenuate the signal.
+ 3. Send a few link probes.
+ 4. Stop attenuating the signal.
+ 5. Ensure that the device did not crash (by checking that it is
+ connected to the expected network).
+ """
+ wutils.wifi_connect(self.dut, network, num_of_tries=3)
+ self.attenuators[0].set_atten(ATTENUATION)
+
+ wutils.send_link_probes(self.dut, NUM_LINK_PROBES, PROBE_DELAY_SEC)
+
+ # we cannot assert for failed link probe when attenuated, this would
+ # depend too much on the attenuator setup => too flaky
+
+ self.attenuators[0].set_atten(0)
+ time.sleep(PROBE_DELAY_SEC * 3)
+ wifi_info = self.dut.droid.wifiGetConnectionInfo()
+ expected = network[WifiEnums.SSID_KEY]
+ actual = wifi_info[WifiEnums.SSID_KEY]
+ asserts.assert_equal(
+ expected, actual,
+ "Device did not remain connected after sending link probes!")
+
+ # TEST METHODS
+
+ @test_tracker_info(uuid='2afd309b-6bf3-4de4-9d8a-e4d35354a2cb')
+ def test_link_probe_does_not_crash_device_2g(self):
+ network = self.reference_networks[0]["2g"]
+ self._test_link_probe_does_not_crash_device(network)
+
+ @test_tracker_info(uuid='69417a6d-7090-4dd0-81ad-55fa3f12b7b1')
+ def test_link_probe_does_not_crash_device_5g(self):
+ network = self.reference_networks[0]["5g"]
+ self._test_link_probe_does_not_crash_device(network)
+
+ @test_tracker_info(uuid='54b8ffaa-c305-4772-928d-03342c51122d')
+ def test_link_probe_ap_attenuated_2g(self):
+ network = self.reference_networks[0]["2g"]
+ self._test_link_probe_ap_attenuated(network)
+
+ @test_tracker_info(uuid='54e29fa4-ff44-4aad-8999-676b361cacf4')
+ def test_link_probe_ap_attenuated_5g(self):
+ network = self.reference_networks[0]["5g"]
+ self._test_link_probe_ap_attenuated(network)
+
diff --git a/acts_tests/tests/google/wifi/WifiMacRandomizationTest.py b/acts_tests/tests/google/wifi/WifiMacRandomizationTest.py
new file mode 100644
index 0000000..f3b5102
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiMacRandomizationTest.py
@@ -0,0 +1,654 @@
+#!/usr/bin/env python3.4
+#
+# 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 itertools
+import pprint
+import queue
+import re
+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.test_decorators import test_tracker_info
+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.wifi.WifiBaseTest import WifiBaseTest
+from scapy.all import *
+from acts.controllers.ap_lib import hostapd_constants
+
+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
+SHORT_TIMEOUT = 5
+
+# Constants for WiFi state change operations.
+FORGET = 1
+TOGGLE = 2
+REBOOT_DUT = 3
+REBOOT_AP = 4
+
+# MAC Randomization setting constants.
+RANDOMIZATION_NONE = 0
+RANDOMIZATION_PERSISTENT = 1
+
+
+class WifiMacRandomizationTest(WifiBaseTest):
+ """Tests for APIs in Android's WifiManager class.
+
+ Test Bed Requirement:
+ * Atleast one Android device and atleast two Access Points.
+ * Several Wi-Fi networks visible to the device.
+ """
+
+ def setup_class(self):
+ super().setup_class()
+
+ 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)
+ req_params = ["dbs_supported_models", "roaming_attn"]
+ opt_param = [
+ "open_network", "reference_networks", "wep_networks"
+ ]
+ self.unpack_userparams(
+ req_param_names=req_params, opt_param_names=opt_param)
+
+ if not hasattr(self, 'packet_capture'):
+ raise signals.TestFailure("Needs packet_capture attribute to "
+ "support sniffing.")
+ self.configure_packet_capture()
+
+ if "AccessPoint" in self.user_params:
+ 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,
+ mirror_ap=True,
+ ap_count=2)
+
+ asserts.assert_true(
+ len(self.reference_networks) > 0,
+ "Need at least one reference network with psk.")
+
+ # Reboot device to reset factory MAC of wlan1
+ self.dut.reboot()
+ self.dut_client.reboot()
+ time.sleep(DEFAULT_TIMEOUT)
+ wutils.wifi_toggle_state(self.dut, True)
+ wutils.wifi_toggle_state(self.dut_client, True)
+ self.soft_ap_factory_mac = self.get_soft_ap_mac_address()
+ self.sta_factory_mac = self.dut.droid.wifigetFactorymacAddresses()[0]
+
+ self.wpapsk_2g = self.reference_networks[0]["2g"]
+ self.wpapsk_5g = self.reference_networks[0]["5g"]
+ self.wep_2g = self.wep_networks[0]["2g"]
+ self.wep_5g = self.wep_networks[0]["5g"]
+ self.open_2g = self.open_network[0]["2g"]
+ self.open_5g = self.open_network[0]["5g"]
+
+ def setup_test(self):
+ super().setup_test()
+ for ad in self.android_devices:
+ ad.droid.wakeLockAcquireBright()
+ ad.droid.wakeUpNow()
+ wutils.wifi_toggle_state(ad, True)
+
+ def teardown_test(self):
+ super().teardown_test()
+ for ad in self.android_devices:
+ ad.droid.wakeLockRelease()
+ ad.droid.goToSleepNow()
+ self.dut.droid.wifiRemoveNetworkSuggestions([])
+ wutils.reset_wifi(self.dut)
+ wutils.reset_wifi(self.dut_client)
+
+ def teardown_class(self):
+ if "AccessPoint" in self.user_params:
+ del self.user_params["reference_networks"]
+ del self.user_params["open_network"]
+ del self.user_params["wep_networks"]
+
+
+ """Helper Functions"""
+
+
+ def get_randomized_mac(self, network):
+ """Get the randomized MAC address.
+
+ Args:
+ network: dict, network information.
+
+ Returns:
+ The randomized MAC address string for the network.
+
+ """
+ return self.dut.droid.wifigetRandomizedMacAddress(network)
+
+ def connect_to_network_and_verify_mac_randomization(self, network,
+ status=RANDOMIZATION_PERSISTENT):
+ """Connect to the given network and verify MAC.
+
+ Args:
+ network: dict, the network information.
+ status: int, MAC randomization level.
+
+ Returns:
+ The randomized MAC addresss string.
+
+ """
+ wutils.connect_to_wifi_network(self.dut, network)
+ return self.verify_mac_randomization(network, status=status)
+
+ def verify_mac_randomization_and_add_to_list(self, network, mac_list):
+ """Connect to a network and populate it's MAC in a reference list,
+ that will be used to verify any repeated MAC addresses.
+
+ Args:
+ network: dict, the network information.
+ mac_list: list of MAC addresss strings.
+
+ """
+ rand_mac = self.connect_to_network_and_verify_mac_randomization(
+ network)
+ if rand_mac in mac_list:
+ raise signals.TestFailure('A new Randomized MAC was not generated '
+ ' for this network %s.' % network)
+ mac_list.append(rand_mac)
+
+ def verify_mac_randomization(self, network, status=RANDOMIZATION_PERSISTENT):
+ """Get the various types of MAC addresses for the device and verify.
+
+ Args:
+ network: dict, the network information.
+ status: int, MAC randomization level.
+
+ Returns:
+ The randomized MAC address string for the network.
+
+ """
+ randomized_mac = self.get_randomized_mac(network)
+ default_mac = self.get_sta_mac_address()
+ self.log.info("Factory MAC = %s\nRandomized MAC = %s\nDefault MAC = %s" %
+ (self.sta_factory_mac, randomized_mac, default_mac))
+ message = ('Randomized MAC and Factory MAC are the same. '
+ 'Randomized MAC = %s, Factory MAC = %s' % (randomized_mac, self.sta_factory_mac))
+ asserts.assert_true(randomized_mac != self.sta_factory_mac, message)
+ if status == RANDOMIZATION_NONE:
+ asserts.assert_true(default_mac == self.sta_factory_mac, "Connection is not "
+ "using Factory MAC as the default MAC.")
+ else:
+ message = ('Connection is not using randomized MAC as the default MAC. '
+ 'Randomized MAC = %s, Deafult MAC = %s' % (randomized_mac, default_mac))
+ asserts.assert_true(default_mac == randomized_mac, message)
+ return randomized_mac
+
+ def check_mac_persistence(self, network, condition):
+ """Check if the MAC is persistent after carrying out specific operations
+ like forget WiFi, toggle WiFi, reboot device and AP.
+
+ Args:
+ network: dict, The network information.
+ condition: int, value to trigger certain operation on the device.
+
+ Raises:
+ TestFaikure is the MAC is not persistent.
+
+ """
+ rand_mac1 = self.connect_to_network_and_verify_mac_randomization(network)
+
+ if condition == FORGET:
+ wutils.wifi_forget_network(self.dut, network['SSID'])
+
+ elif condition == TOGGLE:
+ wutils.wifi_toggle_state(self.dut, False)
+ wutils.wifi_toggle_state(self.dut, True)
+
+ elif condition == REBOOT_DUT:
+ self.dut.reboot()
+ time.sleep(DEFAULT_TIMEOUT)
+
+ elif condition == REBOOT_AP:
+ wutils.turn_ap_off(self, 1)
+ time.sleep(DEFAULT_TIMEOUT)
+ wutils.turn_ap_on(self, 1)
+ time.sleep(DEFAULT_TIMEOUT)
+
+ rand_mac2 = self.connect_to_network_and_verify_mac_randomization(network)
+
+ if rand_mac1 != rand_mac2:
+ raise signals.TestFailure('Randomized MAC is not persistent after '
+ 'forgetting networ. Old MAC = %s New MAC'
+ ' = %s' % (rand_mac1, rand_mac2))
+
+ def verify_mac_not_found_in_pcap(self, mac, packets):
+ 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 Device = %s"
+ % (pkt.show(), self.dut))
+
+ def verify_mac_is_found_in_pcap(self, mac, packets):
+ for pkt in packets:
+ self.log.debug("Packet Summary = %s" % pkt.summary())
+ if mac in pkt.summary():
+ return
+ raise signals.TestFailure("Did not find MAC = %s in packet sniffer."
+ "for device %s" % (mac, self.dut))
+
+ 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 get_soft_ap_mac_address(self):
+ """Gets the current MAC address being used for SoftAp."""
+ if self.dut.model in self.dbs_supported_models:
+ out = self.dut.adb.shell("ifconfig wlan1")
+ return re.match(".* HWaddr (\S+).*", out, re.S).group(1)
+ else:
+ return self.get_sta_mac_address()
+
+ def _add_suggestion_and_verify_mac_randomization(self, network_suggestion):
+ """Add wifi network suggestion and verify MAC randomization.
+
+ Args:
+ network_suggestion: network suggestion to add.
+
+ Returns:
+ Randomized MAC address.
+ """
+ self.log.info("Adding network suggestion")
+ asserts.assert_true(
+ self.dut.droid.wifiAddNetworkSuggestions([network_suggestion]),
+ "Failed to add suggestions")
+ 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])
+ default_mac = self.get_sta_mac_address()
+ randomized_mac = self.dut.droid.wifiGetConnectionInfo()["mac_address"]
+ self.log.info("Factory MAC = %s\nRandomized MAC = %s\nDefault MAC = %s" %
+ (self.sta_factory_mac, randomized_mac, default_mac))
+ asserts.assert_true(
+ default_mac == randomized_mac,
+ "Connection is not using randomized MAC as the default MAC.")
+ return randomized_mac
+
+ def _remove_suggestion_and_verify_disconnect(self, network_suggestion):
+ """Remove wifi network suggestion and verify device disconnects.
+
+ Args:
+ network_suggestion: network suggestion to remove.
+ """
+ self.dut.log.info("Removing network suggestions")
+ asserts.assert_true(
+ self.dut.droid.wifiRemoveNetworkSuggestions([network_suggestion]),
+ "Failed to remove suggestions")
+ wutils.wait_for_disconnect(self.dut)
+ self.dut.ed.clear_all_events()
+ asserts.assert_false(
+ wutils.wait_for_connect(
+ self.dut,
+ network_suggestion[WifiEnums.SSID_KEY],
+ assert_on_fail=False),
+ "Device should not connect back")
+
+ """Tests"""
+
+
+ @test_tracker_info(uuid="2dd0a05e-a318-45a6-81cd-962e098fa242")
+ def test_set_mac_randomization_to_none(self):
+ self.pcap_procs = wutils.start_pcap(
+ self.packet_capture, 'dual', self.test_name)
+ network = self.wpapsk_2g
+ # Set macRandomizationSetting to RANDOMIZATION_NONE.
+ network["macRand"] = RANDOMIZATION_NONE
+ self.connect_to_network_and_verify_mac_randomization(network,
+ status=RANDOMIZATION_NONE)
+ pcap_fname = '%s_%s.pcap' % \
+ (self.pcap_procs[hostapd_constants.BAND_2G][1],
+ hostapd_constants.BAND_2G.upper())
+ time.sleep(SHORT_TIMEOUT)
+ wutils.stop_pcap(self.packet_capture, self.pcap_procs, False)
+ packets = rdpcap(pcap_fname)
+ self.verify_mac_is_found_in_pcap(self.sta_factory_mac, packets)
+
+ @test_tracker_info(uuid="d9e64202-02d5-421a-967c-42e45f1f7f91")
+ def test_mac_randomization_wpapsk(self):
+ """Verify MAC randomization for a WPA network.
+
+ Steps:
+ 1. Connect to WPA network.
+ 2. Get the Factory, Randomized and Default MACs.
+ 3. Verify randomized MAC is the default MAC for the device.
+
+ """
+ self.connect_to_network_and_verify_mac_randomization(self.wpapsk_2g)
+
+ @test_tracker_info(uuid="b5be7c53-2edf-449e-ba70-a1fb7acf735e")
+ def test_mac_randomization_wep(self):
+ """Verify MAC randomization for a WEP network.
+
+ Steps:
+ 1. Connect to WEP network.
+ 2. Get the Factory, Randomized and Default MACs.
+ 3. Verify randomized MAC is the default MAC for the device.
+
+ """
+ self.connect_to_network_and_verify_mac_randomization(self.wep_2g)
+
+ @test_tracker_info(uuid="f5347ac0-68d5-4882-a58d-1bd0d575503c")
+ def test_mac_randomization_open(self):
+ """Verify MAC randomization for a open network.
+
+ Steps:
+ 1. Connect to open network.
+ 2. Get the Factory, Randomized and Default MACs.
+ 3. Verify randomized MAC is the default MAC for the device.
+
+ """
+ self.connect_to_network_and_verify_mac_randomization(self.open_2g)
+
+ @test_tracker_info(uuid="5d260421-2adf-4ace-b281-3d15aec39b2a")
+ def test_persistent_mac_after_forget(self):
+ """Check if MAC is persistent after forgetting/adding a network.
+
+ Steps:
+ 1. Connect to WPA network and get the randomized MAC.
+ 2. Forget the network.
+ 3. Connect to the same network again.
+ 4. Verify randomized MAC has not changed.
+
+ """
+ self.check_mac_persistence(self.wpapsk_2g, FORGET)
+
+ @test_tracker_info(uuid="09d40a93-ead2-45ca-9905-14b05fd79f34")
+ def test_persistent_mac_after_toggle(self):
+ """Check if MAC is persistent after toggling WiFi network.
+
+ Steps:
+ 1. Connect to WPA network and get the randomized MAC.
+ 2. Turn WiFi ON/OFF.
+ 3. Connect to the same network again.
+ 4. Verify randomized MAC has not changed.
+
+ """
+ self.check_mac_persistence(self.wpapsk_2g, TOGGLE)
+
+ @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.
+
+ Steps:
+ 1. Connect to WPA network and get the randomized MAC.
+ 2. Reboot DUT.
+ 3. Connect to the same network again.
+ 4. Verify randomized MAC has not changed.
+
+ """
+ self.check_mac_persistence(self.wpapsk_2g, REBOOT_DUT)
+
+ # Disable reboot test for debugging purpose.
+ #@test_tracker_info(uuid="82d691a0-22e4-4a3d-9596-e150531fcd34")
+ def persistent_mac_after_ap_reboot(self):
+ """Check if MAC is persistent after AP reboots itself.
+
+ Steps:
+ 1. Connect to WPA network and get the randomized MAC.
+ 2. Reboot AP(basically restart hostapd in our case).
+ 3. Connect to the same network again.
+ 4. Verify randomized MAC has not changed.
+
+ """
+ self.check_mac_persistence(self.wpapsk_2g, REBOOT_AP)
+
+ @test_tracker_info(uuid="e1f33dbc-808c-4e61-8a4a-3a72c1f63c7e")
+ def test_mac_randomization_multiple_networks(self):
+ """Connect to multiple networks and verify same MAC.
+
+ Steps:
+ 1. Connect to network A, get randomizd MAC.
+ 2. Conenct to network B, get randomized MAC.
+ 3. Connect back to network A and verify same MAC.
+ 4. Connect back to network B and verify same MAC.
+
+ """
+ mac_list = list()
+
+ # Connect to two different networks and get randomized MAC addresses.
+ self.verify_mac_randomization_and_add_to_list(self.wpapsk_2g, mac_list)
+ self.verify_mac_randomization_and_add_to_list(self.open_2g, mac_list)
+
+ # Connect to the previous network and check MAC is persistent.
+ mac_wpapsk = self.connect_to_network_and_verify_mac_randomization(
+ self.wpapsk_2g)
+ msg = ('Randomized MAC is not persistent for this network %s. Old MAC = '
+ '%s \nNew MAC = %s')
+ if mac_wpapsk != mac_list[0]:
+ raise signals.TestFailure(msg % (self.wpapsk_5g, mac_list[0], mac_wpapsk))
+ mac_open = self.connect_to_network_and_verify_mac_randomization(
+ self.open_2g)
+ if mac_open != mac_list[1]:
+ raise signals.TestFailure(msg %(self.open_5g, mac_list[1], mac_open))
+
+ @test_tracker_info(uuid="edb5a0e5-7f3b-4147-b1d3-48ad7ad9799e")
+ def test_mac_randomization_different_APs(self):
+ """Verify randomization using two different APs.
+
+ Steps:
+ 1. Connect to network A on AP1, get the randomized MAC.
+ 2. Connect to network B on AP2, get the randomized MAC.
+ 3. Veirfy the two MACs are different.
+
+ """
+ ap1 = self.wpapsk_2g
+ ap2 = self.reference_networks[1]["5g"]
+ mac_ap1 = self.connect_to_network_and_verify_mac_randomization(ap1)
+ mac_ap2 = self.connect_to_network_and_verify_mac_randomization(ap2)
+ if mac_ap1 == mac_ap2:
+ raise signals.TestFailure("Same MAC address was generated for both "
+ "APs: %s" % mac_ap1)
+
+ @test_tracker_info(uuid="b815e9ce-bccd-4fc3-9774-1e1bc123a2a8")
+ def test_mac_randomization_ap_sta(self):
+ """Bring up STA and softAP and verify MAC randomization.
+
+ Steps:
+ 1. Connect to a network and get randomized MAC.
+ 2. Bring up softAP on the DUT.
+ 3. Connect to softAP network on the client and get MAC.
+ 4. Verify AP and STA use different randomized MACs.
+ 5. Find the channel of the SoftAp network.
+ 6. Configure sniffer on that channel.
+ 7. Verify the factory MAC is not leaked.
+
+ """
+ wutils.set_wifi_country_code(self.dut, wutils.WifiEnums.CountryCode.US)
+ wutils.set_wifi_country_code(self.dut_client, wutils.WifiEnums.CountryCode.US)
+ mac_sta = self.connect_to_network_and_verify_mac_randomization(
+ self.wpapsk_2g)
+ softap = wutils.start_softap_and_verify(self, WIFI_CONFIG_APBAND_2G)
+ wutils.connect_to_wifi_network(self.dut_client, softap)
+ softap_info = self.dut_client.droid.wifiGetConnectionInfo()
+ mac_ap = softap_info['mac_address']
+ if mac_sta == mac_ap:
+ raise signals.TestFailure("Same MAC address was used for both "
+ "AP and STA: %s" % mac_sta)
+
+ # Verify SoftAp MAC is randomized
+ softap_mac = self.get_soft_ap_mac_address()
+ message = ('Randomized SoftAp MAC and Factory SoftAp MAC are the same. '
+ 'Randomized SoftAp MAC = %s, Factory SoftAp MAC = %s'
+ % (softap_mac, self.soft_ap_factory_mac))
+ asserts.assert_true(softap_mac != self.soft_ap_factory_mac, message)
+
+ softap_channel = hostapd_constants.CHANNEL_MAP[softap_info['frequency']]
+ self.log.info("softap_channel = %s\n" % (softap_channel))
+ result = self.packet_capture.configure_monitor_mode(
+ hostapd_constants.BAND_2G, softap_channel)
+ if not result:
+ raise ValueError("Failed to configure channel for 2G band")
+ self.pcap_procs = wutils.start_pcap(
+ self.packet_capture, 'dual', self.test_name)
+ # re-connect to the softAp network after sniffer is started
+ wutils.connect_to_wifi_network(self.dut_client, self.wpapsk_2g)
+ wutils.connect_to_wifi_network(self.dut_client, softap)
+ time.sleep(SHORT_TIMEOUT)
+ wutils.stop_pcap(self.packet_capture, self.pcap_procs, False)
+ pcap_fname = '%s_%s.pcap' % \
+ (self.pcap_procs[hostapd_constants.BAND_2G][1],
+ hostapd_constants.BAND_2G.upper())
+ packets = rdpcap(pcap_fname)
+ self.verify_mac_not_found_in_pcap(self.soft_ap_factory_mac, packets)
+ self.verify_mac_not_found_in_pcap(self.sta_factory_mac, packets)
+ self.verify_mac_is_found_in_pcap(softap_mac, packets)
+ self.verify_mac_is_found_in_pcap(self.get_sta_mac_address(), packets)
+
+ @test_tracker_info(uuid="3ca3f911-29f1-41fb-b836-4d25eac1669f")
+ def test_roaming_mac_randomization(self):
+ """test MAC randomization in the roaming scenario.
+
+ Steps:
+ 1. Connect to network A on AP1, get randomized MAC.
+ 2. Set AP1 to MAX attenuation so that we roam to AP2.
+ 3. Wait for device to roam to AP2 and get randomized MAC.
+ 4. Veirfy that the device uses same AMC for both APs.
+
+ """
+ AP1_network = self.reference_networks[0]["5g"]
+ AP2_network = self.reference_networks[1]["5g"]
+ if "OpenWrtAP" in self.user_params:
+ AP1_network["bssid"] = self.bssid_map[0]["5g"][AP1_network["SSID"]]
+ AP2_network["bssid"] = self.bssid_map[1]["5g"][AP2_network["SSID"]]
+ 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, 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, 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 "
+ "roaming from AP1 to AP2.\nMAC before roam = %s\nMAC after "
+ "roam = %s" %(mac_before_roam, mac_after_roam))
+
+ @test_tracker_info(uuid="17b12f1a-7c62-4188-b5a5-52d7a0bb7849")
+ def test_check_mac_sta_with_link_probe(self):
+ """Test to ensure Factory MAC is not exposed, using sniffer data.
+
+ Steps:
+ 1. Configure and start the sniffer on 5GHz band.
+ 2. Connect to 5GHz network.
+ 3. Send link probes.
+ 4. Stop the sniffer.
+ 5. Invoke scapy to read the .pcap file.
+ 6. Read each packet summary and make sure Factory MAC is not used.
+
+ """
+ self.pcap_procs = wutils.start_pcap(
+ self.packet_capture, 'dual', self.test_name)
+ time.sleep(SHORT_TIMEOUT)
+ network = self.wpapsk_5g
+ rand_mac = self.connect_to_network_and_verify_mac_randomization(network)
+ wutils.send_link_probes(self.dut, 3, 3)
+ pcap_fname = '%s_%s.pcap' % \
+ (self.pcap_procs[hostapd_constants.BAND_5G][1],
+ hostapd_constants.BAND_5G.upper())
+ wutils.stop_pcap(self.packet_capture, self.pcap_procs, False)
+ time.sleep(SHORT_TIMEOUT)
+ packets = rdpcap(pcap_fname)
+ self.verify_mac_not_found_in_pcap(self.sta_factory_mac, packets)
+ self.verify_mac_is_found_in_pcap(self.get_sta_mac_address(), packets)
+
+ @test_tracker_info(uuid="1c2cc0fd-a340-40c4-b679-6acc5f526451")
+ def test_check_mac_in_wifi_scan(self):
+ """Test to ensure Factory MAC is not exposed, in Wi-Fi scans
+
+ Steps:
+ 1. Configure and start the sniffer on both bands.
+ 2. Perform a full scan.
+ 3. Stop the sniffer.
+ 4. Invoke scapy to read the .pcap file.
+ 5. Read each packet summary and make sure Factory MAC is not used.
+
+ """
+ self.pcap_procs = wutils.start_pcap(
+ self.packet_capture, 'dual', self.test_name)
+ wutils.start_wifi_connection_scan(self.dut)
+ time.sleep(SHORT_TIMEOUT)
+ wutils.stop_pcap(self.packet_capture, self.pcap_procs, False)
+ pcap_fname = '%s_%s.pcap' % \
+ (self.pcap_procs[hostapd_constants.BAND_2G][1],
+ hostapd_constants.BAND_2G.upper())
+ packets = rdpcap(pcap_fname)
+ self.verify_mac_not_found_in_pcap(self.sta_factory_mac, packets)
+
+ @test_tracker_info(uuid="7714d31f-bb08-4f29-b246-0ce1398a3c03")
+ def test_mac_randomization_for_network_suggestion(self):
+ """Add network suggestion and verify MAC randomization.
+
+ Steps:
+ 1. Add a network suggestion and verify device connects to it.
+ 2. Verify the device uses randomized MAC address for this network.
+ """
+ network_suggestion = self.reference_networks[0]["5g"]
+ self._add_suggestion_and_verify_mac_randomization(network_suggestion)
+
+ @test_tracker_info(uuid="144ad0b4-b79d-4b1d-a8a9-3c612a76c32c")
+ def test_enhanced_mac_randomization_for_network_suggestion(self):
+ """Test enhanced MAC randomization.
+
+ Steps:
+ 1. Add a network suggestion with enhanced mac randomization enabled.
+ 2. Connect to the network and verify the MAC address is random.
+ 3. Remove the suggestion network and add it back.
+ 4. Connect to the network. Verify the MAC address is random and
+ different from the randomized MAC observed in step 2.
+ """
+ network_suggestion = self.reference_networks[0]["5g"]
+ network_suggestion["enhancedMacRandomizationEnabled"] = True
+
+ # add network suggestion with enhanced mac randomization
+ randomized_mac1 = self._add_suggestion_and_verify_mac_randomization(
+ network_suggestion)
+
+ # remove network suggestion and verify no connection
+ self._remove_suggestion_and_verify_disconnect(network_suggestion)
+
+ # add network suggestion and verify device connects back
+ randomized_mac2 = self._add_suggestion_and_verify_mac_randomization(
+ network_suggestion)
+
+ # verify both randomized mac addrs are different
+ asserts.assert_true(randomized_mac1 != randomized_mac2,
+ "Randomized MAC addresses are same.")
+
diff --git a/acts_tests/tests/google/wifi/WifiManagerTest.py b/acts_tests/tests/google/wifi/WifiManagerTest.py
new file mode 100644
index 0000000..8b4c197
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiManagerTest.py
@@ -0,0 +1,1039 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2016 - 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.test_decorators import test_tracker_info
+from acts.test_utils.bt.bt_test_utils import enable_bluetooth
+from acts.test_utils.bt.bt_test_utils import disable_bluetooth
+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 WifiManagerTest(WifiBaseTest):
+ """Tests for APIs in Android's WifiManager class.
+
+ Test Bed Requirement:
+ * Two Android device
+ * Several Wi-Fi networks visible to the device, including an open Wi-Fi
+ network.
+ """
+
+ def __init__(self, configs):
+ super().__init__(configs)
+ self.enable_packet_log = True
+
+ def setup_class(self):
+ super().setup_class()
+
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ wutils.wifi_toggle_state(self.dut, True)
+
+ self.dut_client = None
+ if len(self.android_devices) > 1:
+ self.dut_client = self.android_devices[1]
+ wutils.wifi_test_device_init(self.dut_client)
+ wutils.wifi_toggle_state(self.dut_client, True)
+
+ req_params = []
+ opt_param = [
+ "open_network", "reference_networks", "iperf_server_address",
+ "wpa_networks", "wep_networks", "iperf_server_port"
+ ]
+ 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, 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,
+ "Need at least one reference network with psk.")
+ self.wpapsk_2g = self.reference_networks[0]["2g"]
+ self.wpapsk_5g = self.reference_networks[0]["5g"]
+ self.open_network_2g = self.open_network[0]["2g"]
+ self.open_network_5g = self.open_network[0]["5g"]
+
+ def setup_test(self):
+ super().setup_test()
+ for ad in self.android_devices:
+ ad.droid.wakeLockAcquireBright()
+ ad.droid.wakeUpNow()
+ wutils.wifi_toggle_state(self.dut, True)
+
+ def teardown_test(self):
+ super().teardown_test()
+ for ad in self.android_devices:
+ 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)
+
+ 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 connect_to_wifi_network(self, params):
+ """Connection logic for open and psk wifi networks.
+
+ Args:
+ params: A tuple of network info and AndroidDevice object.
+ """
+ network, ad = params
+ droid = ad.droid
+ ed = ad.ed
+ SSID = network[WifiEnums.SSID_KEY]
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ ad, SSID);
+ wutils.wifi_connect(ad, network, num_of_tries=3)
+
+ def get_connection_data(self, dut, network):
+ """Get network id and ssid info from connection data.
+
+ Args:
+ dut: The Android device object under test.
+ network: dict representing the network to connect to.
+
+ Returns:
+ A convenience dict with the connected network's ID and SSID.
+
+ """
+ params = (network, dut)
+ self.connect_to_wifi_network(params)
+ connect_data = dut.droid.wifiGetConnectionInfo()
+ ssid_id_dict = dict()
+ ssid_id_dict[WifiEnums.NETID_KEY] = connect_data[WifiEnums.NETID_KEY]
+ ssid_id_dict[WifiEnums.SSID_KEY] = connect_data[WifiEnums.SSID_KEY]
+ return ssid_id_dict
+
+ def connect_multiple_networks(self, dut):
+ """Connect to one 2.4GHz and one 5Ghz network.
+
+ Args:
+ dut: The Android device object under test.
+
+ Returns:
+ A list with the connection details for the 2GHz and 5GHz networks.
+
+ """
+ network_list = list()
+ connect_2g_data = self.get_connection_data(dut, self.wpapsk_2g)
+ network_list.append(connect_2g_data)
+ connect_5g_data = self.get_connection_data(dut, self.wpapsk_5g)
+ network_list.append(connect_5g_data)
+ return network_list
+
+ 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.
+
+ Return:
+ Network dict of the unconnected network.
+
+ """
+ wifi_info = self.dut.droid.wifiGetConnectionInfo()
+ enabled = network1
+ if wifi_info[WifiEnums.SSID_KEY] == network1[WifiEnums.SSID_KEY]:
+ enabled = network2
+ return enabled
+
+ def check_configstore_networks(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 = (
+ "Length of configured networks before and after reboot 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 reboot
+ 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 reboot" %
+ network[WifiEnums.SSID_KEY])
+ # Get the new network id for each network after reboot.
+ network[WifiEnums.NETID_KEY] = exists[0]['networkId']
+ if exists[0]['status'] == 'CURRENT':
+ current_count += 1
+ # At any given point, there can only be one currently active
+ # network, defined with 'status':'CURRENT'
+ if current_count > 1:
+ raise signals.TestFailure("More than one network showing"
+ "as 'CURRENT' after reboot")
+
+ def connect_to_wifi_network_with_id(self, network_id, network_ssid):
+ """Connect to the given network using network id and verify SSID.
+
+ Args:
+ network_id: int Network Id of the network.
+ network_ssid: string SSID of the network.
+
+ Returns: True if connect using network id was successful;
+ False otherwise.
+
+ """
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ self.dut, network_ssid);
+ wutils.wifi_connect_by_id(self.dut, network_id)
+ connect_data = self.dut.droid.wifiGetConnectionInfo()
+ connect_ssid = connect_data[WifiEnums.SSID_KEY]
+ self.log.debug("Expected SSID = %s Connected SSID = %s" %
+ (network_ssid, connect_ssid))
+ if connect_ssid != network_ssid:
+ return False
+ return True
+
+ def run_iperf_client(self, params):
+ """Run iperf traffic after connection.
+
+ Args:
+ params: A tuple of network info and AndroidDevice object.
+ """
+ if "iperf_server_address" in self.user_params:
+ wait_time = 5
+ network, ad = params
+ SSID = network[WifiEnums.SSID_KEY]
+ self.log.info("Starting iperf traffic through {}".format(SSID))
+ time.sleep(wait_time)
+ port_arg = "-p {}".format(self.iperf_server_port)
+ success, data = ad.run_iperf_client(self.iperf_server_address,
+ port_arg)
+ self.log.debug(pprint.pformat(data))
+ asserts.assert_true(success, "Error occurred in iPerf traffic.")
+
+ def connect_to_wifi_network_toggle_wifi_and_run_iperf(self, params):
+ """ Connect to the provided network and then toggle wifi mode and wait
+ for reconnection to the provided network.
+
+ Logic steps are
+ 1. Connect to the network.
+ 2. Turn wifi off.
+ 3. Turn wifi on.
+ 4. Wait for connection to the network.
+ 5. Run iperf traffic.
+
+ Args:
+ params: A tuple of network info and AndroidDevice object.
+ """
+ network, ad = params
+ self.connect_to_wifi_network(params)
+ wutils.toggle_wifi_and_wait_for_reconnection(
+ ad, network, num_of_tries=5)
+ self.run_iperf_client(params)
+
+ def run_iperf(self, iperf_args):
+ if "iperf_server_address" not in self.user_params:
+ self.log.error(("Missing iperf_server_address. "
+ "Provide one in config."))
+ else:
+ iperf_addr = self.user_params["iperf_server_address"]
+ self.log.info("Running iperf client.")
+ result, data = self.dut.run_iperf_client(iperf_addr, iperf_args)
+ self.log.debug(data)
+
+ def run_iperf_rx_tx(self, time, omit=10):
+ args = "-p {} -t {} -O 10".format(self.iperf_server_port, time, omit)
+ self.log.info("Running iperf client {}".format(args))
+ self.run_iperf(args)
+ args = "-p {} -t {} -O 10 -R".format(self.iperf_server_port, time,
+ omit)
+ self.log.info("Running iperf client {}".format(args))
+ self.run_iperf(args)
+
+ def get_energy_info(self):
+ """ Steps:
+ 1. Check that the WiFi energy info reporting support on this device
+ is as expected (support or not).
+ 2. If the device does not support energy info reporting as
+ expected, skip the test.
+ 3. Call API to get WiFi energy info.
+ 4. Verify the values of "ControllerEnergyUsed" and
+ "ControllerIdleTimeMillis" in energy info don't decrease.
+ 5. Repeat from Step 3 for 10 times.
+ """
+ # Check if dut supports energy info reporting.
+ actual_support = self.dut.droid.wifiIsEnhancedPowerReportingSupported()
+ model = self.dut.model
+ if not actual_support:
+ asserts.skip(
+ ("Device %s does not support energy info reporting as "
+ "expected.") % model)
+ # Verify reported values don't decrease.
+ self.log.info(("Device %s supports energy info reporting, verify that "
+ "the reported values don't decrease.") % model)
+ energy = 0
+ idle_time = 0
+ for i in range(10):
+ info = self.dut.droid.wifiGetControllerActivityEnergyInfo()
+ self.log.debug("Iteration %d, got energy info: %s" % (i, info))
+ new_energy = info["ControllerEnergyUsed"]
+ new_idle_time = info["ControllerIdleTimeMillis"]
+ asserts.assert_true(new_energy >= energy,
+ "Energy value decreased: previous %d, now %d" %
+ (energy, new_energy))
+ energy = new_energy
+ asserts.assert_true(new_idle_time >= idle_time,
+ "Idle time decreased: previous %d, now %d" % (
+ idle_time, new_idle_time))
+ idle_time = new_idle_time
+ wutils.start_wifi_connection_scan(self.dut)
+
+ def turn_location_on_and_scan_toggle_on(self):
+ """ Turns on wifi location scans.
+ """
+ acts.utils.set_location_service(self.dut, True)
+ self.dut.droid.wifiScannerToggleAlwaysAvailable(True)
+ msg = "Failed to turn on location service's scan."
+ asserts.assert_true(self.dut.droid.wifiScannerIsAlwaysAvailable(), msg)
+
+ def turn_location_off_and_scan_toggle_off(self):
+ """ Turns off wifi location scans.
+ """
+ acts.utils.set_location_service(self.dut, False)
+ self.dut.droid.wifiScannerToggleAlwaysAvailable(False)
+ msg = "Failed to turn off location service's scan."
+ asserts.assert_true(not self.dut.droid.wifiScannerIsAlwaysAvailable(), msg)
+
+ def turn_location_on_and_scan_toggle_off(self):
+ """ Turns off wifi location scans, but keeps location on.
+ """
+ acts.utils.set_location_service(self.dut, True)
+ self.dut.droid.wifiScannerToggleAlwaysAvailable(False)
+ msg = "Failed to turn off location service's scan."
+ asserts.assert_true(not self.dut.droid.wifiScannerIsAlwaysAvailable(), msg)
+
+ def helper_reconnect_toggle_wifi(self):
+ """Connect to multiple networks, turn off/on wifi, then reconnect to
+ a previously connected network.
+
+ Steps:
+ 1. Connect to a 2GHz network.
+ 2. Connect to a 5GHz network.
+ 3. Turn WiFi OFF/ON.
+ 4. Reconnect to the non-current network.
+
+ """
+ connect_2g_data = self.get_connection_data(self.dut, self.wpapsk_2g)
+ connect_5g_data = self.get_connection_data(self.dut, self.wpapsk_5g)
+ wutils.toggle_wifi_off_and_on(self.dut)
+ reconnect_to = self.get_enabled_network(connect_2g_data,
+ connect_5g_data)
+ reconnect = self.connect_to_wifi_network_with_id(
+ reconnect_to[WifiEnums.NETID_KEY],
+ reconnect_to[WifiEnums.SSID_KEY])
+ if not reconnect:
+ raise signals.TestFailure("Device did not connect to the correct"
+ " network after toggling WiFi.")
+
+ def helper_reconnect_toggle_airplane(self):
+ """Connect to multiple networks, turn on/off Airplane moce, then
+ reconnect a previously connected network.
+
+ Steps:
+ 1. Connect to a 2GHz network.
+ 2. Connect to a 5GHz network.
+ 3. Turn ON/OFF Airplane mode.
+ 4. Reconnect to the non-current network.
+
+ """
+ connect_2g_data = self.get_connection_data(self.dut, self.wpapsk_2g)
+ connect_5g_data = self.get_connection_data(self.dut, self.wpapsk_5g)
+ wutils.toggle_airplane_mode_on_and_off(self.dut)
+ reconnect_to = self.get_enabled_network(connect_2g_data,
+ connect_5g_data)
+ reconnect = self.connect_to_wifi_network_with_id(
+ reconnect_to[WifiEnums.NETID_KEY],
+ reconnect_to[WifiEnums.SSID_KEY])
+ if not reconnect:
+ raise signals.TestFailure("Device did not connect to the correct"
+ " network after toggling Airplane mode.")
+
+ def helper_reboot_configstore_reconnect(self):
+ """Connect to multiple networks, reboot then reconnect to previously
+ connected network.
+
+ Steps:
+ 1. Connect to a 2GHz network.
+ 2. Connect to a 5GHz network.
+ 3. Reboot device.
+ 4. Verify all networks are persistent after reboot.
+ 5. Reconnect to the non-current network.
+
+ """
+ network_list = self.connect_multiple_networks(self.dut)
+ self.dut.reboot()
+ time.sleep(DEFAULT_TIMEOUT)
+ self.check_configstore_networks(network_list)
+
+ reconnect_to = self.get_enabled_network(network_list[BAND_2GHZ],
+ network_list[BAND_5GHZ])
+
+ reconnect = self.connect_to_wifi_network_with_id(
+ reconnect_to[WifiEnums.NETID_KEY],
+ reconnect_to[WifiEnums.SSID_KEY])
+ if not reconnect:
+ raise signals.TestFailure(
+ "Device failed to reconnect to the correct"
+ " network after reboot.")
+
+ def helper_toggle_wifi_reboot_configstore_reconnect(self):
+ """Connect to multiple networks, disable WiFi, reboot, then
+ reconnect to previously connected network.
+
+ Steps:
+ 1. Connect to a 2GHz network.
+ 2. Connect to a 5GHz network.
+ 3. Turn WiFi OFF.
+ 4. Reboot device.
+ 5. Turn WiFi ON.
+ 4. Verify all networks are persistent after reboot.
+ 5. Reconnect to the non-current network.
+
+ """
+ network_list = self.connect_multiple_networks(self.dut)
+ self.log.debug("Toggling wifi OFF")
+ wutils.wifi_toggle_state(self.dut, False)
+ time.sleep(DEFAULT_TIMEOUT)
+ self.dut.reboot()
+ time.sleep(DEFAULT_TIMEOUT)
+ self.log.debug("Toggling wifi ON")
+ wutils.wifi_toggle_state(self.dut, True)
+ time.sleep(DEFAULT_TIMEOUT)
+ self.check_configstore_networks(network_list)
+ reconnect_to = self.get_enabled_network(network_list[BAND_2GHZ],
+ network_list[BAND_5GHZ])
+ reconnect = self.connect_to_wifi_network_with_id(
+ reconnect_to[WifiEnums.NETID_KEY],
+ reconnect_to[WifiEnums.SSID_KEY])
+ if not reconnect:
+ msg = ("Device failed to reconnect to the correct network after"
+ " toggling WiFi and rebooting.")
+ raise signals.TestFailure(msg)
+
+ def helper_toggle_airplane_reboot_configstore_reconnect(self):
+ """Connect to multiple networks, enable Airplane mode, reboot, then
+ reconnect to previously connected network.
+
+ Steps:
+ 1. Connect to a 2GHz network.
+ 2. Connect to a 5GHz network.
+ 3. Toggle Airplane mode ON.
+ 4. Reboot device.
+ 5. Toggle Airplane mode OFF.
+ 4. Verify all networks are persistent after reboot.
+ 5. Reconnect to the non-current network.
+
+ """
+ network_list = self.connect_multiple_networks(self.dut)
+ self.log.debug("Toggling Airplane mode ON")
+ asserts.assert_true(
+ acts.utils.force_airplane_mode(self.dut, True),
+ "Can not turn on airplane mode on: %s" % self.dut.serial)
+ time.sleep(DEFAULT_TIMEOUT)
+ self.dut.reboot()
+ time.sleep(DEFAULT_TIMEOUT)
+ self.log.debug("Toggling Airplane mode OFF")
+ asserts.assert_true(
+ acts.utils.force_airplane_mode(self.dut, False),
+ "Can not turn on airplane mode on: %s" % self.dut.serial)
+ time.sleep(DEFAULT_TIMEOUT)
+ self.check_configstore_networks(network_list)
+ reconnect_to = self.get_enabled_network(network_list[BAND_2GHZ],
+ network_list[BAND_5GHZ])
+ reconnect = self.connect_to_wifi_network_with_id(
+ reconnect_to[WifiEnums.NETID_KEY],
+ reconnect_to[WifiEnums.SSID_KEY])
+ if not reconnect:
+ msg = ("Device failed to reconnect to the correct network after"
+ " toggling Airplane mode and rebooting.")
+ raise signals.TestFailure(msg)
+
+ def verify_traffic_between_devices(self,dest_device,src_device,num_of_tries=2):
+ """Test the clients and DUT can ping each other.
+
+ Args:
+ num_of_tries: the retry times of ping test.
+ dest_device:Test device.
+ src_device:Second DUT access same AP
+ """
+ dest_device = dest_device.droid.connectivityGetIPv4Addresses('wlan0')[0]
+ for _ in range(num_of_tries):
+ if acts.utils.adb_shell_ping(src_device, count=10, dest_ip=dest_device, timeout=20):
+ break
+ else:
+ asserts.fail("Ping to %s from %s failed" % (src_device.serial, dest_device))
+
+ """Tests"""
+
+ @test_tracker_info(uuid="525fc5e3-afba-4bfd-9a02-5834119e3c66")
+ def test_toggle_wifi_state_and_get_startupTime(self):
+ """Test toggling wifi"""
+ self.log.debug("Going from on to off.")
+ wutils.wifi_toggle_state(self.dut, False)
+ self.log.debug("Going from off to on.")
+ startTime = time.time()
+ wutils.wifi_toggle_state(self.dut, True)
+ startup_time = time.time() - startTime
+ self.log.debug("WiFi was enabled on the device in %s s." % startup_time)
+
+ @test_tracker_info(uuid="e9d11563-2bbe-4c96-87eb-ec919b51435b")
+ def test_toggle_with_screen(self):
+ """Test toggling wifi with screen on/off"""
+ wait_time = 5
+ self.log.debug("Screen from off to on.")
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ time.sleep(wait_time)
+ self.log.debug("Going from on to off.")
+ try:
+ wutils.wifi_toggle_state(self.dut, False)
+ time.sleep(wait_time)
+ self.log.debug("Going from off to on.")
+ wutils.wifi_toggle_state(self.dut, True)
+ finally:
+ self.dut.droid.wakeLockRelease()
+ time.sleep(wait_time)
+ self.dut.droid.goToSleepNow()
+
+ @test_tracker_info(uuid="71556e06-7fb1-4e2b-9338-b01f1f8e286e")
+ def test_scan(self):
+ """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="3ea09efb-6921-429e-afb1-705ef5a09afa")
+ def test_scan_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)
+
+ """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="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"""
+ self.turn_location_on_and_scan_toggle_off()
+ wutils.wifi_toggle_state(self.dut, False)
+
+ """Test wifi connection scan should fail."""
+ self.dut.droid.wifiStartScan()
+ try:
+ self.dut.ed.pop_event("WifiManagerScanResultsAvailable", 60)
+ except queue.Empty:
+ self.log.debug("Wifi scan results not received.")
+ else:
+ asserts.fail("Wi-Fi scan results received")
+
+ @test_tracker_info(uuid="a4ad9930-a8fa-4868-81ed-a79c7483e502")
+ def test_add_network(self):
+ """Test wifi connection scan."""
+ ssid = self.open_network_2g[WifiEnums.SSID_KEY]
+ nId = self.dut.droid.wifiAddNetwork(self.open_network_2g)
+ asserts.assert_true(nId > -1, "Failed to add network.")
+ configured_networks = self.dut.droid.wifiGetConfiguredNetworks()
+ self.log.debug(
+ ("Configured networks after adding: %s" % configured_networks))
+ wutils.assert_network_in_list({
+ WifiEnums.SSID_KEY: ssid
+ }, configured_networks)
+
+ @test_tracker_info(uuid="aca85551-10ba-4007-90d9-08bcdeb16a60")
+ def test_forget_network(self):
+ ssid = self.open_network_2g[WifiEnums.SSID_KEY]
+ nId = self.dut.droid.wifiAddNetwork(self.open_network_2g)
+ asserts.assert_true(nId > -1, "Failed to add network.")
+ configured_networks = self.dut.droid.wifiGetConfiguredNetworks()
+ self.log.debug(
+ ("Configured networks after adding: %s" % configured_networks))
+ wutils.assert_network_in_list({
+ WifiEnums.SSID_KEY: ssid
+ }, configured_networks)
+ wutils.wifi_forget_network(self.dut, ssid)
+ configured_networks = self.dut.droid.wifiGetConfiguredNetworks()
+ for nw in configured_networks:
+ asserts.assert_true(
+ nw[WifiEnums.BSSID_KEY] != ssid,
+ "Found forgotten network %s in configured networks." % ssid)
+
+ @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.
+
+ Steps:
+ 1. Connect to a 2GHz network.
+ 2. Connect to a 5GHz network.
+ 3. Reconnect to the 2GHz network using its network id.
+ 4. Reconnect to the 5GHz network using its network id.
+
+ """
+ connect_2g_data = self.get_connection_data(self.dut, self.wpapsk_2g)
+ connect_5g_data = self.get_connection_data(self.dut, self.wpapsk_5g)
+ 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.")
+ 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="334175c3-d26a-4c87-a8ab-8eb220b2d80f")
+ def test_reconnect_toggle_wifi(self):
+ """Connect to multiple networks, turn off/on wifi, then reconnect to
+ a previously connected network.
+
+ Steps:
+ 1. Connect to a 2GHz network.
+ 2. Connect to a 5GHz network.
+ 3. Turn WiFi OFF/ON.
+ 4. Reconnect to the non-current network.
+
+ """
+ self.helper_reconnect_toggle_wifi()
+
+ @test_tracker_info(uuid="bd2cec9e-7f17-44ef-8a0c-4da92a9b55ae")
+ def test_reconnect_toggle_wifi_with_location_scan_on(self):
+ """Connect to multiple networks, turn off/on wifi, then reconnect to
+ a previously connected network.
+
+ Steps:
+ 1. Turn on location scans.
+ 2. Connect to a 2GHz network.
+ 3. Connect to a 5GHz network.
+ 4. Turn WiFi OFF/ON.
+ 5. Reconnect to the non-current network.
+
+ """
+ self.turn_location_on_and_scan_toggle_on()
+ self.helper_reconnect_toggle_wifi()
+
+ @test_tracker_info(uuid="8e6e6c21-fefb-4fe8-9fb1-f09b1182b76d")
+ def test_reconnect_toggle_airplane(self):
+ """Connect to multiple networks, turn on/off Airplane moce, then
+ reconnect a previously connected network.
+
+ Steps:
+ 1. Connect to a 2GHz network.
+ 2. Connect to a 5GHz network.
+ 3. Turn ON/OFF Airplane mode.
+ 4. Reconnect to the non-current network.
+
+ """
+ self.helper_reconnect_toggle_airplane()
+
+ @test_tracker_info(uuid="28562f13-8a0a-492e-932c-e587560db5f2")
+ def test_reconnect_toggle_airplane_with_location_scan_on(self):
+ """Connect to multiple networks, turn on/off Airplane moce, then
+ reconnect a previously connected network.
+
+ Steps:
+ 1. Turn on location scans.
+ 2. Connect to a 2GHz network.
+ 3. Connect to a 5GHz network.
+ 4. Turn ON/OFF Airplane mode.
+ 5. Reconnect to the non-current network.
+
+ """
+ self.turn_location_on_and_scan_toggle_on()
+ self.helper_reconnect_toggle_airplane()
+
+ @test_tracker_info(uuid="3d041c12-05e2-46a7-ab9b-e3f60cc735db")
+ def test_reboot_configstore_reconnect(self):
+ """Connect to multiple networks, reboot then reconnect to previously
+ connected network.
+
+ Steps:
+ 1. Connect to a 2GHz network.
+ 2. Connect to a 5GHz network.
+ 3. Reboot device.
+ 4. Verify all networks are persistent after reboot.
+ 5. Reconnect to the non-current network.
+
+ """
+ self.helper_reboot_configstore_reconnect()
+
+ @test_tracker_info(uuid="a70d5853-67b5-4d48-bdf7-08ee51fafd21")
+ def test_reboot_configstore_reconnect_with_location_scan_on(self):
+ """Connect to multiple networks, reboot then reconnect to previously
+ connected network.
+
+ Steps:
+ 1. Turn on location scans.
+ 2. Connect to a 2GHz network.
+ 3. Connect to a 5GHz network.
+ 4. Reboot device.
+ 5. Verify all networks are persistent after reboot.
+ 6. Reconnect to the non-current network.
+
+ """
+ self.turn_location_on_and_scan_toggle_on()
+ self.helper_reboot_configstore_reconnect()
+
+ @test_tracker_info(uuid="26d94dfa-1349-4c8b-aea0-475eb73bb521")
+ def test_toggle_wifi_reboot_configstore_reconnect(self):
+ """Connect to multiple networks, disable WiFi, reboot, then
+ reconnect to previously connected network.
+
+ Steps:
+ 1. Connect to a 2GHz network.
+ 2. Connect to a 5GHz network.
+ 3. Turn WiFi OFF.
+ 4. Reboot device.
+ 5. Turn WiFi ON.
+ 4. Verify all networks are persistent after reboot.
+ 5. Reconnect to the non-current network.
+
+ """
+ self.helper_toggle_wifi_reboot_configstore_reconnect()
+
+ @test_tracker_info(uuid="7c004a3b-c1c6-4371-9124-0f34650be915")
+ def test_toggle_wifi_reboot_configstore_reconnect_with_location_scan_on(self):
+ """Connect to multiple networks, disable WiFi, reboot, then
+ reconnect to previously connected network.
+
+ Steps:
+ 1. Turn on location scans.
+ 2. Connect to a 2GHz network.
+ 3. Connect to a 5GHz network.
+ 4. Turn WiFi OFF.
+ 5. Reboot device.
+ 6. Turn WiFi ON.
+ 7. Verify all networks are persistent after reboot.
+ 8. Reconnect to the non-current network.
+
+ """
+ self.turn_location_on_and_scan_toggle_on()
+ self.helper_toggle_wifi_reboot_configstore_reconnect()
+
+ @test_tracker_info(uuid="4fce017b-b443-40dc-a598-51d59d3bb38f")
+ def test_toggle_airplane_reboot_configstore_reconnect(self):
+ """Connect to multiple networks, enable Airplane mode, reboot, then
+ reconnect to previously connected network.
+
+ Steps:
+ 1. Connect to a 2GHz network.
+ 2. Connect to a 5GHz network.
+ 3. Toggle Airplane mode ON.
+ 4. Reboot device.
+ 5. Toggle Airplane mode OFF.
+ 4. Verify all networks are persistent after reboot.
+ 5. Reconnect to the non-current network.
+
+ """
+ self.helper_toggle_airplane_reboot_configstore_reconnect()
+
+ @test_tracker_info(uuid="7f0810f9-2338-4158-95f5-057f5a1905b6")
+ def test_toggle_airplane_reboot_configstore_reconnect_with_location_scan_on(self):
+ """Connect to multiple networks, enable Airplane mode, reboot, then
+ reconnect to previously connected network.
+
+ Steps:
+ 1. Turn on location scans.
+ 2. Connect to a 2GHz network.
+ 3. Connect to a 5GHz network.
+ 4. Toggle Airplane mode ON.
+ 5. Reboot device.
+ 6. Toggle Airplane mode OFF.
+ 7. Verify all networks are persistent after reboot.
+ 8. Reconnect to the non-current network.
+
+ """
+ self.turn_location_on_and_scan_toggle_on()
+ self.helper_toggle_airplane_reboot_configstore_reconnect()
+
+ @test_tracker_info(uuid="81eb7527-4c92-4422-897a-6b5f6445e84a")
+ def test_config_store_with_wpapsk_2g(self):
+ self.connect_to_wifi_network_toggle_wifi_and_run_iperf(
+ (self.wpapsk_2g, self.dut))
+
+ @test_tracker_info(uuid="8457903d-cb7e-4c89-bcea-7f59585ea6e0")
+ def test_config_store_with_wpapsk_5g(self):
+ self.connect_to_wifi_network_toggle_wifi_and_run_iperf(
+ (self.wpapsk_5g, self.dut))
+
+ @test_tracker_info(uuid="b9fbc13a-47b4-4f64-bd2c-e5a3cb24ab2f")
+ def test_tdls_supported(self):
+ model = self.dut.model
+ self.log.debug("Model is %s" % model)
+ if not model.startswith("volantis"):
+ asserts.assert_true(self.dut.droid.wifiIsTdlsSupported(), (
+ "TDLS should be supported on %s, but device is "
+ "reporting not supported.") % model)
+ else:
+ asserts.assert_false(self.dut.droid.wifiIsTdlsSupported(), (
+ "TDLS should not be supported on %s, but device "
+ "is reporting supported.") % model)
+
+ @test_tracker_info(uuid="50637d40-ea59-4f4b-9fc1-e6641d64074c")
+ def test_energy_info(self):
+ """Verify the WiFi energy info reporting feature """
+ self.get_energy_info()
+
+ @test_tracker_info(uuid="1f1cf549-53eb-4f36-9f33-ce06c9158efc")
+ def test_energy_info_connected(self):
+ """Verify the WiFi energy info reporting feature when connected.
+
+ Connect to a wifi network, then the same as test_energy_info.
+ """
+ wutils.wifi_connect(self.dut, self.open_network_2g)
+ self.get_energy_info()
+
+ @test_tracker_info(uuid="2622c253-defc-4a35-93a6-ca9d29a8238c")
+ def test_connect_to_wep_2g(self):
+ """Verify DUT can connect to 2GHz WEP network
+
+ Steps:
+ 1. Ensure the 2GHz WEP network is visible in scan result.
+ 2. Connect to the network and validate internet connection.
+ """
+ wutils.connect_to_wifi_network(self.dut, self.wep_networks[0]["2g"])
+
+ @test_tracker_info(uuid="1f2d17a2-e92d-43af-966b-3421c0db8620")
+ def test_connect_to_wep_5g(self):
+ """Verify DUT can connect to 5GHz WEP network
+
+ Steps:
+ 1. Ensure the 5GHz WEP network is visible in scan result.
+ 2. Connect to the network and validate internet connection.
+ """
+ wutils.connect_to_wifi_network(self.dut, self.wep_networks[0]["5g"])
+
+ @test_tracker_info(uuid="4a957952-289d-4657-9882-e1475274a7ff")
+ def test_connect_to_wpa_2g(self):
+ """Verify DUT can connect to 2GHz WPA-PSK network
+
+ Steps:
+ 1. Ensure the 2GHz WPA-PSK network is visible in scan result.
+ 2. Connect to the network and validate internet connection.
+ """
+ wutils.connect_to_wifi_network(self.dut, self.wpa_networks[0]["2g"])
+
+ @test_tracker_info(uuid="612c3c31-a4c5-4014-9a2d-3f4bcc20c0d7")
+ def test_connect_to_wpa_5g(self):
+ """Verify DUT can connect to 5GHz WPA-PSK network
+
+ Steps:
+ 1. Ensure the 5GHz WPA-PSK network is visible in scan result.
+ 2. Connect to the network and validate internet connection.
+ """
+ wutils.connect_to_wifi_network(self.dut, self.wpa_networks[0]["5g"])
+
+ @test_tracker_info(uuid="2a617fb4-1d8e-44e9-a500-a5456e1df83f")
+ def test_connect_to_2g_can_be_pinged(self):
+ """Verify DUT can be pinged by another device when it connects to 2GHz AP
+
+ Steps:
+ 1. Ensure the 2GHz WPA-PSK network is visible in scan result.
+ 2. Connect to the network and validate internet connection.
+ 3. Check DUT can be pinged by another device
+ """
+ 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"])
+ self.verify_traffic_between_devices(self.dut,self.dut_client)
+
+ @test_tracker_info(uuid="94bdd657-649b-4a2c-89c3-3ec6ba18e08e")
+ def test_connect_to_5g_can_be_pinged(self):
+ """Verify DUT can be pinged by another device when it connects to 5GHz AP
+
+ Steps:
+ 1. Ensure the 5GHz WPA-PSK network is visible in scan result.
+ 2. Connect to the network and validate internet connection.
+ 3. Check DUT can be pinged by another device
+ """
+ wutils.connect_to_wifi_network(self.dut, self.wpa_networks[0]["5g"])
+ wutils.connect_to_wifi_network(self.dut_client, self.wpa_networks[0]["5g"])
+ self.verify_traffic_between_devices(self.dut,self.dut_client)
+
+ @test_tracker_info(uuid="d87359aa-c4da-4554-b5de-8e3fa852a6b0")
+ def test_sta_turn_off_screen_can_be_pinged(self):
+ """Verify DUT can be pinged by another device after idle for a while
+
+ Steps:
+ 1. Ensure the 2GHz WPA-PSK network is visible in scan result.
+ 2. DUT and DUT_Client connect to the network and validate internet connection.
+ 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_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_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()
+ # TODO(hsiuchangchen): find a way to check system already suspended
+ # instead of waiting 5 mins
+ 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_client_a,
+ self.dut_client_b)
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+
+ @test_tracker_info(uuid="402cfaa8-297f-4865-9e27-6bab6adca756")
+ def test_reboot_wifi_and_bluetooth_on(self):
+ """Toggle WiFi and bluetooth ON then reboot """
+ wutils.wifi_toggle_state(self.dut, True)
+ enable_bluetooth(self.dut.droid, self.dut.ed)
+
+ self.dut.reboot()
+ time.sleep(DEFAULT_TIMEOUT)
+
+ asserts.assert_true(self.dut.droid.bluetoothCheckState(),
+ "bluetooth state changed after reboot")
+ asserts.assert_true(self.dut.droid.wifiCheckState(),
+ "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/tests/google/wifi/WifiNativeTest.py b/acts_tests/tests/google/wifi/WifiNativeTest.py
new file mode 100644
index 0000000..56e2195
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiNativeTest.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2016 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.controllers import native_android_device
+
+class WifiNativeTest(base_test.BaseTestClass):
+
+ def setup_class(self):
+ self.native_devices = self.register_controller(native_android_device)
+ self.dut = self.native_devices[0]
+
+ def setup_test(self):
+# TODO: uncomment once wifi_toggle_state (or alternative)
+# work with sl4n
+# assert wutils.wifi_toggle_state(self.device, True)
+ return self.dut.droid.WifiInit()
+
+# TODO: uncomment once wifi_toggle_state (or alternative)
+# work with sl4n
+# def teardown_test(self):
+# assert wutils.wifi_toggle_state(self.device, False)
+
+ def test_hal_get_features(self):
+ result = self.dut.droid.WifiGetSupportedFeatureSet()
+ self.log.info("Wi-Fi feature set: %d", result)
diff --git a/acts_tests/tests/google/wifi/WifiNetworkRequestTest.py b/acts_tests/tests/google/wifi/WifiNetworkRequestTest.py
new file mode 100644
index 0000000..81aa86d
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiNetworkRequestTest.py
@@ -0,0 +1,494 @@
+#!/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
+
+# Network request timeout to use.
+NETWORK_REQUEST_TIMEOUT_MS = 60 * 1000
+# Timeout to wait for instant failure.
+NETWORK_REQUEST_INSTANT_FAILURE_TIMEOUT_SEC = 5
+
+class WifiNetworkRequestTest(WifiBaseTest):
+ """Tests for NetworkRequest with WifiNetworkSpecifier API surface.
+
+ Test Bed Requirement:
+ * one Android device
+ * Several Wi-Fi networks visible to the device, including an open Wi-Fi
+ network.
+ """
+ def __init__(self, configs):
+ super().__init__(configs)
+ self.enable_packet_log = True
+
+ 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"
+ ]
+ 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,
+ 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,
+ "Need at least one reference network with psk.")
+ self.wpa_psk_2g = self.reference_networks[0]["2g"]
+ self.wpa_psk_5g = self.reference_networks[0]["5g"]
+ self.open_2g = self.open_network[0]["2g"]
+ self.open_5g = self.open_network[0]["5g"]
+
+ def setup_test(self):
+ super().setup_test()
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ self.remove_approvals()
+ self.clear_user_disabled_networks()
+ wutils.wifi_toggle_state(self.dut, True)
+ self.dut.ed.clear_all_events()
+
+ def teardown_test(self):
+ super().teardown_test()
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ 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 teardown_class(self):
+ if "AccessPoint" in self.user_params:
+ del self.user_params["reference_networks"]
+ del self.user_params["open_network"]
+
+ """Helper Functions"""
+ def wait_for_network_lost(self):
+ """
+ Wait for network lost callback from connectivity service (wifi
+ disconnect).
+
+ Args:
+ ad: Android device object.
+ """
+ try:
+ self.dut.droid.wifiStartTrackingStateChange()
+ event = self.dut.ed.pop_event(
+ wifi_constants.WIFI_NETWORK_CB_ON_LOST, 10)
+ self.dut.droid.wifiStopTrackingStateChange()
+ except queue.Empty:
+ raise signals.TestFailure(
+ "Device did not disconnect from the network")
+
+ def remove_approvals(self):
+ self.dut.log.debug("Removing all approvals from sl4a app")
+ self.dut.adb.shell(
+ "cmd wifi network-requests-remove-user-approved-access-points"
+ + " " + SL4A_APK_NAME)
+
+ def clear_user_disabled_networks(self):
+ self.dut.log.debug("Clearing user disabled networks")
+ self.dut.adb.shell(
+ "cmd wifi clear-user-disabled-networks")
+
+ @test_tracker_info(uuid="d70c8380-61ba-48a3-b76c-a0b55ce4eabf")
+ def test_connect_to_wpa_psk_2g_with_ssid(self):
+ """
+ Initiates a connection to network via network request with specific SSID
+
+ Steps:
+ 1. Send a network specifier with the specific SSID/credentials of
+ WPA-PSK 2G network.
+ 2. Wait for platform to scan and find matching networks.
+ 3. Simulate user selecting the network.
+ 4. Ensure that the device connects to the network.
+ """
+ wutils.wifi_connect_using_network_request(self.dut, self.wpa_psk_2g,
+ self.wpa_psk_2g)
+
+ @test_tracker_info(uuid="44f2bf40-a282-4413-b8f2-3abb3caa49ee")
+ def test_connect_to_open_5g_with_ssid(self):
+ """
+ Initiates a connection to network via network request with specific SSID
+
+ Steps:
+ 1. Send a network specifier with the specific SSID of Open 5G network.
+ 2. Wait for platform to scan and find matching networks.
+ 3. Simulate user selecting the network.
+ 4. Ensure that the device connects to the network.
+ """
+ wutils.wifi_connect_using_network_request(self.dut, self.open_5g,
+ self.open_5g)
+
+ @test_tracker_info(uuid="09d1823e-4f85-42f8-8c20-de7637f6d4be")
+ def test_connect_to_wpa_psk_5g_with_ssid_pattern(self):
+ """
+ Initiates a connection to network via network request with SSID pattern
+
+ Steps:
+ 1. Send a network specifier with the SSID pattern/credentials of
+ WPA-PSK 5G network.
+ 2. Wait for platform to scan and find matching networks.
+ 3. Simulate user selecting the network.
+ 4. Ensure that the device connects to the network.
+ """
+ network_specifier = self.wpa_psk_5g.copy();
+ # Remove ssid & replace with ssid pattern.
+ network_ssid = network_specifier.pop(WifiEnums.SSID_KEY)
+ # Remove the last element of ssid & replace with .* to create a matching
+ # pattern.
+ network_specifier[WifiEnums.SSID_PATTERN_KEY] = network_ssid[:-1] + ".*"
+ wutils.wifi_connect_using_network_request(self.dut, self.wpa_psk_5g,
+ network_specifier)
+
+ @test_tracker_info(uuid="52216329-06f1-45ef-8d5f-de8b02d9f975")
+ def test_connect_to_open_5g_after_connecting_to_wpa_psk_2g(self):
+ """
+ Initiates a connection to network via network request with SSID pattern
+
+ Steps:
+ 1. Send a network specifier with the specific SSID of Open 5G network.
+ 2. Wait for platform to scan and find matching networks.
+ 3. Simulate user selecting the network.
+ 4. Ensure that the device connects to the network.
+ 5. Release the network request.
+ 6. Send another network specifier with the specific SSID & credentials
+ of WPA-PSK 2G network.
+ 7. Ensure we disconnect from the previous network.
+ 8. Wait for platform to scan and find matching networks.
+ 9. Simulate user selecting the new network.
+ 10. Ensure that the device connects to the new network.
+ """
+ # Complete flow for the first request.
+ wutils.wifi_connect_using_network_request(self.dut, self.wpa_psk_2g,
+ self.wpa_psk_2g)
+ # Release the request.
+ self.dut.droid.wifiReleaseNetwork(self.wpa_psk_2g)
+ # 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)
+
+ @test_tracker_info(uuid="f28b5dc9-771f-42ef-8178-e55e9a16f5c7")
+ def test_connect_to_wpa_psk_5g_while_connecting_to_open_2g(self):
+ """
+ Initiates a connection to network via network request with specific SSID
+
+ Steps:
+ 1. Send a network specifier with the specific SSID & credentials of
+ WPA-PSK 5G network.
+ 2. Send another network specifier with the specific SSID of Open 2G
+ network.
+ 3. Ensure we disconnect from the previous network.
+ 4. Wait for platform to scan and find matching networks.
+ 5. Simulate user selecting the new network.
+ 6. Ensure that the device connects to the new network.
+ """
+ # Make the first request.
+ self.dut.droid.wifiRequestNetworkWithSpecifier(self.open_2g)
+ self.dut.log.info("Sent network request with %s", self.open_2g)
+ # Complete flow for the second request.
+ wutils.wifi_connect_using_network_request(self.dut, self.wpa_psk_5g,
+ self.wpa_psk_5g)
+
+ @test_tracker_info(uuid="2ab82a98-37da-4b27-abb6-578bedebccdc")
+ def test_connect_to_open_5g_while_connected_to_wpa_psk_2g(self):
+ """
+ Initiates a connection to network via network request with specific SSID
+
+ Steps:
+ 1. Send a network specifier with the specific SSID of Open 5G network.
+ 2. Wait for platform to scan and find matching networks.
+ 3. Simulate user selecting the network.
+ 4. Ensure that the device connects to the network.
+ 5. Send another network specifier with the specific SSID & credentials
+ of WPA-PSK 2G network.
+ 6. Ensure we disconnect from the previous network.
+ 7. Wait for platform to scan and find matching networks.
+ 8. Simulate user selecting the new network.
+ 9. Ensure that the device connects to the new network.
+ """
+ # Complete flow for the first request.
+ wutils.wifi_connect_using_network_request(self.dut, self.wpa_psk_2g,
+ self.wpa_psk_2g)
+ # Send the second request.
+ self.dut.droid.wifiRequestNetworkWithSpecifier(self.open_5g)
+ self.dut.log.info("Sent network request with %s", self.open_5g)
+ # Ensure we do not disconnect from the previous network until the user
+ # approves the new request.
+ self.dut.ed.clear_all_events()
+ wutils.ensure_no_disconnect(self.dut)
+
+ # Now complete the flow and ensure we connected to second request.
+ wutils.wait_for_wifi_connect_after_network_request(self.dut,
+ self.open_5g)
+
+ @test_tracker_info(uuid="f0bb2213-b3d1-4fb8-bbdc-ad55c4fb05ed")
+ def test_connect_to_wpa_psk_2g_which_is_already_approved(self):
+ """
+ Initiates a connection to network via network request with specific SSID
+ bypassing user approval.
+
+ Steps:
+ 1. Send a network specifier with the specific SSID/credentials of
+ WPA-PSK 2G network.
+ 2. Wait for platform to scan and find matching networks.
+ 3. Simulate user selecting the network.
+ 4. Ensure that the device connects to the network.
+ 5. Ensure we disconnect from the previous network.
+ 6. Send another network specifier with the specific
+ SSID/BSSID/credentials of WPA-PSK 2G network.
+ 7. Ensure that the device bypasses user approval & connects to the
+ same network.
+ """
+ # Complete flow for the first request.
+ wutils.wifi_connect_using_network_request(self.dut, self.wpa_psk_2g,
+ self.wpa_psk_2g)
+ # Release the request.
+ self.dut.droid.wifiReleaseNetwork(self.wpa_psk_2g)
+ # Ensure we disconnected from the 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()
+
+ # Find bssid for the WPA-PSK 2G network.
+ scan_results = self.dut.droid.wifiGetScanResults()
+ match_results = wutils.match_networks(
+ {WifiEnums.SSID_KEY: self.wpa_psk_2g[WifiEnums.SSID_KEY]},
+ scan_results)
+ asserts.assert_equal(len(match_results), 1,
+ "Cannot find bssid for WPA-PSK 2G network")
+ bssid = match_results[0][WifiEnums.BSSID_KEY]
+ # Send the second request with bssid.
+ network_specifier_with_bssid = self.wpa_psk_2g.copy();
+ network_specifier_with_bssid[WifiEnums.BSSID_KEY] = bssid
+ self.dut.droid.wifiRequestNetworkWithSpecifier(
+ network_specifier_with_bssid)
+ self.dut.log.info("Sent network request with %r",
+ network_specifier_with_bssid)
+
+ # Ensure we connected to second request without user approval.
+ wutils.wait_for_connect(self.dut, self.wpa_psk_2g[WifiEnums.SSID_KEY])
+
+ @test_tracker_info(uuid="fcf84d94-5f6e-4bd6-9f76-40a0228d4ebe")
+ def test_connect_to_wpa_psk_2g_which_is_already_approved_but_then_forgot(self):
+ """
+ Initiates a connection to network via network request with specific SSID
+ with user approval.
+
+ Steps:
+ 1. Send a network specifier with the specific SSID/credentials of
+ WPA-PSK 2G network.
+ 2. Wait for platform to scan and find matching networks.
+ 3. Simulate user selecting the network.
+ 4. Ensure that the device connects to the network.
+ 4. Simulate user fogetting the network from the UI.
+ 6. Ensure we disconnect from the previous network.
+ 7. Send another network specifier with the specific
+ SSID/BSSID/credentials of WPA-PSK 2G network.
+ 8. Ensure that the device does not bypass user approval & connects to the
+ same network with user approval. (This should also clear the blacklist)
+ 9. Send the same network specifier with the specific
+ SSID/BSSID/credentials of WPA-PSK 2G network.
+ 10.Ensure that the device bypasses user approval now & connects to the
+ same network.
+ """
+ # Complete flow for the first request.
+ wutils.wifi_connect_using_network_request(self.dut, self.wpa_psk_2g,
+ self.wpa_psk_2g)
+
+ # Simulate user forgeting the ephemeral network.
+ 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)
+ self.dut.ed.clear_all_events()
+ # Release the first request.
+ self.dut.droid.wifiReleaseNetwork(self.wpa_psk_2g)
+
+ # Find bssid for the WPA-PSK 2G network.
+ scan_results = self.dut.droid.wifiGetScanResults()
+ match_results = wutils.match_networks(
+ {WifiEnums.SSID_KEY: self.wpa_psk_2g[WifiEnums.SSID_KEY]},
+ scan_results)
+ asserts.assert_equal(len(match_results), 1,
+ "Cannot find bssid for WPA-PSK 2G network")
+ bssid = match_results[0][WifiEnums.BSSID_KEY]
+ # Send the second request with bssid.
+ network_specifier_with_bssid = self.wpa_psk_2g.copy();
+ network_specifier_with_bssid[WifiEnums.BSSID_KEY] = bssid
+ self.dut.droid.wifiRequestNetworkWithSpecifier(
+ network_specifier_with_bssid)
+ self.dut.log.info("Sent network request with %r",
+ network_specifier_with_bssid)
+
+ # Ensure that we did not connect bypassing user approval.
+ assert_msg = "Device should not connect without user approval"
+ asserts.assert_false(
+ wutils.wait_for_connect(self.dut,
+ self.wpa_psk_2g[WifiEnums.SSID_KEY],
+ assert_on_fail=False),
+ assert_msg)
+
+ # Now complete the flow and ensure we connected to second request.
+ wutils.wait_for_wifi_connect_after_network_request(self.dut,
+ self.wpa_psk_2g)
+
+ # Now make the same request again & ensure that we connect without user
+ # approval.
+ self.dut.droid.wifiRequestNetworkWithSpecifier(
+ network_specifier_with_bssid)
+ self.dut.log.info("Sent network request with %r",
+ network_specifier_with_bssid)
+ wutils.wait_for_connect(self.dut, self.wpa_psk_2g[WifiEnums.SSID_KEY])
+
+ @test_tracker_info(uuid="2f90a266-f04d-4932-bb5b-d075bedfd56d")
+ def test_match_failure_with_invalid_ssid_pattern(self):
+ """
+ Initiates a connection to network via network request with SSID pattern
+ that does not match any networks.
+
+ Steps:
+ 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)
+ network_specifier[WifiEnums.SSID_PATTERN_KEY] = \
+ network_ssid + "blah" + ".*"
+
+ self.dut.droid.wifiStartTrackingStateChange()
+ expected_ssid = network[WifiEnums.SSID_KEY]
+
+ self.dut.droid.wifiRequestNetworkWithSpecifierWithTimeout(
+ network_specifier, NETWORK_REQUEST_TIMEOUT_MS)
+ self.dut.log.info("Sent network request with invalid specifier %s",
+ network_specifier)
+ 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 * 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="caa96f57-840e-4997-9280-655edd3b76ee")
+ def test_connect_failure_user_rejected(self):
+ """
+ Initiates a connection to network via network request with specific SSID
+ which the user rejected.
+
+ Steps:
+ 1. Send a network specifier with the specific SSID/credentials of
+ WPA-PSK 5G network.
+ 2. Wait for platform to scan and find matching networks.
+ 3. Simulate user rejecting the network.
+ 4. Ensure that we get an instant onUnavailable callback.
+ 5. Simulate user fogetting the network from the UI.
+ """
+ network = self.wpa_psk_5g
+ expected_ssid = network[WifiEnums.SSID_KEY]
+
+ self.dut.droid.wifiStartTrackingStateChange()
+
+ self.dut.droid.wifiRequestNetworkWithSpecifierWithTimeout(
+ network, NETWORK_REQUEST_TIMEOUT_MS)
+ self.dut.log.info("Sent network request with specifier %s", network)
+ time.sleep(wifi_constants.NETWORK_REQUEST_CB_REGISTER_DELAY_SEC)
+ self.dut.droid.wifiRegisterNetworkRequestMatchCallback()
+
+ # Wait for the platform to scan and return a list of networks
+ # matching the request
+ try:
+ matched_network = None
+ for _ in [0, 3]:
+ on_match_event = self.dut.ed.pop_event(
+ wifi_constants.WIFI_NETWORK_REQUEST_MATCH_CB_ON_MATCH, 30)
+ asserts.assert_true(on_match_event,
+ "Network request on match not received.")
+ matched_scan_results = on_match_event["data"]
+ self.dut.log.debug("Network request on match results %s",
+ matched_scan_results)
+ matched_network = wutils.match_networks(
+ {WifiEnums.SSID_KEY: network[WifiEnums.SSID_KEY]},
+ matched_scan_results)
+ if matched_network:
+ break;
+ asserts.assert_true(
+ matched_network, "Target network %s not found" % network)
+
+ # Send user rejection.
+ self.dut.droid.wifiSendUserRejectionForNetworkRequestMatch()
+ self.dut.log.info("Sent user rejection for network request %s",
+ expected_ssid)
+
+ # Wait for the platform to raise unavailable callback
+ # instantaneously.
+ on_unavailable_event = self.dut.ed.pop_event(
+ wifi_constants.WIFI_NETWORK_CB_ON_UNAVAILABLE,
+ NETWORK_REQUEST_INSTANT_FAILURE_TIMEOUT_SEC)
+ asserts.assert_true(on_unavailable_event,
+ "Network request on available not received.")
+ except queue.Empty:
+ asserts.fail("Expected events not returned")
+ finally:
+ self.dut.droid.wifiStopTrackingStateChange()
diff --git a/acts_tests/tests/google/wifi/WifiNetworkSelectorTest.py b/acts_tests/tests/google/wifi/WifiNetworkSelectorTest.py
new file mode 100644
index 0000000..7de603c
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiNetworkSelectorTest.py
@@ -0,0 +1,448 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2016 - 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
+import time
+
+import acts.signals as signals
+
+from acts import asserts
+from acts import base_test
+from acts.controllers import android_device
+from acts.controllers import attenuator
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+WifiEnums = wutils.WifiEnums
+
+AP_1 = 0
+AP_2 = 1
+# 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):
+ """These tests verify the behavior of the Android Wi-Fi Network Selector
+ feature.
+ """
+ def __init__(self, configs):
+ super().__init__(configs)
+ self.enable_packet_log = True
+
+ def setup_class(self):
+ super().setup_class()
+
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ self.ap1_2g_attn = 0
+ self.ap1_5g_attn = 1
+ self.ap2_2g_attn = 2
+ self.ap2_5g_attn = 3
+ 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(open_network=True,
+ wpa_network=True,
+ ap_count=2)
+ self.ap1_5g_attn, self.ap2_2g_attn, self.ap2_5g_attn, = 0, 1, 1
+ self.configure_packet_capture()
+
+ def setup_test(self):
+ super().setup_test()
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ self.dut.ed.clear_all_events()
+ for a in self.attenuators:
+ a.set_atten(MAX_ATTN)
+ time.sleep(ATTN_SLEEP)
+
+ def teardown_test(self):
+ super().teardown_test()
+ for a in self.attenuators:
+ a.set_atten(MIN_ATTN)
+ wutils.reset_wifi(self.dut)
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+
+ 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_networks(self, ad, networks):
+ """Add Wi-Fi networks to an Android device and verify the networks were
+ added correctly.
+
+ Args:
+ ad: the AndroidDevice object to add networks to.
+ networks: a list of dicts, each dict represents a Wi-Fi network.
+ """
+ for network in networks:
+ ret = ad.droid.wifiAddNetwork(network)
+ asserts.assert_true(ret != -1,
+ "Failed to add network %s" % network)
+ ad.droid.wifiEnableNetwork(ret, 0)
+
+ configured_networks = ad.droid.wifiGetConfiguredNetworks()
+ self.log.info("Configured networks: %s", configured_networks)
+
+ 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.
+
+ Args:
+ expected_bssid: Network bssid to which connection.
+
+ Returns:
+ True if connection to given network happen, else return False.
+ """
+ expected_ssid = network['SSID']
+ expected_bssid = network['bssid']
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ self.dut, expected_ssid)
+ time.sleep(20)
+ actual_network = self.dut.droid.wifiGetConnectionInfo()
+ self.log.info("Actual network: %s", actual_network)
+ asserts.assert_true(
+ actual_network and WifiEnums.BSSID_KEY in actual_network and \
+ expected_bssid.lower() == actual_network[
+ WifiEnums.BSSID_KEY].lower(),
+ "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 """
+
+ @test_tracker_info(uuid="ffa5e278-db3f-4e17-af11-6c7a3e7c5cc2")
+ def test_network_selector_automatic_connection(self):
+ """
+ 1. Add one saved network to DUT.
+ 2. Move the DUT in range.
+ 3. Verify the DUT is connected to the network.
+ """
+ # 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[self.ap1_5g_attn].set_atten(MIN_ATTN)
+ time.sleep(ATTN_SLEEP)
+
+ # verify DUT is connected to AP_1 5g network
+ network = self.reference_networks[AP_1]['5g'].copy()
+ if "OpenWrtAP" in self.user_params:
+ network['bssid'] = self.bssid_map[AP_1]['5g'][network["SSID"]]
+ self.connect_and_verify_connected_bssid(network)
+
+ @test_tracker_info(uuid="3ea818f2-10d7-4aad-bfab-7d8fb25aae78")
+ def test_network_selector_basic_connection_prefer_5g(self):
+ """
+ 1. Add one saved SSID with 2G and 5G BSSIDs of similar RSSI.
+ 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
+ networks = [self.reference_networks[AP_1]['2g'],
+ self.reference_networks[AP_1]['5g']]
+ self.add_networks(self.dut, networks)
+
+ # Move DUT in range
+ self.attenuators[self.ap1_2g_attn].set_atten(MIN_ATTN)
+ self.attenuators[self.ap1_5g_attn].set_atten(MIN_ATTN)
+ time.sleep(ATTN_SLEEP)
+
+ # verify DUT is connected to 5G network
+ network = self.reference_networks[AP_1]['5g'].copy()
+ if "OpenWrtAP" in self.user_params:
+ network['bssid'] = self.bssid_map[AP_1]['5g'][network["SSID"]]
+ self.connect_and_verify_connected_bssid(network)
+
+ @test_tracker_info(uuid="bebb29ca-4486-4cde-b390-c5f8f2e1580c")
+ def test_network_selector_prefer_stronger_rssi(self):
+ """
+ 1. Add two saved SSIDs to DUT, same band, one has stronger RSSI
+ than the other.
+ 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']]
+ self.add_networks(self.dut, networks)
+
+ # move the DUT in range
+ self.attenuators[self.ap1_2g_attn].set_atten(LVL1_ATTN)
+ self.attenuators[self.ap2_2g_attn].set_atten(LVL2_ATTN)
+ time.sleep(ATTN_SLEEP)
+
+ # verify DUT is connected AP_1
+ network = self.reference_networks[AP_1]['2g'].copy()
+ if "OpenWrtAP" in self.user_params:
+ network['bssid'] = self.bssid_map[AP_1]['2g'][network["SSID"]]
+ self.connect_and_verify_connected_bssid(network)
+
+ @test_tracker_info(uuid="f9f72dc5-034f-4fe2-a27d-df1b6cae76cd")
+ def test_network_selector_prefer_secure_over_open_network(self):
+ """
+ 1. Add two saved networks to DUT, same band, similar RSSI, one uses
+ WPA2 security, the other is open.
+ 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']]
+ self.add_networks(self.dut, networks)
+
+ # Move DUT in range
+ self.attenuators[self.ap1_5g_attn].set_atten(MIN_ATTN)
+ time.sleep(ATTN_SLEEP)
+
+ # verify DUT connects to secure network
+ network = self.reference_networks[AP_1]['5g'].copy()
+ if "OpenWrtAP" in self.user_params:
+ network['bssid'] = self.bssid_map[AP_1]['5g'][network["SSID"]]
+ self.connect_and_verify_connected_bssid(network)
+
+ @test_tracker_info(uuid="ab2c527c-0f9c-4f09-a13f-e3f461b7da52")
+ def test_network_selector_blacklist_by_connection_failure(self):
+ """
+ 1. Add two saved secured networks X and Y to DUT. X has stronger
+ RSSI than Y. X has wrong password configured.
+ 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
+ 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 AP_1 5G has stronger RSSI than AP_2 5G
+ self.attenuators[self.ap1_5g_attn].set_atten(MIN_ATTN)
+ self.attenuators[self.ap2_5g_attn].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)
+
+ # verify DUT is connect AP_2 5G
+ network = self.reference_networks[AP_2]['5g'].copy()
+ if "OpenWrtAP" in self.user_params:
+ network['bssid'] = self.bssid_map[AP_2]['5g'][network["SSID"]]
+ self.connect_and_verify_connected_bssid(network)
+
+ @test_tracker_info(uuid="71d88fcf-c7b8-4fd2-a7cb-84ac4a130ecf")
+ 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.
+ 2. Attenuate the networks so that the DUT is connected to SSID_A's
+ 2G in the beginning.
+ 3. Increase the RSSI of both SSID_A's 5G and SSID_B's 5G.
+ 4. Verify the DUT switches to SSID_A's 5G.
+ """
+ #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 AP_1 2G in range
+ self.attenuators[AP_1_2G_ATTENUATOR].set_atten(0)
+ #verify
+ self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
+ '2g']['bssid'])
+ #make both AP_1 and AP_2 5G in range with similar RSSI
+ self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
+ self.attenuators[AP_2_5G_ATTENUATOR].set_atten(0)
+ #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'])
+
+ @test_tracker_info(uuid="c1243cf4-d96e-427e-869e-3d640bee3f28")
+ 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.
+ 2. Attenuate the networks so that the DUT is connected to SSID_A's
+ 2G in the beginning.
+ 3. Increase the RSSI of SSID_B's 5G while attenuate down SSID_A's
+ 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']]
+ 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
+ self.attenuators[AP_1_2G_ATTENUATOR].set_atten(0)
+ self.attenuators[AP_2_5G_ATTENUATOR].set_atten(20)
+ #verify
+ self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
+ '2g']['bssid'])
+ #bump up AP_2 5G RSSI and reduce AP_1 2G RSSI
+ self.attenuators[AP_1_2G_ATTENUATOR].set_atten(40)
+ self.attenuators[AP_2_5G_ATTENUATOR].set_atten(0)
+ #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_2][
+ '5g']['bssid'])
+
+ @test_tracker_info(uuid="10da95df-83ed-4447-89f8-735b08dbe2eb")
+ 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.
+ 3. Connect the DUT to the 5G BSSID.
+ 4. Bring up the 2G RSSI and attenuate down the 5G RSSI.
+ 5. Verify the DUT switches to the 2G BSSID.
+ """
+ #add a saved network to DUT
+ networks = [self.reference_networks[AP_1]['2g']]
+ self.add_networks(self.dut, networks)
+ #make both AP_1 2G and AP_2 5G in range, and AP_1 5G
+ #has much stronger RSSI than AP_2 2G
+ self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
+ self.attenuators[AP_1_2G_ATTENUATOR].set_atten(50)
+ #verify
+ self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
+ '5g']['bssid'])
+ #bump up AP_1 2G RSSI and reduce AP_1 5G RSSI
+ self.attenuators[AP_1_2G_ATTENUATOR].set_atten(0)
+ self.attenuators[AP_1_5G_ATTENUATOR].set_atten(30)
+ #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][
+ '2g']['bssid'])
+
+ @test_tracker_info(uuid="ead78ae0-27ab-4bb8-ae77-0b9fe588436a")
+ def test_network_selector_stay_on_sufficient_network(self):
+ """
+ 1. Add two 5G WPA2 BSSIDs X and Y to the DUT. X has higher RSSI
+ than Y.
+ 2. Connect the DUT to X.
+ 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']]
+ 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[self.ap1_5g_attn].set_atten(LVL1_ATTN)
+ self.attenuators[self.ap2_5g_attn].set_atten(LVL2_ATTN)
+ time.sleep(ATTN_SLEEP)
+
+ # verify DUT is connected to AP_1
+ network = self.reference_networks[AP_1]['5g'].copy()
+ if "OpenWrtAP" in self.user_params:
+ network['bssid'] = self.bssid_map[AP_1]['5g'][network["SSID"]]
+ self.connect_and_verify_connected_bssid(network)
+
+ # bump up AP_2 5G RSSI over AP_1 5G RSSI
+ self.attenuators[self.ap2_5g_attn].set_atten(MIN_ATTN)
+
+ # ensure the time gap between two network selections
+ time.sleep(NETWORK_SELECTION_TIME_GAP)
+
+ # verify DUT is still connected to AP_1
+ self.connect_and_verify_connected_bssid(network)
+
+ @test_tracker_info(uuid="5470010f-8b62-4b1c-8b83-1f91422eced0")
+ def test_network_selector_stay_on_user_selected_network(self):
+ """
+ 1. Connect the DUT to SSID_A with a very low RSSI via the user select code path.
+ 2. Add SSID_B to the DUT as saved network. SSID_B has higher RSSI than SSID_A.
+ 3. Start a scan and network selection.
+ 4. Verify DUT stays on SSID_A.
+ """
+ # set max attenuation on AP_2 and make AP_1 5G in range with low RSSI
+ self.attenuators[self.ap2_5g_attn].set_atten(MIN_ATTN)
+ self.attenuators[self.ap1_5g_attn].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
+ time.sleep(NETWORK_SELECTION_TIME_GAP)
+
+ # verify we are still connected to AP_1 5G
+ network = self.reference_networks[AP_1]['5g'].copy()
+ if "OpenWrtAP" in self.user_params:
+ network['bssid'] = self.bssid_map[AP_1]['5g'][network["SSID"]]
+ self.connect_and_verify_connected_bssid(network)
+
+ @test_tracker_info(uuid="f08d8f73-8c94-42af-bba9-4c49bbf16420")
+ def test_network_selector_reselect_after_forget_network(self):
+ """
+ 1. Add two 5G BSSIDs X and Y to the DUT. X has higher RSSI
+ than Y.
+ 2. Connect the DUT to X.
+ 3. Forget X.
+ 5. Verify the DUT reselect and connect to Y.
+ """
+ # 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[self.ap1_5g_attn].set_atten(MIN_ATTN)
+ self.attenuators[self.ap2_5g_attn].set_atten(LVL1_ATTN)
+ time.sleep(ATTN_SLEEP)
+
+ # verify DUT connected to AP1
+ network = self.reference_networks[AP_1]['5g'].copy()
+ if "OpenWrtAP" in self.user_params:
+ network['bssid'] = self.bssid_map[AP_1]['5g'][network["SSID"]]
+ self.connect_and_verify_connected_bssid(network)
+
+ # forget AP_1
+ wutils.wifi_forget_network(
+ self.dut, self.reference_networks[AP_1]['5g']['SSID'])
+
+ # verify DUT connected to AP2
+ network = self.reference_networks[AP_2]['5g'].copy()
+ if "OpenWrtAP" in self.user_params:
+ network['bssid'] = self.bssid_map[AP_2]['5g'][network["SSID"]]
+ self.connect_and_verify_connected_bssid(network)
diff --git a/acts_tests/tests/google/wifi/WifiNetworkSuggestionTest.py b/acts_tests/tests/google/wifi/WifiNetworkSuggestionTest.py
new file mode 100644
index 0000000..bdca451
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiNetworkSuggestionTest.py
@@ -0,0 +1,880 @@
+#!/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 __init__(self, configs):
+ super().__init__(configs)
+ self.enable_packet_log = True
+
+ 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]
+ if hasattr(self, "passpoint_networks"):
+ self.passpoint_network = self.passpoint_networks[ATT]
+ self.passpoint_network[WifiEnums.SSID_KEY] = \
+ self.passpoint_networks[ATT][WifiEnums.SSID_KEY][0]
+ self.dut.droid.wifiRemoveNetworkSuggestions([])
+ self.dut.adb.shell(
+ "pm disable com.google.android.apps.carrier.carrierwifi")
+
+ def setup_test(self):
+ super().setup_test()
+ 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):
+ super().teardown_test()
+ 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 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],
+ "carrierId": str(self.dut.droid.telephonyGetSimCarrierId()),
+ }
+ 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],
+ Ent.ALTSUBJECT_MATCH: self.altsubject_match,
+ }
+ 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_network
+ 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_network
+ 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_network
+ 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_network
+ 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/tests/google/wifi/WifiNewSetupAutoJoinTest.py b/acts_tests/tests/google/wifi/WifiNewSetupAutoJoinTest.py
new file mode 100644
index 0000000..59d65e7
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiNewSetupAutoJoinTest.py
@@ -0,0 +1,515 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2016 - 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 asserts
+from acts import base_test
+from acts.test_decorators import test_tracker_info
+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
+NETWORK_ID_ERROR = "Network don't have ID"
+NETWORK_ERROR = "Device is not connected to reference network"
+
+
+class WifiNewSetupAutoJoinTest(WifiBaseTest):
+
+ def __init__(self, configs):
+ super().__init__(configs)
+ self.enable_packet_log = True
+
+ 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.dut.droid.wifiEnableNetwork(ret, 0)
+
+ def setup_class(self):
+ """It will setup the required dependencies from config file and configure
+ the required networks for auto-join testing. Configured networks will
+ not be removed. If networks are already configured it will skip
+ configuring the networks
+
+ Returns:
+ True if successfully configured the requirements for testing.
+ """
+ super().setup_class()
+
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ req_params = ("atten_val", "ping_addr")
+ opt_param = ["reference_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(ap_count=2)
+
+ configured_networks = self.dut.droid.wifiGetConfiguredNetworks()
+ self.log.debug("Configured networks :: {}".format(configured_networks))
+ count_confnet = 0
+ result = False
+ if self.reference_networks[0]['2g']['SSID'] == self.reference_networks[
+ 0]['5g']['SSID']:
+ self.ref_ssid_count = 1
+ else:
+ self.ref_ssid_count = 2 # Different SSID for 2g and 5g
+ for confnet in configured_networks:
+ if confnet[WifiEnums.SSID_KEY] == self.reference_networks[0]['2g'][
+ 'SSID']:
+ count_confnet += 1
+ elif confnet[WifiEnums.SSID_KEY] == self.reference_networks[0][
+ '5g']['SSID']:
+ count_confnet += 1
+ self.log.info("count_confnet {}".format(count_confnet))
+ if count_confnet == self.ref_ssid_count:
+ return
+ else:
+ self.log.info("Configured networks for testing")
+ self.attenuators[0].set_atten(0)
+ self.attenuators[1].set_atten(0)
+ self.attenuators[2].set_atten(90)
+ self.attenuators[3].set_atten(90)
+ wait_time = 15
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ # Add and enable all networks.
+ for network in self.reference_networks:
+ self.add_network_and_enable(network['2g'])
+ self.add_network_and_enable(network['5g'])
+ self.dut.droid.wifiLockRelease()
+ self.dut.droid.goToSleepNow()
+
+ def check_connection(self, network_bssid):
+ """Check current wifi connection networks.
+ Args:
+ network_bssid: Network bssid to which connection.
+ Returns:
+ True if connection to given network happen, else return False.
+ """
+ time.sleep(40) #time for connection state to be updated
+ self.log.info("Check network for {}".format(network_bssid))
+ current_network = self.dut.droid.wifiGetConnectionInfo()
+ self.log.debug("Current network: {}".format(current_network))
+ if WifiEnums.BSSID_KEY in current_network:
+ return current_network[WifiEnums.BSSID_KEY] == network_bssid
+ return False
+
+ def set_attn_and_validate_connection(self, attn_value, bssid):
+ """Validate wifi connection status on different attenuation setting.
+
+ Args:
+ attn_value: Attenuation value for different APs signal.
+ bssid: Bssid of excepted network.
+
+ Returns:
+ True if bssid of current network match, else false.
+ """
+ self.attenuators[0].set_atten(attn_value[0])
+ self.attenuators[1].set_atten(attn_value[1])
+ self.attenuators[2].set_atten(attn_value[2])
+ self.attenuators[3].set_atten(attn_value[3])
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ try:
+ asserts.assert_true(
+ self.check_connection(bssid),
+ "Device is not connected to required bssid {}".format(bssid))
+ time.sleep(10) #wait for connection to be active
+ asserts.assert_true(
+ wutils.validate_connection(self.dut, self.ping_addr),
+ "Error, No Internet connection for current bssid {}".format(
+ bssid))
+ finally:
+ self.dut.droid.wifiLockRelease()
+ self.dut.droid.goToSleepNow()
+
+ def teardown_class(self):
+ for ad in self.android_devices:
+ wutils.reset_wifi(ad)
+ if "AccessPoint" in self.user_params:
+ del self.user_params["reference_networks"]
+ del self.user_params["open_network"]
+
+ """ Tests Begin """
+
+ """ Test wifi auto join functionality move in range of AP1.
+
+ 1. Attenuate the signal to low range of AP1 and Ap2 not visible at all.
+ 2. Wake up the device.
+ 3. Check that device is connected to right BSSID and maintain stable
+ connection to BSSID in range.
+ """
+ @test_tracker_info(uuid="9ea2c78d-d305-497f-87a5-f621f0a4b34e")
+ def test_autojoin_Ap1_2g_AP1_20_AP2_95_AP3_95(self):
+ att0, att1, att2, att3 = self.atten_val["Ap1_2g"]
+ variance = 5
+ attn_value = [att0 + variance * 2, att1, att2, att3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[0]["2g"]['bssid'])
+
+ @test_tracker_info(uuid="7c34a508-2ffa-4bca-82b3-9637b7c8250a")
+ def test_autojoin_Ap1_2g_AP1_15_AP2_95_AP3_95(self):
+ att0, att1, att2, att3 = self.atten_val["Ap1_2g"]
+ variance = 5
+ attn_value = [att0 + variance, att1, att2, att3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[0]["2g"]['bssid'])
+
+ @test_tracker_info(uuid="ea614fcc-7fca-4172-ba3a-5978427eb40f")
+ def test_autojoin_Ap1_2g_AP1_10_AP2_95_AP3_95(self):
+ att0, att1, att2, att3 = self.atten_val["Ap1_2g"]
+ variance = 5
+ attn_value = [att0, att1, att2, att3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[0]["2g"]['bssid'])
+
+ @test_tracker_info(uuid="a1ad25cf-11e7-4240-b3c0-9f14325d5b2d")
+ def test_autojoin_Ap1_2g_AP1_5_AP2_95_AP3_95(self):
+ att0, att1, att2, att3 = self.atten_val["Ap1_2g"]
+ variance = 5
+ attn_value = [att0 - variance, att1, att2, att3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[0]["2g"]['bssid'])
+
+ """ Test wifi auto join functionality move to high range.
+
+ 1. Attenuate the signal to high range of AP1.
+ 2. Wake up the device.
+ 3. Check that device is connected to right BSSID and maintain stable
+ connection to BSSID in range.
+ """
+ @test_tracker_info(uuid="b5eba5ec-96e5-4bd8-b483-f5b2a9504558")
+ def test_autojoin_Ap1_2gto5g_AP1_55_AP2_10_AP3_95(self):
+ att0, att1, att2, attn3 = self.atten_val["Ap1_2gto5g"]
+ variance = 5
+ attn_value = [att0 + variance * 2, att1, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[0]["5g"]['bssid'])
+
+ @test_tracker_info(uuid="e63543f7-5f43-4ba2-a5bd-2af3c159a622")
+ def test_autojoin_Ap1_2gto5g_AP1_50_AP2_10_AP3_95(self):
+ att0, att1, att2, attn3 = self.atten_val["Ap1_2gto5g"]
+ variance = 5
+ attn_value = [att0 + variance, att1, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[0]["5g"]['bssid'])
+
+ @test_tracker_info(uuid="0c2cef5d-695d-4d4e-832d-f5e8b393a09c")
+ def test_autojoin_Ap1_2gto5g_AP1_45_AP2_10_AP3_95(self):
+ att0, att1, att2, attn3 = self.atten_val["Ap1_2gto5g"]
+ variance = 5
+ attn_value = [att0, att1, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[0]["5g"]['bssid'])
+
+ """ Test wifi auto join functionality move to low range toward AP2.
+
+ 1. Attenuate the signal to medium range of AP1 and low range of AP2.
+ 2. Wake up the device.
+ 3. Check that device is connected to right BSSID and maintain stable
+ connection to BSSID in range.
+ """
+ @test_tracker_info(uuid="37822578-d35c-462c-87c0-7a2d9252938c")
+ def test_autojoin_in_AP1_5gto2g_AP1_5_AP2_80_AP3_95(self):
+ att0, att1, att2, attn3 = self.atten_val["In_AP1_5gto2g"]
+ variance = 5
+ attn_value = [att0 - variance, att1 + variance, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[0]["2g"]['bssid'])
+
+ @test_tracker_info(uuid="194ffe44-9718-4beb-b69e-cccb569f9b81")
+ def test_autojoin_in_AP1_5gto2g_AP1_10_AP2_75_AP3_95(self):
+ att0, att1, att2, attn3 = self.atten_val["In_AP1_5gto2g"]
+ variance = 5
+ attn_value = [att0, att1, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[0]["2g"]['bssid'])
+
+ @test_tracker_info(uuid="09bdcb0f-7833-4604-a839-f7d981bf4aca")
+ def test_autojoin_in_AP1_5gto2g_AP1_15_AP2_70_AP3_95(self):
+ att0, att1, att2, attn3 = self.atten_val["In_AP1_5gto2g"]
+ variance = 5
+ attn_value = [att0 + variance, att1 - variance, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[0]["2g"]['bssid'])
+
+ """ Test wifi auto join functionality move from low range of AP1 to better
+ range of AP2.
+
+ 1. Attenuate the signal to low range of AP1 and medium range of AP2.
+ 2. Wake up the device.
+ 3. Check that device is connected to right BSSID and maintain stable
+ connection to BSSID in range.
+ """
+ @test_tracker_info(uuid="8ffdcab1-2bfb-4acd-b1e8-089ba8a4ec41")
+ def test_autojoin_swtich_AP1toAp2_AP1_65_AP2_75_AP3_2(self):
+ att0, att1, att2, attn3 = self.atten_val["Swtich_AP1toAp2"]
+ variance = 5
+ attn_value = [att0 - variance, att1 + variance, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[1]["2g"]['bssid'])
+
+ @test_tracker_info(uuid="23e05821-3c53-4033-830e-a446b6105831")
+ def test_autojoin_swtich_AP1toAp2_AP1_70_AP2_70_AP3_2(self):
+ att0, att1, att2, attn3 = self.atten_val["Swtich_AP1toAp2"]
+ variance = 5
+ attn_value = [att0, att1, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[1]["2g"]['bssid'])
+
+ @test_tracker_info(uuid="a56ad87d-d37f-4606-9ae8-af6f55cbb6cf")
+ def test_autojoin_swtich_AP1toAp2_AP1_75_AP2_65_AP3_2(self):
+ att0, att1, att2, attn3 = self.atten_val["Swtich_AP1toAp2"]
+ variance = 5
+ attn_value = [att0 + variance, att1 - variance, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[1]["2g"]['bssid'])
+
+ """ Test wifi auto join functionality move to high range of AP2.
+
+ 1. Attenuate the signal to out range of AP1 and high range of AP2.
+ 2. Wake up the device.
+ 3. Check that device is connected to right BSSID and maintain stable
+ connection to BSSID in range.
+ """
+ @test_tracker_info(uuid="7a8b9242-f93c-449a-90a6-4562274a213a")
+ def test_autojoin_Ap2_2gto5g_AP1_70_AP2_85_AP3_75(self):
+ att0, att1, att2, attn3 = self.atten_val["Ap2_2gto5g"]
+ variance = 5
+ attn_value = [att0 - variance, att1 + variance * 2, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[1]["5g"]['bssid'])
+
+ @test_tracker_info(uuid="5e0c5485-a3ae-438a-92e5-9a6b5a22cb82")
+ def test_autojoin_Ap2_2gto5g_AP1_75_AP2_80_AP3_75(self):
+ att0, att1, att2, attn3 = self.atten_val["Ap2_2gto5g"]
+ variance = 5
+ attn_value = [att0, att1 + variance, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[1]["5g"]['bssid'])
+
+ @test_tracker_info(uuid="3b289144-a12a-424f-822e-8d173d75c3c3")
+ def test_autojoin_Ap2_2gto5g_AP1_75_AP2_75_AP3_75(self):
+ att0, att1, att2, attn3 = self.atten_val["Ap2_2gto5g"]
+ variance = 5
+ attn_value = [att0, att1, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[1]["5g"]['bssid'])
+
+ """ Test wifi auto join functionality move to low range of AP2.
+
+ 1. Attenuate the signal to low range of AP2.
+ 2. Wake up the device.
+ 3. Check that device is connected to right BSSID and maintain stable.
+ """
+ @test_tracker_info(uuid="009457df-f430-402c-96ab-c456b043b6f5")
+ def test_autojoin_Ap2_5gto2g_AP1_75_AP2_70_AP3_10(self):
+ att0, att1, att2, attn3 = self.atten_val["Ap2_5gto2g"]
+ variance = 5
+ attn_value = [att0, att1 - variance, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[1]["2g"]['bssid'])
+
+ @test_tracker_info(uuid="15ef731c-ddfb-4118-aedb-c177f50bdea0")
+ def test_autojoin_Ap2_5gto2g_AP1_75_AP2_75_AP3_10(self):
+ att0, att1, att2, attn3 = self.atten_val["Ap2_5gto2g"]
+ variance = 5
+ attn_value = [att0, att1, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[1]["2g"]['bssid'])
+
+ @test_tracker_info(uuid="f79b0570-56a0-43d2-962d-9e114d48bacf")
+ def test_autojoin_Ap2_5gto2g_AP1_75_AP2_80_AP3_10(self):
+ att0, att1, att2, attn3 = self.atten_val["Ap2_5gto2g"]
+ variance = 5
+ attn_value = [att0, att1 + variance, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[1]["2g"]['bssid'])
+
+ @test_tracker_info(uuid="c6d070af-b601-42f1-adec-5ac564154b29")
+ def test_autojoin_out_of_range(self):
+ """Test wifi auto join functionality move to low range.
+
+ 1. Attenuate the signal to out of range.
+ 2. Wake up the device.
+ 3. Start the scan.
+ 4. Check that device is not connected to any network.
+ """
+ self.attenuators[0].set_atten(90)
+ self.attenuators[1].set_atten(90)
+ self.attenuators[2].set_atten(90)
+ self.attenuators[3].set_atten(90)
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ try:
+ wutils.start_wifi_connection_scan(self.dut)
+ wifi_results = self.dut.droid.wifiGetScanResults()
+ self.log.debug("Scan result {}".format(wifi_results))
+ time.sleep(20)
+ current_network = self.dut.droid.wifiGetConnectionInfo()
+ self.log.info("Current network: {}".format(current_network))
+ asserts.assert_true(
+ ('network_id' in current_network and
+ current_network['network_id'] == -1),
+ "Device is connected to network {}".format(current_network))
+ finally:
+ self.dut.droid.wifiLockRelease()
+ self.dut.droid.goToSleepNow()
+
+ """ Test wifi auto join functionality move in low range of AP2.
+
+ 1. Attenuate the signal to move in range of AP2 and Ap1 not visible at all.
+ 2. Wake up the device.
+ 3. Check that device is connected to right BSSID and maintain stable
+ connection to BSSID in range.
+ """
+ @test_tracker_info(uuid="15c27654-bae0-4d2d-bdc8-54fb04b901d1")
+ def test_autojoin_Ap2_2g_AP1_75_AP2_85_AP3_10(self):
+ att0, att1, att2, attn3 = self.atten_val["Ap2_2g"]
+ variance = 5
+ attn_value = [att0, att1 + variance * 2, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[1]["2g"]['bssid'])
+
+ @test_tracker_info(uuid="af40824a-4d65-4789-980f-d534abeca36b")
+ def test_autojoin_Ap2_2g_AP1_75_AP2_80_AP3_10(self):
+ att0, att1, att2, attn3 = self.atten_val["Ap2_2g"]
+ variance = 5
+ attn_value = [att0, att1 + variance, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[1]["2g"]['bssid'])
+
+ @test_tracker_info(uuid="2d482060-9865-472b-810b-c74c6a099e6c")
+ def test_autojoin_Ap2_2g_AP1_75_AP2_75_AP3_10(self):
+ att0, att1, att2, attn3 = self.atten_val["Ap2_2g"]
+ variance = 5
+ attn_value = [att0, att1, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[1]["2g"]['bssid'])
+
+ @test_tracker_info(uuid="b5cad09e-6e31-40f4-a568-2a1d5271e20c")
+ def test_autojoin_Ap2_2g_AP1_75_AP2_70_AP3_10(self):
+ att0, att1, att2, attn3 = self.atten_val["Ap2_2g"]
+ variance = 5
+ attn_value = [att0, att1 - variance, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[1]["2g"]['bssid'])
+
+ """ Test wifi auto join functionality move to medium range of Ap2 and
+ low range of AP1.
+
+ 1. Attenuate the signal to move in medium range of AP2 and low range of AP1.
+ 2. Wake up the device.
+ 3. Check that device is connected to right BSSID and maintain stable
+ connection to BSSID in range.
+ """
+ @test_tracker_info(uuid="80e74c78-59e2-46db-809d-cb03bd1b6824")
+ def test_autojoin_in_Ap2_5gto2g_AP1_75_AP2_70_AP3_10(self):
+ att0, att1, att2, attn3 = self.atten_val["In_Ap2_5gto2g"]
+ variance = 5
+ attn_value = [att0, att1 - variance, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[1]["2g"]['bssid'])
+
+ @test_tracker_info(uuid="d2a188bd-91cf-4412-a098-739c0c236fe1")
+ def test_autojoin_in_Ap2_5gto2g_AP1_75_AP2_75_AP3_10(self):
+ att0, att1, att2, attn3 = self.atten_val["In_Ap2_5gto2g"]
+ variance = 5
+ attn_value = [att0, att1, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[1]["2g"]['bssid'])
+
+ @test_tracker_info(uuid="032e81e9-bc8a-4fa2-a96b-d733c241869e")
+ def test_autojoin_in_Ap2_5gto2g_AP1_75_AP2_80_AP3_10(self):
+ att0, att1, att2, attn3 = self.atten_val["In_Ap2_5gto2g"]
+ variance = 5
+ attn_value = [att0, att1 + variance, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[1]["2g"]['bssid'])
+
+ """ Test wifi auto join functionality move from low range of AP2 to better
+ range of AP1.
+
+ 1. Attenuate the signal to low range of AP2 and medium range of AP1.
+ 2. Wake up the device.
+ 3. Check that device is connected to right BSSID and maintain stable
+ connection to BSSID in range.
+ """
+ @test_tracker_info(uuid="01faeba0-bd66-4d30-a3d9-b81e959025b2")
+ def test_autojoin_swtich_AP2toAp1_AP1_15_AP2_65_AP3_75(self):
+ att0, att1, att2, attn3 = self.atten_val["Swtich_AP2toAp1"]
+ variance = 5
+ attn_value = [att0 + variance, att1 - variance, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[0]["2g"]['bssid'])
+
+ @test_tracker_info(uuid="68b15c50-03ab-4385-9231-280002315fe5")
+ def test_autojoin_swtich_AP2toAp1_AP1_10_AP2_70_AP3_75(self):
+ att0, att1, att2, attn3 = self.atten_val["Swtich_AP2toAp1"]
+ variance = 5
+ attn_value = [att0, att1, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[0]["2g"]['bssid'])
+
+ @test_tracker_info(uuid="1986d79b-097e-44c9-9aff-7bcd56490c3b")
+ def test_autojoin_swtich_AP2toAp1_AP1_5_AP2_75_AP3_75(self):
+ att0, att1, att2, attn3 = self.atten_val["Swtich_AP2toAp1"]
+ variance = 5
+ attn_value = [att0 - variance, att1 + variance, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[0]["2g"]['bssid'])
+
+ """ Test wifi auto join functionality move to medium range of AP1.
+
+ 1. Attenuate the signal to medium range of AP1.
+ 2. Wake up the device.
+ 3. Check that device is connected to right BSSID and maintain stable
+ connection to BSSID in range.
+ """
+ @test_tracker_info(uuid="ec509d40-e339-47c2-995e-cc77f5a28687")
+ def test_autojoin_Ap1_5gto2g_AP1_10_AP2_80_AP3_95(self):
+ att0, att1, att2, attn3 = self.atten_val["Ap1_5gto2g"]
+ variance = 5
+ attn_value = [att0, att1, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[0]["2g"]['bssid'])
+
+ @test_tracker_info(uuid="ddc66d1e-3241-4040-996a-85bc2a2a4d67")
+ def test_autojoin_Ap1_5gto2g_AP1_15_AP2_80_AP3_95(self):
+ att0, att1, att2, attn3 = self.atten_val["Ap1_5gto2g"]
+ variance = 5
+ attn_value = [att0 + variance, att1, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[0]["2g"]['bssid'])
+
+ @test_tracker_info(uuid="dfc84504-230f-428e-b701-edc496d0e7b3")
+ def test_autojoin_Ap1_5gto2g_AP1_20_AP2_80_AP3_95(self):
+ att0, att1, att2, attn3 = self.atten_val["Ap1_5gto2g"]
+ variance = 5
+ attn_value = [att0 + variance * 2, att1, att2, attn3]
+ self.set_attn_and_validate_connection(
+ attn_value, self.reference_networks[0]["2g"]['bssid'])
+
+ """ Tests End """
diff --git a/acts_tests/tests/google/wifi/WifiPasspointTest.py b/acts_tests/tests/google/wifi/WifiPasspointTest.py
new file mode 100755
index 0000000..4998ac4
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiPasspointTest.py
@@ -0,0 +1,452 @@
+#!/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
+
+from acts.test_utils.net import ui_utils as uutils
+import acts.test_utils.wifi.wifi_test_utils as wutils
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+import WifiManagerTest
+from acts import asserts
+from acts import signals
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.tel.tel_test_utils import get_operator_name
+from acts.utils import force_airplane_mode
+
+WifiEnums = wutils.WifiEnums
+
+DEFAULT_TIMEOUT = 10
+OSU_TEST_TIMEOUT = 300
+
+# Constants for providers.
+GLOBAL_RE = 0
+OSU_BOINGO = 0
+BOINGO = 1
+ATT = 2
+
+# Constants used for various device operations.
+RESET = 1
+TOGGLE = 2
+
+UNKNOWN_FQDN = "@#@@!00fffffx"
+
+# Constants for Boingo UI automator
+EDIT_TEXT_CLASS_NAME = "android.widget.EditText"
+PASSWORD_TEXT = "Password"
+PASSPOINT_BUTTON = "Get Passpoint"
+BOINGO_UI_TEXT = "Online Sign Up"
+
+class WifiPasspointTest(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 setup_class(self):
+ super().setup_class()
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ req_params = ["passpoint_networks",
+ "boingo_username",
+ "boingo_password",]
+ self.unpack_userparams(req_param_names=req_params,)
+ asserts.assert_true(
+ len(self.passpoint_networks) > 0,
+ "Need at least one Passpoint network.")
+ wutils.wifi_toggle_state(self.dut, True)
+ self.unknown_fqdn = UNKNOWN_FQDN
+
+
+ def setup_test(self):
+ super().setup_test()
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ self.dut.unlock_screen()
+
+
+ def teardown_test(self):
+ super().teardown_test()
+ 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)
+
+
+ """Helper Functions"""
+
+
+ def install_passpoint_profile(self, passpoint_config):
+ """Install the Passpoint network Profile.
+
+ Args:
+ passpoint_config: A JSON dict of the Passpoint configuration.
+
+ """
+ asserts.assert_true(WifiEnums.SSID_KEY in passpoint_config,
+ "Key '%s' must be present in network definition." %
+ WifiEnums.SSID_KEY)
+ # Install the Passpoint profile.
+ self.dut.droid.addUpdatePasspointConfig(passpoint_config)
+
+
+ def check_passpoint_connection(self, passpoint_network):
+ """Verify the device is automatically able to connect to the Passpoint
+ network.
+
+ Args:
+ passpoint_network: SSID of the Passpoint network.
+
+ """
+ ad = self.dut
+ ad.ed.clear_all_events()
+ 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()
+ 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):
+ """Get configured Passpoint network and delete using its FQDN."""
+ passpoint_config = self.dut.droid.getPasspointConfigs()
+ if not len(passpoint_config):
+ raise signals.TestFailure("Failed to fetch the list of configured"
+ "passpoint networks.")
+ if not wutils.delete_passpoint(self.dut, passpoint_config[0]):
+ raise signals.TestFailure("Failed to delete Passpoint configuration"
+ " with FQDN = %s" % passpoint_config[0])
+
+ def ui_automator_boingo(self):
+ """Run UI automator for boingo passpoint."""
+ # Verify the boingo login page shows
+ asserts.assert_true(
+ uutils.has_element(self.dut, text=BOINGO_UI_TEXT),
+ "Failed to launch boingohotspot login page")
+
+ # Go to the bottom of the page
+ for _ in range(3):
+ self.dut.adb.shell("input swipe 300 900 300 300")
+
+ # Enter username
+ uutils.wait_and_input_text(self.dut,
+ input_text=self.boingo_username,
+ text="",
+ class_name=EDIT_TEXT_CLASS_NAME)
+ self.dut.adb.shell("input keyevent 111") # collapse keyboard
+ self.dut.adb.shell("input swipe 300 900 300 750") # swipe up to show text
+
+ # Enter password
+ uutils.wait_and_input_text(self.dut,
+ input_text=self.boingo_password,
+ text=PASSWORD_TEXT)
+ self.dut.adb.shell("input keyevent 111") # collapse keyboard
+ self.dut.adb.shell("input swipe 300 900 300 750") # swipe up to show text
+
+ # Login
+ uutils.wait_and_click(self.dut, text=PASSPOINT_BUTTON)
+
+
+ def start_subscription_provisioning(self, state):
+ """Start subscription provisioning with a default provider."""
+
+ self.unpack_userparams(('osu_configs',))
+ asserts.assert_true(
+ len(self.osu_configs) > 0,
+ "Need at least one osu config.")
+ osu_config = self.osu_configs[OSU_BOINGO]
+ # Clear all previous events.
+ self.dut.ed.clear_all_events()
+ self.dut.droid.startSubscriptionProvisioning(osu_config)
+ start_time = time.time()
+ while time.time() < start_time + OSU_TEST_TIMEOUT:
+ dut_event = self.dut.ed.pop_event("onProvisioningCallback",
+ DEFAULT_TIMEOUT * 18)
+ if dut_event['data']['tag'] == 'success':
+ self.log.info("Passpoint Provisioning Success")
+ # Reset WiFi after provisioning success.
+ if state == RESET:
+ wutils.reset_wifi(self.dut)
+ time.sleep(DEFAULT_TIMEOUT)
+ # Toggle WiFi after provisioning success.
+ elif state == TOGGLE:
+ wutils.toggle_wifi_off_and_on(self.dut)
+ time.sleep(DEFAULT_TIMEOUT)
+ break
+ if dut_event['data']['tag'] == 'failure':
+ raise signals.TestFailure(
+ "Passpoint Provisioning is failed with %s" %
+ dut_event['data'][
+ 'reason'])
+ break
+ if dut_event['data']['tag'] == 'status':
+ self.log.info(
+ "Passpoint Provisioning status %s" % dut_event['data'][
+ 'status'])
+ if int(dut_event['data']['status']) == 7:
+ time.sleep(DEFAULT_TIMEOUT)
+ self.ui_automator_boingo()
+ # Clear all previous events.
+ self.dut.ed.clear_all_events()
+
+ # Verify device connects to the Passpoint network.
+ time.sleep(DEFAULT_TIMEOUT)
+ current_passpoint = self.dut.droid.wifiGetConnectionInfo()
+ if current_passpoint[WifiEnums.SSID_KEY] not in osu_config[
+ "expected_ssids"]:
+ raise signals.TestFailure("Device did not connect to the %s"
+ " passpoint network" % osu_config[
+ "expected_ssids"])
+ # Delete the Passpoint profile.
+ self.get_configured_passpoint_and_delete()
+ wutils.wait_for_disconnect(self.dut)
+
+
+ """Tests"""
+
+ @test_tracker_info(uuid="b0bc0153-77bb-4594-8f19-cea2c6bd2f43")
+ def test_add_passpoint_network(self):
+ """Add a Passpoint network and verify device connects to it.
+
+ Steps:
+ 1. Install a Passpoint Profile.
+ 2. Verify the device connects to the required Passpoint SSID.
+ 3. Get the Passpoint configuration added above.
+ 4. Delete Passpoint configuration using its FQDN.
+ 5. Verify that we are disconnected from the Passpoint network.
+
+ """
+ passpoint_config = self.passpoint_networks[BOINGO]
+ self.install_passpoint_profile(passpoint_config)
+ ssid = passpoint_config[WifiEnums.SSID_KEY]
+ self.check_passpoint_connection(ssid)
+ self.get_configured_passpoint_and_delete()
+ wutils.wait_for_disconnect(self.dut)
+
+
+ @test_tracker_info(uuid="eb29d6e2-a755-4c9c-9e4e-63ea2277a64a")
+ def test_update_passpoint_network(self):
+ """Update a previous Passpoint network and verify device still connects
+ to it.
+
+ 1. Install a Passpoint Profile.
+ 2. Verify the device connects to the required Passpoint SSID.
+ 3. Update the Passpoint Profile.
+ 4. Verify device is still connected to the Passpoint SSID.
+ 5. Get the Passpoint configuration added above.
+ 6. Delete Passpoint configuration using its FQDN.
+
+ """
+ passpoint_config = self.passpoint_networks[BOINGO]
+ self.install_passpoint_profile(passpoint_config)
+ ssid = passpoint_config[WifiEnums.SSID_KEY]
+ self.check_passpoint_connection(ssid)
+
+ # Update passpoint configuration using the original profile because we
+ # do not have real profile with updated credentials to use.
+ self.install_passpoint_profile(passpoint_config)
+
+ # Wait for a Disconnect event from the supplicant.
+ wutils.wait_for_disconnect(self.dut)
+
+ # Now check if we are again connected with the updated profile.
+ self.check_passpoint_connection(ssid)
+
+ self.get_configured_passpoint_and_delete()
+ wutils.wait_for_disconnect(self.dut)
+
+
+ @test_tracker_info(uuid="b6e8068d-faa1-49f2-b421-c60defaed5f0")
+ def test_add_delete_list_of_passpoint_network(self):
+ """Add multiple passpoint networks, list them and delete one by one.
+
+ 1. Install Passpoint Profile A.
+ 2. Install Passpoint Profile B.
+ 3. Get all the Passpoint configurations added above and verify.
+ 6. Ensure all Passpoint configurations can be deleted.
+
+ """
+ for passpoint_config in self.passpoint_networks[:2]:
+ self.install_passpoint_profile(passpoint_config)
+ time.sleep(DEFAULT_TIMEOUT)
+ configs = self.dut.droid.getPasspointConfigs()
+ # It is length -1 because ATT profile will be handled separately
+ if not len(configs) or len(configs) != len(self.passpoint_networks[:2]):
+ raise signals.TestFailure("Failed to fetch some or all of the"
+ " configured passpoint networks.")
+ for config in configs:
+ if not wutils.delete_passpoint(self.dut, config):
+ raise signals.TestFailure("Failed to delete Passpoint"
+ " configuration with FQDN = %s" %
+ config)
+
+
+ @test_tracker_info(uuid="a53251be-7aaf-41fc-a5f3-63984269d224")
+ def test_delete_unknown_fqdn(self):
+ """Negative test to delete Passpoint profile using an unknown FQDN.
+
+ 1. Pass an unknown FQDN for removal.
+ 2. Verify that it was not successful.
+
+ """
+ if wutils.delete_passpoint(self.dut, self.unknown_fqdn):
+ raise signals.TestFailure("Failed because an unknown FQDN"
+ " was successfully deleted.")
+
+
+ @test_tracker_info(uuid="bf03c03a-e649-4e2b-a557-1f791bd98951")
+ def test_passpoint_failover(self):
+ """Add a pair of passpoint networks and test failover when one of the"
+ profiles is removed.
+
+ 1. Install a Passpoint Profile A and B.
+ 2. Verify device connects to a Passpoint network and get SSID.
+ 3. Delete the current Passpoint profile using its FQDN.
+ 4. Verify device fails over and connects to the other Passpoint SSID.
+ 5. Delete Passpoint configuration using its FQDN.
+
+ """
+ # Install both Passpoint profiles on the device.
+ passpoint_ssid = list()
+ for passpoint_config in self.passpoint_networks[:2]:
+ passpoint_ssid.extend(passpoint_config[WifiEnums.SSID_KEY])
+ self.install_passpoint_profile(passpoint_config)
+ time.sleep(DEFAULT_TIMEOUT)
+
+ # Get the current network and the failover network.
+ wutils.wait_for_connect(self.dut)
+ current_passpoint = self.dut.droid.wifiGetConnectionInfo()
+ current_ssid = current_passpoint[WifiEnums.SSID_KEY]
+ if current_ssid not in passpoint_ssid:
+ raise signals.TestFailure("Device did not connect to any of the "
+ "configured Passpoint networks.")
+
+ expected_ssid = self.passpoint_networks[0][WifiEnums.SSID_KEY]
+ if current_ssid in expected_ssid:
+ expected_ssid = self.passpoint_networks[1][WifiEnums.SSID_KEY]
+
+ # Remove the current Passpoint profile.
+ for network in self.passpoint_networks[:2]:
+ if current_ssid in network[WifiEnums.SSID_KEY]:
+ if not wutils.delete_passpoint(self.dut, network["fqdn"]):
+ raise signals.TestFailure("Failed to delete Passpoint"
+ " configuration with FQDN = %s" %
+ network["fqdn"])
+ # Verify device fails over and connects to the other passpoint network.
+ time.sleep(DEFAULT_TIMEOUT)
+
+ current_passpoint = self.dut.droid.wifiGetConnectionInfo()
+ if current_passpoint[WifiEnums.SSID_KEY] not in expected_ssid:
+ raise signals.TestFailure("Device did not failover to the %s"
+ " passpoint network" % expected_ssid)
+
+ # Delete the remaining Passpoint profile.
+ self.get_configured_passpoint_and_delete()
+ 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.
+
+ It is used for only installing the profile for other tests.
+ """
+ isFound = False
+ for passpoint_config in self.passpoint_networks:
+ if 'att' in passpoint_config['fqdn']:
+ isFound = True
+ self.install_passpoint_profile(passpoint_config)
+ break
+ if not isFound:
+ raise signals.TestFailure("cannot find ATT profile.")
+
+
+ @test_tracker_info(uuid="e3e826d2-7c39-4c37-ab3f-81992d5aa0e8")
+ def test_att_passpoint_network(self):
+ """Add a AT&T Passpoint network and verify device connects to it.
+
+ Steps:
+ 1. Install a AT&T Passpoint Profile.
+ 2. Verify the device connects to the required Passpoint SSID.
+ 3. Get the Passpoint configuration added above.
+ 4. Delete Passpoint configuration using its FQDN.
+ 5. Verify that we are disconnected from the Passpoint network.
+
+ """
+ carriers = ["att"]
+ operator = get_operator_name(self.log, self.dut)
+ asserts.skip_if(operator not in carriers,
+ "Device %s does not have a ATT sim" % self.dut.model)
+
+ passpoint_config = self.passpoint_networks[ATT]
+ self.install_passpoint_profile(passpoint_config)
+ ssid = passpoint_config[WifiEnums.SSID_KEY]
+ self.check_passpoint_connection(ssid)
+ self.get_configured_passpoint_and_delete()
+ wutils.wait_for_disconnect(self.dut)
+
+
+ @test_tracker_info(uuid="c85c81b2-7133-4635-8328-9498169ae802")
+ def test_start_subscription_provisioning(self):
+ self.start_subscription_provisioning(0)
+
+
+ @test_tracker_info(uuid="fd09a643-0d4b-45a9-881a-a771f9707ab1")
+ def test_start_subscription_provisioning_and_reset_wifi(self):
+ self.start_subscription_provisioning(RESET)
+
+
+ @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/tests/google/wifi/WifiPerformancePreflightTest.py b/acts_tests/tests/google/wifi/WifiPerformancePreflightTest.py
new file mode 100644
index 0000000..97d9374
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiPerformancePreflightTest.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python3.4
+#
+# 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.metrics.loggers.blackbox import BlackboxMappedMetricLogger
+from acts.test_utils.wifi import wifi_performance_test_utils as wputils
+from acts.test_utils.wifi import wifi_retail_ap as retail_ap
+
+
+class WifiPerformancePreflightTest(base_test.BaseTestClass):
+ """Class for Wifi performance preflight tests.
+
+ This class implements WiFi performance tests to perform before any other
+ test suite. Currently, the preflight checklist checks the wifi firmware and
+ config files, i.e., bdf files for any changes by retrieving their version
+ number and checksum.
+ """
+ def __init__(self, controllers):
+ base_test.BaseTestClass.__init__(self, controllers)
+ self.testcase_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_case())
+
+ def setup_class(self):
+ self.dut = self.android_devices[-1]
+ # Initialize AP to ensure that tests can be run in later suites
+ req_params = ['RetailAccessPoints']
+ opt_params = ['bdf', 'firmware']
+ self.unpack_userparams(req_params, opt_params)
+ self.access_point = retail_ap.create(self.RetailAccessPoints)[0]
+ # Load BDF and firmware if needed
+ if hasattr(self, 'bdf'):
+ self.log.info('Pushing WiFi BDF to DUT.')
+ wputils.push_config(self.dut, self.bdf[0])
+ if hasattr(self, 'firmware'):
+ self.log.info('Pushing WiFi firmware to DUT.')
+ wputils.push_firmware(self.dut, self.firmware)
+
+ for ad in self.android_devices:
+ ad.droid.wifiEnableVerboseLogging(1)
+ ad.adb.shell("wpa_cli -i wlan0 -p -g@android:wpa_wlan0 IFNAME="
+ "wlan0 log_level EXCESSIVE")
+
+ def test_wifi_sw_signature(self):
+ sw_signature = wputils.get_sw_signature(self.dut)
+ self.testcase_metric_logger.add_metric(
+ 'config_signature', sw_signature['config_signature'])
+ self.testcase_metric_logger.add_metric('fw_signature',
+ sw_signature['fw_signature'])
+ self.testcase_metric_logger.add_metric('serial_hash',
+ sw_signature['serial_hash'])
+
+ def teardown_class(self):
+ # Teardown AP and release its lockfile
+ self.access_point.teardown()
diff --git a/acts_tests/tests/google/wifi/WifiPingTest.py b/acts_tests/tests/google/wifi/WifiPingTest.py
new file mode 100644
index 0000000..1df52d5
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiPingTest.py
@@ -0,0 +1,802 @@
+#!/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 collections
+import itertools
+import json
+import logging
+import os
+import statistics
+from acts import asserts
+from acts import context
+from acts import base_test
+from acts import utils
+from acts.controllers.utils_lib import ssh
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
+from acts.test_utils.wifi import ota_chamber
+from acts.test_utils.wifi import ota_sniffer
+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 acts.test_utils.wifi import wifi_test_utils as wutils
+from functools import partial
+
+
+class WifiPingTest(base_test.BaseTestClass):
+ """Class for ping-based Wifi performance tests.
+
+ This class implements WiFi ping performance tests such as range and RTT.
+ The class setups up the AP in the desired configurations, configures
+ and connects the phone to the AP, and runs For an example config file to
+ run this test class see example_connectivity_performance_ap_sta.json.
+ """
+
+ TEST_TIMEOUT = 10
+ RSSI_POLL_INTERVAL = 0.2
+ SHORT_SLEEP = 1
+ MED_SLEEP = 5
+ MAX_CONSECUTIVE_ZEROS = 5
+ DISCONNECTED_PING_RESULT = {
+ 'connected': 0,
+ 'rtt': [],
+ 'time_stamp': [],
+ 'ping_interarrivals': [],
+ 'packet_loss_percentage': 100
+ }
+
+ def __init__(self, controllers):
+ base_test.BaseTestClass.__init__(self, controllers)
+ self.testcase_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_case())
+ self.testclass_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_class())
+ self.publish_testcase_metrics = True
+
+ def setup_class(self):
+ self.dut = self.android_devices[-1]
+ req_params = [
+ 'ping_test_params', 'testbed_params', 'main_network',
+ 'RetailAccessPoints', 'RemoteServer'
+ ]
+ opt_params = ['OTASniffer']
+ self.unpack_userparams(req_params, opt_params)
+ self.testclass_params = self.ping_test_params
+ self.num_atten = self.attenuators[0].instrument.num_atten
+ self.ping_server = ssh.connection.SshConnection(
+ ssh.settings.from_config(self.RemoteServer[0]['ssh_config']))
+ self.access_point = retail_ap.create(self.RetailAccessPoints)[0]
+ if hasattr(self,
+ 'OTASniffer') and self.testbed_params['sniffer_enable']:
+ self.sniffer = ota_sniffer.create(self.OTASniffer)[0]
+ self.log.info('Access Point Configuration: {}'.format(
+ self.access_point.ap_settings))
+ self.log_path = os.path.join(logging.log_path, 'results')
+ os.makedirs(self.log_path, exist_ok=True)
+ self.atten_dut_chain_map = {}
+ self.testclass_results = []
+
+ # 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)
+
+ # Configure test retries
+ self.user_params['retry_tests'] = [self.__class__.__name__]
+
+ def teardown_class(self):
+ # Turn WiFi OFF and reset AP
+ for dev in self.android_devices:
+ wutils.wifi_toggle_state(dev, False)
+ self.process_testclass_results()
+
+ def setup_test(self):
+ self.retry_flag = False
+
+ def teardown_test(self):
+ self.retry_flag = False
+
+ def on_retry(self):
+ """Function to control test logic on retried tests.
+
+ This function is automatically executed on tests that are being
+ retried. In this case the function resets wifi, toggles it off and on
+ and sets a retry_flag to enable further tweaking the test logic on
+ second attempts.
+ """
+ self.retry_flag = True
+ for dev in self.android_devices:
+ wutils.reset_wifi(dev)
+ wutils.toggle_wifi_off_and_on(dev)
+
+ def process_testclass_results(self):
+ """Saves all test results to enable comparison."""
+ testclass_summary = {}
+ for test in self.testclass_results:
+ if 'range' in test['test_name']:
+ testclass_summary[test['test_name']] = test['range']
+ # Save results
+ results_file_path = os.path.join(self.log_path,
+ 'testclass_summary.json')
+ with open(results_file_path, 'w') as results_file:
+ json.dump(testclass_summary, results_file, indent=4)
+
+ def pass_fail_check_ping_rtt(self, result):
+ """Check the test result and decide if it passed or failed.
+
+ The function computes RTT statistics and fails any tests in which the
+ tail of the ping latency results exceeds the threshold defined in the
+ configuration file.
+
+ Args:
+ result: dict containing ping results and other meta data
+ """
+ ignored_fraction = (self.testclass_params['rtt_ignored_interval'] /
+ self.testclass_params['rtt_ping_duration'])
+ sorted_rtt = [
+ sorted(x['rtt'][round(ignored_fraction * len(x['rtt'])):])
+ for x in result['ping_results']
+ ]
+ disconnected = any([len(x) == 0 for x in sorted_rtt])
+ if disconnected:
+ asserts.fail('Test failed. DUT disconnected at least once.')
+
+ rtt_at_test_percentile = [
+ x[int((1 - self.testclass_params['rtt_test_percentile'] / 100) *
+ len(x))] for x in sorted_rtt
+ ]
+ # Set blackbox metric
+ if self.publish_testcase_metrics:
+ self.testcase_metric_logger.add_metric('ping_rtt',
+ max(rtt_at_test_percentile))
+ # Evaluate test pass/fail
+ rtt_failed = any([
+ rtt > self.testclass_params['rtt_threshold'] * 1000
+ for rtt in rtt_at_test_percentile
+ ])
+ if rtt_failed:
+ #TODO: figure out how to cleanly exclude RTT tests from retry
+ asserts.explicit_pass(
+ 'Test failed. RTTs at test percentile = {}'.format(
+ rtt_at_test_percentile))
+ else:
+ asserts.explicit_pass(
+ 'Test Passed. RTTs at test percentile = {}'.format(
+ rtt_at_test_percentile))
+
+ def pass_fail_check_ping_range(self, result):
+ """Check the test result and decide if it passed or failed.
+
+ Checks whether the attenuation at which ping packet losses begin to
+ exceed the threshold matches the range derived from golden
+ rate-vs-range result files. The test fails is ping range is
+ range_gap_threshold worse than RvR range.
+
+ Args:
+ result: dict containing ping results and meta data
+ """
+ # 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))
+
+ # 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']:
+ self.pass_fail_check_ping_range(result)
+ else:
+ self.pass_fail_check_ping_rtt(result)
+
+ def process_ping_results(self, testcase_params, ping_range_result):
+ """Saves and plots ping results.
+
+ Args:
+ ping_range_result: dict containing ping results and metadata
+ """
+ # Compute range
+ ping_loss_over_att = [
+ x['packet_loss_percentage']
+ for x in ping_range_result['ping_results']
+ ]
+ ping_loss_above_threshold = [
+ x > self.testclass_params['range_ping_loss_threshold']
+ for x in ping_loss_over_att
+ ]
+ for idx in range(len(ping_loss_above_threshold)):
+ if all(ping_loss_above_threshold[idx:]):
+ range_index = max(idx, 1) - 1
+ break
+ else:
+ range_index = -1
+ ping_range_result['atten_at_range'] = testcase_params['atten_range'][
+ range_index]
+ ping_range_result['peak_throughput_pct'] = 100 - min(
+ ping_loss_over_att)
+ ping_range_result['range'] = (ping_range_result['atten_at_range'] +
+ ping_range_result['fixed_attenuation'])
+ ping_range_result['llstats_at_range'] = (
+ 'TX MCS = {0} ({1:.1f}%). '
+ 'RX MCS = {2} ({3:.1f}%)'.format(
+ ping_range_result['llstats'][range_index]['summary']
+ ['common_tx_mcs'], ping_range_result['llstats'][range_index]
+ ['summary']['common_tx_mcs_freq'] * 100,
+ ping_range_result['llstats'][range_index]['summary']
+ ['common_rx_mcs'], ping_range_result['llstats'][range_index]
+ ['summary']['common_rx_mcs_freq'] * 100))
+
+ # Save results
+ results_file_path = os.path.join(
+ self.log_path, '{}.json'.format(self.current_test_name))
+ with open(results_file_path, 'w') as results_file:
+ json.dump(ping_range_result, results_file, indent=4)
+
+ # Plot results
+ if 'range' not in self.current_test_name:
+ figure = wputils.BokehFigure(
+ self.current_test_name,
+ x_label='Timestamp (s)',
+ primary_y_label='Round Trip Time (ms)')
+ for idx, result in enumerate(ping_range_result['ping_results']):
+ if len(result['rtt']) > 1:
+ x_data = [
+ t - result['time_stamp'][0]
+ for t in result['time_stamp']
+ ]
+ figure.add_line(
+ x_data, result['rtt'], 'RTT @ {}dB'.format(
+ ping_range_result['attenuation'][idx]))
+
+ output_file_path = os.path.join(
+ self.log_path, '{}.html'.format(self.current_test_name))
+ figure.generate_figure(output_file_path)
+
+ def run_ping_test(self, testcase_params):
+ """Main function to test ping.
+
+ The function sets up the AP in the correct channel and mode
+ configuration and calls get_ping_stats while sweeping attenuation
+
+ Args:
+ testcase_params: dict containing all test parameters
+ Returns:
+ test_result: dict containing ping results and other meta data
+ """
+ # Prepare results dict
+ llstats_obj = wputils.LinkLayerStats(
+ self.dut, self.testclass_params.get('llstats_enabled', True))
+ test_result = collections.OrderedDict()
+ test_result['testcase_params'] = testcase_params.copy()
+ test_result['test_name'] = self.current_test_name
+ test_result['ap_config'] = self.access_point.ap_settings.copy()
+ test_result['attenuation'] = testcase_params['atten_range']
+ test_result['fixed_attenuation'] = self.testbed_params[
+ 'fixed_attenuation'][str(testcase_params['channel'])]
+ test_result['rssi_results'] = []
+ test_result['ping_results'] = []
+ test_result['llstats'] = []
+ # Setup sniffer
+ if self.testbed_params['sniffer_enable']:
+ self.sniffer.start_capture(
+ testcase_params['test_network'],
+ chan=int(testcase_params['channel']),
+ bw=int(testcase_params['mode'][3:]),
+ duration=testcase_params['ping_duration'] *
+ len(testcase_params['atten_range']) + self.TEST_TIMEOUT)
+ # Run ping and sweep attenuation as needed
+ zero_counter = 0
+ for atten in testcase_params['atten_range']:
+ for attenuator in self.attenuators:
+ attenuator.set_atten(atten, strict=False)
+ rssi_future = wputils.get_connected_rssi_nb(
+ self.dut,
+ int(testcase_params['ping_duration'] / 2 /
+ self.RSSI_POLL_INTERVAL), self.RSSI_POLL_INTERVAL,
+ testcase_params['ping_duration'] / 2)
+ # Refresh link layer stats
+ llstats_obj.update_stats()
+ current_ping_stats = wputils.get_ping_stats(
+ self.ping_server, self.dut_ip,
+ testcase_params['ping_duration'],
+ testcase_params['ping_interval'], testcase_params['ping_size'])
+ current_rssi = rssi_future.result()
+ test_result['rssi_results'].append(current_rssi)
+ llstats_obj.update_stats()
+ curr_llstats = llstats_obj.llstats_incremental.copy()
+ test_result['llstats'].append(curr_llstats)
+ if current_ping_stats['connected']:
+ self.log.info(
+ 'Attenuation = {0}dB\tPacket Loss = {1}%\t'
+ 'Avg RTT = {2:.2f}ms\tRSSI = {3} [{4},{5}]\t'.format(
+ atten, current_ping_stats['packet_loss_percentage'],
+ statistics.mean(current_ping_stats['rtt']),
+ current_rssi['signal_poll_rssi']['mean'],
+ current_rssi['chain_0_rssi']['mean'],
+ current_rssi['chain_1_rssi']['mean']))
+ if current_ping_stats['packet_loss_percentage'] == 100:
+ zero_counter = zero_counter + 1
+ else:
+ zero_counter = 0
+ else:
+ self.log.info(
+ 'Attenuation = {}dB. Disconnected.'.format(atten))
+ zero_counter = zero_counter + 1
+ test_result['ping_results'].append(current_ping_stats.as_dict())
+ if zero_counter == self.MAX_CONSECUTIVE_ZEROS:
+ self.log.info('Ping loss stable at 100%. Stopping test now.')
+ for idx in range(
+ len(testcase_params['atten_range']) -
+ len(test_result['ping_results'])):
+ test_result['ping_results'].append(
+ self.DISCONNECTED_PING_RESULT)
+ break
+ if self.testbed_params['sniffer_enable']:
+ self.sniffer.stop_capture()
+ return test_result
+
+ def setup_ap(self, testcase_params):
+ """Sets up the access point in the configuration required by the test.
+
+ Args:
+ testcase_params: dict containing AP and other test params
+ """
+ band = self.access_point.band_lookup_by_channel(
+ testcase_params['channel'])
+ if '2G' in band:
+ frequency = wutils.WifiEnums.channel_2G_to_freq[
+ testcase_params['channel']]
+ else:
+ frequency = wutils.WifiEnums.channel_5G_to_freq[
+ testcase_params['channel']]
+ if frequency in wutils.WifiEnums.DFS_5G_FREQUENCIES:
+ self.access_point.set_region(self.testbed_params['DFS_region'])
+ else:
+ self.access_point.set_region(self.testbed_params['default_region'])
+ self.access_point.set_channel(band, testcase_params['channel'])
+ self.access_point.set_bandwidth(band, testcase_params['mode'])
+ if 'low' in testcase_params['ap_power']:
+ self.log.info('Setting low AP power.')
+ self.access_point.set_power(
+ band, self.testclass_params['low_ap_tx_power'])
+ self.log.info('Access Point Configuration: {}'.format(
+ self.access_point.ap_settings))
+
+ def setup_dut(self, testcase_params):
+ """Sets up the DUT in the configuration required by the test.
+
+ Args:
+ testcase_params: dict containing AP and other test params
+ """
+ # Check battery level before test
+ if not wputils.health_check(self.dut, 10):
+ asserts.skip('Battery level too low. Skipping test.')
+ # Turn screen off to preserve battery
+ self.dut.go_to_sleep()
+ if wputils.validate_network(self.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,
+ self.testclass_params['country_code'])
+ testcase_params['test_network']['channel'] = testcase_params[
+ 'channel']
+ wutils.wifi_connect(self.dut,
+ testcase_params['test_network'],
+ num_of_tries=5,
+ check_connectivity=True)
+ self.dut_ip = self.dut.droid.connectivityGetIPv4Addresses('wlan0')[0]
+ if testcase_params['channel'] not in self.atten_dut_chain_map.keys():
+ self.atten_dut_chain_map[testcase_params[
+ 'channel']] = wputils.get_current_atten_dut_chain_map(
+ self.attenuators, self.dut, self.ping_server)
+ self.log.info("Current Attenuator-DUT Chain Map: {}".format(
+ self.atten_dut_chain_map[testcase_params['channel']]))
+ for idx, atten in enumerate(self.attenuators):
+ if self.atten_dut_chain_map[testcase_params['channel']][
+ idx] == testcase_params['attenuated_chain']:
+ atten.offset = atten.instrument.max_atten
+ else:
+ atten.offset = 0
+
+ def setup_ping_test(self, testcase_params):
+ """Function that gets devices ready for the test.
+
+ Args:
+ testcase_params: dict containing test-specific parameters
+ """
+ # Configure AP
+ self.setup_ap(testcase_params)
+ # Set attenuator to 0 dB
+ for attenuator in self.attenuators:
+ attenuator.set_atten(0, strict=False)
+ # Reset, configure, and connect DUT
+ self.setup_dut(testcase_params)
+
+ def get_range_start_atten(self, testcase_params):
+ """Gets the starting attenuation for this ping test.
+
+ This function is used to get the starting attenuation for ping range
+ tests. This implementation returns the default starting attenuation,
+ however, defining this function enables a more involved configuration
+ for over-the-air test classes.
+
+ Args:
+ testcase_params: dict containing all test params
+ """
+ return self.testclass_params['range_atten_start']
+
+ def compile_test_params(self, testcase_params):
+ band = self.access_point.band_lookup_by_channel(
+ testcase_params['channel'])
+ testcase_params['test_network'] = self.main_network[band]
+ if testcase_params['chain_mask'] in ['0', '1']:
+ testcase_params['attenuated_chain'] = 'DUT-Chain-{}'.format(
+ 1 if testcase_params['chain_mask'] == '0' else 0)
+ else:
+ # Set attenuated chain to -1. Do not set to None as this will be
+ # compared to RF chain map which may include None
+ testcase_params['attenuated_chain'] = -1
+ if testcase_params['test_type'] == 'test_ping_range':
+ testcase_params.update(
+ ping_interval=self.testclass_params['range_ping_interval'],
+ ping_duration=self.testclass_params['range_ping_duration'],
+ ping_size=self.testclass_params['ping_size'],
+ )
+ elif testcase_params['test_type'] == 'test_fast_ping_rtt':
+ testcase_params.update(
+ ping_interval=self.testclass_params['rtt_ping_interval']
+ ['fast'],
+ ping_duration=self.testclass_params['rtt_ping_duration'],
+ ping_size=self.testclass_params['ping_size'],
+ )
+ elif testcase_params['test_type'] == 'test_slow_ping_rtt':
+ testcase_params.update(
+ ping_interval=self.testclass_params['rtt_ping_interval']
+ ['slow'],
+ ping_duration=self.testclass_params['rtt_ping_duration'],
+ ping_size=self.testclass_params['ping_size'])
+
+ if testcase_params['test_type'] == 'test_ping_range':
+ start_atten = self.get_range_start_atten(testcase_params)
+ num_atten_steps = int(
+ (self.testclass_params['range_atten_stop'] - start_atten) /
+ self.testclass_params['range_atten_step'])
+ testcase_params['atten_range'] = [
+ start_atten + x * self.testclass_params['range_atten_step']
+ for x in range(0, num_atten_steps)
+ ]
+ else:
+ testcase_params['atten_range'] = self.testclass_params[
+ 'rtt_test_attenuation']
+ return testcase_params
+
+ def _test_ping(self, testcase_params):
+ """ Function that gets called for each range test case
+
+ The function gets called in each range test case. It customizes the
+ range test based on the test name of the test that called it
+
+ Args:
+ testcase_params: dict containing preliminary set of parameters
+ """
+ # Compile test parameters from config and test name
+ testcase_params = self.compile_test_params(testcase_params)
+ # Run ping test
+ self.setup_ping_test(testcase_params)
+ ping_result = self.run_ping_test(testcase_params)
+ # Postprocess results
+ self.process_ping_results(testcase_params, ping_result)
+ self.testclass_results.append(ping_result)
+ self.pass_fail_check(ping_result)
+
+ def generate_test_cases(self, ap_power, channels, modes, chain_mask,
+ test_types):
+ test_cases = []
+ allowed_configs = {
+ 'VHT20': [
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 149, 153,
+ 157, 161
+ ],
+ 'VHT40': [36, 44, 149, 157],
+ 'VHT80': [36, 149]
+ }
+ for channel, mode, chain, test_type in itertools.product(
+ channels, modes, chain_mask, test_types):
+ if channel not in allowed_configs[mode]:
+ continue
+ testcase_name = '{}_ch{}_{}_ch{}'.format(test_type, channel, mode,
+ chain)
+ testcase_params = collections.OrderedDict(test_type=test_type,
+ ap_power=ap_power,
+ channel=channel,
+ mode=mode,
+ chain_mask=chain)
+ setattr(self, testcase_name,
+ partial(self._test_ping, testcase_params))
+ test_cases.append(testcase_name)
+ return test_cases
+
+
+class WifiPing_TwoChain_Test(WifiPingTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases(
+ ap_power='standard',
+ channels=[1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161],
+ modes=['VHT20', 'VHT40', 'VHT80'],
+ test_types=[
+ 'test_ping_range', 'test_fast_ping_rtt', 'test_slow_ping_rtt'
+ ],
+ chain_mask=['2x2'])
+
+
+class WifiPing_PerChainRange_Test(WifiPingTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases(
+ ap_power='standard',
+ chain_mask=['0', '1', '2x2'],
+ channels=[1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161],
+ modes=['VHT20', 'VHT40', 'VHT80'],
+ test_types=['test_ping_range'])
+
+
+class WifiPing_LowPowerAP_Test(WifiPingTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases(
+ ap_power='low_power',
+ chain_mask=['0', '1', '2x2'],
+ channels=[1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161],
+ modes=['VHT20', 'VHT40', 'VHT80'],
+ test_types=['test_ping_range'])
+
+
+# Over-the air version of ping tests
+class WifiOtaPingTest(WifiPingTest):
+ """Class to test over-the-air ping
+
+ This class tests WiFi ping performance in an OTA chamber. It enables
+ setting turntable orientation and other chamber parameters to study
+ performance in varying channel conditions
+ """
+ def __init__(self, controllers):
+ base_test.BaseTestClass.__init__(self, controllers)
+ self.testcase_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_case())
+ self.testclass_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_class())
+ self.publish_testcase_metrics = False
+
+ def setup_class(self):
+ WifiPingTest.setup_class(self)
+ self.ota_chamber = ota_chamber.create(
+ self.user_params['OTAChamber'])[0]
+
+ def teardown_class(self):
+ WifiPingTest.teardown_class(self)
+ self.process_testclass_results()
+ self.ota_chamber.reset_chamber()
+
+ def process_testclass_results(self):
+ """Saves all test results to enable comparison."""
+ WifiPingTest.process_testclass_results(self)
+
+ range_vs_angle = collections.OrderedDict()
+ for test in self.testclass_results:
+ curr_params = test['testcase_params']
+ curr_config = curr_params['channel']
+ if curr_config in range_vs_angle:
+ if curr_params['position'] not in range_vs_angle[curr_config][
+ 'position']:
+ range_vs_angle[curr_config]['position'].append(
+ curr_params['position'])
+ range_vs_angle[curr_config]['range'].append(test['range'])
+ range_vs_angle[curr_config]['llstats_at_range'].append(
+ test['llstats_at_range'])
+ else:
+ range_vs_angle[curr_config]['range'][-1] = test['range']
+ range_vs_angle[curr_config]['llstats_at_range'][-1] = test[
+ 'llstats_at_range']
+ else:
+ range_vs_angle[curr_config] = {
+ 'position': [curr_params['position']],
+ 'range': [test['range']],
+ 'llstats_at_range': [test['llstats_at_range']]
+ }
+ chamber_mode = self.testclass_results[0]['testcase_params'][
+ 'chamber_mode']
+ if chamber_mode == 'orientation':
+ x_label = 'Angle (deg)'
+ elif chamber_mode == 'stepped stirrers':
+ x_label = 'Position Index'
+ figure = wputils.BokehFigure(
+ title='Range vs. Position',
+ x_label=x_label,
+ primary_y_label='Range (dB)',
+ )
+ for channel, channel_data in range_vs_angle.items():
+ figure.add_line(x_data=channel_data['position'],
+ y_data=channel_data['range'],
+ hover_text=channel_data['llstats_at_range'],
+ legend='Channel {}'.format(channel))
+ average_range = sum(channel_data['range']) / len(
+ channel_data['range'])
+ self.log.info('Average range for Channel {} is: {}dB'.format(
+ channel, average_range))
+ metric_name = 'ota_summary_ch{}.avg_range'.format(channel)
+ self.testclass_metric_logger.add_metric(metric_name, average_range)
+ current_context = context.get_current_context().get_full_output_path()
+ plot_file_path = os.path.join(current_context, 'results.html')
+ figure.generate_figure(plot_file_path)
+
+ # Save results
+ results_file_path = os.path.join(current_context,
+ 'testclass_summary.json')
+ with open(results_file_path, 'w') as results_file:
+ json.dump(range_vs_angle, results_file, indent=4)
+
+ def setup_ping_test(self, testcase_params):
+ WifiPingTest.setup_ping_test(self, testcase_params)
+ # Setup turntable
+ if testcase_params['chamber_mode'] == 'orientation':
+ self.ota_chamber.set_orientation(testcase_params['position'])
+ elif testcase_params['chamber_mode'] == 'stepped stirrers':
+ self.ota_chamber.step_stirrers(testcase_params['total_positions'])
+
+ def extract_test_id(self, testcase_params, id_fields):
+ test_id = collections.OrderedDict(
+ (param, testcase_params[param]) for param in id_fields)
+ return test_id
+
+ def get_range_start_atten(self, testcase_params):
+ """Gets the starting attenuation for this ping test.
+
+ The function gets the starting attenuation by checking whether a test
+ at the same configuration has executed. If so it sets the starting
+ point a configurable number of dBs below the reference test.
+
+ Returns:
+ start_atten: starting attenuation for current test
+ """
+ # If the test is being retried, start from the beginning
+ if self.retry_flag:
+ self.log.info('Retry flag set. Setting attenuation to minimum.')
+ return self.testclass_params['range_atten_start']
+ # Get the current and reference test config. The reference test is the
+ # one performed at the current MCS+1
+ ref_test_params = self.extract_test_id(testcase_params,
+ ['channel', 'mode'])
+ # Check if reference test has been run and set attenuation accordingly
+ previous_params = [
+ self.extract_test_id(result['testcase_params'],
+ ['channel', 'mode'])
+ for result in self.testclass_results
+ ]
+ try:
+ ref_index = previous_params[::-1].index(ref_test_params)
+ ref_index = len(previous_params) - 1 - ref_index
+ start_atten = self.testclass_results[ref_index][
+ 'atten_at_range'] - (
+ self.testclass_params['adjacent_range_test_gap'])
+ except ValueError:
+ self.log.info(
+ 'Reference test not found. Starting from {} dB'.format(
+ self.testclass_params['range_atten_start']))
+ start_atten = self.testclass_params['range_atten_start']
+ return start_atten
+
+ def generate_test_cases(self, ap_power, channels, modes, chamber_mode,
+ positions):
+ test_cases = []
+ allowed_configs = {
+ 'VHT20': [
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 149, 153,
+ 157, 161
+ ],
+ 'VHT40': [36, 44, 149, 157],
+ 'VHT80': [36, 149]
+ }
+ for channel, mode, position in itertools.product(
+ channels, modes, positions):
+ if channel not in allowed_configs[mode]:
+ continue
+ testcase_name = 'test_ping_range_ch{}_{}_pos{}'.format(
+ channel, mode, position)
+ testcase_params = collections.OrderedDict(
+ test_type='test_ping_range',
+ ap_power=ap_power,
+ channel=channel,
+ mode=mode,
+ chain_mask='2x2',
+ chamber_mode=chamber_mode,
+ total_positions=len(positions),
+ position=position)
+ setattr(self, testcase_name,
+ partial(self._test_ping, testcase_params))
+ test_cases.append(testcase_name)
+ return test_cases
+
+
+class WifiOtaPing_TenDegree_Test(WifiOtaPingTest):
+ def __init__(self, controllers):
+ WifiOtaPingTest.__init__(self, controllers)
+ self.tests = self.generate_test_cases(ap_power='standard',
+ channels=[6, 36, 149],
+ modes=['VHT20'],
+ chamber_mode='orientation',
+ positions=list(range(0, 360,
+ 10)))
+
+
+class WifiOtaPing_45Degree_Test(WifiOtaPingTest):
+ def __init__(self, controllers):
+ WifiOtaPingTest.__init__(self, controllers)
+ self.tests = self.generate_test_cases(
+ ap_power='standard',
+ channels=[1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161],
+ modes=['VHT20'],
+ chamber_mode='orientation',
+ positions=list(range(0, 360, 45)))
+
+
+class WifiOtaPing_SteppedStirrers_Test(WifiOtaPingTest):
+ def __init__(self, controllers):
+ WifiOtaPingTest.__init__(self, controllers)
+ self.tests = self.generate_test_cases(ap_power='standard',
+ channels=[6, 36, 149],
+ modes=['VHT20'],
+ chamber_mode='stepped stirrers',
+ positions=list(range(100)))
+
+
+class WifiOtaPing_LowPowerAP_TenDegree_Test(WifiOtaPingTest):
+ def __init__(self, controllers):
+ WifiOtaPingTest.__init__(self, controllers)
+ self.tests = self.generate_test_cases(ap_power='low_power',
+ channels=[6, 36, 149],
+ modes=['VHT20'],
+ chamber_mode='orientation',
+ positions=list(range(0, 360,
+ 10)))
+
+
+class WifiOtaPing_LowPowerAP_45Degree_Test(WifiOtaPingTest):
+ def __init__(self, controllers):
+ WifiOtaPingTest.__init__(self, controllers)
+ self.tests = self.generate_test_cases(
+ ap_power='low_power',
+ channels=[1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161],
+ modes=['VHT20'],
+ chamber_mode='orientation',
+ positions=list(range(0, 360, 45)))
+
+
+class WifiOtaPing_LowPowerAP_SteppedStirrers_Test(WifiOtaPingTest):
+ def __init__(self, controllers):
+ WifiOtaPingTest.__init__(self, controllers)
+ self.tests = self.generate_test_cases(ap_power='low_power',
+ channels=[6, 36, 149],
+ modes=['VHT20'],
+ chamber_mode='stepped stirrers',
+ positions=list(range(100)))
\ No newline at end of file
diff --git a/acts_tests/tests/google/wifi/WifiPnoTest.py b/acts_tests/tests/google/wifi/WifiPnoTest.py
new file mode 100644
index 0000000..879ca0d
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiPnoTest.py
@@ -0,0 +1,205 @@
+#
+# Copyright 2014 - 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 asserts
+from acts import base_test
+from acts.test_decorators import test_tracker_info
+import acts.test_utils.wifi.wifi_test_utils as wutils
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+WifiEnums = wutils.WifiEnums
+MAX_ATTN = 95
+
+class WifiPnoTest(WifiBaseTest):
+
+ def __init__(self, configs):
+ super().__init__(configs)
+ self.enable_packet_log = True
+
+ def setup_class(self):
+ super().setup_class()
+
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ req_params = ["attn_vals", "pno_interval"]
+ opt_param = ["reference_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()
+ elif "OpenWrtAP" in self.user_params:
+ self.configure_openwrt_ap_and_start(wpa_network=True,
+ ap_count=2)
+ self.pno_network_a = self.reference_networks[0]['2g']
+ self.pno_network_b = self.reference_networks[0]['5g']
+ if "OpenWrtAP" in self.user_params:
+ self.pno_network_b = self.reference_networks[1]['5g']
+ self.attn_a = self.attenuators[0]
+ self.attn_b = self.attenuators[1]
+ # Disable second AP's networks, so that it does not interfere during PNO
+ self.attenuators[2].set_atten(MAX_ATTN)
+ self.attenuators[3].set_atten(MAX_ATTN)
+ self.set_attns("default")
+
+ def setup_test(self):
+ super().setup_test()
+ self.dut.droid.wifiStartTrackingStateChange()
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ wutils.reset_wifi(self.dut)
+ self.dut.ed.clear_all_events()
+
+ def teardown_test(self):
+ super().teardown_test()
+ self.dut.droid.wifiStopTrackingStateChange()
+ wutils.reset_wifi(self.dut)
+ self.dut.ed.clear_all_events()
+ self.set_attns("default")
+
+ 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_attns(self, attn_val_name):
+ """Sets attenuation values on attenuators used in this test.
+
+ Args:
+ attn_val_name: Name of the attenuation value pair to use.
+ """
+ self.log.info("Set attenuation values to %s",
+ self.attn_vals[attn_val_name])
+ try:
+ self.attn_a.set_atten(self.attn_vals[attn_val_name][0])
+ self.attn_b.set_atten(self.attn_vals[attn_val_name][1])
+ except:
+ self.log.error("Failed to set attenuation values %s.",
+ attn_val_name)
+ raise
+
+ def trigger_pno_and_assert_connect(self, attn_val_name, expected_con):
+ """Sets attenuators to disconnect current connection to trigger PNO.
+ Validate that the DUT connected to the new SSID as expected after PNO.
+
+ Args:
+ attn_val_name: Name of the attenuation value pair to use.
+ expected_con: The expected info of the network to we expect the DUT
+ to roam to.
+ """
+ connection_info = self.dut.droid.wifiGetConnectionInfo()
+ self.log.info("Triggering PNO connect from %s to %s",
+ connection_info[WifiEnums.SSID_KEY],
+ expected_con[WifiEnums.SSID_KEY])
+ self.set_attns(attn_val_name)
+ self.log.info("Wait %ss for PNO to trigger.", self.pno_interval)
+ time.sleep(self.pno_interval)
+ try:
+ self.log.info("Connected to %s network after PNO interval"
+ % self.dut.droid.wifiGetConnectionInfo())
+ expected_ssid = expected_con[WifiEnums.SSID_KEY]
+ verify_con = {WifiEnums.SSID_KEY: expected_ssid}
+ wutils.verify_wifi_connection_info(self.dut, verify_con)
+ self.log.info("Connected to %s successfully after PNO",
+ expected_ssid)
+ finally:
+ pass
+
+ 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_test_network_"
+ for i in range(0, num_networks):
+ network = {}
+ network[WifiEnums.SSID_KEY] = ssid_name_base + str(i)
+ network[WifiEnums.PWD_KEY] = "pno_test"
+ self.add_network_and_enable(network)
+
+ 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.dut.droid.wifiEnableNetwork(ret, 0)
+
+
+ """ Tests Begin """
+
+ @test_tracker_info(uuid="33d3cae4-5fa7-4e90-b9e2-5d3747bba64c")
+ def test_simple_pno_connection_to_2g(self):
+ """Test PNO triggered autoconnect to a network.
+
+ Steps:
+ 1. Switch off the screen on the device.
+ 2. Save 2 valid network configurations (a & b) in the device.
+ 3. Attenuate 5Ghz network and wait for a few seconds to trigger PNO.
+ 4. Check the device connected to 2Ghz network automatically.
+ """
+ self.add_network_and_enable(self.pno_network_a)
+ self.add_network_and_enable(self.pno_network_b)
+ self.trigger_pno_and_assert_connect("a_on_b_off", self.pno_network_a)
+
+ @test_tracker_info(uuid="39b945a1-830f-4f11-9e6a-9e9641066a96")
+ def test_simple_pno_connection_to_5g(self):
+ """Test PNO triggered autoconnect to a network.
+
+ Steps:
+ 1. Switch off the screen on the device.
+ 2. Save 2 valid network configurations (a & b) in the device.
+ 3. Attenuate 2Ghz network and wait for a few seconds to trigger PNO.
+ 4. Check the device connected to 5Ghz network automatically.
+
+ """
+ self.add_network_and_enable(self.pno_network_a)
+ self.add_network_and_enable(self.pno_network_b)
+ self.trigger_pno_and_assert_connect("b_on_a_off", self.pno_network_b)
+
+ @test_tracker_info(uuid="844b15be-ff45-4b09-a11b-0b2b4bb13b22")
+ def test_pno_connection_with_multiple_saved_networks(self):
+ """Test PNO triggered autoconnect to a network when there are more
+ than 16 networks saved in the device.
+
+ 16 is the max list size of PNO watch list for most devices. The device should automatically
+ pick the 16 most recently connected networks. For networks that were never connected, the
+ networks seen in the previous scan result would have higher priority.
+
+ Steps:
+ 1. Save 16 test network configurations in the device.
+ 2. Add 2 connectable networks and do a normal scan.
+ 3. Trigger PNO scan
+ """
+ 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.
+ wutils.start_wifi_connection_scan_and_return_status(self.dut)
+ self.dut.droid.goToSleepNow()
+ wutils.wifi_toggle_state(self.dut, False)
+ wutils.wifi_toggle_state(self.dut, True)
+ time.sleep(10)
+ self.trigger_pno_and_assert_connect("b_on_a_off", self.pno_network_b)
+
+ """ Tests End """
diff --git a/acts_tests/tests/google/wifi/WifiPreFlightTest.py b/acts_tests/tests/google/wifi/WifiPreFlightTest.py
new file mode 100644
index 0000000..14a4190
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiPreFlightTest.py
@@ -0,0 +1,180 @@
+#!/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 copy
+import pprint
+import time
+
+import acts.base_test
+import acts.test_utils.wifi.wifi_test_utils as wutils
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+SCAN_TIME = 30
+WAIT_TIME = 2
+
+
+class WifiPreFlightTest(WifiBaseTest):
+ """ Pre-flight checks for Wifi tests.
+
+ Test Bed Requirement:
+ * One Android device
+ * 4 reference networks - two 2G and two 5G networks
+ * Attenuators to attenuate each reference network
+
+ Tests:
+ * Check if reference networks show up in wifi scan
+ * Check if attenuators attenuate the correct network
+ """
+
+ def setup_class(self):
+ super().setup_class()
+ self.WIFI_2G = "2g"
+ self.WIFI_5G = "5g"
+ self.PASSWORD = "password"
+ self.MIN_SIGNAL_LEVEL = -45
+
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ wutils.wifi_toggle_state(self.dut, True)
+
+ # Get reference networks as a list
+ opt_params = ["reference_networks"]
+ self.unpack_userparams(opt_param_names=opt_params)
+
+ if "AccessPoint" in self.user_params:
+ self.legacy_configure_ap_and_start(ap_count=2)
+ networks = []
+ for ref_net in self.reference_networks:
+ networks.append(ref_net[self.WIFI_2G])
+ networks.append(ref_net[self.WIFI_5G])
+ self.reference_networks = networks
+ asserts.assert_true(
+ len(self.reference_networks) == 4,
+ "Need at least 4 reference network with psk.")
+
+ def teardown_class(self):
+ wutils.reset_wifi(self.dut)
+ for a in self.attenuators:
+ a.set_atten(0)
+ if "AccessPoint" in self.user_params:
+ del self.user_params["reference_networks"]
+ del self.user_params["open_network"]
+
+ """ Helper functions """
+ def _find_reference_networks_no_attn(self):
+ """ Verify that when ATTN set to 0, all reference networks
+ show up in the scanned results
+
+ Args:
+ 1. List of reference networks
+
+ Returns:
+ 1. List of networks not found. Empty if all reference
+ networks are found
+ """
+ found_networks = copy.deepcopy(self.target_networks)
+ start_time = time.time()
+ while(time.time() < start_time + SCAN_TIME):
+ if not found_networks:
+ break
+ time.sleep(WAIT_TIME)
+ scanned_networks = self.dut.droid.wifiGetScanResults()
+ self.log.info("SCANNED RESULTS %s" % scanned_networks)
+ for net in self.target_networks:
+ if net in found_networks:
+ result = wutils.match_networks(net, scanned_networks)
+ if result and result[0]['level'] > self.MIN_SIGNAL_LEVEL:
+ found_networks.remove(net)
+ elif result:
+ self.log.warn("Signal strength for %s is low: %sdBm"
+ % (net, result[0]['level']))
+ return found_networks
+
+ def _find_network_after_setting_attn(self, target_network):
+ """ Find network after setting attenuation
+
+ Args:
+ 1. target_network to find in the scanned_results
+
+ Returns:
+ 1. True if
+ a. if max_attn is set and target_network not found
+ 2. False if not
+ """
+ start_time = time.time()
+ while(time.time() < start_time + SCAN_TIME):
+ time.sleep(WAIT_TIME)
+ scanned_networks = self.dut.droid.wifiGetScanResults()
+ self.log.info("SCANNED RESULTS %s" % scanned_networks)
+ result = wutils.match_networks(target_network, scanned_networks)
+ if not result:
+ return True
+ return False
+
+ """ Tests """
+ def test_attenuators(self):
+ """ Test if attenuating a channel, disables the correct
+ reference network
+
+ Reference networks for each testbed should match
+ attenuators as follows
+
+ wh_ap1_2g - channel 1
+ wh_ap1_5g - channel 2
+ wh_ap2_2g - channel 3
+ wh_ap2_5g - channel 4
+
+ Steps:
+ 1. Set attenuation on each channel to 95
+ 2. Verify that the corresponding network does not show
+ up in the scanned results
+ """
+ # Set attenuation to 0 and verify reference
+ # networks show up in the scanned results
+ self.log.info("Verify if all reference networks show with "
+ "attenuation set to 0")
+ if getattr(self, "attenuators", []):
+ for a in self.attenuators:
+ a.set_atten(0)
+ self.target_networks = []
+ for ref_net in self.reference_networks:
+ self.target_networks.append( {'BSSID': ref_net['bssid']} )
+ result = self._find_reference_networks_no_attn()
+ asserts.assert_true(not result,
+ "Did not find or signal strength too low "
+ "for the following reference networks\n%s\n" % result)
+
+ # attenuate 1 channel at a time and find the network
+ self.log.info("Verify if attenuation channel matches with "
+ "correct reference network")
+ found_networks = []
+ for i in range(len(self.attenuators)):
+ target_network = {}
+ target_network['BSSID'] = self.reference_networks[i]['bssid']
+
+ # set the attn to max and verify target network is not found
+ self.attenuators[i].set_atten(95)
+ result = self._find_network_after_setting_attn(target_network)
+ if result:
+ target_network['ATTN'] = i
+ found_networks.append(target_network)
+
+ asserts.assert_true(not found_networks,
+ "Attenuators did not match the networks\n %s\n"
+ % pprint.pformat(found_networks))
diff --git a/acts_tests/tests/google/wifi/WifiRoamingPerformanceTest.py b/acts_tests/tests/google/wifi/WifiRoamingPerformanceTest.py
new file mode 100644
index 0000000..bfc96d5
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiRoamingPerformanceTest.py
@@ -0,0 +1,779 @@
+#!/usr/bin/env python3.4
+#
+# 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 collections
+import json
+import math
+import os
+import time
+from acts import asserts
+from acts import base_test
+from acts import context
+from acts import utils
+from acts.controllers import iperf_server as ipf
+from acts.controllers.utils_lib import ssh
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
+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 acts.test_utils.wifi import wifi_test_utils as wutils
+
+SHORT_SLEEP = 1
+MED_SLEEP = 5
+TRAFFIC_GAP_THRESH = 0.5
+IPERF_INTERVAL = 0.25
+
+
+class WifiRoamingPerformanceTest(base_test.BaseTestClass):
+ """Class for ping-based Wifi performance tests.
+
+ This class implements WiFi ping performance tests such as range and RTT.
+ The class setups up the AP in the desired configurations, configures
+ and connects the phone to the AP, and runs For an example config file to
+ run this test class see example_connectivity_performance_ap_sta.json.
+ """
+ def __init__(self, controllers):
+ base_test.BaseTestClass.__init__(self, controllers)
+ self.testcase_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_case())
+ self.testclass_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_class())
+ self.publish_testcase_metrics = True
+
+ def setup_class(self):
+ """Initializes common test hardware and parameters.
+
+ 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', 'roaming_test_params', 'testbed_params'
+ ]
+ opt_params = ['main_network', 'RemoteServer']
+ self.unpack_userparams(req_params, opt_params)
+ self.testclass_params = self.roaming_test_params
+ self.num_atten = self.attenuators[0].instrument.num_atten
+ self.remote_server = ssh.connection.SshConnection(
+ ssh.settings.from_config(self.RemoteServer[0]['ssh_config']))
+ self.remote_server.setup_master_ssh()
+ self.iperf_server = self.iperf_servers[0]
+ self.iperf_client = self.iperf_clients[0]
+ self.access_point = retail_ap.create(self.RetailAccessPoints)[0]
+ self.log.info('Access Point Configuration: {}'.format(
+ self.access_point.ap_settings))
+
+ # Get RF connection map
+ self.log.info("Getting RF connection map.")
+ wutils.wifi_toggle_state(self.dut, True)
+ self.rf_map_by_network, self.rf_map_by_atten = (
+ wputils.get_full_rf_connection_map(self.attenuators, self.dut,
+ self.remote_server,
+ self.main_network))
+ self.log.info("RF Map (by Network): {}".format(self.rf_map_by_network))
+ self.log.info("RF Map (by Atten): {}".format(self.rf_map_by_atten))
+
+ #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)
+
+ def pass_fail_traffic_continuity(self, result):
+ """Pass fail check for traffic continuity
+
+ Currently, the function only reports test results and implicitly passes
+ the test. A pass fail criterion is current being researched.
+
+ Args:
+ result: dict containing test results
+ """
+ self.log.info('Detected {} roam transitions:'.format(
+ len(result['roam_transitions'])))
+ for event in result['roam_transitions']:
+ self.log.info('Roam: {} -> {})'.format(event[0], event[1]))
+ self.log.info('Roam transition statistics: {}'.format(
+ result['roam_counts']))
+
+ formatted_traffic_gaps = [
+ round(gap, 2) for gap in result['traffic_disruption']
+ ]
+ self.log.info('Detected {} traffic gaps of duration: {}'.format(
+ len(result['traffic_disruption']), formatted_traffic_gaps))
+
+ if result['total_roams'] > 0:
+ disruption_percentage = (len(result['traffic_disruption']) /
+ result['total_roams']) * 100
+ max_disruption = max(result['traffic_disruption'])
+ else:
+ disruption_percentage = 0
+ max_disruption = 0
+ self.testcase_metric_logger.add_metric('disruption_percentage',
+ disruption_percentage)
+ self.testcase_metric_logger.add_metric('max_disruption',
+ max_disruption)
+
+ if disruption_percentage == 0:
+ asserts.explicit_pass('Test passed. No traffic disruptions found.')
+ elif max_disruption > self.testclass_params[
+ 'traffic_disruption_threshold']:
+ asserts.fail('Test failed. Disruption Percentage = {}%. '
+ 'Max traffic disruption: {}s.'.format(
+ disruption_percentage, max_disruption))
+ else:
+ asserts.explicit_pass('Test failed. Disruption Percentage = {}%. '
+ 'Max traffic disruption: {}s.'.format(
+ disruption_percentage, max_disruption))
+
+ def pass_fail_roaming_consistency(self, results_dict):
+ """Function to evaluate roaming consistency results.
+
+ The function looks for the roams recorded in multiple runs of the same
+ attenuation waveform and checks that the DUT reliably roams to the
+ same network
+
+ Args:
+ results_dict: dict containing consistency test results
+ """
+ test_fail = False
+ for secondary_atten, roam_stats in results_dict['roam_stats'].items():
+ total_roams = sum(list(roam_stats.values()))
+ common_roam = max(roam_stats.keys(), key=(lambda k: roam_stats[k]))
+ common_roam_frequency = roam_stats[common_roam] / total_roams
+ self.log.info(
+ '{}dB secondary atten. Most common roam: {}. Frequency: {}'.
+ format(secondary_atten, common_roam, common_roam_frequency))
+ if common_roam_frequency < self.testclass_params[
+ 'consistency_threshold']:
+ test_fail = True
+ self.log.info('Unstable Roams at {}dB secondary att'.format(
+ secondary_atten))
+ self.testcase_metric_logger.add_metric('common_roam_frequency',
+ common_roam_frequency)
+ if test_fail:
+ asserts.fail('Incosistent roaming detected.')
+ else:
+ asserts.explicit_pass('Consistent roaming at all levels.')
+
+ def process_traffic_continuity_results(self, testcase_params, result):
+ """Function to process traffic results.
+
+ The function looks for traffic gaps during a roaming test
+
+ Args:
+ testcase_params: dict containing all test results and meta data
+ results_dict: dict containing consistency test results
+ """
+ self.detect_roam_events(result)
+ current_context = context.get_current_context().get_full_output_path()
+ plot_file_path = os.path.join(current_context,
+ self.current_test_name + '.html')
+
+ if 'ping' in self.current_test_name:
+ self.detect_ping_gaps(result)
+ self.plot_ping_result(testcase_params,
+ result,
+ output_file_path=plot_file_path)
+ elif 'iperf' in self.current_test_name:
+ self.detect_iperf_gaps(result)
+ self.plot_iperf_result(testcase_params,
+ result,
+ output_file_path=plot_file_path)
+
+ results_file_path = os.path.join(current_context,
+ self.current_test_name + '.json')
+ with open(results_file_path, 'w') as results_file:
+ json.dump(wputils.serialize_dict(result), results_file, indent=4)
+
+ def process_consistency_results(self, testcase_params, results_dict):
+ """Function to process roaming consistency results.
+
+ The function looks compiles the test of roams recorded in consistency
+ tests and plots results for easy visualization.
+
+ Args:
+ testcase_params: dict containing all test results and meta data
+ results_dict: dict containing consistency test results
+ """
+ # make figure placeholder and get relevant functions
+ if 'ping' in self.current_test_name:
+ detect_gaps = self.detect_ping_gaps
+ plot_result = self.plot_ping_result
+ primary_y_axis = 'RTT (ms)'
+ elif 'iperf' in self.current_test_name:
+ detect_gaps = self.detect_iperf_gaps
+ plot_result = self.plot_iperf_result
+ primary_y_axis = 'Throughput (Mbps)'
+ # loop over results
+ roam_stats = collections.OrderedDict()
+ current_context = context.get_current_context().get_full_output_path()
+ for secondary_atten, results_list in results_dict.items():
+ figure = wputils.BokehFigure(title=self.current_test_name,
+ x_label='Time (ms)',
+ primary_y_label=primary_y_axis,
+ secondary_y_label='RSSI (dBm)')
+ roam_stats[secondary_atten] = collections.OrderedDict()
+ for result in results_list:
+ self.detect_roam_events(result)
+ for roam_transition, count in result['roam_counts'].items():
+ roam_stats[secondary_atten][
+ roam_transition] = roam_stats[secondary_atten].get(
+ roam_transition, 0) + count
+ detect_gaps(result)
+ plot_result(testcase_params, result, figure=figure)
+ # save plot
+ plot_file_name = (self.current_test_name + '_' +
+ str(secondary_atten) + '.html')
+
+ plot_file_path = os.path.join(current_context, plot_file_name)
+ figure.save_figure(plot_file_path)
+ results_dict['roam_stats'] = roam_stats
+
+ results_file_path = os.path.join(current_context,
+ self.current_test_name + '.json')
+ with open(results_file_path, 'w') as results_file:
+ json.dump(wputils.serialize_dict(result), results_file, indent=4)
+
+ def detect_roam_events(self, result):
+ """Function to process roaming results.
+
+ The function detects roams by looking at changes in BSSID and compiles
+ meta data about each roam, e.g., RSSI before and after a roam. The
+ function then calls the relevant method to process traffic results and
+ report traffic disruptions.
+
+ Args:
+ testcase_params: dict containing AP and other test params
+ result: dict containing test results
+ """
+ roam_events = [
+ (idx, idx + 1)
+ for idx in range(len(result['rssi_result']['bssid']) - 1)
+ if result['rssi_result']['bssid'][idx] != result['rssi_result']
+ ['bssid'][idx + 1]
+ ]
+
+ def ignore_entry(vals):
+ for val in vals:
+ if val in {0} or math.isnan(val):
+ return True
+ return False
+
+ for roam_idx, roam_event in enumerate(roam_events):
+ # Find true roam start by scanning earlier samples for valid data
+ while ignore_entry([
+ result['rssi_result']['frequency'][roam_event[0]],
+ result['rssi_result']['signal_poll_rssi']['data'][
+ roam_event[0]]
+ ]):
+ roam_event = (roam_event[0] - 1, roam_event[1])
+ roam_events[roam_idx] = roam_event
+ # Find true roam end by scanning later samples for valid data
+ while ignore_entry([
+ result['rssi_result']['frequency'][roam_event[1]],
+ result['rssi_result']['signal_poll_rssi']['data'][
+ roam_event[1]]
+ ]):
+ roam_event = (roam_event[0], roam_event[1] + 1)
+ roam_events[roam_idx] = roam_event
+
+ roam_events = list(set(roam_events))
+ roam_events.sort(key=lambda event_tuple: event_tuple[1])
+ roam_transitions = []
+ roam_counts = {}
+ total_roams = 0
+ for event in roam_events:
+ from_bssid = next(
+ key for key, value in self.main_network.items()
+ if value['BSSID'] == result['rssi_result']['bssid'][event[0]])
+ to_bssid = next(
+ key for key, value in self.main_network.items()
+ if value['BSSID'] == result['rssi_result']['bssid'][event[1]])
+ curr_bssid_transition = (from_bssid, to_bssid)
+ curr_roam_transition = (
+ (from_bssid,
+ result['rssi_result']['signal_poll_rssi']['data'][event[0]]),
+ (to_bssid,
+ result['rssi_result']['signal_poll_rssi']['data'][event[1]]))
+ roam_transitions.append(curr_roam_transition)
+ roam_counts[curr_bssid_transition] = roam_counts.get(
+ curr_bssid_transition, 0) + 1
+ total_roams = total_roams + 1
+ result['roam_events'] = roam_events
+ result['roam_transitions'] = roam_transitions
+ result['roam_counts'] = roam_counts
+ result['total_roams'] = total_roams
+
+ def detect_ping_gaps(self, result):
+ """Function to process ping results.
+
+ The function looks for gaps in iperf traffic and reports them as
+ disruptions due to roams.
+
+ Args:
+ result: dict containing test results
+ """
+ traffic_disruption = [
+ x for x in result['ping_result']['ping_interarrivals']
+ if x > TRAFFIC_GAP_THRESH
+ ]
+ result['traffic_disruption'] = traffic_disruption
+
+ def detect_iperf_gaps(self, result):
+ """Function to process iperf results.
+
+ The function looks for gaps in iperf traffic and reports them as
+ disruptions due to roams.
+
+ Args:
+ result: dict containing test results
+ """
+ tput_thresholding = [tput < 1 for tput in result['throughput']]
+ window_size = int(TRAFFIC_GAP_THRESH / IPERF_INTERVAL)
+ tput_thresholding = [
+ any(tput_thresholding[max(0, idx - window_size):idx])
+ for idx in range(1,
+ len(tput_thresholding) + 1)
+ ]
+
+ traffic_disruption = []
+ current_disruption = 1 - window_size
+ for tput_low in tput_thresholding:
+ if tput_low:
+ current_disruption += 1
+ elif current_disruption > window_size:
+ traffic_disruption.append(current_disruption * IPERF_INTERVAL)
+ current_disruption = 1 - window_size
+ else:
+ current_disruption = 1 - window_size
+ result['traffic_disruption'] = traffic_disruption
+
+ def plot_ping_result(self,
+ testcase_params,
+ result,
+ figure=None,
+ output_file_path=None):
+ """Function to plot ping results.
+
+ The function plots ping RTTs along with RSSI over time during a roaming
+ test.
+
+ Args:
+ testcase_params: dict containing all test params
+ result: dict containing test results
+ figure: optional bokeh figure object to add current plot to
+ output_file_path: optional path to output file
+ """
+ if not figure:
+ figure = wputils.BokehFigure(title=self.current_test_name,
+ x_label='Time (ms)',
+ primary_y_label='RTT (ms)',
+ secondary_y_label='RSSI (dBm)')
+ figure.add_line(x_data=result['ping_result']['time_stamp'],
+ y_data=result['ping_result']['rtt'],
+ legend='Ping RTT',
+ width=1)
+ figure.add_line(
+ x_data=result['rssi_result']['time_stamp'],
+ y_data=result['rssi_result']['signal_poll_rssi']['data'],
+ legend='RSSI',
+ y_axis='secondary')
+ figure.generate_figure(output_file_path)
+
+ def plot_iperf_result(self,
+ testcase_params,
+ result,
+ figure=None,
+ output_file_path=None):
+ """Function to plot iperf results.
+
+ The function plots iperf throughput and RSSI over time during a roaming
+ test.
+
+ Args:
+ testcase_params: dict containing all test params
+ result: dict containing test results
+ figure: optional bokeh figure object to add current plot to
+ output_file_path: optional path to output file
+ """
+ if not figure:
+ figure = wputils.BokehFigure(title=self.current_test_name,
+ x_label='Time (s)',
+ primary_y_label='Throughput (Mbps)',
+ secondary_y_label='RSSI (dBm)')
+ iperf_time_stamps = [
+ idx * IPERF_INTERVAL for idx in range(len(result['throughput']))
+ ]
+ figure.add_line(iperf_time_stamps,
+ result['throughput'],
+ 'Throughput',
+ width=1)
+ figure.add_line(result['rssi_result']['time_stamp'],
+ result['rssi_result']['signal_poll_rssi']['data'],
+ 'RSSI',
+ y_axis='secondary')
+
+ figure.generate_figure(output_file_path)
+
+ def setup_ap(self, testcase_params):
+ """Sets up the AP and attenuator to the test configuration.
+
+ Args:
+ testcase_params: dict containing AP and other test params
+ """
+ (primary_net_id,
+ primary_net_config) = next(net for net in self.main_network.items()
+ if net[1]['roaming_label'] == 'primary')
+ for idx, atten in enumerate(self.attenuators):
+ nets_on_port = [
+ item["network"] for item in self.rf_map_by_atten[idx]
+ ]
+ if primary_net_id in nets_on_port:
+ atten.set_atten(0)
+ else:
+ atten.set_atten(atten.instrument.max_atten)
+
+ def setup_dut(self, testcase_params):
+ """Sets up the DUT in the configuration required by the test.
+
+ Args:
+ testcase_params: dict containing AP and other test params
+ """
+ # Check battery level before test
+ if not wputils.health_check(self.dut, 10):
+ asserts.skip('Battery level too low. Skipping test.')
+ wutils.reset_wifi(self.dut)
+ wutils.set_wifi_country_code(self.dut,
+ self.testclass_params['country_code'])
+ (primary_net_id,
+ primary_net_config) = next(net for net in self.main_network.items()
+ if net[1]['roaming_label'] == 'primary')
+ network = primary_net_config.copy()
+ network.pop('BSSID', None)
+ self.dut.droid.wifiSetEnableAutoJoinWhenAssociated(1)
+ wutils.wifi_connect(self.dut,
+ network,
+ num_of_tries=5,
+ check_connectivity=False)
+ self.dut.droid.wifiSetEnableAutoJoinWhenAssociated(1)
+ self.dut_ip = self.dut.droid.connectivityGetIPv4Addresses('wlan0')[0]
+ if testcase_params['screen_on']:
+ self.dut.wakeup_screen()
+ self.dut.droid.wakeLockAcquireBright()
+ time.sleep(MED_SLEEP)
+
+ def setup_roaming_test(self, testcase_params):
+ """Function to set up roaming test."""
+ self.setup_ap(testcase_params)
+ self.setup_dut(testcase_params)
+
+ def run_ping_test(self, testcase_params):
+ """Main function for ping roaming tests.
+
+ Args:
+ testcase_params: dict including all test params encoded in test
+ name
+ Returns:
+ dict containing all test results and meta data
+ """
+ self.log.info('Starting ping test.')
+ ping_future = wputils.get_ping_stats_nb(
+ self.remote_server, self.dut_ip,
+ testcase_params['atten_waveforms']['length'],
+ testcase_params['ping_interval'], 64)
+ rssi_future = wputils.get_connected_rssi_nb(
+ self.dut,
+ int(testcase_params['atten_waveforms']['length'] /
+ testcase_params['rssi_polling_frequency']),
+ testcase_params['rssi_polling_frequency'])
+ self.run_attenuation_waveform(testcase_params)
+ return {
+ 'ping_result': ping_future.result().as_dict(),
+ 'rssi_result': rssi_future.result(),
+ 'ap_settings': self.access_point.ap_settings,
+ }
+
+ def run_iperf_test(self, testcase_params):
+ """Main function for iperf roaming tests.
+
+ Args:
+ testcase_params: dict including all test params encoded in test
+ name
+ Returns:
+ result: dict containing all test results and meta data
+ """
+ self.log.info('Starting iperf test.')
+ self.iperf_server.start(extra_args='-i {}'.format(IPERF_INTERVAL))
+ self.dut_ip = self.dut.droid.connectivityGetIPv4Addresses('wlan0')[0]
+ if isinstance(self.iperf_server, ipf.IPerfServerOverAdb):
+ iperf_server_address = self.dut_ip
+ else:
+ iperf_server_address = wputils.get_server_address(
+ self.remote_server, self.dut_ip, '255.255.255.0')
+ iperf_args = '-i {} -t {} -J'.format(
+ IPERF_INTERVAL, testcase_params['atten_waveforms']['length'])
+ if not isinstance(self.iperf_server, ipf.IPerfServerOverAdb):
+ iperf_args = iperf_args + ' -R'
+ iperf_future = wputils.start_iperf_client_nb(
+ self.iperf_client, iperf_server_address, iperf_args, 0,
+ testcase_params['atten_waveforms']['length'] + MED_SLEEP)
+ rssi_future = wputils.get_connected_rssi_nb(
+ self.dut,
+ int(testcase_params['atten_waveforms']['length'] /
+ testcase_params['rssi_polling_frequency']),
+ testcase_params['rssi_polling_frequency'])
+ self.run_attenuation_waveform(testcase_params)
+ client_output_path = iperf_future.result()
+ server_output_path = self.iperf_server.stop()
+ if isinstance(self.iperf_server, ipf.IPerfServerOverAdb):
+ iperf_file = server_output_path
+ else:
+ iperf_file = client_output_path
+ iperf_result = ipf.IPerfResult(iperf_file)
+ instantaneous_rates = [
+ rate * 8 * (1.024**2) for rate in iperf_result.instantaneous_rates
+ ]
+ return {
+ 'throughput': instantaneous_rates,
+ 'rssi_result': rssi_future.result(),
+ 'ap_settings': self.access_point.ap_settings,
+ }
+
+ def run_attenuation_waveform(self, testcase_params, step_duration=1):
+ """Function that generates test params based on the test name.
+
+ Args:
+ testcase_params: dict including all test params encoded in test
+ name
+ step_duration: int representing number of seconds to dwell on each
+ atten level
+ """
+ atten_waveforms = testcase_params['atten_waveforms']
+ for atten_idx in range(atten_waveforms['length']):
+ start_time = time.time()
+ for network, atten_waveform in atten_waveforms.items():
+ for idx, atten in enumerate(self.attenuators):
+ nets_on_port = [
+ item["network"] for item in self.rf_map_by_atten[idx]
+ ]
+ if network in nets_on_port:
+ atten.set_atten(atten_waveform[atten_idx])
+ measure_time = time.time() - start_time
+ time.sleep(step_duration - measure_time)
+
+ def compile_atten_waveforms(self, waveform_params):
+ """Function to compile all attenuation waveforms for roaming test.
+
+ Args:
+ waveform_params: list of dicts representing waveforms to generate
+ """
+ atten_waveforms = {}
+ for network in list(waveform_params[0]):
+ atten_waveforms[network] = []
+
+ for waveform in waveform_params:
+ for network, network_waveform in waveform.items():
+ waveform_vector = self.gen_single_atten_waveform(
+ network_waveform)
+ atten_waveforms[network] += waveform_vector
+
+ waveform_lengths = {
+ len(atten_waveforms[network])
+ for network in atten_waveforms.keys()
+ }
+ if len(waveform_lengths) != 1:
+ raise ValueError(
+ 'Attenuation waveform length should be equal for all networks.'
+ )
+ else:
+ atten_waveforms['length'] = waveform_lengths.pop()
+ return atten_waveforms
+
+ def gen_single_atten_waveform(self, waveform_params):
+ """Function to generate a single attenuation waveform for roaming test.
+
+ Args:
+ waveform_params: dict representing waveform to generate
+ """
+ waveform_vector = []
+ for section in range(len(waveform_params['atten_levels']) - 1):
+ section_limits = waveform_params['atten_levels'][section:section +
+ 2]
+ up_down = (1 - 2 * (section_limits[1] < section_limits[0]))
+ temp_section = list(
+ range(section_limits[0], section_limits[1] + up_down,
+ up_down * waveform_params['step_size']))
+ temp_section = [
+ val for val in temp_section
+ for _ in range(waveform_params['step_duration'])
+ ]
+ waveform_vector += temp_section
+ waveform_vector *= waveform_params['repetitions']
+ return waveform_vector
+
+ def parse_test_params(self, testcase_params):
+ """Function that generates test params based on the test name.
+
+ Args:
+ test_name: current test name
+ Returns:
+ testcase_params: dict including all test params encoded in test
+ name
+ """
+ if testcase_params["waveform_type"] == 'smooth':
+ testcase_params[
+ 'roaming_waveforms_params'] = self.testclass_params[
+ 'smooth_roaming_waveforms']
+ elif testcase_params["waveform_type"] == 'failover':
+ testcase_params[
+ 'roaming_waveforms_params'] = self.testclass_params[
+ 'failover_roaming_waveforms']
+ elif testcase_params["waveform_type"] == 'consistency':
+ testcase_params[
+ 'roaming_waveforms_params'] = self.testclass_params[
+ 'consistency_waveforms']
+ return testcase_params
+
+ def _test_traffic_continuity(self, testcase_params):
+ """Test function for traffic continuity"""
+ # Compile test parameters from config and test name
+ testcase_params = self.parse_test_params(testcase_params)
+ testcase_params.update(self.testclass_params)
+ testcase_params['atten_waveforms'] = self.compile_atten_waveforms(
+ testcase_params['roaming_waveforms_params'])
+ # Run traffic test
+ self.setup_roaming_test(testcase_params)
+ if testcase_params['traffic_type'] == 'iperf':
+ result = self.run_iperf_test(testcase_params)
+ elif testcase_params['traffic_type'] == 'ping':
+ result = self.run_ping_test(testcase_params)
+ # Postprocess results
+ self.process_traffic_continuity_results(testcase_params, result)
+ self.pass_fail_traffic_continuity(result)
+
+ def _test_roam_consistency(self, testcase_params):
+ """Test function for roaming consistency"""
+ testcase_params = self.parse_test_params(testcase_params)
+ testcase_params.update(self.testclass_params)
+ # Run traffic test
+ secondary_attens = range(
+ self.testclass_params['consistency_waveforms']['secondary_loop']
+ ['atten_levels'][0], self.testclass_params['consistency_waveforms']
+ ['secondary_loop']['atten_levels'][1],
+ self.testclass_params['consistency_waveforms']['secondary_loop']
+ ['step_size'])
+ results = collections.OrderedDict()
+ for secondary_atten in secondary_attens:
+ primary_waveform = self.gen_single_atten_waveform(
+ testcase_params['roaming_waveforms_params']['primary_sweep'])
+ secondary_waveform_params = {
+ 'atten_levels': [secondary_atten, secondary_atten],
+ 'step_size': 1,
+ 'step_duration': len(primary_waveform),
+ 'repetitions': 1
+ }
+ secondary_waveform = self.gen_single_atten_waveform(
+ secondary_waveform_params)
+ testcase_params['atten_waveforms'] = {
+ 'length': len(primary_waveform)
+ }
+ for network_key, network_info in self.main_network.items():
+ if 'primary' in network_info['roaming_label']:
+ testcase_params['atten_waveforms'][
+ network_key] = primary_waveform
+ else:
+ testcase_params['atten_waveforms'][
+ network_key] = secondary_waveform
+ results[secondary_atten] = []
+ for run in range(self.testclass_params['consistency_num_runs']):
+ self.setup_roaming_test(testcase_params)
+ results[secondary_atten].append(
+ self.run_ping_test(testcase_params))
+ # Postprocess results
+ self.process_consistency_results(testcase_params, results)
+ self.pass_fail_roaming_consistency(results)
+
+ def test_consistency_roaming_screen_on_ping(self):
+ testcase_params = {
+ "waveform_type": "consistency",
+ "screen_on": 1,
+ "traffic_type": "ping"
+ }
+ self._test_roam_consistency(testcase_params)
+
+ def test_smooth_roaming_screen_on_ping_continuity(self):
+ testcase_params = {
+ "waveform_type": "smooth",
+ "screen_on": 1,
+ "traffic_type": "ping"
+ }
+ self._test_traffic_continuity(testcase_params)
+
+ def test_smooth_roaming_screen_on_iperf_continuity(self):
+ testcase_params = {
+ "waveform_type": "smooth",
+ "screen_on": 1,
+ "traffic_type": "iperf"
+ }
+ self._test_traffic_continuity(testcase_params)
+
+ def test_failover_roaming_screen_on_ping_continuity(self):
+ testcase_params = {
+ "waveform_type": "failover",
+ "screen_on": 1,
+ "traffic_type": "ping"
+ }
+ self._test_traffic_continuity(testcase_params)
+
+ def test_failover_roaming_screen_on_iperf_continuity(self):
+ testcase_params = {
+ "waveform_type": "failover",
+ "screen_on": 1,
+ "traffic_type": "iperf"
+ }
+ self._test_traffic_continuity(testcase_params)
+
+ def test_smooth_roaming_screen_off_ping_continuity(self):
+ testcase_params = {
+ "waveform_type": "smooth",
+ "screen_on": 0,
+ "traffic_type": "ping"
+ }
+ self._test_traffic_continuity(testcase_params)
+
+ def test_smooth_roaming_screen_off_iperf_continuity(self):
+ testcase_params = {
+ "waveform_type": "smooth",
+ "screen_on": 0,
+ "traffic_type": "iperf"
+ }
+ self._test_traffic_continuity(testcase_params)
+
+ def test_failover_roaming_screen_off_ping_continuity(self):
+ testcase_params = {
+ "waveform_type": "failover",
+ "screen_on": 0,
+ "traffic_type": "ping"
+ }
+ self._test_traffic_continuity(testcase_params)
+
+ def test_failover_roaming_screen_off_iperf_continuity(self):
+ testcase_params = {
+ "waveform_type": "failover",
+ "screen_on": 0,
+ "traffic_type": "iperf"
+ }
+ self._test_traffic_continuity(testcase_params)
diff --git a/acts_tests/tests/google/wifi/WifiRoamingTest.py b/acts_tests/tests/google/wifi/WifiRoamingTest.py
new file mode 100644
index 0000000..0ef6bb6
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiRoamingTest.py
@@ -0,0 +1,184 @@
+#
+# 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.
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+
+class WifiRoamingTest(WifiBaseTest):
+
+ def setup_class(self):
+ """Configure the required networks for testing roaming."""
+ super().setup_class()
+
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ req_params = ["roaming_attn",]
+ self.unpack_userparams(req_param_names=req_params,)
+
+ if "AccessPoint" in self.user_params:
+ self.legacy_configure_ap_and_start(ap_count=2)
+ elif "OpenWrtAP" in self.user_params:
+ self.configure_openwrt_ap_and_start(open_network=True,
+ wpa_network=True,
+ owe_network=True,
+ sae_network=True,
+ ap_count=2,
+ mirror_ap=True)
+
+ def teardown_class(self):
+ self.dut.ed.clear_all_events()
+ if "AccessPoint" in self.user_params:
+ del self.user_params["reference_networks"]
+ del self.user_params["open_network"]
+
+ def setup_test(self):
+ super().setup_test()
+ self.dut.ed.clear_all_events()
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+
+ def teardown_test(self):
+ super().teardown_test()
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ wutils.reset_wifi(self.dut)
+ wutils.set_attns(self.attenuators, "default")
+
+ ### Helper Methods ###
+
+ def roaming_from_AP1_and_AP2(self, AP1_network, AP2_network):
+ """Test roaming between two APs.
+
+ Args:
+ AP1_network: AP-1's network information.
+ AP2_network: AP-2's network information.
+
+ Steps:
+ 1. Make AP1 visible, AP2 not visible.
+ 2. Connect to AP1's ssid.
+ 3. Make AP1 not visible, AP2 visible.
+ 4. Expect DUT to roam to AP2.
+ 5. Validate connection information and ping.
+ """
+ wutils.set_attns(self.attenuators, "AP1_on_AP2_off", self.roaming_attn)
+ 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,
+ self.roaming_attn)
+
+ ### Test Cases ###
+
+ @test_tracker_info(uuid="db8a46f9-713f-4b98-8d9f-d36319905b0a")
+ def test_roaming_between_AP1_to_AP2_open_2g(self):
+ ap1_network = self.open_network[0]["2g"]
+ ap2_network = self.open_network[1]["2g"]
+ if "OpenWrtAP" in self.user_params:
+ ap1_network["bssid"] = self.bssid_map[0]["2g"][ap1_network["SSID"]]
+ ap2_network["bssid"] = self.bssid_map[1]["2g"][ap2_network["SSID"]]
+ self.roaming_from_AP1_and_AP2(ap1_network, ap2_network)
+
+ @test_tracker_info(uuid="0db67d9b-6ea9-4f40-acf2-155c4ecf9dc5")
+ def test_roaming_between_AP1_to_AP2_open_5g(self):
+ ap1_network = self.open_network[0]["5g"]
+ ap2_network = self.open_network[1]["5g"]
+ if "OpenWrtAP" in self.user_params:
+ ap1_network["bssid"] = self.bssid_map[0]["5g"][ap1_network["SSID"]]
+ ap2_network["bssid"] = self.bssid_map[1]["5g"][ap2_network["SSID"]]
+ self.roaming_from_AP1_and_AP2(ap1_network, ap2_network)
+
+ @test_tracker_info(uuid="eabc7319-d962-4bef-b679-725e9ff00420")
+ def test_roaming_between_AP1_to_AP2_psk_2g(self):
+ ap1_network = self.reference_networks[0]["2g"]
+ ap2_network = self.reference_networks[1]["2g"]
+ if "OpenWrtAP" in self.user_params:
+ ap1_network["bssid"] = self.bssid_map[0]["2g"][ap1_network["SSID"]]
+ ap2_network["bssid"] = self.bssid_map[1]["2g"][ap2_network["SSID"]]
+ self.roaming_from_AP1_and_AP2(ap1_network, ap2_network)
+
+ @test_tracker_info(uuid="1cf9c681-4ff0-45c1-9719-f01629f6a7f7")
+ def test_roaming_between_AP1_to_AP2_psk_5g(self):
+ ap1_network = self.reference_networks[0]["5g"]
+ ap2_network = self.reference_networks[1]["5g"]
+ if "OpenWrtAP" in self.user_params:
+ ap1_network["bssid"] = self.bssid_map[0]["5g"][ap1_network["SSID"]]
+ ap2_network["bssid"] = self.bssid_map[1]["5g"][ap2_network["SSID"]]
+ self.roaming_from_AP1_and_AP2(ap1_network, ap2_network)
+
+ @test_tracker_info(uuid="a28f7d2e-fae4-4e66-b633-7ee59f8b46e0")
+ def test_roaming_between_AP1_to_AP2_owe_2g(self):
+ ap1_network = self.owe_networks[0]["2g"]
+ ap2_network = self.owe_networks[1]["2g"]
+ if "OpenWrtAP" in self.user_params:
+ ap1_network["bssid"] = self.bssid_map[0]["2g"][ap1_network["SSID"]]
+ ap2_network["bssid"] = self.bssid_map[1]["2g"][ap2_network["SSID"]]
+ self.roaming_from_AP1_and_AP2(ap1_network, ap2_network)
+
+ @test_tracker_info(uuid="3c39110a-9336-4abd-b885-acbba85dc10d")
+ def test_roaming_between_AP1_to_AP2_owe_5g(self):
+ ap1_network = self.owe_networks[0]["5g"]
+ ap2_network = self.owe_networks[1]["5g"]
+ if "OpenWrtAP" in self.user_params:
+ ap1_network["bssid"] = self.bssid_map[0]["5g"][ap1_network["SSID"]]
+ ap2_network["bssid"] = self.bssid_map[1]["5g"][ap2_network["SSID"]]
+ self.roaming_from_AP1_and_AP2(ap1_network, ap2_network)
+
+ @test_tracker_info(uuid="68b2baf6-162a-44f2-a00d-4973e5ac9471")
+ def test_roaming_between_AP1_to_AP2_sae_2g(self):
+ ap1_network = self.sae_networks[0]["2g"]
+ ap2_network = self.sae_networks[1]["2g"]
+ if "OpenWrtAP" in self.user_params:
+ ap1_network["bssid"] = self.bssid_map[0]["2g"][ap1_network["SSID"]]
+ ap2_network["bssid"] = self.bssid_map[1]["2g"][ap2_network["SSID"]]
+ self.roaming_from_AP1_and_AP2(ap1_network, ap2_network)
+
+ @test_tracker_info(uuid="20e24ed3-0cd1-46dd-bd26-2183ffb443e6")
+ def test_roaming_between_AP1_to_AP2_sae_5g(self):
+ ap1_network = self.sae_networks[0]["5g"]
+ ap2_network = self.sae_networks[1]["5g"]
+ if "OpenWrtAP" in self.user_params:
+ ap1_network["bssid"] = self.bssid_map[0]["5g"][ap1_network["SSID"]]
+ ap2_network["bssid"] = self.bssid_map[1]["5g"][ap2_network["SSID"]]
+ self.roaming_from_AP1_and_AP2(ap1_network, ap2_network)
+
+ @test_tracker_info(uuid="3114d625-5cdd-4205-bb46-5a9d057dc80d")
+ def test_roaming_fail_psk_2g(self):
+ network = {'SSID':'test_roaming_fail', 'password':'roam123456@'}
+ # AP2 network with incorrect password.
+ network_fail = {'SSID':'test_roaming_fail', 'password':'roam123456@#$%^'}
+ # Setup AP1 with the correct password.
+ wutils.ap_setup(self, 0, self.access_points[0], network)
+ network_bssid = self.access_points[0].get_bssid_from_ssid(
+ network["SSID"], '2g')
+ # Setup AP2 with the incorrect password.
+ wutils.ap_setup(self, 1, self.access_points[1], network_fail)
+ network_fail_bssid = self.access_points[1].get_bssid_from_ssid(
+ network_fail["SSID"], '2g')
+ network['bssid'] = network_bssid
+ network_fail['bssid'] = network_fail_bssid
+ try:
+ # Initiate roaming with AP2 configured with incorrect password.
+ self.roaming_from_AP1_and_AP2(network, network_fail)
+ except:
+ self.log.info("Roaming failed to AP2 with incorrect password.")
+ # Re-configure AP2 after roaming failed, with correct password.
+ self.log.info("Re-configuring AP2 with correct password.")
+ wutils.ap_setup(self, 1, self.access_points[1], network)
+ self.roaming_from_AP1_and_AP2(network, network_fail)
diff --git a/acts_tests/tests/google/wifi/WifiRssiTest.py b/acts_tests/tests/google/wifi/WifiRssiTest.py
new file mode 100644
index 0000000..9ff9afa
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiRssiTest.py
@@ -0,0 +1,1074 @@
+#!/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 collections
+import itertools
+import json
+import logging
+import math
+import numpy
+import os
+import statistics
+from acts import asserts
+from acts import base_test
+from acts import context
+from acts import utils
+from acts.controllers.utils_lib import ssh
+from acts.controllers import iperf_server as ipf
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
+from acts.test_utils.wifi import ota_chamber
+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 acts.test_utils.wifi import wifi_test_utils as wutils
+from concurrent.futures import ThreadPoolExecutor
+from functools import partial
+
+SHORT_SLEEP = 1
+MED_SLEEP = 6
+CONST_3dB = 3.01029995664
+RSSI_ERROR_VAL = float('nan')
+
+
+class WifiRssiTest(base_test.BaseTestClass):
+ """Class to test WiFi RSSI reporting.
+
+ This class tests RSSI reporting on android devices. The class tests RSSI
+ accuracy by checking RSSI over a large attenuation range, checks for RSSI
+ stability over time when attenuation is fixed, and checks that RSSI quickly
+ and reacts to changes attenuation by checking RSSI trajectories over
+ configurable attenuation waveforms.For an example config file to run this
+ test class see example_connectivity_performance_ap_sta.json.
+ """
+ def __init__(self, controllers):
+ base_test.BaseTestClass.__init__(self, controllers)
+ self.testcase_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_case())
+ self.testclass_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_class())
+ self.publish_test_metrics = True
+
+ def setup_class(self):
+ self.dut = self.android_devices[0]
+ req_params = [
+ 'RemoteServer', 'RetailAccessPoints', 'rssi_test_params',
+ 'main_network', 'testbed_params'
+ ]
+ self.unpack_userparams(req_params)
+ self.testclass_params = self.rssi_test_params
+ self.num_atten = self.attenuators[0].instrument.num_atten
+ self.iperf_server = self.iperf_servers[0]
+ self.iperf_client = self.iperf_clients[0]
+ self.remote_server = ssh.connection.SshConnection(
+ ssh.settings.from_config(self.RemoteServer[0]['ssh_config']))
+ self.access_point = retail_ap.create(self.RetailAccessPoints)[0]
+ self.log_path = os.path.join(logging.log_path, 'results')
+ os.makedirs(self.log_path, exist_ok=True)
+ self.log.info('Access Point Configuration: {}'.format(
+ self.access_point.ap_settings))
+ self.testclass_results = []
+
+ # 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)
+
+ def teardown_test(self):
+ self.iperf_server.stop()
+
+ def pass_fail_check_rssi_stability(self, testcase_params,
+ postprocessed_results):
+ """Check the test result and decide if it passed or failed.
+
+ Checks the RSSI test result and fails the test if the standard
+ deviation of signal_poll_rssi is beyond the threshold defined in the
+ config file.
+
+ Args:
+ testcase_params: dict containing test-specific parameters
+ postprocessed_results: compiled arrays of RSSI measurements
+ """
+ # Set Blackbox metric values
+ if self.publish_test_metrics:
+ self.testcase_metric_logger.add_metric(
+ 'signal_poll_rssi_stdev',
+ max(postprocessed_results['signal_poll_rssi']['stdev']))
+ self.testcase_metric_logger.add_metric(
+ 'chain_0_rssi_stdev',
+ max(postprocessed_results['chain_0_rssi']['stdev']))
+ self.testcase_metric_logger.add_metric(
+ 'chain_1_rssi_stdev',
+ max(postprocessed_results['chain_1_rssi']['stdev']))
+
+ # Evaluate test pass/fail
+ test_failed = any([
+ stdev > self.testclass_params['stdev_tolerance']
+ for stdev in postprocessed_results['signal_poll_rssi']['stdev']
+ ])
+ test_message = (
+ 'RSSI stability {0}. Standard deviation was {1} dB '
+ '(limit {2}), per chain standard deviation [{3}, {4}] dB'.format(
+ 'failed' * test_failed + 'passed' * (not test_failed), [
+ float('{:.2f}'.format(x))
+ for x in postprocessed_results['signal_poll_rssi']['stdev']
+ ], self.testclass_params['stdev_tolerance'], [
+ float('{:.2f}'.format(x))
+ for x in postprocessed_results['chain_0_rssi']['stdev']
+ ], [
+ float('{:.2f}'.format(x))
+ for x in postprocessed_results['chain_1_rssi']['stdev']
+ ]))
+ if test_failed:
+ asserts.fail(test_message)
+ asserts.explicit_pass(test_message)
+
+ def pass_fail_check_rssi_accuracy(self, testcase_params,
+ postprocessed_results):
+ """Check the test result and decide if it passed or failed.
+
+ Checks the RSSI test result and compares and compute its deviation from
+ the predicted RSSI. This computation is done for all reported RSSI
+ values. The test fails if any of the RSSI values specified in
+ rssi_under_test have an average error beyond what is specified in the
+ configuration file.
+
+ Args:
+ postprocessed_results: compiled arrays of RSSI measurements
+ testcase_params: dict containing params such as list of RSSIs under
+ test, i.e., can cause test to fail and boolean indicating whether
+ to look at absolute RSSI accuracy, or centered RSSI accuracy.
+ Centered accuracy is computed after systematic RSSI shifts are
+ removed.
+ """
+ test_failed = False
+ test_message = ''
+ if testcase_params['absolute_accuracy']:
+ error_type = 'absolute'
+ else:
+ error_type = 'centered'
+
+ for key, val in postprocessed_results.items():
+ # Compute the error metrics ignoring invalid RSSI readings
+ # If all readings invalid, set error to RSSI_ERROR_VAL
+ if 'rssi' in key and 'predicted' not in key:
+ filtered_error = [x for x in val['error'] if not math.isnan(x)]
+ if filtered_error:
+ avg_shift = statistics.mean(filtered_error)
+ if testcase_params['absolute_accuracy']:
+ avg_error = statistics.mean(
+ [abs(x) for x in filtered_error])
+ else:
+ avg_error = statistics.mean(
+ [abs(x - avg_shift) for x in filtered_error])
+ else:
+ avg_error = RSSI_ERROR_VAL
+ avg_shift = RSSI_ERROR_VAL
+ # Set Blackbox metric values
+ if self.publish_test_metrics:
+ self.testcase_metric_logger.add_metric(
+ '{}_error'.format(key), avg_error)
+ self.testcase_metric_logger.add_metric(
+ '{}_shift'.format(key), avg_shift)
+ # Evaluate test pass/fail
+ rssi_failure = (avg_error >
+ self.testclass_params['abs_tolerance']
+ ) or math.isnan(avg_error)
+ if rssi_failure and key in testcase_params['rssi_under_test']:
+ test_message = test_message + (
+ '{} failed ({} error = {:.2f} dB, '
+ 'shift = {:.2f} dB)\n').format(key, error_type,
+ avg_error, avg_shift)
+ test_failed = True
+ elif rssi_failure:
+ test_message = test_message + (
+ '{} failed (ignored) ({} error = {:.2f} dB, '
+ 'shift = {:.2f} dB)\n').format(key, error_type,
+ avg_error, avg_shift)
+ else:
+ test_message = test_message + (
+ '{} passed ({} error = {:.2f} dB, '
+ 'shift = {:.2f} dB)\n').format(key, error_type,
+ avg_error, avg_shift)
+ if test_failed:
+ asserts.fail(test_message)
+ asserts.explicit_pass(test_message)
+
+ def post_process_rssi_sweep(self, rssi_result):
+ """Postprocesses and saves JSON formatted results.
+
+ Args:
+ rssi_result: dict containing attenuation, rssi and other meta
+ data
+ Returns:
+ postprocessed_results: compiled arrays of RSSI data used in
+ pass/fail check
+ """
+ # Save output as text file
+ results_file_path = os.path.join(self.log_path, self.current_test_name)
+ with open(results_file_path, 'w') as results_file:
+ json.dump(rssi_result, results_file, indent=4)
+ # Compile results into arrays of RSSIs suitable for plotting
+ # yapf: disable
+ postprocessed_results = collections.OrderedDict(
+ [('signal_poll_rssi', {}),
+ ('signal_poll_avg_rssi', {}),
+ ('scan_rssi', {}),
+ ('chain_0_rssi', {}),
+ ('chain_1_rssi', {}),
+ ('total_attenuation', []),
+ ('predicted_rssi', [])])
+ # yapf: enable
+ for key, val in postprocessed_results.items():
+ if 'scan_rssi' in key:
+ postprocessed_results[key]['data'] = [
+ x for data_point in rssi_result['rssi_result'] for x in
+ data_point[key][rssi_result['connected_bssid']]['data']
+ ]
+ postprocessed_results[key]['mean'] = [
+ x[key][rssi_result['connected_bssid']]['mean']
+ for x in rssi_result['rssi_result']
+ ]
+ postprocessed_results[key]['stdev'] = [
+ x[key][rssi_result['connected_bssid']]['stdev']
+ for x in rssi_result['rssi_result']
+ ]
+ elif 'predicted_rssi' in key:
+ postprocessed_results['total_attenuation'] = [
+ att + rssi_result['fixed_attenuation'] +
+ rssi_result['dut_front_end_loss']
+ for att in rssi_result['attenuation']
+ ]
+ postprocessed_results['predicted_rssi'] = [
+ rssi_result['ap_tx_power'] - att
+ for att in postprocessed_results['total_attenuation']
+ ]
+ elif 'rssi' in key:
+ postprocessed_results[key]['data'] = [
+ x for data_point in rssi_result['rssi_result']
+ for x in data_point[key]['data']
+ ]
+ postprocessed_results[key]['mean'] = [
+ x[key]['mean'] for x in rssi_result['rssi_result']
+ ]
+ postprocessed_results[key]['stdev'] = [
+ x[key]['stdev'] for x in rssi_result['rssi_result']
+ ]
+ # Compute RSSI errors
+ for key, val in postprocessed_results.items():
+ if 'chain' in key:
+ postprocessed_results[key]['error'] = [
+ postprocessed_results[key]['mean'][idx] + CONST_3dB -
+ postprocessed_results['predicted_rssi'][idx]
+ for idx in range(
+ len(postprocessed_results['predicted_rssi']))
+ ]
+ elif 'rssi' in key and 'predicted' not in key:
+ postprocessed_results[key]['error'] = [
+ postprocessed_results[key]['mean'][idx] -
+ postprocessed_results['predicted_rssi'][idx]
+ for idx in range(
+ len(postprocessed_results['predicted_rssi']))
+ ]
+ return postprocessed_results
+
+ def plot_rssi_vs_attenuation(self, postprocessed_results):
+ """Function to plot RSSI vs attenuation sweeps
+
+ Args:
+ postprocessed_results: compiled arrays of RSSI data.
+ """
+ figure = wputils.BokehFigure(self.current_test_name,
+ x_label='Attenuation (dB)',
+ primary_y_label='RSSI (dBm)')
+ figure.add_line(postprocessed_results['total_attenuation'],
+ postprocessed_results['signal_poll_rssi']['mean'],
+ 'Signal Poll RSSI',
+ marker='circle')
+ figure.add_line(postprocessed_results['total_attenuation'],
+ postprocessed_results['scan_rssi']['mean'],
+ 'Scan RSSI',
+ marker='circle')
+ figure.add_line(postprocessed_results['total_attenuation'],
+ postprocessed_results['chain_0_rssi']['mean'],
+ 'Chain 0 RSSI',
+ marker='circle')
+ figure.add_line(postprocessed_results['total_attenuation'],
+ postprocessed_results['chain_1_rssi']['mean'],
+ 'Chain 1 RSSI',
+ marker='circle')
+ figure.add_line(postprocessed_results['total_attenuation'],
+ postprocessed_results['predicted_rssi'],
+ 'Predicted RSSI',
+ marker='circle')
+
+ output_file_path = os.path.join(self.log_path,
+ self.current_test_name + '.html')
+ figure.generate_figure(output_file_path)
+
+ def plot_rssi_vs_time(self, rssi_result, postprocessed_results,
+ center_curves):
+ """Function to plot RSSI vs time.
+
+ Args:
+ rssi_result: dict containing raw RSSI data
+ postprocessed_results: compiled arrays of RSSI data
+ center_curvers: boolean indicating whether to shift curves to align
+ them with predicted RSSIs
+ """
+ figure = wputils.BokehFigure(
+ self.current_test_name,
+ x_label='Time (s)',
+ primary_y_label=center_curves * 'Centered' + 'RSSI (dBm)',
+ )
+
+ # yapf: disable
+ rssi_time_series = collections.OrderedDict(
+ [('signal_poll_rssi', []),
+ ('signal_poll_avg_rssi', []),
+ ('scan_rssi', []),
+ ('chain_0_rssi', []),
+ ('chain_1_rssi', []),
+ ('predicted_rssi', [])])
+ # yapf: enable
+ for key, val in rssi_time_series.items():
+ if 'predicted_rssi' in key:
+ rssi_time_series[key] = [
+ x for x in postprocessed_results[key] for copies in range(
+ len(rssi_result['rssi_result'][0]['signal_poll_rssi']
+ ['data']))
+ ]
+ elif 'rssi' in key:
+ if center_curves:
+ filtered_error = [
+ x for x in postprocessed_results[key]['error']
+ if not math.isnan(x)
+ ]
+ if filtered_error:
+ avg_shift = statistics.mean(filtered_error)
+ else:
+ avg_shift = 0
+ rssi_time_series[key] = [
+ x - avg_shift
+ for x in postprocessed_results[key]['data']
+ ]
+ else:
+ rssi_time_series[key] = postprocessed_results[key]['data']
+ time_vec = [
+ self.testclass_params['polling_frequency'] * x
+ for x in range(len(rssi_time_series[key]))
+ ]
+ if len(rssi_time_series[key]) > 0:
+ figure.add_line(time_vec, rssi_time_series[key], key)
+
+ output_file_path = os.path.join(self.log_path,
+ self.current_test_name + '.html')
+ figure.generate_figure(output_file_path)
+
+ def plot_rssi_distribution(self, postprocessed_results):
+ """Function to plot RSSI distributions.
+
+ Args:
+ postprocessed_results: compiled arrays of RSSI data
+ """
+ monitored_rssis = ['signal_poll_rssi', 'chain_0_rssi', 'chain_1_rssi']
+
+ rssi_dist = collections.OrderedDict()
+ for rssi_key in monitored_rssis:
+ rssi_data = postprocessed_results[rssi_key]
+ rssi_dist[rssi_key] = collections.OrderedDict()
+ unique_rssi = sorted(set(rssi_data['data']))
+ rssi_counts = []
+ for value in unique_rssi:
+ rssi_counts.append(rssi_data['data'].count(value))
+ total_count = sum(rssi_counts)
+ rssi_dist[rssi_key]['rssi_values'] = unique_rssi
+ rssi_dist[rssi_key]['rssi_pdf'] = [
+ x / total_count for x in rssi_counts
+ ]
+ rssi_dist[rssi_key]['rssi_cdf'] = []
+ cum_prob = 0
+ for prob in rssi_dist[rssi_key]['rssi_pdf']:
+ cum_prob += prob
+ rssi_dist[rssi_key]['rssi_cdf'].append(cum_prob)
+
+ figure = wputils.BokehFigure(self.current_test_name,
+ x_label='RSSI (dBm)',
+ primary_y_label='p(RSSI = x)',
+ secondary_y_label='p(RSSI <= x)')
+ for rssi_key, rssi_data in rssi_dist.items():
+ figure.add_line(x_data=rssi_data['rssi_values'],
+ y_data=rssi_data['rssi_pdf'],
+ legend='{} PDF'.format(rssi_key),
+ y_axis='default')
+ figure.add_line(x_data=rssi_data['rssi_values'],
+ y_data=rssi_data['rssi_cdf'],
+ legend='{} CDF'.format(rssi_key),
+ y_axis='secondary')
+ output_file_path = os.path.join(self.log_path,
+ self.current_test_name + '_dist.html')
+ figure.generate_figure(output_file_path)
+
+ def run_rssi_test(self, testcase_params):
+ """Test function to run RSSI tests.
+
+ The function runs an RSSI test in the current device/AP configuration.
+ Function is called from another wrapper function that sets up the
+ testbed for the RvR test
+
+ Args:
+ testcase_params: dict containing test-specific parameters
+ Returns:
+ rssi_result: dict containing rssi_result and meta data
+ """
+ # Run test and log result
+ rssi_result = collections.OrderedDict()
+ rssi_result['test_name'] = self.current_test_name
+ rssi_result['testcase_params'] = testcase_params
+ rssi_result['ap_settings'] = self.access_point.ap_settings.copy()
+ rssi_result['attenuation'] = list(testcase_params['rssi_atten_range'])
+ rssi_result['connected_bssid'] = self.main_network[
+ testcase_params['band']].get('BSSID', '00:00:00:00')
+ channel_mode_combo = '{}_{}'.format(str(testcase_params['channel']),
+ testcase_params['mode'])
+ channel_str = str(testcase_params['channel'])
+ if channel_mode_combo in self.testbed_params['ap_tx_power']:
+ rssi_result['ap_tx_power'] = self.testbed_params['ap_tx_power'][
+ channel_mode_combo]
+ else:
+ rssi_result['ap_tx_power'] = self.testbed_params['ap_tx_power'][
+ str(testcase_params['channel'])]
+ rssi_result['fixed_attenuation'] = self.testbed_params[
+ 'fixed_attenuation'][channel_str]
+ rssi_result['dut_front_end_loss'] = self.testbed_params[
+ 'dut_front_end_loss'][channel_str]
+
+ self.log.info('Start running RSSI test.')
+ rssi_result['rssi_result'] = []
+ rssi_result['llstats'] = []
+ llstats_obj = wputils.LinkLayerStats(self.dut)
+ # Start iperf traffic if required by test
+ if testcase_params['active_traffic'] and testcase_params[
+ 'traffic_type'] == 'iperf':
+ self.iperf_server.start(tag=0)
+ if isinstance(self.iperf_server, ipf.IPerfServerOverAdb):
+ iperf_server_address = self.dut_ip
+ else:
+ iperf_server_address = wputils.get_server_address(
+ self.remote_server, self.dut_ip, '255.255.255.0')
+ executor = ThreadPoolExecutor(max_workers=1)
+ thread_future = executor.submit(
+ self.iperf_client.start, iperf_server_address,
+ testcase_params['iperf_args'], 0,
+ testcase_params['traffic_timeout'] + SHORT_SLEEP)
+ executor.shutdown(wait=False)
+ elif testcase_params['active_traffic'] and testcase_params[
+ 'traffic_type'] == 'ping':
+ thread_future = wputils.get_ping_stats_nb(
+ self.remote_server, self.dut_ip,
+ testcase_params['traffic_timeout'], 0.02, 64)
+ else:
+ thread_future = wputils.get_ping_stats_nb(
+ self.remote_server, self.dut_ip,
+ testcase_params['traffic_timeout'], 0.5, 64)
+ for atten in testcase_params['rssi_atten_range']:
+ # Set Attenuation
+ self.log.info('Setting attenuation to {} dB'.format(atten))
+ for attenuator in self.attenuators:
+ attenuator.set_atten(atten)
+ llstats_obj.update_stats()
+ current_rssi = collections.OrderedDict()
+ current_rssi = wputils.get_connected_rssi(
+ self.dut, testcase_params['connected_measurements'],
+ self.testclass_params['polling_frequency'],
+ testcase_params['first_measurement_delay'])
+ current_rssi['scan_rssi'] = wputils.get_scan_rssi(
+ self.dut, testcase_params['tracked_bssid'],
+ testcase_params['scan_measurements'])
+ rssi_result['rssi_result'].append(current_rssi)
+ llstats_obj.update_stats()
+ curr_llstats = llstats_obj.llstats_incremental.copy()
+ rssi_result['llstats'].append(curr_llstats)
+ self.log.info(
+ 'Connected RSSI at {0:.2f} dB is {1:.2f} [{2:.2f}, {3:.2f}] dB'
+ .format(atten, current_rssi['signal_poll_rssi']['mean'],
+ current_rssi['chain_0_rssi']['mean'],
+ current_rssi['chain_1_rssi']['mean']))
+ # Stop iperf traffic if needed
+ for attenuator in self.attenuators:
+ attenuator.set_atten(0)
+ thread_future.result()
+ if testcase_params['active_traffic'] and testcase_params[
+ 'traffic_type'] == 'iperf':
+ self.iperf_server.stop()
+ return rssi_result
+
+ def setup_ap(self, testcase_params):
+ """Function that gets devices ready for the test.
+
+ Args:
+ testcase_params: dict containing test-specific parameters
+ """
+ if '2G' in testcase_params['band']:
+ frequency = wutils.WifiEnums.channel_2G_to_freq[
+ testcase_params['channel']]
+ else:
+ frequency = wutils.WifiEnums.channel_5G_to_freq[
+ testcase_params['channel']]
+ if frequency in wutils.WifiEnums.DFS_5G_FREQUENCIES:
+ self.access_point.set_region(self.testbed_params['DFS_region'])
+ else:
+ self.access_point.set_region(self.testbed_params['default_region'])
+ self.access_point.set_channel(testcase_params['band'],
+ testcase_params['channel'])
+ self.access_point.set_bandwidth(testcase_params['band'],
+ testcase_params['mode'])
+ self.log.info('Access Point Configuration: {}'.format(
+ self.access_point.ap_settings))
+
+ def setup_dut(self, testcase_params):
+ """Sets up the DUT in the configuration required by the test."""
+ # Check battery level before test
+ if not wputils.health_check(self.dut, 10):
+ asserts.skip('Battery level too low. Skipping test.')
+ # Turn screen off to preserve battery
+ self.dut.go_to_sleep()
+ if wputils.validate_network(self.dut,
+ testcase_params['test_network']['SSID']):
+ self.log.info('Already connected to desired network')
+ else:
+ wutils.wifi_toggle_state(self.dut, True)
+ wutils.reset_wifi(self.dut)
+ self.main_network[testcase_params['band']][
+ 'channel'] = testcase_params['channel']
+ wutils.set_wifi_country_code(self.dut,
+ self.testclass_params['country_code'])
+ wutils.wifi_connect(self.dut,
+ self.main_network[testcase_params['band']],
+ num_of_tries=5)
+ self.dut_ip = self.dut.droid.connectivityGetIPv4Addresses('wlan0')[0]
+
+ def setup_rssi_test(self, testcase_params):
+ """Main function to test RSSI.
+
+ The function sets up the AP in the correct channel and mode
+ configuration and called rssi_test to sweep attenuation and measure
+ RSSI
+
+ Args:
+ testcase_params: dict containing test-specific parameters
+ Returns:
+ rssi_result: dict containing rssi_results and meta data
+ """
+ # Configure AP
+ self.setup_ap(testcase_params)
+ # Initialize attenuators
+ for attenuator in self.attenuators:
+ attenuator.set_atten(testcase_params['rssi_atten_range'][0])
+ # Connect DUT to Network
+ self.setup_dut(testcase_params)
+
+ def get_traffic_timeout(self, testcase_params):
+ """Function to comput iperf session length required in RSSI test.
+
+ Args:
+ testcase_params: dict containing test-specific parameters
+ Returns:
+ traffic_timeout: length of iperf session required in rssi test
+ """
+ atten_step_duration = testcase_params['first_measurement_delay'] + (
+ testcase_params['connected_measurements'] *
+ self.testclass_params['polling_frequency']
+ ) + testcase_params['scan_measurements'] * MED_SLEEP
+ timeout = len(testcase_params['rssi_atten_range']
+ ) * atten_step_duration + MED_SLEEP
+ return timeout
+
+ def compile_rssi_vs_atten_test_params(self, testcase_params):
+ """Function to complete compiling test-specific parameters
+
+ Args:
+ testcase_params: dict containing test-specific parameters
+ """
+ testcase_params.update(
+ connected_measurements=self.
+ testclass_params['rssi_vs_atten_connected_measurements'],
+ scan_measurements=self.
+ testclass_params['rssi_vs_atten_scan_measurements'],
+ first_measurement_delay=MED_SLEEP,
+ rssi_under_test=self.testclass_params['rssi_vs_atten_metrics'],
+ absolute_accuracy=1)
+
+ testcase_params['band'] = self.access_point.band_lookup_by_channel(
+ testcase_params['channel'])
+ testcase_params['test_network'] = self.main_network[
+ testcase_params['band']]
+ testcase_params['tracked_bssid'] = [
+ self.main_network[testcase_params['band']].get(
+ 'BSSID', '00:00:00:00')
+ ]
+
+ num_atten_steps = int((self.testclass_params['rssi_vs_atten_stop'] -
+ self.testclass_params['rssi_vs_atten_start']) /
+ self.testclass_params['rssi_vs_atten_step'])
+ testcase_params['rssi_atten_range'] = [
+ self.testclass_params['rssi_vs_atten_start'] +
+ x * self.testclass_params['rssi_vs_atten_step']
+ for x in range(0, num_atten_steps)
+ ]
+ testcase_params['traffic_timeout'] = self.get_traffic_timeout(
+ testcase_params)
+
+ if isinstance(self.iperf_server, ipf.IPerfServerOverAdb):
+ testcase_params['iperf_args'] = '-i 1 -t {} -J'.format(
+ testcase_params['traffic_timeout'])
+ else:
+ testcase_params['iperf_args'] = '-i 1 -t {} -J -R'.format(
+ testcase_params['traffic_timeout'])
+ return testcase_params
+
+ def compile_rssi_stability_test_params(self, testcase_params):
+ """Function to complete compiling test-specific parameters
+
+ Args:
+ testcase_params: dict containing test-specific parameters
+ """
+ testcase_params.update(
+ connected_measurements=int(
+ self.testclass_params['rssi_stability_duration'] /
+ self.testclass_params['polling_frequency']),
+ scan_measurements=0,
+ first_measurement_delay=MED_SLEEP,
+ rssi_atten_range=self.testclass_params['rssi_stability_atten'])
+ testcase_params['band'] = self.access_point.band_lookup_by_channel(
+ testcase_params['channel'])
+ testcase_params['test_network'] = self.main_network[
+ testcase_params['band']]
+ testcase_params['tracked_bssid'] = [
+ self.main_network[testcase_params['band']].get(
+ 'BSSID', '00:00:00:00')
+ ]
+
+ testcase_params['traffic_timeout'] = self.get_traffic_timeout(
+ testcase_params)
+ if isinstance(self.iperf_server, ipf.IPerfServerOverAdb):
+ testcase_params['iperf_args'] = '-i 1 -t {} -J'.format(
+ testcase_params['traffic_timeout'])
+ else:
+ testcase_params['iperf_args'] = '-i 1 -t {} -J -R'.format(
+ testcase_params['traffic_timeout'])
+ return testcase_params
+
+ def compile_rssi_tracking_test_params(self, testcase_params):
+ """Function to complete compiling test-specific parameters
+
+ Args:
+ testcase_params: dict containing test-specific parameters
+ """
+ testcase_params.update(connected_measurements=int(
+ 1 / self.testclass_params['polling_frequency']),
+ scan_measurements=0,
+ first_measurement_delay=0,
+ rssi_under_test=['signal_poll_rssi'],
+ absolute_accuracy=0)
+ testcase_params['band'] = self.access_point.band_lookup_by_channel(
+ testcase_params['channel'])
+ testcase_params['test_network'] = self.main_network[
+ testcase_params['band']]
+ testcase_params['tracked_bssid'] = [
+ self.main_network[testcase_params['band']].get(
+ 'BSSID', '00:00:00:00')
+ ]
+
+ rssi_atten_range = []
+ for waveform in self.testclass_params['rssi_tracking_waveforms']:
+ waveform_vector = []
+ for section in range(len(waveform['atten_levels']) - 1):
+ section_limits = waveform['atten_levels'][section:section + 2]
+ up_down = (1 - 2 * (section_limits[1] < section_limits[0]))
+ temp_section = list(
+ range(section_limits[0], section_limits[1] + up_down,
+ up_down * waveform['step_size']))
+ temp_section = [
+ temp_section[idx] for idx in range(len(temp_section))
+ for n in range(waveform['step_duration'])
+ ]
+ waveform_vector += temp_section
+ waveform_vector = waveform_vector * waveform['repetitions']
+ rssi_atten_range = rssi_atten_range + waveform_vector
+ testcase_params['rssi_atten_range'] = rssi_atten_range
+ testcase_params['traffic_timeout'] = self.get_traffic_timeout(
+ testcase_params)
+
+ if isinstance(self.iperf_server, ipf.IPerfServerOverAdb):
+ testcase_params['iperf_args'] = '-i 1 -t {} -J'.format(
+ testcase_params['traffic_timeout'])
+ else:
+ testcase_params['iperf_args'] = '-i 1 -t {} -J -R'.format(
+ testcase_params['traffic_timeout'])
+ return testcase_params
+
+ def _test_rssi_vs_atten(self, testcase_params):
+ """Function that gets called for each test case of rssi_vs_atten
+
+ The function gets called in each rssi test case. The function
+ customizes the test based on the test name of the test that called it
+
+ Args:
+ testcase_params: dict containing test-specific parameters
+ """
+ testcase_params = self.compile_rssi_vs_atten_test_params(
+ testcase_params)
+
+ self.setup_rssi_test(testcase_params)
+ rssi_result = self.run_rssi_test(testcase_params)
+ rssi_result['postprocessed_results'] = self.post_process_rssi_sweep(
+ rssi_result)
+ self.testclass_results.append(rssi_result)
+ self.plot_rssi_vs_attenuation(rssi_result['postprocessed_results'])
+ self.pass_fail_check_rssi_accuracy(
+ testcase_params, rssi_result['postprocessed_results'])
+
+ def _test_rssi_stability(self, testcase_params):
+ """ Function that gets called for each test case of rssi_stability
+
+ The function gets called in each stability test case. The function
+ customizes test based on the test name of the test that called it
+ """
+ testcase_params = self.compile_rssi_stability_test_params(
+ testcase_params)
+
+ self.setup_rssi_test(testcase_params)
+ rssi_result = self.run_rssi_test(testcase_params)
+ rssi_result['postprocessed_results'] = self.post_process_rssi_sweep(
+ rssi_result)
+ self.testclass_results.append(rssi_result)
+ self.plot_rssi_vs_time(rssi_result,
+ rssi_result['postprocessed_results'], 1)
+ self.plot_rssi_distribution(rssi_result['postprocessed_results'])
+ self.pass_fail_check_rssi_stability(
+ testcase_params, rssi_result['postprocessed_results'])
+
+ def _test_rssi_tracking(self, testcase_params):
+ """ Function that gets called for each test case of rssi_tracking
+
+ The function gets called in each rssi test case. The function
+ customizes the test based on the test name of the test that called it
+ """
+ testcase_params = self.compile_rssi_tracking_test_params(
+ testcase_params)
+
+ self.setup_rssi_test(testcase_params)
+ rssi_result = self.run_rssi_test(testcase_params)
+ rssi_result['postprocessed_results'] = self.post_process_rssi_sweep(
+ rssi_result)
+ self.testclass_results.append(rssi_result)
+ self.plot_rssi_vs_time(rssi_result,
+ rssi_result['postprocessed_results'], 1)
+ self.pass_fail_check_rssi_accuracy(
+ testcase_params, rssi_result['postprocessed_results'])
+
+ def generate_test_cases(self, test_types, channels, modes, traffic_modes):
+ """Function that auto-generates test cases for a test class."""
+ test_cases = []
+ allowed_configs = {
+ 'VHT20': [
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 149, 153,
+ 157, 161
+ ],
+ 'VHT40': [36, 44, 149, 157],
+ 'VHT80': [36, 149]
+ }
+
+ for channel, mode, traffic_mode, test_type in itertools.product(
+ channels, modes, traffic_modes, test_types):
+ if channel not in allowed_configs[mode]:
+ continue
+ test_name = test_type + '_ch{}_{}_{}'.format(
+ channel, mode, traffic_mode)
+ testcase_params = collections.OrderedDict(
+ channel=channel,
+ mode=mode,
+ active_traffic=(traffic_mode == 'ActiveTraffic'),
+ traffic_type=self.user_params['rssi_test_params']
+ ['traffic_type'],
+ )
+ test_function = getattr(self, '_{}'.format(test_type))
+ setattr(self, test_name, partial(test_function, testcase_params))
+ test_cases.append(test_name)
+ return test_cases
+
+
+class WifiRssi_2GHz_ActiveTraffic_Test(WifiRssiTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases(
+ ['test_rssi_stability', 'test_rssi_vs_atten'], [1, 2, 6, 10, 11],
+ ['VHT20'], ['ActiveTraffic'])
+
+
+class WifiRssi_5GHz_ActiveTraffic_Test(WifiRssiTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases(
+ ['test_rssi_stability', 'test_rssi_vs_atten'],
+ [36, 40, 44, 48, 149, 153, 157, 161], ['VHT20', 'VHT40', 'VHT80'],
+ ['ActiveTraffic'])
+
+
+class WifiRssi_AllChannels_ActiveTraffic_Test(WifiRssiTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases(
+ ['test_rssi_stability', 'test_rssi_vs_atten'],
+ [1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161],
+ ['VHT20', 'VHT40', 'VHT80'], ['ActiveTraffic'])
+
+
+class WifiRssi_SampleChannels_NoTraffic_Test(WifiRssiTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases(
+ ['test_rssi_stability', 'test_rssi_vs_atten'], [6, 36, 149],
+ ['VHT20', 'VHT40', 'VHT80'], ['NoTraffic'])
+
+
+class WifiRssiTrackingTest(WifiRssiTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases(['test_rssi_tracking'],
+ [6, 36, 149],
+ ['VHT20', 'VHT40', 'VHT80'],
+ ['ActiveTraffic', 'NoTraffic'])
+
+
+# Over-the air version of RSSI tests
+class WifiOtaRssiTest(WifiRssiTest):
+ """Class to test over-the-air rssi tests.
+
+ This class implements measures WiFi RSSI tests in an OTA chamber.
+ It allows setting orientation and other chamber parameters to study
+ performance in varying channel conditions
+ """
+ def __init__(self, controllers):
+ base_test.BaseTestClass.__init__(self, controllers)
+ self.testcase_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_case())
+ self.testclass_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_class())
+ self.publish_test_metrics = False
+
+ def setup_class(self):
+ WifiRssiTest.setup_class(self)
+ self.ota_chamber = ota_chamber.create(
+ self.user_params['OTAChamber'])[0]
+
+ def teardown_class(self):
+ self.ota_chamber.reset_chamber()
+ self.process_testclass_results()
+
+ def teardown_test(self):
+ if self.ota_chamber.current_mode == 'continuous':
+ self.ota_chamber.reset_chamber()
+
+ def extract_test_id(self, testcase_params, id_fields):
+ test_id = collections.OrderedDict(
+ (param, testcase_params[param]) for param in id_fields)
+ return test_id
+
+ def process_testclass_results(self):
+ """Saves all test results to enable comparison."""
+ testclass_data = collections.OrderedDict()
+ for test_result in self.testclass_results:
+ current_params = test_result['testcase_params']
+
+ channel = current_params['channel']
+ channel_data = testclass_data.setdefault(
+ channel,
+ collections.OrderedDict(orientation=[],
+ rssi=collections.OrderedDict(
+ signal_poll_rssi=[],
+ chain_0_rssi=[],
+ chain_1_rssi=[])))
+
+ channel_data['orientation'].append(current_params['orientation'])
+ channel_data['rssi']['signal_poll_rssi'].append(
+ test_result['postprocessed_results']['signal_poll_rssi']
+ ['mean'][0])
+ channel_data['rssi']['chain_0_rssi'].append(
+ test_result['postprocessed_results']['chain_0_rssi']['mean']
+ [0])
+ channel_data['rssi']['chain_1_rssi'].append(
+ test_result['postprocessed_results']['chain_1_rssi']['mean']
+ [0])
+
+ # Publish test class metrics
+ for channel, channel_data in testclass_data.items():
+ for rssi_metric, rssi_metric_value in channel_data['rssi'].items():
+ metric_name = 'ota_summary_ch{}.avg_{}'.format(
+ channel, rssi_metric)
+ metric_value = numpy.mean(rssi_metric_value)
+ self.testclass_metric_logger.add_metric(
+ metric_name, metric_value)
+
+ # Plot test class results
+ chamber_mode = self.testclass_results[0]['testcase_params'][
+ 'chamber_mode']
+ if chamber_mode == 'orientation':
+ x_label = 'Angle (deg)'
+ elif chamber_mode == 'stepped stirrers':
+ x_label = 'Position Index'
+ elif chamber_mode == 'StirrersOn':
+ return
+ plots = []
+ for channel, channel_data in testclass_data.items():
+ current_plot = wputils.BokehFigure(
+ title='Channel {} - Rssi vs. Position'.format(channel),
+ x_label=x_label,
+ primary_y_label='RSSI (dBm)',
+ )
+ for rssi_metric, rssi_metric_value in channel_data['rssi'].items():
+ legend = rssi_metric
+ current_plot.add_line(channel_data['orientation'],
+ rssi_metric_value, legend)
+ current_plot.generate_figure()
+ plots.append(current_plot)
+ current_context = context.get_current_context().get_full_output_path()
+ plot_file_path = os.path.join(current_context, 'results.html')
+ wputils.BokehFigure.save_figures(plots, plot_file_path)
+
+ def setup_rssi_test(self, testcase_params):
+ # Test setup
+ WifiRssiTest.setup_rssi_test(self, testcase_params)
+ if testcase_params['chamber_mode'] == 'StirrersOn':
+ self.ota_chamber.start_continuous_stirrers()
+ else:
+ self.ota_chamber.set_orientation(testcase_params['orientation'])
+
+ def compile_ota_rssi_test_params(self, testcase_params):
+ """Function to complete compiling test-specific parameters
+
+ Args:
+ testcase_params: dict containing test-specific parameters
+ """
+ if "rssi_over_orientation" in self.test_name:
+ rssi_test_duration = self.testclass_params[
+ 'rssi_over_orientation_duration']
+ elif "rssi_variation" in self.test_name:
+ rssi_test_duration = self.testclass_params[
+ 'rssi_variation_duration']
+
+ testcase_params.update(
+ connected_measurements=int(
+ rssi_test_duration /
+ self.testclass_params['polling_frequency']),
+ scan_measurements=0,
+ first_measurement_delay=MED_SLEEP,
+ rssi_atten_range=[
+ self.testclass_params['rssi_ota_test_attenuation']
+ ])
+ testcase_params['band'] = self.access_point.band_lookup_by_channel(
+ testcase_params['channel'])
+ testcase_params['test_network'] = self.main_network[
+ testcase_params['band']]
+ testcase_params['tracked_bssid'] = [
+ self.main_network[testcase_params['band']].get(
+ 'BSSID', '00:00:00:00')
+ ]
+
+ testcase_params['traffic_timeout'] = self.get_traffic_timeout(
+ testcase_params)
+ if isinstance(self.iperf_server, ipf.IPerfServerOverAdb):
+ testcase_params['iperf_args'] = '-i 1 -t {} -J'.format(
+ testcase_params['traffic_timeout'])
+ else:
+ testcase_params['iperf_args'] = '-i 1 -t {} -J -R'.format(
+ testcase_params['traffic_timeout'])
+ return testcase_params
+
+ def _test_ota_rssi(self, testcase_params):
+ testcase_params = self.compile_ota_rssi_test_params(testcase_params)
+
+ self.setup_rssi_test(testcase_params)
+ rssi_result = self.run_rssi_test(testcase_params)
+ rssi_result['postprocessed_results'] = self.post_process_rssi_sweep(
+ rssi_result)
+ self.testclass_results.append(rssi_result)
+ self.plot_rssi_vs_time(rssi_result,
+ rssi_result['postprocessed_results'], 1)
+ self.plot_rssi_distribution(rssi_result['postprocessed_results'])
+
+ def generate_test_cases(self, test_types, channels, modes, traffic_modes,
+ chamber_modes, orientations):
+ test_cases = []
+ allowed_configs = {
+ 'VHT20': [
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 149, 153,
+ 157, 161
+ ],
+ 'VHT40': [36, 44, 149, 157],
+ 'VHT80': [36, 149]
+ }
+
+ for (channel, mode, traffic, chamber_mode, orientation,
+ test_type) in itertools.product(channels, modes, traffic_modes,
+ chamber_modes, orientations,
+ test_types):
+ if channel not in allowed_configs[mode]:
+ continue
+ test_name = test_type + '_ch{}_{}_{}_{}deg'.format(
+ channel, mode, traffic, orientation)
+ testcase_params = collections.OrderedDict(
+ channel=channel,
+ mode=mode,
+ active_traffic=(traffic == 'ActiveTraffic'),
+ traffic_type=self.user_params['rssi_test_params']
+ ['traffic_type'],
+ chamber_mode=chamber_mode,
+ orientation=orientation)
+ test_function = self._test_ota_rssi
+ setattr(self, test_name, partial(test_function, testcase_params))
+ test_cases.append(test_name)
+ return test_cases
+
+
+class WifiOtaRssi_Accuracy_Test(WifiOtaRssiTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases(['test_rssi_vs_atten'],
+ [6, 36, 149], ['VHT20'],
+ ['ActiveTraffic'],
+ ['orientation'],
+ list(range(0, 360, 45)))
+
+
+class WifiOtaRssi_StirrerVariation_Test(WifiOtaRssiTest):
+ def __init__(self, controllers):
+ WifiRssiTest.__init__(self, controllers)
+ self.tests = self.generate_test_cases(['test_rssi_variation'],
+ [6, 36, 149], ['VHT20'],
+ ['ActiveTraffic'],
+ ['StirrersOn'], [0])
+
+
+class WifiOtaRssi_TenDegree_Test(WifiOtaRssiTest):
+ def __init__(self, controllers):
+ WifiRssiTest.__init__(self, controllers)
+ self.tests = self.generate_test_cases(['test_rssi_over_orientation'],
+ [6, 36, 149], ['VHT20'],
+ ['ActiveTraffic'],
+ ['orientation'],
+ list(range(0, 360, 10)))
diff --git a/acts_tests/tests/google/wifi/WifiRttManagerTest.py b/acts_tests/tests/google/wifi/WifiRttManagerTest.py
new file mode 100644
index 0000000..f0985de
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiRttManagerTest.py
@@ -0,0 +1,574 @@
+#!/usr/bin/env python3.4
+#
+# Copyright (C) 2016 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 pprint
+import queue
+
+import acts.base_test
+import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts.utils
+from acts import asserts
+from acts.controllers.sl4a_lib import rpc_client
+
+WifiEnums = wutils.WifiEnums
+
+# Macros for RttParam keywords
+RttParam = WifiEnums.RttParam
+# Macros for RttManager
+Rtt = WifiEnums.Rtt
+RttBW = WifiEnums.RttBW
+RttPreamble = WifiEnums.RttPreamble
+RttPeerType = WifiEnums.RttPeerType
+RttType = WifiEnums.RttType
+
+ScanResult = WifiEnums.ScanResult
+RTT_MARGIN_OF_ERROR = WifiEnums.RTT_MARGIN_OF_ERROR
+
+
+class WifiRTTRangingError(Exception):
+ """Error in WifiScanner Rtt."""
+
+
+class WifiRttManagerTest(acts.base_test.BaseTestClass):
+ """Tests for wifi's RttManager APIs."""
+ tests = None
+ MAX_RTT_AP = 10
+
+ def __init__(self, controllers):
+ acts.base_test.BaseTestClass.__init__(self, controllers)
+ self.tests = ("test_support_check", "test_invalid_params",
+ "test_capability_check",
+ "test_rtt_ranging_single_AP_stress",
+ "test_regular_scan_then_rtt_ranging_stress",
+ "test_gscan_then_rtt_ranging_stress")
+
+ def setup_class(self):
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ required_params = ("support_models", "stress_num", "vht80_5g",
+ "actual_distance")
+ self.unpack_userparams(required_params)
+ asserts.assert_true(
+ self.actual_distance >= 5,
+ "Actual distance should be no shorter than 5 meters.")
+ self.visible_networks = (self.vht80_5g, )
+ self.default_rtt_params = {
+ RttParam.request_type: RttType.TYPE_TWO_SIDED,
+ RttParam.device_type: RttPeerType.PEER_TYPE_AP,
+ RttParam.preamble: RttPreamble.PREAMBLE_HT,
+ RttParam.bandwidth: RttBW.BW_80_SUPPORT
+ }
+ # Expected capability for devices that don't support RTT.
+ rtt_cap_neg = {
+ 'lcrSupported': False,
+ 'bwSupported': 0,
+ 'twoSided11McRttSupported': False,
+ 'preambleSupported': 0,
+ 'oneSidedRttSupported': False,
+ 'lciSupported': False
+ }
+ rtt_cap_shamu = {
+ 'lcrSupported': False,
+ 'bwSupported': 0x1C,
+ 'twoSided11McRttSupported': True,
+ 'preambleSupported': 6,
+ 'oneSidedRttSupported': False,
+ 'lciSupported': False
+ }
+ rtt_cap_bullhead = {
+ 'lcrSupported': True,
+ 'bwSupported': 0x1C,
+ 'twoSided11McRttSupported': True,
+ 'preambleSupported': 7,
+ 'oneSidedRttSupported': True,
+ 'lciSupported': True
+ }
+ rtt_cap_angler = {
+ 'lcrSupported': True,
+ 'bwSupported': 0x1C,
+ 'twoSided11McRttSupported': True,
+ 'preambleSupported': 6,
+ 'oneSidedRttSupported': False,
+ 'lciSupported': True
+ }
+ self.rtt_cap_table = {
+ "hammerhead": rtt_cap_neg,
+ "shamu": rtt_cap_shamu,
+ "volantis": rtt_cap_neg,
+ "volantisg": rtt_cap_neg,
+ "bullhead": rtt_cap_bullhead,
+ "angler": rtt_cap_angler
+ }
+
+ """Helper Functions"""
+
+ def invalid_params_logic(self, rtt_params):
+ try:
+ self.dut.droid.wifiRttStartRanging([rtt_params])
+ except rpc_client.Sl4aApiError as e:
+ e_str = str(e)
+ asserts.assert_true(
+ "IllegalArgumentException" in e_str,
+ "Missing IllegalArgumentException in %s." % e_str)
+ msg = "Got expected exception with invalid param %s." % rtt_params
+ self.log.info(msg)
+
+ def get_rtt_results(self, rtt_params):
+ """Starts RTT ranging and get results.
+
+ Args:
+ rtt_params: A list of dicts each representing an RttParam.
+
+ Returns:
+ Rtt ranging results.
+ """
+ self.log.debug("Start ranging with:\n%s" % pprint.pformat(rtt_params))
+ idx = self.dut.droid.wifiRttStartRanging(rtt_params)
+ event = None
+ try:
+ event = self.dut.ed.pop_events("WifiRttRanging%d" % idx, 30)
+ if event[0]["name"].endswith("onSuccess"):
+ results = event[0]["data"]["Results"]
+ result_len = len(results)
+ param_len = len(rtt_params)
+ asserts.assert_true(result_len == param_len,
+ "Expected %d results, got %d." %
+ (param_len, result_len))
+ # Add acceptable margin of error to results, which will be used
+ # during result processing.
+ for i, r in enumerate(results):
+ bw_mode = rtt_params[i][RttParam.bandwidth]
+ r[RttParam.margin] = RTT_MARGIN_OF_ERROR[bw_mode]
+ self.log.debug(pprint.pformat(event))
+ return event
+ except queue.Empty:
+ self.log.error("Waiting for RTT event timed out.")
+ return None
+
+ def network_selector(self, network_info):
+ """Decides if a network should be used for rtt ranging.
+
+ There are a few conditions:
+ 1. This network supports 80211mc.
+ 2. This network's info matches certain conditions.
+
+ This is added to better control which networks to range against instead
+ of blindly use all 80211mc networks in air.
+
+ Args:
+ network_info: A dict representing a WiFi network.
+
+ Returns:
+ True if the input network should be used for ranging, False
+ otherwise.
+ """
+ target_params = {
+ "is80211McRTTResponder": True,
+ WifiEnums.BSSID_KEY: self.vht80_5g[WifiEnums.BSSID_KEY],
+ }
+ for k, v in target_params.items():
+ if k not in network_info:
+ return False
+ if type(network_info[k]) is str:
+ network_info[k] = network_info[k].lower()
+ v = v.lower()
+ if network_info[k] != v:
+ return False
+ return True
+
+ def regular_scan_for_rtt_networks(self):
+ """Scans for 11mc-capable WiFi networks using regular wifi scan.
+
+ Networks are selected based on self.network_selector.
+
+ Returns:
+ A list of networks that have RTTResponders.
+ """
+ wutils.start_wifi_connection_scan(self.dut)
+ networks = self.dut.droid.wifiGetScanResults()
+ rtt_networks = []
+ for nw in networks:
+ if self.network_selector(nw):
+ rtt_networks.append(nw)
+ return rtt_networks
+
+ def gscan_for_rtt_networks(self):
+ """Scans for 11mc-capable WiFi networks using wifi gscan.
+
+ Networks are selected based on self.network_selector.
+
+ Returns:
+ A list of networks that have RTTResponders.
+ """
+ s = {
+ "reportEvents": WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT,
+ "band": WifiEnums.WIFI_BAND_BOTH,
+ "periodInMs": 10000,
+ "numBssidsPerScan": 32
+ }
+ idx = wutils.start_wifi_single_scan(self.android_devices[0],
+ s)["Index"]
+ self.log.info("Scan index is %d" % idx)
+ event_name = "WifiScannerScan%donFullResult" % idx
+
+ def condition(event):
+ nw = event["data"]["Results"][0]
+ return self.network_selector(nw)
+
+ rtt_networks = []
+ try:
+ for i in range(len(self.visible_networks)):
+ event = self.dut.ed.wait_for_event(event_name, condition, 30)
+ rtt_networks.append(event["data"]["Results"][0])
+ self.log.info("Waiting for gscan to finish.")
+ event_name = "WifiScannerScan%donResults" % idx
+ event = self.dut.ed.pop_event(event_name, 300)
+ total_network_cnt = len(event["data"]["Results"][0]["ScanResults"])
+ self.log.info("Found %d networks in total." % total_network_cnt)
+ self.log.debug(rtt_networks)
+ return rtt_networks
+ except queue.Empty:
+ self.log.error("Timed out waiting for gscan result.")
+
+ def process_rtt_events(self, events):
+ """Processes rtt ranging events.
+
+ Validates RTT event types.
+ Validates RTT response status and measured RTT values.
+ Enforces success rate.
+
+ Args:
+ events: A list of callback results from RTT ranging.
+ """
+ total = aborted = failure = invalid = out_of_range = 0
+ for e in events:
+ if e["name"].endswith("onAborted"):
+ aborted += 1
+ if e["name"].endswith("onFailure"):
+ failure += 1
+ if e["name"].endswith("onSuccess"):
+ results = e["data"]["Results"]
+ for r in results:
+ total += 1
+ # Status needs to be "success".
+ status = r["status"]
+ if status != Rtt.STATUS_SUCCESS:
+ self.log.warning("Got error status %d." % status)
+ invalid += 1
+ continue
+ # RTT value should be positive.
+ value = r["rtt"]
+ if value <= 0:
+ self.log.warning("Got error RTT value %d." % value)
+ invalid += 1
+ continue
+ # Vadlidate values in successful responses.
+ acd = self.actual_distance
+ margin = r[RttParam.margin]
+ # If the distance is >= 0, check distance only.
+ d = r["distance"] / 100.0
+ if d > 0:
+ # Distance should be in acceptable range.
+ is_d_valid = (acd - margin) <= d <= acd + (margin)
+ if not is_d_valid:
+ self.log.warning(
+ ("Reported distance %.2fm is out of the"
+ " acceptable range %.2f±%.2fm.") % (d, acd,
+ margin))
+ out_of_range += 1
+ continue
+ # Check if the RTT value is in range.
+ d = (value / 2) / 1E10 * wutils.SPEED_OF_LIGHT
+ is_rtt_valid = (acd - margin) <= d <= (acd + margin)
+ if not is_rtt_valid:
+ self.log.warning((
+ "Distance calculated from RTT value %d - %.2fm is "
+ "out of the acceptable range %.2f±%dm.") %
+ (value, d, acd, margin))
+ out_of_range += 1
+ continue
+ # Check if the RSSI value is in range.
+ rssi = r["rssi"]
+ # average rssi in 0.5dB steps, e.g. 143 implies -71.5dB,
+ # so the valid range is 0 to 200
+ is_rssi_valid = 0 <= rssi <= 200
+ if not is_rssi_valid:
+ self.log.warning(("Reported RSSI %d is out of the"
+ " acceptable range 0-200") % rssi)
+ out_of_range += 1
+ continue
+ self.log.info((
+ "Processed %d RTT events. %d aborted, %s failed. Among"
+ " the %d responses in successful callbacks, %s are invalid, %s has"
+ " RTT values that are out of range.") %
+ (len(events), aborted, failure, total, invalid,
+ out_of_range))
+ asserts.assert_true(total > 0, "No RTT response received.")
+ # Percentage of responses that are valid should be >= 90%.
+ valid_total = float(total - invalid)
+ valid_response_rate = valid_total / total
+ self.log.info("%.2f%% of the responses are valid." %
+ (valid_response_rate * 100))
+ asserts.assert_true(valid_response_rate >= 0.9,
+ "Valid response rate is below 90%%.")
+ # Among the valid responses, the percentage of having an in-range RTT
+ # value should be >= 67%.
+ valid_value_rate = (total - invalid - out_of_range) / valid_total
+ self.log.info("%.2f%% of valid responses have in-range RTT value" %
+ (valid_value_rate * 100))
+ msg = "In-range response rate is below 67%%."
+ asserts.assert_true(valid_value_rate >= 0.67, msg)
+
+ def scan_then_rtt_ranging_stress_logic(self, scan_func):
+ """Test logic to scan then do rtt ranging based on the scan results.
+
+ Steps:
+ 1. Start scan and get scan results.
+ 2. Filter out the networks that support rtt in scan results.
+ 3. Start rtt ranging against those networks that support rtt.
+ 4. Repeat
+ 5. Process RTT events.
+
+ Args:
+ scan_func: A function that does a wifi scan and only returns the
+ networks that support rtt in the scan results.
+
+ Returns:
+ True if rtt behaves as expected, False otherwise.
+ """
+ total = self.stress_num
+ failed = 0
+ all_results = []
+ for i in range(total):
+ self.log.info("Iteration %d" % i)
+ rtt_networks = scan_func()
+ if not rtt_networks:
+ self.log.warning("Found no rtt network, skip this iteration.")
+ failed += 1
+ continue
+ self.log.debug("Found rtt networks:%s" % rtt_networks)
+ rtt_params = []
+ for rn in rtt_networks:
+ rtt_params.append(self.rtt_config_from_scan_result(rn))
+ results = self.get_rtt_results(rtt_params)
+ if results:
+ self.log.debug(results)
+ all_results += results
+ self.process_rtt_events(all_results)
+
+ def rtt_config_from_scan_result(self, scan_result):
+ """Creates an Rtt configuration based on the scan result of a network.
+ """
+ scan_result_channel_width_to_rtt = {
+ ScanResult.CHANNEL_WIDTH_20MHZ: RttBW.BW_20_SUPPORT,
+ ScanResult.CHANNEL_WIDTH_40MHZ: RttBW.BW_40_SUPPORT,
+ ScanResult.CHANNEL_WIDTH_80MHZ: RttBW.BW_80_SUPPORT,
+ ScanResult.CHANNEL_WIDTH_160MHZ: RttBW.BW_160_SUPPORT,
+ ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ: RttBW.BW_160_SUPPORT
+ }
+ p = {}
+ freq = scan_result[RttParam.frequency]
+ p[RttParam.frequency] = freq
+ p[RttParam.BSSID] = scan_result[WifiEnums.BSSID_KEY]
+ if freq > 5000:
+ p[RttParam.preamble] = RttPreamble.PREAMBLE_VHT
+ else:
+ p[RttParam.preamble] = RttPreamble.PREAMBLE_HT
+ cf0 = scan_result[RttParam.center_freq0]
+ if cf0 > 0:
+ p[RttParam.center_freq0] = cf0
+ cf1 = scan_result[RttParam.center_freq1]
+ if cf1 > 0:
+ p[RttParam.center_freq1] = cf1
+ cw = scan_result["channelWidth"]
+ p[RttParam.channel_width] = cw
+ p[RttParam.bandwidth] = scan_result_channel_width_to_rtt[cw]
+ if scan_result["is80211McRTTResponder"]:
+ p[RttParam.request_type] = RttType.TYPE_TWO_SIDED
+ else:
+ p[RttParam.request_type] = RttType.TYPE_ONE_SIDED
+ return p
+
+ """Tests"""
+
+ def test_invalid_params(self):
+ """Tests the check function in RttManager.
+ """
+ param_list = [{
+ RttParam.device_type: 3
+ }, {
+ RttParam.device_type: 1,
+ RttParam.request_type: 3
+ }, {
+ RttParam.device_type: 1,
+ RttParam.request_type: 1,
+ RttParam.BSSID: None
+ }, {
+ RttParam.BSSID: "xxxxxxxx",
+ RttParam.number_burst: 1
+ }, {
+ RttParam.number_burst: 0,
+ RttParam.num_samples_per_burst: -1
+ }, {
+ RttParam.num_samples_per_burst: 32
+ }, {
+ RttParam.num_samples_per_burst: 5,
+ RttParam.num_retries_per_measurement_frame: -1
+ }, {
+ RttParam.num_retries_per_measurement_frame: 4
+ }, {
+ RttParam.num_retries_per_measurement_frame: 2,
+ RttParam.num_retries_per_FTMR: -1
+ }, {
+ RttParam.num_retries_per_FTMR: 4
+ }]
+ for param in param_list:
+ self.invalid_params_logic(param)
+ return True
+
+ def test_support_check(self):
+ """No device supports device-to-device RTT; only shamu and volantis
+ devices support device-to-ap RTT.
+ """
+ model = acts.utils.trim_model_name(self.dut.model)
+ asserts.assert_true(self.dut.droid.wifiIsDeviceToDeviceRttSupported(),
+ "Device to device is supposed to be supported.")
+ if any([model in m for m in self.support_models]):
+ asserts.assert_true(self.dut.droid.wifiIsDeviceToApRttSupported(),
+ "%s should support device-to-ap RTT." % model)
+ self.log.info("%s supports device-to-ap RTT as expected." % model)
+ else:
+ asserts.assert_false(
+ self.dut.droid.wifiIsDeviceToApRttSupported(),
+ "%s should not support device-to-ap RTT." % model)
+ self.log.info(
+ ("%s does not support device-to-ap RTT as expected.") % model)
+ asserts.abort_class(
+ "Device %s does not support RTT, abort." % model)
+ return True
+
+ def test_capability_check(self):
+ """Checks the capabilities params are reported as expected.
+ """
+ caps = self.dut.droid.wifiRttGetCapabilities()
+ asserts.assert_true(caps, "Unable to get rtt capabilities.")
+ self.log.debug("Got rtt capabilities %s" % caps)
+ model = acts.utils.trim_model_name(self.dut.model)
+ asserts.assert_true(model in self.rtt_cap_table,
+ "Unknown model %s" % model)
+ expected_caps = self.rtt_cap_table[model]
+ for k, v in expected_caps.items():
+ asserts.assert_true(k in caps, "%s missing in capabilities." % k)
+ asserts.assert_true(v == caps[k], "Expected %s for %s, got %s." %
+ (v, k, caps[k]))
+ return True
+
+ def test_discovery(self):
+ """Make sure all the expected 11mc BSSIDs are discovered properly, and
+ they are all reported as 802.11mc Rtt Responder.
+
+ Procedures:
+ 1. Scan for wifi networks.
+
+ Expect:
+ All the RTT networks show up in scan results and their
+ "is80211McRTTResponder" is True.
+ All the non-RTT networks show up in scan results and their
+ "is80211McRTTResponder" is False.
+ """
+ wutils.start_wifi_connection_scan(self.dut)
+ scan_results = self.dut.droid.wifiGetScanResults()
+ self.log.debug(scan_results)
+ for n in visible_networks:
+ asserts.assert_true(
+ wutils.match_networks(n, scan_results),
+ "Network %s was not discovered properly." % n)
+ return True
+
+ def test_missing_bssid(self):
+ """Start Rtt ranging with a config that does not have BSSID set.
+ Should not get onSuccess.
+ """
+ p = {}
+ p[RttParam.request_type] = RttType.TYPE_TWO_SIDED
+ p[RttParam.device_type] = RttPeerType.PEER_TYPE_AP
+ p[RttParam.preamble] = RttPreamble.PREAMBLE_VHT
+ p[RttParam.bandwidth] = RttBW.BW_80_SUPPORT
+ p[RttParam.frequency] = self.vht80_5g[WifiEnums.frequency_key]
+ p[RttParam.center_freq0] = self.vht80_5g[RttParam.center_freq0]
+ results = self.get_rtt_results([p])
+ asserts.assert_true(results, "Did not get any result.")
+ self.log.info(pprint.pformat(results))
+
+ def test_rtt_ranging_single_AP_stress(self):
+ """Stress test for Rtt against one AP.
+
+ Steps:
+ 1. Do RTT ranging against the self.vht80_5g BSSID.
+ 2. Repeat self.stress_num times.
+ 3. Verify RTT results.
+ """
+ p = {}
+ p[RttParam.request_type] = RttType.TYPE_TWO_SIDED
+ p[RttParam.device_type] = RttPeerType.PEER_TYPE_AP
+ p[RttParam.preamble] = RttPreamble.PREAMBLE_VHT
+ p[RttParam.bandwidth] = RttBW.BW_80_SUPPORT
+ p[RttParam.BSSID] = self.vht80_5g[WifiEnums.BSSID_KEY]
+ p[RttParam.frequency] = self.vht80_5g[WifiEnums.frequency_key]
+ p[RttParam.center_freq0] = self.vht80_5g[RttParam.center_freq0]
+ p[RttParam.channel_width] = ScanResult.CHANNEL_WIDTH_80MHZ
+ all_results = []
+ for i in range(self.stress_num):
+ self.log.info("RTT Ranging iteration %d" % (i + 1))
+ results = self.get_rtt_results([p])
+ if results:
+ all_results += results
+ else:
+ self.log.warning("Did not get result for iteration %d." % i)
+ frate = self.process_rtt_events(all_results)
+
+ def test_regular_scan_then_rtt_ranging_stress(self):
+ """Stress test for regular scan then start rtt ranging against the RTT
+ compatible networks found by the scan.
+
+ Steps:
+ 1. Start a WiFi connection scan.
+ 2. Get scan results.
+ 3. Find all the 11mc capable BSSIDs and choose the ones to use
+ (self.network_selector)
+ 4. Do RTT ranging against the selected BSSIDs, with the info from
+ the scan results.
+ 5. Repeat self.stress_num times.
+ 6. Verify RTT results.
+ """
+ scan_func = self.regular_scan_for_rtt_networks
+ self.scan_then_rtt_ranging_stress_logic(scan_func)
+
+ def test_gscan_then_rtt_ranging_stress(self):
+ """Stress test for gscan then start rtt ranging against the RTT
+ compatible networks found by the scan.
+
+ Steps:
+ 1. Start a WifiScanner single shot scan on all channels.
+ 2. Wait for full scan results of the expected 11mc capable BSSIDs.
+ 3. Wait for single shot scan to finish on all channels.
+ 4. Do RTT ranging against the selected BSSIDs, with the info from
+ the scan results.
+ 5. Repeat self.stress_num times.
+ 6. Verify RTT results.
+ """
+ scan_func = self.gscan_for_rtt_networks
+ self.scan_then_rtt_ranging_stress_logic(scan_func)
diff --git a/acts_tests/tests/google/wifi/WifiRvrTest.py b/acts_tests/tests/google/wifi/WifiRvrTest.py
new file mode 100644
index 0000000..979ac76
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiRvrTest.py
@@ -0,0 +1,895 @@
+#!/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 collections
+import itertools
+import json
+import logging
+import numpy
+import os
+import time
+from acts import asserts
+from acts import base_test
+from acts import utils
+from acts.controllers import iperf_server as ipf
+from acts.controllers.utils_lib import ssh
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
+from acts.test_utils.wifi import ota_chamber
+from acts.test_utils.wifi import ota_sniffer
+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 acts.test_utils.wifi import wifi_test_utils as wutils
+from functools import partial
+
+
+class WifiRvrTest(base_test.BaseTestClass):
+ """Class to test WiFi rate versus range.
+
+ This class implements WiFi rate versus range tests on single AP single STA
+ links. The class setups up the AP in the desired configurations, configures
+ and connects the phone to the AP, and runs iperf throughput test while
+ sweeping attenuation. For an example config file to run this test class see
+ example_connectivity_performance_ap_sta.json.
+ """
+
+ TEST_TIMEOUT = 6
+ MAX_CONSECUTIVE_ZEROS = 3
+
+ def __init__(self, controllers):
+ base_test.BaseTestClass.__init__(self, controllers)
+ self.testcase_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_case())
+ self.testclass_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_class())
+ self.publish_testcase_metrics = True
+
+ def setup_class(self):
+ """Initializes common test hardware and parameters.
+
+ This function initializes hardwares and compiles parameters that are
+ common to all tests in this class.
+ """
+ req_params = [
+ 'RetailAccessPoints', 'rvr_test_params', 'testbed_params',
+ 'RemoteServer', 'main_network'
+ ]
+ opt_params = ['golden_files_list', 'OTASniffer']
+ self.unpack_userparams(req_params, opt_params)
+ self.testclass_params = self.rvr_test_params
+ self.num_atten = self.attenuators[0].instrument.num_atten
+ self.iperf_server = self.iperf_servers[0]
+ self.remote_server = ssh.connection.SshConnection(
+ ssh.settings.from_config(self.RemoteServer[0]['ssh_config']))
+ self.iperf_client = self.iperf_clients[0]
+ self.access_point = retail_ap.create(self.RetailAccessPoints)[0]
+ if hasattr(self,
+ 'OTASniffer') and self.testbed_params['sniffer_enable']:
+ self.sniffer = ota_sniffer.create(self.OTASniffer)[0]
+ self.log.info('Access Point Configuration: {}'.format(
+ self.access_point.ap_settings))
+ self.log_path = os.path.join(logging.log_path, 'results')
+ os.makedirs(self.log_path, exist_ok=True)
+ if not hasattr(self, 'golden_files_list'):
+ if 'golden_results_path' in self.testbed_params:
+ self.golden_files_list = [
+ os.path.join(self.testbed_params['golden_results_path'],
+ file) for file in
+ os.listdir(self.testbed_params['golden_results_path'])
+ ]
+ else:
+ self.log.warning('No golden files found.')
+ self.golden_files_list = []
+ self.testclass_results = []
+
+ # Turn WiFi ON
+ if self.testclass_params.get('airplane_mode', 1):
+ 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()
+
+ def teardown_class(self):
+ # Turn WiFi OFF
+ for dev in self.android_devices:
+ wutils.wifi_toggle_state(dev, False)
+ self.process_testclass_results()
+
+ def process_testclass_results(self):
+ """Saves plot with all test results to enable comparison."""
+ # Plot and save all results
+ plots = collections.OrderedDict()
+ for result in self.testclass_results:
+ plot_id = (result['testcase_params']['channel'],
+ result['testcase_params']['mode'])
+ if plot_id not in plots:
+ plots[plot_id] = wputils.BokehFigure(
+ title='Channel {} {} ({})'.format(
+ result['testcase_params']['channel'],
+ result['testcase_params']['mode'],
+ result['testcase_params']['traffic_type']),
+ x_label='Attenuation (dB)',
+ primary_y_label='Throughput (Mbps)')
+ plots[plot_id].add_line(result['total_attenuation'],
+ result['throughput_receive'],
+ result['test_name'],
+ marker='circle')
+ figure_list = []
+ for plot_id, plot in plots.items():
+ plot.generate_figure()
+ figure_list.append(plot)
+ output_file_path = os.path.join(self.log_path, 'results.html')
+ wputils.BokehFigure.save_figures(figure_list, output_file_path)
+
+ def pass_fail_check(self, rvr_result):
+ """Check the test result and decide if it passed or failed.
+
+ Checks the RvR test result and compares to a throughput limites for
+ the same configuration. The pass/fail tolerances are provided in the
+ config file.
+
+ Args:
+ rvr_result: dict containing attenuation, throughput and other data
+ """
+ try:
+ throughput_limits = self.compute_throughput_limits(rvr_result)
+ except:
+ asserts.explicit_pass(
+ 'Test passed by default. Golden file not found')
+
+ failure_count = 0
+ for idx, current_throughput in enumerate(
+ rvr_result['throughput_receive']):
+ if (current_throughput < throughput_limits['lower_limit'][idx]
+ or current_throughput >
+ throughput_limits['upper_limit'][idx]):
+ failure_count = failure_count + 1
+
+ # Set test metrics
+ rvr_result['metrics']['failure_count'] = failure_count
+ if self.publish_testcase_metrics:
+ self.testcase_metric_logger.add_metric('failure_count',
+ failure_count)
+
+ # Assert pass or fail
+ if failure_count >= self.testclass_params['failure_count_tolerance']:
+ asserts.fail('Test failed. Found {} points outside limits.'.format(
+ failure_count))
+ asserts.explicit_pass(
+ 'Test passed. Found {} points outside throughput limits.'.format(
+ failure_count))
+
+ def compute_throughput_limits(self, rvr_result):
+ """Compute throughput limits for current test.
+
+ Checks the RvR test result and compares to a throughput limites for
+ the same configuration. The pass/fail tolerances are provided in the
+ config file.
+
+ Args:
+ rvr_result: dict containing attenuation, throughput and other meta
+ data
+ Returns:
+ throughput_limits: dict containing attenuation and throughput limit data
+ """
+ test_name = self.current_test_name
+ golden_path = next(file_name for file_name in self.golden_files_list
+ if test_name in file_name)
+ with open(golden_path, 'r') as golden_file:
+ golden_results = json.load(golden_file)
+ golden_attenuation = [
+ att + golden_results['fixed_attenuation']
+ for att in golden_results['attenuation']
+ ]
+ attenuation = []
+ lower_limit = []
+ upper_limit = []
+ for idx, current_throughput in enumerate(
+ rvr_result['throughput_receive']):
+ current_att = rvr_result['attenuation'][idx] + rvr_result[
+ 'fixed_attenuation']
+ att_distances = [
+ abs(current_att - golden_att)
+ for golden_att in golden_attenuation
+ ]
+ sorted_distances = sorted(enumerate(att_distances),
+ key=lambda x: x[1])
+ closest_indeces = [dist[0] for dist in sorted_distances[0:3]]
+ closest_throughputs = [
+ golden_results['throughput_receive'][index]
+ for index in closest_indeces
+ ]
+ closest_throughputs.sort()
+
+ attenuation.append(current_att)
+ lower_limit.append(
+ max(
+ closest_throughputs[0] - max(
+ self.testclass_params['abs_tolerance'],
+ closest_throughputs[0] *
+ self.testclass_params['pct_tolerance'] / 100), 0))
+ upper_limit.append(closest_throughputs[-1] + max(
+ self.testclass_params['abs_tolerance'], closest_throughputs[-1]
+ * self.testclass_params['pct_tolerance'] / 100))
+ throughput_limits = {
+ 'attenuation': attenuation,
+ 'lower_limit': lower_limit,
+ 'upper_limit': upper_limit
+ }
+ return throughput_limits
+
+ def process_test_results(self, rvr_result):
+ """Saves plots and JSON formatted results.
+
+ Args:
+ rvr_result: dict containing attenuation, throughput and other meta
+ data
+ """
+ # Save output as text file
+ test_name = self.current_test_name
+ results_file_path = os.path.join(
+ self.log_path, '{}.json'.format(self.current_test_name))
+ with open(results_file_path, 'w') as results_file:
+ json.dump(rvr_result, results_file, indent=4)
+ # Plot and save
+ figure = wputils.BokehFigure(title=test_name,
+ x_label='Attenuation (dB)',
+ primary_y_label='Throughput (Mbps)')
+ try:
+ golden_path = next(file_name
+ for file_name in self.golden_files_list
+ if test_name in file_name)
+ with open(golden_path, 'r') as golden_file:
+ golden_results = json.load(golden_file)
+ golden_attenuation = [
+ att + golden_results['fixed_attenuation']
+ for att in golden_results['attenuation']
+ ]
+ throughput_limits = self.compute_throughput_limits(rvr_result)
+ shaded_region = {
+ 'x_vector': throughput_limits['attenuation'],
+ 'lower_limit': throughput_limits['lower_limit'],
+ 'upper_limit': throughput_limits['upper_limit']
+ }
+ figure.add_line(golden_attenuation,
+ golden_results['throughput_receive'],
+ 'Golden Results',
+ color='green',
+ marker='circle',
+ shaded_region=shaded_region)
+ except:
+ self.log.warning('ValueError: Golden file not found')
+
+ # Generate graph annotatios
+ hover_text = [
+ 'TX MCS = {0} ({1:.1f}%). RX MCS = {2} ({3:.1f}%)'.format(
+ curr_llstats['summary']['common_tx_mcs'],
+ curr_llstats['summary']['common_tx_mcs_freq'] * 100,
+ curr_llstats['summary']['common_rx_mcs'],
+ curr_llstats['summary']['common_rx_mcs_freq'] * 100)
+ for curr_llstats in rvr_result['llstats']
+ ]
+ figure.add_line(rvr_result['total_attenuation'],
+ rvr_result['throughput_receive'],
+ 'Test Results',
+ hover_text=hover_text,
+ color='red',
+ marker='circle')
+
+ output_file_path = os.path.join(self.log_path,
+ '{}.html'.format(test_name))
+ figure.generate_figure(output_file_path)
+
+ #Set test metrics
+ rvr_result['metrics'] = {}
+ rvr_result['metrics']['peak_tput'] = max(
+ rvr_result['throughput_receive'])
+ if self.publish_testcase_metrics:
+ self.testcase_metric_logger.add_metric(
+ 'peak_tput', rvr_result['metrics']['peak_tput'])
+
+ tput_below_limit = [
+ tput < self.testclass_params['tput_metric_targets'][
+ rvr_result['testcase_params']['mode']]['high']
+ for tput in rvr_result['throughput_receive']
+ ]
+ rvr_result['metrics']['high_tput_range'] = -1
+ for idx in range(len(tput_below_limit)):
+ if all(tput_below_limit[idx:]):
+ if idx == 0:
+ #Throughput was never above limit
+ rvr_result['metrics']['high_tput_range'] = -1
+ else:
+ rvr_result['metrics']['high_tput_range'] = rvr_result[
+ 'total_attenuation'][max(idx, 1) - 1]
+ break
+ if self.publish_testcase_metrics:
+ self.testcase_metric_logger.add_metric(
+ 'high_tput_range', rvr_result['metrics']['high_tput_range'])
+
+ tput_below_limit = [
+ tput < self.testclass_params['tput_metric_targets'][
+ rvr_result['testcase_params']['mode']]['low']
+ for tput in rvr_result['throughput_receive']
+ ]
+ for idx in range(len(tput_below_limit)):
+ if all(tput_below_limit[idx:]):
+ rvr_result['metrics']['low_tput_range'] = rvr_result[
+ 'total_attenuation'][max(idx, 1) - 1]
+ break
+ else:
+ rvr_result['metrics']['low_tput_range'] = -1
+ if self.publish_testcase_metrics:
+ self.testcase_metric_logger.add_metric(
+ 'low_tput_range', rvr_result['metrics']['low_tput_range'])
+
+ def run_rvr_test(self, testcase_params):
+ """Test function to run RvR.
+
+ The function runs an RvR test in the current device/AP configuration.
+ Function is called from another wrapper function that sets up the
+ testbed for the RvR test
+
+ Args:
+ testcase_params: dict containing test-specific parameters
+ Returns:
+ rvr_result: dict containing rvr_results and meta data
+ """
+ self.log.info('Start running RvR')
+ # Refresh link layer stats before test
+ llstats_obj = wputils.LinkLayerStats(
+ self.monitored_dut,
+ self.testclass_params.get('monitor_llstats', 1))
+ zero_counter = 0
+ throughput = []
+ llstats = []
+ rssi = []
+ for atten in testcase_params['atten_range']:
+ for dev in self.android_devices:
+ if not wputils.health_check(dev, 5, 50):
+ asserts.skip('DUT health check failed. Skipping test.')
+ # Set Attenuation
+ for attenuator in self.attenuators:
+ attenuator.set_atten(atten, strict=False)
+ # Refresh link layer stats
+ llstats_obj.update_stats()
+ # Setup sniffer
+ if self.testbed_params['sniffer_enable']:
+ self.sniffer.start_capture(
+ network=testcase_params['test_network'],
+ chan=int(testcase_params['channel']),
+ 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))
+ 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()
+ 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']:
+ self.sniffer.stop_capture(tag=str(atten))
+ # Parse and log result
+ if testcase_params['use_client_output']:
+ iperf_file = client_output_path
+ else:
+ iperf_file = server_output_path
+ try:
+ iperf_result = ipf.IPerfResult(iperf_file)
+ curr_throughput = numpy.mean(iperf_result.instantaneous_rates[
+ self.testclass_params['iperf_ignored_interval']:-1]
+ ) * 8 * (1.024**2)
+ except:
+ self.log.warning(
+ 'ValueError: Cannot get iperf result. Setting to 0')
+ curr_throughput = 0
+ throughput.append(curr_throughput)
+ llstats_obj.update_stats()
+ curr_llstats = llstats_obj.llstats_incremental.copy()
+ llstats.append(curr_llstats)
+ self.log.info(
+ ('Throughput at {0:.2f} dB is {1:.2f} Mbps. '
+ 'RSSI = {2:.2f} [{3:.2f}, {4:.2f}].').format(
+ atten, curr_throughput, current_rssi['signal_poll_rssi'],
+ current_rssi['chain_0_rssi'],
+ current_rssi['chain_1_rssi']))
+ if curr_throughput == 0 and (
+ current_rssi['signal_poll_rssi'] < -80
+ or numpy.isnan(current_rssi['signal_poll_rssi'])):
+ zero_counter = zero_counter + 1
+ else:
+ zero_counter = 0
+ if zero_counter == self.MAX_CONSECUTIVE_ZEROS:
+ self.log.info(
+ 'Throughput stable at 0 Mbps. Stopping test now.')
+ throughput.extend(
+ [0] *
+ (len(testcase_params['atten_range']) - len(throughput)))
+ break
+ for attenuator in self.attenuators:
+ attenuator.set_atten(0, strict=False)
+ # Compile test result and meta data
+ rvr_result = collections.OrderedDict()
+ rvr_result['test_name'] = self.current_test_name
+ rvr_result['testcase_params'] = testcase_params.copy()
+ rvr_result['ap_settings'] = self.access_point.ap_settings.copy()
+ rvr_result['fixed_attenuation'] = self.testbed_params[
+ 'fixed_attenuation'][str(testcase_params['channel'])]
+ rvr_result['attenuation'] = list(testcase_params['atten_range'])
+ rvr_result['total_attenuation'] = [
+ att + rvr_result['fixed_attenuation']
+ for att in rvr_result['attenuation']
+ ]
+ rvr_result['rssi'] = rssi
+ rvr_result['throughput_receive'] = throughput
+ rvr_result['llstats'] = llstats
+ return rvr_result
+
+ def setup_ap(self, testcase_params):
+ """Sets up the access point in the configuration required by the test.
+
+ Args:
+ testcase_params: dict containing AP and other test params
+ """
+ band = self.access_point.band_lookup_by_channel(
+ testcase_params['channel'])
+ if '2G' in band:
+ frequency = wutils.WifiEnums.channel_2G_to_freq[
+ testcase_params['channel']]
+ else:
+ frequency = wutils.WifiEnums.channel_5G_to_freq[
+ testcase_params['channel']]
+ if frequency in wutils.WifiEnums.DFS_5G_FREQUENCIES:
+ self.access_point.set_region(self.testbed_params['DFS_region'])
+ else:
+ self.access_point.set_region(self.testbed_params['default_region'])
+ self.access_point.set_channel(band, testcase_params['channel'])
+ self.access_point.set_bandwidth(band, testcase_params['mode'])
+ self.log.info('Access Point Configuration: {}'.format(
+ self.access_point.ap_settings))
+
+ def setup_dut(self, testcase_params):
+ """Sets up the DUT in the configuration required by the test.
+
+ 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.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.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.sta_dut)
+ wutils.set_wifi_country_code(self.sta_dut,
+ self.testclass_params['country_code'])
+ 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)
+ 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.
+
+ Args:
+ testcase_params: dict containing test-specific parameters
+ """
+ # Configure AP
+ self.setup_ap(testcase_params)
+ # Set attenuator to 0 dB
+ for attenuator in self.attenuators:
+ attenuator.set_atten(0, strict=False)
+ # Reset, configure, and connect DUT
+ self.setup_dut(testcase_params)
+ # Wait before running the first wifi test
+ first_test_delay = self.testclass_params.get('first_test_delay', 600)
+ if first_test_delay > 0 and len(self.testclass_results) == 0:
+ self.log.info('Waiting before the first RvR test.')
+ 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'] = sta_dut_ip
+ else:
+ testcase_params[
+ 'iperf_server_address'] = wputils.get_server_address(
+ 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.
+
+ Args:
+ testcase_params: dict containing test-specific parameters
+ """
+ num_atten_steps = int((self.testclass_params['atten_stop'] -
+ self.testclass_params['atten_start']) /
+ self.testclass_params['atten_step'])
+ testcase_params['atten_range'] = [
+ self.testclass_params['atten_start'] +
+ x * self.testclass_params['atten_step']
+ for x in range(0, num_atten_steps)
+ ]
+ 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'
+ and isinstance(self.iperf_server, ipf.IPerfServerOverAdb)):
+ testcase_params['iperf_args'] = wputils.get_iperf_arg_string(
+ duration=self.testclass_params['iperf_duration'],
+ reverse_direction=1,
+ 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'],
+ socket_size=testcase_params['iperf_socket_size'],
+ num_processes=testcase_params['iperf_processes'])
+ testcase_params['use_client_output'] = False
+ return testcase_params
+
+ def _test_rvr(self, testcase_params):
+ """ Function that gets called for each test case
+
+ Args:
+ testcase_params: dict containing test-specific parameters
+ """
+ # Compile test parameters from config and test name
+ testcase_params = self.compile_test_params(testcase_params)
+
+ # Prepare devices and run test
+ self.setup_rvr_test(testcase_params)
+ rvr_result = self.run_rvr_test(testcase_params)
+
+ # Post-process results
+ self.testclass_results.append(rvr_result)
+ self.process_test_results(rvr_result)
+ self.pass_fail_check(rvr_result)
+
+ def generate_test_cases(self, channels, modes, traffic_types,
+ traffic_directions):
+ """Function that auto-generates test cases for a test class."""
+ test_cases = []
+ allowed_configs = {
+ 'VHT20': [
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 64, 100,
+ 116, 132, 140, 149, 153, 157, 161
+ ],
+ 'VHT40': [36, 44, 100, 149, 157],
+ 'VHT80': [36, 100, 149]
+ }
+
+ for channel, mode, traffic_type, traffic_direction in itertools.product(
+ channels, modes, traffic_types, traffic_directions):
+ if channel not in allowed_configs[mode]:
+ continue
+ test_name = 'test_rvr_{}_{}_ch{}_{}'.format(
+ traffic_type, traffic_direction, channel, mode)
+ test_params = collections.OrderedDict(
+ channel=channel,
+ mode=mode,
+ traffic_type=traffic_type,
+ traffic_direction=traffic_direction)
+ setattr(self, test_name, partial(self._test_rvr, test_params))
+ test_cases.append(test_name)
+ return test_cases
+
+
+# Classes defining test suites
+class WifiRvr_2GHz_Test(WifiRvrTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases(channels=[1, 6, 11],
+ modes=['VHT20'],
+ traffic_types=['TCP'],
+ traffic_directions=['DL', 'UL'])
+
+
+class WifiRvr_UNII1_Test(WifiRvrTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases(
+ channels=[36, 40, 44, 48],
+ modes=['VHT20', 'VHT40', 'VHT80'],
+ traffic_types=['TCP'],
+ traffic_directions=['DL', 'UL'])
+
+
+class WifiRvr_UNII3_Test(WifiRvrTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases(
+ channels=[149, 153, 157, 161],
+ modes=['VHT20', 'VHT40', 'VHT80'],
+ traffic_types=['TCP'],
+ traffic_directions=['DL', 'UL'])
+
+
+class WifiRvr_SampleDFS_Test(WifiRvrTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases(
+ channels=[64, 100, 116, 132, 140],
+ modes=['VHT20', 'VHT40', 'VHT80'],
+ traffic_types=['TCP'],
+ traffic_directions=['DL', 'UL'])
+
+
+class WifiRvr_SampleUDP_Test(WifiRvrTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases(
+ channels=[6, 36, 149],
+ modes=['VHT20', 'VHT40', 'VHT80'],
+ traffic_types=['UDP'],
+ traffic_directions=['DL', 'UL'])
+
+
+class WifiRvr_TCP_All_Test(WifiRvrTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases(
+ channels=[1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161],
+ modes=['VHT20', 'VHT40', 'VHT80'],
+ traffic_types=['TCP'],
+ traffic_directions=['DL', 'UL'])
+
+
+class WifiRvr_TCP_Downlink_Test(WifiRvrTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases(
+ channels=[1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161],
+ modes=['VHT20', 'VHT40', 'VHT80'],
+ traffic_types=['TCP'],
+ traffic_directions=['DL'])
+
+
+class WifiRvr_TCP_Uplink_Test(WifiRvrTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases(
+ channels=[1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161],
+ modes=['VHT20', 'VHT40', 'VHT80'],
+ traffic_types=['TCP'],
+ traffic_directions=['UL'])
+
+
+# Over-the air version of RVR tests
+class WifiOtaRvrTest(WifiRvrTest):
+ """Class to test over-the-air RvR
+
+ This class implements measures WiFi RvR tests in an OTA chamber. It enables
+ setting turntable orientation and other chamber parameters to study
+ performance in varying channel conditions
+ """
+ def __init__(self, controllers):
+ base_test.BaseTestClass.__init__(self, controllers)
+ self.testcase_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_case())
+ self.testclass_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_class())
+ self.publish_testcase_metrics = False
+
+ def setup_class(self):
+ WifiRvrTest.setup_class(self)
+ self.ota_chamber = ota_chamber.create(
+ self.user_params['OTAChamber'])[0]
+
+ def teardown_class(self):
+ WifiRvrTest.teardown_class(self)
+ self.ota_chamber.reset_chamber()
+
+ def extract_test_id(self, testcase_params, id_fields):
+ test_id = collections.OrderedDict(
+ (param, testcase_params[param]) for param in id_fields)
+ return test_id
+
+ def process_testclass_results(self):
+ """Saves plot with all test results to enable comparison."""
+ # Plot individual test id results raw data and compile metrics
+ plots = collections.OrderedDict()
+ compiled_data = collections.OrderedDict()
+ for result in self.testclass_results:
+ test_id = tuple(
+ self.extract_test_id(
+ result['testcase_params'],
+ ['channel', 'mode', 'traffic_type', 'traffic_direction'
+ ]).items())
+ if test_id not in plots:
+ # Initialize test id data when not present
+ compiled_data[test_id] = {'throughput': [], 'metrics': {}}
+ compiled_data[test_id]['metrics'] = {
+ key: []
+ for key in result['metrics'].keys()
+ }
+ plots[test_id] = wputils.BokehFigure(
+ title='Channel {} {} ({} {})'.format(
+ result['testcase_params']['channel'],
+ result['testcase_params']['mode'],
+ result['testcase_params']['traffic_type'],
+ result['testcase_params']['traffic_direction']),
+ x_label='Attenuation (dB)',
+ primary_y_label='Throughput (Mbps)')
+ # Compile test id data and metrics
+ compiled_data[test_id]['throughput'].append(
+ result['throughput_receive'])
+ compiled_data[test_id]['total_attenuation'] = result[
+ 'total_attenuation']
+ for metric_key, metric_value in result['metrics'].items():
+ compiled_data[test_id]['metrics'][metric_key].append(
+ metric_value)
+ # Add test id to plots
+ plots[test_id].add_line(result['total_attenuation'],
+ result['throughput_receive'],
+ result['test_name'],
+ width=1,
+ style='dashed',
+ marker='circle')
+
+ # Compute average RvRs and compount metrics over orientations
+ for test_id, test_data in compiled_data.items():
+ test_id_dict = dict(test_id)
+ metric_tag = '{}_{}_ch{}_{}'.format(
+ test_id_dict['traffic_type'],
+ test_id_dict['traffic_direction'], test_id_dict['channel'],
+ test_id_dict['mode'])
+ high_tput_hit_freq = numpy.mean(
+ numpy.not_equal(test_data['metrics']['high_tput_range'], -1))
+ self.testclass_metric_logger.add_metric(
+ '{}.high_tput_hit_freq'.format(metric_tag), high_tput_hit_freq)
+ for metric_key, metric_value in test_data['metrics'].items():
+ metric_key = "{}.avg_{}".format(metric_tag, metric_key)
+ metric_value = numpy.mean(metric_value)
+ self.testclass_metric_logger.add_metric(
+ metric_key, metric_value)
+ test_data['avg_rvr'] = numpy.mean(test_data['throughput'], 0)
+ test_data['median_rvr'] = numpy.median(test_data['throughput'], 0)
+ plots[test_id].add_line(test_data['total_attenuation'],
+ test_data['avg_rvr'],
+ legend='Average Throughput',
+ marker='circle')
+ plots[test_id].add_line(test_data['total_attenuation'],
+ test_data['median_rvr'],
+ legend='Median Throughput',
+ marker='square')
+
+ figure_list = []
+ for test_id, plot in plots.items():
+ plot.generate_figure()
+ figure_list.append(plot)
+ output_file_path = os.path.join(self.log_path, 'results.html')
+ wputils.BokehFigure.save_figures(figure_list, output_file_path)
+
+ def setup_rvr_test(self, testcase_params):
+ # Set turntable orientation
+ self.ota_chamber.set_orientation(testcase_params['orientation'])
+ # Continue test setup
+ WifiRvrTest.setup_rvr_test(self, testcase_params)
+
+ def generate_test_cases(self, channels, modes, angles, traffic_types,
+ directions):
+ test_cases = []
+ allowed_configs = {
+ 'VHT20': [
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 149, 153,
+ 157, 161
+ ],
+ 'VHT40': [36, 44, 149, 157],
+ 'VHT80': [36, 149]
+ }
+ for channel, mode, angle, traffic_type, direction in itertools.product(
+ channels, modes, angles, traffic_types, directions):
+ if channel not in allowed_configs[mode]:
+ continue
+ testcase_name = 'test_rvr_{}_{}_ch{}_{}_{}deg'.format(
+ traffic_type, direction, channel, mode, angle)
+ test_params = collections.OrderedDict(channel=channel,
+ mode=mode,
+ traffic_type=traffic_type,
+ traffic_direction=direction,
+ orientation=angle)
+ setattr(self, testcase_name, partial(self._test_rvr, test_params))
+ test_cases.append(testcase_name)
+ return test_cases
+
+
+class WifiOtaRvr_StandardOrientation_Test(WifiOtaRvrTest):
+ def __init__(self, controllers):
+ WifiOtaRvrTest.__init__(self, controllers)
+ self.tests = self.generate_test_cases(
+ [1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161],
+ ['VHT20', 'VHT40', 'VHT80'], list(range(0, 360,
+ 45)), ['TCP'], ['DL'])
+
+
+class WifiOtaRvr_SampleChannel_Test(WifiOtaRvrTest):
+ def __init__(self, controllers):
+ WifiOtaRvrTest.__init__(self, controllers)
+ self.tests = self.generate_test_cases([6], ['VHT20'],
+ list(range(0, 360, 45)), ['TCP'],
+ ['DL'])
+ self.tests.extend(
+ self.generate_test_cases([36, 149], ['VHT80'],
+ list(range(0, 360, 45)), ['TCP'], ['DL']))
+
+
+class WifiOtaRvr_SingleOrientation_Test(WifiOtaRvrTest):
+ def __init__(self, controllers):
+ WifiOtaRvrTest.__init__(self, controllers)
+ self.tests = self.generate_test_cases(
+ [6, 36, 40, 44, 48, 149, 153, 157, 161],
+ ['VHT20', 'VHT40', 'VHT80'], [0], ['TCP'], ['DL', 'UL'])
\ No newline at end of file
diff --git a/acts_tests/tests/google/wifi/WifiRvrTwTest.py b/acts_tests/tests/google/wifi/WifiRvrTwTest.py
new file mode 100644
index 0000000..2f9dc12
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiRvrTwTest.py
@@ -0,0 +1,480 @@
+#!/usr/bin/env python3
+#
+# 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 time
+
+import acts.signals
+import acts.test_utils.wifi.wifi_test_utils as wutils
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from acts.controllers import iperf_server as ipf
+
+import json
+import logging
+import math
+import os
+from acts import utils
+import csv
+
+import serial
+import sys
+
+
+WifiEnums = wutils.WifiEnums
+
+
+class WifiRvrTWTest(WifiBaseTest):
+ """ Tests for wifi RVR performance
+
+ Test Bed Requirement:
+ * One Android device
+ * Wi-Fi networks visible to the device
+ """
+ TEST_TIMEOUT = 10
+
+ def setup_class(self):
+ super().setup_class()
+
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+
+ req_params = [ "iot_networks","rvr_test_params"]
+ opt_params = [ "angle_params","usb_port"]
+ self.unpack_userparams(req_param_names=req_params,
+ opt_param_names=opt_params)
+
+ asserts.assert_true(
+ len(self.iot_networks) > 0,
+ "Need at least one iot network with psk.")
+
+ wutils.wifi_toggle_state(self.dut, True)
+ if "rvr_test_params" in self.user_params:
+ self.iperf_server = self.iperf_servers[0]
+ self.MaxdB= self.rvr_test_params ["rvr_atten_MaxDB"]
+ self.MindB= self.rvr_test_params ["rvr_atten_MinDB"]
+ self.stepdB= self.rvr_test_params ["rvr_atten_step"]
+
+ if "angle_params" in self.user_params:
+ self.angle = self.angle_params
+
+ if "usb_port" in self.user_params:
+ self.T1=self.readport(self.usb_port["turntable"])
+ self.ATT1=self.readport(self.usb_port["atten1"])
+ self.ATT2=self.readport(self.usb_port["atten2"])
+ self.ATT3=self.readport(self.usb_port["atten3"])
+
+ # create hashmap for testcase name and SSIDs
+ self.iot_test_prefix = "test_iot_connection_to_"
+ self.ssid_map = {}
+ for network in self.iot_networks:
+ SSID = network['SSID'].replace('-','_')
+ self.ssid_map[SSID] = network
+
+ # create folder for rvr test result
+ self.log_path = os.path.join(logging.log_path, "rvr_results")
+ os.makedirs(self.log_path, exist_ok=True)
+
+ Header=("test_SSID","Turn table (angle)","Attenuator(dBm)",
+ "TX throughput (Mbps)","RX throughput (Mbps)",
+ "RSSI","Link speed","Frequency")
+ self.csv_write(Header)
+
+ 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 teardown_class(self):
+ if "rvr_test_params" in self.user_params:
+ self.iperf_server.stop()
+
+ 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)
+
+ """Helper Functions"""
+
+ def csv_write(self,data):
+ """Output .CSV file for test result.
+
+ Args:
+ data: Dict containing attenuation, throughput and other meta data.
+ """
+ with open("{}/Result.csv".format(self.log_path), "a", newline="") as csv_file:
+ csv_writer = csv.writer(csv_file,delimiter=',')
+ csv_writer.writerow(data)
+ csv_file.close()
+
+ def readport(self,com):
+ """Read com port for current test.
+
+ Args:
+ com: Attenuator or turn table com port
+ """
+ port=serial.Serial(com,9600,timeout=1)
+ time.sleep(1)
+ return port
+
+ def getdB(self,port):
+ """Get attenuator dB for current test.
+
+ Args:
+ port: Attenuator com port
+ """
+ port.write('V?;'.encode())
+ dB=port.readline().decode()
+ dB=dB.strip(';')
+ dB=dB[dB.find('V')+1:]
+ return int(dB)
+
+ def setdB(self,port,dB):
+ """Setup attenuator dB for current test.
+
+ Args:
+ port: Attenuator com port
+ dB: Attenuator setup dB
+ """
+ if dB<0:
+ dB=0
+ elif dB>101:
+ dB=101
+ self.log.info("Set dB to "+str(dB))
+ InputdB=str('V')+str(dB)+str(';')
+ port.write(InputdB.encode())
+ time.sleep(0.1)
+
+ def set_Three_Att_dB(self,port1,port2,port3,dB):
+ """Setup 3 attenuator dB for current test.
+
+ Args:
+ port1: Attenuator1 com port
+ port1: Attenuator2 com port
+ port1: Attenuator com port
+ dB: Attenuator setup dB
+ """
+ self.setdB(port1,dB)
+ self.setdB(port2,dB)
+ self.setdB(port3,dB)
+ self.checkdB(port1,dB)
+ self.checkdB(port2,dB)
+ self.checkdB(port3,dB)
+
+ def checkdB(self,port,dB):
+ """Check attenuator dB for current test.
+
+ Args:
+ port: Attenuator com port
+ dB: Attenuator setup dB
+ """
+ retry=0
+ while self.getdB(port)!=dB and retry<10:
+ retry=retry+1
+ self.log.info("Current dB = "+str(self.getdB(port)))
+ self.log.info("Fail to set Attenuator to "+str(dB)+", "
+ +str(retry)+" times try to reset")
+ self.setdB(port,dB)
+ if retry ==10:
+ self.log.info("Retry Attenuator fail for 9 cycles, end test!")
+ sys.exit()
+ return 0
+
+ def getDG(self,port):
+ """Get turn table angle for current test.
+
+ Args:
+ port: Turn table com port
+ """
+ DG = ""
+ port.write('DG?;'.encode())
+ time.sleep(0.1)
+ data = port.readline().decode('utf-8')
+ for i in range(len(data)):
+ if (data[i].isdigit()) == True:
+ DG = DG + data[i]
+ if DG == "":
+ return -1
+ return int(DG)
+
+ def setDG(self,port,DG):
+ """Setup turn table angle for current test.
+
+ Args:
+ port: Turn table com port
+ DG: Turn table setup angle
+ """
+ if DG>359:
+ DG=359
+ elif DG<0:
+ DG=0
+ self.log.info("Set angle to "+str(DG))
+ InputDG=str('DG')+str(DG)+str(';')
+ port.write(InputDG.encode())
+
+ def checkDG(self,port,DG):
+ """Check turn table angle for current test.
+
+ Args:
+ port: Turn table com port
+ DG: Turn table setup angle
+ """
+ retrytime = self.TEST_TIMEOUT
+ retry = 0
+ while self.getDG(port)!=DG and retry<retrytime:
+ retry=retry+1
+ self.log.info('Current angle = '+str(self.getDG(port)))
+ self.log.info('Fail set angle to '+str(DG)+', '+str(retry)+' times try to reset')
+ self.setDG(port,DG)
+ time.sleep(10)
+ if retry == retrytime:
+ self.log.info('Retry turntable fail for '+str(retry)+' cycles, end test!')
+ sys.exit()
+ return 0
+
+ def getwifiinfo(self):
+ """Get WiFi RSSI/ link speed/ frequency for current test.
+
+ Returns:
+ [RSSI,LS,FR]: WiFi RSSI/ link speed/ frequency
+ """
+ def is_number(string):
+ for i in string:
+ if i.isdigit() == False:
+ if (i=="-" or i=="."):
+ continue
+ return str(-1)
+ return string
+
+ try:
+ cmd = "adb shell iw wlan0 link"
+ wifiinfo = utils.subprocess.check_output(cmd,shell=True,
+ timeout=self.TEST_TIMEOUT)
+ # Check RSSI
+ RSSI = wifiinfo.decode("utf-8")[wifiinfo.decode("utf-8").find("signal:") +
+ 7:wifiinfo.decode("utf-8").find("dBm") - 1]
+ RSSI = RSSI.strip(' ')
+ RSSI = is_number(RSSI)
+ # Check link speed
+ LS = wifiinfo.decode("utf-8")[wifiinfo.decode("utf-8").find("bitrate:") +
+ 8:wifiinfo.decode("utf-8").find("Bit/s") - 2]
+ LS = LS.strip(' ')
+ LS = is_number(LS)
+ # Check frequency
+ FR = wifiinfo.decode("utf-8")[wifiinfo.decode("utf-8").find("freq:") +
+ 6:wifiinfo.decode("utf-8").find("freq:") + 10]
+ FR = FR.strip(' ')
+ FR = is_number(FR)
+ except:
+ return -1, -1, -1
+ return [RSSI,LS,FR]
+
+ def post_process_results(self, rvr_result):
+ """Saves JSON formatted results.
+
+ Args:
+ rvr_result: Dict containing attenuation, throughput and other meta
+ data
+ """
+ # Save output as text file
+ data=(rvr_result["test_name"],rvr_result["test_angle"],rvr_result["test_dB"],
+ rvr_result["throughput_TX"][0],rvr_result["throughput_RX"][0],
+ rvr_result["test_RSSI"],rvr_result["test_LS"],rvr_result["test_FR"])
+ self.csv_write(data)
+
+ results_file_path = "{}/{}_angle{}_{}dB.json".format(self.log_path,
+ self.ssid,
+ self.angle[self.ag],self.DB)
+ with open(results_file_path, 'w') as results_file:
+ json.dump(rvr_result, results_file, indent=4)
+
+ def connect_to_wifi_network(self, network):
+ """Connection logic for psk wifi networks.
+
+ Args:
+ params: Dictionary with network info.
+ """
+ SSID = network[WifiEnums.SSID_KEY]
+ self.dut.ed.clear_all_events()
+ wutils.start_wifi_connection_scan(self.dut)
+ scan_results = self.dut.droid.wifiGetScanResults()
+ wutils.assert_network_in_list({WifiEnums.SSID_KEY: SSID}, scan_results)
+ wutils.wifi_connect(self.dut, network, num_of_tries=3)
+
+ def run_iperf_client(self, network):
+ """Run iperf TX throughput after connection.
+
+ Args:
+ params: Dictionary with network info.
+
+ Returns:
+ rvr_result: Dict containing rvr_results
+ """
+ rvr_result = []
+ self.iperf_server.start(tag="TX_server_{}_angle{}_{}dB".format(
+ self.ssid,self.angle[self.ag],self.DB))
+ wait_time = 5
+ SSID = network[WifiEnums.SSID_KEY]
+ self.log.info("Starting iperf traffic TX through {}".format(SSID))
+ time.sleep(wait_time)
+ port_arg = "-p {} -J {}".format(self.iperf_server.port,
+ self.rvr_test_params["iperf_port_arg"])
+ success, data = self.dut.run_iperf_client(
+ self.rvr_test_params["iperf_server_address"],
+ port_arg,
+ timeout=self.rvr_test_params["iperf_duration"] + self.TEST_TIMEOUT)
+ # Parse and log result
+ client_output_path = os.path.join(
+ self.iperf_server.log_path, "IperfDUT,{},TX_client_{}_angle{}_{}dB".format(
+ self.iperf_server.port,self.ssid,self.angle[self.ag],self.DB))
+ with open(client_output_path, 'w') as out_file:
+ out_file.write("\n".join(data))
+ self.iperf_server.stop()
+
+ iperf_file = self.iperf_server.log_files[-1]
+ try:
+ iperf_result = ipf.IPerfResult(iperf_file)
+ curr_throughput = (math.fsum(iperf_result.instantaneous_rates[
+ self.rvr_test_params["iperf_ignored_interval"]:-1]) / len(
+ iperf_result.instantaneous_rates[self.rvr_test_params[
+ "iperf_ignored_interval"]:-1])) * 8 * (1.024**2)
+ except:
+ self.log.warning(
+ "ValueError: Cannot get iperf result. Setting to 0")
+ curr_throughput = 0
+ rvr_result.append(curr_throughput)
+ self.log.info("TX Throughput at {0:.2f} dB is {1:.2f} Mbps".format(
+ self.DB, curr_throughput))
+
+ self.log.debug(pprint.pformat(data))
+ asserts.assert_true(success, "Error occurred in iPerf traffic.")
+ return rvr_result
+
+ def run_iperf_server(self, network):
+ """Run iperf RX throughput after connection.
+
+ Args:
+ params: Dictionary with network info.
+
+ Returns:
+ rvr_result: Dict containing rvr_results
+ """
+ rvr_result = []
+ self.iperf_server.start(tag="RX_client_{}_angle{}_{}dB".format(
+ self.ssid,self.angle[self.ag],self.DB))
+ wait_time = 5
+ SSID = network[WifiEnums.SSID_KEY]
+ self.log.info("Starting iperf traffic RX through {}".format(SSID))
+ time.sleep(wait_time)
+ port_arg = "-p {} -J -R {}".format(self.iperf_server.port,
+ self.rvr_test_params["iperf_port_arg"])
+ success, data = self.dut.run_iperf_client(
+ self.rvr_test_params["iperf_server_address"],
+ port_arg,
+ timeout=self.rvr_test_params["iperf_duration"] + self.TEST_TIMEOUT)
+ # Parse and log result
+ client_output_path = os.path.join(
+ self.iperf_server.log_path, "IperfDUT,{},RX_server_{}_angle{}_{}dB".format(
+ self.iperf_server.port,self.ssid,self.angle[self.ag],self.DB))
+ with open(client_output_path, 'w') as out_file:
+ out_file.write("\n".join(data))
+ self.iperf_server.stop()
+
+ iperf_file = client_output_path
+ try:
+ iperf_result = ipf.IPerfResult(iperf_file)
+ curr_throughput = (math.fsum(iperf_result.instantaneous_rates[
+ self.rvr_test_params["iperf_ignored_interval"]:-1]) / len(
+ iperf_result.instantaneous_rates[self.rvr_test_params[
+ "iperf_ignored_interval"]:-1])) * 8 * (1.024**2)
+ except:
+ self.log.warning(
+ "ValueError: Cannot get iperf result. Setting to 0")
+ curr_throughput = 0
+ rvr_result.append(curr_throughput)
+ self.log.info("RX Throughput at {0:.2f} dB is {1:.2f} Mbps".format(
+ self.DB, curr_throughput))
+
+ self.log.debug(pprint.pformat(data))
+ asserts.assert_true(success, "Error occurred in iPerf traffic.")
+ return rvr_result
+
+ def iperf_test_func(self,network):
+ """Main function to test iperf TX/RX.
+
+ Args:
+ params: Dictionary with network info
+ """
+ if "rvr_test_params" in self.user_params:
+ # Initialize
+ rvr_result = {}
+ # Run RvR and log result
+ wifiinfo = self.getwifiinfo()
+ rvr_result["throughput_TX"] = self.run_iperf_client(network)
+ rvr_result["throughput_RX"] = self.run_iperf_server(network)
+ rvr_result["test_name"] = self.ssid
+ rvr_result["test_angle"] = self.angle[self.ag]
+ rvr_result["test_dB"] = self.DB
+ rvr_result["test_RSSI"] = wifiinfo[0]
+ rvr_result["test_LS"] = wifiinfo[1]
+ rvr_result["test_FR"] = wifiinfo[2]
+ self.post_process_results(rvr_result)
+
+ def rvr_test(self,network):
+ """Test function to run RvR.
+
+ The function runs an RvR test in the current device/AP configuration.
+ Function is called from another wrapper function that sets up the
+ testbed for the RvR test
+
+ Args:
+ params: Dictionary with network info
+ """
+ wait_time = 5
+ utils.subprocess.check_output('adb root', shell=True, timeout=20)
+ self.ssid = network[WifiEnums.SSID_KEY]
+ self.log.info("Start rvr test")
+ for i in range(len(self.angle)):
+ self.setDG(self.T1,self.angle[i])
+ time.sleep(wait_time)
+ self.checkDG(self.T1,self.angle[i])
+ self.set_Three_Att_dB(self.ATT1,self.ATT2,self.ATT3,0)
+ time.sleep(wait_time)
+ self.connect_to_wifi_network(network)
+ self.set_Three_Att_dB(self.ATT1,self.ATT2,self.ATT3,self.MindB)
+ for j in range(self.MindB,self.MaxdB+self.stepdB,self.stepdB):
+ self.DB=j
+ self.ag=i
+ self.set_Three_Att_dB(self.ATT1,self.ATT2,self.ATT3,self.DB)
+ self.iperf_test_func(network)
+ wutils.reset_wifi(self.dut)
+
+ """Tests"""
+
+ @test_tracker_info(uuid="93816af8-4c63-45f8-b296-cb49fae0b158")
+ def test_iot_connection_to_RVR_2G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.rvr_test(self.ssid_map[ssid_key])
+
+ @test_tracker_info(uuid="e1a67e13-946f-4d91-aa73-3f945438a1ac")
+ def test_iot_connection_to_RVR_5G(self):
+ ssid_key = self.current_test_name.replace(self.iot_test_prefix, "")
+ self.rvr_test(self.ssid_map[ssid_key])
\ No newline at end of file
diff --git a/acts_tests/tests/google/wifi/WifiScannerBssidTest.py b/acts_tests/tests/google/wifi/WifiScannerBssidTest.py
new file mode 100644
index 0000000..e91c449
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiScannerBssidTest.py
@@ -0,0 +1,482 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2016 - 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 queue
+
+from acts import asserts
+from acts import base_test
+from acts import utils
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi import wifi_test_utils as wutils
+
+BSSID_EVENT_WAIT = 30
+
+BSSID_EVENT_TAG = "WifiScannerBssid"
+SCAN_EVENT_TAG = "WifiScannerScan"
+SCANTIME = 10000 #framework support only 10s as minimum scan interval
+
+
+class WifiScannerBssidError(Exception):
+ pass
+
+
+class WifiScannerBssidTest(base_test.BaseTestClass):
+ def __init__(self, controllers):
+ base_test.BaseTestClass.__init__(self, controllers)
+ # A list of all test cases to be executed in this class.
+ self.tests = ("test_wifi_track_bssid_sanity",
+ "test_wifi_track_bssid_found",
+ "test_wifi_track_bssid_lost",
+ "test_wifi_track_bssid_for_2g_while_scanning_5g_channels",
+ "test_wifi_track_bssid_for_5g_while_scanning_2g_channels",)
+
+ def setup_class(self):
+ self.default_scan_setting = {
+ "band": wutils.WifiEnums.WIFI_BAND_BOTH_WITH_DFS,
+ "periodInMs": SCANTIME,
+ "reportEvents": wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+ 'numBssidsPerScan': 32
+ }
+ self.leeway = 5
+ self.stime_channel = 47 #dwell time plus 2ms
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ self.attenuators = wutils.group_attenuators(self.attenuators)
+ asserts.assert_true(self.dut.droid.wifiIsScannerSupported(),
+ "Device %s doesn't support WifiScanner, abort." %
+ self.dut.model)
+ """It will setup the required dependencies and fetch the user params from
+ config file"""
+ self.attenuators[0].set_atten(0)
+ self.attenuators[1].set_atten(0)
+ req_params = ("bssid_2g", "bssid_5g", "bssid_dfs", "attenuator_id",
+ "max_bugreports")
+ self.wifi_chs = wutils.WifiChannelUS(self.dut.model)
+ self.unpack_userparams(req_params, two_ap_testbed=False)
+
+ def teardown_class(self):
+ BaseTestClass.teardown_test(self)
+ self.log.debug("Shut down all wifi scanner activities.")
+ self.dut.droid.wifiScannerShutdown()
+
+ def on_fail(self, test_name, begin_time):
+ if self.max_bugreports > 0:
+ self.dut.take_bug_report(test_name, begin_time)
+ self.max_bugreports -= 1
+
+ """ Helper Functions Begin """
+
+ def fetch_scan_result(self, scan_idx, scan_setting):
+ """Fetch the scan result for provider listener index.
+
+ This function calculate the time required for scanning based on scan setting
+ and wait for scan result event, on triggering of event process the scan result.
+
+ Args:
+ scan_idx: Index of the scan listener.
+ scan_setting: Setting used for starting the scan.
+
+ Returns:
+ scan_results: if scan result available.
+ """
+ #generating event wait time from scan setting plus leeway
+ self.log.debug(scan_setting)
+ scan_time, scan_channels = wutils.get_scan_time_and_channels(
+ self.wifi_chs, scan_setting, self.stime_channel)
+ scan_time += scan_setting['periodInMs'
+ ] #add scan period delay for next cycle
+ if scan_setting[
+ "reportEvents"] == wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN:
+ waittime = int(scan_time / 1000) + self.leeway
+ else:
+ time_cache = scan_setting['periodInMs'] * 10 #default cache
+ waittime = int((time_cache + scan_time) / 1000) + self.leeway
+ event_name = "%s%sonResults" % (SCAN_EVENT_TAG, scan_idx)
+ self.log.info("Waiting for the scan result event %s", event_name)
+ event = self.dut.ed.pop_event(event_name, waittime)
+ results = event["data"]["Results"]
+ if len(results) > 0 and "ScanResults" in results[0]:
+ return results[0]["ScanResults"]
+
+ def start_scan_and_validate_environment(self, scan_setting,
+ bssid_settings):
+ """Validate environment for test using current scan result for provided
+ settings.
+
+ This function start the scan for given setting and verify that interested
+ Bssids are in scan result or not.
+
+ Args:
+ scan_setting: Setting used for starting the scan.
+ bssid_settings: list of bssid settings.
+
+ Returns:
+ True, if bssid not found in scan result.
+ """
+ try:
+ data = wutils.start_wifi_background_scan(self.dut, scan_setting)
+ self.scan_idx = data["Index"]
+ results = self.fetch_scan_result(self.scan_idx, scan_setting)
+ self.log.debug("scan result %s.", results)
+ asserts.assert_true(results,
+ "Device is not able to fetch the scan results")
+ for result in results:
+ for bssid_setting in bssid_settings:
+ if bssid_setting[wutils.WifiEnums.BSSID_KEY] == result[
+ wutils.WifiEnums.BSSID_KEY]:
+ asserts.fail(("Test environment is not valid: Bssid %s"
+ "already exist in current scan results")
+ % result[wutils.WifiEnums.BSSID_KEY])
+ except queue.Empty as error:
+ self.dut.droid.wifiScannerStopBackgroundScan(self.scan_idx)
+ raise AssertionError(
+ "OnResult event did not triggered for scanner\n%s" % error)
+
+ def check_bssid_in_found_result(self, bssid_settings, found_results):
+ """look for any tracked bssid in reported result of found bssids.
+
+ Args:
+ bssid_settings:Setting used for tracking bssids.
+ found_results: Result reported in found event.
+
+ Returns:
+ True if bssid is present in result.
+ """
+ for bssid_setting in bssid_settings:
+ for found_result in found_results:
+ if found_result[wutils.WifiEnums.BSSID_KEY] == bssid_setting[
+ wutils.WifiEnums.BSSID_KEY]:
+ return
+ asserts.fail("Test fail because Bssid %s is not found in event results"
+ % bssid_settings)
+
+ def track_bssid_with_vaild_scan_for_found(self, track_setting):
+ """Common logic for tracking a bssid for Found event.
+
+ 1. Starts Wifi Scanner bssid tracking for interested bssids in track_setting.
+ 2. Start Wifi Scanner scan with default scan settings.
+ 3. Validate the environment to check AP is not in range.
+ 4. Attenuate the signal to make AP in range.
+ 5. Verified that onFound event is triggered for interested bssids in
+ track setting.
+
+ Args:
+ track_setting: Setting for bssid tracking.
+
+ Returns:
+ True if found event occur for interested BSSID.
+ """
+ self.attenuators[self.attenuator_id].set_atten(90)
+ data = wutils.start_wifi_track_bssid(self.dut, track_setting)
+ idx = data["Index"]
+ self.start_scan_and_validate_environment(self.default_scan_setting,
+ track_setting["bssidInfos"])
+ try:
+ self.attenuators[self.attenuator_id].set_atten(0)
+ event_name = "%s%sonFound" % (BSSID_EVENT_TAG, idx)
+ self.log.info("Waiting for the BSSID event %s", event_name)
+ event = self.dut.ed.pop_event(event_name, BSSID_EVENT_WAIT)
+ self.log.debug(event)
+ self.check_bssid_in_found_result(track_setting["bssidInfos"],
+ event["data"]["Results"])
+ except queue.Empty as error:
+ self.log.error(error)
+ # log scan result for debugging
+ results = self.fetch_scan_result(self.scan_idx,
+ self.default_scan_setting)
+ self.log.debug("scan result %s", results)
+ raise AssertionError("Event %s did not triggered for %s\n%s" %
+ (event_name, track_setting["bssidInfos"],
+ error))
+ finally:
+ self.dut.droid.wifiScannerStopBackgroundScan(self.scan_idx)
+ self.dut.droid.wifiScannerStopTrackingBssids(idx)
+
+ def track_bssid_with_vaild_scan_for_lost(self, track_setting):
+ """Common logic for tracking a bssid for Lost event.
+
+ 1. Start Wifi Scanner scan with default scan settings.
+ 2. Validate the environment to check AP is not in range.
+ 3. Starts Wifi Scanner bssid tracking for interested bssids in track_setting.
+ 4. Attenuate the signal to make Bssids in range.
+ 5. Verified that onFound event is triggered for interested bssids in
+ track setting.
+ 6. Attenuate the signal to make Bssids out of range.
+ 7. Verified that onLost event is triggered.
+
+ Args:
+ track_setting: Setting for bssid tracking.
+ scan_setting: Setting used for starting the scan.
+
+ Returns:
+ True if Lost event occur for interested BSSID.
+ """
+ self.attenuators[self.attenuator_id].set_atten(90)
+ self.start_scan_and_validate_environment(self.default_scan_setting,
+ track_setting["bssidInfos"])
+ idx = None
+ found = False
+ try:
+ data = wutils.start_wifi_track_bssid(self.dut, track_setting)
+ idx = data["Index"]
+ self.attenuators[self.attenuator_id].set_atten(0)
+ #onFound event should be occurre before tracking for onLost event
+ event_name = "%s%sonFound" % (BSSID_EVENT_TAG, idx)
+ self.log.info("Waiting for the BSSID event %s", event_name)
+ event = self.dut.ed.pop_event(event_name, BSSID_EVENT_WAIT)
+ self.log.debug(event)
+ self.check_bssid_in_found_result(track_setting["bssidInfos"],
+ event["data"]["Results"])
+ self.attenuators[self.attenuator_id].set_atten(90)
+ # log scan result for debugging
+ for i in range(1, track_setting["apLostThreshold"]):
+ results = self.fetch_scan_result(self.scan_idx,
+ self.default_scan_setting)
+ self.log.debug("scan result %s %s", i, results)
+ event_name = "%s%sonLost" % (BSSID_EVENT_TAG, idx)
+ self.log.info("Waiting for the BSSID event %s", event_name)
+ event = self.dut.ed.pop_event(event_name, BSSID_EVENT_WAIT)
+ self.log.debug(event)
+ except queue.Empty as error:
+ raise AssertionError("Event %s did not triggered for %s\n%s" %
+ (event_name, track_setting["bssidInfos"],
+ error))
+ finally:
+ self.dut.droid.wifiScannerStopBackgroundScan(self.scan_idx)
+ if idx:
+ self.dut.droid.wifiScannerStopTrackingBssids(idx)
+
+ def wifi_generate_track_bssid_settings(self, isLost):
+ """Generates all the combinations of different track setting parameters.
+
+ Returns:
+ A list of dictionaries each representing a set of track settings.
+ """
+ bssids = [[self.bssid_2g], [self.bssid_5g],
+ [self.bssid_2g, self.bssid_5g]]
+ if self.dut.model != "hammerhead" or not self.two_ap_testbed:
+ bssids.append([self.bssid_dfs])
+ if isLost:
+ apthreshold = (3, 5)
+ else:
+ apthreshold = (1, )
+ # Create track setting strings based on the combinations
+ setting_combinations = list(itertools.product(bssids, apthreshold))
+ # Create scan setting strings based on the combinations
+ track_settings = []
+ for combo in setting_combinations:
+ s = {}
+ s["bssidInfos"] = combo[0]
+ s["apLostThreshold"] = combo[1]
+ track_settings.append(s)
+ return track_settings
+
+ def track_setting_to_string(self, track_setting):
+ """Convert track setting to string for Bssids in that"""
+ string = ""
+ for bssid_setting in track_setting:
+ string += bssid_setting[wutils.WifiEnums.BSSID_KEY]
+ string += "_"
+ return string
+
+ def combineBssids(self, *track_settings):
+ """Combine bssids in the track_settings to one list"""
+ bssids = []
+ for track_setting in track_settings:
+ bssids.extend(track_setting["bssidInfos"])
+ return bssids
+
+ """ Helper Functions End """
+ """ Tests Begin """
+
+ @test_tracker_info(uuid="599a30b8-73ad-4314-a245-7ec58fc7e74b")
+ def test_wifi_track_bssid_found(self):
+ """Test bssid track for event found with a list of different settings.
+
+ 1. Starts Wifi Scanner bssid tracking for interested bssids in track_setting.
+ 2. Start Wifi Scanner scan with default scan settings.
+ 3. Validate the environment to check AP is not in range.
+ 4. Attenuate the signal to make AP in range.
+ 5. Verified that onFound event is triggered for interested bssids in
+ track setting.
+ """
+ track_settings = self.wifi_generate_track_bssid_settings(False)
+ name_func = lambda track_setting: "test_wifi_track_found_bssidInfos_%sapLostThreshold_%s" % (self.track_setting_to_string(track_setting["bssidInfos"]), track_setting["apLostThreshold"])
+ failed = self.run_generated_testcases(
+ self.track_bssid_with_vaild_scan_for_found,
+ track_settings,
+ name_func=name_func)
+ asserts.assert_false(
+ failed, "Track bssid found failed with these bssids: %s" % failed)
+
+ @test_tracker_info(uuid="7ebd4b61-c408-45b3-b9b6-098753d46aa7")
+ def test_wifi_track_bssid_lost(self):
+ """Test bssid track for event lost with a list of different settings.
+
+ 1. Start Wifi Scanner scan with default scan settings.
+ 2. Validate the environment to check AP is not in range.
+ 3. Starts Wifi Scanner bssid tracking for interested bssids in track_setting.
+ 4. Attenuate the signal to make Bssids in range.
+ 5. Verified that onFound event is triggered for interested bssids in
+ track setting.
+ 6. Attenuate the signal to make Bssids out of range.
+ 7. Verified that onLost event is triggered.
+ """
+ track_settings = self.wifi_generate_track_bssid_settings(True)
+ name_func = lambda track_setting: "test_wifi_track_lost_bssidInfos_%sapLostThreshold_%s" % (self.track_setting_to_string(track_setting["bssidInfos"]), track_setting["apLostThreshold"])
+ failed = self.run_generated_testcases(
+ self.track_bssid_with_vaild_scan_for_lost,
+ track_settings,
+ name_func=name_func)
+ asserts.assert_false(
+ failed, "Track bssid lost failed with these bssids: %s" % failed)
+
+ def test_wifi_track_bssid_sanity(self):
+ """Test bssid track for event found and lost with default settings.
+
+ 1. Start WifiScanner scan for default scan settings.
+ 2. Start Bssid track for "bssid_2g" AP.
+ 3. Attenuate the signal to move in AP range.
+ 4. Verify that onFound event occur.
+ 5. Attenuate the signal to move out of range
+ 6. Verify that onLost event occur.
+ """
+ track_setting = {"bssidInfos": [self.bssid_2g], "apLostThreshold": 3}
+ self.track_bssid_with_vaild_scan_for_lost(track_setting)
+
+ def test_wifi_track_bssid_for_2g_while_scanning_5g_channels(self):
+ """Test bssid track for 2g bssids while scanning 5g channels.
+
+ 1. Starts Wifi Scanner bssid tracking for 2g bssids in track_setting.
+ 2. Start Wifi Scanner scan for 5G Band only.
+ 3. Validate the environment to check AP is not in range.
+ 4. Attenuate the signal to make AP in range.
+ 5. Verified that onFound event isn't triggered for 2g bssids.
+ """
+ self.attenuators[self.attenuator_id].set_atten(90)
+ scan_setting = {"band": wutils.WifiEnums.WIFI_BAND_5_GHZ,
+ "periodInMs": SCANTIME,
+ "reportEvents":
+ wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+ "numBssidsPerScan": 32}
+ track_setting = {"bssidInfos": [self.bssid_2g], "apLostThreshold": 3}
+ self.start_scan_and_validate_environment(scan_setting,
+ track_setting["bssidInfos"])
+ idx = None
+ try:
+ data = wutils.start_wifi_track_bssid(self.dut, track_setting)
+ idx = data["Index"]
+ self.attenuators[self.attenuator_id].set_atten(0)
+ event_name = "%s%sonFound" % (BSSID_EVENT_TAG, idx)
+ self.log.info("Waiting for the BSSID event %s", event_name)
+ #waiting for 2x time to make sure
+ event = self.dut.ed.pop_event(event_name, BSSID_EVENT_WAIT * 2)
+ self.log.debug(event)
+ self.check_bssid_in_found_result(track_setting["bssidInfos"],
+ event["data"]["Results"])
+ except queue.Empty as error:
+ self.log.info(
+ "As excepted event didn't occurred with different scan setting")
+ finally:
+ self.dut.droid.wifiScannerStopBackgroundScan(self.scan_idx)
+ if idx:
+ self.dut.droid.wifiScannerStopTrackingBssids(idx)
+
+ def test_wifi_track_bssid_for_5g_while_scanning_2g_channels(self):
+ """Test bssid track for 5g bssids while scanning 2g channels.
+
+ 1. Starts Wifi Scanner bssid tracking for 5g bssids in track_setting.
+ 2. Start Wifi Scanner scan for 2G Band only.
+ 3. Validate the environment to check AP is not in range.
+ 4. Attenuate the signal to make AP in range.
+ 5. Verified that onFound event isn't triggered for 5g bssids.
+ """
+ self.attenuators[self.attenuator_id].set_atten(90)
+ scan_setting = {"band": wutils.WifiEnums.WIFI_BAND_24_GHZ,
+ "periodInMs": SCANTIME,
+ "reportEvents":
+ wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+ "numBssidsPerScan": 32}
+ track_setting = {"bssidInfos": [self.bssid_5g], "apLostThreshold": 3}
+ data = wutils.start_wifi_track_bssid(self.dut, track_setting)
+ idx = data["Index"]
+ self.start_scan_and_validate_environment(scan_setting,
+ track_setting["bssidInfos"])
+ try:
+ self.attenuators[self.attenuator_id].set_atten(0)
+ event_name = "%s%sonFound" % (BSSID_EVENT_TAG, idx)
+ self.log.info("Waiting for the BSSID event %s", event_name)
+ #waiting for 2x time to make sure
+ event = self.dut.ed.pop_event(event_name, BSSID_EVENT_WAIT * 2)
+ self.log.debug(event)
+ self.check_bssid_in_found_result(track_setting["bssidInfos"],
+ event["data"]["Results"])
+ except queue.Empty as error:
+ self.log.info(
+ "As excepted event didn't occurred with different scan setting")
+ finally:
+ self.dut.droid.wifiScannerStopBackgroundScan(self.scan_idx)
+ if idx:
+ self.dut.droid.wifiScannerStopTrackingBssids(idx)
+
+ def test_wifi_tracking_bssid_multi_listeners_found(self):
+ """Test bssid tracking for multiple listeners
+ 1. Start BSSID tracking for 5g bssids
+ 2. Start BSSID tracking for 2g bssids
+ 3. Start WifiScanner scan on both bands.
+ 4. Valid the environment and check the APs are not in range.
+ 5. Attenuate the signal to make the APs in range.
+ 6. Verify onFound event triggered on both APs.
+ """
+ # Attenuate the signal to make APs invisible.
+ self.attenuators[self.attenuator_id].set_atten(90)
+ scan_setting = { "band": WifiEnums.WIFI_BAND_BOTH_WITH_DFS,
+ "periodInMs": SCANTIME,
+ "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+ "numBssidsPerScan": 32}
+ track_setting_5g = {"bssidInfos":[self.bssid_5g], "apLostThreshold":3}
+ data_5g = start_wifi_track_bssid(self.dut, track_setting_5g)
+ idx_5g = data_5g["Index"]
+
+ track_setting_2g = {"bssidInfos":[self.bssid_2g], "apLostThreshold":3}
+ data_2g = start_wifi_track_bssid(self.dut, track_setting_2g)
+ idx_2g = data_2g["Index"]
+
+ valid_env = self.start_scan_and_validate_environment(
+ scan_setting, self.combineBssids(track_setting_5g, track_setting_2g))
+ try:
+ asserts.assert_true(valid_env,
+ "Test environment is not valid, AP is in range")
+ self.attenuators[self.attenuator_id].set_atten(0)
+ event_name = "{}{}{}{}onFound".format(BSSID_EVENT_TAG, idx_5g, BSSID_EVENT_TAG, idx_2g)
+ self.log.info("Waiting for the BSSID event {}".format(event_name))
+ #waiting for 2x time to make sure
+ event = self.dut.ed.pop_event(event_name, BSSID_EVENT_WAIT * 2)
+ self.log.debug(event)
+ found = self.check_bssid_in_found_result(
+ self.combineBssids(track_setting_5g, track_setting_2g),
+ event["data"]["Results"])
+ asserts.assert_true(found,
+ "Test failed because Bssid onFound event is not triggered")
+ finally:
+ self.dut.droid.wifiScannerStopBackgroundScan(self.scan_idx)
+ if idx_5g:
+ self.dut.droid.wifiScannerStopTrackingBssids(idx_5g)
+ if idx_2g:
+ self.dut.droid.wifiScannerStopTrackingBssids(idx_2g);
+
+""" Tests End """
diff --git a/acts_tests/tests/google/wifi/WifiScannerMultiScanTest.py b/acts_tests/tests/google/wifi/WifiScannerMultiScanTest.py
new file mode 100755
index 0000000..da88012
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiScannerMultiScanTest.py
@@ -0,0 +1,650 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2016 - 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 queue
+import time
+
+from acts import asserts
+from acts import base_test
+from acts import signals
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+WifiChannelUS = wutils.WifiChannelUS
+WifiEnums = wutils.WifiEnums
+
+SCAN_EVENT_TAG = "WifiScannerScan"
+
+
+class WifiScanResultEvents():
+ """This class stores the setting of a scan, parameters generated
+ from starting the scan, and events reported later from the scan
+ for validation.
+
+ Attributes:
+ scan_setting: Setting used to perform the scan.
+ scan_channels: Channels used for scanning.
+ events: A list to store the scan result events.
+ """
+
+ def __init__(self, scan_setting, scan_channels):
+ self.scan_setting = scan_setting
+ self.scan_channels = scan_channels
+ self.results_events = []
+
+ def add_results_event(self, event):
+ self.results_events.append(event)
+
+ def check_interval(self, scan_result, scan_result_next):
+ """Verifies that the time gap between two consecutive results is within
+ expected range.
+
+ Right now it is hard coded to be 20 percent of the interval specified
+ by scan settings. This threshold can be imported from the configuration
+ file in the future if necessary later.
+
+ Note the scan result timestamps are in microseconds, but "periodInMs"
+ in scan settings is in milliseconds.
+
+ Args:
+ scan_result: A dictionary representing a scan result for a BSSID.
+ scan_result_next: A dictionary representing a scan result for a
+ BSSID, whose scan happened after scan_result.
+ """
+ actual_interval = scan_result_next["timestamp"] - scan_result[
+ "timestamp"]
+ expected_interval = self.scan_setting['periodInMs'] * 1000
+ delta = abs(actual_interval - expected_interval)
+ margin = expected_interval * 0.25 # 25% of the expected_interval
+ asserts.assert_true(
+ delta < margin, "The difference in time between scan %s and "
+ "%s is %dms, which is out of the expected range %sms" % (
+ scan_result, scan_result_next, delta / 1000,
+ self.scan_setting['periodInMs']))
+
+ def verify_one_scan_result(self, scan_result):
+ """Verifies the scan result of a single BSSID.
+
+ 1. Verifies the frequency of the network is within the range requested
+ in the scan.
+
+ Args:
+ scan_result: A dictionary representing the scan result of a single
+ BSSID.
+ """
+ freq = scan_result["frequency"]
+ asserts.assert_true(
+ freq in self.scan_channels,
+ "Frequency %d of result entry %s is out of the expected range %s."
+ % (freq, scan_result, self.scan_channels))
+ # TODO(angli): add RSSI check.
+
+ def verify_one_scan_result_group(self, batch):
+ """Verifies a group of scan results obtained during one scan.
+
+ 1. Verifies the number of BSSIDs in the batch is less than the
+ threshold set by scan settings.
+ 2. Verifies each scan result for individual BSSID.
+
+ Args:
+ batch: A list of dictionaries, each dictionary represents a scan
+ result.
+ """
+ scan_results = batch["ScanResults"]
+ actual_num_of_results = len(scan_results)
+ expected_num_of_results = self.scan_setting['numBssidsPerScan']
+ asserts.assert_true(actual_num_of_results <= expected_num_of_results,
+ "Expected no more than %d BSSIDs, got %d." %
+ (expected_num_of_results, actual_num_of_results))
+ for scan_result in scan_results:
+ self.verify_one_scan_result(scan_result)
+
+ def have_enough_events(self):
+ """Check if there are enough events to properly validate the scan"""
+ return len(self.results_events) >= 2
+
+ def check_scan_results(self):
+ """Validate the reported scan results against the scan settings.
+ Assert if any error detected in the results.
+
+ 1. For each scan setting there should be no less than 2 events received.
+ 2. For batch scan, the number of buffered results in each event should
+ be exactly what the scan setting specified.
+ 3. Each scan result should contain no more BBSIDs than what scan
+ setting specified.
+ 4. The frequency reported by each scan result should comply with its
+ scan setting.
+ 5. The time gap between two consecutive scan results should be
+ approximately equal to the scan interval specified by the scan
+ setting.
+ A scan result looks like this:
+ {
+ 'data':
+ {
+ 'Type': 'onResults',
+ 'ResultElapsedRealtime': 4280931,
+ 'Index': 10,
+ 'Results': [
+ {
+ 'Flags': 0,
+ 'Id': 4,
+ 'ScanResults':[
+ {
+ 'is80211McRTTResponder': False,
+ 'channelWidth': 0,
+ 'numUsage': 0,
+ 'SSID': '"wh_ap1_2g"',
+ 'timestamp': 4280078660,
+ 'BSSID': '30:b5:c2:33:f9:05',
+ 'frequency': 2412,
+ 'distanceSdCm': 0,
+ 'distanceCm': 0,
+ 'centerFreq1': 0,
+ 'centerFreq0': 0,
+ 'venueName': '',
+ 'seen': 0,
+ 'operatorFriendlyName': '',
+ 'level': -31,
+ 'passpointNetwork': False,
+ 'untrusted': False
+ }
+ ]
+ }
+ ]
+ },
+ 'time': 1491744576383,
+ 'name': 'WifiScannerScan10onResults'
+ }
+ """
+ num_of_events = len(self.results_events)
+ asserts.assert_true(
+ num_of_events >= 2,
+ "Expected more than one scan result events, got %d." %
+ num_of_events)
+ for event_idx in range(num_of_events):
+ batches = self.results_events[event_idx]["data"]["Results"]
+ actual_num_of_batches = len(batches)
+ if not actual_num_of_batches:
+ raise signals.TestFailure("Scan returned empty Results list %s "
+ "% batches")
+ # For batch scan results.
+ report_type = self.scan_setting['reportEvents']
+ if not (report_type & WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN):
+ # Verifies that the number of buffered results matches the
+ # number defined in scan settings.
+ expected_num_of_batches = self.scan_setting['maxScansToCache']
+ asserts.assert_true(
+ actual_num_of_batches <= expected_num_of_batches,
+ "Expected to get at most %d batches in event No.%d, got %d."
+ % (expected_num_of_batches, event_idx,
+ actual_num_of_batches))
+ # Check the results within each event of batch scan
+ for batch_idx in range(actual_num_of_batches):
+ if not len(batches[batch_idx]["ScanResults"]):
+ raise signals.TestFailure("Scan event %d returned empty"
+ " scan results in batch %d" % (event_idx, batch_idx))
+ # Start checking interval from the second batch.
+ if batch_idx >=1:
+ self.check_interval(
+ batches[batch_idx - 1]["ScanResults"][0],
+ batches[batch_idx]["ScanResults"][0])
+ for batch in batches:
+ self.verify_one_scan_result_group(batch)
+
+ # Check the time gap between the first result of an event and
+ # the last result of its previous event
+ # Skip the very first event.
+ if event_idx >= 1:
+ previous_batches = self.results_events[event_idx - 1]["data"][
+ "Results"]
+ self.check_interval(previous_batches[-1]["ScanResults"][0],
+ batches[0]["ScanResults"][0])
+
+
+class WifiScannerMultiScanTest(WifiBaseTest):
+ """This class is the WiFi Scanner Multi-Scan Test suite.
+ It collects a number of test cases, sets up and executes
+ the tests, and validates the scan results.
+
+ Attributes:
+ tests: A collection of tests to excute.
+ leeway: Scan interval drift time (in seconds).
+ stime_channels: Dwell time plus 2ms.
+ dut: Android device(s).
+ wifi_chs: WiFi channels according to the device model.
+ """
+
+ def __init__(self, configs):
+ super().__init__(configs)
+ self.enable_packet_log = True
+ self.tests = (
+ 'test_wifi_two_scans_at_same_interval',
+ 'test_wifi_two_scans_at_different_interval',
+ 'test_wifi_scans_24GHz_and_both',
+ 'test_wifi_scans_5GHz_and_both',
+ 'test_wifi_scans_batch_and_24GHz',
+ 'test_wifi_scans_batch_and_5GHz',
+ 'test_wifi_scans_24GHz_5GHz_full_result',)
+
+ def setup_class(self):
+ super().setup_class()
+ self.leeway = 5 # seconds, for event wait time computation
+ self.stime_channel = 47 #dwell time plus 2ms
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ asserts.assert_true(self.dut.droid.wifiIsScannerSupported(),
+ "Device %s doesn't support WifiScanner, abort." %
+ self.dut.model)
+ """ Setup the required dependencies and fetch the user params from
+ config file.
+ """
+ opt_param = ["reference_networks"]
+ self.unpack_userparams(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.wifi_chs = WifiChannelUS(self.dut.model)
+
+ def teardown_class(self):
+ if "AccessPoint" in self.user_params:
+ del self.user_params["reference_networks"]
+ del self.user_params["open_network"]
+
+ """ Helper Functions Begin """
+
+ def start_scan(self, scan_setting):
+ data = wutils.start_wifi_background_scan(self.dut, scan_setting)
+ idx = data["Index"]
+ # Calculate event wait time from scan setting plus leeway
+ scan_time, scan_channels = wutils.get_scan_time_and_channels(
+ self.wifi_chs, scan_setting, self.stime_channel)
+ scan_period = scan_setting['periodInMs']
+ report_type = scan_setting['reportEvents']
+ if report_type & WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN:
+ scan_time += scan_period
+ else:
+ max_scan = scan_setting['maxScansToCache']
+ scan_time += max_scan * scan_period
+ wait_time = scan_time / 1000 + self.leeway
+ return idx, wait_time, scan_channels
+
+ def validate_scan_results(self, scan_results_dict):
+ # 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
+ scan_result_obj.check_scan_results()
+
+ def wait_for_scan_events(self, wait_time_list, scan_results_dict):
+ """Poll for WifiScanner events and record them"""
+
+ # Compute the event wait time
+ event_wait_time = min(wait_time_list)
+
+ # Compute the maximum test time that guarantee that even the scan
+ # which requires the most wait time will receive at least two
+ # results.
+ max_wait_time = max(wait_time_list)
+ max_end_time = time.monotonic() + max_wait_time
+ self.log.debug("Event wait time %s seconds", event_wait_time)
+
+ try:
+ # Wait for scan results on all the caller specified bands
+ event_name = SCAN_EVENT_TAG
+ while True:
+ self.log.debug("Waiting for events '%s' for up to %s seconds",
+ event_name, event_wait_time)
+ events = self.dut.ed.pop_events(event_name, event_wait_time)
+ for event in events:
+ self.log.debug("Event received: %s", event)
+ # Event name is the key to the scan results dictionary
+ actual_event_name = event["name"]
+ asserts.assert_true(
+ actual_event_name in scan_results_dict,
+ "Expected one of these event names: %s, got '%s'." %
+ (scan_results_dict.keys(), actual_event_name))
+
+ # TODO validate full result callbacks also
+ if event["name"].endswith("onResults"):
+ # Append the event
+ scan_results_dict[actual_event_name].add_results_event(
+ event)
+
+ # If we time out then stop waiting for events.
+ if time.monotonic() >= max_end_time:
+ break
+ # If enough scan results have been returned to validate the
+ # results then break early.
+ have_enough_events = True
+ for key in scan_results_dict:
+ if not scan_results_dict[key].have_enough_events():
+ have_enough_events = False
+ if have_enough_events:
+ break
+ except queue.Empty:
+ asserts.fail("Event did not trigger for {} in {} seconds".format(
+ event_name, event_wait_time))
+
+ def scan_and_validate_results(self, scan_settings):
+ """Perform WifiScanner scans and check the scan results
+
+ Procedures:
+ * Start scans for each caller specified setting
+ * Wait for at least two results for each scan
+ * Check the results received for each scan
+ """
+ # Awlays get a clean start
+ self.dut.ed.clear_all_events()
+
+ # Start scanning with the caller specified settings and
+ # compute parameters for receiving events
+ idx_list = []
+ wait_time_list = []
+ scan_results_dict = {}
+
+ try:
+ for scan_setting in scan_settings:
+ self.log.debug(
+ "Scan setting: band %s, interval %s, reportEvents "
+ "%s, numBssidsPerScan %s", scan_setting["band"],
+ scan_setting["periodInMs"], scan_setting["reportEvents"],
+ scan_setting["numBssidsPerScan"])
+ idx, wait_time, scan_chan = self.start_scan(scan_setting)
+ self.log.debug(
+ "Scan started for band %s: idx %s, wait_time %ss, scan_channels %s",
+ scan_setting["band"], idx, wait_time, scan_chan)
+ idx_list.append(idx)
+ wait_time_list.append(wait_time)
+
+ report_type = scan_setting['reportEvents']
+ scan_results_events = WifiScanResultEvents(scan_setting,
+ scan_chan)
+ scan_results_dict["{}{}onResults".format(
+ SCAN_EVENT_TAG, idx)] = scan_results_events
+ if (scan_setting['reportEvents']
+ & WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT):
+ scan_results_dict["{}{}onFullResult".format(
+ SCAN_EVENT_TAG, idx)] = scan_results_events
+
+ self.wait_for_scan_events(wait_time_list, scan_results_dict)
+
+ # Validate the scan results
+ self.validate_scan_results(scan_results_dict)
+
+ finally:
+ # Tear down and clean up
+ for idx in idx_list:
+ self.dut.droid.wifiScannerStopBackgroundScan(idx)
+ self.dut.ed.clear_all_events()
+
+ """ Helper Functions End """
+ """ Tests Begin """
+
+ @test_tracker_info(uuid="d490b146-5fc3-4fc3-9958-78ba0ad63211")
+ @WifiBaseTest.wifi_test_wrap
+ def test_wifi_two_scans_at_same_interval(self):
+ """Perform two WifiScanner background scans, one at 2.4GHz and the other
+ at 5GHz, the same interval and number of BSSIDs per scan.
+
+ Initial Conditions:
+ * Set multiple APs broadcasting 2.4GHz and 5GHz.
+
+ Expected Results:
+ * DUT reports success for starting both scans
+ * Scan results for each callback contains only the results on the
+ frequency scanned
+ * Wait for at least two scan results and confirm that separation
+ between them approximately equals to the expected interval
+ * Number of BSSIDs doesn't exceed
+ """
+ scan_settings = [{"band": WifiEnums.WIFI_BAND_24_GHZ,
+ "periodInMs": 10000, # ms
+ "reportEvents":
+ WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+ "numBssidsPerScan": 24},
+ {"band": WifiEnums.WIFI_BAND_5_GHZ,
+ "periodInMs": 10000, # ms
+ "reportEvents":
+ WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+ "numBssidsPerScan": 24}]
+
+ self.scan_and_validate_results(scan_settings)
+
+ @test_tracker_info(uuid="0ec9a554-f942-41a9-8096-6b0b400f60b0")
+ @WifiBaseTest.wifi_test_wrap
+ def test_wifi_two_scans_at_different_interval(self):
+ """Perform two WifiScanner background scans, one at 2.4GHz and the other
+ at 5GHz, different interval and number of BSSIDs per scan.
+
+ Initial Conditions:
+ * Set multiple APs broadcasting 2.4GHz and 5GHz.
+
+ Expected Results:
+ * DUT reports success for starting both scans
+ * Scan results for each callback contains only the results on the
+ frequency scanned
+ * Wait for at least two scan results and confirm that separation
+ between them approximately equals to the expected interval
+ * Number of BSSIDs doesn't exceed
+ """
+ scan_settings = [{"band": WifiEnums.WIFI_BAND_24_GHZ,
+ "periodInMs": 10000, # ms
+ "reportEvents":
+ WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+ "numBssidsPerScan": 20},
+ {"band": WifiEnums.WIFI_BAND_5_GHZ,
+ "periodInMs": 30000, # ms
+ "reportEvents":
+ WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+ "numBssidsPerScan": 24}]
+
+ self.scan_and_validate_results(scan_settings)
+
+ @test_tracker_info(uuid="0d616591-0d32-4be6-8fd4-e4a5e9ccdce0")
+ @WifiBaseTest.wifi_test_wrap
+ def test_wifi_scans_24GHz_and_both(self):
+ """Perform two WifiScanner background scans, one at 2.4GHz and
+ the other at both 2.4GHz and 5GHz
+
+ Initial Conditions:
+ * Set multiple APs broadcasting 2.4GHz and 5GHz.
+
+ Expected Results:
+ * DUT reports success for starting both scans
+ * Scan results for each callback contains only the results on the
+ frequency scanned
+ * Wait for at least two scan results and confirm that separation
+ between them approximately equals to the expected interval
+ * Number of BSSIDs doesn't exceed
+ """
+ scan_settings = [{"band": WifiEnums.WIFI_BAND_BOTH,
+ "periodInMs": 10000, # ms
+ "reportEvents":
+ WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+ "numBssidsPerScan": 24},
+ {"band": WifiEnums.WIFI_BAND_24_GHZ,
+ "periodInMs": 10000, # ms
+ "reportEvents":
+ WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+ "numBssidsPerScan": 24}]
+
+ self.scan_and_validate_results(scan_settings)
+
+ @test_tracker_info(uuid="ddcf959e-512a-4e86-b3d3-18cebd0b22a0")
+ @WifiBaseTest.wifi_test_wrap
+ def test_wifi_scans_5GHz_and_both(self):
+ """Perform two WifiScanner scans, one at 5GHz and the other at both
+ 2.4GHz and 5GHz
+
+ Initial Conditions:
+ * Set multiple APs broadcasting 2.4GHz and 5GHz.
+
+ Expected Results:
+ * DUT reports success for starting both scans
+ * Scan results for each callback contains only the results on the
+ frequency scanned
+ * Wait for at least two scan results and confirm that separation
+ between them approximately equals to the expected interval
+ * Number of BSSIDs doesn't exceed
+ """
+ scan_settings = [{"band": WifiEnums.WIFI_BAND_BOTH,
+ "periodInMs": 10000, # ms
+ "reportEvents":
+ WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+ "numBssidsPerScan": 24},
+ {"band": WifiEnums.WIFI_BAND_5_GHZ,
+ "periodInMs": 10000, # ms
+ "reportEvents":
+ WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+ "numBssidsPerScan": 24}]
+
+ self.scan_and_validate_results(scan_settings)
+
+ @test_tracker_info(uuid="060469f1-fc6b-4255-ab6e-b1d5b54db53d")
+ @WifiBaseTest.wifi_test_wrap
+ def test_wifi_scans_24GHz_5GHz_and_DFS(self):
+ """Perform three WifiScanner scans, one at 5GHz, one at 2.4GHz and the
+ other at just 5GHz DFS channels
+
+ Initial Conditions:
+ * Set multiple APs broadcasting 2.4GHz and 5GHz.
+
+ Expected Results:
+ * DUT reports success for starting both scans
+ * Scan results for each callback contains only the results on the
+ frequency scanned
+ * Wait for at least two scan results and confirm that separation
+ between them approximately equals to the expected interval
+ * Number of BSSIDs doesn't exceed
+ """
+ scan_settings = [
+ {"band": WifiEnums.WIFI_BAND_5_GHZ_DFS_ONLY,
+ "periodInMs": 10000, # ms
+ "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+ "numBssidsPerScan": 24},
+ {"band": WifiEnums.WIFI_BAND_5_GHZ,
+ "periodInMs": 10000, # ms
+ "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+ "numBssidsPerScan": 24},
+ {"band": WifiEnums.WIFI_BAND_24_GHZ,
+ "periodInMs": 30000, # ms
+ "reportEvents": WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+ "numBssidsPerScan": 24}
+ ]
+
+ self.scan_and_validate_results(scan_settings)
+
+ @test_tracker_info(uuid="14104e98-27a0-43d5-9525-b36b65ac3957")
+ @WifiBaseTest.wifi_test_wrap
+ def test_wifi_scans_batch_and_24GHz(self):
+ """Perform two WifiScanner background scans, one in batch mode for both
+ bands and the other in periodic mode at 2.4GHz
+
+ Initial Conditions:
+ * Set multiple APs broadcasting 2.4GHz and 5GHz.
+
+ Expected Results:
+ * DUT reports success for starting both scans
+ * Scan results for each callback contains only the results on the
+ frequency scanned
+ * Wait for at least two scan results and confirm that separation
+ between them approximately equals to the expected interval
+ * Number of results in batch mode should match the setting
+ * Number of BSSIDs doesn't exceed
+ """
+ scan_settings = [{"band": WifiEnums.WIFI_BAND_BOTH,
+ "periodInMs": 10000, # ms
+ "reportEvents":
+ WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL,
+ "numBssidsPerScan": 24,
+ "maxScansToCache": 2},
+ {"band": WifiEnums.WIFI_BAND_24_GHZ,
+ "periodInMs": 10000, # ms
+ "reportEvents":
+ WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+ "numBssidsPerScan": 24}]
+
+ self.scan_and_validate_results(scan_settings)
+
+ @test_tracker_info(uuid="cd6064b5-840b-4334-8cd4-8320a6cda52f")
+ @WifiBaseTest.wifi_test_wrap
+ def test_wifi_scans_batch_and_5GHz(self):
+ """Perform two WifiScanner background scans, one in batch mode for both
+ bands and the other in periodic mode at 5GHz
+
+ Initial Conditions:
+ * Set multiple APs broadcasting 2.4GHz and 5GHz.
+
+ Expected Results:
+ * DUT reports success for starting both scans
+ * Scan results for each callback contains only the results on the
+ frequency scanned
+ * Wait for at least two scan results and confirm that separation
+ between them approximately equals to the expected interval
+ * Number of results in batch mode should match the setting
+ * Number of BSSIDs doesn't exceed
+ """
+ scan_settings = [{"band": WifiEnums.WIFI_BAND_BOTH,
+ "periodInMs": 10000, # ms
+ "reportEvents":
+ WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL,
+ "numBssidsPerScan": 24,
+ "maxScansToCache": 2},
+ {"band": WifiEnums.WIFI_BAND_5_GHZ,
+ "periodInMs": 10000, # ms
+ "reportEvents":
+ WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+ "numBssidsPerScan": 24}]
+
+ self.scan_and_validate_results(scan_settings)
+
+ @test_tracker_info(uuid="9f48cb0c-de87-4cd2-9e50-857579d44079")
+ @WifiBaseTest.wifi_test_wrap
+ def test_wifi_scans_24GHz_5GHz_full_result(self):
+ """Perform two WifiScanner background scans, one at 2.4GHz and
+ the other at 5GHz. Report full scan results.
+
+ Initial Conditions:
+ * Set multiple APs broadcasting 2.4GHz and 5GHz.
+
+ Expected Results:
+ * DUT reports success for starting both scans
+ * Scan results for each callback contains only the results on the
+ frequency scanned
+ * Wait for at least two scan results and confirm that separation
+ between them approximately equals to the expected interval
+ * Number of BSSIDs doesn't exceed
+ """
+ scan_settings = [
+ {"band": WifiEnums.WIFI_BAND_24_GHZ,
+ "periodInMs": 10000, # ms
+ "reportEvents": WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT
+ | WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+ "numBssidsPerScan": 24},
+ {"band": WifiEnums.WIFI_BAND_5_GHZ,
+ "periodInMs": 10000, # ms
+ "reportEvents": WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT
+ | WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+ "numBssidsPerScan": 24}
+ ]
+
+ self.scan_and_validate_results(scan_settings)
+
+ """ Tests End """
diff --git a/acts_tests/tests/google/wifi/WifiScannerScanTest.py b/acts_tests/tests/google/wifi/WifiScannerScanTest.py
new file mode 100755
index 0000000..f267078
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiScannerScanTest.py
@@ -0,0 +1,1094 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2016 - 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 queue
+import time
+import traceback
+
+from acts import asserts
+from acts import utils
+from acts.test_decorators import test_tracker_info
+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
+
+SCANTIME = 10000 #framework support only 10s as minimum scan interval
+NUMBSSIDPERSCAN = 8
+EVENT_TAG = "WifiScannerScan"
+SCAN_TIME_PASSIVE = 47 # dwell time plus 2ms
+SCAN_TIME_ACTIVE = 32 # dwell time plus 2ms
+SHORT_TIMEOUT = 30
+NETWORK_ID_ERROR = "Network don't have ID"
+NETWORK_ERROR = "Device is not connected to reference network"
+INVALID_RESULT = "Test fail because scan result reported are not valid"
+EMPTY_RESULT = "Test fail because empty scan result reported"
+KEY_RET = "ResultElapsedRealtime"
+ATTENUATOR = 0
+
+class WifiScannerScanError(Exception):
+ pass
+
+
+class WifiScannerScanTest(WifiBaseTest):
+ def __init__(self, configs):
+ super().__init__(configs)
+ self.enable_packet_log = True
+ # TODO(angli): Remove this list.
+ # There are order dependencies among these tests so we'll have to leave
+ # it here for now. :(
+ self.tests = (
+ "test_available_channels_band_1",
+ "test_available_channels_band_2",
+ "test_available_channels_band_3",
+ "test_available_channels_band_4",
+ "test_available_channels_band_6",
+ "test_available_channels_band_7",
+ "test_wifi_scanner_single_scan_channel_sanity",
+ "test_wifi_scanner_with_wifi_off",
+ "test_single_scan_report_each_scan_for_channels_with_enumerated_params",
+ "test_single_scan_report_each_scan_for_band_with_enumerated_params",
+ "test_single_scan_report_full_scan_for_channels_with_enumerated_params",
+ "test_single_scan_report_full_scan_for_band_with_enumerated_params",
+ "test_single_scan_while_pno",
+ "test_wifi_scanner_single_scan_in_isolated",
+ "test_wifi_scanner_with_invalid_numBssidsPerScan",
+ "test_wifi_scanner_dual_radio_low_latency",
+ "test_wifi_scanner_dual_radio_low_power",
+ "test_wifi_scanner_dual_radio_high_accuracy")
+
+ def setup_class(self):
+ super().setup_class()
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ req_params = ("run_extended_test", "ping_addr", "dbs_supported_models")
+ opt_param = ["reference_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(ap_count=2, mirror_ap=False)
+ elif "OpenWrtAP" in self.user_params:
+ self.configure_openwrt_ap_and_start(open_network=True,
+ wpa_network=True,
+ ap_count=2)
+
+ self.leeway = 10
+ self.stime_channel = SCAN_TIME_PASSIVE
+ self.default_scan_setting = {
+ "band": wutils.WifiEnums.WIFI_BAND_BOTH,
+ "periodInMs": SCANTIME,
+ "reportEvents": wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN
+ }
+ self.default_batch_scan_setting = {
+ "band": wutils.WifiEnums.WIFI_BAND_BOTH,
+ "periodInMs": SCANTIME,
+ "reportEvents": wutils.WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL
+ }
+ self.log.debug("Run extended test: {}".format(self.run_extended_test))
+ self.wifi_chs = wutils.WifiChannelUS(self.dut.model)
+ self.attenuators = wutils.group_attenuators(self.attenuators)
+ self.attenuators[0].set_atten(0)
+ self.attenuators[1].set_atten(0)
+
+ def teardown_test(self):
+ super().teardown_test()
+ self.log.debug("Shut down all wifi scanner activities.")
+ self.dut.droid.wifiScannerShutdown()
+
+ def teardown_class(self):
+ if "AccessPoint" in self.user_params:
+ del self.user_params["reference_networks"]
+ del self.user_params["open_network"]
+
+ """ Helper Functions Begin """
+
+ def wifi_generate_scanner_scan_settings(self, extended, scan_type,
+ report_result):
+ """Generates all the combinations of different scan setting parameters.
+
+ Args:
+ extended: True for extended setting
+ scan_type: key for type of scan
+ report_result: event type of report scan results
+
+ Returns:
+ A list of dictionaries each representing a set of scan settings.
+ """
+ base_scan_time = [SCANTIME * 2]
+ if scan_type == "band":
+ scan_types_setting = [wutils.WifiEnums.WIFI_BAND_BOTH]
+ else:
+ scan_types_setting = [self.wifi_chs.MIX_CHANNEL_SCAN]
+ num_of_bssid = [NUMBSSIDPERSCAN * 4]
+ max_scan_cache = [0]
+ if extended:
+ base_scan_time.append(SCANTIME)
+ if scan_type == "band":
+ scan_types_setting.extend(
+ [wutils.WifiEnums.WIFI_BAND_24_GHZ,
+ wutils.WifiEnums.WIFI_BAND_5_GHZ_WITH_DFS,
+ wutils.WifiEnums.WIFI_BAND_BOTH_WITH_DFS])
+ else:
+ scan_types_setting.extend(
+ [self.wifi_chs.NONE_DFS_5G_FREQUENCIES, self.wifi_chs.
+ ALL_2G_FREQUENCIES, self.wifi_chs.DFS_5G_FREQUENCIES,
+ self.wifi_chs.ALL_5G_FREQUENCIES])
+ num_of_bssid.append(NUMBSSIDPERSCAN * 3)
+ max_scan_cache.append(5)
+ # Generate all the combinations of report types and scan types
+ if report_result == wutils.WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT:
+ report_types = {"reportEvents": report_result}
+ setting_combinations = list(itertools.product(scan_types_setting,
+ base_scan_time))
+ # Create scan setting strings based on the combinations
+ scan_settings = []
+ for combo in setting_combinations:
+ s = dict(report_types)
+ s[scan_type] = combo[0]
+ s["periodInMs"] = combo[1]
+ scan_settings.append(s)
+ else:
+ report_types = {"reportEvents": report_result}
+ setting_combinations = list(
+ itertools.product(scan_types_setting, base_scan_time,
+ num_of_bssid, max_scan_cache))
+ # Create scan setting strings based on the combinations
+ scan_settings = []
+ for combo in setting_combinations:
+ s = dict(report_types)
+ s[scan_type] = combo[0]
+ s["periodInMs"] = combo[1]
+ s["numBssidsPerScan"] = combo[2]
+ s["maxScansToCache"] = combo[3]
+ scan_settings.append(s)
+ return scan_settings
+
+ def proces_and_valid_batch_scan_result(self, scan_resutls, scan_rt,
+ result_rt, scan_setting):
+ """This function process scan results and validate against settings used
+ while starting the scan.
+
+ There are two steps for the verification. First it checks that all the
+ wifi networks in results are of the correct frequencies set by scan setting
+ params. Then it checks that the delta between the batch of scan results less
+ than the time required for scanning channel set by scan setting params.
+
+ Args:
+ scan_results: scan results reported.
+ scan_rt: Elapsed real time on start scan.
+ result_rt: Elapsed ral time on results reported.
+ scan_setting: The params for the single scan.
+
+ Returns:
+ bssids: total number of bssids scan result have
+ validity: True if the all scan result are valid.
+ """
+ bssids = 0
+ validity = True
+ scan_time_mic = 0
+ scan_channels = []
+ scan_time, scan_channels = wutils.get_scan_time_and_channels(
+ self.wifi_chs, scan_setting, self.stime_channel)
+ scan_time_mic = scan_time * 1000
+ for i, batch in enumerate(scan_resutls, start=1):
+ asserts.assert_true(
+ batch["ScanResults"],
+ "At least one scan result is required to validate")
+ max_scan_interval = batch["ScanResults"][0][
+ "timestamp"] + scan_time_mic
+ self.log.debug("max_scan_interval: %s", max_scan_interval)
+ for result in batch["ScanResults"]:
+ # Though the tests are run in shield box, there are leakes
+ # from outside environment. This would ignore any such SSIDs
+ ssid = result["SSID"]
+ if not ssid.startswith("2g_") or not ssid.startswith("5g_"):
+ continue
+ if (result["frequency"] not in scan_channels or
+ result["timestamp"] > max_scan_interval or
+ result["timestamp"] < scan_rt * 1000 or
+ result["timestamp"] > result_rt * 1000):
+ self.log.error("Result didn't match requirement: %s",
+ result)
+ validity = False
+ self.log.info("Number of scan result in batch %s: %s", i,
+ len(batch["ScanResults"]))
+ bssids += len(batch["ScanResults"])
+ return bssids, validity
+
+ def pop_scan_result_events(self, event_name):
+ """Function to pop all the scan result events.
+
+ Args:
+ event_name: event name.
+
+ Returns:
+ results: list of scan result reported in events
+ """
+ results = []
+ try:
+ events = self.dut.ed.pop_all(event_name)
+ for event in events:
+ results.append(event["data"]["Results"])
+ except queue.Empty as error:
+ self.log.debug("Number of Full scan results %s", len(results))
+ return results
+
+ def wifi_scanner_single_scan(self, scan_setting):
+ """Common logic for an enumerated wifi scanner single scan test case.
+
+ 1. Start WifiScanner single scan for scan_setting.
+ 2. Wait for the scan result event, wait time depend on scan settings
+ parameter.
+ 3. Verify that scan results match with scan settings parameters.
+ 4. Also verify that only one scan result event trigger.
+
+ Args:
+ scan_setting: The params for the single scan.
+ """
+ data = wutils.start_wifi_single_scan(self.dut, scan_setting)
+ idx = data["Index"]
+ scan_rt = data["ScanElapsedRealtime"]
+ self.log.info(
+ "Wifi single shot scan started index: %s at real time: %s", idx,
+ scan_rt)
+ results = []
+ #generating event wait time from scan setting plus leeway
+ scan_time, scan_channels = wutils.get_scan_time_and_channels(
+ self.wifi_chs, scan_setting, self.stime_channel)
+ wait_time = int(scan_time / 1000) + self.leeway
+ validity = False
+ #track number of result received
+ result_received = 0
+ try:
+ for snumber in range(1, 3):
+ event_name = "{}{}onResults".format(EVENT_TAG, idx)
+ self.log.debug("Waiting for event: %s for time %s", event_name,
+ wait_time)
+ event = self.dut.ed.pop_event(event_name, wait_time)
+ self.log.debug("Event received: %s", event)
+ results = event["data"]["Results"]
+ result_received += 1
+ bssids, validity = self.proces_and_valid_batch_scan_result(
+ results, scan_rt, event["data"][KEY_RET], scan_setting)
+ asserts.assert_equal(
+ len(results), 1,
+ "Test fail because number of scan result %s" %
+ len(results))
+ asserts.assert_true(bssids > 0, EMPTY_RESULT)
+ asserts.assert_true(validity, INVALID_RESULT)
+ self.log.info("Scan number Buckets: %s\nTotal BSSID: %s",
+ len(results), bssids)
+ except queue.Empty as error:
+ asserts.assert_true(
+ result_received >= 1,
+ "Event did not triggered for single shot {}".format(error))
+ finally:
+ self.dut.droid.wifiScannerStopScan(idx)
+ #For single shot number of result received and length of result should be one
+ asserts.assert_true(
+ result_received == 1,
+ "Test fail because received result {}".format(result_received))
+
+ def wifi_scanner_single_scan_full(self, scan_setting):
+ """Common logic for single scan test case for full scan result.
+
+ 1. Start WifiScanner single scan with scan_setting for full scan result.
+ 2. Wait for the scan result event, wait time depend on scan settings
+ parameter.
+ 3. Pop all full scan result events occurred earlier.
+ 4. Verify that full scan results match with normal scan results.
+ 5. If the scan type is included in scan_setting, verify that the
+ radioChainInfos length.
+
+ Args:
+ scan_setting: The parameters for the single scan.
+ """
+ self.dut.ed.clear_all_events()
+ data = wutils.start_wifi_single_scan(self.dut, scan_setting)
+ idx = data["Index"]
+ scan_rt = data["ScanElapsedRealtime"]
+ self.log.info("Wifi single shot scan started with index: %s", idx)
+ #generating event wait time from scan setting plus leeway
+ scan_time, scan_channels = wutils.get_scan_time_and_channels(
+ self.wifi_chs, scan_setting, self.stime_channel)
+ wait_time = int(scan_time / 1000) + self.leeway
+ results = []
+ validity = False
+ try:
+ event_name = "%s%sonResults" % (EVENT_TAG, idx)
+ self.log.debug("Waiting for event: %s for time %s", event_name,
+ wait_time)
+ event = self.dut.ed.pop_event(event_name, wait_time)
+ self.log.info("Event received: %s", event)
+ bssids, validity = (self.proces_and_valid_batch_scan_result(
+ event["data"]["Results"], scan_rt, event["data"][KEY_RET],
+ scan_setting))
+ asserts.assert_true(bssids > 0, EMPTY_RESULT)
+ asserts.assert_true(validity, INVALID_RESULT)
+ event_name = "{}{}onFullResult".format(EVENT_TAG, idx)
+ results = self.pop_scan_result_events(event_name)
+ asserts.assert_true(
+ len(results) >= bssids,
+ "Full single shot result don't match {}".format(len(results)))
+ if 'type' in scan_setting.keys():
+ for item in results:
+ self.verify_radio_chain_length(scan_setting['type'], item)
+ except queue.Empty as error:
+ raise AssertionError(
+ "Event did not triggered for single shot {}".format(error))
+ finally:
+ self.dut.droid.wifiScannerStopScan(idx)
+
+ def verify_radio_chain_length(self, scan_setting_type, scan_result):
+ llen = len(scan_result[0]["radioChainInfos"])
+ if scan_setting_type == wutils.WifiEnums.SCAN_TYPE_LOW_LATENCY \
+ or scan_setting_type == wutils.WifiEnums.SCAN_TYPE_LOW_POWER:
+ asserts.assert_true(llen == 1,
+ "radioChainInfos len expected:{} "
+ "actual:{}".format(1, llen))
+ else:
+ asserts.assert_true(llen == 2,
+ "radioChainInfos len expected:{} "
+ "actual:{}".format(2, llen))
+
+ def wifi_scanner_batch_scan_full(self, scan_setting):
+ """Common logic for batch scan test case for full scan result.
+
+ 1. Start WifiScanner batch scan with scan_setting for full scan result.
+ 2. Wait for the scan result event, wait time depend on scan settings
+ parameter.
+ 3. Pop all full scan result events occurred earlier.
+ 4. Verify that full scan results match with scan results.
+
+ Args:
+ scan_setting: The params for the batch scan.
+ """
+ self.dut.ed.clear_all_events()
+ data = wutils.start_wifi_background_scan(self.dut, scan_setting)
+ idx = data["Index"]
+ scan_rt = data["ScanElapsedRealtime"]
+ self.log.info("Wifi batch shot scan started with index: %s", idx)
+ #generating event wait time from scan setting plus leeway
+ scan_time, scan_channels = wutils.get_scan_time_and_channels(
+ self.wifi_chs, scan_setting, self.stime_channel)
+ # multiply scan period by two to account for scheduler changing period
+ scan_time += scan_setting[
+ 'periodInMs'] * 2 #add scan period delay for next cycle
+ wait_time = scan_time / 1000 + self.leeway
+ validity = False
+ try:
+ for snumber in range(1, 3):
+ results = []
+ event_name = "%s%sonResults" % (EVENT_TAG, idx)
+ self.log.debug("Waiting for event: %s for time %s", event_name,
+ wait_time)
+ event = self.dut.ed.pop_event(event_name, wait_time)
+ self.log.debug("Event received: %s", event)
+ bssids, validity = self.proces_and_valid_batch_scan_result(
+ event["data"]["Results"], scan_rt, event["data"][KEY_RET],
+ scan_setting)
+ event_name = "%s%sonFullResult" % (EVENT_TAG, idx)
+ results = self.pop_scan_result_events(event_name)
+ asserts.assert_true(
+ len(results) >= bssids,
+ "Full single shot result don't match %s" % len(results))
+ asserts.assert_true(bssids > 0, EMPTY_RESULT)
+ asserts.assert_true(validity, INVALID_RESULT)
+ except queue.Empty as error:
+ raise AssertionError("Event did not triggered for batch scan %s" %
+ error)
+ finally:
+ self.dut.droid.wifiScannerStopBackgroundScan(idx)
+ self.dut.ed.clear_all_events()
+
+ def wifi_scanner_batch_scan(self, scan_setting):
+ """Common logic for an enumerated wifi scanner batch scan test case.
+
+ 1. Start WifiScanner batch scan for given scan_setting.
+ 2. Wait for the scan result event, wait time depend on scan settings
+ parameter.
+ 3. Verify that scan results match with scan settings parameters.
+ 4. Also verify that multiple scan result events trigger.
+
+ Args:
+ scan_setting: The parameters for the batch scan.
+ """
+ data = wutils.start_wifi_background_scan(self.dut, scan_setting)
+ idx = data["Index"]
+ scan_rt = data["ScanElapsedRealtime"]
+ self.log.info(
+ "Wifi background scan started with index: %s real time %s", idx,
+ scan_rt)
+ scan_time, scan_channels = wutils.get_scan_time_and_channels(
+ self.wifi_chs, scan_setting, self.stime_channel)
+ #generating event wait time from scan setting plus leeway
+ time_cache = 0
+ number_bucket = 1 #bucket for Report result on each scan
+ check_get_result = False
+ if scan_setting[
+ 'reportEvents'] == wutils.WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL:
+ check_get_result = True
+ if ('maxScansToCache' in scan_setting and
+ scan_setting['maxScansToCache'] != 0):
+ time_cache = (scan_setting['maxScansToCache'] *
+ scan_setting['periodInMs'])
+ number_bucket = scan_setting['maxScansToCache']
+ else:
+ time_cache = 10 * scan_setting['periodInMs'
+ ] #10 as default max scan cache
+ number_bucket = 10
+ else:
+ time_cache = scan_setting[
+ 'periodInMs'
+ ] #need while waiting for seconds resutls
+ # multiply cache time by two to account for scheduler changing period
+ wait_time = (time_cache * 2 + scan_time) / 1000 + self.leeway
+ validity = False
+ try:
+ for snumber in range(1, 3):
+ event_name = "%s%sonResults" % (EVENT_TAG, idx)
+ self.log.info("Waiting for event: %s for time %s", event_name,
+ wait_time)
+ event = self.dut.ed.pop_event(event_name, wait_time)
+ self.log.debug("Event received: %s", event)
+ results = event["data"]["Results"]
+ bssids, validity = (self.proces_and_valid_batch_scan_result(
+ results, scan_rt, event["data"][KEY_RET], scan_setting))
+ self.log.info("Scan number: %s\n Buckets: %s\n BSSID: %s",
+ snumber, len(results), bssids)
+ asserts.assert_equal(
+ len(results), number_bucket,
+ "Test fail because number_bucket %s" % len(results))
+ asserts.assert_true(bssids >= 1, EMPTY_RESULT)
+ asserts.assert_true(validity, INVALID_RESULT)
+ if snumber % 2 == 1 and check_get_result:
+ self.log.info("Get Scan result using GetScanResult API")
+ time.sleep(wait_time / number_bucket)
+ if self.dut.droid.wifiScannerGetScanResults():
+ event = self.dut.ed.pop_event(event_name, 1)
+ self.log.debug("Event onResults: %s", event)
+ results = event["data"]["Results"]
+ bssids, validity = self.proces_and_valid_batch_scan_result(
+ results, scan_rt, event["data"][KEY_RET],
+ scan_setting)
+ self.log.info("Got Scan result number: %s BSSID: %s",
+ snumber, bssids)
+ asserts.assert_true(bssids >= 1, EMPTY_RESULT)
+ asserts.assert_true(validity, INVALID_RESULT)
+ else:
+ self.log.error("Error while fetching the scan result")
+ except queue.Empty as error:
+ raise AssertionError("Event did not triggered for batch scan %s" %
+ error)
+ finally:
+ self.dut.droid.wifiScannerStopBackgroundScan(idx)
+ self.dut.ed.clear_all_events()
+
+ def start_wifi_scanner_single_scan_expect_failure(self, scan_setting):
+ """Common logic to test wif scanner single scan with invalid settings
+ or environment
+
+ 1. Start WifiScanner batch scan for setting parameters.
+ 2. Verify that scan is not started.
+
+ Args:
+ scan_setting: The params for the single scan.
+ """
+ try:
+ idx = self.dut.droid.wifiScannerStartScan(scan_setting)
+ event = self.dut.ed.pop_event(
+ "{}{}onFailure".format(EVENT_TAG, idx), SHORT_TIMEOUT)
+ except queue.Empty as error:
+ raise AssertionError("Did not get expected onFailure {}".format(
+ error))
+
+ def start_wifi_scanner_background_scan_expect_failure(self, scan_setting):
+ """Common logic to test wif scanner batch scan with invalid settings
+ or environment
+
+ 1. Start WifiScanner batch scan for setting parameters.
+ 2. Verify that scan is not started.
+
+ Args:
+ scan_setting: The params for the single scan.
+ """
+ try:
+ idx = self.dut.droid.wifiScannerStartBackgroundScan(scan_setting)
+ event = self.dut.ed.pop_event(
+ "{}{}onFailure".format(EVENT_TAG, idx), SHORT_TIMEOUT)
+ except queue.Empty as error:
+ raise AssertionError("Did not get expected onFailure {}".format(
+ error))
+
+ def check_get_available_channels_with_one_band(self, band):
+ """Common logic to check available channels for a band.
+
+ 1. Get available channels for band.
+ 2. Verify that channels match with supported channels for band.
+
+ Args:
+ band: wifi band."""
+
+ r = self.dut.droid.wifiScannerGetAvailableChannels(band)
+ 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):
+ """Connect to reference network and make sure that connection happen"""
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ try:
+ self.dut.droid.wifiConnectByConfig(self.reference_networks[0]["2g"])
+ connect_result = self.dut.ed.pop_event(
+ wifi_constants.CONNECT_BY_CONFIG_SUCCESS, SHORT_TIMEOUT)
+ self.log.info(connect_result)
+ return wutils.track_connection(self.dut,
+ self.reference_networks[0]["2g"]["SSID"], 1)
+ except Exception as error:
+ self.log.exception(traceback.format_exc())
+ self.log.error("Connection to network fail because %s", error)
+ return False
+ finally:
+ self.dut.droid.wifiLockRelease()
+ self.dut.droid.goToSleepNow()
+
+ """ Helper Functions End """
+ """ Tests Begin """
+
+ # Test channels
+ """ Test available channels for different bands.
+
+ 1. Get available channels for different bands.
+ 2. Verify that channels match with supported channels for respective band.
+ """
+ @test_tracker_info(uuid="7cca8142-529f-4951-ab6f-cd03b359b3cc")
+ def test_available_channels_band_1(self):
+ self.check_get_available_channels_with_one_band(1)
+
+ @test_tracker_info(uuid="612afda1-0d74-4d2f-bc37-72ef2b98310a")
+ def test_available_channels_band_2(self):
+ self.check_get_available_channels_with_one_band(2)
+
+ @test_tracker_info(uuid="a9275bb9-afa7-4dd4-b2e0-60296ffd33bb")
+ def test_available_channels_band_3(self):
+ self.check_get_available_channels_with_one_band(3)
+
+ @test_tracker_info(uuid="5413632e-ce72-4ecc-bf9b-33ac9e4bf3fc")
+ def test_available_channels_band_4(self):
+ self.check_get_available_channels_with_one_band(4)
+
+ @test_tracker_info(uuid="a8f40b4f-d79d-4d2f-bed8-3b139a082f6c")
+ def test_available_channels_band_6(self):
+ self.check_get_available_channels_with_one_band(6)
+
+ @test_tracker_info(uuid="84cdfc25-8e64-42c7-b7f9-0a04e45d78b6")
+ def test_available_channels_band_7(self):
+ self.check_get_available_channels_with_one_band(7)
+
+ @test_tracker_info(uuid="95069244-b76c-4834-b3a6-96b0d8da98d8")
+ def test_single_scan_report_each_scan_for_channels_with_enumerated_params(
+ self):
+ """Test WiFi scanner single scan for channels with enumerated settings.
+
+ 1. Start WifiScanner single scan for different channels with enumerated
+ scan settings.
+ 2. Verify that scan results match with respective scan settings.
+ """
+ scan_settings = self.wifi_generate_scanner_scan_settings(
+ self.run_extended_test, "channels",
+ wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN)
+ self.log.debug("Scan settings: %s\n%s", len(scan_settings),
+ scan_settings)
+ self.wifi_scanner_single_scan(scan_settings[0])
+
+ @test_tracker_info(uuid="5595ebe5-6d91-4379-a606-be59967e5ec9")
+ def test_single_scan_report_each_scan_for_band_with_enumerated_params(
+ self):
+ """Test WiFi scanner single scan for bands with enumerated settings.
+
+ 1. Start WifiScanner single scan for different bands with enumerated
+ scan settings.
+ 2. Verify that scan results match with respective scan settings.
+ """
+ scan_settings = self.wifi_generate_scanner_scan_settings(
+ self.run_extended_test, "band",
+ wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN)
+ self.log.debug("Scan settings:%s\n%s", len(scan_settings),
+ scan_settings)
+ self.wifi_scanner_single_scan(scan_settings[0])
+
+ @test_tracker_info(uuid="44989f93-e63b-4c2e-a90a-a483477303bb")
+ def test_batch_scan_report_buffer_full_for_channels_with_enumerated_params(
+ self):
+ """Test WiFi scanner batch scan using channels with enumerated settings
+ to report buffer full scan results.
+
+ 1. Start WifiScanner batch scan using different channels with enumerated
+ scan settings to report buffer full scan results.
+ 2. Verify that scan results match with respective scan settings.
+ """
+ scan_settings = self.wifi_generate_scanner_scan_settings(
+ self.run_extended_test, "channels",
+ wutils.WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL)
+ self.log.debug("Scan settings:%s\n%s", len(scan_settings),
+ scan_settings)
+ self.wifi_scanner_batch_scan(scan_settings[0])
+
+ @test_tracker_info(uuid="63538df6-388a-4c16-964f-e9c19b750e07")
+ def test_batch_scan_report_buffer_full_for_band_with_enumerated_params(
+ self):
+ """Test WiFi scanner batch scan using band with enumerated settings
+ to report buffer full scan results.
+
+ 1. Start WifiScanner batch scan using different bands with enumerated
+ scan settings to report buffer full scan results.
+ 2. Verify that scan results match with respective scan settings.
+ """
+ scan_settings = self.wifi_generate_scanner_scan_settings(
+ self.run_extended_test, "band",
+ wutils.WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL)
+ self.log.debug("Scan settings:{}\n{}".format(
+ len(scan_settings), scan_settings))
+ self.wifi_scanner_batch_scan(scan_settings[0])
+
+ @test_tracker_info(uuid="bd4e8c53-16c8-4ed6-b680-55c1ba688ad8")
+ def test_batch_scan_report_each_scan_for_channels_with_enumerated_params(
+ self):
+ """Test WiFi scanner batch scan using channels with enumerated settings
+ to report each scan results.
+
+ 1. Start WifiScanner batch scan using different channels with enumerated
+ scan settings to report each scan results.
+ 2. Verify that scan results match with respective scan settings.
+ """
+ scan_settings = self.wifi_generate_scanner_scan_settings(
+ self.run_extended_test, "channels",
+ wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN)
+ self.log.debug("Scan settings:{}\n{}".format(
+ len(scan_settings), scan_settings))
+ self.wifi_scanner_batch_scan(scan_settings[0])
+
+ @test_tracker_info(uuid="d11e8c09-97d0-49c1-bf09-b9ec672c2fa6")
+ def test_batch_scan_report_each_scan_for_band_with_enumerated_params(self):
+ """Test WiFi scanner batch scan using band with enumerated settings
+ to report each scan results.
+
+ 1. Start WifiScanner batch scan using different bands with enumerated
+ scan settings to report each scan results.
+ 2. Verify that scan results match with respective scan settings.
+ """
+ scan_settings = self.wifi_generate_scanner_scan_settings(
+ self.run_extended_test, "band",
+ wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN)
+ self.log.debug("Scan settings:{}\n{}".format(
+ len(scan_settings), scan_settings))
+ self.wifi_scanner_batch_scan(scan_settings[0])
+
+ @test_tracker_info(uuid="7f967b0e-82fe-403e-9d74-0dee7f09a21d")
+ def test_single_scan_report_full_scan_for_channels_with_enumerated_params(
+ self):
+ """Test WiFi scanner single scan using channels with enumerated settings
+ to report full scan results.
+
+ 1. Start WifiScanner single scan using different channels with enumerated
+ scan settings to report full scan results.
+ 2. Verify that scan results match with respective scan settings.
+ """
+ scan_settings = self.wifi_generate_scanner_scan_settings(
+ self.run_extended_test, "channels",
+ wutils.WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT)
+ self.log.debug("Full Scan settings:{}\n{}".format(
+ len(scan_settings), scan_settings))
+ self.wifi_scanner_single_scan_full(scan_settings[0])
+
+ @test_tracker_info(uuid="34d09f60-31bf-4952-8fb3-03fc93ec98fa")
+ def test_single_scan_report_full_scan_for_band_with_enumerated_params(
+ self):
+ """Test WiFi scanner single scan using band with enumerated settings
+ to report full scan results.
+
+ 1. Start WifiScanner single scan using different bands with enumerated
+ scan settings to report full scan results.
+ 2. Verify that scan results match with respective scan settings.
+ """
+ scan_settings = self.wifi_generate_scanner_scan_settings(
+ self.run_extended_test, "band",
+ wutils.WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT)
+ self.log.debug("Full Scan settings:{}\n{}".format(
+ len(scan_settings), scan_settings))
+ self.wifi_scanner_single_scan_full(scan_settings[0])
+
+ @test_tracker_info(uuid="0ddccf2e-b518-45a7-ae75-96924070b841")
+ def test_batch_scan_report_full_scan_for_channels_with_enumerated_params(
+ self):
+ """Test WiFi scanner batch scan using channels with enumerated settings
+ to report full scan results.
+
+ 1. Start WifiScanner batch scan using different channels with enumerated
+ scan settings to report full scan results.
+ 2. Verify that scan results match with respective scan settings.
+ """
+ scan_settings = self.wifi_generate_scanner_scan_settings(
+ self.run_extended_test, "channels",
+ wutils.WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT)
+ self.log.debug("Full Scan settings:{}\n{}".format(
+ len(scan_settings), scan_settings))
+ self.wifi_scanner_batch_scan_full(scan_settings[0])
+
+ @test_tracker_info(uuid="0685b667-8470-43a0-923d-dee71428f8ce")
+ def test_batch_scan_report_full_scan_for_band_with_enumerated_params(self):
+ """Test WiFi scanner batch scan using channels with enumerated settings
+ to report full scan results.
+
+ 1. Start WifiScanner batch scan using different channels with enumerated
+ scan settings to report full scan results.
+ 2. Verify that scan results match with respective scan settings.
+ """
+ scan_settings = self.wifi_generate_scanner_scan_settings(
+ self.run_extended_test, "band",
+ wutils.WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT)
+ self.log.debug("Full Scan settings:{}\n{}".format(
+ len(scan_settings), scan_settings))
+ self.wifi_scanner_batch_scan_full(scan_settings[0])
+
+ @test_tracker_info(uuid="e9a7cfb5-21c4-4c40-8169-8d88b65a1dee")
+ @WifiBaseTest.wifi_test_wrap
+ def test_single_scan_while_pno(self):
+ """Test wifi scanner single scan parallel to PNO connection.
+
+ 1. Check device have a saved network.
+ 2. Trigger PNO by attenuate the signal to move out of range.
+ 3. Start WifiScanner single scan for both band with default scan settings.
+ 4. Verify that scanner report single scan results.
+ 5. Attenuate the signal to move in range.
+ 6. Verify connection occurred through PNO.
+ """
+ self.log.info("Check connection through PNO for reference network")
+ self.attenuators[ATTENUATOR].set_atten(0)
+ asserts.assert_true(self.connect_to_reference_network(), NETWORK_ERROR)
+ time.sleep(10) #wait for connection to be active
+ asserts.assert_true(
+ wutils.validate_connection(self.dut, self.ping_addr),
+ "Error, No internet connection for current network")
+
+ current_network = self.dut.droid.wifiGetConnectionInfo()
+ self.log.info("Current network: {}".format(current_network))
+ asserts.assert_true('network_id' in current_network, NETWORK_ID_ERROR)
+ asserts.assert_true(current_network['network_id'] >= 0, NETWORK_ERROR)
+ self.log.info("Kicking PNO for reference network")
+ self.attenuators[ATTENUATOR].set_atten(90)
+ time.sleep(10) #wait for PNO to be kicked
+ self.log.info("Starting single scan while PNO")
+ self.wifi_scanner_single_scan(self.default_scan_setting)
+ self.attenuators[ATTENUATOR].set_atten(0)
+ self.log.info("Check connection through PNO for reference network")
+ time.sleep(60) #wait for connection through PNO
+ current_network = self.dut.droid.wifiGetConnectionInfo()
+ self.log.info("Current network: {}".format(current_network))
+ asserts.assert_true('network_id' in current_network, NETWORK_ID_ERROR)
+ asserts.assert_true(current_network['network_id'] >= 0, NETWORK_ERROR)
+ time.sleep(10) #wait for IP to be assigned
+ asserts.assert_true(
+ wutils.validate_connection(self.dut, self.ping_addr),
+ "Error, No internet connection for current network")
+ wutils.wifi_forget_network(self.dut,
+ self.reference_networks[0]["2g"]["SSID"])
+
+ @test_tracker_info(uuid="fc18d947-0b5a-42b4-98f3-dd1f2b52a7af")
+ def test_wifi_connection_and_pno_while_batch_scan(self):
+ """Test configuring a connection and PNO connection parallel to wifi
+ scanner batch scan.
+
+ 1. Start WifiScanner batch scan with default batch scan settings.
+ 2. Wait for scan result event for a time depend on scan settings.
+ 3. Verify reported batch scan results.
+ 4. Configure a connection to reference network.
+ 5. Verify that connection to reference network occurred.
+ 6. Wait for scan result event for a time depend on scan settings.
+ 7. Verify reported batch scan results.
+ 8. Trigger PNO by attenuate the signal to move out of range.
+ 9. Wait for scan result event for a time depend on scan settings.
+ 10. Verify reported batch scan results.
+ 11. Attenuate the signal to move in range.
+ 12. Verify connection occurred through PNO.
+ """
+ self.attenuators[ATTENUATOR].set_atten(0)
+ data = wutils.start_wifi_background_scan(
+ self.dut, self.default_batch_scan_setting)
+ idx = data["Index"]
+ scan_rt = data["ScanElapsedRealtime"]
+ self.log.info(
+ "Wifi background scan started with index: {} rt {}".format(
+ idx, scan_rt))
+ #generating event wait time from scan setting plus leeway
+ scan_time, scan_channels = wutils.get_scan_time_and_channels(
+ self.wifi_chs, self.default_batch_scan_setting, self.stime_channel)
+ #default number buckets
+ number_bucket = 10
+ time_cache = self.default_batch_scan_setting[
+ 'periodInMs'] * number_bucket #default cache
+ #add 2 seconds extra time for switch between the channel for connection scan
+ #multiply cache time by two to account for scheduler changing period
+ wait_time = (time_cache * 2 + scan_time) / 1000 + self.leeway + 2
+ result_flag = 0
+ try:
+ for snumber in range(1, 7):
+ event_name = "{}{}onResults".format(EVENT_TAG, idx)
+ self.log.info("Waiting for event: {}".format(event_name))
+ event = self.dut.ed.pop_event(event_name, wait_time)
+ self.log.debug("Event onResults: {}".format(event))
+ results = event["data"]["Results"]
+ bssids, validity = self.proces_and_valid_batch_scan_result(
+ results, scan_rt, event["data"][KEY_RET],
+ self.default_batch_scan_setting)
+ self.log.info(
+ "Scan number: {}\n Buckets: {}\n BSSID: {}".format(
+ snumber, len(results), bssids))
+ asserts.assert_true(bssids >= 1,
+ "Not able to fetch scan result")
+ if snumber == 1:
+ self.log.info(
+ "Try to connect AP while waiting for event: {}".format(
+ event_name))
+ asserts.assert_true(self.connect_to_reference_network(),
+ NETWORK_ERROR)
+ time.sleep(10) #wait for connection to be active
+ asserts.assert_true(
+ wutils.validate_connection(self.dut, self.ping_addr),
+ "Error, No internet connection for current network")
+ elif snumber == 3:
+ self.log.info("Kicking PNO for reference network")
+ self.attenuators[ATTENUATOR].set_atten(90)
+ elif snumber == 4:
+ self.log.info("Bring back device for PNO connection")
+ current_network = self.dut.droid.wifiGetConnectionInfo()
+ self.log.info("Current network: {}".format(
+ current_network))
+ asserts.assert_true('network_id' in current_network,
+ NETWORK_ID_ERROR)
+ asserts.assert_true(
+ current_network['network_id'] == -1,
+ "Device is still connected to network {}".format(
+ current_network[wutils.WifiEnums.SSID_KEY]))
+ self.attenuators[ATTENUATOR].set_atten(0)
+ time.sleep(
+ 10
+ ) #wait for connection to take place before waiting for scan result
+ elif snumber == 6:
+ self.log.info(
+ "Check connection through PNO for reference network")
+ current_network = self.dut.droid.wifiGetConnectionInfo()
+ self.log.info("Current network: {}".format(
+ current_network))
+ asserts.assert_true('network_id' in current_network,
+ NETWORK_ID_ERROR)
+ asserts.assert_true(current_network['network_id'] >= 0,
+ NETWORK_ERROR)
+ time.sleep(10) #wait for connection to be active
+ asserts.assert_true(
+ wutils.validate_connection(self.dut, self.ping_addr),
+ "Error, No internet connection for current network")
+ wutils.wifi_forget_network(self.dut,
+ self.reference_networks[0]["2g"]["SSID"])
+ except queue.Empty as error:
+ raise AssertionError(
+ "Event did not triggered for batch scan {}".format(error))
+ finally:
+ self.dut.droid.wifiScannerStopBackgroundScan(idx)
+ self.dut.ed.clear_all_events()
+
+ @test_tracker_info(uuid="7c25ce32-0fae-4a68-a7cb-fdf6d4d03caf")
+ def test_wifi_scanner_single_scan_channel_sanity(self):
+ """Test WiFi scanner single scan for mix channel with default setting
+ parameters.
+
+ 1. Start WifiScanner single scan for mix channels with default setting
+ parameters.
+ 2. Verify that scan results match with respective scan settings.
+ """
+ scan_setting = {"channels": self.wifi_chs.MIX_CHANNEL_SCAN,
+ "periodInMs": SCANTIME,
+ "reportEvents":
+ wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN}
+ self.wifi_scanner_single_scan(scan_setting)
+
+ @test_tracker_info(uuid="7c8da0c4-dec7-4d04-abd4-f8ea467a5c6d")
+ @WifiBaseTest.wifi_test_wrap
+ def test_wifi_scanner_dual_radio_low_latency(self):
+ """Test WiFi scanner single scan for mix channel with default setting
+ parameters.
+
+ 1. Start WifiScanner single scan for type = SCAN_TYPE_LOW_LATENCY.
+ 2. Verify that scan results match with respective scan settings.
+ """
+ if self.dut.model not in self.dbs_supported_models:
+ asserts.skip(
+ ("Device %s does not support dual radio scanning.")
+ % self.dut.model)
+ scan_setting = {"channels": self.wifi_chs.MIX_CHANNEL_SCAN,
+ "periodInMs": SCANTIME,
+ "reportEvents":
+ wutils.WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT,
+ "type": wutils.WifiEnums.SCAN_TYPE_LOW_LATENCY}
+ self.wifi_scanner_single_scan_full(scan_setting)
+
+ @test_tracker_info(uuid="58b49b01-851b-4e45-b218-9fd27c0be921")
+ @WifiBaseTest.wifi_test_wrap
+ def test_wifi_scanner_dual_radio_low_power(self):
+ """Test WiFi scanner single scan for mix channel with default setting
+ parameters.
+
+ 1. Start WifiScanner single scan for type = SCAN_TYPE_LOW_POWER.
+ 2. Verify that scan results match with respective scan settings.
+ """
+ if self.dut.model not in self.dbs_supported_models:
+ asserts.skip(
+ ("Device %s does not support dual radio scanning.")
+ % self.dut.model)
+ scan_setting = {"channels": self.wifi_chs.MIX_CHANNEL_SCAN,
+ "periodInMs": SCANTIME,
+ "reportEvents":
+ wutils.WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT,
+ "type": wutils.WifiEnums.SCAN_TYPE_LOW_POWER}
+ self.wifi_scanner_single_scan_full(scan_setting)
+
+ @test_tracker_info(uuid="3e7288bc-45e4-497c-bf3a-977eec4e896e")
+ @WifiBaseTest.wifi_test_wrap
+ def test_wifi_scanner_dual_radio_high_accuracy(self):
+ """Test WiFi scanner single scan for mix channel with default setting
+ parameters.
+
+ 1. Start WifiScanner single scan for type = SCAN_TYPE_HIGH_ACCURACY.
+ 2. Verify that scan results match with respective scan settings.
+ """
+ if self.dut.model not in self.dbs_supported_models:
+ asserts.skip(
+ ("Device %s does not support dual radio scanning.")
+ % self.dut.model)
+ scan_setting = {"channels": self.wifi_chs.MIX_CHANNEL_SCAN,
+ "periodInMs": SCANTIME,
+ "reportEvents":
+ wutils.WifiEnums.REPORT_EVENT_FULL_SCAN_RESULT,
+ "type": wutils.WifiEnums.SCAN_TYPE_HIGH_ACCURACY}
+ self.wifi_scanner_single_scan_full(scan_setting)
+
+ @test_tracker_info(uuid="e9f3aaad-4af3-4c54-9829-65dc1d6d4987")
+ def test_wifi_scanner_batch_scan_channel_sanity(self):
+ """Test WiFi scanner batch scan for mix channel with default setting
+ parameters to report the result on buffer full.
+
+ 1. Start WifiScanner batch scan for mix channels with default setting
+ parameters.
+ 2. Verify that scan results match with respective scan settings.
+ """
+ scan_setting = {"channels": self.wifi_chs.MIX_CHANNEL_SCAN,
+ "periodInMs": SCANTIME,
+ "reportEvents":
+ wutils.WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL}
+ self.wifi_scanner_batch_scan(scan_setting)
+
+ @test_tracker_info(uuid="49ba245a-52e2-4c9b-90ad-a2fbc97e3d9f")
+ def test_wifi_scanner_batch_scan_period_too_short(self):
+ """Test WiFi scanner batch scan for band with too short period time.
+
+ 1. Start WifiScanner batch scan for both band with interval period as 5s.
+ 2. Verify that scan is not started."""
+ scan_setting = {"band": wutils.WifiEnums.WIFI_BAND_BOTH_WITH_DFS,
+ "periodInMs": 5000,
+ "reportEvents":
+ wutils.WifiEnums.REPORT_EVENT_AFTER_BUFFER_FULL}
+ self.start_wifi_scanner_background_scan_expect_failure(scan_setting)
+
+ @test_tracker_info(uuid="6fe45cd7-4fac-4ddd-a950-b9431e68f735")
+ def test_wifi_scanner_single_scan_in_isolated(self):
+ """Test WiFi scanner in isolated environment with default scan settings.
+
+ 1. Created isolated environment by attenuating the single by 90db
+ 2. Start WifiScanner single scan for mix channels with default setting
+ parameters.
+ 3. Verify that empty scan results reported.
+ """
+ self.attenuators[0].set_atten(90)
+ self.attenuators[1].set_atten(90)
+ data = wutils.start_wifi_single_scan(self.dut,
+ self.default_scan_setting)
+ idx = data["Index"]
+ scan_rt = data["ScanElapsedRealtime"]
+ self.log.info("Wifi single shot scan started with index: {}".format(
+ idx))
+ results = []
+ #generating event wait time from scan setting plus leeway
+ scan_time, scan_channels = wutils.get_scan_time_and_channels(
+ self.wifi_chs, self.default_scan_setting, self.stime_channel)
+ wait_time = int(scan_time / 1000) + self.leeway
+ try:
+ event_name = "{}{}onResults".format(EVENT_TAG, idx)
+ self.log.debug("Waiting for event: {} for time {}".format(
+ event_name, wait_time))
+ event = self.dut.ed.pop_event(event_name, wait_time)
+ self.log.debug("Event received: {}".format(event))
+ results = event["data"]["Results"]
+ for batch in results:
+ asserts.assert_false(batch["ScanResults"],
+ "Test fail because report scan "
+ "results reported are not empty")
+ except queue.Empty as error:
+ raise AssertionError(
+ "Event did not triggered for in isolated environment {}".format(
+ error))
+ finally:
+ self.dut.ed.clear_all_events()
+ self.attenuators[0].set_atten(0)
+ self.attenuators[1].set_atten(0)
+
+ @test_tracker_info(uuid="46f817b9-97a3-455e-af2c-56f9aea64f7e")
+ def test_wifi_scanner_with_wifi_off(self):
+ """Test WiFi scanner single scan when wifi is off.
+
+ 1. Toggle wifi state to off.
+ 2. Start WifiScanner single scan for both band with default scan settings.
+ 3. Verify that scan is not started.
+ """
+ self.log.debug("Make sure wifi is off.")
+ wutils.wifi_toggle_state(self.dut, False)
+ self.start_wifi_scanner_single_scan_expect_failure(
+ self.default_scan_setting)
+ self.log.debug("Turning wifi back on.")
+ wutils.wifi_toggle_state(self.dut, True)
+
+ @test_tracker_info(uuid="257ad734-c21f-49f4-b448-3986b70eba3d")
+ def test_wifi_scanner_with_invalid_numBssidsPerScan(self):
+ """Test WiFi scanner single scan with invalid number of bssids reported
+ per scan.
+
+ 1. Start WifiScanner single scan with invalid number of bssids reported
+ per scan.
+ 2. Verify that scan results triggered for default supported number of
+ bssids per scan.
+ """
+ scan_setting = {
+ "band": wutils.WifiEnums.WIFI_BAND_BOTH_WITH_DFS,
+ "periodInMs": SCANTIME,
+ "reportEvents": wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN,
+ 'numBssidsPerScan': 33
+ }
+ self.wifi_scanner_single_scan(scan_setting)
+
+ """ Tests End """
diff --git a/acts_tests/tests/google/wifi/WifiScannerTests.config b/acts_tests/tests/google/wifi/WifiScannerTests.config
new file mode 100755
index 0000000..85d599c
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiScannerTests.config
@@ -0,0 +1,28 @@
+{
+ "_description": "Default wireless network setup for APs used in the test.",
+ "AP": [{"index": 0,
+ "radio0": {"settings": {"channel": 1}, "wifi-iface" : [{"ssid": "Test_1", "key": "hahahaha", "encryption": "psk2"},{"ssid": "Test_1.1", "key": "hahahaha", "encryption": "psk2"},
+ {"ssid": "Test_1.2", "key": "hahahaha", "encryption": "psk2"},{"ssid": "Test_1.3", "key": "hahahaha", "encryption": "psk2"}]},
+ "radio1": {"settings": {"channel": 40}, "wifi-iface" : [{"ssid": "Test_40", "key": "hahahaha", "encryption": "psk2"},{"ssid": "Test_40.1", "key": "hahahaha", "encryption": "psk2"},
+ {"ssid": "Test_40.2", "key": "hahahaha", "encryption": "psk2"},{"ssid": "Test_40.3", "key": "hahahaha", "encryption": "psk2"}]}
+ },
+ {"index": 1,
+ "radio0": {"settings": {"channel": 6}, "wifi-iface" : [{"ssid": "Test_6", "key": "hahahaha", "encryption": "psk"}, {"ssid": "Test_6.1", "key": "hahahaha", "encryption": "psk2"},
+ {"ssid": "Test_6.2", "key": "hahahaha", "encryption": "psk2"}, {"ssid": "Test_6.3", "key": "hahahaha", "encryption": "psk2"}]},
+ "radio1": {"settings": {"channel": 40}, "wifi-iface" : [{"ssid": "Test_40", "key": "hahahaha", "encryption": "psk"}, {"ssid": "Test_40.1", "key": "hahahaha", "encryption": "psk2"},
+ {"ssid": "Test_40.3", "key": "hahahaha", "encryption": "psk2"}, {"ssid": "Test_40.2", "key": "hahahaha", "encryption": "psk2"}]}
+ },
+ {"index": 2,
+ "radio0": {"settings": {"channel": 10}, "wifi-iface" : [{"ssid": "Test_10", "key": "hahahaha", "encryption": "psk2"}, {"ssid": "Test_10.1", "key": "hahahaha", "encryption": "psk2"},
+ {"ssid": "Test_10.2", "key": "hahahaha", "encryption": "psk2"}, {"ssid": "Test_10.3", "key": "hahahaha", "encryption": "psk2"}]},
+ "radio1": {"settings": {"channel": 44}, "wifi-iface" : [{"ssid": "Test_44", "key": "hahahaha", "encryption": "psk2"}, {"ssid": "Test_44.1", "key": "hahahaha", "encryption": "psk2"},
+ {"ssid": "Test_44.2", "key": "hahahaha", "encryption": "psk2"}, {"ssid": "Test_44.3", "key": "hahahaha", "encryption": "psk2"}]}
+ },
+ {"index": 3,
+ "radio0": {"settings": {"channel": 11}, "wifi-iface" : [{"ssid": "Test_11", "key": "hahahaha", "encryption": "psk2"}, {"ssid": "Test_11.1", "key": "hahahaha", "encryption": "psk2"},
+ {"ssid": "Test_11.2", "key": "hahahaha", "encryption": "psk2"}, {"ssid": "Test_11.3", "key": "hahahaha", "encryption": "psk2"}]},
+ "radio1": {"settings": {"channel": 149}, "wifi-iface" : [{"ssid": "Test_149", "key": "hahahaha", "encryption": "psk2"}, {"ssid": "Test_149.1", "key": "hahahaha", "encryption": "psk2"},
+ {"ssid": "Test_149.2", "key": "hahahaha", "encryption": "psk2"}, {"ssid": "Test_149.3", "key": "hahahaha", "encryption": "psk2"}]}
+ }
+ ]
+}
\ No newline at end of file
diff --git a/acts_tests/tests/google/wifi/WifiSensitivityTest.py b/acts_tests/tests/google/wifi/WifiSensitivityTest.py
new file mode 100644
index 0000000..7307850
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiSensitivityTest.py
@@ -0,0 +1,934 @@
+#!/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 collections
+import csv
+import itertools
+import logging
+import numpy
+import os
+from acts import asserts
+from acts import context
+from acts import base_test
+from acts import utils
+from acts.controllers import iperf_client
+from acts.controllers.utils_lib import ssh
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
+from acts.test_utils.wifi import ota_chamber
+from acts.test_utils.wifi import wifi_performance_test_utils as wputils
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi import wifi_retail_ap as retail_ap
+from functools import partial
+from WifiRvrTest import WifiRvrTest
+from WifiPingTest import WifiPingTest
+
+
+class WifiSensitivityTest(WifiRvrTest, WifiPingTest):
+ """Class to test WiFi sensitivity tests.
+
+ This class implements measures WiFi sensitivity per rate. It heavily
+ leverages the WifiRvrTest class and introduced minor differences to set
+ specific rates and the access point, and implements a different pass/fail
+ check. For an example config file to run this test class see
+ example_connectivity_performance_ap_sta.json.
+ """
+
+ RSSI_POLL_INTERVAL = 0.2
+ VALID_TEST_CONFIGS = {
+ 1: ['legacy', 'VHT20'],
+ 2: ['legacy', 'VHT20'],
+ 6: ['legacy', 'VHT20'],
+ 10: ['legacy', 'VHT20'],
+ 11: ['legacy', 'VHT20'],
+ 36: ['legacy', 'VHT20', 'VHT40', 'VHT80'],
+ 40: ['legacy', 'VHT20'],
+ 44: ['legacy', 'VHT20'],
+ 48: ['legacy', 'VHT20'],
+ 149: ['legacy', 'VHT20', 'VHT40', 'VHT80'],
+ 153: ['legacy', 'VHT20'],
+ 157: ['legacy', 'VHT20'],
+ 161: ['legacy', 'VHT20']
+ }
+ RateTuple = collections.namedtuple(('RateTuple'),
+ ['mcs', 'streams', 'data_rate'])
+ #yapf:disable
+ VALID_RATES = {
+ 'legacy_2GHz': [
+ RateTuple(54, 1, 54), RateTuple(48, 1, 48),
+ RateTuple(36, 1, 36), RateTuple(24, 1, 24),
+ RateTuple(18, 1, 18), RateTuple(12, 1, 12),
+ RateTuple(11, 1, 11), RateTuple(9, 1, 9),
+ RateTuple(6, 1, 6), RateTuple(5.5, 1, 5.5),
+ RateTuple(2, 1, 2), RateTuple(1, 1, 1)],
+ 'legacy_5GHz': [
+ RateTuple(54, 1, 54), RateTuple(48, 1, 48),
+ RateTuple(36, 1, 36), RateTuple(24, 1, 24),
+ RateTuple(18, 1, 18), RateTuple(12, 1, 12),
+ RateTuple(9, 1, 9), RateTuple(6, 1, 6)],
+ 'HT20': [
+ RateTuple(7, 1, 72.2), RateTuple(6, 1, 65),
+ RateTuple(5, 1, 57.8), RateTuple(4, 1, 43.3),
+ RateTuple(3, 1, 26), RateTuple(2, 1, 21.7),
+ RateTuple(1, 1, 14.4), RateTuple(0, 1, 7.2),
+ RateTuple(15, 2, 144.4), RateTuple(14, 2, 130),
+ RateTuple(13, 2, 115.6), RateTuple(12, 2, 86.7),
+ RateTuple(11, 2, 57.8), RateTuple(10, 2, 43.4),
+ RateTuple(9, 2, 28.9), RateTuple(8, 2, 14.4)],
+ 'VHT20': [
+ RateTuple(9, 1, 96), RateTuple(8, 1, 86.7),
+ RateTuple(7, 1, 72.2), RateTuple(6, 1, 65),
+ RateTuple(5, 1, 57.8), RateTuple(4, 1, 43.3),
+ RateTuple(3, 1, 28.9), RateTuple(2, 1, 21.7),
+ RateTuple(1, 1, 14.4), RateTuple(0, 1, 7.2),
+ RateTuple(9, 2, 192), RateTuple(8, 2, 173.3),
+ RateTuple(7, 2, 144.4), RateTuple(6, 2, 130.3),
+ RateTuple(5, 2, 115.6), RateTuple(4, 2, 86.7),
+ RateTuple(3, 2, 57.8), RateTuple(2, 2, 43.3),
+ RateTuple(1, 2, 28.9), RateTuple(0, 2, 14.4)],
+ 'VHT40': [
+ RateTuple(9, 1, 96), RateTuple(8, 1, 86.7),
+ RateTuple(7, 1, 72.2), RateTuple(6, 1, 65),
+ RateTuple(5, 1, 57.8), RateTuple(4, 1, 43.3),
+ RateTuple(3, 1, 28.9), RateTuple(2, 1, 21.7),
+ RateTuple(1, 1, 14.4), RateTuple(0, 1, 7.2),
+ RateTuple(9, 2, 192), RateTuple(8, 2, 173.3),
+ RateTuple(7, 2, 144.4), RateTuple(6, 2, 130.3),
+ RateTuple(5, 2, 115.6), RateTuple(4, 2, 86.7),
+ RateTuple(3, 2, 57.8), RateTuple(2, 2, 43.3),
+ RateTuple(1, 2, 28.9), RateTuple(0, 2, 14.4)],
+ 'VHT80': [
+ RateTuple(9, 1, 96), RateTuple(8, 1, 86.7),
+ RateTuple(7, 1, 72.2), RateTuple(6, 1, 65),
+ RateTuple(5, 1, 57.8), RateTuple(4, 1, 43.3),
+ RateTuple(3, 1, 28.9), RateTuple(2, 1, 21.7),
+ RateTuple(1, 1, 14.4), RateTuple(0, 1, 7.2),
+ RateTuple(9, 2, 192), RateTuple(8, 2, 173.3),
+ RateTuple(7, 2, 144.4), RateTuple(6, 2, 130.3),
+ RateTuple(5, 2, 115.6), RateTuple(4, 2, 86.7),
+ RateTuple(3, 2, 57.8), RateTuple(2, 2, 43.3),
+ RateTuple(1, 2, 28.9), RateTuple(0, 2, 14.4)],
+ }
+ #yapf:enable
+
+ def __init__(self, controllers):
+ base_test.BaseTestClass.__init__(self, controllers)
+ self.testcase_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_case())
+ self.testclass_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_class())
+ self.publish_testcase_metrics = True
+
+ def setup_class(self):
+ """Initializes common test hardware and parameters.
+
+ 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', 'sensitivity_test_params', 'testbed_params',
+ 'RemoteServer'
+ ]
+ opt_params = ['main_network']
+ self.unpack_userparams(req_params, opt_params)
+ self.testclass_params = self.sensitivity_test_params
+ self.num_atten = self.attenuators[0].instrument.num_atten
+ self.ping_server = ssh.connection.SshConnection(
+ ssh.settings.from_config(self.RemoteServer[0]['ssh_config']))
+ self.iperf_server = self.iperf_servers[0]
+ self.iperf_client = self.iperf_clients[0]
+ self.access_point = retail_ap.create(self.RetailAccessPoints)[0]
+ self.log.info('Access Point Configuration: {}'.format(
+ self.access_point.ap_settings))
+ self.log_path = os.path.join(logging.log_path, 'results')
+ os.makedirs(self.log_path, exist_ok=True)
+ self.atten_dut_chain_map = {}
+ self.testclass_results = []
+
+ # 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)
+
+ # Configure test retries
+ self.user_params['retry_tests'] = [self.__class__.__name__]
+
+ def teardown_class(self):
+ # Turn WiFi OFF
+ for dev in self.android_devices:
+ wutils.wifi_toggle_state(dev, False)
+ self.process_testclass_results()
+
+ def setup_test(self):
+ self.retry_flag = False
+
+ def teardown_test(self):
+ self.retry_flag = False
+
+ def on_retry(self):
+ """Function to control test logic on retried tests.
+
+ This function is automatically executed on tests that are being
+ retried. In this case the function resets wifi, toggles it off and on
+ and sets a retry_flag to enable further tweaking the test logic on
+ second attempts.
+ """
+ self.retry_flag = True
+ for dev in self.android_devices:
+ wutils.reset_wifi(dev)
+ wutils.toggle_wifi_off_and_on(dev)
+
+ def pass_fail_check(self, result):
+ """Checks sensitivity results and decides on pass/fail.
+
+ Args:
+ result: dict containing attenuation, throughput and other meta
+ data
+ """
+ result_string = ('Throughput = {}%, Sensitivity = {}.'.format(
+ result['peak_throughput_pct'], result['sensitivity']))
+ if result['peak_throughput_pct'] < 95:
+ asserts.fail('Result unreliable. {}'.format(result_string))
+ else:
+ asserts.explicit_pass('Test Passed. {}'.format(result_string))
+
+ def process_testclass_results(self):
+ """Saves and plots test results from all executed test cases."""
+ # write json output
+ testclass_results_dict = collections.OrderedDict()
+ id_fields = ['mode', 'rate', 'num_streams', 'chain_mask']
+ channels_tested = []
+ for result in self.testclass_results:
+ testcase_params = result['testcase_params']
+ test_id = self.extract_test_id(testcase_params, id_fields)
+ test_id = tuple(test_id.items())
+ if test_id not in testclass_results_dict:
+ testclass_results_dict[test_id] = collections.OrderedDict()
+ channel = testcase_params['channel']
+ if channel not in channels_tested:
+ channels_tested.append(channel)
+ if result['peak_throughput_pct'] >= 95:
+ testclass_results_dict[test_id][channel] = result[
+ 'sensitivity']
+ else:
+ testclass_results_dict[test_id][channel] = ''
+
+ # calculate average metrics
+ metrics_dict = collections.OrderedDict()
+ id_fields = ['channel', 'mode', 'num_streams', 'chain_mask']
+ for test_id in testclass_results_dict.keys():
+ for channel in testclass_results_dict[test_id].keys():
+ metric_tag = collections.OrderedDict(test_id, channel=channel)
+ metric_tag = self.extract_test_id(metric_tag, id_fields)
+ metric_tag = tuple(metric_tag.items())
+ metrics_dict.setdefault(metric_tag, [])
+ sensitivity_result = testclass_results_dict[test_id][channel]
+ if sensitivity_result != '':
+ metrics_dict[metric_tag].append(sensitivity_result)
+ for metric_tag_tuple, metric_data in metrics_dict.items():
+ metric_tag_dict = collections.OrderedDict(metric_tag_tuple)
+ metric_tag = 'ch{}_{}_nss{}_chain{}'.format(
+ metric_tag_dict['channel'], metric_tag_dict['mode'],
+ metric_tag_dict['num_streams'], metric_tag_dict['chain_mask'])
+ metric_key = "{}.avg_sensitivity".format(metric_tag)
+ metric_value = numpy.nanmean(metric_data)
+ self.testclass_metric_logger.add_metric(metric_key, metric_value)
+
+ # write csv
+ csv_header = ['Mode', 'MCS', 'Streams', 'Chain', 'Rate (Mbps)']
+ for channel in channels_tested:
+ csv_header.append('Ch. ' + str(channel))
+ results_file_path = os.path.join(self.log_path, 'results.csv')
+ with open(results_file_path, mode='w') as csv_file:
+ writer = csv.DictWriter(csv_file, fieldnames=csv_header)
+ writer.writeheader()
+ for test_id, test_results in testclass_results_dict.items():
+ test_id_dict = dict(test_id)
+ if 'legacy' in test_id_dict['mode']:
+ rate_list = self.VALID_RATES['legacy_2GHz']
+ else:
+ rate_list = self.VALID_RATES[test_id_dict['mode']]
+ data_rate = next(rate.data_rate for rate in rate_list
+ if rate[:-1] == (test_id_dict['rate'],
+ test_id_dict['num_streams']))
+ row_value = {
+ 'Mode': test_id_dict['mode'],
+ 'MCS': test_id_dict['rate'],
+ 'Streams': test_id_dict['num_streams'],
+ 'Chain': test_id_dict['chain_mask'],
+ 'Rate (Mbps)': data_rate,
+ }
+ for channel in channels_tested:
+ row_value['Ch. ' + str(channel)] = test_results.pop(
+ channel, ' ')
+ writer.writerow(row_value)
+
+ if not self.testclass_params['traffic_type'].lower() == 'ping':
+ WifiRvrTest.process_testclass_results(self)
+
+ def process_rvr_test_results(self, testcase_params, rvr_result):
+ """Post processes RvR results to compute sensitivity.
+
+ Takes in the results of the RvR tests and computes the sensitivity of
+ the current rate by looking at the point at which throughput drops
+ below the percentage specified in the config file. The function then
+ calls on its parent class process_test_results to plot the result.
+
+ Args:
+ rvr_result: dict containing attenuation, throughput and other meta
+ data
+ """
+ rvr_result['peak_throughput'] = max(rvr_result['throughput_receive'])
+ rvr_result['peak_throughput_pct'] = 100
+ throughput_check = [
+ throughput < rvr_result['peak_throughput'] *
+ (self.testclass_params['throughput_pct_at_sensitivity'] / 100)
+ for throughput in rvr_result['throughput_receive']
+ ]
+ consistency_check = [
+ idx for idx in range(len(throughput_check))
+ if all(throughput_check[idx:])
+ ]
+ rvr_result['atten_at_range'] = rvr_result['attenuation'][
+ consistency_check[0] - 1]
+ rvr_result['range'] = rvr_result['fixed_attenuation'] + (
+ rvr_result['atten_at_range'])
+ rvr_result['sensitivity'] = self.testclass_params['ap_tx_power'] + (
+ self.testbed_params['ap_tx_power_offset'][str(
+ testcase_params['channel'])] - rvr_result['range'])
+ WifiRvrTest.process_test_results(self, rvr_result)
+
+ def process_ping_test_results(self, testcase_params, ping_result):
+ """Post processes RvR results to compute sensitivity.
+
+ Takes in the results of the RvR tests and computes the sensitivity of
+ the current rate by looking at the point at which throughput drops
+ below the percentage specified in the config file. The function then
+ calls on its parent class process_test_results to plot the result.
+
+ Args:
+ rvr_result: dict containing attenuation, throughput and other meta
+ data
+ """
+ WifiPingTest.process_ping_results(self, testcase_params, ping_result)
+ ping_result['sensitivity'] = self.testclass_params['ap_tx_power'] + (
+ self.testbed_params['ap_tx_power_offset'][str(
+ testcase_params['channel'])] - ping_result['range'])
+
+ def setup_sensitivity_test(self, testcase_params):
+ if testcase_params['traffic_type'].lower() == 'ping':
+ self.setup_ping_test(testcase_params)
+ self.run_sensitivity_test = self.run_ping_test
+ self.process_sensitivity_test_results = (
+ self.process_ping_test_results)
+ else:
+ self.setup_rvr_test(testcase_params)
+ self.run_sensitivity_test = self.run_rvr_test
+ self.process_sensitivity_test_results = (
+ self.process_rvr_test_results)
+
+ def setup_ap(self, testcase_params):
+ """Sets up the AP and attenuator to compensate for AP chain imbalance.
+
+ Args:
+ testcase_params: dict containing AP and other test params
+ """
+ band = self.access_point.band_lookup_by_channel(
+ testcase_params['channel'])
+ if '2G' in band:
+ frequency = wutils.WifiEnums.channel_2G_to_freq[
+ testcase_params['channel']]
+ else:
+ frequency = wutils.WifiEnums.channel_5G_to_freq[
+ testcase_params['channel']]
+ if frequency in wutils.WifiEnums.DFS_5G_FREQUENCIES:
+ self.access_point.set_region(self.testbed_params['DFS_region'])
+ else:
+ self.access_point.set_region(self.testbed_params['default_region'])
+ self.access_point.set_channel(band, testcase_params['channel'])
+ self.access_point.set_bandwidth(band, testcase_params['mode'])
+ self.access_point.set_power(band, testcase_params['ap_tx_power'])
+ self.access_point.set_rate(band, testcase_params['mode'],
+ testcase_params['num_streams'],
+ testcase_params['rate'],
+ testcase_params['short_gi'])
+ # Set attenuator offsets and set attenuators to initial condition
+ atten_offsets = self.testbed_params['chain_offset'][str(
+ testcase_params['channel'])]
+ for atten in self.attenuators:
+ if 'AP-Chain-0' in atten.path:
+ atten.offset = atten_offsets[0]
+ elif 'AP-Chain-1' in atten.path:
+ atten.offset = atten_offsets[1]
+ else:
+ atten.offset = 0
+ self.log.info('Access Point Configuration: {}'.format(
+ self.access_point.ap_settings))
+
+ def setup_dut(self, testcase_params):
+ """Sets up the DUT in the configuration required by the test.
+
+ Args:
+ testcase_params: dict containing AP and other test params
+ """
+ # Check battery level before test
+ if not wputils.health_check(self.dut, 10):
+ asserts.skip('Battery level too low. Skipping test.')
+ # Turn screen off to preserve battery
+ self.dut.go_to_sleep()
+ if wputils.validate_network(self.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,
+ self.testclass_params['country_code'])
+ testcase_params['test_network']['channel'] = testcase_params[
+ 'channel']
+ wutils.wifi_connect(self.dut,
+ testcase_params['test_network'],
+ num_of_tries=5,
+ check_connectivity=False)
+ self.dut_ip = self.dut.droid.connectivityGetIPv4Addresses('wlan0')[0]
+ # Activate/attenuate the correct chains
+ if testcase_params['channel'] not in self.atten_dut_chain_map.keys():
+ self.atten_dut_chain_map[testcase_params[
+ 'channel']] = wputils.get_current_atten_dut_chain_map(
+ self.attenuators, self.dut, self.ping_server)
+ self.log.info("Current Attenuator-DUT Chain Map: {}".format(
+ self.atten_dut_chain_map[testcase_params['channel']]))
+ for idx, atten in enumerate(self.attenuators):
+ if self.atten_dut_chain_map[testcase_params['channel']][
+ idx] == testcase_params['attenuated_chain']:
+ atten.offset = atten.instrument.max_atten
+
+ def extract_test_id(self, testcase_params, id_fields):
+ test_id = collections.OrderedDict(
+ (param, testcase_params[param]) for param in id_fields)
+ return test_id
+
+ def get_start_atten(self, testcase_params):
+ """Gets the starting attenuation for this sensitivity test.
+
+ The function gets the starting attenuation by checking whether a test
+ as the next higher MCS has been executed. If so it sets the starting
+ point a configurable number of dBs below the next MCS's sensitivity.
+
+ Returns:
+ start_atten: starting attenuation for current test
+ """
+ # If the test is being retried, start from the beginning
+ if self.retry_flag:
+ self.log.info('Retry flag set. Setting attenuation to minimum.')
+ return self.testclass_params['atten_start']
+ # Get the current and reference test config. The reference test is the
+ # one performed at the current MCS+1
+ current_rate = testcase_params['rate']
+ ref_test_params = self.extract_test_id(
+ testcase_params,
+ ['channel', 'mode', 'rate', 'num_streams', 'chain_mask'])
+ if 'legacy' in testcase_params['mode']:
+ if testcase_params['channel'] <= 13:
+ rate_list = self.VALID_RATES['legacy_2GHz']
+ else:
+ rate_list = self.VALID_RATES['legacy_5GHz']
+ ref_index = max(
+ 0,
+ rate_list.index(self.RateTuple(current_rate, 1, current_rate))
+ - 1)
+ ref_test_params['rate'] = rate_list[ref_index].mcs
+ else:
+ ref_test_params['rate'] = current_rate + 1
+
+ # Check if reference test has been run and set attenuation accordingly
+ previous_params = [
+ self.extract_test_id(
+ result['testcase_params'],
+ ['channel', 'mode', 'rate', 'num_streams', 'chain_mask'])
+ for result in self.testclass_results
+ ]
+
+ try:
+ ref_index = previous_params.index(ref_test_params)
+ start_atten = self.testclass_results[ref_index][
+ 'atten_at_range'] - (
+ self.testclass_params['adjacent_mcs_range_gap'])
+ except ValueError:
+ self.log.warning(
+ 'Reference test not found. Starting from {} dB'.format(
+ self.testclass_params['atten_start']))
+ start_atten = self.testclass_params['atten_start']
+ start_atten = max(start_atten, 0)
+ return start_atten
+
+ def compile_test_params(self, testcase_params):
+ """Function that generates test params based on the test name."""
+ band = self.access_point.band_lookup_by_channel(
+ testcase_params['channel'])
+ testcase_params['test_network'] = self.main_network[band]
+ if testcase_params['chain_mask'] in ['0', '1']:
+ testcase_params['attenuated_chain'] = 'DUT-Chain-{}'.format(
+ 1 if testcase_params['chain_mask'] == '0' else 0)
+ else:
+ # Set attenuated chain to -1. Do not set to None as this will be
+ # compared to RF chain map which may include None
+ testcase_params['attenuated_chain'] = -1
+
+ self.testclass_params[
+ 'range_ping_loss_threshold'] = 100 - self.testclass_params[
+ 'throughput_pct_at_sensitivity']
+ if self.testclass_params['traffic_type'] == 'UDP':
+ testcase_params['iperf_args'] = '-i 1 -t {} -J -u -b {}'.format(
+ self.testclass_params['iperf_duration'],
+ self.testclass_params['UDP_rates'][testcase_params['mode']])
+ elif self.testclass_params['traffic_type'] == 'TCP':
+ testcase_params['iperf_args'] = '-i 1 -t {} -J'.format(
+ self.testclass_params['iperf_duration'])
+
+ if self.testclass_params['traffic_type'] != 'ping' and isinstance(
+ self.iperf_client, iperf_client.IPerfClientOverAdb):
+ testcase_params['iperf_args'] += ' -R'
+ testcase_params['use_client_output'] = True
+ else:
+ testcase_params['use_client_output'] = False
+
+ return testcase_params
+
+ def _test_sensitivity(self, testcase_params):
+ """ Function that gets called for each test case
+
+ The function gets called in each rvr test case. The function customizes
+ the rvr test based on the test name of the test that called it
+ """
+ # Compile test parameters from config and test name
+ testcase_params = self.compile_test_params(testcase_params)
+ testcase_params.update(self.testclass_params)
+ testcase_params['atten_start'] = self.get_start_atten(testcase_params)
+ num_atten_steps = int(
+ (testcase_params['atten_stop'] - testcase_params['atten_start']) /
+ testcase_params['atten_step'])
+ testcase_params['atten_range'] = [
+ testcase_params['atten_start'] + x * testcase_params['atten_step']
+ for x in range(0, num_atten_steps)
+ ]
+
+ # Prepare devices and run test
+ self.setup_sensitivity_test(testcase_params)
+ result = self.run_sensitivity_test(testcase_params)
+ self.process_sensitivity_test_results(testcase_params, result)
+
+ # Post-process results
+ self.testclass_results.append(result)
+ self.pass_fail_check(result)
+
+ def generate_test_cases(self, channels, modes, chain_mask):
+ """Function that auto-generates test cases for a test class."""
+ test_cases = []
+ for channel in channels:
+ requested_modes = [
+ mode for mode in modes
+ if mode in self.VALID_TEST_CONFIGS[channel]
+ ]
+ for mode in requested_modes:
+ if 'VHT' in mode:
+ rates = self.VALID_RATES[mode]
+ elif 'HT' in mode:
+ rates = self.VALID_RATES[mode]
+ elif 'legacy' in mode and channel < 14:
+ rates = self.VALID_RATES['legacy_2GHz']
+ elif 'legacy' in mode and channel > 14:
+ rates = self.VALID_RATES['legacy_5GHz']
+ else:
+ raise ValueError('Invalid test mode.')
+ for chain, rate in itertools.product(chain_mask, rates):
+ testcase_params = collections.OrderedDict(
+ channel=channel,
+ mode=mode,
+ rate=rate.mcs,
+ num_streams=rate.streams,
+ short_gi=1,
+ chain_mask=chain)
+ if chain in ['0', '1'] and rate[1] == 2:
+ # Do not test 2-stream rates in single chain mode
+ continue
+ if 'legacy' in mode:
+ testcase_name = ('test_sensitivity_ch{}_{}_{}_nss{}'
+ '_ch{}'.format(
+ channel, mode,
+ str(rate.mcs).replace('.', 'p'),
+ rate.streams, chain))
+ else:
+ testcase_name = ('test_sensitivity_ch{}_{}_mcs{}_nss{}'
+ '_ch{}'.format(
+ channel, mode, rate.mcs,
+ rate.streams, chain))
+ setattr(self, testcase_name,
+ partial(self._test_sensitivity, testcase_params))
+ test_cases.append(testcase_name)
+ return test_cases
+
+
+class WifiSensitivity_AllChannels_Test(WifiSensitivityTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases(
+ [6, 36, 40, 44, 48, 149, 153, 157, 161],
+ ['VHT20', 'VHT40', 'VHT80'], ['0', '1', '2x2'])
+
+
+class WifiSensitivity_SampleChannels_Test(WifiSensitivityTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases([6, 36, 149],
+ ['VHT20', 'VHT40', 'VHT80'],
+ ['0', '1', '2x2'])
+
+
+class WifiSensitivity_2GHz_Test(WifiSensitivityTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases([1, 2, 6, 10, 11], ['VHT20'],
+ ['0', '1', '2x2'])
+
+
+class WifiSensitivity_5GHz_Test(WifiSensitivityTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases(
+ [36, 40, 44, 48, 149, 153, 157, 161], ['VHT20', 'VHT40', 'VHT80'],
+ ['0', '1', '2x2'])
+
+
+class WifiSensitivity_UNII1_Test(WifiSensitivityTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases([36, 40, 44, 48],
+ ['VHT20', 'VHT40', 'VHT80'],
+ ['0', '1', '2x2'])
+
+
+class WifiSensitivity_UNII3_Test(WifiSensitivityTest):
+ def __init__(self, controllers):
+ super().__init__(controllers)
+ self.tests = self.generate_test_cases([149, 153, 157, 161],
+ ['VHT20', 'VHT40', 'VHT80'],
+ ['0', '1', '2x2'])
+
+
+# Over-the air version of senstivity tests
+class WifiOtaSensitivityTest(WifiSensitivityTest):
+ """Class to test over-the-air senstivity.
+
+ This class implements measures WiFi sensitivity tests in an OTA chamber.
+ It allows setting orientation and other chamber parameters to study
+ performance in varying channel conditions
+ """
+ def __init__(self, controllers):
+ base_test.BaseTestClass.__init__(self, controllers)
+ self.testcase_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_case())
+ self.testclass_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_class())
+ self.publish_testcase_metrics = False
+
+ def setup_class(self):
+ WifiSensitivityTest.setup_class(self)
+ self.current_chain_mask = '2x2'
+ self.ota_chamber = ota_chamber.create(
+ self.user_params['OTAChamber'])[0]
+
+ def teardown_class(self):
+ WifiSensitivityTest.teardown_class(self)
+ self.ota_chamber.reset_chamber()
+
+ def setup_sensitivity_test(self, testcase_params):
+ # Setup turntable
+ self.ota_chamber.set_orientation(testcase_params['orientation'])
+ # Continue test setup
+ WifiSensitivityTest.setup_sensitivity_test(self, testcase_params)
+
+ def setup_dut(self, testcase_params):
+ """Sets up the DUT in the configuration required by the test.
+
+ Args:
+ testcase_params: dict containing AP and other test params
+ """
+ # Configure the right INI settings
+ if testcase_params['chain_mask'] != self.current_chain_mask:
+ self.log.info('Updating WiFi chain mask to: {}'.format(
+ testcase_params['chain_mask']))
+ self.current_chain_mask = testcase_params['chain_mask']
+ if testcase_params['chain_mask'] in ['0', '1']:
+ wputils.set_ini_single_chain_mode(
+ self.dut, int(testcase_params['chain_mask']))
+ else:
+ wputils.set_ini_two_chain_mode(self.dut)
+ # Check battery level before test
+ if not wputils.health_check(self.dut, 10):
+ asserts.skip('Battery level too low. Skipping test.')
+ # Turn screen off to preserve battery
+ self.dut.go_to_sleep()
+ if wputils.validate_network(self.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,
+ self.testclass_params['country_code'])
+ testcase_params['test_network']['channel'] = testcase_params[
+ 'channel']
+ wutils.wifi_connect(self.dut,
+ testcase_params['test_network'],
+ num_of_tries=5,
+ check_connectivity=False)
+ self.dut_ip = self.dut.droid.connectivityGetIPv4Addresses('wlan0')[0]
+
+ def process_testclass_results(self):
+ """Saves and plots test results from all executed test cases."""
+ testclass_results_dict = collections.OrderedDict()
+ id_fields = ['channel', 'mode', 'rate']
+ plots = []
+ for result in self.testclass_results:
+ test_id = self.extract_test_id(result['testcase_params'],
+ id_fields)
+ test_id = tuple(test_id.items())
+ chain_mask = result['testcase_params']['chain_mask']
+ num_streams = result['testcase_params']['num_streams']
+ line_id = (chain_mask, num_streams)
+ if test_id not in testclass_results_dict:
+ testclass_results_dict[test_id] = collections.OrderedDict()
+ if line_id not in testclass_results_dict[test_id]:
+ testclass_results_dict[test_id][line_id] = {
+ 'orientation': [],
+ 'sensitivity': []
+ }
+ orientation = result['testcase_params']['orientation']
+ if result['peak_throughput_pct'] >= 95:
+ sensitivity = result['sensitivity']
+ else:
+ sensitivity = float('nan')
+ if orientation not in testclass_results_dict[test_id][line_id][
+ 'orientation']:
+ testclass_results_dict[test_id][line_id]['orientation'].append(
+ orientation)
+ testclass_results_dict[test_id][line_id]['sensitivity'].append(
+ sensitivity)
+ else:
+ testclass_results_dict[test_id][line_id]['sensitivity'][
+ -1] = sensitivity
+
+ for test_id, test_data in testclass_results_dict.items():
+ test_id_dict = dict(test_id)
+ if 'legacy' in test_id_dict['mode']:
+ test_id_str = 'Channel {} - {} {}Mbps'.format(
+ test_id_dict['channel'], test_id_dict['mode'],
+ test_id_dict['rate'])
+ else:
+ test_id_str = 'Channel {} - {} MCS{}'.format(
+ test_id_dict['channel'], test_id_dict['mode'],
+ test_id_dict['rate'])
+ curr_plot = wputils.BokehFigure(
+ title=str(test_id_str),
+ x_label='Orientation (deg)',
+ primary_y_label='Sensitivity (dBm)')
+ for line_id, line_results in test_data.items():
+ curr_plot.add_line(line_results['orientation'],
+ line_results['sensitivity'],
+ legend='Nss{} - Chain Mask {}'.format(
+ line_id[1], line_id[0]),
+ marker='circle')
+ if 'legacy' in test_id_dict['mode']:
+ metric_tag = 'ota_summary_ch{}_{}_{}_ch{}'.format(
+ test_id_dict['channel'], test_id_dict['mode'],
+ test_id_dict['rate'], line_id[0])
+ else:
+ metric_tag = 'ota_summary_ch{}_{}_mcs{}_nss{}_ch{}'.format(
+ test_id_dict['channel'], test_id_dict['mode'],
+ test_id_dict['rate'], line_id[1], line_id[0])
+
+ metric_name = metric_tag + '.avg_sensitivity'
+ metric_value = numpy.nanmean(line_results['sensitivity'])
+ self.testclass_metric_logger.add_metric(
+ metric_name, metric_value)
+ self.log.info(("Average Sensitivity for {}: {:.1f}").format(
+ metric_tag, metric_value))
+ current_context = (
+ context.get_current_context().get_full_output_path())
+ output_file_path = os.path.join(current_context,
+ str(test_id_str) + '.html')
+ curr_plot.generate_figure(output_file_path)
+ plots.append(curr_plot)
+ output_file_path = os.path.join(current_context, 'results.html')
+ wputils.BokehFigure.save_figures(plots, output_file_path)
+
+ def get_start_atten(self, testcase_params):
+ """Gets the starting attenuation for this sensitivity test.
+
+ The function gets the starting attenuation by checking whether a test
+ at the same rate configuration has executed. If so it sets the starting
+ point a configurable number of dBs below the reference test.
+
+ Returns:
+ start_atten: starting attenuation for current test
+ """
+ # If the test is being retried, start from the beginning
+ if self.retry_flag:
+ self.log.info('Retry flag set. Setting attenuation to minimum.')
+ return self.testclass_params['atten_start']
+ # Get the current and reference test config. The reference test is the
+ # one performed at the current MCS+1
+ ref_test_params = self.extract_test_id(
+ testcase_params,
+ ['channel', 'mode', 'rate', 'num_streams', 'chain_mask'])
+ # Check if reference test has been run and set attenuation accordingly
+ previous_params = [
+ self.extract_test_id(
+ result['testcase_params'],
+ ['channel', 'mode', 'rate', 'num_streams', 'chain_mask'])
+ for result in self.testclass_results
+ ]
+ try:
+ ref_index = previous_params[::-1].index(ref_test_params)
+ ref_index = len(previous_params) - 1 - ref_index
+ start_atten = self.testclass_results[ref_index][
+ 'atten_at_range'] - (
+ self.testclass_params['adjacent_mcs_range_gap'])
+ except ValueError:
+ print('Reference test not found. Starting from {} dB'.format(
+ self.testclass_params['atten_start']))
+ start_atten = self.testclass_params['atten_start']
+ start_atten = max(start_atten, 0)
+ return start_atten
+
+ def generate_test_cases(self, channels, modes, requested_rates, chain_mask,
+ angles):
+ """Function that auto-generates test cases for a test class."""
+ test_cases = []
+ for channel in channels:
+ requested_modes = [
+ mode for mode in modes
+ if mode in self.VALID_TEST_CONFIGS[channel]
+ ]
+ for chain, mode in itertools.product(chain_mask, requested_modes):
+ if 'VHT' in mode:
+ valid_rates = self.VALID_RATES[mode]
+ elif 'HT' in mode:
+ valid_rates = self.VALID_RATES[mode]
+ elif 'legacy' in mode and channel < 14:
+ valid_rates = self.VALID_RATES['legacy_2GHz']
+ elif 'legacy' in mode and channel > 14:
+ valid_rates = self.VALID_RATES['legacy_5GHz']
+ else:
+ raise ValueError('Invalid test mode.')
+ for rate, angle in itertools.product(valid_rates, angles):
+ testcase_params = collections.OrderedDict(
+ channel=channel,
+ mode=mode,
+ rate=rate.mcs,
+ num_streams=rate.streams,
+ short_gi=1,
+ chain_mask=chain,
+ orientation=angle)
+ if rate not in requested_rates:
+ continue
+ if str(chain) in ['0', '1'] and rate[1] == 2:
+ # Do not test 2-stream rates in single chain mode
+ continue
+ if 'legacy' in mode:
+ testcase_name = ('test_sensitivity_ch{}_{}_{}_nss{}'
+ '_ch{}_{}deg'.format(
+ channel, mode,
+ str(rate.mcs).replace('.', 'p'),
+ rate.streams, chain, angle))
+ else:
+ testcase_name = ('test_sensitivity_ch{}_{}_mcs{}_nss{}'
+ '_ch{}_{}deg'.format(
+ channel, mode, rate.mcs,
+ rate.streams, chain, angle))
+ setattr(self, testcase_name,
+ partial(self._test_sensitivity, testcase_params))
+ test_cases.append(testcase_name)
+ return test_cases
+
+
+class WifiOtaSensitivity_TenDegree_Test(WifiOtaSensitivityTest):
+ def __init__(self, controllers):
+ WifiOtaSensitivityTest.__init__(self, controllers)
+ requested_channels = [6, 36, 149]
+ requested_rates = [
+ self.RateTuple(8, 1, 86.7),
+ self.RateTuple(2, 1, 21.7),
+ self.RateTuple(8, 2, 173.3),
+ self.RateTuple(2, 2, 43.3)
+ ]
+ self.tests = self.generate_test_cases(requested_channels,
+ ['VHT20', 'VHT80'],
+ requested_rates, ['2x2'],
+ list(range(0, 360, 10)))
+
+
+class WifiOtaSensitivity_PerChain_TenDegree_Test(WifiOtaSensitivityTest):
+ def __init__(self, controllers):
+ WifiOtaSensitivityTest.__init__(self, controllers)
+ requested_channels = [6, 36, 149]
+ requested_rates = [
+ self.RateTuple(2, 1, 21.7),
+ self.RateTuple(2, 2, 43.3)
+ ]
+ self.tests = self.generate_test_cases(requested_channels, ['VHT20'],
+ requested_rates,
+ ['0', '1', '2x2'],
+ list(range(0, 360, 10)))
+
+
+class WifiOtaSensitivity_ThirtyDegree_Test(WifiOtaSensitivityTest):
+ def __init__(self, controllers):
+ WifiOtaSensitivityTest.__init__(self, controllers)
+ requested_channels = [6, 36, 149]
+ requested_rates = [
+ self.RateTuple(9, 1, 96),
+ self.RateTuple(8, 1, 86.7),
+ self.RateTuple(7, 1, 72.2),
+ self.RateTuple(4, 1, 43.3),
+ self.RateTuple(2, 1, 21.7),
+ self.RateTuple(0, 1, 7.2),
+ self.RateTuple(9, 2, 192),
+ self.RateTuple(8, 2, 173.3),
+ self.RateTuple(7, 2, 144.4),
+ self.RateTuple(4, 2, 86.7),
+ self.RateTuple(2, 2, 43.3),
+ self.RateTuple(0, 2, 14.4)
+ ]
+ self.tests = self.generate_test_cases(requested_channels,
+ ['VHT20', 'VHT80'],
+ requested_rates, ['2x2'],
+ list(range(0, 360, 30)))
+
+
+class WifiOtaSensitivity_45Degree_Test(WifiOtaSensitivityTest):
+ def __init__(self, controllers):
+ WifiOtaSensitivityTest.__init__(self, controllers)
+ requested_rates = [
+ self.RateTuple(8, 1, 86.7),
+ self.RateTuple(2, 1, 21.7),
+ self.RateTuple(8, 2, 173.3),
+ self.RateTuple(2, 2, 43.3)
+ ]
+ self.tests = self.generate_test_cases(
+ [1, 6, 11, 36, 40, 44, 48, 149, 153, 157, 161], ['VHT20', 'VHT80'],
+ requested_rates, ['2x2'], list(range(0, 360, 45)))
diff --git a/acts_tests/tests/google/wifi/WifiServiceApiTest.py b/acts_tests/tests/google/wifi/WifiServiceApiTest.py
new file mode 100644
index 0000000..afe2a84
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiServiceApiTest.py
@@ -0,0 +1,209 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2016 - 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
+import queue
+import sys
+import time
+
+from acts import signals
+from acts import utils
+from acts.test_decorators import test_tracker_info
+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
+
+
+class WifiServiceApiTest(WifiBaseTest):
+ """This class tests the API surface of WifiManager in different wifi states.
+
+ Attributes:
+ The tests in this class only require one DUT.
+ The tests in this class do not require a SIM (but it is ok if one is
+ present).
+ """
+
+
+ TEST_SSID_PREFIX = "test_config_"
+ CONFIG_ELEMENT = 'config'
+ NETWORK_ID_ELEMENT = 'network_id'
+
+ 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.
+ """
+ super().setup_class()
+ self.dut = self.android_devices[0]
+ # Do a simple version of init - mainly just sync the time and enable
+ # verbose logging. We would also like to test with phones in less
+ # constrained states (or add variations where we specifically
+ # constrain).
+ utils.require_sl4a((self.dut, ))
+ utils.sync_device_time(self.dut)
+
+ # Enable verbose logging on the dut
+ self.dut.droid.wifiEnableVerboseLogging(1)
+ if self.dut.droid.wifiGetVerboseLoggingLevel() != 1:
+ raise signals.TestFailure(
+ "Failed to enable WiFi verbose logging on the dut.")
+
+ def teardown_class(self):
+ wutils.reset_wifi(self.dut)
+
+ def create_and_save_wifi_network_config(self):
+ """ Create a config with random SSID and password.
+
+ Returns:
+ A tuple with the config and networkId for the newly created and saved network.
+ """
+ config_ssid = self.TEST_SSID_PREFIX + utils.rand_ascii_str(8)
+ config_password = utils.rand_ascii_str(8)
+ self.dut.log.info("creating config: %s %s", config_ssid, config_password)
+ config = {wutils.WifiEnums.SSID_KEY: config_ssid}
+ config[wutils.WifiEnums.PWD_KEY] = config_password
+
+ # Now save the config.
+ network_id = self.dut.droid.wifiAddNetwork(config)
+ self.dut.log.info("saved config: network_id = %s", network_id)
+ return {self.NETWORK_ID_ELEMENT: network_id, self.CONFIG_ELEMENT: config}
+
+ def check_network_config_saved(self, config):
+ """ Get the configured networks and check of the provided config
+ is present. This method only checks if the SSID is the same.
+ TODO: should do a deeper check to make sure this is the
+ correct config.
+
+ Args:
+ config: WifiConfig for a network.
+
+ Returns:
+ True if the WifiConfig is present.
+ """
+ networks = self.dut.droid.wifiGetConfiguredNetworks()
+ if not networks:
+ return False
+ ssid_key = wutils.WifiEnums.SSID_KEY
+ for network in networks:
+ if config[ssid_key] == network[ssid_key]:
+ return True
+ return False
+
+ def forget_network(self, network_id):
+ """ Simple method to call wifiForgetNetwork and wait for confirmation
+ callback. The method returns False if it was not removed.
+
+ Returns:
+ True if network was successfully deleted.
+ """
+ self.dut.log.info("deleting config: networkId = %s", network_id)
+ self.dut.droid.wifiForgetNetwork(network_id)
+ try:
+ event = self.dut.ed.pop_event(wifi_constants.WIFI_FORGET_NW_SUCCESS, 10)
+ return True
+ except queue.Empty:
+ self.dut.log.error("Failed to forget network")
+ return False
+
+
+ """ Tests Begin """
+ @test_tracker_info(uuid="f4df08c2-d3d5-4032-a433-c15f55130d4a")
+ def test_remove_config_wifi_enabled(self):
+ """ Test if config can be deleted when wifi is enabled.
+
+ 1. Enable wifi, if needed
+ 2. Create and save a random config.
+ 3. Confirm the config is present.
+ 4. Remove the config.
+ 5. Confirm the config is not listed.
+ """
+ wutils.wifi_toggle_state(self.dut, True)
+ test_network = self.create_and_save_wifi_network_config()
+ if not self.check_network_config_saved(test_network[self.CONFIG_ELEMENT]):
+ raise signals.TestFailure(
+ "Test network not found in list of configured networks.")
+ if not self.forget_network(test_network[self.NETWORK_ID_ELEMENT]):
+ raise signals.TestFailure(
+ "Test network not deleted from configured networks.")
+ if self.check_network_config_saved(test_network[self.CONFIG_ELEMENT]):
+ raise signals.TestFailure(
+ "Deleted network was in configured networks list.")
+
+ @test_tracker_info(uuid="9af96c7d-a316-4d57-ba5f-c992427c237b")
+ def test_remove_config_wifi_disabled(self):
+ """ Test if config can be deleted when wifi is disabled.
+
+ 1. Enable wifi, if needed
+ 2. Create and save a random config.
+ 3. Confirm the config is present.
+ 4. Disable wifi.
+ 5. Remove the config.
+ 6. Confirm the config is not listed.
+ """
+ wutils.wifi_toggle_state(self.dut, True)
+ test_network = self.create_and_save_wifi_network_config()
+ if not self.check_network_config_saved(test_network[self.CONFIG_ELEMENT]):
+ raise signals.TestFailure(
+ "Test network not found in list of configured networks.")
+ wutils.wifi_toggle_state(self.dut, False)
+ if not self.forget_network(test_network[self.NETWORK_ID_ELEMENT]):
+ raise signals.TestFailure("Failed to delete network.")
+ if self.check_network_config_saved(test_network[self.CONFIG_ELEMENT]):
+ raise signals.TestFailure(
+ "Test network was found in list of configured networks.")
+
+ @test_tracker_info(uuid="79204ae6-323b-4257-a2cb-2225d44199d4")
+ def test_retrieve_config_wifi_enabled(self):
+ """ Test if config can be retrieved when wifi is enabled.
+
+ 1. Enable wifi
+ 2. Create and save a random config
+ 3. Retrieve the config
+ 4. Remove the config (clean up from the test)
+ """
+ wutils.wifi_toggle_state(self.dut, True)
+ test_network = self.create_and_save_wifi_network_config()
+
+ if not self.check_network_config_saved(test_network[self.CONFIG_ELEMENT]):
+ raise signals.TestFailure(
+ "Test network not found in list of configured networks.")
+ if not self.forget_network(test_network[self.NETWORK_ID_ELEMENT]):
+ raise signals.TestFailure("Failed to delete network.")
+
+ @test_tracker_info(uuid="58fb4f81-bc19-43e1-b0af-89dbd17f45b2")
+ def test_retrieve_config_wifi_disabled(self):
+ """ Test if config can be retrieved when wifi is disabled.
+
+ 1. Disable wifi
+ 2. Create and save a random config
+ 3. Retrieve the config
+ 4. Remove the config (clean up from the test)
+ """
+ wutils.wifi_toggle_state(self.dut, False)
+ test_network = self.create_and_save_wifi_network_config()
+ if not self.check_network_config_saved(test_network[self.CONFIG_ELEMENT]):
+ raise signals.TestFailure(
+ "Test network not found in list of configured networks.")
+ if not self.forget_network(test_network[self.NETWORK_ID_ELEMENT]):
+ raise signals.TestFailure("Failed to delete network.")
+
+ """ Tests End """
+
+
+if __name__ == "__main__":
+ pass
diff --git a/acts_tests/tests/google/wifi/WifiSoftApAcsTest.py b/acts_tests/tests/google/wifi/WifiSoftApAcsTest.py
new file mode 100644
index 0000000..a3cbdfe
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiSoftApAcsTest.py
@@ -0,0 +1,616 @@
+#!/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 sys
+import time
+
+import acts.base_test
+import acts.signals as signals
+import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts.utils as utils
+
+from acts import asserts
+from acts.controllers.ap_lib import hostapd_constants
+from acts.test_decorators import test_tracker_info
+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.wifi.WifiBaseTest import WifiBaseTest
+from threading import Thread
+
+WifiEnums = wutils.WifiEnums
+WIFI_CONFIG_APBAND_AUTO = WifiEnums.WIFI_CONFIG_SOFTAP_BAND_2G_5G
+GET_FREQUENCY_NUM_RETRIES = 3
+
+class WifiSoftApAcsTest(WifiBaseTest):
+ """Tests for Automatic Channel Selection.
+
+ Test Bed Requirement:
+ * Two Android devices and an AP.
+ * 2GHz and 5GHz Wi-Fi network visible to the device.
+ """
+
+ def setup_class(self):
+ super().setup_class()
+
+ 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)
+ 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 = []
+ opt_param = ["iperf_server_address", "reference_networks",
+ "iperf_server_port", "pixel_models"]
+ self.unpack_userparams(
+ req_param_names=req_params, opt_param_names=opt_param)
+ self.chan_map = {v: k for k, v in hostapd_constants.CHANNEL_MAP.items()}
+ self.pcap_procs = None
+
+ def setup_test(self):
+ super().setup_test()
+ if hasattr(self, 'packet_capture'):
+ chan = self.test_name.split('_')[-1]
+ if chan.isnumeric():
+ band = '2G' if self.chan_map[int(chan)] < 5000 else '5G'
+ self.packet_capture[0].configure_monitor_mode(band, int(chan))
+ self.pcap_procs = wutils.start_pcap(
+ self.packet_capture[0], band, self.test_name)
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+
+ def teardown_test(self):
+ super().teardown_test()
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ wutils.stop_wifi_tethering(self.dut)
+ wutils.reset_wifi(self.dut)
+ wutils.reset_wifi(self.dut_client)
+ if hasattr(self, 'packet_capture') and self.pcap_procs:
+ wutils.stop_pcap(self.packet_capture[0], self.pcap_procs, False)
+ self.pcap_procs = None
+ try:
+ if "AccessPoint" in self.user_params:
+ del self.user_params["reference_networks"]
+ del self.user_params["open_network"]
+ except:
+ pass
+ self.access_points[0].close()
+
+ """Helper Functions"""
+
+ def run_iperf_client(self, params):
+ """Run iperf traffic after connection.
+
+ Args:
+ params: A tuple of network info and AndroidDevice object.
+
+ """
+ if "iperf_server_address" in self.user_params:
+ network, ad = params
+ SSID = network[WifiEnums.SSID_KEY]
+ self.log.info("Starting iperf traffic through {}".format(SSID))
+ port_arg = "-p {} -t {}".format(self.iperf_server_port, 3)
+ success, data = ad.run_iperf_client(self.iperf_server_address,
+ port_arg)
+ self.log.debug(pprint.pformat(data))
+ asserts.assert_true(success, "Error occurred in iPerf traffic.")
+ self.log.info("Finished iperf traffic through {}".format(SSID))
+
+ def start_softap_and_verify(self, band):
+ """Bring-up softap and verify AP mode and in scan results.
+
+ Args:
+ band: The band to use for softAP.
+
+ """
+ config = wutils.create_softap_config()
+ wutils.start_wifi_tethering(self.dut,
+ config[wutils.WifiEnums.SSID_KEY],
+ config[wutils.WifiEnums.PWD_KEY], band=band)
+ asserts.assert_true(self.dut.droid.wifiIsApEnabled(),
+ "SoftAp is not reported as running")
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ self.dut_client, config[wutils.WifiEnums.SSID_KEY])
+ return config
+
+ def get_softap_acs(self, softap):
+ """Connect to the softap on client-dut and get the softap channel
+ information.
+
+ Args:
+ softap: The softap network configuration information.
+
+ """
+ wutils.connect_to_wifi_network(self.dut_client, softap,
+ check_connectivity=False)
+ 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):
+ """Configure and bring up AP on required channel.
+
+ Args:
+ channel_2g: The channel number to use for 2GHz network.
+ 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:
+ 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.
+
+ Args:
+ network: Network information of the network to connect to.
+ softap_band: The band to use for softAP.
+
+ """
+ if not network:
+ # For a clean environment just bring up softap and return channel.
+ softap = self.start_softap_and_verify(softap_band)
+ channel = self.get_softap_acs(softap)
+ return channel
+ # Connect to the AP and start IPerf traffic, while we bring up softap.
+ wutils.connect_to_wifi_network(self.dut_client, network)
+ t = Thread(target=self.run_iperf_client,args=((network,self.dut_client),))
+ t.setDaemon(True)
+ t.start()
+ time.sleep(1)
+ softap = self.start_softap_and_verify(softap_band)
+ t.join()
+ channel = self.get_softap_acs(softap)
+ return channel
+
+ def verify_acs_channel(self, chan, avoid_chan):
+ """Verify ACS algorithm by ensuring that softAP came up on a channel,
+ different than the active channels.
+
+ Args:
+ chan: The channel number softap came-up on.
+ avoid_chan: The channel to avoid during this test.
+
+ """
+ if avoid_chan in range(1,12):
+ avoid_chan2 = hostapd_constants.AP_DEFAULT_CHANNEL_5G
+ elif avoid_chan in range(36, 166):
+ avoid_chan2 = hostapd_constants.AP_DEFAULT_CHANNEL_2G
+ if chan == avoid_chan or chan == avoid_chan2:
+ raise signals.TestFailure("ACS chose the same channel that the "
+ "AP was beaconing on. Channel = %d" % chan)
+
+ """Tests"""
+ @test_tracker_info(uuid="3507bd18-e787-4380-8725-1872916d4267")
+ def test_softap_2G_clean_env(self):
+ """Test to bring up SoftAp on 2GHz in clean environment."""
+ network = None
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ if not chan in range(1, 12):
+ raise signals.TestFailure("ACS chose incorrect channel %d for 2GHz "
+ "band" % chan)
+
+ @test_tracker_info(uuid="3d18da8b-d29a-45f9-8018-5348e10099e9")
+ def test_softap_5G_clean_env(self):
+ """Test to bring up SoftAp on 5GHz in clean environment."""
+ network = None
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ if not chan in range(36, 166):
+ # Note: This does not treat DFS channel separately.
+ raise signals.TestFailure("ACS chose incorrect channel %d for 5GHz "
+ "band" % chan)
+
+ @test_tracker_info(uuid="cc353bda-3831-4d6e-b990-e501b8e4eafe")
+ def test_softap_auto_clean_env(self):
+ """Test to bring up SoftAp on AUTO-band in clean environment."""
+ network = None
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_AUTO)
+ if not chan in range(36, 166):
+ # Note: This does not treat DFS channel separately.
+ raise signals.TestFailure("ACS chose incorrect channel %d for 5GHz "
+ "band" % chan)
+
+ @test_tracker_info(uuid="a5f6a926-76d2-46a7-8136-426e35b5a5a8")
+ def test_softap_2G_avoid_channel_1(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_2g=1)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="757e2019-b027-40bf-a562-2b01f3e5957e")
+ def test_softap_5G_avoid_channel_1(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_2g=1)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="b96e39d1-9041-4662-a55f-22641c2e2b02")
+ def test_softap_2G_avoid_channel_2(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_2g=2)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="941c4e2b-ae35-4b49-aa81-13d3dc44b5b6")
+ def test_softap_5G_avoid_channel_2(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_2g=2)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="444c4a34-7f6b-4f02-9802-2e896e7d1796")
+ def test_softap_2G_avoid_channel_3(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_2g=3)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="eccd06b1-6df5-4144-8fda-1504cb822375")
+ def test_softap_5G_avoid_channel_3(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_2g=3)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="fb257644-2081-4c3d-8394-7a308dde0047")
+ def test_softap_2G_avoid_channel_4(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_2g=4)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="88b9cd16-4541-408a-8607-415fe60001f2")
+ def test_softap_5G_avoid_channel_4(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_2g=4)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="b3626ec8-50e8-412c-bdbe-5c5ade647d7b")
+ def test_softap_2G_avoid_channel_5(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_2g=5)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="45c4396b-9b0c-44f3-adf2-ea9c86fcab1d")
+ def test_softap_5G_avoid_channel_5(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_2g=5)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="f70634e7-c6fd-403d-8cd7-439fbbda6af0")
+ def test_softap_2G_avoid_channel_6(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_2g=6)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="f3341136-10bc-44e2-b9a8-2d27d3284b73")
+ def test_softap_5G_avoid_channel_6(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_2g=6)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="8129594d-1608-448b-8548-5a8c4022f2a1")
+ def test_softap_2G_avoid_channel_7(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_2g=7)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="7b470b82-d19b-438c-8f98-ce697e0eb474")
+ def test_softap_5G_avoid_channel_7(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_2g=7)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="11540182-d471-4bf0-8f8b-add89443c329")
+ def test_softap_2G_avoid_channel_8(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_2g=8)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="1280067c-389e-42e9-aa75-6bfbd61340f3")
+ def test_softap_5G_avoid_channel_8(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_2g=8)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="6feeb83c-2723-49cb-93c1-6297d4a3d853")
+ def test_softap_2G_avoid_channel_9(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_2g=9)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="49a110cd-03e8-4e99-9327-5123eab40902")
+ def test_softap_5G_avoid_channel_9(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_2g=9)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="a03c9e45-8763-4b5c-bead-e574fb9899a2")
+ def test_softap_2G_avoid_channel_10(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_2g=10)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="c1a1d272-a646-4c2d-8425-09d2ae6ae8e6")
+ def test_softap_5G_avoid_channel_10(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_2g=10)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="f38d8911-92d4-4dcd-ba23-1e1667fa1f5a")
+ def test_softap_2G_avoid_channel_11(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_2g=11)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="24cc35ba-45e3-4b7a-9bc9-25b7abe92fa9")
+ def test_softap_5G_avoid_channel_11(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_2g=11)
+ network = self.reference_networks[0]["2g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="85aef720-4f3c-43bb-9de0-615b88c2bfe0")
+ def test_softap_2G_avoid_channel_36(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_5g=36)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="433e8db3-93b5-463e-a83c-0d4b9b9a8700")
+ def test_softap_5G_avoid_channel_36(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_5g=36)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="326e0e42-3219-4e63-a18d-5dc32c58e7d8")
+ def test_softap_2G_avoid_channel_40(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_5g=40)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="45953c03-c978-4775-a39b-fb7e70c8990a")
+ def test_softap_5G_avoid_channel_40(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_5g=40)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="e8e89cec-aa27-4780-8ff8-546d5af820f7")
+ def test_softap_2G_avoid_channel_44(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_5g=44)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="5e386d7d-d4c9-40cf-9333-06da55e11ba1")
+ def test_softap_5G_avoid_channel_44(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_5g=44)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="cb51dfca-f8de-4dfc-b513-e590c838c766")
+ def test_softap_2G_avoid_channel_48(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_5g=48)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="490b8ed1-196c-4941-b06b-5f0721ca440b")
+ def test_softap_5G_avoid_channel_48(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_5g=48)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="c5ab141b-e145-4cc1-b0d7-dd610cbfb462")
+ def test_softap_2G_avoid_channel_149(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_5g=149)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="108d7ef8-6fe7-49ba-b684-3820e881fcf0")
+ def test_softap_5G_avoid_channel_149(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_5g=149)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="f6926f40-0afc-41c5-9b38-c95a99788ff5")
+ def test_softap_2G_avoid_channel_153(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_5g=153)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="3d7b653b-c094-4c57-8e6a-047629b05216")
+ def test_softap_5G_avoid_channel_153(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_5g=153)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="b866ceea-d3ca-45d4-964a-4edea96026e6")
+ def test_softap_2G_avoid_channel_157(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_5g=157)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="03cb9163-bca3-442e-9691-6df82f8c51c7")
+ def test_softap_5G_avoid_channel_157(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_5g=157)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="ae10f23a-da70-43c8-9991-2c2f4a602724")
+ def test_softap_2G_avoid_channel_161(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_5g=161)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="521686c2-acfa-42d1-861b-aa10ac4dad34")
+ def test_softap_5G_avoid_channel_161(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_5g=161)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="77ebecd7-c036-463f-b77d-2cd70d89bc81")
+ def test_softap_2G_avoid_channel_165(self):
+ """Test to configure AP and bring up SoftAp on 2G."""
+ self.configure_ap(channel_5g=165)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_2G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
+
+ @test_tracker_info(uuid="85d9386d-fe60-4708-9f91-75bbf8bec54f")
+ def test_softap_5G_avoid_channel_165(self):
+ """Test to configure AP and bring up SoftAp on 5G."""
+ self.configure_ap(channel_5g=165)
+ network = self.reference_networks[0]["5g"]
+ chan = self.start_traffic_and_softap(network, WIFI_CONFIG_APBAND_5G)
+ avoid_chan = int(sys._getframe().f_code.co_name.split('_')[-1])
+ self.verify_acs_channel(chan, avoid_chan)
diff --git a/acts_tests/tests/google/wifi/WifiSoftApPerformanceTest.py b/acts_tests/tests/google/wifi/WifiSoftApPerformanceTest.py
new file mode 100644
index 0000000..375ae93
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiSoftApPerformanceTest.py
@@ -0,0 +1,392 @@
+#!/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 collections
+import logging
+import os
+from acts import asserts
+from acts import base_test
+from acts.controllers import iperf_server as ipf
+from acts.controllers import iperf_client as ipc
+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'),
+ ['ap_settings'])
+
+
+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_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 = (
+ BlackboxMappedMetricLogger.for_test_class())
+ self.publish_testcase_metrics = True
+
+ def setup_class(self):
+ """Initializes common test hardware and parameters.
+
+ This function initializes hardwares and compiles parameters that are
+ common to all tests in this class.
+ """
+ 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([{
+ 'AndroidDevice':
+ self.android_devices[0].serial,
+ 'port':
+ '5201'
+ }])[0]
+ self.iperf_client = ipc.create([{
+ 'AndroidDevice':
+ self.android_devices[1].serial,
+ 'port':
+ '5201'
+ }])[0]
+ if hasattr(self,
+ 'OTASniffer') and self.testbed_params['sniffer_enable']:
+ self.sniffer = ota_sniffer.create(self.OTASniffer)[0]
+
+ self.log_path = os.path.join(logging.log_path, 'results')
+ os.makedirs(self.log_path, exist_ok=True)
+ if not hasattr(self, 'golden_files_list'):
+ if 'golden_results_path' in self.testbed_params:
+ self.golden_files_list = [
+ os.path.join(self.testbed_params['golden_results_path'],
+ file) for file in
+ os.listdir(self.testbed_params['golden_results_path'])
+ ]
+ else:
+ self.log.warning('No golden files found.')
+ self.golden_files_list = []
+ self.testclass_results = []
+
+ # Turn WiFi ON
+ for dev in self.android_devices:
+ wutils.wifi_toggle_state(dev, True)
+
+ def teardown_class(self):
+ # Turn WiFi OFF
+ wutils.stop_wifi_tethering(self.android_devices[0])
+ 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()
+ wutils.stop_wifi_tethering(self.android_devices[0])
+
+ def get_sap_connection_info(self):
+ info = {}
+ info['client_ip_address'] = self.android_devices[
+ 1].droid.connectivityGetIPv4Addresses('wlan0')[0]
+ info['ap_ip_address'] = self.android_devices[
+ 0].droid.connectivityGetIPv4Addresses('wlan1')[0]
+ info['frequency'] = self.android_devices[1].adb.shell(
+ '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_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:
+ 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))
+ wutils.start_wifi_tethering(self.android_devices[0],
+ sap_config[wutils.WifiEnums.SSID_KEY],
+ sap_config[wutils.WifiEnums.PWD_KEY],
+ testcase_params['sap_band_enum'])
+ # 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.sta_dut,
+ testcase_params['test_network'],
+ num_of_tries=5,
+ check_connectivity=False)
+ # Compile meta data
+ #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.
+
+ Args:
+ testcase_params: dict containing test-specific parameters
+ """
+ num_atten_steps = int((self.testclass_params['atten_stop'] -
+ self.testclass_params['atten_start']) /
+ self.testclass_params['atten_step'])
+ testcase_params['atten_range'] = [
+ self.testclass_params['atten_start'] +
+ x * self.testclass_params['atten_step']
+ for x in range(0, num_atten_steps)
+ ]
+
+ if testcase_params['traffic_direction'] == 'DL':
+ testcase_params['iperf_args'] = wputils.get_iperf_arg_string(
+ duration=self.testclass_params['iperf_duration'],
+ reverse_direction=1,
+ traffic_type=testcase_params['traffic_type'])
+ 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'])
+ 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):
+ """ Function that gets called for each test case
+
+ Args:
+ testcase_params: dict containing test-specific parameters
+ """
+ # Compile test parameters from config and test name
+ testcase_params = self.compile_test_params(testcase_params)
+
+ self.setup_sap_rvr_test(testcase_params)
+ result = self.run_rvr_test(testcase_params)
+ self.testclass_results.append(result)
+ self.process_test_results(result)
+ self.pass_fail_check(result)
+
+ #Test cases
+ def test_rvr_TCP_DL_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=[False, False])
+ self._test_sap_rvr(testcase_params)
+
+ def test_rvr_TCP_UL_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=[False, False])
+ self._test_sap_rvr(testcase_params)
+
+ def test_rvr_TCP_DL_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=[False, False])
+ self._test_sap_rvr(testcase_params)
+
+ def test_rvr_TCP_UL_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=[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/tests/google/wifi/WifiSoftApTest.py b/acts_tests/tests/google/wifi/WifiSoftApTest.py
new file mode 100644
index 0000000..bcaab8d
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiSoftApTest.py
@@ -0,0 +1,876 @@
+#!/usr/bin/env python3.4
+#
+# Copyright 2016 - 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
+import queue
+import random
+import time
+
+from acts import asserts
+from acts import utils
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.net import socket_test_utils as sutils
+from acts.test_utils.tel import tel_defines
+from acts.test_utils.tel import tel_test_utils as tel_utils
+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_CONFIG_APBAND_AUTO
+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):
+ """It will setup the required dependencies from config file and configure
+ the devices for softap mode testing.
+
+ Returns:
+ True if successfully configured the requirements for testing.
+ """
+ super().setup_class()
+ self.dut = self.android_devices[0]
+ self.dut_client = self.android_devices[1]
+ req_params = ["dbs_supported_models"]
+ opt_param = ["open_network"]
+ 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()
+ 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
+ # 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.")
+ wutils.wifi_toggle_state(self.dut, True)
+ wutils.wifi_toggle_state(self.dut_client, True)
+ self.AP_IFACE = 'wlan0'
+ if self.dut.model in self.dbs_supported_models:
+ self.AP_IFACE = 'wlan1'
+ if len(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.")
+ self.dut_client_2 = self.android_devices[2]
+
+ def teardown_class(self):
+ 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:
+ del self.user_params["reference_networks"]
+ del self.user_params["open_network"]
+
+ def setup_test(self):
+ super().setup_test()
+ for ad in self.android_devices:
+ wutils.wifi_toggle_state(ad, True)
+
+ def teardown_test(self):
+ super().teardown_test()
+ self.dut.log.debug("Toggling Airplane mode OFF.")
+ asserts.assert_true(utils.force_airplane_mode(self.dut, False),
+ "Can not turn off airplane mode: %s" % self.dut.serial)
+ if self.dut.droid.wifiIsApEnabled():
+ wutils.stop_wifi_tethering(self.dut)
+ wutils.set_wifi_country_code(self.dut, wutils.WifiEnums.CountryCode.US)
+
+ """ Helper Functions """
+ def create_softap_config(self):
+ """Create a softap config with ssid and password."""
+ ap_ssid = "softap_" + utils.rand_ascii_str(8)
+ ap_password = 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
+ return config
+
+ def confirm_softap_in_scan_results(self, ap_ssid):
+ """Confirm the ap started by wifi tethering is seen in scan results.
+
+ Args:
+ ap_ssid: SSID of the ap we are looking for.
+ """
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ self.dut_client, ap_ssid);
+
+ def confirm_softap_not_in_scan_results(self, ap_ssid):
+ """Confirm the ap started by wifi tethering is not seen in scan results.
+
+ Args:
+ ap_ssid: SSID of the ap we are looking for.
+ """
+ wutils.start_wifi_connection_scan_and_ensure_network_not_found(
+ self.dut_client, ap_ssid);
+
+ def check_cell_data_and_enable(self):
+ """Make sure that cell data is enabled if there is a sim present.
+
+ If a sim is active, cell data needs to be enabled to allow provisioning
+ checks through (when applicable). This is done to relax hardware
+ requirements on DUTs - without this check, running this set of tests
+ after other wifi tests may cause failures.
+ """
+ # We do have a sim. Make sure data is enabled so we can tether.
+ if not self.dut.droid.telephonyIsDataEnabled():
+ self.dut.log.info("need to enable data")
+ self.dut.droid.telephonyToggleDataConnection(True)
+ asserts.assert_true(self.dut.droid.telephonyIsDataEnabled(),
+ "Failed to enable cell data for softap dut.")
+
+ def validate_full_tether_startup(self, band=None, hidden=None,
+ test_ping=False, test_clients=None):
+ """Test full startup of wifi tethering
+
+ 1. Report current state.
+ 2. Switch to AP mode.
+ 3. verify SoftAP active.
+ 4. Shutdown wifi tethering.
+ 5. verify back to previous mode.
+ """
+ initial_wifi_state = self.dut.droid.wifiCheckState()
+ initial_cell_state = tel_utils.is_sim_ready(self.log, self.dut)
+ self.dut.log.info("current state: %s", initial_wifi_state)
+ self.dut.log.info("is sim ready? %s", initial_cell_state)
+ if initial_cell_state:
+ self.check_cell_data_and_enable()
+ config = self.create_softap_config()
+ wutils.start_wifi_tethering(self.dut,
+ config[wutils.WifiEnums.SSID_KEY],
+ config[wutils.WifiEnums.PWD_KEY], band, hidden)
+ if hidden:
+ # First ensure it's not seen in scan results.
+ self.confirm_softap_not_in_scan_results(
+ config[wutils.WifiEnums.SSID_KEY])
+ # If the network is hidden, it should be saved on the client to be
+ # seen in scan results.
+ config[wutils.WifiEnums.HIDDEN_KEY] = True
+ ret = self.dut_client.droid.wifiAddNetwork(config)
+ asserts.assert_true(ret != -1, "Add network %r failed" % config)
+ self.dut_client.droid.wifiEnableNetwork(ret, 0)
+ self.confirm_softap_in_scan_results(config[wutils.WifiEnums.SSID_KEY])
+ if test_ping:
+ self.validate_ping_between_softap_and_client(config)
+ if test_clients:
+ if len(self.android_devices) > 2:
+ self.validate_ping_between_two_clients(config)
+ wutils.stop_wifi_tethering(self.dut)
+ asserts.assert_false(self.dut.droid.wifiIsApEnabled(),
+ "SoftAp is still reported as running")
+ if initial_wifi_state:
+ wutils.wait_for_wifi_state(self.dut, True)
+ elif self.dut.droid.wifiCheckState():
+ asserts.fail("Wifi was disabled before softap and now it is enabled")
+
+ def validate_ping_between_softap_and_client(self, config):
+ """Test ping between softap and its client.
+
+ Connect one android device to the wifi hotspot.
+ Verify they can ping each other.
+
+ Args:
+ config: wifi network config with SSID, password
+ """
+ wutils.wifi_connect(self.dut_client, config, check_connectivity=False)
+
+ dut_ip = self.dut.droid.connectivityGetIPv4Addresses(self.AP_IFACE)[0]
+ dut_client_ip = self.dut_client.droid.connectivityGetIPv4Addresses('wlan0')[0]
+
+ self.dut.log.info("Try to ping %s" % dut_client_ip)
+ asserts.assert_true(
+ utils.adb_shell_ping(self.dut, count=10, dest_ip=dut_client_ip, timeout=20),
+ "%s ping %s failed" % (self.dut.serial, dut_client_ip))
+
+ self.dut_client.log.info("Try to ping %s" % dut_ip)
+ asserts.assert_true(
+ utils.adb_shell_ping(self.dut_client, count=10, dest_ip=dut_ip, timeout=20),
+ "%s ping %s failed" % (self.dut_client.serial, dut_ip))
+
+ wutils.stop_wifi_tethering(self.dut)
+
+ def validate_ping_between_two_clients(self, config):
+ """Test ping between softap's clients.
+
+ Connect two android device to the wifi hotspot.
+ Verify the clients can ping each other.
+
+ Args:
+ config: wifi network config with SSID, password
+ """
+ # Connect DUT to Network
+ ad1 = self.dut_client
+ ad2 = self.android_devices[2]
+
+ wutils.wifi_connect(ad1, config, check_connectivity=False)
+ wutils.wifi_connect(ad2, config, check_connectivity=False)
+ ad1_ip = ad1.droid.connectivityGetIPv4Addresses('wlan0')[0]
+ ad2_ip = ad2.droid.connectivityGetIPv4Addresses('wlan0')[0]
+
+ # Ping each other
+ ad1.log.info("Try to ping %s" % ad2_ip)
+ asserts.assert_true(
+ utils.adb_shell_ping(ad1, count=10, dest_ip=ad2_ip, timeout=20),
+ "%s ping %s failed" % (ad1.serial, ad2_ip))
+
+ ad2.log.info("Try to ping %s" % ad1_ip)
+ asserts.assert_true(
+ utils.adb_shell_ping(ad2, count=10, dest_ip=ad1_ip, timeout=20),
+ "%s ping %s failed" % (ad2.serial, ad1_ip))
+
+ """ Tests Begin """
+
+ @test_tracker_info(uuid="495f1252-e440-461c-87a7-2c45f369e129")
+ def test_check_wifi_tethering_supported(self):
+ """Test check for wifi tethering support.
+
+ 1. Call method to check if wifi hotspot is supported
+ """
+ # TODO(silberst): wifiIsPortableHotspotSupported() is currently failing.
+ # Remove the extra check and logging when b/30800811 is resolved
+ hotspot_supported = self.dut.droid.wifiIsPortableHotspotSupported()
+ tethering_supported = self.dut.droid.connectivityIsTetheringSupported()
+ self.log.info(
+ "IsPortableHotspotSupported: %s, IsTetheringSupported %s." % (
+ hotspot_supported, tethering_supported))
+ asserts.assert_true(hotspot_supported,
+ "DUT should support wifi tethering but is reporting false.")
+ asserts.assert_true(tethering_supported,
+ "DUT should also support wifi tethering when called from ConnectivityManager")
+
+ @test_tracker_info(uuid="09c19c35-c708-48a5-939b-ac2bbb403d54")
+ def test_full_tether_startup(self):
+ """Test full startup of wifi tethering in default band.
+
+ 1. Report current state.
+ 2. Switch to AP mode.
+ 3. verify SoftAP active.
+ 4. Shutdown wifi tethering.
+ 5. verify back to previous mode.
+ """
+ self.validate_full_tether_startup()
+
+ @test_tracker_info(uuid="6437727d-7db1-4f69-963e-f26a7797e47f")
+ def test_full_tether_startup_2G(self):
+ """Test full startup of wifi tethering in 2G band.
+
+ 1. Report current state.
+ 2. Switch to AP mode.
+ 3. verify SoftAP active.
+ 4. Shutdown wifi tethering.
+ 5. verify back to previous mode.
+ """
+ self.validate_full_tether_startup(WIFI_CONFIG_APBAND_2G)
+
+ @test_tracker_info(uuid="970272fa-1302-429b-b261-51efb4dad779")
+ def test_full_tether_startup_5G(self):
+ """Test full startup of wifi tethering in 5G band.
+
+ 1. Report current state.
+ 2. Switch to AP mode.
+ 3. verify SoftAP active.
+ 4. Shutdown wifi tethering.
+ 5. verify back to previous mode.
+ """
+ self.validate_full_tether_startup(WIFI_CONFIG_APBAND_5G)
+
+ @test_tracker_info(uuid="f76ed37a-519a-48b4-b260-ee3fc5a9cae0")
+ def test_full_tether_startup_auto(self):
+ """Test full startup of wifi tethering in auto-band.
+
+ 1. Report current state.
+ 2. Switch to AP mode.
+ 3. verify SoftAP active.
+ 4. Shutdown wifi tethering.
+ 5. verify back to previous mode.
+ """
+ self.validate_full_tether_startup(WIFI_CONFIG_APBAND_AUTO)
+
+ @test_tracker_info(uuid="d26ee4df-5dcb-4191-829f-05a10b1218a7")
+ def test_full_tether_startup_2G_hidden(self):
+ """Test full startup of wifi tethering in 2G band using hidden AP.
+
+ 1. Report current state.
+ 2. Switch to AP mode.
+ 3. verify SoftAP active.
+ 4. Shutdown wifi tethering.
+ 5. verify back to previous mode.
+ """
+ self.validate_full_tether_startup(WIFI_CONFIG_APBAND_2G, True)
+
+ @test_tracker_info(uuid="229cd585-a789-4c9a-8948-89fa72de9dd5")
+ def test_full_tether_startup_5G_hidden(self):
+ """Test full startup of wifi tethering in 5G band using hidden AP.
+
+ 1. Report current state.
+ 2. Switch to AP mode.
+ 3. verify SoftAP active.
+ 4. Shutdown wifi tethering.
+ 5. verify back to previous mode.
+ """
+ self.validate_full_tether_startup(WIFI_CONFIG_APBAND_5G, True)
+
+ @test_tracker_info(uuid="d546a143-6047-4ffd-b3c6-5ec81a38001f")
+ def test_full_tether_startup_auto_hidden(self):
+ """Test full startup of wifi tethering in auto-band using hidden AP.
+
+ 1. Report current state.
+ 2. Switch to AP mode.
+ 3. verify SoftAP active.
+ 4. Shutdown wifi tethering.
+ 5. verify back to previous mode.
+ """
+ self.validate_full_tether_startup(WIFI_CONFIG_APBAND_AUTO, True)
+
+ @test_tracker_info(uuid="b2f75330-bf33-4cdd-851a-de390f891ef7")
+ def test_tether_startup_while_connected_to_a_network(self):
+ """Test full startup of wifi tethering in auto-band while the device
+ is connected to a network.
+
+ 1. Connect to an open network.
+ 2. Turn on AP mode (in auto band).
+ 3. Verify SoftAP active.
+ 4. Make a client connect to the AP.
+ 5. Shutdown wifi tethering.
+ 6. Ensure that the client disconnected.
+ """
+ wutils.wifi_toggle_state(self.dut, True)
+ wutils.wifi_connect(self.dut, self.open_network)
+ config = self.create_softap_config()
+ wutils.start_wifi_tethering(self.dut,
+ config[wutils.WifiEnums.SSID_KEY],
+ config[wutils.WifiEnums.PWD_KEY],
+ WIFI_CONFIG_APBAND_AUTO)
+ asserts.assert_true(self.dut.droid.wifiIsApEnabled(),
+ "SoftAp is not reported as running")
+ # local hotspot may not have internet connectivity
+ self.confirm_softap_in_scan_results(config[wutils.WifiEnums.SSID_KEY])
+ wutils.wifi_connect(self.dut_client, config, check_connectivity=False)
+ wutils.stop_wifi_tethering(self.dut)
+ wutils.wait_for_disconnect(self.dut_client)
+
+ @test_tracker_info(uuid="f2cf56ad-b8b9-43b6-ab15-a47b1d96b92e")
+ def test_full_tether_startup_2G_with_airplane_mode_on(self):
+ """Test full startup of wifi tethering in 2G band with
+ airplane mode on.
+
+ 1. Turn on airplane mode.
+ 2. Report current state.
+ 3. Switch to AP mode.
+ 4. verify SoftAP active.
+ 5. Shutdown wifi tethering.
+ 6. verify back to previous mode.
+ 7. Turn off airplane mode.
+ """
+ self.dut.log.debug("Toggling Airplane mode ON.")
+ asserts.assert_true(utils.force_airplane_mode(self.dut, True),
+ "Can not turn on airplane mode: %s" % self.dut.serial)
+ wutils.wifi_toggle_state(self.dut, True)
+ self.validate_full_tether_startup(WIFI_CONFIG_APBAND_2G)
+
+ @test_tracker_info(uuid="883dd5b1-50c6-4958-a50f-bb4bea77ccaf")
+ def test_full_tether_startup_2G_one_client_ping_softap(self):
+ """(AP) 1 Device can connect to 2G hotspot
+
+ Steps:
+ 1. Turn on DUT's 2G softap
+ 2. Client connects to the softap
+ 3. Client and DUT ping each other
+ """
+ self.validate_full_tether_startup(WIFI_CONFIG_APBAND_2G, test_ping=True)
+
+ @test_tracker_info(uuid="6604e848-99d6-422c-9fdc-2882642438b6")
+ def test_full_tether_startup_5G_one_client_ping_softap(self):
+ """(AP) 1 Device can connect to 5G hotspot
+
+ Steps:
+ 1. Turn on DUT's 5G softap
+ 2. Client connects to the softap
+ 3. Client and DUT ping each other
+ """
+ self.validate_full_tether_startup(WIFI_CONFIG_APBAND_5G, test_ping=True)
+
+ @test_tracker_info(uuid="17725ecd-f900-4cf7-8b2d-d7515b0a595c")
+ def test_softap_2G_two_clients_ping_each_other(self):
+ """Test for 2G hotspot with 2 clients
+
+ 1. Turn on 2G hotspot
+ 2. Two clients connect to the hotspot
+ 3. Two clients ping each other
+ """
+ asserts.skip_if(len(self.android_devices) < 3,
+ "No extra android devices. Skip test")
+ self.validate_full_tether_startup(WIFI_CONFIG_APBAND_2G, test_clients=True)
+
+ @test_tracker_info(uuid="98c09888-1021-4f79-9065-b3cf9b132146")
+ def test_softap_5G_two_clients_ping_each_other(self):
+ """Test for 5G hotspot with 2 clients
+
+ 1. Turn on 5G hotspot
+ 2. Two clients connect to the hotspot
+ 3. Two clients ping each other
+ """
+ asserts.skip_if(len(self.android_devices) < 3,
+ "No extra android devices. Skip test")
+ self.validate_full_tether_startup(WIFI_CONFIG_APBAND_5G, test_clients=True)
+
+ @test_tracker_info(uuid="b991129e-030a-4998-9b08-0687270bec24")
+ def test_number_of_softap_clients(self):
+ """Test for number of softap clients to be updated correctly
+
+ 1. Turn of hotspot
+ 2. Register softap callback
+ 3. Let client connect to the hotspot
+ 4. Register second softap callback
+ 5. Force client connect/disconnect to hotspot
+ 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 unregister
+ """
+ config = wutils.start_softap_and_verify(self, WIFI_CONFIG_APBAND_AUTO)
+ # 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)
+
+ # 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)
+
+ # Register another callback to verify multi callback clients case
+ callbackId_2 = self.dut.droid.registerSoftApCallback()
+ # Verify clients will update immediately after register callback
+ wutils.wait_for_expected_number_of_softap_clients(
+ self.dut, callbackId_2, 1)
+ wutils.wait_for_expected_softap_state(self.dut, callbackId_2,
+ wifi_constants.WIFI_AP_ENABLED_STATE)
+
+ # Client Off/On Wifi to verify number of softap clients will be updated
+ wutils.toggle_wifi_and_wait_for_reconnection(self.dut_client, config)
+
+ wutils.wait_for_expected_number_of_softap_clients(self.dut,
+ callbackId, 0)
+ wutils.wait_for_expected_number_of_softap_clients(self.dut,
+ callbackId_2, 0)
+ wutils.wait_for_expected_number_of_softap_clients(self.dut,
+ callbackId, 1)
+ wutils.wait_for_expected_number_of_softap_clients(self.dut,
+ callbackId_2, 1)
+
+ # Unregister callbackId_2 to verify multi callback clients case
+ self.dut.droid.unregisterSoftApCallback(callbackId_2)
+
+ if len(self.android_devices) > 2:
+ wutils.wifi_connect(self.android_devices[2], config,
+ check_connectivity=False)
+ wutils.wait_for_expected_number_of_softap_clients(
+ self.dut, callbackId, 2)
+
+ # Turn off softap when clients connected
+ wutils.stop_wifi_tethering(self.dut)
+ wutils.wait_for_disconnect(self.dut_client)
+ if len(self.android_devices) > 2:
+ wutils.wait_for_disconnect(self.android_devices[2])
+
+ # Verify client number change back to 0 after softap stop if client
+ # 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)
+ wutils.wait_for_expected_number_of_softap_clients(
+ self.dut, callbackId, 0)
+ # Unregister callback
+ self.dut.droid.unregisterSoftApCallback(callbackId)
+
+ # Check no any callbackId_2 event after unregister
+ asserts.assert_equal(
+ wutils.get_current_number_of_softap_clients(
+ self.dut, callbackId_2), None)
+
+ @test_tracker_info(uuid="35bc4ba1-bade-42ee-a563-0c73afb2402a")
+ def test_softap_auto_shut_off(self):
+ """Test for softap auto shut off
+
+ 1. Turn off hotspot
+ 2. Register softap callback
+ 3. Let client connect to the hotspot
+ 4. Start wait [wifi_constants.DEFAULT_SOFTAP_TIMEOUT_S] seconds
+ 5. Check hotspot doesn't shut off
+ 6. Let client disconnect to the hotspot
+ 7. Start wait [wifi_constants.DEFAULT_SOFTAP_TIMEOUT_S] seconds
+ 8. Check hotspot auto shut off
+ """
+ config = wutils.start_softap_and_verify(self, WIFI_CONFIG_APBAND_AUTO)
+ # 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)
+
+ # 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 ",
+ wifi_constants.DEFAULT_SOFTAP_TIMEOUT_S*1.1)
+ time.sleep(wifi_constants.DEFAULT_SOFTAP_TIMEOUT_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",
+ wifi_constants.DEFAULT_SOFTAP_TIMEOUT_S*1.1)
+ time.sleep(wifi_constants.DEFAULT_SOFTAP_TIMEOUT_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)
+
+ @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)
+
+ @test_tracker_info(uuid="07b4e5b3-48ce-49b9-a83e-3e288bb88e91")
+ def test_softap_5g_preferred_country_code_de(self):
+ """Verify softap works when set to 5G preferred band
+ with country code 'DE'.
+
+ Steps:
+ 1. Set country code to Germany
+ 2. Save a softap configuration set to 5G preferred band.
+ 3. Start softap and verify it works
+ 4. Verify a client device can connect to it.
+ """
+ wutils.set_wifi_country_code(
+ self.dut, wutils.WifiEnums.CountryCode.GERMANY)
+ sap_config = self.create_softap_config()
+ wifi_network = sap_config.copy()
+ sap_config[
+ WifiEnums.AP_BAND_KEY] = WifiEnums.WIFI_CONFIG_SOFTAP_BAND_2G_5G
+ sap_config[WifiEnums.SECURITY] = WifiEnums.SoftApSecurityType.WPA2
+ asserts.assert_true(
+ self.dut.droid.wifiSetWifiApConfiguration(sap_config),
+ "Failed to set WifiAp Configuration")
+ wutils.start_wifi_tethering_saved_config(self.dut)
+ softap_conf = self.dut.droid.wifiGetApConfiguration()
+ self.log.info("softap conf: %s" % softap_conf)
+ sap_band = softap_conf[WifiEnums.AP_BAND_KEY]
+ asserts.assert_true(
+ sap_band == WifiEnums.WIFI_CONFIG_SOFTAP_BAND_2G_5G,
+ "Soft AP didn't start in 5G preferred band")
+ wutils.connect_to_wifi_network(self.dut_client, wifi_network)
+ """ Tests End """
+
+
+if __name__ == "__main__":
+ pass
diff --git a/acts_tests/tests/google/wifi/WifiStaApConcurrencyStressTest.py b/acts_tests/tests/google/wifi/WifiStaApConcurrencyStressTest.py
new file mode 100755
index 0000000..f84c06c
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiStaApConcurrencyStressTest.py
@@ -0,0 +1,256 @@
+#!/usr/bin/env python3
+#
+# 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 time
+import pprint
+
+from acts import asserts
+from acts import signals
+from acts import utils
+from acts.test_decorators import test_tracker_info
+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 WifiStaApConcurrencyTest import WifiStaApConcurrencyTest
+import acts.test_utils.wifi.wifi_test_utils as wutils
+
+WifiEnums = wutils.WifiEnums
+
+# Channels to configure the AP for various test scenarios.
+WIFI_NETWORK_AP_CHANNEL_2G = 1
+WIFI_NETWORK_AP_CHANNEL_5G = 36
+WIFI_NETWORK_AP_CHANNEL_5G_DFS = 132
+
+class WifiStaApConcurrencyStressTest(WifiStaApConcurrencyTest):
+ """Stress tests for STA + AP concurrency scenarios.
+
+ Test Bed Requirement:
+ * At least two Android devices (For AP)
+ * One Wi-Fi network visible to the device (for STA).
+ """
+
+ def __init__(self, controllers):
+ WifiStaApConcurrencyTest.__init__(self, controllers)
+ self.tests = ("test_stress_wifi_connection_2G_softap_2G",
+ "test_stress_wifi_connection_5G_softap_5G",
+ "test_stress_wifi_connection_5G_DFS_softap_5G",
+ "test_stress_wifi_connection_5G_softap_2G",
+ "test_stress_wifi_connection_5G_DFS_softap_2G",
+ "test_stress_wifi_connection_2G_softap_5G",
+ "test_stress_wifi_connection_5G_softap_2G_with_location_scan_on",
+ "test_stress_softap_2G_wifi_connection_2G",
+ "test_stress_softap_5G_wifi_connection_5G",
+ "test_stress_softap_5G_wifi_connection_5G_DFS",
+ "test_stress_softap_5G_wifi_connection_2G",
+ "test_stress_softap_2G_wifi_connection_5G",
+ "test_stress_softap_2G_wifi_connection_5G_DFS",
+ "test_stress_softap_5G_wifi_connection_2G_with_location_scan_on")
+
+ def setup_class(self):
+ 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))
+ if len(self.android_devices) > 2:
+ self.log.info("Testbed has extra android devices, do more validation")
+ 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)
+ if len(self.android_devices) > 2:
+ self.log.info("Testbed has extra android devices, do more validation")
+ 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:
+ wutils.reset_wifi(self.android_devices[2])
+ wutils.stop_wifi_tethering(self.dut)
+
+ """Tests"""
+ @test_tracker_info(uuid="615997cc-8290-4af3-b3ac-1f5bd5af6ed1")
+ def test_stress_wifi_connection_2G_softap_2G(self):
+ """Tests connection to 2G network the enable/disable SoftAp on 2G N times.
+ """
+ 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.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.open_2g, WIFI_CONFIG_APBAND_2G)
+
+ @test_tracker_info(uuid="03362d54-a624-4fb8-ad97-7abb9e6f655c")
+ def test_stress_wifi_connection_5G_softap_5G(self):
+ """Tests connection to 5G network followed by bringing up SoftAp on 5G.
+ """
+ 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.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.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):
+ """Tests connection to 5G DFS network followed by bringing up SoftAp on 5G.
+ """
+ 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.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.open_5g, WIFI_CONFIG_APBAND_5G)
+
+ @test_tracker_info(uuid="b3621721-7714-43eb-8438-b578164b9194")
+ def test_stress_wifi_connection_5G_softap_2G(self):
+ """Tests connection to 5G network followed by bringing up SoftAp on 2G.
+ """
+ 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.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.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):
+ """Tests connection to 5G DFS network followed by bringing up SoftAp on 2G.
+ """
+ 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.open_5g, self.dut))
+ for count in range(self.stress_count):
+ 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):
+ """Tests connection to 2G network followed by bringing up SoftAp on 5G.
+ """
+ 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.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.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):
+ """Tests connection to 5G network followed by bringing up SoftAp on 2G
+ with location scans turned on.
+ """
+ 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.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.open_5g, WIFI_CONFIG_APBAND_2G)
+
+ @test_tracker_info(uuid="0edb1500-6c60-442e-9268-a2ad9ee2b55c")
+ def test_stress_softap_2G_wifi_connection_2G(self):
+ """Tests enable SoftAp on 2G then connection/disconnection to 2G network for N times.
+ """
+ self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
+ softap_config = self.start_softap_and_verify(
+ 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.open_2g, softap_config)
+
+ @test_tracker_info(uuid="162a6679-edd5-4daa-9f25-75d79cf4bb4a")
+ def test_stress_softap_5G_wifi_connection_5G(self):
+ """Tests enable SoftAp on 5G then connection/disconnection to 5G network for N times.
+ """
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
+ softap_config = self.start_softap_and_verify(
+ 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.open_5g, softap_config)
+
+ @test_tracker_info(uuid="ee98f2dd-c4f9-4f48-ab59-f577267760d5")
+ def test_stress_softap_5G_wifi_connection_5G_DFS(self):
+ """Tests enable SoftAp on 5G then connection/disconnection to 5G DFS network for N times.
+ """
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
+ softap_config = self.start_softap_and_verify(
+ 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.open_5g, softap_config)
+
+ @test_tracker_info(uuid="b50750b5-d5b9-4687-b9e7-9fb15f54b428")
+ def test_stress_softap_5G_wifi_connection_2G(self):
+ """Tests enable SoftAp on 5G then connection/disconnection to 2G network for N times.
+ """
+ self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
+ softap_config = self.start_softap_and_verify(
+ 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.open_2g, softap_config)
+
+ @test_tracker_info(uuid="9a2865db-8e4b-4339-9999-000ce9b6970b")
+ def test_stress_softap_2G_wifi_connection_5G(self):
+ """Tests enable SoftAp on 2G then connection/disconnection to 5G network for N times.
+ """
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
+ softap_config = self.start_softap_and_verify(
+ 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.open_5g, softap_config)
+
+ @test_tracker_info(uuid="add6609d-91d6-4b89-94c5-0ad8b941e3d1")
+ def test_stress_softap_2G_wifi_connection_5G_DFS(self):
+ """Tests enable SoftAp on 2G then connection/disconnection to 5G DFS network for N times.
+ """
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
+ softap_config = self.start_softap_and_verify(
+ 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.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):
+ """Tests enable SoftAp on 5G then connection/disconnection to 2G network for N times
+ with location scans turned on.
+ """
+ self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
+ self.turn_location_on_and_scan_toggle_on()
+ softap_config = self.start_softap_and_verify(
+ 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.open_2g, softap_config)
diff --git a/acts_tests/tests/google/wifi/WifiStaApConcurrencyTest.py b/acts_tests/tests/google/wifi/WifiStaApConcurrencyTest.py
new file mode 100644
index 0000000..035cac3
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiStaApConcurrencyTest.py
@@ -0,0 +1,434 @@
+#!/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 pprint
+import time
+import re
+
+from acts import asserts
+from acts import base_test
+from acts.controllers.ap_lib import hostapd_constants
+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_2G
+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
+import acts.test_utils.tel.tel_test_utils as tel_utils
+
+
+WifiEnums = wutils.WifiEnums
+WLAN = "wlan0"
+# Channels to configure the AP for various test scenarios.
+WIFI_NETWORK_AP_CHANNEL_2G = 1
+WIFI_NETWORK_AP_CHANNEL_5G = 36
+WIFI_NETWORK_AP_CHANNEL_5G_DFS = 132
+
+
+class WifiStaApConcurrencyTest(WifiBaseTest):
+ """Tests for STA + AP concurrency scenarios.
+
+ Test Bed Requirement:
+ * Two Android devices (For AP)
+ * One Wi-Fi network visible to the device (for STA).
+ """
+
+ def setup_class(self):
+ super().setup_class()
+
+ self.dut = self.android_devices[0]
+ self.dut_client = self.android_devices[1]
+
+ # 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.android_devices)
+
+ for ad in self.android_devices:
+ wutils.wifi_test_device_init(ad)
+ utils.sync_device_time(ad)
+ # Set country code explicitly to "US".
+ wutils.set_wifi_country_code(ad, WifiEnums.CountryCode.US)
+ # Enable verbose logging on the duts.
+ ad.droid.wifiEnableVerboseLogging(1)
+
+ req_params = ["dbs_supported_models",
+ "iperf_server_address",
+ "iperf_server_port"]
+ self.unpack_userparams(req_param_names=req_params,)
+ asserts.abort_class_if(
+ self.dut.model not in self.dbs_supported_models,
+ "Device %s does not support dual interfaces." % self.dut.model)
+
+ def setup_test(self):
+ super().setup_test()
+ for ad in self.android_devices:
+ ad.droid.wakeLockAcquireBright()
+ ad.droid.wakeUpNow()
+ self.turn_location_off_and_scan_toggle_off()
+
+ def teardown_test(self):
+ super().teardown_test()
+ # Prevent the stop wifi tethering failure to block ap close
+ try:
+ wutils.stop_wifi_tethering(self.dut)
+ except signals.TestFailure:
+ pass
+ for ad in self.android_devices:
+ ad.droid.wakeLockRelease()
+ ad.droid.goToSleepNow()
+ wutils.reset_wifi(ad)
+ self.turn_location_on_and_scan_toggle_on()
+ wutils.wifi_toggle_state(self.dut, True)
+ self.access_points[0].close()
+ 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")
+
+ ### Helper Functions ###
+
+ def configure_ap(self, channel_2g=None, channel_5g=None):
+ """Configure and bring up AP on required channel.
+
+ Args:
+ channel_2g: The channel number to use for 2GHz network.
+ 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:
+ 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"]
+
+ def turn_location_on_and_scan_toggle_on(self):
+ """Turns on wifi location scans."""
+ utils.set_location_service(self.dut, True)
+ self.dut.droid.wifiScannerToggleAlwaysAvailable(True)
+ msg = "Failed to turn on location service's scan."
+ asserts.assert_true(self.dut.droid.wifiScannerIsAlwaysAvailable(), msg)
+
+ def turn_location_off_and_scan_toggle_off(self):
+ """Turns off wifi location scans."""
+ utils.set_location_service(self.dut, False)
+ self.dut.droid.wifiScannerToggleAlwaysAvailable(False)
+ msg = "Failed to turn off location service's scan."
+ asserts.assert_false(self.dut.droid.wifiScannerIsAlwaysAvailable(), msg)
+
+ def run_iperf_client(self, params):
+ """Run iperf traffic after connection.
+
+ Args:
+ params: A tuple of network info and AndroidDevice object.
+ """
+ if "iperf_server_address" in self.user_params:
+ wait_time = 5
+ network, ad = params
+ ssid = network[WifiEnums.SSID_KEY]
+ self.log.info("Starting iperf traffic through {}".format(ssid))
+ time.sleep(wait_time)
+ port_arg = "-p {}".format(self.iperf_server_port)
+ success, data = ad.run_iperf_client(self.iperf_server_address,
+ port_arg)
+ self.log.debug(pprint.pformat(data))
+ asserts.assert_true(success, "Error occurred in iPerf traffic.")
+
+ def create_softap_config(self):
+ """Create a softap config with ssid and password."""
+ ap_ssid = "softap_" + utils.rand_ascii_str(8)
+ ap_password = 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
+ return config
+
+ def start_softap_and_verify(self, band, check_connectivity=True):
+ """Test startup of softap.
+
+ 1. Bring up AP mode.
+ 2. Verify SoftAP active using the client device.
+
+ Args:
+ band: wifi band to start soft ap on
+ check_connectivity: If set, verify internet connectivity
+
+ Returns:
+ Softap config
+ """
+ config = self.create_softap_config()
+ wutils.start_wifi_tethering(self.dut,
+ config[WifiEnums.SSID_KEY],
+ config[WifiEnums.PWD_KEY],
+ band)
+ for ad in self.android_devices[1:]:
+ wutils.connect_to_wifi_network(
+ ad, config, check_connectivity=check_connectivity)
+ return config
+
+ def connect_to_wifi_network_and_start_softap(self, nw_params, softap_band):
+ """Test concurrent wifi connection and softap.
+
+ This helper method first makes a wifi connection and then starts SoftAp.
+ 1. Bring up wifi.
+ 2. Establish connection to a network.
+ 3. Bring up softap and verify AP can be connected by a client device.
+ 4. Run iperf on the wifi/softap connection to the network.
+
+ Args:
+ nw_params: Params for network STA connection.
+ softap_band: Band for the AP.
+ """
+ wutils.connect_to_wifi_network(self.dut, nw_params)
+ softap_config = self.start_softap_and_verify(softap_band)
+ self.run_iperf_client((nw_params, self.dut))
+ self.run_iperf_client((softap_config, self.dut_client))
+
+ if len(self.android_devices) > 2:
+ self.log.info("Testbed has extra devices, do more validation")
+ self.verify_traffic_between_dut_clients(
+ self.dut_client, self.android_devices[2])
+
+ asserts.assert_true(self.dut.droid.wifiCheckState(),
+ "Wifi is not reported as running")
+ asserts.assert_true(self.dut.droid.wifiIsApEnabled(),
+ "SoftAp is not reported as running")
+
+ def start_softap_and_connect_to_wifi_network(self, nw_params, softap_band):
+ """Test concurrent wifi connection and softap.
+
+ This helper method first starts SoftAp and then makes a wifi connection.
+ 1. Bring up softap and verify AP can be connected by a client device.
+ 2. Bring up wifi.
+ 3. Establish connection to a network.
+ 4. Run iperf on the wifi/softap connection to the network.
+ 5. Verify wifi state and softap state.
+
+ Args:
+ nw_params: Params for network STA connection.
+ softap_band: Band for the AP.
+ """
+ softap_config = self.start_softap_and_verify(softap_band, False)
+ wutils.connect_to_wifi_network(self.dut, nw_params)
+ self.run_iperf_client((nw_params, self.dut))
+ self.run_iperf_client((softap_config, self.dut_client))
+
+ if len(self.android_devices) > 2:
+ self.log.info("Testbed has extra devices, do more validation")
+ self.verify_traffic_between_dut_clients(
+ self.dut, self.android_devices[2])
+
+ asserts.assert_true(self.dut.droid.wifiCheckState(),
+ "Wifi is not reported as running")
+ asserts.assert_true(self.dut.droid.wifiIsApEnabled(),
+ "SoftAp is not reported as running")
+
+ def verify_traffic_between_dut_clients(self, ad1, ad2, num_of_tries=2):
+ """Test the clients that connect to DUT's softap can ping each other.
+
+ Args:
+ ad1: DUT 1
+ ad2: DUT 2
+ num_of_tries: the retry times of ping test.
+ """
+ ad1_ip = ad1.droid.connectivityGetIPv4Addresses(WLAN)[0]
+ ad2_ip = ad2.droid.connectivityGetIPv4Addresses(WLAN)[0]
+ # Ping each other
+ for _ in range(num_of_tries):
+ if utils.adb_shell_ping(ad1, count=10, dest_ip=ad2_ip, timeout=20):
+ break
+ else:
+ asserts.fail("%s ping %s failed" % (ad1.serial, ad2_ip))
+ for _ in range(num_of_tries):
+ if utils.adb_shell_ping(ad2, count=10, dest_ip=ad1_ip, timeout=20):
+ break
+ else:
+ asserts.fail("%s ping %s failed" % (ad2.serial, ad1_ip))
+
+ def softap_change_band(self, ad):
+ """
+ Switch DUT SoftAp to 5G band if currently in 2G.
+ Switch DUT SoftAp to 2G band if currently in 5G.
+ """
+ wlan1_freq = int(self.get_wlan1_status(self.dut)['freq'])
+ if wlan1_freq in wutils.WifiEnums.ALL_5G_FREQUENCIES:
+ band = WIFI_CONFIG_APBAND_2G
+ elif wlan1_freq in wutils.WifiEnums.ALL_2G_FREQUENCIES:
+ band = WIFI_CONFIG_APBAND_5G
+ wutils.stop_wifi_tethering(ad)
+ self.start_softap_and_verify(band)
+
+ def get_wlan1_status(self, ad):
+ """ get wlan1 interface status"""
+ get_wlan1 = 'hostapd_cli status'
+ out_wlan1 = ad.adb.shell(get_wlan1)
+ out_wlan1 = dict(re.findall(r'(\S+)=(".*?"|\S+)', out_wlan1))
+ return out_wlan1
+
+ def enable_mobile_data(self, ad):
+ """Make sure that cell data is enabled if there is a sim present."""
+ init_sim_state = tel_utils.is_sim_ready(self.log, ad)
+ if init_sim_state:
+ if not ad.droid.telephonyIsDataEnabled():
+ ad.droid.telephonyToggleDataConnection(True)
+ asserts.assert_true(ad.droid.telephonyIsDataEnabled(),
+ "Failed to enable Mobile Data")
+ else:
+ raise signals.TestSkip("Please insert sim card with "
+ "Mobile Data enabled before test")
+
+ ### Tests ###
+
+ @test_tracker_info(uuid="c396e7ac-cf22-4736-a623-aa6d3c50193a")
+ def test_wifi_connection_2G_softap_2G(self):
+ """Test connection to 2G network followed by SoftAp on 2G."""
+ self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
+ self.connect_to_wifi_network_and_start_softap(
+ self.open_2g, WIFI_CONFIG_APBAND_2G)
+
+ @test_tracker_info(uuid="1cd6120d-3db4-4624-9bae-55c976533a48")
+ def test_wifi_connection_5G_softap_5G(self):
+ """Test connection to 5G network followed by SoftAp on 5G."""
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
+ self.connect_to_wifi_network_and_start_softap(
+ self.open_5g, WIFI_CONFIG_APBAND_5G)
+
+ @test_tracker_info(uuid="5f980007-3490-413e-b94e-7700ffab8534")
+ def test_wifi_connection_5G_DFS_softap_5G(self):
+ """Test connection to 5G DFS network followed by SoftAp on 5G."""
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
+ self.connect_to_wifi_network_and_start_softap(
+ self.open_5g, WIFI_CONFIG_APBAND_5G)
+
+ @test_tracker_info(uuid="d05d5d44-c738-4372-9f01-ce2a640a2f25")
+ def test_wifi_connection_5G_softap_2G(self):
+ """Test connection to 5G network followed by SoftAp on 2G."""
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
+ self.connect_to_wifi_network_and_start_softap(
+ self.open_5g, WIFI_CONFIG_APBAND_2G)
+
+ @test_tracker_info(uuid="909ac713-1ad3-4dad-9be3-ad60f00ed25e")
+ def test_wifi_connection_5G_DFS_softap_2G(self):
+ """Test connection to 5G DFS network followed by SoftAp on 2G."""
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
+ self.connect_to_wifi_network_and_start_softap(
+ self.open_5g, WIFI_CONFIG_APBAND_2G)
+
+ @test_tracker_info(uuid="e8de724a-25d3-4801-94cc-22e9e0ecc8d1")
+ def test_wifi_connection_2G_softap_5G(self):
+ """Test connection to 2G network followed by SoftAp on 5G."""
+ self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
+ self.connect_to_wifi_network_and_start_softap(
+ self.open_2g, WIFI_CONFIG_APBAND_5G)
+
+ @test_tracker_info(uuid="647f4e17-5c7a-4249-98af-f791d163a39f")
+ def test_wifi_connection_5G_softap_2G_with_location_scan_on(self):
+ """Test connection to 5G network, SoftAp on 2G with location scan on."""
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
+ self.turn_location_on_and_scan_toggle_on()
+ self.connect_to_wifi_network_and_start_softap(
+ self.open_5g, WIFI_CONFIG_APBAND_2G)
+ # Now toggle wifi off & ensure we can still scan.
+ wutils.wifi_toggle_state(self.dut, False)
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ self.dut, self.open_5g[WifiEnums.SSID_KEY])
+
+ @test_tracker_info(uuid="4aa56c11-e5bc-480b-bd61-4b4ee577a5da")
+ def test_softap_2G_wifi_connection_2G(self):
+ """Test SoftAp on 2G followed by connection to 2G network."""
+ self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
+ self.start_softap_and_connect_to_wifi_network(
+ self.open_2g, WIFI_CONFIG_APBAND_2G)
+
+ @test_tracker_info(uuid="5f954957-ad20-4de1-b20c-6c97d0463bdd")
+ def test_softap_5G_wifi_connection_5G(self):
+ """Test SoftAp on 5G followed by connection to 5G network."""
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
+ self.start_softap_and_connect_to_wifi_network(
+ self.open_5g, WIFI_CONFIG_APBAND_5G)
+
+ @test_tracker_info(uuid="1306aafc-a07e-4654-ba78-674f90cf748e")
+ def test_softap_5G_wifi_connection_5G_DFS(self):
+ """Test SoftAp on 5G followed by connection to 5G DFS network."""
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
+ self.start_softap_and_connect_to_wifi_network(
+ self.open_5g, WIFI_CONFIG_APBAND_5G)
+
+ @test_tracker_info(uuid="5e28e8b5-3faa-4cff-a782-13a796d7f572")
+ def test_softap_5G_wifi_connection_2G(self):
+ """Test SoftAp on 5G followed by connection to 2G network."""
+ self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
+ self.start_softap_and_connect_to_wifi_network(
+ self.open_2g, WIFI_CONFIG_APBAND_5G)
+
+ @test_tracker_info(uuid="a2c62bc6-9ccd-4bc4-8a23-9a1b5d0b4b5c")
+ def test_softap_2G_wifi_connection_5G(self):
+ """Test SoftAp on 2G followed by connection to 5G network."""
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
+ self.start_softap_and_connect_to_wifi_network(
+ self.open_5g, WIFI_CONFIG_APBAND_2G)
+
+ @test_tracker_info(uuid="75400685-a9d9-4091-8af3-97bd539c246a")
+ def test_softap_2G_wifi_connection_5G_DFS(self):
+ """Test SoftAp on 2G followed by connection to 5G DFS network."""
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
+ self.start_softap_and_connect_to_wifi_network(
+ self.open_5g, WIFI_CONFIG_APBAND_2G)
+
+ @test_tracker_info(uuid="aa23a3fc-31a1-4d5c-8cf5-2eb9fdf9e7ce")
+ def test_softap_5G_wifi_connection_2G_with_location_scan_on(self):
+ """Test SoftAp on 5G, connection to 2G network with location scan on."""
+ self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
+ self.turn_location_on_and_scan_toggle_on()
+ self.start_softap_and_connect_to_wifi_network(
+ self.open_2g, WIFI_CONFIG_APBAND_5G)
+ # Now toggle wifi off & ensure we can still scan.
+ wutils.wifi_toggle_state(self.dut, False)
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ self.dut, self.open_2g[WifiEnums.SSID_KEY])
+
+ @test_tracker_info(uuid="9decb951-4500-4476-8161-f4054760f709")
+ def test_wifi_connection_2G_softap_2G_to_softap_5g(self):
+ """Test connection to 2G network followed by SoftAp on 2G,
+ and switch SoftAp to 5G."""
+ self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
+ self.connect_to_wifi_network_and_start_softap(
+ self.open_2g, WIFI_CONFIG_APBAND_2G)
+ self.softap_change_band(self.dut)
+
+ @test_tracker_info(uuid="e17e0fb8-2c1d-4f3c-af2a-7374485f210c")
+ def test_wifi_connection_5G_softap_2G_to_softap_5g(self):
+ """Test connection to 5G network followed by SoftAp on 2G,
+ and switch SoftAp to 5G."""
+ self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
+ self.connect_to_wifi_network_and_start_softap(
+ self.open_2g, WIFI_CONFIG_APBAND_2G)
+ self.softap_change_band(self.dut)
+
diff --git a/acts_tests/tests/google/wifi/WifiStressTest.py b/acts_tests/tests/google/wifi/WifiStressTest.py
new file mode 100644
index 0000000..10ba578
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiStressTest.py
@@ -0,0 +1,533 @@
+#!/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 pprint
+import queue
+import threading
+import time
+
+import acts.base_test
+import acts.test_utils.wifi.wifi_test_utils as wutils
+import acts.test_utils.tel.tel_test_utils as tutils
+
+from acts import asserts
+from acts import signals
+from acts import utils
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.bt.bt_test_utils import enable_bluetooth
+from acts.test_utils.bt.bt_test_utils import disable_bluetooth
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+WifiEnums = wutils.WifiEnums
+
+WAIT_FOR_AUTO_CONNECT = 40
+WAIT_BEFORE_CONNECTION = 30
+
+TIMEOUT = 5
+PING_ADDR = 'www.google.com'
+
+class WifiStressTest(WifiBaseTest):
+ """WiFi Stress test class.
+
+ Test Bed Requirement:
+ * Two Android device
+ * Several Wi-Fi networks visible to the device, including an open Wi-Fi
+ network.
+ """
+ def __init__(self, configs):
+ super().__init__(configs)
+ self.enable_packet_log = True
+
+ def setup_class(self):
+ super().setup_class()
+
+ self.dut = self.android_devices[0]
+ # Note that test_stress_softAP_startup_and_stop_5g will always fail
+ # when testing with a single device.
+ if len(self.android_devices) > 1:
+ self.dut_client = self.android_devices[1]
+ else:
+ self.dut_client = None
+ wutils.wifi_test_device_init(self.dut)
+ req_params = []
+ opt_param = [
+ "open_network", "reference_networks", "iperf_server_address",
+ "stress_count", "stress_hours", "attn_vals", "pno_interval",
+ "iperf_server_port"]
+ 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(ap_count=2)
+
+ asserts.assert_true(
+ len(self.reference_networks) > 0,
+ "Need at least one reference network with psk.")
+ self.wpa_2g = self.reference_networks[0]["2g"]
+ self.wpa_5g = self.reference_networks[0]["5g"]
+ self.open_2g = self.open_network[0]["2g"]
+ self.open_5g = self.open_network[0]["5g"]
+ self.networks = [self.wpa_2g, self.wpa_5g, self.open_2g, self.open_5g]
+
+ def setup_test(self):
+ super().setup_test()
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+
+ def teardown_test(self):
+ super().teardown_test()
+ if self.dut.droid.wifiIsApEnabled():
+ wutils.stop_wifi_tethering(self.dut)
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ wutils.reset_wifi(self.dut)
+
+ def teardown_class(self):
+ wutils.reset_wifi(self.dut)
+ if "AccessPoint" in self.user_params:
+ del self.user_params["reference_networks"]
+ del self.user_params["open_network"]
+
+ """Helper Functions"""
+
+ def scan_and_connect_by_ssid(self, ad, network):
+ """Scan for network and connect using network information.
+
+ Args:
+ network: A dictionary representing the network to connect to.
+
+ """
+ ssid = network[WifiEnums.SSID_KEY]
+ wutils.start_wifi_connection_scan_and_ensure_network_found(ad, ssid)
+ wutils.wifi_connect(ad, network, num_of_tries=3)
+
+ def scan_and_connect_by_id(self, network, net_id):
+ """Scan for network and connect using network id.
+
+ Args:
+ net_id: Integer specifying the network id of the network.
+
+ """
+ ssid = network[WifiEnums.SSID_KEY]
+ wutils.start_wifi_connection_scan_and_ensure_network_found(self.dut,
+ ssid)
+ wutils.wifi_connect_by_id(self.dut, net_id)
+
+ def run_ping(self, sec):
+ """Run ping for given number of seconds.
+
+ Args:
+ sec: Time in seconds to run teh ping traffic.
+
+ """
+ self.log.info("Running ping for %d seconds" % sec)
+ result = self.dut.adb.shell("ping -w %d %s" %(sec, PING_ADDR),
+ timeout=sec+1)
+ self.log.debug("Ping Result = %s" % result)
+ if "100% packet loss" in result:
+ raise signals.TestFailure("100% packet loss during ping")
+
+ def start_youtube_video(self, url=None, secs=60):
+ """Start a youtube video and check if it's playing.
+
+ Args:
+ url: The URL of the youtube video to play.
+ secs: Time to play video in seconds.
+
+ """
+ ad = self.dut
+ ad.log.info("Start a youtube video")
+ ad.ensure_screen_on()
+ video_played = False
+ for count in range(2):
+ ad.unlock_screen()
+ ad.adb.shell('am start -a android.intent.action.VIEW -d "%s"' % url)
+ if tutils.wait_for_state(ad.droid.audioIsMusicActive, True, 15, 1):
+ ad.log.info("Started a video in youtube.")
+ # Play video for given seconds.
+ time.sleep(secs)
+ video_played = True
+ break
+ if not video_played:
+ raise signals.TestFailure("Youtube video did not start. Current WiFi "
+ "state is %d" % self.dut.droid.wifiCheckState())
+
+ def add_networks(self, ad, networks):
+ """Add Wi-Fi networks to an Android device and verify the networks were
+ added correctly.
+
+ Args:
+ ad: the AndroidDevice object to add networks to.
+ networks: a list of dicts, each dict represents a Wi-Fi network.
+ """
+ for network in networks:
+ ret = ad.droid.wifiAddNetwork(network)
+ asserts.assert_true(ret != -1, "Failed to add network %s" %
+ network)
+ ad.droid.wifiEnableNetwork(ret, 0)
+ configured_networks = ad.droid.wifiGetConfiguredNetworks()
+ self.log.debug("Configured networks: %s", configured_networks)
+
+ def connect_and_verify_connected_ssid(self, expected_con, is_pno=False):
+ """Start a scan to get the DUT connected to an AP and verify the DUT
+ is connected to the correct SSID.
+
+ Args:
+ expected_con: The expected info of the network to we expect the DUT
+ to roam to.
+ """
+ connection_info = self.dut.droid.wifiGetConnectionInfo()
+ self.log.info("Triggering network selection from %s to %s",
+ connection_info[WifiEnums.SSID_KEY],
+ expected_con[WifiEnums.SSID_KEY])
+ self.attenuators[0].set_atten(0)
+ if is_pno:
+ self.log.info("Wait %ss for PNO to trigger.", self.pno_interval)
+ time.sleep(self.pno_interval)
+ else:
+ # force start a single scan so we don't have to wait for the scheduled scan.
+ wutils.start_wifi_connection_scan_and_return_status(self.dut)
+ self.log.info("Wait 60s for network selection.")
+ time.sleep(60)
+ try:
+ self.log.info("Connected to %s network after network selection"
+ % self.dut.droid.wifiGetConnectionInfo())
+ expected_ssid = expected_con[WifiEnums.SSID_KEY]
+ verify_con = {WifiEnums.SSID_KEY: expected_ssid}
+ wutils.verify_wifi_connection_info(self.dut, verify_con)
+ self.log.info("Connected to %s successfully after network selection",
+ expected_ssid)
+ finally:
+ pass
+
+ def run_long_traffic(self, sec, args, q):
+ try:
+ # Start IPerf traffic
+ self.log.info("Running iperf client {}".format(args))
+ result, data = self.dut.run_iperf_client(self.iperf_server_address,
+ args, timeout=sec+1)
+ if not result:
+ self.log.debug("Error occurred in iPerf traffic.")
+ self.run_ping(sec)
+ q.put(True)
+ except:
+ q.put(False)
+
+ """Tests"""
+
+ @test_tracker_info(uuid="cd0016c6-58cf-4361-b551-821c0b8d2554")
+ def test_stress_toggle_wifi_state(self):
+ """Toggle WiFi state ON and OFF for N times."""
+ for count in range(self.stress_count):
+ """Test toggling wifi"""
+ try:
+ self.log.debug("Going from on to off.")
+ wutils.wifi_toggle_state(self.dut, False)
+ self.log.debug("Going from off to on.")
+ startTime = time.time()
+ wutils.wifi_toggle_state(self.dut, True)
+ startup_time = time.time() - startTime
+ self.log.debug("WiFi was enabled on the device in %s s." %
+ startup_time)
+ except:
+ signals.TestFailure(details="", extras={"Iterations":"%d" %
+ self.stress_count, "Pass":"%d" %count})
+ raise signals.TestPass(details="", extras={"Iterations":"%d" %
+ self.stress_count, "Pass":"%d" %(count+1)})
+
+ @test_tracker_info(uuid="4e591cec-9251-4d52-bc6e-6621507524dc")
+ def test_stress_toggle_wifi_state_bluetooth_on(self):
+ """Toggle WiFi state ON and OFF for N times when bluetooth ON."""
+ enable_bluetooth(self.dut.droid, self.dut.ed)
+ for count in range(self.stress_count):
+ """Test toggling wifi"""
+ try:
+ self.log.debug("Going from on to off.")
+ wutils.wifi_toggle_state(self.dut, False)
+ self.log.debug("Going from off to on.")
+ startTime = time.time()
+ wutils.wifi_toggle_state(self.dut, True)
+ startup_time = time.time() - startTime
+ self.log.debug("WiFi was enabled on the device in %s s." %
+ startup_time)
+ except:
+ signals.TestFailure(details="", extras={"Iterations":"%d" %
+ self.stress_count, "Pass":"%d" %count})
+ disable_bluetooth(self.dut.droid)
+ raise signals.TestPass(details="", extras={"Iterations":"%d" %
+ self.stress_count, "Pass":"%d" %(count+1)})
+
+ @test_tracker_info(uuid="49e3916a-9580-4bf7-a60d-a0f2545dcdde")
+ def test_stress_connect_traffic_disconnect_5g(self):
+ """Test to connect and disconnect from a network for N times.
+
+ Steps:
+ 1. Scan and connect to a network.
+ 2. Run IPerf to upload data for few seconds.
+ 3. Disconnect.
+ 4. Repeat 1-3.
+
+ """
+ for count in range(self.stress_count):
+ try:
+ net_id = self.dut.droid.wifiAddNetwork(self.wpa_5g)
+ asserts.assert_true(net_id != -1, "Add network %r failed" % self.wpa_5g)
+ self.scan_and_connect_by_id(self.wpa_5g, net_id)
+ # Start IPerf traffic from phone to server.
+ # Upload data for 10s.
+ args = "-p {} -t {}".format(self.iperf_server_port, 10)
+ self.log.info("Running iperf client {}".format(args))
+ result, data = self.dut.run_iperf_client(self.iperf_server_address, args)
+ if not result:
+ self.log.debug("Error occurred in iPerf traffic.")
+ self.run_ping(10)
+ wutils.wifi_forget_network(self.dut,self.wpa_5g[WifiEnums.SSID_KEY])
+ time.sleep(WAIT_BEFORE_CONNECTION)
+ except:
+ raise signals.TestFailure("Network connect-disconnect failed."
+ "Look at logs", extras={"Iterations":"%d" %
+ self.stress_count, "Pass":"%d" %count})
+ raise signals.TestPass(details="", extras={"Iterations":"%d" %
+ self.stress_count, "Pass":"%d" %(count+1)})
+
+ @test_tracker_info(uuid="e9827dff-0755-43ec-8b50-1f9756958460")
+ def test_stress_connect_long_traffic_5g(self):
+ """Test to connect to network and hold connection for few hours.
+
+ Steps:
+ 1. Scan and connect to a network.
+ 2. Run IPerf to download data for few hours.
+ 3. Run IPerf to upload data for few hours.
+ 4. Verify no WiFi disconnects/data interruption.
+
+ """
+ self.scan_and_connect_by_ssid(self.dut, self.wpa_5g)
+ self.scan_and_connect_by_ssid(self.dut_client, self.wpa_5g)
+
+ q = queue.Queue()
+ sec = self.stress_hours * 60 * 60
+ start_time = time.time()
+
+ dl_args = "-p {} -t {} -R".format(self.iperf_server_port, sec)
+ dl = threading.Thread(target=self.run_long_traffic, args=(sec, dl_args, q))
+ dl.start()
+ dl.join()
+
+ total_time = time.time() - start_time
+ self.log.debug("WiFi state = %d" %self.dut.droid.wifiCheckState())
+ while(q.qsize() > 0):
+ if not q.get():
+ raise signals.TestFailure("Network long-connect failed.",
+ extras={"Total Hours":"%d" %self.stress_hours,
+ "Seconds Run":"%d" %total_time})
+ raise signals.TestPass(details="", extras={"Total Hours":"%d" %
+ self.stress_hours, "Seconds Run":"%d" %total_time})
+
+ def test_stress_youtube_5g(self):
+ """Test to connect to network and play various youtube videos.
+
+ Steps:
+ 1. Scan and connect to a network.
+ 2. Loop through and play a list of youtube videos.
+ 3. Verify no WiFi disconnects/data interruption.
+
+ """
+ # List of Youtube 4K videos.
+ videos = ["https://www.youtube.com/watch?v=TKmGU77INaM",
+ "https://www.youtube.com/watch?v=WNCl-69POro",
+ "https://www.youtube.com/watch?v=dVkK36KOcqs",
+ "https://www.youtube.com/watch?v=0wCC3aLXdOw",
+ "https://www.youtube.com/watch?v=rN6nlNC9WQA",
+ "https://www.youtube.com/watch?v=RK1K2bCg4J8"]
+ try:
+ self.scan_and_connect_by_ssid(self.dut, self.wpa_5g)
+ start_time = time.time()
+ for video in videos:
+ self.start_youtube_video(url=video, secs=10*60)
+ except:
+ total_time = time.time() - start_time
+ raise signals.TestFailure("The youtube stress test has failed."
+ "WiFi State = %d" %self.dut.droid.wifiCheckState(),
+ extras={"Total Hours":"1", "Seconds Run":"%d" %total_time})
+ total_time = time.time() - start_time
+ self.log.debug("WiFi state = %d" %self.dut.droid.wifiCheckState())
+ raise signals.TestPass(details="", extras={"Total Hours":"1",
+ "Seconds Run":"%d" %total_time})
+
+ @test_tracker_info(uuid="d367c83e-5b00-4028-9ed8-f7b875997d13")
+ def test_stress_wifi_failover(self):
+ """This test does aggressive failover to several networks in list.
+
+ Steps:
+ 1. Add and enable few networks.
+ 2. Let device auto-connect.
+ 3. Remove the connected network.
+ 4. Repeat 2-3.
+ 5. Device should connect to a network until all networks are
+ exhausted.
+
+ """
+ for count in range(int(self.stress_count/4)):
+ wutils.reset_wifi(self.dut)
+ ssids = list()
+ for network in self.networks:
+ ssids.append(network[WifiEnums.SSID_KEY])
+ ret = self.dut.droid.wifiAddNetwork(network)
+ asserts.assert_true(ret != -1, "Add network %r failed" % network)
+ self.dut.droid.wifiEnableNetwork(ret, 0)
+ self.dut.droid.wifiStartScan()
+ time.sleep(WAIT_FOR_AUTO_CONNECT)
+ cur_network = self.dut.droid.wifiGetConnectionInfo()
+ cur_ssid = cur_network[WifiEnums.SSID_KEY]
+ self.log.info("Cur_ssid = %s" % cur_ssid)
+ for i in range(0,len(self.networks)):
+ self.log.debug("Forget network %s" % cur_ssid)
+ wutils.wifi_forget_network(self.dut, cur_ssid)
+ time.sleep(WAIT_FOR_AUTO_CONNECT)
+ cur_network = self.dut.droid.wifiGetConnectionInfo()
+ cur_ssid = cur_network[WifiEnums.SSID_KEY]
+ self.log.info("Cur_ssid = %s" % cur_ssid)
+ if i == len(self.networks) - 1:
+ break
+ if cur_ssid not in ssids:
+ raise signals.TestFailure("Device did not failover to the "
+ "expected network. SSID = %s" % cur_ssid)
+ network_config = self.dut.droid.wifiGetConfiguredNetworks()
+ self.log.info("Network Config = %s" % network_config)
+ if len(network_config):
+ raise signals.TestFailure("All the network configurations were not "
+ "removed. Configured networks = %s" % network_config,
+ extras={"Iterations":"%d" % self.stress_count,
+ "Pass":"%d" %(count*4)})
+ raise signals.TestPass(details="", extras={"Iterations":"%d" %
+ self.stress_count, "Pass":"%d" %((count+1)*4)})
+
+ @test_tracker_info(uuid="2c19e8d1-ac16-4d7e-b309-795144e6b956")
+ def test_stress_softAP_startup_and_stop_5g(self):
+ """Test to bring up softAP and down for N times.
+
+ Steps:
+ 1. Bring up softAP on 5G.
+ 2. Check for softAP on teh client device.
+ 3. Turn ON WiFi.
+ 4. Verify softAP is turned down and WiFi is up.
+
+ """
+ ap_ssid = "softap_" + utils.rand_ascii_str(8)
+ ap_password = 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
+ # 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)
+ for count in range(self.stress_count):
+ initial_wifi_state = self.dut.droid.wifiCheckState()
+ wutils.start_wifi_tethering(self.dut,
+ ap_ssid,
+ ap_password,
+ WifiEnums.WIFI_CONFIG_APBAND_5G)
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ self.dut_client, ap_ssid)
+ wutils.stop_wifi_tethering(self.dut)
+ asserts.assert_false(self.dut.droid.wifiIsApEnabled(),
+ "SoftAp failed to shutdown!")
+ # Give some time for WiFi to come back to previous state.
+ time.sleep(2)
+ cur_wifi_state = self.dut.droid.wifiCheckState()
+ if initial_wifi_state != cur_wifi_state:
+ raise signals.TestFailure("Wifi state was %d before softAP and %d now!" %
+ (initial_wifi_state, cur_wifi_state),
+ extras={"Iterations":"%d" % self.stress_count,
+ "Pass":"%d" %count})
+ raise signals.TestPass(details="", extras={"Iterations":"%d" %
+ self.stress_count, "Pass":"%d" %(count+1)})
+
+ @test_tracker_info(uuid="eb22e26b-95d1-4580-8c76-85dfe6a42a0f")
+ def test_stress_wifi_roaming(self):
+ AP1_network = self.reference_networks[0]["5g"]
+ AP2_network = self.reference_networks[1]["5g"]
+ wutils.set_attns(self.attenuators, "AP1_on_AP2_off")
+ self.scan_and_connect_by_ssid(self.dut, AP1_network)
+ # Reduce iteration to half because each iteration does two roams.
+ for count in range(int(self.stress_count/2)):
+ self.log.info("Roaming iteration %d, from %s to %s", count,
+ AP1_network, AP2_network)
+ try:
+ wutils.trigger_roaming_and_validate(self.dut, self.attenuators,
+ "AP1_off_AP2_on", AP2_network)
+ self.log.info("Roaming iteration %d, from %s to %s", count,
+ AP2_network, AP1_network)
+ wutils.trigger_roaming_and_validate(self.dut, self.attenuators,
+ "AP1_on_AP2_off", AP1_network)
+ except:
+ raise signals.TestFailure("Roaming failed. Look at logs",
+ extras={"Iterations":"%d" %self.stress_count, "Pass":"%d" %
+ (count*2)})
+ raise signals.TestPass(details="", extras={"Iterations":"%d" %
+ self.stress_count, "Pass":"%d" %((count+1)*2)})
+
+ @test_tracker_info(uuid="e8ae8cd2-c315-4c08-9eb3-83db65b78a58")
+ def test_stress_network_selector_2G_connection(self):
+ """
+ 1. Add one saved 2G network to DUT.
+ 2. Move the DUT in range.
+ 3. Verify the DUT is connected to the network.
+ 4. Move the DUT out of range
+ 5. Repeat step 2-4
+ """
+ for attenuator in self.attenuators:
+ attenuator.set_atten(95)
+ # add a saved network to DUT
+ networks = [self.reference_networks[0]['2g']]
+ self.add_networks(self.dut, networks)
+ for count in range(self.stress_count):
+ self.connect_and_verify_connected_ssid(self.reference_networks[0]['2g'])
+ # move the DUT out of range
+ self.attenuators[0].set_atten(95)
+ time.sleep(10)
+ wutils.set_attns(self.attenuators, "default")
+ raise signals.TestPass(details="", extras={"Iterations":"%d" %
+ self.stress_count, "Pass":"%d" %(count+1)})
+
+ @test_tracker_info(uuid="5d5d14cb-3cd1-4b3d-8c04-0d6f4b764b6b")
+ def test_stress_pno_connection_to_2g(self):
+ """Test PNO triggered autoconnect to a network for N times
+
+ Steps:
+ 1. Save 2Ghz valid network configuration in the device.
+ 2. Screen off DUT
+ 3. Attenuate 5Ghz network and wait for a few seconds to trigger PNO.
+ 4. Check the device connected to 2Ghz network automatically.
+ 5. Repeat step 3-4
+ """
+ for attenuator in self.attenuators:
+ attenuator.set_atten(95)
+ # add a saved network to DUT
+ networks = [self.reference_networks[0]['2g']]
+ self.add_networks(self.dut, networks)
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+ for count in range(self.stress_count):
+ self.connect_and_verify_connected_ssid(self.reference_networks[0]['2g'], is_pno=True)
+ wutils.wifi_forget_network(
+ self.dut, networks[0][WifiEnums.SSID_KEY])
+ # move the DUT out of range
+ self.attenuators[0].set_atten(95)
+ time.sleep(10)
+ self.add_networks(self.dut, networks)
+ wutils.set_attns(self.attenuators, "default")
+ raise signals.TestPass(details="", extras={"Iterations":"%d" %
+ self.stress_count, "Pass":"%d" %(count+1)})
diff --git a/acts_tests/tests/google/wifi/WifiTeleCoexTest.py b/acts_tests/tests/google/wifi/WifiTeleCoexTest.py
new file mode 100644
index 0000000..6d5fef8
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiTeleCoexTest.py
@@ -0,0 +1,310 @@
+#!/usr/bin/env python3.4
+
+import queue
+import time
+
+import acts.base_test
+import acts.test_utils.wifi.wifi_test_utils as wifi_utils
+import acts.test_utils.tel.tel_test_utils as tele_utils
+import acts.utils
+
+from acts import asserts
+from acts import signals
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
+from acts.test_utils.tel.tel_voice_utils import phone_setup_voice_general
+from acts.test_utils.tel.tel_voice_utils import two_phone_call_short_seq
+
+WifiEnums = wifi_utils.WifiEnums
+
+ATTENUATORS = "attenuators"
+WIFI_SSID = "wifi_network_ssid"
+WIFI_PWD = "wifi_network_pass"
+STRESS_COUNT = "stress_iteration"
+
+class WifiTeleCoexTest(TelephonyBaseTest):
+ """Tests for WiFi, Celular Co-existance."""
+
+
+ def setup_class(self):
+ TelephonyBaseTest.setup_class(self)
+
+ self.dut = self.android_devices[0]
+ wifi_utils.wifi_test_device_init(self.dut)
+ # Set attenuation to 0 on all channels.
+ if getattr(self, ATTENUATORS, []):
+ for a in self.attenuators:
+ a.set_atten(0)
+ self.ads = self.android_devices
+ self.dut = self.android_devices[0]
+ self.wifi_network_ssid = self.user_params.get(WIFI_SSID)
+ self.wifi_network_pass = self.user_params.get(WIFI_PWD)
+ self.network = { WifiEnums.SSID_KEY : self.wifi_network_ssid,
+ WifiEnums.PWD_KEY : self.wifi_network_pass
+ }
+ self.stress_count = self.user_params.get(STRESS_COUNT)
+
+
+ def setup_test(self):
+ wifi_utils.wifi_toggle_state(self.dut, True)
+
+
+ def teardown_test(self):
+ wifi_utils.reset_wifi(self.dut)
+
+
+ """Helper Functions"""
+
+
+ def connect_to_wifi(self, ad, network):
+ """Connection logic for open and psk wifi networks.
+
+ Args:
+ ad: Android device object.
+ network: A JSON dict of the WiFi network configuration.
+
+ """
+ ad.ed.clear_all_events()
+ wifi_utils.start_wifi_connection_scan(ad)
+ scan_results = ad.droid.wifiGetScanResults()
+ wifi_utils.assert_network_in_list({WifiEnums.SSID_KEY:
+ self.wifi_network_ssid}, scan_results)
+ wifi_utils.wifi_connect(ad, network)
+ self.log.debug("Connected to %s network on %s device" % (
+ network[WifiEnums.SSID_KEY], ad.serial))
+
+
+ def stress_toggle_wifi(self, stress_count):
+ """Toggle WiFi in a loop.
+
+ Args:
+ stress_count: Number of times to toggle WiFi OFF and ON.
+
+ """
+ for count in range(stress_count):
+ self.log.debug("stress_toggle_wifi: Iteration %d" % count)
+ wifi_utils.toggle_wifi_off_and_on(self.dut)
+
+ if not self.dut.droid.wifiGetisWifiEnabled():
+ raise signals.TestFailure("WiFi did not turn on after toggling it"
+ " %d times" % self.stress_count)
+
+
+ def stress_toggle_airplane(self, stress_count):
+ """Toggle Airplane mode in a loop.
+
+ Args:
+ stress_count: Number of times to toggle Airplane mode OFF and ON.
+
+ """
+ for count in range(stress_count):
+ self.log.debug("stress_toggle_airplane: Iteration %d" % count)
+ wifi_utils.toggle_airplane_mode_on_and_off(self.dut)
+
+ if not self.dut.droid.wifiGetisWifiEnabled():
+ raise signals.TestFailure("WiFi did not turn on after toggling it"
+ " %d times" % self.stress_count)
+
+
+ def stress_toggle_airplane_and_wifi(self, stress_count):
+ """Toggle Airplane and WiFi modes in a loop.
+
+ Args:
+ stress_count: Number of times to perform Airplane mode ON, WiFi ON,
+ Airplane mode OFF, in a sequence.
+
+ """
+ for count in range(stress_count):
+ self.log.debug("stress_toggle_airplane_and_wifi: Iteration %d" % count)
+ self.log.debug("Toggling Airplane mode ON")
+ asserts.assert_true(
+ acts.utils.force_airplane_mode(self.dut, True),
+ "Can not turn on airplane mode on: %s" % self.dut.serial)
+ # Sleep for atleast 500ms so that, call to enable wifi is not deferred.
+ time.sleep(1)
+ self.log.debug("Toggling wifi ON")
+ wifi_utils.wifi_toggle_state(self.dut, True)
+ # Sleep for 1s before getting new WiFi state.
+ time.sleep(1)
+ if not self.dut.droid.wifiGetisWifiEnabled():
+ raise signals.TestFailure("WiFi did not turn on after turning ON"
+ " Airplane mode")
+ asserts.assert_true(
+ acts.utils.force_airplane_mode(self.dut, False),
+ "Can not turn on airplane mode on: %s" % self.dut.serial)
+
+ if not self.dut.droid.wifiGetisWifiEnabled():
+ raise signals.TestFailure("WiFi did not turn on after toggling it"
+ " %d times" % self.stress_count)
+
+
+ def setup_cellular_voice_calling(self):
+ """Setup phone for voice general calling and make sure phone is
+ attached to voice."""
+ # Make sure Phone A and B are attached to voice network.
+ for ad in self.ads:
+ if not phone_setup_voice_general(self.log, ad):
+ raise signals.TestFailure("Phone failed to setup for voice"
+ " calling serial:%s" % ad.serial)
+ self.log.debug("Finished setting up phones for voice calling")
+
+
+ def validate_cellular_and_wifi(self):
+ """Validate WiFi, make some cellular calls.
+
+ Steps:
+ 1. Check if device is still connected to the WiFi network.
+ 2. If WiFi looks good, check if deivce is attached to voice.
+ 3. Make a short sequence voice call between Phone A and B.
+
+ """
+ # Sleep for 30s before getting new WiFi state.
+ time.sleep(30)
+ wifi_info = self.dut.droid.wifiGetConnectionInfo()
+ if wifi_info[WifiEnums.SSID_KEY] != self.wifi_network_ssid:
+ raise signals.TestFailure("Phone failed to connect to %s network on"
+ " %s" % (self.wifi_network_ssid,
+ self.dut.serial))
+
+ # Make short call sequence between Phone A and Phone B.
+ two_phone_call_short_seq(self.log, self.ads[0], None, None, self.ads[1],
+ None, None)
+
+ """Tests"""
+
+
+ @test_tracker_info(uuid="8b9b6fb9-964b-43e7-b75f-675774ee346f")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_toggle_wifi_call(self):
+ """Test to toggle WiFi and then perform WiFi connection and
+ cellular calls.
+
+ Steps:
+ 1. Attach device to voice subscription network.
+ 2. Connect to a WiFi network.
+ 3. Toggle WiFi OFF and ON.
+ 4. Verify device auto-connects to the WiFi network.
+ 5. Verify device is attached to voice network.
+ 6. Make short sequence voice calls.
+
+ """
+ self.setup_cellular_voice_calling()
+ self.connect_to_wifi(self.dut, self.network)
+ wifi_utils.toggle_wifi_off_and_on(self.dut)
+ self.validate_cellular_and_wifi()
+ return True
+
+
+ @test_tracker_info(uuid="caf22447-6354-4a2e-99e5-0ff235fc8f20")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_toggle_airplane_call(self):
+ """Test to toggle Airplane mode and perform WiFi connection and
+ cellular calls.
+
+ Steps:
+ 1. Attach device to voice subscription network.
+ 2. Connect to a WiFi network.
+ 3. Toggle Airplane mode OFF and ON.
+ 4. Verify device auto-connects to the WiFi network.
+ 5. Verify device is attached to voice network.
+ 6. Make short sequence voice calls.
+
+ """
+ self.setup_cellular_voice_calling()
+ self.connect_to_wifi(self.dut, self.network)
+ wifi_utils.toggle_airplane_mode_on_and_off(self.dut)
+ self.validate_cellular_and_wifi()
+ return True
+
+
+ @test_tracker_info(uuid="dd888b35-f820-409a-89af-4b0f6551e4d6")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_toggle_airplane_and_wifi_call(self):
+ """Test to toggle WiFi in a loop and perform WiFi connection and
+ cellular calls.
+
+ Steps:
+ 1. Attach device to voice subscription network.
+ 2. Connect to a WiFi network.
+ 3. Toggle Airplane mode ON.
+ 4. Turn WiFi ON.
+ 5. Toggle Airplane mode OFF.
+ 3. Verify device auto-connects to the WiFi network.
+ 4. Verify device is attached to voice network.
+ 5. Make short sequence voice calls.
+
+ """
+ self.setup_cellular_voice_calling()
+ self.connect_to_wifi(self.dut, self.network)
+ self.stress_toggle_airplane_and_wifi(1)
+ self.validate_cellular_and_wifi()
+ return True
+
+
+ @test_tracker_info(uuid="15db5b7e-827e-4bc8-8e77-7fcce343a323")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_stress_toggle_wifi_call(self):
+ """Stress test to toggle WiFi in a loop, then perform WiFi connection
+ and cellular calls.
+
+ Steps:
+ 1. Attach device to voice subscription network.
+ 2. Connect to a WiFi network.
+ 3. Toggle WiFi OFF and ON in a loop.
+ 4. Verify device auto-connects to the WiFi network.
+ 5. Verify device is attached to voice network.
+ 6. Make short sequence voice calls.
+
+ """
+ self.setup_cellular_voice_calling()
+ self.connect_to_wifi(self.dut, self.network)
+ self.stress_toggle_wifi(self.stress_count)
+ self.validate_cellular_and_wifi()
+ return True
+
+
+ @test_tracker_info(uuid="80a2f1bf-5e41-453a-9b8e-be3b41d4d313")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_stress_toggle_airplane_call(self):
+ """Stress test to toggle Airplane mode in a loop, then perform WiFi and
+ cellular calls.
+
+ Steps:
+ 1. Attach device to voice subscription network.
+ 2. Connect to a WiFi network.
+ 3. Toggle Airplane mode OFF and ON in a loop.
+ 4. Verify device auto-connects to the WiFi network.
+ 5. Verify device is attached to voice network.
+ 6. Make short sequence voice calls.
+
+ """
+ self.setup_cellular_voice_calling()
+ self.connect_to_wifi(self.dut, self.network)
+ self.stress_toggle_airplane(self.stress_count)
+ self.validate_cellular_and_wifi()
+ return True
+
+
+ @test_tracker_info(uuid="b88ad3e7-6462-4280-ad57-22d0ac91fdd8")
+ @TelephonyBaseTest.tel_test_wrap
+ def test_stress_toggle_airplane_and_wifi_call(self):
+ """Stress test to toggle Airplane and WiFi mode in a loop, then perform
+ WiFi connection and cellular calls.
+
+ Steps:
+ 1. Attach device to voice subscription network.
+ 2. Connect to a WiFi network.
+ 3. Toggle Airplane mode ON.
+ 4. Turn WiFi ON.
+ 5. Toggle Airplane mode OFF.
+ 6. Repeat 3, 4 & 5, in a loop.
+ 7. Verify device auto-connects to the WiFi network.
+ 8. Verify device is attached to voice network.
+ 9. Make short sequence voice calls.
+
+ """
+ self.setup_cellular_voice_calling()
+ self.connect_to_wifi(self.dut, self.network)
+ self.stress_toggle_airplane_and_wifi(self.stress_count)
+ self.validate_cellular_and_wifi()
+ return True
diff --git a/acts_tests/tests/google/wifi/WifiTethering2GOpenOTATest.py b/acts_tests/tests/google/wifi/WifiTethering2GOpenOTATest.py
new file mode 100755
index 0000000..0716158
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiTethering2GOpenOTATest.py
@@ -0,0 +1,85 @@
+#!/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 acts.signals as signals
+
+from acts import asserts
+from acts.base_test import BaseTestClass
+from acts.libs.ota import ota_updater
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+
+import acts.test_utils.net.net_test_utils as nutils
+import acts.test_utils.wifi.wifi_test_utils as wutils
+
+
+class WifiTethering2GOpenOTATest(BaseTestClass):
+ """Wifi Tethering 2G Open OTA tests"""
+
+ def setup_class(self):
+
+ super(WifiTethering2GOpenOTATest, self).setup_class()
+ ota_updater.initialize(self.user_params, self.android_devices)
+
+ self.hotspot_device = self.android_devices[0]
+ self.tethered_device = self.android_devices[1]
+ req_params = ("wifi_hotspot_open", )
+ self.unpack_userparams(req_params)
+
+ # verify hotspot device has lte data and supports tethering
+ nutils.verify_lte_data_and_tethering_supported(self.hotspot_device)
+
+ # Save a wifi soft ap configuration and verify that it works
+ wutils.save_wifi_soft_ap_config(self.hotspot_device,
+ self.wifi_hotspot_open,
+ WIFI_CONFIG_APBAND_2G)
+ self._verify_wifi_tethering()
+
+ # Run OTA below, if ota fails then abort all tests.
+ try:
+ ota_updater.update(self.hotspot_device)
+ except Exception as err:
+ raise signals.TestAbortClass(
+ "Failed up apply OTA update. Aborting tests")
+
+ def on_fail(self, test_name, begin_time):
+ for ad in self.android_devices:
+ ad.take_bug_report(test_name, begin_time)
+
+ def teardown_class(self):
+ wutils.wifi_toggle_state(self.hotspot_device, True)
+
+ """Helper Functions"""
+
+ def _verify_wifi_tethering(self):
+ """Verify wifi tethering"""
+ wutils.start_wifi_tethering_saved_config(self.hotspot_device)
+ wutils.wifi_connect(self.tethered_device, self.wifi_hotspot_open)
+ # (TODO: @gmoturu) Change to stop_wifi_tethering. See b/109876061
+ wutils.wifi_toggle_state(self.hotspot_device, True)
+
+ """Tests"""
+
+ @test_tracker_info(uuid="fe502bc3-f9c6-4bed-98ad-acaa7e166521")
+ def test_wifi_tethering_ota_2g_open(self):
+ """ Verify wifi hotspot after ota upgrade
+
+ Steps:
+ 1. Save a wifi hotspot config with 2g band and open auth
+ 2. Do a OTA update
+ 3. Verify that wifi hotspot works with teh saved config
+ """
+ self._verify_wifi_tethering()
diff --git a/acts_tests/tests/google/wifi/WifiTethering2GPskOTATest.py b/acts_tests/tests/google/wifi/WifiTethering2GPskOTATest.py
new file mode 100755
index 0000000..7399e32
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiTethering2GPskOTATest.py
@@ -0,0 +1,85 @@
+#!/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 acts.signals as signals
+
+from acts import asserts
+from acts.base_test import BaseTestClass
+from acts.libs.ota import ota_updater
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
+
+import acts.test_utils.net.net_test_utils as nutils
+import acts.test_utils.wifi.wifi_test_utils as wutils
+
+
+class WifiTethering2GPskOTATest(BaseTestClass):
+ """Wifi Tethering 2G Psk OTA tests"""
+
+ def setup_class(self):
+
+ super(WifiTethering2GPskOTATest, self).setup_class()
+ ota_updater.initialize(self.user_params, self.android_devices)
+
+ self.hotspot_device = self.android_devices[0]
+ self.tethered_device = self.android_devices[1]
+ req_params = ("wifi_hotspot_psk", )
+ self.unpack_userparams(req_params)
+
+ # verify hotspot device has lte data and supports tethering
+ nutils.verify_lte_data_and_tethering_supported(self.hotspot_device)
+
+ # Save a wifi soft ap configuration and verify that it works
+ wutils.save_wifi_soft_ap_config(self.hotspot_device,
+ self.wifi_hotspot_psk,
+ WIFI_CONFIG_APBAND_2G)
+ self._verify_wifi_tethering()
+
+ # Run OTA below, if ota fails then abort all tests.
+ try:
+ ota_updater.update(self.hotspot_device)
+ except Exception as err:
+ raise signals.TestAbortClass(
+ "Failed up apply OTA update. Aborting tests")
+
+ def on_fail(self, test_name, begin_time):
+ for ad in self.android_devices:
+ ad.take_bug_report(test_name, begin_time)
+
+ def teardown_class(self):
+ wutils.wifi_toggle_state(self.hotspot_device, True)
+
+ """Helper Functions"""
+
+ def _verify_wifi_tethering(self):
+ """Verify wifi tethering"""
+ wutils.start_wifi_tethering_saved_config(self.hotspot_device)
+ wutils.wifi_connect(self.tethered_device, self.wifi_hotspot_psk)
+ # (TODO: @gmoturu) Change to stop_wifi_tethering. See b/109876061
+ wutils.wifi_toggle_state(self.hotspot_device, True)
+
+ """Tests"""
+
+ @test_tracker_info(uuid="4b1cec63-d1d2-4046-84e9-e806bb08ce41")
+ def test_wifi_tethering_ota_2g_psk(self):
+ """ Verify wifi hotspot after ota upgrade
+
+ Steps:
+ 1. Save a wifi hotspot config with 2g band and psk auth
+ 2. Do a OTA update
+ 3. Verify that wifi hotspot works with teh saved config
+ """
+ self._verify_wifi_tethering()
diff --git a/acts_tests/tests/google/wifi/WifiTethering5GOpenOTATest.py b/acts_tests/tests/google/wifi/WifiTethering5GOpenOTATest.py
new file mode 100755
index 0000000..985e7a7
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiTethering5GOpenOTATest.py
@@ -0,0 +1,85 @@
+#!/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 acts.signals as signals
+
+from acts import asserts
+from acts.base_test import BaseTestClass
+from acts.libs.ota import ota_updater
+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.net.net_test_utils as nutils
+import acts.test_utils.wifi.wifi_test_utils as wutils
+
+
+class WifiTethering5GOpenOTATest(BaseTestClass):
+ """Wifi Tethering 5G Open OTA tests"""
+
+ def setup_class(self):
+
+ super(WifiTethering5GOpenOTATest, self).setup_class()
+ ota_updater.initialize(self.user_params, self.android_devices)
+
+ self.hotspot_device = self.android_devices[0]
+ self.tethered_device = self.android_devices[1]
+ req_params = ("wifi_hotspot_open", )
+ self.unpack_userparams(req_params)
+
+ # verify hotspot device has lte data and supports tethering
+ nutils.verify_lte_data_and_tethering_supported(self.hotspot_device)
+
+ # Save a wifi soft ap configuration and verify that it works
+ wutils.save_wifi_soft_ap_config(self.hotspot_device,
+ self.wifi_hotspot_open,
+ WIFI_CONFIG_APBAND_5G)
+ self._verify_wifi_tethering()
+
+ # Run OTA below, if ota fails then abort all tests.
+ try:
+ ota_updater.update(self.hotspot_device)
+ except Exception as err:
+ raise signals.TestAbortClass(
+ "Failed up apply OTA update. Aborting tests")
+
+ def on_fail(self, test_name, begin_time):
+ for ad in self.android_devices:
+ ad.take_bug_report(test_name, begin_time)
+
+ def teardown_class(self):
+ wutils.wifi_toggle_state(self.hotspot_device, True)
+
+ """Helper Functions"""
+
+ def _verify_wifi_tethering(self):
+ """Verify wifi tethering"""
+ wutils.start_wifi_tethering_saved_config(self.hotspot_device)
+ wutils.wifi_connect(self.tethered_device, self.wifi_hotspot_open)
+ # (TODO: @gmoturu) Change to stop_wifi_tethering. See b/109876061
+ wutils.wifi_toggle_state(self.hotspot_device, True)
+
+ """Tests"""
+
+ @test_tracker_info(uuid="b1a94c8f-f3ed-4755-be4a-764e205b4483")
+ def test_wifi_tethering_ota_5g_open(self):
+ """ Verify wifi hotspot after ota upgrade
+
+ Steps:
+ 1. Save a wifi hotspot config with 5g band and open auth
+ 2. Do a OTA update
+ 3. Verify that wifi hotspot works with teh saved config
+ """
+ self._verify_wifi_tethering()
diff --git a/acts_tests/tests/google/wifi/WifiTethering5GPskOTATest.py b/acts_tests/tests/google/wifi/WifiTethering5GPskOTATest.py
new file mode 100755
index 0000000..9e68f22
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiTethering5GPskOTATest.py
@@ -0,0 +1,85 @@
+#!/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 acts.signals as signals
+
+from acts import asserts
+from acts.base_test import BaseTestClass
+from acts.libs.ota import ota_updater
+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.net.net_test_utils as nutils
+import acts.test_utils.wifi.wifi_test_utils as wutils
+
+
+class WifiTethering5GPskOTATest(BaseTestClass):
+ """Wifi Tethering 5G Psk OTA tests"""
+
+ def setup_class(self):
+
+ super(WifiTethering5GPskOTATest, self).setup_class()
+ ota_updater.initialize(self.user_params, self.android_devices)
+
+ self.hotspot_device = self.android_devices[0]
+ self.tethered_device = self.android_devices[1]
+ req_params = ("wifi_hotspot_psk", )
+ self.unpack_userparams(req_params)
+
+ # verify hotspot device has lte data and supports tethering
+ nutils.verify_lte_data_and_tethering_supported(self.hotspot_device)
+
+ # Save a wifi soft ap configuration and verify that it works
+ wutils.save_wifi_soft_ap_config(self.hotspot_device,
+ self.wifi_hotspot_psk,
+ WIFI_CONFIG_APBAND_5G)
+ self._verify_wifi_tethering()
+
+ # Run OTA below, if ota fails then abort all tests.
+ try:
+ ota_updater.update(self.hotspot_device)
+ except Exception as err:
+ raise signals.TestAbortClass(
+ "Failed up apply OTA update. Aborting tests")
+
+ def on_fail(self, test_name, begin_time):
+ for ad in self.android_devices:
+ ad.take_bug_report(test_name, begin_time)
+
+ def teardown_class(self):
+ wutils.wifi_toggle_state(self.hotspot_device, True)
+
+ """Helper Functions"""
+
+ def _verify_wifi_tethering(self):
+ """Verify wifi tethering"""
+ wutils.start_wifi_tethering_saved_config(self.hotspot_device)
+ wutils.wifi_connect(self.tethered_device, self.wifi_hotspot_psk)
+ # (TODO: @gmoturu) Change to stop_wifi_tethering. See b/109876061
+ wutils.wifi_toggle_state(self.hotspot_device, True)
+
+ """Tests"""
+
+ @test_tracker_info(uuid="111d3a33-3152-4993-b1ba-307daaf2a6ff")
+ def test_wifi_tethering_ota_5g_psk(self):
+ """ Verify wifi hotspot after ota upgrade
+
+ Steps:
+ 1. Save a wifi hotspot config with 5g band and psk auth
+ 2. Do a OTA update
+ 3. Verify that wifi hotspot works with teh saved config
+ """
+ self._verify_wifi_tethering()
diff --git a/acts_tests/tests/google/wifi/WifiTetheringPowerTest.py b/acts_tests/tests/google/wifi/WifiTetheringPowerTest.py
new file mode 100644
index 0000000..dd3cf74
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiTetheringPowerTest.py
@@ -0,0 +1,266 @@
+#!/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 os
+import threading
+import time
+
+from acts import base_test
+from acts import asserts
+from acts.controllers import adb
+from acts.controllers import monsoon
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.tel import tel_data_utils as tel_utils
+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 http_file_download_by_chrome
+from acts.utils import force_airplane_mode
+from acts.utils import set_adaptive_brightness
+from acts.utils import set_ambient_display
+from acts.utils import set_auto_rotate
+from acts.utils import set_location_service
+
+
+class WifiTetheringPowerTest(base_test.BaseTestClass):
+
+ def setup_class(self):
+ self.hotspot_device = self.android_devices[0]
+ self.tethered_devices = self.android_devices[1:]
+ req_params = ("ssid", "password", "url")
+ self.unpack_userparams(req_params)
+ self.network = { "SSID": self.ssid, "password": self.password }
+
+ self.offset = 1 * 60
+ self.hz = 5000
+ self.duration = 9 * 60 + self.offset
+ self.mon_data_path = os.path.join(self.log_path, "Monsoon")
+ self.mon = self.monsoons[0]
+ self.mon.set_voltage(4.2)
+ self.mon.set_max_current(7.8)
+ self.mon.attach_device(self.hotspot_device)
+
+ asserts.assert_true(self.mon.usb("auto"),
+ "Failed to turn USB mode to auto on monsoon.")
+ set_location_service(self.hotspot_device, False)
+ set_adaptive_brightness(self.hotspot_device, False)
+ set_ambient_display(self.hotspot_device, False)
+ self.hotspot_device.adb.shell("settings put system screen_brightness 0")
+ set_auto_rotate(self.hotspot_device, False)
+ wutils.wifi_toggle_state(self.hotspot_device, False)
+ self.hotspot_device.droid.telephonyToggleDataConnection(True)
+ tel_utils.wait_for_cell_data_connection(self.log, self.hotspot_device, True)
+ asserts.assert_true(
+ tel_utils.verify_http_connection(self.log, self.hotspot_device),
+ "HTTP verification failed on cell data connection")
+ for ad in self.tethered_devices:
+ wutils.reset_wifi(ad)
+
+ def teardown_class(self):
+ self.mon.usb("on")
+ wutils.wifi_toggle_state(self.hotspot_device, True)
+
+ def on_fail(self, test_name, begin_time):
+ self.hotspot_device.take_bug_report(test_name, begin_time)
+
+ def on_pass(self, test_name, begin_time):
+ self.hotspot_device.take_bug_report(test_name, begin_time)
+
+ """ Helper functions """
+ def _measure_and_process_result(self):
+ """ Measure the current drawn by the device for the period of
+ self.duration, at the frequency of self.hz.
+ """
+ tag = self.current_test_name
+ result = self.mon.measure_power(self.hz,
+ self.duration,
+ tag=tag,
+ offset=self.offset)
+ asserts.assert_true(result,
+ "Got empty measurement data set in %s." % tag)
+ self.log.info(repr(result))
+ data_path = os.path.join(self.mon_data_path, "%s.txt" % tag)
+ monsoon.MonsoonData.save_to_text_file([result], data_path)
+ actual_current = result.average_current
+ actual_current_str = "%.2fmA" % actual_current
+ result_extra = {"Average Current": actual_current_str}
+
+ def _start_wifi_tethering(self, wifi_band):
+ """ Start wifi tethering on hotspot device
+
+ Args:
+ 1. wifi_band: specifies the wifi band to start the hotspot
+ on. The current options are 2G and 5G
+ """
+ wutils.start_wifi_tethering(self.hotspot_device,
+ self.ssid,
+ self.password,
+ wifi_band)
+
+ def _start_traffic_on_device(self, ad):
+ """ Start traffic on the device by downloading data
+ Run the traffic continuosly for self.duration
+
+ Args:
+ 1. ad device object
+ """
+ timeout = time.time() + self.duration
+ while True:
+ if time.time() > timeout:
+ break
+ http_file_download_by_chrome(ad, self.url)
+
+ def _start_traffic_measure_power(self, ad_list):
+ """ Start traffic on the tethered devices and measure power
+
+ Args:
+ 1. ad_list: list of tethered devices to run traffic on
+ """
+ threads = []
+ for ad in ad_list:
+ t = threading.Thread(target = self._start_traffic_on_device,
+ args = (ad,))
+ t.start()
+ threads.append(t)
+ try:
+ self._measure_and_process_result()
+ finally:
+ for t in threads:
+ t.join()
+
+
+ """ Tests begin """
+ @test_tracker_info(uuid="ebb74144-e22a-46e1-b8c1-9ada22b13133")
+ def test_power_wifi_tethering_2ghz_no_devices_connected(self):
+ """ Steps:
+ 1. Start wifi hotspot with 2.4Ghz band
+ 2. No devices connected to hotspot
+ 3. Measure power
+ """
+ self._start_wifi_tethering(WIFI_CONFIG_APBAND_2G)
+ self._measure_and_process_result()
+ wutils.stop_wifi_tethering(self.hotspot_device)
+
+ @test_tracker_info(uuid="2560c088-4010-4354-ade3-6aaac83b1cfd")
+ def test_power_wifi_tethering_5ghz_no_devices_connected(self):
+ """ Steps:
+ 1. Start wifi hotspot with 5Ghz band
+ 2. No devices connected to hotspot
+ 3. Measure power
+ """
+ self._start_wifi_tethering(WIFI_CONFIG_APBAND_5G)
+ self._measure_and_process_result()
+ wutils.stop_wifi_tethering(self.hotspot_device)
+
+ @test_tracker_info(uuid="644795b0-cd30-4a8f-82ee-cc0618c41c6b")
+ def test_power_wifi_tethering_2ghz_connect_1device(self):
+ """ Steps:
+ 1. Start wifi hotspot with 2.4GHz band
+ 2. Connect 1 device to hotspot
+ 3. Measure power
+ """
+ self._start_wifi_tethering(WIFI_CONFIG_APBAND_2G)
+ wutils.wifi_connect(self.tethered_devices[0], self.network)
+ self._measure_and_process_result()
+ wutils.stop_wifi_tethering(self.hotspot_device)
+
+ @test_tracker_info(uuid="8fca9898-f493-44c3-810f-d2262ac72187")
+ def test_power_wifi_tethering_5ghz_connect_1device(self):
+ """ Steps:
+ 1. Start wifi hotspot with 5GHz band
+ 2. Connect 1 device to hotspot
+ 3. Measure power
+ """
+ self._start_wifi_tethering(WIFI_CONFIG_APBAND_5G)
+ wutils.wifi_connect(self.tethered_devices[0], self.network)
+ self._measure_and_process_result()
+ wutils.stop_wifi_tethering(self.hotspot_device)
+
+ @test_tracker_info(uuid="16ef5f63-1a7a-44ae-bf8d-c3a181c89b63")
+ def test_power_wifi_tethering_2ghz_connect_5devices(self):
+ """ Steps:
+ 1. Start wifi hotspot with 2GHz band
+ 2. Connect 5 devices to hotspot
+ 3. Measure power
+ """
+ self._start_wifi_tethering(WIFI_CONFIG_APBAND_2G)
+ for ad in self.tethered_devices:
+ wutils.wifi_connect(ad, self.network)
+ self._measure_and_process_result()
+ wutils.stop_wifi_tethering(self.hotspot_device)
+
+ @test_tracker_info(uuid="769aedfc-d309-40e0-95dd-51ff40f4e097")
+ def test_power_wifi_tethering_5ghz_connect_5devices(self):
+ """ Steps:
+ 1. Start wifi hotspot with 5GHz band
+ 2. Connect 5 devices to hotspot
+ 3. Measure power
+ """
+ self._start_wifi_tethering(WIFI_CONFIG_APBAND_5G)
+ for ad in self.tethered_devices:
+ wutils.wifi_connect(ad, self.network)
+ self._measure_and_process_result()
+ wutils.stop_wifi_tethering(self.hotspot_device)
+
+ @test_tracker_info(uuid="e5b71f34-1dc0-4045-a45e-48c1e9426ec3")
+ def test_power_wifi_tethering_2ghz_connect_1device_with_traffic(self):
+ """ Steps:
+ 1. Start wifi hotspot with 2GHz band
+ 2. Connect 1 device to hotspot device
+ 3. Start traffic and measure power
+ """
+ self._start_wifi_tethering(WIFI_CONFIG_APBAND_2G)
+ wutils.wifi_connect(self.tethered_devices[0], self.network)
+ self._start_traffic_measure_power(self.tethered_devices[0:1])
+ wutils.stop_wifi_tethering(self.hotspot_device)
+
+ @test_tracker_info(uuid="29c5cd6e-8df1-46e5-a735-526dc9154f6e")
+ def test_power_wifi_tethering_5ghz_connect_1device_with_traffic(self):
+ """ Steps:
+ 1. Start wifi hotspot with 5GHz band
+ 2. Connect 1 device to hotspot device
+ 3. Start traffic and measure power
+ """
+ self._start_wifi_tethering(WIFI_CONFIG_APBAND_5G)
+ wutils.wifi_connect(self.tethered_devices[0], self.network)
+ self._start_traffic_measure_power(self.tethered_devices[0:1])
+ wutils.stop_wifi_tethering(self.hotspot_device)
+
+ @test_tracker_info(uuid="da71b06f-7b98-4c14-a2e2-361f395b39a8")
+ def test_power_wifi_tethering_2ghz_connect_5devices_with_traffic(self):
+ """ Steps:
+ 1. Start wifi hotspot with 2GHz band
+ 2. Connect 5 devices to hotspot device
+ 3. Start traffic and measure power
+ """
+ self._start_wifi_tethering(WIFI_CONFIG_APBAND_2G)
+ for ad in self.tethered_devices:
+ wutils.wifi_connect(ad, self.network)
+ self._start_traffic_measure_power(self.tethered_devices)
+ wutils.stop_wifi_tethering(self.hotspot_device)
+
+ @test_tracker_info(uuid="7f3173ab-fd8f-4579-8c45-f9a8c5cd17f7")
+ def test_power_wifi_tethering_5ghz_connect_5devices_with_traffic(self):
+ """ Steps:
+ 1. Start wifi hotspot with 2GHz band
+ 2. Connect 5 devices to hotspot device
+ 3. Start traffic and measure power
+ """
+ self._start_wifi_tethering(WIFI_CONFIG_APBAND_5G)
+ for ad in self.tethered_devices:
+ wutils.wifi_connect(ad, self.network)
+ self._start_traffic_measure_power(self.tethered_devices)
+ wutils.stop_wifi_tethering(self.hotspot_device)
diff --git a/acts_tests/tests/google/wifi/WifiTetheringTest.py b/acts_tests/tests/google/wifi/WifiTetheringTest.py
new file mode 100644
index 0000000..56c6427
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiTetheringTest.py
@@ -0,0 +1,590 @@
+#
+# 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 logging
+import random
+import socket
+import time
+
+from acts import asserts
+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.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
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+WAIT_TIME = 5
+
+class WifiTetheringTest(WifiBaseTest):
+ """ Tests for Wifi Tethering """
+
+ def setup_class(self):
+ """ Setup devices for tethering and unpack params """
+ super().setup_class()
+ self.hotspot_device = self.android_devices[0]
+ self.tethered_devices = self.android_devices[1:]
+ req_params = ("url", "open_network")
+ 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 = "hs_%s" % utils.rand_ascii_str(6)
+
+ nutils.verify_lte_data_and_tethering_supported(self.hotspot_device)
+ for ad in self.tethered_devices:
+ wutils.wifi_test_device_init(ad)
+
+ def setup_test(self):
+ super().setup_test()
+ self.tethered_devices[0].droid.telephonyToggleDataConnection(False)
+
+ def teardown_test(self):
+ super().teardown_test()
+ if self.hotspot_device.droid.wifiIsApEnabled():
+ wutils.stop_wifi_tethering(self.hotspot_device)
+ self.tethered_devices[0].droid.telephonyToggleDataConnection(True)
+
+ def teardown_class(self):
+ """ Reset devices """
+ for ad in self.tethered_devices:
+ wutils.reset_wifi(ad)
+
+ """ Helper functions """
+
+ def _is_ipaddress_ipv6(self, ip_address):
+ """ Verify if the given string is a valid IPv6 address
+
+ Args:
+ 1. string which contains the IP address
+
+ Returns:
+ True: if valid ipv6 address
+ False: if not
+ """
+ try:
+ socket.inet_pton(socket.AF_INET6, ip_address)
+ return True
+ except socket.error:
+ return False
+
+ def _supports_ipv6_tethering(self, dut):
+ """ Check if provider supports IPv6 tethering.
+ Currently, only Verizon supports IPv6 tethering
+
+ Returns:
+ 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
+
+ def _carrier_supports_ipv6(self,dut):
+ """ Verify if carrier supports ipv6
+ Currently, only verizon and t-mobile supports IPv6
+
+ Returns:
+ 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
+
+ def _verify_ipv6_tethering(self, dut):
+ """ Verify IPv6 tethering """
+ http_response = dut.droid.httpRequestString(self.url)
+ self.log.info("IP address %s " % http_response)
+ active_link_addrs = dut.droid.connectivityGetAllAddressesOfActiveLink()
+ if dut==self.hotspot_device and self._carrier_supports_ipv6(dut)\
+ or self._supports_ipv6_tethering(self.hotspot_device):
+ asserts.assert_true(self._is_ipaddress_ipv6(http_response),
+ "The http response did not return IPv6 address")
+ asserts.assert_true(
+ active_link_addrs and http_response in str(active_link_addrs),
+ "Could not find IPv6 address in link properties")
+ asserts.assert_true(
+ dut.droid.connectivityHasIPv6DefaultRoute(),
+ "Could not find IPv6 default route in link properties")
+ else:
+ asserts.assert_true(
+ not dut.droid.connectivityHasIPv6DefaultRoute(),
+ "Found IPv6 default route in link properties")
+
+ def _start_wifi_tethering(self, wifi_band=WIFI_CONFIG_APBAND_2G):
+ """ Start wifi tethering on hotspot device
+
+ Args:
+ 1. wifi_band: specifies the wifi band to start the hotspot
+ on. The current options are 2G and 5G
+ """
+ wutils.start_wifi_tethering(self.hotspot_device,
+ self.network[wutils.WifiEnums.SSID_KEY],
+ self.network[wutils.WifiEnums.PWD_KEY],
+ wifi_band)
+
+ def _connect_disconnect_devices(self):
+ """ Randomly connect and disconnect devices from the
+ self.tethered_devices list to hotspot device
+ """
+ device_connected = [ False ] * len(self.tethered_devices)
+ for _ in range(50):
+ dut_id = random.randint(0, len(self.tethered_devices)-1)
+ dut = self.tethered_devices[dut_id]
+ # wait for 1 sec between connect & disconnect stress test
+ time.sleep(1)
+ if device_connected[dut_id]:
+ wutils.wifi_forget_network(dut, self.network["SSID"])
+ else:
+ wutils.wifi_connect(dut, self.network)
+ device_connected[dut_id] = not device_connected[dut_id]
+
+ def _connect_disconnect_android_device(self, dut_id, wifi_state):
+ """ Connect or disconnect wifi on android device depending on the
+ current wifi state
+
+ Args:
+ 1. dut_id: tethered device to change the wifi state
+ 2. wifi_state: current wifi state
+ """
+ ad = self.tethered_devices[dut_id]
+ if wifi_state:
+ self.log.info("Disconnecting wifi on android device")
+ wutils.wifi_forget_network(ad, self.network["SSID"])
+ else:
+ self.log.info("Connecting to wifi on android device")
+ wutils.wifi_connect(ad, self.network)
+
+ def _connect_disconnect_wifi_dongle(self, dut_id, wifi_state):
+ """ Connect or disconnect wifi on wifi dongle depending on the
+ current wifi state
+
+ Args:
+ 1. dut_id: wifi dongle to change the wifi state
+ 2. wifi_state: current wifi state
+ """
+ wd = self.arduino_wifi_dongles[dut_id]
+ if wifi_state:
+ self.log.info("Disconnecting wifi on dongle")
+ dutils.disconnect_wifi(wd)
+ else:
+ self.log.info("Connecting to wifi on dongle")
+ dutils.connect_wifi(wd, self.network)
+
+ def _connect_disconnect_tethered_devices(self):
+ """ Connect disconnect tethered devices to wifi hotspot """
+ num_android_devices = len(self.tethered_devices)
+ num_wifi_dongles = 0
+ if hasattr(self, 'arduino_wifi_dongles'):
+ num_wifi_dongles = len(self.arduino_wifi_dongles)
+ total_devices = num_android_devices + num_wifi_dongles
+ device_connected = [False] * total_devices
+ for _ in range(50):
+ dut_id = random.randint(0, total_devices-1)
+ wifi_state = device_connected[dut_id]
+ if dut_id < num_android_devices:
+ self._connect_disconnect_android_device(dut_id, wifi_state)
+ else:
+ self._connect_disconnect_wifi_dongle(dut_id-num_android_devices,
+ wifi_state)
+ device_connected[dut_id] = not device_connected[dut_id]
+
+ def _verify_ping(self, dut, ip, isIPv6=False):
+ """ Verify ping works from the dut to IP/hostname
+
+ Args:
+ 1. dut - ad object to check ping from
+ 2. ip - ip/hostname to ping (IPv4 and IPv6)
+
+ Returns:
+ True - if ping is successful
+ False - if not
+ """
+ self.log.info("Pinging %s from dut %s" % (ip, dut.serial))
+ if isIPv6 or self._is_ipaddress_ipv6(ip):
+ return dut.droid.pingHost(ip, 5, "ping6")
+ return dut.droid.pingHost(ip)
+
+ def _return_ip_for_interface(self, dut, iface_name):
+ """ Return list of IP addresses for an interface
+
+ Args:
+ 1. dut - ad object
+ 2. iface_name - interface name
+
+ Returns:
+ List of IPv4 and IPv6 addresses
+ """
+ return dut.droid.connectivityGetIPv4Addresses(iface_name) + \
+ dut.droid.connectivityGetIPv6Addresses(iface_name)
+
+ def _test_traffic_between_two_tethered_devices(self, ad, wd):
+ """ Verify pinging interfaces of one DUT from another
+
+ Args:
+ 1. ad - android device
+ 2. wd - wifi dongle
+ """
+ wutils.wifi_connect(ad, self.network)
+ dutils.connect_wifi(wd, self.network)
+ local_ip = ad.droid.connectivityGetIPv4Addresses('wlan0')[0]
+ remote_ip = wd.ip_address()
+ port = 8888
+
+ time.sleep(6) # wait until UDP packets method is invoked
+ socket = sutils.open_datagram_socket(ad, local_ip, port)
+ sutils.send_recv_data_datagram_sockets(
+ ad, ad, socket, socket, remote_ip, port)
+ sutils.close_datagram_socket(ad, socket)
+
+ def _ping_hotspot_interfaces_from_tethered_device(self, dut):
+ """ Ping hotspot interfaces from tethered device
+
+ Args:
+ 1. dut - tethered device
+
+ Returns:
+ True - if all IP addresses are pingable
+ False - if not
+ """
+ ifaces = self.hotspot_device.droid.connectivityGetNetworkInterfaces()
+ return_result = True
+ for interface in ifaces:
+ iface_name = interface.split()[0].split(':')[1]
+ if iface_name == "lo":
+ continue
+ ip_list = self._return_ip_for_interface(
+ self.hotspot_device, iface_name)
+ for ip in ip_list:
+ ping_result = self._verify_ping(dut, ip)
+ self.log.info("Ping result: %s %s %s" %
+ (iface_name, ip, ping_result))
+ return_result = return_result and ping_result
+
+ return return_result
+
+ def _save_wifi_softap_configuration(self, ad, config):
+ """ Save soft AP configuration
+
+ Args:
+ 1. dut - device to save configuration on
+ 2. config - soft ap configuration
+ """
+ asserts.assert_true(ad.droid.wifiSetWifiApConfiguration(config),
+ "Failed to set WifiAp Configuration")
+ wifi_ap = ad.droid.wifiGetApConfiguration()
+ asserts.assert_true(wifi_ap[wutils.WifiEnums.SSID_KEY] == config[wutils.WifiEnums.SSID_KEY],
+ "Configured wifi hotspot SSID does not match with the expected SSID")
+
+ def _turn_on_wifi_hotspot(self, ad):
+ """ Turn on wifi hotspot with a config that is already saved
+
+ Args:
+ 1. dut - device to turn wifi hotspot on
+ """
+ ad.droid.wifiStartTrackingTetherStateChange()
+ ad.droid.connectivityStartTethering(tel_defines.TETHERING_WIFI, False)
+ try:
+ ad.ed.pop_event("ConnectivityManagerOnTetheringStarted")
+ ad.ed.wait_for_event("TetherStateChanged",
+ lambda x: x["data"]["ACTIVE_TETHER"], 30)
+ except:
+ asserts.fail("Didn't receive wifi tethering starting confirmation")
+ ad.droid.wifiStopTrackingTetherStateChange()
+
+ """ Test Cases """
+
+ @test_tracker_info(uuid="36d03295-bea3-446e-8342-b9f8f1962a32")
+ def test_ipv6_tethering(self):
+ """ IPv6 tethering test
+
+ Steps:
+ 1. Start wifi tethering on hotspot device
+ 2. Verify IPv6 address on hotspot device (VZW & TMO only)
+ 3. Connect tethered device to hotspot device
+ 4. Verify IPv6 address on the client's link properties (VZW only)
+ 5. Verify ping on client using ping6 which should pass (VZW only)
+ 6. Disable mobile data on provider and verify that link properties
+ does not have IPv6 address and default route (VZW only)
+ """
+ # Start wifi tethering on the hotspot device
+ wutils.toggle_wifi_off_and_on(self.hotspot_device)
+ self._start_wifi_tethering()
+
+ # Verify link properties on hotspot device
+ self.log.info("Check IPv6 properties on the hotspot device. "
+ "Verizon & T-mobile should have IPv6 in link properties")
+ self._verify_ipv6_tethering(self.hotspot_device)
+
+ # Connect the client to the SSID
+ wutils.wifi_connect(self.tethered_devices[0], self.network)
+
+ # Need to wait atleast 2 seconds for IPv6 address to
+ # show up in the link properties
+ time.sleep(WAIT_TIME)
+
+ # Verify link properties on tethered device
+ self.log.info("Check IPv6 properties on the tethered device. "
+ "Device should have IPv6 if carrier is Verizon")
+ self._verify_ipv6_tethering(self.tethered_devices[0])
+
+ # Verify ping6 on tethered device
+ ping_result = self._verify_ping(self.tethered_devices[0],
+ wutils.DEFAULT_PING_ADDR, True)
+ if self._supports_ipv6_tethering(self.hotspot_device):
+ asserts.assert_true(ping_result, "Ping6 failed on the client")
+ else:
+ asserts.assert_true(not ping_result, "Ping6 failed as expected")
+
+ # Disable mobile data on hotspot device
+ # and verify the link properties on tethered device
+ self.log.info("Disabling mobile data to verify ipv6 default route")
+ self.hotspot_device.droid.telephonyToggleDataConnection(False)
+ asserts.assert_equal(
+ self.hotspot_device.droid.telephonyGetDataConnectionState(),
+ tel_defines.DATA_STATE_CONNECTED,
+ "Could not disable cell data")
+
+ time.sleep(WAIT_TIME) # wait until the IPv6 is removed from link properties
+
+ result = self.tethered_devices[0].droid.connectivityHasIPv6DefaultRoute()
+ self.hotspot_device.droid.telephonyToggleDataConnection(True)
+ if result:
+ asserts.fail("Found IPv6 default route in link properties:Data off")
+ self.log.info("Did not find IPv6 address in link properties")
+
+ # Disable wifi tethering
+ wutils.stop_wifi_tethering(self.hotspot_device)
+
+ @test_tracker_info(uuid="110b61d1-8af2-4589-8413-11beac7a3025")
+ def test_wifi_tethering_2ghz_traffic_between_2tethered_devices(self):
+ """ Steps:
+
+ 1. Start wifi hotspot with 2G band
+ 2. Connect 2 tethered devices to the hotspot device
+ 3. Ping interfaces between the tethered devices
+ """
+ asserts.skip_if(not hasattr(self, 'arduino_wifi_dongles'),
+ "No wifi dongles connected. Skipping test")
+ wutils.toggle_wifi_off_and_on(self.hotspot_device)
+ self._start_wifi_tethering(WIFI_CONFIG_APBAND_2G)
+ self._test_traffic_between_two_tethered_devices(self.tethered_devices[0],
+ self.arduino_wifi_dongles[0])
+ wutils.stop_wifi_tethering(self.hotspot_device)
+
+ @test_tracker_info(uuid="953f6e2e-27bd-4b73-85a6-d2eaa4e755d5")
+ def wifi_tethering_5ghz_traffic_between_2tethered_devices(self):
+ """ Steps:
+
+ 1. Start wifi hotspot with 5ghz band
+ 2. Connect 2 tethered devices to the hotspot device
+ 3. Send traffic between the tethered devices
+ """
+ wutils.toggle_wifi_off_and_on(self.hotspot_device)
+ self._start_wifi_tethering(WIFI_CONFIG_APBAND_5G)
+ self._test_traffic_between_two_tethered_devices(self.tethered_devices[0],
+ self.arduino_wifi_dongles[0])
+ wutils.stop_wifi_tethering(self.hotspot_device)
+
+ @test_tracker_info(uuid="d7d5aa51-682d-4882-a334-61966d93b68c")
+ def test_wifi_tethering_2ghz_connect_disconnect_devices(self):
+ """ Steps:
+
+ 1. Start wifi hotspot with 2ghz band
+ 2. Connect and disconnect multiple devices randomly
+ 3. Verify the correct functionality
+ """
+ wutils.toggle_wifi_off_and_on(self.hotspot_device)
+ self._start_wifi_tethering(WIFI_CONFIG_APBAND_2G)
+ self._connect_disconnect_tethered_devices()
+ wutils.stop_wifi_tethering(self.hotspot_device)
+
+ @test_tracker_info(uuid="34abd6c9-c7f1-4d89-aa2b-a66aeabed9aa")
+ def test_wifi_tethering_5ghz_connect_disconnect_devices(self):
+ """ Steps:
+
+ 1. Start wifi hotspot with 5ghz band
+ 2. Connect and disconnect multiple devices randomly
+ 3. Verify the correct functionality
+ """
+ wutils.toggle_wifi_off_and_on(self.hotspot_device)
+ self._start_wifi_tethering(WIFI_CONFIG_APBAND_5G)
+ self._connect_disconnect_devices()
+ wutils.stop_wifi_tethering(self.hotspot_device)
+
+ @test_tracker_info(uuid="7edfb220-37f8-42ea-8d7c-39712fbe9be5")
+ def test_wifi_tethering_wpapsk_network_2g(self):
+ """ Steps:
+
+ 1. Start wifi tethering with wpapsk network 2G band
+ 2. Connect tethered device to the SSID
+ 3. Verify internet connectivity
+ """
+ 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_wpapsk_network_5g(self):
+ """ Steps:
+
+ 1. Start wifi tethering with wpapsk network 5G band
+ 2. Connect tethered device to the SSID
+ 3. Verify internet connectivity
+ """
+ self._start_wifi_tethering(WIFI_CONFIG_APBAND_5G)
+ 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):
+ """ Steps:
+
+ 1. Start wifi tethering
+ 2. Verify wifi Ap configuration
+ 3. Change the SSID of the wifi hotspot while hotspot is on
+ 4. Verify the new SSID in wifi ap configuration
+ 5. Restart tethering and verify that the tethered device is able
+ to connect to the new SSID
+ """
+ dut = self.hotspot_device
+
+ # start tethering and verify the wifi ap configuration settings
+ self._start_wifi_tethering()
+ wifi_ap = dut.droid.wifiGetApConfiguration()
+ asserts.assert_true(
+ wifi_ap[wutils.WifiEnums.SSID_KEY] == \
+ self.network[wutils.WifiEnums.SSID_KEY],
+ "Configured wifi hotspot SSID did not match with the expected SSID")
+ 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.AP_BAND_KEY] = WIFI_CONFIG_APBAND_2G
+ self._save_wifi_softap_configuration(dut, config)
+
+ # start wifi tethering with new wifi ap configuration
+ wutils.stop_wifi_tethering(dut)
+ self._turn_on_wifi_hotspot(dut)
+
+ # verify dut can connect to new wifi ap configuration
+ new_network = {wutils.WifiEnums.SSID_KEY: self.new_ssid,
+ wutils.WifiEnums.PWD_KEY: \
+ self.network[wutils.WifiEnums.PWD_KEY]}
+ 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):
+ """ Steps:
+
+ 1. Start wifi tethering with open network 2G band
+ (Not allowed manually. b/72412729)
+ 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,
+ 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):
+ """ Steps:
+
+ 1. Start wifi tethering with open network 5G band
+ (Not allowed manually. b/72412729)
+ 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,
+ 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):
+ """ Steps:
+
+ 1. Save wifi hotspot configuration with open network 2G band
+ (Not allowed manually. b/72412729)
+ 2. Turn on wifi hotspot
+ 3. Connect tethered device and verify internet connectivity
+ 4. Turn off wifi hotspot
+ 5. Repeat steps 2 to 4
+ """
+ # save open network wifi ap configuration with 2G band
+ config = {wutils.WifiEnums.SSID_KEY:
+ self.open_network[wutils.WifiEnums.SSID_KEY]}
+ 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
+ for _ in range(9):
+ self._turn_on_wifi_hotspot(self.hotspot_device)
+ wutils.connect_to_wifi_network(self.tethered_devices[0], self.open_network)
+ wutils.stop_wifi_tethering(self.hotspot_device)
+ time.sleep(1) # wait for some time before turning on hotspot
+
+ @test_tracker_info(uuid="c7ef840c-4003-41fc-80e3-755f9057b542")
+ def test_wifi_tethering_open_network_5g_stress(self):
+ """ Steps:
+
+ 1. Save wifi hotspot configuration with open network 5G band
+ (Not allowed manually. b/72412729)
+ 2. Turn on wifi hotspot
+ 3. Connect tethered device and verify internet connectivity
+ 4. Turn off wifi hotspot
+ 5. Repeat steps 2 to 4
+ """
+ # save open network wifi ap configuration with 5G band
+ config = {wutils.WifiEnums.SSID_KEY:
+ self.open_network[wutils.WifiEnums.SSID_KEY]}
+ 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
+ for _ in range(9):
+ self._turn_on_wifi_hotspot(self.hotspot_device)
+ wutils.connect_to_wifi_network(self.tethered_devices[0], self.open_network)
+ wutils.stop_wifi_tethering(self.hotspot_device)
+ time.sleep(1) # wait for some time before turning on hotspot
diff --git a/acts_tests/tests/google/wifi/WifiThroughputStabilityTest.py b/acts_tests/tests/google/wifi/WifiThroughputStabilityTest.py
new file mode 100644
index 0000000..71ed011
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiThroughputStabilityTest.py
@@ -0,0 +1,627 @@
+#!/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 collections
+import itertools
+import json
+import logging
+import numpy
+import os
+import time
+from acts import asserts
+from acts import base_test
+from acts import context
+from acts import utils
+from acts.controllers import iperf_server as ipf
+from acts.controllers.utils_lib import ssh
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
+from acts.test_utils.wifi import ota_chamber
+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 acts.test_utils.wifi import wifi_test_utils as wutils
+from functools import partial
+
+TEST_TIMEOUT = 10
+SHORT_SLEEP = 1
+MED_SLEEP = 6
+
+
+class WifiThroughputStabilityTest(base_test.BaseTestClass):
+ """Class to test WiFi throughput stability.
+
+ This class tests throughput stability and identifies cases where throughput
+ fluctuates over time. The class setups up the AP, configures and connects
+ the phone, and runs iperf throughput test at several attenuations For an
+ example config file to run this test class see
+ example_connectivity_performance_ap_sta.json.
+ """
+ def __init__(self, controllers):
+ base_test.BaseTestClass.__init__(self, controllers)
+ # Define metrics to be uploaded to BlackBox
+ self.testcase_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_case())
+ self.testclass_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_class())
+ self.publish_testcase_metrics = True
+ # Generate test cases
+ self.tests = self.generate_test_cases([6, 36, 149],
+ ['VHT20', 'VHT40', 'VHT80'],
+ ['TCP', 'UDP'], ['DL', 'UL'],
+ ['high', 'low'])
+
+ def generate_test_cases(self, channels, modes, traffic_types,
+ traffic_directions, signal_levels):
+ """Function that auto-generates test cases for a test class."""
+ allowed_configs = {
+ 'VHT20': [
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 149, 153,
+ 157, 161
+ ],
+ 'VHT40': [36, 44, 149, 157],
+ 'VHT80': [36, 149]
+ }
+ test_cases = []
+ for channel, mode, signal_level, traffic_type, traffic_direction in itertools.product(
+ channels,
+ modes,
+ signal_levels,
+ traffic_types,
+ traffic_directions,
+ ):
+ if channel not in allowed_configs[mode]:
+ continue
+ testcase_params = collections.OrderedDict(
+ channel=channel,
+ mode=mode,
+ traffic_type=traffic_type,
+ traffic_direction=traffic_direction,
+ signal_level=signal_level)
+ testcase_name = ('test_tput_stability'
+ '_{}_{}_{}_ch{}_{}'.format(
+ signal_level, traffic_type, traffic_direction,
+ channel, mode))
+ setattr(self, testcase_name,
+ partial(self._test_throughput_stability, testcase_params))
+ test_cases.append(testcase_name)
+ return test_cases
+
+ def setup_class(self):
+ self.dut = self.android_devices[0]
+ req_params = [
+ 'throughput_stability_test_params', 'testbed_params',
+ 'main_network', 'RetailAccessPoints', 'RemoteServer'
+ ]
+ self.unpack_userparams(req_params)
+ self.testclass_params = self.throughput_stability_test_params
+ self.num_atten = self.attenuators[0].instrument.num_atten
+ self.remote_server = ssh.connection.SshConnection(
+ ssh.settings.from_config(self.RemoteServer[0]['ssh_config']))
+ self.iperf_server = self.iperf_servers[0]
+ self.iperf_client = self.iperf_clients[0]
+ self.access_point = retail_ap.create(self.RetailAccessPoints)[0]
+ self.log_path = os.path.join(logging.log_path, 'test_results')
+ os.makedirs(self.log_path, exist_ok=True)
+ self.log.info('Access Point Configuration: {}'.format(
+ self.access_point.ap_settings))
+ self.ref_attenuations = {}
+ self.testclass_results = []
+
+ # 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)
+
+ def teardown_test(self):
+ self.iperf_server.stop()
+
+ def pass_fail_check(self, test_result_dict):
+ """Check the test result and decide if it passed or failed.
+
+ Checks the throughput stability test's PASS/FAIL criteria based on
+ minimum instantaneous throughput, and standard deviation.
+
+ Args:
+ test_result_dict: dict containing attenuation, throughput and other
+ meta data
+ """
+ avg_throughput = test_result_dict['iperf_results']['avg_throughput']
+ min_throughput = test_result_dict['iperf_results']['min_throughput']
+ std_dev_percent = (
+ test_result_dict['iperf_results']['std_deviation'] /
+ test_result_dict['iperf_results']['avg_throughput']) * 100
+ # Set blackbox metrics
+ if self.publish_testcase_metrics:
+ self.testcase_metric_logger.add_metric('avg_throughput',
+ avg_throughput)
+ self.testcase_metric_logger.add_metric('min_throughput',
+ min_throughput)
+ self.testcase_metric_logger.add_metric('std_dev_percent',
+ std_dev_percent)
+ # Evaluate pass/fail
+ min_throughput_check = (
+ (min_throughput / avg_throughput) *
+ 100) > self.testclass_params['min_throughput_threshold']
+ std_deviation_check = std_dev_percent < self.testclass_params[
+ 'std_deviation_threshold']
+
+ test_message = (
+ 'Atten: {0:.2f}dB, RSSI: {1:.2f}dB. '
+ 'Throughput (Mean: {2:.2f}, Std. Dev:{3:.2f}%, Min: {4:.2f} Mbps).'
+ 'LLStats : {5}'.format(test_result_dict['attenuation'],
+ test_result_dict['rssi'], avg_throughput,
+ std_dev_percent, min_throughput,
+ test_result_dict['llstats']))
+ if min_throughput_check and std_deviation_check:
+ asserts.explicit_pass('Test Passed.' + test_message)
+ asserts.fail('Test Failed. ' + test_message)
+
+ def post_process_results(self, test_result):
+ """Extracts results and saves plots and JSON formatted results.
+
+ Args:
+ test_result: dict containing attenuation, iPerfResult object and
+ other meta data
+ Returns:
+ test_result_dict: dict containing post-processed results including
+ avg throughput, other metrics, and other meta data
+ """
+ # Save output as text file
+ test_name = self.current_test_name
+ results_file_path = os.path.join(self.log_path,
+ '{}.txt'.format(test_name))
+ test_result_dict = {}
+ test_result_dict['ap_settings'] = test_result['ap_settings'].copy()
+ test_result_dict['attenuation'] = test_result['attenuation']
+ test_result_dict['rssi'] = test_result['rssi_result'][
+ 'signal_poll_rssi']['mean']
+ test_result_dict['llstats'] = (
+ 'TX MCS = {0} ({1:.1f}%). '
+ 'RX MCS = {2} ({3:.1f}%)'.format(
+ test_result['llstats']['summary']['common_tx_mcs'],
+ test_result['llstats']['summary']['common_tx_mcs_freq'] * 100,
+ test_result['llstats']['summary']['common_rx_mcs'],
+ test_result['llstats']['summary']['common_rx_mcs_freq'] * 100))
+ if test_result['iperf_result'].instantaneous_rates:
+ instantaneous_rates_Mbps = [
+ rate * 8 * (1.024**2)
+ for rate in test_result['iperf_result'].instantaneous_rates[
+ self.testclass_params['iperf_ignored_interval']:-1]
+ ]
+ tput_standard_deviation = test_result[
+ 'iperf_result'].get_std_deviation(
+ self.testclass_params['iperf_ignored_interval']) * 8
+ else:
+ instantaneous_rates_Mbps = float('nan')
+ tput_standard_deviation = float('nan')
+ test_result_dict['iperf_results'] = {
+ 'instantaneous_rates': instantaneous_rates_Mbps,
+ 'avg_throughput': numpy.mean(instantaneous_rates_Mbps),
+ 'std_deviation': tput_standard_deviation,
+ 'min_throughput': min(instantaneous_rates_Mbps)
+ }
+ with open(results_file_path, 'w') as results_file:
+ json.dump(test_result_dict, results_file)
+ # Plot and save
+ figure = wputils.BokehFigure(test_name,
+ x_label='Time (s)',
+ primary_y_label='Throughput (Mbps)')
+ time_data = list(range(0, len(instantaneous_rates_Mbps)))
+ figure.add_line(time_data,
+ instantaneous_rates_Mbps,
+ legend=self.current_test_name,
+ marker='circle')
+ output_file_path = os.path.join(self.log_path,
+ '{}.html'.format(test_name))
+ figure.generate_figure(output_file_path)
+ return test_result_dict
+
+ def setup_ap(self, testcase_params):
+ """Sets up the access point in the configuration required by the test.
+
+ Args:
+ testcase_params: dict containing AP and other test params
+ """
+ band = self.access_point.band_lookup_by_channel(
+ testcase_params['channel'])
+ if '2G' in band:
+ frequency = wutils.WifiEnums.channel_2G_to_freq[
+ testcase_params['channel']]
+ else:
+ frequency = wutils.WifiEnums.channel_5G_to_freq[
+ testcase_params['channel']]
+ if frequency in wutils.WifiEnums.DFS_5G_FREQUENCIES:
+ self.access_point.set_region(self.testbed_params['DFS_region'])
+ else:
+ self.access_point.set_region(self.testbed_params['default_region'])
+ self.access_point.set_channel(band, testcase_params['channel'])
+ self.access_point.set_bandwidth(band, testcase_params['mode'])
+ self.log.info('Access Point Configuration: {}'.format(
+ self.access_point.ap_settings))
+
+ def setup_dut(self, testcase_params):
+ """Sets up the DUT in the configuration required by the test.
+
+ Args:
+ testcase_params: dict containing AP and other test params
+ """
+ # Check battery level before test
+ if not wputils.health_check(self.dut, 10):
+ asserts.skip('Battery level too low. Skipping test.')
+ # Turn screen off to preserve battery
+ self.dut.go_to_sleep()
+ band = self.access_point.band_lookup_by_channel(
+ testcase_params['channel'])
+ if wputils.validate_network(self.dut,
+ testcase_params['test_network']['SSID']):
+ self.log.info('Already connected to desired network')
+ else:
+ wutils.wifi_toggle_state(self.dut, True)
+ wutils.reset_wifi(self.dut)
+ wutils.set_wifi_country_code(self.dut,
+ self.testclass_params['country_code'])
+ self.main_network[band]['channel'] = testcase_params['channel']
+ wutils.wifi_connect(self.dut,
+ testcase_params['test_network'],
+ num_of_tries=5,
+ check_connectivity=False)
+ self.dut_ip = self.dut.droid.connectivityGetIPv4Addresses('wlan0')[0]
+
+ def setup_throughput_stability_test(self, testcase_params):
+ """Function that gets devices ready for the test.
+
+ Args:
+ testcase_params: dict containing test-specific parameters
+ """
+ # Configure AP
+ self.setup_ap(testcase_params)
+ # Reset, configure, and connect DUT
+ self.setup_dut(testcase_params)
+ # Wait before running the first wifi test
+ first_test_delay = self.testclass_params.get('first_test_delay', 600)
+ if first_test_delay > 0 and len(self.testclass_results) == 0:
+ self.log.info('Waiting before the first test.')
+ time.sleep(first_test_delay)
+ self.setup_dut(testcase_params)
+ # Get and set attenuation levels for test
+ testcase_params['atten_level'] = self.get_target_atten(testcase_params)
+ self.log.info('Setting attenuation to {} dB'.format(
+ testcase_params['atten_level']))
+ for attenuator in self.attenuators:
+ attenuator.set_atten(testcase_params['atten_level'])
+ # Configure iperf
+ if isinstance(self.iperf_server, ipf.IPerfServerOverAdb):
+ testcase_params['iperf_server_address'] = self.dut_ip
+ else:
+ testcase_params[
+ 'iperf_server_address'] = wputils.get_server_address(
+ self.remote_server, self.dut_ip, '255.255.255.0')
+
+ def run_throughput_stability_test(self, testcase_params):
+ """Main function to test throughput stability.
+
+ The function sets up the AP in the correct channel and mode
+ configuration and runs an iperf test to measure throughput.
+
+ Args:
+ testcase_params: dict containing test specific parameters
+ Returns:
+ test_result: dict containing test result and meta data
+ """
+ # Run test and log result
+ # Start iperf session
+ self.log.info('Starting iperf test.')
+ llstats_obj = wputils.LinkLayerStats(self.dut)
+ llstats_obj.update_stats()
+ self.iperf_server.start(tag=str(testcase_params['atten_level']))
+ current_rssi = wputils.get_connected_rssi_nb(
+ dut=self.dut,
+ num_measurements=self.testclass_params['iperf_duration'] - 1,
+ polling_frequency=1,
+ first_measurement_delay=1,
+ disconnect_warning=1,
+ ignore_samples=1)
+ client_output_path = self.iperf_client.start(
+ testcase_params['iperf_server_address'],
+ testcase_params['iperf_args'], str(testcase_params['atten_level']),
+ self.testclass_params['iperf_duration'] + TEST_TIMEOUT)
+ current_rssi = current_rssi.result()
+ server_output_path = self.iperf_server.stop()
+ # Set attenuator to 0 dB
+ for attenuator in self.attenuators:
+ attenuator.set_atten(0)
+ # Parse and log result
+ if testcase_params['use_client_output']:
+ iperf_file = client_output_path
+ else:
+ iperf_file = server_output_path
+ try:
+ iperf_result = ipf.IPerfResult(iperf_file)
+ except:
+ asserts.fail('Cannot get iperf result.')
+ llstats_obj.update_stats()
+ curr_llstats = llstats_obj.llstats_incremental.copy()
+ test_result = collections.OrderedDict()
+ test_result['testcase_params'] = testcase_params.copy()
+ test_result['ap_settings'] = self.access_point.ap_settings.copy()
+ test_result['attenuation'] = testcase_params['atten_level']
+ test_result['iperf_result'] = iperf_result
+ test_result['rssi_result'] = current_rssi
+ test_result['llstats'] = curr_llstats
+ self.testclass_results.append(test_result)
+ return test_result
+
+ def get_target_atten(self, testcase_params):
+ """Function gets attenuation used for test
+
+ The function fetches the attenuation at which the test should be
+ performed.
+
+ Args:
+ testcase_params: dict containing test specific parameters
+ Returns:
+ test_atten: target attenuation for test
+ """
+ # Get attenuation from reference test if it has been run
+ ref_test_fields = ['channel', 'mode', 'signal_level']
+ test_id = wputils.extract_sub_dict(testcase_params, ref_test_fields)
+ test_id = tuple(test_id.items())
+ if test_id in self.ref_attenuations:
+ return self.ref_attenuations[test_id]
+
+ # Get attenuation for target RSSI
+ if testcase_params['signal_level'] == 'low':
+ target_rssi = self.testclass_params['low_throughput_target']
+ else:
+ target_rssi = self.testclass_params['high_throughput_target']
+ target_atten = wputils.get_atten_for_target_rssi(
+ target_rssi, self.attenuators, self.dut, self.remote_server)
+
+ self.ref_attenuations[test_id] = target_atten
+ return self.ref_attenuations[test_id]
+
+ def compile_test_params(self, testcase_params):
+ """Function that completes setting the test case parameters."""
+ 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'
+ and isinstance(self.iperf_server, ipf.IPerfServerOverAdb)):
+ testcase_params['iperf_args'] = wputils.get_iperf_arg_string(
+ duration=self.testclass_params['iperf_duration'],
+ reverse_direction=1,
+ 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'],
+ socket_size=testcase_params['iperf_socket_size'],
+ num_processes=testcase_params['iperf_processes'])
+ testcase_params['use_client_output'] = False
+
+ return testcase_params
+
+ def _test_throughput_stability(self, testcase_params):
+ """ Function that gets called for each test case
+
+ The function gets called in each test case. The function customizes
+ the test based on the test name of the test that called it
+
+ Args:
+ testcase_params: dict containing test specific parameters
+ """
+ testcase_params = self.compile_test_params(testcase_params)
+ self.setup_throughput_stability_test(testcase_params)
+ test_result = self.run_throughput_stability_test(testcase_params)
+ test_result_postprocessed = self.post_process_results(test_result)
+ self.pass_fail_check(test_result_postprocessed)
+
+
+# Over-the air version of ping tests
+class WifiOtaThroughputStabilityTest(WifiThroughputStabilityTest):
+ """Class to test over-the-air ping
+
+ This class tests WiFi ping performance in an OTA chamber. It enables
+ setting turntable orientation and other chamber parameters to study
+ performance in varying channel conditions
+ """
+ def __init__(self, controllers):
+ base_test.BaseTestClass.__init__(self, controllers)
+ # Define metrics to be uploaded to BlackBox
+ self.testcase_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_case())
+ self.testclass_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_class())
+ self.publish_testcase_metrics = False
+
+ def setup_class(self):
+ WifiThroughputStabilityTest.setup_class(self)
+ self.ota_chamber = ota_chamber.create(
+ self.user_params['OTAChamber'])[0]
+
+ def teardown_class(self):
+ self.ota_chamber.reset_chamber()
+ self.process_testclass_results()
+
+ def extract_test_id(self, testcase_params, id_fields):
+ test_id = collections.OrderedDict(
+ (param, testcase_params[param]) for param in id_fields)
+ return test_id
+
+ def process_testclass_results(self):
+ """Saves all test results to enable comparison."""
+ testclass_data = collections.OrderedDict()
+ for test in self.testclass_results:
+ current_params = test['testcase_params']
+ channel_data = testclass_data.setdefault(current_params['channel'],
+ collections.OrderedDict())
+ test_id = tuple(
+ self.extract_test_id(current_params, [
+ 'mode', 'traffic_type', 'traffic_direction', 'signal_level'
+ ]).items())
+ test_data = channel_data.setdefault(
+ test_id, collections.OrderedDict(position=[], throughput=[]))
+ current_throughput = (numpy.mean(
+ test['iperf_result'].instantaneous_rates[
+ self.testclass_params['iperf_ignored_interval']:-1])
+ ) * 8 * (1.024**2)
+ test_data['position'].append(current_params['position'])
+ test_data['throughput'].append(current_throughput)
+
+ chamber_mode = self.testclass_results[0]['testcase_params'][
+ 'chamber_mode']
+ if chamber_mode == 'orientation':
+ x_label = 'Angle (deg)'
+ elif chamber_mode == 'stepped stirrers':
+ x_label = 'Position Index'
+
+ # Publish test class metrics
+ for channel, channel_data in testclass_data.items():
+ for test_id, test_data in channel_data.items():
+ test_id_dict = dict(test_id)
+ metric_tag = 'ota_summary_{}_{}_{}_ch{}_{}'.format(
+ test_id_dict['signal_level'], test_id_dict['traffic_type'],
+ test_id_dict['traffic_direction'], channel,
+ test_id_dict['mode'])
+ metric_name = metric_tag + '.avg_throughput'
+ metric_value = numpy.mean(test_data['throughput'])
+ self.testclass_metric_logger.add_metric(
+ metric_name, metric_value)
+ metric_name = metric_tag + '.min_throughput'
+ metric_value = min(test_data['throughput'])
+ self.testclass_metric_logger.add_metric(
+ metric_name, metric_value)
+
+ # Plot test class results
+ plots = []
+ for channel, channel_data in testclass_data.items():
+ current_plot = wputils.BokehFigure(
+ title='Channel {} - Rate vs. Position'.format(channel),
+ x_label=x_label,
+ primary_y_label='Rate (Mbps)',
+ )
+ for test_id, test_data in channel_data.items():
+ test_id_dict = dict(test_id)
+ legend = '{}, {} {}, {} RSSI'.format(
+ test_id_dict['mode'], test_id_dict['traffic_type'],
+ test_id_dict['traffic_direction'],
+ test_id_dict['signal_level'])
+ current_plot.add_line(test_data['position'],
+ test_data['throughput'], legend)
+ current_plot.generate_figure()
+ plots.append(current_plot)
+ current_context = context.get_current_context().get_full_output_path()
+ plot_file_path = os.path.join(current_context, 'results.html')
+ wputils.BokehFigure.save_figures(plots, plot_file_path)
+
+ def setup_throughput_stability_test(self, testcase_params):
+ WifiThroughputStabilityTest.setup_throughput_stability_test(
+ self, testcase_params)
+ # Setup turntable
+ if testcase_params['chamber_mode'] == 'orientation':
+ self.ota_chamber.set_orientation(testcase_params['position'])
+ elif testcase_params['chamber_mode'] == 'stepped stirrers':
+ self.ota_chamber.step_stirrers(testcase_params['total_positions'])
+
+ def get_target_atten(self, testcase_params):
+ if testcase_params['signal_level'] == 'high':
+ test_atten = self.testclass_params['default_atten_levels'][0]
+ elif testcase_params['signal_level'] == 'low':
+ test_atten = self.testclass_params['default_atten_levels'][1]
+ return test_atten
+
+ def generate_test_cases(self, channels, modes, traffic_types,
+ traffic_directions, signal_levels, chamber_mode,
+ positions):
+ allowed_configs = {
+ 'VHT20': [
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 149, 153,
+ 157, 161
+ ],
+ 'VHT40': [36, 44, 149, 157],
+ 'VHT80': [36, 149]
+ }
+ test_cases = []
+ for channel, mode, position, traffic_type, signal_level, traffic_direction in itertools.product(
+ channels, modes, positions, traffic_types, signal_levels,
+ traffic_directions):
+ if channel not in allowed_configs[mode]:
+ continue
+ testcase_params = collections.OrderedDict(
+ channel=channel,
+ mode=mode,
+ traffic_type=traffic_type,
+ traffic_direction=traffic_direction,
+ signal_level=signal_level,
+ chamber_mode=chamber_mode,
+ total_positions=len(positions),
+ position=position)
+ testcase_name = ('test_tput_stability'
+ '_{}_{}_{}_ch{}_{}_pos{}'.format(
+ signal_level, traffic_type, traffic_direction,
+ channel, mode, position))
+ setattr(self, testcase_name,
+ partial(self._test_throughput_stability, testcase_params))
+ test_cases.append(testcase_name)
+ return test_cases
+
+
+class WifiOtaThroughputStability_TenDegree_Test(WifiOtaThroughputStabilityTest
+ ):
+ def __init__(self, controllers):
+ WifiOtaThroughputStabilityTest.__init__(self, controllers)
+ self.tests = self.generate_test_cases([6, 36, 149], ['VHT20', 'VHT80'],
+ ['TCP'], ['DL', 'UL'],
+ ['high', 'low'], 'orientation',
+ list(range(0, 360, 10)))
+
+
+class WifiOtaThroughputStability_45Degree_Test(WifiOtaThroughputStabilityTest):
+ def __init__(self, controllers):
+ WifiOtaThroughputStabilityTest.__init__(self, controllers)
+ self.tests = self.generate_test_cases([6, 36, 149], ['VHT20', 'VHT80'],
+ ['TCP'], ['DL', 'UL'],
+ ['high', 'low'], 'orientation',
+ list(range(0, 360, 45)))
+
+
+class WifiOtaThroughputStability_SteppedStirrers_Test(
+ WifiOtaThroughputStabilityTest):
+ def __init__(self, controllers):
+ WifiOtaThroughputStabilityTest.__init__(self, controllers)
+ self.tests = self.generate_test_cases([6, 36, 149], ['VHT20', 'VHT80'],
+ ['TCP'], ['DL', 'UL'],
+ ['high', 'low'],
+ 'stepped stirrers',
+ list(range(100)))
\ No newline at end of file
diff --git a/acts_tests/tests/google/wifi/WifiWakeTest.py b/acts_tests/tests/google/wifi/WifiWakeTest.py
new file mode 100644
index 0000000..52ab2fd
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiWakeTest.py
@@ -0,0 +1,421 @@
+#!/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 time
+import queue
+
+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
+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
+DISCONNECT_TIMEOUT_SEC = 20
+
+
+class WifiWakeTest(WifiBaseTest):
+ """
+ Tests Wifi Wake.
+
+ Test Bed Requirements:
+ * One Android Device
+ * Two APs that can be turned on and off
+ """
+ def __init__(self, configs):
+ super().__init__(configs)
+ self.enable_packet_log = True
+
+ def setup_class(self):
+ super().setup_class()
+
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ # turn location back on
+ acts.utils.set_location_service(self.dut, True)
+ self.dut.droid.wifiScannerToggleAlwaysAvailable(True)
+
+ self.unpack_userparams(req_param_names=[],
+ opt_param_names=["reference_networks"])
+
+ 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"]
+ self.ap_b = self.reference_networks[1]["2g"]
+
+ 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)
+ self.log.info('Turned AP B on')
+
+ def setup_test(self):
+ super().setup_test()
+ self.dut.droid.wakeLockAcquireBright()
+ self.dut.droid.wakeUpNow()
+ self.ap_a_on()
+ self.ap_b_on()
+ self.ap_a_atten.set_atten(0)
+ self.ap_b_atten.set_atten(0)
+ wutils.reset_wifi(self.dut)
+ wutils.wifi_toggle_state(self.dut, new_state=True)
+ # clear events from event dispatcher
+ self.dut.droid.wifiStartTrackingStateChange()
+ self.dut.droid.wifiStopTrackingStateChange()
+ self.dut.ed.clear_all_events()
+
+ def teardown_test(self):
+ super().teardown_test()
+ self.dut.droid.wakeLockRelease()
+ self.dut.droid.goToSleepNow()
+
+ 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,
+ "reportEvents": wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN
+ }
+
+ wifi_chs = wutils.WifiChannelUS(self.dut.model)
+ stime_channel = 47 # dwell time plus 2ms
+ leeway = 10
+
+ for i in range(num_times):
+ self.log.info("Scan count: {}".format(i))
+ data = wutils.start_wifi_single_scan(self.dut, scan_settings)
+ idx = data["Index"]
+ scan_rt = data["ScanElapsedRealtime"]
+ self.log.debug(
+ "Wifi single shot scan started index: %s at real time: %s", idx,
+ scan_rt)
+ # generating event wait time from scan setting plus leeway
+ scan_time, scan_channels = wutils.get_scan_time_and_channels(
+ wifi_chs, scan_settings, stime_channel)
+ wait_time = int(scan_time / 1000) + leeway
+ # track number of result received
+ result_received = 0
+ try:
+ for _ in range(1, 3):
+ event_name = "{}{}onResults".format("WifiScannerScan", idx)
+ self.log.debug("Waiting for event: %s for time %s",
+ event_name, wait_time)
+ 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,
+ "Event did not triggered for single shot {}".format(error))
+ finally:
+ self.dut.droid.wifiScannerStopScan(idx)
+ # For single shot number of result received and length of result
+ # should be one
+ asserts.assert_true(
+ result_received == 1,
+ "Test fail because received result {}".format(
+ result_received))
+
+ @test_tracker_info(uuid="372b9b74-4241-46ce-8f18-e6a97d3a3452")
+ def test_no_reconnect_manual_disable_wifi(self):
+ """
+ Tests that Wifi Wake does not reconnect to a network if the user turned
+ off Wifi while connected to that network and the user has not moved
+ (i.e. moved out of range of the AP then came back).
+ """
+ wutils.wifi_connect(self.dut, self.ap_a, num_of_tries=5)
+ wutils.wifi_toggle_state(self.dut, new_state=False)
+ time.sleep(PRESCAN_DELAY_SEC)
+ self.do_location_scan(
+ 2 * CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2)
+ asserts.assert_false(
+ self.dut.droid.wifiCheckState(),
+ "Expect Wifi Wake to not enable Wifi, but Wifi was enabled.")
+
+ @test_tracker_info(uuid="ec7a54a5-f293-43f5-a1dd-d41679aa1825")
+ def test_reconnect_wifi_saved_network(self):
+ """Tests that Wifi Wake re-enables Wifi for a saved network."""
+ wutils.wifi_connect(self.dut, self.ap_a, num_of_tries=5)
+ wutils.wifi_connect(self.dut, self.ap_b, num_of_tries=5)
+ self.dut.ed.clear_all_events()
+ self.ap_a_off()
+ self.ap_b_off()
+ wutils.wait_for_disconnect(self.dut, DISCONNECT_TIMEOUT_SEC)
+ self.log.info("Wifi Disconnected")
+ self.do_location_scan(2)
+ time.sleep(LAST_DISCONNECT_TIMEOUT_SEC * 1.2)
+ wutils.wifi_toggle_state(self.dut, new_state=False)
+ time.sleep(PRESCAN_DELAY_SEC)
+ self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2)
+
+ self.ap_a_on()
+ 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="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")
+ asserts.assert_true(
+ self.dut.droid.wifiAddNetworkSuggestions([self.ap_a]),
+ "Failed to add suggestions")
+ asserts.assert_true(
+ 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.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()
+ 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, DISCONNECT_TIMEOUT_SEC)
+ self.log.info("Wifi Disconnected")
+ self.do_location_scan(2)
+ time.sleep(LAST_DISCONNECT_TIMEOUT_SEC * 1.2)
+ wutils.wifi_toggle_state(self.dut, new_state=False)
+ time.sleep(PRESCAN_DELAY_SEC)
+ self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2)
+
+ self.ap_a_on()
+ 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="6c77ca9b-ff34-4bc7-895f-cc7340e0e645")
+ def test_reconnect_wifi_move_back_in_range(self):
+ """
+ Tests that Wifi Wake re-enables Wifi if the device moves out of range of
+ the AP then came back.
+ """
+ wutils.wifi_connect(self.dut, self.ap_a, num_of_tries=5)
+ wutils.wifi_toggle_state(self.dut, new_state=False)
+ time.sleep(PRESCAN_DELAY_SEC)
+ # init Wakeup Lock with AP A
+ self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2)
+ self.ap_a_off()
+ # 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(
+ 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="08e8284a-a523-48f3-b9ea-9c6bf27d711e")
+ def test_no_reconnect_to_flaky_ap(self):
+ """
+ Tests that Wifi Wake does not reconnect to flaky networks.
+ If a network sporadically connects and disconnects, and the user turns
+ off Wifi even during the disconnected phase, Wifi Wake should not
+ re-enable Wifi for that network.
+ """
+ wutils.wifi_connect(self.dut, self.ap_a, num_of_tries=5)
+ self.ap_a_off()
+ time.sleep(LAST_DISCONNECT_TIMEOUT_SEC * 0.4)
+ wutils.wifi_toggle_state(self.dut, new_state=False)
+ time.sleep(PRESCAN_DELAY_SEC)
+ self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2)
+ self.ap_a_on()
+ self.do_location_scan(
+ 2 * CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2)
+ asserts.assert_false(
+ self.dut.droid.wifiCheckState(),
+ "Expect Wifi Wake to not enable Wifi, but Wifi was enabled.")
+
+ @test_tracker_info(uuid="b990a8f7-e3a0-4774-89cf-2067ccd64903")
+ def test_reconnect_wifi_disabled_after_disconnecting(self):
+ """
+ Tests that Wifi Wake reconnects to a network if Wifi was disabled long
+ after disconnecting from a network.
+ """
+ wutils.wifi_connect(self.dut, self.ap_a, num_of_tries=5)
+ self.dut.ed.clear_all_events()
+ self.ap_a_off()
+ wutils.wait_for_disconnect(self.dut, DISCONNECT_TIMEOUT_SEC)
+ self.log.info("Wifi Disconnected")
+ self.do_location_scan(2)
+ time.sleep(LAST_DISCONNECT_TIMEOUT_SEC * 1.2)
+ wutils.wifi_toggle_state(self.dut, new_state=False)
+ time.sleep(PRESCAN_DELAY_SEC)
+ self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2)
+ self.ap_a_on()
+ 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="bb217794-d3ee-4fb9-87ff-7a594d0223b0")
+ def test_no_reconnect_if_exists_ap_in_wakeup_lock(self):
+ """
+ 2 APs in Wakeup Lock, user moves out of range of one AP but stays in
+ range of the other, should not reconnect when user moves back in range
+ of both.
+ """
+ wutils.wifi_connect(self.dut, self.ap_a, num_of_tries=5)
+ wutils.wifi_connect(self.dut, self.ap_b, num_of_tries=5)
+ wutils.wifi_toggle_state(self.dut, new_state=False)
+ time.sleep(PRESCAN_DELAY_SEC)
+ self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2)
+ self.ap_b_off()
+ self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2)
+ self.ap_b_on()
+ self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2)
+ asserts.assert_false(
+ self.dut.droid.wifiCheckState(),
+ "Expect Wifi Wake to not enable Wifi, but Wifi was enabled.")
+
+ @test_tracker_info(uuid="567a0663-4ce0-488d-8fe2-db79a3ebf068")
+ def test_reconnect_if_both_ap_evicted_from_wakeup_lock(self):
+ """
+ 2 APs in Wakeup Lock, user moves out of range of both APs, should
+ reconnect when user moves back in range of either AP.
+ """
+ wutils.wifi_connect(self.dut, self.ap_a, num_of_tries=5)
+ wutils.wifi_connect(self.dut, self.ap_b, num_of_tries=5)
+ wutils.wifi_toggle_state(self.dut, new_state=False)
+ time.sleep(PRESCAN_DELAY_SEC)
+ self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2)
+ self.ap_a_off()
+ self.ap_b_off()
+ self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2)
+ self.ap_a_on()
+ 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="d67657c8-3de3-46a6-a103-428cdab89423")
+ def test_reconnect_to_better_saved_network(self):
+ """
+ 2 saved APs, one attenuated, one unattenuated, Wifi Wake should connect
+ to the unattenuated AP
+ """
+ wutils.wifi_connect(self.dut, self.ap_a, num_of_tries=5)
+ wutils.wifi_connect(self.dut, self.ap_b, num_of_tries=5)
+ self.dut.ed.clear_all_events()
+ self.ap_a_off()
+ self.ap_b_off()
+ wutils.wait_for_disconnect(self.dut, DISCONNECT_TIMEOUT_SEC)
+ self.log.info("Wifi Disconnected")
+ self.do_location_scan(2)
+ time.sleep(LAST_DISCONNECT_TIMEOUT_SEC * 1.2)
+ wutils.wifi_toggle_state(self.dut, new_state=False)
+ time.sleep(PRESCAN_DELAY_SEC)
+ self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2)
+
+ self.ap_a_on()
+ self.ap_b_on()
+ self.ap_a_atten.set_atten(30)
+ self.ap_b_atten.set_atten(0)
+
+ 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]
+ wutils.wait_for_connect(self.dut, expected_ssid)
diff --git a/acts_tests/tests/google/wifi/WifiWpa3EnterpriseTest.py b/acts_tests/tests/google/wifi/WifiWpa3EnterpriseTest.py
new file mode 100644
index 0000000..5257766
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiWpa3EnterpriseTest.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python3.4
+#
+# 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 asserts
+from acts.test_decorators import test_tracker_info
+import acts.test_utils.wifi.wifi_test_utils as wutils
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+WifiEnums = wutils.WifiEnums
+
+EAP = WifiEnums.Eap
+Ent = WifiEnums.Enterprise
+WPA3_SECURITY = "SUITE_B_192"
+
+
+class WifiWpa3EnterpriseTest(WifiBaseTest):
+ """Tests for WPA3 Enterprise."""
+
+ def setup_class(self):
+ super().setup_class()
+
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ req_params = [
+ "ec2_ca_cert", "ec2_client_cert", "ec2_client_key", "rsa3072_ca_cert",
+ "rsa3072_client_cert", "rsa3072_client_key", "wpa3_ec2_network",
+ "wpa3_rsa3072_network"
+ ]
+ self.unpack_userparams(req_param_names=req_params,)
+
+ def setup_test(self):
+ super().setup_test()
+ for ad in self.android_devices:
+ ad.droid.wakeLockAcquireBright()
+ ad.droid.wakeUpNow()
+ wutils.wifi_toggle_state(self.dut, True)
+
+ def teardown_test(self):
+ super().teardown_test()
+ for ad in self.android_devices:
+ ad.droid.wakeLockRelease()
+ ad.droid.goToSleepNow()
+ wutils.reset_wifi(self.dut)
+
+ ### Tests ###
+
+ @test_tracker_info(uuid="404c6165-6e23-4ec1-bc2c-9dfdd5c7dc87")
+ def test_connect_to_wpa3_enterprise_ec2(self):
+ asserts.skip_if(
+ self.dut.build_info["build_id"].startswith("R"),
+ "No SL4A support for EC certs in R builds. Skipping this testcase")
+ config = {
+ Ent.EAP: int(EAP.TLS),
+ Ent.CA_CERT: self.ec2_ca_cert,
+ WifiEnums.SSID_KEY: self.wpa3_ec2_network[WifiEnums.SSID_KEY],
+ Ent.CLIENT_CERT: self.ec2_client_cert,
+ Ent.PRIVATE_KEY_ID: self.ec2_client_key,
+ WifiEnums.SECURITY: WPA3_SECURITY,
+ "identity": self.wpa3_ec2_network["identity"],
+ "domain_suffix_match": self.wpa3_ec2_network["domain"],
+ "cert_algo": self.wpa3_ec2_network["cert_algo"]
+ }
+ wutils.connect_to_wifi_network(self.dut, config)
+
+ @test_tracker_info(uuid="b6d22585-f7c1-418d-bd4b-b627af8c228c")
+ def test_connect_to_wpa3_enterprise_rsa3072(self):
+ config = {
+ Ent.EAP: int(EAP.TLS),
+ Ent.CA_CERT: self.rsa3072_ca_cert,
+ WifiEnums.SSID_KEY: self.wpa3_rsa3072_network[WifiEnums.SSID_KEY],
+ Ent.CLIENT_CERT: self.rsa3072_client_cert,
+ Ent.PRIVATE_KEY_ID: self.rsa3072_client_key,
+ WifiEnums.SECURITY: WPA3_SECURITY,
+ "identity": self.wpa3_rsa3072_network["identity"],
+ "domain_suffix_match": self.wpa3_rsa3072_network["domain"]
+ }
+ wutils.connect_to_wifi_network(self.dut, config)
diff --git a/acts_tests/tests/google/wifi/WifiWpa3OweTest.py b/acts_tests/tests/google/wifi/WifiWpa3OweTest.py
new file mode 100644
index 0000000..a3c70f3
--- /dev/null
+++ b/acts_tests/tests/google/wifi/WifiWpa3OweTest.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python3.4
+#
+# 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 acts.test_utils.wifi.wifi_test_utils as wutils
+
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+
+class WifiWpa3OweTest(WifiBaseTest):
+ """Tests for APIs in Android's WifiManager class.
+
+ Test Bed Requirement:
+ * At least one Android device and atleast two Access Points.
+ * Several Wi-Fi networks visible to the device.
+ """
+ def __init__(self, configs):
+ super().__init__(configs)
+ self.enable_packet_log = True
+
+ def setup_class(self):
+ super().setup_class()
+
+ self.dut = self.android_devices[0]
+ wutils.wifi_test_device_init(self.dut)
+ req_params = ["owe_networks", "sae_networks"]
+ self.unpack_userparams(req_param_names=req_params,)
+ wutils.wifi_toggle_state(self.dut, True)
+ if "OpenWrtAP" in self.user_params:
+ self.configure_openwrt_ap_and_start(owe_network=True,
+ sae_network=True)
+ self.owe_2g = self.owe_networks[0]["2g"]
+ self.owe_5g = self.owe_networks[0]["5g"]
+ self.wpa3_personal_2g = self.sae_networks[0]["2g"]
+ self.wpa3_personal_5g = self.sae_networks[0]["5g"]
+
+ def setup_test(self):
+ super().setup_test()
+ for ad in self.android_devices:
+ ad.droid.wakeLockAcquireBright()
+ ad.droid.wakeUpNow()
+ wutils.wifi_toggle_state(ad, True)
+
+ def teardown_test(self):
+ super().teardown_test()
+ for ad in self.android_devices:
+ ad.droid.wakeLockRelease()
+ ad.droid.goToSleepNow()
+ wutils.reset_wifi(self.dut)
+
+ ### Test cases ###
+
+ @test_tracker_info(uuid="a7755f1f-5740-4d45-8c29-3711172b1bd7")
+ def test_connect_to_owe_2g(self):
+ wutils.connect_to_wifi_network(self.dut, self.owe_2g)
+
+ @test_tracker_info(uuid="9977765e-03da-4614-ab96-4c1597101118")
+ def test_connect_to_owe_5g(self):
+ wutils.connect_to_wifi_network(self.dut, self.owe_5g)
+
+ @test_tracker_info(uuid="3670702a-3d78-4184-b5e1-7fcf5fa48fd8")
+ def test_connect_to_wpa3_personal_2g(self):
+ wutils.connect_to_wifi_network(self.dut, self.wpa3_personal_2g)
+
+ @test_tracker_info(uuid="c4528eaf-7960-4ecd-8f11-d5439bdf1c58")
+ def test_connect_to_wpa3_personal_5g(self):
+ 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.connect_to_wifi_network(self.dut, self.wpa3_personal_2g)
+ wutils.toggle_wifi_off_and_on(self.dut)
+ 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 0000000..e69de29
--- /dev/null
+++ b/acts_tests/tests/google/wifi/__init__.py
diff --git a/acts_tests/tests/google/wifi/aware/README.md b/acts_tests/tests/google/wifi/aware/README.md
new file mode 100644
index 0000000..6b96914
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/README.md
@@ -0,0 +1,50 @@
+# Wi-Fi Aware Integrated (ACTS/sl4a) Test Suite
+
+This directory contains ACTS/sl4a test scripts to verify and characterize
+the Wi-Fi Aware implementation in Android.
+
+There are 4 groups of tests (in 4 sub-directories):
+
+* functional: Functional tests that each implementation must pass. These
+are pass/fail tests.
+* performance: Tests which measure performance of an implementation - e.g.
+latency or throughput. Some of the tests may not have pass/fail results -
+they just record the measured performance. Even when tests do have a pass/
+fail criteria - that criteria may not apply to all implementations.
+* stress: Tests which run through a large number of iterations to stress
+test the implementation. Considering that some failures are expected,
+especially in an over-the-air situation, pass/fail criteria are either
+not provided or may not apply to all implementations or test environments.
+* ota (over-the-air): A small number of tests which configure the device
+in a particular mode and expect the tester to capture an over-the-air
+sniffer trace and analyze it for validity. These tests are **not** automated.
+
+The tests can be executed in several ways:
+
+1. Individual test(s): `act.py -c <config> -tc {<test_class>|<test_class>:<test_name>}`
+
+Where a test file is any of the `.py` files in any of the test sub-directories.
+If a test class is specified, then all tests within that test class are executed.
+
+2. All tests in a test group: `act.py -c <config> -tf <test_file>`
+
+Where `<test_file>` is a file containing a list of tests. Each of the test
+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`
+
+## Test Configurations
+The test configurations, the `<config>` in the commands above, are stored in
+the *config* sub-directory. The configurations simply use all connected
+devices without listing specific serial numbers. Note that some tests use a
+single device while others use 2 devices. In addition, the configurations
+define the following key to configure the test:
+
+* **aware_default_power_mode**: The power mode in which to run all tests. Options
+are `INTERACTIVE` and `NON_INTERACTIVE`.
+
+The following configurations are provided:
+* wifi_aware.json: Normal (high power/interactive) test mode.
+* wifi_aware_non_interactive.json: Low power (non-interactive) test mode.
diff --git a/acts_tests/tests/google/wifi/aware/config/wifi_aware.json b/acts_tests/tests/google/wifi/aware/config/wifi_aware.json
new file mode 100644
index 0000000..8f1252e
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/config/wifi_aware.json
@@ -0,0 +1,16 @@
+{
+ "_description": "This is a test configuration file for Wi-Fi Aware tests in INTERACTIVE (high-power) mode.",
+ "testbed":
+ [
+ {
+ "_description": "Wi-Fi Aware testbed: auto-detect all attached devices",
+ "name": "WifiAwareAllAttached",
+ "AndroidDevice": "*"
+ }
+ ],
+ "logpath": "~/logs",
+ "testpaths": ["./tools/test/connectivity/acts_tests/tests/google/wifi"],
+ "dbs_supported_models": "*",
+ "adb_logcat_param": "-b all",
+ "aware_default_power_mode": "INTERACTIVE"
+}
diff --git a/acts_tests/tests/google/wifi/aware/config/wifi_aware_non_interactive.json b/acts_tests/tests/google/wifi/aware/config/wifi_aware_non_interactive.json
new file mode 100644
index 0000000..23dd1bb
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/config/wifi_aware_non_interactive.json
@@ -0,0 +1,16 @@
+{
+ "_description": "This is a test configuration file for Wi-Fi Aware tests in NON-INTERACTIVE (low-power) mode.",
+ "testbed":
+ [
+ {
+ "_description": "Wi-Fi Aware testbed: auto-detect all attached devices",
+ "name": "WifiAwareAllAttached",
+ "AndroidDevice": "*"
+ }
+ ],
+ "logpath": "~/logs",
+ "testpaths": ["./tools/test/connectivity/acts_tests/tests/google/wifi"],
+ "dbs_supported_models": "*",
+ "adb_logcat_param": "-b all",
+ "aware_default_power_mode": "NON_INTERACTIVE"
+}
diff --git a/acts_tests/tests/google/wifi/aware/functional/AttachTest.py b/acts_tests/tests/google/wifi/aware/functional/AttachTest.py
new file mode 100644
index 0000000..0df82a1
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/functional/AttachTest.py
@@ -0,0 +1,161 @@
+#!/usr/bin/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 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.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
+
+
+class AttachTest(AwareBaseTest):
+ @test_tracker_info(uuid="cdafd1e0-bcf5-4fe8-ae32-f55483db9925")
+ def test_attach(self):
+ """Functional test case / Attach test cases / attach
+
+ Validates that attaching to the Wi-Fi Aware service works (receive
+ the expected callback).
+ """
+ dut = self.android_devices[0]
+ dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
+ autils.fail_on_event(dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+
+ @test_tracker_info(uuid="82f2a8bc-a62b-49c2-ac8a-fe8460010ba2")
+ def test_attach_with_identity(self):
+ """Functional test case / Attach test cases / attach with identity callback
+
+ Validates that attaching to the Wi-Fi Aware service works (receive
+ the expected callbacks).
+ """
+ dut = self.android_devices[0]
+ dut.droid.wifiAwareAttach(True)
+ autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
+ autils.wait_for_event(dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+
+ @test_tracker_info(uuid="d2714d14-f330-47d4-b8e9-ee4d5e5b7ea0")
+ def test_attach_multiple_sessions(self):
+ """Functional test case / Attach test cases / multiple attach sessions
+
+ Validates that when creating multiple attach sessions each can be
+ configured independently as to whether or not to receive an identity
+ callback.
+ """
+ dut = self.android_devices[0]
+
+ # Create 3 attach sessions: 2 without identity callback, 1 with
+ id1 = dut.droid.wifiAwareAttach(False, None, True)
+ time.sleep(10) # to make sure all calls and callbacks are done
+ id2 = dut.droid.wifiAwareAttach(True, None, True)
+ time.sleep(10) # to make sure all calls and callbacks are done
+ id3 = dut.droid.wifiAwareAttach(False, None, True)
+ dut.log.info('id1=%d, id2=%d, id3=%d', id1, id2, id3)
+
+ # Attach session 1: wait for attach, should not get identity
+ autils.wait_for_event(
+ dut, autils.decorate_event(aconsts.EVENT_CB_ON_ATTACHED, id1))
+ autils.fail_on_event(
+ dut,
+ autils.decorate_event(aconsts.EVENT_CB_ON_IDENTITY_CHANGED, id1))
+
+ # Attach session 2: wait for attach and for identity callback
+ autils.wait_for_event(
+ dut, autils.decorate_event(aconsts.EVENT_CB_ON_ATTACHED, id2))
+ autils.wait_for_event(
+ dut,
+ autils.decorate_event(aconsts.EVENT_CB_ON_IDENTITY_CHANGED, id2))
+
+ # Attach session 3: wait for attach, should not get identity
+ autils.wait_for_event(
+ dut, autils.decorate_event(aconsts.EVENT_CB_ON_ATTACHED, id3))
+ autils.fail_on_event(
+ dut,
+ autils.decorate_event(aconsts.EVENT_CB_ON_IDENTITY_CHANGED, id3))
+
+ @test_tracker_info(uuid="b8ea4d02-ae23-42a7-a85e-def52932c858")
+ def test_attach_with_no_wifi(self):
+ """Function test case / Attach test cases / attempt to attach with wifi off
+
+ Validates that if trying to attach with Wi-Fi disabled will receive the
+ expected failure callback. As a side-effect also validates that the
+ broadcast for Aware unavailable is received.
+ """
+ dut = self.android_devices[0]
+ wutils.wifi_toggle_state(dut, False)
+ autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_NOT_AVAILABLE)
+ dut.droid.wifiAwareAttach()
+ autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACH_FAILED)
+
+ @test_tracker_info(uuid="7dcc4530-c936-4447-9d22-a7c5b315e2ce")
+ def test_attach_with_doze(self):
+ """Function test case / Attach test cases / attempt to attach with doze on
+
+ Validates that if trying to attach with device in doze mode will receive the
+ expected failure callback. As a side-effect also validates that the
+ broadcast for Aware unavailable is received.
+ """
+ dut = self.android_devices[0]
+ asserts.assert_true(utils.enable_doze(dut), "Can't enable doze")
+ autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_NOT_AVAILABLE)
+ dut.droid.wifiAwareAttach()
+ autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACH_FAILED)
+ asserts.assert_true(utils.disable_doze(dut), "Can't disable doze")
+ autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_AVAILABLE)
+
+ @test_tracker_info(uuid="2574fd01-8974-4dd0-aeb8-a7194461140e")
+ def test_attach_with_location_off(self):
+ """Function test case / Attach test cases / attempt to attach with location
+ mode off.
+
+ Validates that if trying to attach with device location mode off will
+ receive the expected failure callback. As a side-effect also validates that
+ the broadcast for Aware unavailable is received.
+ """
+ dut = self.android_devices[0]
+ utils.set_location_service(dut, False)
+ autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_NOT_AVAILABLE)
+ dut.droid.wifiAwareAttach()
+ autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACH_FAILED)
+ utils.set_location_service(dut, True)
+ autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_AVAILABLE)
+
+ @test_tracker_info(uuid="7ffde8e7-a010-4b77-97f5-959f263b5249")
+ def test_attach_apm_toggle_attach_again(self):
+ """Validates that enabling Airplane mode while Aware is on resets it
+ correctly, and allows it to be re-enabled when Airplane mode is then
+ disabled."""
+ dut = self.android_devices[0]
+
+ # enable Aware (attach)
+ dut.droid.wifiAwareAttach()
+ autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # enable airplane mode
+ utils.force_airplane_mode(dut, True)
+ autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_NOT_AVAILABLE)
+
+ # wait a few seconds and disable airplane mode
+ time.sleep(10)
+ utils.force_airplane_mode(dut, False)
+ autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_AVAILABLE)
+
+ # try enabling Aware again (attach)
+ dut.droid.wifiAwareAttach()
+ autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
diff --git a/acts_tests/tests/google/wifi/aware/functional/CapabilitiesTest.py b/acts_tests/tests/google/wifi/aware/functional/CapabilitiesTest.py
new file mode 100644
index 0000000..3bed77f
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/functional/CapabilitiesTest.py
@@ -0,0 +1,275 @@
+#!/usr/bin/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.
+
+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.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
+
+
+class CapabilitiesTest(AwareBaseTest):
+ """Set of tests for Wi-Fi Aware Capabilities - verifying that the provided
+ capabilities are real (i.e. available)."""
+
+ SERVICE_NAME = "GoogleTestXYZ"
+
+ def create_config(self, dtype, service_name):
+ """Create a discovery configuration based on input parameters.
+
+ Args:
+ dtype: Publish or Subscribe discovery type
+ service_name: Service name.
+
+ Returns:
+ Discovery configuration object.
+ """
+ config = {}
+ config[aconsts.DISCOVERY_KEY_DISCOVERY_TYPE] = dtype
+ config[aconsts.DISCOVERY_KEY_SERVICE_NAME] = service_name
+ return config
+
+ def start_discovery_session(self, dut, session_id, is_publish, dtype,
+ service_name, expect_success):
+ """Start a discovery session
+
+ Args:
+ dut: Device under test
+ session_id: ID of the Aware session in which to start discovery
+ is_publish: True for a publish session, False for subscribe session
+ dtype: Type of the discovery session
+ service_name: Service name to use for the discovery session
+ expect_success: True if expect session to be created, False otherwise
+
+ Returns:
+ Discovery session ID.
+ """
+ config = {}
+ config[aconsts.DISCOVERY_KEY_DISCOVERY_TYPE] = dtype
+ config[aconsts.DISCOVERY_KEY_SERVICE_NAME] = service_name
+
+ if is_publish:
+ disc_id = dut.droid.wifiAwarePublish(session_id, config)
+ event_name = aconsts.SESSION_CB_ON_PUBLISH_STARTED
+ else:
+ disc_id = dut.droid.wifiAwareSubscribe(session_id, config)
+ event_name = aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED
+
+ if expect_success:
+ autils.wait_for_event(dut, event_name)
+ else:
+ autils.wait_for_event(dut,
+ aconsts.SESSION_CB_ON_SESSION_CONFIG_FAILED)
+
+ return disc_id
+
+ ###############################
+
+ @test_tracker_info(uuid="45da8a41-6c02-4434-9eb9-aa0a36ff9f65")
+ def test_max_discovery_sessions(self):
+ """Validate that the device can create as many discovery sessions as are
+ indicated in the device capabilities
+ """
+ dut = self.android_devices[0]
+
+ # attach
+ session_id = dut.droid.wifiAwareAttach(True)
+ autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ service_name_template = 'GoogleTestService-%s-%d'
+
+ # start the max number of publish sessions
+ for i in range(dut.aware_capabilities[aconsts.CAP_MAX_PUBLISHES]):
+ # create publish discovery session of both types
+ pub_disc_id = self.start_discovery_session(
+ dut, session_id, True, aconsts.PUBLISH_TYPE_UNSOLICITED
+ if i % 2 == 0 else aconsts.PUBLISH_TYPE_SOLICITED,
+ service_name_template % ('pub', i), True)
+
+ # start the max number of subscribe sessions
+ for i in range(dut.aware_capabilities[aconsts.CAP_MAX_SUBSCRIBES]):
+ # create publish discovery session of both types
+ sub_disc_id = self.start_discovery_session(
+ dut, session_id, False, aconsts.SUBSCRIBE_TYPE_PASSIVE
+ if i % 2 == 0 else aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ service_name_template % ('sub', i), True)
+
+ # start another publish & subscribe and expect failure
+ self.start_discovery_session(
+ dut, session_id, True, aconsts.PUBLISH_TYPE_UNSOLICITED,
+ service_name_template % ('pub', 900), False)
+ self.start_discovery_session(
+ dut, session_id, False, aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ service_name_template % ('pub', 901), False)
+
+ # delete one of the publishes and try again (see if can create subscribe
+ # instead - should not)
+ dut.droid.wifiAwareDestroyDiscoverySession(pub_disc_id)
+ self.start_discovery_session(
+ dut, session_id, False, aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ service_name_template % ('pub', 902), False)
+ self.start_discovery_session(
+ dut, session_id, True, aconsts.PUBLISH_TYPE_UNSOLICITED,
+ service_name_template % ('pub', 903), True)
+
+ # delete one of the subscribes and try again (see if can create publish
+ # instead - should not)
+ dut.droid.wifiAwareDestroyDiscoverySession(sub_disc_id)
+ self.start_discovery_session(
+ dut, session_id, True, aconsts.PUBLISH_TYPE_UNSOLICITED,
+ service_name_template % ('pub', 904), False)
+ self.start_discovery_session(
+ dut, session_id, False, aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ service_name_template % ('pub', 905), True)
+
+ def test_max_ndp(self):
+ """Validate that the device can create as many NDPs as are specified
+ by its capabilities.
+
+ Mechanics:
+ - Publisher on DUT (first device)
+ - Subscribers on all other devices
+ - On discovery set up NDP
+
+ Note: the test requires MAX_NDP + 2 devices to be validated. If these are
+ not available the test will fail.
+ """
+ dut = self.android_devices[0]
+
+ # get max NDP: using first available device (assumes all devices are the
+ # same)
+ max_ndp = dut.aware_capabilities[aconsts.CAP_MAX_NDP_SESSIONS]
+
+ # get number of attached devices: needs to be max_ndp+2 to allow for max_ndp
+ # NDPs + an additional one expected to fail.
+ # However, will run the test with max_ndp+1 devices to verify that at least
+ # that many NDPs can be created. Will still fail at the end to indicate that
+ # full test was not run.
+ num_peer_devices = min(len(self.android_devices) - 1, max_ndp + 1)
+ asserts.assert_true(
+ num_peer_devices >= max_ndp,
+ 'A minimum of %d devices is needed to run the test, have %d' %
+ (max_ndp + 1, len(self.android_devices)))
+
+ # attach
+ session_id = dut.droid.wifiAwareAttach()
+ autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # start publisher
+ p_disc_id = self.start_discovery_session(
+ dut,
+ session_id,
+ is_publish=True,
+ dtype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ service_name=self.SERVICE_NAME,
+ expect_success=True)
+
+ # loop over other DUTs
+ for i in range(num_peer_devices):
+ other_dut = self.android_devices[i + 1]
+
+ # attach
+ other_session_id = other_dut.droid.wifiAwareAttach()
+ autils.wait_for_event(other_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # start subscriber
+ s_disc_id = self.start_discovery_session(
+ other_dut,
+ other_session_id,
+ is_publish=False,
+ dtype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ service_name=self.SERVICE_NAME,
+ expect_success=True)
+
+ discovery_event = autils.wait_for_event(
+ other_dut, aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+ peer_id_on_sub = discovery_event['data'][
+ aconsts.SESSION_CB_KEY_PEER_ID]
+
+ # Subscriber: send message to peer (Publisher - so it knows our address)
+ other_dut.droid.wifiAwareSendMessage(s_disc_id, peer_id_on_sub,
+ self.get_next_msg_id(),
+ "ping",
+ aconsts.MAX_TX_RETRIES)
+ autils.wait_for_event(other_dut,
+ aconsts.SESSION_CB_ON_MESSAGE_SENT)
+
+ # Publisher: wait for received message
+ pub_rx_msg_event = autils.wait_for_event(
+ dut, aconsts.SESSION_CB_ON_MESSAGE_RECEIVED)
+ peer_id_on_pub = pub_rx_msg_event['data'][
+ aconsts.SESSION_CB_KEY_PEER_ID]
+
+ # publisher (responder): request network
+ p_req_key = autils.request_network(
+ dut,
+ dut.droid.wifiAwareCreateNetworkSpecifier(
+ p_disc_id, peer_id_on_pub))
+
+ # subscriber (initiator): request network
+ s_req_key = autils.request_network(
+ other_dut,
+ other_dut.droid.wifiAwareCreateNetworkSpecifier(
+ s_disc_id, peer_id_on_sub))
+
+ # wait for network (or not - on the last iteration)
+ if i != max_ndp:
+ p_net_event = autils.wait_for_event_with_keys(
+ dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+ s_net_event = autils.wait_for_event_with_keys(
+ other_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+
+ p_aware_if = p_net_event['data'][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ s_aware_if = s_net_event['data'][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ self.log.info('Interface names: p=%s, s=%s', p_aware_if,
+ s_aware_if)
+
+ p_ipv6 = dut.droid.connectivityGetLinkLocalIpv6Address(
+ p_aware_if).split('%')[0]
+ s_ipv6 = other_dut.droid.connectivityGetLinkLocalIpv6Address(
+ s_aware_if).split('%')[0]
+ self.log.info('Interface addresses (IPv6): p=%s, s=%s', p_ipv6,
+ s_ipv6)
+ else:
+ autils.fail_on_event_with_keys(
+ dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+ autils.fail_on_event_with_keys(
+ other_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+
+ asserts.assert_true(
+ num_peer_devices > max_ndp,
+ 'Needed %d devices to run the test, have %d' %
+ (max_ndp + 2, len(self.android_devices)))
diff --git a/acts_tests/tests/google/wifi/aware/functional/DataPathTest.py b/acts_tests/tests/google/wifi/aware/functional/DataPathTest.py
new file mode 100644
index 0000000..b8ac693
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/functional/DataPathTest.py
@@ -0,0 +1,2298 @@
+#!/usr/bin/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 time
+
+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
+
+
+class DataPathTest(AwareBaseTest):
+ """Set of tests for Wi-Fi Aware data-path."""
+
+ # configuration parameters used by tests
+ ENCR_TYPE_OPEN = 0
+ ENCR_TYPE_PASSPHRASE = 1
+ ENCR_TYPE_PMK = 2
+
+ PASSPHRASE = "This is some random passphrase - very very secure!!"
+ PASSPHRASE_MIN = "01234567"
+ PASSPHRASE_MAX = "012345678901234567890123456789012345678901234567890123456789012"
+ PMK = "ODU0YjE3YzdmNDJiNWI4NTQ2NDJjNDI3M2VkZTQyZGU="
+ PASSPHRASE2 = "This is some random passphrase - very very secure - but diff!!"
+ PMK2 = "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="
+
+ PING_MSG = "ping"
+
+ # message re-transmit counter (increases reliability in open-environment)
+ # Note: reliability of message transmission is tested elsewhere
+ MSG_RETX_COUNT = 5 # hard-coded max value, internal API
+
+ # number of second to 'reasonably' wait to make sure that devices synchronize
+ # with each other - useful for OOB test cases, where the OOB discovery would
+ # take some time
+ WAIT_FOR_CLUSTER = 5
+
+ def create_config(self, dtype):
+ """Create a base configuration based on input parameters.
+
+ Args:
+ dtype: Publish or Subscribe discovery type
+
+ Returns:
+ Discovery configuration object.
+ """
+ config = {}
+ config[aconsts.DISCOVERY_KEY_DISCOVERY_TYPE] = dtype
+ config[
+ aconsts.DISCOVERY_KEY_SERVICE_NAME] = "GoogleTestServiceDataPath"
+ return config
+
+ def request_network(self, dut, ns):
+ """Request a Wi-Fi Aware network.
+
+ Args:
+ dut: Device
+ ns: Network specifier
+ Returns: the request key
+ """
+ network_req = {"TransportType": 5, "NetworkSpecifier": ns}
+ return dut.droid.connectivityRequestWifiAwareNetwork(network_req)
+
+ def set_up_discovery(self,
+ ptype,
+ stype,
+ get_peer_id,
+ pub_on_both=False,
+ pub_on_both_same=True):
+ """Set up discovery sessions and wait for service discovery.
+
+ Args:
+ ptype: Publish discovery type
+ stype: Subscribe discovery type
+ get_peer_id: Send a message across to get the peer's id
+ pub_on_both: If True then set up a publisher on both devices. The second
+ publisher isn't used (existing to test use-case).
+ pub_on_both_same: If True then the second publish uses an identical
+ service name, otherwise a different service name.
+ """
+ p_dut = self.android_devices[0]
+ p_dut.pretty_name = "Publisher"
+ s_dut = self.android_devices[1]
+ s_dut.pretty_name = "Subscriber"
+
+ # Publisher+Subscriber: attach and wait for confirmation
+ p_id = p_dut.droid.wifiAwareAttach()
+ autils.wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(self.device_startup_offset)
+ s_id = s_dut.droid.wifiAwareAttach()
+ autils.wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # Publisher: start publish and wait for confirmation
+ p_disc_id = p_dut.droid.wifiAwarePublish(p_id,
+ self.create_config(ptype))
+ autils.wait_for_event(p_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+
+ # Optionally set up a publish session on the Subscriber device
+ if pub_on_both:
+ p2_config = self.create_config(ptype)
+ if not pub_on_both_same:
+ p2_config[aconsts.DISCOVERY_KEY_SERVICE_NAME] = (
+ p2_config[aconsts.DISCOVERY_KEY_SERVICE_NAME] + "-XYZXYZ")
+ s_dut.droid.wifiAwarePublish(s_id, p2_config)
+ autils.wait_for_event(s_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+
+ # Subscriber: start subscribe and wait for confirmation
+ s_disc_id = s_dut.droid.wifiAwareSubscribe(s_id,
+ self.create_config(stype))
+ autils.wait_for_event(s_dut, aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED)
+
+ # Subscriber: wait for service discovery
+ discovery_event = autils.wait_for_event(
+ s_dut, aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+ peer_id_on_sub = discovery_event["data"][
+ aconsts.SESSION_CB_KEY_PEER_ID]
+
+ peer_id_on_pub = None
+ if get_peer_id: # only need message to receive peer ID
+ # Subscriber: send message to peer (Publisher - so it knows our address)
+ s_dut.droid.wifiAwareSendMessage(s_disc_id, peer_id_on_sub,
+ self.get_next_msg_id(),
+ self.PING_MSG,
+ self.MSG_RETX_COUNT)
+ autils.wait_for_event(s_dut, aconsts.SESSION_CB_ON_MESSAGE_SENT)
+
+ # Publisher: wait for received message
+ pub_rx_msg_event = autils.wait_for_event(
+ p_dut, aconsts.SESSION_CB_ON_MESSAGE_RECEIVED)
+ peer_id_on_pub = pub_rx_msg_event["data"][
+ aconsts.SESSION_CB_KEY_PEER_ID]
+
+ return (p_dut, s_dut, p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub,
+ peer_id_on_pub)
+
+ def run_ib_data_path_test(self,
+ ptype,
+ stype,
+ encr_type,
+ use_peer_id,
+ passphrase_to_use=None,
+ pub_on_both=False,
+ pub_on_both_same=True,
+ expect_failure=False):
+ """Runs the in-band data-path tests.
+
+ Args:
+ ptype: Publish discovery type
+ stype: Subscribe discovery type
+ encr_type: Encryption type, one of ENCR_TYPE_*
+ use_peer_id: On Responder (publisher): True to use peer ID, False to
+ accept any request
+ passphrase_to_use: The passphrase to use if encr_type=ENCR_TYPE_PASSPHRASE
+ If None then use self.PASSPHRASE
+ pub_on_both: If True then set up a publisher on both devices. The second
+ publisher isn't used (existing to test use-case).
+ pub_on_both_same: If True then the second publish uses an identical
+ service name, otherwise a different service name.
+ expect_failure: If True then don't expect NDP formation, otherwise expect
+ NDP setup to succeed.
+ """
+ (p_dut, s_dut, p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub,
+ peer_id_on_pub) = self.set_up_discovery(
+ ptype,
+ stype,
+ use_peer_id,
+ pub_on_both=pub_on_both,
+ pub_on_both_same=pub_on_both_same)
+
+ passphrase = None
+ pmk = None
+ if encr_type == self.ENCR_TYPE_PASSPHRASE:
+ passphrase = (self.PASSPHRASE
+ if passphrase_to_use == None else passphrase_to_use)
+ elif encr_type == self.ENCR_TYPE_PMK:
+ pmk = self.PMK
+
+ port = 1234
+ transport_protocol = 6 # TCP/IP
+
+ # Publisher: request network
+ if encr_type == self.ENCR_TYPE_OPEN:
+ p_req_key = self.request_network(
+ p_dut,
+ p_dut.droid.wifiAwareCreateNetworkSpecifier(
+ p_disc_id, peer_id_on_pub
+ if use_peer_id else None, passphrase, pmk))
+ else:
+ p_req_key = self.request_network(
+ p_dut,
+ p_dut.droid.wifiAwareCreateNetworkSpecifier(
+ p_disc_id, peer_id_on_pub if use_peer_id else None,
+ passphrase, pmk, port, transport_protocol))
+
+ # Subscriber: request network
+ s_req_key = self.request_network(
+ s_dut,
+ s_dut.droid.wifiAwareCreateNetworkSpecifier(
+ s_disc_id, peer_id_on_sub, passphrase, pmk))
+
+ if expect_failure:
+ # Publisher & Subscriber: expect unavailable callbacks
+ autils.wait_for_event_with_keys(
+ p_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT, cconsts.NETWORK_CB_UNAVAILABLE))
+ autils.wait_for_event_with_keys(
+ s_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT, cconsts.NETWORK_CB_UNAVAILABLE))
+ else:
+ # Publisher & Subscriber: wait for network formation
+ p_net_event_nc = autils.wait_for_event_with_keys(
+ p_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+ s_net_event_nc = autils.wait_for_event_with_keys(
+ s_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+
+ # validate no leak of information
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in p_net_event_nc[
+ "data"], "Network specifier leak!")
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in s_net_event_nc[
+ "data"], "Network specifier leak!")
+
+ # note that Pub <-> Sub since IPv6 are of peer's!
+ s_ipv6 = p_net_event_nc["data"][aconsts.NET_CAP_IPV6]
+ p_ipv6 = s_net_event_nc["data"][aconsts.NET_CAP_IPV6]
+
+ self.verify_network_info(
+ p_net_event_nc["data"], s_net_event_nc["data"],
+ encr_type == self.ENCR_TYPE_OPEN, port, transport_protocol)
+
+ p_net_event_lp = autils.wait_for_event_with_keys(
+ p_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+ s_net_event_lp = autils.wait_for_event_with_keys(
+ s_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+
+ p_aware_if = p_net_event_lp["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ s_aware_if = s_net_event_lp["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+
+ 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)
+
+ # open sockets to test connection
+ asserts.assert_true(
+ autils.verify_socket_connect(p_dut, s_dut, p_ipv6, s_ipv6, 0),
+ "Failed socket link with Pub as Server")
+ asserts.assert_true(
+ autils.verify_socket_connect(s_dut, p_dut, s_ipv6, p_ipv6, 0),
+ "Failed socket link with Sub as Server")
+
+ # terminate sessions and wait for ON_LOST callbacks
+ p_dut.droid.wifiAwareDestroy(p_id)
+ s_dut.droid.wifiAwareDestroy(s_id)
+
+ autils.wait_for_event_with_keys(
+ p_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT, cconsts.NETWORK_CB_LOST),
+ (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+ autils.wait_for_event_with_keys(
+ s_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT, cconsts.NETWORK_CB_LOST),
+ (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+
+ # clean-up
+ p_dut.droid.connectivityUnregisterNetworkCallback(p_req_key)
+ s_dut.droid.connectivityUnregisterNetworkCallback(s_req_key)
+
+ def run_oob_data_path_test(self,
+ encr_type,
+ use_peer_id,
+ setup_discovery_sessions=False,
+ expect_failure=False):
+ """Runs the out-of-band data-path tests.
+
+ Args:
+ encr_type: Encryption type, one of ENCR_TYPE_*
+ use_peer_id: On Responder: True to use peer ID, False to accept any
+ request
+ setup_discovery_sessions: If True also set up a (spurious) discovery
+ session (pub on both sides, sub on Responder side). Validates a corner
+ case.
+ expect_failure: If True then don't expect NDP formation, otherwise expect
+ NDP setup to succeed.
+ """
+ init_dut = self.android_devices[0]
+ init_dut.pretty_name = "Initiator"
+ resp_dut = self.android_devices[1]
+ resp_dut.pretty_name = "Responder"
+
+ # Initiator+Responder: attach and wait for confirmation & identity
+ init_id = init_dut.droid.wifiAwareAttach(True)
+ autils.wait_for_event(init_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ init_ident_event = autils.wait_for_event(
+ init_dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+ init_mac = init_ident_event["data"]["mac"]
+ time.sleep(self.device_startup_offset)
+ resp_id = resp_dut.droid.wifiAwareAttach(True)
+ autils.wait_for_event(resp_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ resp_ident_event = autils.wait_for_event(
+ resp_dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+ resp_mac = resp_ident_event["data"]["mac"]
+
+ # wait for for devices to synchronize with each other - there are no other
+ # mechanisms to make sure this happens for OOB discovery (except retrying
+ # to execute the data-path request)
+ time.sleep(self.WAIT_FOR_CLUSTER)
+
+ if setup_discovery_sessions:
+ init_dut.droid.wifiAwarePublish(
+ init_id, self.create_config(aconsts.PUBLISH_TYPE_UNSOLICITED))
+ autils.wait_for_event(init_dut,
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+ resp_dut.droid.wifiAwarePublish(
+ resp_id, self.create_config(aconsts.PUBLISH_TYPE_UNSOLICITED))
+ autils.wait_for_event(resp_dut,
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+ resp_dut.droid.wifiAwareSubscribe(
+ resp_id, self.create_config(aconsts.SUBSCRIBE_TYPE_PASSIVE))
+ autils.wait_for_event(resp_dut,
+ aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED)
+ autils.wait_for_event(resp_dut,
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+
+ passphrase = None
+ pmk = None
+ if encr_type == self.ENCR_TYPE_PASSPHRASE:
+ passphrase = self.PASSPHRASE
+ elif encr_type == self.ENCR_TYPE_PMK:
+ pmk = self.PMK
+
+ # Responder: request network
+ resp_req_key = self.request_network(
+ resp_dut,
+ resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ resp_id, aconsts.DATA_PATH_RESPONDER, init_mac
+ if use_peer_id else None, passphrase, pmk))
+
+ # Initiator: request network
+ init_req_key = self.request_network(
+ init_dut,
+ init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ init_id, aconsts.DATA_PATH_INITIATOR, resp_mac, passphrase,
+ pmk))
+
+ if expect_failure:
+ # Initiator & Responder: expect unavailable callbacks
+ 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))
+ 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))
+ else:
+ # Initiator & Responder: wait for network formation
+ init_net_event_nc = autils.wait_for_event_with_keys(
+ init_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, init_req_key))
+ resp_net_event_nc = autils.wait_for_event_with_keys(
+ resp_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
+
+ # validate no leak of information
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in init_net_event_nc[
+ "data"], "Network specifier leak!")
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in resp_net_event_nc[
+ "data"], "Network specifier leak!")
+
+ # note that Init <-> Resp since IPv6 are of peer's!
+ init_ipv6 = resp_net_event_nc["data"][aconsts.NET_CAP_IPV6]
+ resp_ipv6 = init_net_event_nc["data"][aconsts.NET_CAP_IPV6]
+
+ init_net_event_lp = autils.wait_for_event_with_keys(
+ init_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, init_req_key))
+ resp_net_event_lp = autils.wait_for_event_with_keys(
+ resp_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
+
+ init_aware_if = init_net_event_lp["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ resp_aware_if = resp_net_event_lp["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+
+ self.log.info("Interface names: I=%s, R=%s", init_aware_if,
+ resp_aware_if)
+ self.log.info("Interface addresses (IPv6): I=%s, R=%s", init_ipv6,
+ resp_ipv6)
+
+ # open sockets to test connection
+ asserts.assert_true(
+ autils.verify_socket_connect(init_dut, resp_dut, init_ipv6,
+ resp_ipv6, 0),
+ "Failed socket link with Initiator as Server")
+ asserts.assert_true(
+ autils.verify_socket_connect(resp_dut, init_dut, resp_ipv6,
+ init_ipv6, 0),
+ "Failed socket link with Responder as Server")
+
+ # terminate sessions and wait for ON_LOST callbacks
+ init_dut.droid.wifiAwareDestroy(init_id)
+ resp_dut.droid.wifiAwareDestroy(resp_id)
+
+ autils.wait_for_event_with_keys(
+ init_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT, cconsts.NETWORK_CB_LOST),
+ (cconsts.NETWORK_CB_KEY_ID, init_req_key))
+ autils.wait_for_event_with_keys(
+ resp_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT, cconsts.NETWORK_CB_LOST),
+ (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
+
+ # clean-up
+ resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key)
+ init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key)
+
+ def run_mismatched_ib_data_path_test(self, pub_mismatch, sub_mismatch):
+ """Runs the negative in-band data-path tests: mismatched peer ID.
+
+ Args:
+ pub_mismatch: Mismatch the publisher's ID
+ sub_mismatch: Mismatch the subscriber's ID
+ """
+ (p_dut, s_dut, p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub,
+ peer_id_on_pub) = self.set_up_discovery(
+ aconsts.PUBLISH_TYPE_UNSOLICITED, aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ True)
+
+ if pub_mismatch:
+ peer_id_on_pub = peer_id_on_pub - 1
+ if sub_mismatch:
+ peer_id_on_sub = peer_id_on_sub - 1
+
+ # Publisher: request network
+ p_req_key = self.request_network(
+ p_dut,
+ p_dut.droid.wifiAwareCreateNetworkSpecifier(
+ p_disc_id, peer_id_on_pub, None))
+
+ # Subscriber: request network
+ s_req_key = self.request_network(
+ s_dut,
+ s_dut.droid.wifiAwareCreateNetworkSpecifier(
+ s_disc_id, peer_id_on_sub, None))
+
+ # Publisher & Subscriber:
+ # - expect unavailable callbacks on the party with the bad ID
+ # - also expect unavailable on the Initiator party (i.e. the
+ # Subscriber) if the Publisher has a bad ID
+ # - but a Publisher with a valid ID will keep waiting ...
+ autils.wait_for_event_with_keys(
+ s_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT, cconsts.NETWORK_CB_UNAVAILABLE))
+ if pub_mismatch:
+ autils.wait_for_event_with_keys(
+ p_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT, cconsts.NETWORK_CB_UNAVAILABLE))
+ else:
+ time.sleep(autils.EVENT_NDP_TIMEOUT)
+ autils.fail_on_event_with_keys(
+ p_dut, cconsts.EVENT_NETWORK_CALLBACK, 0,
+ (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+
+ # clean-up
+ p_dut.droid.connectivityUnregisterNetworkCallback(p_req_key)
+ s_dut.droid.connectivityUnregisterNetworkCallback(s_req_key)
+
+ def run_mismatched_oob_data_path_test(self,
+ init_mismatch_mac=False,
+ resp_mismatch_mac=False,
+ init_encr_type=ENCR_TYPE_OPEN,
+ resp_encr_type=ENCR_TYPE_OPEN):
+ """Runs the negative out-of-band data-path tests: mismatched information
+ between Responder and Initiator.
+
+ Args:
+ init_mismatch_mac: True to mismatch the Initiator MAC address
+ resp_mismatch_mac: True to mismatch the Responder MAC address
+ init_encr_type: Encryption type of Initiator - ENCR_TYPE_*
+ resp_encr_type: Encryption type of Responder - ENCR_TYPE_*
+ """
+ init_dut = self.android_devices[0]
+ init_dut.pretty_name = "Initiator"
+ resp_dut = self.android_devices[1]
+ resp_dut.pretty_name = "Responder"
+
+ # Initiator+Responder: attach and wait for confirmation & identity
+ init_id = init_dut.droid.wifiAwareAttach(True)
+ autils.wait_for_event(init_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ init_ident_event = autils.wait_for_event(
+ init_dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+ init_mac = init_ident_event["data"]["mac"]
+ time.sleep(self.device_startup_offset)
+ resp_id = resp_dut.droid.wifiAwareAttach(True)
+ autils.wait_for_event(resp_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ resp_ident_event = autils.wait_for_event(
+ resp_dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+ resp_mac = resp_ident_event["data"]["mac"]
+
+ if init_mismatch_mac: # assumes legit ones don't start with "00"
+ init_mac = "00" + init_mac[2:]
+ if resp_mismatch_mac:
+ resp_mac = "00" + resp_mac[2:]
+
+ # wait for for devices to synchronize with each other - there are no other
+ # mechanisms to make sure this happens for OOB discovery (except retrying
+ # to execute the data-path request)
+ time.sleep(self.WAIT_FOR_CLUSTER)
+
+ # set up separate keys: even if types are the same we want a mismatch
+ init_passphrase = None
+ init_pmk = None
+ if init_encr_type == self.ENCR_TYPE_PASSPHRASE:
+ init_passphrase = self.PASSPHRASE
+ elif init_encr_type == self.ENCR_TYPE_PMK:
+ init_pmk = self.PMK
+
+ resp_passphrase = None
+ resp_pmk = None
+ if resp_encr_type == self.ENCR_TYPE_PASSPHRASE:
+ resp_passphrase = self.PASSPHRASE2
+ elif resp_encr_type == self.ENCR_TYPE_PMK:
+ resp_pmk = self.PMK2
+
+ # Responder: request network
+ resp_req_key = self.request_network(
+ resp_dut,
+ resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ resp_id, aconsts.DATA_PATH_RESPONDER, init_mac,
+ resp_passphrase, resp_pmk))
+
+ # Initiator: request network
+ init_req_key = self.request_network(
+ init_dut,
+ init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ init_id, aconsts.DATA_PATH_INITIATOR, resp_mac,
+ init_passphrase, init_pmk))
+
+ # Initiator & Responder:
+ # - expect unavailable on the Initiator party if the
+ # 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)
+ 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)
+ init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key)
+
+ def verify_network_info(self, p_data, s_data, open, port,
+ transport_protocol):
+ """Verify that the port and transport protocol information is correct.
+ - should only exist on subscriber (received from publisher)
+ and match transmitted values
+ - should only exist on an encrypted NDP
+
+ Args:
+ p_data, s_data: Pub and Sub (respectively) net cap event data.
+ open: True if NDP unencrypted, False if encrypted.
+ port: Expected port value.
+ transport_protocol: Expected transport protocol value.
+ """
+ asserts.assert_true(aconsts.NET_CAP_PORT not in p_data,
+ "port info not expected on Pub")
+ asserts.assert_true(aconsts.NET_CAP_TRANSPORT_PROTOCOL not in p_data,
+ "transport protocol info not expected on Pub")
+ if open:
+ asserts.assert_true(aconsts.NET_CAP_PORT not in s_data,
+ "port info not expected on Sub (open NDP)")
+ asserts.assert_true(
+ aconsts.NET_CAP_TRANSPORT_PROTOCOL not in s_data,
+ "transport protocol info not expected on Sub (open NDP)")
+ else:
+ asserts.assert_equal(s_data[aconsts.NET_CAP_PORT], port,
+ "Port info does not match on Sub (from Pub)")
+ asserts.assert_equal(
+ s_data[aconsts.NET_CAP_TRANSPORT_PROTOCOL], transport_protocol,
+ "Transport protocol info does not match on Sub (from Pub)")
+
+ #######################################
+ # Positive In-Band (IB) tests key:
+ #
+ # names is: test_ib_<pub_type>_<sub_type>_<encr_type>_<peer_spec>
+ # where:
+ #
+ # pub_type: Type of publish discovery session: unsolicited or solicited.
+ # sub_type: Type of subscribe discovery session: passive or active.
+ # encr_type: Encription type: open, passphrase
+ # peer_spec: Peer specification method: any or specific
+ #
+ # Note: In-Band means using Wi-Fi Aware for discovery and referring to the
+ # peer using the Aware-provided peer handle (as opposed to a MAC address).
+ #######################################
+
+ @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
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ encr_type=self.ENCR_TYPE_OPEN,
+ 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
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ encr_type=self.ENCR_TYPE_OPEN,
+ 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
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ encr_type=self.ENCR_TYPE_PASSPHRASE,
+ 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
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ encr_type=self.ENCR_TYPE_PASSPHRASE,
+ 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
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ encr_type=self.ENCR_TYPE_PMK,
+ 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
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ encr_type=self.ENCR_TYPE_PMK,
+ 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
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_SOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ encr_type=self.ENCR_TYPE_OPEN,
+ 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
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_SOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ encr_type=self.ENCR_TYPE_OPEN,
+ 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
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_SOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ encr_type=self.ENCR_TYPE_PASSPHRASE,
+ 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
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_SOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ encr_type=self.ENCR_TYPE_PASSPHRASE,
+ 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
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_SOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ encr_type=self.ENCR_TYPE_PMK,
+ 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
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_SOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ encr_type=self.ENCR_TYPE_PMK,
+ use_peer_id=False)
+
+ #######################################
+ # Positive In-Band (IB) with a publish session running on the subscriber
+ # tests key:
+ #
+ # names is: test_ib_extra_pub_<same|diff>_<pub_type>_<sub_type>
+ # _<encr_type>_<peer_spec>
+ # where:
+ #
+ # same|diff: Whether the extra publish session (on the subscriber) is the same
+ # or different from the primary session.
+ # pub_type: Type of publish discovery session: unsolicited or solicited.
+ # sub_type: Type of subscribe discovery session: passive or active.
+ # encr_type: Encryption type: open, passphrase
+ # peer_spec: Peer specification method: any or specific
+ #
+ # Note: In-Band means using Wi-Fi Aware for discovery and referring to the
+ # peer using the Aware-provided peer handle (as opposed to a MAC address).
+ #######################################
+
+ @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.
+
+ Configuration contains a publisher (for the same service) running on *both*
+ devices.
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ encr_type=self.ENCR_TYPE_OPEN,
+ use_peer_id=True,
+ pub_on_both=True,
+ pub_on_both_same=True)
+
+ @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.
+
+ Configuration contains a publisher (for the same service) running on *both*
+ devices.
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ encr_type=self.ENCR_TYPE_OPEN,
+ use_peer_id=False,
+ pub_on_both=True,
+ 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.
+
+ Configuration contains a publisher (for a different service) running on
+ *both* devices.
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ encr_type=self.ENCR_TYPE_OPEN,
+ use_peer_id=True,
+ pub_on_both=True,
+ 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.
+
+ Configuration contains a publisher (for a different service) running on
+ *both* devices.
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ encr_type=self.ENCR_TYPE_OPEN,
+ use_peer_id=False,
+ pub_on_both=True,
+ pub_on_both_same=False)
+
+ #######################################
+ # Positive Out-of-Band (OOB) tests key:
+ #
+ # names is: test_oob_<encr_type>_<peer_spec>
+ # where:
+ #
+ # encr_type: Encryption type: open, passphrase
+ # peer_spec: Peer specification method: any or specific
+ #
+ # Optionally set up an extra discovery session to test coexistence. If so
+ # add "ib_coex" to test name.
+ #
+ # Note: Out-of-Band means using a non-Wi-Fi Aware mechanism for discovery and
+ # exchange of MAC addresses and then Wi-Fi Aware for data-path.
+ #######################################
+
+ @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
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_oob_data_path_test(
+ 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
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_oob_data_path_test(
+ 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
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_oob_data_path_test(
+ 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
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_oob_data_path_test(
+ 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
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_oob_data_path_test(
+ 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
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_oob_data_path_test(
+ 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
+ consists of Publisher on both ends, and a Subscriber on the Responder.
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_oob_data_path_test(
+ encr_type=self.ENCR_TYPE_OPEN,
+ use_peer_id=True,
+ 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
+ consists of Publisher on both ends, and a Subscriber on the Responder.
+
+ Verifies end-to-end discovery + data-path creation.
+ """
+ self.run_oob_data_path_test(
+ encr_type=self.ENCR_TYPE_OPEN,
+ use_peer_id=False,
+ setup_discovery_sessions=True)
+
+ ##############################################################
+
+ @test_tracker_info(uuid="1c2c9805-dc1e-43b5-a1b8-315e8c9a4337")
+ @WifiBaseTest.wifi_test_wrap
+ def test_passphrase_min(self):
+ """Data-path: minimum passphrase length
+
+ Use in-band, unsolicited/passive, any peer combination
+ """
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ encr_type=self.ENCR_TYPE_PASSPHRASE,
+ use_peer_id=False,
+ 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
+
+ Use in-band, unsolicited/passive, any peer combination
+ """
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ encr_type=self.ENCR_TYPE_PASSPHRASE,
+ use_peer_id=False,
+ 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(
+ init_encr_type=self.ENCR_TYPE_PASSPHRASE,
+ 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(
+ init_encr_type=self.ENCR_TYPE_PMK,
+ 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(
+ init_encr_type=self.ENCR_TYPE_OPEN,
+ 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(
+ init_encr_type=self.ENCR_TYPE_OPEN,
+ 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(
+ init_encr_type=self.ENCR_TYPE_PMK,
+ 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(
+ init_encr_type=self.ENCR_TYPE_PASSPHRASE,
+ 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(
+ init_encr_type=self.ENCR_TYPE_PMK,
+ 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(
+ init_encr_type=self.ENCR_TYPE_PASSPHRASE,
+ resp_encr_type=self.ENCR_TYPE_OPEN)
+
+ ##########################################################################
+
+ def wait_for_request_responses(self, dut, req_keys, aware_ifs, aware_ipv6):
+ """Wait for network request confirmation for all request keys.
+
+ Args:
+ dut: Device under test
+ req_keys: (in) A list of the network requests
+ aware_ifs: (out) A list into which to append the network interface
+ aware_ipv6: (out) A list into which to append the network ipv6 address
+ """
+ num_events = 0 # looking for 2 events per NDP: link-prop + net-cap
+ while num_events != 2 * len(req_keys):
+ event = autils.wait_for_event(
+ dut,
+ cconsts.EVENT_NETWORK_CALLBACK,
+ timeout=autils.EVENT_NDP_TIMEOUT)
+ if (event["data"][cconsts.NETWORK_CB_KEY_EVENT] ==
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED):
+ if event["data"][cconsts.NETWORK_CB_KEY_ID] in req_keys:
+ num_events = num_events + 1
+ aware_ifs.append(
+ event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME])
+ else:
+ self.log.info(
+ "Received an unexpected connectivity, the revoked "
+ "network request probably went through -- %s", event)
+ elif (event["data"][cconsts.NETWORK_CB_KEY_EVENT] ==
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED):
+ if event["data"][cconsts.NETWORK_CB_KEY_ID] in req_keys:
+ num_events = num_events + 1
+ aware_ipv6.append(event["data"][aconsts.NET_CAP_IPV6])
+ else:
+ self.log.info(
+ "Received an unexpected connectivity, the revoked "
+ "network request probably went through -- %s", event)
+ # validate no leak of information
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in event["data"],
+ "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.
+
+ Verify that the interface and IPv6 address is the same for all networks.
+ """
+ init_dut = self.android_devices[0]
+ init_dut.pretty_name = "Initiator"
+ resp_dut = self.android_devices[1]
+ resp_dut.pretty_name = "Responder"
+
+ N = 2 # first iteration (must be 2 to give us a chance to cancel the first)
+ M = 5 # second iteration
+
+ init_ids = []
+ resp_ids = []
+
+ # Initiator+Responder: attach and wait for confirmation & identity
+ # create N+M sessions to be used in the different (but identical) NDPs
+ for i in range(N + M):
+ id, init_mac = autils.attach_with_identity(init_dut)
+ init_ids.append(id)
+ id, resp_mac = autils.attach_with_identity(resp_dut)
+ resp_ids.append(id)
+
+ # wait for for devices to synchronize with each other - there are no other
+ # mechanisms to make sure this happens for OOB discovery (except retrying
+ # to execute the data-path request)
+ time.sleep(autils.WAIT_FOR_CLUSTER)
+
+ resp_req_keys = []
+ init_req_keys = []
+ resp_aware_ifs = []
+ init_aware_ifs = []
+ resp_aware_ipv6 = []
+ init_aware_ipv6 = []
+
+ # issue N quick requests for identical NDPs - without waiting for result
+ # tests whether pre-setup multiple NDP procedure
+ for i in range(N):
+ # Responder: request network
+ resp_req_keys.append(
+ autils.request_network(
+ resp_dut,
+ resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ resp_ids[i], aconsts.DATA_PATH_RESPONDER, init_mac,
+ None)))
+
+ # Initiator: request network
+ init_req_keys.append(
+ autils.request_network(
+ init_dut,
+ init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ init_ids[i], aconsts.DATA_PATH_INITIATOR, resp_mac,
+ None)))
+
+ # remove the first request (hopefully before completed) testing that NDP
+ # is still created
+ resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_keys[0])
+ resp_req_keys.remove(resp_req_keys[0])
+ init_dut.droid.connectivityUnregisterNetworkCallback(init_req_keys[0])
+ init_req_keys.remove(init_req_keys[0])
+
+ # wait for network formation for all initial requests
+ # note: for IPv6 Init <--> Resp since each reports the other's IPv6 address
+ # in it's transport-specific network info
+ self.wait_for_request_responses(resp_dut, resp_req_keys,
+ resp_aware_ifs, init_aware_ipv6)
+ self.wait_for_request_responses(init_dut, init_req_keys,
+ init_aware_ifs, resp_aware_ipv6)
+
+ # issue M more requests for the same NDPs - tests post-setup multiple NDP
+ for i in range(M):
+ # Responder: request network
+ resp_req_keys.append(
+ autils.request_network(
+ resp_dut,
+ resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ resp_ids[N + i], aconsts.DATA_PATH_RESPONDER, init_mac,
+ None)))
+
+ # Initiator: request network
+ init_req_keys.append(
+ autils.request_network(
+ init_dut,
+ init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ init_ids[N + i], aconsts.DATA_PATH_INITIATOR, resp_mac,
+ None)))
+
+ # wait for network formation for all subsequent requests
+ self.wait_for_request_responses(resp_dut, resp_req_keys[N - 1:],
+ resp_aware_ifs, init_aware_ipv6)
+ self.wait_for_request_responses(init_dut, init_req_keys[N - 1:],
+ init_aware_ifs, resp_aware_ipv6)
+
+ # determine whether all interfaces and ipv6 addresses are identical
+ # (single NDP)
+ init_aware_ifs = list(set(init_aware_ifs))
+ resp_aware_ifs = list(set(resp_aware_ifs))
+ init_aware_ipv6 = list(set(init_aware_ipv6))
+ resp_aware_ipv6 = list(set(resp_aware_ipv6))
+
+ self.log.info("Interface names: I=%s, R=%s", init_aware_ifs,
+ resp_aware_ifs)
+ self.log.info("Interface IPv6: I=%s, R=%s", init_aware_ipv6,
+ resp_aware_ipv6)
+ self.log.info("Initiator requests: %s", init_req_keys)
+ self.log.info("Responder requests: %s", resp_req_keys)
+
+ asserts.assert_equal(
+ len(init_aware_ifs), 1, "Multiple initiator interfaces")
+ asserts.assert_equal(
+ len(resp_aware_ifs), 1, "Multiple responder interfaces")
+ asserts.assert_equal(
+ len(init_aware_ipv6), 1, "Multiple initiator IPv6 addresses")
+ asserts.assert_equal(
+ len(resp_aware_ipv6), 1, "Multiple responder IPv6 addresses")
+
+ for i in range(
+ init_dut.aware_capabilities[aconsts.CAP_MAX_NDI_INTERFACES]):
+ # note: using get_ipv6_addr (ifconfig method) since want to verify
+ # that interfaces which do not have any NDPs on them do not have
+ # an IPv6 link-local address.
+ if_name = "%s%d" % (aconsts.AWARE_NDI_PREFIX, i)
+ init_ipv6 = autils.get_ipv6_addr(init_dut, if_name)
+ resp_ipv6 = autils.get_ipv6_addr(resp_dut, if_name)
+
+ asserts.assert_equal(
+ init_ipv6 is None, if_name not in init_aware_ifs,
+ "Initiator interface %s in unexpected state" % if_name)
+ asserts.assert_equal(
+ resp_ipv6 is None, if_name not in resp_aware_ifs,
+ "Responder interface %s in unexpected state" % if_name)
+
+ # release requests
+ for resp_req_key in resp_req_keys:
+ resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key)
+ 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.
+
+ Verify that the interface and IPv6 address is the same for all networks.
+ """
+ dut1 = self.android_devices[0]
+ dut2 = self.android_devices[1]
+
+ id1, mac1 = autils.attach_with_identity(dut1)
+ id2, mac2 = autils.attach_with_identity(dut2)
+
+ # wait for for devices to synchronize with each other - there are no other
+ # mechanisms to make sure this happens for OOB discovery (except retrying
+ # to execute the data-path request)
+ time.sleep(autils.WAIT_FOR_CLUSTER)
+
+ # first NDP: DUT1 (Init) -> DUT2 (Resp)
+ req_a_resp = autils.request_network(
+ dut2,
+ dut2.droid.wifiAwareCreateNetworkSpecifierOob(
+ id2, aconsts.DATA_PATH_RESPONDER, mac1))
+
+ req_a_init = autils.request_network(
+ dut1,
+ dut1.droid.wifiAwareCreateNetworkSpecifierOob(
+ id1, aconsts.DATA_PATH_INITIATOR, mac2))
+
+ req_a_resp_event_nc = autils.wait_for_event_with_keys(
+ dut2, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, req_a_resp))
+ req_a_init_event_nc = autils.wait_for_event_with_keys(
+ dut1, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, req_a_init))
+
+ # validate no leak of information
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in req_a_resp_event_nc[
+ "data"], "Network specifier leak!")
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in req_a_init_event_nc[
+ "data"], "Network specifier leak!")
+
+ # note that Init <-> Resp since IPv6 are of peer's!
+ req_a_ipv6_init = req_a_resp_event_nc["data"][aconsts.NET_CAP_IPV6]
+ req_a_ipv6_resp = req_a_init_event_nc["data"][aconsts.NET_CAP_IPV6]
+
+ req_a_resp_event_lp = autils.wait_for_event_with_keys(
+ dut2, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, req_a_resp))
+ req_a_init_event_lp = autils.wait_for_event_with_keys(
+ dut1, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, req_a_init))
+
+ req_a_if_resp = req_a_resp_event_lp["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ req_a_if_init = req_a_init_event_lp["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+
+ self.log.info("Interface names for A: I=%s, R=%s", req_a_if_init,
+ req_a_if_resp)
+ self.log.info("Interface addresses (IPv6) for A: I=%s, R=%s",
+ req_a_ipv6_init, req_a_ipv6_resp)
+
+ # second NDP: DUT2 (Init) -> DUT1 (Resp)
+ req_b_resp = autils.request_network(
+ dut1,
+ dut1.droid.wifiAwareCreateNetworkSpecifierOob(
+ id1, aconsts.DATA_PATH_RESPONDER, mac2))
+
+ req_b_init = autils.request_network(
+ dut2,
+ dut2.droid.wifiAwareCreateNetworkSpecifierOob(
+ id2, aconsts.DATA_PATH_INITIATOR, mac1))
+
+ req_b_resp_event_nc = autils.wait_for_event_with_keys(
+ dut1, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, req_b_resp))
+ req_b_init_event_nc = autils.wait_for_event_with_keys(
+ dut2, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, req_b_init))
+
+ # validate no leak of information
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in req_b_resp_event_nc[
+ "data"], "Network specifier leak!")
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in req_b_init_event_nc[
+ "data"], "Network specifier leak!")
+
+ # note that Init <-> Resp since IPv6 are of peer's!
+ req_b_ipv6_init = req_b_resp_event_nc["data"][aconsts.NET_CAP_IPV6]
+ req_b_ipv6_resp = req_b_init_event_nc["data"][aconsts.NET_CAP_IPV6]
+
+ req_b_resp_event_lp = autils.wait_for_event_with_keys(
+ dut1, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, req_b_resp))
+ req_b_init_event_lp = autils.wait_for_event_with_keys(
+ dut2, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, req_b_init))
+
+ req_b_if_resp = req_b_resp_event_lp["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ req_b_if_init = req_b_init_event_lp["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+
+ self.log.info("Interface names for B: I=%s, R=%s", req_b_if_init,
+ req_b_if_resp)
+ self.log.info("Interface addresses (IPv6) for B: I=%s, R=%s",
+ req_b_ipv6_init, req_b_ipv6_resp)
+
+ # validate equality of NDPs (using interface names & ipv6)
+ asserts.assert_equal(req_a_if_init, req_b_if_resp,
+ "DUT1 NDPs are on different interfaces")
+ asserts.assert_equal(req_a_if_resp, req_b_if_init,
+ "DUT2 NDPs are on different interfaces")
+ asserts.assert_equal(req_a_ipv6_init, req_b_ipv6_resp,
+ "DUT1 NDPs are using different IPv6 addresses")
+ asserts.assert_equal(req_a_ipv6_resp, req_b_ipv6_init,
+ "DUT2 NDPs are using different IPv6 addresses")
+
+ # release requests
+ dut1.droid.connectivityUnregisterNetworkCallback(req_a_init)
+ dut1.droid.connectivityUnregisterNetworkCallback(req_b_resp)
+ dut2.droid.connectivityUnregisterNetworkCallback(req_a_resp)
+ dut2.droid.connectivityUnregisterNetworkCallback(req_b_init)
+
+ ########################################################################
+
+ def run_multiple_ndi(self, sec_configs, flip_init_resp=False):
+ """Validate that the device can create and use multiple NDIs.
+
+ The security configuration can be:
+ - None: open
+ - String: passphrase
+ - otherwise: PMK (byte array)
+
+ Args:
+ sec_configs: list of security configurations
+ flip_init_resp: if True the roles of Initiator and Responder are flipped
+ between the 2 devices, otherwise same devices are always
+ configured in the same role.
+ """
+ dut1 = self.android_devices[0]
+ dut2 = self.android_devices[1]
+
+ asserts.skip_if(
+ dut1.aware_capabilities[aconsts.CAP_MAX_NDI_INTERFACES] <
+ len(sec_configs)
+ or dut2.aware_capabilities[aconsts.CAP_MAX_NDI_INTERFACES] <
+ len(sec_configs), "DUTs do not support enough NDIs")
+
+ id1, mac1 = autils.attach_with_identity(dut1)
+ id2, mac2 = autils.attach_with_identity(dut2)
+
+ # wait for for devices to synchronize with each other - there are no other
+ # mechanisms to make sure this happens for OOB discovery (except retrying
+ # to execute the data-path request)
+ time.sleep(autils.WAIT_FOR_CLUSTER)
+
+ dut2_req_keys = []
+ dut1_req_keys = []
+ dut2_aware_ifs = []
+ dut1_aware_ifs = []
+ dut2_aware_ipv6s = []
+ dut1_aware_ipv6s = []
+
+ dut2_type = aconsts.DATA_PATH_RESPONDER
+ dut1_type = aconsts.DATA_PATH_INITIATOR
+ dut2_is_responder = True
+ for sec in sec_configs:
+ if dut2_is_responder:
+ # DUT2 (Responder): request network
+ dut2_req_key = autils.request_network(
+ dut2,
+ autils.get_network_specifier(dut2, id2, dut2_type, mac1,
+ sec))
+ dut2_req_keys.append(dut2_req_key)
+
+ # DUT1 (Initiator): request network
+ dut1_req_key = autils.request_network(
+ dut1,
+ autils.get_network_specifier(dut1, id1, dut1_type, mac2,
+ sec))
+ dut1_req_keys.append(dut1_req_key)
+ else:
+ # DUT1 (Responder): request network
+ dut1_req_key = autils.request_network(
+ dut1,
+ autils.get_network_specifier(dut1, id1, dut1_type, mac2,
+ sec))
+ dut1_req_keys.append(dut1_req_key)
+
+ # DUT2 (Initiator): request network
+ dut2_req_key = autils.request_network(
+ dut2,
+ autils.get_network_specifier(dut2, id2, dut2_type, mac1,
+ sec))
+ dut2_req_keys.append(dut2_req_key)
+
+ # Wait for network
+ dut1_net_event_nc = autils.wait_for_event_with_keys(
+ dut1, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, dut1_req_key))
+ dut2_net_event_nc = autils.wait_for_event_with_keys(
+ dut2, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, dut2_req_key))
+
+ # validate no leak of information
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in dut1_net_event_nc[
+ "data"], "Network specifier leak!")
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in dut2_net_event_nc[
+ "data"], "Network specifier leak!")
+
+ # Note: dut1 <--> dut2 IPv6's addresses since it is peer's info
+ dut2_aware_ipv6 = dut1_net_event_nc["data"][aconsts.NET_CAP_IPV6]
+ dut1_aware_ipv6 = dut2_net_event_nc["data"][aconsts.NET_CAP_IPV6]
+
+ dut1_net_event_lp = autils.wait_for_event_with_keys(
+ dut1, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, dut1_req_key))
+ dut2_net_event_lp = autils.wait_for_event_with_keys(
+ dut2, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, dut2_req_key))
+
+ dut2_aware_if = dut2_net_event_lp["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ dut1_aware_if = dut1_net_event_lp["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+
+ dut2_aware_ifs.append(dut2_aware_if)
+ dut1_aware_ifs.append(dut1_aware_if)
+ dut2_aware_ipv6s.append(dut2_aware_ipv6)
+ dut1_aware_ipv6s.append(dut1_aware_ipv6)
+
+ if flip_init_resp:
+ if dut2_is_responder:
+ dut2_type = aconsts.DATA_PATH_INITIATOR
+ dut1_type = aconsts.DATA_PATH_RESPONDER
+ else:
+ dut2_type = aconsts.DATA_PATH_RESPONDER
+ dut1_type = aconsts.DATA_PATH_INITIATOR
+ dut2_is_responder = not dut2_is_responder
+
+ # check that we are using 2 NDIs & that they have unique IPv6 addresses
+ dut1_aware_ifs = list(set(dut1_aware_ifs))
+ dut2_aware_ifs = list(set(dut2_aware_ifs))
+ dut1_aware_ipv6s = list(set(dut1_aware_ipv6s))
+ dut2_aware_ipv6s = list(set(dut2_aware_ipv6s))
+
+ self.log.info("Interface names: DUT1=%s, DUT2=%s", dut1_aware_ifs,
+ dut2_aware_ifs)
+ self.log.info("IPv6 addresses: DUT1=%s, DUT2=%s", dut1_aware_ipv6s,
+ dut2_aware_ipv6s)
+ self.log.info("DUT1 requests: %s", dut1_req_keys)
+ self.log.info("DUT2 requests: %s", dut2_req_keys)
+
+ asserts.assert_equal(
+ len(dut1_aware_ifs), len(sec_configs), "Multiple DUT1 interfaces")
+ asserts.assert_equal(
+ len(dut2_aware_ifs), len(sec_configs), "Multiple DUT2 interfaces")
+ asserts.assert_equal(
+ len(dut1_aware_ipv6s), len(sec_configs),
+ "Multiple DUT1 IPv6 addresses")
+ asserts.assert_equal(
+ len(dut2_aware_ipv6s), len(sec_configs),
+ "Multiple DUT2 IPv6 addresses")
+
+ for i in range(len(sec_configs)):
+ # note: using get_ipv6_addr (ifconfig method) since want to verify
+ # that the system information is the same as the reported information
+ if_name = "%s%d" % (aconsts.AWARE_NDI_PREFIX, i)
+ dut1_ipv6 = autils.get_ipv6_addr(dut1, if_name)
+ dut2_ipv6 = autils.get_ipv6_addr(dut2, if_name)
+
+ asserts.assert_equal(
+ dut1_ipv6 is None, if_name not in dut1_aware_ifs,
+ "DUT1 interface %s in unexpected state" % if_name)
+ asserts.assert_equal(
+ dut2_ipv6 is None, if_name not in dut2_aware_ifs,
+ "DUT2 interface %s in unexpected state" % if_name)
+
+ # release requests
+ for dut2_req_key in dut2_req_keys:
+ dut2.droid.connectivityUnregisterNetworkCallback(dut2_req_key)
+ for dut1_req_key in dut1_req_keys:
+ 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
+ different NDIs"""
+ 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
+ different NDIs"""
+ 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
+ two different NDIs"""
+ 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
+ different NDIs"""
+ 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
+ NDIs"""
+ 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
+ different NDIs.
+
+ Flip Initiator and Responder roles.
+ """
+ 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
+ different NDIs
+
+ Flip Initiator and Responder roles.
+ """
+ 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
+ two different NDIs
+
+ Flip Initiator and Responder roles.
+ """
+ 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
+ different NDIs
+
+ Flip Initiator and Responder roles.
+ """
+ self.run_multiple_ndi(
+ [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
+ NDIs
+
+ Flip Initiator and Responder roles.
+ """
+ self.run_multiple_ndi([self.PMK, self.PMK2], flip_init_resp=True)
+
+ #######################################
+
+ @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
+ API check to validate that possible (i.e. that failure at current API level
+ is due to an API check and not some underlying failure).
+ """
+
+ # configure all devices to override API check and allow a Responder from ANY
+ for ad in self.android_devices:
+ autils.configure_ndp_allow_any_override(ad, True)
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ encr_type=self.ENCR_TYPE_OPEN,
+ use_peer_id=False)
+
+ # configure all devices to respect API check - i.e. disallow a Responder
+ # from ANY
+ for ad in self.android_devices:
+ autils.configure_ndp_allow_any_override(ad, False)
+ self.run_ib_data_path_test(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ encr_type=self.ENCR_TYPE_OPEN,
+ use_peer_id=False,
+ 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.
+ Override API check to validate that possible (i.e. that failure at current
+ API level is due to an API check and not some underlying failure).
+ """
+
+ # configure all devices to override API check and allow a Responder from ANY
+ for ad in self.android_devices:
+ autils.configure_ndp_allow_any_override(ad, True)
+ self.run_oob_data_path_test(
+ encr_type=self.ENCR_TYPE_OPEN, use_peer_id=False)
+
+ # configure all devices to respect API check - i.e. disallow a Responder
+ # from ANY
+ for ad in self.android_devices:
+ autils.configure_ndp_allow_any_override(ad, False)
+ self.run_oob_data_path_test(
+ encr_type=self.ENCR_TYPE_OPEN,
+ use_peer_id=False,
+ expect_failure=True)
+
+ #######################################
+
+ def run_multiple_regulatory_domains(self, use_ib, init_domain,
+ resp_domain):
+ """Verify that a data-path setup with two conflicting regulatory domains
+ works (the result should be run in Channel 6 - but that is not tested).
+
+ Args:
+ use_ib: True to use in-band discovery, False to use out-of-band discovery.
+ init_domain: The regulatory domain of the Initiator/Subscriber.
+ resp_domain: The regulator domain of the Responder/Publisher.
+ """
+ init_dut = self.android_devices[0]
+ resp_dut = self.android_devices[1]
+
+ wutils.set_wifi_country_code(init_dut, init_domain)
+ wutils.set_wifi_country_code(resp_dut, resp_domain)
+
+ if use_ib:
+ (resp_req_key, init_req_key, resp_aware_if, init_aware_if,
+ resp_ipv6, init_ipv6) = autils.create_ib_ndp(
+ resp_dut, init_dut,
+ autils.create_discovery_config(
+ "GoogleTestXyz", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ autils.create_discovery_config(
+ "GoogleTestXyz", aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ self.device_startup_offset)
+ else:
+ (init_req_key, resp_req_key, init_aware_if, resp_aware_if,
+ init_ipv6, resp_ipv6) = autils.create_oob_ndp(init_dut, resp_dut)
+
+ self.log.info("Interface names: I=%s, R=%s", init_aware_if,
+ resp_aware_if)
+ self.log.info("Interface addresses (IPv6): I=%s, R=%s", init_ipv6,
+ resp_ipv6)
+
+ # clean-up
+ resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key)
+ 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.
+
+ - Uses in-band discovery
+ - Subscriber=US, Publisher=JP
+ """
+ self.run_multiple_regulatory_domains(
+ use_ib=True,
+ init_domain=wutils.WifiEnums.CountryCode.US,
+ 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.
+
+ - Uses in-band discovery
+ - Subscriber=JP, Publisher=US
+ """
+ self.run_multiple_regulatory_domains(
+ use_ib=True,
+ init_domain=wutils.WifiEnums.CountryCode.JAPAN,
+ 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.
+
+ - Uses out-f-band discovery
+ - Initiator=US, Responder=JP
+ """
+ self.run_multiple_regulatory_domains(
+ use_ib=False,
+ init_domain=wutils.WifiEnums.CountryCode.US,
+ 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.
+
+ - Uses out-of-band discovery
+ - Initiator=JP, Responder=US
+ """
+ self.run_multiple_regulatory_domains(
+ use_ib=False,
+ init_domain=wutils.WifiEnums.CountryCode.JAPAN,
+ resp_domain=wutils.WifiEnums.CountryCode.US)
+
+ ########################################################################
+
+ def run_mix_ib_oob(self, same_request, ib_first, inits_on_same_dut):
+ """Validate that multiple network requests issued using both in-band and
+ out-of-band discovery behave as expected.
+
+ The same_request parameter controls whether identical single NDP is
+ expected, if True, or whether multiple NDPs on different NDIs are expected,
+ if False.
+
+ Args:
+ same_request: Issue canonically identical requests (same NMI peer, same
+ passphrase) if True, if False use different passphrases.
+ ib_first: If True then the in-band network is requested first, otherwise
+ (if False) then the out-of-band network is requested first.
+ inits_on_same_dut: If True then the Initiators are run on the same device,
+ otherwise (if False) then the Initiators are run on
+ different devices. Note that Subscribe == Initiator.
+ """
+ if not same_request:
+ asserts.skip_if(
+ self.android_devices[0]
+ .aware_capabilities[aconsts.CAP_MAX_NDI_INTERFACES] < 2
+ or self.android_devices[1]
+ .aware_capabilities[aconsts.CAP_MAX_NDI_INTERFACES] < 2,
+ "DUTs do not support enough NDIs")
+
+ (p_dut, s_dut, p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub,
+ peer_id_on_pub_null) = self.set_up_discovery(
+ aconsts.PUBLISH_TYPE_UNSOLICITED, aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ False)
+
+ p_id2, p_mac = autils.attach_with_identity(p_dut)
+ s_id2, s_mac = autils.attach_with_identity(s_dut)
+
+ if inits_on_same_dut:
+ resp_dut = p_dut
+ resp_id = p_id2
+ resp_mac = p_mac
+
+ init_dut = s_dut
+ init_id = s_id2
+ init_mac = s_mac
+ else:
+ resp_dut = s_dut
+ resp_id = s_id2
+ resp_mac = s_mac
+
+ init_dut = p_dut
+ init_id = p_id2
+ init_mac = p_mac
+
+ passphrase = None if same_request else self.PASSPHRASE
+
+ if ib_first:
+ # request in-band network (to completion)
+ p_req_key = self.request_network(
+ p_dut,
+ p_dut.droid.wifiAwareCreateNetworkSpecifier(p_disc_id, None))
+ s_req_key = self.request_network(
+ s_dut,
+ s_dut.droid.wifiAwareCreateNetworkSpecifier(
+ s_disc_id, peer_id_on_sub))
+
+ # Publisher & Subscriber: wait for network formation
+ p_net_event_nc = autils.wait_for_event_with_keys(
+ p_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+ s_net_event_nc = autils.wait_for_event_with_keys(
+ s_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+ p_net_event_lp = autils.wait_for_event_with_keys(
+ p_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+ s_net_event_lp = autils.wait_for_event_with_keys(
+ s_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+
+ # validate no leak of information
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in p_net_event_nc[
+ "data"], "Network specifier leak!")
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in s_net_event_nc[
+ "data"], "Network specifier leak!")
+
+ # request out-of-band network
+ resp_req_key = autils.request_network(
+ resp_dut,
+ resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ resp_id, aconsts.DATA_PATH_RESPONDER, init_mac, passphrase))
+ init_req_key = autils.request_network(
+ init_dut,
+ init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ init_id, aconsts.DATA_PATH_INITIATOR, resp_mac, passphrase))
+
+ resp_net_event_nc = autils.wait_for_event_with_keys(
+ resp_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
+ init_net_event_nc = autils.wait_for_event_with_keys(
+ init_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, init_req_key))
+ resp_net_event_lp = autils.wait_for_event_with_keys(
+ resp_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
+ init_net_event_lp = autils.wait_for_event_with_keys(
+ init_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, init_req_key))
+
+ # validate no leak of information
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in resp_net_event_nc[
+ "data"], "Network specifier leak!")
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in init_net_event_nc[
+ "data"], "Network specifier leak!")
+
+ if not ib_first:
+ # request in-band network (to completion)
+ p_req_key = self.request_network(
+ p_dut,
+ p_dut.droid.wifiAwareCreateNetworkSpecifier(p_disc_id, None))
+ s_req_key = self.request_network(
+ s_dut,
+ s_dut.droid.wifiAwareCreateNetworkSpecifier(
+ s_disc_id, peer_id_on_sub))
+
+ # Publisher & Subscriber: wait for network formation
+ p_net_event_nc = autils.wait_for_event_with_keys(
+ p_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+ s_net_event_nc = autils.wait_for_event_with_keys(
+ s_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+ p_net_event_lp = autils.wait_for_event_with_keys(
+ p_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+ s_net_event_lp = autils.wait_for_event_with_keys(
+ s_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+
+ # validate no leak of information
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in p_net_event_nc[
+ "data"], "Network specifier leak!")
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in s_net_event_nc[
+ "data"], "Network specifier leak!")
+
+ # note that Init <-> Resp & Pub <--> Sub since IPv6 are of peer's!
+ init_ipv6 = resp_net_event_nc["data"][aconsts.NET_CAP_IPV6]
+ resp_ipv6 = init_net_event_nc["data"][aconsts.NET_CAP_IPV6]
+ pub_ipv6 = s_net_event_nc["data"][aconsts.NET_CAP_IPV6]
+ sub_ipv6 = p_net_event_nc["data"][aconsts.NET_CAP_IPV6]
+
+ # extract net info
+ pub_interface = p_net_event_lp["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ sub_interface = s_net_event_lp["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ resp_interface = resp_net_event_lp["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ init_interface = init_net_event_lp["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+
+ self.log.info("Interface names: Pub=%s, Sub=%s, Resp=%s, Init=%s",
+ pub_interface, sub_interface, resp_interface,
+ init_interface)
+ self.log.info(
+ "Interface addresses (IPv6): Pub=%s, Sub=%s, Resp=%s, Init=%s",
+ pub_ipv6, sub_ipv6, resp_ipv6, init_ipv6)
+
+ # validate NDP/NDI conditions (using interface names & ipv6)
+ if same_request:
+ asserts.assert_equal(
+ pub_interface, resp_interface if inits_on_same_dut else
+ init_interface, "NDP interfaces don't match on Pub/other")
+ asserts.assert_equal(
+ sub_interface, init_interface if inits_on_same_dut else
+ resp_interface, "NDP interfaces don't match on Sub/other")
+
+ asserts.assert_equal(pub_ipv6, resp_ipv6
+ if inits_on_same_dut else init_ipv6,
+ "NDP IPv6 don't match on Pub/other")
+ asserts.assert_equal(sub_ipv6, init_ipv6
+ if inits_on_same_dut else resp_ipv6,
+ "NDP IPv6 don't match on Sub/other")
+ else:
+ asserts.assert_false(
+ pub_interface == (resp_interface
+ if inits_on_same_dut else init_interface),
+ "NDP interfaces match on Pub/other")
+ asserts.assert_false(
+ sub_interface == (init_interface
+ if inits_on_same_dut else resp_interface),
+ "NDP interfaces match on Sub/other")
+
+ asserts.assert_false(
+ pub_ipv6 == (resp_ipv6 if inits_on_same_dut else init_ipv6),
+ "NDP IPv6 match on Pub/other")
+ asserts.assert_false(
+ sub_ipv6 == (init_ipv6 if inits_on_same_dut else resp_ipv6),
+ "NDP IPv6 match on Sub/other")
+
+ # release requests
+ p_dut.droid.connectivityUnregisterNetworkCallback(p_req_key)
+ s_dut.droid.connectivityUnregisterNetworkCallback(s_req_key)
+ resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key)
+ 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.
+
+ The in-band request is issued first. Both Initiators (Sub == Initiator) are
+ run on the same device.
+ """
+ self.run_mix_ib_oob(
+ 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.
+
+ The out-of-band request is issued first. Both Initiators (Sub == Initiator)
+ are run on the same device.
+ """
+ self.run_mix_ib_oob(
+ 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.
+
+ The in-band request is issued first. Initiators (Sub == Initiator) are
+ run on different devices.
+ """
+ self.run_mix_ib_oob(
+ same_request=True, ib_first=True, inits_on_same_dut=False)
+
+ @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.
+
+ The out-of-band request is issued first. Initiators (Sub == Initiator) are
+ run on different devices.
+ """
+ self.run_mix_ib_oob(
+ 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
+ to request the different NDPs.
+
+ The in-band request is issued first. Initiators (Sub == Initiator) are
+ run on the same device.
+ """
+ self.run_mix_ib_oob(
+ 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
+ to request the different NDPs.
+
+ The out-of-band request is issued first. Initiators (Sub == Initiator) are
+ run on the same device.
+ """
+ self.run_mix_ib_oob(
+ 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
+ to request the different NDPs.
+
+ The in-band request is issued first. Initiators (Sub == Initiator) are
+ run on different devices.
+ """
+ self.run_mix_ib_oob(
+ 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
+ to request the different NDPs.
+
+ The out-of-band request is issued first. Initiators (Sub == Initiator) are
+ run on different devices.
+ """
+ self.run_mix_ib_oob(
+ same_request=False, ib_first=False, inits_on_same_dut=False)
+
+ ########################################################################
+
+ @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.
+
+ A - B
+ B - C
+ C - A
+
+ The NDPs are all OPEN (no encryption).
+ """
+ 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))
+
+ duts = self.android_devices
+ loop_len = len(duts)
+ ids = []
+ macs = []
+ reqs = []
+ ifs = []
+ ipv6s = []
+
+ for i in range(loop_len):
+ duts[i].pretty_name = chr(ord("A") + i)
+ reqs.append([])
+ ifs.append([])
+ ipv6s.append([])
+
+ # start-up 3 devices (attach w/ identity)
+ for i in range(loop_len):
+ ids.append(duts[i].droid.wifiAwareAttach(True))
+ autils.wait_for_event(duts[i], aconsts.EVENT_CB_ON_ATTACHED)
+ ident_event = autils.wait_for_event(
+ duts[i], aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+ macs.append(ident_event['data']['mac'])
+
+ # wait for for devices to synchronize with each other - there are no other
+ # mechanisms to make sure this happens for OOB discovery (except retrying
+ # to execute the data-path request)
+ time.sleep(autils.WAIT_FOR_CLUSTER)
+
+ # create the N NDPs: i to (i+1) % N
+ for i in range(loop_len):
+ peer_device = (i + 1) % loop_len
+
+ (init_req_key, resp_req_key, init_aware_if, resp_aware_if,
+ init_ipv6, resp_ipv6) = autils.create_oob_ndp_on_sessions(
+ duts[i], duts[peer_device], ids[i], macs[i], ids[peer_device],
+ macs[peer_device])
+
+ reqs[i].append(init_req_key)
+ reqs[peer_device].append(resp_req_key)
+ ifs[i].append(init_aware_if)
+ ifs[peer_device].append(resp_aware_if)
+ ipv6s[i].append(init_ipv6)
+ ipv6s[peer_device].append(resp_ipv6)
+
+ # clean-up
+ for i in range(loop_len):
+ for req in reqs[i]:
+ duts[i].droid.connectivityUnregisterNetworkCallback(req)
+
+ # info
+ self.log.info("MACs: %s", macs)
+ self.log.info("Interface names: %s", ifs)
+ self.log.info("IPv6 addresses: %s", ipv6s)
+ asserts.explicit_pass(
+ "NDP loop test", extras={
+ "macs": macs,
+ "ifs": ifs,
+ "ipv6s": ipv6s
+ })
diff --git a/acts_tests/tests/google/wifi/aware/functional/DiscoveryTest.py b/acts_tests/tests/google/wifi/aware/functional/DiscoveryTest.py
new file mode 100644
index 0000000..31255af
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/functional/DiscoveryTest.py
@@ -0,0 +1,1263 @@
+#!/usr/bin/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 string
+import time
+
+from acts import asserts
+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):
+ """Set of tests for Wi-Fi Aware discovery."""
+
+ # configuration parameters used by tests
+ PAYLOAD_SIZE_MIN = 0
+ PAYLOAD_SIZE_TYPICAL = 1
+ PAYLOAD_SIZE_MAX = 2
+
+ # message strings
+ query_msg = "How are you doing? 你好嗎?"
+ response_msg = "Doing ok - thanks! 做的不錯 - 謝謝!"
+
+ # message re-transmit counter (increases reliability in open-environment)
+ # Note: reliability of message transmission is tested elsewhere
+ msg_retx_count = 5 # hard-coded max value, internal API
+
+ def create_base_config(self, caps, is_publish, ptype, stype, payload_size,
+ ttl, term_ind_on, null_match):
+ """Create a base configuration based on input parameters.
+
+ Args:
+ caps: device capability dictionary
+ is_publish: True if a publish config, else False
+ ptype: unsolicited or solicited (used if is_publish is True)
+ stype: passive or active (used if is_publish is False)
+ payload_size: min, typical, max (PAYLOAD_SIZE_xx)
+ ttl: time-to-live configuration (0 - forever)
+ term_ind_on: is termination indication enabled
+ null_match: null-out the middle match filter
+ Returns:
+ publish discovery configuration object.
+ """
+ config = {}
+ if is_publish:
+ config[aconsts.DISCOVERY_KEY_DISCOVERY_TYPE] = ptype
+ else:
+ config[aconsts.DISCOVERY_KEY_DISCOVERY_TYPE] = stype
+ config[aconsts.DISCOVERY_KEY_TTL] = ttl
+ config[aconsts.DISCOVERY_KEY_TERM_CB_ENABLED] = term_ind_on
+ if payload_size == self.PAYLOAD_SIZE_MIN:
+ config[aconsts.DISCOVERY_KEY_SERVICE_NAME] = "a"
+ config[aconsts.DISCOVERY_KEY_SSI] = None
+ config[aconsts.DISCOVERY_KEY_MATCH_FILTER_LIST] = []
+ elif payload_size == self.PAYLOAD_SIZE_TYPICAL:
+ config[aconsts.DISCOVERY_KEY_SERVICE_NAME] = "GoogleTestServiceX"
+ if is_publish:
+ config[aconsts.DISCOVERY_KEY_SSI] = string.ascii_letters
+ else:
+ config[aconsts.
+ DISCOVERY_KEY_SSI] = string.ascii_letters[::
+ -1] # reverse
+ config[
+ aconsts.DISCOVERY_KEY_MATCH_FILTER_LIST] = autils.encode_list(
+ [(10).to_bytes(1, byteorder="big"), "hello there string"
+ if not null_match else None,
+ bytes(range(40))])
+ else: # PAYLOAD_SIZE_MAX
+ config[aconsts.DISCOVERY_KEY_SERVICE_NAME] = "VeryLong" + "X" * (
+ caps[aconsts.CAP_MAX_SERVICE_NAME_LEN] - 8)
+ config[aconsts.DISCOVERY_KEY_SSI] = (
+ "P" if is_publish else
+ "S") * caps[aconsts.CAP_MAX_SERVICE_SPECIFIC_INFO_LEN]
+ mf = autils.construct_max_match_filter(
+ caps[aconsts.CAP_MAX_MATCH_FILTER_LEN])
+ if null_match:
+ mf[2] = None
+ config[aconsts.
+ DISCOVERY_KEY_MATCH_FILTER_LIST] = autils.encode_list(mf)
+
+ return config
+
+ def create_publish_config(self, caps, ptype, payload_size, ttl,
+ term_ind_on, null_match):
+ """Create a publish configuration based on input parameters.
+
+ Args:
+ caps: device capability dictionary
+ ptype: unsolicited or solicited
+ payload_size: min, typical, max (PAYLOAD_SIZE_xx)
+ ttl: time-to-live configuration (0 - forever)
+ term_ind_on: is termination indication enabled
+ null_match: null-out the middle match filter
+ Returns:
+ publish discovery configuration object.
+ """
+ return self.create_base_config(caps, True, ptype, None, payload_size,
+ ttl, term_ind_on, null_match)
+
+ def create_subscribe_config(self, caps, stype, payload_size, ttl,
+ term_ind_on, null_match):
+ """Create a subscribe configuration based on input parameters.
+
+ Args:
+ caps: device capability dictionary
+ stype: passive or active
+ payload_size: min, typical, max (PAYLOAD_SIZE_xx)
+ ttl: time-to-live configuration (0 - forever)
+ term_ind_on: is termination indication enabled
+ null_match: null-out the middle match filter
+ Returns:
+ subscribe discovery configuration object.
+ """
+ return self.create_base_config(caps, False, None, stype, payload_size,
+ ttl, term_ind_on, null_match)
+
+ def positive_discovery_test_utility(self, ptype, stype, payload_size):
+ """Utility which runs a positive discovery test:
+ - Discovery (publish/subscribe) with TTL=0 (non-self-terminating)
+ - Exchange messages
+ - Update publish/subscribe
+ - Terminate
+
+ Args:
+ ptype: Publish discovery type
+ stype: Subscribe discovery type
+ payload_size: One of PAYLOAD_SIZE_* constants - MIN, TYPICAL, MAX
+ """
+ p_dut = self.android_devices[0]
+ p_dut.pretty_name = "Publisher"
+ s_dut = self.android_devices[1]
+ s_dut.pretty_name = "Subscriber"
+
+ # Publisher+Subscriber: attach and wait for confirmation
+ p_id = p_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(self.device_startup_offset)
+ s_id = s_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # Publisher: start publish and wait for confirmation
+ p_config = self.create_publish_config(
+ p_dut.aware_capabilities,
+ ptype,
+ payload_size,
+ ttl=0,
+ term_ind_on=False,
+ null_match=False)
+ p_disc_id = p_dut.droid.wifiAwarePublish(p_id, p_config)
+ autils.wait_for_event(p_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+
+ # Subscriber: start subscribe and wait for confirmation
+ s_config = self.create_subscribe_config(
+ s_dut.aware_capabilities,
+ stype,
+ payload_size,
+ ttl=0,
+ term_ind_on=False,
+ null_match=True)
+ s_disc_id = s_dut.droid.wifiAwareSubscribe(s_id, s_config)
+ autils.wait_for_event(s_dut, aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED)
+
+ # Subscriber: wait for service discovery
+ discovery_event = autils.wait_for_event(
+ s_dut, aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+ peer_id_on_sub = discovery_event["data"][
+ aconsts.SESSION_CB_KEY_PEER_ID]
+
+ # Subscriber: validate contents of discovery:
+ # - SSI: publisher's
+ # - Match filter: UNSOLICITED - publisher, SOLICITED - subscriber
+ autils.assert_equal_strings(
+ bytes(discovery_event["data"][
+ aconsts.SESSION_CB_KEY_SERVICE_SPECIFIC_INFO]).decode("utf-8"),
+ p_config[aconsts.DISCOVERY_KEY_SSI],
+ "Discovery mismatch: service specific info (SSI)")
+ asserts.assert_equal(
+ autils.decode_list(discovery_event["data"][
+ aconsts.SESSION_CB_KEY_MATCH_FILTER_LIST]),
+ autils.decode_list(
+ p_config[aconsts.DISCOVERY_KEY_MATCH_FILTER_LIST]
+ if ptype == aconsts.PUBLISH_TYPE_UNSOLICITED else s_config[
+ aconsts.DISCOVERY_KEY_MATCH_FILTER_LIST]),
+ "Discovery mismatch: match filter")
+
+ # Subscriber: send message to peer (Publisher)
+ s_dut.droid.wifiAwareSendMessage(s_disc_id, peer_id_on_sub,
+ self.get_next_msg_id(),
+ self.query_msg, self.msg_retx_count)
+ autils.wait_for_event(s_dut, aconsts.SESSION_CB_ON_MESSAGE_SENT)
+
+ # Publisher: wait for received message
+ pub_rx_msg_event = autils.wait_for_event(
+ p_dut, aconsts.SESSION_CB_ON_MESSAGE_RECEIVED)
+ peer_id_on_pub = pub_rx_msg_event["data"][
+ aconsts.SESSION_CB_KEY_PEER_ID]
+
+ # Publisher: validate contents of message
+ asserts.assert_equal(
+ pub_rx_msg_event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING],
+ self.query_msg, "Subscriber -> Publisher message corrupted")
+
+ # Publisher: send message to peer (Subscriber)
+ p_dut.droid.wifiAwareSendMessage(p_disc_id, peer_id_on_pub,
+ self.get_next_msg_id(),
+ self.response_msg,
+ self.msg_retx_count)
+ autils.wait_for_event(p_dut, aconsts.SESSION_CB_ON_MESSAGE_SENT)
+
+ # Subscriber: wait for received message
+ sub_rx_msg_event = autils.wait_for_event(
+ s_dut, aconsts.SESSION_CB_ON_MESSAGE_RECEIVED)
+
+ # Subscriber: validate contents of message
+ asserts.assert_equal(
+ sub_rx_msg_event["data"][aconsts.SESSION_CB_KEY_PEER_ID],
+ peer_id_on_sub,
+ "Subscriber received message from different peer ID then discovery!?"
+ )
+ autils.assert_equal_strings(
+ sub_rx_msg_event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING],
+ self.response_msg, "Publisher -> Subscriber message corrupted")
+
+ # Subscriber: validate that we're not getting another Service Discovery
+ autils.fail_on_event(s_dut, aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+
+ # Publisher: update publish and wait for confirmation
+ p_config[aconsts.DISCOVERY_KEY_SSI] = "something else"
+ p_dut.droid.wifiAwareUpdatePublish(p_disc_id, p_config)
+ autils.wait_for_event(p_dut,
+ aconsts.SESSION_CB_ON_SESSION_CONFIG_UPDATED)
+
+ # Subscriber: expect a new service discovery
+ discovery_event = autils.wait_for_event(
+ s_dut, aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+
+ # Subscriber: validate contents of discovery
+ autils.assert_equal_strings(
+ bytes(discovery_event["data"][
+ aconsts.SESSION_CB_KEY_SERVICE_SPECIFIC_INFO]).decode("utf-8"),
+ p_config[aconsts.DISCOVERY_KEY_SSI],
+ "Discovery mismatch (after pub update): service specific info (SSI)"
+ )
+ asserts.assert_equal(
+ autils.decode_list(discovery_event["data"][
+ aconsts.SESSION_CB_KEY_MATCH_FILTER_LIST]),
+ autils.decode_list(
+ p_config[aconsts.DISCOVERY_KEY_MATCH_FILTER_LIST]
+ if ptype == aconsts.PUBLISH_TYPE_UNSOLICITED else s_config[
+ aconsts.DISCOVERY_KEY_MATCH_FILTER_LIST]),
+ "Discovery mismatch: match filter")
+ asserts.assert_equal(
+ peer_id_on_sub,
+ discovery_event["data"][aconsts.SESSION_CB_KEY_PEER_ID],
+ "Peer ID changed when publish was updated!?")
+
+ # Subscribe: update subscribe and wait for confirmation
+ s_config = self.create_subscribe_config(
+ s_dut.aware_capabilities,
+ stype,
+ payload_size,
+ ttl=0,
+ term_ind_on=False,
+ null_match=False)
+ s_dut.droid.wifiAwareUpdateSubscribe(s_disc_id, s_config)
+ autils.wait_for_event(s_dut,
+ aconsts.SESSION_CB_ON_SESSION_CONFIG_UPDATED)
+
+ # Publisher+Subscriber: Terminate sessions
+ p_dut.droid.wifiAwareDestroyDiscoverySession(p_disc_id)
+ s_dut.droid.wifiAwareDestroyDiscoverySession(s_disc_id)
+
+ # sleep for timeout period and then verify all 'fail_on_event' together
+ time.sleep(autils.EVENT_TIMEOUT)
+
+ # verify that there were no other events
+ autils.verify_no_more_events(p_dut, timeout=0)
+ autils.verify_no_more_events(s_dut, timeout=0)
+
+ # verify that forbidden callbacks aren't called
+ autils.validate_forbidden_callbacks(p_dut, {aconsts.CB_EV_MATCH: 0})
+
+ def verify_discovery_session_term(self, dut, disc_id, config, is_publish,
+ term_ind_on):
+ """Utility to verify that the specified discovery session has terminated (by
+ waiting for the TTL and then attempting to reconfigure).
+
+ Args:
+ dut: device under test
+ disc_id: discovery id for the existing session
+ config: configuration of the existing session
+ is_publish: True if the configuration was publish, False if subscribe
+ term_ind_on: True if a termination indication is expected, False otherwise
+ """
+ # Wait for session termination
+ if term_ind_on:
+ autils.wait_for_event(
+ dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SESSION_TERMINATED,
+ disc_id))
+ else:
+ # can't defer wait to end since in any case have to wait for session to
+ # expire
+ autils.fail_on_event(
+ dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SESSION_TERMINATED,
+ disc_id))
+
+ # Validate that session expired by trying to configure it (expect failure)
+ config[aconsts.DISCOVERY_KEY_SSI] = "something else"
+ if is_publish:
+ dut.droid.wifiAwareUpdatePublish(disc_id, config)
+ else:
+ dut.droid.wifiAwareUpdateSubscribe(disc_id, config)
+
+ # The response to update discovery session is:
+ # term_ind_on=True: session was cleaned-up so won't get an explicit failure, but won't get a
+ # success either. Can check for no SESSION_CB_ON_SESSION_CONFIG_UPDATED but
+ # will defer to the end of the test (no events on queue).
+ # term_ind_on=False: session was not cleaned-up (yet). So expect
+ # SESSION_CB_ON_SESSION_CONFIG_FAILED.
+ if not term_ind_on:
+ autils.wait_for_event(
+ dut,
+ autils.decorate_event(
+ aconsts.SESSION_CB_ON_SESSION_CONFIG_FAILED, disc_id))
+
+ def positive_ttl_test_utility(self, is_publish, ptype, stype, term_ind_on):
+ """Utility which runs a positive discovery session TTL configuration test
+
+ Iteration 1: Verify session started with TTL
+ Iteration 2: Verify session started without TTL and reconfigured with TTL
+ Iteration 3: Verify session started with (long) TTL and reconfigured with
+ (short) TTL
+
+ Args:
+ is_publish: True if testing publish, False if testing subscribe
+ ptype: Publish discovery type (used if is_publish is True)
+ stype: Subscribe discovery type (used if is_publish is False)
+ term_ind_on: Configuration of termination indication
+ """
+ SHORT_TTL = 5 # 5 seconds
+ LONG_TTL = 100 # 100 seconds
+ dut = self.android_devices[0]
+
+ # Attach and wait for confirmation
+ id = dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # Iteration 1: Start discovery session with TTL
+ config = self.create_base_config(
+ dut.aware_capabilities, is_publish, ptype, stype,
+ self.PAYLOAD_SIZE_TYPICAL, SHORT_TTL, term_ind_on, False)
+ if is_publish:
+ disc_id = dut.droid.wifiAwarePublish(id, config, True)
+ autils.wait_for_event(
+ dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_PUBLISH_STARTED,
+ disc_id))
+ else:
+ disc_id = dut.droid.wifiAwareSubscribe(id, config, True)
+ autils.wait_for_event(
+ dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED,
+ disc_id))
+
+ # Wait for session termination & verify
+ self.verify_discovery_session_term(dut, disc_id, config, is_publish,
+ term_ind_on)
+
+ # Iteration 2: Start a discovery session without TTL
+ config = self.create_base_config(
+ dut.aware_capabilities, is_publish, ptype, stype,
+ self.PAYLOAD_SIZE_TYPICAL, 0, term_ind_on, False)
+ if is_publish:
+ disc_id = dut.droid.wifiAwarePublish(id, config, True)
+ autils.wait_for_event(
+ dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_PUBLISH_STARTED,
+ disc_id))
+ else:
+ disc_id = dut.droid.wifiAwareSubscribe(id, config, True)
+ autils.wait_for_event(
+ dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED,
+ disc_id))
+
+ # Update with a TTL
+ config = self.create_base_config(
+ dut.aware_capabilities, is_publish, ptype, stype,
+ self.PAYLOAD_SIZE_TYPICAL, SHORT_TTL, term_ind_on, False)
+ if is_publish:
+ dut.droid.wifiAwareUpdatePublish(disc_id, config)
+ else:
+ dut.droid.wifiAwareUpdateSubscribe(disc_id, config)
+ autils.wait_for_event(
+ dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SESSION_CONFIG_UPDATED,
+ disc_id))
+
+ # Wait for session termination & verify
+ self.verify_discovery_session_term(dut, disc_id, config, is_publish,
+ term_ind_on)
+
+ # Iteration 3: Start a discovery session with (long) TTL
+ config = self.create_base_config(
+ dut.aware_capabilities, is_publish, ptype, stype,
+ self.PAYLOAD_SIZE_TYPICAL, LONG_TTL, term_ind_on, False)
+ if is_publish:
+ disc_id = dut.droid.wifiAwarePublish(id, config, True)
+ autils.wait_for_event(
+ dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_PUBLISH_STARTED,
+ disc_id))
+ else:
+ disc_id = dut.droid.wifiAwareSubscribe(id, config, True)
+ autils.wait_for_event(
+ dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED,
+ disc_id))
+
+ # Update with a TTL
+ config = self.create_base_config(
+ dut.aware_capabilities, is_publish, ptype, stype,
+ self.PAYLOAD_SIZE_TYPICAL, SHORT_TTL, term_ind_on, False)
+ if is_publish:
+ dut.droid.wifiAwareUpdatePublish(disc_id, config)
+ else:
+ dut.droid.wifiAwareUpdateSubscribe(disc_id, config)
+ autils.wait_for_event(
+ dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SESSION_CONFIG_UPDATED,
+ disc_id))
+
+ # Wait for session termination & verify
+ self.verify_discovery_session_term(dut, disc_id, config, is_publish,
+ term_ind_on)
+
+ # verify that there were no other events
+ autils.verify_no_more_events(dut)
+
+ # verify that forbidden callbacks aren't called
+ if not term_ind_on:
+ autils.validate_forbidden_callbacks(
+ dut, {
+ aconsts.CB_EV_PUBLISH_TERMINATED: 0,
+ aconsts.CB_EV_SUBSCRIBE_TERMINATED: 0
+ })
+
+ def discovery_mismatch_test_utility(self,
+ is_expected_to_pass,
+ p_type,
+ s_type,
+ p_service_name=None,
+ s_service_name=None,
+ p_mf_1=None,
+ s_mf_1=None):
+ """Utility which runs the negative discovery test for mismatched service
+ configs.
+
+ Args:
+ is_expected_to_pass: True if positive test, False if negative
+ p_type: Publish discovery type
+ s_type: Subscribe discovery type
+ p_service_name: Publish service name (or None to leave unchanged)
+ s_service_name: Subscribe service name (or None to leave unchanged)
+ p_mf_1: Publish match filter element [1] (or None to leave unchanged)
+ s_mf_1: Subscribe match filter element [1] (or None to leave unchanged)
+ """
+ p_dut = self.android_devices[0]
+ p_dut.pretty_name = "Publisher"
+ s_dut = self.android_devices[1]
+ s_dut.pretty_name = "Subscriber"
+
+ # create configurations
+ p_config = self.create_publish_config(
+ p_dut.aware_capabilities,
+ p_type,
+ self.PAYLOAD_SIZE_TYPICAL,
+ ttl=0,
+ term_ind_on=False,
+ null_match=False)
+ if p_service_name is not None:
+ p_config[aconsts.DISCOVERY_KEY_SERVICE_NAME] = p_service_name
+ if p_mf_1 is not None:
+ p_config[
+ aconsts.DISCOVERY_KEY_MATCH_FILTER_LIST] = autils.encode_list(
+ [(10).to_bytes(1, byteorder="big"), p_mf_1,
+ bytes(range(40))])
+ s_config = self.create_publish_config(
+ s_dut.aware_capabilities,
+ s_type,
+ self.PAYLOAD_SIZE_TYPICAL,
+ ttl=0,
+ term_ind_on=False,
+ null_match=False)
+ if s_service_name is not None:
+ s_config[aconsts.DISCOVERY_KEY_SERVICE_NAME] = s_service_name
+ if s_mf_1 is not None:
+ s_config[
+ aconsts.DISCOVERY_KEY_MATCH_FILTER_LIST] = autils.encode_list(
+ [(10).to_bytes(1, byteorder="big"), s_mf_1,
+ bytes(range(40))])
+
+ p_id = p_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(self.device_startup_offset)
+ s_id = s_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # Publisher: start publish and wait for confirmation
+ p_disc_id = p_dut.droid.wifiAwarePublish(p_id, p_config)
+ autils.wait_for_event(p_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+
+ # Subscriber: start subscribe and wait for confirmation
+ s_disc_id = s_dut.droid.wifiAwareSubscribe(s_id, s_config)
+ autils.wait_for_event(s_dut, aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED)
+
+ # Subscriber: fail on service discovery
+ if is_expected_to_pass:
+ autils.wait_for_event(s_dut,
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+ else:
+ autils.fail_on_event(s_dut,
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+
+ # Publisher+Subscriber: Terminate sessions
+ p_dut.droid.wifiAwareDestroyDiscoverySession(p_disc_id)
+ s_dut.droid.wifiAwareDestroyDiscoverySession(s_disc_id)
+
+ # verify that there were no other events (including terminations)
+ time.sleep(autils.EVENT_TIMEOUT)
+ autils.verify_no_more_events(p_dut, timeout=0)
+ autils.verify_no_more_events(s_dut, timeout=0)
+
+ #######################################
+ # Positive tests key:
+ #
+ # names is: test_<pub_type>_<sub_type>_<size>
+ # where:
+ #
+ # pub_type: Type of publish discovery session: unsolicited or solicited.
+ # sub_type: Type of subscribe discovery session: passive or active.
+ # size: Size of payload fields (service name, service specific info, and match
+ # filter: typical, max, or min.
+ #######################################
+
+ @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
+ - Typical payload fields size
+
+ Verifies that discovery and message exchange succeeds.
+ """
+ self.positive_discovery_test_utility(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ payload_size=self.PAYLOAD_SIZE_TYPICAL)
+
+ @test_tracker_info(uuid="67fb22bb-6985-4345-95a4-90b76681a58b")
+ def test_positive_unsolicited_passive_min(self):
+ """Functional test case / Discovery test cases / positive test case:
+ - Solicited publish + passive subscribe
+ - Minimal payload fields size
+
+ Verifies that discovery and message exchange succeeds.
+ """
+ self.positive_discovery_test_utility(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ payload_size=self.PAYLOAD_SIZE_MIN)
+
+ @test_tracker_info(uuid="a02a47b9-41bb-47bb-883b-921024a2c30d")
+ def test_positive_unsolicited_passive_max(self):
+ """Functional test case / Discovery test cases / positive test case:
+ - Solicited publish + passive subscribe
+ - Maximal payload fields size
+
+ Verifies that discovery and message exchange succeeds.
+ """
+ self.positive_discovery_test_utility(
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ payload_size=self.PAYLOAD_SIZE_MAX)
+
+ @test_tracker_info(uuid="586c657f-2388-4e7a-baee-9bce2f3d1a16")
+ def test_positive_solicited_active_typical(self):
+ """Functional test case / Discovery test cases / positive test case:
+ - Unsolicited publish + active subscribe
+ - Typical payload fields size
+
+ Verifies that discovery and message exchange succeeds.
+ """
+ self.positive_discovery_test_utility(
+ ptype=aconsts.PUBLISH_TYPE_SOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ payload_size=self.PAYLOAD_SIZE_TYPICAL)
+
+ @test_tracker_info(uuid="5369e4ff-f406-48c5-b41a-df38ec340146")
+ def test_positive_solicited_active_min(self):
+ """Functional test case / Discovery test cases / positive test case:
+ - Unsolicited publish + active subscribe
+ - Minimal payload fields size
+
+ Verifies that discovery and message exchange succeeds.
+ """
+ self.positive_discovery_test_utility(
+ ptype=aconsts.PUBLISH_TYPE_SOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ payload_size=self.PAYLOAD_SIZE_MIN)
+
+ @test_tracker_info(uuid="634c6eb8-2c4f-42bd-9bbb-d874d0ec22f3")
+ def test_positive_solicited_active_max(self):
+ """Functional test case / Discovery test cases / positive test case:
+ - Unsolicited publish + active subscribe
+ - Maximal payload fields size
+
+ Verifies that discovery and message exchange succeeds.
+ """
+ self.positive_discovery_test_utility(
+ ptype=aconsts.PUBLISH_TYPE_SOLICITED,
+ stype=aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ payload_size=self.PAYLOAD_SIZE_MAX)
+
+ #######################################
+ # TTL tests key:
+ #
+ # names is: test_ttl_<pub_type|sub_type>_<term_ind>
+ # where:
+ #
+ # pub_type: Type of publish discovery session: unsolicited or solicited.
+ # sub_type: Type of subscribe discovery session: passive or active.
+ # term_ind: ind_on or ind_off
+ #######################################
+
+ @test_tracker_info(uuid="9d7e758e-e0e2-4550-bcee-bfb6a2bff63e")
+ def test_ttl_unsolicited_ind_on(self):
+ """Functional test case / Discovery test cases / TTL test case:
+ - Unsolicited publish
+ - Termination indication enabled
+ """
+ self.positive_ttl_test_utility(
+ is_publish=True,
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=None,
+ term_ind_on=True)
+
+ @test_tracker_info(uuid="48fd69bc-cc2a-4f65-a0a1-63d7c1720702")
+ def test_ttl_unsolicited_ind_off(self):
+ """Functional test case / Discovery test cases / TTL test case:
+ - Unsolicited publish
+ - Termination indication disabled
+ """
+ self.positive_ttl_test_utility(
+ is_publish=True,
+ ptype=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ stype=None,
+ term_ind_on=False)
+
+ @test_tracker_info(uuid="afb75fc1-9ba7-446a-b5ed-7cd37ab51b1c")
+ def test_ttl_solicited_ind_on(self):
+ """Functional test case / Discovery test cases / TTL test case:
+ - Solicited publish
+ - Termination indication enabled
+ """
+ self.positive_ttl_test_utility(
+ is_publish=True,
+ ptype=aconsts.PUBLISH_TYPE_SOLICITED,
+ stype=None,
+ term_ind_on=True)
+
+ @test_tracker_info(uuid="703311a6-e444-4055-94ee-ea9b9b71799e")
+ def test_ttl_solicited_ind_off(self):
+ """Functional test case / Discovery test cases / TTL test case:
+ - Solicited publish
+ - Termination indication disabled
+ """
+ self.positive_ttl_test_utility(
+ is_publish=True,
+ ptype=aconsts.PUBLISH_TYPE_SOLICITED,
+ stype=None,
+ term_ind_on=False)
+
+ @test_tracker_info(uuid="38a541c4-ff55-4387-87b7-4d940489da9d")
+ def test_ttl_passive_ind_on(self):
+ """Functional test case / Discovery test cases / TTL test case:
+ - Passive subscribe
+ - Termination indication enabled
+ """
+ self.positive_ttl_test_utility(
+ is_publish=False,
+ ptype=None,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ term_ind_on=True)
+
+ @test_tracker_info(uuid="ba971e12-b0ca-417c-a1b5-9451598de47d")
+ def test_ttl_passive_ind_off(self):
+ """Functional test case / Discovery test cases / TTL test case:
+ - Passive subscribe
+ - Termination indication disabled
+ """
+ self.positive_ttl_test_utility(
+ is_publish=False,
+ ptype=None,
+ stype=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ term_ind_on=False)
+
+ @test_tracker_info(uuid="7b5d96f2-2415-4b98-9a51-32957f0679a0")
+ def test_ttl_active_ind_on(self):
+ """Functional test case / Discovery test cases / TTL test case:
+ - Active subscribe
+ - Termination indication enabled
+ """
+ self.positive_ttl_test_utility(
+ is_publish=False,
+ ptype=None,
+ stype=aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ term_ind_on=True)
+
+ @test_tracker_info(uuid="c9268eca-0a30-42dd-8e6c-b8b0b84697fb")
+ def test_ttl_active_ind_off(self):
+ """Functional test case / Discovery test cases / TTL test case:
+ - Active subscribe
+ - Termination indication disabled
+ """
+ self.positive_ttl_test_utility(
+ is_publish=False,
+ ptype=None,
+ stype=aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ term_ind_on=False)
+
+ #######################################
+ # Mismatched service name tests key:
+ #
+ # names is: test_mismatch_service_name_<pub_type>_<sub_type>
+ # where:
+ #
+ # pub_type: Type of publish discovery session: unsolicited or solicited.
+ # sub_type: Type of subscribe discovery session: passive or active.
+ #######################################
+
+ @test_tracker_info(uuid="175415e9-7d07-40d0-95f0-3a5f91ea4711")
+ def test_mismatch_service_name_unsolicited_passive(self):
+ """Functional test case / Discovery test cases / Mismatch service name
+ - Unsolicited publish
+ - Passive subscribe
+ """
+ self.discovery_mismatch_test_utility(
+ is_expected_to_pass=False,
+ p_type=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ s_type=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ p_service_name="GoogleTestServiceXXX",
+ s_service_name="GoogleTestServiceYYY")
+
+ @test_tracker_info(uuid="c22a54ce-9e46-47a5-ac44-831faf93d317")
+ def test_mismatch_service_name_solicited_active(self):
+ """Functional test case / Discovery test cases / Mismatch service name
+ - Solicited publish
+ - Active subscribe
+ """
+ self.discovery_mismatch_test_utility(
+ is_expected_to_pass=False,
+ p_type=aconsts.PUBLISH_TYPE_SOLICITED,
+ s_type=aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ p_service_name="GoogleTestServiceXXX",
+ s_service_name="GoogleTestServiceYYY")
+
+ #######################################
+ # Mismatched discovery session type tests key:
+ #
+ # names is: test_mismatch_service_type_<pub_type>_<sub_type>
+ # where:
+ #
+ # pub_type: Type of publish discovery session: unsolicited or solicited.
+ # sub_type: Type of subscribe discovery session: passive or active.
+ #######################################
+
+ @test_tracker_info(uuid="4806f631-d9eb-45fd-9e75-24674962770f")
+ def test_mismatch_service_type_unsolicited_active(self):
+ """Functional test case / Discovery test cases / Mismatch service name
+ - Unsolicited publish
+ - Active subscribe
+ """
+ self.discovery_mismatch_test_utility(
+ is_expected_to_pass=True,
+ p_type=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ s_type=aconsts.SUBSCRIBE_TYPE_ACTIVE)
+
+ @test_tracker_info(uuid="12d648fd-b8fa-4c0f-9467-95e2366047de")
+ def test_mismatch_service_type_solicited_passive(self):
+ """Functional test case / Discovery test cases / Mismatch service name
+ - Unsolicited publish
+ - Active subscribe
+ """
+ self.discovery_mismatch_test_utility(
+ is_expected_to_pass=False,
+ p_type=aconsts.PUBLISH_TYPE_SOLICITED,
+ s_type=aconsts.SUBSCRIBE_TYPE_PASSIVE)
+
+ #######################################
+ # Mismatched discovery match filter tests key:
+ #
+ # names is: test_mismatch_match_filter_<pub_type>_<sub_type>
+ # where:
+ #
+ # pub_type: Type of publish discovery session: unsolicited or solicited.
+ # sub_type: Type of subscribe discovery session: passive or active.
+ #######################################
+
+ @test_tracker_info(uuid="d98454cb-64af-4266-8fed-f0b545a2d7c4")
+ def test_mismatch_match_filter_unsolicited_passive(self):
+ """Functional test case / Discovery test cases / Mismatch match filter
+ - Unsolicited publish
+ - Passive subscribe
+ """
+ self.discovery_mismatch_test_utility(
+ is_expected_to_pass=False,
+ p_type=aconsts.PUBLISH_TYPE_UNSOLICITED,
+ s_type=aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ p_mf_1="hello there string",
+ s_mf_1="goodbye there string")
+
+ @test_tracker_info(uuid="663c1008-ae11-4e1a-87c7-c311d83f481c")
+ def test_mismatch_match_filter_solicited_active(self):
+ """Functional test case / Discovery test cases / Mismatch match filter
+ - Solicited publish
+ - Active subscribe
+ """
+ self.discovery_mismatch_test_utility(
+ is_expected_to_pass=False,
+ p_type=aconsts.PUBLISH_TYPE_SOLICITED,
+ s_type=aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ p_mf_1="hello there string",
+ s_mf_1="goodbye there string")
+
+ #######################################
+ # Multiple concurrent services
+ #######################################
+
+ def run_multiple_concurrent_services(self, type_x, type_y):
+ """Validate multiple identical discovery services running on both devices:
+ - DUT1 & DUT2 running Publish for X
+ - DUT1 & DUT2 running Publish for Y
+ - DUT1 Subscribes for X
+ - DUT2 Subscribes for Y
+ Message exchanges.
+
+ Note: test requires that devices support 2 publish 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.
+ """
+ dut1 = self.android_devices[0]
+ dut2 = self.android_devices[1]
+
+ X_SERVICE_NAME = "ServiceXXX"
+ Y_SERVICE_NAME = "ServiceYYY"
+
+ asserts.skip_if(
+ dut1.aware_capabilities[aconsts.CAP_MAX_PUBLISHES] < 2
+ or dut2.aware_capabilities[aconsts.CAP_MAX_PUBLISHES] < 2,
+ "Devices do not support 2 publish sessions")
+
+ # attach and wait for confirmation
+ id1 = dut1.droid.wifiAwareAttach(False)
+ autils.wait_for_event(dut1, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(self.device_startup_offset)
+ id2 = dut2.droid.wifiAwareAttach(False)
+ autils.wait_for_event(dut2, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # DUT1 & DUT2: start publishing both X & Y services and wait for
+ # confirmations
+ dut1_x_pid = dut1.droid.wifiAwarePublish(
+ id1, autils.create_discovery_config(X_SERVICE_NAME, type_x[0]))
+ event = autils.wait_for_event(dut1,
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+ asserts.assert_equal(event["data"][aconsts.SESSION_CB_KEY_SESSION_ID],
+ dut1_x_pid,
+ "Unexpected DUT1 X publish session discovery ID")
+
+ dut1_y_pid = dut1.droid.wifiAwarePublish(
+ id1, autils.create_discovery_config(Y_SERVICE_NAME, type_y[0]))
+ event = autils.wait_for_event(dut1,
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+ asserts.assert_equal(event["data"][aconsts.SESSION_CB_KEY_SESSION_ID],
+ dut1_y_pid,
+ "Unexpected DUT1 Y publish session discovery ID")
+
+ dut2_x_pid = dut2.droid.wifiAwarePublish(
+ id2, autils.create_discovery_config(X_SERVICE_NAME, type_x[0]))
+ event = autils.wait_for_event(dut2,
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+ asserts.assert_equal(event["data"][aconsts.SESSION_CB_KEY_SESSION_ID],
+ dut2_x_pid,
+ "Unexpected DUT2 X publish session discovery ID")
+
+ dut2_y_pid = dut2.droid.wifiAwarePublish(
+ id2, autils.create_discovery_config(Y_SERVICE_NAME, type_y[0]))
+ event = autils.wait_for_event(dut2,
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+ asserts.assert_equal(event["data"][aconsts.SESSION_CB_KEY_SESSION_ID],
+ dut2_y_pid,
+ "Unexpected DUT2 Y publish session discovery ID")
+
+ # DUT1: start subscribing for X
+ dut1_x_sid = dut1.droid.wifiAwareSubscribe(
+ id1, autils.create_discovery_config(X_SERVICE_NAME, type_x[1]))
+ autils.wait_for_event(dut1, aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED)
+
+ # DUT2: start subscribing for Y
+ dut2_y_sid = dut2.droid.wifiAwareSubscribe(
+ id2, autils.create_discovery_config(Y_SERVICE_NAME, type_y[1]))
+ autils.wait_for_event(dut2, aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED)
+
+ # DUT1 & DUT2: wait for service discovery
+ event = autils.wait_for_event(dut1,
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+ asserts.assert_equal(
+ event["data"][aconsts.SESSION_CB_KEY_SESSION_ID], dut1_x_sid,
+ "Unexpected DUT1 X subscribe session discovery ID")
+ dut1_peer_id_for_dut2_x = event["data"][aconsts.SESSION_CB_KEY_PEER_ID]
+
+ event = autils.wait_for_event(dut2,
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+ asserts.assert_equal(
+ event["data"][aconsts.SESSION_CB_KEY_SESSION_ID], dut2_y_sid,
+ "Unexpected DUT2 Y subscribe session discovery ID")
+ dut2_peer_id_for_dut1_y = event["data"][aconsts.SESSION_CB_KEY_PEER_ID]
+
+ # DUT1.X send message to DUT2
+ x_msg = "Hello X on DUT2!"
+ dut1.droid.wifiAwareSendMessage(dut1_x_sid, dut1_peer_id_for_dut2_x,
+ self.get_next_msg_id(), x_msg,
+ self.msg_retx_count)
+ autils.wait_for_event(dut1, aconsts.SESSION_CB_ON_MESSAGE_SENT)
+ event = autils.wait_for_event(dut2,
+ aconsts.SESSION_CB_ON_MESSAGE_RECEIVED)
+ asserts.assert_equal(
+ event["data"][aconsts.SESSION_CB_KEY_SESSION_ID], dut2_x_pid,
+ "Unexpected publish session ID on DUT2 for meesage "
+ "received on service X")
+ asserts.assert_equal(
+ event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING], x_msg,
+ "Message on service X from DUT1 to DUT2 not received correctly")
+
+ # DUT2.Y send message to DUT1
+ y_msg = "Hello Y on DUT1!"
+ dut2.droid.wifiAwareSendMessage(dut2_y_sid, dut2_peer_id_for_dut1_y,
+ self.get_next_msg_id(), y_msg,
+ self.msg_retx_count)
+ autils.wait_for_event(dut2, aconsts.SESSION_CB_ON_MESSAGE_SENT)
+ event = autils.wait_for_event(dut1,
+ aconsts.SESSION_CB_ON_MESSAGE_RECEIVED)
+ asserts.assert_equal(
+ event["data"][aconsts.SESSION_CB_KEY_SESSION_ID], dut1_y_pid,
+ "Unexpected publish session ID on DUT1 for meesage "
+ "received on service Y")
+ asserts.assert_equal(
+ event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING], y_msg,
+ "Message on service Y from DUT2 to DUT1 not received correctly")
+
+ @test_tracker_info(uuid="eef80cf3-1fd2-4526-969b-6af2dce785d7")
+ def test_multiple_concurrent_services_both_unsolicited_passive(self):
+ """Validate multiple concurrent discovery sessions running on both devices.
+ - DUT1 & DUT2 running Publish for X
+ - DUT1 & DUT2 running Publish for Y
+ - DUT1 Subscribes for X
+ - DUT2 Subscribes for Y
+ Message exchanges.
+
+ Both sessions are Unsolicited/Passive.
+
+ Note: test requires that devices support 2 publish sessions concurrently.
+ The test will be skipped if the devices are not capable.
+ """
+ self.run_multiple_concurrent_services(
+ type_x=[
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE
+ ],
+ type_y=[
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE
+ ])
+
+ @test_tracker_info(uuid="46739f04-ab2b-4556-b1a4-9aa2774869b5")
+ def test_multiple_concurrent_services_both_solicited_active(self):
+ """Validate multiple concurrent discovery sessions running on both devices.
+ - DUT1 & DUT2 running Publish for X
+ - DUT1 & DUT2 running Publish for Y
+ - DUT1 Subscribes for X
+ - DUT2 Subscribes for Y
+ Message exchanges.
+
+ Both sessions are Solicited/Active.
+
+ Note: test requires that devices support 2 publish sessions concurrently.
+ The test will be skipped if the devices are not capable.
+ """
+ self.run_multiple_concurrent_services(
+ type_x=[
+ aconsts.PUBLISH_TYPE_SOLICITED, aconsts.SUBSCRIBE_TYPE_ACTIVE
+ ],
+ type_y=[
+ aconsts.PUBLISH_TYPE_SOLICITED, aconsts.SUBSCRIBE_TYPE_ACTIVE
+ ])
+
+ @test_tracker_info(uuid="5f8f7fd2-4a0e-4cca-8cbb-6d54353f2baa")
+ def test_multiple_concurrent_services_mix_unsolicited_solicited(self):
+ """Validate multiple concurrent discovery sessions running on both devices.
+ - DUT1 & DUT2 running Publish for X
+ - DUT1 & DUT2 running Publish for Y
+ - DUT1 Subscribes for X
+ - DUT2 Subscribes for Y
+ Message exchanges.
+
+ Session A is Unsolicited/Passive.
+ Session B is Solicited/Active.
+
+ Note: test requires that devices support 2 publish sessions concurrently.
+ The test will be skipped if the devices are not capable.
+ """
+ self.run_multiple_concurrent_services(
+ type_x=[
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE
+ ],
+ type_y=[
+ aconsts.PUBLISH_TYPE_SOLICITED, aconsts.SUBSCRIBE_TYPE_ACTIVE
+ ])
+
+ #########################################################
+
+ @test_tracker_info(uuid="908ec896-fc7a-4ee4-b633-a2f042b74448")
+ def test_upper_lower_service_name_equivalence(self):
+ """Validate that Service Name is case-insensitive. Publish a service name
+ with mixed case, subscribe to the same service name with alternative case
+ and verify that discovery happens."""
+ p_dut = self.android_devices[0]
+ s_dut = self.android_devices[1]
+
+ pub_service_name = "GoogleAbCdEf"
+ sub_service_name = "GoogleaBcDeF"
+
+ autils.create_discovery_pair(
+ p_dut,
+ s_dut,
+ p_config=autils.create_discovery_config(
+ pub_service_name, aconsts.PUBLISH_TYPE_UNSOLICITED),
+ 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/tests/google/wifi/aware/functional/MacRandomNoLeakageTest.py b/acts_tests/tests/google/wifi/aware/functional/MacRandomNoLeakageTest.py
new file mode 100644
index 0000000..3898832
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/functional/MacRandomNoLeakageTest.py
@@ -0,0 +1,255 @@
+#!/usr/bin/python3.4
+#
+# 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.
+
+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.aware import aware_const as aconsts
+from acts.controllers.ap_lib.hostapd_constants import BAND_2G
+from acts.controllers.ap_lib.hostapd_constants import BAND_5G
+from acts.test_utils.wifi.aware import aware_test_utils as autils
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+from scapy.all import *
+
+
+class MacRandomNoLeakageTest(AwareBaseTest, WifiBaseTest):
+ """Set of tests for Wi-Fi Aware MAC address randomization of NMI (NAN
+ management interface) and NDI (NAN data interface)."""
+
+ SERVICE_NAME = "GoogleTestServiceXYZ"
+ ping_msg = 'PING'
+
+ AWARE_DEFAULT_CHANNEL_5_BAND = 149
+ AWARE_DEFAULT_CHANNEL_24_BAND = 6
+
+ ENCR_TYPE_OPEN = 0
+ ENCR_TYPE_PASSPHRASE = 1
+ ENCR_TYPE_PMK = 2
+
+ PASSPHRASE = "This is some random passphrase - very very secure!!"
+ PMK = "ODU0YjE3YzdmNDJiNWI4NTQ2NDJjNDI3M2VkZTQyZGU="
+
+ def setup_class(self):
+ super().setup_class()
+
+ asserts.assert_true(hasattr(self, 'packet_capture'),
+ "Needs packet_capture attribute to support sniffing.")
+ self.configure_packet_capture(channel_5g=self.AWARE_DEFAULT_CHANNEL_5_BAND,
+ channel_2g=self.AWARE_DEFAULT_CHANNEL_24_BAND)
+
+ def setup_test(self):
+ WifiBaseTest.setup_test(self)
+ AwareBaseTest.setup_test(self)
+
+ def teardown_test(self):
+ WifiBaseTest.teardown_test(self)
+ AwareBaseTest.teardown_test(self)
+
+ def verify_mac_no_leakage(self, pcap_procs, factory_mac_addresses, mac_addresses):
+ # Get 2G and 5G pcaps
+ pcap_fname = '%s_%s.pcap' % (pcap_procs[BAND_5G][1], BAND_5G.upper())
+ pcap_5g = rdpcap(pcap_fname)
+
+ pcap_fname = '%s_%s.pcap' % (pcap_procs[BAND_2G][1], BAND_2G.upper())
+ pcap_2g = rdpcap(pcap_fname)
+ pcaps = pcap_5g + pcap_2g
+
+ # Verify factory MAC is not leaked in both 2G and 5G 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 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
+
+ Args:
+ mac: String of mac without ':'
+ @return: Lower case String of mac like "xx:xx:xx:xx:xx:xx"
+ """
+ return re.sub(r"(?<=\w)(?=(?:\w\w)+$)", ":", mac.lower())
+
+ def start_aware(self, dut, is_publish):
+ """Start Aware attach, then start Publish/Subscribe based on role
+
+ Args:
+ dut: Aware device
+ is_publish: True for Publisher, False for subscriber
+ @:return: dict with Aware discovery session info
+ """
+ aware_id = dut.droid.wifiAwareAttach(True)
+ autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
+ event = autils.wait_for_event(dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+ mac = self.transfer_mac_format(event["data"]["mac"])
+ dut.log.info("NMI=%s", mac)
+
+ if is_publish:
+ config = autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED)
+ disc_id = dut.droid.wifiAwarePublish(aware_id, config)
+ autils.wait_for_event(dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+ else:
+ config = autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE)
+ disc_id = dut.droid.wifiAwareSubscribe(aware_id, config)
+ autils.wait_for_event(dut, aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED)
+ aware_session = {"awareId": aware_id, "discId": disc_id, "mac": mac}
+ return aware_session
+
+ def create_date_path(self, p_dut, pub_session, s_dut, sub_session, sec_type):
+ """Create NDP based on the security type(open, PMK, PASSPHRASE), run socket connect
+
+ Args:
+ p_dut: Publish device
+ p_disc_id: Publisher discovery id
+ peer_id_on_pub: peer id on publisher
+ s_dut: Subscribe device
+ s_disc_id: Subscriber discovery id
+ peer_id_on_sub: peer id on subscriber
+ sec_type: NDP security type(open, PMK or PASSPHRASE)
+ @:return: dict with NDP info
+ """
+
+ passphrase = None
+ pmk = None
+
+ if sec_type == self.ENCR_TYPE_PASSPHRASE:
+ passphrase = self.PASSPHRASE
+ if sec_type == self.ENCR_TYPE_PMK:
+ pmk = self.PMK
+
+ p_req_key = autils.request_network(
+ p_dut,
+ p_dut.droid.wifiAwareCreateNetworkSpecifier(pub_session["discId"],
+ None,
+ passphrase, pmk))
+ s_req_key = autils.request_network(
+ s_dut,
+ s_dut.droid.wifiAwareCreateNetworkSpecifier(sub_session["discId"],
+ sub_session["peerId"],
+ passphrase, pmk))
+
+ p_net_event_nc = autils.wait_for_event_with_keys(
+ p_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+ s_net_event_nc = autils.wait_for_event_with_keys(
+ s_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+ p_net_event_lp = autils.wait_for_event_with_keys(
+ p_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+ s_net_event_lp = autils.wait_for_event_with_keys(
+ s_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+
+ p_aware_if = p_net_event_lp["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ s_aware_if = s_net_event_lp["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ p_if_mac = self.transfer_mac_format(autils.get_mac_addr(p_dut, p_aware_if))
+ p_dut.log.info("NDI %s=%s", p_aware_if, p_if_mac)
+ s_if_mac = self.transfer_mac_format(autils.get_mac_addr(s_dut, s_aware_if))
+ s_dut.log.info("NDI %s=%s", s_aware_if, s_if_mac)
+
+ s_ipv6 = p_net_event_nc["data"][aconsts.NET_CAP_IPV6]
+ p_ipv6 = s_net_event_nc["data"][aconsts.NET_CAP_IPV6]
+ asserts.assert_true(
+ autils.verify_socket_connect(p_dut, s_dut, p_ipv6, s_ipv6, 0),
+ "Failed socket link with Pub as Server")
+ asserts.assert_true(
+ autils.verify_socket_connect(s_dut, p_dut, s_ipv6, p_ipv6, 0),
+ "Failed socket link with Sub as Server")
+
+ ndp_info = {"pubReqKey": p_req_key, "pubIfMac": p_if_mac,
+ "subReqKey": s_req_key, "subIfMac": s_if_mac}
+ return ndp_info
+
+ @test_tracker_info(uuid="c9c66873-a8e0-4830-8baa-ada03223bcef")
+ def test_ib_multi_data_path_mac_random_test(self):
+ """Verify there is no factory MAC Address leakage during the Aware discovery, NDP creation,
+ socket setup and IP service connection."""
+
+ p_dut = self.android_devices[0]
+ s_dut = self.android_devices[1]
+ mac_addresses = []
+ factory_mac_addresses = []
+ sec_types = [self.ENCR_TYPE_PMK, self.ENCR_TYPE_PASSPHRASE]
+
+ self.log.info("Starting packet capture")
+ pcap_procs = wutils.start_pcap(
+ self.packet_capture, 'dual', self.test_name)
+
+ factory_mac_1 = p_dut.droid.wifigetFactorymacAddresses()[0]
+ p_dut.log.info("Factory Address: %s", factory_mac_1)
+ factory_mac_2 = s_dut.droid.wifigetFactorymacAddresses()[0]
+ s_dut.log.info("Factory Address: %s", factory_mac_2)
+ factory_mac_addresses.append(factory_mac_1)
+ factory_mac_addresses.append(factory_mac_2)
+
+ # Start Aware and exchange messages
+ publish_session = self.start_aware(p_dut, True)
+ subscribe_session = self.start_aware(s_dut, False)
+ mac_addresses.append(publish_session["mac"])
+ mac_addresses.append(subscribe_session["mac"])
+ discovery_event = autils.wait_for_event(s_dut, aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+ subscribe_session["peerId"] = discovery_event['data'][aconsts.SESSION_CB_KEY_PEER_ID]
+
+ msg_id = self.get_next_msg_id()
+ s_dut.droid.wifiAwareSendMessage(subscribe_session["discId"], subscribe_session["peerId"],
+ msg_id, self.ping_msg, aconsts.MAX_TX_RETRIES)
+ autils.wait_for_event(s_dut, aconsts.SESSION_CB_ON_MESSAGE_SENT)
+ pub_rx_msg_event = autils.wait_for_event(p_dut, aconsts.SESSION_CB_ON_MESSAGE_RECEIVED)
+ publish_session["peerId"] = pub_rx_msg_event['data'][aconsts.SESSION_CB_KEY_PEER_ID]
+
+ msg_id = self.get_next_msg_id()
+ p_dut.droid.wifiAwareSendMessage(publish_session["discId"], publish_session["peerId"],
+ msg_id, self.ping_msg, aconsts.MAX_TX_RETRIES)
+ autils.wait_for_event(p_dut, aconsts.SESSION_CB_ON_MESSAGE_SENT)
+ autils.wait_for_event(s_dut, aconsts.SESSION_CB_ON_MESSAGE_RECEIVED)
+
+ # Create Aware NDP
+ p_req_keys = []
+ s_req_keys = []
+ for sec in sec_types:
+ ndp_info = self.create_date_path(p_dut, publish_session, s_dut, subscribe_session, sec)
+ p_req_keys.append(ndp_info["pubReqKey"])
+ s_req_keys.append(ndp_info["subReqKey"])
+ mac_addresses.append(ndp_info["pubIfMac"])
+ mac_addresses.append(ndp_info["subIfMac"])
+
+ # clean-up
+ for p_req_key in p_req_keys:
+ p_dut.droid.connectivityUnregisterNetworkCallback(p_req_key)
+ for s_req_key in s_req_keys:
+ s_dut.droid.connectivityUnregisterNetworkCallback(s_req_key)
+ p_dut.droid.wifiAwareDestroyAll()
+ s_dut.droid.wifiAwareDestroyAll()
+
+ self.log.info("Stopping packet capture")
+ wutils.stop_pcap(self.packet_capture, pcap_procs, False)
+
+ self.verify_mac_no_leakage(pcap_procs, factory_mac_addresses, mac_addresses)
diff --git a/acts_tests/tests/google/wifi/aware/functional/MacRandomTest.py b/acts_tests/tests/google/wifi/aware/functional/MacRandomTest.py
new file mode 100644
index 0000000..6c8d5b1
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/functional/MacRandomTest.py
@@ -0,0 +1,139 @@
+#!/usr/bin/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 time
+
+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.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
+
+
+class MacRandomTest(AwareBaseTest):
+ """Set of tests for Wi-Fi Aware MAC address randomization of NMI (NAN
+ management interface) and NDI (NAN data interface)."""
+
+ NUM_ITERATIONS = 10
+
+ # number of second to 'reasonably' wait to make sure that devices synchronize
+ # with each other - useful for OOB test cases, where the OOB discovery would
+ # take some time
+ WAIT_FOR_CLUSTER = 5
+
+ def request_network(self, dut, ns):
+ """Request a Wi-Fi Aware network.
+
+ Args:
+ dut: Device
+ ns: Network specifier
+ Returns: the request key
+ """
+ network_req = {"TransportType": 5, "NetworkSpecifier": ns}
+ return dut.droid.connectivityRequestWifiAwareNetwork(network_req)
+
+ ##########################################################################
+
+ @test_tracker_info(uuid="09964368-146a-48e4-9f33-6a319f9eeadc")
+ def test_nmi_ndi_randomization_on_enable(self):
+ """Validate randomization of the NMI (NAN management interface) and all NDIs
+ (NAN data-interface) on each enable/disable cycle"""
+ dut = self.android_devices[0]
+
+ # re-enable randomization interval (since if disabled it may also disable
+ # the 'randomize on enable' feature).
+ autils.configure_mac_random_interval(dut, 1800)
+
+ # DUT: attach and wait for confirmation & identity 10 times
+ mac_addresses = {}
+ for i in range(self.NUM_ITERATIONS):
+ id = dut.droid.wifiAwareAttach(True)
+ autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
+ ident_event = autils.wait_for_event(
+ dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+
+ # process NMI
+ mac = ident_event["data"]["mac"]
+ dut.log.info("NMI=%s", mac)
+ if mac in mac_addresses:
+ mac_addresses[mac] = mac_addresses[mac] + 1
+ else:
+ mac_addresses[mac] = 1
+
+ # process NDIs
+ time.sleep(5) # wait for NDI creation to complete
+ for j in range(
+ dut.aware_capabilities[aconsts.CAP_MAX_NDI_INTERFACES]):
+ ndi_interface = "%s%d" % (aconsts.AWARE_NDI_PREFIX, j)
+ ndi_mac = autils.get_mac_addr(dut, ndi_interface)
+ dut.log.info("NDI %s=%s", ndi_interface, ndi_mac)
+ if ndi_mac in mac_addresses:
+ mac_addresses[ndi_mac] = mac_addresses[ndi_mac] + 1
+ else:
+ mac_addresses[ndi_mac] = 1
+
+ dut.droid.wifiAwareDestroy(id)
+
+ # Test for uniqueness
+ for mac in mac_addresses.keys():
+ if mac_addresses[mac] != 1:
+ asserts.fail("MAC address %s repeated %d times (all=%s)" %
+ (mac, mac_addresses[mac], mac_addresses))
+
+ # Verify that infra interface (e.g. wlan0) MAC address is not used for NMI
+ infra_mac = autils.get_wifi_mac_address(dut)
+ asserts.assert_false(
+ infra_mac in mac_addresses,
+ "Infrastructure MAC address (%s) is used for Aware NMI (all=%s)" %
+ (infra_mac, mac_addresses))
+
+ @test_tracker_info(uuid="0fb0b5d8-d9cb-4e37-b9af-51811be5670d")
+ def test_nmi_randomization_on_interval(self):
+ """Validate randomization of the NMI (NAN management interface) on a set
+ interval. Default value is 30 minutes - change to a small value to allow
+ testing in real-time"""
+ RANDOM_INTERVAL = 120 # minimal value in current implementation
+
+ dut = self.android_devices[0]
+
+ # set randomization interval to 120 seconds
+ autils.configure_mac_random_interval(dut, RANDOM_INTERVAL)
+
+ # attach and wait for first identity
+ id = dut.droid.wifiAwareAttach(True)
+ autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
+ ident_event = autils.wait_for_event(
+ dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+ mac1 = ident_event["data"]["mac"]
+
+ # wait for second identity callback
+ # Note: exact randomization interval is not critical, just approximate,
+ # hence giving a few more seconds.
+ ident_event = autils.wait_for_event(
+ dut,
+ aconsts.EVENT_CB_ON_IDENTITY_CHANGED,
+ timeout=RANDOM_INTERVAL + 5)
+ mac2 = ident_event["data"]["mac"]
+
+ # validate MAC address is randomized
+ asserts.assert_false(
+ mac1 == mac2,
+ "Randomized MAC addresses (%s, %s) should be different" % (mac1,
+ mac2))
+
+ # clean-up
+ dut.droid.wifiAwareDestroy(id)
diff --git a/acts_tests/tests/google/wifi/aware/functional/MatchFilterTest.py b/acts_tests/tests/google/wifi/aware/functional/MatchFilterTest.py
new file mode 100644
index 0000000..e008e12
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/functional/MatchFilterTest.py
@@ -0,0 +1,190 @@
+#!/usr/bin/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 base64
+import time
+import queue
+
+from acts import asserts
+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
+
+
+class MatchFilterTest(AwareBaseTest):
+ """Set of tests for Wi-Fi Aware Discovery Match Filter behavior. These all
+ use examples from Appendix H of the Wi-Fi Aware standard."""
+
+ SERVICE_NAME = "GoogleTestServiceMFMFMF"
+
+ MF_NNNNN = bytes([0x0, 0x0, 0x0, 0x0, 0x0])
+ MF_12345 = bytes([0x1, 0x1, 0x1, 0x2, 0x1, 0x3, 0x1, 0x4, 0x1, 0x5])
+ MF_12145 = bytes([0x1, 0x1, 0x1, 0x2, 0x1, 0x1, 0x1, 0x4, 0x1, 0x5])
+ MF_1N3N5 = bytes([0x1, 0x1, 0x0, 0x1, 0x3, 0x0, 0x1, 0x5])
+ MF_N23N5 = bytes([0x0, 0x1, 0x2, 0x1, 0x3, 0x0, 0x1, 0x5])
+ MF_N2N4 = bytes([0x0, 0x1, 0x2, 0x0, 0x1, 0x4])
+ MF_1N3N = bytes([0x1, 0x1, 0x0, 0x1, 0x3, 0x0])
+
+ # Set of sample match filters from the spec. There is a set of matched
+ # filters:
+ # - Filter 1
+ # - Filter 2
+ # - Expected to match if the Subscriber uses Filter 1 as Tx and the Publisher
+ # uses Filter 2 as Rx (implies Solicited/Active)
+ # - (the reverse) Expected to match if the Publisher uses Filter 1 as Tx and
+ # the Subscriber uses Filter 2 as Rx (implies Unsolicited/Passive)
+ match_filters = [[None, None, True, True], [None, MF_NNNNN, True, True], [
+ MF_NNNNN, None, True, True
+ ], [None, MF_12345, True, False], [MF_12345, None, False, True], [
+ MF_NNNNN, MF_12345, True, True
+ ], [MF_12345, MF_NNNNN, True, True], [MF_12345, MF_12345, True, True], [
+ MF_12345, MF_12145, False, False
+ ], [MF_1N3N5, MF_12345, True, True], [MF_12345, MF_N23N5, True, True],
+ [MF_N2N4, MF_12345, True,
+ False], [MF_12345, MF_1N3N, False, True]]
+
+ def run_discovery(self, p_dut, s_dut, p_mf, s_mf, do_unsolicited_passive,
+ expect_discovery):
+ """Creates a discovery session (publish and subscribe) with the specified
+ configuration.
+
+ Args:
+ p_dut: Device to use as publisher.
+ s_dut: Device to use as subscriber.
+ p_mf: Publish's match filter.
+ s_mf: Subscriber's match filter.
+ do_unsolicited_passive: True to use an Unsolicited/Passive discovery,
+ False for a Solicited/Active discovery session.
+ expect_discovery: True if service should be discovered, False otherwise.
+ Returns: True on success, False on failure (based on expect_discovery arg)
+ """
+ # Encode the match filters
+ p_mf = base64.b64encode(p_mf).decode(
+ "utf-8") if p_mf is not None else None
+ s_mf = base64.b64encode(s_mf).decode(
+ "utf-8") if s_mf is not None else None
+
+ # Publisher+Subscriber: attach and wait for confirmation
+ p_id = p_dut.droid.wifiAwareAttach()
+ autils.wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(self.device_startup_offset)
+ s_id = s_dut.droid.wifiAwareAttach()
+ autils.wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # Publisher: start publish and wait for confirmation
+ p_dut.droid.wifiAwarePublish(
+ p_id,
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ d_type=aconsts.PUBLISH_TYPE_UNSOLICITED
+ if do_unsolicited_passive else aconsts.PUBLISH_TYPE_SOLICITED,
+ match_filter=p_mf))
+ autils.wait_for_event(p_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+
+ # Subscriber: start subscribe and wait for confirmation
+ s_dut.droid.wifiAwareSubscribe(
+ s_id,
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ d_type=aconsts.SUBSCRIBE_TYPE_PASSIVE
+ if do_unsolicited_passive else aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ match_filter=s_mf))
+ autils.wait_for_event(s_dut, aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED)
+
+ # Subscriber: wait or fail on service discovery
+ event = None
+ try:
+ event = s_dut.ed.pop_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, autils.EVENT_TIMEOUT)
+ s_dut.log.info("[Subscriber] SESSION_CB_ON_SERVICE_DISCOVERED: %s",
+ event)
+ except queue.Empty:
+ s_dut.log.info("[Subscriber] No SESSION_CB_ON_SERVICE_DISCOVERED")
+
+ # clean-up
+ p_dut.droid.wifiAwareDestroy(p_id)
+ s_dut.droid.wifiAwareDestroy(s_id)
+
+ if expect_discovery:
+ return event is not None
+ else:
+ return event is None
+
+ def run_match_filters_per_spec(self, do_unsolicited_passive):
+ """Validate all the match filter combinations in the Wi-Fi Aware spec,
+ Appendix H.
+
+ Args:
+ do_unsolicited_passive: True to run the Unsolicited/Passive tests, False
+ to run the Solicited/Active tests.
+ """
+ p_dut = self.android_devices[0]
+ p_dut.pretty_name = "Publisher"
+ s_dut = self.android_devices[1]
+ s_dut.pretty_name = "Subscriber"
+
+ fails = []
+ for i in range(len(self.match_filters)):
+ test_info = self.match_filters[i]
+ if do_unsolicited_passive:
+ pub_type = "Unsolicited"
+ sub_type = "Passive"
+ pub_mf = test_info[0]
+ sub_mf = test_info[1]
+ expect_discovery = test_info[3]
+ else:
+ pub_type = "Solicited"
+ sub_type = "Active"
+ pub_mf = test_info[1]
+ sub_mf = test_info[0]
+ expect_discovery = test_info[2]
+
+ self.log.info("Test #%d: %s Pub MF=%s, %s Sub MF=%s: Discovery %s",
+ i, pub_type, pub_mf, sub_type, sub_mf, "EXPECTED"
+ if test_info[2] else "UNEXPECTED")
+ result = self.run_discovery(
+ p_dut,
+ s_dut,
+ p_mf=pub_mf,
+ s_mf=sub_mf,
+ do_unsolicited_passive=do_unsolicited_passive,
+ expect_discovery=expect_discovery)
+ self.log.info("Test #%d %s Pub/%s Sub %s", i, pub_type, sub_type,
+ "PASS" if result else "FAIL")
+ if not result:
+ fails.append(i)
+
+ asserts.assert_true(
+ len(fails) == 0,
+ "Some match filter tests are failing",
+ extras={"data": fails})
+
+ ###############################################################
+
+ @test_tracker_info(uuid="bd734f8c-895a-4cf9-820f-ec5060517fe9")
+ def test_match_filters_per_spec_unsolicited_passive(self):
+ """Validate all the match filter combinations in the Wi-Fi Aware spec,
+ Appendix H for Unsolicited Publish (tx filter) Passive Subscribe (rx
+ filter)"""
+ self.run_match_filters_per_spec(do_unsolicited_passive=True)
+
+ @test_tracker_info(uuid="6560124d-69e5-49ff-a7e5-3cb305983723")
+ def test_match_filters_per_spec_solicited_active(self):
+ """Validate all the match filter combinations in the Wi-Fi Aware spec,
+ Appendix H for Solicited Publish (rx filter) Active Subscribe (tx
+ filter)"""
+ self.run_match_filters_per_spec(do_unsolicited_passive=False)
diff --git a/acts_tests/tests/google/wifi/aware/functional/MessageTest.py b/acts_tests/tests/google/wifi/aware/functional/MessageTest.py
new file mode 100644
index 0000000..040f4e4
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/functional/MessageTest.py
@@ -0,0 +1,462 @@
+#!/usr/bin/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 string
+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
+
+
+class MessageTest(AwareBaseTest):
+ """Set of tests for Wi-Fi Aware L2 (layer 2) message exchanges."""
+
+ # configuration parameters used by tests
+ PAYLOAD_SIZE_MIN = 0
+ PAYLOAD_SIZE_TYPICAL = 1
+ PAYLOAD_SIZE_MAX = 2
+
+ NUM_MSGS_NO_QUEUE = 10
+ NUM_MSGS_QUEUE_DEPTH_MULT = 2 # number of messages = mult * queue depth
+
+ def create_msg(self, caps, payload_size, id):
+ """Creates a message string of the specified size containing the input id.
+
+ Args:
+ caps: Device capabilities.
+ payload_size: The size of the message to create - min (null or empty
+ message), typical, max (based on device capabilities). Use
+ the PAYLOAD_SIZE_xx constants.
+ id: Information to include in the generated message (or None).
+
+ Returns: A string of the requested size, optionally containing the id.
+ """
+ if payload_size == self.PAYLOAD_SIZE_MIN:
+ # arbitrarily return a None or an empty string (equivalent messages)
+ return None if id % 2 == 0 else ""
+ elif payload_size == self.PAYLOAD_SIZE_TYPICAL:
+ return "*** ID=%d ***" % id + string.ascii_uppercase
+ else: # PAYLOAD_SIZE_MAX
+ return "*** ID=%4d ***" % id + "M" * (
+ caps[aconsts.CAP_MAX_SERVICE_SPECIFIC_INFO_LEN] - 15)
+
+ def create_config(self, is_publish, extra_diff=None):
+ """Create a base configuration based on input parameters.
+
+ Args:
+ is_publish: True for publish, False for subscribe sessions.
+ extra_diff: String to add to service name: allows differentiating
+ discovery sessions.
+
+ Returns:
+ publish discovery configuration object.
+ """
+ config = {}
+ if is_publish:
+ config[
+ aconsts.
+ DISCOVERY_KEY_DISCOVERY_TYPE] = aconsts.PUBLISH_TYPE_UNSOLICITED
+ else:
+ config[
+ aconsts.
+ DISCOVERY_KEY_DISCOVERY_TYPE] = aconsts.SUBSCRIBE_TYPE_PASSIVE
+ config[aconsts.DISCOVERY_KEY_SERVICE_NAME] = "GoogleTestServiceX" + (
+ extra_diff if extra_diff is not None else "")
+ return config
+
+ def prep_message_exchange(self, extra_diff=None):
+ """Creates a discovery session (publish and subscribe), and waits for
+ service discovery - at that point the sessions are ready for message
+ exchange.
+
+ Args:
+ extra_diff: String to add to service name: allows differentiating
+ discovery sessions.
+ """
+ p_dut = self.android_devices[0]
+ p_dut.pretty_name = "Publisher"
+ s_dut = self.android_devices[1]
+ s_dut.pretty_name = "Subscriber"
+
+ # if differentiating (multiple) sessions then should decorate events with id
+ use_id = extra_diff is not None
+
+ # Publisher+Subscriber: attach and wait for confirmation
+ p_id = p_dut.droid.wifiAwareAttach(False, None, use_id)
+ autils.wait_for_event(
+ p_dut, aconsts.EVENT_CB_ON_ATTACHED
+ if not use_id else 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, aconsts.EVENT_CB_ON_ATTACHED
+ if not use_id else autils.decorate_event(
+ aconsts.EVENT_CB_ON_ATTACHED, s_id))
+
+ # Publisher: start publish and wait for confirmation
+ p_disc_id = p_dut.droid.wifiAwarePublish(
+ p_id, self.create_config(True, extra_diff=extra_diff), use_id)
+ autils.wait_for_event(
+ p_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED
+ if not use_id else autils.decorate_event(
+ aconsts.SESSION_CB_ON_PUBLISH_STARTED, p_disc_id))
+
+ # Subscriber: start subscribe and wait for confirmation
+ s_disc_id = s_dut.droid.wifiAwareSubscribe(
+ s_id, self.create_config(False, extra_diff=extra_diff), use_id)
+ autils.wait_for_event(
+ s_dut, aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED
+ if not use_id else autils.decorate_event(
+ aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED, s_disc_id))
+
+ # Subscriber: wait for service discovery
+ discovery_event = autils.wait_for_event(
+ s_dut, aconsts.SESSION_CB_ON_SERVICE_DISCOVERED
+ if not use_id else autils.decorate_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, s_disc_id))
+ peer_id_on_sub = discovery_event["data"][
+ aconsts.SESSION_CB_KEY_PEER_ID]
+
+ return {
+ "p_dut": p_dut,
+ "s_dut": s_dut,
+ "p_id": p_id,
+ "s_id": s_id,
+ "p_disc_id": p_disc_id,
+ "s_disc_id": s_disc_id,
+ "peer_id_on_sub": peer_id_on_sub
+ }
+
+ def run_message_no_queue(self, payload_size):
+ """Validate L2 message exchange between publisher & subscriber with no
+ queueing - i.e. wait for an ACK on each message before sending the next
+ message.
+
+ Args:
+ payload_size: min, typical, or max (PAYLOAD_SIZE_xx).
+ """
+ discovery_info = self.prep_message_exchange()
+ p_dut = discovery_info["p_dut"]
+ s_dut = discovery_info["s_dut"]
+ p_disc_id = discovery_info["p_disc_id"]
+ s_disc_id = discovery_info["s_disc_id"]
+ peer_id_on_sub = discovery_info["peer_id_on_sub"]
+
+ for i in range(self.NUM_MSGS_NO_QUEUE):
+ msg = self.create_msg(s_dut.aware_capabilities, payload_size, i)
+ msg_id = self.get_next_msg_id()
+ s_dut.droid.wifiAwareSendMessage(s_disc_id, peer_id_on_sub, msg_id,
+ msg, 0)
+ tx_event = autils.wait_for_event(
+ s_dut, aconsts.SESSION_CB_ON_MESSAGE_SENT)
+ rx_event = autils.wait_for_event(
+ p_dut, aconsts.SESSION_CB_ON_MESSAGE_RECEIVED)
+ asserts.assert_equal(
+ msg_id, tx_event["data"][aconsts.SESSION_CB_KEY_MESSAGE_ID],
+ "Subscriber -> Publisher message ID corrupted")
+ autils.assert_equal_strings(
+ msg,
+ rx_event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING],
+ "Subscriber -> Publisher message %d corrupted" % i)
+
+ peer_id_on_pub = rx_event["data"][aconsts.SESSION_CB_KEY_PEER_ID]
+ for i in range(self.NUM_MSGS_NO_QUEUE):
+ msg = self.create_msg(s_dut.aware_capabilities, payload_size,
+ 1000 + i)
+ msg_id = self.get_next_msg_id()
+ p_dut.droid.wifiAwareSendMessage(p_disc_id, peer_id_on_pub, msg_id,
+ msg, 0)
+ tx_event = autils.wait_for_event(
+ p_dut, aconsts.SESSION_CB_ON_MESSAGE_SENT)
+ rx_event = autils.wait_for_event(
+ s_dut, aconsts.SESSION_CB_ON_MESSAGE_RECEIVED)
+ asserts.assert_equal(
+ msg_id, tx_event["data"][aconsts.SESSION_CB_KEY_MESSAGE_ID],
+ "Publisher -> Subscriber message ID corrupted")
+ autils.assert_equal_strings(
+ msg,
+ rx_event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING],
+ "Publisher -> Subscriber message %d corrupted" % i)
+
+ # verify there are no more events
+ time.sleep(autils.EVENT_TIMEOUT)
+ autils.verify_no_more_events(p_dut, timeout=0)
+ autils.verify_no_more_events(s_dut, timeout=0)
+
+ def wait_for_messages(self,
+ tx_msgs,
+ tx_msg_ids,
+ tx_disc_id,
+ rx_disc_id,
+ tx_dut,
+ rx_dut,
+ are_msgs_empty=False):
+ """Validate that all expected messages are transmitted correctly and
+ received as expected. Method is called after the messages are sent into
+ the transmission queue.
+
+ Note: that message can be transmitted and received out-of-order (which is
+ acceptable and the method handles that correctly).
+
+ Args:
+ tx_msgs: dictionary of transmitted messages
+ tx_msg_ids: dictionary of transmitted message ids
+ tx_disc_id: transmitter discovery session id (None for no decoration)
+ rx_disc_id: receiver discovery session id (None for no decoration)
+ tx_dut: transmitter device
+ rx_dut: receiver device
+ are_msgs_empty: True if the messages are None or empty (changes dup detection)
+
+ Returns: the peer ID from any of the received messages
+ """
+ # peer id on receiver
+ peer_id_on_rx = None
+
+ # wait for all messages to be transmitted
+ still_to_be_tx = len(tx_msg_ids)
+ while still_to_be_tx != 0:
+ tx_event = autils.wait_for_event(
+ tx_dut, aconsts.SESSION_CB_ON_MESSAGE_SENT
+ if tx_disc_id is None else autils.decorate_event(
+ aconsts.SESSION_CB_ON_MESSAGE_SENT, tx_disc_id))
+ tx_msg_id = tx_event["data"][aconsts.SESSION_CB_KEY_MESSAGE_ID]
+ tx_msg_ids[tx_msg_id] = tx_msg_ids[tx_msg_id] + 1
+ if tx_msg_ids[tx_msg_id] == 1:
+ still_to_be_tx = still_to_be_tx - 1
+
+ # check for any duplicate transmit notifications
+ asserts.assert_equal(
+ len(tx_msg_ids), sum(tx_msg_ids.values()),
+ "Duplicate transmit message IDs: %s" % tx_msg_ids)
+
+ # wait for all messages to be received
+ still_to_be_rx = len(tx_msg_ids)
+ while still_to_be_rx != 0:
+ rx_event = autils.wait_for_event(
+ rx_dut, aconsts.SESSION_CB_ON_MESSAGE_RECEIVED
+ if rx_disc_id is None else autils.decorate_event(
+ aconsts.SESSION_CB_ON_MESSAGE_RECEIVED, rx_disc_id))
+ peer_id_on_rx = rx_event["data"][aconsts.SESSION_CB_KEY_PEER_ID]
+ if are_msgs_empty:
+ still_to_be_rx = still_to_be_rx - 1
+ else:
+ rx_msg = rx_event["data"][
+ aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING]
+ asserts.assert_true(
+ rx_msg in tx_msgs,
+ "Received a message we did not send!? -- '%s'" % rx_msg)
+ tx_msgs[rx_msg] = tx_msgs[rx_msg] + 1
+ if tx_msgs[rx_msg] == 1:
+ still_to_be_rx = still_to_be_rx - 1
+
+ # check for any duplicate received messages
+ if not are_msgs_empty:
+ asserts.assert_equal(
+ len(tx_msgs), sum(tx_msgs.values()),
+ "Duplicate transmit messages: %s" % tx_msgs)
+
+ return peer_id_on_rx
+
+ def run_message_with_queue(self, payload_size):
+ """Validate L2 message exchange between publisher & subscriber with
+ queueing - i.e. transmit all messages and then wait for ACKs.
+
+ Args:
+ payload_size: min, typical, or max (PAYLOAD_SIZE_xx).
+ """
+ discovery_info = self.prep_message_exchange()
+ p_dut = discovery_info["p_dut"]
+ s_dut = discovery_info["s_dut"]
+ p_disc_id = discovery_info["p_disc_id"]
+ s_disc_id = discovery_info["s_disc_id"]
+ peer_id_on_sub = discovery_info["peer_id_on_sub"]
+
+ msgs = {}
+ msg_ids = {}
+ for i in range(
+ self.NUM_MSGS_QUEUE_DEPTH_MULT * s_dut.
+ aware_capabilities[aconsts.CAP_MAX_QUEUED_TRANSMIT_MESSAGES]):
+ msg = self.create_msg(s_dut.aware_capabilities, payload_size, i)
+ msg_id = self.get_next_msg_id()
+ msgs[msg] = 0
+ msg_ids[msg_id] = 0
+ s_dut.droid.wifiAwareSendMessage(s_disc_id, peer_id_on_sub, msg_id,
+ msg, 0)
+ peer_id_on_pub = self.wait_for_messages(
+ msgs, msg_ids, None, None, s_dut, p_dut,
+ payload_size == self.PAYLOAD_SIZE_MIN)
+
+ msgs = {}
+ msg_ids = {}
+ for i in range(
+ self.NUM_MSGS_QUEUE_DEPTH_MULT * p_dut.
+ aware_capabilities[aconsts.CAP_MAX_QUEUED_TRANSMIT_MESSAGES]):
+ msg = self.create_msg(p_dut.aware_capabilities, payload_size,
+ 1000 + i)
+ msg_id = self.get_next_msg_id()
+ msgs[msg] = 0
+ msg_ids[msg_id] = 0
+ p_dut.droid.wifiAwareSendMessage(p_disc_id, peer_id_on_pub, msg_id,
+ msg, 0)
+ self.wait_for_messages(msgs, msg_ids, None, None, p_dut, s_dut,
+ payload_size == self.PAYLOAD_SIZE_MIN)
+
+ # verify there are no more events
+ time.sleep(autils.EVENT_TIMEOUT)
+ autils.verify_no_more_events(p_dut, timeout=0)
+ autils.verify_no_more_events(s_dut, timeout=0)
+
+ def run_message_multi_session_with_queue(self, payload_size):
+ """Validate L2 message exchange between publishers & subscribers with
+ queueing - i.e. transmit all messages and then wait for ACKs. Uses 2
+ discovery sessions running concurrently and validates that messages
+ arrive at the correct destination.
+
+ Args:
+ payload_size: min, typical, or max (PAYLOAD_SIZE_xx)
+ """
+ discovery_info1 = self.prep_message_exchange(extra_diff="-111")
+ p_dut = discovery_info1["p_dut"] # same for both sessions
+ s_dut = discovery_info1["s_dut"] # same for both sessions
+ p_disc_id1 = discovery_info1["p_disc_id"]
+ s_disc_id1 = discovery_info1["s_disc_id"]
+ peer_id_on_sub1 = discovery_info1["peer_id_on_sub"]
+
+ discovery_info2 = self.prep_message_exchange(extra_diff="-222")
+ p_disc_id2 = discovery_info2["p_disc_id"]
+ s_disc_id2 = discovery_info2["s_disc_id"]
+ peer_id_on_sub2 = discovery_info2["peer_id_on_sub"]
+
+ msgs1 = {}
+ msg_ids1 = {}
+ msgs2 = {}
+ msg_ids2 = {}
+ for i in range(
+ self.NUM_MSGS_QUEUE_DEPTH_MULT * s_dut.
+ aware_capabilities[aconsts.CAP_MAX_QUEUED_TRANSMIT_MESSAGES]):
+ msg1 = self.create_msg(s_dut.aware_capabilities, payload_size, i)
+ msg_id1 = self.get_next_msg_id()
+ msgs1[msg1] = 0
+ msg_ids1[msg_id1] = 0
+ s_dut.droid.wifiAwareSendMessage(s_disc_id1, peer_id_on_sub1,
+ msg_id1, msg1, 0)
+ msg2 = self.create_msg(s_dut.aware_capabilities, payload_size,
+ 100 + i)
+ msg_id2 = self.get_next_msg_id()
+ msgs2[msg2] = 0
+ msg_ids2[msg_id2] = 0
+ s_dut.droid.wifiAwareSendMessage(s_disc_id2, peer_id_on_sub2,
+ msg_id2, msg2, 0)
+
+ peer_id_on_pub1 = self.wait_for_messages(
+ msgs1, msg_ids1, s_disc_id1, p_disc_id1, s_dut, p_dut,
+ payload_size == self.PAYLOAD_SIZE_MIN)
+ peer_id_on_pub2 = self.wait_for_messages(
+ msgs2, msg_ids2, s_disc_id2, p_disc_id2, s_dut, p_dut,
+ payload_size == self.PAYLOAD_SIZE_MIN)
+
+ msgs1 = {}
+ msg_ids1 = {}
+ msgs2 = {}
+ msg_ids2 = {}
+ for i in range(
+ self.NUM_MSGS_QUEUE_DEPTH_MULT * p_dut.
+ aware_capabilities[aconsts.CAP_MAX_QUEUED_TRANSMIT_MESSAGES]):
+ msg1 = self.create_msg(p_dut.aware_capabilities, payload_size,
+ 1000 + i)
+ msg_id1 = self.get_next_msg_id()
+ msgs1[msg1] = 0
+ msg_ids1[msg_id1] = 0
+ p_dut.droid.wifiAwareSendMessage(p_disc_id1, peer_id_on_pub1,
+ msg_id1, msg1, 0)
+ msg2 = self.create_msg(p_dut.aware_capabilities, payload_size,
+ 1100 + i)
+ msg_id2 = self.get_next_msg_id()
+ msgs2[msg2] = 0
+ msg_ids2[msg_id2] = 0
+ p_dut.droid.wifiAwareSendMessage(p_disc_id2, peer_id_on_pub2,
+ msg_id2, msg2, 0)
+
+ self.wait_for_messages(msgs1, msg_ids1, p_disc_id1, s_disc_id1, p_dut,
+ s_dut, payload_size == self.PAYLOAD_SIZE_MIN)
+ self.wait_for_messages(msgs2, msg_ids2, p_disc_id2, s_disc_id2, p_dut,
+ s_dut, payload_size == self.PAYLOAD_SIZE_MIN)
+
+ # verify there are no more events
+ 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="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 "")
+ """
+ self.run_message_no_queue(self.PAYLOAD_SIZE_MIN)
+
+ @test_tracker_info(uuid="2c26170a-5d0a-4cf4-b0b9-56ef03f5dcf4")
+ def test_message_no_queue_typical(self):
+ """Functional / Message / No queue
+ - Typical payload size
+ """
+ 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)
+ """
+ self.run_message_no_queue(self.PAYLOAD_SIZE_MAX)
+
+ @test_tracker_info(uuid="3f06de73-31ab-4e0c-bc6f-59abdaf87f4f")
+ def test_message_with_queue_min(self):
+ """Functional / Message / With queue
+ - Minimal payload size (none or "")
+ """
+ self.run_message_with_queue(self.PAYLOAD_SIZE_MIN)
+
+ @test_tracker_info(uuid="9b7f5bd8-b0b1-479e-8e4b-9db0bb56767b")
+ def test_message_with_queue_typical(self):
+ """Functional / Message / With queue
+ - Typical payload size
+ """
+ self.run_message_with_queue(self.PAYLOAD_SIZE_TYPICAL)
+
+ @test_tracker_info(uuid="4f9a6dce-3050-4e6a-a143-53592c6c7c28")
+ def test_message_with_queue_max(self):
+ """Functional / Message / With queue
+ - Max payload size (based on device capabilities)
+ """
+ self.run_message_with_queue(self.PAYLOAD_SIZE_MAX)
+
+ @test_tracker_info(uuid="4cece232-0983-4d6b-90a9-1bb9314b64f0")
+ def test_message_with_multiple_discovery_sessions_typical(self):
+ """Functional / Message / Multiple sessions
+
+ Sets up 2 discovery sessions on 2 devices. Sends a message in each
+ direction on each discovery session and verifies that reaches expected
+ destination.
+ """
+ self.run_message_multi_session_with_queue(self.PAYLOAD_SIZE_TYPICAL)
diff --git a/acts_tests/tests/google/wifi/aware/functional/NonConcurrencyTest.py b/acts_tests/tests/google/wifi/aware/functional/NonConcurrencyTest.py
new file mode 100644
index 0000000..a92552b
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/functional/NonConcurrencyTest.py
@@ -0,0 +1,237 @@
+#!/usr/bin/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 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
+from acts.test_utils.wifi.aware import aware_test_utils as autils
+from acts.test_utils.wifi.aware.AwareBaseTest import AwareBaseTest
+
+# arbitrary timeout for events
+EVENT_TIMEOUT = 10
+
+
+class NonConcurrencyTest(AwareBaseTest):
+ """Tests lack of concurrency scenarios Wi-Fi Aware with WFD (p2p) and
+ SoftAP
+
+ Note: these tests should be modified if the concurrency behavior changes!"""
+
+ SERVICE_NAME = "GoogleTestXYZ"
+ TETHER_SSID = "GoogleTestSoftApXYZ"
+
+ def teardown_test(self):
+ 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
+ """
+ dut = self.android_devices[0]
+
+ # start Aware
+ 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()
+ 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)
+
+ 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 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)
+
+ # 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)
+
+ 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)")
+
+ dut.droid.wifiAwareAttach()
+ if is_p2p:
+ # 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)
+
+ # 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
+ address. Such an association may trigger interface down and up - possibly disrupting a Wi-Fi
+ Aware session.
+
+ Test behavior:
+ - Start Aware
+ - Associate STA
+ - Check if an Aware state change Broadcast received
+ - If necessary (Broadcast received) restart Aware
+ - Start publish
+ - Start Subscribe on peer
+ - Verify discovery
+ """
+ dut = self.android_devices[0]
+ dut_ap = self.android_devices[1]
+ wutils.reset_wifi(dut)
+ wutils.reset_wifi(dut_ap)
+ 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)
+
+ # create random SSID and start softAp on dut_ap
+ ap_ssid = self.TETHER_SSID + utils.rand_ascii_str(8)
+ ap_password = utils.rand_ascii_str(8)
+ config = {wutils.WifiEnums.SSID_KEY: ap_ssid, wutils.WifiEnums.PWD_KEY: ap_password}
+ wutils.start_wifi_tethering(dut_ap, ap_ssid, ap_password)
+ asserts.assert_true(dut_ap.droid.wifiIsApEnabled(),
+ "SoftAp is not reported as running")
+
+ # dut start Aware attach and connect to softAp on dut_ap
+ 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)
+
+ # Check if the WifiAwareState changes then restart the Aware
+ state_change = False
+ try:
+ dut.ed.pop_event(aconsts.BROADCAST_WIFI_AWARE_AVAILABLE, EVENT_TIMEOUT)
+ dut.log.info(aconsts.BROADCAST_WIFI_AWARE_AVAILABLE)
+ p_id = dut.droid.wifiAwareAttach()
+ autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
+ wutils.ensure_no_disconnect(dut)
+ state_change = True
+ except queue.Empty:
+ dut.log.info('WifiAware state was not changed')
+
+ # dut_ap stop softAp and start publish
+ wutils.stop_wifi_tethering(dut_ap)
+ autils.wait_for_event(dut_ap, aconsts.BROADCAST_WIFI_AWARE_AVAILABLE)
+ s_id = dut_ap.droid.wifiAwareAttach()
+ autils.wait_for_event(dut_ap, aconsts.EVENT_CB_ON_ATTACHED)
+ s_disc_id = dut_ap.droid.wifiAwarePublish(s_id, s_config)
+ autils.wait_for_event(dut_ap, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+
+ # dut start subscribe
+ wutils.wait_for_disconnect(dut)
+ if state_change:
+ autils.wait_for_event(dut, aconsts.BROADCAST_WIFI_AWARE_AVAILABLE)
+ p_id = dut.droid.wifiAwareAttach()
+ autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
+ p_disc_id = dut.droid.wifiAwareSubscribe(p_id, p_config)
+ autils.wait_for_event(dut, aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED)
+
+ # Check discovery session
+ autils.wait_for_event(dut, aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+
+ ##########################################################################
+
+ @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/tests/google/wifi/aware/functional/ProtocolsTest.py b/acts_tests/tests/google/wifi/aware/functional/ProtocolsTest.py
new file mode 100644
index 0000000..15e84ff
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/functional/ProtocolsTest.py
@@ -0,0 +1,266 @@
+#!/usr/bin/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.
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.net import nsd_const as nconsts
+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
+
+
+class ProtocolsTest(AwareBaseTest):
+ """Set of tests for Wi-Fi Aware data-paths: validating protocols running on
+ top of a data-path"""
+
+ SERVICE_NAME = "GoogleTestServiceXY"
+
+ def run_ping6(self, dut, peer_ipv6):
+ """Run a ping6 over the specified device/link
+
+ Args:
+ dut: Device on which to execute ping6
+ peer_ipv6: Scoped IPv6 address of the peer to ping
+ """
+ cmd = "ping6 -c 3 -W 5 %s" % peer_ipv6
+ results = dut.adb.shell(cmd)
+ self.log.info("cmd='%s' -> '%s'", cmd, results)
+ if results == "":
+ asserts.fail("ping6 empty results - seems like a failure")
+
+ ########################################################################
+
+ @test_tracker_info(uuid="ce103067-7fdd-4379-9a2b-d238959f1d53")
+ def test_ping6_oob(self):
+ """Validate that ping6 works correctly on an NDP created using OOB (out-of
+ band) discovery"""
+ init_dut = self.android_devices[0]
+ resp_dut = self.android_devices[1]
+
+ # create NDP
+ (init_req_key, resp_req_key, init_aware_if, resp_aware_if, init_ipv6,
+ resp_ipv6) = autils.create_oob_ndp(init_dut, resp_dut)
+ self.log.info("Interface names: I=%s, R=%s", init_aware_if,
+ resp_aware_if)
+ self.log.info("Interface addresses (IPv6): I=%s, R=%s", init_ipv6,
+ resp_ipv6)
+
+ # run ping6
+ self.run_ping6(init_dut, resp_ipv6)
+ self.run_ping6(resp_dut, init_ipv6)
+
+ # clean-up
+ resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key)
+ init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key)
+
+ @test_tracker_info(uuid="fef86a48-0e05-464b-8c66-64316275c5ba")
+ def test_ping6_ib_unsolicited_passive(self):
+ """Validate that ping6 works correctly on an NDP created using Aware
+ discovery with UNSOLICITED/PASSIVE sessions."""
+ p_dut = self.android_devices[0]
+ s_dut = self.android_devices[1]
+
+ # create NDP
+ (p_req_key, s_req_key, p_aware_if, s_aware_if, p_ipv6,
+ s_ipv6) = autils.create_ib_ndp(
+ 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)
+ 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)
+
+ # run ping6
+ self.run_ping6(p_dut, s_ipv6)
+ self.run_ping6(s_dut, p_ipv6)
+
+ # clean-up
+ p_dut.droid.connectivityUnregisterNetworkCallback(p_req_key)
+ s_dut.droid.connectivityUnregisterNetworkCallback(s_req_key)
+
+ @test_tracker_info(uuid="5bbd68a9-994b-4c26-88cd-43388cec280b")
+ def test_ping6_ib_solicited_active(self):
+ """Validate that ping6 works correctly on an NDP created using Aware
+ discovery with SOLICITED/ACTIVE sessions."""
+ p_dut = self.android_devices[0]
+ s_dut = self.android_devices[1]
+
+ # create NDP
+ (p_req_key, s_req_key, p_aware_if, s_aware_if, p_ipv6,
+ s_ipv6) = autils.create_ib_ndp(
+ p_dut,
+ s_dut,
+ p_config=autils.create_discovery_config(
+ self.SERVICE_NAME, aconsts.PUBLISH_TYPE_SOLICITED),
+ s_config=autils.create_discovery_config(
+ self.SERVICE_NAME, aconsts.SUBSCRIBE_TYPE_ACTIVE),
+ device_startup_offset=self.device_startup_offset)
+ 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)
+
+ # run ping6
+ self.run_ping6(p_dut, s_ipv6)
+ self.run_ping6(s_dut, p_ipv6)
+
+ # clean-up
+ p_dut.droid.connectivityUnregisterNetworkCallback(p_req_key)
+ s_dut.droid.connectivityUnregisterNetworkCallback(s_req_key)
+
+ def test_ping6_oob_max_ndp(self):
+ """Validate that ping6 works correctly on multiple NDPs brought up
+ concurrently. Uses the capability of the device to determine the max
+ number of NDPs to set up.
+
+ Note: the test requires MAX_NDP + 1 devices to be validated. If these are
+ not available the test will fail."""
+ dut = self.android_devices[0]
+
+ # get max NDP: using first available device (assumes all devices are the
+ # same)
+ max_ndp = dut.aware_capabilities[aconsts.CAP_MAX_NDP_SESSIONS]
+ asserts.assert_true(
+ len(self.android_devices) > max_ndp,
+ 'Needed %d devices to run the test, have %d' %
+ (max_ndp + 1, len(self.android_devices)))
+
+ # create all NDPs
+ dut_aware_if = None
+ dut_ipv6 = None
+ peers_aware_ifs = []
+ peers_ipv6s = []
+ dut_requests = []
+ peers_requests = []
+ for i in range(max_ndp):
+ (init_req_key, resp_req_key, init_aware_if, resp_aware_if,
+ init_ipv6, resp_ipv6) = autils.create_oob_ndp(
+ dut, self.android_devices[i + 1])
+ self.log.info("Interface names: I=%s, R=%s", init_aware_if,
+ resp_aware_if)
+ self.log.info("Interface addresses (IPv6): I=%s, R=%s", init_ipv6,
+ resp_ipv6)
+
+ dut_requests.append(init_req_key)
+ peers_requests.append(resp_req_key)
+ if dut_aware_if is None:
+ dut_aware_if = init_aware_if
+ else:
+ asserts.assert_equal(
+ dut_aware_if, init_aware_if,
+ "DUT (Initiator) interface changed on subsequent NDPs!?")
+ if dut_ipv6 is None:
+ dut_ipv6 = init_ipv6
+ else:
+ asserts.assert_equal(
+ dut_ipv6, init_ipv6,
+ "DUT (Initiator) IPv6 changed on subsequent NDPs!?")
+ peers_aware_ifs.append(resp_aware_if)
+ peers_ipv6s.append(resp_ipv6)
+
+ # run ping6
+ for i in range(max_ndp):
+ self.run_ping6(dut, peers_ipv6s[i])
+ self.run_ping6(self.android_devices[i + 1], dut_ipv6)
+
+ # cleanup
+ for i in range(max_ndp):
+ dut.droid.connectivityUnregisterNetworkCallback(dut_requests[i])
+ self.android_devices[
+ i + 1].droid.connectivityUnregisterNetworkCallback(
+ peers_requests[i])
+
+ def test_nsd_oob(self):
+ """Validate that NSD (mDNS) works correctly on an NDP created using OOB
+ (out-of band) discovery"""
+ init_dut = self.android_devices[0]
+ resp_dut = self.android_devices[1]
+
+ # create NDP
+ (init_req_key, resp_req_key, init_aware_if, resp_aware_if, init_ipv6,
+ resp_ipv6) = autils.create_oob_ndp(init_dut, resp_dut)
+ self.log.info("Interface names: I=%s, R=%s", init_aware_if,
+ resp_aware_if)
+ self.log.info("Interface addresses (IPv6): I=%s, R=%s", init_ipv6,
+ resp_ipv6)
+
+ # run NSD
+ nsd_service_info = {
+ "serviceInfoServiceName": "sl4aTestAwareNsd",
+ "serviceInfoServiceType": "_simple-tx-rx._tcp.",
+ "serviceInfoPort": 2257
+ }
+ nsd_reg = None
+ nsd_discovery = None
+ try:
+ # Initiator registers an NSD service
+ nsd_reg = init_dut.droid.nsdRegisterService(nsd_service_info)
+ event_nsd = autils.wait_for_event_with_keys(
+ init_dut, nconsts.REG_LISTENER_EVENT, autils.EVENT_TIMEOUT,
+ (nconsts.REG_LISTENER_CALLBACK,
+ nconsts.REG_LISTENER_EVENT_ON_SERVICE_REGISTERED))
+ self.log.info("Initiator %s: %s",
+ nconsts.REG_LISTENER_EVENT_ON_SERVICE_REGISTERED,
+ event_nsd["data"])
+
+ # Responder starts an NSD discovery
+ nsd_discovery = resp_dut.droid.nsdDiscoverServices(
+ nsd_service_info[nconsts.NSD_SERVICE_INFO_SERVICE_TYPE])
+ event_nsd = autils.wait_for_event_with_keys(
+ resp_dut, nconsts.DISCOVERY_LISTENER_EVENT,
+ autils.EVENT_TIMEOUT,
+ (nconsts.DISCOVERY_LISTENER_DATA_CALLBACK,
+ nconsts.DISCOVERY_LISTENER_EVENT_ON_SERVICE_FOUND))
+ self.log.info("Responder %s: %s",
+ nconsts.DISCOVERY_LISTENER_EVENT_ON_SERVICE_FOUND,
+ event_nsd["data"])
+
+ # Responder resolves IP address of Initiator from NSD service discovery
+ resp_dut.droid.nsdResolveService(event_nsd["data"])
+ event_nsd = autils.wait_for_event_with_keys(
+ resp_dut, nconsts.RESOLVE_LISTENER_EVENT, autils.EVENT_TIMEOUT,
+ (nconsts.RESOLVE_LISTENER_DATA_CALLBACK,
+ nconsts.RESOLVE_LISTENER_EVENT_ON_SERVICE_RESOLVED))
+ self.log.info("Responder %s: %s",
+ nconsts.RESOLVE_LISTENER_EVENT_ON_SERVICE_RESOLVED,
+ event_nsd["data"])
+
+ # mDNS returns first character as '/' - strip
+ # out to get clean IPv6
+ init_ipv6_nsd = event_nsd["data"][nconsts.NSD_SERVICE_INFO_HOST][
+ 1:]
+
+ # mDNS doesn't seem to return a scoped host so strip it out of the
+ # local result (for now)
+ scope_index = init_ipv6.find("%")
+ if scope_index != -1:
+ init_ipv6 = init_ipv6[:scope_index]
+
+ asserts.assert_equal(
+ init_ipv6, init_ipv6_nsd,
+ "Initiator's IPv6 address obtained through NSD doesn't match!?"
+ )
+ finally:
+ # Stop NSD
+ if nsd_reg is not None:
+ init_dut.droid.nsdUnregisterService(nsd_reg)
+ if nsd_discovery is not None:
+ resp_dut.droid.nsdStopServiceDiscovery(nsd_discovery)
+
+ # clean-up
+ resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key)
+ init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key)
diff --git a/acts_tests/tests/google/wifi/aware/functional/functional b/acts_tests/tests/google/wifi/aware/functional/functional
new file mode 100644
index 0000000..e13a470
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/functional/functional
@@ -0,0 +1,9 @@
+AttachTest
+DiscoveryTest
+MessageTest
+DataPathTest
+MacRandomTest
+CapabilitiesTest
+ProtocolsTest
+NonConcurrencyTest
+MatchFilterTest
\ No newline at end of file
diff --git a/acts_tests/tests/google/wifi/aware/ota/ServiceIdsTest.py b/acts_tests/tests/google/wifi/aware/ota/ServiceIdsTest.py
new file mode 100644
index 0000000..e29cd71
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/ota/ServiceIdsTest.py
@@ -0,0 +1,98 @@
+#!/usr/bin/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 time
+
+from acts import asserts
+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
+
+
+class ServiceIdsTest(AwareBaseTest):
+ """Set of tests for Wi-Fi Aware to verify that beacons include service IDs
+ for discovery.
+
+ Note: this test is an OTA (over-the-air) and requires a Sniffer.
+ """
+
+ def start_discovery_session(self, dut, session_id, is_publish, dtype,
+ service_name):
+ """Start a discovery session
+
+ Args:
+ dut: Device under test
+ session_id: ID of the Aware session in which to start discovery
+ is_publish: True for a publish session, False for subscribe session
+ dtype: Type of the discovery session
+ service_name: Service name to use for the discovery session
+
+ Returns:
+ Discovery session ID.
+ """
+ config = {}
+ config[aconsts.DISCOVERY_KEY_DISCOVERY_TYPE] = dtype
+ config[aconsts.DISCOVERY_KEY_SERVICE_NAME] = service_name
+
+ if is_publish:
+ disc_id = dut.droid.wifiAwarePublish(session_id, config)
+ event_name = aconsts.SESSION_CB_ON_PUBLISH_STARTED
+ else:
+ disc_id = dut.droid.wifiAwareSubscribe(session_id, config)
+ event_name = aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED
+
+ autils.wait_for_event(dut, event_name)
+ return disc_id
+
+ ####################################################################
+
+ def test_service_ids_in_beacon(self):
+ """Verify that beacons include service IDs for both publish and subscribe
+ sessions of all types: solicited/unsolicited/active/passive."""
+ dut = self.android_devices[0]
+
+ self.log.info("Reminder: start a sniffer before running test")
+
+ # attach
+ session_id = dut.droid.wifiAwareAttach(True)
+ autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
+ ident_event = autils.wait_for_event(
+ dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+ mac = ident_event["data"]["mac"]
+ self.log.info("Source MAC Address of 'interesting' packets = %s", mac)
+ self.log.info("Wireshark filter = 'wlan.ta == %s:%s:%s:%s:%s:%s'",
+ mac[0:2], mac[2:4], mac[4:6], mac[6:8], mac[8:10],
+ mac[10:12])
+
+ time.sleep(5) # get some samples pre-discovery
+
+ # start 4 discovery session (one of each type)
+ self.start_discovery_session(dut, session_id, True,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ "GoogleTestService-Pub-Unsolicited")
+ self.start_discovery_session(dut, session_id, True,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ "GoogleTestService-Pub-Solicited")
+ self.start_discovery_session(dut, session_id, False,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ "GoogleTestService-Sub-Active")
+ self.start_discovery_session(dut, session_id, False,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ "GoogleTestService-Sub-Passive")
+
+ time.sleep(15) # get some samples while discovery is alive
+
+ self.log.info("Reminder: stop sniffer")
diff --git a/acts_tests/tests/google/wifi/aware/ota/ota b/acts_tests/tests/google/wifi/aware/ota/ota
new file mode 100644
index 0000000..27f6724
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/ota/ota
@@ -0,0 +1 @@
+ServiceIdsTest
\ No newline at end of file
diff --git a/acts_tests/tests/google/wifi/aware/performance/LatencyTest.py b/acts_tests/tests/google/wifi/aware/performance/LatencyTest.py
new file mode 100644
index 0000000..8ebff89
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/performance/LatencyTest.py
@@ -0,0 +1,825 @@
+#!/usr/bin/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 queue
+import time
+
+from acts import asserts
+from acts.test_utils.net import connectivity_const as cconsts
+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
+
+
+class LatencyTest(AwareBaseTest):
+ """Set of tests for Wi-Fi Aware to measure latency of Aware operations."""
+ SERVICE_NAME = "GoogleTestServiceXY"
+
+ # number of second to 'reasonably' wait to make sure that devices synchronize
+ # with each other - useful for OOB test cases, where the OOB discovery would
+ # take some time
+ WAIT_FOR_CLUSTER = 5
+
+ def start_discovery_session(self, dut, session_id, is_publish, dtype):
+ """Start a discovery session
+
+ Args:
+ dut: Device under test
+ session_id: ID of the Aware session in which to start discovery
+ is_publish: True for a publish session, False for subscribe session
+ dtype: Type of the discovery session
+
+ Returns:
+ Discovery session started event.
+ """
+ config = {}
+ config[aconsts.DISCOVERY_KEY_DISCOVERY_TYPE] = dtype
+ config[aconsts.DISCOVERY_KEY_SERVICE_NAME] = "GoogleTestServiceXY"
+
+ if is_publish:
+ disc_id = dut.droid.wifiAwarePublish(session_id, config)
+ event_name = aconsts.SESSION_CB_ON_PUBLISH_STARTED
+ else:
+ disc_id = dut.droid.wifiAwareSubscribe(session_id, config)
+ event_name = aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED
+
+ event = autils.wait_for_event(dut, event_name)
+ return disc_id, event
+
+ def run_synchronization_latency(self, results, do_unsolicited_passive,
+ dw_24ghz, dw_5ghz, num_iterations,
+ startup_offset, timeout_period):
+ """Run the synchronization latency test with the specified DW intervals.
+ There is no direct measure of synchronization. Instead starts a discovery
+ session as soon as possible and measures both probability of discovery
+ within a timeout period and the actual discovery time (not necessarily
+ accurate).
+
+ Args:
+ results: Result array to be populated - will add results (not erase it)
+ do_unsolicited_passive: True for unsolicited/passive, False for
+ solicited/active.
+ dw_24ghz: DW interval in the 2.4GHz band.
+ dw_5ghz: DW interval in the 5GHz band.
+ startup_offset: The start-up gap (in seconds) between the two devices
+ timeout_period: Time period over which to measure synchronization
+ """
+ key = "%s_dw24_%d_dw5_%d_offset_%d" % ("unsolicited_passive"
+ if do_unsolicited_passive else
+ "solicited_active", dw_24ghz,
+ dw_5ghz, startup_offset)
+ results[key] = {}
+ results[key]["num_iterations"] = num_iterations
+
+ p_dut = self.android_devices[0]
+ p_dut.pretty_name = "Publisher"
+ s_dut = self.android_devices[1]
+ s_dut.pretty_name = "Subscriber"
+
+ # override the default DW configuration
+ autils.config_power_settings(p_dut, dw_24ghz, dw_5ghz)
+ autils.config_power_settings(s_dut, dw_24ghz, dw_5ghz)
+
+ latencies = []
+ failed_discoveries = 0
+ for i in range(num_iterations):
+ # Publisher+Subscriber: attach and wait for confirmation
+ p_id = p_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(startup_offset)
+ s_id = s_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # start publish
+ p_disc_id, p_disc_event = self.start_discovery_session(
+ p_dut, p_id, True, aconsts.PUBLISH_TYPE_UNSOLICITED
+ if do_unsolicited_passive else aconsts.PUBLISH_TYPE_SOLICITED)
+
+ # start subscribe
+ s_disc_id, s_session_event = self.start_discovery_session(
+ s_dut, s_id, False, aconsts.SUBSCRIBE_TYPE_PASSIVE
+ if do_unsolicited_passive else aconsts.SUBSCRIBE_TYPE_ACTIVE)
+
+ # wait for discovery (allow for failures here since running lots of
+ # samples and would like to get the partial data even in the presence of
+ # errors)
+ try:
+ discovery_event = s_dut.ed.pop_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED, timeout_period)
+ s_dut.log.info(
+ "[Subscriber] SESSION_CB_ON_SERVICE_DISCOVERED: %s",
+ discovery_event["data"])
+ except queue.Empty:
+ s_dut.log.info("[Subscriber] Timed out while waiting for "
+ "SESSION_CB_ON_SERVICE_DISCOVERED")
+ failed_discoveries = failed_discoveries + 1
+ continue
+ finally:
+ # destroy sessions
+ p_dut.droid.wifiAwareDestroyDiscoverySession(p_disc_id)
+ s_dut.droid.wifiAwareDestroyDiscoverySession(s_disc_id)
+ p_dut.droid.wifiAwareDestroy(p_id)
+ s_dut.droid.wifiAwareDestroy(s_id)
+
+ # collect latency information
+ latencies.append(
+ discovery_event["data"][aconsts.SESSION_CB_KEY_TIMESTAMP_MS] -
+ s_session_event["data"][aconsts.SESSION_CB_KEY_TIMESTAMP_MS])
+ self.log.info("Latency #%d = %d" % (i, latencies[-1]))
+
+ autils.extract_stats(
+ s_dut,
+ data=latencies,
+ results=results[key],
+ key_prefix="",
+ log_prefix="Subscribe Session Sync/Discovery (%s, dw24=%d, dw5=%d)"
+ % ("Unsolicited/Passive" if do_unsolicited_passive else
+ "Solicited/Active", dw_24ghz, dw_5ghz))
+ results[key]["num_failed_discovery"] = failed_discoveries
+
+ def run_discovery_latency(self, results, do_unsolicited_passive, dw_24ghz,
+ dw_5ghz, num_iterations):
+ """Run the service discovery latency test with the specified DW intervals.
+
+ Args:
+ results: Result array to be populated - will add results (not erase it)
+ do_unsolicited_passive: True for unsolicited/passive, False for
+ solicited/active.
+ dw_24ghz: DW interval in the 2.4GHz band.
+ dw_5ghz: DW interval in the 5GHz band.
+ """
+ key = "%s_dw24_%d_dw5_%d" % ("unsolicited_passive"
+ if do_unsolicited_passive else
+ "solicited_active", dw_24ghz, dw_5ghz)
+ results[key] = {}
+ results[key]["num_iterations"] = num_iterations
+
+ p_dut = self.android_devices[0]
+ p_dut.pretty_name = "Publisher"
+ s_dut = self.android_devices[1]
+ s_dut.pretty_name = "Subscriber"
+
+ # override the default DW configuration
+ autils.config_power_settings(p_dut, dw_24ghz, dw_5ghz)
+ autils.config_power_settings(s_dut, dw_24ghz, dw_5ghz)
+
+ # Publisher+Subscriber: attach and wait for confirmation
+ p_id = p_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(self.device_startup_offset)
+ s_id = s_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # start publish
+ p_disc_event = self.start_discovery_session(
+ p_dut, p_id, True, aconsts.PUBLISH_TYPE_UNSOLICITED
+ if do_unsolicited_passive else aconsts.PUBLISH_TYPE_SOLICITED)
+
+ # wait for for devices to synchronize with each other - used so that first
+ # discovery isn't biased by synchronization.
+ time.sleep(self.WAIT_FOR_CLUSTER)
+
+ # loop, perform discovery, and collect latency information
+ latencies = []
+ failed_discoveries = 0
+ for i in range(num_iterations):
+ # start subscribe
+ s_disc_id, s_session_event = self.start_discovery_session(
+ s_dut, s_id, False, aconsts.SUBSCRIBE_TYPE_PASSIVE
+ if do_unsolicited_passive else aconsts.SUBSCRIBE_TYPE_ACTIVE)
+
+ # wait for discovery (allow for failures here since running lots of
+ # samples and would like to get the partial data even in the presence of
+ # errors)
+ try:
+ discovery_event = s_dut.ed.pop_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ autils.EVENT_TIMEOUT)
+ except queue.Empty:
+ s_dut.log.info("[Subscriber] Timed out while waiting for "
+ "SESSION_CB_ON_SERVICE_DISCOVERED")
+ failed_discoveries = failed_discoveries + 1
+ continue
+ finally:
+ # destroy subscribe
+ s_dut.droid.wifiAwareDestroyDiscoverySession(s_disc_id)
+
+ # collect latency information
+ latencies.append(
+ discovery_event["data"][aconsts.SESSION_CB_KEY_TIMESTAMP_MS] -
+ s_session_event["data"][aconsts.SESSION_CB_KEY_TIMESTAMP_MS])
+ self.log.info("Latency #%d = %d" % (i, latencies[-1]))
+
+ autils.extract_stats(
+ s_dut,
+ data=latencies,
+ results=results[key],
+ key_prefix="",
+ log_prefix="Subscribe Session Discovery (%s, dw24=%d, dw5=%d)" %
+ ("Unsolicited/Passive" if do_unsolicited_passive else
+ "Solicited/Active", dw_24ghz, dw_5ghz))
+ results[key]["num_failed_discovery"] = failed_discoveries
+
+ # clean up
+ p_dut.droid.wifiAwareDestroyAll()
+ s_dut.droid.wifiAwareDestroyAll()
+
+ def run_message_latency(self, results, dw_24ghz, dw_5ghz, num_iterations):
+ """Run the message tx latency test with the specified DW intervals.
+
+ Args:
+ results: Result array to be populated - will add results (not erase it)
+ dw_24ghz: DW interval in the 2.4GHz band.
+ dw_5ghz: DW interval in the 5GHz band.
+ """
+ key = "dw24_%d_dw5_%d" % (dw_24ghz, dw_5ghz)
+ results[key] = {}
+ results[key]["num_iterations"] = num_iterations
+
+ p_dut = self.android_devices[0]
+ s_dut = self.android_devices[1]
+
+ # override the default DW configuration
+ autils.config_power_settings(p_dut, dw_24ghz, dw_5ghz)
+ autils.config_power_settings(s_dut, dw_24ghz, dw_5ghz)
+
+ # Start up a discovery session
+ (p_id, s_id, p_disc_id, s_disc_id,
+ peer_id_on_sub) = 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)
+
+ latencies = []
+ failed_tx = 0
+ messages_rx = 0
+ missing_rx = 0
+ corrupted_rx = 0
+ for i in range(num_iterations):
+ # send message
+ msg_s2p = "Message Subscriber -> Publisher #%d" % i
+ next_msg_id = self.get_next_msg_id()
+ s_dut.droid.wifiAwareSendMessage(s_disc_id, peer_id_on_sub,
+ next_msg_id, msg_s2p, 0)
+
+ # wait for Tx confirmation
+ try:
+ sub_tx_msg_event = s_dut.ed.pop_event(
+ aconsts.SESSION_CB_ON_MESSAGE_SENT,
+ 2 * autils.EVENT_TIMEOUT)
+ latencies.append(sub_tx_msg_event["data"][
+ aconsts.SESSION_CB_KEY_LATENCY_MS])
+ except queue.Empty:
+ s_dut.log.info("[Subscriber] Timed out while waiting for "
+ "SESSION_CB_ON_MESSAGE_SENT")
+ failed_tx = failed_tx + 1
+ continue
+
+ # wait for Rx confirmation (and validate contents)
+ try:
+ pub_rx_msg_event = p_dut.ed.pop_event(
+ aconsts.SESSION_CB_ON_MESSAGE_RECEIVED,
+ 2 * autils.EVENT_TIMEOUT)
+ messages_rx = messages_rx + 1
+ if (pub_rx_msg_event["data"]
+ [aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING] != msg_s2p):
+ corrupted_rx = corrupted_rx + 1
+ except queue.Empty:
+ s_dut.log.info("[Publisher] Timed out while waiting for "
+ "SESSION_CB_ON_MESSAGE_RECEIVED")
+ missing_rx = missing_rx + 1
+ continue
+
+ autils.extract_stats(
+ s_dut,
+ data=latencies,
+ results=results[key],
+ key_prefix="",
+ log_prefix="Subscribe Session Discovery (dw24=%d, dw5=%d)" %
+ (dw_24ghz, dw_5ghz))
+ results[key]["failed_tx"] = failed_tx
+ results[key]["messages_rx"] = messages_rx
+ results[key]["missing_rx"] = missing_rx
+ results[key]["corrupted_rx"] = corrupted_rx
+
+ # clean up
+ p_dut.droid.wifiAwareDestroyAll()
+ s_dut.droid.wifiAwareDestroyAll()
+
+ def run_ndp_oob_latency(self, results, dw_24ghz, dw_5ghz, num_iterations):
+ """Runs the NDP setup with OOB (out-of-band) discovery latency test.
+
+ Args:
+ results: Result array to be populated - will add results (not erase it)
+ dw_24ghz: DW interval in the 2.4GHz band.
+ dw_5ghz: DW interval in the 5GHz band.
+ """
+ key_avail = "on_avail_dw24_%d_dw5_%d" % (dw_24ghz, dw_5ghz)
+ key_link_props = "link_props_dw24_%d_dw5_%d" % (dw_24ghz, dw_5ghz)
+ results[key_avail] = {}
+ results[key_link_props] = {}
+ results[key_avail]["num_iterations"] = num_iterations
+
+ init_dut = self.android_devices[0]
+ init_dut.pretty_name = 'Initiator'
+ resp_dut = self.android_devices[1]
+ resp_dut.pretty_name = 'Responder'
+
+ # override the default DW configuration
+ autils.config_power_settings(init_dut, dw_24ghz, dw_5ghz)
+ autils.config_power_settings(resp_dut, dw_24ghz, dw_5ghz)
+
+ # Initiator+Responder: attach and wait for confirmation & identity
+ init_id = init_dut.droid.wifiAwareAttach(True)
+ autils.wait_for_event(init_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ init_ident_event = autils.wait_for_event(
+ init_dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+ init_mac = init_ident_event['data']['mac']
+ time.sleep(self.device_startup_offset)
+ resp_id = resp_dut.droid.wifiAwareAttach(True)
+ autils.wait_for_event(resp_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ resp_ident_event = autils.wait_for_event(
+ resp_dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+ resp_mac = resp_ident_event['data']['mac']
+
+ # wait for for devices to synchronize with each other - there are no other
+ # mechanisms to make sure this happens for OOB discovery (except retrying
+ # to execute the data-path request)
+ time.sleep(autils.WAIT_FOR_CLUSTER)
+
+ on_available_latencies = []
+ link_props_latencies = []
+ ndp_setup_failures = 0
+ for i in range(num_iterations):
+ # Responder: request network
+ resp_req_key = autils.request_network(
+ resp_dut,
+ resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ resp_id, aconsts.DATA_PATH_RESPONDER, init_mac, None))
+
+ # Initiator: request network
+ init_req_key = autils.request_network(
+ init_dut,
+ init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ init_id, aconsts.DATA_PATH_INITIATOR, resp_mac, None))
+
+ # Initiator & Responder: wait for network formation
+ got_on_available = False
+ got_on_link_props = False
+ while not got_on_available or not got_on_link_props:
+ try:
+ nc_event = init_dut.ed.pop_event(
+ cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT)
+ if nc_event["data"][
+ cconsts.
+ NETWORK_CB_KEY_EVENT] == cconsts.NETWORK_CB_AVAILABLE:
+ got_on_available = True
+ on_available_latencies.append(
+ nc_event["data"][cconsts.NETWORK_CB_KEY_CURRENT_TS]
+ -
+ nc_event["data"][cconsts.NETWORK_CB_KEY_CREATE_TS])
+ elif (nc_event["data"][cconsts.NETWORK_CB_KEY_EVENT] ==
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED):
+ got_on_link_props = True
+ link_props_latencies.append(
+ nc_event["data"][cconsts.NETWORK_CB_KEY_CURRENT_TS]
+ -
+ nc_event["data"][cconsts.NETWORK_CB_KEY_CREATE_TS])
+ except queue.Empty:
+ ndp_setup_failures = ndp_setup_failures + 1
+ init_dut.log.info(
+ "[Initiator] Timed out while waiting for "
+ "EVENT_NETWORK_CALLBACK")
+ break
+
+ # clean-up
+ init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key)
+ resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key)
+
+ # wait to make sure previous NDP terminated, otherwise its termination
+ # time will be counted in the setup latency!
+ time.sleep(2)
+
+ autils.extract_stats(
+ init_dut,
+ data=on_available_latencies,
+ results=results[key_avail],
+ key_prefix="",
+ log_prefix="NDP setup OnAvailable(dw24=%d, dw5=%d)" % (dw_24ghz,
+ dw_5ghz))
+ autils.extract_stats(
+ init_dut,
+ data=link_props_latencies,
+ results=results[key_link_props],
+ key_prefix="",
+ log_prefix="NDP setup OnLinkProperties (dw24=%d, dw5=%d)" %
+ (dw_24ghz, dw_5ghz))
+ results[key_avail]["ndp_setup_failures"] = ndp_setup_failures
+
+ def run_end_to_end_latency(self, results, dw_24ghz, dw_5ghz,
+ num_iterations, startup_offset, include_setup):
+ """Measure the latency for end-to-end communication link setup:
+ - Start Aware
+ - Discovery
+ - Message from Sub -> Pub
+ - Message from Pub -> Sub
+ - NDP setup
+
+ Args:
+ results: Result array to be populated - will add results (not erase it)
+ dw_24ghz: DW interval in the 2.4GHz band.
+ dw_5ghz: DW interval in the 5GHz band.
+ startup_offset: The start-up gap (in seconds) between the two devices
+ include_setup: True to include the cluster setup in the latency
+ measurements.
+ """
+ key = "dw24_%d_dw5_%d" % (dw_24ghz, dw_5ghz)
+ results[key] = {}
+ results[key]["num_iterations"] = num_iterations
+
+ p_dut = self.android_devices[0]
+ p_dut.pretty_name = "Publisher"
+ s_dut = self.android_devices[1]
+ s_dut.pretty_name = "Subscriber"
+
+ # override the default DW configuration
+ autils.config_power_settings(p_dut, dw_24ghz, dw_5ghz)
+ autils.config_power_settings(s_dut, dw_24ghz, dw_5ghz)
+
+ latencies = []
+
+ # allow for failures here since running lots of samples and would like to
+ # get the partial data even in the presence of errors
+ failures = 0
+
+ if not include_setup:
+ # Publisher+Subscriber: attach and wait for confirmation
+ p_id = p_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(startup_offset)
+ s_id = s_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ for i in range(num_iterations):
+ while (True): # for pseudo-goto/finalize
+ timestamp_start = time.perf_counter()
+
+ if include_setup:
+ # Publisher+Subscriber: attach and wait for confirmation
+ p_id = p_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(startup_offset)
+ s_id = s_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # start publish
+ p_disc_id, p_disc_event = self.start_discovery_session(
+ p_dut, p_id, True, aconsts.PUBLISH_TYPE_UNSOLICITED)
+
+ # start subscribe
+ s_disc_id, s_session_event = self.start_discovery_session(
+ s_dut, s_id, False, aconsts.SUBSCRIBE_TYPE_PASSIVE)
+
+ # wait for discovery (allow for failures here since running lots of
+ # samples and would like to get the partial data even in the presence of
+ # errors)
+ try:
+ event = s_dut.ed.pop_event(
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ autils.EVENT_TIMEOUT)
+ s_dut.log.info(
+ "[Subscriber] SESSION_CB_ON_SERVICE_DISCOVERED: %s",
+ event["data"])
+ peer_id_on_sub = event['data'][
+ aconsts.SESSION_CB_KEY_PEER_ID]
+ except queue.Empty:
+ s_dut.log.info("[Subscriber] Timed out while waiting for "
+ "SESSION_CB_ON_SERVICE_DISCOVERED")
+ failures = failures + 1
+ break
+
+ # message from Sub -> Pub
+ msg_s2p = "Message Subscriber -> Publisher #%d" % i
+ next_msg_id = self.get_next_msg_id()
+ s_dut.droid.wifiAwareSendMessage(s_disc_id, peer_id_on_sub,
+ next_msg_id, msg_s2p, 0)
+
+ # wait for Tx confirmation
+ try:
+ s_dut.ed.pop_event(aconsts.SESSION_CB_ON_MESSAGE_SENT,
+ autils.EVENT_TIMEOUT)
+ except queue.Empty:
+ s_dut.log.info("[Subscriber] Timed out while waiting for "
+ "SESSION_CB_ON_MESSAGE_SENT")
+ failures = failures + 1
+ break
+
+ # wait for Rx confirmation (and validate contents)
+ try:
+ event = p_dut.ed.pop_event(
+ aconsts.SESSION_CB_ON_MESSAGE_RECEIVED,
+ autils.EVENT_TIMEOUT)
+ peer_id_on_pub = event['data'][
+ aconsts.SESSION_CB_KEY_PEER_ID]
+ if (event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING]
+ != msg_s2p):
+ p_dut.log.info(
+ "[Publisher] Corrupted input message - %s", event)
+ failures = failures + 1
+ break
+ except queue.Empty:
+ p_dut.log.info("[Publisher] Timed out while waiting for "
+ "SESSION_CB_ON_MESSAGE_RECEIVED")
+ failures = failures + 1
+ break
+
+ # message from Pub -> Sub
+ msg_p2s = "Message Publisher -> Subscriber #%d" % i
+ next_msg_id = self.get_next_msg_id()
+ p_dut.droid.wifiAwareSendMessage(p_disc_id, peer_id_on_pub,
+ next_msg_id, msg_p2s, 0)
+
+ # wait for Tx confirmation
+ try:
+ p_dut.ed.pop_event(aconsts.SESSION_CB_ON_MESSAGE_SENT,
+ autils.EVENT_TIMEOUT)
+ except queue.Empty:
+ p_dut.log.info("[Publisher] Timed out while waiting for "
+ "SESSION_CB_ON_MESSAGE_SENT")
+ failures = failures + 1
+ break
+
+ # wait for Rx confirmation (and validate contents)
+ try:
+ event = s_dut.ed.pop_event(
+ aconsts.SESSION_CB_ON_MESSAGE_RECEIVED,
+ autils.EVENT_TIMEOUT)
+ if (event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING]
+ != msg_p2s):
+ s_dut.log.info(
+ "[Subscriber] Corrupted input message - %s", event)
+ failures = failures + 1
+ break
+ except queue.Empty:
+ s_dut.log.info("[Subscriber] Timed out while waiting for "
+ "SESSION_CB_ON_MESSAGE_RECEIVED")
+ failures = failures + 1
+ break
+
+ # create NDP
+
+ # Publisher: request network
+ p_req_key = autils.request_network(
+ p_dut,
+ p_dut.droid.wifiAwareCreateNetworkSpecifier(
+ p_disc_id, peer_id_on_pub, None))
+
+ # Subscriber: request network
+ s_req_key = autils.request_network(
+ s_dut,
+ s_dut.droid.wifiAwareCreateNetworkSpecifier(
+ s_disc_id, peer_id_on_sub, None))
+
+ # Publisher & Subscriber: wait for network formation
+ try:
+ p_net_event = autils.wait_for_event_with_keys(
+ p_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+ s_net_event = autils.wait_for_event_with_keys(
+ s_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+ except:
+ failures = failures + 1
+ break
+
+ p_aware_if = p_net_event["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ s_aware_if = s_net_event["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+
+ p_ipv6 = \
+ p_dut.droid.connectivityGetLinkLocalIpv6Address(p_aware_if).split("%")[
+ 0]
+ s_ipv6 = \
+ s_dut.droid.connectivityGetLinkLocalIpv6Address(s_aware_if).split("%")[
+ 0]
+
+ p_dut.log.info("[Publisher] IF=%s, IPv6=%s", p_aware_if,
+ p_ipv6)
+ s_dut.log.info("[Subscriber] IF=%s, IPv6=%s", s_aware_if,
+ s_ipv6)
+
+ latencies.append(time.perf_counter() - timestamp_start)
+ break
+
+ # destroy sessions
+ p_dut.droid.wifiAwareDestroyDiscoverySession(p_disc_id)
+ s_dut.droid.wifiAwareDestroyDiscoverySession(s_disc_id)
+ if include_setup:
+ p_dut.droid.wifiAwareDestroy(p_id)
+ s_dut.droid.wifiAwareDestroy(s_id)
+
+ autils.extract_stats(
+ p_dut,
+ data=latencies,
+ results=results[key],
+ key_prefix="",
+ log_prefix="End-to-End(dw24=%d, dw5=%d)" % (dw_24ghz, dw_5ghz))
+ results[key]["failures"] = failures
+
+ ########################################################################
+
+ def test_synchronization_default_dws(self):
+ """Measure the device synchronization for default dws. Loop over values
+ from 0 to 4 seconds."""
+ results = {}
+ for startup_offset in range(5):
+ self.run_synchronization_latency(
+ results=results,
+ do_unsolicited_passive=True,
+ dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+ dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
+ num_iterations=10,
+ startup_offset=startup_offset,
+ timeout_period=20)
+ asserts.explicit_pass(
+ "test_synchronization_default_dws finished", extras=results)
+
+ def test_synchronization_non_interactive_dws(self):
+ """Measure the device synchronization for non-interactive dws. Loop over
+ values from 0 to 4 seconds."""
+ results = {}
+ for startup_offset in range(5):
+ self.run_synchronization_latency(
+ results=results,
+ do_unsolicited_passive=True,
+ dw_24ghz=aconsts.POWER_DW_24_NON_INTERACTIVE,
+ dw_5ghz=aconsts.POWER_DW_5_NON_INTERACTIVE,
+ num_iterations=10,
+ startup_offset=startup_offset,
+ timeout_period=20)
+ asserts.explicit_pass(
+ "test_synchronization_non_interactive_dws finished",
+ extras=results)
+
+ def test_discovery_latency_default_dws(self):
+ """Measure the service discovery latency with the default DW configuration.
+ """
+ results = {}
+ self.run_discovery_latency(
+ results=results,
+ do_unsolicited_passive=True,
+ dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+ dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
+ num_iterations=100)
+ asserts.explicit_pass(
+ "test_discovery_latency_default_parameters finished",
+ extras=results)
+
+ def test_discovery_latency_non_interactive_dws(self):
+ """Measure the service discovery latency with the DW configuration for non
+ -interactive mode (lower power)."""
+ results = {}
+ self.run_discovery_latency(
+ results=results,
+ do_unsolicited_passive=True,
+ dw_24ghz=aconsts.POWER_DW_24_NON_INTERACTIVE,
+ dw_5ghz=aconsts.POWER_DW_5_NON_INTERACTIVE,
+ num_iterations=100)
+ asserts.explicit_pass(
+ "test_discovery_latency_non_interactive_dws finished",
+ extras=results)
+
+ def test_discovery_latency_all_dws(self):
+ """Measure the service discovery latency with all DW combinations (low
+ iteration count)"""
+ results = {}
+ for dw24 in range(1, 6): # permitted values: 1-5
+ for dw5 in range(0, 6): # permitted values: 0, 1-5
+ self.run_discovery_latency(
+ results=results,
+ do_unsolicited_passive=True,
+ dw_24ghz=dw24,
+ dw_5ghz=dw5,
+ num_iterations=10)
+ asserts.explicit_pass(
+ "test_discovery_latency_all_dws finished", extras=results)
+
+ def test_message_latency_default_dws(self):
+ """Measure the send message latency with the default DW configuration. Test
+ performed on non-queued message transmission - i.e. waiting for confirmation
+ of reception (ACK) before sending the next message."""
+ results = {}
+ self.run_message_latency(
+ results=results,
+ dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+ dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
+ num_iterations=100)
+ asserts.explicit_pass(
+ "test_message_latency_default_dws finished", extras=results)
+
+ def test_message_latency_non_interactive_dws(self):
+ """Measure the send message latency with the DW configuration for
+ non-interactive mode. Test performed on non-queued message transmission -
+ i.e. waiting for confirmation of reception (ACK) before sending the next
+ message."""
+ results = {}
+ self.run_message_latency(
+ results=results,
+ dw_24ghz=aconsts.POWER_DW_24_NON_INTERACTIVE,
+ dw_5ghz=aconsts.POWER_DW_5_NON_INTERACTIVE,
+ num_iterations=100)
+ asserts.explicit_pass(
+ "test_message_latency_non_interactive_dws finished",
+ extras=results)
+
+ def test_oob_ndp_setup_latency_default_dws(self):
+ """Measure the NDP setup latency with the default DW configuration. The
+ NDP is setup with OOB (out-of-band) configuration."""
+ results = {}
+ self.run_ndp_oob_latency(
+ results=results,
+ dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+ dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
+ num_iterations=100)
+ asserts.explicit_pass(
+ "test_ndp_setup_latency_default_dws finished", extras=results)
+
+ def test_oob_ndp_setup_latency_non_interactive_dws(self):
+ """Measure the NDP setup latency with the DW configuration for
+ non-interactive mode. The NDP is setup with OOB (out-of-band)
+ configuration"""
+ results = {}
+ self.run_ndp_oob_latency(
+ results=results,
+ dw_24ghz=aconsts.POWER_DW_24_NON_INTERACTIVE,
+ dw_5ghz=aconsts.POWER_DW_5_NON_INTERACTIVE,
+ num_iterations=100)
+ asserts.explicit_pass(
+ "test_ndp_setup_latency_non_interactive_dws finished",
+ extras=results)
+
+ def test_end_to_end_latency_default_dws(self):
+ """Measure the latency for end-to-end communication link setup:
+ - Start Aware
+ - Discovery
+ - Message from Sub -> Pub
+ - Message from Pub -> Sub
+ - NDP setup
+ """
+ results = {}
+ self.run_end_to_end_latency(
+ results,
+ dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+ dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
+ num_iterations=10,
+ startup_offset=0,
+ include_setup=True)
+ asserts.explicit_pass(
+ "test_end_to_end_latency_default_dws finished", extras=results)
+
+ def test_end_to_end_latency_post_attach_default_dws(self):
+ """Measure the latency for end-to-end communication link setup without
+ the initial synchronization:
+ - Start Aware & synchronize initially
+ - Loop:
+ - Discovery
+ - Message from Sub -> Pub
+ - Message from Pub -> Sub
+ - NDP setup
+ """
+ results = {}
+ self.run_end_to_end_latency(
+ results,
+ dw_24ghz=aconsts.POWER_DW_24_INTERACTIVE,
+ dw_5ghz=aconsts.POWER_DW_5_INTERACTIVE,
+ num_iterations=10,
+ startup_offset=0,
+ include_setup=False)
+ asserts.explicit_pass(
+ "test_end_to_end_latency_post_attach_default_dws finished",
+ extras=results)
diff --git a/acts_tests/tests/google/wifi/aware/performance/ThroughputTest.py b/acts_tests/tests/google/wifi/aware/performance/ThroughputTest.py
new file mode 100644
index 0000000..2dab276
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/performance/ThroughputTest.py
@@ -0,0 +1,440 @@
+#!/usr/bin/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 json
+import pprint
+import queue
+import threading
+import time
+
+from acts import asserts
+from acts.test_utils.net import connectivity_const as cconsts
+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
+
+
+class ThroughputTest(AwareBaseTest):
+ """Set of tests for Wi-Fi Aware to measure latency of Aware operations."""
+
+ SERVICE_NAME = "GoogleTestServiceXYZ"
+
+ PASSPHRASE = "This is some random passphrase - very very secure!!"
+ PASSPHRASE2 = "This is some random passphrase - very very secure - but diff!!"
+
+ def request_network(self, dut, ns):
+ """Request a Wi-Fi Aware network.
+
+ Args:
+ dut: Device
+ ns: Network specifier
+ Returns: the request key
+ """
+ network_req = {"TransportType": 5, "NetworkSpecifier": ns}
+ return dut.droid.connectivityRequestWifiAwareNetwork(network_req)
+
+ def run_iperf_single_ndp_aware_only(self, use_ib, results):
+ """Measure iperf performance on a single NDP, with Aware enabled and no
+ infrastructure connection - i.e. device is not associated to an AP.
+
+ Args:
+ use_ib: True to use in-band discovery, False to use out-of-band discovery.
+ results: Dictionary into which to place test results.
+ """
+ init_dut = self.android_devices[0]
+ resp_dut = self.android_devices[1]
+
+ if use_ib:
+ # note: Publisher = Responder, Subscribe = Initiator
+ (resp_req_key, init_req_key, resp_aware_if, init_aware_if,
+ resp_ipv6, init_ipv6) = autils.create_ib_ndp(
+ resp_dut, init_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)
+ else:
+ (init_req_key, resp_req_key, init_aware_if, resp_aware_if,
+ init_ipv6, resp_ipv6) = autils.create_oob_ndp(init_dut, resp_dut)
+ self.log.info("Interface names: I=%s, R=%s", init_aware_if,
+ resp_aware_if)
+ self.log.info("Interface addresses (IPv6): I=%s, R=%s", init_ipv6,
+ resp_ipv6)
+
+ # Run iperf3
+ result, data = init_dut.run_iperf_server("-D")
+ asserts.assert_true(result, "Can't start iperf3 server")
+
+ result, data = resp_dut.run_iperf_client("%s" % init_ipv6, "-6 -J")
+ self.log.debug(data)
+ asserts.assert_true(result,
+ "Failure starting/running iperf3 in client mode")
+ self.log.debug(pprint.pformat(data))
+
+ # clean-up
+ resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key)
+ init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key)
+
+ # Collect results
+ data_json = json.loads("".join(data))
+ if "error" in data_json:
+ asserts.fail(
+ "iperf run failed: %s" % data_json["error"], extras=data_json)
+ results["tx_rate"] = data_json["end"]["sum_sent"]["bits_per_second"]
+ results["rx_rate"] = data_json["end"]["sum_received"][
+ "bits_per_second"]
+ self.log.info("iPerf3: Sent = %d bps Received = %d bps",
+ results["tx_rate"], results["rx_rate"])
+
+ def run_iperf(self, q, dut, peer_dut, peer_aware_if, dut_ipv6, port):
+ """Runs iperf and places results in the queue.
+
+ Args:
+ q: The queue into which to place the results
+ dut: The DUT on which to run the iperf server command.
+ peer_dut: The DUT on which to run the iperf client command.
+ peer_aware_if: The interface on the DUT.
+ dut_ipv6: The IPv6 address of the server.
+ port: The port to use for the server and client.
+ """
+ result, data = dut.run_iperf_server("-D -p %d" % port)
+ asserts.assert_true(result, "Can't start iperf3 server")
+
+ result, data = peer_dut.run_iperf_client("%s" % dut_ipv6,
+ "-6 -J -p %d" % port)
+ self.log.debug(data)
+ q.put((result, data))
+
+ def run_iperf_max_ndp_aware_only(self, results):
+ """Measure iperf performance on the max number of concurrent OOB NDPs, with
+ Aware enabled and no infrastructure connection - i.e. device is not
+ associated to an AP.
+
+ Note: the test requires MAX_NDP + 1 devices to be validated. If these are
+ not available the test will fail.
+
+ Args:
+ results: Dictionary into which to place test results.
+ """
+ dut = self.android_devices[0]
+
+ # get max NDP: using first available device (assumes all devices are the
+ # same)
+ max_ndp = dut.aware_capabilities[aconsts.CAP_MAX_NDP_SESSIONS]
+ asserts.assert_true(
+ len(self.android_devices) > max_ndp,
+ 'Needed %d devices to run the test, have %d' %
+ (max_ndp + 1, len(self.android_devices)))
+
+ # create all NDPs
+ dut_aware_if = None
+ dut_ipv6 = None
+ peers_aware_ifs = []
+ peers_ipv6s = []
+ dut_requests = []
+ peers_requests = []
+ for i in range(max_ndp):
+ (init_req_key, resp_req_key, init_aware_if, resp_aware_if,
+ init_ipv6, resp_ipv6) = autils.create_oob_ndp(
+ dut, self.android_devices[i + 1])
+ self.log.info("Interface names: I=%s, R=%s", init_aware_if,
+ resp_aware_if)
+ self.log.info("Interface addresses (IPv6): I=%s, R=%s", init_ipv6,
+ resp_ipv6)
+
+ dut_requests.append(init_req_key)
+ peers_requests.append(resp_req_key)
+ if dut_aware_if is None:
+ dut_aware_if = init_aware_if
+ else:
+ asserts.assert_equal(
+ dut_aware_if, init_aware_if,
+ "DUT (Initiator) interface changed on subsequent NDPs!?")
+ if dut_ipv6 is None:
+ dut_ipv6 = init_ipv6
+ else:
+ asserts.assert_equal(
+ dut_ipv6, init_ipv6,
+ "DUT (Initiator) IPv6 changed on subsequent NDPs!?")
+ peers_aware_ifs.append(resp_aware_if)
+ peers_ipv6s.append(resp_ipv6)
+
+ # create threads, start them, and wait for all to finish
+ base_port = 5000
+ q = queue.Queue()
+ threads = []
+ for i in range(max_ndp):
+ threads.append(
+ threading.Thread(
+ target=self.run_iperf,
+ args=(q, dut, self.android_devices[i + 1],
+ peers_aware_ifs[i], dut_ipv6, base_port + i)))
+
+ for thread in threads:
+ thread.start()
+
+ for thread in threads:
+ thread.join()
+
+ # cleanup
+ for i in range(max_ndp):
+ dut.droid.connectivityUnregisterNetworkCallback(dut_requests[i])
+ self.android_devices[
+ i + 1].droid.connectivityUnregisterNetworkCallback(
+ peers_requests[i])
+
+ # collect data
+ for i in range(max_ndp):
+ results[i] = {}
+ result, data = q.get()
+ asserts.assert_true(
+ result, "Failure starting/running iperf3 in client mode")
+ self.log.debug(pprint.pformat(data))
+ data_json = json.loads("".join(data))
+ if "error" in data_json:
+ asserts.fail(
+ "iperf run failed: %s" % data_json["error"],
+ extras=data_json)
+ results[i]["tx_rate"] = data_json["end"]["sum_sent"][
+ "bits_per_second"]
+ results[i]["rx_rate"] = data_json["end"]["sum_received"][
+ "bits_per_second"]
+ self.log.info("iPerf3: Sent = %d bps Received = %d bps",
+ results[i]["tx_rate"], results[i]["rx_rate"])
+
+ ########################################################################
+
+ def test_iperf_single_ndp_aware_only_ib(self):
+ """Measure throughput using iperf on a single NDP, with Aware enabled and
+ no infrastructure connection. Use in-band discovery."""
+ results = {}
+ self.run_iperf_single_ndp_aware_only(use_ib=True, results=results)
+ asserts.explicit_pass(
+ "test_iperf_single_ndp_aware_only_ib passes", extras=results)
+
+ def test_iperf_single_ndp_aware_only_oob(self):
+ """Measure throughput using iperf on a single NDP, with Aware enabled and
+ no infrastructure connection. Use out-of-band discovery."""
+ results = {}
+ self.run_iperf_single_ndp_aware_only(use_ib=False, results=results)
+ asserts.explicit_pass(
+ "test_iperf_single_ndp_aware_only_oob passes", extras=results)
+
+ def test_iperf_max_ndp_aware_only_oob(self):
+ """Measure throughput using iperf on all possible concurrent NDPs, with
+ Aware enabled and no infrastructure connection. Use out-of-band discovery.
+ """
+ results = {}
+ self.run_iperf_max_ndp_aware_only(results=results)
+ asserts.explicit_pass(
+ "test_iperf_max_ndp_aware_only_oob passes", extras=results)
+
+ ########################################################################
+
+ def run_iperf_max_ndi_aware_only(self, sec_configs, results):
+ """Measure iperf performance on multiple NDPs between 2 devices using
+ different security configurations (and hence different NDIs). Test with
+ Aware enabled and no infrastructure connection - i.e. device is not
+ associated to an AP.
+
+ The security configuration can be:
+ - None: open
+ - String: passphrase
+ - otherwise: PMK (byte array)
+
+ Args:
+ sec_configs: list of security configurations
+ results: Dictionary into which to place test results.
+ """
+ init_dut = self.android_devices[0]
+ init_dut.pretty_name = "Initiator"
+ resp_dut = self.android_devices[1]
+ resp_dut.pretty_name = "Responder"
+
+ asserts.skip_if(
+ init_dut.aware_capabilities[aconsts.CAP_MAX_NDI_INTERFACES] <
+ len(sec_configs)
+ or resp_dut.aware_capabilities[aconsts.CAP_MAX_NDI_INTERFACES] <
+ len(sec_configs),
+ "Initiator or Responder do not support multiple NDIs")
+
+ init_id, init_mac = autils.attach_with_identity(init_dut)
+ resp_id, resp_mac = autils.attach_with_identity(resp_dut)
+
+ # wait for for devices to synchronize with each other - there are no other
+ # mechanisms to make sure this happens for OOB discovery (except retrying
+ # to execute the data-path request)
+ time.sleep(autils.WAIT_FOR_CLUSTER)
+
+ resp_req_keys = []
+ init_req_keys = []
+ resp_aware_ifs = []
+ init_aware_ifs = []
+ resp_aware_ipv6s = []
+ init_aware_ipv6s = []
+
+ for sec in sec_configs:
+ # Responder: request network
+ resp_req_key = autils.request_network(
+ resp_dut,
+ autils.get_network_specifier(resp_dut, resp_id,
+ aconsts.DATA_PATH_RESPONDER,
+ init_mac, sec))
+ resp_req_keys.append(resp_req_key)
+
+ # Initiator: request network
+ init_req_key = autils.request_network(
+ init_dut,
+ autils.get_network_specifier(init_dut, init_id,
+ aconsts.DATA_PATH_INITIATOR,
+ resp_mac, sec))
+ init_req_keys.append(init_req_key)
+
+ # Wait for network
+ init_net_event_nc = autils.wait_for_event_with_keys(
+ init_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, init_req_key))
+ resp_net_event_nc = autils.wait_for_event_with_keys(
+ resp_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
+
+ # validate no leak of information
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in init_net_event_nc[
+ "data"], "Network specifier leak!")
+ asserts.assert_false(
+ cconsts.NETWORK_CB_KEY_NETWORK_SPECIFIER in resp_net_event_nc[
+ "data"], "Network specifier leak!")
+
+ # note that Init <-> Resp since IPv6 are of peer's!
+ resp_ipv6 = init_net_event_nc["data"][aconsts.NET_CAP_IPV6]
+ init_ipv6 = resp_net_event_nc["data"][aconsts.NET_CAP_IPV6]
+
+ init_net_event_lp = autils.wait_for_event_with_keys(
+ init_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, init_req_key))
+ resp_net_event_lp = autils.wait_for_event_with_keys(
+ resp_dut, cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, resp_req_key))
+
+ resp_aware_ifs.append(resp_net_event_lp["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME])
+ init_aware_ifs.append(init_net_event_lp["data"][
+ cconsts.NETWORK_CB_KEY_INTERFACE_NAME])
+
+ resp_aware_ipv6s.append(resp_ipv6)
+ init_aware_ipv6s.append(init_ipv6)
+
+ self.log.info("Initiator interfaces/ipv6: %s / %s", init_aware_ifs,
+ init_aware_ipv6s)
+ self.log.info("Responder interfaces/ipv6: %s / %s", resp_aware_ifs,
+ resp_aware_ipv6s)
+
+ # create threads, start them, and wait for all to finish
+ base_port = 5000
+ q = queue.Queue()
+ threads = []
+ for i in range(len(sec_configs)):
+ threads.append(
+ threading.Thread(
+ target=self.run_iperf,
+ args=(q, init_dut, resp_dut, resp_aware_ifs[i],
+ init_aware_ipv6s[i], base_port + i)))
+
+ for thread in threads:
+ thread.start()
+
+ for thread in threads:
+ thread.join()
+
+ # release requests
+ for resp_req_key in resp_req_keys:
+ resp_dut.droid.connectivityUnregisterNetworkCallback(resp_req_key)
+ for init_req_key in init_req_keys:
+ init_dut.droid.connectivityUnregisterNetworkCallback(init_req_key)
+
+ # collect data
+ for i in range(len(sec_configs)):
+ results[i] = {}
+ result, data = q.get()
+ asserts.assert_true(
+ result, "Failure starting/running iperf3 in client mode")
+ self.log.debug(pprint.pformat(data))
+ data_json = json.loads("".join(data))
+ if "error" in data_json:
+ asserts.fail(
+ "iperf run failed: %s" % data_json["error"],
+ extras=data_json)
+ results[i]["tx_rate"] = data_json["end"]["sum_sent"][
+ "bits_per_second"]
+ results[i]["rx_rate"] = data_json["end"]["sum_received"][
+ "bits_per_second"]
+ self.log.info("iPerf3: Sent = %d bps Received = %d bps",
+ results[i]["tx_rate"], results[i]["rx_rate"])
+
+ def test_iperf_max_ndi_aware_only_passphrases(self):
+ """Test throughput for multiple NDIs configured with different passphrases.
+ """
+ results = {}
+ self.run_iperf_max_ndi_aware_only(
+ [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/tests/google/wifi/aware/performance/WifiAwareRvrTest.py b/acts_tests/tests/google/wifi/aware/performance/WifiAwareRvrTest.py
new file mode 100644
index 0000000..b1e71e1
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/performance/WifiAwareRvrTest.py
@@ -0,0 +1,622 @@
+#!/usr/bin/env python3.4
+#
+# 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 collections
+import json
+import logging
+import os
+from acts import asserts
+from acts import base_test
+from acts import utils
+from acts.controllers import iperf_server as ipf
+from acts.controllers import iperf_client as ipc
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
+from acts.test_utils.wifi import ota_sniffer
+from acts.test_utils.wifi import wifi_retail_ap as retail_ap
+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.aware import aware_const as aconsts
+from acts.test_utils.wifi.aware import aware_test_utils as autils
+from WifiRvrTest import WifiRvrTest
+
+AccessPointTuple = collections.namedtuple(('AccessPointTuple'),
+ ['ap_settings'])
+
+
+class WifiAwareRvrTest(WifiRvrTest):
+
+ # message ID counter to make sure all uses are unique
+ msg_id = 0
+
+ # offset (in seconds) to separate the start-up of multiple devices.
+ # De-synchronizes the start-up time so that they don't start and stop scanning
+ # at the same time - which can lead to very long clustering times.
+ device_startup_offset = 2
+
+ SERVICE_NAME = "GoogleTestServiceXYZ"
+
+ PASSPHRASE = "This is some random passphrase - very very secure!!"
+ PASSPHRASE2 = "This is some random passphrase - very very secure - but diff!!"
+
+ def __init__(self, controllers):
+ base_test.BaseTestClass.__init__(self, controllers)
+ self.testcase_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_case())
+ self.testclass_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_class())
+ self.publish_testcase_metrics = True
+
+ def setup_class(self):
+ """Initializes common test hardware and parameters.
+
+ This function initializes hardwares and compiles parameters that are
+ common to all tests in this class.
+ """
+ req_params = [
+ 'aware_rvr_test_params', 'testbed_params',
+ 'aware_default_power_mode', 'dbs_supported_models'
+ ]
+ opt_params = ['RetailAccessPoints', 'ap_networks', 'OTASniffer']
+ self.unpack_userparams(req_params, opt_params)
+ if hasattr(self, 'RetailAccessPoints'):
+ self.access_points = retail_ap.create(self.RetailAccessPoints)
+ self.access_point = self.access_points[0]
+ else:
+ self.access_point = AccessPointTuple({})
+ self.testclass_params = self.aware_rvr_test_params
+ self.num_atten = self.attenuators[0].instrument.num_atten
+ self.iperf_server = ipf.create([{
+ 'AndroidDevice':
+ self.android_devices[0].serial,
+ 'port':
+ '5201'
+ }])[0]
+ self.iperf_client = ipc.create([{
+ 'AndroidDevice':
+ self.android_devices[1].serial,
+ 'port':
+ '5201'
+ }])[0]
+
+ self.log_path = os.path.join(logging.log_path, 'results')
+ if hasattr(self,
+ 'OTASniffer') and self.testbed_params['sniffer_enable']:
+ self.sniffer = ota_sniffer.create(self.OTASniffer)[0]
+ os.makedirs(self.log_path, exist_ok=True)
+ if not hasattr(self, 'golden_files_list'):
+ if 'golden_results_path' in self.testbed_params:
+ self.golden_files_list = [
+ os.path.join(self.testbed_params['golden_results_path'],
+ file) for file in
+ os.listdir(self.testbed_params['golden_results_path'])
+ ]
+ else:
+ self.log.warning('No golden files found.')
+ self.golden_files_list = []
+
+ self.testclass_results = []
+
+ # Turn WiFi ON
+ if self.testclass_params.get('airplane_mode', 1):
+ self.log.info('Turning on airplane mode.')
+ for ad in self.android_devices:
+ asserts.assert_true(utils.force_airplane_mode(ad, True),
+ "Can not turn on airplane mode.")
+ for ad in self.android_devices:
+ wutils.wifi_toggle_state(ad, True)
+
+ def teardown_class(self):
+ # Turn WiFi OFF
+ for dev in self.android_devices:
+ wutils.wifi_toggle_state(dev, False)
+ self.process_testclass_results()
+ # Teardown AP and release its lockfile
+ self.access_point.teardown()
+
+ def teardown_test(self):
+ self.iperf_server.stop()
+ for ad in self.android_devices:
+ if not ad.droid.doesDeviceSupportWifiAwareFeature():
+ return
+ ad.droid.wifiP2pClose()
+ ad.droid.wifiAwareDestroyAll()
+ autils.reset_device_parameters(ad)
+ autils.validate_forbidden_callbacks(ad)
+ wutils.reset_wifi(ad)
+
+ 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):
+ # Check battery level before test
+ for ad in self.android_devices:
+ if not wputils.health_check(ad, 20):
+ asserts.skip('Overheating or Battery low. Skipping test.')
+ ad.go_to_sleep()
+ wutils.reset_wifi(ad)
+ # Turn screen off to preserve battery
+ 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_aware_connection(self, testcase_params):
+ # Basic aware setup
+ for ad in self.android_devices:
+ asserts.skip_if(
+ not ad.droid.doesDeviceSupportWifiAwareFeature(),
+ "Device under test does not support Wi-Fi Aware - skipping test"
+ )
+ aware_avail = ad.droid.wifiIsAwareAvailable()
+ ad.droid.wifiP2pClose()
+ wutils.wifi_toggle_state(ad, True)
+ utils.set_location_service(ad, True)
+ if not aware_avail:
+ self.log.info('Aware not available. Waiting ...')
+ autils.wait_for_event(ad,
+ aconsts.BROADCAST_WIFI_AWARE_AVAILABLE,
+ timeout=30)
+ ad.aware_capabilities = autils.get_aware_capabilities(ad)
+ autils.reset_device_parameters(ad)
+ autils.reset_device_statistics(ad)
+ autils.set_power_mode_parameters(ad, testcase_params['power_mode'])
+ wutils.set_wifi_country_code(ad, wutils.WifiEnums.CountryCode.US)
+ autils.configure_ndp_allow_any_override(ad, True)
+ # 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()
+
+ # Establish Aware Connection
+ self.init_dut = self.android_devices[0]
+ self.resp_dut = self.android_devices[1]
+
+ # note: Publisher = Responder, Subscribe = Initiator
+ (resp_req_key, init_req_key, resp_aware_if, init_aware_if, resp_ipv6,
+ init_ipv6) = autils.create_ib_ndp(
+ self.resp_dut, self.init_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)
+ testcase_params['aware_config'] = {
+ "init_req_key": init_req_key,
+ "resp_req_key": resp_req_key,
+ "init_aware_if": init_aware_if,
+ "resp_aware_if": resp_aware_if,
+ "init_ipv6": init_ipv6,
+ "resp_ipv6": resp_ipv6
+ }
+ testcase_params['iperf_server_address'] = init_ipv6
+ for ad in self.android_devices:
+ self.log.warning(
+ ad.adb.shell('cmd wifiaware native_cb get_channel_info'))
+ ndp_config = self.android_devices[0].adb.shell(
+ 'cmd wifiaware native_cb get_channel_info')
+ ndp_config = json.loads(ndp_config)
+ ndp_config = ndp_config[list(ndp_config.keys())[0]][0]
+ testcase_params['channel'] = wutils.WifiEnums.freq_to_channel[
+ ndp_config['channelFreq']]
+ if testcase_params['channel'] < 13:
+ testcase_params['mode'] = 'VHT20'
+ else:
+ testcase_params['mode'] = 'VHT80'
+ testcase_params['test_network'] = {'SSID': 'Aware'}
+ self.log.info('Wifi Aware Connection Established on Channel {} {} '
+ '(Interfaces: {},{})'.format(testcase_params['channel'],
+ testcase_params['mode'],
+ init_aware_if,
+ resp_aware_if))
+
+ def setup_aware_rvr_test(self, testcase_params):
+ # Setup the aps
+ self.setup_aps(testcase_params)
+ # Setup the duts
+ self.setup_duts(testcase_params)
+ # Set attenuator to 0 dB
+ for attenuator in self.attenuators:
+ attenuator.set_atten(0, strict=False)
+ # Setup the aware connection
+ self.setup_aware_connection(testcase_params)
+ # Set DUT to monitor RSSI and LLStats on
+ self.monitored_dut = self.android_devices[1]
+
+ def cleanup_aware_rvr_test(self, testcase_params):
+ # clean-up
+ self.resp_dut.droid.connectivityUnregisterNetworkCallback(
+ testcase_params['aware_config']['resp_req_key'])
+ self.init_dut.droid.connectivityUnregisterNetworkCallback(
+ testcase_params['aware_config']['init_req_key'])
+
+ def compile_test_params(self, testcase_params):
+ """Function that completes all test params based on the test name.
+
+ Args:
+ testcase_params: dict containing test-specific parameters
+ """
+ # Compile RvR parameters
+ num_atten_steps = int((self.testclass_params['atten_stop'] -
+ self.testclass_params['atten_start']) /
+ self.testclass_params['atten_step'])
+ testcase_params['atten_range'] = [
+ self.testclass_params['atten_start'] +
+ x * self.testclass_params['atten_step']
+ for x in range(0, num_atten_steps)
+ ]
+
+ # Compile iperf arguments
+ 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)
+ testcase_params['iperf_args'] = wputils.get_iperf_arg_string(
+ duration=self.testclass_params['iperf_duration'],
+ reverse_direction=(testcase_params['traffic_direction'] == 'DL'),
+ socket_size=testcase_params['iperf_socket_size'],
+ num_processes=testcase_params['iperf_processes'],
+ traffic_type=testcase_params['traffic_type'],
+ ipv6=True)
+ testcase_params['use_client_output'] = (
+ testcase_params['traffic_direction'] == 'DL')
+
+ # 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 is the only dut connected, assign it to ap 0
+ ap_id = 0
+ elif band == ap_networks[0]['band']:
+ # if its connected to same band, connect to ap 1
+ ap_id = 1
+ else:
+ # if its on a different band, connect to ap 0 as well
+ 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_aware_rvr(self, testcase_params):
+ """ Function that gets called for each test case
+
+ Args:
+ testcase_params: dict containing test-specific parameters
+ """
+ # Compile test parameters from config and test name
+ testcase_params = self.compile_test_params(testcase_params)
+
+ # Prepare devices and run test
+ self.setup_aware_rvr_test(testcase_params)
+ rvr_result = self.run_rvr_test(testcase_params)
+ self.cleanup_aware_rvr_test(testcase_params)
+
+ # Post-process results
+ self.testclass_results.append(rvr_result)
+ self.process_test_results(rvr_result)
+ self.pass_fail_check(rvr_result)
+
+
+class WifiAwareRvr_TCP_Test(WifiAwareRvrTest):
+ #Test cases
+ def test_aware_rvr_TCP_DL_ib_disconnected_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='DL',
+ power_mode='INTERACTIVE',
+ dut_connected=[False, False],
+ )
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_TCP_DL_ib_connected_2G_1_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='DL',
+ power_mode='INTERACTIVE',
+ dut_connected=['2G_1', False])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_TCP_DL_ib_connected_5G_1_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='DL',
+ power_mode='INTERACTIVE',
+ dut_connected=['5G_1', False])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_TCP_DL_ib_connected_2G_1_connected_2G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='DL',
+ power_mode='INTERACTIVE',
+ dut_connected=['2G_1', '2G_1'])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_TCP_DL_ib_connected_5G_1_connected_5G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='DL',
+ power_mode='INTERACTIVE',
+ dut_connected=['5G_1', '5G_1'])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_TCP_DL_ib_connected_2G_1_connected_5G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='DL',
+ power_mode='INTERACTIVE',
+ dut_connected=['2G_1', '5G_1'])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_TCP_DL_ib_connected_2G_1_connected_2G_2(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='DL',
+ power_mode='INTERACTIVE',
+ dut_connected=['2G_1', '2G_2'])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_TCP_DL_ib_connected_5G_1_connected_5G_2(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='DL',
+ power_mode='INTERACTIVE',
+ dut_connected=['5G_1', '5G_2'])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_TCP_UL_ib_disconnected_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='UL',
+ power_mode='INTERACTIVE',
+ dut_connected=[False, False],
+ )
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_TCP_UL_ib_connected_2G_1_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='UL',
+ power_mode='INTERACTIVE',
+ dut_connected=['2G_1', False])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_TCP_UL_ib_connected_5G_1_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='UL',
+ power_mode='INTERACTIVE',
+ dut_connected=['5G_1', False])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_TCP_UL_ib_connected_2G_1_connected_2G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='UL',
+ power_mode='INTERACTIVE',
+ dut_connected=['2G_1', '2G_1'])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_TCP_UL_ib_connected_5G_1_connected_5G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='UL',
+ power_mode='INTERACTIVE',
+ dut_connected=['5G_1', '5G_1'])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_TCP_UL_ib_connected_2G_1_connected_5G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='UL',
+ power_mode='INTERACTIVE',
+ dut_connected=['2G_1', '5G_1'])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_TCP_UL_ib_connected_2G_1_connected_2G_2(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='UL',
+ power_mode='INTERACTIVE',
+ dut_connected=['2G_1', '2G_2'])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_TCP_UL_ib_connected_5G_1_connected_5G_2(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='UL',
+ power_mode='INTERACTIVE',
+ dut_connected=['5G_1', '5G_2'])
+ self._test_aware_rvr(testcase_params)
+
+
+class WifiAwareRvr_UDP_Test(WifiAwareRvrTest):
+ #Test cases
+ def test_aware_rvr_UDP_DL_ib_disconnected_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='DL',
+ power_mode='INTERACTIVE',
+ dut_connected=[False, False],
+ )
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_UDP_DL_ib_connected_2G_1_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='DL',
+ power_mode='INTERACTIVE',
+ dut_connected=['2G_1', False])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_UDP_DL_ib_connected_5G_1_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='DL',
+ power_mode='INTERACTIVE',
+ dut_connected=['5G_1', False])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_UDP_DL_ib_connected_2G_1_connected_2G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='DL',
+ power_mode='INTERACTIVE',
+ dut_connected=['2G_1', '2G_1'])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_UDP_DL_ib_connected_5G_1_connected_5G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='DL',
+ power_mode='INTERACTIVE',
+ dut_connected=['5G_1', '5G_1'])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_UDP_DL_ib_connected_2G_1_connected_5G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='DL',
+ power_mode='INTERACTIVE',
+ dut_connected=['2G_1', '5G_1'])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_UDP_DL_ib_connected_2G_1_connected_2G_2(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='DL',
+ power_mode='INTERACTIVE',
+ dut_connected=['2G_1', '2G_2'])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_UDP_DL_ib_connected_5G_1_connected_5G_2(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='DL',
+ power_mode='INTERACTIVE',
+ dut_connected=['5G_1', '5G_2'])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_UDP_UL_ib_disconnected_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='UL',
+ power_mode='INTERACTIVE',
+ dut_connected=[False, False],
+ )
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_UDP_UL_ib_connected_2G_1_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='UL',
+ power_mode='INTERACTIVE',
+ dut_connected=['2G_1', False])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_UDP_UL_ib_connected_5G_1_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='UL',
+ power_mode='INTERACTIVE',
+ dut_connected=['5G_1', False])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_UDP_UL_ib_connected_2G_1_connected_2G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='UL',
+ power_mode='INTERACTIVE',
+ dut_connected=['2G_1', '2G_1'])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_UDP_UL_ib_connected_5G_1_connected_5G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='UL',
+ power_mode='INTERACTIVE',
+ dut_connected=['5G_1', '5G_1'])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_UDP_UL_ib_connected_2G_1_connected_5G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='UL',
+ power_mode='INTERACTIVE',
+ dut_connected=['2G_1', '5G_1'])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_UDP_UL_ib_connected_2G_1_connected_2G_2(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='UL',
+ power_mode='INTERACTIVE',
+ dut_connected=['2G_1', '2G_2'])
+ self._test_aware_rvr(testcase_params)
+
+ def test_aware_rvr_UDP_UL_ib_connected_5G_1_connected_5G_2(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='UL',
+ power_mode='INTERACTIVE',
+ dut_connected=['5G_1', '5G_2'])
+ self._test_aware_rvr(testcase_params)
diff --git a/acts_tests/tests/google/wifi/aware/performance/performance b/acts_tests/tests/google/wifi/aware/performance/performance
new file mode 100644
index 0000000..17e3963
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/performance/performance
@@ -0,0 +1,2 @@
+LatencyTest
+ThroughputTest
\ No newline at end of file
diff --git a/acts_tests/tests/google/wifi/aware/stress/DataPathStressTest.py b/acts_tests/tests/google/wifi/aware/stress/DataPathStressTest.py
new file mode 100644
index 0000000..d2e95df
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/stress/DataPathStressTest.py
@@ -0,0 +1,243 @@
+#!/usr/bin/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 queue
+import time
+
+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.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
+
+
+class DataPathStressTest(AwareBaseTest):
+
+ # Number of iterations on create/destroy Attach sessions.
+ ATTACH_ITERATIONS = 2
+
+ # Number of iterations on create/destroy NDP in each discovery session.
+ NDP_ITERATIONS = 50
+
+ # Maximum percentage of NDP setup failures over all iterations
+ MAX_FAILURE_PERCENTAGE = 1
+
+ ################################################################
+
+ def run_oob_ndp_stress(self,
+ attach_iterations,
+ ndp_iterations,
+ trigger_failure_on_index=None):
+ """Run NDP (NAN data-path) stress test creating and destroying Aware
+ attach sessions, discovery sessions, and NDPs.
+
+ Args:
+ attach_iterations: Number of attach sessions.
+ ndp_iterations: Number of NDP to be attempted per attach session.
+ trigger_failure_on_index: Trigger a failure on this NDP iteration (the
+ mechanism is to request NDP on Initiator
+ before issuing the requeest on the Responder).
+ If None then no artificial failure triggered.
+ """
+ init_dut = self.android_devices[0]
+ init_dut.pretty_name = 'Initiator'
+ resp_dut = self.android_devices[1]
+ resp_dut.pretty_name = 'Responder'
+
+ ndp_init_setup_success = 0
+ ndp_init_setup_failures = 0
+ ndp_resp_setup_success = 0
+ ndp_resp_setup_failures = 0
+ ndp_full_socket_success = 0
+
+ for attach_iter in range(attach_iterations):
+ init_id = init_dut.droid.wifiAwareAttach(True)
+ autils.wait_for_event(init_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ init_ident_event = autils.wait_for_event(
+ init_dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+ init_mac = init_ident_event['data']['mac']
+ time.sleep(self.device_startup_offset)
+ resp_id = resp_dut.droid.wifiAwareAttach(True)
+ autils.wait_for_event(resp_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ resp_ident_event = autils.wait_for_event(
+ resp_dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+ resp_mac = resp_ident_event['data']['mac']
+
+ # wait for for devices to synchronize with each other - there are no other
+ # mechanisms to make sure this happens for OOB discovery (except retrying
+ # to execute the data-path request)
+ time.sleep(autils.WAIT_FOR_CLUSTER)
+
+ for ndp_iteration in range(ndp_iterations):
+ if trigger_failure_on_index != ndp_iteration:
+ # Responder: request network
+ resp_req_key = autils.request_network(
+ resp_dut,
+ resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ resp_id, aconsts.DATA_PATH_RESPONDER, init_mac,
+ None))
+
+ # Wait a minimal amount of time to let the Responder configure itself
+ # and be ready for the request. While calling it first may be
+ # sufficient there are no guarantees that a glitch may slow the
+ # Responder slightly enough to invert the setup order.
+ time.sleep(1)
+
+ # Initiator: request network
+ init_req_key = autils.request_network(
+ init_dut,
+ init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ init_id, aconsts.DATA_PATH_INITIATOR, resp_mac,
+ None))
+ else:
+ # Initiator: request network
+ init_req_key = autils.request_network(
+ init_dut,
+ init_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ init_id, aconsts.DATA_PATH_INITIATOR, resp_mac,
+ None))
+
+ # Wait a minimal amount of time to let the Initiator configure itself
+ # to guarantee failure!
+ time.sleep(2)
+
+ # Responder: request network
+ resp_req_key = autils.request_network(
+ resp_dut,
+ resp_dut.droid.wifiAwareCreateNetworkSpecifierOob(
+ resp_id, aconsts.DATA_PATH_RESPONDER, init_mac,
+ None))
+
+ init_ipv6 = None
+ resp_ipv6 = None
+
+ # Initiator: wait for network formation
+ got_on_available = False
+ got_on_link_props = False
+ got_on_net_cap = False
+ while not got_on_available or not got_on_link_props or not got_on_net_cap:
+ try:
+ nc_event = init_dut.ed.pop_event(
+ cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT)
+ if nc_event['data'][
+ cconsts.
+ NETWORK_CB_KEY_EVENT] == cconsts.NETWORK_CB_AVAILABLE:
+ got_on_available = True
+ elif (nc_event['data'][cconsts.NETWORK_CB_KEY_EVENT] ==
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED):
+ got_on_link_props = True
+ elif (nc_event['data'][cconsts.NETWORK_CB_KEY_EVENT] ==
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED):
+ got_on_net_cap = True
+ if aconsts.NET_CAP_IPV6 in nc_event["data"]:
+ resp_ipv6 = nc_event["data"][
+ aconsts.NET_CAP_IPV6]
+ except queue.Empty:
+ ndp_init_setup_failures = ndp_init_setup_failures + 1
+ init_dut.log.info(
+ '[Initiator] Timed out while waiting for '
+ 'EVENT_NETWORK_CALLBACK')
+ break
+
+ if got_on_available and got_on_link_props and got_on_net_cap:
+ ndp_init_setup_success = ndp_init_setup_success + 1
+
+ # Responder: wait for network formation
+ got_on_available = False
+ got_on_link_props = False
+ got_on_net_cap = False
+ while not got_on_available or not got_on_link_props or not got_on_net_cap:
+ try:
+ nc_event = resp_dut.ed.pop_event(
+ cconsts.EVENT_NETWORK_CALLBACK,
+ autils.EVENT_NDP_TIMEOUT)
+ if nc_event['data'][
+ cconsts.
+ NETWORK_CB_KEY_EVENT] == cconsts.NETWORK_CB_AVAILABLE:
+ got_on_available = True
+ elif (nc_event['data'][cconsts.NETWORK_CB_KEY_EVENT] ==
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED):
+ got_on_link_props = True
+ elif (nc_event['data'][cconsts.NETWORK_CB_KEY_EVENT] ==
+ cconsts.NETWORK_CB_CAPABILITIES_CHANGED):
+ got_on_net_cap = True
+ if aconsts.NET_CAP_IPV6 in nc_event["data"]:
+ init_ipv6 = nc_event["data"][
+ aconsts.NET_CAP_IPV6]
+ except queue.Empty:
+ ndp_resp_setup_failures = ndp_resp_setup_failures + 1
+ init_dut.log.info(
+ '[Responder] Timed out while waiting for '
+ 'EVENT_NETWORK_CALLBACK')
+ break
+
+ if got_on_available and got_on_link_props and got_on_net_cap:
+ ndp_resp_setup_success = ndp_resp_setup_success + 1
+
+ # open sockets to test connection
+ if autils.verify_socket_connect(init_dut, resp_dut, init_ipv6,
+ resp_ipv6, 0):
+ if autils.verify_socket_connect(resp_dut, init_dut,
+ resp_ipv6, init_ipv6, 0):
+ ndp_full_socket_success = ndp_full_socket_success + 1
+
+ # clean-up
+ init_dut.droid.connectivityUnregisterNetworkCallback(
+ init_req_key)
+ resp_dut.droid.connectivityUnregisterNetworkCallback(
+ resp_req_key)
+
+ # clean-up at end of iteration
+ init_dut.droid.wifiAwareDestroy(init_id)
+ resp_dut.droid.wifiAwareDestroy(resp_id)
+
+ results = {}
+ results['ndp_init_setup_success'] = ndp_init_setup_success
+ results['ndp_init_setup_failures'] = ndp_init_setup_failures
+ results['ndp_resp_setup_success'] = ndp_resp_setup_success
+ results['ndp_resp_setup_failures'] = ndp_resp_setup_failures
+ results['ndp_full_socket_success'] = ndp_full_socket_success
+ max_failures = (self.MAX_FAILURE_PERCENTAGE * attach_iterations *
+ ndp_iterations / 100)
+ if max_failures == 0:
+ max_failures = 1
+ if trigger_failure_on_index is not None:
+ max_failures = max_failures + 1 # for the triggered failure
+ asserts.assert_true(
+ (ndp_init_setup_failures + ndp_resp_setup_failures) <
+ (2 * max_failures),
+ 'NDP setup failure rate exceeds threshold',
+ extras=results)
+ asserts.explicit_pass("test_oob_ndp_stress* done", extras=results)
+
+ @test_tracker_info(uuid="a20a96ba-e71f-4d31-b850-b88a75381981")
+ def test_oob_ndp_stress(self):
+ """Run NDP (NAN data-path) stress test creating and destroying Aware
+ attach sessions, discovery sessions, and NDPs."""
+ self.run_oob_ndp_stress(self.ATTACH_ITERATIONS, self.NDP_ITERATIONS)
+
+ @test_tracker_info(uuid="1fb4a383-bf1a-411a-a904-489dd9e29c6a")
+ def test_oob_ndp_stress_failure_case(self):
+ """Run NDP (NAN data-path) stress test creating and destroying Aware
+ attach sessions, discovery sessions, and NDPs.
+
+ Verify recovery from failure by triggering an artifical failure and
+ verifying that all subsequent iterations succeed.
+ """
+ self.run_oob_ndp_stress(
+ attach_iterations=1, ndp_iterations=10, trigger_failure_on_index=3)
diff --git a/acts_tests/tests/google/wifi/aware/stress/DiscoveryStressTest.py b/acts_tests/tests/google/wifi/aware/stress/DiscoveryStressTest.py
new file mode 100644
index 0000000..55545ea
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/stress/DiscoveryStressTest.py
@@ -0,0 +1,113 @@
+#!/usr/bin/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 queue
+
+from acts import asserts
+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
+
+
+class DiscoveryStressTest(AwareBaseTest):
+ """Stress tests for Discovery sessions"""
+
+ # Number of iterations on create/destroy Attach sessions.
+ ATTACH_ITERATIONS = 2
+
+ # Number of iterations on create/destroy Discovery sessions
+ DISCOVERY_ITERATIONS = 40
+
+ ####################################################################
+
+ @test_tracker_info(uuid="783791e5-7726-44e0-ac5b-98c1dbf493cb")
+ def test_discovery_stress(self):
+ """Create and destroy a random array of discovery sessions, up to the
+ limit of capabilities."""
+ dut = self.android_devices[0]
+
+ discovery_setup_success = 0
+ discovery_setup_fail = 0
+
+ for attach_iter in range(self.ATTACH_ITERATIONS):
+ # attach
+ session_id = dut.droid.wifiAwareAttach(True)
+ autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ p_discovery_ids = []
+ s_discovery_ids = []
+ for discovery_iter in range(self.DISCOVERY_ITERATIONS):
+ service_name = 'GoogleTestService-%d-%d' % (attach_iter,
+ discovery_iter)
+
+ p_config = None
+ s_config = None
+
+ if discovery_iter % 4 == 0: # publish/unsolicited
+ p_config = autils.create_discovery_config(
+ service_name, aconsts.PUBLISH_TYPE_UNSOLICITED)
+ elif discovery_iter % 4 == 1: # publish/solicited
+ p_config = autils.create_discovery_config(
+ service_name, aconsts.PUBLISH_TYPE_SOLICITED)
+ elif discovery_iter % 4 == 2: # subscribe/passive
+ s_config = autils.create_discovery_config(
+ service_name, aconsts.SUBSCRIBE_TYPE_PASSIVE)
+ elif discovery_iter % 4 == 3: # subscribe/active
+ s_config = autils.create_discovery_config(
+ service_name, aconsts.SUBSCRIBE_TYPE_ACTIVE)
+
+ if p_config is not None:
+ if len(p_discovery_ids) == dut.aware_capabilities[
+ aconsts.CAP_MAX_PUBLISHES]:
+ dut.droid.wifiAwareDestroyDiscoverySession(
+ p_discovery_ids.pop(
+ dut.aware_capabilities[aconsts.
+ CAP_MAX_PUBLISHES] //
+ 2))
+ disc_id = dut.droid.wifiAwarePublish(session_id, p_config)
+ event_name = aconsts.SESSION_CB_ON_PUBLISH_STARTED
+ p_discovery_ids.append(disc_id)
+ else:
+ if len(s_discovery_ids) == dut.aware_capabilities[
+ aconsts.CAP_MAX_SUBSCRIBES]:
+ dut.droid.wifiAwareDestroyDiscoverySession(
+ s_discovery_ids.pop(
+ dut.aware_capabilities[aconsts.
+ CAP_MAX_SUBSCRIBES] //
+ 2))
+ disc_id = dut.droid.wifiAwareSubscribe(
+ session_id, s_config)
+ event_name = aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED
+ s_discovery_ids.append(disc_id)
+
+ try:
+ dut.ed.pop_event(event_name, autils.EVENT_TIMEOUT)
+ discovery_setup_success = discovery_setup_success + 1
+ except queue.Empty:
+ discovery_setup_fail = discovery_setup_fail + 1
+
+ dut.droid.wifiAwareDestroy(session_id)
+
+ results = {}
+ results['discovery_setup_success'] = discovery_setup_success
+ results['discovery_setup_fail'] = discovery_setup_fail
+ asserts.assert_equal(
+ discovery_setup_fail,
+ 0,
+ 'Discovery setup failures',
+ extras=results)
+ asserts.explicit_pass('test_discovery_stress done', extras=results)
diff --git a/acts_tests/tests/google/wifi/aware/stress/InfraAssociationStressTest.py b/acts_tests/tests/google/wifi/aware/stress/InfraAssociationStressTest.py
new file mode 100644
index 0000000..58e2c84
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/stress/InfraAssociationStressTest.py
@@ -0,0 +1,160 @@
+#!/usr/bin/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 queue
+import threading
+
+from acts import asserts
+from acts.test_utils.wifi import wifi_constants as wconsts
+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
+
+
+class InfraAssociationStressTest(AwareBaseTest):
+ # Length of test in seconds
+ TEST_DURATION_SECONDS = 300
+
+ # Service name
+ SERVICE_NAME = "GoogleTestServiceXYXYXY"
+
+ def is_associated(self, dut):
+ """Checks whether the device is associated (to any AP).
+
+ Args:
+ dut: Device under test.
+
+ Returns: True if associated (to any AP), False otherwise.
+ """
+ info = dut.droid.wifiGetConnectionInfo()
+ return info is not None and info["supplicant_state"] != "disconnected"
+
+ def wait_for_disassociation(self, q, dut):
+ """Waits for a disassociation event on the specified DUT for the given
+ timeout. Place a result into the queue (False) only if disassociation
+ observed.
+
+ Args:
+ q: The synchronization queue into which to place the results.
+ dut: The device to track.
+ """
+ try:
+ dut.ed.pop_event(wconsts.WIFI_DISCONNECTED,
+ self.TEST_DURATION_SECONDS)
+ q.put(True)
+ except queue.Empty:
+ pass
+
+ def run_infra_assoc_oob_ndp_stress(self, with_ndp_traffic):
+ """Validates that Wi-Fi Aware NDP does not interfere with infrastructure
+ (AP) association.
+
+ Test assumes (and verifies) that device is already associated to an AP.
+
+ Args:
+ with_ndp_traffic: True to run traffic over the NDP.
+ """
+ init_dut = self.android_devices[0]
+ resp_dut = self.android_devices[1]
+
+ # check that associated and start tracking
+ init_dut.droid.wifiStartTrackingStateChange()
+ resp_dut.droid.wifiStartTrackingStateChange()
+ asserts.assert_true(
+ self.is_associated(init_dut), "DUT is not associated to an AP!")
+ asserts.assert_true(
+ self.is_associated(resp_dut), "DUT is not associated to an AP!")
+
+ # set up NDP
+ (init_req_key, resp_req_key, init_aware_if, resp_aware_if, init_ipv6,
+ resp_ipv6) = autils.create_oob_ndp(init_dut, resp_dut)
+ self.log.info("Interface names: I=%s, R=%s", init_aware_if,
+ resp_aware_if)
+ self.log.info("Interface addresses (IPv6): I=%s, R=%s", init_ipv6,
+ resp_ipv6)
+
+ # wait for any disassociation change events
+ q = queue.Queue()
+ init_thread = threading.Thread(
+ target=self.wait_for_disassociation, args=(q, init_dut))
+ resp_thread = threading.Thread(
+ target=self.wait_for_disassociation, args=(q, resp_dut))
+
+ init_thread.start()
+ resp_thread.start()
+
+ any_disassociations = False
+ try:
+ q.get(True, self.TEST_DURATION_SECONDS)
+ any_disassociations = True # only happens on any disassociation
+ except queue.Empty:
+ pass
+ finally:
+ # TODO: no way to terminate thread (so even if we fast fail we still have
+ # to wait for the full timeout.
+ init_dut.droid.wifiStopTrackingStateChange()
+ resp_dut.droid.wifiStopTrackingStateChange()
+
+ asserts.assert_false(any_disassociations,
+ "Wi-Fi disassociated during test run")
+
+ ################################################################
+
+ def test_infra_assoc_discovery_stress(self):
+ """Validates that Wi-Fi Aware discovery does not interfere with
+ infrastructure (AP) association.
+
+ Test assumes (and verifies) that device is already associated to an AP.
+ """
+ dut = self.android_devices[0]
+
+ # check that associated and start tracking
+ dut.droid.wifiStartTrackingStateChange()
+ asserts.assert_true(
+ self.is_associated(dut), "DUT is not associated to an AP!")
+
+ # attach
+ session_id = dut.droid.wifiAwareAttach(True)
+ autils.wait_for_event(dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # publish
+ p_disc_id = dut.droid.wifiAwarePublish(
+ session_id,
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED))
+ autils.wait_for_event(dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+
+ # wait for any disassociation change events
+ any_disassociations = False
+ try:
+ dut.ed.pop_event(wconsts.WIFI_DISCONNECTED,
+ self.TEST_DURATION_SECONDS)
+ any_disassociations = True
+ except queue.Empty:
+ pass
+ finally:
+ dut.droid.wifiStopTrackingStateChange()
+
+ asserts.assert_false(any_disassociations,
+ "Wi-Fi disassociated during test run")
+
+ def test_infra_assoc_ndp_no_traffic_stress(self):
+ """Validates that Wi-Fi Aware NDP (with no traffic) does not interfere with
+ infrastructure (AP) association.
+
+ Test assumes (and verifies) that devices are already associated to an AP.
+ """
+ self.run_infra_assoc_oob_ndp_stress(with_ndp_traffic=False)
diff --git a/acts_tests/tests/google/wifi/aware/stress/MessagesStressTest.py b/acts_tests/tests/google/wifi/aware/stress/MessagesStressTest.py
new file mode 100644
index 0000000..2d2c514
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/stress/MessagesStressTest.py
@@ -0,0 +1,445 @@
+#!/usr/bin/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 queue
+
+from acts import asserts
+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
+
+KEY_ID = "id"
+KEY_TX_OK_COUNT = "tx_ok_count"
+KEY_TX_FAIL_COUNT = "tx_fail_count"
+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 = 200
+
+ # 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
+
+ # Maximum permitted percentage of messages which are received more than once
+ # (indicating, most likely, that the ACK wasn't received and the message was
+ # retransmitted)
+ MAX_DUPLICATE_RX_PERCENTAGE = 2
+
+ SERVICE_NAME = "GoogleTestServiceXY"
+
+ def init_info(self, msg, id, messages_by_msg, messages_by_id):
+ """Initialize the message data structures.
+
+ Args:
+ msg: message text
+ id: message id
+ messages_by_msg: {text -> {id, tx_ok_count, tx_fail_count, rx_count}}
+ messages_by_id: {id -> text}
+ """
+ messages_by_msg[msg] = {}
+ messages_by_msg[msg][KEY_ID] = id
+ messages_by_msg[msg][KEY_TX_OK_COUNT] = 0
+ messages_by_msg[msg][KEY_TX_FAIL_COUNT] = 0
+ messages_by_msg[msg][KEY_RX_COUNT] = 0
+ messages_by_id[id] = msg
+
+ def wait_for_tx_events(self, dut, num_msgs, messages_by_msg,
+ messages_by_id):
+ """Wait for messages to be transmitted and update data structures.
+
+ Args:
+ dut: device under test
+ num_msgs: number of expected message tx
+ messages_by_msg: {text -> {id, tx_ok_count, tx_fail_count, rx_count}}
+ messages_by_id: {id -> text}
+ """
+ num_ok_tx_confirmations = 0
+ num_fail_tx_confirmations = 0
+ num_unexpected_ids = 0
+ tx_events_regex = "%s|%s" % (aconsts.SESSION_CB_ON_MESSAGE_SEND_FAILED,
+ aconsts.SESSION_CB_ON_MESSAGE_SENT)
+ while num_ok_tx_confirmations + num_fail_tx_confirmations < num_msgs:
+ try:
+ events = dut.ed.pop_events(tx_events_regex,
+ autils.EVENT_TIMEOUT)
+ for event in events:
+ if (event["name"] != aconsts.SESSION_CB_ON_MESSAGE_SENT
+ and event["name"] !=
+ aconsts.SESSION_CB_ON_MESSAGE_SEND_FAILED):
+ asserts.fail("Unexpected event: %s" % event)
+ is_tx_ok = event[
+ "name"] == aconsts.SESSION_CB_ON_MESSAGE_SENT
+
+ id = event["data"][aconsts.SESSION_CB_KEY_MESSAGE_ID]
+ if id in messages_by_id:
+ msg = messages_by_id[id]
+ if is_tx_ok:
+ messages_by_msg[msg][
+ KEY_TX_OK_COUNT] = messages_by_msg[msg][KEY_TX_OK_COUNT] + 1
+ if messages_by_msg[msg][KEY_TX_OK_COUNT] == 1:
+ num_ok_tx_confirmations = num_ok_tx_confirmations + 1
+ else:
+ messages_by_msg[msg][KEY_TX_FAIL_COUNT] = (
+ messages_by_msg[msg][KEY_TX_FAIL_COUNT] + 1)
+ if messages_by_msg[msg][KEY_TX_FAIL_COUNT] == 1:
+ num_fail_tx_confirmations = num_fail_tx_confirmations + 1
+ else:
+ self.log.warning(
+ "Tx confirmation of unknown message ID received: %s",
+ event)
+ num_unexpected_ids = num_unexpected_ids + 1
+ except queue.Empty:
+ self.log.warning(
+ "[%s] Timed out waiting for any MESSAGE_SEND* event - "
+ "assuming the rest are not coming", dut.pretty_name)
+ break
+
+ return (num_ok_tx_confirmations, num_fail_tx_confirmations,
+ num_unexpected_ids)
+
+ def wait_for_rx_events(self, dut, num_msgs, messages_by_msg):
+ """Wait for messages to be received and update data structures
+
+ Args:
+ dut: device under test
+ num_msgs: number of expected messages to receive
+ messages_by_msg: {text -> {id, tx_ok_count, tx_fail_count, rx_count}}
+ """
+ num_rx_msgs = 0
+ while num_rx_msgs < num_msgs:
+ try:
+ event = dut.ed.pop_event(
+ aconsts.SESSION_CB_ON_MESSAGE_RECEIVED,
+ autils.EVENT_TIMEOUT)
+ msg = event["data"][aconsts.SESSION_CB_KEY_MESSAGE_AS_STRING]
+ if msg not in messages_by_msg:
+ messages_by_msg[msg] = {}
+ messages_by_msg[msg][KEY_ID] = -1
+ messages_by_msg[msg][KEY_TX_OK_COUNT] = 0
+ messages_by_msg[msg][KEY_TX_FAIL_COUNT] = 0
+ messages_by_msg[msg][KEY_RX_COUNT] = 1
+
+ messages_by_msg[msg][
+ KEY_RX_COUNT] = messages_by_msg[msg][KEY_RX_COUNT] + 1
+ if messages_by_msg[msg][KEY_RX_COUNT] == 1:
+ num_rx_msgs = num_rx_msgs + 1
+ except queue.Empty:
+ self.log.warning(
+ "[%s] Timed out waiting for ON_MESSAGE_RECEIVED event - "
+ "assuming the rest are not coming", dut.pretty_name)
+ break
+
+ def analyze_results(self, results, messages_by_msg):
+ """Analyze the results of the stress message test and add to the results
+ dictionary
+
+ Args:
+ results: result dictionary into which to add data
+ messages_by_msg: {text -> {id, tx_ok_count, tx_fail_count, rx_count}}
+ """
+ results["raw_data"] = messages_by_msg
+ results["tx_count_success"] = 0
+ results["tx_count_duplicate_success"] = 0
+ results["tx_count_fail"] = 0
+ results["tx_count_duplicate_fail"] = 0
+ results["tx_count_neither"] = 0
+ results["tx_count_tx_ok_but_no_rx"] = 0
+ results["rx_count"] = 0
+ results["rx_count_duplicate"] = 0
+ results["rx_count_no_ok_tx_indication"] = 0
+ results["rx_count_fail_tx_indication"] = 0
+ results["rx_count_no_tx_message"] = 0
+
+ for msg, data in messages_by_msg.items():
+ 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"] += 1
+ if data[KEY_TX_FAIL_COUNT] > 0:
+ results["tx_count_fail"] += 1
+ if data[KEY_TX_FAIL_COUNT] > 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"] += 1
+ if data[KEY_TX_OK_COUNT] > 0 and data[KEY_RX_COUNT] == 0:
+ results["tx_count_tx_ok_but_no_rx"] += 1
+ if data[KEY_RX_COUNT] > 0:
+ results["rx_count"] += 1
+ if data[KEY_RX_COUNT] > 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"] += 1
+ if data[KEY_RX_COUNT] > 0 and data[KEY_TX_FAIL_COUNT] > 0:
+ results["rx_count_fail_tx_indication"] += 1
+ if data[KEY_RX_COUNT] > 0 and data[KEY_ID] == -1:
+ results["rx_count_no_tx_message"] += 1
+
+ #######################################################################
+
+ @test_tracker_info(uuid="e88c060f-4ca7-41c1-935a-d3d62878ec0b")
+ 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]
+
+ # 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}
+
+ # send all messages at once (one in each direction)
+ for i in range(self.NUM_ITERATIONS):
+ msg_p2s = "Message Publisher -> Subscriber #%d" % i
+ 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" % i
+ 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)
+
+ # 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_ITERATIONS, 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, self.NUM_ITERATIONS, messages_by_msg, messages_by_id)
+ self.log.info(
+ "Transmission done: pub=%d, sub=%d transmitted successfully",
+ p_tx_ok_count, s_tx_ok_count)
+
+ # 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, self.NUM_ITERATIONS, messages_by_msg)
+ self.wait_for_rx_events(s_dut, self.NUM_ITERATIONS, messages_by_msg)
+
+ # analyze results
+ results = {}
+ results["tx_count"] = 2 * self.NUM_ITERATIONS
+ results["tx_unknown_ids"] = p_tx_unknown_id + s_tx_unknown_id
+ 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["rx_count_duplicate"] <=
+ (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_throttling done", extras=results)
diff --git a/acts_tests/tests/google/wifi/aware/stress/stress b/acts_tests/tests/google/wifi/aware/stress/stress
new file mode 100644
index 0000000..f79b158
--- /dev/null
+++ b/acts_tests/tests/google/wifi/aware/stress/stress
@@ -0,0 +1,4 @@
+MessagesStressTest
+DataPathStressTest
+DiscoveryStressTest
+InfraAssociationStressTest
\ No newline at end of file
diff --git a/acts_tests/tests/google/wifi/example_config_iot.json b/acts_tests/tests/google/wifi/example_config_iot.json
new file mode 100644
index 0000000..133cbe3
--- /dev/null
+++ b/acts_tests/tests/google/wifi/example_config_iot.json
@@ -0,0 +1,38 @@
+{
+ "_description": "This and example IOT WiFi testbed.",
+ "testbed": [
+ {
+ "_description": "WiFi testbed with 1 devices",
+ "name": "<test station name>",
+ "AndroidDevice": [
+ "<device serial>"
+ ],
+ "IPerfServer": [
+ 5005
+ ]
+ }
+ ],
+ "logpath": "/tmp/ACTS_logs",
+ "testpaths": [
+ "<path to acts root>/tools/test/connectivity/acts_tests/tests/google/wifi"
+ ],
+ "iot_networks": [
+ {
+ "SSID": "<your SSID 2G>",
+ "password": "<your password>"
+ },
+ {
+ "SSID": "<your SSID 5G>",
+ "password": "<your password>"
+ },
+ {
+ "SSID": "<your SSID 2G 2>",
+ "password": "<your password>"
+ },
+ {
+ "SSID": "<your SSID 5G 2>",
+ "password": "<your password>"
+ }
+ ],
+ "iperf_server_address": "<your IP address>"
+}
\ No newline at end of file
diff --git a/acts_tests/tests/google/wifi/example_config_sanity.json b/acts_tests/tests/google/wifi/example_config_sanity.json
new file mode 100644
index 0000000..b23d3bf
--- /dev/null
+++ b/acts_tests/tests/google/wifi/example_config_sanity.json
@@ -0,0 +1,207 @@
+{
+ "testbed": [
+ {
+ "name": "test_station_name",
+ "AndroidDevice": [
+ "<serial number 1>",
+ "<serial number 2 if necessary and 3 etc>"
+ ],
+ "AccessPoint": [
+ { "ssh_config" :
+ {
+ "user" : "root",
+ "host" : "<ip 1, e.g. 192.168.1.2>"
+ }
+ },
+ { "ssh_config" :
+ {
+ "user" : "root",
+ "host" : "<ip 2 (if necessary) and ip 3 ...>"
+ }
+ }
+ ],
+ "Attenuator": [
+ {
+ "Address": "<attenuator ip address>",
+ "InstrumentCount": 4,
+ "Model": "<model, e.g. minicircuits>",
+ "Paths": [
+ "AP1-2G",
+ "AP1-5G",
+ "AP2-2G",
+ "AP2-5G"
+ ],
+ "Port": 22
+ }
+ ],
+ "IPerfServer": [
+ 5004
+ ],
+ "bssid_2g": {
+ "BSSID": "<bssid, e.g. 00:01:02:03:04:05>",
+ "high": "-10",
+ "low": "-85"
+ },
+ "bssid_5g": {
+ "BSSID": "<bssid>",
+ "high": "-10",
+ "low": "-85"
+ },
+ "bssid_dfs": {
+ "BSSID": "<bssid>",
+ "high": "-10",
+ "low": "-85"
+ },
+ "iperf_server_address": "100.107.126.31"
+ }
+ ],
+ "atten_val": {
+ "Ap1_2g": [
+ 10,
+ 95,
+ 95,
+ 95
+ ],
+ "Ap1_2gto5g": [
+ 45,
+ 10,
+ 95,
+ 95
+ ],
+ "Ap1_5gto2g": [
+ 10,
+ 80,
+ 95,
+ 95
+ ],
+ "Ap2_2g": [
+ 75,
+ 75,
+ 10,
+ 75
+ ],
+ "Ap2_2gto5g": [
+ 75,
+ 75,
+ 75,
+ 10
+ ],
+ "Ap2_5gto2g": [
+ 75,
+ 75,
+ 10,
+ 75
+ ],
+ "Back_from_blacklist": [
+ 40,
+ 95,
+ 95
+ ],
+ "In_AP1_5gto2g": [
+ 10,
+ 75,
+ 95,
+ 95
+ ],
+ "In_Ap2_5gto2g": [
+ 75,
+ 75,
+ 10,
+ 75
+ ],
+ "In_blacklist": [
+ 95,
+ 95,
+ 0
+ ],
+ "Swtich_AP1toAp2": [
+ 70,
+ 70,
+ 2,
+ 70
+ ],
+ "Swtich_AP2toAp1": [
+ 10,
+ 70,
+ 75,
+ 75
+ ],
+ "Swtich_to_blacklist": [
+ 60,
+ 90,
+ 40
+ ]
+ },
+ "attenuator_id": 0,
+ "roaming_attn": {
+ "AP1_on_AP2_off": [
+ 0,
+ 0,
+ 95,
+ 95
+ ],
+ "AP1_off_AP2_on": [
+ 95,
+ 95,
+ 0,
+ 0
+ ],
+ "default": [
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "attn_vals": {
+ "a_b_on": [
+ 0,
+ 0
+ ],
+ "a_on_b_off": [
+ 0,
+ 95
+ ],
+ "b_on_a_off": [
+ 95,
+ 0
+ ],
+ "default": [
+ 0,
+ 0
+ ]
+ },
+ "device_password": "hahahaha",
+ "eap_password": "password",
+ "fqdn": "red.com",
+ "max_bugreports": 5,
+ "other_network": {
+ "SSID": "wh_ap3_2g",
+ "password": "hahahaha"
+ },
+ "ping_addr": "https://www.google.com/robots.txt",
+ "pno_interval": 120,
+ "provider_friendly_name": "red",
+ "realm": "red.com",
+ "roam_interval": 60,
+ "run_extended_test": false,
+ "two_ap_testbed": true,
+ "aware_default_power_mode": "INTERACTIVE",
+ "stress_count": 100,
+ "stress_hours": 5,
+ "dbs_supported_models": ["<product name 1>", "<product name 2>"],
+ "lci_reference": [],
+ "lcr_reference": [],
+ "rtt_reference_distance_mm": 4600,
+ "stress_test_min_iteration_count": 100,
+ "stress_test_target_run_time_sec" : 30,
+ "energy_info_models": [
+ "<product name 1 (adb shell getprop ro.build.product)>",
+ "<product name 2>"
+ ],
+ "tdls_models": [
+ "<product name 1>",
+ "<product name 2>"
+ ]
+}
+
diff --git a/acts_tests/tests/google/wifi/example_connectivity_performance_ap_sta.json b/acts_tests/tests/google/wifi/example_connectivity_performance_ap_sta.json
new file mode 100644
index 0000000..6ec0501
--- /dev/null
+++ b/acts_tests/tests/google/wifi/example_connectivity_performance_ap_sta.json
@@ -0,0 +1,89 @@
+{
+ "testbed": [{
+ "name": "<your testbed name>",
+ "AndroidDevice": ["<your device serial number>"],
+ "bug_report": 1,
+ "RetailAccessPoints": ["<your ap configuration. see class definition in wifi_retail_ap.py>"],
+ "Attenuator": ["<your attenuator configuration. see attenuator class definition>"],
+ "main_network": {
+ "<your network name>": {
+ "SSID": "<your SSID>",
+ "password": "<your key>",
+ "BSSID": "<your BSSID>"
+ },
+ "<your other network names>": {
+ "SSID": "<your SSID>",
+ "password": "<your key>",
+ "BSSID": "<your BSSID>"
+ }
+ },
+ "IPerfServer": ["<your iperf server configuation. see class definition in iperf_server>"],
+ "testbed_params": {
+ "default_region": "<default access point region to run tests in. This will be used for all non DFS channels>",
+ "DFS_region": "<access point region to run DFS tests in>",
+ "iperf_server_address": "<ip address of iperf server generating or accepting test traffic>",
+ "fixed_attenuation": {"<your channel number 1>": "<your testbed attenuation on this channel>", "<your channel number 2>": "<your testbed attenuation on this channel>"},
+ "dut_front_end_loss": {"<your channel number 1>": "<your DUT front end loss on this channel>", "<your channel number 2>": "<your DUT front end loss on this channel>"},
+ "ap_tx_power": {"<your channel number 1>": "<your access point transmit power on this channel>", "<your channel number 2>": "<your access point transmit power on this channel>"},
+ "golden_results_path": "<your full path to golden results used for pass fail check>"
+ }
+ }
+ ],
+ "rvr_test_params":{
+ "country_code": "<device country code to set during rvr tests>",
+ "iperf_duration": 30,
+ "iperf_ignored_interval": 2,
+ "UDP_rates": {"VHT20": "<throughput to transmit in this mode>", "VHT40": "<throughput to transmit in this mode>", "VHT80": "<throughput to transmit in this mode>"},
+ "rvr_atten_start": 20,
+ "rvr_atten_stop": 30,
+ "rvr_atten_step": 5,
+ "pct_tolerance": 5,
+ "abs_tolerance": 5,
+ "failure_count_tolerance": 1
+ },
+ "rssi_test_params":{
+ "country_code": "<device country code to set during rvr tests>",
+ "rssi_vs_atten_start": 20,
+ "rssi_vs_atten_stop": 80,
+ "rssi_vs_atten_step": 1,
+ "rssi_vs_atten_connected_measurements": 10,
+ "rssi_vs_atten_scan_measurements": 0,
+ "rssi_vs_atten_metrics": ["signal_poll_rssi", "scan_rssi", "chain_0_rssi", "chain_1_rssi"],
+ "rssi_stability_atten": [20, 55],
+ "rssi_stability_duration": 10,
+ "rssi_tracking_waveforms": [{"atten_levels": [40, 61, 40], "step_size": 1, "step_duration": 1, "repetitions":1}],
+ "polling_frequency": 0.25,
+ "abs_tolerance": 2.5,
+ "stdev_tolerance": 1
+ },
+ "throughput_stability_test_params":{
+ "country_code": "<device country code to set during rvr tests>",
+ "iperf_duration": 30,
+ "iperf_ignored_interval": 5,
+ "UDP_rates": {"VHT20": "200M", "VHT40": "400M", "VHT80": "700M"},
+ "low_rssi_backoff_from_range": 10,
+ "min_throughput_threshold": 75,
+ "std_deviation_threshold": 5
+
+ },
+ "ping_test_params":{
+ "country_code": "<device country code to set during rvr tests>",
+ "ping_size": 64,
+ "range_ping_duration": 1,
+ "range_ping_interval": 0.002,
+ "range_atten_start": 60,
+ "range_atten_step": 1,
+ "range_atten_stop": 70,
+ "range_ping_loss_threshold": 25,
+ "range_gap_threshold": 2,
+ "rtt_ping_duration": 30,
+ "rtt_ping_interval": {"fast": 0.002, "slow": 0.5},
+ "rtt_ignored_interval": 0.15,
+ "rtt_test_attenuation": [20, 50],
+ "rtt_test_percentile": 5,
+ "rtt_threshold": 0.2,
+ "rtt_std_deviation_threshold": 5
+ },
+ "logpath": "<path to logs>",
+ "testpaths": ["<path to ACTS root folder>/tools/test/connectivity/acts_tests/tests/google/wifi"]
+}
diff --git a/acts_tests/tests/google/wifi/p2p/config/wifi_p2p.json b/acts_tests/tests/google/wifi/p2p/config/wifi_p2p.json
new file mode 100644
index 0000000..9a1a3ad
--- /dev/null
+++ b/acts_tests/tests/google/wifi/p2p/config/wifi_p2p.json
@@ -0,0 +1,15 @@
+{
+ "_description": "This is a test configuration file for Wi-Fi P2p tests.",
+ "testbed":
+ [
+ {
+ "_description": "Wi-Fi P2P testbed: auto-detect all attached devices",
+ "name": "WifiP2pAllAttached",
+ "AndroidDevice": "*"
+ }
+ ],
+ "skip_read_factory_mac": 1,
+ "logpath": "~/logs",
+ "testpaths": ["./tools/test/connectivity/acts_tests/tests/google/wifi/p2p"],
+ "adb_logcat_param": "-b all"
+}
diff --git a/acts_tests/tests/google/wifi/p2p/config/wifi_p2p_group.json b/acts_tests/tests/google/wifi/p2p/config/wifi_p2p_group.json
new file mode 100644
index 0000000..2e03d52
--- /dev/null
+++ b/acts_tests/tests/google/wifi/p2p/config/wifi_p2p_group.json
@@ -0,0 +1,18 @@
+{
+ "_description": "This is a test configuration file for Wi-Fi P2p group tests.",
+ "testbed":
+ [
+ {
+ "_description": "Wi-Fi P2P testbed: auto-detect all attached devices",
+ "name": "WifiP2pAllAttached",
+ "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"],
+ "adb_logcat_param": "-b all"
+}
diff --git a/acts_tests/tests/google/wifi/p2p/functional/WifiP2pGroupTest.py b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pGroupTest.py
new file mode 100644
index 0000000..dd27f21
--- /dev/null
+++ b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pGroupTest.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python3
+#
+# 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 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.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
+
+
+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)
+
+ def setup_class(self):
+ super().setup_class()
+ if not "network_name" in self.user_params.keys():
+ self.log.error("Missing mandatory user config \"network_name\"!")
+ self.network_name = self.user_params["network_name"]
+ if not "passphrase" in self.user_params.keys():
+ self.log.error("Missing mandatory user config \"passphrase\"!")
+ self.passphrase = self.user_params["passphrase"]
+ if not "group_band" in self.user_params.keys():
+ self.log.error("Missing mandatory user config \"group_band\"!")
+ self.group_band = self.user_params["group_band"]
+
+ def setup_test(self):
+ super().setup_test()
+ self.dut1.droid.wifiP2pRemoveGroup()
+ self.dut2.droid.wifiP2pRemoveGroup()
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+
+ def teardown_test(self):
+ self.dut1.droid.wifiP2pRemoveGroup()
+ self.dut2.droid.wifiP2pRemoveGroup()
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+ super().teardown_test()
+
+ def p2p_group_join(self, wps_type):
+ """ General flow for p2p group join
+
+ Steps:
+ 1. GO creates a group.
+ 2. GC joins the group.
+ 3. connection check via ping from GC to GO
+ """
+ go_dut = self.dut1
+ gc_dut = self.dut2
+ # Create a group
+ wp2putils.p2p_create_group(go_dut)
+ 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,
+ p2p_connect_type=p2pconsts.P2P_CONNECT_JOIN)
+
+ go_ip = wp2putils.p2p_go_ip(gc_dut)
+ wp2putils.p2p_connection_ping_test(gc_dut, go_ip)
+ # trigger disconnect
+ wp2putils.p2p_disconnect(go_dut)
+ 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):
+ """Verify the p2p creates a group and join this group via WPS PBC method.
+ """
+ self.p2p_group_join(WPS_PBC)
+
+ @test_tracker_info(uuid="56eb339f-d7e4-44f0-9802-6094e9255957")
+ def test_p2p_group_join_via_display(self):
+ """Verify the p2p creates a group and join this group via WPS DISPLAY method.
+ """
+ self.p2p_group_join(WPS_DISPLAY)
+
+ @test_tracker_info(uuid="27075cab-7859-49a7-afe9-b6cc6e8faddb")
+ def test_p2p_group_with_config(self):
+ """Verify the p2p creates a group and join an this group with config.
+
+ Steps:
+ 1. GO creates a group with config.
+ 2. GC joins the group with config.
+ 3. connection check via ping from GC to GO
+ """
+ go_dut = self.dut1
+ gc_dut = self.dut2
+ # Create a group
+ 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)
+
+ go_ip = wp2putils.p2p_go_ip(gc_dut)
+ wp2putils.p2p_connection_ping_test(gc_dut, go_ip)
+ # trigger disconnect
+ wp2putils.p2p_disconnect(go_dut)
+ wp2putils.check_disconnect(gc_dut)
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
diff --git a/acts_tests/tests/google/wifi/p2p/functional/WifiP2pLocalServiceTest.py b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pLocalServiceTest.py
new file mode 100644
index 0000000..b043eb9
--- /dev/null
+++ b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pLocalServiceTest.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python3
+#
+# 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 time
+
+from acts.test_decorators import test_tracker_info
+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.
+
+ Test Bed Requirement:
+ * At least two Android devices
+ * 3 Android devices for WifiP2pMultiPeersTest.py
+ """
+ def __init__(self, controllers):
+ WifiP2pBaseTest.__init__(self, controllers)
+
+ """Test Cases"""
+ @test_tracker_info(uuid="ba879c8d-0fbd-41fb-805c-5cd1cd312090")
+ def test_p2p_upnp_service(self):
+ """Verify the p2p discovery functionality
+ Steps:
+ 1. dut1 add local Upnp service
+ 2. dut2 register Upnp Service listener
+ 3. Check dut2 peer list if it only included dut1
+ 4. Setup p2p upnp local service request with different query string
+ 5. Check p2p upnp local servier query result is expect or not
+ 6. Test different query string and check query result
+ 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.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)
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+ 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"""
+ @test_tracker_info(uuid="470306fa-5c46-4258-ade2-9c0834bb04f9")
+ def test_p2p_bonjour_service(self):
+ """Verify the p2p discovery functionality
+ Steps:
+ 1. dut1 add local bonjour service - IPP and AFP
+ 2. dut2 register bonjour Service listener - dnssd and dnssd_txrecord
+ 3. Check dut2 peer list if it only included dut1
+ 4. Setup p2p bonjour local service request with different query string
+ 5. Check p2p bonjour local servier query result is expect or not
+ 6. Test different query string and check query result
+ 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.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)
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+ 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")
diff --git a/acts_tests/tests/google/wifi/p2p/functional/WifiP2pManagerTest.py b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pManagerTest.py
new file mode 100644
index 0000000..6bda400
--- /dev/null
+++ b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pManagerTest.py
@@ -0,0 +1,207 @@
+#!/usr/bin/env python3
+#
+# 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 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.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
+
+
+class WifiP2pManagerTest(WifiP2pBaseTest):
+ """Tests for APIs in Android's WifiP2pManager class.
+
+ Test Bed Requirement:
+ * At least two Android devices
+ * 3 Android devices for WifiP2pMultiPeersTest.py
+ """
+ def __init__(self, controllers):
+ WifiP2pBaseTest.__init__(self, controllers)
+
+ """Test Cases"""
+ @test_tracker_info(uuid="28ddb16c-2ce4-44da-92f9-701d0dacc321")
+ def test_p2p_discovery(self):
+ """Verify the p2p discovery functionality
+ Steps:
+ 1. Discover the target device
+ 2. Check the target device in peer list
+ """
+ self.log.info("Device discovery")
+ wp2putils.find_p2p_device(self.dut1, self.dut2)
+ wp2putils.find_p2p_device(self.dut2, self.dut1)
+
+ @test_tracker_info(uuid="0016e6db-9b46-44fb-a53e-10a81eee955e")
+ def test_p2p_connect_via_pbc_and_ping_and_reconnect(self):
+ """Verify the p2p connect via pbc functionality
+
+ Steps:
+ 1. Request the connection which include discover the target device
+ 2. check which dut is GO and which dut is GC
+ 3. connection check via ping from GC to GO
+ 4. disconnect
+ 5. Trigger connect again from GO for reconnect test.
+ 6. GO trigger disconnect
+ 7. Trigger connect again from GC for reconnect test.
+ 8. GC trigger disconnect
+ """
+ # Request the connection
+ wp2putils.p2p_connect(self.dut1, self.dut2, False, WPS_PBC)
+
+ if wp2putils.is_go(self.dut1):
+ go_dut = self.dut1
+ gc_dut = self.dut2
+ elif wp2putils.is_go(self.dut2):
+ go_dut = self.dut2
+ gc_dut = self.dut1
+
+ go_ip = wp2putils.p2p_go_ip(gc_dut)
+ wp2putils.p2p_connection_ping_test(gc_dut, go_ip)
+
+ # trigger disconnect
+ wp2putils.p2p_disconnect(self.dut1)
+ wp2putils.check_disconnect(self.dut2)
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+
+ self.log.info("Reconnect test, triggered by GO")
+ # trigger reconnect from GO
+ go_dut.ed.clear_all_events()
+ gc_dut.ed.clear_all_events()
+ wp2putils.p2p_connect(go_dut, gc_dut, True, WPS_PBC)
+ wp2putils.p2p_disconnect(go_dut)
+ wp2putils.check_disconnect(gc_dut)
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+
+ # trigger reconnect from GC
+ self.log.info("Reconnect test, triggered by GC")
+ go_dut.ed.clear_all_events()
+ 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, timeout=p2pconsts.DEFAULT_GROUP_CLIENT_LOST_TIME)
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+
+ @test_tracker_info(uuid="12bbe73a-5a6c-4307-9797-c77c7efdc4b5")
+ def test_p2p_connect_via_display_and_ping_and_reconnect(self):
+ """Verify the p2p connect via display functionality
+
+ Steps:
+ 1. Request the connection which include discover the target device
+ 2. check which dut is GO and which dut is GC
+ 3. connection check via ping from GC to GO
+ 4. disconnect
+ 5. Trigger connect again from GO for reconnect test.
+ 6. GO trigger disconnect
+ 7. Trigger connect again from GC for reconnect test.
+ 8. GC trigger disconnect
+ """
+ # Request the connection
+ wp2putils.p2p_connect(self.dut1, self.dut2, False, WPS_DISPLAY)
+
+ if wp2putils.is_go(self.dut1):
+ go_dut = self.dut1
+ gc_dut = self.dut2
+ elif wp2putils.is_go(self.dut2):
+ go_dut = self.dut2
+ gc_dut = self.dut1
+
+ go_ip = wp2putils.p2p_go_ip(gc_dut)
+ wp2putils.p2p_connection_ping_test(gc_dut, go_ip)
+
+ # trigger disconnect
+ wp2putils.p2p_disconnect(self.dut1)
+ wp2putils.check_disconnect(self.dut2)
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+
+ self.log.info("Reconnect test, triggered by GO")
+ # trigger reconnect from GO
+ go_dut.ed.clear_all_events()
+ gc_dut.ed.clear_all_events()
+ wp2putils.p2p_connect(go_dut, gc_dut, True, WPS_DISPLAY)
+ wp2putils.p2p_disconnect(go_dut)
+ wp2putils.check_disconnect(gc_dut)
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+
+ # trigger reconnect from GC
+ self.log.info("Reconnect test, triggered by GC")
+ go_dut.ed.clear_all_events()
+ 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, timeout=p2pconsts.DEFAULT_GROUP_CLIENT_LOST_TIME)
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+
+ @test_tracker_info(uuid="efe88f57-5a08-4195-9592-2f6945a9d18a")
+ def test_p2p_connect_via_keypad_and_ping_and_reconnect(self):
+ """Verify the p2p connect via keypad functionality
+
+ Steps:
+ 1. Request the connection which include discover the target device
+ 2. check which dut is GO and which dut is GC
+ 3. connection check via ping from GC to GO
+ 4. disconnect
+ 5. Trigger connect again from GO for reconnect test.
+ 6. GO trigger disconnect
+ 7. Trigger connect again from GC for reconnect test.
+ 8. GC trigger disconnect
+ """
+ # Request the connection
+ wp2putils.p2p_connect(self.dut1, self.dut2, False, WPS_KEYPAD)
+
+ if wp2putils.is_go(self.dut1):
+ go_dut = self.dut1
+ gc_dut = self.dut2
+ elif wp2putils.is_go(self.dut2):
+ go_dut = self.dut2
+ gc_dut = self.dut1
+
+ go_ip = wp2putils.p2p_go_ip(gc_dut)
+ wp2putils.p2p_connection_ping_test(gc_dut, go_ip)
+
+ # trigger disconnect
+ wp2putils.p2p_disconnect(self.dut1)
+ wp2putils.check_disconnect(self.dut2)
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+
+ self.log.info("Reconnect test, triggered by GO")
+ # trigger reconnect from GO
+ go_dut.ed.clear_all_events()
+ gc_dut.ed.clear_all_events()
+ wp2putils.p2p_connect(go_dut, gc_dut, True, WPS_KEYPAD)
+ wp2putils.p2p_disconnect(go_dut)
+ wp2putils.check_disconnect(gc_dut)
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+
+ # trigger reconnect from GC
+ self.log.info("Reconnect test, triggered by GC")
+ go_dut.ed.clear_all_events()
+ 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, 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 0000000..2951be6
--- /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/tests/google/wifi/p2p/functional/WifiP2pSnifferTest.py b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pSnifferTest.py
new file mode 100644
index 0000000..2db5273
--- /dev/null
+++ b/acts_tests/tests/google/wifi/p2p/functional/WifiP2pSnifferTest.py
@@ -0,0 +1,147 @@
+#!/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 acts.test_utils.wifi.wifi_test_utils as wutils
+import acts.utils
+import time
+import re
+
+from acts import asserts
+from acts import utils
+
+from acts.test_decorators import test_tracker_info
+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
+from acts.controllers.ap_lib.hostapd_constants import BAND_2G
+from scapy.all import *
+
+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
+DEFAULT_TIMEOUT = 10
+
+
+class WifiP2pSnifferTest(WifiP2pBaseTest):
+ """Tests factory MAC is not leaked for p2p discovery and associated cases.
+
+ Test Bed Requirement:
+ * At least two Android devices
+ * An access point as sniffer
+ """
+ def __init__(self, controllers):
+ WifiP2pBaseTest.__init__(self, controllers)
+
+ def setup_class(self):
+ super(WifiP2pSnifferTest, self).setup_class()
+ wp2putils.wifi_p2p_set_channels_for_current_group(self.dut1, 6, 6)
+ wp2putils.wifi_p2p_set_channels_for_current_group(self.dut2, 6, 6)
+ self.configure_packet_capture()
+
+ def setup_test(self):
+ super(WifiP2pSnifferTest, self).setup_test()
+ self.pcap_procs = wutils.start_pcap(self.packet_capture, '2g',
+ self.test_name)
+
+ def teardown_test(self):
+ if self.pcap_procs:
+ wutils.stop_pcap(self.packet_capture, self.pcap_procs, False)
+ super(WifiP2pSnifferTest, self).teardown_test()
+
+ def configure_packet_capture(self):
+ """Configure packet capture on the social channels."""
+ self.packet_capture = self.packet_capture[0]
+ result = self.packet_capture.configure_monitor_mode(BAND_2G, 6)
+ if not result:
+ raise ValueError("Failed to configure channel for 2G band")
+
+ def verify_mac_no_leakage(self):
+ time.sleep(DEFAULT_TIMEOUT)
+ self.log.info("Stopping packet capture")
+ wutils.stop_pcap(self.packet_capture, self.pcap_procs, False)
+ # 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, 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")
+ def test_p2p_discovery_sniffer(self):
+ """Verify the p2p discovery functionality
+ Steps:
+ 1. Discover the target device
+ 2. Check the target device in peer list
+ """
+ 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):
+ """Verify the p2p connect via pbc functionality
+
+ Steps:
+ 1. Request the connection which include discover the target device
+ 2. check which dut is GO and which dut is GC
+ 3. connection check via ping from GC to GO
+ 4. disconnect
+ 5. Trigger connect again from GO for reconnect test.
+ 6. GO trigger disconnect
+ 7. Trigger connect again from GC for reconnect test.
+ 8. GC trigger disconnect
+ """
+ # Request the connection
+ wp2putils.p2p_connect(self.dut1, self.dut2, False, WPS_PBC)
+
+ if wp2putils.is_go(self.dut1):
+ go_dut = self.dut1
+ gc_dut = self.dut2
+ elif wp2putils.is_go(self.dut2):
+ go_dut = self.dut2
+ gc_dut = self.dut1
+
+ go_ip = wp2putils.p2p_go_ip(gc_dut)
+ wp2putils.p2p_connection_ping_test(gc_dut, go_ip)
+
+ # trigger disconnect
+ wp2putils.p2p_disconnect(self.dut1)
+ wp2putils.check_disconnect(self.dut2)
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+
+ self.log.info("Reconnect test, triggered by GO")
+ # trigger reconnect from GO
+ go_dut.ed.clear_all_events()
+ gc_dut.ed.clear_all_events()
+ wp2putils.p2p_connect(go_dut, gc_dut, True, WPS_PBC)
+ wp2putils.p2p_disconnect(go_dut)
+ wp2putils.check_disconnect(gc_dut)
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+
+ # trigger reconnect from GC
+ self.log.info("Reconnect test, triggered by GC")
+ go_dut.ed.clear_all_events()
+ 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)
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+
+ # teardown
+ self.verify_mac_no_leakage()
diff --git a/acts_tests/tests/google/wifi/p2p/performance/WifiP2pRvrTest.py b/acts_tests/tests/google/wifi/p2p/performance/WifiP2pRvrTest.py
new file mode 100644
index 0000000..85b9a0d
--- /dev/null
+++ b/acts_tests/tests/google/wifi/p2p/performance/WifiP2pRvrTest.py
@@ -0,0 +1,777 @@
+#!/usr/bin/env python3.4
+#
+# 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 collections
+import logging
+import os
+import re
+import time
+from acts import asserts
+from acts import base_test
+from acts import utils
+from acts.controllers import iperf_server as ipf
+from acts.controllers import iperf_client as ipc
+from acts.metrics.loggers.blackbox import BlackboxMappedMetricLogger
+from acts.test_utils.wifi import ota_sniffer
+from acts.test_utils.wifi import wifi_retail_ap as retail_ap
+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.p2p import wifi_p2p_const as p2pconsts
+from acts.test_utils.wifi.p2p import wifi_p2p_test_utils as wp2putils
+from WifiRvrTest import WifiRvrTest
+
+AccessPointTuple = collections.namedtuple(('AccessPointTuple'),
+ ['ap_settings'])
+
+
+class WifiP2pRvrTest(WifiRvrTest):
+ def __init__(self, controllers):
+ base_test.BaseTestClass.__init__(self, controllers)
+ self.testcase_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_case())
+ self.testclass_metric_logger = (
+ BlackboxMappedMetricLogger.for_test_class())
+ self.publish_testcase_metrics = True
+
+ def setup_class(self):
+ """Initializes common test hardware and parameters.
+
+ This function initializes hardwares and compiles parameters that are
+ common to all tests in this class.
+ """
+ req_params = ['p2p_rvr_test_params', 'testbed_params']
+ opt_params = ['RetailAccessPoints', 'ap_networks', 'OTASniffer']
+ self.unpack_userparams(req_params, opt_params)
+ if hasattr(self, 'RetailAccessPoints'):
+ self.access_points = retail_ap.create(self.RetailAccessPoints)
+ self.access_point = self.access_points[0]
+ else:
+ self.access_point = AccessPointTuple({})
+ self.testclass_params = self.p2p_rvr_test_params
+ self.num_atten = self.attenuators[0].instrument.num_atten
+ self.iperf_server = ipf.create([{
+ 'AndroidDevice':
+ self.android_devices[0].serial,
+ 'port':
+ '5201'
+ }])[0]
+ self.iperf_client = ipc.create([{
+ 'AndroidDevice':
+ self.android_devices[1].serial,
+ 'port':
+ '5201'
+ }])[0]
+ if hasattr(self,
+ 'OTASniffer') and self.testbed_params['sniffer_enable']:
+ self.sniffer = ota_sniffer.create(self.OTASniffer)[0]
+ self.log_path = os.path.join(logging.log_path, 'results')
+ os.makedirs(self.log_path, exist_ok=True)
+ if not hasattr(self, 'golden_files_list'):
+ if 'golden_results_path' in self.testbed_params:
+ self.golden_files_list = [
+ os.path.join(self.testbed_params['golden_results_path'],
+ file) for file in
+ os.listdir(self.testbed_params['golden_results_path'])
+ ]
+ else:
+ self.log.warning('No golden files found.')
+ self.golden_files_list = []
+
+ self.testclass_results = []
+
+ # Turn WiFi ON
+ for ad in self.android_devices:
+ self.init_device(ad)
+
+ # Configure test retries
+ self.user_params['retry_tests'] = [self.__class__.__name__]
+
+ def init_device(self, ad):
+ asserts.assert_true(utils.force_airplane_mode(ad, False),
+ "Can not turn off airplane mode.")
+ utils.set_location_service(ad, True)
+ ad.droid.wifiScannerToggleAlwaysAvailable(False)
+ asserts.assert_true(not ad.droid.wifiScannerIsAlwaysAvailable(),
+ "Failed to turn off location service's scan.")
+ wutils.reset_wifi(ad)
+ utils.sync_device_time(ad)
+ ad.droid.telephonyToggleDataConnection(False)
+ country_code = self.testclass_params.get('country_code', 'US')
+ wutils.set_wifi_country_code(ad, country_code)
+ ad.droid.wifiP2pInitialize()
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+ asserts.assert_true(
+ ad.droid.wifiP2pIsEnabled(),
+ "{} p2p was not properly initialized".format(ad.serial))
+ ad.name = "Android_" + ad.serial
+ ad.droid.wifiP2pSetDeviceName(ad.name)
+
+ def teardown_class(self):
+ # Turn WiFi OFF
+ for ad in self.android_devices:
+ ad.droid.wifiP2pClose()
+ utils.set_location_service(ad, False)
+ #wutils.wifi_toggle_state(ad, False)
+ self.process_testclass_results()
+ # Teardown AP and release its lockfile
+ self.access_point.teardown()
+
+ def setup_test(self):
+ for ad in self.android_devices:
+ ad.droid.wakeLockAcquireBright()
+ ad.droid.wakeUpNow()
+ ad.ed.clear_all_events()
+ ad.droid.wifiP2pRemoveGroup()
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+
+ def teardown_test(self):
+ self.iperf_server.stop()
+ for ad in self.android_devices:
+ ad.droid.wifiP2pRemoveGroup()
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+ for ad in self.android_devices:
+ # Clear p2p group info
+ ad.droid.wifiP2pRequestPersistentGroupInfo()
+ event = ad.ed.pop_event("WifiP2pOnPersistentGroupInfoAvailable",
+ 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 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):
+ # Check battery level before test
+ for ad in self.android_devices:
+ if not wputils.health_check(ad, 20):
+ asserts.skip('Overheating or Battery low. Skipping test.')
+ ad.go_to_sleep()
+ wutils.reset_wifi(ad)
+ # Turn BT on or off
+ bt_status = self.testclass_params.get('bluetooth_enabled', 1)
+ self.log.info('Setting Bluetooth status to {}.'.format(bt_status))
+ for ad in self.android_devices:
+ ad.droid.bluetoothToggleState(bt_status)
+ # Turn screen off to preserve battery
+ 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 get_p2p_mac_address(self, ad):
+ """Gets the current MAC address being used for Wi-Fi Direct."""
+ out = ad.adb.shell("ifconfig p2p0")
+ return re.match(".* HWaddr (\S+).*", out, re.S).group(1)
+
+ def _setup_p2p_connection_join_group(self, testcase_params):
+ if self.testbed_params['sniffer_enable']:
+ self.sniffer.start_capture(network={'SSID': 'dummy'},
+ chan=11,
+ bw=20,
+ duration=180)
+ # Create a group
+ self.go_dut = self.android_devices[0]
+ self.gc_dut = self.android_devices[1]
+ wp2putils.p2p_create_group(self.go_dut)
+ self.go_dut.ed.pop_event(p2pconsts.CONNECTED_EVENT,
+ p2pconsts.DEFAULT_TIMEOUT)
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+ # Request the connection
+ try:
+ wp2putils.p2p_connect(
+ self.gc_dut,
+ self.go_dut,
+ isReconnect=False,
+ p2p_connect_type=p2pconsts.P2P_CONNECT_JOIN,
+ wpsSetup=wp2putils.WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_PBC)
+ except Exception as e:
+ # Stop sniffer
+ if self.testbed_params['sniffer_enable']:
+ self.sniffer.stop_capture(tag='connection_setup')
+ raise e
+ if self.testbed_params['sniffer_enable']:
+ self.sniffer.stop_capture(tag='connection_setup')
+
+ def _setup_p2p_connection_negotiation(self, testcase_params):
+ if self.testbed_params['sniffer_enable']:
+ self.sniffer.start_capture(network={'SSID': 'dummy'},
+ chan=11,
+ bw=20,
+ duration=180)
+ try:
+ wp2putils.p2p_connect(
+ self.android_devices[0],
+ self.android_devices[1],
+ False,
+ wpsSetup=wp2putils.WifiP2PEnums.WpsInfo.WIFI_WPS_INFO_PBC)
+ if wp2putils.is_go(self.android_devices[0]):
+ self.go_dut = self.android_devices[0]
+ self.gc_dut = self.android_devices[1]
+ elif wp2putils.is_go(self.android_devices[1]):
+ self.go_dut = self.android_devices[1]
+ self.gc_dut = self.android_devices[0]
+ except Exception as e:
+ # Stop sniffer
+ if self.testbed_params['sniffer_enable']:
+ self.sniffer.stop_capture(tag='connection_setup')
+ raise e
+ if self.testbed_params['sniffer_enable']:
+ self.sniffer.stop_capture(tag='connection_setup')
+
+ def _get_gc_ip(self, subnet_mask='255.255.255.0'):
+ subnet_mask = ['255', '255', '255', '0']
+ go_ip = wp2putils.p2p_go_ip(self.gc_dut)
+ dut_subnet = [
+ int(dut) & int(subnet)
+ for dut, subnet in zip(go_ip.split('.'), subnet_mask)
+ ]
+ ifconfig_out = self.gc_dut.adb.shell('ifconfig')
+ ip_list = re.findall('inet (?:addr:)?(\d+.\d+.\d+.\d+)', ifconfig_out)
+ for current_ip in ip_list:
+ current_subnet = [
+ int(ip) & int(subnet)
+ for ip, subnet in zip(current_ip.split('.'), subnet_mask)
+ ]
+ if current_subnet == dut_subnet:
+ return current_ip
+ logging.error('No IP address found in requested subnet')
+
+ def setup_p2p_connection(self, testcase_params):
+ """Sets up WiFi Direct connection before running RvR."""
+
+ if self.testclass_params['p2p_group_negotiaton']:
+ self._setup_p2p_connection_negotiation(testcase_params)
+ else:
+ self._setup_p2p_connection_join_group(testcase_params)
+
+ # Get iperf server address
+ if wp2putils.is_go(self.android_devices[0]):
+ testcase_params['iperf_server_address'] = wp2putils.p2p_go_ip(
+ self.gc_dut)
+ else:
+ testcase_params['iperf_server_address'] = self._get_gc_ip()
+
+ p2p_interface = wp2putils.p2p_get_current_group(
+ self.gc_dut)['Interface']
+ connection_rssi = wputils.get_connected_rssi(self.gc_dut,
+ interface=p2p_interface)
+ testcase_params['test_network'] = {'SSID': connection_rssi['ssid'][0]}
+ testcase_params['channel'] = wutils.WifiEnums.freq_to_channel[
+ connection_rssi['frequency'][0]]
+ if testcase_params['channel'] < 13:
+ testcase_params['mode'] = 'VHT20'
+ else:
+ testcase_params['mode'] = 'VHT80'
+ self.log.info('Wifi Direct Connection Established on Channel {} {} '
+ '(SSID: {})'.format(
+ testcase_params['channel'], testcase_params['mode'],
+ testcase_params['test_network']['SSID']))
+
+ def setup_p2p_rvr_test(self, testcase_params):
+ # Setup the aps
+ self.setup_aps(testcase_params)
+ # Setup the duts
+ self.setup_duts(testcase_params)
+ # Set attenuator to 0 dB
+ for attenuator in self.attenuators:
+ attenuator.set_atten(0, strict=False)
+ # Setup the p2p connection
+ self.setup_p2p_connection(testcase_params)
+ # Set DUT to monitor RSSI and LLStats on
+ self.monitored_dut = self.gc_dut
+ self.monitored_interface = wp2putils.p2p_get_current_group(
+ self.gc_dut)['Interface']
+
+ def cleanup_p2p_rvr_test(self, testcase_params):
+ # clean-up
+ wp2putils.p2p_disconnect(self.go_dut)
+ wp2putils.check_disconnect(self.gc_dut)
+ time.sleep(p2pconsts.DEFAULT_FUNCTION_SWITCH_TIME)
+
+ def compile_test_params(self, testcase_params):
+ """Function that completes all test params based on the test name.
+
+ Args:
+ testcase_params: dict containing test-specific parameters
+ """
+ # Compile RvR parameters
+ num_atten_steps = int((self.testclass_params['atten_stop'] -
+ self.testclass_params['atten_start']) /
+ self.testclass_params['atten_step'])
+ testcase_params['atten_range'] = [
+ self.testclass_params['atten_start'] +
+ x * self.testclass_params['atten_step']
+ for x in range(0, num_atten_steps)
+ ]
+
+ # Compile iperf arguments
+ 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)
+ testcase_params['iperf_args'] = wputils.get_iperf_arg_string(
+ duration=self.testclass_params['iperf_duration'],
+ reverse_direction=(testcase_params['traffic_direction'] == 'DL'),
+ traffic_type=testcase_params['traffic_type'],
+ socket_size=testcase_params['iperf_socket_size'],
+ num_processes=testcase_params['iperf_processes'],
+ ipv6=False)
+ testcase_params['use_client_output'] = (
+ testcase_params['traffic_direction'] == 'DL')
+
+ # 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 is the only dut connected, assign it to ap 0
+ ap_id = 0
+ elif band == ap_networks[0]['band']:
+ # if its connected to same band, connect to ap 1
+ ap_id = 1
+ else:
+ # if its on a different band, connect to ap 0 as well
+ 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_p2p_rvr(self, testcase_params):
+ """ Function that gets called for each test case
+
+ Args:
+ testcase_params: dict containing test-specific parameters
+ """
+ # Compile test parameters from config and test name
+ testcase_params = self.compile_test_params(testcase_params)
+
+ # Prepare devices and run test
+ self.setup_p2p_rvr_test(testcase_params)
+ rvr_result = self.run_rvr_test(testcase_params)
+ self.cleanup_p2p_rvr_test(testcase_params)
+
+ # Post-process results
+ self.testclass_results.append(rvr_result)
+ self.process_test_results(rvr_result)
+ self.pass_fail_check(rvr_result)
+
+
+class WifiP2pRvr_TCP_Test(WifiP2pRvrTest):
+ #Test cases
+ def test_p2p_rvr_TCP_DL_disconnected_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='DL',
+ dut_connected=[False, False],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_TCP_DL_connected_2G_1_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='DL',
+ dut_connected=['2G_1', False],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_TCP_DL_connected_5G_1_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='DL',
+ dut_connected=['5G_1', False],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_TCP_DL_disconnected_connected_2G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='DL',
+ dut_connected=[False, '2G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_TCP_DL_disconnected_connected_5G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='DL',
+ dut_connected=[False, '5G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_TCP_DL_connected_2G_1_connected_2G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='DL',
+ dut_connected=['2G_1', '2G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_TCP_DL_connected_5G_1_connected_5G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='DL',
+ dut_connected=['5G_1', '5G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_TCP_DL_connected_2G_1_connected_5G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='DL',
+ dut_connected=['2G_1', '5G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_TCP_DL_connected_5G_1_connected_2G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='DL',
+ dut_connected=['5G_1', '2G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_TCP_DL_connected_2G_1_connected_2G_2(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='DL',
+ dut_connected=['2G_1', '2G_2'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_TCP_DL_connected_5G_1_connected_5G_2(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='DL',
+ dut_connected=['5G_1', '5G_2'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_TCP_UL_disconnected_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='UL',
+ dut_connected=[False, False],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_TCP_UL_connected_2G_1_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='UL',
+ dut_connected=['2G_1', False],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_TCP_UL_connected_5G_1_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='UL',
+ dut_connected=['5G_1', False],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_TCP_UL_disconnected_connected_2G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='UL',
+ dut_connected=[False, '2G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_TCP_UL_disconnected_connected_5G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='UL',
+ dut_connected=[False, '5G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_TCP_UL_connected_2G_1_connected_2G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='UL',
+ dut_connected=['2G_1', '2G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_TCP_UL_connected_5G_1_connected_5G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='UL',
+ dut_connected=['5G_1', '5G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_TCP_UL_connected_2G_1_connected_5G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='UL',
+ dut_connected=['2G_1', '5G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_TCP_UL_connected_5G_1_connected_2G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='UL',
+ dut_connected=['5G_1', '2G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_TCP_UL_connected_2G_1_connected_2G_2(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='UL',
+ dut_connected=['2G_1', '2G_2'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_TCP_UL_connected_5G_1_connected_5G_2(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='TCP',
+ traffic_direction='UL',
+ dut_connected=['5G_1', '5G_2'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+
+class WifiP2pRvr_UDP_Test(WifiP2pRvrTest):
+ #Test cases
+ def test_p2p_rvr_UDP_DL_disconnected_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='DL',
+ dut_connected=[False, False],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_UDP_DL_connected_2G_1_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='DL',
+ dut_connected=['2G_1', False],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_UDP_DL_connected_5G_1_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='DL',
+ dut_connected=['5G_1', False],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_UDP_DL_disconnected_connected_2G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='DL',
+ dut_connected=[False, '2G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_UDP_DL_disconnected_connected_5G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='DL',
+ dut_connected=[False, '5G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_UDP_DL_connected_2G_1_connected_2G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='DL',
+ dut_connected=['2G_1', '2G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_UDP_DL_connected_5G_1_connected_5G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='DL',
+ dut_connected=['5G_1', '5G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_UDP_DL_connected_2G_1_connected_5G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='DL',
+ dut_connected=['2G_1', '5G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_UDP_DL_connected_5G_1_connected_2G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='DL',
+ dut_connected=['5G_1', '2G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_UDP_DL_connected_2G_1_connected_2G_2(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='DL',
+ dut_connected=['2G_1', '2G_2'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_UDP_DL_connected_5G_1_connected_5G_2(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='DL',
+ dut_connected=['5G_1', '5G_2'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_UDP_UL_disconnected_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='UL',
+ dut_connected=[False, False],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_UDP_UL_connected_2G_1_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='UL',
+ dut_connected=['2G_1', False],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_UDP_UL_connected_5G_1_disconnected(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='UL',
+ dut_connected=['5G_1', False],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_UDP_UL_disconnected_connected_2G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='UL',
+ dut_connected=[False, '2G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_UDP_UL_disconnected_connected_5G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='UL',
+ dut_connected=[False, '5G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_UDP_UL_connected_2G_1_connected_2G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='UL',
+ dut_connected=['2G_1', '2G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_UDP_UL_connected_5G_1_connected_5G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='UL',
+ dut_connected=['5G_1', '5G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_UDP_UL_connected_2G_1_connected_5G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='UL',
+ dut_connected=['2G_1', '5G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_UDP_UL_connected_5G_1_connected_2G_1(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='UL',
+ dut_connected=['5G_1', '2G_1'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_UDP_UL_connected_2G_1_connected_2G_2(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='UL',
+ dut_connected=['2G_1', '2G_2'],
+ )
+ self._test_p2p_rvr(testcase_params)
+
+ def test_p2p_rvr_UDP_UL_connected_5G_1_connected_5G_2(self):
+ testcase_params = collections.OrderedDict(
+ traffic_type='UDP',
+ traffic_direction='UL',
+ dut_connected=['5G_1', '5G_2'],
+ )
+ self._test_p2p_rvr(testcase_params)
diff --git a/acts_tests/tests/google/wifi/rtt/README.md b/acts_tests/tests/google/wifi/rtt/README.md
new file mode 100644
index 0000000..1e435eb
--- /dev/null
+++ b/acts_tests/tests/google/wifi/rtt/README.md
@@ -0,0 +1,59 @@
+# Wi-Fi RTT (IEEE 802.11mc) Integrated (ACTS/sl4a) Test Suite
+
+This directory contains ACTS/sl4a test scripts to verify and characterize
+the Wi-Fi RTT (IEEE 802.11mc) implementation in Android.
+
+There are 2 groups of tests (in 2 sub-directories):
+
+* functional: Functional tests that each implementation must pass. These
+are pass/fail tests.
+* stress: Tests which run through a large number of iterations to stress
+test the implementation. Considering that some failures are expected,
+especially in an over-the-air situation, pass/fail criteria are either
+not provided or may not apply to all implementations or test environments.
+
+The tests can be executed using:
+
+`act.py -c <config> -tc {<test_class>|<test_class>:<test_name>}`
+
+Where a test file is any of the `.py` files in any of the test sub-directories.
+If a test class is specified, then all tests within that test class are executed.
+
+## Test Beds
+The Wi-Fi RTT tests support several different test scenarios which require different test bed
+configuration. The test beds and their corresponding test files are:
+
+* Device Under Test + AP which supports IEEE 802.11mc
+ * functional/RangeApSupporting11McTest.py
+ * functional/RttRequestManagementTest.py
+ * functional/RttDisableTest.py
+ * stress/StressRangeApTest.py
+* Device Under Test + AP which does **not** support IEEE 802.11mc
+ * functional/RangeApNonSupporting11McTest.py
+* 2 Devices Under Test
+ * functional/RangeAwareTest.py
+ * functional/AwareDiscoveryWithRangingTest.py
+ * functional/RangeSoftApTest.py
+ * stress/StressRangeAwareTest.py
+
+## Test Configurations
+The test configuration, the `<config>` in the commands above, is stored in
+the *config* sub-directory. The configuration simply uses all connected
+devices without listing specific serial numbers. Note that some tests use a
+single device while others use 2 devices.
+
+The only provided configuration is *wifi_rtt.json*.
+
+The configuration defines the following keys to configure the test:
+
+* **lci_reference**, **lcr_reference**: Arrays of bytes used to validate that the *correct* LCI and
+LCR were received from the AP. These are empty by default and should be configured to match the
+configuration of the AP used in the test.
+* **rtt_reference_distance_mm**: The reference distance, in mm, between the test device and the test
+AP or between the two test devices (for Aware ranging tests).
+* **stress_test_min_iteration_count**, **stress_test_target_run_time_sec**: Parameters used to
+control the length and duration of the stress tests. The stress test runs for the specified number
+of iterations or for the specified duration - whichever is longer.
+* **dbs_supported_models**: A list of device models which support DBS. Used to determine whether
+RTT will run while a SoftAP (SAP) is enabled. The model name corresponds to the value returned by
+*android_device.model*.
diff --git a/acts_tests/tests/google/wifi/rtt/config/wifi_rtt.json b/acts_tests/tests/google/wifi/rtt/config/wifi_rtt.json
new file mode 100644
index 0000000..1870fe9
--- /dev/null
+++ b/acts_tests/tests/google/wifi/rtt/config/wifi_rtt.json
@@ -0,0 +1,21 @@
+{
+ "_description": "This is a test configuration file for Wi-Fi RTT tests.",
+ "testbed":
+ [
+ {
+ "_description": "Wi-Fi RTT testbed: auto-detect all attached devices",
+ "name": "WifiRttAllAttached",
+ "AndroidDevice": "*"
+ }
+ ],
+ "logpath": "~/logs",
+ "testpaths": ["./tools/test/connectivity/acts_tests/tests/google/wifi"],
+ "adb_logcat_param": "-b all",
+ "aware_default_power_mode": "INTERACTIVE",
+ "lci_reference": [],
+ "lcr_reference": [],
+ "rtt_reference_distance_mm": 100,
+ "stress_test_min_iteration_count": 100,
+ "stress_test_target_run_time_sec" : 30,
+ "dbs_supported_models" : []
+}
diff --git a/acts_tests/tests/google/wifi/rtt/functional/AwareDiscoveryWithRangingTest.py b/acts_tests/tests/google/wifi/rtt/functional/AwareDiscoveryWithRangingTest.py
new file mode 100644
index 0000000..da0ba4b
--- /dev/null
+++ b/acts_tests/tests/google/wifi/rtt/functional/AwareDiscoveryWithRangingTest.py
@@ -0,0 +1,1928 @@
+#!/usr/bin/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 sys
+import time
+
+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.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.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class AwareDiscoveryWithRangingTest(AwareBaseTest, RttBaseTest):
+ """Set of tests for Wi-Fi Aware discovery configured with ranging (RTT)."""
+
+ SERVICE_NAME = "GoogleTestServiceRRRRR"
+
+ # Flag indicating whether the device has a limitation that does not allow it
+ # to execute Aware-based Ranging (whether direct or as part of discovery)
+ # whenever NDP is enabled.
+ RANGING_NDP_CONCURRENCY_LIMITATION = True
+
+ # Flag indicating whether the device has a limitation that does not allow it
+ # to execute Aware-based Ranging (whether direct or as part of discovery)
+ # for both Initiators and Responders. Only the first mode works.
+ RANGING_INITIATOR_RESPONDER_CONCURRENCY_LIMITATION = True
+
+ def setup_test(self):
+ """Manual setup here due to multiple inheritance: explicitly execute the
+ setup method from both parents."""
+ AwareBaseTest.setup_test(self)
+ RttBaseTest.setup_test(self)
+
+ def teardown_test(self):
+ """Manual teardown here due to multiple inheritance: explicitly execute the
+ teardown method from both parents."""
+ AwareBaseTest.teardown_test(self)
+ RttBaseTest.teardown_test(self)
+
+ #########################################################################
+
+ def run_discovery(self,
+ p_config,
+ s_config,
+ expect_discovery,
+ expect_range=False):
+ """Run discovery on the 2 input devices with the specified configurations.
+
+ Args:
+ p_config, s_config: Publisher and Subscriber discovery configuration.
+ expect_discovery: True or False indicating whether discovery is expected
+ with the specified configurations.
+ expect_range: True if we expect distance results (i.e. ranging to happen).
+ Only relevant if expect_discovery is True.
+ Returns:
+ p_dut, s_dut: Publisher/Subscribe DUT
+ p_disc_id, s_disc_id: Publisher/Subscribe discovery session ID
+ """
+ p_dut = self.android_devices[0]
+ p_dut.pretty_name = "Publisher"
+ s_dut = self.android_devices[1]
+ s_dut.pretty_name = "Subscriber"
+
+ # Publisher+Subscriber: attach and wait for confirmation
+ p_id = p_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(self.device_startup_offset)
+ s_id = s_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # Publisher: start publish and wait for confirmation
+ p_disc_id = p_dut.droid.wifiAwarePublish(p_id, p_config)
+ autils.wait_for_event(p_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+
+ # Subscriber: start subscribe and wait for confirmation
+ s_disc_id = s_dut.droid.wifiAwareSubscribe(s_id, s_config)
+ autils.wait_for_event(s_dut, aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED)
+
+ # Subscriber: wait or fail on service discovery
+ if expect_discovery:
+ event = autils.wait_for_event(
+ s_dut, aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+ if expect_range:
+ asserts.assert_true(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging expected!")
+ else:
+ asserts.assert_false(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging NOT expected!")
+ else:
+ autils.fail_on_event(s_dut,
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+
+ # (single) sleep for timeout period and then verify that no further events
+ time.sleep(autils.EVENT_TIMEOUT)
+ autils.verify_no_more_events(p_dut, timeout=0)
+ autils.verify_no_more_events(s_dut, timeout=0)
+
+ return p_dut, s_dut, p_disc_id, s_disc_id
+
+ def run_discovery_update(self,
+ p_dut,
+ s_dut,
+ p_disc_id,
+ s_disc_id,
+ p_config,
+ s_config,
+ expect_discovery,
+ expect_range=False):
+ """Run discovery on the 2 input devices with the specified update
+ configurations. I.e. update the existing discovery sessions with the
+ configurations.
+
+ Args:
+ p_dut, s_dut: Publisher/Subscriber DUTs.
+ p_disc_id, s_disc_id: Publisher/Subscriber discovery session IDs.
+ p_config, s_config: Publisher and Subscriber discovery configuration.
+ expect_discovery: True or False indicating whether discovery is expected
+ with the specified configurations.
+ expect_range: True if we expect distance results (i.e. ranging to happen).
+ Only relevant if expect_discovery is True.
+ """
+
+ # try to perform reconfiguration at same time (and wait once for all
+ # confirmations)
+ if p_config is not None:
+ p_dut.droid.wifiAwareUpdatePublish(p_disc_id, p_config)
+ if s_config is not None:
+ s_dut.droid.wifiAwareUpdateSubscribe(s_disc_id, s_config)
+
+ if p_config is not None:
+ autils.wait_for_event(p_dut,
+ aconsts.SESSION_CB_ON_SESSION_CONFIG_UPDATED)
+ if s_config is not None:
+ autils.wait_for_event(s_dut,
+ aconsts.SESSION_CB_ON_SESSION_CONFIG_UPDATED)
+
+ # Subscriber: wait or fail on service discovery
+ if expect_discovery:
+ event = autils.wait_for_event(
+ s_dut, aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+ if expect_range:
+ asserts.assert_true(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging expected!")
+ else:
+ asserts.assert_false(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging NOT expected!")
+ else:
+ autils.fail_on_event(s_dut,
+ aconsts.SESSION_CB_ON_SERVICE_DISCOVERED)
+
+ # (single) sleep for timeout period and then verify that no further events
+ time.sleep(autils.EVENT_TIMEOUT)
+ autils.verify_no_more_events(p_dut, timeout=0)
+ autils.verify_no_more_events(s_dut, timeout=0)
+
+ def run_discovery_prange_sminmax_outofrange(self, is_unsolicited_passive):
+ """Run discovery with ranging:
+ - Publisher enables ranging
+ - Subscriber enables ranging with min/max such that out of range (min=large,
+ max=large+1)
+
+ Expected: no discovery
+
+ This is a baseline test for the update-configuration tests.
+
+ Args:
+ is_unsolicited_passive: True for Unsolicited/Passive, False for
+ Solicited/Active.
+ Returns: the return arguments of the run_discovery.
+ """
+ pub_type = (aconsts.PUBLISH_TYPE_UNSOLICITED if is_unsolicited_passive
+ else aconsts.PUBLISH_TYPE_SOLICITED)
+ sub_type = (aconsts.SUBSCRIBE_TYPE_PASSIVE if is_unsolicited_passive
+ else aconsts.SUBSCRIBE_TYPE_ACTIVE)
+ return self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME, pub_type, ssi=self.getname(2)),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME, sub_type, ssi=self.getname(2)),
+ min_distance_mm=1000000,
+ max_distance_mm=1000001),
+ expect_discovery=False)
+
+ def getname(self, level=1):
+ """Python magic to return the name of the *calling* function.
+
+ Args:
+ level: How many levels up to go for the method name. Default = calling
+ method.
+ """
+ return sys._getframe(level).f_code.co_name
+
+ #########################################################################
+ # Run discovery with ranging configuration.
+ #
+ # Names: test_ranged_discovery_<ptype>_<stype>_<p_range>_<s_range>_<ref_dist>
+ #
+ # where:
+ # <ptype>_<stype>: unsolicited_passive or solicited_active
+ # <p_range>: prange or pnorange
+ # <s_range>: smin or smax or sminmax or snorange
+ # <ref_distance>: inrange or outoforange
+ #########################################################################
+
+ @test_tracker_info(uuid="3a216e9a-7a57-4741-89c0-84456975e1ac")
+ def test_ranged_discovery_unsolicited_passive_prange_snorange(self):
+ """Verify discovery with ranging:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber disables ranging
+
+ Expect: normal discovery (as if no ranging performed) - no distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ expect_discovery=True,
+ expect_range=False)
+
+ @test_tracker_info(uuid="859a321e-18e2-437b-aa7a-2a45a42ee737")
+ def test_ranged_discovery_solicited_active_prange_snorange(self):
+ """Verify discovery with ranging:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber disables ranging
+
+ Expect: normal discovery (as if no ranging performed) - no distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ expect_discovery=True,
+ expect_range=False)
+
+ @test_tracker_info(uuid="12a4f899-4f70-4641-8f3c-351004669b71")
+ def test_ranged_discovery_unsolicited_passive_pnorange_smax_inrange(self):
+ """Verify discovery with ranging:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher disables ranging
+ - Subscriber enables ranging with max such that always within range (large
+ max)
+
+ Expect: normal discovery (as if no ranging performed) - no distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ ssi=self.getname()),
+ enable_ranging=False),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=None,
+ max_distance_mm=1000000),
+ expect_discovery=True,
+ expect_range=False)
+
+ @test_tracker_info(uuid="b7f90793-113d-4355-be20-856d92ac939f")
+ def test_ranged_discovery_solicited_active_pnorange_smax_inrange(self):
+ """Verify discovery with ranging:
+ - Solicited Publish/Active Subscribe
+ - Publisher disables ranging
+ - Subscriber enables ranging with max such that always within range (large
+ max)
+
+ Expect: normal discovery (as if no ranging performed) - no distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ ssi=self.getname()),
+ enable_ranging=False),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=None,
+ max_distance_mm=1000000),
+ expect_discovery=True,
+ expect_range=False)
+
+ @test_tracker_info(uuid="da3ab6df-58f9-44ae-b7be-8200d9e1bb76")
+ def test_ranged_discovery_unsolicited_passive_pnorange_smin_outofrange(
+ self):
+ """Verify discovery with ranging:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher disables ranging
+ - Subscriber enables ranging with min such that always out of range (large
+ min)
+
+ Expect: normal discovery (as if no ranging performed) - no distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ ssi=self.getname()),
+ enable_ranging=False),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=1000000,
+ max_distance_mm=None),
+ expect_discovery=True,
+ expect_range=False)
+
+ @test_tracker_info(uuid="275e0806-f266-4fa6-9ca0-1cfd7b65a6ca")
+ def test_ranged_discovery_solicited_active_pnorange_smin_outofrange(self):
+ """Verify discovery with ranging:
+ - Solicited Publish/Active Subscribe
+ - Publisher disables ranging
+ - Subscriber enables ranging with min such that always out of range (large
+ min)
+
+ Expect: normal discovery (as if no ranging performed) - no distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ ssi=self.getname()),
+ enable_ranging=False),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=1000000,
+ max_distance_mm=None),
+ expect_discovery=True,
+ expect_range=False)
+
+ @test_tracker_info(uuid="8cd0aa1e-6866-4a5d-a550-f25483eebea1")
+ def test_ranged_discovery_unsolicited_passive_prange_smin_inrange(self):
+ """Verify discovery with ranging:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with min such that in range (min=0)
+
+ Expect: discovery with distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=0,
+ max_distance_mm=None),
+ expect_discovery=True,
+ expect_range=True)
+
+ @test_tracker_info(uuid="97c22c54-669b-4f7a-bf51-2f484e5f3e74")
+ def test_ranged_discovery_unsolicited_passive_prange_smax_inrange(self):
+ """Verify discovery with ranging:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with max such that in range (max=large)
+
+ Expect: discovery with distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=None,
+ max_distance_mm=1000000),
+ expect_discovery=True,
+ expect_range=True)
+
+ @test_tracker_info(uuid="616673d7-9d0b-43de-a378-e5e949b51b32")
+ def test_ranged_discovery_unsolicited_passive_prange_sminmax_inrange(self):
+ """Verify discovery with ranging:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with min/max such that in range (min=0,
+ max=large)
+
+ Expect: discovery with distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=0,
+ max_distance_mm=1000000),
+ expect_discovery=True,
+ expect_range=True)
+
+ @test_tracker_info(uuid="2bf84912-dcad-4a8f-971f-e445a07f05ce")
+ def test_ranged_discovery_solicited_active_prange_smin_inrange(self):
+ """Verify discovery with ranging:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with min such that in range (min=0)
+
+ Expect: discovery with distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=0,
+ max_distance_mm=None),
+ expect_discovery=True,
+ expect_range=True)
+
+ @test_tracker_info(uuid="5cfd7961-9665-4742-a1b5-2d1fc97f9795")
+ def test_ranged_discovery_solicited_active_prange_smax_inrange(self):
+ """Verify discovery with ranging:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with max such that in range (max=large)
+
+ Expect: discovery with distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=None,
+ max_distance_mm=1000000),
+ expect_discovery=True,
+ expect_range=True)
+
+ @test_tracker_info(uuid="5cf650ad-0b42-4b7d-9e05-d5f45fe0554d")
+ def test_ranged_discovery_solicited_active_prange_sminmax_inrange(self):
+ """Verify discovery with ranging:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with min/max such that in range (min=0,
+ max=large)
+
+ Expect: discovery with distance
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=0,
+ max_distance_mm=1000000),
+ expect_discovery=True,
+ expect_range=True)
+
+ @test_tracker_info(uuid="5277f418-ac35-43ce-9b30-3c895272898e")
+ def test_ranged_discovery_unsolicited_passive_prange_smin_outofrange(self):
+ """Verify discovery with ranging:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with min such that out of range (min=large)
+
+ Expect: no discovery
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=1000000,
+ max_distance_mm=None),
+ expect_discovery=False)
+
+ @test_tracker_info(uuid="8a7e6ab1-acf4-41a7-a5fb-8c164d593b5f")
+ def test_ranged_discovery_unsolicited_passive_prange_smax_outofrange(self):
+ """Verify discovery with ranging:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with max such that in range (max=0)
+
+ Expect: no discovery
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=None,
+ max_distance_mm=0),
+ expect_discovery=False)
+
+ @test_tracker_info(uuid="b744f5f9-2641-4373-bf86-3752e2f9aace")
+ def test_ranged_discovery_unsolicited_passive_prange_sminmax_outofrange(
+ self):
+ """Verify discovery with ranging:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with min/max such that out of range (min=large,
+ max=large+1)
+
+ Expect: no discovery
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=1000000,
+ max_distance_mm=1000001),
+ expect_discovery=False)
+
+ @test_tracker_info(uuid="d2e94199-b2e6-4fa5-a347-24594883c801")
+ def test_ranged_discovery_solicited_active_prange_smin_outofrange(self):
+ """Verify discovery with ranging:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with min such that out of range (min=large)
+
+ Expect: no discovery
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=1000000,
+ max_distance_mm=None),
+ expect_discovery=False)
+
+ @test_tracker_info(uuid="a5619835-496a-4244-a428-f85cba3d4115")
+ def test_ranged_discovery_solicited_active_prange_smax_outofrange(self):
+ """Verify discovery with ranging:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with max such that out of range (max=0)
+
+ Expect: no discovery
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=None,
+ max_distance_mm=0),
+ expect_discovery=False)
+
+ @test_tracker_info(uuid="12ebd91f-a973-410b-8ee1-0bd86024b921")
+ def test_ranged_discovery_solicited_active_prange_sminmax_outofrange(self):
+ """Verify discovery with ranging:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber enables ranging with min/max such that out of range (min=large,
+ max=large+1)
+
+ Expect: no discovery
+ """
+ self.run_discovery(
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=1000000,
+ max_distance_mm=1000001),
+ expect_discovery=False)
+
+ #########################################################################
+ # Run discovery with ranging configuration & update configurations after
+ # first run.
+ #
+ # Names: test_ranged_updated_discovery_<ptype>_<stype>_<scenario>
+ #
+ # where:
+ # <ptype>_<stype>: unsolicited_passive or solicited_active
+ # <scenario>: test scenario (details in name)
+ #########################################################################
+
+ @test_tracker_info(uuid="59442180-4a6c-428f-b926-86000e8339b4")
+ def test_ranged_updated_discovery_unsolicited_passive_oor_to_ir(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber:
+ - Starts: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Reconfigured to: Ranging enabled, min/max such that in range (min=0,
+ max=large)
+
+ Expect: discovery + ranging after update
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(True)
+ self.run_discovery_update(
+ p_dut,
+ s_dut,
+ p_disc_id,
+ s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=0,
+ max_distance_mm=1000000),
+ expect_discovery=True,
+ expect_range=True)
+
+ @test_tracker_info(uuid="60188508-104d-42d5-ac3a-3605093c45d7")
+ def test_ranged_updated_discovery_unsolicited_passive_pub_unrange(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Reconfigured to: Publisher disables ranging
+
+ Expect: discovery w/o ranging after update
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(True)
+ self.run_discovery_update(
+ p_dut,
+ s_dut,
+ p_disc_id,
+ s_disc_id,
+ p_config=autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ ssi=self.getname()),
+ s_config=None, # no updates
+ expect_discovery=True,
+ expect_range=False)
+
+ @test_tracker_info(uuid="f96b434e-751d-4eb5-ae01-0c5c3a6fb4a2")
+ def test_ranged_updated_discovery_unsolicited_passive_sub_unrange(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber:
+ - Starts: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Reconfigured to: Ranging disabled
+
+ Expect: discovery w/o ranging after update
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(True)
+ self.run_discovery_update(
+ p_dut,
+ s_dut,
+ p_disc_id,
+ s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ expect_discovery=True,
+ expect_range=False)
+
+ @test_tracker_info(uuid="78970de8-9362-4647-931a-3513bcf58e80")
+ def test_ranged_updated_discovery_unsolicited_passive_sub_oor(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber:
+ - Starts: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Reconfigured to: different out-of-range setting
+
+ Expect: no discovery after update
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(True)
+ self.run_discovery_update(
+ p_dut,
+ s_dut,
+ p_disc_id,
+ s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=100000,
+ max_distance_mm=100001),
+ expect_discovery=False)
+
+ @test_tracker_info(uuid="0841ad05-4899-4521-bd24-04a8e2e345ac")
+ def test_ranged_updated_discovery_unsolicited_passive_pub_same(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Reconfigured to: Publisher with same settings (ranging enabled)
+
+ Expect: no discovery after update
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(True)
+ self.run_discovery_update(
+ p_dut,
+ s_dut,
+ p_disc_id,
+ s_disc_id,
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=None, # no updates
+ expect_discovery=False)
+
+ @test_tracker_info(uuid="ec6ca57b-f115-4516-813a-4572b930c8d3")
+ def test_ranged_updated_discovery_unsolicited_passive_multi_step(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Unsolicited Publish/Passive Subscribe
+ - Publisher enables ranging
+ - Subscriber: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Expect: no discovery
+ - Reconfigured to: Ranging enabled, min/max such that in-range (min=0)
+ - Expect: discovery with ranging
+ - Reconfigured to: Ranging enabled, min/max such that out-of-range
+ (min=large)
+ - Expect: no discovery
+ - Reconfigured to: Ranging disabled
+ - Expect: discovery without ranging
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(True)
+ self.run_discovery_update(
+ p_dut,
+ s_dut,
+ p_disc_id,
+ s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=0,
+ max_distance_mm=None),
+ expect_discovery=True,
+ expect_range=True)
+ self.run_discovery_update(
+ p_dut,
+ s_dut,
+ p_disc_id,
+ s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ min_distance_mm=1000000,
+ max_distance_mm=None),
+ expect_discovery=False)
+ self.run_discovery_update(
+ p_dut,
+ s_dut,
+ p_disc_id,
+ s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_PASSIVE,
+ ssi=self.getname()),
+ expect_discovery=True,
+ expect_range=False)
+
+ @test_tracker_info(uuid="bbaac63b-000c-415f-bf19-0906f04031cd")
+ def test_ranged_updated_discovery_solicited_active_oor_to_ir(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber:
+ - Starts: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Reconfigured to: Ranging enabled, min/max such that in range (min=0,
+ max=large)
+
+ Expect: discovery + ranging after update
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(False)
+ self.run_discovery_update(
+ p_dut,
+ s_dut,
+ p_disc_id,
+ s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=0,
+ max_distance_mm=1000000),
+ expect_discovery=True,
+ expect_range=True)
+
+ @test_tracker_info(uuid="c385b361-7955-4f34-9109-8d8ca81cb4cc")
+ def test_ranged_updated_discovery_solicited_active_pub_unrange(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Reconfigured to: Publisher disables ranging
+
+ Expect: discovery w/o ranging after update
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(False)
+ self.run_discovery_update(
+ p_dut,
+ s_dut,
+ p_disc_id,
+ s_disc_id,
+ p_config=autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ ssi=self.getname()),
+ s_config=None, # no updates
+ expect_discovery=True,
+ expect_range=False)
+
+ @test_tracker_info(uuid="ec5120ea-77ec-48c6-8820-48b82ad3dfd4")
+ def test_ranged_updated_discovery_solicited_active_sub_unrange(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber:
+ - Starts: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Reconfigured to: Ranging disabled
+
+ Expect: discovery w/o ranging after update
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(False)
+ self.run_discovery_update(
+ p_dut,
+ s_dut,
+ p_disc_id,
+ s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ expect_discovery=True,
+ expect_range=False)
+
+ @test_tracker_info(uuid="6231cb42-91e4-48d3-b9db-b37efbe8537c")
+ def test_ranged_updated_discovery_solicited_active_sub_oor(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber:
+ - Starts: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Reconfigured to: different out-of-range setting
+
+ Expect: no discovery after update
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(False)
+ self.run_discovery_update(
+ p_dut,
+ s_dut,
+ p_disc_id,
+ s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=100000,
+ max_distance_mm=100001),
+ expect_discovery=False)
+
+ @test_tracker_info(uuid="ec999420-6a50-455e-b624-f4c9b4cb7ea5")
+ def test_ranged_updated_discovery_solicited_active_pub_same(self):
+ """Verify discovery with ranging operation with updated configuration:
+ - Solicited Publish/Active Subscribe
+ - Publisher enables ranging
+ - Subscriber: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Reconfigured to: Publisher with same settings (ranging enabled)
+
+ Expect: no discovery after update
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(False)
+ self.run_discovery_update(
+ p_dut,
+ s_dut,
+ p_disc_id,
+ s_disc_id,
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_SOLICITED,
+ ssi=self.getname()),
+ enable_ranging=True),
+ s_config=None, # no updates
+ expect_discovery=False)
+
+ @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
+ - Publisher enables ranging
+ - Subscriber: Ranging enabled, min/max such that out of range (min=large,
+ max=large+1)
+ - Expect: no discovery
+ - Reconfigured to: Ranging enabled, min/max such that in-range (min=0)
+ - Expect: discovery with ranging
+ - Reconfigured to: Ranging enabled, min/max such that out-of-range
+ (min=large)
+ - Expect: no discovery
+ - Reconfigured to: Ranging disabled
+ - Expect: discovery without ranging
+ """
+ (p_dut, s_dut, p_disc_id,
+ s_disc_id) = self.run_discovery_prange_sminmax_outofrange(True)
+ self.run_discovery_update(
+ p_dut,
+ s_dut,
+ p_disc_id,
+ s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=0,
+ max_distance_mm=None),
+ expect_discovery=True,
+ expect_range=True)
+ self.run_discovery_update(
+ p_dut,
+ s_dut,
+ p_disc_id,
+ s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ min_distance_mm=1000000,
+ max_distance_mm=None),
+ expect_discovery=False)
+ self.run_discovery_update(
+ p_dut,
+ s_dut,
+ p_disc_id,
+ s_disc_id,
+ p_config=None, # no updates
+ s_config=autils.create_discovery_config(
+ self.SERVICE_NAME,
+ aconsts.SUBSCRIBE_TYPE_ACTIVE,
+ ssi=self.getname()),
+ expect_discovery=True,
+ expect_range=False)
+
+ #########################################################################
+
+ @test_tracker_info(uuid="6edc47ab-7300-4bff-b7dd-5de83f58928a")
+ def test_ranged_discovery_multi_session(self):
+ """Verify behavior with multiple concurrent discovery session with different
+ configurations:
+
+ Device A (Publisher):
+ Publisher AA: ranging enabled
+ Publisher BB: ranging enabled
+ Publisher CC: ranging enabled
+ Publisher DD: ranging disabled
+ Device B (Subscriber):
+ Subscriber AA: ranging out-of-range -> no match
+ Subscriber BB: ranging in-range -> match w/range
+ Subscriber CC: ranging disabled -> match w/o range
+ Subscriber DD: ranging out-of-range -> match w/o range
+ """
+ p_dut = self.android_devices[0]
+ p_dut.pretty_name = "Publisher"
+ s_dut = self.android_devices[1]
+ s_dut.pretty_name = "Subscriber"
+
+ # Publisher+Subscriber: attach and wait for confirmation
+ p_id = p_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(self.device_startup_offset)
+ s_id = s_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # Subscriber: start sessions
+ aa_s_disc_id = s_dut.droid.wifiAwareSubscribe(
+ s_id,
+ autils.add_ranging_to_sub(
+ autils.create_discovery_config("AA",
+ aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=1000000,
+ max_distance_mm=1000001), True)
+ bb_s_disc_id = s_dut.droid.wifiAwareSubscribe(
+ s_id,
+ autils.add_ranging_to_sub(
+ autils.create_discovery_config("BB",
+ aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=0,
+ max_distance_mm=1000000), True)
+ cc_s_disc_id = s_dut.droid.wifiAwareSubscribe(
+ s_id,
+ autils.create_discovery_config(
+ "CC", aconsts.SUBSCRIBE_TYPE_PASSIVE), True)
+ dd_s_disc_id = s_dut.droid.wifiAwareSubscribe(
+ s_id,
+ autils.add_ranging_to_sub(
+ autils.create_discovery_config("DD",
+ aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=1000000,
+ max_distance_mm=1000001), True)
+
+ autils.wait_for_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED,
+ aa_s_disc_id))
+ autils.wait_for_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED,
+ bb_s_disc_id))
+ autils.wait_for_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED,
+ cc_s_disc_id))
+ autils.wait_for_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED,
+ dd_s_disc_id))
+
+ # Publisher: start sessions
+ aa_p_disc_id = p_dut.droid.wifiAwarePublish(
+ p_id,
+ autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ "AA", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True), True)
+ bb_p_disc_id = p_dut.droid.wifiAwarePublish(
+ p_id,
+ autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ "BB", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True), True)
+ cc_p_disc_id = p_dut.droid.wifiAwarePublish(
+ p_id,
+ autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ "CC", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True), True)
+ dd_p_disc_id = p_dut.droid.wifiAwarePublish(
+ p_id,
+ autils.create_discovery_config(
+ "DD", aconsts.PUBLISH_TYPE_UNSOLICITED), True)
+
+ autils.wait_for_event(
+ p_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_PUBLISH_STARTED,
+ aa_p_disc_id))
+ autils.wait_for_event(
+ p_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_PUBLISH_STARTED,
+ bb_p_disc_id))
+ autils.wait_for_event(
+ p_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_PUBLISH_STARTED,
+ cc_p_disc_id))
+ autils.wait_for_event(
+ p_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_PUBLISH_STARTED,
+ dd_p_disc_id))
+
+ # Expected and unexpected service discovery
+ event = autils.wait_for_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ bb_s_disc_id))
+ asserts.assert_true(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for BB expected!")
+ event = autils.wait_for_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ cc_s_disc_id))
+ asserts.assert_false(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for CC NOT expected!")
+ event = autils.wait_for_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ dd_s_disc_id))
+ asserts.assert_false(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for DD NOT expected!")
+ autils.fail_on_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ aa_s_disc_id))
+
+ # (single) sleep for timeout period and then verify that no further events
+ 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="deede47f-a54c-46d9-88bb-f4482fbd8470")
+ def test_ndp_concurrency(self):
+ """Verify the behavior of Wi-Fi Aware Ranging whenever an NDP is created -
+ for those devices that have a concurrency limitation that does not allow
+ Aware Ranging, whether direct or as part of discovery.
+
+ Publisher: start 3 services
+ AA w/o ranging
+ BB w/ ranging
+ CC w/ ranging
+ DD w/ ranging
+ Subscriber: start 2 services
+ AA w/o ranging
+ BB w/ ranging out-of-range
+ (do not start CC!)
+ DD w/ ranging in-range
+ Expect AA discovery, DD discovery w/range, but no BB
+ Start NDP in context of AA
+ IF NDP_CONCURRENCY_LIMITATION:
+ Verify discovery on BB w/o range
+ Start EE w/ranging out-of-range
+ Start FF w/ranging in-range
+ IF NDP_CONCURRENCY_LIMITATION:
+ Verify discovery on EE w/o range
+ Verify discovery on FF w/o range
+ Else:
+ Verify discovery on FF w/ range
+ Tear down NDP
+ Subscriber
+ Start CC w/ ranging out-of-range
+ Wait to verify that do not get match
+ Update configuration to be in-range
+ Verify that get match with ranging information
+ """
+ p_dut = self.android_devices[0]
+ p_dut.pretty_name = "Publisher"
+ s_dut = self.android_devices[1]
+ s_dut.pretty_name = "Subscriber"
+
+ # Publisher+Subscriber: attach and wait for confirmation
+ p_id = p_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(p_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(self.device_startup_offset)
+ s_id = s_dut.droid.wifiAwareAttach(False)
+ autils.wait_for_event(s_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # Publisher: AA w/o ranging, BB w/ ranging, CC w/ ranging, DD w/ ranging
+ aa_p_id = p_dut.droid.wifiAwarePublish(
+ p_id,
+ autils.create_discovery_config(
+ "AA", aconsts.PUBLISH_TYPE_SOLICITED), True)
+ autils.wait_for_event(
+ p_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_PUBLISH_STARTED,
+ aa_p_id))
+ bb_p_id = p_dut.droid.wifiAwarePublish(
+ p_id,
+ autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ "BB", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(
+ p_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_PUBLISH_STARTED,
+ bb_p_id))
+ cc_p_id = p_dut.droid.wifiAwarePublish(
+ p_id,
+ autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ "CC", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(
+ p_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_PUBLISH_STARTED,
+ cc_p_id))
+ dd_p_id = p_dut.droid.wifiAwarePublish(
+ p_id,
+ autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ "DD", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(
+ p_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_PUBLISH_STARTED,
+ dd_p_id))
+
+ # Subscriber: AA w/o ranging, BB w/ranging out-of-range,
+ # DD w /ranging in-range
+ aa_s_id = s_dut.droid.wifiAwareSubscribe(
+ s_id,
+ autils.create_discovery_config(
+ "AA", aconsts.SUBSCRIBE_TYPE_ACTIVE), True)
+ autils.wait_for_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED,
+ aa_s_id))
+ bb_s_id = s_dut.droid.wifiAwareSubscribe(
+ s_id,
+ autils.add_ranging_to_sub(
+ autils.create_discovery_config("BB",
+ aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=1000000,
+ max_distance_mm=1000001), True)
+ autils.wait_for_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED,
+ bb_s_id))
+ dd_s_id = s_dut.droid.wifiAwareSubscribe(
+ s_id,
+ autils.add_ranging_to_sub(
+ autils.create_discovery_config("DD",
+ aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=None,
+ max_distance_mm=1000000), True)
+ autils.wait_for_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED,
+ dd_s_id))
+
+ # verify: AA discovered, BB not discovered, DD discovery w/range
+ event = autils.wait_for_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ aa_s_id))
+ asserts.assert_false(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for AA NOT expected!")
+ aa_peer_id_on_sub = event['data'][aconsts.SESSION_CB_KEY_PEER_ID]
+ autils.fail_on_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ bb_s_id))
+ event = autils.wait_for_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ dd_s_id))
+ asserts.assert_true(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for DD expected!")
+
+ # start NDP in context of AA:
+
+ # Publisher: request network (from ANY)
+ p_req_key = autils.request_network(
+ p_dut, p_dut.droid.wifiAwareCreateNetworkSpecifier(aa_p_id, None))
+
+ # Subscriber: request network
+ s_req_key = autils.request_network(
+ s_dut,
+ s_dut.droid.wifiAwareCreateNetworkSpecifier(
+ aa_s_id, aa_peer_id_on_sub))
+
+ # Publisher & Subscriber: wait for network formation
+ p_net_event = autils.wait_for_event_with_keys(
+ p_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, p_req_key))
+ s_net_event = autils.wait_for_event_with_keys(
+ s_dut, cconsts.EVENT_NETWORK_CALLBACK, autils.EVENT_TIMEOUT,
+ (cconsts.NETWORK_CB_KEY_EVENT,
+ cconsts.NETWORK_CB_LINK_PROPERTIES_CHANGED),
+ (cconsts.NETWORK_CB_KEY_ID, s_req_key))
+
+ p_aware_if = p_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+ s_aware_if = s_net_event["data"][cconsts.NETWORK_CB_KEY_INTERFACE_NAME]
+
+ p_ipv6 = p_dut.droid.connectivityGetLinkLocalIpv6Address(
+ p_aware_if).split("%")[0]
+ s_ipv6 = s_dut.droid.connectivityGetLinkLocalIpv6Address(
+ s_aware_if).split("%")[0]
+
+ self.log.info("AA NDP Interface names: P=%s, S=%s", p_aware_if,
+ s_aware_if)
+ self.log.info("AA NDP Interface addresses (IPv6): P=%s, S=%s", p_ipv6,
+ s_ipv6)
+
+ if self.RANGING_NDP_CONCURRENCY_LIMITATION:
+ # Expect BB to now discover w/o ranging
+ event = autils.wait_for_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ bb_s_id))
+ asserts.assert_false(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for BB NOT expected!")
+
+ # Publishers: EE, FF w/ ranging
+ ee_p_id = p_dut.droid.wifiAwarePublish(
+ p_id,
+ autils.add_ranging_to_pub(
+ autils.create_discovery_config("EE",
+ aconsts.PUBLISH_TYPE_SOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(
+ p_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_PUBLISH_STARTED,
+ ee_p_id))
+ ff_p_id = p_dut.droid.wifiAwarePublish(
+ p_id,
+ autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ "FF", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(
+ p_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_PUBLISH_STARTED,
+ ff_p_id))
+
+ # Subscribers: EE out-of-range, FF in-range
+ ee_s_id = s_dut.droid.wifiAwareSubscribe(
+ s_id,
+ autils.add_ranging_to_sub(
+ autils.create_discovery_config("EE",
+ aconsts.SUBSCRIBE_TYPE_ACTIVE),
+ min_distance_mm=1000000,
+ max_distance_mm=1000001), True)
+ autils.wait_for_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED,
+ ee_s_id))
+ ff_s_id = s_dut.droid.wifiAwareSubscribe(
+ s_id,
+ autils.add_ranging_to_sub(
+ autils.create_discovery_config("FF",
+ aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=None,
+ max_distance_mm=1000000), True)
+ autils.wait_for_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED,
+ ff_s_id))
+
+ if self.RANGING_NDP_CONCURRENCY_LIMITATION:
+ # Expect EE & FF discovery w/o range
+ event = autils.wait_for_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ ee_s_id))
+ asserts.assert_false(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for EE NOT expected!")
+ event = autils.wait_for_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ ff_s_id))
+ asserts.assert_false(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for FF NOT expected!")
+ else:
+ event = autils.wait_for_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ ff_s_id))
+ asserts.assert_true(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for FF expected!")
+
+ # tear down NDP
+ p_dut.droid.connectivityUnregisterNetworkCallback(p_req_key)
+ s_dut.droid.connectivityUnregisterNetworkCallback(s_req_key)
+
+ time.sleep(5) # give time for NDP termination to finish
+
+ # Subscriber: start CC out-of-range - no discovery expected!
+ cc_s_id = s_dut.droid.wifiAwareSubscribe(
+ s_id,
+ autils.add_ranging_to_sub(
+ autils.create_discovery_config("CC",
+ aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=1000000,
+ max_distance_mm=1000001), True)
+ autils.wait_for_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED,
+ cc_s_id))
+ autils.fail_on_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ cc_s_id))
+
+ # Subscriber: modify CC to in-range - expect discovery w/ range
+ s_dut.droid.wifiAwareUpdateSubscribe(
+ cc_s_id,
+ autils.add_ranging_to_sub(
+ autils.create_discovery_config("CC",
+ aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=None,
+ max_distance_mm=1000001))
+ autils.wait_for_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SESSION_CONFIG_UPDATED,
+ cc_s_id))
+ event = autils.wait_for_event(
+ s_dut,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ cc_s_id))
+ asserts.assert_true(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for CC expected!")
+
+ @test_tracker_info(uuid="d94dac91-4090-4c03-a867-6dfac6558ba3")
+ def test_role_concurrency(self):
+ """Verify the behavior of Wi-Fi Aware Ranging (in the context of discovery)
+ when the device has concurrency limitations which do not permit concurrent
+ Initiator and Responder roles on the same device. In such case it is
+ expected that normal discovery without ranging is executed AND that ranging
+ is restored whenever the concurrency constraints are removed.
+
+ Note: all Subscribers are in-range.
+
+ DUT1: start multiple services
+ Publish AA w/ ranging (unsolicited)
+ Subscribe BB w/ ranging (active)
+ Publish CC w/ ranging (unsolicited)
+ Publish DD w/o ranging (solicited)
+ Subscribe EE w/ ranging (passive)
+ Subscribe FF w/ ranging (active)
+ DUT2: start multiple services
+ Subscribe AA w/ ranging (passive)
+ Publish BB w/ ranging (solicited)
+ Subscribe DD w/o ranging (active)
+ Expect
+ DUT2: AA match w/ range information
+ DUT1: BB match w/o range information (concurrency disables ranging)
+ DUT2: DD match w/o range information
+ DUT1: Terminate AA
+ DUT2:
+ Terminate AA
+ Start Publish EE w/ ranging (unsolicited)
+ DUT1: expect EE w/o ranging
+ DUT1: Terminate CC
+ DUT2: Start Publish FF w/ ranging (solicited)
+ DUT1: expect FF w/ ranging information - should finally be back up
+ """
+ dut1 = self.android_devices[0]
+ dut1.pretty_name = "DUT1"
+ dut2 = self.android_devices[1]
+ dut2.pretty_name = "DUT2"
+
+ # Publisher+Subscriber: attach and wait for confirmation
+ dut1_id = dut1.droid.wifiAwareAttach(False)
+ autils.wait_for_event(dut1, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(self.device_startup_offset)
+ dut2_id = dut2.droid.wifiAwareAttach(False)
+ autils.wait_for_event(dut2, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # DUT1: initial service bringup
+ aa_p_id = dut1.droid.wifiAwarePublish(
+ dut1_id,
+ autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ "AA", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(
+ dut1,
+ autils.decorate_event(aconsts.SESSION_CB_ON_PUBLISH_STARTED,
+ aa_p_id))
+ bb_s_id = dut1.droid.wifiAwareSubscribe(
+ dut1_id,
+ autils.add_ranging_to_sub(
+ autils.create_discovery_config("BB",
+ aconsts.SUBSCRIBE_TYPE_ACTIVE),
+ min_distance_mm=None,
+ max_distance_mm=1000000), True)
+ autils.wait_for_event(
+ dut1,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED,
+ bb_s_id))
+ cc_p_id = dut1.droid.wifiAwarePublish(
+ dut1_id,
+ autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ "CC", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(
+ dut1,
+ autils.decorate_event(aconsts.SESSION_CB_ON_PUBLISH_STARTED,
+ cc_p_id))
+ dd_p_id = dut1.droid.wifiAwarePublish(
+ dut1_id,
+ autils.create_discovery_config(
+ "DD", aconsts.PUBLISH_TYPE_SOLICITED), True)
+ autils.wait_for_event(
+ dut1,
+ autils.decorate_event(aconsts.SESSION_CB_ON_PUBLISH_STARTED,
+ dd_p_id))
+ ee_s_id = dut1.droid.wifiAwareSubscribe(
+ dut1_id,
+ autils.add_ranging_to_sub(
+ autils.create_discovery_config("EE",
+ aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=None,
+ max_distance_mm=1000000), True)
+ autils.wait_for_event(
+ dut1,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED,
+ ee_s_id))
+ ff_s_id = dut1.droid.wifiAwareSubscribe(
+ dut1_id,
+ autils.add_ranging_to_sub(
+ autils.create_discovery_config("FF",
+ aconsts.SUBSCRIBE_TYPE_ACTIVE),
+ min_distance_mm=None,
+ max_distance_mm=1000000), True)
+ autils.wait_for_event(
+ dut1,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED,
+ ff_s_id))
+
+ # DUT2: initial service bringup
+ aa_s_id = dut2.droid.wifiAwareSubscribe(
+ dut2_id,
+ autils.add_ranging_to_sub(
+ autils.create_discovery_config("AA",
+ aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=None,
+ max_distance_mm=1000000), True)
+ autils.wait_for_event(
+ dut2,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED,
+ aa_s_id))
+ bb_p_id = dut2.droid.wifiAwarePublish(
+ dut2_id,
+ autils.add_ranging_to_pub(
+ autils.create_discovery_config("BB",
+ aconsts.PUBLISH_TYPE_SOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(
+ dut2,
+ autils.decorate_event(aconsts.SESSION_CB_ON_PUBLISH_STARTED,
+ bb_p_id))
+ dd_s_id = dut2.droid.wifiAwareSubscribe(
+ dut2_id,
+ autils.create_discovery_config(
+ "AA", aconsts.SUBSCRIBE_TYPE_ACTIVE), True)
+ autils.wait_for_event(
+ dut2,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED,
+ dd_s_id))
+
+ # Initial set of discovery events for AA, BB, and DD (which are up)
+ event = autils.wait_for_event(
+ dut2,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ aa_s_id))
+ asserts.assert_true(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for AA expected!")
+ event = autils.wait_for_event(
+ dut1,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ bb_s_id))
+ if self.RANGING_INITIATOR_RESPONDER_CONCURRENCY_LIMITATION:
+ asserts.assert_false(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for BB NOT expected!")
+ else:
+ asserts.assert_true(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for BB expected!")
+ event = autils.wait_for_event(
+ dut2,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ dd_s_id))
+ asserts.assert_false(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for DD NOT expected!")
+
+ # DUT1/DUT2: terminate AA
+ dut1.droid.wifiAwareDestroyDiscoverySession(aa_p_id)
+ dut2.droid.wifiAwareDestroyDiscoverySession(aa_s_id)
+
+ time.sleep(
+ 5) # guarantee that session terminated (and host recovered?)
+
+ # DUT2: try EE service - ranging still disabled
+ ee_p_id = dut2.droid.wifiAwarePublish(
+ dut2_id,
+ autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ "EE", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(
+ dut2,
+ autils.decorate_event(aconsts.SESSION_CB_ON_PUBLISH_STARTED,
+ ee_p_id))
+
+ event = autils.wait_for_event(
+ dut1,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ ee_s_id))
+ if self.RANGING_INITIATOR_RESPONDER_CONCURRENCY_LIMITATION:
+ asserts.assert_false(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for EE NOT expected!")
+ else:
+ asserts.assert_true(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for EE expected!")
+
+ # DUT1: terminate CC - last publish w/ ranging on DUT!
+ dut1.droid.wifiAwareDestroyDiscoverySession(cc_p_id)
+
+ time.sleep(
+ 5) # guarantee that session terminated (and host recovered?)
+
+ # DUT2: try FF service - ranging should now function
+ ff_p_id = dut2.droid.wifiAwarePublish(
+ dut2_id,
+ autils.add_ranging_to_pub(
+ autils.create_discovery_config("FF",
+ aconsts.PUBLISH_TYPE_SOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(
+ dut2,
+ autils.decorate_event(aconsts.SESSION_CB_ON_PUBLISH_STARTED,
+ ff_p_id))
+
+ event = autils.wait_for_event(
+ dut1,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ ff_s_id))
+ asserts.assert_true(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for FF expected!")
+
+ @test_tracker_info(uuid="6700eab8-a172-43cd-aed3-e6577ce8fd89")
+ def test_discovery_direct_concurrency(self):
+ """Verify the behavior of Wi-Fi Aware Ranging used as part of discovery and
+ as direct ranging to a peer device.
+
+ Process:
+ - Start YYY service with ranging in-range
+ - Start XXX service with ranging out-of-range
+ - Start performing direct Ranging
+ - While above going on update XXX to be in-range
+ - Keep performing direct Ranging in context of YYY
+ - Stop direct Ranging and look for XXX to discover
+ """
+ dut1 = self.android_devices[0]
+ dut1.pretty_name = "DUT1"
+ dut2 = self.android_devices[1]
+ dut2.pretty_name = "DUT2"
+
+ # DUTs: attach and wait for confirmation
+ dut1_id = dut1.droid.wifiAwareAttach(False)
+ autils.wait_for_event(dut1, aconsts.EVENT_CB_ON_ATTACHED)
+ time.sleep(self.device_startup_offset)
+ dut2_id = dut2.droid.wifiAwareAttach(True)
+ event = autils.wait_for_event(dut2,
+ aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+ dut2_mac = event['data']['mac']
+
+ # DUT1: publishers bring-up
+ xxx_p_id = dut1.droid.wifiAwarePublish(
+ dut1_id,
+ autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ "XXX", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(
+ dut1,
+ autils.decorate_event(aconsts.SESSION_CB_ON_PUBLISH_STARTED,
+ xxx_p_id))
+ yyy_p_id = dut1.droid.wifiAwarePublish(
+ dut1_id,
+ autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ "YYY", aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True), True)
+ autils.wait_for_event(
+ dut1,
+ autils.decorate_event(aconsts.SESSION_CB_ON_PUBLISH_STARTED,
+ yyy_p_id))
+
+ # DUT2: subscribers bring-up
+ xxx_s_id = dut2.droid.wifiAwareSubscribe(
+ dut2_id,
+ autils.add_ranging_to_sub(
+ autils.create_discovery_config("XXX",
+ aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=1000000,
+ max_distance_mm=1000001), True)
+ autils.wait_for_event(
+ dut2,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED,
+ xxx_s_id))
+ yyy_s_id = dut2.droid.wifiAwareSubscribe(
+ dut2_id,
+ autils.add_ranging_to_sub(
+ autils.create_discovery_config("YYY",
+ aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=None,
+ max_distance_mm=1000000), True)
+ autils.wait_for_event(
+ dut2,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SUBSCRIBE_STARTED,
+ yyy_s_id))
+
+ # Service discovery: YYY (with range info), but no XXX
+ event = autils.wait_for_event(
+ dut2,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ yyy_s_id))
+ asserts.assert_true(
+ aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"],
+ "Discovery with ranging for YYY expected!")
+ yyy_peer_id_on_sub = event['data'][aconsts.SESSION_CB_KEY_PEER_ID]
+
+ autils.fail_on_event(
+ dut2,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ xxx_s_id))
+
+ # Direct ranging
+ results21 = []
+ for iter in range(10):
+ id = dut2.droid.wifiRttStartRangingToAwarePeerId(
+ yyy_peer_id_on_sub)
+ event = autils.wait_for_event(
+ dut2,
+ rutils.decorate_event(rconsts.EVENT_CB_RANGING_ON_RESULT, id))
+ results21.append(
+ event["data"][rconsts.EVENT_CB_RANGING_KEY_RESULTS][0])
+
+ time.sleep(5) # while switching roles
+
+ results12 = []
+ for iter in range(10):
+ id = dut1.droid.wifiRttStartRangingToAwarePeerMac(dut2_mac)
+ event = autils.wait_for_event(
+ dut1,
+ rutils.decorate_event(rconsts.EVENT_CB_RANGING_ON_RESULT, id))
+ results12.append(
+ event["data"][rconsts.EVENT_CB_RANGING_KEY_RESULTS][0])
+
+ stats = [
+ rutils.extract_stats(results12, 0, 0, 0),
+ rutils.extract_stats(results21, 0, 0, 0)
+ ]
+
+ # Update XXX to be within range
+ dut2.droid.wifiAwareUpdateSubscribe(
+ xxx_s_id,
+ autils.add_ranging_to_sub(
+ autils.create_discovery_config("XXX",
+ aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ min_distance_mm=None,
+ max_distance_mm=1000000))
+ autils.wait_for_event(
+ dut2,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SESSION_CONFIG_UPDATED,
+ xxx_s_id))
+
+ # Expect discovery on XXX - wait until discovery with ranging:
+ # - 0 or more: without ranging info (due to concurrency limitations)
+ # - 1 or more: with ranging (once concurrency limitation relieved)
+ num_events = 0
+ while True:
+ event = autils.wait_for_event(
+ dut2,
+ autils.decorate_event(aconsts.SESSION_CB_ON_SERVICE_DISCOVERED,
+ xxx_s_id))
+ if aconsts.SESSION_CB_KEY_DISTANCE_MM in event["data"]:
+ break
+ num_events = num_events + 1
+ asserts.assert_true(
+ num_events < 10, # arbitrary safety valve
+ "Way too many discovery events without ranging!")
+
+ asserts.explicit_pass(
+ "Discovery/Direct RTT Concurrency Pass", extras={"data": stats})
diff --git a/acts_tests/tests/google/wifi/rtt/functional/RangeApMiscTest.py b/acts_tests/tests/google/wifi/rtt/functional/RangeApMiscTest.py
new file mode 100644
index 0000000..c68ca92
--- /dev/null
+++ b/acts_tests/tests/google/wifi/rtt/functional/RangeApMiscTest.py
@@ -0,0 +1,90 @@
+#!/usr/bin/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.
+
+from acts import asserts
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class RangeApMiscTest(RttBaseTest):
+ """Test class for RTT ranging to Access Points - miscellaneous tests which
+ do not fit into the strict IEEE 802.11mc supporting or non-supporting test
+ beds - e.g. a mixed test."""
+
+ # Number of RTT iterations
+ NUM_ITER = 10
+
+ # Time gap (in seconds) between iterations
+ TIME_BETWEEN_ITERATIONS = 0
+
+ #############################################################################
+
+ def test_rtt_mixed_80211mc_supporting_aps_wo_privilege(self):
+ """Scan for APs and perform RTT on one supporting and one non-supporting
+ IEEE 802.11mc APs with the device not having privilege access (expect
+ failures)."""
+ dut = self.android_devices[0]
+ rutils.config_privilege_override(dut, True)
+ rtt_aps = rutils.scan_with_rtt_support_constraint(dut, True)
+ non_rtt_aps = rutils.scan_with_rtt_support_constraint(dut, False)
+ mix_list = [rtt_aps[0], non_rtt_aps[0]]
+ dut.log.debug("Visible non-IEEE 802.11mc APs=%s", mix_list)
+ events = rutils.run_ranging(dut, mix_list, self.NUM_ITER,
+ self.TIME_BETWEEN_ITERATIONS)
+ stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
+ self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm,
+ self.lci_reference, self.lcr_reference)
+ dut.log.debug("Stats=%s", stats)
+
+ for bssid, stat in stats.items():
+ asserts.assert_true(
+ stat['num_no_results'] == 0,
+ "Missing (timed-out) results",
+ extras=stats)
+ if bssid == rtt_aps[0][wutils.WifiEnums.BSSID_KEY]:
+ asserts.assert_false(
+ stat['any_lci_mismatch'], "LCI mismatch", extras=stats)
+ asserts.assert_false(
+ stat['any_lcr_mismatch'], "LCR mismatch", extras=stats)
+ asserts.assert_equal(
+ stat['num_invalid_rssi'], 0, "Invalid RSSI", extras=stats)
+ asserts.assert_true(
+ stat['num_failures'] <=
+ self.rtt_max_failure_rate_two_sided_rtt_percentage *
+ stat['num_results'] / 100,
+ "Failure rate is too high",
+ extras=stats)
+ asserts.assert_true(
+ stat['num_range_out_of_margin'] <=
+ self.rtt_max_margin_exceeded_rate_two_sided_rtt_percentage
+ * stat['num_success_results'] / 100,
+ "Results exceeding error margin rate is too high",
+ extras=stats)
+ else:
+ asserts.assert_true(
+ stat['num_failures'] == self.NUM_ITER,
+ "All one-sided RTT requests must fail when executed without privilege",
+ extras=stats)
+ for code in stat['status_codes']:
+ asserts.assert_true(
+ code == rconsts.
+ EVENT_CB_RANGING_STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC,
+ "Expected non-support error code",
+ extras=stats)
+ asserts.explicit_pass("RTT test done", extras=stats)
diff --git a/acts_tests/tests/google/wifi/rtt/functional/RangeApNonSupporting11McTest.py b/acts_tests/tests/google/wifi/rtt/functional/RangeApNonSupporting11McTest.py
new file mode 100644
index 0000000..a40647b
--- /dev/null
+++ b/acts_tests/tests/google/wifi/rtt/functional/RangeApNonSupporting11McTest.py
@@ -0,0 +1,177 @@
+#!/usr/bin/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.
+
+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.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class RangeApNonSupporting11McTest(RttBaseTest, WifiBaseTest):
+ """Test class for RTT ranging to Access Points which do not support IEEE
+ 802.11mc
+ """
+
+ # Number of RTT iterations
+ NUM_ITER = 10
+
+ # Time gap (in seconds) between iterations
+ TIME_BETWEEN_ITERATIONS = 0
+
+ def setup_class(self):
+ super().setup_class()
+ if "AccessPoint" in self.user_params:
+ self.legacy_configure_ap_and_start()
+
+ def run_test_rtt_non_80211mc_supporting_aps(self, dut, accuracy_evaluation=False):
+ """Scan for APs and perform RTT on non-IEEE 802.11mc supporting APs
+ Args:
+ dut: test device
+ accuracy_evaluation: False - only evaluate success rate.
+ True - evaluate both success rate and accuracy
+ default is False.
+ """
+ asserts.skip_if(
+ not dut.rtt_capabilities[rconsts.CAP_RTT_ONE_SIDED_SUPPORTED],
+ "Device does not support one-sided RTT")
+
+ non_rtt_aps = rutils.select_best_scan_results(
+ rutils.scan_with_rtt_support_constraint(dut, False),
+ select_count=1)
+ dut.log.debug("Visible non-IEEE 802.11mc APs=%s", non_rtt_aps)
+ asserts.assert_true(len(non_rtt_aps) > 0, "Need at least one AP!")
+ events = rutils.run_ranging(dut, non_rtt_aps, self.NUM_ITER,
+ self.TIME_BETWEEN_ITERATIONS)
+ stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
+ self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm,
+ [], [])
+ dut.log.debug("Stats=%s", stats)
+
+ for bssid, stat in stats.items():
+ asserts.assert_true(
+ stat['num_no_results'] == 0,
+ "Missing (timed-out) results",
+ extras=stats)
+ asserts.assert_false(
+ stat['any_lci_mismatch'], "LCI mismatch", extras=stats)
+ asserts.assert_false(
+ stat['any_lcr_mismatch'], "LCR mismatch", extras=stats)
+ asserts.assert_equal(
+ stat['num_invalid_rssi'], 0, "Invalid RSSI", extras=stats)
+ asserts.assert_true(
+ stat['num_failures'] <=
+ self.rtt_max_failure_rate_one_sided_rtt_percentage *
+ stat['num_results'] / 100,
+ "Failure rate is too high",
+ extras=stats)
+ if accuracy_evaluation:
+ asserts.assert_true(
+ stat['num_range_out_of_margin'] <=
+ self.rtt_max_margin_exceeded_rate_one_sided_rtt_percentage *
+ stat['num_success_results'] / 100,
+ "Results exceeding error margin rate is too high",
+ extras=stats)
+ asserts.explicit_pass("RTT test done", extras=stats)
+
+ @test_tracker_info(uuid="cde756e9-11f3-43da-b9ae-9edf85764f82")
+ def test_rtt_non_80211mc_supporting_aps(self):
+ """Scan for APs and perform RTT on non-IEEE 802.11mc supporting APs,
+ Functionality test: Only evaluate success rate.
+ """
+ dut = self.android_devices[0]
+ self.run_test_rtt_non_80211mc_supporting_aps(dut)
+
+ @test_tracker_info(uuid="8fea37f7-0499-4b02-bd33-5ae4d697a4b7")
+ def test_rtt_non_80211mc_supporting_aps_with_accuracy_evaluation(self):
+ """Scan for APs and perform RTT on non-IEEE 802.11mc supporting APs,
+ Performance test: evaluate success rate and accuracy.
+ """
+ dut = self.android_devices[0]
+ self.run_test_rtt_non_80211mc_supporting_aps(dut, accuracy_evaluation=True)
+
+ @test_tracker_info(uuid="c9e22185-16d4-4fe6-894f-5823587b3288")
+ def test_rtt_non_80211mc_supporting_aps_wo_privilege(self):
+ """Scan for APs and perform RTT on non-IEEE 802.11mc supporting APs with the
+ device not having privilege access (expect failures).
+ """
+ dut = self.android_devices[0]
+ rutils.config_privilege_override(dut, True)
+ non_rtt_aps = rutils.select_best_scan_results(
+ rutils.scan_with_rtt_support_constraint(dut, False),
+ select_count=1)
+ dut.log.debug("Visible non-IEEE 802.11mc APs=%s", non_rtt_aps)
+ asserts.assert_true(len(non_rtt_aps) > 0, "Need at least one AP!")
+ events = rutils.run_ranging(dut, non_rtt_aps, self.NUM_ITER,
+ self.TIME_BETWEEN_ITERATIONS)
+ stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
+ self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm,
+ self.lci_reference, self.lcr_reference)
+ dut.log.debug("Stats=%s", stats)
+
+ for bssid, stat in stats.items():
+ asserts.assert_true(
+ stat['num_no_results'] == 0,
+ "Missing (timed-out) results",
+ extras=stats)
+ asserts.assert_true(
+ stat['num_failures'] == self.NUM_ITER,
+ "All one-sided RTT requests must fail when executed without privilege",
+ extras=stats)
+ for code in stat['status_codes']:
+ asserts.assert_true(
+ code == rconsts.
+ EVENT_CB_RANGING_STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC,
+ "Expected non-support error code",
+ extras=stats)
+ asserts.explicit_pass("RTT test done", extras=stats)
+
+ @test_tracker_info(uuid="e117af56-bd3f-40ae-a2fd-4175f0daa7fa")
+ def test_rtt_non_80211mc_supporting_ap_faked_as_supporting(self):
+ """Scan for APs which do not support IEEE 802.11mc, maliciously modify the
+ Responder config to indicate support and pass-through to service. Verify
+ that get an error result.
+ """
+ dut = self.android_devices[0]
+ non_rtt_aps = rutils.select_best_scan_results(
+ rutils.scan_with_rtt_support_constraint(dut, False),
+ select_count=1)
+ dut.log.debug("Visible non-IEEE 802.11mc APs=%s", non_rtt_aps)
+ asserts.assert_true(len(non_rtt_aps) > 0, "Need at least one AP!")
+ non_rtt_aps = non_rtt_aps[0:1] # pick first
+ non_rtt_aps[0][rconsts.SCAN_RESULT_KEY_RTT_RESPONDER] = True # falsify
+ dut.log.debug("Visible non-IEEE 802.11mc APs=%s", non_rtt_aps)
+ events = rutils.run_ranging(dut, non_rtt_aps, self.NUM_ITER,
+ self.TIME_BETWEEN_ITERATIONS)
+ stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
+ self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm,
+ self.lci_reference, self.lcr_reference)
+ dut.log.debug("Stats=%s", stats)
+
+ for bssid, stat in stats.items():
+ asserts.assert_true(
+ stat['num_no_results'] == 0,
+ "Missing (timed-out) results",
+ extras=stats)
+ asserts.assert_true(
+ stat['num_failures'] == self.NUM_ITER,
+ "Failures expected for falsified responder config",
+ extras=stats)
+ asserts.explicit_pass("RTT test done", extras=stats)
diff --git a/acts_tests/tests/google/wifi/rtt/functional/RangeApSupporting11McTest.py b/acts_tests/tests/google/wifi/rtt/functional/RangeApSupporting11McTest.py
new file mode 100644
index 0000000..c218898
--- /dev/null
+++ b/acts_tests/tests/google/wifi/rtt/functional/RangeApSupporting11McTest.py
@@ -0,0 +1,329 @@
+#!/usr/bin/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 queue
+import time
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class RangeApSupporting11McTest(RttBaseTest):
+ """Test class for RTT ranging to Access Points which support IEEE 802.11mc"""
+
+ # Number of RTT iterations
+ NUM_ITER = 10
+
+ # Time gap (in seconds) between iterations
+ TIME_BETWEEN_ITERATIONS = 0
+
+ # Soft AP SSID
+ SOFT_AP_SSID = "RTT_TEST_SSID"
+
+ # Soft AP Password (irrelevant)
+ SOFT_AP_PASSWORD = "ABCDEFGH"
+
+ # Time to wait before configuration changes
+ WAIT_FOR_CONFIG_CHANGES_SEC = 1
+
+ def run_test_rtt_80211mc_supporting_aps(self, dut, accuracy_evaluation=False):
+ """Scan for APs and perform RTT only to those which support 802.11mc
+ Args:
+ dut: test device
+ accuracy_evaluation: False - only evaluate success rate.
+ True - evaluate both success rate and accuracy
+ default is False.
+ """
+ rtt_supporting_aps = rutils.select_best_scan_results(
+ rutils.scan_with_rtt_support_constraint(dut, True, repeat=10),
+ select_count=2)
+ dut.log.debug("RTT Supporting APs=%s", rtt_supporting_aps)
+ events = rutils.run_ranging(dut, rtt_supporting_aps, self.NUM_ITER,
+ self.TIME_BETWEEN_ITERATIONS)
+ stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
+ self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm,
+ self.lci_reference, self.lcr_reference)
+ dut.log.debug("Stats=%s", stats)
+
+ for bssid, stat in stats.items():
+ asserts.assert_true(
+ stat['num_no_results'] == 0,
+ "Missing (timed-out) results",
+ extras=stats)
+ asserts.assert_false(
+ stat['any_lci_mismatch'], "LCI mismatch", extras=stats)
+ asserts.assert_false(
+ stat['any_lcr_mismatch'], "LCR mismatch", extras=stats)
+ asserts.assert_false(
+ stat['invalid_num_attempted'],
+ "Invalid (0) number of attempts",
+ extras=stats)
+ asserts.assert_false(
+ stat['invalid_num_successful'],
+ "Invalid (0) number of successes",
+ extras=stats)
+ asserts.assert_equal(
+ stat['num_invalid_rssi'], 0, "Invalid RSSI", extras=stats)
+ asserts.assert_true(
+ stat['num_failures'] <=
+ self.rtt_max_failure_rate_two_sided_rtt_percentage *
+ stat['num_results'] / 100,
+ "Failure rate is too high",
+ extras=stats)
+ if accuracy_evaluation:
+ asserts.assert_true(
+ stat['num_range_out_of_margin'] <=
+ self.rtt_max_margin_exceeded_rate_two_sided_rtt_percentage *
+ stat['num_success_results'] / 100,
+ "Results exceeding error margin rate is too high",
+ extras=stats)
+ asserts.explicit_pass("RTT test done", extras=stats)
+
+ @test_tracker_info(uuid="6705270f-924b-4bef-b50a-0f0a7eb9ce52")
+ def test_rtt_80211mc_supporting_aps(self):
+ """Scan for APs and perform RTT only to those which support 802.11mc,
+ Functionality test: Only evaluate success rate."""
+ dut = self.android_devices[0]
+ self.run_test_rtt_80211mc_supporting_aps(dut)
+
+ @test_tracker_info(uuid="56a8ca4c-b69d-436e-aa80-e86adb6f57d8")
+ def test_rtt_80211mc_supporting_aps_with_accuracy_evaluation(self):
+ """Scan for APs and perform RTT only to those which support 802.11mc,
+ Performance test: evaluate success rate and accuracy."""
+ dut = self.android_devices[0]
+ self.run_test_rtt_80211mc_supporting_aps(dut, accuracy_evaluation=True)
+
+ @test_tracker_info(uuid="eb3fc9f5-ae15-47f5-8468-697bb9aa9ddf")
+ def test_rtt_in_and_after_softap_mode(self):
+ """Verify behavior when a SoftAP is enabled and then disabled on the
+ device:
+
+ - SAP Enabled: depending on device characteristics RTT may succeed or
+ fail.
+ - SAP Disabled: RTT must now succeed.
+ """
+ supp_required_params = ("dbs_supported_models", )
+ self.unpack_userparams(supp_required_params)
+
+ dut = self.android_devices[0]
+
+ rtt_supporting_aps = rutils.select_best_scan_results(
+ rutils.scan_with_rtt_support_constraint(dut, True, repeat=10),
+ select_count=1)
+ dut.log.debug("RTT Supporting APs=%s", rtt_supporting_aps)
+
+ # phase 1 (pre-SAP)
+ events = rutils.run_ranging(dut, rtt_supporting_aps, self.NUM_ITER,
+ self.TIME_BETWEEN_ITERATIONS)
+ stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
+ self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm,
+ self.lci_reference, self.lcr_reference)
+ dut.log.debug("Stats Phase 1 (pre-SAP)=%s", stats)
+
+ for bssid, stat in stats.items():
+ asserts.assert_true(
+ stat['num_no_results'] == 0,
+ "Phase 1 (pre-SAP) missing (timed-out) results",
+ extras=stats)
+
+ # phase 2 (SAP)
+ wutils.start_wifi_tethering(
+ dut,
+ self.SOFT_AP_SSID,
+ self.SOFT_AP_PASSWORD,
+ band=WIFI_CONFIG_APBAND_5G,
+ hidden=False)
+ time.sleep(self.WAIT_FOR_CONFIG_CHANGES_SEC)
+
+ if dut.model not in self.dbs_supported_models:
+ rutils.wait_for_event(dut, rconsts.BROADCAST_WIFI_RTT_NOT_AVAILABLE)
+ asserts.assert_false(dut.droid.wifiIsRttAvailable(),
+ "RTT is available")
+
+ events = rutils.run_ranging(dut, rtt_supporting_aps, self.NUM_ITER,
+ self.TIME_BETWEEN_ITERATIONS)
+ stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
+ self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm,
+ self.lci_reference, self.lcr_reference)
+ dut.log.debug("Stats Phase 2 (SAP)=%s", stats)
+
+ for bssid, stat in stats.items():
+ if dut.model in self.dbs_supported_models:
+ asserts.assert_true(
+ stat['num_no_results'] == 0,
+ "Phase 2 (SAP) missing (timed-out) results",
+ extras=stats)
+ else:
+ asserts.assert_true(
+ stat['num_success_results'] == 0,
+ "Phase 2 (SAP) valid results - but unexpected in SAP!?",
+ extras=stats)
+
+ # phase 3 (post-SAP)
+
+ # enabling Wi-Fi first: on some devices this will also disable SAP
+ # (that's the scenario we're primarily testing). Additionally,
+ # explicitly disable SAP (which may be a NOP on some devices).
+ wutils.wifi_toggle_state(dut, True)
+ time.sleep(self.WAIT_FOR_CONFIG_CHANGES_SEC)
+ wutils.stop_wifi_tethering(dut)
+
+ if dut.model not in self.dbs_supported_models:
+ rutils.wait_for_event(dut, rconsts.BROADCAST_WIFI_RTT_AVAILABLE)
+ asserts.assert_true(dut.droid.wifiIsRttAvailable(),
+ "RTT is not available")
+
+ events = rutils.run_ranging(dut, rtt_supporting_aps, self.NUM_ITER,
+ self.TIME_BETWEEN_ITERATIONS)
+ stats = rutils.analyze_results(events, self.rtt_reference_distance_mm,
+ self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm,
+ self.lci_reference, self.lcr_reference)
+ dut.log.debug("Stats Phase 3 (post-SAP)=%s", stats)
+
+ for bssid, stat in stats.items():
+ asserts.assert_true(
+ stat['num_no_results'] == 0,
+ "Phase 3 (post-SAP) missing (timed-out) results",
+ extras=stats)
+
+ #########################################################################
+ #
+ # LEGACY API test code
+ #
+ #########################################################################
+
+ @test_tracker_info(uuid="18be9737-2f03-4e35-9a23-f722dea7b82d")
+ def test_legacy_rtt_80211mc_supporting_aps(self):
+ """Scan for APs and perform RTT only to those which support 802.11mc - using
+ the LEGACY API!
+ """
+ dut = self.android_devices[0]
+ rtt_supporting_aps = rutils.select_best_scan_results(
+ rutils.scan_with_rtt_support_constraint(dut, True, repeat=10),
+ select_count=2)
+ dut.log.debug("RTT Supporting APs=%s", rtt_supporting_aps)
+
+ rtt_configs = []
+ for ap in rtt_supporting_aps:
+ rtt_configs.append(self.rtt_config_from_scan_result(ap))
+ dut.log.debug("RTT configs=%s", rtt_configs)
+
+ results = []
+ num_missing = 0
+ num_failed_aborted = 0
+ for i in range(self.NUM_ITER):
+ idx = dut.droid.wifiRttStartRanging(rtt_configs)
+ event = None
+ try:
+ events = dut.ed.pop_events("WifiRttRanging%d" % idx, 30)
+ dut.log.debug("Event=%s", events)
+ for event in events:
+ if rconsts.EVENT_CB_RANGING_KEY_RESULTS in event["data"]:
+ results.append(event["data"][
+ rconsts.EVENT_CB_RANGING_KEY_RESULTS])
+ else:
+ self.log.info("RTT failed/aborted - %s", event)
+ results.append([])
+ num_failed_aborted = num_failed_aborted + 1
+ except queue.Empty:
+ self.log.debug("Waiting for RTT event timed out.")
+ results.append([])
+ num_missing = num_missing + 1
+
+ # basic error checking:
+ # 1. no missing
+ # 2. no full failed/aborted (i.e. operation not even tried)
+ # 3. overall (all BSSIDs) success rate > threshold
+ asserts.assert_equal(
+ num_missing,
+ 0,
+ "Missing results (timeout waiting for event)",
+ extras={"data": results})
+ asserts.assert_equal(
+ num_failed_aborted,
+ 0,
+ "Failed or aborted operations (not tried)",
+ extras={"data": results})
+
+ num_results = 0
+ num_errors = 0
+ for result_group in results:
+ num_results = num_results + len(result_group)
+ for result in result_group:
+ if result["status"] != 0:
+ num_errors = num_errors + 1
+
+ extras = [
+ results, {
+ "num_results": num_results,
+ "num_errors": num_errors
+ }
+ ]
+ asserts.assert_true(
+ num_errors <= self.rtt_max_failure_rate_two_sided_rtt_percentage *
+ num_results / 100,
+ "Failure rate is too high",
+ extras={"data": extras})
+ asserts.explicit_pass("RTT test done", extras={"data": extras})
+
+ def rtt_config_from_scan_result(self, scan_result):
+ """Creates an Rtt configuration based on the scan result of a network.
+ """
+ WifiEnums = wutils.WifiEnums
+ ScanResult = WifiEnums.ScanResult
+ RttParam = WifiEnums.RttParam
+ RttBW = WifiEnums.RttBW
+ RttPreamble = WifiEnums.RttPreamble
+ RttType = WifiEnums.RttType
+
+ scan_result_channel_width_to_rtt = {
+ ScanResult.CHANNEL_WIDTH_20MHZ: RttBW.BW_20_SUPPORT,
+ ScanResult.CHANNEL_WIDTH_40MHZ: RttBW.BW_40_SUPPORT,
+ ScanResult.CHANNEL_WIDTH_80MHZ: RttBW.BW_80_SUPPORT,
+ ScanResult.CHANNEL_WIDTH_160MHZ: RttBW.BW_160_SUPPORT,
+ ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ: RttBW.BW_160_SUPPORT
+ }
+ p = {}
+ freq = scan_result[RttParam.frequency]
+ p[RttParam.frequency] = freq
+ p[RttParam.BSSID] = scan_result[WifiEnums.BSSID_KEY]
+ if freq > 5000:
+ p[RttParam.preamble] = RttPreamble.PREAMBLE_VHT
+ else:
+ p[RttParam.preamble] = RttPreamble.PREAMBLE_HT
+ cf0 = scan_result[RttParam.center_freq0]
+ if cf0 > 0:
+ p[RttParam.center_freq0] = cf0
+ cf1 = scan_result[RttParam.center_freq1]
+ if cf1 > 0:
+ p[RttParam.center_freq1] = cf1
+ cw = scan_result["channelWidth"]
+ p[RttParam.channel_width] = cw
+ p[RttParam.bandwidth] = scan_result_channel_width_to_rtt[cw]
+ if scan_result["is80211McRTTResponder"]:
+ p[RttParam.request_type] = RttType.TYPE_TWO_SIDED
+ else:
+ p[RttParam.request_type] = RttType.TYPE_ONE_SIDED
+ return p
diff --git a/acts_tests/tests/google/wifi/rtt/functional/RangeAwareTest.py b/acts_tests/tests/google/wifi/rtt/functional/RangeAwareTest.py
new file mode 100644
index 0000000..a3262b1
--- /dev/null
+++ b/acts_tests/tests/google/wifi/rtt/functional/RangeAwareTest.py
@@ -0,0 +1,559 @@
+#!/usr/bin/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 queue
+import time
+
+from acts import asserts
+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.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class RangeAwareTest(AwareBaseTest, RttBaseTest):
+ """Test class for RTT ranging to Wi-Fi Aware peers"""
+ SERVICE_NAME = "GoogleTestServiceXY"
+
+ # Number of RTT iterations
+ NUM_ITER = 10
+
+ # Time gap (in seconds) between iterations
+ TIME_BETWEEN_ITERATIONS = 0
+
+ # Time gap (in seconds) when switching between Initiator and Responder
+ TIME_BETWEEN_ROLES = 4
+
+ # Min and max of the test subscribe range.
+ DISTANCE_MIN = 0
+ DISTANCE_MAX = 1000000
+
+ def setup_test(self):
+ """Manual setup here due to multiple inheritance: explicitly execute the
+ setup method from both parents.
+ """
+ AwareBaseTest.setup_test(self)
+ RttBaseTest.setup_test(self)
+
+ def teardown_test(self):
+ """Manual teardown here due to multiple inheritance: explicitly execute the
+ teardown method from both parents.
+ """
+ AwareBaseTest.teardown_test(self)
+ RttBaseTest.teardown_test(self)
+
+ #############################################################################
+
+ def run_rtt_discovery(self, init_dut, resp_mac=None, resp_peer_id=None):
+ """Perform single RTT measurement, using Aware, from the Initiator DUT to
+ a Responder. The RTT Responder can be specified using its MAC address
+ (obtained using out- of-band discovery) or its Peer ID (using Aware
+ discovery).
+
+ Args:
+ init_dut: RTT Initiator device
+ resp_mac: MAC address of the RTT Responder device
+ resp_peer_id: Peer ID of the RTT Responder device
+ """
+ asserts.assert_true(
+ resp_mac is not None or resp_peer_id is not None,
+ "One of the Responder specifications (MAC or Peer ID)"
+ " must be provided!")
+ if resp_mac is not None:
+ id = init_dut.droid.wifiRttStartRangingToAwarePeerMac(resp_mac)
+ else:
+ id = init_dut.droid.wifiRttStartRangingToAwarePeerId(resp_peer_id)
+ event_name = rutils.decorate_event(rconsts.EVENT_CB_RANGING_ON_RESULT,
+ id)
+ try:
+ event = init_dut.ed.pop_event(event_name, rutils.EVENT_TIMEOUT)
+ result = event["data"][rconsts.EVENT_CB_RANGING_KEY_RESULTS][0]
+ if resp_mac is not None:
+ rutils.validate_aware_mac_result(result, resp_mac, "DUT")
+ else:
+ rutils.validate_aware_peer_id_result(result, resp_peer_id,
+ "DUT")
+ return result
+ except queue.Empty:
+ self.log.warning("Timed-out waiting for %s", event_name)
+ return None
+
+ def run_rtt_ib_discovery_set(self, do_both_directions, iter_count,
+ time_between_iterations, time_between_roles):
+ """Perform a set of RTT measurements, using in-band (Aware) discovery.
+
+ Args:
+ do_both_directions: False - perform all measurements in one direction,
+ True - perform 2 measurements one in both directions.
+ iter_count: Number of measurements to perform.
+ time_between_iterations: Number of seconds to wait between iterations.
+ time_between_roles: Number of seconds to wait when switching between
+ Initiator and Responder roles (only matters if
+ do_both_directions=True).
+
+ Returns: a list of the events containing the RTT results (or None for a
+ failed measurement). If both directions are tested then returns a list of
+ 2 elements: one set for each direction.
+ """
+ p_dut = self.android_devices[0]
+ s_dut = self.android_devices[1]
+
+ (p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub,
+ peer_id_on_pub) = autils.create_discovery_pair(
+ p_dut,
+ s_dut,
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME, aconsts.PUBLISH_TYPE_UNSOLICITED),
+ True),
+ s_config=autils.add_ranging_to_sub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME, aconsts.SUBSCRIBE_TYPE_PASSIVE),
+ self.DISTANCE_MIN, self.DISTANCE_MAX),
+ device_startup_offset=self.device_startup_offset,
+ msg_id=self.get_next_msg_id())
+
+ resultsPS = []
+ resultsSP = []
+ for i in range(iter_count):
+ if i != 0 and time_between_iterations != 0:
+ time.sleep(time_between_iterations)
+
+ # perform RTT from pub -> sub
+ resultsPS.append(
+ self.run_rtt_discovery(p_dut, resp_peer_id=peer_id_on_pub))
+
+ if do_both_directions:
+ if time_between_roles != 0:
+ time.sleep(time_between_roles)
+
+ # perform RTT from sub -> pub
+ resultsSP.append(
+ self.run_rtt_discovery(s_dut, resp_peer_id=peer_id_on_sub))
+
+ return resultsPS if not do_both_directions else [resultsPS, resultsSP]
+
+ def run_rtt_oob_discovery_set(self, do_both_directions, iter_count,
+ time_between_iterations, time_between_roles):
+ """Perform a set of RTT measurements, using out-of-band discovery.
+
+ Args:
+ do_both_directions: False - perform all measurements in one direction,
+ True - perform 2 measurements one in both directions.
+ iter_count: Number of measurements to perform.
+ time_between_iterations: Number of seconds to wait between iterations.
+ time_between_roles: Number of seconds to wait when switching between
+ Initiator and Responder roles (only matters if
+ do_both_directions=True).
+ enable_ranging: True to enable Ranging, False to disable.
+
+ Returns: a list of the events containing the RTT results (or None for a
+ failed measurement). If both directions are tested then returns a list of
+ 2 elements: one set for each direction.
+ """
+ dut0 = self.android_devices[0]
+ dut1 = self.android_devices[1]
+
+ id0, mac0 = autils.attach_with_identity(dut0)
+ id1, mac1 = autils.attach_with_identity(dut1)
+
+ # wait for for devices to synchronize with each other - there are no other
+ # mechanisms to make sure this happens for OOB discovery (except retrying
+ # to execute the data-path request)
+ time.sleep(autils.WAIT_FOR_CLUSTER)
+
+ # start publisher(s) on the Responder(s) with ranging enabled
+ p_config = autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True)
+ dut1.droid.wifiAwarePublish(id1, p_config)
+ autils.wait_for_event(dut1, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+ if do_both_directions:
+ dut0.droid.wifiAwarePublish(id0, p_config)
+ autils.wait_for_event(dut0, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+
+ results01 = []
+ results10 = []
+ for i in range(iter_count):
+ if i != 0 and time_between_iterations != 0:
+ time.sleep(time_between_iterations)
+
+ # perform RTT from dut0 -> dut1
+ results01.append(self.run_rtt_discovery(dut0, resp_mac=mac1))
+
+ if do_both_directions:
+ if time_between_roles != 0:
+ time.sleep(time_between_roles)
+
+ # perform RTT from dut1 -> dut0
+ results10.append(self.run_rtt_discovery(dut1, resp_mac=mac0))
+
+ return results01 if not do_both_directions else [results01, results10]
+
+ def verify_results(self, results, results_reverse_direction=None, accuracy_evaluation=False):
+ """Verifies the results of the RTT experiment.
+
+ Args:
+ results: List of RTT results.
+ results_reverse_direction: List of RTT results executed in the
+ reverse direction. Optional.
+ accuracy_evaluation: False - only evaluate success rate.
+ True - evaluate both success rate and accuracy
+ default is False.
+ """
+ stats = rutils.extract_stats(results, self.rtt_reference_distance_mm,
+ self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm)
+ stats_reverse_direction = None
+ if results_reverse_direction is not None:
+ stats_reverse_direction = rutils.extract_stats(
+ results_reverse_direction, self.rtt_reference_distance_mm,
+ self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm)
+ self.log.debug("Stats: %s", stats)
+ if stats_reverse_direction is not None:
+ self.log.debug("Stats in reverse direction: %s",
+ stats_reverse_direction)
+
+ extras = stats if stats_reverse_direction is None else {
+ "forward": stats,
+ "reverse": stats_reverse_direction
+ }
+
+ asserts.assert_true(
+ stats['num_no_results'] == 0,
+ "Missing (timed-out) results",
+ extras=extras)
+ asserts.assert_false(
+ stats['any_lci_mismatch'], "LCI mismatch", extras=extras)
+ asserts.assert_false(
+ stats['any_lcr_mismatch'], "LCR mismatch", extras=extras)
+ asserts.assert_false(
+ stats['invalid_num_attempted'],
+ "Invalid (0) number of attempts",
+ extras=stats)
+ asserts.assert_false(
+ stats['invalid_num_successful'],
+ "Invalid (0) number of successes",
+ extras=stats)
+ asserts.assert_equal(
+ stats['num_invalid_rssi'], 0, "Invalid RSSI", extras=extras)
+ asserts.assert_true(
+ stats['num_failures'] <=
+ self.rtt_max_failure_rate_two_sided_rtt_percentage *
+ stats['num_results'] / 100,
+ "Failure rate is too high",
+ extras=extras)
+ if accuracy_evaluation:
+ asserts.assert_true(
+ stats['num_range_out_of_margin'] <=
+ self.rtt_max_margin_exceeded_rate_two_sided_rtt_percentage *
+ stats['num_success_results'] / 100,
+ "Results exceeding error margin rate is too high",
+ extras=extras)
+
+ if stats_reverse_direction is not None:
+ asserts.assert_true(
+ stats_reverse_direction['num_no_results'] == 0,
+ "Missing (timed-out) results",
+ extras=extras)
+ asserts.assert_false(
+ stats_reverse_direction['any_lci_mismatch'], "LCI mismatch", extras=extras)
+ asserts.assert_false(
+ stats_reverse_direction['any_lcr_mismatch'], "LCR mismatch", extras=extras)
+ asserts.assert_equal(
+ stats_reverse_direction['num_invalid_rssi'], 0, "Invalid RSSI", extras=extras)
+ asserts.assert_true(
+ stats_reverse_direction['num_failures'] <=
+ self.rtt_max_failure_rate_two_sided_rtt_percentage *
+ stats_reverse_direction['num_results'] / 100,
+ "Failure rate is too high",
+ extras=extras)
+ if accuracy_evaluation:
+ asserts.assert_true(
+ stats_reverse_direction['num_range_out_of_margin'] <=
+ self.rtt_max_margin_exceeded_rate_two_sided_rtt_percentage *
+ stats_reverse_direction['num_success_results'] / 100,
+ "Results exceeding error margin rate is too high",
+ extras=extras)
+
+ asserts.explicit_pass("RTT Aware test done", extras=extras)
+
+ def run_rtt_with_aware_session_disabled_ranging(self, disable_publish):
+ """Try to perform RTT operation when there publish or subscribe disabled ranging.
+
+ Args:
+ disable_publish: if true disable ranging on publish config, otherwise disable ranging on
+ subscribe config
+ """
+ p_dut = self.android_devices[0]
+ s_dut = self.android_devices[1]
+ pub_config = autils.create_discovery_config(
+ self.SERVICE_NAME, aconsts.PUBLISH_TYPE_UNSOLICITED)
+ sub_config = autils.create_discovery_config(
+ self.SERVICE_NAME, aconsts.SUBSCRIBE_TYPE_PASSIVE)
+ if disable_publish:
+ sub_config = autils.add_ranging_to_sub(sub_config, self.DISTANCE_MIN, self.DISTANCE_MAX)
+ else:
+ pub_config = autils.add_ranging_to_pub(pub_config, True)
+ (p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub,
+ peer_id_on_pub) = autils.create_discovery_pair(
+ p_dut,
+ s_dut,
+ p_config=pub_config,
+ s_config=sub_config,
+ device_startup_offset=self.device_startup_offset,
+ msg_id=self.get_next_msg_id())
+ for i in range(self.NUM_ITER):
+ result_sub = self.run_rtt_discovery(s_dut, resp_peer_id=peer_id_on_sub)
+ asserts.assert_equal(rconsts.EVENT_CB_RANGING_STATUS_FAIL,
+ result_sub[rconsts.EVENT_CB_RANGING_KEY_STATUS],
+ "Ranging to publisher should fail.",
+ extras={"data": result_sub})
+
+ time.sleep(self.TIME_BETWEEN_ROLES)
+
+ for i in range(self.NUM_ITER):
+ result_pub = self.run_rtt_discovery(p_dut, resp_peer_id=peer_id_on_pub)
+ asserts.assert_equal(rconsts.EVENT_CB_RANGING_STATUS_FAIL,
+ result_pub[rconsts.EVENT_CB_RANGING_KEY_STATUS],
+ "Ranging to subscriber should fail.",
+ extras={"data": result_pub})
+
+ #############################################################################
+
+ @test_tracker_info(uuid="9e4e7ab4-2254-498c-9788-21e15ed9a370")
+ def test_rtt_oob_discovery_one_way(self):
+ """Perform RTT between 2 Wi-Fi Aware devices. Use out-of-band discovery
+ to communicate the MAC addresses to the peer. Test one-direction RTT only.
+ Functionality test: Only evaluate success rate.
+ """
+ rtt_results = self.run_rtt_oob_discovery_set(
+ do_both_directions=False,
+ iter_count=self.NUM_ITER,
+ time_between_iterations=self.TIME_BETWEEN_ITERATIONS,
+ time_between_roles=self.TIME_BETWEEN_ROLES)
+ self.verify_results(rtt_results)
+
+ @test_tracker_info(uuid="22edba77-eeb2-43ee-875a-84437550ad84")
+ def test_rtt_oob_discovery_both_ways(self):
+ """Perform RTT between 2 Wi-Fi Aware devices. Use out-of-band discovery
+ to communicate the MAC addresses to the peer. Test RTT both-ways:
+ switching rapidly between Initiator and Responder.
+ Functionality test: Only evaluate success rate.
+ """
+ rtt_results1, rtt_results2 = self.run_rtt_oob_discovery_set(
+ do_both_directions=True,
+ iter_count=self.NUM_ITER,
+ time_between_iterations=self.TIME_BETWEEN_ITERATIONS,
+ time_between_roles=self.TIME_BETWEEN_ROLES)
+ self.verify_results(rtt_results1, rtt_results2)
+
+ @test_tracker_info(uuid="18cef4be-95b4-4f7d-a140-5165874e7d1c")
+ def test_rtt_ib_discovery_one_way(self):
+ """Perform RTT between 2 Wi-Fi Aware devices. Use in-band (Aware) discovery
+ to communicate the MAC addresses to the peer. Test one-direction RTT only.
+ Functionality test: Only evaluate success rate.
+ """
+ rtt_results = self.run_rtt_ib_discovery_set(
+ do_both_directions=False,
+ iter_count=self.NUM_ITER,
+ time_between_iterations=self.TIME_BETWEEN_ITERATIONS,
+ time_between_roles=self.TIME_BETWEEN_ROLES)
+ self.verify_results(rtt_results)
+
+ @test_tracker_info(uuid="c67c8e70-c417-42d9-9bca-af3a89f1ddd9")
+ def test_rtt_ib_discovery_both_ways(self):
+ """Perform RTT between 2 Wi-Fi Aware devices. Use in-band (Aware) discovery
+ to communicate the MAC addresses to the peer. Test RTT both-ways:
+ switching rapidly between Initiator and Responder.
+ Functionality test: Only evaluate success rate.
+ """
+ rtt_results1, rtt_results2 = self.run_rtt_ib_discovery_set(
+ do_both_directions=True,
+ iter_count=self.NUM_ITER,
+ time_between_iterations=self.TIME_BETWEEN_ITERATIONS,
+ time_between_roles=self.TIME_BETWEEN_ROLES)
+ self.verify_results(rtt_results1, rtt_results2)
+
+ @test_tracker_info(uuid="3a1d19a2-7241-49e0-aaf2-0a1da4c29783")
+ def test_rtt_oob_discovery_one_way_with_accuracy_evaluation(self):
+ """Perform RTT between 2 Wi-Fi Aware devices. Use out-of-band discovery
+ to communicate the MAC addresses to the peer. Test one-direction RTT only.
+ Performance test: evaluate success rate and accuracy.
+ """
+ rtt_results = self.run_rtt_oob_discovery_set(
+ do_both_directions=False,
+ iter_count=self.NUM_ITER,
+ time_between_iterations=self.TIME_BETWEEN_ITERATIONS,
+ time_between_roles=self.TIME_BETWEEN_ROLES)
+ self.verify_results(rtt_results, accuracy_evaluation=True)
+
+ @test_tracker_info(uuid="82f954a5-c0ec-4bc6-8940-3b72199328ac")
+ def test_rtt_oob_discovery_both_ways_with_accuracy_evaluation(self):
+ """Perform RTT between 2 Wi-Fi Aware devices. Use out-of-band discovery
+ to communicate the MAC addresses to the peer. Test RTT both-ways:
+ switching rapidly between Initiator and Responder.
+ Performance test: evaluate success rate and accuracy.
+ """
+ rtt_results1, rtt_results2 = self.run_rtt_oob_discovery_set(
+ do_both_directions=True,
+ iter_count=self.NUM_ITER,
+ time_between_iterations=self.TIME_BETWEEN_ITERATIONS,
+ time_between_roles=self.TIME_BETWEEN_ROLES)
+ self.verify_results(rtt_results1, rtt_results2, accuracy_evaluation=True)
+
+ @test_tracker_info(uuid="4194e00c-ea49-488e-b67f-ad9360daa5fa")
+ def test_rtt_ib_discovery_one_way_with_accuracy_evaluation(self):
+ """Perform RTT between 2 Wi-Fi Aware devices. Use in-band (Aware) discovery
+ to communicate the MAC addresses to the peer. Test one-direction RTT only.
+ Performance test: evaluate success rate and accuracy.
+ """
+ rtt_results = self.run_rtt_ib_discovery_set(
+ do_both_directions=False,
+ iter_count=self.NUM_ITER,
+ time_between_iterations=self.TIME_BETWEEN_ITERATIONS,
+ time_between_roles=self.TIME_BETWEEN_ROLES)
+ self.verify_results(rtt_results, accuracy_evaluation=True)
+
+ @test_tracker_info(uuid="bea3ac8b-756d-4cd8-8936-b8bfe64676c9")
+ def test_rtt_ib_discovery_both_ways_with_accuracy_evaluation(self):
+ """Perform RTT between 2 Wi-Fi Aware devices. Use in-band (Aware) discovery
+ to communicate the MAC addresses to the peer. Test RTT both-ways:
+ switching rapidly between Initiator and Responder.
+ Performance test: evaluate success rate and accuracy.
+ """
+ rtt_results1, rtt_results2 = self.run_rtt_ib_discovery_set(
+ do_both_directions=True,
+ iter_count=self.NUM_ITER,
+ time_between_iterations=self.TIME_BETWEEN_ITERATIONS,
+ time_between_roles=self.TIME_BETWEEN_ROLES)
+ self.verify_results(rtt_results1, rtt_results2, accuracy_evaluation=True)
+
+ @test_tracker_info(uuid="54f9693d-45e5-4979-adbb-1b875d217c0c")
+ def test_rtt_without_initiator_aware(self):
+ """Try to perform RTT operation when there is no local Aware session (on the
+ Initiator). The Responder is configured normally: Aware on and a Publisher
+ with Ranging enable. Should FAIL.
+ """
+ init_dut = self.android_devices[0]
+ resp_dut = self.android_devices[1]
+
+ # Enable a Responder and start a Publisher
+ resp_id = resp_dut.droid.wifiAwareAttach(True)
+ autils.wait_for_event(resp_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ resp_ident_event = autils.wait_for_event(
+ resp_dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+ resp_mac = resp_ident_event['data']['mac']
+
+ resp_config = autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True)
+ resp_dut.droid.wifiAwarePublish(resp_id, resp_config)
+ autils.wait_for_event(resp_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+
+ # Initiate an RTT to Responder (no Aware started on Initiator!)
+ results = []
+ num_no_responses = 0
+ num_successes = 0
+ for i in range(self.NUM_ITER):
+ result = self.run_rtt_discovery(init_dut, resp_mac=resp_mac)
+ self.log.debug("result: %s", result)
+ results.append(result)
+ if result is None:
+ num_no_responses = num_no_responses + 1
+ elif (result[rconsts.EVENT_CB_RANGING_KEY_STATUS] ==
+ rconsts.EVENT_CB_RANGING_STATUS_SUCCESS):
+ num_successes = num_successes + 1
+
+ asserts.assert_equal(
+ num_no_responses, 0, "No RTT response?", extras={"data": results})
+ asserts.assert_equal(
+ num_successes,
+ 0,
+ "Aware RTT w/o Aware should FAIL!",
+ extras={"data": results})
+ asserts.explicit_pass("RTT Aware test done", extras={"data": results})
+
+ @test_tracker_info(uuid="87a69053-8261-4928-8ec1-c93aac7f3a8d")
+ def test_rtt_without_responder_aware(self):
+ """Try to perform RTT operation when there is no peer Aware session (on the
+ Responder). Should FAIL."""
+ init_dut = self.android_devices[0]
+ resp_dut = self.android_devices[1]
+
+ # Enable a Responder and start a Publisher
+ resp_id = resp_dut.droid.wifiAwareAttach(True)
+ autils.wait_for_event(resp_dut, aconsts.EVENT_CB_ON_ATTACHED)
+ resp_ident_event = autils.wait_for_event(
+ resp_dut, aconsts.EVENT_CB_ON_IDENTITY_CHANGED)
+ resp_mac = resp_ident_event['data']['mac']
+
+ resp_config = autils.add_ranging_to_pub(
+ autils.create_discovery_config(self.SERVICE_NAME,
+ aconsts.PUBLISH_TYPE_UNSOLICITED),
+ enable_ranging=True)
+ resp_dut.droid.wifiAwarePublish(resp_id, resp_config)
+ autils.wait_for_event(resp_dut, aconsts.SESSION_CB_ON_PUBLISH_STARTED)
+
+ # Disable Responder
+ resp_dut.droid.wifiAwareDestroy(resp_id)
+
+ # Enable the Initiator
+ init_id = init_dut.droid.wifiAwareAttach()
+ autils.wait_for_event(init_dut, aconsts.EVENT_CB_ON_ATTACHED)
+
+ # Initiate an RTT to Responder (no Aware started on Initiator!)
+ results = []
+ num_no_responses = 0
+ num_successes = 0
+ for i in range(self.NUM_ITER):
+ result = self.run_rtt_discovery(init_dut, resp_mac=resp_mac)
+ self.log.debug("result: %s", result)
+ results.append(result)
+ if result is None:
+ num_no_responses = num_no_responses + 1
+ elif (result[rconsts.EVENT_CB_RANGING_KEY_STATUS] ==
+ rconsts.EVENT_CB_RANGING_STATUS_SUCCESS):
+ num_successes = num_successes + 1
+
+ asserts.assert_equal(
+ num_no_responses, 0, "No RTT response?", extras={"data": results})
+ asserts.assert_equal(
+ num_successes,
+ 0,
+ "Aware RTT w/o Aware should FAIL!",
+ extras={"data": results})
+ asserts.explicit_pass("RTT Aware test done", extras={"data": results})
+
+ @test_tracker_info(uuid="80b0f96e-f87d-4dc9-a2b9-fae48558c8d8")
+ def test_rtt_with_publish_ranging_disabled(self):
+ """
+ Try to perform RTT operation when publish ranging disabled, should fail.
+ """
+ self.run_rtt_with_aware_session_disabled_ranging(True)
+
+ @test_tracker_info(uuid="cb93a902-9b7a-4734-ba81-864878f9fa55")
+ def test_rtt_with_subscribe_ranging_disabled(self):
+ """
+ Try to perform RTT operation when subscribe ranging disabled, should fail.
+ """
+ self.run_rtt_with_aware_session_disabled_ranging(False)
diff --git a/acts_tests/tests/google/wifi/rtt/functional/RangeSoftApTest.py b/acts_tests/tests/google/wifi/rtt/functional/RangeSoftApTest.py
new file mode 100644
index 0000000..c23b5b0
--- /dev/null
+++ b/acts_tests/tests/google/wifi/rtt/functional/RangeSoftApTest.py
@@ -0,0 +1,103 @@
+#!/usr/bin/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.
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
+from acts.test_utils.wifi import wifi_test_utils as wutils
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class RangeSoftApTest(RttBaseTest):
+ """Test class for RTT ranging to an Android Soft AP."""
+
+ # Soft AP SSID
+ SOFT_AP_SSID = "RTT_TEST_SSID"
+
+ # Soft AP Password (irrelevant)
+ SOFT_AP_PASSWORD = "ABCDEFGH"
+
+ # Number of RTT iterations
+ NUM_ITER = 10
+
+ #########################################################################
+
+ @test_tracker_info(uuid="578f0725-31e3-4e60-ad62-0212d93cf5b8")
+ def test_rtt_to_soft_ap(self):
+ """Set up a Soft AP on one device and try performing an RTT ranging to it
+ from another device. The attempt must fail - RTT on Soft AP must be
+ disabled."""
+ sap = self.android_devices[0]
+ sap.pretty_name = "SoftAP"
+ client = self.android_devices[1]
+ client.pretty_name = "Client"
+
+ # start Soft AP
+ wutils.start_wifi_tethering(
+ sap,
+ self.SOFT_AP_SSID,
+ self.SOFT_AP_PASSWORD,
+ band=WIFI_CONFIG_APBAND_5G,
+ hidden=False)
+
+ try:
+ # start scanning on the client
+ wutils.start_wifi_connection_scan_and_ensure_network_found(
+ client, self.SOFT_AP_SSID)
+ scans = client.droid.wifiGetScanResults()
+ scanned_softap = None
+ for scanned_ap in scans:
+ if scanned_ap[wutils.WifiEnums.SSID_KEY] == self.SOFT_AP_SSID:
+ scanned_softap = scanned_ap
+ break
+
+ asserts.assert_false(
+ scanned_softap == None,
+ "Soft AP not found in scan!",
+ extras=scans)
+
+ # validate that Soft AP does not advertise 802.11mc support
+ asserts.assert_false(
+ rconsts.SCAN_RESULT_KEY_RTT_RESPONDER in scanned_softap
+ and scanned_softap[rconsts.SCAN_RESULT_KEY_RTT_RESPONDER],
+ "Soft AP advertises itself as supporting 802.11mc!",
+ extras=scanned_softap)
+
+ # falsify the SoftAP's support for IEEE 802.11 so we try a 2-sided RTT
+ scanned_softap[
+ rconsts.SCAN_RESULT_KEY_RTT_RESPONDER] = True # falsify
+
+ # actually try ranging to the Soft AP
+ events = rutils.run_ranging(client, [scanned_softap],
+ self.NUM_ITER, 0)
+ stats = rutils.analyze_results(
+ events, self.rtt_reference_distance_mm,
+ self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm, self.lci_reference,
+ self.lcr_reference)
+
+ asserts.assert_equal(
+ stats[scanned_ap[wutils.WifiEnums.BSSID_KEY]]['num_failures'],
+ self.NUM_ITER,
+ "Some RTT operations to Soft AP succeed!?",
+ extras=stats)
+
+ asserts.explicit_pass(
+ "SoftAP + RTT validation done", extras=events)
+ finally:
+ wutils.stop_wifi_tethering(sap)
diff --git a/acts_tests/tests/google/wifi/rtt/functional/RttDisableTest.py b/acts_tests/tests/google/wifi/rtt/functional/RttDisableTest.py
new file mode 100644
index 0000000..703efac
--- /dev/null
+++ b/acts_tests/tests/google/wifi/rtt/functional/RttDisableTest.py
@@ -0,0 +1,118 @@
+#!/usr/bin/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.
+
+from acts import asserts
+from acts import utils
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
+
+
+class RttDisableTest(WifiBaseTest, RttBaseTest):
+ """Test class for RTT ranging enable/disable flows."""
+
+ MODE_DISABLE_WIFI = 0
+ MODE_ENABLE_DOZE = 1
+ MODE_DISABLE_LOCATIONING = 2
+
+ def setup_test(self):
+ RttBaseTest.setup_test(self)
+
+ def setup_class(self):
+ 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
+ doze mode.
+
+ Args:
+ disable_mode: The particular mechanism in which RTT is disabled. One of
+ the MODE_* constants.
+ """
+ dut = self.android_devices[0]
+
+ # validate start-up conditions
+ asserts.assert_true(dut.droid.wifiIsRttAvailable(),
+ "RTT is not available")
+
+ # scan to get some APs to be used later
+ all_aps = rutils.select_best_scan_results(
+ rutils.scan_networks(dut), select_count=1)
+ asserts.assert_true(len(all_aps) > 0, "Need at least one visible AP!")
+
+ # disable RTT and validate broadcast & API
+ if disable_mode == self.MODE_DISABLE_WIFI:
+ # disabling Wi-Fi is not sufficient: since scan mode (and hence RTT) will
+ # remain enabled - we need to disable the Wi-Fi chip aka Airplane Mode
+ asserts.assert_true(
+ utils.force_airplane_mode(dut, True),
+ "Can not turn on airplane mode on: %s" % dut.serial)
+ elif disable_mode == self.MODE_ENABLE_DOZE:
+ asserts.assert_true(utils.enable_doze(dut), "Can't enable doze")
+ elif disable_mode == self.MODE_DISABLE_LOCATIONING:
+ utils.set_location_service(dut, False)
+
+ rutils.wait_for_event(dut, rconsts.BROADCAST_WIFI_RTT_NOT_AVAILABLE)
+ asserts.assert_false(dut.droid.wifiIsRttAvailable(),
+ "RTT is available")
+
+ # request a range and validate error
+ id = dut.droid.wifiRttStartRangingToAccessPoints(all_aps[0:1])
+ event = rutils.wait_for_event(
+ dut, rutils.decorate_event(rconsts.EVENT_CB_RANGING_ON_FAIL, id))
+ asserts.assert_equal(
+ event["data"][rconsts.EVENT_CB_RANGING_KEY_STATUS],
+ rconsts.RANGING_FAIL_CODE_RTT_NOT_AVAILABLE, "Invalid error code")
+
+ # enable RTT and validate broadcast & API
+ if disable_mode == self.MODE_DISABLE_WIFI:
+ asserts.assert_true(
+ utils.force_airplane_mode(dut, False),
+ "Can not turn off airplane mode on: %s" % dut.serial)
+ elif disable_mode == self.MODE_ENABLE_DOZE:
+ asserts.assert_true(utils.disable_doze(dut), "Can't disable doze")
+ elif disable_mode == self.MODE_DISABLE_LOCATIONING:
+ utils.set_location_service(dut, True)
+
+ rutils.wait_for_event(dut, rconsts.BROADCAST_WIFI_RTT_AVAILABLE)
+ asserts.assert_true(dut.droid.wifiIsRttAvailable(),
+ "RTT is not available")
+
+ ############################################################################
+
+ @test_tracker_info(uuid="498c49ab-a188-4612-998d-c47b35ff285e")
+ def test_disable_wifi(self):
+ """Validate that getting expected broadcast when Wi-Fi is disabled and that
+ any range requests are rejected."""
+ self.run_disable_rtt(self.MODE_DISABLE_WIFI)
+
+ @test_tracker_info(uuid="f71f731f-4aaf-402b-8595-db94b625b544")
+ def test_enable_doze(self):
+ """Validate that getting expected broadcast when RTT is disabled due to doze
+ mode and that any range requests are rejected."""
+ self.run_disable_rtt(self.MODE_ENABLE_DOZE)
+
+ @test_tracker_info(uuid="6a1c83a8-9eaf-49db-b547-5131cba0eafe")
+ def test_disable_location(self):
+ """Validate that getting expected broadcast when locationing is disabled and
+ that any range requests are rejected."""
+ self.run_disable_rtt(self.MODE_DISABLE_LOCATIONING)
diff --git a/acts_tests/tests/google/wifi/rtt/functional/RttRequestManagementTest.py b/acts_tests/tests/google/wifi/rtt/functional/RttRequestManagementTest.py
new file mode 100644
index 0000000..c91521d
--- /dev/null
+++ b/acts_tests/tests/google/wifi/rtt/functional/RttRequestManagementTest.py
@@ -0,0 +1,146 @@
+#!/usr/bin/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 random
+import time
+
+from acts import asserts
+from acts.test_decorators import test_tracker_info
+from acts.test_utils.wifi.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class RttRequestManagementTest(RttBaseTest):
+ """Test class for RTT request management flows."""
+
+ SPAMMING_LIMIT = 20
+
+ #############################################################################
+
+ @test_tracker_info(uuid="29ff4a02-2952-47df-bf56-64f30c963093")
+ def test_cancel_ranging(self):
+ """Request a 'large' number of range operations with various UIDs (using the
+ work-source API), then cancel some of them.
+
+ We can't guarantee a reaction time - it is possible that a cancelled test
+ was already finished and it's results dispatched back. The test therefore
+ stacks the request queue. The sequence is:
+
+ - Request:
+ - 50 tests @ UIDs = {uid1, uid2, uid3}
+ - 2 tests @ UIDs = {uid2, uid3}
+ - 1 test2 @ UIDs = {uid1, uid2, uid3}
+ - Cancel UIDs = {uid2, uid3}
+
+ Expect to receive only 51 results.
+ """
+ dut = self.android_devices[0]
+ max_peers = dut.droid.wifiRttMaxPeersInRequest()
+
+ all_uids = [1000, 20,
+ 30] # 1000 = System Server (makes requests foreground)
+ some_uids = [20, 30]
+
+ aps = rutils.select_best_scan_results(
+ rutils.scan_with_rtt_support_constraint(dut, True, repeat=10),
+ select_count=1)
+ dut.log.info("RTT Supporting APs=%s", aps)
+
+ asserts.assert_true(
+ len(aps) > 0, "Need at least one AP which supports 802.11mc!")
+ if len(aps) > max_peers:
+ aps = aps[0:max_peers]
+
+ group1_ids = []
+ group2_ids = []
+ group3_ids = []
+
+ # step 1: request <spam_limit> ranging operations on [uid1, uid2, uid3]
+ for i in range(self.SPAMMING_LIMIT):
+ group1_ids.append(
+ dut.droid.wifiRttStartRangingToAccessPoints(aps, all_uids))
+
+ # step 2: request 2 ranging operations on [uid2, uid3]
+ for i in range(2):
+ group2_ids.append(
+ dut.droid.wifiRttStartRangingToAccessPoints(aps, some_uids))
+
+ # step 3: request 1 ranging operation on [uid1, uid2, uid3]
+ for i in range(1):
+ group3_ids.append(
+ dut.droid.wifiRttStartRangingToAccessPoints(aps, all_uids))
+
+ # step 4: cancel ranging requests on [uid2, uid3]
+ dut.droid.wifiRttCancelRanging(some_uids)
+
+ # collect results
+ for i in range(len(group1_ids)):
+ rutils.wait_for_event(
+ dut,
+ rutils.decorate_event(rconsts.EVENT_CB_RANGING_ON_RESULT,
+ group1_ids[i]))
+ time.sleep(
+ rutils.EVENT_TIMEOUT) # optimize time-outs below to single one
+ for i in range(len(group2_ids)):
+ rutils.fail_on_event(
+ dut,
+ rutils.decorate_event(rconsts.EVENT_CB_RANGING_ON_RESULT,
+ group2_ids[i]), 0)
+ for i in range(len(group3_ids)):
+ rutils.wait_for_event(
+ dut,
+ rutils.decorate_event(rconsts.EVENT_CB_RANGING_ON_RESULT,
+ group3_ids[i]))
+
+ @test_tracker_info(uuid="48297480-c026-4780-8c13-476e7bea440c")
+ def test_throttling(self):
+ """Request sequential range operations using a bogus UID (which will
+ translate as a throttled process) and similarly using the ACTS/sl4a as
+ the source (a foreground/unthrottled process)."""
+ dut = self.android_devices[0]
+ max_peers = dut.droid.wifiRttMaxPeersInRequest()
+
+ # Need to use a random number since the system keeps states and so the
+ # background uid will be throttled on the next run of this script
+ fake_uid = [random.randint(10, 9999)]
+
+ aps = rutils.select_best_scan_results(
+ rutils.scan_with_rtt_support_constraint(dut, True, repeat=10),
+ select_count=1)
+ dut.log.info("RTT Supporting APs=%s", aps)
+
+ asserts.assert_true(
+ len(aps) > 0, "Need at least one AP which supports 802.11mc!")
+ if len(aps) > max_peers:
+ aps = aps[0:max_peers]
+
+ id1 = dut.droid.wifiRttStartRangingToAccessPoints(aps) # as ACTS/sl4a
+ id2 = dut.droid.wifiRttStartRangingToAccessPoints(aps, fake_uid)
+ id3 = dut.droid.wifiRttStartRangingToAccessPoints(aps, fake_uid)
+ id4 = dut.droid.wifiRttStartRangingToAccessPoints(aps) # as ACTS/sl4a
+
+ rutils.wait_for_event(
+ dut, rutils.decorate_event(rconsts.EVENT_CB_RANGING_ON_RESULT,
+ id1))
+ rutils.wait_for_event(
+ dut, rutils.decorate_event(rconsts.EVENT_CB_RANGING_ON_RESULT,
+ id2))
+ rutils.wait_for_event(
+ dut, rutils.decorate_event(rconsts.EVENT_CB_RANGING_ON_FAIL, id3))
+ rutils.wait_for_event(
+ dut, rutils.decorate_event(rconsts.EVENT_CB_RANGING_ON_RESULT,
+ id4))
diff --git a/acts_tests/tests/google/wifi/rtt/stress/StressRangeApTest.py b/acts_tests/tests/google/wifi/rtt/stress/StressRangeApTest.py
new file mode 100644
index 0000000..d0e9fe9
--- /dev/null
+++ b/acts_tests/tests/google/wifi/rtt/stress/StressRangeApTest.py
@@ -0,0 +1,83 @@
+#!/usr/bin/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.
+
+from acts import asserts
+from acts.base_test import BaseTestClass
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class StressRangeApTest(RttBaseTest):
+ """Test class for stress testing of RTT ranging to Access Points"""
+
+ #############################################################################
+
+ def test_rtt_supporting_ap_only(self):
+ """Scan for APs and perform RTT only to those which support 802.11mc.
+
+ Stress test: repeat ranging to the same AP. Verify rate of success and
+ stability of results.
+ """
+ dut = self.android_devices[0]
+ rtt_supporting_aps = rutils.scan_with_rtt_support_constraint(
+ dut, True, repeat=10)
+ dut.log.debug("RTT Supporting APs=%s", rtt_supporting_aps)
+
+ num_iter = self.stress_test_min_iteration_count
+
+ max_peers = dut.droid.wifiRttMaxPeersInRequest()
+ asserts.assert_true(
+ len(rtt_supporting_aps) > 0,
+ "Need at least one AP which supports 802.11mc!")
+ if len(rtt_supporting_aps) > max_peers:
+ rtt_supporting_aps = rtt_supporting_aps[0:max_peers]
+
+ events = rutils.run_ranging(dut, rtt_supporting_aps, num_iter, 0,
+ self.stress_test_target_run_time_sec)
+ stats = rutils.analyze_results(
+ events,
+ self.rtt_reference_distance_mm,
+ self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm,
+ self.lci_reference,
+ self.lcr_reference,
+ summary_only=True)
+ dut.log.debug("Stats=%s", stats)
+
+ for bssid, stat in stats.items():
+ asserts.assert_true(
+ stat['num_no_results'] == 0,
+ "Missing (timed-out) results",
+ extras=stats)
+ asserts.assert_false(
+ stat['any_lci_mismatch'], "LCI mismatch", extras=stats)
+ asserts.assert_false(
+ stat['any_lcr_mismatch'], "LCR mismatch", extras=stats)
+ asserts.assert_equal(
+ stat['num_invalid_rssi'], 0, "Invalid RSSI", extras=stats)
+ asserts.assert_true(
+ stat['num_failures'] <=
+ self.rtt_max_failure_rate_two_sided_rtt_percentage *
+ stat['num_results'] / 100,
+ "Failure rate is too high",
+ extras=stats)
+ asserts.assert_true(
+ stat['num_range_out_of_margin'] <=
+ self.rtt_max_margin_exceeded_rate_two_sided_rtt_percentage *
+ stat['num_success_results'] / 100,
+ "Results exceeding error margin rate is too high",
+ extras=stats)
+ asserts.explicit_pass("RTT test done", extras=stats)
diff --git a/acts_tests/tests/google/wifi/rtt/stress/StressRangeAwareTest.py b/acts_tests/tests/google/wifi/rtt/stress/StressRangeAwareTest.py
new file mode 100644
index 0000000..ccf8b9d
--- /dev/null
+++ b/acts_tests/tests/google/wifi/rtt/stress/StressRangeAwareTest.py
@@ -0,0 +1,147 @@
+#!/usr/bin/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 queue
+import time
+
+from acts import asserts
+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.rtt import rtt_const as rconsts
+from acts.test_utils.wifi.rtt import rtt_test_utils as rutils
+from acts.test_utils.wifi.rtt.RttBaseTest import RttBaseTest
+
+
+class StressRangeAwareTest(AwareBaseTest, RttBaseTest):
+ """Test class for stress testing of RTT ranging to Wi-Fi Aware peers."""
+ SERVICE_NAME = "GoogleTestServiceXY"
+
+ def setup_test(self):
+ """Manual setup here due to multiple inheritance: explicitly execute the
+ setup method from both parents."""
+ AwareBaseTest.setup_test(self)
+ RttBaseTest.setup_test(self)
+
+ def teardown_test(self):
+ """Manual teardown here due to multiple inheritance: explicitly execute the
+ teardown method from both parents."""
+ AwareBaseTest.teardown_test(self)
+ RttBaseTest.teardown_test(self)
+
+ #############################################################################
+
+ def run_rtt_discovery(self, init_dut, resp_mac=None, resp_peer_id=None):
+ """Perform single RTT measurement, using Aware, from the Initiator DUT to
+ a Responder. The RTT Responder can be specified using its MAC address
+ (obtained using out- of-band discovery) or its Peer ID (using Aware
+ discovery).
+
+ Args:
+ init_dut: RTT Initiator device
+ resp_mac: MAC address of the RTT Responder device
+ resp_peer_id: Peer ID of the RTT Responder device
+ """
+ asserts.assert_true(
+ resp_mac is not None or resp_peer_id is not None,
+ "One of the Responder specifications (MAC or Peer ID)"
+ " must be provided!")
+ if resp_mac is not None:
+ id = init_dut.droid.wifiRttStartRangingToAwarePeerMac(resp_mac)
+ else:
+ id = init_dut.droid.wifiRttStartRangingToAwarePeerId(resp_peer_id)
+ try:
+ event = init_dut.ed.pop_event(
+ rutils.decorate_event(rconsts.EVENT_CB_RANGING_ON_RESULT, id),
+ rutils.EVENT_TIMEOUT)
+ result = event["data"][rconsts.EVENT_CB_RANGING_KEY_RESULTS][0]
+ if resp_mac is not None:
+ rutils.validate_aware_mac_result(result, resp_mac, "DUT")
+ else:
+ rutils.validate_aware_peer_id_result(result, resp_peer_id,
+ "DUT")
+ return result
+ except queue.Empty:
+ return None
+
+ def test_stress_rtt_ib_discovery_set(self):
+ """Perform a set of RTT measurements, using in-band (Aware) discovery, and
+ switching Initiator and Responder roles repeatedly.
+
+ Stress test: repeat ranging operations. Verify rate of success and
+ stability of results.
+ """
+ p_dut = self.android_devices[0]
+ s_dut = self.android_devices[1]
+
+ (p_id, s_id, p_disc_id, s_disc_id, peer_id_on_sub,
+ peer_id_on_pub) = autils.create_discovery_pair(
+ p_dut,
+ s_dut,
+ p_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME, aconsts.PUBLISH_TYPE_UNSOLICITED),
+ True),
+ s_config=autils.add_ranging_to_pub(
+ autils.create_discovery_config(
+ self.SERVICE_NAME, aconsts.SUBSCRIBE_TYPE_PASSIVE), True),
+ device_startup_offset=self.device_startup_offset,
+ msg_id=self.get_next_msg_id())
+
+ results = []
+ start_clock = time.time()
+ iterations_done = 0
+ run_time = 0
+ while iterations_done < self.stress_test_min_iteration_count or (
+ self.stress_test_target_run_time_sec != 0
+ and run_time < self.stress_test_target_run_time_sec):
+ results.append(
+ self.run_rtt_discovery(p_dut, resp_peer_id=peer_id_on_pub))
+ results.append(
+ self.run_rtt_discovery(s_dut, resp_peer_id=peer_id_on_sub))
+
+ iterations_done = iterations_done + 1
+ run_time = time.time() - start_clock
+
+ stats = rutils.extract_stats(
+ results,
+ self.rtt_reference_distance_mm,
+ self.rtt_reference_distance_margin_mm,
+ self.rtt_min_expected_rssi_dbm,
+ summary_only=True)
+ self.log.debug("Stats: %s", stats)
+ asserts.assert_true(
+ stats['num_no_results'] == 0,
+ "Missing (timed-out) results",
+ extras=stats)
+ asserts.assert_false(
+ stats['any_lci_mismatch'], "LCI mismatch", extras=stats)
+ asserts.assert_false(
+ stats['any_lcr_mismatch'], "LCR mismatch", extras=stats)
+ asserts.assert_equal(
+ stats['num_invalid_rssi'], 0, "Invalid RSSI", extras=stats)
+ asserts.assert_true(
+ stats['num_failures'] <=
+ self.rtt_max_failure_rate_two_sided_rtt_percentage *
+ stats['num_results'] / 100,
+ "Failure rate is too high",
+ extras=stats)
+ asserts.assert_true(
+ stats['num_range_out_of_margin'] <=
+ self.rtt_max_margin_exceeded_rate_two_sided_rtt_percentage *
+ stats['num_success_results'] / 100,
+ "Results exceeding error margin rate is too high",
+ extras=stats)