• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
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 os
23import time
24
25import acts.controllers.access_point as ap
26
27from acts import asserts
28from acts import signals
29from acts import utils
30from acts.base_test import BaseTestClass
31from acts.signals import TestSignal
32from acts.controllers import android_device
33from acts.controllers.access_point import AccessPoint
34from acts.controllers.ap_lib import hostapd_ap_preset
35from acts.controllers.ap_lib import hostapd_bss_settings
36from acts.controllers.ap_lib import hostapd_constants
37from acts.controllers.ap_lib import hostapd_security
38from acts.keys import Config
39from acts_contrib.test_utils.net import net_test_utils as nutils
40from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
41
42AP_1 = 0
43AP_2 = 1
44MAX_AP_COUNT = 2
45
46
47class WifiBaseTest(BaseTestClass):
48    def __init__(self, configs):
49        super().__init__(configs)
50        self.enable_packet_log = False
51        self.packet_log_2g = hostapd_constants.AP_DEFAULT_CHANNEL_2G
52        self.packet_log_5g = hostapd_constants.AP_DEFAULT_CHANNEL_5G
53
54    def setup_class(self):
55        if hasattr(self, 'attenuators') and self.attenuators:
56            for attenuator in self.attenuators:
57                attenuator.set_atten(0)
58        opt_param = ["country_code_file"]
59        self.unpack_userparams(opt_param_names=opt_param)
60        if self.enable_packet_log and hasattr(self, "packet_capture"):
61            self.packet_logger = self.packet_capture[0]
62            self.packet_logger.configure_monitor_mode("2G", self.packet_log_2g)
63            self.packet_logger.configure_monitor_mode("5G", self.packet_log_5g)
64        if hasattr(self, "android_devices"):
65            for ad in self.android_devices:
66                wutils.wifi_test_device_init(ad)
67                if hasattr(self, "country_code_file"):
68                    if isinstance(self.country_code_file, list):
69                        self.country_code_file = self.country_code_file[0]
70                    if not os.path.isfile(self.country_code_file):
71                        self.country_code_file = os.path.join(
72                            self.user_params[Config.key_config_path.value],
73                            self.country_code_file)
74                    self.country_code = utils.load_config(
75                        self.country_code_file)["country"]
76                    wutils.set_wifi_country_code(ad, self.country_code)
77
78    def setup_test(self):
79        if (hasattr(self, "android_devices")):
80            wutils.start_all_wlan_logs(self.android_devices)
81        self.tcpdump_proc = []
82        if hasattr(self, "android_devices"):
83            for ad in self.android_devices:
84                proc = nutils.start_tcpdump(ad, self.test_name)
85                self.tcpdump_proc.append((ad, proc))
86        if hasattr(self, "packet_logger"):
87            self.packet_log_pid = wutils.start_pcap(self.packet_logger, 'dual',
88                                                    self.test_name)
89
90    def teardown_test(self):
91        if (hasattr(self, "android_devices")):
92            wutils.stop_all_wlan_logs(self.android_devices)
93            for proc in self.tcpdump_proc:
94                nutils.stop_tcpdump(proc[0],
95                                    proc[1],
96                                    self.test_name,
97                                    pull_dump=False)
98            self.tcpdump_proc = []
99        if hasattr(self, "packet_logger") and self.packet_log_pid:
100            wutils.stop_pcap(self.packet_logger,
101                             self.packet_log_pid,
102                             test_status=True)
103            self.packet_log_pid = {}
104
105    def on_fail(self, test_name, begin_time):
106        if hasattr(self, "android_devices"):
107            for ad in self.android_devices:
108                ad.take_bug_report(test_name, begin_time)
109                ad.cat_adb_log(test_name, begin_time)
110                wutils.get_ssrdumps(ad)
111            wutils.stop_all_wlan_logs(self.android_devices)
112            for ad in self.android_devices:
113                wutils.get_wlan_logs(ad)
114            for proc in self.tcpdump_proc:
115                nutils.stop_tcpdump(proc[0], proc[1], self.test_name)
116            self.tcpdump_proc = []
117        if hasattr(self, "packet_logger") and self.packet_log_pid:
118            wutils.stop_pcap(self.packet_logger,
119                             self.packet_log_pid,
120                             test_status=False)
121            self.packet_log_pid = {}
122
123    def get_psk_network(
124            self,
125            mirror_ap,
126            reference_networks,
127            hidden=False,
128            same_ssid=False,
129            security_mode=hostapd_constants.WPA2_STRING,
130            ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G,
131            ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G,
132            passphrase_length_2g=hostapd_constants.AP_PASSPHRASE_LENGTH_2G,
133            passphrase_length_5g=hostapd_constants.AP_PASSPHRASE_LENGTH_5G):
134        """Generates SSID and passphrase for a WPA2 network using random
135           generator.
136
137           Args:
138               mirror_ap: Boolean, determines if both APs use the same hostapd
139                          config or different configs.
140               reference_networks: List of PSK networks.
141               same_ssid: Boolean, determines if both bands on AP use the same
142                          SSID.
143               ssid_length_2gecond AP Int, number of characters to use for 2G SSID.
144               ssid_length_5g: Int, number of characters to use for 5G SSID.
145               passphrase_length_2g: Int, length of password for 2G network.
146               passphrase_length_5g: Int, length of password for 5G network.
147
148           Returns: A dict of 2G and 5G network lists for hostapd configuration.
149
150        """
151        network_dict_2g = {}
152        network_dict_5g = {}
153        ref_5g_security = security_mode
154        ref_2g_security = security_mode
155
156        if same_ssid:
157            ref_2g_ssid = 'xg_%s' % utils.rand_ascii_str(ssid_length_2g)
158            ref_5g_ssid = ref_2g_ssid
159
160            ref_2g_passphrase = utils.rand_ascii_str(passphrase_length_2g)
161            ref_5g_passphrase = ref_2g_passphrase
162
163        else:
164            ref_2g_ssid = '2g_%s' % utils.rand_ascii_str(ssid_length_2g)
165            ref_2g_passphrase = utils.rand_ascii_str(passphrase_length_2g)
166
167            ref_5g_ssid = '5g_%s' % utils.rand_ascii_str(ssid_length_5g)
168            ref_5g_passphrase = utils.rand_ascii_str(passphrase_length_5g)
169
170        network_dict_2g = {
171            "SSID": ref_2g_ssid,
172            "security": ref_2g_security,
173            "password": ref_2g_passphrase,
174            "hiddenSSID": hidden
175        }
176
177        network_dict_5g = {
178            "SSID": ref_5g_ssid,
179            "security": ref_5g_security,
180            "password": ref_5g_passphrase,
181            "hiddenSSID": hidden
182        }
183
184        ap = 0
185        for ap in range(MAX_AP_COUNT):
186            reference_networks.append({
187                "2g": copy.copy(network_dict_2g),
188                "5g": copy.copy(network_dict_5g)
189            })
190            if not mirror_ap:
191                break
192        return {"2g": network_dict_2g, "5g": network_dict_5g}
193
194    def get_open_network(self,
195                         mirror_ap,
196                         open_network,
197                         hidden=False,
198                         same_ssid=False,
199                         ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G,
200                         ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G,
201                         security_mode='none'):
202        """Generates SSIDs for a open network using a random generator.
203
204        Args:
205            mirror_ap: Boolean, determines if both APs use the same hostapd
206                       config or different configs.
207            open_network: List of open networks.
208            same_ssid: Boolean, determines if both bands on AP use the same
209                       SSID.
210            ssid_length_2g: Int, number of characters to use for 2G SSID.
211            ssid_length_5g: Int, number of characters to use for 5G SSID.
212            security_mode: 'none' for open and 'OWE' for WPA3 OWE.
213
214        Returns: A dict of 2G and 5G network lists for hostapd configuration.
215
216        """
217        network_dict_2g = {}
218        network_dict_5g = {}
219
220        if same_ssid:
221            open_2g_ssid = 'xg_%s' % utils.rand_ascii_str(ssid_length_2g)
222            open_5g_ssid = open_2g_ssid
223
224        else:
225            open_2g_ssid = '2g_%s' % utils.rand_ascii_str(ssid_length_2g)
226            open_5g_ssid = '5g_%s' % utils.rand_ascii_str(ssid_length_5g)
227
228        network_dict_2g = {
229            "SSID": open_2g_ssid,
230            "security": security_mode,
231            "hiddenSSID": hidden
232        }
233
234        network_dict_5g = {
235            "SSID": open_5g_ssid,
236            "security": security_mode,
237            "hiddenSSID": hidden
238        }
239
240        ap = 0
241        for ap in range(MAX_AP_COUNT):
242            open_network.append({
243                "2g": copy.copy(network_dict_2g),
244                "5g": copy.copy(network_dict_5g)
245            })
246            if not mirror_ap:
247                break
248        return {"2g": network_dict_2g, "5g": network_dict_5g}
249
250    def get_wep_network(
251            self,
252            mirror_ap,
253            networks,
254            hidden=False,
255            same_ssid=False,
256            ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G,
257            ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G,
258            passphrase_length_2g=hostapd_constants.AP_PASSPHRASE_LENGTH_2G,
259            passphrase_length_5g=hostapd_constants.AP_PASSPHRASE_LENGTH_5G):
260        """Generates SSID and passphrase for a WEP network using random
261           generator.
262
263           Args:
264               mirror_ap: Boolean, determines if both APs use the same hostapd
265                          config or different configs.
266               networks: List of WEP networks.
267               same_ssid: Boolean, determines if both bands on AP use the same
268                          SSID.
269               ssid_length_2gecond AP Int, number of characters to use for 2G SSID.
270               ssid_length_5g: Int, number of characters to use for 5G SSID.
271               passphrase_length_2g: Int, length of password for 2G network.
272               passphrase_length_5g: Int, length of password for 5G network.
273
274           Returns: A dict of 2G and 5G network lists for hostapd configuration.
275
276        """
277        network_dict_2g = {}
278        network_dict_5g = {}
279        ref_5g_security = hostapd_constants.WEP_STRING
280        ref_2g_security = hostapd_constants.WEP_STRING
281
282        if same_ssid:
283            ref_2g_ssid = 'xg_%s' % utils.rand_ascii_str(ssid_length_2g)
284            ref_5g_ssid = ref_2g_ssid
285
286            ref_2g_passphrase = utils.rand_hex_str(passphrase_length_2g)
287            ref_5g_passphrase = ref_2g_passphrase
288
289        else:
290            ref_2g_ssid = '2g_%s' % utils.rand_ascii_str(ssid_length_2g)
291            ref_2g_passphrase = utils.rand_hex_str(passphrase_length_2g)
292
293            ref_5g_ssid = '5g_%s' % utils.rand_ascii_str(ssid_length_5g)
294            ref_5g_passphrase = utils.rand_hex_str(passphrase_length_5g)
295
296        network_dict_2g = {
297            "SSID": ref_2g_ssid,
298            "security": ref_2g_security,
299            "wepKeys": [ref_2g_passphrase] * 4,
300            "hiddenSSID": hidden
301        }
302
303        network_dict_5g = {
304            "SSID": ref_5g_ssid,
305            "security": ref_5g_security,
306            "wepKeys": [ref_2g_passphrase] * 4,
307            "hiddenSSID": hidden
308        }
309
310        ap = 0
311        for ap in range(MAX_AP_COUNT):
312            networks.append({
313                "2g": copy.copy(network_dict_2g),
314                "5g": copy.copy(network_dict_5g)
315            })
316            if not mirror_ap:
317                break
318        return {"2g": network_dict_2g, "5g": network_dict_5g}
319
320    def update_bssid(self, ap_instance, ap, network, band):
321        """Get bssid and update network dictionary.
322
323        Args:
324            ap_instance: Accesspoint index that was configured.
325            ap: Accesspoint object corresponding to ap_instance.
326            network: Network dictionary.
327            band: Wifi networks' band.
328
329        """
330        bssid = ap.get_bssid_from_ssid(network["SSID"], band)
331
332        if network["security"] == hostapd_constants.WPA2_STRING:
333            # TODO:(bamahadev) Change all occurances of reference_networks
334            # to wpa_networks.
335            self.reference_networks[ap_instance][band]["bssid"] = bssid
336        if network["security"] == hostapd_constants.WPA_STRING:
337            self.wpa_networks[ap_instance][band]["bssid"] = bssid
338        if network["security"] == hostapd_constants.WEP_STRING:
339            self.wep_networks[ap_instance][band]["bssid"] = bssid
340        if network["security"] == hostapd_constants.ENT_STRING:
341            if "bssid" not in self.ent_networks[ap_instance][band]:
342                self.ent_networks[ap_instance][band]["bssid"] = bssid
343            else:
344                self.ent_networks_pwd[ap_instance][band]["bssid"] = bssid
345        if network["security"] == 'none':
346            self.open_network[ap_instance][band]["bssid"] = bssid
347
348    def populate_bssid(self, ap_instance, ap, networks_5g, networks_2g):
349        """Get bssid for a given SSID and add it to the network dictionary.
350
351        Args:
352            ap_instance: Accesspoint index that was configured.
353            ap: Accesspoint object corresponding to ap_instance.
354            networks_5g: List of 5g networks configured on the APs.
355            networks_2g: List of 2g networks configured on the APs.
356
357        """
358
359        if not (networks_5g or networks_2g):
360            return
361
362        for network in networks_5g:
363            if 'channel' in network:
364                continue
365            self.update_bssid(ap_instance, ap, network,
366                              hostapd_constants.BAND_5G)
367
368        for network in networks_2g:
369            if 'channel' in network:
370                continue
371            self.update_bssid(ap_instance, ap, network,
372                              hostapd_constants.BAND_2G)
373
374    def configure_openwrt_ap_and_start(
375            self,
376            channel_5g=hostapd_constants.AP_DEFAULT_CHANNEL_5G,
377            channel_2g=hostapd_constants.AP_DEFAULT_CHANNEL_2G,
378            channel_5g_ap2=None,
379            channel_2g_ap2=None,
380            ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G,
381            passphrase_length_2g=hostapd_constants.AP_PASSPHRASE_LENGTH_2G,
382            ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G,
383            passphrase_length_5g=hostapd_constants.AP_PASSPHRASE_LENGTH_5G,
384            mirror_ap=False,
385            hidden=False,
386            same_ssid=False,
387            open_network=False,
388            wpa1_network=False,
389            wpa_network=False,
390            wep_network=False,
391            ent_network=False,
392            ent_network_pwd=False,
393            owe_network=False,
394            sae_network=False,
395            saemixed_network=False,
396            radius_conf_2g=None,
397            radius_conf_5g=None,
398            radius_conf_pwd=None,
399            ap_count=1,
400            ieee80211w=None):
401        """Create, configure and start OpenWrt AP.
402
403        Args:
404            channel_5g: 5G channel to configure.
405            channel_2g: 2G channel to configure.
406            channel_5g_ap2: 5G channel to configure on AP2.
407            channel_2g_ap2: 2G channel to configure on AP2.
408            ssid_length_2g: Int, number of characters to use for 2G SSID.
409            passphrase_length_2g: Int, length of password for 2G network.
410            ssid_length_5g: Int, number of characters to use for 5G SSID.
411            passphrase_length_5g: Int, length of password for 5G network.
412            same_ssid: Boolean, determines if both bands on AP use the same SSID.
413            open_network: Boolean, to check if open network should be configured.
414            wpa_network: Boolean, to check if wpa network should be configured.
415            wep_network: Boolean, to check if wep network should be configured.
416            ent_network: Boolean, to check if ent network should be configured.
417            ent_network_pwd: Boolean, to check if ent pwd network should be configured.
418            owe_network: Boolean, to check if owe network should be configured.
419            sae_network: Boolean, to check if sae network should be configured.
420            saemixed_network: Boolean, to check if saemixed network should be configured.
421            radius_conf_2g: dictionary with enterprise radius server details.
422            radius_conf_5g: dictionary with enterprise radius server details.
423            radius_conf_pwd: dictionary with enterprise radiuse server details.
424            ap_count: APs to configure.
425            ieee80211w:PMF to configure
426        """
427        if mirror_ap and ap_count == 1:
428            raise ValueError("ap_count cannot be 1 if mirror_ap is True.")
429        if (channel_5g_ap2 or channel_2g_ap2) and ap_count == 1:
430            raise ValueError(
431                "ap_count cannot be 1 if channels of AP2 are provided.")
432        # we are creating a channel list for 2G and 5G bands. The list is of
433        # size 2 and this is based on the assumption that each testbed will have
434        # at most 2 APs.
435        if not channel_5g_ap2:
436            channel_5g_ap2 = channel_5g
437        if not channel_2g_ap2:
438            channel_2g_ap2 = channel_2g
439        channels_2g = [channel_2g, channel_2g_ap2]
440        channels_5g = [channel_5g, channel_5g_ap2]
441
442        self.reference_networks = []
443        self.wpa1_networks = []
444        self.wpa_networks = []
445        self.wep_networks = []
446        self.ent_networks = []
447        self.ent_networks_pwd = []
448        self.open_network = []
449        self.owe_networks = []
450        self.sae_networks = []
451        self.saemixed_networks = []
452        self.bssid_map = []
453        for i in range(ap_count):
454            network_list = []
455            if wpa1_network:
456                wpa1_dict = self.get_psk_network(mirror_ap,
457                                                 self.wpa1_networks,
458                                                 hidden, same_ssid,
459                                                 ssid_length_2g, ssid_length_5g,
460                                                 passphrase_length_2g,
461                                                 passphrase_length_5g)
462                wpa1_dict[hostapd_constants.BAND_2G]["security"] = "psk"
463                wpa1_dict[hostapd_constants.BAND_5G]["security"] = "psk"
464                wpa1_dict[hostapd_constants.BAND_2G]["ieee80211w"] = ieee80211w
465                wpa1_dict[hostapd_constants.BAND_5G]["ieee80211w"] = ieee80211w
466                self.wpa1_networks.append(wpa1_dict)
467                network_list.append(wpa1_dict)
468            if wpa_network:
469                wpa_dict = self.get_psk_network(mirror_ap,
470                                                self.reference_networks,
471                                                hidden, same_ssid,
472                                                ssid_length_2g, ssid_length_5g,
473                                                passphrase_length_2g,
474                                                passphrase_length_5g)
475                wpa_dict[hostapd_constants.BAND_2G]["security"] = "psk2"
476                wpa_dict[hostapd_constants.BAND_5G]["security"] = "psk2"
477                wpa_dict[hostapd_constants.BAND_2G]["ieee80211w"] = ieee80211w
478                wpa_dict[hostapd_constants.BAND_5G]["ieee80211w"] = ieee80211w
479                self.wpa_networks.append(wpa_dict)
480                network_list.append(wpa_dict)
481            if wep_network:
482                wep_dict = self.get_wep_network(mirror_ap, self.wep_networks,
483                                                hidden, same_ssid,
484                                                ssid_length_2g, ssid_length_5g)
485                network_list.append(wep_dict)
486            if ent_network:
487                ent_dict = self.get_open_network(mirror_ap, self.ent_networks,
488                                                 hidden, same_ssid,
489                                                 ssid_length_2g,
490                                                 ssid_length_5g)
491                ent_dict["2g"]["security"] = "wpa2"
492                ent_dict["2g"].update(radius_conf_2g)
493                ent_dict["5g"]["security"] = "wpa2"
494                ent_dict["5g"].update(radius_conf_5g)
495                network_list.append(ent_dict)
496            if ent_network_pwd:
497                ent_pwd_dict = self.get_open_network(mirror_ap,
498                                                     self.ent_networks_pwd,
499                                                     hidden, same_ssid,
500                                                     ssid_length_2g,
501                                                     ssid_length_5g)
502                ent_pwd_dict["2g"]["security"] = "wpa2"
503                ent_pwd_dict["2g"].update(radius_conf_pwd)
504                ent_pwd_dict["5g"]["security"] = "wpa2"
505                ent_pwd_dict["5g"].update(radius_conf_pwd)
506                network_list.append(ent_pwd_dict)
507            if open_network:
508                open_dict = self.get_open_network(mirror_ap, self.open_network,
509                                                  hidden, same_ssid,
510                                                  ssid_length_2g,
511                                                  ssid_length_5g)
512                network_list.append(open_dict)
513            if owe_network:
514                owe_dict = self.get_open_network(mirror_ap, self.owe_networks,
515                                                 hidden, same_ssid,
516                                                 ssid_length_2g,
517                                                 ssid_length_5g, "OWE")
518                owe_dict[hostapd_constants.BAND_2G]["security"] = "owe"
519                owe_dict[hostapd_constants.BAND_5G]["security"] = "owe"
520                network_list.append(owe_dict)
521            if sae_network:
522                sae_dict = self.get_psk_network(mirror_ap, self.sae_networks,
523                                                hidden, same_ssid,
524                                                hostapd_constants.SAE_KEY_MGMT,
525                                                ssid_length_2g, ssid_length_5g,
526                                                passphrase_length_2g,
527                                                passphrase_length_5g)
528                sae_dict[hostapd_constants.BAND_2G]["security"] = "sae"
529                sae_dict[hostapd_constants.BAND_5G]["security"] = "sae"
530                network_list.append(sae_dict)
531            if saemixed_network:
532                saemixed_dict = self.get_psk_network(mirror_ap, self.saemixed_networks,
533                                                hidden, same_ssid,
534                                                hostapd_constants.SAE_KEY_MGMT,
535                                                ssid_length_2g, ssid_length_5g,
536                                                passphrase_length_2g,
537                                                passphrase_length_5g)
538                saemixed_dict[hostapd_constants.BAND_2G]["security"] = "sae-mixed"
539                saemixed_dict[hostapd_constants.BAND_5G]["security"] = "sae-mixed"
540                saemixed_dict[hostapd_constants.BAND_2G]["ieee80211w"] = ieee80211w
541                saemixed_dict[hostapd_constants.BAND_5G]["ieee80211w"] = ieee80211w
542                network_list.append(saemixed_dict)
543            self.access_points[i].configure_ap(network_list, channels_2g[i],
544                                               channels_5g[i])
545            self.access_points[i].start_ap()
546            self.bssid_map.append(
547                self.access_points[i].get_bssids_for_wifi_networks())
548            if mirror_ap:
549                self.access_points[i + 1].configure_ap(network_list,
550                                                       channels_2g[i+1],
551                                                       channels_5g[i+1])
552                self.access_points[i + 1].start_ap()
553                self.bssid_map.append(
554                    self.access_points[i + 1].get_bssids_for_wifi_networks())
555                break
556
557    def legacy_configure_ap_and_start(
558            self,
559            channel_5g=hostapd_constants.AP_DEFAULT_CHANNEL_5G,
560            channel_2g=hostapd_constants.AP_DEFAULT_CHANNEL_2G,
561            max_2g_networks=hostapd_constants.AP_DEFAULT_MAX_SSIDS_2G,
562            max_5g_networks=hostapd_constants.AP_DEFAULT_MAX_SSIDS_5G,
563            ap_ssid_length_2g=hostapd_constants.AP_SSID_LENGTH_2G,
564            ap_passphrase_length_2g=hostapd_constants.AP_PASSPHRASE_LENGTH_2G,
565            ap_ssid_length_5g=hostapd_constants.AP_SSID_LENGTH_5G,
566            ap_passphrase_length_5g=hostapd_constants.AP_PASSPHRASE_LENGTH_5G,
567            hidden=False,
568            same_ssid=False,
569            mirror_ap=True,
570            wpa_network=False,
571            wep_network=False,
572            ent_network=False,
573            radius_conf_2g=None,
574            radius_conf_5g=None,
575            ent_network_pwd=False,
576            radius_conf_pwd=None,
577            ap_count=1):
578
579        config_count = 1
580        count = 0
581
582        # For example, the NetworkSelector tests use 2 APs and require that
583        # both APs are not mirrored.
584        if not mirror_ap and ap_count == 1:
585            raise ValueError("ap_count cannot be 1 if mirror_ap is False.")
586
587        if not mirror_ap:
588            config_count = ap_count
589
590        self.user_params["reference_networks"] = []
591        self.user_params["open_network"] = []
592        if wpa_network:
593            self.user_params["wpa_networks"] = []
594        if wep_network:
595            self.user_params["wep_networks"] = []
596        if ent_network:
597            self.user_params["ent_networks"] = []
598        if ent_network_pwd:
599            self.user_params["ent_networks_pwd"] = []
600
601        # kill hostapd & dhcpd if the cleanup was not successful
602        for i in range(len(self.access_points)):
603            self.log.debug("Check ap state and cleanup")
604            self._cleanup_hostapd_and_dhcpd(i)
605
606        for count in range(config_count):
607
608            network_list_2g = []
609            network_list_5g = []
610
611            orig_network_list_2g = []
612            orig_network_list_5g = []
613
614            network_list_2g.append({"channel": channel_2g})
615            network_list_5g.append({"channel": channel_5g})
616
617            networks_dict = self.get_psk_network(
618                mirror_ap,
619                self.user_params["reference_networks"],
620                hidden=hidden,
621                same_ssid=same_ssid)
622            self.reference_networks = self.user_params["reference_networks"]
623
624            network_list_2g.append(networks_dict["2g"])
625            network_list_5g.append(networks_dict["5g"])
626
627            # When same_ssid is set, only configure one set of WPA networks.
628            # We cannot have more than one set because duplicate interface names
629            # are not allowed.
630            # TODO(bmahadev): Provide option to select the type of network,
631            # instead of defaulting to WPA.
632            if not same_ssid:
633                networks_dict = self.get_open_network(
634                    mirror_ap,
635                    self.user_params["open_network"],
636                    hidden=hidden,
637                    same_ssid=same_ssid)
638                self.open_network = self.user_params["open_network"]
639
640                network_list_2g.append(networks_dict["2g"])
641                network_list_5g.append(networks_dict["5g"])
642
643                if wpa_network:
644                    networks_dict = self.get_psk_network(
645                        mirror_ap,
646                        self.user_params["wpa_networks"],
647                        hidden=hidden,
648                        same_ssid=same_ssid,
649                        security_mode=hostapd_constants.WPA_STRING)
650                    self.wpa_networks = self.user_params["wpa_networks"]
651
652                    network_list_2g.append(networks_dict["2g"])
653                    network_list_5g.append(networks_dict["5g"])
654
655                if wep_network:
656                    networks_dict = self.get_wep_network(
657                        mirror_ap,
658                        self.user_params["wep_networks"],
659                        hidden=hidden,
660                        same_ssid=same_ssid)
661                    self.wep_networks = self.user_params["wep_networks"]
662
663                    network_list_2g.append(networks_dict["2g"])
664                    network_list_5g.append(networks_dict["5g"])
665
666                if ent_network:
667                    networks_dict = self.get_open_network(
668                        mirror_ap,
669                        self.user_params["ent_networks"],
670                        hidden=hidden,
671                        same_ssid=same_ssid)
672                    networks_dict["2g"][
673                        "security"] = hostapd_constants.ENT_STRING
674                    networks_dict["2g"].update(radius_conf_2g)
675                    networks_dict["5g"][
676                        "security"] = hostapd_constants.ENT_STRING
677                    networks_dict["5g"].update(radius_conf_5g)
678                    self.ent_networks = self.user_params["ent_networks"]
679
680                    network_list_2g.append(networks_dict["2g"])
681                    network_list_5g.append(networks_dict["5g"])
682
683                if ent_network_pwd:
684                    networks_dict = self.get_open_network(
685                        mirror_ap,
686                        self.user_params["ent_networks_pwd"],
687                        hidden=hidden,
688                        same_ssid=same_ssid)
689                    networks_dict["2g"][
690                        "security"] = hostapd_constants.ENT_STRING
691                    networks_dict["2g"].update(radius_conf_pwd)
692                    networks_dict["5g"][
693                        "security"] = hostapd_constants.ENT_STRING
694                    networks_dict["5g"].update(radius_conf_pwd)
695                    self.ent_networks_pwd = self.user_params[
696                        "ent_networks_pwd"]
697
698                    network_list_2g.append(networks_dict["2g"])
699                    network_list_5g.append(networks_dict["5g"])
700
701            orig_network_list_5g = copy.copy(network_list_5g)
702            orig_network_list_2g = copy.copy(network_list_2g)
703
704            if len(network_list_5g) > 1:
705                self.config_5g = self._generate_legacy_ap_config(
706                    network_list_5g)
707            if len(network_list_2g) > 1:
708                self.config_2g = self._generate_legacy_ap_config(
709                    network_list_2g)
710
711            self.access_points[count].start_ap(self.config_2g)
712            self.access_points[count].start_ap(self.config_5g)
713            self.populate_bssid(count, self.access_points[count],
714                                orig_network_list_5g, orig_network_list_2g)
715
716        # Repeat configuration on the second router.
717        if mirror_ap and ap_count == 2:
718            self.access_points[AP_2].start_ap(self.config_2g)
719            self.access_points[AP_2].start_ap(self.config_5g)
720            self.populate_bssid(AP_2, self.access_points[AP_2],
721                                orig_network_list_5g, orig_network_list_2g)
722
723    def _kill_processes(self, ap, daemon):
724        """ Kill hostapd and dhcpd daemons
725
726        Args:
727            ap: AP to cleanup
728            daemon: process to kill
729
730        Returns: True/False if killing process is successful
731        """
732        self.log.info("Killing %s" % daemon)
733        pids = ap.ssh.run('pidof %s' % daemon, ignore_status=True)
734        if pids.stdout:
735            ap.ssh.run('kill %s' % pids.stdout, ignore_status=True)
736        time.sleep(3)
737        pids = ap.ssh.run('pidof %s' % daemon, ignore_status=True)
738        if pids.stdout:
739            return False
740        return True
741
742    def _cleanup_hostapd_and_dhcpd(self, count):
743        """ Check if AP was cleaned up properly
744
745        Kill hostapd and dhcpd processes if cleanup was not successful in the
746        last run
747
748        Args:
749            count: AP to check
750
751        Returns:
752            New AccessPoint object if AP required cleanup
753
754        Raises:
755            Error: if the AccessPoint timed out to setup
756        """
757        ap = self.access_points[count]
758        phy_ifaces = ap.interfaces.get_physical_interface()
759        kill_hostapd = False
760        for iface in phy_ifaces:
761            if '2g_' in iface or '5g_' in iface or 'xg_' in iface:
762                kill_hostapd = True
763                break
764
765        if not kill_hostapd:
766            return
767
768        self.log.debug("Cleanup AP")
769        if not self._kill_processes(ap, 'hostapd') or \
770            not self._kill_processes(ap, 'dhcpd'):
771            raise ("Failed to cleanup AP")
772
773        ap.__init__(self.user_params['AccessPoint'][count])
774
775    def _generate_legacy_ap_config(self, network_list):
776        bss_settings = []
777        wlan_2g = self.access_points[AP_1].wlan_2g
778        wlan_5g = self.access_points[AP_1].wlan_5g
779        ap_settings = network_list.pop(0)
780        # TODO:(bmahadev) This is a bug. We should not have to pop the first
781        # network in the list and treat it as a separate case. Instead,
782        # create_ap_preset() should be able to take NULL ssid and security and
783        # build config based on the bss_Settings alone.
784        hostapd_config_settings = network_list.pop(0)
785        for network in network_list:
786            if "password" in network:
787                bss_settings.append(
788                    hostapd_bss_settings.BssSettings(
789                        name=network["SSID"],
790                        ssid=network["SSID"],
791                        hidden=network["hiddenSSID"],
792                        security=hostapd_security.Security(
793                            security_mode=network["security"],
794                            password=network["password"])))
795            elif "wepKeys" in network:
796                bss_settings.append(
797                    hostapd_bss_settings.BssSettings(
798                        name=network["SSID"],
799                        ssid=network["SSID"],
800                        hidden=network["hiddenSSID"],
801                        security=hostapd_security.Security(
802                            security_mode=network["security"],
803                            password=network["wepKeys"][0])))
804            elif network["security"] == hostapd_constants.ENT_STRING:
805                bss_settings.append(
806                    hostapd_bss_settings.BssSettings(
807                        name=network["SSID"],
808                        ssid=network["SSID"],
809                        hidden=network["hiddenSSID"],
810                        security=hostapd_security.Security(
811                            security_mode=network["security"],
812                            radius_server_ip=network["radius_server_ip"],
813                            radius_server_port=network["radius_server_port"],
814                            radius_server_secret=network[
815                                "radius_server_secret"])))
816            else:
817                bss_settings.append(
818                    hostapd_bss_settings.BssSettings(
819                        name=network["SSID"],
820                        ssid=network["SSID"],
821                        hidden=network["hiddenSSID"]))
822        if "password" in hostapd_config_settings:
823            config = hostapd_ap_preset.create_ap_preset(
824                iface_wlan_2g=wlan_2g,
825                iface_wlan_5g=wlan_5g,
826                channel=ap_settings["channel"],
827                ssid=hostapd_config_settings["SSID"],
828                hidden=hostapd_config_settings["hiddenSSID"],
829                security=hostapd_security.Security(
830                    security_mode=hostapd_config_settings["security"],
831                    password=hostapd_config_settings["password"]),
832                bss_settings=bss_settings)
833        elif "wepKeys" in hostapd_config_settings:
834            config = hostapd_ap_preset.create_ap_preset(
835                iface_wlan_2g=wlan_2g,
836                iface_wlan_5g=wlan_5g,
837                channel=ap_settings["channel"],
838                ssid=hostapd_config_settings["SSID"],
839                hidden=hostapd_config_settings["hiddenSSID"],
840                security=hostapd_security.Security(
841                    security_mode=hostapd_config_settings["security"],
842                    password=hostapd_config_settings["wepKeys"][0]),
843                bss_settings=bss_settings)
844        else:
845            config = hostapd_ap_preset.create_ap_preset(
846                iface_wlan_2g=wlan_2g,
847                iface_wlan_5g=wlan_5g,
848                channel=ap_settings["channel"],
849                ssid=hostapd_config_settings["SSID"],
850                hidden=hostapd_config_settings["hiddenSSID"],
851                bss_settings=bss_settings)
852        return config
853
854    def configure_packet_capture(
855            self,
856            channel_5g=hostapd_constants.AP_DEFAULT_CHANNEL_5G,
857            channel_2g=hostapd_constants.AP_DEFAULT_CHANNEL_2G):
858        """Configure packet capture for 2G and 5G bands.
859
860        Args:
861            channel_5g: Channel to set the monitor mode to for 5G band.
862            channel_2g: Channel to set the monitor mode to for 2G band.
863        """
864        self.packet_capture = self.packet_capture[0]
865        result = self.packet_capture.configure_monitor_mode(
866            hostapd_constants.BAND_2G, channel_2g)
867        if not result:
868            raise ValueError("Failed to configure channel for 2G band")
869
870        result = self.packet_capture.configure_monitor_mode(
871            hostapd_constants.BAND_5G, channel_5g)
872        if not result:
873            raise ValueError("Failed to configure channel for 5G band.")
874
875    @staticmethod
876    def wifi_test_wrap(fn):
877        def _safe_wrap_test_case(self, *args, **kwargs):
878            test_id = "%s:%s:%s" % (self.__class__.__name__, self.test_name,
879                                    self.log_begin_time.replace(' ', '-'))
880            self.test_id = test_id
881            self.result_detail = ""
882            tries = int(self.user_params.get("wifi_auto_rerun", 3))
883            for ad in self.android_devices:
884                ad.log_path = self.log_path
885            for i in range(tries + 1):
886                result = True
887                if i > 0:
888                    log_string = "[Test Case] RETRY:%s %s" % (i,
889                                                              self.test_name)
890                    self.log.info(log_string)
891                    self._teardown_test(self.test_name)
892                    self._setup_test(self.test_name)
893                try:
894                    result = fn(self, *args, **kwargs)
895                except signals.TestFailure as e:
896                    self.log.warn("Error msg: %s" % e)
897                    if self.result_detail:
898                        signal.details = self.result_detail
899                    result = False
900                except signals.TestSignal:
901                    if self.result_detail:
902                        signal.details = self.result_detail
903                    raise
904                except Exception as e:
905                    self.log.exception(e)
906                    asserts.fail(self.result_detail)
907                if result is False:
908                    if i < tries:
909                        continue
910                else:
911                    break
912            if result is not False:
913                asserts.explicit_pass(self.result_detail)
914            else:
915                asserts.fail(self.result_detail)
916
917        return _safe_wrap_test_case
918