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