1#!/usr/bin/env python3
2#
3#   Copyright 2019 - 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.utils_lib.wlan_utils import validate_setup_ap_and_associate
26from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
27from acts.utils import rand_ascii_str
28
29FREQUENCY_24 = ['2.4GHz']
30FREQUENCY_5 = ['5GHz']
31CHANNEL_BANDWIDTH_20 = ['HT20']
32CHANNEL_BANDWIDTH_40_LOWER = ['HT40-']
33CHANNEL_BANDWIDTH_40_UPPER = ['HT40+']
34SECURITY_OPEN = 'open'
35SECURITY_WPA2 = 'wpa2'
36LDPC = [hostapd_constants.N_CAPABILITY_LDPC, '']
37TX_STBC = [hostapd_constants.N_CAPABILITY_TX_STBC, '']
38RX_STBC = [hostapd_constants.N_CAPABILITY_RX_STBC1, '']
39SGI_20 = [hostapd_constants.N_CAPABILITY_SGI20, '']
40SGI_40 = [hostapd_constants.N_CAPABILITY_SGI40, '']
41DSSS_CCK = [hostapd_constants.N_CAPABILITY_DSSS_CCK_40, '']
42INTOLERANT_40 = [hostapd_constants.N_CAPABILITY_40_INTOLERANT, '']
43MAX_AMPDU_7935 = [hostapd_constants.N_CAPABILITY_MAX_AMSDU_7935, '']
44SMPS = [hostapd_constants.N_CAPABILITY_SMPS_STATIC, '']
45
46
47def generate_test_name(settings):
48    """Generates a string based on the n_capabilities for a test case
49
50    Args:
51        settings: A dictionary of hostapd constant n_capabilities.
52
53    Returns:
54        A string that represents a test case name.
55    """
56    ret = []
57    for cap in hostapd_constants.N_CAPABILITIES_MAPPING.keys():
58        if cap in settings['n_capabilities']:
59            ret.append(hostapd_constants.N_CAPABILITIES_MAPPING[cap])
60    return '%s_%s_%s_%s' % (settings['frequency'], settings['chbw'],
61                            settings['security'], ''.join(ret))
62
63
64class WlanPhyCompliance11NTest(WifiBaseTest):
65    """Tests for validating 11n PHYS.
66
67    Test Bed Requirement:
68    * One Android device or Fuchsia device
69    * One Access Point
70    """
71    def __init__(self, controllers):
72        WifiBaseTest.__init__(self, controllers)
73        self.tests = [
74            'test_11n_capabilities_24_HT20',
75            'test_11n_capabilities_24_HT40_lower',
76            'test_11n_capabilities_24_HT40_upper',
77            'test_11n_capabilities_5_HT20',
78            'test_11n_capabilities_5_HT40_lower',
79            'test_11n_capabilities_5_HT40_upper',
80            'test_11n_capabilities_24_HT20_wpa2',
81            'test_11n_capabilities_24_HT40_lower_wpa2',
82            'test_11n_capabilities_24_HT40_upper_wpa2',
83            'test_11n_capabilities_5_HT20_wpa2',
84            'test_11n_capabilities_5_HT40_lower_wpa2',
85            'test_11n_capabilities_5_HT40_upper_wpa2'
86        ]
87        if 'debug_11n_tests' in self.user_params:
88            self.tests.append('test_11n_capabilities_debug')
89
90    def setup_class(self):
91        super().setup_class()
92        if 'dut' in self.user_params:
93            if self.user_params['dut'] == 'fuchsia_devices':
94                self.dut = create_wlan_device(self.fuchsia_devices[0])
95            elif self.user_params['dut'] == 'android_devices':
96                self.dut = create_wlan_device(self.android_devices[0])
97            else:
98                raise ValueError('Invalid DUT specified in config. (%s)' %
99                                 self.user_params['dut'])
100        else:
101            # Default is an android device, just like the other tests
102            self.dut = create_wlan_device(self.android_devices[0])
103
104        self.access_point = self.access_points[0]
105        self.access_point.stop_all_aps()
106
107    def setup_test(self):
108        if hasattr(self, "android_devices"):
109            for ad in self.android_devices:
110                ad.droid.wakeLockAcquireBright()
111                ad.droid.wakeUpNow()
112        self.dut.wifi_toggle_state(True)
113
114    def teardown_test(self):
115        if hasattr(self, "android_devices"):
116            for ad in self.android_devices:
117                ad.droid.wakeLockRelease()
118                ad.droid.goToSleepNow()
119        self.dut.turn_location_off_and_scan_toggle_off()
120        self.dut.disconnect()
121        self.dut.reset_wifi()
122        self.access_point.stop_all_aps()
123
124    def on_fail(self, test_name, begin_time):
125        self.dut.take_bug_report(test_name, begin_time)
126        self.dut.get_log(test_name, begin_time)
127
128    def setup_and_connect(self, ap_settings):
129        """Generates a hostapd config, setups up the AP with that config, then
130           attempts to associate a DUT
131
132        Args:
133               ap_settings: A dictionary of hostapd constant n_capabilities.
134        """
135        security_profile = None
136        password = None
137        temp_n_capabilities = list(ap_settings['n_capabilities'])
138        n_capabilities = []
139        for n_capability in temp_n_capabilities:
140            if n_capability in hostapd_constants.N_CAPABILITIES_MAPPING.keys():
141                n_capabilities.append(n_capability)
142
143        if ap_settings['chbw'] == 'HT20' or ap_settings['chbw'] == 'HT40+':
144            if ap_settings['frequency'] == '2.4GHz':
145                channel = 1
146            elif ap_settings['frequency'] == '5GHz':
147                channel = 36
148            else:
149                raise ValueError('Invalid frequence: %s' %
150                                 ap_settings['frequency'])
151
152        if ap_settings['chbw'] == 'HT40-':
153            if ap_settings['frequency'] == '2.4GHz':
154                channel = 11
155            elif ap_settings['frequency'] == '5GHz':
156                channel = 60
157            else:
158                raise ValueError('Invalid frequency: %s' %
159                                 ap_settings['frequency'])
160
161        if ap_settings['chbw'] == 'HT40-' or ap_settings['chbw'] == 'HT40+':
162            if hostapd_config.ht40_plus_allowed(channel):
163                extended_channel = hostapd_constants.N_CAPABILITY_HT40_PLUS
164            elif hostapd_config.ht40_minus_allowed(channel):
165                extended_channel = hostapd_constants.N_CAPABILITY_HT40_MINUS
166            else:
167                raise ValueError('Invalid channel: %s' % channel)
168            n_capabilities.append(extended_channel)
169
170        if ap_settings['security'] == 'wpa2':
171            security_profile = Security(security_mode=SECURITY_WPA2,
172                                        password=rand_ascii_str(20),
173                                        wpa_cipher='CCMP',
174                                        wpa2_cipher='CCMP')
175            password = security_profile.password
176
177        validate_setup_ap_and_associate(access_point=self.access_point,
178                                        client=self.dut,
179                                        profile_name='whirlwind',
180                                        mode=hostapd_constants.MODE_11N_MIXED,
181                                        channel=channel,
182                                        n_capabilities=n_capabilities,
183                                        ac_capabilities=[],
184                                        force_wmm=True,
185                                        ssid=utils.rand_ascii_str(20),
186                                        security=security_profile,
187                                        password=password)
188
189    def test_11n_capabilities_24_HT20(self):
190        test_list = []
191        for combination in itertools.product(FREQUENCY_24,
192                                             CHANNEL_BANDWIDTH_20, LDPC,
193                                             TX_STBC, RX_STBC, SGI_20,
194                                             INTOLERANT_40, MAX_AMPDU_7935,
195                                             SMPS):
196            test_frequency = combination[0]
197            test_chbw = combination[1]
198            n_capabilities = combination[2:]
199            test_list.append({
200                'frequency': test_frequency,
201                'chbw': test_chbw,
202                'security': SECURITY_OPEN,
203                'n_capabilities': n_capabilities
204            })
205        self.run_generated_testcases(self.setup_and_connect,
206                                     settings=test_list,
207                                     name_func=generate_test_name)
208
209    def test_11n_capabilities_24_HT40_lower(self):
210        test_list = []
211        for combination in itertools.product(FREQUENCY_24,
212                                             CHANNEL_BANDWIDTH_40_LOWER, LDPC,
213                                             TX_STBC, RX_STBC, SGI_20, SGI_40,
214                                             MAX_AMPDU_7935, SMPS, DSSS_CCK):
215            test_frequency = combination[0]
216            test_chbw = combination[1]
217            n_capabilities = combination[2:]
218            test_list.append({
219                'frequency': test_frequency,
220                'chbw': test_chbw,
221                'security': SECURITY_OPEN,
222                'n_capabilities': n_capabilities
223            })
224        self.run_generated_testcases(self.setup_and_connect,
225                                     settings=test_list,
226                                     name_func=generate_test_name)
227
228    def test_11n_capabilities_24_HT40_upper(self):
229        test_list = []
230        for combination in itertools.product(FREQUENCY_24,
231                                             CHANNEL_BANDWIDTH_40_UPPER, LDPC,
232                                             TX_STBC, RX_STBC, SGI_20, SGI_40,
233                                             MAX_AMPDU_7935, SMPS, DSSS_CCK):
234            test_frequency = combination[0]
235            test_chbw = combination[1]
236            n_capabilities = combination[2:]
237            test_list.append({
238                'frequency': test_frequency,
239                'chbw': test_chbw,
240                'security': SECURITY_OPEN,
241                'n_capabilities': n_capabilities
242            })
243        self.run_generated_testcases(self.setup_and_connect,
244                                     settings=test_list,
245                                     name_func=generate_test_name)
246
247    def test_11n_capabilities_5_HT20(self):
248        test_list = []
249        for combination in itertools.product(FREQUENCY_5, CHANNEL_BANDWIDTH_20,
250                                             LDPC, TX_STBC, RX_STBC, SGI_20,
251                                             INTOLERANT_40, MAX_AMPDU_7935,
252                                             SMPS):
253            test_frequency = combination[0]
254            test_chbw = combination[1]
255            n_capabilities = combination[2:]
256            test_list.append({
257                'frequency': test_frequency,
258                'chbw': test_chbw,
259                'security': SECURITY_OPEN,
260                'n_capabilities': n_capabilities
261            })
262        self.run_generated_testcases(self.setup_and_connect,
263                                     settings=test_list,
264                                     name_func=generate_test_name)
265
266    def test_11n_capabilities_5_HT40_lower(self):
267        test_list = []
268        for combination in itertools.product(FREQUENCY_5,
269                                             CHANNEL_BANDWIDTH_40_LOWER, LDPC,
270                                             TX_STBC, RX_STBC, SGI_20, SGI_40,
271                                             MAX_AMPDU_7935, SMPS, DSSS_CCK):
272            test_frequency = combination[0]
273            test_chbw = combination[1]
274            n_capabilities = combination[2:]
275            test_list.append({
276                'frequency': test_frequency,
277                'chbw': test_chbw,
278                'security': SECURITY_OPEN,
279                'n_capabilities': n_capabilities
280            })
281        self.run_generated_testcases(self.setup_and_connect,
282                                     settings=test_list,
283                                     name_func=generate_test_name)
284
285    def test_11n_capabilities_5_HT40_upper(self):
286        test_list = []
287        for combination in itertools.product(FREQUENCY_5,
288                                             CHANNEL_BANDWIDTH_40_UPPER, LDPC,
289                                             TX_STBC, RX_STBC, SGI_20, SGI_40,
290                                             MAX_AMPDU_7935, SMPS, DSSS_CCK):
291            test_frequency = combination[0]
292            test_chbw = combination[1]
293            n_capabilities = combination[2:]
294            test_list.append({
295                'frequency': test_frequency,
296                'chbw': test_chbw,
297                'security': SECURITY_OPEN,
298                'n_capabilities': n_capabilities
299            })
300        self.run_generated_testcases(self.setup_and_connect,
301                                     settings=test_list,
302                                     name_func=generate_test_name)
303
304    def test_11n_capabilities_24_HT20_wpa2(self):
305        test_list = []
306        for combination in itertools.product(FREQUENCY_24,
307                                             CHANNEL_BANDWIDTH_20, LDPC,
308                                             TX_STBC, RX_STBC, SGI_20,
309                                             INTOLERANT_40, MAX_AMPDU_7935,
310                                             SMPS):
311            test_frequency = combination[0]
312            test_chbw = combination[1]
313            n_capabilities = combination[2:]
314            test_list.append({
315                'frequency': test_frequency,
316                'chbw': test_chbw,
317                'security': SECURITY_WPA2,
318                'n_capabilities': n_capabilities
319            })
320        self.run_generated_testcases(self.setup_and_connect,
321                                     settings=test_list,
322                                     name_func=generate_test_name)
323
324    def test_11n_capabilities_24_HT40_lower_wpa2(self):
325        test_list = []
326        for combination in itertools.product(FREQUENCY_24,
327                                             CHANNEL_BANDWIDTH_40_LOWER, LDPC,
328                                             TX_STBC, RX_STBC, SGI_20, SGI_40,
329                                             MAX_AMPDU_7935, SMPS, DSSS_CCK):
330            test_frequency = combination[0]
331            test_chbw = combination[1]
332            n_capabilities = combination[2:]
333            test_list.append({
334                'frequency': test_frequency,
335                'chbw': test_chbw,
336                'security': SECURITY_WPA2,
337                'n_capabilities': n_capabilities
338            })
339        self.run_generated_testcases(self.setup_and_connect,
340                                     settings=test_list,
341                                     name_func=generate_test_name)
342
343    def test_11n_capabilities_24_HT40_upper_wpa2(self):
344        test_list = []
345        for combination in itertools.product(FREQUENCY_24,
346                                             CHANNEL_BANDWIDTH_40_UPPER, LDPC,
347                                             TX_STBC, RX_STBC, SGI_20, SGI_40,
348                                             MAX_AMPDU_7935, SMPS, DSSS_CCK):
349            test_frequency = combination[0]
350            test_chbw = combination[1]
351            n_capabilities = combination[2:]
352            test_list.append({
353                'frequency': test_frequency,
354                'chbw': test_chbw,
355                'security': SECURITY_WPA2,
356                'n_capabilities': n_capabilities
357            })
358        self.run_generated_testcases(self.setup_and_connect,
359                                     settings=test_list,
360                                     name_func=generate_test_name)
361
362    def test_11n_capabilities_5_HT20_wpa2(self):
363        test_list = []
364        for combination in itertools.product(FREQUENCY_5, CHANNEL_BANDWIDTH_20,
365                                             LDPC, TX_STBC, RX_STBC, SGI_20,
366                                             INTOLERANT_40, MAX_AMPDU_7935,
367                                             SMPS):
368            test_frequency = combination[0]
369            test_chbw = combination[1]
370            n_capabilities = combination[2:]
371            test_list.append({
372                'frequency': test_frequency,
373                'chbw': test_chbw,
374                'security': SECURITY_WPA2,
375                'n_capabilities': n_capabilities
376            })
377        self.run_generated_testcases(self.setup_and_connect,
378                                     settings=test_list,
379                                     name_func=generate_test_name)
380
381    def test_11n_capabilities_5_HT40_lower_wpa2(self):
382        test_list = []
383        for combination in itertools.product(FREQUENCY_5,
384                                             CHANNEL_BANDWIDTH_40_LOWER, LDPC,
385                                             TX_STBC, RX_STBC, SGI_20, SGI_40,
386                                             MAX_AMPDU_7935, SMPS, DSSS_CCK):
387            test_frequency = combination[0]
388            test_chbw = combination[1]
389            n_capabilities = combination[2:]
390            test_list.append({
391                'frequency': test_frequency,
392                'chbw': test_chbw,
393                'security': SECURITY_WPA2,
394                'n_capabilities': n_capabilities
395            })
396        self.run_generated_testcases(self.setup_and_connect,
397                                     settings=test_list,
398                                     name_func=generate_test_name)
399
400    def test_11n_capabilities_5_HT40_upper_wpa2(self):
401        test_list = []
402        for combination in itertools.product(FREQUENCY_5,
403                                             CHANNEL_BANDWIDTH_40_UPPER, LDPC,
404                                             TX_STBC, RX_STBC, SGI_20, SGI_40,
405                                             MAX_AMPDU_7935, SMPS, DSSS_CCK):
406            test_frequency = combination[0]
407            test_chbw = combination[1]
408            n_capabilities = combination[2:]
409            test_list.append({
410                'frequency': test_frequency,
411                'chbw': test_chbw,
412                'security': SECURITY_WPA2,
413                'n_capabilities': n_capabilities
414            })
415        self.run_generated_testcases(self.setup_and_connect,
416                                     settings=test_list,
417                                     name_func=generate_test_name)
418
419    def test_11n_capabilities_debug(self):
420        allowed_frequencies = FREQUENCY_5 + FREQUENCY_24
421        allowed_chbw = (CHANNEL_BANDWIDTH_20 + CHANNEL_BANDWIDTH_40_LOWER +
422                        CHANNEL_BANDWIDTH_40_UPPER)
423        allowed_security = [SECURITY_WPA2, SECURITY_OPEN]
424        freq_chbw_sec = re.compile(r'(.*)_(.*)_(.*)_(\[.*\])?$')
425        for test_title in self.user_params['debug_11n_tests']:
426            test_list = []
427            test_to_run = re.match(freq_chbw_sec, test_title)
428            if test_to_run:
429                test_frequency = test_to_run.group(1)
430                test_chbw = test_to_run.group(2)
431                security = test_to_run.group(3)
432                if (test_frequency in allowed_frequencies
433                        and test_chbw in allowed_chbw
434                        and security in allowed_security):
435                    if test_to_run.group(4):
436                        n_capabilities_str = test_to_run.group(4)
437                    else:
438                        n_capabilities_str = ''
439                    n_capabilities_list = []
440                    if '[LDPC]' in n_capabilities_str:
441                        n_capabilities_list.append(
442                            hostapd_constants.N_CAPABILITY_LDPC)
443                    if '[TX-STBC]' in n_capabilities_str:
444                        n_capabilities_list.append(
445                            hostapd_constants.N_CAPABILITY_TX_STBC)
446                    if '[RX-STBC1]' in n_capabilities_str:
447                        n_capabilities_list.append(
448                            hostapd_constants.N_CAPABILITY_RX_STBC1)
449                    if '[SHORT-GI-20]' in n_capabilities_str:
450                        n_capabilities_list.append(
451                            hostapd_constants.N_CAPABILITY_SGI20)
452                    if '[SHORT-GI-40]' in n_capabilities_str:
453                        n_capabilities_list.append(
454                            hostapd_constants.N_CAPABILITY_SGI40)
455                    if '[DSSS_CCK-40]' in n_capabilities_str:
456                        n_capabilities_list.append(
457                            hostapd_constants.N_CAPABILITY_DSSS_CCK_40)
458                    if '[40-INTOLERANT]' in n_capabilities_str:
459                        n_capabilities_list.append(
460                            hostapd_constants.N_CAPABILITY_40_INTOLERANT)
461                    if '[MAX-AMSDU-7935]' in n_capabilities_str:
462                        n_capabilities_list.append(
463                            hostapd_constants.N_CAPABILITY_MAX_AMSDU_7935)
464                    if '[SMPS-STATIC]' in n_capabilities_str:
465                        n_capabilities_list.append(
466                            hostapd_constants.N_CAPABILITY_SMPS_STATIC)
467                    n_capabilities = tuple(n_capabilities_list)
468                    test_list.append({
469                        'frequency': test_frequency,
470                        'chbw': test_chbw,
471                        'security': security,
472                        'n_capabilities': n_capabilities
473                    })
474                    self.run_generated_testcases(self.setup_and_connect,
475                                                 settings=test_list,
476                                                 name_func=generate_test_name)
477                else:
478                    self.log.error('Invalid test (%s). Trying the next one.' %
479                                   test_title)
480            else:
481                self.log.error('Invalid test (%s). Trying the next one.' %
482                               test_title)
483