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 itertools 18import pprint 19import queue 20import time 21 22from acts.test_utils.net import ui_utils as uutils 23import acts.test_utils.wifi.wifi_test_utils as wutils 24from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest 25 26 27from acts import asserts 28from acts import signals 29from acts.test_decorators import test_tracker_info 30from acts.test_utils.tel.tel_test_utils import get_operator_name 31from acts.test_utils.wifi.WifiBaseTest import WifiBaseTest 32from acts.utils import force_airplane_mode 33 34WifiEnums = wutils.WifiEnums 35 36DEFAULT_TIMEOUT = 10 37OSU_TEST_TIMEOUT = 300 38 39# Constants for providers. 40GLOBAL_RE = 0 41OSU_BOINGO = 0 42BOINGO = 1 43ATT = 2 44 45# Constants used for various device operations. 46RESET = 1 47TOGGLE = 2 48 49UNKNOWN_FQDN = "@#@@!00fffffx" 50 51# Constants for Boingo UI automator 52EDIT_TEXT_CLASS_NAME = "android.widget.EditText" 53PASSWORD_TEXT = "Password" 54PASSPOINT_BUTTON = "Get Passpoint" 55BOINGO_UI_TEXT = "Online Sign Up" 56 57class WifiPasspointTest(WifiBaseTest): 58 """Tests for APIs in Android's WifiManager class. 59 60 Test Bed Requirement: 61 * One Android device 62 * Several Wi-Fi networks visible to the device, including an open Wi-Fi 63 network. 64 """ 65 66 def setup_class(self): 67 super().setup_class() 68 self.dut = self.android_devices[0] 69 wutils.wifi_test_device_init(self.dut) 70 req_params = ["passpoint_networks", 71 "boingo_username", 72 "boingo_password",] 73 self.unpack_userparams(req_param_names=req_params,) 74 asserts.assert_true( 75 len(self.passpoint_networks) > 0, 76 "Need at least one Passpoint network.") 77 wutils.wifi_toggle_state(self.dut, True) 78 self.unknown_fqdn = UNKNOWN_FQDN 79 80 81 def setup_test(self): 82 super().setup_test() 83 self.dut.droid.wakeLockAcquireBright() 84 self.dut.droid.wakeUpNow() 85 self.dut.unlock_screen() 86 87 88 def teardown_test(self): 89 super().teardown_test() 90 self.dut.droid.wakeLockRelease() 91 self.dut.droid.goToSleepNow() 92 passpoint_configs = self.dut.droid.getPasspointConfigs() 93 for config in passpoint_configs: 94 wutils.delete_passpoint(self.dut, config) 95 wutils.reset_wifi(self.dut) 96 97 """Helper Functions""" 98 99 100 def install_passpoint_profile(self, passpoint_config): 101 """Install the Passpoint network Profile. 102 103 Args: 104 passpoint_config: A JSON dict of the Passpoint configuration. 105 106 """ 107 asserts.assert_true(WifiEnums.SSID_KEY in passpoint_config, 108 "Key '%s' must be present in network definition." % 109 WifiEnums.SSID_KEY) 110 # Install the Passpoint profile. 111 self.dut.droid.addUpdatePasspointConfig(passpoint_config) 112 113 114 def check_passpoint_connection(self, passpoint_network): 115 """Verify the device is automatically able to connect to the Passpoint 116 network. 117 118 Args: 119 passpoint_network: SSID of the Passpoint network. 120 121 """ 122 ad = self.dut 123 ad.ed.clear_all_events() 124 try: 125 wutils.start_wifi_connection_scan_and_return_status(ad) 126 wutils.wait_for_connect(ad) 127 except: 128 pass 129 # Re-verify we are connected to the correct network. 130 network_info = self.dut.droid.wifiGetConnectionInfo() 131 self.log.info("Network Info: %s" % network_info) 132 if not network_info or not network_info[WifiEnums.SSID_KEY] or \ 133 network_info[WifiEnums.SSID_KEY] not in passpoint_network: 134 raise signals.TestFailure( 135 "Device did not connect to passpoint network.") 136 137 138 def get_configured_passpoint_and_delete(self): 139 """Get configured Passpoint network and delete using its FQDN.""" 140 passpoint_config = self.dut.droid.getPasspointConfigs() 141 if not len(passpoint_config): 142 raise signals.TestFailure("Failed to fetch the list of configured" 143 "passpoint networks.") 144 if not wutils.delete_passpoint(self.dut, passpoint_config[0]): 145 raise signals.TestFailure("Failed to delete Passpoint configuration" 146 " with FQDN = %s" % passpoint_config[0]) 147 148 def ui_automator_boingo(self): 149 """Run UI automator for boingo passpoint.""" 150 # Verify the boingo login page shows 151 asserts.assert_true( 152 uutils.has_element(self.dut, text=BOINGO_UI_TEXT), 153 "Failed to launch boingohotspot login page") 154 155 # Go to the bottom of the page 156 for _ in range(3): 157 self.dut.adb.shell("input swipe 300 900 300 300") 158 159 # Enter username 160 uutils.wait_and_input_text(self.dut, 161 input_text=self.boingo_username, 162 text="", 163 class_name=EDIT_TEXT_CLASS_NAME) 164 self.dut.adb.shell("input keyevent 111") # collapse keyboard 165 self.dut.adb.shell("input swipe 300 900 300 750") # swipe up to show text 166 167 # Enter password 168 uutils.wait_and_input_text(self.dut, 169 input_text=self.boingo_password, 170 text=PASSWORD_TEXT) 171 self.dut.adb.shell("input keyevent 111") # collapse keyboard 172 self.dut.adb.shell("input swipe 300 900 300 750") # swipe up to show text 173 174 # Login 175 uutils.wait_and_click(self.dut, text=PASSPOINT_BUTTON) 176 time.sleep(DEFAULT_TIMEOUT) 177 178 def start_subscription_provisioning(self, state): 179 """Start subscription provisioning with a default provider.""" 180 181 self.unpack_userparams(('osu_configs',)) 182 asserts.assert_true( 183 len(self.osu_configs) > 0, 184 "Need at least one osu config.") 185 osu_config = self.osu_configs[OSU_BOINGO] 186 # Clear all previous events. 187 self.dut.ed.clear_all_events() 188 self.dut.droid.startSubscriptionProvisioning(osu_config) 189 start_time = time.time() 190 while time.time() < start_time + OSU_TEST_TIMEOUT: 191 dut_event = self.dut.ed.pop_event("onProvisioningCallback", 192 DEFAULT_TIMEOUT * 18) 193 if dut_event['data']['tag'] == 'success': 194 self.log.info("Passpoint Provisioning Success") 195 # Reset WiFi after provisioning success. 196 if state == RESET: 197 wutils.reset_wifi(self.dut) 198 time.sleep(DEFAULT_TIMEOUT) 199 # Toggle WiFi after provisioning success. 200 elif state == TOGGLE: 201 wutils.toggle_wifi_off_and_on(self.dut) 202 time.sleep(DEFAULT_TIMEOUT) 203 break 204 if dut_event['data']['tag'] == 'failure': 205 raise signals.TestFailure( 206 "Passpoint Provisioning is failed with %s" % 207 dut_event['data'][ 208 'reason']) 209 break 210 if dut_event['data']['tag'] == 'status': 211 self.log.info( 212 "Passpoint Provisioning status %s" % dut_event['data'][ 213 'status']) 214 if int(dut_event['data']['status']) == 7: 215 self.ui_automator_boingo() 216 # Clear all previous events. 217 self.dut.ed.clear_all_events() 218 219 # Verify device connects to the Passpoint network. 220 time.sleep(DEFAULT_TIMEOUT) 221 current_passpoint = self.dut.droid.wifiGetConnectionInfo() 222 if current_passpoint[WifiEnums.SSID_KEY] not in osu_config[ 223 "expected_ssids"]: 224 raise signals.TestFailure("Device did not connect to the %s" 225 " passpoint network" % osu_config[ 226 "expected_ssids"]) 227 # Delete the Passpoint profile. 228 self.get_configured_passpoint_and_delete() 229 wutils.wait_for_disconnect(self.dut) 230 231 232 """Tests""" 233 234 @test_tracker_info(uuid="b0bc0153-77bb-4594-8f19-cea2c6bd2f43") 235 def test_add_passpoint_network(self): 236 """Add a Passpoint network and verify device connects to it. 237 238 Steps: 239 1. Install a Passpoint Profile. 240 2. Verify the device connects to the required Passpoint SSID. 241 3. Get the Passpoint configuration added above. 242 4. Delete Passpoint configuration using its FQDN. 243 5. Verify that we are disconnected from the Passpoint network. 244 245 """ 246 passpoint_config = self.passpoint_networks[BOINGO] 247 self.install_passpoint_profile(passpoint_config) 248 ssid = passpoint_config[WifiEnums.SSID_KEY] 249 self.check_passpoint_connection(ssid) 250 self.get_configured_passpoint_and_delete() 251 wutils.wait_for_disconnect(self.dut) 252 253 254 @test_tracker_info(uuid="eb29d6e2-a755-4c9c-9e4e-63ea2277a64a") 255 def test_update_passpoint_network(self): 256 """Update a previous Passpoint network and verify device still connects 257 to it. 258 259 1. Install a Passpoint Profile. 260 2. Verify the device connects to the required Passpoint SSID. 261 3. Update the Passpoint Profile. 262 4. Verify device is still connected to the Passpoint SSID. 263 5. Get the Passpoint configuration added above. 264 6. Delete Passpoint configuration using its FQDN. 265 266 """ 267 passpoint_config = self.passpoint_networks[BOINGO] 268 self.install_passpoint_profile(passpoint_config) 269 ssid = passpoint_config[WifiEnums.SSID_KEY] 270 self.check_passpoint_connection(ssid) 271 272 # Update passpoint configuration using the original profile because we 273 # do not have real profile with updated credentials to use. 274 self.install_passpoint_profile(passpoint_config) 275 276 # Wait for a Disconnect event from the supplicant. 277 wutils.wait_for_disconnect(self.dut) 278 279 # Now check if we are again connected with the updated profile. 280 self.check_passpoint_connection(ssid) 281 282 self.get_configured_passpoint_and_delete() 283 wutils.wait_for_disconnect(self.dut) 284 285 286 @test_tracker_info(uuid="b6e8068d-faa1-49f2-b421-c60defaed5f0") 287 def test_add_delete_list_of_passpoint_network(self): 288 """Add multiple passpoint networks, list them and delete one by one. 289 290 1. Install Passpoint Profile A. 291 2. Install Passpoint Profile B. 292 3. Get all the Passpoint configurations added above and verify. 293 6. Ensure all Passpoint configurations can be deleted. 294 295 """ 296 for passpoint_config in self.passpoint_networks[:2]: 297 self.install_passpoint_profile(passpoint_config) 298 time.sleep(DEFAULT_TIMEOUT) 299 configs = self.dut.droid.getPasspointConfigs() 300 # It is length -1 because ATT profile will be handled separately 301 if not len(configs) or len(configs) != len(self.passpoint_networks[:2]): 302 raise signals.TestFailure("Failed to fetch some or all of the" 303 " configured passpoint networks.") 304 for config in configs: 305 if not wutils.delete_passpoint(self.dut, config): 306 raise signals.TestFailure("Failed to delete Passpoint" 307 " configuration with FQDN = %s" % 308 config) 309 310 311 @test_tracker_info(uuid="a53251be-7aaf-41fc-a5f3-63984269d224") 312 def test_delete_unknown_fqdn(self): 313 """Negative test to delete Passpoint profile using an unknown FQDN. 314 315 1. Pass an unknown FQDN for removal. 316 2. Verify that it was not successful. 317 318 """ 319 if wutils.delete_passpoint(self.dut, self.unknown_fqdn): 320 raise signals.TestFailure("Failed because an unknown FQDN" 321 " was successfully deleted.") 322 323 324 @test_tracker_info(uuid="bf03c03a-e649-4e2b-a557-1f791bd98951") 325 def test_passpoint_failover(self): 326 """Add a pair of passpoint networks and test failover when one of the" 327 profiles is removed. 328 329 1. Install a Passpoint Profile A and B. 330 2. Verify device connects to a Passpoint network and get SSID. 331 3. Delete the current Passpoint profile using its FQDN. 332 4. Verify device fails over and connects to the other Passpoint SSID. 333 5. Delete Passpoint configuration using its FQDN. 334 335 """ 336 # Install both Passpoint profiles on the device. 337 passpoint_ssid = list() 338 for passpoint_config in self.passpoint_networks[:2]: 339 passpoint_ssid.extend(passpoint_config[WifiEnums.SSID_KEY]) 340 self.install_passpoint_profile(passpoint_config) 341 time.sleep(DEFAULT_TIMEOUT) 342 343 # Get the current network and the failover network. 344 wutils.wait_for_connect(self.dut) 345 current_passpoint = self.dut.droid.wifiGetConnectionInfo() 346 current_ssid = current_passpoint[WifiEnums.SSID_KEY] 347 if current_ssid not in passpoint_ssid: 348 raise signals.TestFailure("Device did not connect to any of the " 349 "configured Passpoint networks.") 350 351 expected_ssid = self.passpoint_networks[0][WifiEnums.SSID_KEY] 352 if current_ssid in expected_ssid: 353 expected_ssid = self.passpoint_networks[1][WifiEnums.SSID_KEY] 354 355 # Remove the current Passpoint profile. 356 for network in self.passpoint_networks[:2]: 357 if current_ssid in network[WifiEnums.SSID_KEY]: 358 if not wutils.delete_passpoint(self.dut, network["fqdn"]): 359 raise signals.TestFailure("Failed to delete Passpoint" 360 " configuration with FQDN = %s" % 361 network["fqdn"]) 362 # Verify device fails over and connects to the other passpoint network. 363 time.sleep(DEFAULT_TIMEOUT) 364 365 current_passpoint = self.dut.droid.wifiGetConnectionInfo() 366 if current_passpoint[WifiEnums.SSID_KEY] not in expected_ssid: 367 raise signals.TestFailure("Device did not failover to the %s" 368 " passpoint network" % expected_ssid) 369 370 # Delete the remaining Passpoint profile. 371 self.get_configured_passpoint_and_delete() 372 wutils.wait_for_disconnect(self.dut) 373 374 375 @test_tracker_info(uuid="37ae0223-0cb7-43f3-8ba8-474fad6e4b71") 376 def test_install_att_passpoint_profile(self): 377 """Add an AT&T Passpoint profile. 378 379 It is used for only installing the profile for other tests. 380 """ 381 isFound = False 382 for passpoint_config in self.passpoint_networks: 383 if 'att' in passpoint_config['fqdn']: 384 isFound = True 385 self.install_passpoint_profile(passpoint_config) 386 break 387 if not isFound: 388 raise signals.TestFailure("cannot find ATT profile.") 389 390 391 @test_tracker_info(uuid="e3e826d2-7c39-4c37-ab3f-81992d5aa0e8") 392 @WifiBaseTest.wifi_test_wrap 393 def test_att_passpoint_network(self): 394 """Add a AT&T Passpoint network and verify device connects to it. 395 396 Steps: 397 1. Install a AT&T Passpoint Profile. 398 2. Verify the device connects to the required Passpoint SSID. 399 3. Get the Passpoint configuration added above. 400 4. Delete Passpoint configuration using its FQDN. 401 5. Verify that we are disconnected from the Passpoint network. 402 403 """ 404 carriers = ["att"] 405 operator = get_operator_name(self.log, self.dut) 406 asserts.skip_if(operator not in carriers, 407 "Device %s does not have a ATT sim" % self.dut.model) 408 409 passpoint_config = self.passpoint_networks[ATT] 410 self.install_passpoint_profile(passpoint_config) 411 ssid = passpoint_config[WifiEnums.SSID_KEY] 412 self.check_passpoint_connection(ssid) 413 self.get_configured_passpoint_and_delete() 414 wutils.wait_for_disconnect(self.dut) 415 416 417 @test_tracker_info(uuid="c85c81b2-7133-4635-8328-9498169ae802") 418 def test_start_subscription_provisioning(self): 419 self.start_subscription_provisioning(0) 420 421 422 @test_tracker_info(uuid="fd09a643-0d4b-45a9-881a-a771f9707ab1") 423 def test_start_subscription_provisioning_and_reset_wifi(self): 424 self.start_subscription_provisioning(RESET) 425 426 427 @test_tracker_info(uuid="f43ea759-673f-4567-aa11-da3bc2cabf08") 428 def test_start_subscription_provisioning_and_toggle_wifi(self): 429 self.start_subscription_provisioning(TOGGLE) 430 431 @test_tracker_info(uuid="ad6d5eb8-a3c5-4ce0-9e10-d0f201cd0f40") 432 def test_user_override_auto_join_on_passpoint_network(self): 433 """Add a Passpoint network, simulate user change the auto join to false, ensure the device 434 doesn't auto connect to this passponit network 435 436 Steps: 437 1. Install a Passpoint Profile. 438 2. Verify the device connects to the required Passpoint SSID. 439 3. Disable auto join Passpoint configuration using its FQDN. 440 4. disable and enable Wifi toggle, ensure we don't connect back 441 """ 442 passpoint_config = self.passpoint_networks[BOINGO] 443 self.install_passpoint_profile(passpoint_config) 444 ssid = passpoint_config[WifiEnums.SSID_KEY] 445 self.check_passpoint_connection(ssid) 446 self.dut.log.info("Disable auto join on passpoint") 447 self.dut.droid.wifiEnableAutojoinPasspoint(passpoint_config['fqdn'], False) 448 wutils.wifi_toggle_state(self.dut, False) 449 wutils.wifi_toggle_state(self.dut, True) 450 asserts.assert_false( 451 wutils.wait_for_connect(self.dut, ssid, assert_on_fail=False), 452 "Device should not connect.") 453