1#   !/usr/bin/env python3.4
2#
3#   Copyright 2017 - 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 re
18from acts import asserts
19from acts.controllers.android_device import SL4A_APK_NAME
20from acts.libs.ota import ota_updater
21import acts.signals as signals
22from acts.test_decorators import test_tracker_info
23from acts.test_utils.tel.tel_test_utils import WIFI_CONFIG_APBAND_5G
24import acts.test_utils.wifi.wifi_test_utils as wutils
25from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest
26import acts.utils as utils
27
28WifiEnums = wutils.WifiEnums
29SSID = WifiEnums.SSID_KEY
30PWD = WifiEnums.PWD_KEY
31NETID = WifiEnums.NETID_KEY
32# Default timeout used for reboot, toggle WiFi and Airplane mode,
33# for the system to settle down after the operation.
34DEFAULT_TIMEOUT = 10
35BAND_2GHZ = 0
36BAND_5GHZ = 1
37
38
39class WifiAutoUpdateTest(WifiBaseTest):
40    """Tests for APIs in Android's WifiManager class.
41
42    Test Bed Requirement:
43    * One Android device
44    * Several Wi-Fi networks visible to the device, including an open Wi-Fi
45      network.
46    """
47
48    def __init__(self, controllers):
49        WifiBaseTest.__init__(self, controllers)
50        self.tests = (
51            "test_check_wifi_state_after_au",
52            "test_verify_networks_after_au",
53            "test_configstore_after_au",
54            "test_mac_randomization_after_au",
55            "test_wifi_hotspot_5g_psk_after_au",
56            "test_all_networks_connectable_after_au",
57            "test_connect_to_network_suggestion_after_au",
58            "test_check_wifi_toggling_after_au",
59            "test_connection_to_new_networks",
60            "test_reset_wifi_after_au")
61
62    def setup_class(self):
63        super(WifiAutoUpdateTest, self).setup_class()
64        ota_updater.initialize(self.user_params, self.android_devices)
65        self.dut = self.android_devices[0]
66        self.dut_client = self.android_devices[1]
67        wutils.wifi_test_device_init(self.dut)
68        wutils.wifi_toggle_state(self.dut, True)
69
70        # configure APs
71        self.legacy_configure_ap_and_start(wpa_network=True)
72        self.wpapsk_2g = self.reference_networks[0]["2g"]
73        self.wpapsk_5g = self.reference_networks[0]["5g"]
74        self.open_2g = self.open_network[0]["2g"]
75        self.open_5g = self.open_network[0]["5g"]
76
77        # saved & connected networks, network suggestions
78        # and new networks
79        self.saved_networks = [self.open_2g]
80        self.network_suggestions = [self.wpapsk_5g]
81        self.connected_networks = [self.wpapsk_2g, self.open_5g]
82        self.new_networks = [self.reference_networks[1]["2g"],
83                             self.open_network[1]["5g"]]
84
85        # add pre ota upgrade configuration
86        self.wifi_config_list = []
87        self.pre_default_mac = {}
88        self.pre_random_mac = {}
89        self.pst_default_mac = {}
90        self.pst_random_mac = {}
91        self.add_pre_update_configuration()
92
93        # Run OTA below, if ota fails then abort all tests.
94        try:
95            ota_updater.update(self.dut)
96        except Exception as e:
97            raise signals.TestAbortClass(
98                "Failed up apply OTA update. Aborting tests: %s" % e)
99
100    def setup_test(self):
101        self.dut.droid.wakeLockAcquireBright()
102        self.dut.droid.wakeUpNow()
103
104    def teardown_test(self):
105        self.dut.droid.wakeLockRelease()
106        self.dut.droid.goToSleepNow()
107
108    def on_fail(self, test_name, begin_time):
109        self.dut.take_bug_report(test_name, begin_time)
110        self.dut.cat_adb_log(test_name, begin_time)
111
112    def teardown_class(self):
113        if "AccessPoint" in self.user_params:
114            del self.user_params["reference_networks"]
115            del self.user_params["open_network"]
116
117    ### Helper Methods
118
119    def add_pre_update_configuration(self):
120        self.add_network_suggestions(self.network_suggestions)
121        self.add_network_and_enable(self.saved_networks[0])
122        self.add_wifi_hotspot()
123        self.connect_to_multiple_networks(self.connected_networks)
124
125    def add_wifi_hotspot(self):
126        self.wifi_hotspot = {"SSID": "hotspot_%s" % utils.rand_ascii_str(6),
127                             "password": "pass_%s" % utils.rand_ascii_str(6)}
128        band = WIFI_CONFIG_APBAND_5G
129        if self.dut.build_info["build_id"].startswith("Q"):
130            band = WifiEnums.WIFI_CONFIG_APBAND_5G_OLD
131            self.wifi_hotspot[WifiEnums.AP_BAND_KEY] = band
132            asserts.assert_true(
133                self.dut.droid.wifiSetWifiApConfiguration(self.wifi_hotspot),
134                "Failed to set WifiAp Configuration")
135            wifi_ap = self.dut.droid.wifiGetApConfiguration()
136            asserts.assert_true(
137                wifi_ap[WifiEnums.SSID_KEY] == self.wifi_hotspot[WifiEnums.SSID_KEY],
138                "Hotspot SSID doesn't match with expected SSID")
139            return
140        wutils.save_wifi_soft_ap_config(self.dut, self.wifi_hotspot, band)
141
142    def verify_wifi_hotspot(self):
143        """Verify wifi tethering."""
144        wutils.start_wifi_tethering_saved_config(self.dut)
145        wutils.connect_to_wifi_network(self.dut_client,
146                                       self.wifi_hotspot,
147                                       check_connectivity=False)
148        wutils.stop_wifi_tethering(self.dut)
149
150    def connect_to_multiple_networks(self, networks):
151        """Connect to a list of wifi networks.
152
153        Args:
154            networks : list of wifi networks.
155        """
156        self.log.info("Connect to multiple wifi networks")
157        for network in networks:
158            ssid = network[SSID]
159            wutils.start_wifi_connection_scan_and_ensure_network_found(
160                self.dut, ssid)
161            wutils.wifi_connect(self.dut, network, num_of_tries=6)
162            self.wifi_config_list.append(network)
163            self.pre_default_mac[network[SSID]] = self.get_sta_mac_address()
164            self.pre_random_mac[network[SSID]] = \
165                self.dut.droid.wifigetRandomizedMacAddress(network)
166
167    def get_sta_mac_address(self):
168        """Gets the current MAC address being used for client mode."""
169        out = self.dut.adb.shell("ifconfig wlan0")
170        res = re.match(".* HWaddr (\S+).*", out, re.S)
171        return res.group(1)
172
173    def add_network_suggestions(self, network_suggestions):
174        """Add wifi network suggestions to DUT.
175
176        Args:
177            network_suggestions : suggestions to add.
178        """
179        self.dut.log.info("Adding network suggestions")
180        asserts.assert_true(
181            self.dut.droid.wifiAddNetworkSuggestions(network_suggestions),
182            "Failed to add suggestions")
183
184        # Enable suggestions by the app.
185        self.dut.log.debug("Enabling suggestions from test")
186        self.dut.adb.shell(
187            "cmd wifi network-suggestions-set-user-approved %s yes" % \
188                SL4A_APK_NAME)
189
190    def remove_suggestions_and_ensure_no_connection(self,
191                                                    network_suggestions,
192                                                    expected_ssid):
193        """Remove network suggestions.
194
195        Args:
196            network_suggestions : suggestions to remove.
197            expected_ssid : SSID to verify that DUT is not connected.
198        """
199        # remove network suggestion and verify disconnect
200        self.dut.log.info("Removing network suggestions")
201        asserts.assert_true(
202            self.dut.droid.wifiRemoveNetworkSuggestions(network_suggestions),
203            "Failed to remove suggestions")
204
205        wutils.wait_for_disconnect(self.dut)
206        self.dut.ed.clear_all_events()
207
208        # Now ensure that we didn't connect back.
209        asserts.assert_false(
210            wutils.wait_for_connect(self.dut,
211                                    expected_ssid,
212                                    assert_on_fail=False),
213            "Device should not connect back")
214
215    def add_network_and_enable(self, network):
216        """Add a network and enable it.
217
218        Args:
219            network : Network details for the network to be added.
220        """
221        self.log.info("Add a wifi network and enable it")
222        ret = self.dut.droid.wifiAddNetwork(network)
223        asserts.assert_true(ret != -1, "Add network %r failed" % network)
224        self.wifi_config_list.append({SSID: network[SSID], NETID: ret})
225        self.dut.droid.wifiEnableNetwork(ret, 0)
226
227    def check_networks_after_autoupdate(self, networks):
228        """Verify that all previously configured networks are persistent.
229
230        Args:
231            networks: List of network dicts.
232        """
233        network_info = self.dut.droid.wifiGetConfiguredNetworks()
234        if len(network_info) != len(networks):
235            msg = (
236                "Number of configured networks before and after Auto-update "
237                "don't match. \nBefore reboot = %s \n After reboot = %s" %
238                (networks, network_info))
239            raise signals.TestFailure(msg)
240
241        # For each network, check if it exists in configured list after Auto-
242        # update.
243        for network in networks:
244            exists = wutils.match_networks({SSID: network[SSID]}, network_info)
245            if not exists:
246                raise signals.TestFailure("%s network is not present in the"
247                                          " configured list after Auto-update" %
248                                          network[SSID])
249            # Get the new network id for each network after reboot.
250            network[NETID] = exists[0]["networkId"]
251
252    def get_enabled_network(self, network1, network2):
253        """Check network status and return currently unconnected network.
254
255        Args:
256            network1: dict representing a network.
257            network2: dict representing a network.
258
259        Returns:
260            Network dict of the unconnected network.
261        """
262        wifi_info = self.dut.droid.wifiGetConnectionInfo()
263        enabled = network1
264        if wifi_info[SSID] == network1[SSID]:
265            enabled = network2
266        return enabled
267
268    ### Tests
269
270    @test_tracker_info(uuid="9ff1f01e-e5ff-408b-9a95-29e87a2df2d8")
271    def test_check_wifi_state_after_au(self):
272        """Check if the state of WiFi is enabled after Auto-update."""
273        if not self.dut.droid.wifiCheckState():
274            raise signals.TestFailure("WiFi is disabled after Auto-update!!!")
275
276    @test_tracker_info(uuid="e3ebdbba-71dd-4281-aef8-5b3d42b88770")
277    def test_verify_networks_after_au(self):
278        """Check if the previously added networks are intact.
279
280           Steps:
281               Number of networs should be the same and match each network.
282
283        """
284        self.check_networks_after_autoupdate(self.wifi_config_list)
285
286    @test_tracker_info(uuid="799e83c2-305d-4510-921e-dac3c0dbb6c5")
287    def test_configstore_after_au(self):
288        """Verify DUT automatically connects to wifi networks after ota.
289
290           Steps:
291               1. Connect to two wifi networks pre ota.
292               2. Verify DUT automatically connects to 1 after ota.
293               3. Re-connect to the other wifi network.
294        """
295        wifi_info = self.dut.droid.wifiGetConnectionInfo()
296        self.pst_default_mac[wifi_info[SSID]] = self.get_sta_mac_address()
297        self.pst_random_mac[wifi_info[SSID]] = \
298            self.dut.droid.wifigetRandomizedMacAddress(wifi_info)
299        reconnect_to = self.get_enabled_network(self.wifi_config_list[1],
300                                                self.wifi_config_list[2])
301        wutils.start_wifi_connection_scan_and_ensure_network_found(
302            self.dut, reconnect_to[SSID])
303        wutils.wifi_connect_by_id(self.dut, reconnect_to[NETID])
304        connect_data = self.dut.droid.wifiGetConnectionInfo()
305        connect_ssid = connect_data[SSID]
306        self.log.info("Expected SSID = %s" % reconnect_to[SSID])
307        self.log.info("Connected SSID = %s" % connect_ssid)
308        if connect_ssid != reconnect_to[SSID]:
309            raise signals.TestFailure(
310                "Device failed to reconnect to the correct"
311                " network after reboot.")
312        self.pst_default_mac[wifi_info[SSID]] = self.get_sta_mac_address()
313        self.pst_random_mac[wifi_info[SSID]] = \
314            self.dut.droid.wifigetRandomizedMacAddress(wifi_info)
315
316        for network in self.connected_networks:
317            wutils.wifi_forget_network(self.dut, network[SSID])
318
319    @test_tracker_info(uuid="e26d0ed9-9457-4a95-a962-4d43b0032bac")
320    def test_mac_randomization_after_au(self):
321        """Verify randomized MAC addrs are persistent after ota.
322
323           Steps:
324               1. Reconnect to the wifi networks configured pre ota.
325               2. Get the randomized MAC addrs.
326        """
327        for ssid, mac in self.pst_random_mac.items():
328            asserts.assert_true(
329                self.pre_random_mac[ssid] == mac,
330                "MAC addr of %s is %s after ota. Expected %s" %
331                (ssid, mac, self.pre_random_mac[ssid]))
332
333    @test_tracker_info(uuid="f68a65e6-97b7-4746-bad8-4c206551d87e")
334    def test_wifi_hotspot_5g_psk_after_au(self):
335        """Verify hotspot after ota upgrade.
336
337           Steps:
338               1. Start wifi hotspot on the saved config.
339               2. Verify DUT client connects to it.
340        """
341        self.verify_wifi_hotspot()
342
343    @test_tracker_info(uuid="21f91372-88a6-44b9-a4e8-d4664823dffb")
344    def test_connect_to_network_suggestion_after_au(self):
345        """Verify connection to network suggestion after ota.
346
347           Steps:
348               1. DUT has network suggestion added before OTA.
349               2. Wait for the device to connect to it.
350               3. Remove the suggestions and ensure the device does not
351                  connect back.
352        """
353        wutils.start_wifi_connection_scan_and_return_status(self.dut)
354        wutils.wait_for_connect(self.dut, self.network_suggestions[0][SSID])
355        self.remove_suggestions_and_ensure_no_connection(
356            self.network_suggestions, self.network_suggestions[0][SSID])
357
358    @test_tracker_info(uuid="b8e47a4f-62fe-4a0e-b999-27ae1ebf4d19")
359    def test_connection_to_new_networks(self):
360        """Check if we can connect to new networks after Auto-update.
361
362           Steps:
363               1. Connect to a PSK network.
364               2. Connect to an open network.
365               3. Forget ntworks added in 1 & 2.
366               TODO: (@bmahadev) Add WEP network once it's ready.
367        """
368        for network in self.new_networks:
369            wutils.connect_to_wifi_network(self.dut, network)
370        for network in self.new_networks:
371            wutils.wifi_forget_network(self.dut, network[SSID])
372
373    @test_tracker_info(uuid="1d8309e4-d5a2-4f48-ba3b-895a58c9bf3a")
374    def test_all_networks_connectable_after_au(self):
375        """Check if previously added networks are connectable.
376
377           Steps:
378               1. Connect to previously added PSK network using network id.
379               2. Connect to previously added open network using network id.
380               TODO: (@bmahadev) Add WEP network once it's ready.
381        """
382        network = self.wifi_config_list[0]
383        if not wutils.connect_to_wifi_network_with_id(self.dut,
384                                                      network[NETID],
385                                                      network[SSID]):
386            raise signals.TestFailure("Failed to connect to %s after OTA" %
387                                      network[SSID])
388        wutils.wifi_forget_network(self.dut, network[SSID])
389
390    @test_tracker_info(uuid="05671859-38b1-4dbf-930c-18048971d075")
391    def test_check_wifi_toggling_after_au(self):
392        """Check if WiFi can be toggled ON/OFF after auto-update."""
393        self.log.debug("Going from on to off.")
394        wutils.wifi_toggle_state(self.dut, False)
395        self.log.debug("Going from off to on.")
396        wutils.wifi_toggle_state(self.dut, True)
397
398    @test_tracker_info(uuid="440edf32-4b00-42b0-9811-9f2bc4a83efb")
399    def test_reset_wifi_after_au(self):
400        """"Check if WiFi can be reset after auto-update."""
401        wutils.reset_wifi(self.dut)
402