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