1#!/usr/bin/env python3.4
2#
3# Copyright (C) 2018 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may not
6# use this file except in compliance with the License. You may obtain a copy of
7# 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, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations under
15# the License.
16#
17"""
18This test exercises basic scanning functionality to confirm expected behavior
19related to wlan scanning
20"""
21
22from datetime import datetime
23
24import pprint
25import time
26
27import acts.base_test
28import acts_contrib.test_utils.wifi.wifi_test_utils as wutils
29
30from acts import signals
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
35from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
36
37
38class WlanScanTest(WifiBaseTest):
39    """WLAN scan test class.
40
41    Test Bed Requirement:
42    * One or more Fuchsia devices
43    * Several Wi-Fi networks visible to the device, including an open Wi-Fi
44      network or a onHub/GoogleWifi
45    """
46    def setup_class(self):
47        super().setup_class()
48
49        self.start_access_point = False
50        for fd in self.fuchsia_devices:
51            fd.configure_wlan(association_mechanism='drivers')
52        if "AccessPoint" in self.user_params:
53            # This section sets up the config that could be sent to the AP if
54            # the AP is needed. The reasoning is since ACTS already connects
55            # to the AP if it is in the config, generating the config in memory
56            # has no over head is used if need by the test if one of the ssids
57            # needed for the test is not included in the config.  The logic
58            # here creates 2 ssids on each radio, 5ghz and 2.4ghz, with an
59            # open, no security network and one that is wpa2, for a total of 4
60            # networks.  However, if all of the ssids are specified in the
61            # the config will never be written to the AP and the AP will not be
62            # brought up.  For more information about how to configure the
63            # hostapd config info, see the hostapd libraries, which have more
64            # documentation.
65            bss_settings_2g = []
66            bss_settings_5g = []
67            open_network = self.get_open_network(False, [])
68            self.open_network_2g = open_network['2g']
69            self.open_network_5g = open_network['5g']
70            wpa2_settings = self.get_psk_network(False, [])
71            self.wpa2_network_2g = wpa2_settings['2g']
72            self.wpa2_network_5g = wpa2_settings['5g']
73            bss_settings_2g.append(
74                hostapd_bss_settings.BssSettings(
75                    name=self.wpa2_network_2g['SSID'],
76                    ssid=self.wpa2_network_2g['SSID'],
77                    security=hostapd_security.Security(
78                        security_mode=self.wpa2_network_2g["security"],
79                        password=self.wpa2_network_2g["password"])))
80            bss_settings_5g.append(
81                hostapd_bss_settings.BssSettings(
82                    name=self.wpa2_network_5g['SSID'],
83                    ssid=self.wpa2_network_5g['SSID'],
84                    security=hostapd_security.Security(
85                        security_mode=self.wpa2_network_5g["security"],
86                        password=self.wpa2_network_5g["password"])))
87            self.ap_2g = hostapd_ap_preset.create_ap_preset(
88                iface_wlan_2g=self.access_points[0].wlan_2g,
89                iface_wlan_5g=self.access_points[0].wlan_5g,
90                channel=hostapd_constants.AP_DEFAULT_CHANNEL_2G,
91                ssid=self.open_network_2g['SSID'],
92                bss_settings=bss_settings_2g)
93            self.ap_5g = hostapd_ap_preset.create_ap_preset(
94                iface_wlan_2g=self.access_points[0].wlan_2g,
95                iface_wlan_5g=self.access_points[0].wlan_5g,
96                channel=hostapd_constants.AP_DEFAULT_CHANNEL_5G,
97                ssid=self.open_network_5g['SSID'],
98                bss_settings=bss_settings_5g)
99
100        if "wlan_open_network_2g" in self.user_params:
101            self.open_network_2g = self.user_params.get("wlan_open_network_2g")
102        elif "AccessPoint" in self.user_params:
103            self.start_access_point_2g = True
104        else:
105            raise Exception('Missing parameter in config '
106                            '(wlan_open_network_2g)')
107
108        if "wlan_open_network_5g" in self.user_params:
109            self.open_network_5g = self.user_params.get("wlan_open_network_5g")
110        elif "AccessPoint" in self.user_params:
111            self.start_access_point_5g = True
112        else:
113            raise Exception('Missing parameter in config '
114                            '(wlan_open_network_5g)')
115
116        if "wlan_wpa2_network_2g" in self.user_params:
117            self.wpa2_network_2g = self.user_params.get("wlan_wpa2_network_2g")
118        elif "AccessPoint" in self.user_params:
119            self.start_access_point_2g = True
120        else:
121            raise Exception('Missing parameter in config '
122                            '(wlan_wpa2_network_2g)')
123
124        if "wlan_wpa2_network_5g" in self.user_params:
125            self.wpa2_network_5g = self.user_params.get("wlan_wpa2_network_5g")
126        elif "AccessPoint" in self.user_params:
127            self.start_access_point_5g = True
128        else:
129            raise Exception('Missing parameter in config '
130                            '(wlan_wpa2_network_5g)')
131
132        # Only bring up the APs that are needed for the test.  Each ssid is
133        # randomly generated so there is no chance of re associating to a
134        # previously saved ssid on the device.
135        if self.start_access_point_2g:
136            self.start_access_point = True
137            self.access_points[0].start_ap(hostapd_config=self.ap_2g)
138        if self.start_access_point_5g:
139            self.start_access_point = True
140            self.access_points[0].start_ap(hostapd_config=self.ap_5g)
141
142    def setup_test(self):
143        for fd in self.fuchsia_devices:
144            # stub for setting up all the fuchsia devices in the testbed.
145            pass
146
147    def teardown_test(self):
148        for fd in self.fuchsia_devices:
149            fd.wlan_lib.wlanDisconnect()
150
151    def teardown_class(self):
152        if self.start_access_point:
153            self.access_points[0].stop_all_aps()
154
155    """Helper Functions"""
156
157    def check_connect_response(self, connection_response):
158        """ Checks the result of connecting to a wlan.
159            Args:
160                connection_response: The response from SL4F after attempting
161                    to connect to a wlan.
162        """
163        if connection_response.get("error") is None:
164            # the command did not get an error response - go ahead and
165            # check the result
166            connection_result = connection_response.get("result")
167            if connection_result:
168                self.log.info("connection to network successful")
169            else:
170                # ideally, we would have the actual error...  but logging
171                # here to cover that error case
172                raise signals.TestFailure("Connect call failed, aborting test")
173        else:
174            # the response indicates an error - log and raise failure
175            raise signals.TestFailure("Aborting test - Connect call failed "
176                                      "with error: %s" %
177                                      connection_response.get("error"))
178
179    def scan_while_connected(self, wlan_network_params, fd):
180        """ Connects to as specified network and initiates a scan
181                Args:
182                    wlan_network_params: A dictionary containing wlan
183                        infomation.
184                    fd: The fuchsia device to connect to the wlan.
185        """
186        target_ssid = wlan_network_params['SSID']
187        self.log.info("got the ssid! %s", target_ssid)
188        target_pwd = None
189        if 'password' in wlan_network_params:
190            target_pwd = wlan_network_params['password']
191
192        bss_scan_response = fd.wlan_lib.wlanScanForBSSInfo().get('result')
193        connection_response = fd.wlan_lib.wlanConnectToNetwork(
194            target_ssid,
195            bss_scan_response[target_ssid][0],
196            target_pwd=target_pwd)
197        self.check_connect_response(connection_response)
198        self.basic_scan_request(fd)
199
200    def basic_scan_request(self, fd):
201        """ Initiates a basic scan on a Fuchsia device
202            Args:
203                fd: A fuchsia device
204        """
205        start_time = datetime.now()
206
207        scan_response = fd.wlan_lib.wlanStartScan()
208
209        # first check if we received an error
210        if scan_response.get("error") is None:
211            # the scan command did not get an error response - go ahead
212            # and check for scan results
213            scan_results = scan_response["result"]
214        else:
215            # the response indicates an error - log and raise failure
216            raise signals.TestFailure("Aborting test - scan failed with "
217                                      "error: %s" % scan_response.get("error"))
218
219        self.log.info("scan contained %d results", len(scan_results))
220
221        total_time_ms = (datetime.now() - start_time).total_seconds() * 1000
222        self.log.info("scan time: %d ms", total_time_ms)
223
224        if len(scan_results) > 0:
225            raise signals.TestPass(details="",
226                                   extras={"Scan time": "%d" % total_time_ms})
227        else:
228            raise signals.TestFailure("Scan failed or did not "
229                                      "find any networks")
230
231    """Tests"""
232
233    def test_basic_scan_request(self):
234        """Verify a general scan trigger returns at least one result"""
235        for fd in self.fuchsia_devices:
236            self.basic_scan_request(fd)
237
238    def test_scan_while_connected_open_network_2g(self):
239        for fd in self.fuchsia_devices:
240            self.scan_while_connected(self.open_network_2g, fd)
241
242    def test_scan_while_connected_wpa2_network_2g(self):
243        for fd in self.fuchsia_devices:
244            self.scan_while_connected(self.wpa2_network_2g, fd)
245
246    def test_scan_while_connected_open_network_5g(self):
247        for fd in self.fuchsia_devices:
248            self.scan_while_connected(self.open_network_5g, fd)
249
250    def test_scan_while_connected_wpa2_network_5g(self):
251        for fd in self.fuchsia_devices:
252            self.scan_while_connected(self.wpa2_network_5g, fd)
253