1#!/usr/bin/env python3.4
2#
3#   Copyright 2018 - The Android Open Source Project
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an "AS IS" BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16
17import logging
18import pprint
19import time
20import re
21
22from acts import asserts
23from acts import base_test
24from acts.controllers.ap_lib import hostapd_constants
25import acts.signals as signals
26from acts.test_decorators import test_tracker_info
27from acts.controllers.iperf_server import IPerfServer
28from acts_contrib.test_utils.tel.tel_wifi_utils import WIFI_CONFIG_APBAND_2G
29from acts_contrib.test_utils.tel.tel_wifi_utils import WIFI_CONFIG_APBAND_5G
30import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
31from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
32import acts.utils as utils
33import acts_contrib.test_utils.tel.tel_test_utils as tel_utils
34
35
36WifiEnums = wutils.WifiEnums
37WLAN = "wlan0"
38# Channels to configure the AP for various test scenarios.
39WIFI_NETWORK_AP_CHANNEL_2G = 1
40WIFI_NETWORK_AP_CHANNEL_5G = 36
41WIFI_NETWORK_AP_CHANNEL_5G_DFS = 132
42
43
44class WifiStaApConcurrencyTest(WifiBaseTest):
45    """Tests for STA + AP concurrency scenarios.
46
47    Test Bed Requirement:
48    * Two Android devices (For AP)
49    * One Wi-Fi network visible to the device (for STA).
50    """
51
52    def setup_class(self):
53        super().setup_class()
54        self.dut = self.android_devices[0]
55        self.dut_client = self.android_devices[1]
56
57        # Do a simple version of init - mainly just sync the time and enable
58        # verbose logging.  This test will fail if the DUT has a sim and cell
59        # data is disabled.  We would also like to test with phones in less
60        # constrained states (or add variations where we specifically
61        # constrain).
62        utils.require_sl4a(self.android_devices)
63
64        for ad in self.android_devices:
65            wutils.wifi_test_device_init(ad)
66            utils.sync_device_time(ad)
67            # Set country code explicitly to "US".
68            wutils.set_wifi_country_code(ad, WifiEnums.CountryCode.US)
69            # Enable verbose logging on the duts.
70            ad.droid.wifiEnableVerboseLogging(1)
71
72        req_params = ["dbs_supported_models",
73                      "wifi6_models"]
74        self.unpack_userparams(req_param_names=req_params,)
75
76        self.iperf_server_port = wutils.get_iperf_server_port()
77        try:
78          self.iperf_server = IPerfServer(self.iperf_server_port)
79          self.iperf_server.start()
80          logging.info(f"IPerf server started on {self.iperf_server_port}")
81        except Exception as e:
82          raise signals.TestFailure("Failed to start iperf3 server: %s" % e)
83
84    def setup_test(self):
85        asserts.skip_if(
86            self.dut.model not in self.dbs_supported_models,
87            "Device %s does not support dual interfaces" % self.dut.model)
88
89        super().setup_test()
90        for ad in self.android_devices:
91            ad.droid.wakeLockAcquireBright()
92            ad.droid.wakeUpNow()
93        self.turn_location_off_and_scan_toggle_off()
94
95    def teardown_test(self):
96        super().teardown_test()
97        # Prevent the stop wifi tethering failure to block ap close
98        try:
99            wutils.stop_wifi_tethering(self.dut)
100        except signals.TestFailure:
101            pass
102        for ad in self.android_devices:
103            ad.droid.wakeLockRelease()
104            ad.droid.goToSleepNow()
105            wutils.reset_wifi(ad)
106        self.turn_location_on_and_scan_toggle_on()
107        wutils.wifi_toggle_state(self.dut, True)
108        self.access_points[0].close()
109        if "AccessPoint" in self.user_params:
110            try:
111                del self.user_params["reference_networks"]
112                del self.user_params["open_network"]
113            except KeyError as e:
114                self.log.warn("There is no 'reference_network' or "
115                              "'open_network' to delete")
116
117    def teardown_class(self):
118        self.iperf_server.stop()
119
120    ### Helper Functions ###
121
122    def configure_ap(self, hidden=False, channel_2g=None, channel_5g=None):
123        """Configure and bring up AP on required channel.
124
125        Args:
126            channel_2g: The channel number to use for 2GHz network.
127            channel_5g: The channel number to use for 5GHz network.
128
129        """
130        if not channel_2g:
131            channel_2g = hostapd_constants.AP_DEFAULT_CHANNEL_2G
132        if not channel_5g:
133            channel_5g = hostapd_constants.AP_DEFAULT_CHANNEL_5G
134        if "AccessPoint" in self.user_params:
135            self.legacy_configure_ap_and_start(channel_2g=channel_2g,
136                                               channel_5g=channel_5g,
137                                               hidden=hidden)
138        elif "OpenWrtAP" in self.user_params:
139            self.configure_openwrt_ap_and_start(open_network=True,
140                                                channel_2g=channel_2g,
141                                                channel_5g=channel_5g,
142                                                hidden=hidden)
143        self.open_2g = self.open_network[0]["2g"]
144        self.open_5g = self.open_network[0]["5g"]
145
146    def turn_location_on_and_scan_toggle_on(self):
147        """Turns on wifi location scans."""
148        utils.set_location_service(self.dut, True)
149        self.dut.droid.wifiScannerToggleAlwaysAvailable(True)
150        msg = "Failed to turn on location service's scan."
151        asserts.assert_true(self.dut.droid.wifiScannerIsAlwaysAvailable(), msg)
152
153    def turn_location_off_and_scan_toggle_off(self):
154        """Turns off wifi location scans."""
155        utils.set_location_service(self.dut, False)
156        self.dut.droid.wifiScannerToggleAlwaysAvailable(False)
157        msg = "Failed to turn off location service's scan."
158        asserts.assert_false(self.dut.droid.wifiScannerIsAlwaysAvailable(), msg)
159
160    def run_iperf_client(self, params):
161        """Run iperf traffic after connection.
162
163        Args:
164            params: A tuple of network info and AndroidDevice object.
165        """
166        if "iperf_server_address" in self.user_params:
167            wait_time = 5
168            network, ad = params
169            ssid = network[WifiEnums.SSID_KEY]
170            # Use local host as iperf server.
171            self.iperf_server_address = wutils.get_host_iperf_ipv4_address(ad)
172            asserts.assert_true(self.iperf_server_address, "The host has no "
173                                "available IPv4 address for iperf client to "
174                                "connect to.")
175
176            ad.log.info("Starting iperf traffic through {} to {} port:{}".
177                format(ssid, self.iperf_server_address,
178                       self.iperf_server_port))
179            time.sleep(wait_time)
180            port_arg = "-p {}".format(self.iperf_server_port)
181            success, data = ad.run_iperf_client(self.iperf_server_address,
182                                                port_arg)
183            self.log.debug(pprint.pformat(data))
184            asserts.assert_true(success, "Error occurred in iPerf traffic.")
185
186    def create_softap_config(self):
187        """Create a softap config with ssid and password."""
188        ap_ssid = "softap_" + utils.rand_ascii_str(8)
189        ap_password = utils.rand_ascii_str(8)
190        self.dut.log.info("softap setup: %s %s", ap_ssid, ap_password)
191        config = {wutils.WifiEnums.SSID_KEY: ap_ssid}
192        config[wutils.WifiEnums.PWD_KEY] = ap_password
193        return config
194
195    def start_softap_and_verify(self, band, check_connectivity=True):
196        """Test startup of softap.
197
198        1. Bring up AP mode.
199        2. Verify SoftAP active using the client device.
200
201        Args:
202            band: wifi band to start soft ap on
203            check_connectivity: If set, verify internet connectivity
204
205        Returns:
206            Softap config
207        """
208        config = self.create_softap_config()
209        wutils.start_wifi_tethering(self.dut,
210                                    config[WifiEnums.SSID_KEY],
211                                    config[WifiEnums.PWD_KEY],
212                                    band)
213        for ad in self.android_devices[1:]:
214            wutils.connect_to_wifi_network(
215                ad, config, check_connectivity=check_connectivity)
216            wutils.verify_11ax_softap(self.dut, ad, self.wifi6_models)
217        return config
218
219    def connect_to_wifi_network_and_start_softap(self, nw_params,
220                                                 softap_band,
221                                                 hidden=False):
222        """Test concurrent wifi connection and softap.
223
224        This helper method first makes a wifi connection and then starts SoftAp.
225        1. Bring up wifi.
226        2. Establish connection to a network.
227        3. Bring up softap and verify AP can be connected by a client device.
228        4. Run iperf on the wifi/softap connection to the network.
229
230        Args:
231            nw_params: Params for network STA connection.
232            softap_band: Band for the AP.
233
234        Raises:
235            TestError if there is iperf traffic connection error.
236        """
237        wutils.connect_to_wifi_network(self.dut, nw_params, hidden=hidden)
238        wutils.verify_11ax_wifi_connection(
239            self.dut, self.wifi6_models, "wifi6_ap" in self.user_params)
240        softap_config = self.start_softap_and_verify(softap_band)
241        try:
242            self.run_iperf_client((nw_params, self.dut))
243            self.run_iperf_client((softap_config, self.dut_client))
244        except:
245            raise signals.TestError("Check if the iperf Server {} is "
246                                    "up and running and the Port {} is "
247                                    "listening for requests."
248                                    .format(self.iperf_server_address,
249                                            self.iperf_server_port))
250
251        if len(self.android_devices) > 2:
252            self.log.info("Testbed has extra devices, do more validation")
253            self.verify_traffic_between_dut_clients(
254                self.dut_client, self.android_devices[2])
255
256        asserts.assert_true(self.dut.droid.wifiCheckState(),
257                            "Wifi is not reported as running")
258        asserts.assert_true(self.dut.droid.wifiIsApEnabled(),
259                            "SoftAp is not reported as running")
260
261    def start_softap_and_connect_to_wifi_network(
262            self, nw_params, softap_band,
263            num_of_scan_tries=wutils.DEFAULT_SCAN_TRIES):
264        """Test concurrent wifi connection and softap.
265
266        This helper method first starts SoftAp and then makes a wifi connection.
267        1. Bring up softap and verify AP can be connected by a client device.
268        2. Bring up wifi.
269        3. Establish connection to a network.
270        4. Run iperf on the wifi/softap connection to the network.
271        5. Verify wifi state and softap state.
272
273        Args:
274            nw_params: Params for network STA connection.
275            softap_band: Band for the AP.
276            num_of_scan_tries: Number of tries to connect to wifi network
277        """
278        softap_config = self.start_softap_and_verify(softap_band, False)
279        wutils.connect_to_wifi_network(
280            self.dut, nw_params, num_of_scan_tries=num_of_scan_tries)
281        wutils.verify_11ax_wifi_connection(
282            self.dut, self.wifi6_models, "wifi6_ap" in self.user_params)
283        self.run_iperf_client((nw_params, self.dut))
284        self.run_iperf_client((softap_config, self.dut_client))
285
286        if len(self.android_devices) > 2:
287            self.log.info("Testbed has extra devices, do more validation")
288            self.verify_traffic_between_dut_clients(
289                self.dut, self.android_devices[2])
290
291        asserts.assert_true(self.dut.droid.wifiCheckState(),
292                            "Wifi is not reported as running")
293        asserts.assert_true(self.dut.droid.wifiIsApEnabled(),
294                            "SoftAp is not reported as running")
295
296    def verify_traffic_between_dut_clients(self, ad1, ad2, num_of_tries=2):
297        """Test the clients that connect to DUT's softap can ping each other.
298
299        Args:
300            ad1: DUT 1
301            ad2: DUT 2
302            num_of_tries: the retry times of ping test.
303        """
304        ad1_ip = ad1.droid.connectivityGetIPv4Addresses(WLAN)[0]
305        ad2_ip = ad2.droid.connectivityGetIPv4Addresses(WLAN)[0]
306        # Ping each other
307        for _ in range(num_of_tries):
308            if utils.adb_shell_ping(ad1, count=10, dest_ip=ad2_ip, timeout=20):
309                break
310        else:
311            asserts.fail("%s ping %s failed" % (ad1.serial, ad2_ip))
312        for _ in range(num_of_tries):
313            if utils.adb_shell_ping(ad2, count=10, dest_ip=ad1_ip, timeout=20):
314                break
315        else:
316            asserts.fail("%s ping %s failed" % (ad2.serial, ad1_ip))
317
318    def softap_change_band(self, ad):
319        """
320        Switch DUT SoftAp to 5G band if currently in 2G.
321        Switch DUT SoftAp to 2G band if currently in 5G.
322        """
323        wlan1_freq = int(self.get_wlan1_status(self.dut)['freq'])
324        if wlan1_freq in wutils.WifiEnums.ALL_5G_FREQUENCIES:
325            band = WIFI_CONFIG_APBAND_2G
326        elif wlan1_freq in wutils.WifiEnums.ALL_2G_FREQUENCIES:
327            band = WIFI_CONFIG_APBAND_5G
328        wutils.stop_wifi_tethering(ad)
329        self.start_softap_and_verify(band)
330
331    def get_wlan1_status(self, ad):
332        """ get wlan1 interface status"""
333        get_wlan1 = 'hostapd_cli status'
334        out_wlan1 = ad.adb.shell(get_wlan1)
335        out_wlan1 = dict(re.findall(r'(\S+)=(".*?"|\S+)', out_wlan1))
336        return out_wlan1
337
338    def enable_mobile_data(self, ad):
339        """Make sure that cell data is enabled if there is a sim present."""
340        init_sim_state = tel_utils.is_sim_ready(self.log, ad)
341        if init_sim_state:
342            if not ad.droid.telephonyIsDataEnabled():
343                ad.droid.telephonyToggleDataConnection(True)
344            asserts.assert_true(ad.droid.telephonyIsDataEnabled(),
345                                "Failed to enable Mobile Data")
346        else:
347            raise signals.TestSkip("Please insert sim card with "
348                                   "Mobile Data enabled before test")
349
350    ### Tests ###
351
352    @test_tracker_info(uuid="c396e7ac-cf22-4736-a623-aa6d3c50193a")
353    def test_wifi_connection_2G_softap_2G(self):
354        """Test connection to 2G network followed by SoftAp on 2G."""
355        self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
356        self.connect_to_wifi_network_and_start_softap(
357            self.open_2g, WIFI_CONFIG_APBAND_2G)
358
359    @test_tracker_info(uuid="1cd6120d-3db4-4624-9bae-55c976533a48")
360    def test_wifi_connection_5G_softap_5G(self):
361        """Test connection to 5G network followed by SoftAp on 5G."""
362        self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
363        self.connect_to_wifi_network_and_start_softap(
364            self.open_5g, WIFI_CONFIG_APBAND_5G)
365
366    @test_tracker_info(uuid="5f980007-3490-413e-b94e-7700ffab8534")
367    def test_wifi_connection_5G_DFS_softap_5G(self):
368        """Test connection to 5G DFS network followed by SoftAp on 5G."""
369        self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
370        self.connect_to_wifi_network_and_start_softap(
371            self.open_5g, WIFI_CONFIG_APBAND_5G)
372
373    @test_tracker_info(uuid="d05d5d44-c738-4372-9f01-ce2a640a2f25")
374    def test_wifi_connection_5G_softap_2G(self):
375        """Test connection to 5G network followed by SoftAp on 2G."""
376        self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
377        self.connect_to_wifi_network_and_start_softap(
378            self.open_5g, WIFI_CONFIG_APBAND_2G)
379
380    @test_tracker_info(uuid="909ac713-1ad3-4dad-9be3-ad60f00ed25e")
381    def test_wifi_connection_5G_DFS_softap_2G(self):
382        """Test connection to 5G DFS network followed by SoftAp on 2G."""
383        self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
384        self.connect_to_wifi_network_and_start_softap(
385            self.open_5g, WIFI_CONFIG_APBAND_2G)
386
387    @test_tracker_info(uuid="e8de724a-25d3-4801-94cc-22e9e0ecc8d1")
388    def test_wifi_connection_2G_softap_5G(self):
389        """Test connection to 2G network followed by SoftAp on 5G."""
390        self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
391        self.connect_to_wifi_network_and_start_softap(
392            self.open_2g, WIFI_CONFIG_APBAND_5G)
393
394    @test_tracker_info(uuid="647f4e17-5c7a-4249-98af-f791d163a39f")
395    def test_wifi_connection_5G_softap_2G_with_location_scan_on(self):
396        """Test connection to 5G network, SoftAp on 2G with location scan on."""
397        self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
398        self.turn_location_on_and_scan_toggle_on()
399        self.connect_to_wifi_network_and_start_softap(
400            self.open_5g, WIFI_CONFIG_APBAND_2G)
401        # Now toggle wifi off & ensure we can still scan.
402        wutils.wifi_toggle_state(self.dut, False)
403        wutils.start_wifi_connection_scan_and_ensure_network_found(
404            self.dut, self.open_5g[WifiEnums.SSID_KEY])
405
406    @test_tracker_info(uuid="4aa56c11-e5bc-480b-bd61-4b4ee577a5da")
407    def test_softap_2G_wifi_connection_2G(self):
408        """Test SoftAp on 2G followed by connection to 2G network."""
409        self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
410        self.start_softap_and_connect_to_wifi_network(
411            self.open_2g, WIFI_CONFIG_APBAND_2G)
412
413    @test_tracker_info(uuid="5f954957-ad20-4de1-b20c-6c97d0463bdd")
414    def test_softap_5G_wifi_connection_5G(self):
415        """Test SoftAp on 5G followed by connection to 5G network."""
416        self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
417        self.start_softap_and_connect_to_wifi_network(
418            self.open_5g, WIFI_CONFIG_APBAND_5G)
419
420    @test_tracker_info(uuid="1306aafc-a07e-4654-ba78-674f90cf748e")
421    def test_softap_5G_wifi_connection_5G_DFS(self):
422        """Test SoftAp on 5G followed by connection to 5G DFS network."""
423        self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
424        # Set scan tries to 10 to fit the 32ms limitation.
425        # SoftAp uses CTS2SELF frame to go offchannel for scan, and max duration
426        # we can set in CTS2SELF frame is 32ms.
427        # Since DUT SAP is enabled and clients are connect to the SAP, firmware
428        # is allocating only 28ms for passive scan in DFS channel for offchannel
429        # scan operation. We need to increase scan tries to get beacons from AP.
430        self.start_softap_and_connect_to_wifi_network(
431            self.open_5g, WIFI_CONFIG_APBAND_5G, num_of_scan_tries=10)
432
433    @test_tracker_info(uuid="5e28e8b5-3faa-4cff-a782-13a796d7f572")
434    def test_softap_5G_wifi_connection_2G(self):
435        """Test SoftAp on 5G followed by connection to 2G network."""
436        self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
437        self.start_softap_and_connect_to_wifi_network(
438            self.open_2g, WIFI_CONFIG_APBAND_5G)
439
440    @test_tracker_info(uuid="a2c62bc6-9ccd-4bc4-8a23-9a1b5d0b4b5c")
441    def test_softap_2G_wifi_connection_5G(self):
442        """Test SoftAp on 2G followed by connection to 5G network."""
443        self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
444        self.start_softap_and_connect_to_wifi_network(
445            self.open_5g, WIFI_CONFIG_APBAND_2G)
446
447    @test_tracker_info(uuid="75400685-a9d9-4091-8af3-97bd539c246a")
448    def test_softap_2G_wifi_connection_5G_DFS(self):
449        """Test SoftAp on 2G followed by connection to 5G DFS network."""
450        self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
451        self.start_softap_and_connect_to_wifi_network(
452            self.open_5g, WIFI_CONFIG_APBAND_2G)
453
454    @test_tracker_info(uuid="aa23a3fc-31a1-4d5c-8cf5-2eb9fdf9e7ce")
455    def test_softap_5G_wifi_connection_2G_with_location_scan_on(self):
456        """Test SoftAp on 5G, connection to 2G network with location scan on."""
457        self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
458        self.turn_location_on_and_scan_toggle_on()
459        self.start_softap_and_connect_to_wifi_network(
460            self.open_2g, WIFI_CONFIG_APBAND_5G)
461        # Now toggle wifi off & ensure we can still scan.
462        wutils.wifi_toggle_state(self.dut, False)
463        wutils.start_wifi_connection_scan_and_ensure_network_found(
464            self.dut, self.open_2g[WifiEnums.SSID_KEY])
465
466    @test_tracker_info(uuid="9decb951-4500-4476-8161-f4054760f709")
467    def test_wifi_connection_2G_softap_2G_to_softap_5g(self):
468        """Test connection to 2G network followed by SoftAp on 2G,
469        and switch SoftAp to 5G."""
470        self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
471        self.connect_to_wifi_network_and_start_softap(
472            self.open_2g, WIFI_CONFIG_APBAND_2G)
473        self.softap_change_band(self.dut)
474
475    @test_tracker_info(uuid="e17e0fb8-2c1d-4f3c-af2a-7374485f210c")
476    def test_wifi_connection_5G_softap_2G_to_softap_5g(self):
477        """Test connection to 5G network followed by SoftAp on 2G,
478        and switch SoftAp to 5G."""
479        self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
480        self.connect_to_wifi_network_and_start_softap(
481            self.open_2g, WIFI_CONFIG_APBAND_2G)
482        self.softap_change_band(self.dut)
483
484    @test_tracker_info(uuid="d549a18e-73d9-45e7-b4df-b59446c4b833")
485    def test_wifi_connection_hidden_2g_softap_2G_to_softap_5g(self):
486        """Test connection to a hidden 2G network on Channel 1 and
487        followed by SoftAp on 2G, and switch SoftAp to 5G.
488        1. Connect to a hidden 2.4G Wi-Fi AP(channel 1)
489        2. DUT turn on 2.4G hotspot and client connect to DUT
490        3. Change AP Band of DUT Hotspot from 2.4GHz to 5GHz
491        Expected results: Both DUT and Hotspot client can access the Internet
492        """
493        self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G, hidden=True)
494        self.connect_to_wifi_network_and_start_softap(
495            self.open_2g,
496            WIFI_CONFIG_APBAND_2G,
497            hidden=True)
498        self.softap_change_band(self.dut)
499