summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--service/java/com/android/server/wifi/WifiStateMachinePrime.java296
-rw-r--r--tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java300
2 files changed, 578 insertions, 18 deletions
diff --git a/service/java/com/android/server/wifi/WifiStateMachinePrime.java b/service/java/com/android/server/wifi/WifiStateMachinePrime.java
index 33cccbb88..dbb826293 100644
--- a/service/java/com/android/server/wifi/WifiStateMachinePrime.java
+++ b/service/java/com/android/server/wifi/WifiStateMachinePrime.java
@@ -16,8 +16,17 @@
package com.android.server.wifi;
+import android.net.wifi.IApInterface;
+import android.net.wifi.IWificond;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.INetworkManagementService;
+import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
@@ -32,8 +41,99 @@ public class WifiStateMachinePrime {
private ModeStateMachine mModeStateMachine;
- WifiStateMachinePrime() {
- mModeStateMachine = new ModeStateMachine(TAG);
+ private final WifiInjector mWifiInjector;
+ private final Looper mLooper;
+ private final INetworkManagementService mNMService;
+
+ private IWificond mWificond;
+
+ /* The base for wifi message types */
+ static final int BASE = Protocol.BASE_WIFI;
+
+ /* Start the soft access point */
+ static final int CMD_START_AP = BASE + 21;
+ /* Indicates soft ap start failed */
+ static final int CMD_START_AP_FAILURE = BASE + 22;
+ /* Stop the soft access point */
+ static final int CMD_STOP_AP = BASE + 23;
+ /* Soft access point teardown is completed. */
+ static final int CMD_AP_STOPPED = BASE + 24;
+
+ WifiStateMachinePrime(WifiInjector wifiInjector,
+ Looper looper,
+ INetworkManagementService nmService) {
+ mWifiInjector = wifiInjector;
+ mLooper = looper;
+ mNMService = nmService;
+
+ // Clean up existing interfaces in wificond.
+ // This ensures that the framework and wificond are in a consistent state after a framework
+ // restart.
+ try {
+ mWificond = mWifiInjector.makeWificond();
+ if (mWificond != null) {
+ mWificond.tearDownInterfaces();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "wificond died during framework startup");
+ }
+ }
+
+ /**
+ * Method to switch wifi into client mode where connections to configured networks will be
+ * attempted.
+ */
+ public void enterClientMode() {
+ changeMode(ModeStateMachine.CMD_START_CLIENT_MODE);
+ }
+
+ /**
+ * Method to switch wifi into scan only mode where network connection attempts will not be made.
+ *
+ * This mode is utilized by location scans. If wifi is disabled by a user, but they have
+ * previously configured their device to perform location scans, this mode allows wifi to
+ * fulfill the location scan requests but will not be used for connectivity.
+ */
+ public void enterScanOnlyMode() {
+ changeMode(ModeStateMachine.CMD_START_SCAN_ONLY_MODE);
+ }
+
+ /**
+ * Method to enable soft ap for wifi hotspot.
+ */
+ public void enterSoftAPMode() {
+ changeMode(ModeStateMachine.CMD_START_SOFT_AP_MODE);
+ }
+
+ /**
+ * Method to fully disable wifi.
+ *
+ * This mode will completely shut down wifi and will not perform any network scans.
+ */
+ public void disableWifi() {
+ changeMode(ModeStateMachine.CMD_DISABLE_WIFI);
+ }
+
+ protected String getCurrentMode() {
+ if (mModeStateMachine != null) {
+ return mModeStateMachine.getCurrentMode();
+ }
+ return "WifiDisabledState";
+ }
+
+ private void changeMode(int newMode) {
+ if (mModeStateMachine == null) {
+ if (newMode == ModeStateMachine.CMD_DISABLE_WIFI) {
+ // command is to disable wifi, but it is already disabled.
+ 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);
+ }
}
private class ModeStateMachine extends StateMachine {
@@ -43,7 +143,6 @@ public class WifiStateMachinePrime {
public static final int CMD_START_SOFT_AP_MODE = 2;
public static final int CMD_DISABLE_WIFI = 3;
-
// Create the base modes for WSM.
private final State mClientModeState = new ClientModeState();
private final State mScanOnlyModeState = new ScanOnlyModeState();
@@ -55,8 +154,8 @@ public class WifiStateMachinePrime {
private final State mScanOnlyModeActiveState = new ScanOnlyModeActiveState();
private final State mSoftAPModeActiveState = new SoftAPModeActiveState();
- ModeStateMachine(String name) {
- super(name);
+ ModeStateMachine(int initialMode) {
+ super(TAG, mLooper);
// CHECKSTYLE:OFF IndentationCheck
addState(mClientModeState);
@@ -67,54 +166,168 @@ public class WifiStateMachinePrime {
addState(mSoftAPModeActiveState, mSoftAPModeState);
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);
+ start();
}
private String getCurrentMode() {
- try {
- return getCurrentState().getName();
- } catch (NullPointerException e) {
- // current state is not set
- return null;
+ return getCurrentState().getName();
+ }
+
+ private boolean checkForAndHandleModeChange(Message message) {
+ switch(message.what) {
+ case ModeStateMachine.CMD_START_CLIENT_MODE:
+ Log.d(TAG, "Switching from " + getCurrentMode() + " to ClientMode");
+ mModeStateMachine.transitionTo(mClientModeState);
+ break;
+ case ModeStateMachine.CMD_START_SCAN_ONLY_MODE:
+ Log.d(TAG, "Switching from " + getCurrentMode() + " to ScanOnlyMode");
+ mModeStateMachine.transitionTo(mScanOnlyModeState);
+ break;
+ case ModeStateMachine.CMD_START_SOFT_AP_MODE:
+ Log.d(TAG, "Switching from " + getCurrentMode() + " to SoftApMode");
+ mModeStateMachine.transitionTo(mSoftAPModeState);
+ break;
+ case ModeStateMachine.CMD_DISABLE_WIFI:
+ Log.d(TAG, "Switching from " + getCurrentMode() + " to WifiDisabled");
+ mModeStateMachine.transitionTo(mWifiDisabledState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+
+ private void tearDownInterfaces() {
+ if (mWificond != null) {
+ try {
+ mWificond.tearDownInterfaces();
+ } catch (RemoteException e) {
+ // There is very little we can do here
+ Log.e(TAG, "Failed to tear down interfaces via wificond");
+ }
+ mWificond = null;
}
+ return;
}
class ClientModeState extends State {
+ @Override
+ public void enter() {
+ mWificond = mWifiInjector.makeWificond();
+ }
@Override
public boolean processMessage(Message message) {
+ if (checkForAndHandleModeChange(message)) {
+ return HANDLED;
+ }
return NOT_HANDLED;
}
@Override
public void exit() {
-
+ tearDownInterfaces();
}
}
class ScanOnlyModeState extends State {
+ @Override
+ public void enter() {
+ }
@Override
public boolean processMessage(Message message) {
- // handle Mode changes and any events requiring setup or restarting services
-
+ if (checkForAndHandleModeChange(message)) {
+ return HANDLED;
+ }
return NOT_HANDLED;
}
@Override
public void exit() {
- // tear down running services
+ // Do not tear down interfaces yet since this mode is not actively controlled or
+ // used in tests at this time.
+ // tearDownInterfaces();
}
}
class SoftAPModeState extends State {
+ IApInterface mApInterface = null;
+
+ @Override
+ public void enter() {
+ mApInterface = null;
+ mWificond = mWifiInjector.makeWificond();
+ if (mWificond == null) {
+ Log.e(TAG, "Failed to get reference to wificond");
+ mModeStateMachine.sendMessage(CMD_START_AP_FAILURE);
+ return;
+ }
+
+ try {
+ mApInterface = mWificond.createApInterface();
+ } catch (RemoteException e1) { }
+
+ if (mApInterface == null) {
+ Log.e(TAG, "Could not get IApInterface instance from wificond");
+ mModeStateMachine.sendMessage(CMD_START_AP_FAILURE);
+ return;
+ }
+ mModeStateMachine.sendMessage(CMD_START_AP);
+ }
@Override
public boolean processMessage(Message message) {
- return NOT_HANDLED;
+ if (checkForAndHandleModeChange(message)) {
+ return HANDLED;
+ }
+
+ switch(message.what) {
+ case CMD_START_AP:
+ Log.d(TAG, "Transitioning to softapmodeactivestate");
+ mModeStateMachine.transitionTo(mSoftAPModeActiveState);
+ break;
+ case CMD_STOP_AP:
+ // not in active state, nothing to stop.
+ break;
+ case CMD_START_AP_FAILURE:
+ Log.e(TAG, "Failed to start SoftApMode. Wait for next mode command.");
+ break;
+ case CMD_AP_STOPPED:
+ Log.d(TAG, "SoftApModeActiveState stopped. Wait for next mode command.");
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
}
@Override
public void exit() {
+ tearDownInterfaces();
+ }
+
+ protected IApInterface getInterface() {
+ return mApInterface;
}
}
@@ -163,11 +376,58 @@ public class WifiStateMachinePrime {
}
class SoftAPModeActiveState extends ModeActiveState {
+ private class SoftApListener implements SoftApManager.Listener {
+ @Override
+ public void onStateChanged(int state, int reason) {
+ if (state == WifiManager.WIFI_AP_STATE_DISABLED) {
+ mModeStateMachine.sendMessage(CMD_AP_STOPPED);
+ } else if (state == WifiManager.WIFI_AP_STATE_FAILED) {
+ mModeStateMachine.sendMessage(CMD_START_AP_FAILURE);
+ }
+ }
+ }
+
@Override
public void enter() {
- // The SoftApManager is not empty at this time, will populate in later CLs.
- //this.mActiveModeManager = new SoftApManager();
+ Log.d(TAG, "Entering SoftApModeActiveState");
+ final Message message = mModeStateMachine.getCurrentMessage();
+ if (message.what != CMD_START_AP) {
+ throw new RuntimeException("Illegal transition to SoftApState: " + message);
+ }
+ // 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);
+ mActiveModeManager.start();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ switch(message.what) {
+ case CMD_START_AP:
+ Log.d(TAG, "Received CMD_START_AP with SoftApMode already active. drop");
+ break;
+ case CMD_STOP_AP:
+ mActiveModeManager.stop();
+ break;
+ case CMD_START_AP_FAILURE:
+ Log.d(TAG, "Failed to start SoftApMode. Return to SoftApMode (inactive).");
+ mModeStateMachine.transitionTo(mSoftAPModeState);
+ break;
+ case CMD_AP_STOPPED:
+ Log.d(TAG, "SoftApModeActiveState stopped."
+ + " Return to SoftApMode (inactive).");
+ mModeStateMachine.transitionTo(mSoftAPModeState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
}
}
- } // class ModeStateMachine
+ } // class ModeStateMachine
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java
new file mode 100644
index 000000000..cf20ecc56
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.*;
+
+import android.net.wifi.IApInterface;
+import android.net.wifi.IWificond;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.INetworkManagementService;
+import android.os.test.TestLooper;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.WifiStateMachinePrime}.
+ */
+@SmallTest
+public class WifiStateMachinePrimeTest {
+ public static final String TAG = "WifiStateMachinePrimeTest";
+
+ private static final String CLIENT_MODE_STATE_STRING = "ClientModeState";
+ private static final String SCAN_ONLY_MODE_STATE_STRING = "ScanOnlyModeState";
+ private static final String SOFT_AP_MODE_STATE_STRING = "SoftAPModeState";
+ private static final String WIFI_DISABLED_STATE_STRING = "WifiDisabledState";
+ private static final String CLIENT_MODE_ACTIVE_STATE_STRING = "ClientModeActiveState";
+ private static final String SCAN_ONLY_MODE_ACTIVE_STATE_STRING = "ScanOnlyModeActiveState";
+ private static final String SOFT_AP_MODE_ACTIVE_STATE_STRING = "SoftAPModeActiveState";
+
+ @Mock WifiInjector mWifiInjector;
+ TestLooper mLooper;
+ @Mock IWificond mWificond;
+ @Mock IApInterface mApInterface;
+ @Mock INetworkManagementService mNMService;
+ @Mock SoftApManager mSoftApManager;
+ SoftApManager.Listener mSoftApListener;
+ @Mock WifiConfiguration mApConfig;
+ WifiStateMachinePrime mWifiStateMachinePrime;
+
+ /**
+ * Set up the test environment.
+ */
+ @Before
+ public void setUp() throws Exception {
+ Log.d(TAG, "Setting up ...");
+
+ MockitoAnnotations.initMocks(this);
+ mLooper = new TestLooper();
+
+ mWifiInjector = mock(WifiInjector.class);
+ mWifiStateMachinePrime = createWifiStateMachinePrime();
+ }
+
+ private WifiStateMachinePrime createWifiStateMachinePrime() {
+ when(mWifiInjector.makeWificond()).thenReturn(null);
+ return new WifiStateMachinePrime(mWifiInjector, mLooper.getLooper(), mNMService);
+ }
+
+ /**
+ * Clean up after tests - explicitly set tested object to null.
+ */
+ @After
+ public void cleanUp() throws Exception {
+ mWifiStateMachinePrime = 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 {
+ 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];
+ 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());
+ 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();
+ }
+
+ /**
+ * Test that when a new instance of WifiStateMachinePrime is created, any existing interfaces in
+ * the retrieved Wificond instance are cleaned up.
+ * Expectations: When the new WifiStateMachinePrime instance is created a call to
+ * Wificond.tearDownInterfaces() is made.
+ */
+ @Test
+ public void testWificondExistsOnStartup() throws Exception {
+ when(mWifiInjector.makeWificond()).thenReturn(mWificond);
+ WifiStateMachinePrime testWifiStateMachinePrime =
+ new WifiStateMachinePrime(mWifiInjector, mLooper.getLooper(), mNMService);
+ verify(mWificond).tearDownInterfaces();
+ }
+
+ /**
+ * Test that WifiStateMachinePrime properly enters the SoftApModeActiveState from the
+ * WifiDisabled state.
+ */
+ @Test
+ public void testEnterSoftApModeFromDisabled() throws Exception {
+ enterSoftApActiveMode();
+ }
+
+ /**
+ * Test that WifiStateMachinePrime properly enters the SoftApModeActiveState from another state.
+ * Expectations: When going from one state to another, any interfaces that are still up are torn
+ * down.
+ */
+ @Test
+ public void testEnterSoftApModeFromDifferentState() throws Exception {
+ when(mWifiInjector.makeWificond()).thenReturn(mWificond);
+ mWifiStateMachinePrime.enterClientMode();
+ mLooper.dispatchNext();
+ assertEquals(CLIENT_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+ enterSoftApActiveMode();
+ }
+
+ /**
+ * Test that we can disable wifi fully from the SoftApModeActiveState.
+ */
+ @Test
+ public void testDisableWifiFromSoftApModeActiveState() throws Exception {
+ enterSoftApActiveMode();
+
+ mWifiStateMachinePrime.disableWifi();
+ mLooper.dispatchNext();
+ verify(mSoftApManager).stop();
+ verify(mWificond).tearDownInterfaces();
+ assertEquals(WIFI_DISABLED_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+ }
+
+ /**
+ * Test that we can disable wifi fully from the SoftApModeState.
+ */
+ @Test
+ 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();
+ assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+
+ mWifiStateMachinePrime.disableWifi();
+ mLooper.dispatchNext();
+ // mWificond will be null due to this test, no call to tearDownInterfaces here.
+ assertEquals(WIFI_DISABLED_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+ }
+
+ /**
+ * Test that we can switch from SoftApActiveMode to another mode.
+ * Expectation: When switching out of SoftApModeActiveState we stop the SoftApManager and tear
+ * down existing interfaces.
+ */
+ @Test
+ public void testSwitchModeWhenSoftApActiveMode() throws Exception {
+ enterSoftApActiveMode();
+
+ mWifiStateMachinePrime.enterClientMode();
+ mLooper.dispatchNext();
+ verify(mSoftApManager).stop();
+ verify(mWificond).tearDownInterfaces();
+ assertEquals(CLIENT_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+ }
+
+ /**
+ * Test that we do not attempt to enter SoftApModeActiveState when we cannot get a reference to
+ * wificond.
+ * Expectations: After a failed attempt to get wificond from WifiInjector, we should remain in
+ * the SoftApModeState.
+ */
+ @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();
+ assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+ }
+
+ /**
+ * Test that we do not attempt to enter SoftApModeActiveState when we cannot get an ApInterface
+ * from wificond.
+ * Expectations: After a failed attempt to get an ApInterface from WifiInjector, we should
+ * remain in the SoftApModeState.
+ */
+ @Test
+ 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();
+ assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+ }
+
+ /**
+ * Test that we do can enter the SoftApModeActiveState if we are already in the SoftApModeState.
+ * Expectations: We should exit the current SoftApModeState and re-enter before successfully
+ * entering the SoftApModeActiveState.
+ */
+ @Test
+ 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();
+ assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+
+ enterSoftApActiveMode();
+ verify(mWificond).tearDownInterfaces();
+ }
+
+ /**
+ * Test that we return to the SoftApModeState after a failure is reported when in the
+ * SoftApModeActiveState.
+ * Expectations: We should exit the SoftApModeActiveState and stop the SoftApManager.
+ */
+ @Test
+ public void testSoftApFailureWhenActive() throws Exception {
+ enterSoftApActiveMode();
+ // now inject failure through the SoftApManager.Listener
+ mSoftApListener.onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, 0);
+ mLooper.dispatchNext();
+ assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+ verify(mSoftApManager).stop();
+ }
+
+ /**
+ * Test that we return to the SoftApModeState after the SoftApManager is stopped in the
+ * SoftApModeActiveState.
+ * Expectations: We should exit the SoftApModeActiveState and stop the SoftApManager.
+ */
+ @Test
+ public void testSoftApDisabledWhenActive() throws Exception {
+ enterSoftApActiveMode();
+ // now inject failure through the SoftApManager.Listener
+ mSoftApListener.onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, 0);
+ mLooper.dispatchNext();
+ assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+ verify(mSoftApManager).stop();
+ }
+
+ /**
+ * 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.
+ */
+ @Test
+ public void disableWifiWhenAlreadyOff() throws Exception {
+ verifyNoMoreInteractions(mWificond);
+ mWifiStateMachinePrime.disableWifi();
+ }
+}