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
18
19from acts import asserts
20from acts import utils
21from acts.controllers.access_point import setup_ap
22from acts.controllers.ap_lib.hostapd_security import Security
23from acts.controllers.ap_lib import hostapd_constants
24from acts_contrib.test_utils.abstract_devices.wlan_device import create_wlan_device
25from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
26from acts.utils import rand_ascii_str
27
28# AC Capabilities
29"""
30Capabilities Not Supported on Whirlwind:
31    - Supported Channel Width ([VHT160], [VHT160-80PLUS80]): 160mhz and 80+80
32        unsupported
33    - SU Beamformer [SU-BEAMFORMER]
34    - SU Beamformee [SU-BEAMFORMEE]
35    - MU Beamformer [MU-BEAMFORMER]
36    - MU Beamformee [MU-BEAMFORMEE]
37    - BF Antenna ([BF-ANTENNA-2], [BF-ANTENNA-3], [BF-ANTENNA-4])
38    - Rx STBC 2, 3, & 4 ([RX-STBC-12],[RX-STBC-123],[RX-STBC-124])
39    - VHT Link Adaptation ([VHT-LINK-ADAPT2],[VHT-LINK-ADAPT3])
40    - VHT TXOP Power Save [VHT-TXOP-PS]
41    - HTC-VHT [HTC-VHT]
42"""
43VHT_MAX_MPDU_LEN = [
44    hostapd_constants.AC_CAPABILITY_MAX_MPDU_7991,
45    hostapd_constants.AC_CAPABILITY_MAX_MPDU_11454, ''
46]
47RXLDPC = [hostapd_constants.AC_CAPABILITY_RXLDPC, '']
48SHORT_GI_80 = [hostapd_constants.AC_CAPABILITY_SHORT_GI_80, '']
49TX_STBC = [hostapd_constants.AC_CAPABILITY_TX_STBC_2BY1, '']
50RX_STBC = [hostapd_constants.AC_CAPABILITY_RX_STBC_1, '']
51MAX_A_MPDU = [
52    hostapd_constants.AC_CAPABILITY_MAX_A_MPDU_LEN_EXP0,
53    hostapd_constants.AC_CAPABILITY_MAX_A_MPDU_LEN_EXP1,
54    hostapd_constants.AC_CAPABILITY_MAX_A_MPDU_LEN_EXP2,
55    hostapd_constants.AC_CAPABILITY_MAX_A_MPDU_LEN_EXP3,
56    hostapd_constants.AC_CAPABILITY_MAX_A_MPDU_LEN_EXP4,
57    hostapd_constants.AC_CAPABILITY_MAX_A_MPDU_LEN_EXP5,
58    hostapd_constants.AC_CAPABILITY_MAX_A_MPDU_LEN_EXP6,
59    hostapd_constants.AC_CAPABILITY_MAX_A_MPDU_LEN_EXP7, ''
60]
61RX_ANTENNA = [hostapd_constants.AC_CAPABILITY_RX_ANTENNA_PATTERN, '']
62TX_ANTENNA = [hostapd_constants.AC_CAPABILITY_TX_ANTENNA_PATTERN, '']
63
64# Default 11N Capabilities
65N_CAPABS_40MHZ = [
66    hostapd_constants.N_CAPABILITY_LDPC, hostapd_constants.N_CAPABILITY_SGI20,
67    hostapd_constants.N_CAPABILITY_RX_STBC1,
68    hostapd_constants.N_CAPABILITY_SGI20, hostapd_constants.N_CAPABILITY_SGI40,
69    hostapd_constants.N_CAPABILITY_MAX_AMSDU_7935,
70    hostapd_constants.N_CAPABILITY_HT40_PLUS
71]
72
73N_CAPABS_20MHZ = [
74    hostapd_constants.N_CAPABILITY_LDPC, hostapd_constants.N_CAPABILITY_SGI20,
75    hostapd_constants.N_CAPABILITY_RX_STBC1,
76    hostapd_constants.N_CAPABILITY_SGI20,
77    hostapd_constants.N_CAPABILITY_MAX_AMSDU_7935,
78    hostapd_constants.N_CAPABILITY_HT20
79]
80
81# Default wpa2 profile.
82WPA2_SECURITY = Security(security_mode=hostapd_constants.WPA2_STRING,
83                         password=rand_ascii_str(20),
84                         wpa_cipher=hostapd_constants.WPA2_DEFAULT_CIPER,
85                         wpa2_cipher=hostapd_constants.WPA2_DEFAULT_CIPER)
86
87SECURITIES = [None, WPA2_SECURITY]
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(WifiBaseTest):
111    """Tests for validating 11ac PHYS.
112
113    Test Bed Requirement:
114    * One Android device or Fuchsia device
115    * One Access Point
116    """
117
118    def __init__(self, controllers):
119        super().__init__(controllers)
120
121    def setup_generated_tests(self):
122        test_args = self._generate_20mhz_test_args() + \
123            self._generate_40mhz_test_args() + \
124            self._generate_80mhz_test_args()
125        self.generate_tests(test_logic=self.setup_and_connect,
126                            name_func=generate_test_name,
127                            arg_sets=test_args)
128
129    def setup_class(self):
130        super().setup_class()
131        if 'dut' in self.user_params:
132            if self.user_params['dut'] == 'fuchsia_devices':
133                self.dut = create_wlan_device(self.fuchsia_devices[0])
134            elif self.user_params['dut'] == 'android_devices':
135                self.dut = create_wlan_device(self.android_devices[0])
136            else:
137                raise ValueError('Invalid DUT specified in config. (%s)' %
138                                 self.user_params['dut'])
139        else:
140            self.dut = create_wlan_device(self.android_devices[0])
141
142        self.access_point = self.access_points[0]
143        self.android_devices = getattr(self, 'android_devices', [])
144        self.access_point.stop_all_aps()
145
146    def setup_test(self):
147        for ad in self.android_devices:
148            ad.droid.wakeLockAcquireBright()
149            ad.droid.wakeUpNow()
150        self.dut.wifi_toggle_state(True)
151
152    def teardown_test(self):
153        for ad in self.android_devices:
154            ad.droid.wakeLockRelease()
155            ad.droid.goToSleepNow()
156        self.dut.turn_location_off_and_scan_toggle_off()
157        self.dut.disconnect()
158        self.dut.reset_wifi()
159        self.download_ap_logs()
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        ssid = rand_ascii_str(20)
175        security = ap_settings['security']
176        chbw = ap_settings['chbw']
177        password = None
178        target_security = None
179        if security:
180            password = security.password
181            target_security = security.security_mode_string
182        n_capabilities = ap_settings['n_capabilities']
183        ac_capabilities = ap_settings['ac_capabilities']
184
185        setup_ap(access_point=self.access_point,
186                 profile_name='whirlwind',
187                 mode=hostapd_constants.MODE_11AC_MIXED,
188                 channel=36,
189                 n_capabilities=n_capabilities,
190                 ac_capabilities=ac_capabilities,
191                 force_wmm=True,
192                 ssid=ssid,
193                 security=security,
194                 vht_bandwidth=chbw,
195                 password=password)
196        asserts.assert_true(
197            self.dut.associate(ssid,
198                               target_pwd=password,
199                               target_security=target_security),
200            'Failed to associate.')
201
202    # 1728 tests
203    def _generate_20mhz_test_args(self):
204        test_args = []
205
206        # 864 test cases for open security
207        # 864 test cases for wpa2 security
208        for combination in itertools.product(SECURITIES, VHT_MAX_MPDU_LEN,
209                                             RXLDPC, RX_STBC, TX_STBC,
210                                             MAX_A_MPDU, RX_ANTENNA,
211                                             TX_ANTENNA):
212            security = combination[0]
213            ac_capabilities = combination[1:]
214            test_args.append(({
215                'chbw': 20,
216                'security': security,
217                'n_capabilities': N_CAPABS_20MHZ,
218                'ac_capabilities': ac_capabilities
219            }, ))
220
221        return test_args
222
223    # 1728 tests
224    def _generate_40mhz_test_args(self):
225        test_args = []
226
227        # 864 test cases for open security
228        # 864 test cases for wpa2 security
229        for combination in itertools.product(SECURITIES, VHT_MAX_MPDU_LEN,
230                                             RXLDPC, RX_STBC, TX_STBC,
231                                             MAX_A_MPDU, RX_ANTENNA,
232                                             TX_ANTENNA):
233            security = combination[0]
234            ac_capabilities = combination[1:]
235            test_args.append(({
236                'chbw': 40,
237                'security': security,
238                'n_capabilities': N_CAPABS_40MHZ,
239                'ac_capabilities': ac_capabilities
240            }, ))
241
242        return test_args
243
244    # 3456 tests
245    def _generate_80mhz_test_args(self):
246        test_args = []
247
248        # 1728 test cases for open security
249        # 1728 test cases for wpa2 security
250        for combination in itertools.product(SECURITIES, VHT_MAX_MPDU_LEN,
251                                             RXLDPC, SHORT_GI_80, RX_STBC,
252                                             TX_STBC, MAX_A_MPDU, RX_ANTENNA,
253                                             TX_ANTENNA):
254            security = combination[0]
255            ac_capabilities = combination[1:]
256            test_args.append(({
257                'chbw': 80,
258                'security': security,
259                'n_capabilities': N_CAPABS_40MHZ,
260                'ac_capabilities': ac_capabilities
261            }, ))
262        return test_args
263