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 pprint
18import time
19
20from acts import asserts
21from acts import base_test
22from acts.controllers.ap_lib import hostapd_constants
23import acts.signals as signals
24from acts.test_decorators import test_tracker_info
25from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_2G
26from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
27import acts.test_utils.wifi.wifi_test_utils as wutils
28from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
29import acts.utils as utils
30
31WifiEnums = wutils.WifiEnums
32WLAN = "wlan0"
33# Channels to configure the AP for various test scenarios.
34WIFI_NETWORK_AP_CHANNEL_2G = 1
35WIFI_NETWORK_AP_CHANNEL_5G = 36
36WIFI_NETWORK_AP_CHANNEL_5G_DFS = 132
37
38
39class WifiStaApConcurrencyTest(WifiBaseTest):
40    """Tests for STA + AP concurrency scenarios.
41
42    Test Bed Requirement:
43    * Two Android devices (For AP)
44    * One Wi-Fi network visible to the device (for STA).
45    """
46
47    def setup_class(self):
48        super().setup_class()
49
50        self.dut = self.android_devices[0]
51        self.dut_client = self.android_devices[1]
52
53        # Do a simple version of init - mainly just sync the time and enable
54        # verbose logging.  This test will fail if the DUT has a sim and cell
55        # data is disabled.  We would also like to test with phones in less
56        # constrained states (or add variations where we specifically
57        # constrain).
58        utils.require_sl4a(self.android_devices)
59
60        for ad in self.android_devices:
61            wutils.wifi_test_device_init(ad)
62            utils.sync_device_time(ad)
63            # Set country code explicitly to "US".
64            wutils.set_wifi_country_code(ad, WifiEnums.CountryCode.US)
65            # Enable verbose logging on the duts.
66            ad.droid.wifiEnableVerboseLogging(1)
67
68        req_params = ["dbs_supported_models",
69                      "iperf_server_address",
70                      "iperf_server_port"]
71        self.unpack_userparams(req_param_names=req_params,)
72        asserts.abort_class_if(
73            self.dut.model not in self.dbs_supported_models,
74            "Device %s does not support dual interfaces." % self.dut.model)
75
76    def setup_test(self):
77        for ad in self.android_devices:
78            ad.droid.wakeLockAcquireBright()
79            ad.droid.wakeUpNow()
80        self.turn_location_off_and_scan_toggle_off()
81
82    def teardown_test(self):
83        # Prevent the stop wifi tethering failure to block ap close
84        try:
85            wutils.stop_wifi_tethering(self.dut)
86        except signals.TestFailure:
87            pass
88        for ad in self.android_devices:
89            ad.droid.wakeLockRelease()
90            ad.droid.goToSleepNow()
91            wutils.reset_wifi(ad)
92        self.turn_location_on_and_scan_toggle_on()
93        wutils.wifi_toggle_state(self.dut, True)
94        self.access_points[0].close()
95        del self.user_params["reference_networks"]
96        del self.user_params["open_network"]
97
98    def on_fail(self, test_name, begin_time):
99        for ad in self.android_devices:
100            ad.take_bug_report(test_name, begin_time)
101            ad.cat_adb_log(test_name, begin_time)
102
103    ### Helper Functions ###
104
105    def configure_ap(self, channel_2g=None, channel_5g=None):
106        """Configure and bring up AP on required channel.
107
108        Args:
109            channel_2g: The channel number to use for 2GHz network.
110            channel_5g: The channel number to use for 5GHz network.
111
112        """
113        if not channel_2g:
114            channel_2g = hostapd_constants.AP_DEFAULT_CHANNEL_2G
115        if not channel_5g:
116            channel_5g = hostapd_constants.AP_DEFAULT_CHANNEL_5G
117        self.legacy_configure_ap_and_start(channel_2g=channel_2g,
118                                           channel_5g=channel_5g)
119        self.open_2g = self.open_network[0]["2g"]
120        self.open_5g = self.open_network[0]["5g"]
121
122    def turn_location_on_and_scan_toggle_on(self):
123        """Turns on wifi location scans."""
124        utils.set_location_service(self.dut, True)
125        self.dut.droid.wifiScannerToggleAlwaysAvailable(True)
126        msg = "Failed to turn on location service's scan."
127        asserts.assert_true(self.dut.droid.wifiScannerIsAlwaysAvailable(), msg)
128
129    def turn_location_off_and_scan_toggle_off(self):
130        """Turns off wifi location scans."""
131        utils.set_location_service(self.dut, False)
132        self.dut.droid.wifiScannerToggleAlwaysAvailable(False)
133        msg = "Failed to turn off location service's scan."
134        asserts.assert_false(self.dut.droid.wifiScannerIsAlwaysAvailable(), msg)
135
136    def run_iperf_client(self, params):
137        """Run iperf traffic after connection.
138
139        Args:
140            params: A tuple of network info and AndroidDevice object.
141        """
142        if "iperf_server_address" in self.user_params:
143            wait_time = 5
144            network, ad = params
145            ssid = network[WifiEnums.SSID_KEY]
146            self.log.info("Starting iperf traffic through {}".format(ssid))
147            time.sleep(wait_time)
148            port_arg = "-p {}".format(self.iperf_server_port)
149            success, data = ad.run_iperf_client(self.iperf_server_address,
150                                                port_arg)
151            self.log.debug(pprint.pformat(data))
152            asserts.assert_true(success, "Error occurred in iPerf traffic.")
153
154    def create_softap_config(self):
155        """Create a softap config with ssid and password."""
156        ap_ssid = "softap_" + utils.rand_ascii_str(8)
157        ap_password = utils.rand_ascii_str(8)
158        self.dut.log.info("softap setup: %s %s", ap_ssid, ap_password)
159        config = {wutils.WifiEnums.SSID_KEY: ap_ssid}
160        config[wutils.WifiEnums.PWD_KEY] = ap_password
161        return config
162
163    def start_softap_and_verify(self, band, check_connectivity=True):
164        """Test startup of softap.
165
166        1. Bring up AP mode.
167        2. Verify SoftAP active using the client device.
168
169        Args:
170            band: wifi band to start soft ap on
171            check_connectivity: If set, verify internet connectivity
172
173        Returns:
174            Softap config
175        """
176        config = self.create_softap_config()
177        wutils.start_wifi_tethering(self.dut,
178                                    config[WifiEnums.SSID_KEY],
179                                    config[WifiEnums.PWD_KEY],
180                                    band)
181        for ad in self.android_devices[1:]:
182            wutils.connect_to_wifi_network(
183                ad, config, check_connectivity=check_connectivity)
184        return config
185
186    def connect_to_wifi_network_and_start_softap(self, nw_params, softap_band):
187        """Test concurrent wifi connection and softap.
188
189        This helper method first makes a wifi connection and then starts SoftAp.
190        1. Bring up wifi.
191        2. Establish connection to a network.
192        3. Bring up softap and verify AP can be connected by a client device.
193        4. Run iperf on the wifi/softap connection to the network.
194
195        Args:
196            nw_params: Params for network STA connection.
197            softap_band: Band for the AP.
198        """
199        wutils.connect_to_wifi_network(self.dut, nw_params)
200        softap_config = self.start_softap_and_verify(softap_band)
201        self.run_iperf_client((nw_params, self.dut))
202        self.run_iperf_client((softap_config, self.dut_client))
203
204        if len(self.android_devices) > 2:
205            self.log.info("Testbed has extra devices, do more validation")
206            self.verify_traffic_between_dut_clients(
207                self.dut_client, self.android_devices[2])
208
209        asserts.assert_true(self.dut.droid.wifiCheckState(),
210                            "Wifi is not reported as running")
211        asserts.assert_true(self.dut.droid.wifiIsApEnabled(),
212                            "SoftAp is not reported as running")
213
214    def start_softap_and_connect_to_wifi_network(self, nw_params, softap_band):
215        """Test concurrent wifi connection and softap.
216
217        This helper method first starts SoftAp and then makes a wifi connection.
218        1. Bring up softap and verify AP can be connected by a client device.
219        2. Bring up wifi.
220        3. Establish connection to a network.
221        4. Run iperf on the wifi/softap connection to the network.
222        5. Verify wifi state and softap state.
223
224        Args:
225            nw_params: Params for network STA connection.
226            softap_band: Band for the AP.
227        """
228        softap_config = self.start_softap_and_verify(softap_band, False)
229        wutils.connect_to_wifi_network(self.dut, nw_params)
230        self.run_iperf_client((nw_params, self.dut))
231        self.run_iperf_client((softap_config, self.dut_client))
232
233        if len(self.android_devices) > 2:
234            self.log.info("Testbed has extra devices, do more validation")
235            self.verify_traffic_between_dut_clients(
236                self.dut, self.android_devices[2])
237
238        asserts.assert_true(self.dut.droid.wifiCheckState(),
239                            "Wifi is not reported as running")
240        asserts.assert_true(self.dut.droid.wifiIsApEnabled(),
241                            "SoftAp is not reported as running")
242
243    def verify_traffic_between_dut_clients(self, ad1, ad2, num_of_tries=2):
244        """Test the clients that connect to DUT's softap can ping each other.
245
246        Args:
247            ad1: DUT 1
248            ad2: DUT 2
249            num_of_tries: the retry times of ping test.
250        """
251        ad1_ip = ad1.droid.connectivityGetIPv4Addresses(WLAN)[0]
252        ad2_ip = ad2.droid.connectivityGetIPv4Addresses(WLAN)[0]
253        # Ping each other
254        for _ in range(num_of_tries):
255            if utils.adb_shell_ping(ad1, count=10, dest_ip=ad2_ip, timeout=20):
256                break
257        else:
258            asserts.fail("%s ping %s failed" % (ad1.serial, ad2_ip))
259        for _ in range(num_of_tries):
260            if utils.adb_shell_ping(ad2, count=10, dest_ip=ad1_ip, timeout=20):
261                break
262        else:
263            asserts.fail("%s ping %s failed" % (ad2.serial, ad1_ip))
264
265    ### Tests ###
266
267    @test_tracker_info(uuid="c396e7ac-cf22-4736-a623-aa6d3c50193a")
268    def test_wifi_connection_2G_softap_2G(self):
269        """Test connection to 2G network followed by SoftAp on 2G."""
270        self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
271        self.connect_to_wifi_network_and_start_softap(
272            self.open_2g, WIFI_CONFIG_APBAND_2G)
273
274    @test_tracker_info(uuid="1cd6120d-3db4-4624-9bae-55c976533a48")
275    def test_wifi_connection_5G_softap_5G(self):
276        """Test connection to 5G network followed by SoftAp on 5G."""
277        self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
278        self.connect_to_wifi_network_and_start_softap(
279            self.open_5g, WIFI_CONFIG_APBAND_5G)
280
281    @test_tracker_info(uuid="5f980007-3490-413e-b94e-7700ffab8534")
282    def test_wifi_connection_5G_DFS_softap_5G(self):
283        """Test connection to 5G DFS network followed by SoftAp on 5G."""
284        self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
285        self.connect_to_wifi_network_and_start_softap(
286            self.open_5g, WIFI_CONFIG_APBAND_5G)
287
288    @test_tracker_info(uuid="d05d5d44-c738-4372-9f01-ce2a640a2f25")
289    def test_wifi_connection_5G_softap_2G(self):
290        """Test connection to 5G network followed by SoftAp on 2G."""
291        self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
292        self.connect_to_wifi_network_and_start_softap(
293            self.open_5g, WIFI_CONFIG_APBAND_2G)
294
295    @test_tracker_info(uuid="909ac713-1ad3-4dad-9be3-ad60f00ed25e")
296    def test_wifi_connection_5G_DFS_softap_2G(self):
297        """Test connection to 5G DFS network followed by SoftAp on 2G."""
298        self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
299        self.connect_to_wifi_network_and_start_softap(
300            self.open_5g, WIFI_CONFIG_APBAND_2G)
301
302    @test_tracker_info(uuid="e8de724a-25d3-4801-94cc-22e9e0ecc8d1")
303    def test_wifi_connection_2G_softap_5G(self):
304        """Test connection to 2G network followed by SoftAp on 5G."""
305        self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
306        self.connect_to_wifi_network_and_start_softap(
307            self.open_2g, WIFI_CONFIG_APBAND_5G)
308
309    @test_tracker_info(uuid="647f4e17-5c7a-4249-98af-f791d163a39f")
310    def test_wifi_connection_5G_softap_2G_with_location_scan_on(self):
311        """Test connection to 5G network, SoftAp on 2G with location scan on."""
312        self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
313        self.turn_location_on_and_scan_toggle_on()
314        self.connect_to_wifi_network_and_start_softap(
315            self.open_5g, WIFI_CONFIG_APBAND_2G)
316        # Now toggle wifi off & ensure we can still scan.
317        wutils.wifi_toggle_state(self.dut, False)
318        wutils.start_wifi_connection_scan_and_ensure_network_found(
319            self.dut, self.open_5g[WifiEnums.SSID_KEY])
320
321    @test_tracker_info(uuid="4aa56c11-e5bc-480b-bd61-4b4ee577a5da")
322    def test_softap_2G_wifi_connection_2G(self):
323        """Test SoftAp on 2G followed by connection to 2G network."""
324        self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
325        self.start_softap_and_connect_to_wifi_network(
326            self.open_2g, WIFI_CONFIG_APBAND_2G)
327
328    @test_tracker_info(uuid="5f954957-ad20-4de1-b20c-6c97d0463bdd")
329    def test_softap_5G_wifi_connection_5G(self):
330        """Test SoftAp on 5G followed by connection to 5G network."""
331        self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
332        self.start_softap_and_connect_to_wifi_network(
333            self.open_5g, WIFI_CONFIG_APBAND_5G)
334
335    @test_tracker_info(uuid="1306aafc-a07e-4654-ba78-674f90cf748e")
336    def test_softap_5G_wifi_connection_5G_DFS(self):
337        """Test SoftAp on 5G followed by connection to 5G DFS network."""
338        self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
339        self.start_softap_and_connect_to_wifi_network(
340            self.open_5g, WIFI_CONFIG_APBAND_5G)
341
342    @test_tracker_info(uuid="5e28e8b5-3faa-4cff-a782-13a796d7f572")
343    def test_softap_5G_wifi_connection_2G(self):
344        """Test SoftAp on 5G followed by connection to 2G network."""
345        self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
346        self.start_softap_and_connect_to_wifi_network(
347            self.open_2g, WIFI_CONFIG_APBAND_5G)
348
349    @test_tracker_info(uuid="a2c62bc6-9ccd-4bc4-8a23-9a1b5d0b4b5c")
350    def test_softap_2G_wifi_connection_5G(self):
351        """Test SoftAp on 2G followed by connection to 5G network."""
352        self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G)
353        self.start_softap_and_connect_to_wifi_network(
354            self.open_5g, WIFI_CONFIG_APBAND_2G)
355
356    @test_tracker_info(uuid="75400685-a9d9-4091-8af3-97bd539c246a")
357    def test_softap_2G_wifi_connection_5G_DFS(self):
358        """Test SoftAp on 2G followed by connection to 5G DFS network."""
359        self.configure_ap(channel_5g=WIFI_NETWORK_AP_CHANNEL_5G_DFS)
360        self.start_softap_and_connect_to_wifi_network(
361            self.open_5g, WIFI_CONFIG_APBAND_2G)
362
363    @test_tracker_info(uuid="aa23a3fc-31a1-4d5c-8cf5-2eb9fdf9e7ce")
364    def test_softap_5G_wifi_connection_2G_with_location_scan_on(self):
365        """Test SoftAp on 5G, connection to 2G network with location scan on."""
366        self.configure_ap(channel_2g=WIFI_NETWORK_AP_CHANNEL_2G)
367        self.turn_location_on_and_scan_toggle_on()
368        self.start_softap_and_connect_to_wifi_network(
369            self.open_2g, WIFI_CONFIG_APBAND_5G)
370        # Now toggle wifi off & ensure we can still scan.
371        wutils.wifi_toggle_state(self.dut, False)
372        wutils.start_wifi_connection_scan_and_ensure_network_found(
373            self.dut, self.open_2g[WifiEnums.SSID_KEY])
374