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 org.junit.Assert.assertEquals; 20import static org.mockito.Mockito.*; 21 22import android.net.wifi.IApInterface; 23import android.net.wifi.IWificond; 24import android.net.wifi.WifiConfiguration; 25import android.net.wifi.WifiManager; 26import android.os.INetworkManagementService; 27import android.os.test.TestLooper; 28import android.test.suitebuilder.annotation.SmallTest; 29import android.util.Log; 30 31import org.junit.After; 32import org.junit.Before; 33import org.junit.Test; 34import org.mockito.Mock; 35import org.mockito.MockitoAnnotations; 36import org.mockito.invocation.InvocationOnMock; 37import org.mockito.stubbing.Answer; 38 39/** 40 * Unit tests for {@link com.android.server.wifi.WifiStateMachinePrime}. 41 */ 42@SmallTest 43public class WifiStateMachinePrimeTest { 44 public static final String TAG = "WifiStateMachinePrimeTest"; 45 46 private static final String CLIENT_MODE_STATE_STRING = "ClientModeState"; 47 private static final String SCAN_ONLY_MODE_STATE_STRING = "ScanOnlyModeState"; 48 private static final String SOFT_AP_MODE_STATE_STRING = "SoftAPModeState"; 49 private static final String WIFI_DISABLED_STATE_STRING = "WifiDisabledState"; 50 private static final String CLIENT_MODE_ACTIVE_STATE_STRING = "ClientModeActiveState"; 51 private static final String SCAN_ONLY_MODE_ACTIVE_STATE_STRING = "ScanOnlyModeActiveState"; 52 private static final String SOFT_AP_MODE_ACTIVE_STATE_STRING = "SoftAPModeActiveState"; 53 54 @Mock WifiInjector mWifiInjector; 55 @Mock WifiApConfigStore mWifiApConfigStore; 56 TestLooper mLooper; 57 @Mock IWificond mWificond; 58 @Mock IApInterface mApInterface; 59 @Mock INetworkManagementService mNMService; 60 @Mock SoftApManager mSoftApManager; 61 SoftApManager.Listener mSoftApListener; 62 WifiStateMachinePrime mWifiStateMachinePrime; 63 64 /** 65 * Set up the test environment. 66 */ 67 @Before 68 public void setUp() throws Exception { 69 Log.d(TAG, "Setting up ..."); 70 71 MockitoAnnotations.initMocks(this); 72 mLooper = new TestLooper(); 73 74 mWifiInjector = mock(WifiInjector.class); 75 mWifiStateMachinePrime = createWifiStateMachinePrime(); 76 } 77 78 private WifiStateMachinePrime createWifiStateMachinePrime() { 79 when(mWifiInjector.makeWificond()).thenReturn(null); 80 return new WifiStateMachinePrime(mWifiInjector, mLooper.getLooper(), mNMService); 81 } 82 83 /** 84 * Clean up after tests - explicitly set tested object to null. 85 */ 86 @After 87 public void cleanUp() throws Exception { 88 mWifiStateMachinePrime = null; 89 } 90 91 private void enterSoftApActiveMode() throws Exception { 92 enterSoftApActiveMode(null); 93 } 94 95 /** 96 * Helper method to enter the SoftApActiveMode for WifiStateMachinePrime. 97 * 98 * This method puts the test object into the correct state and verifies steps along the way. 99 */ 100 private void enterSoftApActiveMode(WifiConfiguration wifiConfig) throws Exception { 101 String fromState = mWifiStateMachinePrime.getCurrentMode(); 102 when(mWifiInjector.makeWificond()).thenReturn(mWificond); 103 when(mWificond.createApInterface()).thenReturn(mApInterface); 104 doAnswer( 105 new Answer<Object>() { 106 public SoftApManager answer(InvocationOnMock invocation) { 107 Object[] args = invocation.getArguments(); 108 assertEquals(mNMService, (INetworkManagementService) args[0]); 109 mSoftApListener = (SoftApManager.Listener) args[1]; 110 assertEquals(mApInterface, (IApInterface) args[2]); 111 assertEquals(wifiConfig, (WifiConfiguration) args[3]); 112 return mSoftApManager; 113 } 114 }).when(mWifiInjector).makeSoftApManager(any(INetworkManagementService.class), 115 any(SoftApManager.Listener.class), 116 any(IApInterface.class), 117 any()); 118 mWifiStateMachinePrime.enterSoftAPMode(wifiConfig); 119 mLooper.dispatchAll(); 120 Log.e("WifiStateMachinePrimeTest", "check fromState: " + fromState); 121 if (!fromState.equals(WIFI_DISABLED_STATE_STRING)) { 122 verify(mWificond).tearDownInterfaces(); 123 } 124 assertEquals(SOFT_AP_MODE_ACTIVE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); 125 verify(mSoftApManager).start(); 126 } 127 128 /** 129 * Test that when a new instance of WifiStateMachinePrime is created, any existing interfaces in 130 * the retrieved Wificond instance are cleaned up. 131 * Expectations: When the new WifiStateMachinePrime instance is created a call to 132 * Wificond.tearDownInterfaces() is made. 133 */ 134 @Test 135 public void testWificondExistsOnStartup() throws Exception { 136 when(mWifiInjector.makeWificond()).thenReturn(mWificond); 137 WifiStateMachinePrime testWifiStateMachinePrime = 138 new WifiStateMachinePrime(mWifiInjector, mLooper.getLooper(), mNMService); 139 verify(mWificond).tearDownInterfaces(); 140 } 141 142 /** 143 * Test that WifiStateMachinePrime properly enters the SoftApModeActiveState from the 144 * WifiDisabled state. 145 */ 146 @Test 147 public void testEnterSoftApModeFromDisabled() throws Exception { 148 enterSoftApActiveMode(); 149 } 150 151 /** 152 * Test that WifiStateMachinePrime properly enters the SoftApModeActiveState from another state. 153 * Expectations: When going from one state to another, any interfaces that are still up are torn 154 * down. 155 */ 156 @Test 157 public void testEnterSoftApModeFromDifferentState() throws Exception { 158 when(mWifiInjector.makeWificond()).thenReturn(mWificond); 159 mWifiStateMachinePrime.enterClientMode(); 160 mLooper.dispatchAll(); 161 assertEquals(CLIENT_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); 162 enterSoftApActiveMode(); 163 } 164 165 /** 166 * Test that we can disable wifi fully from the SoftApModeActiveState. 167 */ 168 @Test 169 public void testDisableWifiFromSoftApModeActiveState() throws Exception { 170 enterSoftApActiveMode(); 171 172 mWifiStateMachinePrime.disableWifi(); 173 mLooper.dispatchAll(); 174 verify(mSoftApManager).stop(); 175 verify(mWificond).tearDownInterfaces(); 176 assertEquals(WIFI_DISABLED_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); 177 } 178 179 /** 180 * Test that we can disable wifi fully from the SoftApModeState. 181 */ 182 @Test 183 public void testDisableWifiFromSoftApModeState() throws Exception { 184 // Use a failure getting wificond to stay in the SoftAPModeState 185 when(mWifiInjector.makeWificond()).thenReturn(null); 186 mWifiStateMachinePrime.enterSoftAPMode(null); 187 mLooper.dispatchAll(); 188 assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); 189 190 mWifiStateMachinePrime.disableWifi(); 191 mLooper.dispatchAll(); 192 // mWificond will be null due to this test, no call to tearDownInterfaces here. 193 assertEquals(WIFI_DISABLED_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); 194 } 195 196 /** 197 * Test that we can switch from SoftApActiveMode to another mode. 198 * Expectation: When switching out of SoftApModeActiveState we stop the SoftApManager and tear 199 * down existing interfaces. 200 */ 201 @Test 202 public void testSwitchModeWhenSoftApActiveMode() throws Exception { 203 enterSoftApActiveMode(); 204 205 mWifiStateMachinePrime.enterClientMode(); 206 mLooper.dispatchAll(); 207 verify(mSoftApManager).stop(); 208 verify(mWificond).tearDownInterfaces(); 209 assertEquals(CLIENT_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); 210 } 211 212 /** 213 * Test that we do not attempt to enter SoftApModeActiveState when we cannot get a reference to 214 * wificond. 215 * Expectations: After a failed attempt to get wificond from WifiInjector, we should remain in 216 * the SoftApModeState. 217 */ 218 @Test 219 public void testWificondNullWhenSwitchingToApMode() throws Exception { 220 when(mWifiInjector.makeWificond()).thenReturn(null); 221 mWifiStateMachinePrime.enterSoftAPMode(null); 222 mLooper.dispatchAll(); 223 assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); 224 } 225 226 /** 227 * Test that we do not attempt to enter SoftApModeActiveState when we cannot get an ApInterface 228 * from wificond. 229 * Expectations: After a failed attempt to get an ApInterface from WifiInjector, we should 230 * remain in the SoftApModeState. 231 */ 232 @Test 233 public void testAPInterfaceFailedWhenSwitchingToApMode() throws Exception { 234 when(mWifiInjector.makeWificond()).thenReturn(mWificond); 235 when(mWificond.createApInterface()).thenReturn(null); 236 mWifiStateMachinePrime.enterSoftAPMode(null); 237 mLooper.dispatchAll(); 238 assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); 239 } 240 241 /** 242 * Test that we do can enter the SoftApModeActiveState if we are already in the SoftApModeState. 243 * Expectations: We should exit the current SoftApModeState and re-enter before successfully 244 * entering the SoftApModeActiveState. 245 */ 246 @Test 247 public void testEnterSoftApModeActiveWhenAlreadyInSoftApMode() throws Exception { 248 when(mWifiInjector.makeWificond()).thenReturn(mWificond); 249 when(mWificond.createApInterface()).thenReturn(null); 250 mWifiStateMachinePrime.enterSoftAPMode(null); 251 mLooper.dispatchAll(); 252 assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); 253 254 enterSoftApActiveMode(); 255 } 256 257 /** 258 * Test that we return to the SoftApModeState after a failure is reported when in the 259 * SoftApModeActiveState. 260 * Expectations: We should exit the SoftApModeActiveState and stop the SoftApManager. 261 */ 262 @Test 263 public void testSoftApFailureWhenActive() throws Exception { 264 enterSoftApActiveMode(); 265 // now inject failure through the SoftApManager.Listener 266 mSoftApListener.onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, 0); 267 mLooper.dispatchAll(); 268 assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); 269 verify(mSoftApManager).stop(); 270 } 271 272 /** 273 * Test that we return to the SoftApModeState after the SoftApManager is stopped in the 274 * SoftApModeActiveState. 275 * Expectations: We should exit the SoftApModeActiveState and stop the SoftApManager. 276 */ 277 @Test 278 public void testSoftApDisabledWhenActive() throws Exception { 279 enterSoftApActiveMode(); 280 // now inject failure through the SoftApManager.Listener 281 mSoftApListener.onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, 0); 282 mLooper.dispatchAll(); 283 assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); 284 verify(mSoftApManager).stop(); 285 } 286 287 /** 288 * Test that a config passed in to the call to enterSoftApMode is used to create the new 289 * SoftApManager. 290 * Expectations: We should create a SoftApManager in WifiInjector with the config passed in to 291 * WifiStateMachinePrime to switch to SoftApMode. 292 */ 293 @Test 294 public void testConfigIsPassedToWifiInjector() throws Exception { 295 WifiConfiguration config = new WifiConfiguration(); 296 config.SSID = "ThisIsAConfig"; 297 enterSoftApActiveMode(config); 298 } 299 300 /** 301 * Test that when enterSoftAPMode is called with a null config, we pass a null config to 302 * WifiInjector.makeSoftApManager. 303 * 304 * Passing a null config to SoftApManager indicates that the default config should be used. 305 * 306 * Expectations: WifiInjector should be called with a null config. 307 */ 308 @Test 309 public void testNullConfigIsPassedToWifiInjector() throws Exception { 310 enterSoftApActiveMode(null); 311 } 312 313 /** 314 * Test that the proper config is used if a prior attempt fails without using the config. 315 * Expectations: A call to start softap with a null config fails, but a second call has a set 316 * config - this second call should use the correct config. 317 */ 318 @Test 319 public void testNullConfigFailsSecondCallWithConfigSuccessful() throws Exception { 320 when(mWifiInjector.makeWificond()).thenReturn(mWificond); 321 when(mWificond.createApInterface()).thenReturn(null); 322 mWifiStateMachinePrime.enterSoftAPMode(null); 323 mLooper.dispatchAll(); 324 assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); 325 WifiConfiguration config = new WifiConfiguration(); 326 config.SSID = "ThisIsAConfig"; 327 enterSoftApActiveMode(config); 328 } 329 330 /** 331 * Test that a failed call to start softap with a valid config has the config saved for future 332 * calls to enable softap. 333 * 334 * Expectations: A call to start SoftAPMode with a config should write out the config if we 335 * did not create a SoftApManager. 336 */ 337 @Test 338 public void testValidConfigIsSavedOnFailureToStart() throws Exception { 339 when(mWifiInjector.makeWificond()).thenReturn(null); 340 when(mWifiInjector.getWifiApConfigStore()).thenReturn(mWifiApConfigStore); 341 WifiConfiguration config = new WifiConfiguration(); 342 config.SSID = "ThisIsAConfig"; 343 mWifiStateMachinePrime.enterSoftAPMode(config); 344 mLooper.dispatchAll(); 345 assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); 346 verify(mWifiApConfigStore).setApConfiguration(eq(config)); 347 } 348 349 /** 350 * Thest that two calls to switch to SoftAPMode in succession ends up with the correct config. 351 * 352 * Expectation: we should end up in SoftAPMode state configured with the second config. 353 */ 354 @Test 355 public void testStartSoftApModeTwiceWithTwoConfigs() throws Exception { 356 when(mWifiInjector.makeWificond()).thenReturn(mWificond); 357 when(mWificond.createApInterface()).thenReturn(mApInterface); 358 when(mWifiInjector.getWifiApConfigStore()).thenReturn(mWifiApConfigStore); 359 WifiConfiguration config1 = new WifiConfiguration(); 360 config1.SSID = "ThisIsAConfig"; 361 WifiConfiguration config2 = new WifiConfiguration(); 362 config2.SSID = "ThisIsASecondConfig"; 363 364 when(mWifiInjector.makeSoftApManager(any(INetworkManagementService.class), 365 any(SoftApManager.Listener.class), 366 any(IApInterface.class), 367 eq(config1))) 368 .thenReturn(mSoftApManager); 369 when(mWifiInjector.makeSoftApManager(any(INetworkManagementService.class), 370 any(SoftApManager.Listener.class), 371 any(IApInterface.class), 372 eq(config2))) 373 .thenReturn(mSoftApManager); 374 375 376 mWifiStateMachinePrime.enterSoftAPMode(config1); 377 mWifiStateMachinePrime.enterSoftAPMode(config2); 378 mLooper.dispatchAll(); 379 assertEquals(SOFT_AP_MODE_ACTIVE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); 380 } 381 382 /** 383 * Test that we safely disable wifi if it is already disabled. 384 * Expectations: We should not interact with wificond since we should have already cleaned up 385 * everything. 386 */ 387 @Test 388 public void disableWifiWhenAlreadyOff() throws Exception { 389 verifyNoMoreInteractions(mWificond); 390 mWifiStateMachinePrime.disableWifi(); 391 } 392} 393