1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.wifi; 18 19import static android.net.wifi.WifiManager.WIFI_MODE_FULL; 20 21import static com.android.server.wifi.WifiController.CMD_AP_STOPPED; 22import static com.android.server.wifi.WifiController.CMD_DEVICE_IDLE; 23import static com.android.server.wifi.WifiController.CMD_EMERGENCY_CALL_STATE_CHANGED; 24import static com.android.server.wifi.WifiController.CMD_EMERGENCY_MODE_CHANGED; 25import static com.android.server.wifi.WifiController.CMD_RESTART_WIFI; 26import static com.android.server.wifi.WifiController.CMD_SET_AP; 27import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED; 28 29import static org.junit.Assert.assertEquals; 30import static org.mockito.Mockito.*; 31 32import android.content.ContentResolver; 33import android.content.Context; 34import android.database.ContentObserver; 35import android.net.Uri; 36import android.os.WorkSource; 37import android.os.test.TestLooper; 38import android.test.suitebuilder.annotation.SmallTest; 39import android.util.Log; 40 41import com.android.internal.util.IState; 42import com.android.internal.util.StateMachine; 43 44import org.junit.After; 45import org.junit.Before; 46import org.junit.Test; 47import org.mockito.ArgumentCaptor; 48import org.mockito.InOrder; 49import org.mockito.Mock; 50import org.mockito.MockitoAnnotations; 51 52import java.io.ByteArrayOutputStream; 53import java.io.PrintWriter; 54import java.lang.reflect.Method; 55import java.util.List; 56 57/** 58 * Test WifiController for changes in and out of ECM and SoftAP modes. 59 */ 60@SmallTest 61public class WifiControllerTest { 62 63 private static final String TAG = "WifiControllerTest"; 64 65 private void dumpState() { 66 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 67 PrintWriter writer = new PrintWriter(stream); 68 mWifiController.dump(null, writer, null); 69 writer.flush(); 70 Log.d(TAG, "WifiStateMachine state -" + stream.toString()); 71 } 72 73 private IState getCurrentState() throws Exception { 74 Method method = StateMachine.class.getDeclaredMethod("getCurrentState"); 75 method.setAccessible(true); 76 return (IState) method.invoke(mWifiController); 77 } 78 79 private void initializeSettingsStore() throws Exception { 80 when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); 81 when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); 82 when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); 83 } 84 85 TestLooper mLooper; 86 @Mock Context mContext; 87 @Mock WifiServiceImpl mService; 88 @Mock FrameworkFacade mFacade; 89 @Mock WifiSettingsStore mSettingsStore; 90 @Mock WifiStateMachine mWifiStateMachine; 91 @Mock WifiLockManager mWifiLockManager; 92 @Mock ContentResolver mContentResolver; 93 94 ContentObserver mStayAwakeObserver; 95 ContentObserver mWifiIdleTimeObserver; 96 ContentObserver mWifiSleepPolicyObserver; 97 98 WifiController mWifiController; 99 100 @Before 101 public void setUp() throws Exception { 102 MockitoAnnotations.initMocks(this); 103 104 mLooper = new TestLooper(); 105 106 initializeSettingsStore(); 107 108 when(mContext.getContentResolver()).thenReturn(mContentResolver); 109 ArgumentCaptor<ContentObserver> observerCaptor = 110 ArgumentCaptor.forClass(ContentObserver.class); 111 112 mWifiController = new WifiController(mContext, mWifiStateMachine, 113 mSettingsStore, mWifiLockManager, mLooper.getLooper(), mFacade); 114 verify(mFacade, times(3)).registerContentObserver(eq(mContext), any(Uri.class), eq(false), 115 observerCaptor.capture()); 116 117 List<ContentObserver> observers = observerCaptor.getAllValues(); 118 mStayAwakeObserver = observers.get(0); 119 mWifiIdleTimeObserver = observers.get(1); 120 mWifiSleepPolicyObserver = observers.get(2); 121 122 mWifiController.start(); 123 mLooper.dispatchAll(); 124 } 125 126 @After 127 public void cleanUp() { 128 mLooper.dispatchAll(); 129 } 130 131 @Test 132 public void enableWifi() throws Exception { 133 assertEquals("StaDisabledWithScanState", getCurrentState().getName()); 134 135 when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true); 136 mWifiController.sendMessage(CMD_WIFI_TOGGLED); 137 mLooper.dispatchAll(); 138 assertEquals("DeviceActiveState", getCurrentState().getName()); 139 140 when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); 141 mWifiController.sendMessage(CMD_WIFI_TOGGLED); 142 mLooper.dispatchAll(); 143 assertEquals("StaDisabledWithScanState", getCurrentState().getName()); 144 145 when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true); 146 mWifiController.sendMessage(CMD_WIFI_TOGGLED); 147 mLooper.dispatchAll(); 148 assertEquals("DeviceActiveState", getCurrentState().getName()); 149 } 150 151 @Test 152 public void testEcmOn() throws Exception { 153 enableWifi(); 154 155 // Test with WifiDisableInECBM turned on: 156 when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); 157 doTestEcm(true); 158 } 159 160 @Test 161 public void testEcmOff() throws Exception { 162 enableWifi(); 163 164 // Test with WifiDisableInECBM turned off 165 when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(false); 166 doTestEcm(false); 167 } 168 169 private void assertInEcm(boolean ecmEnabled) throws Exception { 170 if (ecmEnabled) { 171 assertEquals("EcmState", getCurrentState().getName()); 172 } else { 173 assertEquals("DeviceActiveState", getCurrentState().getName()); 174 } 175 } 176 177 178 private void doTestEcm(boolean ecmEnabled) throws Exception { 179 180 // test ecm changed 181 mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); 182 mLooper.dispatchAll(); 183 assertInEcm(ecmEnabled); 184 185 mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 0); 186 mLooper.dispatchAll(); 187 assertEquals("DeviceActiveState", getCurrentState().getName()); 188 189 // test call state changed 190 mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 1); 191 mLooper.dispatchAll(); 192 assertInEcm(ecmEnabled); 193 194 mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 0); 195 mLooper.dispatchAll(); 196 assertEquals("DeviceActiveState", getCurrentState().getName()); 197 198 199 // test both changed (variation 1 - the good case) 200 mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 1); 201 mLooper.dispatchAll(); 202 assertInEcm(ecmEnabled); 203 204 mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); 205 mLooper.dispatchAll(); 206 assertInEcm(ecmEnabled); 207 208 mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 0); 209 mLooper.dispatchAll(); 210 assertInEcm(ecmEnabled); 211 212 mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 0); 213 mLooper.dispatchAll(); 214 assertEquals("DeviceActiveState", getCurrentState().getName()); 215 216 // test both changed (variation 2 - emergency call in ecm) 217 mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); 218 mLooper.dispatchAll(); 219 assertInEcm(ecmEnabled); 220 221 mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 1); 222 mLooper.dispatchAll(); 223 assertInEcm(ecmEnabled); 224 225 mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 0); 226 mLooper.dispatchAll(); 227 assertInEcm(ecmEnabled); 228 229 mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 0); 230 mLooper.dispatchAll(); 231 assertEquals("DeviceActiveState", getCurrentState().getName()); 232 233 // test both changed (variation 3 - not so good order of events) 234 mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 1); 235 mLooper.dispatchAll(); 236 assertInEcm(ecmEnabled); 237 238 mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 1); 239 mLooper.dispatchAll(); 240 assertInEcm(ecmEnabled); 241 242 mWifiController.sendMessage(CMD_EMERGENCY_MODE_CHANGED, 0); 243 mLooper.dispatchAll(); 244 assertInEcm(ecmEnabled); 245 246 mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 0); 247 mLooper.dispatchAll(); 248 assertEquals("DeviceActiveState", getCurrentState().getName()); 249 250 // test that Wifi toggle doesn't exit Ecm 251 mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 1); 252 mLooper.dispatchAll(); 253 assertInEcm(ecmEnabled); 254 255 when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true); 256 mWifiController.sendMessage(CMD_WIFI_TOGGLED); 257 mLooper.dispatchAll(); 258 assertInEcm(ecmEnabled); 259 260 mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 0); 261 mLooper.dispatchAll(); 262 assertEquals("DeviceActiveState", getCurrentState().getName()); 263 } 264 265 /** 266 * When AP mode is enabled and wifi was previously in AP mode, we should return to 267 * DeviceActiveState after the AP is disabled. 268 * Enter DeviceActiveState, activate AP mode, disable AP mode. 269 * <p> 270 * Expected: AP should successfully start and exit, then return to DeviceActiveState. 271 */ 272 @Test 273 public void testReturnToDeviceActiveStateAfterAPModeShutdown() throws Exception { 274 enableWifi(); 275 assertEquals("DeviceActiveState", getCurrentState().getName()); 276 277 mWifiController.obtainMessage(CMD_SET_AP, 1, 0).sendToTarget(); 278 mLooper.dispatchAll(); 279 assertEquals("ApEnabledState", getCurrentState().getName()); 280 281 when(mSettingsStore.getWifiSavedState()).thenReturn(1); 282 mWifiController.obtainMessage(CMD_AP_STOPPED).sendToTarget(); 283 mLooper.dispatchAll(); 284 285 InOrder inOrder = inOrder(mWifiStateMachine); 286 inOrder.verify(mWifiStateMachine).setSupplicantRunning(true); 287 inOrder.verify(mWifiStateMachine).setOperationalMode(WifiStateMachine.CONNECT_MODE); 288 assertEquals("DeviceActiveState", getCurrentState().getName()); 289 } 290 291 /** 292 * When AP mode is enabled and wifi is toggled on, we should transition to 293 * DeviceActiveState after the AP is disabled. 294 * Enter DeviceActiveState, activate AP mode, toggle WiFi. 295 * <p> 296 * Expected: AP should successfully start and exit, then return to DeviceActiveState. 297 */ 298 @Test 299 public void testReturnToDeviceActiveStateAfterWifiEnabledShutdown() throws Exception { 300 enableWifi(); 301 assertEquals("DeviceActiveState", getCurrentState().getName()); 302 303 mWifiController.obtainMessage(CMD_SET_AP, 1, 0).sendToTarget(); 304 mLooper.dispatchAll(); 305 assertEquals("ApEnabledState", getCurrentState().getName()); 306 307 when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true); 308 mWifiController.obtainMessage(CMD_WIFI_TOGGLED).sendToTarget(); 309 mWifiController.obtainMessage(CMD_AP_STOPPED).sendToTarget(); 310 mLooper.dispatchAll(); 311 312 InOrder inOrder = inOrder(mWifiStateMachine); 313 inOrder.verify(mWifiStateMachine).setSupplicantRunning(true); 314 inOrder.verify(mWifiStateMachine).setOperationalMode(WifiStateMachine.CONNECT_MODE); 315 assertEquals("DeviceActiveState", getCurrentState().getName()); 316 } 317 318 /** 319 * When the wifi device is idle, AP mode is enabled and disabled 320 * we should return to the appropriate Idle state. 321 * Enter DeviceActiveState, indicate idle device, activate AP mode, disable AP mode. 322 * <p> 323 * Expected: AP should successfully start and exit, then return to a device idle state. 324 */ 325 @Test 326 public void testReturnToDeviceIdleStateAfterAPModeShutdown() throws Exception { 327 enableWifi(); 328 assertEquals("DeviceActiveState", getCurrentState().getName()); 329 330 // make sure mDeviceIdle is set to true 331 when(mWifiLockManager.getStrongestLockMode()).thenReturn(WIFI_MODE_FULL); 332 when(mWifiLockManager.createMergedWorkSource()).thenReturn(new WorkSource()); 333 mWifiController.sendMessage(CMD_DEVICE_IDLE); 334 mLooper.dispatchAll(); 335 assertEquals("FullLockHeldState", getCurrentState().getName()); 336 337 mWifiController.obtainMessage(CMD_SET_AP, 1, 0).sendToTarget(); 338 mLooper.dispatchAll(); 339 assertEquals("ApEnabledState", getCurrentState().getName()); 340 341 when(mSettingsStore.getWifiSavedState()).thenReturn(1); 342 mWifiController.obtainMessage(CMD_AP_STOPPED).sendToTarget(); 343 mLooper.dispatchAll(); 344 345 InOrder inOrder = inOrder(mWifiStateMachine); 346 inOrder.verify(mWifiStateMachine).setSupplicantRunning(true); 347 inOrder.verify(mWifiStateMachine).setOperationalMode(WifiStateMachine.CONNECT_MODE); 348 assertEquals("FullLockHeldState", getCurrentState().getName()); 349 } 350 351 /** 352 * The command to trigger a WiFi reset should not trigger any action by WifiController if we 353 * are not in STA mode. 354 * WiFi is not in connect mode, so any calls to reset the wifi stack due to connection failures 355 * should be ignored. 356 * Create and start WifiController in ApStaDisabledState, send command to restart WiFi 357 * <p> 358 * Expected: WiFiController should not call WifiStateMachine.setSupplicantRunning(false) 359 */ 360 @Test 361 public void testRestartWifiStackInApStaDisabledState() throws Exception { 362 // Start a new WifiController with wifi disabled 363 when(mSettingsStore.isAirplaneModeOn()).thenReturn(false); 364 when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); 365 when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(false); 366 367 when(mContext.getContentResolver()).thenReturn(mock(ContentResolver.class)); 368 369 mWifiController = new WifiController(mContext, mWifiStateMachine, 370 mSettingsStore, mWifiLockManager, mLooper.getLooper(), mFacade); 371 372 mWifiController.start(); 373 mLooper.dispatchAll(); 374 375 reset(mWifiStateMachine); 376 assertEquals("ApStaDisabledState", getCurrentState().getName()); 377 mWifiController.sendMessage(CMD_RESTART_WIFI); 378 mLooper.dispatchAll(); 379 verifyZeroInteractions(mWifiStateMachine); 380 } 381 382 /** 383 * The command to trigger a WiFi reset should not trigger any action by WifiController if we 384 * are not in STA mode, even if scans are allowed. 385 * WiFi is not in connect mode, so any calls to reset the wifi stack due to connection failures 386 * should be ignored. 387 * Create and start WifiController in StaDisablediWithScanState, send command to restart WiFi 388 * <p> 389 * Expected: WiFiController should not call WifiStateMachine.setSupplicantRunning(false) 390 */ 391 @Test 392 public void testRestartWifiStackInStaDisabledWithScanState() throws Exception { 393 reset(mWifiStateMachine); 394 assertEquals("StaDisabledWithScanState", getCurrentState().getName()); 395 mWifiController.sendMessage(CMD_RESTART_WIFI); 396 mLooper.dispatchAll(); 397 verifyZeroInteractions(mWifiStateMachine); 398 } 399 400 /** 401 * The command to trigger a WiFi reset should trigger a wifi reset in WifiStateMachine through 402 * the WifiStateMachine.setSupplicantRunning(false) call when in STA mode. 403 * WiFi is in connect mode, calls to reset the wifi stack due to connection failures 404 * should trigger a supplicant stop, and subsequently, a driver reload. 405 * Create and start WifiController in DeviceActiveState, send command to restart WiFi 406 * <p> 407 * Expected: WiFiController should call WifiStateMachine.setSupplicantRunning(false), 408 * WifiStateMachine should enter CONNECT_MODE and the wifi driver should be started. 409 */ 410 @Test 411 public void testRestartWifiStackInStaEnabledState() throws Exception { 412 enableWifi(); 413 414 reset(mWifiStateMachine); 415 assertEquals("DeviceActiveState", getCurrentState().getName()); 416 mWifiController.sendMessage(CMD_RESTART_WIFI); 417 mLooper.dispatchAll(); 418 InOrder inOrder = inOrder(mWifiStateMachine); 419 inOrder.verify(mWifiStateMachine).setSupplicantRunning(false); 420 inOrder.verify(mWifiStateMachine).setSupplicantRunning(true); 421 inOrder.verify(mWifiStateMachine).setOperationalMode(WifiStateMachine.CONNECT_MODE); 422 assertEquals("DeviceActiveState", getCurrentState().getName()); 423 } 424 425 /** 426 * The command to trigger a WiFi reset should not trigger a reset when in ECM mode. 427 * Enable wifi and enter ECM state, send command to restart wifi. 428 * <p> 429 * Expected: The command to trigger a wifi reset should be ignored and we should remain in ECM 430 * mode. 431 */ 432 @Test 433 public void testRestartWifiStackDoesNotExitECMMode() throws Exception { 434 enableWifi(); 435 assertEquals("DeviceActiveState", getCurrentState().getName()); 436 when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); 437 438 mWifiController.sendMessage(CMD_EMERGENCY_CALL_STATE_CHANGED, 1); 439 mLooper.dispatchAll(); 440 assertInEcm(true); 441 442 reset(mWifiStateMachine); 443 mWifiController.sendMessage(CMD_RESTART_WIFI); 444 mLooper.dispatchAll(); 445 assertInEcm(true); 446 verifyZeroInteractions(mWifiStateMachine); 447 } 448 449 /** 450 * The command to trigger a WiFi reset should not trigger a reset when in AP mode. 451 * Enter AP mode, send command to restart wifi. 452 * <p> 453 * Expected: The command to trigger a wifi reset should be ignored and we should remain in AP 454 * mode. 455 */ 456 @Test 457 public void testRestartWifiStackDoesNotExitAPMode() throws Exception { 458 mWifiController.obtainMessage(CMD_SET_AP, 1).sendToTarget(); 459 mLooper.dispatchAll(); 460 assertEquals("ApEnabledState", getCurrentState().getName()); 461 462 reset(mWifiStateMachine); 463 mWifiController.sendMessage(CMD_RESTART_WIFI); 464 mLooper.dispatchAll(); 465 verifyZeroInteractions(mWifiStateMachine); 466 } 467} 468