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