1#!/usr/bin/env python3.4
2#
3#   Copyright 2016 - The Android Open Source Project
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
17import logging
18import time
19
20import acts.signals as signals
21
22from acts import asserts
23from acts import base_test
24from acts.controllers import android_device
25from acts.controllers import attenuator
26from acts.test_decorators import test_tracker_info
27from acts.test_utils.wifi import wifi_test_utils as wutils
28from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
29
30WifiEnums = wutils.WifiEnums
31
32AP_1 = 0
33AP_2 = 1
34AP_1_2G_ATTENUATOR = 0
35AP_1_5G_ATTENUATOR = 1
36AP_2_2G_ATTENUATOR = 2
37AP_2_5G_ATTENUATOR = 3
38ATTENUATOR_INITIAL_SETTING = 60
39# WifiNetworkSelector imposes a 10 seconds gap between two selections
40NETWORK_SELECTION_TIME_GAP = 12
41
42
43class WifiNetworkSelectorTest(WifiBaseTest):
44    """These tests verify the behavior of the Android Wi-Fi Network Selector
45    feature.
46    """
47
48    def __init__(self, controllers):
49        WifiBaseTest.__init__(self, controllers)
50
51    def setup_class(self):
52        self.dut = self.android_devices[0]
53        wutils.wifi_test_device_init(self.dut)
54        req_params = []
55        opt_param = ["open_network", "reference_networks"]
56        self.unpack_userparams(
57            req_param_names=req_params, opt_param_names=opt_param)
58
59        if "AccessPoint" in self.user_params:
60            self.legacy_configure_ap_and_start(ap_count=2)
61
62    def setup_test(self):
63        #reset and clear all saved networks on the DUT
64        wutils.reset_wifi(self.dut)
65        #move the APs out of range
66        for attenuator in self.attenuators:
67            attenuator.set_atten(ATTENUATOR_INITIAL_SETTING)
68        #turn on the screen
69        self.dut.droid.wakeLockAcquireBright()
70        self.dut.droid.wakeUpNow()
71        self.dut.ed.clear_all_events()
72
73    def teardown_test(self):
74        #turn off the screen
75        self.dut.droid.wakeLockRelease()
76        self.dut.droid.goToSleepNow()
77
78    def on_fail(self, test_name, begin_time):
79        self.dut.take_bug_report(test_name, begin_time)
80        self.dut.cat_adb_log(test_name, begin_time)
81
82    def teardown_class(self):
83        if "AccessPoint" in self.user_params:
84            del self.user_params["reference_networks"]
85            del self.user_params["open_network"]
86
87    """ Helper Functions """
88
89    def add_networks(self, ad, networks):
90        """Add Wi-Fi networks to an Android device and verify the networks were
91        added correctly.
92
93        Args:
94            ad: the AndroidDevice object to add networks to.
95            networks: a list of dicts, each dict represents a Wi-Fi network.
96        """
97        for network in networks:
98            ret = ad.droid.wifiAddNetwork(network)
99            asserts.assert_true(ret != -1, "Failed to add network %s" %
100                                network)
101            ad.droid.wifiEnableNetwork(ret, 0)
102        configured_networks = ad.droid.wifiGetConfiguredNetworks()
103        logging.debug("Configured networks: %s", configured_networks)
104
105    def connect_and_verify_connected_bssid(self, expected_bssid):
106        """Start a scan to get the DUT connected to an AP and verify the DUT
107        is connected to the correct BSSID.
108
109        Args:
110            expected_bssid: Network bssid to which connection.
111
112        Returns:
113            True if connection to given network happen, else return False.
114        """
115        #wait for the attenuator to stablize
116        time.sleep(10)
117        #force start a single scan so we don't have to wait for the
118        #WCM scheduled scan.
119        wutils.start_wifi_connection_scan(self.dut)
120        #wait for connection
121        time.sleep(20)
122        #verify connection
123        actual_network = self.dut.droid.wifiGetConnectionInfo()
124        logging.info("Actual network: %s", actual_network)
125        try:
126            asserts.assert_equal(expected_bssid,
127                                 actual_network[WifiEnums.BSSID_KEY])
128        except:
129           msg = "Device did not connect to any network."
130           raise signals.TestFailure(msg)
131
132    """ Tests Begin """
133
134    @test_tracker_info(uuid="ffa5e278-db3f-4e17-af11-6c7a3e7c5cc2")
135    def test_network_selector_automatic_connection(self):
136        """
137            1. Add one saved network to DUT.
138            2. Move the DUT in range.
139            3. Verify the DUT is connected to the network.
140        """
141        #add a saved network to DUT
142        networks = [self.reference_networks[AP_1]['5g']]
143        self.add_networks(self.dut, networks)
144        #move the DUT in range
145        self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
146        #verify
147        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
148            '5g']['bssid'])
149
150    @test_tracker_info(uuid="3ea818f2-10d7-4aad-bfab-7d8fb25aae78")
151    def test_network_selector_basic_connection_prefer_5g(self):
152        """
153            1. Add one saved SSID with 2G and 5G BSSIDs of similar RSSI.
154            2. Move the DUT in range.
155            3. Verify the DUT is connected to the 5G BSSID.
156        """
157        #add a saved network with both 2G and 5G BSSIDs to DUT
158        # TODO: bmahadev Change this to a single SSID once we migrate tests to
159        # use dynamic AP.
160        networks = [self.reference_networks[AP_1]['2g'],
161                    self.reference_networks[AP_1]['5g']]
162        self.add_networks(self.dut, networks)
163        #move the DUT in range
164        self.attenuators[AP_1_2G_ATTENUATOR].set_atten(0)
165        self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
166        #verify
167        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
168            '5g']['bssid'])
169
170    @test_tracker_info(uuid="bebb29ca-4486-4cde-b390-c5f8f2e1580c")
171    def test_network_selector_prefer_stronger_rssi(self):
172        """
173            1. Add two saved SSIDs to DUT, same band, one has stronger RSSI
174               than the other.
175            2. Move the DUT in range.
176            3. Verify the DUT is connected to the SSID with stronger RSSI.
177        """
178        #add a 2G and a 5G saved network to DUT
179        networks = [
180            self.reference_networks[AP_1]['2g'], self.reference_networks[AP_2][
181                '2g']
182        ]
183        self.add_networks(self.dut, networks)
184        #move the DUT in range
185        self.attenuators[AP_1_2G_ATTENUATOR].set_atten(20)
186        self.attenuators[AP_2_2G_ATTENUATOR].set_atten(40)
187        #verify
188        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
189            '2g']['bssid'])
190
191    @test_tracker_info(uuid="f9f72dc5-034f-4fe2-a27d-df1b6cae76cd")
192    def test_network_selector_prefer_secure_over_open_network(self):
193        """
194            1. Add two saved networks to DUT, same band, similar RSSI, one uses
195               WPA2 security, the other is open.
196            2. Move the DUT in range.
197            3. Verify the DUT is connected to the secure network that uses WPA2.
198        """
199        #add a open network and a secure saved network to DUT
200        networks = [
201            self.open_network[AP_1]['5g'], self.reference_networks[AP_1]['5g']
202        ]
203        self.add_networks(self.dut, networks)
204        #move the DUT in range
205        #TODO: control open network attenuator
206        self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
207        #verify
208        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
209            '5g']['bssid'])
210
211    @test_tracker_info(uuid="ab2c527c-0f9c-4f09-a13f-e3f461b7da52")
212    def test_network_selector_blacklist_by_connection_failure(self):
213        """
214            1. Add two saved secured networks X and Y to DUT. X has stronger
215               RSSI than Y. X has wrong password configured.
216            2. Move the DUT in range.
217            3. Verify the DUT is connected to network Y.
218        """
219        #add two saved networks to DUT, and one of them is configured with incorrect password
220        wrong_passwd_network = self.reference_networks[AP_1]['5g'].copy()
221        wrong_passwd_network['password'] += 'haha'
222        networks = [wrong_passwd_network, self.reference_networks[AP_2]['5g']]
223        self.add_networks(self.dut, networks)
224        #make both AP_1 5G and AP_2 5G in range, and AP_1 5G has stronger RSSI than AP_2 5G
225        self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
226        self.attenuators[AP_2_5G_ATTENUATOR].set_atten(10)
227        #start 3 scans to get AP_1 5G blacklisted because of the incorrect password
228        count = 0
229        while count < 3:
230            wutils.start_wifi_connection_scan(self.dut)
231            time.sleep(NETWORK_SELECTION_TIME_GAP)
232            count += 1
233        #verify
234        self.connect_and_verify_connected_bssid(self.reference_networks[AP_2][
235            '5g']['bssid'])
236
237    @test_tracker_info(uuid="71d88fcf-c7b8-4fd2-a7cb-84ac4a130ecf")
238    def test_network_selector_2g_to_5g_prefer_same_SSID(self):
239        """
240            1. Add SSID_A and SSID_B to DUT. Both SSIDs have both 2G and 5G
241               BSSIDs.
242            2. Attenuate the networks so that the DUT is connected to SSID_A's
243               2G in the beginning.
244            3. Increase the RSSI of both SSID_A's 5G and SSID_B's 5G.
245            4. Verify the DUT switches to SSID_A's 5G.
246        """
247        #add two saved networks to DUT
248        networks = [
249            self.reference_networks[AP_1]['2g'], self.reference_networks[AP_2][
250                '2g']
251        ]
252        self.add_networks(self.dut, networks)
253        #make AP_1 2G in range
254        self.attenuators[AP_1_2G_ATTENUATOR].set_atten(0)
255        #verify
256        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
257            '2g']['bssid'])
258        #make both AP_1 and AP_2 5G in range with similar RSSI
259        self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
260        self.attenuators[AP_2_5G_ATTENUATOR].set_atten(0)
261        #ensure the time gap between two network selections
262        time.sleep(NETWORK_SELECTION_TIME_GAP)
263        #verify
264        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
265            '5g']['bssid'])
266
267    @test_tracker_info(uuid="c1243cf4-d96e-427e-869e-3d640bee3f28")
268    def test_network_selector_2g_to_5g_different_ssid(self):
269        """
270            1. Add SSID_A and SSID_B to DUT. Both SSIDs have both 2G and 5G
271               BSSIDs.
272            2. Attenuate the networks so that the DUT is connected to SSID_A's
273               2G in the beginning.
274            3. Increase the RSSI of SSID_B's 5G while attenuate down SSID_A's
275               2G RSSI.
276            4. Verify the DUT switches to SSID_B's 5G.
277        """
278        #add two saved networks to DUT
279        networks = [
280            self.reference_networks[AP_1]['2g'], self.reference_networks[AP_2][
281                '2g']
282        ]
283        self.add_networks(self.dut, networks)
284        #make both AP_1 2G and AP_2 5G in range, and AP_1 2G
285        #has much stronger RSSI than AP_2 5G
286        self.attenuators[AP_1_2G_ATTENUATOR].set_atten(0)
287        self.attenuators[AP_2_5G_ATTENUATOR].set_atten(20)
288        #verify
289        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
290            '2g']['bssid'])
291        #bump up AP_2 5G RSSI and reduce AP_1 2G RSSI
292        self.attenuators[AP_1_2G_ATTENUATOR].set_atten(40)
293        self.attenuators[AP_2_5G_ATTENUATOR].set_atten(0)
294        #ensure the time gap between two network selections
295        time.sleep(NETWORK_SELECTION_TIME_GAP)
296        #verify
297        self.connect_and_verify_connected_bssid(self.reference_networks[AP_2][
298            '5g']['bssid'])
299
300    @test_tracker_info(uuid="10da95df-83ed-4447-89f8-735b08dbe2eb")
301    def test_network_selector_5g_to_2g_same_ssid(self):
302        """
303            1. Add one SSID that has both 2G and 5G to the DUT.
304            2. Attenuate down the 2G RSSI.
305            3. Connect the DUT to the 5G BSSID.
306            4. Bring up the 2G RSSI and attenuate down the 5G RSSI.
307            5. Verify the DUT switches to the 2G BSSID.
308        """
309        #add a saved network to DUT
310        networks = [self.reference_networks[AP_1]['2g']]
311        self.add_networks(self.dut, networks)
312        #make both AP_1 2G and AP_2 5G in range, and AP_1 5G
313        #has much stronger RSSI than AP_2 2G
314        self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
315        self.attenuators[AP_1_2G_ATTENUATOR].set_atten(50)
316        #verify
317        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
318            '5g']['bssid'])
319        #bump up AP_1 2G RSSI and reduce AP_1 5G RSSI
320        self.attenuators[AP_1_2G_ATTENUATOR].set_atten(0)
321        self.attenuators[AP_1_5G_ATTENUATOR].set_atten(30)
322        #ensure the time gap between two network selections
323        time.sleep(NETWORK_SELECTION_TIME_GAP)
324        #verify
325        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
326            '2g']['bssid'])
327
328    @test_tracker_info(uuid="ead78ae0-27ab-4bb8-ae77-0b9fe588436a")
329    def test_network_selector_stay_on_sufficient_network(self):
330        """
331            1. Add two 5G WPA2 BSSIDs X and Y to the DUT. X has higher RSSI
332               than Y.
333            2. Connect the DUT to X.
334            3. Change attenuation so that Y's RSSI goes above X's.
335            4. Verify the DUT stays on X.
336        """
337        #add two saved networks to DUT
338        networks = [
339            self.reference_networks[AP_1]['5g'], self.reference_networks[AP_2][
340                '5g']
341        ]
342        self.add_networks(self.dut, networks)
343        #make both AP_1 5G and AP_2 5G in range, and AP_1 5G
344        #has stronger RSSI than AP_2 5G
345        self.attenuators[AP_1_5G_ATTENUATOR].set_atten(10)
346        self.attenuators[AP_2_5G_ATTENUATOR].set_atten(20)
347        #verify
348        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
349            '5g']['bssid'])
350        #bump up AP_2 5G RSSI over AP_1 5G RSSI
351        self.attenuators[AP_2_5G_ATTENUATOR].set_atten(0)
352        #ensure the time gap between two network selections
353        time.sleep(NETWORK_SELECTION_TIME_GAP)
354        #verify
355        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
356            '5g']['bssid'])
357
358    @test_tracker_info(uuid="5470010f-8b62-4b1c-8b83-1f91422eced0")
359    def test_network_selector_stay_on_user_selected_network(self):
360        """
361            1. Connect the DUT to SSID_A with a very low RSSI via the user select code path.
362            2. Add SSID_B to the DUT as saved network. SSID_B has higher RSSI than SSID_A.
363            3. Start a scan and network selection.
364            4. Verify DUT stays on SSID_A.
365        """
366        #make AP_1 5G in range with a low RSSI
367        self.attenuators[AP_1_5G_ATTENUATOR].set_atten(10)
368        #connect to AP_1 via user selection
369        wutils.wifi_connect(self.dut, self.reference_networks[AP_1]['5g'])
370        #verify
371        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
372            '5g']['bssid'])
373        #make AP_2 5G in range with a strong RSSI
374        self.attenuators[AP_2_5G_ATTENUATOR].set_atten(0)
375        #add AP_2 as a saved network to DUT
376        networks = [self.reference_networks[AP_2]['5g']]
377        self.add_networks(self.dut, networks)
378        #ensure the time gap between two network selections
379        time.sleep(NETWORK_SELECTION_TIME_GAP)
380        #verify we are still connected to AP_1 5G
381        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
382            '5g']['bssid'])
383
384    @test_tracker_info(uuid="f08d8f73-8c94-42af-bba9-4c49bbf16420")
385    def test_network_selector_reselect_after_forget_network(self):
386        """
387            1. Add two 5G BSSIDs X and Y to the DUT. X has higher RSSI
388               than Y.
389            2. Connect the DUT to X.
390            3. Forget X.
391            5. Verify the DUT reselect and connect to Y.
392        """
393        #add two saved networks to DUT
394        networks = [
395            self.reference_networks[AP_1]['5g'], self.reference_networks[AP_2][
396                '5g']
397        ]
398        self.add_networks(self.dut, networks)
399        #make both AP_1 5G and AP_2 5G in range. AP_1 5G has stronger
400        #RSSI than AP_2 5G
401        self.attenuators[AP_1_5G_ATTENUATOR].set_atten(0)
402        self.attenuators[AP_2_5G_ATTENUATOR].set_atten(10)
403        #verify
404        self.connect_and_verify_connected_bssid(self.reference_networks[AP_1][
405            '5g']['bssid'])
406        #forget AP_1
407        wutils.wifi_forget_network(self.dut,
408                                   self.reference_networks[AP_1]['5g']['SSID'])
409        #verify
410        self.connect_and_verify_connected_bssid(self.reference_networks[AP_2][
411            '5g']['bssid'])
412