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 35# Network request timeout to use. 36NETWORK_REQUEST_TIMEOUT_MS = 60 * 1000 37# Timeout to wait for instant failure. 38NETWORK_REQUEST_INSTANT_FAILURE_TIMEOUT_SEC = 5 39 40class WifiNetworkRequestTest(WifiBaseTest): 41 """Tests for NetworkRequest with WifiNetworkSpecifier API surface. 42 43 Test Bed Requirement: 44 * one Android device 45 * Several Wi-Fi networks visible to the device, including an open Wi-Fi 46 network. 47 """ 48 49 def setup_class(self): 50 super().setup_class() 51 52 self.dut = self.android_devices[0] 53 wutils.wifi_test_device_init(self.dut) 54 req_params = [] 55 opt_param = [ 56 "open_network", "reference_networks" 57 ] 58 self.unpack_userparams( 59 req_param_names=req_params, opt_param_names=opt_param) 60 61 if "AccessPoint" in self.user_params: 62 self.legacy_configure_ap_and_start(wpa_network=True, 63 wep_network=True) 64 65 asserts.assert_true( 66 len(self.reference_networks) > 0, 67 "Need at least one reference network with psk.") 68 self.wpa_psk_2g = self.reference_networks[0]["2g"] 69 self.wpa_psk_5g = self.reference_networks[0]["5g"] 70 self.open_2g = self.open_network[0]["2g"] 71 self.open_5g = self.open_network[0]["5g"] 72 73 def setup_test(self): 74 self.dut.droid.wakeLockAcquireBright() 75 self.dut.droid.wakeUpNow() 76 self.remove_approvals() 77 self.clear_deleted_ephemeral_networks() 78 wutils.wifi_toggle_state(self.dut, True) 79 self.dut.ed.clear_all_events() 80 81 def teardown_test(self): 82 self.dut.droid.wakeLockRelease() 83 self.dut.droid.goToSleepNow() 84 self.dut.droid.wifiReleaseNetworkAll() 85 self.dut.droid.wifiDisconnect() 86 wutils.reset_wifi(self.dut) 87 # Ensure we disconnected from the current network before the next test. 88 if self.dut.droid.wifiGetConnectionInfo()["supplicant_state"] != "disconnected": 89 wutils.wait_for_disconnect(self.dut) 90 wutils.wifi_toggle_state(self.dut, False) 91 self.dut.ed.clear_all_events() 92 93 def on_fail(self, test_name, begin_time): 94 self.dut.take_bug_report(test_name, begin_time) 95 self.dut.cat_adb_log(test_name, begin_time) 96 97 def teardown_class(self): 98 if "AccessPoint" in self.user_params: 99 del self.user_params["reference_networks"] 100 del self.user_params["open_network"] 101 102 """Helper Functions""" 103 def wait_for_network_lost(self): 104 """ 105 Wait for network lost callback from connectivity service (wifi 106 disconnect). 107 108 Args: 109 ad: Android device object. 110 """ 111 try: 112 self.dut.droid.wifiStartTrackingStateChange() 113 event = self.dut.ed.pop_event( 114 wifi_constants.WIFI_NETWORK_CB_ON_LOST, 10) 115 self.dut.droid.wifiStopTrackingStateChange() 116 except queue.Empty: 117 raise signals.TestFailure( 118 "Device did not disconnect from the network") 119 120 def remove_approvals(self): 121 self.dut.log.debug("Removing all approvals from sl4a app") 122 self.dut.adb.shell( 123 "cmd wifi network-requests-remove-user-approved-access-points" 124 + " " + SL4A_APK_NAME) 125 126 def clear_deleted_ephemeral_networks(self): 127 self.dut.log.debug("Clearing deleted ephemeral networks") 128 self.dut.adb.shell( 129 "cmd wifi clear-deleted-ephemeral-networks") 130 131 @test_tracker_info(uuid="d70c8380-61ba-48a3-b76c-a0b55ce4eabf") 132 def test_connect_to_wpa_psk_2g_with_ssid(self): 133 """ 134 Initiates a connection to network via network request with specific SSID 135 136 Steps: 137 1. Send a network specifier with the specific SSID/credentials of 138 WPA-PSK 2G network. 139 2. Wait for platform to scan and find matching networks. 140 3. Simulate user selecting the network. 141 4. Ensure that the device connects to the network. 142 """ 143 wutils.wifi_connect_using_network_request(self.dut, self.wpa_psk_2g, 144 self.wpa_psk_2g) 145 146 @test_tracker_info(uuid="44f2bf40-a282-4413-b8f2-3abb3caa49ee") 147 def test_connect_to_open_5g_with_ssid(self): 148 """ 149 Initiates a connection to network via network request with specific SSID 150 151 Steps: 152 1. Send a network specifier with the specific SSID of Open 5G network. 153 2. Wait for platform to scan and find matching networks. 154 3. Simulate user selecting the network. 155 4. Ensure that the device connects to the network. 156 """ 157 wutils.wifi_connect_using_network_request(self.dut, self.open_5g, 158 self.open_5g) 159 160 @test_tracker_info(uuid="09d1823e-4f85-42f8-8c20-de7637f6d4be") 161 def test_connect_to_wpa_psk_5g_with_ssid_pattern(self): 162 """ 163 Initiates a connection to network via network request with SSID pattern 164 165 Steps: 166 1. Send a network specifier with the SSID pattern/credentials of 167 WPA-PSK 5G network. 168 2. Wait for platform to scan and find matching networks. 169 3. Simulate user selecting the network. 170 4. Ensure that the device connects to the network. 171 """ 172 network_specifier = self.wpa_psk_5g.copy(); 173 # Remove ssid & replace with ssid pattern. 174 network_ssid = network_specifier.pop(WifiEnums.SSID_KEY) 175 # Remove the last element of ssid & replace with .* to create a matching 176 # pattern. 177 network_specifier[WifiEnums.SSID_PATTERN_KEY] = network_ssid[:-1] + ".*" 178 wutils.wifi_connect_using_network_request(self.dut, self.wpa_psk_5g, 179 network_specifier) 180 181 @test_tracker_info(uuid="52216329-06f1-45ef-8d5f-de8b02d9f975") 182 def test_connect_to_open_5g_after_connecting_to_wpa_psk_2g(self): 183 """ 184 Initiates a connection to network via network request with SSID pattern 185 186 Steps: 187 1. Send a network specifier with the specific SSID of Open 5G network. 188 2. Wait for platform to scan and find matching networks. 189 3. Simulate user selecting the network. 190 4. Ensure that the device connects to the network. 191 5. Release the network request. 192 6. Send another network specifier with the specific SSID & credentials 193 of WPA-PSK 2G network. 194 7. Ensure we disconnect from the previous network. 195 8. Wait for platform to scan and find matching networks. 196 9. Simulate user selecting the new network. 197 10. Ensure that the device connects to the new network. 198 """ 199 # Complete flow for the first request. 200 wutils.wifi_connect_using_network_request(self.dut, self.wpa_psk_2g, 201 self.wpa_psk_2g) 202 # Release the request. 203 self.dut.droid.wifiReleaseNetwork(self.wpa_psk_2g) 204 # Ensure we disconnected from the previous network. 205 wutils.wait_for_disconnect(self.dut) 206 self.dut.log.info("Disconnected from network %s", self.wpa_psk_2g) 207 self.dut.ed.clear_all_events() 208 # Complete flow for the second request. 209 wutils.wifi_connect_using_network_request(self.dut, self.open_5g, 210 self.open_5g) 211 212 @test_tracker_info(uuid="f28b5dc9-771f-42ef-8178-e55e9a16f5c7") 213 def test_connect_to_wpa_psk_5g_while_connecting_to_open_2g(self): 214 """ 215 Initiates a connection to network via network request with specific SSID 216 217 Steps: 218 1. Send a network specifier with the specific SSID & credentials of 219 WPA-PSK 5G network. 220 2. Send another network specifier with the specific SSID of Open 2G 221 network. 222 3. Ensure we disconnect from the previous network. 223 4. Wait for platform to scan and find matching networks. 224 5. Simulate user selecting the new network. 225 6. Ensure that the device connects to the new network. 226 """ 227 # Make the first request. 228 self.dut.droid.wifiRequestNetworkWithSpecifier(self.open_2g) 229 self.dut.log.info("Sent network request with %s", self.open_2g) 230 # Complete flow for the second request. 231 wutils.wifi_connect_using_network_request(self.dut, self.wpa_psk_5g, 232 self.wpa_psk_5g) 233 234 @test_tracker_info(uuid="2ab82a98-37da-4b27-abb6-578bedebccdc") 235 def test_connect_to_open_5g_while_connected_to_wpa_psk_2g(self): 236 """ 237 Initiates a connection to network via network request with specific SSID 238 239 Steps: 240 1. Send a network specifier with the specific SSID of Open 5G network. 241 2. Wait for platform to scan and find matching networks. 242 3. Simulate user selecting the network. 243 4. Ensure that the device connects to the network. 244 5. Send another network specifier with the specific SSID & credentials 245 of WPA-PSK 2G network. 246 6. Ensure we disconnect from the previous network. 247 7. Wait for platform to scan and find matching networks. 248 8. Simulate user selecting the new network. 249 9. Ensure that the device connects to the new network. 250 """ 251 # Complete flow for the first request. 252 wutils.wifi_connect_using_network_request(self.dut, self.wpa_psk_2g, 253 self.wpa_psk_2g) 254 # Send the second request. 255 self.dut.droid.wifiRequestNetworkWithSpecifier(self.open_5g) 256 self.dut.log.info("Sent network request with %s", self.open_5g) 257 # Ensure we do not disconnect from the previous network until the user 258 # approves the new request. 259 self.dut.ed.clear_all_events() 260 wutils.ensure_no_disconnect(self.dut) 261 262 # Now complete the flow and ensure we connected to second request. 263 wutils.wait_for_wifi_connect_after_network_request(self.dut, 264 self.open_5g) 265 266 @test_tracker_info(uuid="f0bb2213-b3d1-4fb8-bbdc-ad55c4fb05ed") 267 def test_connect_to_wpa_psk_2g_which_is_already_approved(self): 268 """ 269 Initiates a connection to network via network request with specific SSID 270 bypassing user approval. 271 272 Steps: 273 1. Send a network specifier with the specific SSID/credentials of 274 WPA-PSK 2G network. 275 2. Wait for platform to scan and find matching networks. 276 3. Simulate user selecting the network. 277 4. Ensure that the device connects to the network. 278 5. Ensure we disconnect from the previous network. 279 6. Send another network specifier with the specific 280 SSID/BSSID/credentials of WPA-PSK 2G network. 281 7. Ensure that the device bypasses user approval & connects to the 282 same network. 283 """ 284 # Complete flow for the first request. 285 wutils.wifi_connect_using_network_request(self.dut, self.wpa_psk_2g, 286 self.wpa_psk_2g) 287 # Release the request. 288 self.dut.droid.wifiReleaseNetwork(self.wpa_psk_2g) 289 # Ensure we disconnected from the network. 290 wutils.wait_for_disconnect(self.dut) 291 self.dut.log.info("Disconnected from network %s", self.wpa_psk_2g) 292 self.dut.ed.clear_all_events() 293 294 # Find bssid for the WPA-PSK 2G network. 295 scan_results = self.dut.droid.wifiGetScanResults() 296 match_results = wutils.match_networks( 297 {WifiEnums.SSID_KEY: self.wpa_psk_2g[WifiEnums.SSID_KEY]}, 298 scan_results) 299 asserts.assert_equal(len(match_results), 1, 300 "Cannot find bssid for WPA-PSK 2G network") 301 bssid = match_results[0][WifiEnums.BSSID_KEY] 302 # Send the second request with bssid. 303 network_specifier_with_bssid = self.wpa_psk_2g.copy(); 304 network_specifier_with_bssid[WifiEnums.BSSID_KEY] = bssid 305 self.dut.droid.wifiRequestNetworkWithSpecifier( 306 network_specifier_with_bssid) 307 self.dut.log.info("Sent network request with %r", 308 network_specifier_with_bssid) 309 310 # Ensure we connected to second request without user approval. 311 wutils.wait_for_connect(self.dut, self.wpa_psk_2g[WifiEnums.SSID_KEY]) 312 313 @test_tracker_info(uuid="fcf84d94-5f6e-4bd6-9f76-40a0228d4ebe") 314 def test_connect_to_wpa_psk_2g_which_is_already_approved_but_then_forgot(self): 315 """ 316 Initiates a connection to network via network request with specific SSID 317 with user approval. 318 319 Steps: 320 1. Send a network specifier with the specific SSID/credentials of 321 WPA-PSK 2G network. 322 2. Wait for platform to scan and find matching networks. 323 3. Simulate user selecting the network. 324 4. Ensure that the device connects to the network. 325 4. Simulate user fogetting the network from the UI. 326 6. Ensure we disconnect from the previous network. 327 7. Send another network specifier with the specific 328 SSID/BSSID/credentials of WPA-PSK 2G network. 329 8. Ensure that the device does not bypass user approval & connects to the 330 same network with user approval. (This should also clear the blacklist) 331 9. Send the same network specifier with the specific 332 SSID/BSSID/credentials of WPA-PSK 2G network. 333 10.Ensure that the device bypasses user approval now & connects to the 334 same network. 335 """ 336 # Complete flow for the first request. 337 wutils.wifi_connect_using_network_request(self.dut, self.wpa_psk_2g, 338 self.wpa_psk_2g) 339 340 # Simulate user forgeting the ephemeral network. 341 self.dut.droid.wifiUserDisconnectNetwork(self.wpa_psk_2g[WifiEnums.SSID_KEY]) 342 # Ensure we disconnected from the network. 343 wutils.wait_for_disconnect(self.dut) 344 self.dut.log.info("Disconnected from network %s", self.wpa_psk_2g) 345 self.dut.ed.clear_all_events() 346 # Release the first request. 347 self.dut.droid.wifiReleaseNetwork(self.wpa_psk_2g) 348 349 # Find bssid for the WPA-PSK 2G network. 350 scan_results = self.dut.droid.wifiGetScanResults() 351 match_results = wutils.match_networks( 352 {WifiEnums.SSID_KEY: self.wpa_psk_2g[WifiEnums.SSID_KEY]}, 353 scan_results) 354 asserts.assert_equal(len(match_results), 1, 355 "Cannot find bssid for WPA-PSK 2G network") 356 bssid = match_results[0][WifiEnums.BSSID_KEY] 357 # Send the second request with bssid. 358 network_specifier_with_bssid = self.wpa_psk_2g.copy(); 359 network_specifier_with_bssid[WifiEnums.BSSID_KEY] = bssid 360 self.dut.droid.wifiRequestNetworkWithSpecifier( 361 network_specifier_with_bssid) 362 self.dut.log.info("Sent network request with %r", 363 network_specifier_with_bssid) 364 365 # Ensure that we did not connect bypassing user approval. 366 assert_msg = "Device should not connect without user approval" 367 asserts.assert_false( 368 wutils.wait_for_connect(self.dut, 369 self.wpa_psk_2g[WifiEnums.SSID_KEY], 370 assert_on_fail=False), 371 assert_msg) 372 373 # Now complete the flow and ensure we connected to second request. 374 wutils.wait_for_wifi_connect_after_network_request(self.dut, 375 self.wpa_psk_2g) 376 377 # Now make the same request again & ensure that we connect without user 378 # approval. 379 self.dut.droid.wifiRequestNetworkWithSpecifier( 380 network_specifier_with_bssid) 381 self.dut.log.info("Sent network request with %r", 382 network_specifier_with_bssid) 383 wutils.wait_for_connect(self.dut, self.wpa_psk_2g[WifiEnums.SSID_KEY]) 384 385 @test_tracker_info(uuid="2f90a266-f04d-4932-bb5b-d075bedfd56d") 386 def test_match_failure_with_invalid_ssid_pattern(self): 387 """ 388 Initiates a connection to network via network request with SSID pattern 389 that does not match any networks. 390 391 Steps: 392 1. Trigger a connect to one of the networks (as a saved network). 393 2. Send a network specifier with the non-matching SSID pattern. 394 3. Ensure that the platform does not return any matching networks. 395 4. Wait for the request to timeout. 396 """ 397 network = self.wpa_psk_5g 398 399 # Trigger a connection to a network as a saved network before the 400 # request and ensure that this does not change the behavior. 401 wutils.connect_to_wifi_network(self.dut, network, check_connectivity=False) 402 403 network_specifier = self.wpa_psk_5g.copy(); 404 # Remove ssid & replace with invalid ssid pattern. 405 network_ssid = network_specifier.pop(WifiEnums.SSID_KEY) 406 network_specifier[WifiEnums.SSID_PATTERN_KEY] = \ 407 network_ssid + "blah" + ".*" 408 409 self.dut.droid.wifiStartTrackingStateChange() 410 expected_ssid = network[WifiEnums.SSID_KEY] 411 412 self.dut.droid.wifiRequestNetworkWithSpecifierWithTimeout( 413 network_specifier, NETWORK_REQUEST_TIMEOUT_MS) 414 self.dut.log.info("Sent network request with invalid specifier %s", 415 network_specifier) 416 time.sleep(wifi_constants.NETWORK_REQUEST_CB_REGISTER_DELAY_SEC) 417 self.dut.droid.wifiRegisterNetworkRequestMatchCallback() 418 # Wait for the request to timeout. 419 timeout_secs = NETWORK_REQUEST_TIMEOUT_MS * 2 / 1000 420 try: 421 on_unavailable_event = self.dut.ed.pop_event( 422 wifi_constants.WIFI_NETWORK_CB_ON_UNAVAILABLE, timeout_secs) 423 asserts.assert_true(on_unavailable_event, "Network request did not timeout") 424 except queue.Empty: 425 asserts.fail("No events returned") 426 finally: 427 self.dut.droid.wifiStopTrackingStateChange() 428 429 @test_tracker_info(uuid="caa96f57-840e-4997-9280-655edd3b76ee") 430 def test_connect_failure_user_rejected(self): 431 """ 432 Initiates a connection to network via network request with specific SSID 433 which the user rejected. 434 435 Steps: 436 1. Send a network specifier with the specific SSID/credentials of 437 WPA-PSK 5G network. 438 2. Wait for platform to scan and find matching networks. 439 3. Simulate user rejecting the network. 440 4. Ensure that we get an instant onUnavailable callback. 441 5. Simulate user fogetting the network from the UI. 442 """ 443 network = self.wpa_psk_5g 444 expected_ssid = network[WifiEnums.SSID_KEY] 445 446 self.dut.droid.wifiStartTrackingStateChange() 447 448 self.dut.droid.wifiRequestNetworkWithSpecifierWithTimeout( 449 network, NETWORK_REQUEST_TIMEOUT_MS) 450 self.dut.log.info("Sent network request with specifier %s", network) 451 time.sleep(wifi_constants.NETWORK_REQUEST_CB_REGISTER_DELAY_SEC) 452 self.dut.droid.wifiRegisterNetworkRequestMatchCallback() 453 454 # Wait for the platform to scan and return a list of networks 455 # matching the request 456 try: 457 matched_network = None 458 for _ in [0, 3]: 459 on_match_event = self.dut.ed.pop_event( 460 wifi_constants.WIFI_NETWORK_REQUEST_MATCH_CB_ON_MATCH, 30) 461 asserts.assert_true(on_match_event, 462 "Network request on match not received.") 463 matched_scan_results = on_match_event["data"] 464 self.dut.log.debug("Network request on match results %s", 465 matched_scan_results) 466 matched_network = wutils.match_networks( 467 {WifiEnums.SSID_KEY: network[WifiEnums.SSID_KEY]}, 468 matched_scan_results) 469 if matched_network: 470 break; 471 asserts.assert_true( 472 matched_network, "Target network %s not found" % network) 473 474 # Send user rejection. 475 self.dut.droid.wifiSendUserRejectionForNetworkRequestMatch() 476 self.dut.log.info("Sent user rejection for network request %s", 477 expected_ssid) 478 479 # Wait for the platform to raise unavailable callback 480 # instantaneously. 481 on_unavailable_event = self.dut.ed.pop_event( 482 wifi_constants.WIFI_NETWORK_CB_ON_UNAVAILABLE, 483 NETWORK_REQUEST_INSTANT_FAILURE_TIMEOUT_SEC) 484 asserts.assert_true(on_unavailable_event, 485 "Network request on available not received.") 486 except queue.Empty: 487 asserts.fail("Expected events not returned") 488 finally: 489 self.dut.droid.wifiStopTrackingStateChange() 490