1#!/usr/bin/env python3.4 2# 3# Copyright 2018 - 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 itertools 18import pprint 19import queue 20import time 21 22import acts.base_test 23import acts.signals as signals 24import acts.test_utils.wifi.wifi_test_utils as wutils 25import acts.utils 26 27from acts import asserts 28from acts.controllers.android_device import SL4A_APK_NAME 29from acts.test_decorators import test_tracker_info 30from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest 31from acts.test_utils.wifi import wifi_constants 32 33WifiEnums = wutils.WifiEnums 34# EAP Macros 35EAP = WifiEnums.Eap 36EapPhase2 = WifiEnums.EapPhase2 37# Enterprise Config Macros 38Ent = WifiEnums.Enterprise 39 40# Default timeout used for reboot, toggle WiFi and Airplane mode, 41# for the system to settle down after the operation. 42DEFAULT_TIMEOUT = 10 43 44 45class WifiNetworkSuggestionTest(WifiBaseTest): 46 """Tests for WifiNetworkSuggestion API surface. 47 48 Test Bed Requirement: 49 * one Android device 50 * Several Wi-Fi networks visible to the device, including an open Wi-Fi 51 network. 52 """ 53 54 def setup_class(self): 55 super().setup_class() 56 57 self.dut = self.android_devices[0] 58 wutils.wifi_test_device_init(self.dut) 59 req_params = [] 60 opt_param = [ 61 "open_network", "reference_networks", "radius_conf_2g", "radius_conf_5g", "ca_cert", 62 "eap_identity", "eap_password", "hidden_networks" 63 ] 64 self.unpack_userparams( 65 req_param_names=req_params, opt_param_names=opt_param) 66 67 if "AccessPoint" in self.user_params: 68 self.legacy_configure_ap_and_start( 69 wpa_network=True, ent_network=True, 70 radius_conf_2g=self.radius_conf_2g, 71 radius_conf_5g=self.radius_conf_5g,) 72 73 asserts.assert_true( 74 len(self.reference_networks) > 0, 75 "Need at least one reference network with psk.") 76 if hasattr(self, "reference_networks"): 77 self.wpa_psk_2g = self.reference_networks[0]["2g"] 78 self.wpa_psk_5g = self.reference_networks[0]["5g"] 79 if hasattr(self, "open_network"): 80 self.open_2g = self.open_network[0]["2g"] 81 self.open_5g = self.open_network[0]["5g"] 82 if hasattr(self, "ent_networks"): 83 self.ent_network_2g = self.ent_networks[0]["2g"] 84 self.ent_network_5g = self.ent_networks[0]["5g"] 85 self.config_aka = { 86 Ent.EAP: int(EAP.AKA), 87 WifiEnums.SSID_KEY: self.ent_network_2g[WifiEnums.SSID_KEY], 88 } 89 self.config_ttls = { 90 Ent.EAP: int(EAP.TTLS), 91 Ent.CA_CERT: self.ca_cert, 92 Ent.IDENTITY: self.eap_identity, 93 Ent.PASSWORD: self.eap_password, 94 Ent.PHASE2: int(EapPhase2.MSCHAPV2), 95 WifiEnums.SSID_KEY: self.ent_network_2g[WifiEnums.SSID_KEY], 96 } 97 if hasattr(self, "hidden_networks"): 98 self.hidden_network = self.hidden_networks[0] 99 self.dut.droid.wifiRemoveNetworkSuggestions([]) 100 101 def setup_test(self): 102 self.dut.droid.wakeLockAcquireBright() 103 self.dut.droid.wakeUpNow() 104 self.clear_deleted_ephemeral_networks() 105 wutils.wifi_toggle_state(self.dut, True) 106 self.dut.ed.clear_all_events() 107 108 def teardown_test(self): 109 self.dut.droid.wakeLockRelease() 110 self.dut.droid.goToSleepNow() 111 self.dut.droid.wifiRemoveNetworkSuggestions([]) 112 self.dut.droid.wifiDisconnect() 113 wutils.reset_wifi(self.dut) 114 wutils.wifi_toggle_state(self.dut, False) 115 self.dut.ed.clear_all_events() 116 117 def on_fail(self, test_name, begin_time): 118 self.dut.take_bug_report(test_name, begin_time) 119 self.dut.cat_adb_log(test_name, begin_time) 120 121 def teardown_class(self): 122 if "AccessPoint" in self.user_params: 123 del self.user_params["reference_networks"] 124 del self.user_params["open_network"] 125 126 """Helper Functions""" 127 def set_approved(self, approved): 128 self.dut.log.debug("Setting suggestions from sl4a app " 129 + "approved" if approved else "not approved") 130 self.dut.adb.shell("cmd wifi network-suggestions-set-user-approved" 131 + " " + SL4A_APK_NAME 132 + " " + ("yes" if approved else "no")) 133 134 def is_approved(self): 135 is_approved_str = self.dut.adb.shell( 136 "cmd wifi network-suggestions-has-user-approved" 137 + " " + SL4A_APK_NAME) 138 return True if (is_approved_str == "yes") else False 139 140 def clear_deleted_ephemeral_networks(self): 141 self.dut.log.debug("Clearing deleted ephemeral networks") 142 self.dut.adb.shell( 143 "cmd wifi clear-deleted-ephemeral-networks") 144 145 def add_suggestions_and_ensure_connection(self, network_suggestions, 146 expected_ssid, 147 expect_post_connection_broadcast): 148 if expect_post_connection_broadcast is not None: 149 self.dut.droid.wifiStartTrackingNetworkSuggestionStateChange() 150 151 self.dut.log.info("Adding network suggestions"); 152 asserts.assert_true( 153 self.dut.droid.wifiAddNetworkSuggestions(network_suggestions), 154 "Failed to add suggestions") 155 # Enable suggestions by the app. 156 self.dut.log.debug("Enabling suggestions from test"); 157 self.set_approved(True) 158 wutils.start_wifi_connection_scan_and_return_status(self.dut) 159 wutils.wait_for_connect(self.dut, expected_ssid) 160 161 if expect_post_connection_broadcast is None: 162 return; 163 164 # Check if we expected to get the broadcast. 165 try: 166 event = self.dut.ed.pop_event( 167 wifi_constants.WIFI_NETWORK_SUGGESTION_POST_CONNECTION, 60) 168 except queue.Empty: 169 if expect_post_connection_broadcast: 170 raise signals.TestFailure( 171 "Did not receive post connection broadcast") 172 else: 173 if not expect_post_connection_broadcast: 174 raise signals.TestFailure( 175 "Received post connection broadcast") 176 finally: 177 self.dut.droid.wifiStopTrackingNetworkSuggestionStateChange() 178 self.dut.ed.clear_all_events() 179 180 def remove_suggestions_disconnect_and_ensure_no_connection_back(self, 181 network_suggestions, 182 expected_ssid): 183 self.dut.log.info("Removing network suggestions") 184 asserts.assert_true( 185 self.dut.droid.wifiRemoveNetworkSuggestions(network_suggestions), 186 "Failed to remove suggestions") 187 # Ensure we did not disconnect 188 wutils.ensure_no_disconnect(self.dut) 189 190 # Trigger a disconnect and wait for the disconnect. 191 self.dut.droid.wifiDisconnect() 192 wutils.wait_for_disconnect(self.dut) 193 self.dut.ed.clear_all_events() 194 195 # Now ensure that we didn't connect back. 196 asserts.assert_false( 197 wutils.wait_for_connect(self.dut, expected_ssid, assert_on_fail=False), 198 "Device should not connect back") 199 200 def _test_connect_to_wifi_network_reboot_config_store(self, 201 network_suggestions, 202 wifi_network): 203 """ Test network suggestion with reboot config store 204 205 Args: 206 1. network_suggestions: network suggestions in list to add to the device. 207 2. wifi_network: expected wifi network to connect to 208 """ 209 210 self.add_suggestions_and_ensure_connection( 211 network_suggestions, wifi_network[WifiEnums.SSID_KEY], None) 212 213 # Reboot and wait for connection back to the same suggestion. 214 self.dut.reboot() 215 time.sleep(DEFAULT_TIMEOUT) 216 217 wutils.wait_for_connect(self.dut, wifi_network[WifiEnums.SSID_KEY]) 218 219 self.remove_suggestions_disconnect_and_ensure_no_connection_back( 220 network_suggestions, wifi_network[WifiEnums.SSID_KEY]) 221 222 @test_tracker_info(uuid="bda8ed20-4382-4380-831a-64cf77eca108") 223 def test_connect_to_wpa_psk_2g(self): 224 """ Adds a network suggestion and ensure that the device connected. 225 226 Steps: 227 1. Send a network suggestion to the device. 228 2. Wait for the device to connect to it. 229 3. Ensure that we did not receive the post connection broadcast 230 (isAppInteractionRequired = False). 231 4. Remove the suggestions and ensure the device does not connect back. 232 """ 233 self.add_suggestions_and_ensure_connection( 234 [self.wpa_psk_2g], self.wpa_psk_2g[WifiEnums.SSID_KEY], 235 False) 236 237 self.remove_suggestions_disconnect_and_ensure_no_connection_back( 238 [self.wpa_psk_2g], self.wpa_psk_2g[WifiEnums.SSID_KEY]) 239 240 @test_tracker_info(uuid="f54bc250-d9e9-4f00-8b5b-b866e8550b43") 241 def test_connect_to_highest_priority(self): 242 """ 243 Adds network suggestions and ensures that device connects to 244 the suggestion with the highest priority. 245 246 Steps: 247 1. Send 2 network suggestions to the device (with different priorities). 248 2. Wait for the device to connect to the network with the highest 249 priority. 250 3. Re-add the suggestions with the priorities reversed. 251 4. Again wait for the device to connect to the network with the highest 252 priority. 253 """ 254 network_suggestion_2g = self.wpa_psk_2g 255 network_suggestion_5g = self.wpa_psk_5g 256 257 # Add suggestions & wait for the connection event. 258 network_suggestion_2g[WifiEnums.PRIORITY] = 5 259 network_suggestion_5g[WifiEnums.PRIORITY] = 2 260 self.add_suggestions_and_ensure_connection( 261 [network_suggestion_2g, network_suggestion_5g], 262 self.wpa_psk_2g[WifiEnums.SSID_KEY], 263 None) 264 265 self.remove_suggestions_disconnect_and_ensure_no_connection_back( 266 [], self.wpa_psk_2g[WifiEnums.SSID_KEY]) 267 268 # Reverse the priority. 269 # Add suggestions & wait for the connection event. 270 network_suggestion_2g[WifiEnums.PRIORITY] = 2 271 network_suggestion_5g[WifiEnums.PRIORITY] = 5 272 self.add_suggestions_and_ensure_connection( 273 [network_suggestion_2g, network_suggestion_5g], 274 self.wpa_psk_5g[WifiEnums.SSID_KEY], 275 None) 276 277 @test_tracker_info(uuid="b1d27eea-23c8-4c4f-b944-ef118e4cc35f") 278 def test_connect_to_wpa_psk_2g_with_post_connection_broadcast(self): 279 """ Adds a network suggestion and ensure that the device connected. 280 281 Steps: 282 1. Send a network suggestion to the device with 283 isAppInteractionRequired set. 284 2. Wait for the device to connect to it. 285 3. Ensure that we did receive the post connection broadcast 286 (isAppInteractionRequired = True). 287 4. Remove the suggestions and ensure the device does not connect back. 288 """ 289 network_suggestion = self.wpa_psk_2g 290 network_suggestion[WifiEnums.IS_APP_INTERACTION_REQUIRED] = True 291 self.add_suggestions_and_ensure_connection( 292 [network_suggestion], self.wpa_psk_2g[WifiEnums.SSID_KEY], 293 True) 294 self.remove_suggestions_disconnect_and_ensure_no_connection_back( 295 [self.wpa_psk_2g], self.wpa_psk_2g[WifiEnums.SSID_KEY]) 296 297 @test_tracker_info(uuid="a036a24d-29c0-456d-ae6a-afdde34da710") 298 def test_connect_to_wpa_psk_5g_reboot_config_store(self): 299 """ 300 Adds a network suggestion and ensure that the device connects to it 301 after reboot. 302 303 Steps: 304 1. Send a network suggestion to the device. 305 2. Wait for the device to connect to it. 306 3. Ensure that we did not receive the post connection broadcast 307 (isAppInteractionRequired = False). 308 4. Reboot the device. 309 5. Wait for the device to connect to back to it. 310 6. Remove the suggestions and ensure the device does not connect back. 311 """ 312 self._test_connect_to_wifi_network_reboot_config_store( 313 [self.wpa_psk_5g], self.wpa_psk_5g) 314 315 @test_tracker_info(uuid="61649a2b-0f00-4272-9b9b-40ad5944da31") 316 def test_connect_to_wpa_ent_config_aka_reboot_config_store(self): 317 """ 318 Adds a network suggestion and ensure that the device connects to it 319 after reboot. 320 321 Steps: 322 1. Send a Enterprise AKA network suggestion to the device. 323 2. Wait for the device to connect to it. 324 3. Ensure that we did not receive the post connection broadcast. 325 4. Reboot the device. 326 5. Wait for the device to connect to the wifi network. 327 6. Remove suggestions and ensure device doesn't connect back to it. 328 """ 329 self._test_connect_to_wifi_network_reboot_config_store( 330 [self.config_aka], self.ent_network_2g) 331 332 @test_tracker_info(uuid="98b2d40a-acb4-4a2f-aba1-b069e2a1d09d") 333 def test_connect_to_wpa_ent_config_ttls_pap_reboot_config_store(self): 334 """ 335 Adds a network suggestion and ensure that the device connects to it 336 after reboot. 337 338 Steps: 339 1. Send a Enterprise TTLS PAP network suggestion to the device. 340 2. Wait for the device to connect to it. 341 3. Ensure that we did not receive the post connection broadcast. 342 4. Reboot the device. 343 5. Wait for the device to connect to the wifi network. 344 6. Remove suggestions and ensure device doesn't connect back to it. 345 """ 346 config = dict(self.config_ttls) 347 config[WifiEnums.Enterprise.PHASE2] = WifiEnums.EapPhase2.PAP.value 348 349 self._test_connect_to_wifi_network_reboot_config_store( 350 [config], self.ent_network_2g) 351 352 @test_tracker_info(uuid="554b5861-22d0-4922-a5f4-712b4cf564eb") 353 def test_fail_to_connect_to_wpa_psk_5g_when_not_approved(self): 354 """ 355 Adds a network suggestion and ensure that the device does not 356 connect to it until we approve the app. 357 358 Steps: 359 1. Send a network suggestion to the device with the app not approved. 360 2. Ensure the network is present in scan results, but we don't connect 361 to it. 362 3. Now approve the app. 363 4. Wait for the device to connect to it. 364 """ 365 self.dut.log.info("Adding network suggestions"); 366 asserts.assert_true( 367 self.dut.droid.wifiAddNetworkSuggestions([self.wpa_psk_5g]), 368 "Failed to add suggestions") 369 370 # Disable suggestions by the app. 371 self.set_approved(False) 372 373 # Ensure the app is not approved. 374 asserts.assert_false( 375 self.is_approved(), 376 "Suggestions should be disabled") 377 378 # Start a new scan to trigger auto-join. 379 wutils.start_wifi_connection_scan_and_ensure_network_found( 380 self.dut, self.wpa_psk_5g[WifiEnums.SSID_KEY]) 381 382 # Ensure we don't connect to the network. 383 asserts.assert_false( 384 wutils.wait_for_connect( 385 self.dut, self.wpa_psk_5g[WifiEnums.SSID_KEY], assert_on_fail=False), 386 "Should not connect to network suggestions from unapproved app") 387 388 self.dut.log.info("Enabling suggestions from test"); 389 # Now Enable suggestions by the app & ensure we connect to the network. 390 self.set_approved(True) 391 392 # Ensure the app is approved. 393 asserts.assert_true( 394 self.is_approved(), 395 "Suggestions should be enabled") 396 397 # Start a new scan to trigger auto-join. 398 wutils.start_wifi_connection_scan_and_ensure_network_found( 399 self.dut, self.wpa_psk_5g[WifiEnums.SSID_KEY]) 400 401 wutils.wait_for_connect(self.dut, self.wpa_psk_5g[WifiEnums.SSID_KEY]) 402 403 @test_tracker_info(uuid="98400dea-776e-4a0a-9024-18845b27331c") 404 def test_fail_to_connect_to_wpa_psk_2g_after_user_forgot_network(self): 405 """ 406 Adds a network suggestion and ensures that the device does not 407 connect to it after the user forgot the network previously. 408 409 Steps: 410 1. Send a network suggestion to the device with 411 isAppInteractionRequired set. 412 2. Wait for the device to connect to it. 413 3. Ensure that we did receive the post connection broadcast 414 (isAppInteractionRequired = True). 415 4. Simulate user forgetting the network and the device does not 416 connecting back even though the suggestion is active from the app. 417 """ 418 network_suggestion = self.wpa_psk_2g 419 network_suggestion[WifiEnums.IS_APP_INTERACTION_REQUIRED] = True 420 self.add_suggestions_and_ensure_connection( 421 [network_suggestion], self.wpa_psk_2g[WifiEnums.SSID_KEY], 422 True) 423 424 # Simulate user forgetting the ephemeral network. 425 self.dut.droid.wifiDisableEphemeralNetwork( 426 self.wpa_psk_2g[WifiEnums.SSID_KEY]) 427 wutils.wait_for_disconnect(self.dut) 428 self.dut.log.info("Disconnected from network %s", self.wpa_psk_2g) 429 self.dut.ed.clear_all_events() 430 431 # Now ensure that we don't connect back even though the suggestion 432 # is still active. 433 asserts.assert_false( 434 wutils.wait_for_connect(self.dut, 435 self.wpa_psk_2g[WifiEnums.SSID_KEY], 436 assert_on_fail=False), 437 "Device should not connect back") 438 439 @test_tracker_info(uuid="93c86b05-fa56-4d79-ad27-009a16f691b1") 440 def test_connect_to_hidden_network(self): 441 """ 442 Adds a network suggestion with hidden SSID config, ensure device can scan 443 and connect to this network. 444 445 Steps: 446 1. Send a hidden network suggestion to the device. 447 2. Wait for the device to connect to it. 448 3. Ensure that we did not receive the post connection broadcast 449 (isAppInteractionRequired = False). 450 4. Remove the suggestions and ensure the device does not connect back. 451 """ 452 asserts.skip_if(not hasattr(self, "hidden_networks"), "No hidden networks, skip this test") 453 454 network_suggestion = self.hidden_network 455 self.add_suggestions_and_ensure_connection( 456 [network_suggestion], network_suggestion[WifiEnums.SSID_KEY], False) 457 self.remove_suggestions_disconnect_and_ensure_no_connection_back( 458 [network_suggestion], network_suggestion[WifiEnums.SSID_KEY]) 459