1#!/usr/bin/env python3.4
2#
3#   Copyright 2017 - Google
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"""
17    Base Class for Defining Common WiFi Test Functionality
18"""
19
20import copy
21import itertools
22import time
23
24import acts.controllers.access_point as ap
25
26from acts import asserts
27from acts import utils
28from acts.base_test import BaseTestClass
29from acts.signals import TestSignal
30from acts.controllers import android_device
31from acts.controllers.ap_lib import hostapd_ap_preset
32from acts.controllers.ap_lib import hostapd_bss_settings
33from acts.controllers.ap_lib import hostapd_constants
34from acts.controllers.ap_lib import hostapd_security
35
36
37class WifiBaseTest(BaseTestClass):
38    def __init__(self, controllers):
39        BaseTestClass.__init__(self, controllers)
40        if hasattr(self, 'attenuators') and self.attenuators:
41            for attenuator in self.attenuators:
42                attenuator.set_atten(0)
43
44    def get_wpa2_network(
45            self,
46            hidden=False,
47            ap_count=1,
48            ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G,
49            ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G,
50            passphrase_length_2g=hostapd_constants.AP_PASSPHRASE_LENGTH_2G,
51            passphrase_length_5g=hostapd_constants.AP_PASSPHRASE_LENGTH_5G):
52        """Generates SSID and passphrase for a WPA2 network using random
53           generator.
54
55           Args:
56               ap_count: Determines if we want to use one or both the APs
57                         for configuration. If set to '2', then both APs will
58                         be configured with the same configuration.
59               ssid_length_2g: Int, number of characters to use for 2G SSID.
60               ssid_length_5g: Int, number of characters to use for 5G SSID.
61               passphrase_length_2g: Int, length of password for 2G network.
62               passphrase_length_5g: Int, length of password for 5G network.
63           Returns: A dict of 2G and 5G network lists for hostapd configuration.
64
65        """
66        network_dict_2g = {}
67        network_dict_5g = {}
68        ref_5g_security = hostapd_constants.WPA2_STRING
69        ref_2g_security = hostapd_constants.WPA2_STRING
70        self.user_params["reference_networks"] = []
71
72        ref_2g_ssid = '2g_%s' % utils.rand_ascii_str(ssid_length_2g)
73        ref_2g_passphrase = utils.rand_ascii_str(passphrase_length_2g)
74
75        ref_5g_ssid = '5g_%s' % utils.rand_ascii_str(ssid_length_5g)
76        ref_5g_passphrase = utils.rand_ascii_str(passphrase_length_5g)
77
78        if hidden:
79           network_dict_2g = {
80              "SSID": ref_2g_ssid,
81              "security": ref_2g_security,
82              "password": ref_2g_passphrase,
83              "hiddenSSID": True
84           }
85
86           network_dict_5g = {
87              "SSID": ref_5g_ssid,
88              "security": ref_5g_security,
89              "password": ref_5g_passphrase,
90              "hiddenSSID": True
91           }
92        else:
93            network_dict_2g = {
94                "SSID": ref_2g_ssid,
95                "security": ref_2g_security,
96                "password": ref_2g_passphrase
97            }
98
99            network_dict_5g = {
100                "SSID": ref_5g_ssid,
101                "security": ref_5g_security,
102                "password": ref_5g_passphrase
103            }
104
105        ap = 0
106        for ap in range(ap_count):
107            self.user_params["reference_networks"].append({
108                "2g":
109                copy.copy(network_dict_2g),
110                "5g":
111                copy.copy(network_dict_5g)
112            })
113        self.reference_networks = self.user_params["reference_networks"]
114        return {"2g": network_dict_2g, "5g": network_dict_5g}
115
116    def get_open_network(self,
117                         hidden=False,
118                         ap_count=1,
119                         ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G,
120                         ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G):
121        """Generates SSIDs for a open network using a random generator.
122
123        Args:
124            ap_count: Determines if we want to use one or both the APs for
125                      for configuration. If set to '2', then both APs will
126                      be configured with the same configuration.
127            ssid_length_2g: Int, number of characters to use for 2G SSID.
128            ssid_length_5g: Int, number of characters to use for 5G SSID.
129        Returns: A dict of 2G and 5G network lists for hostapd configuration.
130
131        """
132        network_dict_2g = {}
133        network_dict_5g = {}
134        self.user_params["open_network"] = []
135        open_2g_ssid = '2g_%s' % utils.rand_ascii_str(ssid_length_2g)
136        open_5g_ssid = '5g_%s' % utils.rand_ascii_str(ssid_length_5g)
137        if hidden:
138            network_dict_2g = {
139            "SSID": open_2g_ssid,
140            "security": 'none',
141            "hiddenSSID": True
142            }
143
144            network_dict_5g = {
145            "SSID": open_5g_ssid,
146            "security": 'none',
147            "hiddenSSID": True
148            }
149        else:
150            network_dict_2g = {
151                "SSID": open_2g_ssid,
152                "security": 'none'
153            }
154
155            network_dict_5g = {
156                "SSID": open_5g_ssid,
157                "security": 'none'
158            }
159
160        ap = 0
161        for ap in range(ap_count):
162            self.user_params["open_network"].append({
163                "2g": network_dict_2g,
164                "5g": network_dict_5g
165            })
166        self.open_network = self.user_params["open_network"]
167        return {"2g": network_dict_2g, "5g": network_dict_5g}
168
169    def populate_bssid(self, ap_instance, ap, networks_5g, networks_2g):
170        """Get bssid for a given SSID and add it to the network dictionary.
171
172        Args:
173            ap_instance: Accesspoint index that was configured.
174            ap: Accesspoint object corresponding to ap_instance.
175            networks_5g: List of 5g networks configured on the APs.
176            networks_2g: List of 2g networks configured on the APs.
177
178        """
179
180        if not (networks_5g or networks_2g):
181            return
182
183        for network in itertools.chain(networks_5g, networks_2g):
184            if 'channel' in network:
185                continue
186            bssid = ap.get_bssid_from_ssid(network["SSID"])
187            if '2g' in network["SSID"]:
188                band = hostapd_constants.BAND_2G
189            else:
190                band = hostapd_constants.BAND_5G
191            if network["security"] == hostapd_constants.WPA2_STRING:
192                # TODO:(bamahadev) Change all occurances of reference_networks
193                # to wpa_networks.
194                self.reference_networks[ap_instance][band]["bssid"] = bssid
195            else:
196                self.open_network[ap_instance][band]["bssid"] = bssid
197
198    def legacy_configure_ap_and_start(
199            self,
200            channel_5g=hostapd_constants.AP_DEFAULT_CHANNEL_5G,
201            channel_2g=hostapd_constants.AP_DEFAULT_CHANNEL_2G,
202            max_2g_networks=hostapd_constants.AP_DEFAULT_MAX_SSIDS_2G,
203            max_5g_networks=hostapd_constants.AP_DEFAULT_MAX_SSIDS_5G,
204            ap_ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G,
205            ap_passphrase_length_2g=hostapd_constants.AP_PASSPHRASE_LENGTH_2G,
206            ap_ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G,
207            ap_passphrase_length_5g=hostapd_constants.AP_PASSPHRASE_LENGTH_5G,
208            hidden=False,
209            ap_count=1):
210        asserts.assert_true(
211            len(self.user_params["AccessPoint"]) == 2,
212            "Exactly two access points must be specified. \
213             Each access point has 2 radios, one each for 2.4GHZ \
214             and 5GHz. A test can choose to use one or both APs.")
215        network_list_2g = []
216        network_list_5g = []
217        network_list_2g.append({"channel": channel_2g})
218        network_list_5g.append({"channel": channel_5g})
219
220        if "reference_networks" in self.user_params:
221            pass
222        else:
223            networks_dict = self.get_wpa2_network(hidden=hidden,
224                ap_count=ap_count)
225            network_list_2g.append(networks_dict["2g"])
226            network_list_5g.append(networks_dict["5g"])
227
228        if "open_network" in self.user_params:
229            pass
230        else:
231            networks_dict = self.get_open_network(hidden=hidden,
232                ap_count=ap_count)
233            network_list_2g.append(networks_dict["2g"])
234            network_list_5g.append(networks_dict["5g"])
235
236        orig_network_list_5g = copy.copy(network_list_5g)
237        orig_network_list_2g = copy.copy(network_list_2g)
238
239        if len(network_list_5g) > 1:
240            self.config_5g = self._generate_legacy_ap_config(network_list_5g)
241        if len(network_list_2g) > 1:
242            self.config_2g = self._generate_legacy_ap_config(network_list_2g)
243        ap = 0
244        for ap in range(ap_count):
245            self.access_points[ap].start_ap(self.config_2g)
246            self.access_points[ap].start_ap(self.config_5g)
247            self.populate_bssid(ap, self.access_points[ap], orig_network_list_5g,
248                                orig_network_list_2g)
249
250    def _generate_legacy_ap_config(self, network_list):
251        bss_settings = []
252        ap_settings = network_list.pop(0)
253        # TODO:(bmahadev) This is a bug. We should not have to pop the first
254        # network in the list and treat it as a separate case. Instead,
255        # create_ap_preset() should be able to take NULL ssid and security and
256        # build config based on the bss_Settings alone.
257        hostapd_config_settings = network_list.pop(0)
258        for network in network_list:
259            if "password" in network and "hiddenSSID" in network:
260                bss_settings.append(
261                    hostapd_bss_settings.BssSettings(
262                        name=network["SSID"],
263                        ssid=network["SSID"],
264                        hidden=True,
265                        security=hostapd_security.Security(
266                            security_mode=network["security"],
267                            password=network["password"])))
268            elif "password" in network and not "hiddenSSID" in network:
269                bss_settings.append(
270                    hostapd_bss_settings.BssSettings(
271                        name=network["SSID"],
272                        ssid=network["SSID"],
273                        security=hostapd_security.Security(
274                            security_mode=network["security"],
275                            password=network["password"])))
276            elif not "password" in network and "hiddenSSID" in network:
277                bss_settings.append(
278                    hostapd_bss_settings.BssSettings(
279                        name=network["SSID"],
280                        ssid=network["SSID"],
281                        hidden=True))
282            elif not "password" in network and not "hiddenSSID" in network:
283                bss_settings.append(
284                    hostapd_bss_settings.BssSettings(
285                        name=network["SSID"],
286                        ssid=network["SSID"]))
287        if "password" in hostapd_config_settings:
288            config = hostapd_ap_preset.create_ap_preset(
289                channel=ap_settings["channel"],
290                ssid=hostapd_config_settings["SSID"],
291                security=hostapd_security.Security(
292                    security_mode=hostapd_config_settings["security"],
293                    password=hostapd_config_settings["password"]),
294                bss_settings=bss_settings,
295                profile_name='whirlwind')
296        else:
297            config = hostapd_ap_preset.create_ap_preset(
298                channel=ap_settings["channel"],
299                ssid=hostapd_config_settings["SSID"],
300                bss_settings=bss_settings,
301                profile_name='whirlwind')
302        return config
303