summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRebecca Silberstein <silberst@google.com>2016-09-22 09:11:02 -0700
committerRebecca Silberstein <silberst@google.com>2016-11-21 09:33:19 -0800
commitbfc30bd4c2ff0c62d6cfff22088a935878966a46 (patch)
tree47e299de0485f51091f61317e98fc4204d5e1c34
parentc6e5fba1ef04ea394a3c3ee7b97b3bdb6a746155 (diff)
WifiStateMachinePrime: Implement SoftApMode path
Implement the logic to start, stop and maintain softap mode in WifiStateMachinePrime. This CL covers the implementation and tests, but does not include the call from WifiController to start softap with the new code. Bug: 31831721 Test: runtests.sh Change-Id: Ib3250eb804b5b6a8e2b3b7218145a422205b2b45
-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();
+ }
+}