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