diff options
3 files changed, 150 insertions, 182 deletions
diff --git a/service/java/com/android/server/wifi/WifiController.java b/service/java/com/android/server/wifi/WifiController.java index 8e9c1d73b..8bf013be0 100644 --- a/service/java/com/android/server/wifi/WifiController.java +++ b/service/java/com/android/server/wifi/WifiController.java @@ -336,6 +336,11 @@ public class WifiController extends StateMachine { mWifiStateMachine.setOperationalMode(WifiStateMachine.DISABLED_MODE, null); break; case CMD_SET_AP: + // first make sure we aren't in airplane mode + if (mSettingsStore.isAirplaneModeOn()) { + log("drop softap requests when in airplane mode"); + break; + } if (msg.arg1 == 1) { if (msg.arg2 == 0) { // previous wifi state has not been saved yet mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_DISABLED); @@ -432,14 +437,15 @@ public class WifiController extends StateMachine { break; case CMD_SET_AP: if (msg.arg1 == 1) { - // remeber that we were enabled + // remember that we were enabled mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_ENABLED); + // since softap is not split out in WifiController, need to explicitly + // disable client and scan modes + mWifiStateMachinePrime.disableWifi(); + mWifiStateMachine.setOperationalMode(WifiStateMachine.DISABLED_MODE, null); + mWifiStateMachinePrime.enterSoftAPMode((SoftApModeConfiguration) msg.obj); transitionTo(mApEnabledState); - // we should just go directly to ApEnabled since we will kill interfaces - // from WSMP - //deferMessage(obtainMessage(msg.what, msg.arg1, 1, msg.obj)); - //transitionTo(mApStaDisabledState); } break; default: @@ -505,13 +511,12 @@ public class WifiController extends StateMachine { // Before starting tethering, turn off supplicant for scan mode if (msg.arg1 == 1) { mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_DISABLED); + // since softap is not split out in WifiController, need to explicitly + // disable client and scan modes + mWifiStateMachinePrime.disableWifi(); mWifiStateMachinePrime.enterSoftAPMode((SoftApModeConfiguration) msg.obj); transitionTo(mApEnabledState); - // we should just go directly to ApEnabled since we will kill interfaces - // from WSMP - //deferMessage(obtainMessage(msg.what, msg.arg1, 1, msg.obj)); - //transitionTo(mApStaDisabledState); } break; case CMD_DEFERRED_TOGGLE: @@ -576,24 +581,26 @@ public class WifiController extends StateMachine { } @Override + public void exit() { + mWifiStateMachinePrime.stopSoftAPMode(); + } + + @Override public boolean processMessage(Message msg) { switch (msg.what) { case CMD_AIRPLANE_TOGGLED: if (mSettingsStore.isAirplaneModeOn()) { - mWifiStateMachinePrime.disableWifi(); - mPendingState = mApStaDisabledState; + transitionTo(mApStaDisabledState); } break; case CMD_WIFI_TOGGLED: if (mSettingsStore.isWifiToggleEnabled()) { - mWifiStateMachinePrime.disableWifi(); - mPendingState = mDeviceActiveState; + transitionTo(mDeviceActiveState); } break; case CMD_SET_AP: if (msg.arg1 == 0) { - mWifiStateMachinePrime.disableWifi(); - mPendingState = getNextWifiState(); + transitionTo(getNextWifiState()); } break; case CMD_AP_STOPPED: @@ -609,8 +616,7 @@ public class WifiController extends StateMachine { case CMD_EMERGENCY_CALL_STATE_CHANGED: case CMD_EMERGENCY_MODE_CHANGED: if (msg.arg1 == 1) { - mWifiStateMachinePrime.disableWifi(); - mPendingState = mEcmState; + transitionTo(mEcmState); } break; case CMD_AP_START_FAILURE: diff --git a/service/java/com/android/server/wifi/WifiStateMachinePrime.java b/service/java/com/android/server/wifi/WifiStateMachinePrime.java index 1da2176b0..46650ed7c 100644 --- a/service/java/com/android/server/wifi/WifiStateMachinePrime.java +++ b/service/java/com/android/server/wifi/WifiStateMachinePrime.java @@ -34,9 +34,6 @@ import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.server.wifi.WifiNative.StatusListener; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; - /** * This class provides the implementation for different WiFi operating modes. * @@ -56,13 +53,12 @@ public class WifiStateMachinePrime { private final WifiInjector mWifiInjector; private final Context mContext; private final Looper mLooper; + private final Handler mHandler; private final WifiNative mWifiNative; private final IBatteryStats mBatteryStats; private final SelfRecovery mSelfRecovery; private BaseWifiDiagnostics mWifiDiagnostics; - private Queue<SoftApModeConfiguration> mApConfigQueue = new ConcurrentLinkedQueue<>(); - // The base for wifi message types static final int BASE = Protocol.BASE_WIFI; @@ -135,6 +131,7 @@ public class WifiStateMachinePrime { mWifiInjector = wifiInjector; mContext = context; mLooper = looper; + mHandler = new Handler(looper); mWifiNative = wifiNative; mActiveModeManagers = new ArraySet(); mDefaultModeManager = defaultModeManager; @@ -175,19 +172,50 @@ public class WifiStateMachinePrime { * @param wifiConfig SoftApModeConfiguration for the hostapd softap */ public void enterSoftAPMode(@NonNull SoftApModeConfiguration wifiConfig) { - mApConfigQueue.offer(wifiConfig); - changeMode(ModeStateMachine.CMD_START_SOFT_AP_MODE); + mHandler.post(() -> { + startSoftAp(wifiConfig); + }); + } + + /** + * Method to stop soft ap for wifi hotspot. + * + * This method will stop any active softAp mode managers, if there is one. + */ + public void stopSoftAPMode() { + mHandler.post(() -> { + for (ActiveModeManager manager : mActiveModeManagers) { + if (manager instanceof SoftApManager) { + Log.d(TAG, "Stopping SoftApModeManager"); + manager.stop(); + mActiveModeManagers.remove(manager); + } + } + updateBatteryStatsWifiState(false); + }); } /** - * Method to fully disable wifi. + * Method to disable wifi in sta/client mode scenarios. * - * This mode will completely shut down wifi and will not perform any network scans. + * This mode will stop any client/scan modes and will not perform any network scans. */ public void disableWifi() { changeMode(ModeStateMachine.CMD_DISABLE_WIFI); } + /** + * Method to stop all active modes, for example, when toggling airplane mode. + */ + public void shutdownWifi() { + mHandler.post(() -> { + for (ActiveModeManager manager : mActiveModeManagers) { + manager.stop(); + } + updateBatteryStatsWifiState(false); + }); + } + protected String getCurrentMode() { return mModeStateMachine.getCurrentMode(); } @@ -196,27 +224,38 @@ public class WifiStateMachinePrime { mModeStateMachine.sendMessage(newMode); } + /** + * Helper class to wrap the ActiveModeManager callback objects. + */ + private class ModeCallback { + ActiveModeManager mActiveManager; + + void setActiveModeManager(ActiveModeManager manager) { + mActiveManager = manager; + } + + ActiveModeManager getActiveModeManager() { + return mActiveManager; + } + } + private class ModeStateMachine extends StateMachine { - // Commands for the state machine. + // Commands for the state machine - these will be removed, + // along with the StateMachine itself public static final int CMD_START_CLIENT_MODE = 0; public static final int CMD_START_SCAN_ONLY_MODE = 1; - public static final int CMD_START_SOFT_AP_MODE = 2; public static final int CMD_DISABLE_WIFI = 3; private final State mWifiDisabledState = new WifiDisabledState(); private final State mClientModeActiveState = new ClientModeActiveState(); private final State mScanOnlyModeActiveState = new ScanOnlyModeActiveState(); - private final State mSoftAPModeActiveState = new SoftAPModeActiveState(); ModeStateMachine() { super(TAG, mLooper); - // CHECKSTYLE:OFF IndentationCheck addState(mClientModeActiveState); addState(mScanOnlyModeActiveState); - addState(mSoftAPModeActiveState); addState(mWifiDisabledState); - // CHECKSTYLE:ON IndentationCheck Log.d(TAG, "Starting Wifi in WifiDisabledState"); setInitialState(mWifiDisabledState); @@ -237,10 +276,6 @@ public class WifiStateMachinePrime { Log.d(TAG, "Switching from " + getCurrentMode() + " to ScanOnlyMode"); mModeStateMachine.transitionTo(mScanOnlyModeActiveState); break; - case ModeStateMachine.CMD_START_SOFT_AP_MODE: - Log.d(TAG, "Switching from " + getCurrentMode() + " to SoftApMode"); - mModeStateMachine.transitionTo(mSoftAPModeActiveState); - break; case ModeStateMachine.CMD_DISABLE_WIFI: Log.d(TAG, "Switching from " + getCurrentMode() + " to WifiDisabled"); mModeStateMachine.transitionTo(mWifiDisabledState); @@ -356,13 +391,12 @@ public class WifiStateMachinePrime { private class ScanOnlyListener implements ScanOnlyModeManager.Listener { @Override public void onStateChanged(int state) { - Log.d(TAG, "State changed from scan only mode."); if (state == WifiManager.WIFI_STATE_UNKNOWN) { - Log.d(TAG, "ScanOnly mode failed"); + Log.d(TAG, "ScanOnlyMode mode failed"); // error while setting up scan mode or an unexpected failure. mModeStateMachine.sendMessage(CMD_SCAN_ONLY_MODE_FAILED); } else if (state == WifiManager.WIFI_STATE_DISABLED) { - Log.d(TAG, "ScanOnly mode stopped"); + Log.d(TAG, "ScanOnlyMode stopped"); //scan only mode stopped mModeStateMachine.sendMessage(CMD_SCAN_ONLY_MODE_STOPPED); } else if (state == WifiManager.WIFI_STATE_ENABLED) { @@ -416,100 +450,70 @@ public class WifiStateMachinePrime { return HANDLED; } } + } // class ModeStateMachine - class SoftAPModeActiveState extends ModeActiveState { - private class SoftApCallbackImpl implements WifiManager.SoftApCallback { - @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); - } - - if (mSoftApCallback != null) { - mSoftApCallback.onStateChanged(state, reason); - } else { - Log.wtf(TAG, "SoftApCallback is null. Dropping StateChanged event."); - } - } - - @Override - public void onNumClientsChanged(int numClients) { - if (mSoftApCallback != null) { - mSoftApCallback.onNumClientsChanged(numClients); - } else { - Log.d(TAG, "SoftApCallback is null. Dropping NumClientsChanged event."); - } - } + private class SoftApCallbackImpl extends ModeCallback implements WifiManager.SoftApCallback { + @Override + public void onStateChanged(int state, int reason) { + if (state == WifiManager.WIFI_AP_STATE_DISABLED) { + mActiveModeManagers.remove(getActiveModeManager()); + updateBatteryStatsWifiState(false); + } else if (state == WifiManager.WIFI_AP_STATE_FAILED) { + mActiveModeManagers.remove(getActiveModeManager()); + updateBatteryStatsWifiState(false); } - @Override - public void enter() { - Log.d(TAG, "Entering SoftApModeActiveState"); - - // make sure everything is torn down - remove when client mode is moved here - cleanup(); - - // until softap mode is freed, make sure wifi scanner is disabled - mDefaultModeManager.sendScanAvailableBroadcast(mContext, false); + if (mSoftApCallback != null) { + mSoftApCallback.onStateChanged(state, reason); + } + } - SoftApModeConfiguration softApModeConfig = mApConfigQueue.poll(); - WifiConfiguration config = softApModeConfig.getWifiConfiguration(); - // TODO (b/67601382): add checks for valid softap configs - if (config != null && config.SSID != null) { - Log.d(TAG, "Passing config to SoftApManager! " + config); - } else { - config = null; - } - mManager = mWifiInjector.makeSoftApManager( - new SoftApCallbackImpl(), softApModeConfig); - mManager.start(); - mActiveModeManagers.add(mManager); - updateBatteryStatsWifiState(true); + @Override + public void onNumClientsChanged(int numClients) { + if (mSoftApCallback != null) { + mSoftApCallback.onNumClientsChanged(numClients); + } else { + Log.d(TAG, "SoftApCallback is null. Dropping NumClientsChanged event."); } + } + } - @Override - public boolean processMessage(Message message) { - if (checkForAndHandleModeChange(message)) { - return HANDLED; - } + private void startSoftAp(SoftApModeConfiguration softapConfig) { + Log.d(TAG, "Starting SoftApModeManager"); - switch(message.what) { - case CMD_START_AP: - Log.d(TAG, "Received CMD_START_AP when active - invalid message - drop"); - break; - case CMD_STOP_AP: - mManager.stop(); - break; - case CMD_START_AP_FAILURE: - Log.d(TAG, "Failed to start SoftApMode. Return to WifiDisabledState."); - mModeStateMachine.transitionTo(mWifiDisabledState); - break; - case CMD_AP_STOPPED: - Log.d(TAG, "SoftApModeActiveState stopped. Return to WifiDisabledState."); - mModeStateMachine.transitionTo(mWifiDisabledState); - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } + WifiConfiguration config = softapConfig.getWifiConfiguration(); + // TODO (b/67601382): add checks for valid softap configs + if (config != null && config.SSID != null) { + Log.d(TAG, "Passing config to SoftApManager! " + config); + } else { + config = null; } - } // class ModeStateMachine + SoftApCallbackImpl callback = new SoftApCallbackImpl(); + ActiveModeManager manager = mWifiInjector.makeSoftApManager(callback, softapConfig); + callback.setActiveModeManager(manager); + manager.start(); + mActiveModeManagers.add(manager); + updateBatteryStatsWifiState(true); + } /** * Helper method to report wifi state as on/off (doesn't matter which mode). * - * @param enabled boolean indicating if wifi is on or off + * @param enabled boolean indicating that some mode has been turned on or off */ private void updateBatteryStatsWifiState(boolean enabled) { try { if (enabled) { - mBatteryStats.noteWifiOn(); + if (mActiveModeManagers.size() == 1) { + // only report wifi on if we haven't already + mBatteryStats.noteWifiOn(); + } } else { - mBatteryStats.noteWifiOff(); + if (mActiveModeManagers.size() == 0) { + // only report if we don't have any active modes + mBatteryStats.noteWifiOff(); + } } } catch (RemoteException e) { Log.e(TAG, "Failed to note battery stats in wifi"); @@ -526,7 +530,6 @@ public class WifiStateMachinePrime { // callback used to receive callbacks about underlying native failures private final class WifiNativeStatusListener implements StatusListener { - Handler mHandler = new Handler(mLooper); @Override public void onStatusChanged(boolean isReady) { diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java index e0812606d..5f136e17a 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java @@ -47,7 +47,6 @@ public class WifiStateMachinePrimeTest { private static final String CLIENT_MODE_STATE_STRING = "ClientModeActiveState"; private static final String SCAN_ONLY_MODE_STATE_STRING = "ScanOnlyModeActiveState"; - private static final String SOFT_AP_MODE_STATE_STRING = "SoftAPModeActiveState"; private static final String WIFI_DISABLED_STATE_STRING = "WifiDisabledState"; private static final String WIFI_IFACE_NAME = "mockWlan"; @@ -181,9 +180,10 @@ public class WifiStateMachinePrimeTest { any()); mWifiStateMachinePrime.enterSoftAPMode(softApConfig); mLooper.dispatchAll(); - assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); verify(mSoftApManager).start(); - verify(mBatteryStats).noteWifiOn(); + if (fromState.equals(WIFI_DISABLED_STATE_STRING)) { + verify(mBatteryStats).noteWifiOn(); + } } private void verifyCleanupCalled() { @@ -218,12 +218,11 @@ public class WifiStateMachinePrimeTest { @Test public void testEnterSoftApModeFromDisabled() throws Exception { enterSoftApActiveMode(); - verify(mWifiNative, times(2)).teardownAllInterfaces(); + verify(mWifiNative).teardownAllInterfaces(); } /** * Test that WifiStateMachinePrime properly enters the SoftApModeActiveState from another state. - * Expectations: When going from one state to another, cleanup will be called */ @Test public void testEnterSoftApModeFromDifferentState() throws Exception { @@ -232,9 +231,7 @@ public class WifiStateMachinePrimeTest { assertEquals(CLIENT_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); reset(mBatteryStats); enterSoftApActiveMode(); - // still only two times since we do not control the interface yet in client mode - verify(mWifiNative, times(2)).teardownAllInterfaces(); - verify(mDefaultModeManager, times(2)).sendScanAvailableBroadcast(eq(mContext), eq(false)); + verify(mWifiNative).teardownAllInterfaces(); } /** @@ -253,41 +250,20 @@ public class WifiStateMachinePrimeTest { } /** - * Test that we can disable wifi fully from the SoftApModeActiveState. + * Test that we can disable wifi from the SoftApModeActiveState and not impact softap. */ @Test - public void testDisableWifiFromSoftApModeActiveState() throws Exception { + public void testDisableWifiFromSoftApModeActiveStateDoesNotStopSoftAp() throws Exception { enterSoftApActiveMode(); reset(mDefaultModeManager); mWifiStateMachinePrime.disableWifi(); mLooper.dispatchAll(); - verify(mSoftApManager).stop(); - verify(mBatteryStats).noteWifiOff(); - verify(mDefaultModeManager).sendScanAvailableBroadcast(eq(mContext), eq(false)); - assertEquals(WIFI_DISABLED_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); - verify(mWifiNative, times(3)).teardownAllInterfaces(); - } - - /** - * Test that we can disable wifi fully from the SoftApModeState. - */ - @Test - public void testDisableWifiFromSoftApModeState() throws Exception { - enterSoftApActiveMode(); - // now inject failure through the SoftApManager.Listener - mSoftApManagerCallback.onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, 0); - mLooper.dispatchAll(); - assertEquals(WIFI_DISABLED_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); - - reset(mDefaultModeManager); - mWifiStateMachinePrime.disableWifi(); - mLooper.dispatchAll(); - verify(mBatteryStats).noteWifiOff(); + verify(mSoftApManager, never()).stop(); + verify(mBatteryStats, never()).noteWifiOff(); verify(mDefaultModeManager).sendScanAvailableBroadcast(eq(mContext), eq(false)); - assertEquals(WIFI_DISABLED_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); - verify(mWifiNative, times(4)).teardownAllInterfaces(); + verify(mWifiNative, times(2)).teardownAllInterfaces(); } /** @@ -307,18 +283,19 @@ public class WifiStateMachinePrimeTest { /** * 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. + * Expectation: When switching out of SoftApModeActiveState we do not impact softap operation */ @Test public void testSwitchModeWhenSoftApActiveMode() throws Exception { enterSoftApActiveMode(); + reset(mWifiNative); + mWifiStateMachinePrime.enterClientMode(); mLooper.dispatchAll(); - verify(mSoftApManager).stop(); + verify(mSoftApManager, never()).stop(); assertEquals(CLIENT_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); - verify(mWifiNative, times(2)).teardownAllInterfaces(); + verify(mWifiNative, never()).teardownAllInterfaces(); } /** @@ -338,7 +315,7 @@ public class WifiStateMachinePrimeTest { reset(mSoftApManager, mBatteryStats); enterSoftApActiveMode(); - verify(mWifiNative, times(4)).teardownAllInterfaces(); + verify(mWifiNative).teardownAllInterfaces(); } /** @@ -370,9 +347,6 @@ public class WifiStateMachinePrimeTest { // now inject failure through the SoftApManager.Listener mSoftApManagerCallback.onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, 0); mLooper.dispatchAll(); - assertEquals(WIFI_DISABLED_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); - verify(mSoftApManager).stop(); - verify(mWifiNative, times(3)).teardownAllInterfaces(); verify(mBatteryStats).noteWifiOff(); } @@ -401,13 +375,12 @@ public class WifiStateMachinePrimeTest { @Test public void testSoftApDisabledWhenActive() throws Exception { enterSoftApActiveMode(); + reset(mWifiNative); // now inject failure through the SoftApManager.Listener mSoftApManagerCallback.onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, 0); mLooper.dispatchAll(); - assertEquals(WIFI_DISABLED_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); - verify(mSoftApManager).stop(); - verify(mWifiNative, times(3)).teardownAllInterfaces(); verify(mBatteryStats).noteWifiOff(); + verifyNoMoreInteractions(mWifiNative); } /** @@ -550,32 +523,18 @@ public class WifiStateMachinePrimeTest { when(mWifiInjector.makeSoftApManager(any(WifiManager.SoftApCallback.class), eq(softApConfig1))) .thenReturn(mSoftApManager); + // make a second softap manager + SoftApManager softapManager = mock(SoftApManager.class); when(mWifiInjector.makeSoftApManager(any(WifiManager.SoftApCallback.class), - eq(softApConfig2))) - .thenReturn(mSoftApManager); - + eq(softApConfig2))) + .thenReturn(softapManager); mWifiStateMachinePrime.enterSoftAPMode(softApConfig1); mWifiStateMachinePrime.enterSoftAPMode(softApConfig2); mLooper.dispatchAll(); - assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); - verify(mBatteryStats).noteWifiOff(); - } - - /** - * Test that when softap manager reports that softap has stopped, we return to the - * WifiDisabledState. - */ - @Test - public void testSoftApStopReturnsToWifiDisabled() throws Exception { - enterSoftApActiveMode(); - - mSoftApManagerCallback.onStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0); - mLooper.dispatchAll(); - assertEquals(WIFI_DISABLED_STATE_STRING, mWifiStateMachinePrime.getCurrentMode()); - verify(mWifiNative, times(3)).teardownAllInterfaces(); - verify(mBatteryStats).noteWifiOff(); - verify(mDefaultModeManager, times(3)).sendScanAvailableBroadcast(eq(mContext), eq(false)); + verify(mSoftApManager).start(); + verify(softapManager).start(); + verify(mBatteryStats).noteWifiOn(); } /** |