1#!/usr/bin/env python3.4
2#
3# Copyright (C) 2020 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"""This test exercises the Scan functionality for the WLAN Policy API."""
18
19from datetime import datetime
20
21from acts import signals
22from acts.controllers.ap_lib import (hostapd_ap_preset, hostapd_bss_settings,
23                                     hostapd_constants, hostapd_security)
24from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest
25
26
27class PolicyScanTest(WifiBaseTest):
28    """WLAN policy scan test class.
29
30    Test Bed Requirement:
31    * One or more Fuchsia devices
32    * One Whirlwind Access Point
33    """
34
35    def setup_class(self):
36        super().setup_class()
37        if len(self.fuchsia_devices) < 1:
38            raise signals.TestFailure("No fuchsia devices found.")
39        for fd in self.fuchsia_devices:
40            fd.configure_wlan(association_mechanism='policy',
41                              preserve_saved_networks=True)
42        if len(self.access_points) < 1:
43            raise signals.TestFailure("No access points found.")
44        # Prepare the AP
45        self.access_point = self.access_points[0]
46        self.access_point.stop_all_aps()
47        # Generate network params.
48        bss_settings_2g = []
49        bss_settings_5g = []
50        open_network = self.get_open_network(False, [])
51        self.open_network_2g = open_network["2g"]
52        self.open_network_5g = open_network["5g"]
53        wpa2_settings = self.get_psk_network(False, [])
54        self.wpa2_network_2g = wpa2_settings["2g"]
55        self.wpa2_network_5g = wpa2_settings["5g"]
56        bss_settings_2g.append(
57            hostapd_bss_settings.BssSettings(
58                name=self.wpa2_network_2g["SSID"],
59                ssid=self.wpa2_network_2g["SSID"],
60                security=hostapd_security.Security(
61                    security_mode=self.wpa2_network_2g["security"],
62                    password=self.wpa2_network_2g["password"])))
63        bss_settings_5g.append(
64            hostapd_bss_settings.BssSettings(
65                name=self.wpa2_network_5g["SSID"],
66                ssid=self.wpa2_network_5g["SSID"],
67                security=hostapd_security.Security(
68                    security_mode=self.wpa2_network_5g["security"],
69                    password=self.wpa2_network_5g["password"])))
70        self.ap_2g = hostapd_ap_preset.create_ap_preset(
71            iface_wlan_2g=self.access_points[0].wlan_2g,
72            iface_wlan_5g=self.access_points[0].wlan_5g,
73            channel=hostapd_constants.AP_DEFAULT_CHANNEL_2G,
74            ssid=self.open_network_2g["SSID"],
75            bss_settings=bss_settings_2g)
76        self.ap_5g = hostapd_ap_preset.create_ap_preset(
77            iface_wlan_2g=self.access_points[0].wlan_2g,
78            iface_wlan_5g=self.access_points[0].wlan_5g,
79            channel=hostapd_constants.AP_DEFAULT_CHANNEL_5G,
80            ssid=self.open_network_5g["SSID"],
81            bss_settings=bss_settings_5g)
82        # Start the networks
83        self.access_point.start_ap(hostapd_config=self.ap_2g)
84        self.access_point.start_ap(hostapd_config=self.ap_5g)
85        # Save the SSIDs
86        self.all_ssids = [
87            self.open_network_2g["SSID"],
88            self.wpa2_network_2g["SSID"],
89            self.open_network_5g["SSID"],
90            self.wpa2_network_5g["SSID"],
91        ]
92
93    def setup_test(self):
94        for fd in self.fuchsia_devices:
95            # stub for setting up all the fuchsia devices in the testbed.
96            return fd.wlan_policy_controller.remove_all_networks_and_wait_for_no_connections(
97            )
98
99    def teardown_test(self):
100        for fd in self.fuchsia_devices:
101            # stub until policy layer has something useful to use here.
102            pass
103
104    def teardown_class(self):
105        pass
106
107    def on_fail(self, test_name, begin_time):
108        for fd in self.fuchsia_devices:
109            try:
110                fd.take_bug_report(test_name, begin_time)
111                fd.get_log(test_name, begin_time)
112            except Exception:
113                pass
114
115            try:
116                if fd.device.hard_reboot_on_fail:
117                    fd.hard_power_cycle(self.pdu_devices)
118            except AttributeError:
119                pass
120
121    """Helper Functions"""
122
123    def perform_scan(self, fd):
124        """ Initiates scan on a Fuchsia device and returns results
125
126        Args:
127            fd: A fuchsia device
128
129        Raises:
130            signals.TestFailure: if an error is reported by the device during
131            the scan
132
133        Returns:
134            A list of scan results
135        """
136        start_time = datetime.now()
137
138        scan_response = fd.sl4f.wlan_policy_lib.wlanScanForNetworks()
139
140        # first check if we received an error
141        if scan_response.get("error") is not None:
142            # the response indicates an error - log and raise failure
143            raise signals.TestFailure("Aborting test - scan failed with "
144                                      "error: %s" % scan_response.get("error"))
145
146        # the scan command did not get an error response - go ahead
147        # and check for scan results
148        scan_results = scan_response["result"]
149        total_time_ms = (datetime.now() - start_time).total_seconds() * 1000
150
151        self.log.info("scan contained %d results", len(scan_results))
152        self.log.info("scan time: %d ms", total_time_ms)
153
154        return scan_results
155
156    def connect_to_network(self, wlan_network_params, fd):
157        """ Connects the Fuchsia device to the specified network
158
159        Args:
160            wlan_network_params: A dictionary containing wlan information.
161            fd: A fuchsia device
162
163        Raises:
164            signals.TestFailure: if the device fails to connect
165        """
166        target_ssid = wlan_network_params["SSID"]
167        target_pwd = wlan_network_params.get("password")
168        target_security = wlan_network_params.get("security")
169
170        # TODO(mnck): use the Policy version of this call, when it is available.
171        connection_response = fd.wlan_policy_controller.save_and_connect(
172            target_ssid, target_security, password=target_pwd)
173        if not connection_response:
174            raise signals.TestFailure("Aborting test - Connect call failed")
175        self.log.info("Network connection successful.")
176
177    def assert_network_is_in_results(self, scan_results, *, ssid):
178        """ Verified scan results contain a specified network
179
180        Args:
181            scan_results: Scan results from a fuchsia Policy API scan
182            ssid: SSID for network that should be in the results
183
184        Raises:
185            signals.TestFailure: if the network is not present in the scan
186            results
187        """
188        if ssid not in scan_results:
189            raise signals.TestFailure(
190                'Network "%s" was not found in scan results: %s', ssid,
191                scan_results)
192
193    """Tests"""
194
195    def test_basic_scan_request(self):
196        """Verify a scan returns all expected networks"""
197        for fd in self.fuchsia_devices:
198            scan_results = self.perform_scan(fd)
199            if len(scan_results) == 0:
200                raise signals.TestFailure("Scan failed or did not "
201                                          "find any networks")
202            for ssid in self.all_ssids:
203                self.assert_network_is_in_results(scan_results, ssid=ssid)
204
205    def test_scan_while_connected_open_network_2g(self):
206        """Connect to an open 2g network and perform a scan"""
207        for fd in self.fuchsia_devices:
208            self.connect_to_network(self.open_network_2g, fd)
209            scan_results = self.perform_scan(fd)
210            for ssid in self.all_ssids:
211                self.assert_network_is_in_results(scan_results, ssid=ssid)
212
213    def test_scan_while_connected_wpa2_network_2g(self):
214        """Connect to a WPA2 2g network and perform a scan"""
215        for fd in self.fuchsia_devices:
216            self.connect_to_network(self.wpa2_network_2g, fd)
217            scan_results = self.perform_scan(fd)
218            for ssid in self.all_ssids:
219                self.assert_network_is_in_results(scan_results, ssid=ssid)
220
221    def test_scan_while_connected_open_network_5g(self):
222        """Connect to an open 5g network and perform a scan"""
223        for fd in self.fuchsia_devices:
224            self.connect_to_network(self.open_network_5g, fd)
225            scan_results = self.perform_scan(fd)
226            for ssid in self.all_ssids:
227                self.assert_network_is_in_results(scan_results, ssid=ssid)
228
229    def test_scan_while_connected_wpa2_network_5g(self):
230        """Connect to a WPA2 5g network and perform a scan"""
231        for fd in self.fuchsia_devices:
232            self.connect_to_network(self.wpa2_network_5g, fd)
233            scan_results = self.perform_scan(fd)
234            for ssid in self.all_ssids:
235                self.assert_network_is_in_results(scan_results, ssid=ssid)
236