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