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