1#!/usr/bin/env python3.4
2#
3#   Copyright 2016 - 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 time
18import json
19import pprint
20
21from acts import asserts
22from acts import utils
23from acts.keys import Config
24from acts.test_decorators import test_tracker_info
25from acts_contrib.test_utils.wifi import wifi_constants
26from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
27from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
28from acts_contrib.test_utils.wifi.wifi_constants import\
29    COEX_BAND, COEX_CHANNEL, COEX_POWER_CAP_DBM, KEY_COEX_UNSAFE_CHANNELS, KEY_COEX_RESTRICTIONS
30
31WifiEnums = wutils.WifiEnums
32WIFI_CONFIG_APBAND_2G = WifiEnums.WIFI_CONFIG_APBAND_2G
33WIFI_CONFIG_APBAND_5G = WifiEnums.WIFI_CONFIG_APBAND_5G
34WIFI_CONFIG_APBAND_AUTO = WifiEnums.WIFI_CONFIG_APBAND_AUTO
35WPA3_SAE_TRANSITION_SOFTAP = WifiEnums.SoftApSecurityType.WPA3_SAE_TRANSITION
36WPA3_SAE_SOFTAP = WifiEnums.SoftApSecurityType.WPA3_SAE
37WAIT_AFTER_REBOOT = 10
38
39BRIDGED_AP_LAUNCH_INTERVAL_5_SECONDS = 5
40
41
42class WifiCellCoexChannelAvoidTest(WifiBaseTest):
43    def __init__(self, configs):
44        super().__init__(configs)
45        self.generate_test_list()
46
47    def generate_test_list(self):
48        """Generates a test list sorted with coex_unsafe_list_sap list.
49            Test with a sorted list can reduce lots of time
50            on switch radio and band start up.
51        """
52        sorted_list = sorted(self.user_params["coex_unsafe_list_sap"],
53                             key=lambda radio: radio["band"])
54        for test_item in sorted_list:
55            self.init_test_case(self.coex_unsafechannel_avoidance, test_item)
56        pprint.pprint("self.tests = {}".format(self.tests))
57
58    def init_test_case(self, coex_unsafechannel_avoidance, test_item):
59        """Generates a single test case from the given data.
60
61        Args:
62            coex_unsafechannel_avoidance: The base test case function to run.
63            test_item: test case required info include ["uuid","coex_unsafe_case"]
64        """
65        test_name = test_item["coex_unsafe_case"]
66        test_tracker_uuid = test_item["uuid"]
67        if not test_name.startswith("test_"):
68            test_name = "test_{}".format(test_name)
69        test_case = test_tracker_info(uuid=test_tracker_uuid)(
70            lambda: coex_unsafechannel_avoidance(test_item))
71        setattr(self, test_name, test_case)
72        self.tests.append(test_name)
73
74    def setup_class(self):
75        """It will setup the required dependencies from config file and configure
76           the devices for softap mode testing.
77
78        Returns:
79            True if successfully configured the requirements for testing.
80        """
81        super().setup_class()
82        self.dut = self.android_devices[0]
83        self.dut_client = self.android_devices[1]
84        req_params = [
85            "dbs_supported_models", "sta_concurrency_supported_models",
86            "wifi6_models", "coex_unsafe_list_sap"
87        ]
88        opt_param = ["reference_networks"]
89        self.unpack_userparams(req_param_names=req_params,
90                               opt_param_names=opt_param)
91        if "AccessPoint" in self.user_params:
92            self.legacy_configure_ap_and_start()
93        elif "OpenWrtAP" in self.user_params:
94            self.configure_openwrt_ap_and_start(wpa_network=True)
95        self.wifi_network = self.reference_networks[0]["2g"]
96        # Do a simple version of init - mainly just sync the time and enable
97        # verbose logging.  This test will fail if the DUT has a sim and cell
98        # data is disabled.  We would also like to test with phones in less
99        # constrained states (or add variations where we specifically
100        # constrain).
101        utils.require_sl4a((self.dut, self.dut_client))
102        utils.sync_device_time(self.dut)
103        utils.sync_device_time(self.dut_client)
104        # Enable verbose logging on the duts
105        self.dut.droid.wifiEnableVerboseLogging(1)
106        asserts.assert_equal(
107            self.dut.droid.wifiGetVerboseLoggingLevel(), 1,
108            "Failed to enable WiFi verbose logging on the softap dut.")
109        self.dut_client.droid.wifiEnableVerboseLogging(1)
110        asserts.assert_equal(
111            self.dut_client.droid.wifiGetVerboseLoggingLevel(), 1,
112            "Failed to enable WiFi verbose logging on the client dut.")
113        wutils.wifi_toggle_state(self.dut, True)
114        wutils.wifi_toggle_state(self.dut_client, True)
115        self.AP_IFACE = 'wlan0'
116        if self.dut.model in self.dbs_supported_models:
117            self.AP_IFACE = 'wlan1'
118        if self.dut.model in self.sta_concurrency_supported_models:
119            self.AP_IFACE = 'wlan2'
120        if len(self.android_devices) > 2:
121            utils.sync_device_time(self.android_devices[2])
122            self.android_devices[2].droid.wifiEnableVerboseLogging(1)
123            asserts.assert_equal(
124                self.android_devices[2].droid.wifiGetVerboseLoggingLevel(), 1,
125                "Failed to enable WiFi verbose logging on the client dut.")
126            self.dut_client_2 = self.android_devices[2]
127
128    def teardown_class(self):
129        super().teardown_class()
130        for ad in self.android_devices:
131            wutils.wifi_toggle_state(ad, True)
132            wutils.reset_wifi(ad)
133            time.sleep(WAIT_AFTER_REBOOT)
134        if self.dut.droid.wifiIsApEnabled():
135            wutils.stop_wifi_tethering(self.dut)
136        if "AccessPoint" in self.user_params:
137            del self.user_params["reference_networks"]
138            del self.user_params["open_network"]
139
140    def setup_test(self):
141        super().setup_test()
142        for ad in self.android_devices:
143            wutils.wifi_toggle_state(ad, True)
144        self.dut.reboot()
145        time.sleep(WAIT_AFTER_REBOOT)
146
147    def teardown_test(self):
148        super().teardown_test()
149        for ad in self.android_devices:
150            wutils.wifi_toggle_state(ad, True)
151        if self.dut.droid.wifiIsApEnabled():
152            wutils.stop_wifi_tethering(self.dut)
153        self.dut.log.debug("Toggling Airplane mode OFF.")
154        asserts.assert_true(
155            utils.force_airplane_mode(self.dut, False),
156            "Can not turn off airplane mode: %s" % self.dut.serial)
157        #reset coexcell setting
158        self.dut.adb.shell('cmd wifi reset-coex-cell-channels')
159
160    """ Helper Functions """
161
162    def coex_unsafe_channel_key(self, unsafe_channel):
163        if COEX_POWER_CAP_DBM in unsafe_channel:
164            return (unsafe_channel[COEX_BAND], unsafe_channel[COEX_CHANNEL],
165                    unsafe_channel[COEX_POWER_CAP_DBM])
166        return (unsafe_channel[COEX_BAND], unsafe_channel[COEX_CHANNEL])
167
168    def enable_softap(self, ad, band=None):
169        """ Enable SoftAp of the DUT
170
171        Returns:
172            (freq1, freq2): Integer; a 2G frequency and a 5G frequency if DUT
173                            support BridgedAp.
174            freq: Integer; a frequency from SoftAp.
175            None, bandwidth: Just a placeholder, won't be used.
176        """
177        # Enable SoftAp
178        # Create SoftAp config.
179        config = wutils.create_softap_config()
180        # If DUT support BridgedAp, then two BridgedAp instances enabled.
181        if self.dut.droid.wifiIsBridgedApConcurrencySupported():
182            wutils.save_wifi_soft_ap_config(
183                ad,
184                config,
185                bands=[
186                    WifiEnums.WIFI_CONFIG_SOFTAP_BAND_2G,
187                    WifiEnums.WIFI_CONFIG_SOFTAP_BAND_2G_5G
188                ])
189        # If DUT does not support BridgedAp, 2G OR 5G SoftAp enabled.
190        else:
191            if self.init_softap_band == BAND_2G:
192                band = WifiEnums.WIFI_CONFIG_SOFTAP_BAND_2G
193            if self.init_softap_band == BAND_5G:
194                band = WifiEnums.WIFI_CONFIG_SOFTAP_BAND_5G
195            wutils.save_wifi_soft_ap_config(ad, config, band=band)
196        wutils.start_wifi_tethering_saved_config(ad)
197        time.sleep(BRIDGED_AP_LAUNCH_INTERVAL_5_SECONDS)
198
199        # if DUT support BridgedAp:
200        if ad.droid.wifiIsBridgedApConcurrencySupported():
201            callbackId = ad.droid.registerSoftApCallback()
202            infos = wutils.get_current_softap_infos(ad, callbackId, True)
203            ad.droid.unregisterSoftApCallback(callbackId)
204            # if DUT BridgedAp has two instances, return two frequencies.
205            if len(infos) == 2:
206                freq_1 = infos[0]["frequency"]
207                freq_2 = infos[1]["frequency"]
208                self.dut.log.info(
209                    "DUT connected to AP on freq: {},{}, chan: {} ,{}".format(
210                        freq_1, freq_2, WifiEnums.freq_to_channel[freq_1],
211                        WifiEnums.freq_to_channel[freq_2]))
212                return freq_1, freq_2
213            # if DUT BridgedAp has only one instances, return the frequency.
214            elif len(infos) == 1:
215                freq = infos[0]["frequency"]
216                self.dut.log.info(
217                    "DUT connected to AP on freq: {}, chan: {}".format(
218                        freq, WifiEnums.freq_to_channel[freq]))
219                return freq
220            else:
221                raise signals.TestFailure("There should be SoftAp instance.")
222        # if DUT does not support BridgedAp:
223        else:
224            # Return SoftAp frequency.
225            callbackId = ad.droid.registerSoftApCallback()
226            freq, bandwidth = wutils.get_current_softap_info(
227                ad, callbackId, True)
228            ad.log.info("SoftAp freq: {}".format(freq))
229            ad.droid.unregisterSoftApCallback(callbackId)
230            self.dut.log.info(
231                "DUT connected to AP on freq: {}, chan: {}".format(
232                    freq, WifiEnums.freq_to_channel[freq]))
233            return freq, bandwidth
234
235    """ Tests Begin """
236
237    def coex_unsafechannel_avoidance(self, test_item):
238        self.radio = test_item["radio"]
239        self.band = test_item["band"]
240        self.cellchannels = test_item["setcoexcellchannels"]
241        time.sleep(WAIT_AFTER_REBOOT)
242        wutils.set_wifi_country_code(self.dut, country_code='US')
243        asserts.skip_if(not self.dut.droid.isSdkAtLeastS(),
244                        "Require SDK at least S to use wifi coex apis.")
245        self.dut.ed.clear_all_events()
246        #Listing the test coex setting from configuration
247        self.dut.log.info(
248            "DUT test cellcoex radio:{}, band:{}, channels setting:{}".format(
249                self.radio, self.band, self.cellchannels))
250        self.dut.adb.shell('cmd wifi set-coex-cell-channels %s %s %s' %
251                           (self.radio, self.band, self.cellchannels))
252        self.dut.droid.wifiRegisterCoexCallback()
253        try:
254            # Wait for the immediate callback from registering and store the current values
255            event = self.dut.ed.pop_event(
256                "WifiManagerCoexCallback#onCoexUnsafeChannelsChanged", 5)
257        except queue.Empty:
258            asserts.fail("Coex callback event not received after registering.")
259        prev_unsafe_channels = sorted(json.loads(
260            event["data"][KEY_COEX_UNSAFE_CHANNELS]),
261                                      key=self.coex_unsafe_channel_key)
262        prev_restrictions = sorted(
263            json.loads(event["data"][KEY_COEX_RESTRICTIONS]))
264        unsafe_channels = []
265        for i in range(len(prev_unsafe_channels)):
266            unsafe_channels.append(prev_unsafe_channels[i]['channel'])
267        self.dut.log.info("DUT unsafe channels:{}".format(unsafe_channels))
268        freq1, freq2 = self.enable_softap(self.dut)
269        sapchan1, sapchan2 = WifiEnums.freq_to_channel[
270            freq1], WifiEnums.freq_to_channel[freq2]
271        if sapchan1 in unsafe_channels or sapchan2 in unsafe_channels:
272            asserts.fail(
273                "devices hotspot's channel open on current unsafe channels " +
274                str(unsafe_channels))
275        else:
276            pass
277        self.dut.droid.wifiUnregisterCoexCallback()
278        self.dut.adb.shell('cmd wifi reset-coex-cell-channels')
279
280    """ Tests End """
281
282
283if __name__ == "__main__":
284    pass
285