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