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 time 18import queue 19 20from acts import asserts 21from acts.controllers.android_device import SL4A_APK_NAME 22from acts.test_decorators import test_tracker_info 23from acts_contrib.test_utils.wifi.WifiBaseTest import WifiBaseTest 24import acts_contrib.test_utils.wifi.wifi_test_utils as wutils 25import acts.utils 26 27WifiEnums = wutils.WifiEnums 28SSID = WifiEnums.SSID_KEY 29CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT = 5 30SCANS_REQUIRED_TO_FIND_SSID = 5 31LAST_DISCONNECT_TIMEOUT_MILLIS = 5000 32LAST_DISCONNECT_TIMEOUT_SEC = LAST_DISCONNECT_TIMEOUT_MILLIS / 1000 33PRESCAN_DELAY_SEC = 5 34WIFI_TOGGLE_DELAY_SEC = 3 35DISCONNECT_TIMEOUT_SEC = 20 36 37 38class WifiWakeTest(WifiBaseTest): 39 """ 40 Tests Wifi Wake. 41 42 Test Bed Requirements: 43 * One Android Device 44 * Two APs that can be turned on and off 45 """ 46 def __init__(self, configs): 47 super().__init__(configs) 48 self.enable_packet_log = True 49 50 def setup_class(self): 51 super().setup_class() 52 53 self.dut = self.android_devices[0] 54 wutils.wifi_test_device_init(self.dut) 55 # turn location back on 56 acts.utils.set_location_service(self.dut, True) 57 self.dut.droid.wifiScannerToggleAlwaysAvailable(True) 58 59 self.unpack_userparams(req_param_names=[], 60 opt_param_names=["reference_networks", 61 "google_pixel_watch_models"]) 62 63 if "AccessPoint" in self.user_params: 64 self.legacy_configure_ap_and_start(mirror_ap=False, ap_count=2) 65 elif "OpenWrtAP" in self.user_params: 66 self.configure_openwrt_ap_and_start(wpa_network=True, 67 ap_count=2) 68 69 # use 2G since Wifi Wake does not work if an AP is on a 5G DFS channel 70 self.ap_a = self.reference_networks[0]["2g"] 71 self.ap_b = self.reference_networks[1]["2g"] 72 73 self.ap_a_atten = self.attenuators[0] 74 self.ap_b_atten = self.attenuators[2] 75 if "OpenWrtAP" in self.user_params: 76 self.ap_b_atten = self.attenuators[1] 77 78 # TODO(b/119040540): this method of disabling/re-enabling Wifi on APs is 79 # hacky, switch to using public methods when they are implemented 80 def ap_a_off(self): 81 if "OpenWrtAP" in self.user_params: 82 self.access_points[0].stop_ap() 83 self.log.info('Turned AP A off') 84 return 85 ap_a_hostapd = self.access_points[0]._aps['wlan0'].hostapd 86 if ap_a_hostapd.is_alive(): 87 ap_a_hostapd.stop() 88 self.log.info('Turned AP A off') 89 90 def ap_a_on(self): 91 if "OpenWrtAP" in self.user_params: 92 self.access_points[0].start_ap() 93 self.log.info('Turned AP A on') 94 return 95 ap_a_hostapd = self.access_points[0]._aps['wlan0'].hostapd 96 if not ap_a_hostapd.is_alive(): 97 ap_a_hostapd.start(ap_a_hostapd.config) 98 self.log.info('Turned AP A on') 99 100 def ap_b_off(self): 101 if "OpenWrtAP" in self.user_params: 102 self.access_points[1].stop_ap() 103 self.log.info('Turned AP B off') 104 return 105 ap_b_hostapd = self.access_points[1]._aps['wlan0'].hostapd 106 if ap_b_hostapd.is_alive(): 107 ap_b_hostapd.stop() 108 self.log.info('Turned AP B off') 109 110 def ap_b_on(self): 111 if "OpenWrtAP" in self.user_params: 112 self.access_points[1].start_ap() 113 self.log.info('Turned AP B on') 114 return 115 ap_b_hostapd = self.access_points[1]._aps['wlan0'].hostapd 116 if not ap_b_hostapd.is_alive(): 117 ap_b_hostapd.start(ap_b_hostapd.config) 118 self.log.info('Turned AP B on') 119 120 def setup_test(self): 121 super().setup_test() 122 self.dut.droid.wakeLockAcquireBright() 123 self.dut.droid.wakeUpNow() 124 self.ap_a_on() 125 self.ap_b_on() 126 self.ap_a_atten.set_atten(0) 127 self.ap_b_atten.set_atten(0) 128 wutils.reset_wifi(self.dut) 129 wutils.wifi_toggle_state(self.dut, new_state=True) 130 # clear events from event dispatcher 131 self.dut.droid.wifiStartTrackingStateChange() 132 self.dut.droid.wifiStopTrackingStateChange() 133 self.dut.ed.clear_all_events() 134 135 def teardown_test(self): 136 super().teardown_test() 137 self.dut.droid.wakeLockRelease() 138 self.dut.droid.goToSleepNow() 139 140 def find_ssid_in_scan_results(self, scan_results_batches, ssid): 141 scan_results_batch = scan_results_batches[0] 142 scan_results = scan_results_batch["ScanResults"] 143 for scan_result in scan_results: 144 if ssid == scan_result["SSID"]: 145 return True 146 return False 147 148 def do_location_scan(self, num_times=1, ssid_to_find=None): 149 scan_settings = { 150 "band": wutils.WifiEnums.WIFI_BAND_BOTH, 151 "periodInMs": 0, 152 "reportEvents": wutils.WifiEnums.REPORT_EVENT_AFTER_EACH_SCAN 153 } 154 155 wifi_chs = wutils.WifiChannelUS(self.dut.model) 156 stime_channel = 47 # dwell time plus 2ms 157 leeway = 10 158 159 for i in range(num_times): 160 self.log.info("Scan count: {}".format(i)) 161 data = wutils.start_wifi_single_scan(self.dut, scan_settings) 162 idx = data["Index"] 163 scan_rt = data["ScanElapsedRealtime"] 164 self.log.debug( 165 "Wifi single shot scan started index: %s at real time: %s", idx, 166 scan_rt) 167 # generating event wait time from scan setting plus leeway 168 scan_time, scan_channels = wutils.get_scan_time_and_channels( 169 wifi_chs, scan_settings, stime_channel) 170 wait_time = int(scan_time / 1000) + leeway 171 # track number of result received 172 result_received = 0 173 try: 174 for _ in range(1, 3): 175 event_name = "{}{}onResults".format("WifiScannerScan", idx) 176 self.log.debug("Waiting for event: %s for time %s", 177 event_name, wait_time) 178 event = self.dut.ed.pop_event(event_name, wait_time) 179 self.log.debug("Event received: %s", event) 180 result_received += 1 181 scan_results_batches = event["data"]["Results"] 182 if ssid_to_find and self.find_ssid_in_scan_results( 183 scan_results_batches, ssid_to_find): 184 return 185 except queue.Empty as error: 186 asserts.assert_true( 187 result_received >= 1, 188 "Event did not triggered for single shot {}".format(error)) 189 finally: 190 self.dut.droid.wifiScannerStopScan(idx) 191 # For single shot number of result received and length of result 192 # should be one 193 asserts.assert_true( 194 result_received == 1, 195 "Test fail because received result {}".format( 196 result_received)) 197 198 @test_tracker_info(uuid="372b9b74-4241-46ce-8f18-e6a97d3a3452") 199 def test_no_reconnect_manual_disable_wifi(self): 200 """ 201 Tests that Wifi Wake does not reconnect to a network if the user turned 202 off Wifi while connected to that network and the user has not moved 203 (i.e. moved out of range of the AP then came back). 204 """ 205 if "google_pixel_watch_models" in self.user_params: 206 if self.dut.model in self.user_params["google_pixel_watch_models"]: 207 wutils.disable_wear_wifimediator(self.dut, True) 208 209 wutils.wifi_connect(self.dut, self.ap_a, num_of_tries=5) 210 wutils.wifi_toggle_state(self.dut, new_state=False) 211 time.sleep(PRESCAN_DELAY_SEC) 212 self.do_location_scan( 213 2 * CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2) 214 asserts.assert_false( 215 self.dut.droid.wifiCheckState(), 216 "Expect Wifi Wake to not enable Wifi, but Wifi was enabled.") 217 218 if "google_pixel_watch_models" in self.user_params: 219 if self.dut.model in self.user_params["google_pixel_watch_models"]: 220 wutils.disable_wear_wifimediator(self.dut, False) 221 222 @test_tracker_info(uuid="ec7a54a5-f293-43f5-a1dd-d41679aa1825") 223 def test_reconnect_wifi_saved_network(self): 224 """Tests that Wifi Wake re-enables Wifi for a saved network.""" 225 wutils.wifi_connect(self.dut, self.ap_a, num_of_tries=5) 226 wutils.wifi_connect(self.dut, self.ap_b, num_of_tries=5) 227 self.dut.ed.clear_all_events() 228 self.ap_a_off() 229 self.ap_b_off() 230 wutils.wait_for_disconnect(self.dut, DISCONNECT_TIMEOUT_SEC) 231 self.log.info("Wifi Disconnected") 232 self.do_location_scan(2) 233 time.sleep(LAST_DISCONNECT_TIMEOUT_SEC * 1.2) 234 wutils.wifi_toggle_state(self.dut, new_state=False) 235 time.sleep(PRESCAN_DELAY_SEC) 236 self.do_location_scan(2 * CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2) 237 238 self.ap_a_on() 239 self.do_location_scan( 240 SCANS_REQUIRED_TO_FIND_SSID, self.ap_a[wutils.WifiEnums.SSID_KEY]) 241 time.sleep(WIFI_TOGGLE_DELAY_SEC) 242 asserts.assert_true( 243 self.dut.droid.wifiCheckState(), 244 "Expect Wifi Wake to enable Wifi, but Wifi is disabled.") 245 246 @test_tracker_info(uuid="3cecd1c5-54bc-44a2-86f7-ad84625bf094") 247 def test_reconnect_wifi_network_suggestion(self): 248 """Tests that Wifi Wake re-enables Wifi for app provided suggestion.""" 249 self.dut.log.info("Adding network suggestions") 250 asserts.assert_true( 251 self.dut.droid.wifiAddNetworkSuggestions([self.ap_a]), 252 "Failed to add suggestions") 253 asserts.assert_true( 254 self.dut.droid.wifiAddNetworkSuggestions([self.ap_b]), 255 "Failed to add suggestions") 256 # Enable suggestions by the app. 257 self.dut.log.debug("Enabling suggestions from test") 258 self.dut.adb.shell("cmd wifi network-suggestions-set-user-approved" 259 + " " + SL4A_APK_NAME + " yes") 260 # Ensure network is seen in scan results & auto-connected to. 261 self.do_location_scan(2) 262 wutils.wait_for_connect(self.dut) 263 current_network = self.dut.droid.wifiGetConnectionInfo() 264 self.dut.ed.clear_all_events() 265 if current_network[SSID] == self.ap_a[SSID]: 266 # connected to AP A, so turn AP B off first to prevent the 267 # device from immediately reconnecting to AP B 268 self.ap_b_off() 269 self.ap_a_off() 270 else: 271 self.ap_a_off() 272 self.ap_b_off() 273 274 wutils.wait_for_disconnect(self.dut, DISCONNECT_TIMEOUT_SEC) 275 self.log.info("Wifi Disconnected") 276 self.do_location_scan(2) 277 time.sleep(LAST_DISCONNECT_TIMEOUT_SEC * 1.2) 278 wutils.wifi_toggle_state(self.dut, new_state=False) 279 time.sleep(PRESCAN_DELAY_SEC) 280 self.do_location_scan(2 * CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2) 281 282 self.ap_a_on() 283 self.do_location_scan( 284 SCANS_REQUIRED_TO_FIND_SSID, self.ap_a[wutils.WifiEnums.SSID_KEY]) 285 time.sleep(WIFI_TOGGLE_DELAY_SEC) 286 asserts.assert_true( 287 self.dut.droid.wifiCheckState(), 288 "Expect Wifi Wake to enable Wifi, but Wifi is disabled.") 289 290 @test_tracker_info(uuid="6c77ca9b-ff34-4bc7-895f-cc7340e0e645") 291 def test_reconnect_wifi_move_back_in_range(self): 292 """ 293 Tests that Wifi Wake re-enables Wifi if the device moves out of range of 294 the AP then came back. 295 """ 296 wutils.wifi_connect(self.dut, self.ap_a, num_of_tries=5) 297 wutils.wifi_toggle_state(self.dut, new_state=False) 298 time.sleep(PRESCAN_DELAY_SEC) 299 # init Wakeup Lock with AP A 300 self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2) 301 self.ap_a_off() 302 # evict AP A from Wakeup Lock 303 self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2) 304 self.ap_a_on() 305 self.do_location_scan( 306 SCANS_REQUIRED_TO_FIND_SSID, self.ap_a[wutils.WifiEnums.SSID_KEY]) 307 time.sleep(WIFI_TOGGLE_DELAY_SEC) 308 asserts.assert_true( 309 self.dut.droid.wifiCheckState(), 310 "Expect Wifi Wake to enable Wifi, but Wifi is disabled.") 311 312 @test_tracker_info(uuid="08e8284a-a523-48f3-b9ea-9c6bf27d711e") 313 def test_no_reconnect_to_flaky_ap(self): 314 """ 315 Tests that Wifi Wake does not reconnect to flaky networks. 316 If a network sporadically connects and disconnects, and the user turns 317 off Wifi even during the disconnected phase, Wifi Wake should not 318 re-enable Wifi for that network. 319 """ 320 wutils.wifi_connect(self.dut, self.ap_a, num_of_tries=5) 321 self.ap_a_off() 322 time.sleep(LAST_DISCONNECT_TIMEOUT_SEC * 0.4) 323 wutils.wifi_toggle_state(self.dut, new_state=False) 324 time.sleep(PRESCAN_DELAY_SEC) 325 self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2) 326 self.ap_a_on() 327 self.do_location_scan( 328 2 * CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2) 329 asserts.assert_false( 330 self.dut.droid.wifiCheckState(), 331 "Expect Wifi Wake to not enable Wifi, but Wifi was enabled.") 332 333 @test_tracker_info(uuid="b990a8f7-e3a0-4774-89cf-2067ccd64903") 334 def test_reconnect_wifi_disabled_after_disconnecting(self): 335 """ 336 Tests that Wifi Wake reconnects to a network if Wifi was disabled long 337 after disconnecting from a network. 338 """ 339 wutils.wifi_connect(self.dut, self.ap_a, num_of_tries=5) 340 self.dut.ed.clear_all_events() 341 self.ap_a_off() 342 wutils.wait_for_disconnect(self.dut, DISCONNECT_TIMEOUT_SEC) 343 self.log.info("Wifi Disconnected") 344 self.do_location_scan(2) 345 time.sleep(LAST_DISCONNECT_TIMEOUT_SEC * 1.2) 346 wutils.wifi_toggle_state(self.dut, new_state=False) 347 time.sleep(PRESCAN_DELAY_SEC) 348 self.do_location_scan(2 * CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2) 349 self.ap_a_on() 350 self.do_location_scan( 351 SCANS_REQUIRED_TO_FIND_SSID, self.ap_a[wutils.WifiEnums.SSID_KEY]) 352 time.sleep(WIFI_TOGGLE_DELAY_SEC) 353 asserts.assert_true( 354 self.dut.droid.wifiCheckState(), 355 "Expect Wifi Wake to enable Wifi, but Wifi is disabled.") 356 357 @test_tracker_info(uuid="bb217794-d3ee-4fb9-87ff-7a594d0223b0") 358 def test_no_reconnect_if_exists_ap_in_wakeup_lock(self): 359 """ 360 2 APs in Wakeup Lock, user moves out of range of one AP but stays in 361 range of the other, should not reconnect when user moves back in range 362 of both. 363 """ 364 wutils.wifi_connect(self.dut, self.ap_a, num_of_tries=5) 365 wutils.wifi_connect(self.dut, self.ap_b, num_of_tries=5) 366 wutils.wifi_toggle_state(self.dut, new_state=False) 367 time.sleep(PRESCAN_DELAY_SEC) 368 self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2) 369 self.ap_b_off() 370 self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2) 371 self.ap_b_on() 372 self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2) 373 asserts.assert_false( 374 self.dut.droid.wifiCheckState(), 375 "Expect Wifi Wake to not enable Wifi, but Wifi was enabled.") 376 377 @test_tracker_info(uuid="567a0663-4ce0-488d-8fe2-db79a3ebf068") 378 def test_reconnect_if_both_ap_evicted_from_wakeup_lock(self): 379 """ 380 2 APs in Wakeup Lock, user moves out of range of both APs, should 381 reconnect when user moves back in range of either AP. 382 """ 383 wutils.wifi_connect(self.dut, self.ap_a, num_of_tries=5) 384 wutils.wifi_connect(self.dut, self.ap_b, num_of_tries=5) 385 wutils.wifi_toggle_state(self.dut, new_state=False) 386 time.sleep(PRESCAN_DELAY_SEC) 387 self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2) 388 self.ap_a_off() 389 self.ap_b_off() 390 self.do_location_scan(CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2) 391 self.ap_a_on() 392 self.do_location_scan( 393 SCANS_REQUIRED_TO_FIND_SSID, self.ap_a[wutils.WifiEnums.SSID_KEY]) 394 time.sleep(WIFI_TOGGLE_DELAY_SEC) 395 asserts.assert_true( 396 self.dut.droid.wifiCheckState(), 397 "Expect Wifi Wake to enable Wifi, but Wifi is disabled.") 398 399 @test_tracker_info(uuid="d67657c8-3de3-46a6-a103-428cdab89423") 400 def test_reconnect_to_better_saved_network(self): 401 """ 402 2 saved APs, one attenuated, one unattenuated, Wifi Wake should connect 403 to the unattenuated AP 404 """ 405 wutils.wifi_connect(self.dut, self.ap_a, num_of_tries=5) 406 wutils.wifi_connect(self.dut, self.ap_b, num_of_tries=5) 407 self.dut.ed.clear_all_events() 408 self.ap_a_off() 409 self.ap_b_off() 410 wutils.wait_for_disconnect(self.dut, DISCONNECT_TIMEOUT_SEC) 411 self.log.info("Wifi Disconnected") 412 413 if "google_pixel_watch_models" in self.user_params: 414 if self.dut.model in self.user_params["google_pixel_watch_models"]: 415 wutils.disable_wear_wifimediator(self.dut, True) 416 417 self.do_location_scan(2) 418 time.sleep(LAST_DISCONNECT_TIMEOUT_SEC * 1.2) 419 wutils.wifi_toggle_state(self.dut, new_state=False) 420 time.sleep(PRESCAN_DELAY_SEC) 421 self.do_location_scan(2 * CONSECUTIVE_MISSED_SCANS_REQUIRED_TO_EVICT + 2) 422 423 self.ap_a_on() 424 self.ap_b_on() 425 self.ap_a_atten.set_atten(30) 426 self.ap_b_atten.set_atten(0) 427 428 if "google_pixel_watch_models" in self.user_params: 429 if self.dut.model in self.user_params["google_pixel_watch_models"]: 430 wutils.disable_wear_wifimediator(self.dut, False) 431 432 self.do_location_scan( 433 SCANS_REQUIRED_TO_FIND_SSID, self.ap_b[wutils.WifiEnums.SSID_KEY]) 434 time.sleep(WIFI_TOGGLE_DELAY_SEC) 435 asserts.assert_true( 436 self.dut.droid.wifiCheckState(), 437 "Expect Wifi Wake to enable Wifi, but Wifi is disabled.") 438 expected_ssid = self.ap_b[wutils.WifiEnums.SSID_KEY] 439 wutils.wait_for_connect(self.dut, expected_ssid) 440