diff options
author | Rebecca Silberstein <silberst@google.com> | 2016-11-21 19:22:19 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2016-11-21 19:22:19 +0000 |
commit | 8cc7a77953dca3ec2d6c8bdc62ca789c508b1efb (patch) | |
tree | d1e685f148d2ec30b052dbd37fbeb473db2ee063 | |
parent | 9dee6afa78e114fb6405746d3f40502a34978002 (diff) | |
parent | fa528234cf518fda63435cf9c4956576d64e8860 (diff) |
WifiStateMachinePrime: allow for configs in softap
am: fa528234cf
Change-Id: I5c44ca0c80956d65a8c35429c9b4aa5a57927645
-rw-r--r-- | service/java/com/android/server/wifi/WifiStateMachinePrime.java | 91 | ||||
-rw-r--r-- | tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java | 152 |
2 files changed, 176 insertions, 67 deletions
diff --git a/service/java/com/android/server/wifi/WifiStateMachinePrime.java b/service/java/com/android/server/wifi/WifiStateMachinePrime.java index dbb826293..200688494 100644 --- a/service/java/com/android/server/wifi/WifiStateMachinePrime.java +++ b/service/java/com/android/server/wifi/WifiStateMachinePrime.java @@ -30,6 +30,9 @@ import com.android.internal.util.Protocol; import com.android.internal.util.State; import com.android.internal.util.StateMachine; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + /** * This class provides the implementation for different WiFi operating modes. * @@ -47,6 +50,8 @@ public class WifiStateMachinePrime { private IWificond mWificond; + private Queue<WifiConfiguration> mApConfigQueue = new ConcurrentLinkedQueue<>(); + /* The base for wifi message types */ static final int BASE = Protocol.BASE_WIFI; @@ -100,8 +105,19 @@ public class WifiStateMachinePrime { /** * Method to enable soft ap for wifi hotspot. + * + * The WifiConfiguration is generally going to be null to indicate that the + * currently saved config in WifiApConfigManager should be used. When the config is + * not null, it will be saved in the WifiApConfigManager. This save is performed in the + * constructor of SoftApManager. + * + * @param wifiConfig WifiConfiguration for the hostapd softap */ - public void enterSoftAPMode() { + public void enterSoftAPMode(WifiConfiguration wifiConfig) { + if (wifiConfig == null) { + wifiConfig = new WifiConfiguration(); + } + mApConfigQueue.offer(wifiConfig); changeMode(ModeStateMachine.CMD_START_SOFT_AP_MODE); } @@ -128,12 +144,10 @@ public class WifiStateMachinePrime { Log.e(TAG, "Received call to disable wifi when it is already disabled."); return; } - // state machine was not initialized yet, we must be starting up - mModeStateMachine = new ModeStateMachine(newMode); - } else { - // we have a current state, go ahead and prep for the new state - mModeStateMachine.sendMessage(newMode); + // state machine was not initialized yet, we must be starting up. + mModeStateMachine = new ModeStateMachine(); } + mModeStateMachine.sendMessage(newMode); } private class ModeStateMachine extends StateMachine { @@ -154,7 +168,7 @@ public class WifiStateMachinePrime { private final State mScanOnlyModeActiveState = new ScanOnlyModeActiveState(); private final State mSoftAPModeActiveState = new SoftAPModeActiveState(); - ModeStateMachine(int initialMode) { + ModeStateMachine() { super(TAG, mLooper); // CHECKSTYLE:OFF IndentationCheck @@ -167,24 +181,8 @@ public class WifiStateMachinePrime { addState(mWifiDisabledState); // CHECKSTYLE:ON IndentationCheck - State startingState; - switch(initialMode) { - case CMD_START_CLIENT_MODE: - startingState = mClientModeState; - break; - case CMD_START_SCAN_ONLY_MODE: - startingState = mScanOnlyModeState; - break; - case CMD_START_SOFT_AP_MODE: - startingState = mSoftAPModeState; - break; - default: - Log.e(TAG, "Attempting to start WifiStateMachinePrime " - + "in an invalid operating mode: " + initialMode); - throw new IllegalArgumentException("Invalid wifi operating mode"); - } - Log.d(TAG, "Switching from WifiDisabled to " + startingState.getName()); - setInitialState(startingState); + Log.d(TAG, "Starting Wifi in WifiDisabledState"); + setInitialState(mWifiDisabledState); start(); } @@ -275,10 +273,18 @@ public class WifiStateMachinePrime { @Override public void enter() { + final Message message = mModeStateMachine.getCurrentMessage(); + if (message.what != ModeStateMachine.CMD_START_SOFT_AP_MODE) { + Log.d(TAG, "Entering SoftAPMode (idle)"); + return; + } + + // Continue with setup since we are changing modes mApInterface = null; mWificond = mWifiInjector.makeWificond(); if (mWificond == null) { Log.e(TAG, "Failed to get reference to wificond"); + writeApConfigDueToStartFailure(); mModeStateMachine.sendMessage(CMD_START_AP_FAILURE); return; } @@ -289,10 +295,11 @@ public class WifiStateMachinePrime { if (mApInterface == null) { Log.e(TAG, "Could not get IApInterface instance from wificond"); + writeApConfigDueToStartFailure(); mModeStateMachine.sendMessage(CMD_START_AP_FAILURE); return; } - mModeStateMachine.sendMessage(CMD_START_AP); + mModeStateMachine.transitionTo(mSoftAPModeActiveState); } @Override @@ -303,8 +310,7 @@ public class WifiStateMachinePrime { switch(message.what) { case CMD_START_AP: - Log.d(TAG, "Transitioning to softapmodeactivestate"); - mModeStateMachine.transitionTo(mSoftAPModeActiveState); + Log.d(TAG, "Received CMD_START_AP (now invalid message) - dropping"); break; case CMD_STOP_AP: // not in active state, nothing to stop. @@ -329,16 +335,29 @@ public class WifiStateMachinePrime { protected IApInterface getInterface() { return mApInterface; } + + private void writeApConfigDueToStartFailure() { + WifiConfiguration config = mApConfigQueue.poll(); + if (config != null && config.SSID != null) { + // Save valid configs for future calls. + mWifiInjector.getWifiApConfigStore().setApConfiguration(config); + } + } } class WifiDisabledState extends State { @Override public void enter() { // make sure everything is torn down + Log.d(TAG, "Entering WifiDisabledState"); } @Override public boolean processMessage(Message message) { + Log.d(TAG, "received a message in WifiDisabledState: " + message); + if (checkForAndHandleModeChange(message)) { + return HANDLED; + } return NOT_HANDLED; } @@ -390,15 +409,13 @@ public class WifiStateMachinePrime { @Override public void enter() { Log.d(TAG, "Entering SoftApModeActiveState"); - final Message message = mModeStateMachine.getCurrentMessage(); - if (message.what != CMD_START_AP) { - throw new RuntimeException("Illegal transition to SoftApState: " + message); + WifiConfiguration config = mApConfigQueue.poll(); + if (config != null && config.SSID != null) { + Log.d(TAG, "Passing config to SoftApManager! " + config); + } else { + config = null; } - // The WifiConfiguration is generally going to be null to indicate that the - // currently saved config in WifiApConfigManager should be used. When the config is - // not null, it will be saved in the WifiApConfigManager. This save is performed in - // the constructor of SoftApManager. - WifiConfiguration config = (WifiConfiguration) message.obj; + this.mActiveModeManager = mWifiInjector.makeSoftApManager(mNMService, new SoftApListener(), ((SoftAPModeState) mSoftAPModeState).getInterface(), config); @@ -409,7 +426,7 @@ public class WifiStateMachinePrime { public boolean processMessage(Message message) { switch(message.what) { case CMD_START_AP: - Log.d(TAG, "Received CMD_START_AP with SoftApMode already active. drop"); + Log.d(TAG, "Received CMD_START_AP when active - invalid message - drop"); break; case CMD_STOP_AP: mActiveModeManager.stop(); diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java index cf20ecc56..879862bf9 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java @@ -52,13 +52,13 @@ public class WifiStateMachinePrimeTest { private static final String SOFT_AP_MODE_ACTIVE_STATE_STRING = "SoftAPModeActiveState"; @Mock WifiInjector mWifiInjector; + @Mock WifiApConfigStore mWifiApConfigStore; TestLooper mLooper; @Mock IWificond mWificond; @Mock IApInterface mApInterface; @Mock INetworkManagementService mNMService; @Mock SoftApManager mSoftApManager; SoftApManager.Listener mSoftApListener; - @Mock WifiConfiguration mApConfig; WifiStateMachinePrime mWifiStateMachinePrime; /** @@ -88,33 +88,39 @@ public class WifiStateMachinePrimeTest { mWifiStateMachinePrime = null; } + private void enterSoftApActiveMode() throws Exception { + enterSoftApActiveMode(null); + } + /** * Helper method to enter the SoftApActiveMode for WifiStateMachinePrime. * * This method puts the test object into the correct state and verifies steps along the way. */ - private void enterSoftApActiveMode() throws Exception { + private void enterSoftApActiveMode(WifiConfiguration wifiConfig) throws Exception { String fromState = mWifiStateMachinePrime.getCurrentMode(); when(mWifiInjector.makeWificond()).thenReturn(mWificond); when(mWificond.createApInterface()).thenReturn(mApInterface); doAnswer( new Answer<Object>() { public SoftApManager answer(InvocationOnMock invocation) { - mSoftApListener = (SoftApManager.Listener) invocation.getArguments()[1]; + Object[] args = invocation.getArguments(); + assertEquals(mNMService, (INetworkManagementService) args[0]); + mSoftApListener = (SoftApManager.Listener) args[1]; + assertEquals(mApInterface, (IApInterface) args[2]); + assertEquals(wifiConfig, (WifiConfiguration) args[3]); return mSoftApManager; } }).when(mWifiInjector).makeSoftApManager(any(INetworkManagementService.class), any(SoftApManager.Listener.class), any(IApInterface.class), any(WifiConfiguration.class)); - mWifiStateMachinePrime.enterSoftAPMode(); - mLooper.dispatchNext(); - assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); + mWifiStateMachinePrime.enterSoftAPMode(wifiConfig); + mLooper.dispatchAll(); Log.e("WifiStateMachinePrimeTest", "check fromState: " + fromState); if (!fromState.equals(WIFI_DISABLED_STATE_STRING)) { verify(mWificond).tearDownInterfaces(); } - mLooper.dispatchNext(); assertEquals(SOFT_AP_MODE_ACTIVE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); verify(mSoftApManager).start(); } @@ -151,7 +157,7 @@ public class WifiStateMachinePrimeTest { public void testEnterSoftApModeFromDifferentState() throws Exception { when(mWifiInjector.makeWificond()).thenReturn(mWificond); mWifiStateMachinePrime.enterClientMode(); - mLooper.dispatchNext(); + mLooper.dispatchAll(); assertEquals(CLIENT_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); enterSoftApActiveMode(); } @@ -164,7 +170,7 @@ public class WifiStateMachinePrimeTest { enterSoftApActiveMode(); mWifiStateMachinePrime.disableWifi(); - mLooper.dispatchNext(); + mLooper.dispatchAll(); verify(mSoftApManager).stop(); verify(mWificond).tearDownInterfaces(); assertEquals(WIFI_DISABLED_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); @@ -177,14 +183,12 @@ public class WifiStateMachinePrimeTest { public void testDisableWifiFromSoftApModeState() throws Exception { // Use a failure getting wificond to stay in the SoftAPModeState when(mWifiInjector.makeWificond()).thenReturn(null); - mWifiStateMachinePrime.enterSoftAPMode(); - mLooper.dispatchNext(); - assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); - mLooper.dispatchNext(); + mWifiStateMachinePrime.enterSoftAPMode(null); + mLooper.dispatchAll(); assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); mWifiStateMachinePrime.disableWifi(); - mLooper.dispatchNext(); + mLooper.dispatchAll(); // mWificond will be null due to this test, no call to tearDownInterfaces here. assertEquals(WIFI_DISABLED_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); } @@ -199,7 +203,7 @@ public class WifiStateMachinePrimeTest { enterSoftApActiveMode(); mWifiStateMachinePrime.enterClientMode(); - mLooper.dispatchNext(); + mLooper.dispatchAll(); verify(mSoftApManager).stop(); verify(mWificond).tearDownInterfaces(); assertEquals(CLIENT_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); @@ -214,10 +218,8 @@ public class WifiStateMachinePrimeTest { @Test public void testWificondNullWhenSwitchingToApMode() throws Exception { when(mWifiInjector.makeWificond()).thenReturn(null); - mWifiStateMachinePrime.enterSoftAPMode(); - mLooper.dispatchNext(); - assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); - mLooper.dispatchNext(); + mWifiStateMachinePrime.enterSoftAPMode(null); + mLooper.dispatchAll(); assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); } @@ -231,10 +233,8 @@ public class WifiStateMachinePrimeTest { public void testAPInterfaceFailedWhenSwitchingToApMode() throws Exception { when(mWifiInjector.makeWificond()).thenReturn(mWificond); when(mWificond.createApInterface()).thenReturn(null); - mWifiStateMachinePrime.enterSoftAPMode(); - mLooper.dispatchNext(); - assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); - mLooper.dispatchNext(); + mWifiStateMachinePrime.enterSoftAPMode(null); + mLooper.dispatchAll(); assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); } @@ -247,14 +247,11 @@ public class WifiStateMachinePrimeTest { public void testEnterSoftApModeActiveWhenAlreadyInSoftApMode() throws Exception { when(mWifiInjector.makeWificond()).thenReturn(mWificond); when(mWificond.createApInterface()).thenReturn(null); - mWifiStateMachinePrime.enterSoftAPMode(); - mLooper.dispatchNext(); - assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); - mLooper.dispatchNext(); + mWifiStateMachinePrime.enterSoftAPMode(null); + mLooper.dispatchAll(); assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); enterSoftApActiveMode(); - verify(mWificond).tearDownInterfaces(); } /** @@ -267,7 +264,7 @@ public class WifiStateMachinePrimeTest { enterSoftApActiveMode(); // now inject failure through the SoftApManager.Listener mSoftApListener.onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, 0); - mLooper.dispatchNext(); + mLooper.dispatchAll(); assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); verify(mSoftApManager).stop(); } @@ -282,12 +279,107 @@ public class WifiStateMachinePrimeTest { enterSoftApActiveMode(); // now inject failure through the SoftApManager.Listener mSoftApListener.onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, 0); - mLooper.dispatchNext(); + mLooper.dispatchAll(); assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); verify(mSoftApManager).stop(); } /** + * Test that a config passed in to the call to enterSoftApMode is used to create the new + * SoftApManager. + * Expectations: We should create a SoftApManager in WifiInjector with the config passed in to + * WifiStateMachinePrime to switch to SoftApMode. + */ + @Test + public void testConfigIsPassedToWifiInjector() throws Exception { + WifiConfiguration config = new WifiConfiguration(); + config.SSID = "ThisIsAConfig"; + enterSoftApActiveMode(config); + } + + /** + * Test that when enterSoftAPMode is called with a null config, we pass a null config to + * WifiInjector.makeSoftApManager. + * + * Passing a null config to SoftApManager indicates that the default config should be used. + * + * Expectations: WifiInjector should be called with a null config. + */ + @Test + public void testNullConfigIsPassedToWifiInjector() throws Exception { + enterSoftApActiveMode(null); + } + + /** + * Test that the proper config is used if a prior attempt fails without using the config. + * Expectations: A call to start softap with a null config fails, but a second call has a set + * config - this second call should use the correct config. + */ + @Test + public void testNullConfigFailsSecondCallWithConfigSuccessful() throws Exception { + when(mWifiInjector.makeWificond()).thenReturn(mWificond); + when(mWificond.createApInterface()).thenReturn(null); + mWifiStateMachinePrime.enterSoftAPMode(null); + mLooper.dispatchAll(); + assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); + WifiConfiguration config = new WifiConfiguration(); + config.SSID = "ThisIsAConfig"; + enterSoftApActiveMode(config); + } + + /** + * Test that a failed call to start softap with a valid config has the config saved for future + * calls to enable softap. + * + * Expectations: A call to start SoftAPMode with a config should write out the config if we + * did not create a SoftApManager. + */ + @Test + public void testValidConfigIsSavedOnFailureToStart() throws Exception { + when(mWifiInjector.makeWificond()).thenReturn(null); + when(mWifiInjector.getWifiApConfigStore()).thenReturn(mWifiApConfigStore); + WifiConfiguration config = new WifiConfiguration(); + config.SSID = "ThisIsAConfig"; + mWifiStateMachinePrime.enterSoftAPMode(config); + mLooper.dispatchAll(); + assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); + verify(mWifiApConfigStore).setApConfiguration(eq(config)); + } + + /** + * Thest that two calls to switch to SoftAPMode in succession ends up with the correct config. + * + * Expectation: we should end up in SoftAPMode state configured with the second config. + */ + @Test + public void testStartSoftApModeTwiceWithTwoConfigs() throws Exception { + when(mWifiInjector.makeWificond()).thenReturn(mWificond); + when(mWificond.createApInterface()).thenReturn(mApInterface); + when(mWifiInjector.getWifiApConfigStore()).thenReturn(mWifiApConfigStore); + WifiConfiguration config1 = new WifiConfiguration(); + config1.SSID = "ThisIsAConfig"; + WifiConfiguration config2 = new WifiConfiguration(); + config2.SSID = "ThisIsASecondConfig"; + + when(mWifiInjector.makeSoftApManager(any(INetworkManagementService.class), + any(SoftApManager.Listener.class), + any(IApInterface.class), + eq(config1))) + .thenReturn(mSoftApManager); + when(mWifiInjector.makeSoftApManager(any(INetworkManagementService.class), + any(SoftApManager.Listener.class), + any(IApInterface.class), + eq(config2))) + .thenReturn(mSoftApManager); + + + mWifiStateMachinePrime.enterSoftAPMode(config1); + mWifiStateMachinePrime.enterSoftAPMode(config2); + mLooper.dispatchAll(); + assertEquals(SOFT_AP_MODE_ACTIVE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); + } + + /** * Test that we safely disable wifi if it is already disabled. * Expectations: We should not interact with wificond since we should have already cleaned up * everything. |