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