1#!/usr/bin/env python3
2#
3#   Copyright 2020 - 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 itertools
18import re
19
20from acts import utils
21from acts.controllers.ap_lib.hostapd_security import Security
22from acts.controllers.ap_lib import hostapd_constants
23from acts.controllers.ap_lib import hostapd_config
24from acts.test_utils.abstract_devices.wlan_device import create_wlan_device
25from acts.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
26from acts.test_utils.abstract_devices.utils_lib.wlan_utils import validate_setup_ap_and_associate
27from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
28from acts.utils import rand_ascii_str
29
30# AC Capabilities
31"""
32Capabilities Not Supported on Whirlwind:
33    - Supported Channel Width ([VHT160], [VHT160-80PLUS80]): 160mhz and 80+80
34        unsupported
35    - SU Beamformer [SU-BEAMFORMER]
36    - SU Beamformee [SU-BEAMFORMEE]
37    - MU Beamformer [MU-BEAMFORMER]
38    - MU Beamformee [MU-BEAMFORMEE]
39    - BF Antenna ([BF-ANTENNA-2], [BF-ANTENNA-3], [BF-ANTENNA-4])
40    - Rx STBC 2, 3, & 4 ([RX-STBC-12],[RX-STBC-123],[RX-STBC-124])
41    - VHT Link Adaptation ([VHT-LINK-ADAPT2],[VHT-LINK-ADAPT3])
42    - VHT TXOP Power Save [VHT-TXOP-PS]
43    - HTC-VHT [HTC-VHT]
44"""
45VHT_MAX_MPDU_LEN = [
46    hostapd_constants.AC_CAPABILITY_MAX_MPDU_7991,
47    hostapd_constants.AC_CAPABILITY_MAX_MPDU_11454, ''
48]
49RXLDPC = [hostapd_constants.AC_CAPABILITY_RXLDPC, '']
50SHORT_GI_80 = [hostapd_constants.AC_CAPABILITY_SHORT_GI_80, '']
51TX_STBC = [hostapd_constants.AC_CAPABILITY_TX_STBC_2BY1, '']
52RX_STBC = [hostapd_constants.AC_CAPABILITY_RX_STBC_1, '']
53MAX_A_MPDU = [
54    hostapd_constants.AC_CAPABILITY_MAX_A_MPDU_LEN_EXP0,
55    hostapd_constants.AC_CAPABILITY_MAX_A_MPDU_LEN_EXP1,
56    hostapd_constants.AC_CAPABILITY_MAX_A_MPDU_LEN_EXP2,
57    hostapd_constants.AC_CAPABILITY_MAX_A_MPDU_LEN_EXP3,
58    hostapd_constants.AC_CAPABILITY_MAX_A_MPDU_LEN_EXP4,
59    hostapd_constants.AC_CAPABILITY_MAX_A_MPDU_LEN_EXP5,
60    hostapd_constants.AC_CAPABILITY_MAX_A_MPDU_LEN_EXP6,
61    hostapd_constants.AC_CAPABILITY_MAX_A_MPDU_LEN_EXP7, ''
62]
63RX_ANTENNA = [hostapd_constants.AC_CAPABILITY_RX_ANTENNA_PATTERN, '']
64TX_ANTENNA = [hostapd_constants.AC_CAPABILITY_TX_ANTENNA_PATTERN, '']
65
66# Default 11N Capabilities
67N_CAPABS_40MHZ = [
68    hostapd_constants.N_CAPABILITY_LDPC, hostapd_constants.N_CAPABILITY_SGI20,
69    hostapd_constants.N_CAPABILITY_RX_STBC1,
70    hostapd_constants.N_CAPABILITY_SGI20, hostapd_constants.N_CAPABILITY_SGI40,
71    hostapd_constants.N_CAPABILITY_MAX_AMSDU_7935,
72    hostapd_constants.N_CAPABILITY_HT40_PLUS
73]
74
75N_CAPABS_20MHZ = [
76    hostapd_constants.N_CAPABILITY_LDPC, hostapd_constants.N_CAPABILITY_SGI20,
77    hostapd_constants.N_CAPABILITY_RX_STBC1,
78    hostapd_constants.N_CAPABILITY_SGI20,
79    hostapd_constants.N_CAPABILITY_MAX_AMSDU_7935,
80    hostapd_constants.N_CAPABILITY_HT20
81]
82
83# Default wpa2 profile.
84WPA2_SECURITY = Security(security_mode=hostapd_constants.WPA2_STRING,
85                         password=rand_ascii_str(20),
86                         wpa_cipher=hostapd_constants.WPA2_DEFAULT_CIPER,
87                         wpa2_cipher=hostapd_constants.WPA2_DEFAULT_CIPER)
88
89
90def generate_test_name(settings):
91    """Generates a test name string based on the ac_capabilities for
92    a test case.
93
94    Args:
95        settings: a dict with the test settings (bandwidth, security, ac_capabs)
96
97    Returns:
98        A string test case name
99    """
100    chbw = settings['chbw']
101    sec = 'wpa2' if settings['security'] else 'open'
102    ret = []
103    for cap in hostapd_constants.AC_CAPABILITIES_MAPPING.keys():
104        if cap in settings['ac_capabilities']:
105            ret.append(hostapd_constants.AC_CAPABILITIES_MAPPING[cap])
106    return 'test_11ac_%smhz_%s_%s' % (chbw, sec, ''.join(ret))
107
108
109# 6912 test cases
110class WlanPhyCompliance11ACTest(AbstractDeviceWlanDeviceBaseTest):
111    """Tests for validating 11ac PHYS.
112
113    Test Bed Requirement:
114    * One Android device or Fuchsia device
115    * One Access Point
116    """
117    def __init__(self, controllers):
118        WifiBaseTest.__init__(self, controllers)
119        self.tests = [
120            'test_11ac_capabilities_20mhz_open',
121            'test_11ac_capabilities_40mhz_open',
122            'test_11ac_capabilities_80mhz_open',
123            'test_11ac_capabilities_20mhz_wpa2',
124            'test_11ac_capabilities_40mhz_wpa2',
125            'test_11ac_capabilities_80mhz_wpa2'
126        ]
127        if 'debug_11ac_tests' in self.user_params:
128            self.tests.append('test_11ac_capabilities_debug')
129
130    def setup_class(self):
131        super().setup_class()
132        if 'dut' in self.user_params:
133            if self.user_params['dut'] == 'fuchsia_devices':
134                self.dut = create_wlan_device(self.fuchsia_devices[0])
135            elif self.user_params['dut'] == 'android_devices':
136                self.dut = create_wlan_device(self.android_devices[0])
137            else:
138                raise ValueError('Invalid DUT specified in config. (%s)' %
139                                 self.user_params['dut'])
140        else:
141            self.dut = create_wlan_device(self.android_devices[0])
142
143        self.access_point = self.access_points[0]
144        self.android_devices = getattr(self, 'android_devices', [])
145        self.access_point.stop_all_aps()
146
147    def setup_test(self):
148        for ad in self.android_devices:
149            ad.droid.wakeLockAcquireBright()
150            ad.droid.wakeUpNow()
151        self.dut.wifi_toggle_state(True)
152
153    def teardown_test(self):
154        for ad in self.android_devices:
155            ad.droid.wakeLockRelease()
156            ad.droid.goToSleepNow()
157        self.dut.turn_location_off_and_scan_toggle_off()
158        self.dut.disconnect()
159        self.dut.reset_wifi()
160        self.access_point.stop_all_aps()
161
162    def on_fail(self, test_name, begin_time):
163        super().on_fail(test_name, begin_time)
164        self.access_point.stop_all_aps()
165
166    def setup_and_connect(self, ap_settings):
167        """Uses ap_settings to set up ap and then attempts to associate a DUT.
168
169        Args:
170            ap_settings: a dict containing test case settings, including
171                bandwidth, security, n_capabilities, and ac_capabilities
172
173        """
174        security = ap_settings['security']
175        chbw = ap_settings['chbw']
176        password = None
177        if security:
178            password = security.password
179        n_capabilities = ap_settings['n_capabilities']
180        ac_capabilities = ap_settings['ac_capabilities']
181
182        validate_setup_ap_and_associate(access_point=self.access_point,
183                                        client=self.dut,
184                                        profile_name='whirlwind',
185                                        mode=hostapd_constants.MODE_11AC_MIXED,
186                                        channel=36,
187                                        n_capabilities=n_capabilities,
188                                        ac_capabilities=ac_capabilities,
189                                        force_wmm=True,
190                                        ssid=utils.rand_ascii_str(20),
191                                        security=security,
192                                        vht_bandwidth=chbw,
193                                        password=password)
194
195    # 864 test cases
196    def test_11ac_capabilities_20mhz_open(self):
197        test_list = []
198        for combination in itertools.product(VHT_MAX_MPDU_LEN, RXLDPC, RX_STBC,
199                                             TX_STBC, MAX_A_MPDU, RX_ANTENNA,
200                                             TX_ANTENNA):
201            test_list.append({
202                'chbw': 20,
203                'security': None,
204                'n_capabilities': N_CAPABS_20MHZ,
205                'ac_capabilities': combination
206            })
207        self.run_generated_testcases(self.setup_and_connect,
208                                     settings=test_list,
209                                     name_func=generate_test_name)
210
211    # 864 test cases
212    def test_11ac_capabilities_40mhz_open(self):
213        test_list = []
214        for combination in itertools.product(VHT_MAX_MPDU_LEN, RXLDPC, RX_STBC,
215                                             TX_STBC, MAX_A_MPDU, RX_ANTENNA,
216                                             TX_ANTENNA):
217            test_list.append({
218                'chbw': 40,
219                'security': None,
220                'n_capabilities': N_CAPABS_40MHZ,
221                'ac_capabilities': combination
222            })
223        self.run_generated_testcases(self.setup_and_connect,
224                                     settings=test_list,
225                                     name_func=generate_test_name)
226
227    # 1728 test cases
228    def test_11ac_capabilities_80mhz_open(self):
229        test_list = []
230        for combination in itertools.product(VHT_MAX_MPDU_LEN, RXLDPC,
231                                             SHORT_GI_80, RX_STBC, TX_STBC,
232                                             MAX_A_MPDU, RX_ANTENNA,
233                                             TX_ANTENNA):
234            test_list.append({
235                'chbw': 80,
236                'security': None,
237                'n_capabilities': N_CAPABS_40MHZ,
238                'ac_capabilities': combination
239            })
240        self.run_generated_testcases(self.setup_and_connect,
241                                     settings=test_list,
242                                     name_func=generate_test_name)
243
244    # 864 test cases
245    def test_11ac_capabilities_20mhz_wpa2(self):
246        test_list = []
247        for combination in itertools.product(VHT_MAX_MPDU_LEN, RXLDPC, RX_STBC,
248                                             TX_STBC, MAX_A_MPDU, RX_ANTENNA,
249                                             TX_ANTENNA):
250            test_list.append({
251                'chbw': 20,
252                'security': WPA2_SECURITY,
253                'n_capabilities': N_CAPABS_20MHZ,
254                'ac_capabilities': combination
255            })
256        self.run_generated_testcases(self.setup_and_connect,
257                                     settings=test_list,
258                                     name_func=generate_test_name)
259
260    # 864 test cases
261    def test_11ac_capabilities_40mhz_wpa2(self):
262        test_list = []
263        for combination in itertools.product(VHT_MAX_MPDU_LEN, RXLDPC, RX_STBC,
264                                             TX_STBC, MAX_A_MPDU, RX_ANTENNA,
265                                             TX_ANTENNA):
266            test_list.append({
267                'chbw': 40,
268                'security': WPA2_SECURITY,
269                'n_capabilities': N_CAPABS_40MHZ,
270                'ac_capabilities': combination
271            })
272        self.run_generated_testcases(self.setup_and_connect,
273                                     settings=test_list,
274                                     name_func=generate_test_name)
275
276    # 1728 test cases
277    def test_11ac_capabilities_80mhz_wpa2(self):
278        test_list = []
279        for combination in itertools.product(VHT_MAX_MPDU_LEN, RXLDPC,
280                                             SHORT_GI_80, RX_STBC, TX_STBC,
281                                             MAX_A_MPDU, RX_ANTENNA,
282                                             TX_ANTENNA):
283            test_list.append({
284                'chbw': 80,
285                'security': WPA2_SECURITY,
286                'n_capabilities': N_CAPABS_40MHZ,
287                'ac_capabilities': combination
288            })
289        self.run_generated_testcases(self.setup_and_connect,
290                                     settings=test_list,
291                                     name_func=generate_test_name)
292
293    def test_11ac_capabilities_debug(self):
294        chbw_sec_capabs = re.compile(
295            r'.*?([0-9]*)mhz.*?(open|wpa2).*?(\[.*\])', re.IGNORECASE)
296        test_list = []
297        for test_name in self.user_params['debug_11ac_tests']:
298            test_to_run = re.match(chbw_sec_capabs, test_name)
299            chbw = int(test_to_run.group(1))
300            security = test_to_run.group(2)
301            capabs = test_to_run.group(3)
302            if chbw >= 40:
303                n_capabs = N_CAPABS_40MHZ
304            else:
305                n_capabs = N_CAPABS_20MHZ
306            if security.lower() == 'open':
307                security = None
308            elif security.lower() == 'wpa2':
309                security = WPA2_SECURITY
310            if capabs:
311                ac_capabs_strings = re.findall(r'\[.*?\]', capabs)
312                ac_capabs = [
313                    hostapd_constants.AC_CAPABILITIES_MAPPING_INVERSE[k]
314                    for k in ac_capabs_strings
315                ]
316            test_list.append({
317                'chbw': chbw,
318                'security': security,
319                'n_capabilities': n_capabs,
320                'ac_capabilities': ac_capabs
321            })
322        self.run_generated_testcases(self.setup_and_connect,
323                                     settings=test_list,
324                                     name_func=generate_test_name)
325