diff options
author | Rebecca Silberstein <silberst@google.com> | 2018-04-03 15:59:39 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2018-04-03 15:59:39 +0000 |
commit | bd0ca3759ab4cbee1ffaf772702a1b2e783a5952 (patch) | |
tree | b29a9e9930821c3fbb01e9ae9603fb1ddb3c6578 | |
parent | 85fa6ab417f39148fceee1b7262063249600c0a2 (diff) | |
parent | 48aeddaf3b150d810f2a93da658e40824a99ae2a (diff) |
Merge changes Ie8265bde,Ic42964e0,I13282eb1,Ifb7fc528 into pi-dev
* changes:
WifiStateMachine: remove SupplicantStartingState
WifiController: disable wifi when recovery throttled
WifiStateMachine: remove supplicant disconnect handling
WifiStateMachine: onDown triggers wifi off
6 files changed, 342 insertions, 155 deletions
diff --git a/service/java/com/android/server/wifi/SelfRecovery.java b/service/java/com/android/server/wifi/SelfRecovery.java index d3985f5fd..c9e95a77f 100644 --- a/service/java/com/android/server/wifi/SelfRecovery.java +++ b/service/java/com/android/server/wifi/SelfRecovery.java @@ -38,11 +38,13 @@ public class SelfRecovery { */ public static final int REASON_LAST_RESORT_WATCHDOG = 0; public static final int REASON_WIFINATIVE_FAILURE = 1; + public static final int REASON_STA_IFACE_DOWN = 2; public static final long MAX_RESTARTS_IN_TIME_WINDOW = 2; // 2 restarts per hour public static final long MAX_RESTARTS_TIME_WINDOW_MILLIS = 60 * 60 * 1000; // 1 hour protected static final String[] REASON_STRINGS = { - "Last Resort Watchdog", // REASON_LAST_RESORT_WATCHDOG - "WifiNative Failure" // REASON_WIFINATIVE_FAILURE + "Last Resort Watchdog", // REASON_LAST_RESORT_WATCHDOG + "WifiNative Failure", // REASON_WIFINATIVE_FAILURE + "Sta Interface Down" // REASON_STA_IFACE_DOWN }; private final WifiController mWifiController; @@ -59,28 +61,39 @@ public class SelfRecovery { * Trigger recovery. * * This method does the following: - * 1. Raises a wtf. - * 2. Sends {@link WifiController#CMD_RESTART_WIFI} to {@link WifiController} to initiate the - * stack restart. + * 1. Checks reason code used to trigger recovery + * 2. Checks for sta iface down triggers and disables wifi by sending {@link + * WifiController#CMD_RECOVERY_DISABLE_WIFI} to {@link WifiController} to disable wifi. + * 3. Throttles restart calls for underlying native failures + * 4. Sends {@link WifiController#CMD_RECOVERY_RESTART_WIFI} to {@link WifiController} to + * initiate the stack restart. * @param reason One of the above |REASON_*| codes. */ public void trigger(int reason) { - if (!(reason == REASON_LAST_RESORT_WATCHDOG || reason == REASON_WIFINATIVE_FAILURE)) { + if (!(reason == REASON_LAST_RESORT_WATCHDOG || reason == REASON_WIFINATIVE_FAILURE + || reason == REASON_STA_IFACE_DOWN)) { Log.e(TAG, "Invalid trigger reason. Ignoring..."); return; } + if (reason == REASON_STA_IFACE_DOWN) { + Log.e(TAG, "STA interface down, disable wifi"); + mWifiController.sendMessage(WifiController.CMD_RECOVERY_DISABLE_WIFI); + return; + } + Log.e(TAG, "Triggering recovery for reason: " + REASON_STRINGS[reason]); if (reason == REASON_WIFINATIVE_FAILURE) { trimPastRestartTimes(); // Ensure there haven't been too many restarts within MAX_RESTARTS_TIME_WINDOW if (mPastRestartTimes.size() >= MAX_RESTARTS_IN_TIME_WINDOW) { Log.e(TAG, "Already restarted wifi (" + MAX_RESTARTS_IN_TIME_WINDOW + ") times in" - + " last (" + MAX_RESTARTS_TIME_WINDOW_MILLIS + "ms ). Ignoring..."); + + " last (" + MAX_RESTARTS_TIME_WINDOW_MILLIS + "ms ). Disabling wifi"); + mWifiController.sendMessage(WifiController.CMD_RECOVERY_DISABLE_WIFI); return; } mPastRestartTimes.add(mClock.getElapsedSinceBootMillis()); } - mWifiController.sendMessage(WifiController.CMD_RESTART_WIFI, reason); + mWifiController.sendMessage(WifiController.CMD_RECOVERY_RESTART_WIFI, reason); } /** diff --git a/service/java/com/android/server/wifi/WifiController.java b/service/java/com/android/server/wifi/WifiController.java index 5cc0306e1..3db804b9b 100644 --- a/service/java/com/android/server/wifi/WifiController.java +++ b/service/java/com/android/server/wifi/WifiController.java @@ -76,21 +76,23 @@ public class WifiController extends StateMachine { private static final int BASE = Protocol.BASE_WIFI_CONTROLLER; - static final int CMD_EMERGENCY_MODE_CHANGED = BASE + 1; - static final int CMD_SCAN_ALWAYS_MODE_CHANGED = BASE + 7; - static final int CMD_WIFI_TOGGLED = BASE + 8; - static final int CMD_AIRPLANE_TOGGLED = BASE + 9; - static final int CMD_SET_AP = BASE + 10; - static final int CMD_DEFERRED_TOGGLE = BASE + 11; - static final int CMD_USER_PRESENT = BASE + 12; - static final int CMD_AP_START_FAILURE = BASE + 13; - static final int CMD_EMERGENCY_CALL_STATE_CHANGED = BASE + 14; - static final int CMD_AP_STOPPED = BASE + 15; - static final int CMD_STA_START_FAILURE = BASE + 16; + static final int CMD_EMERGENCY_MODE_CHANGED = BASE + 1; + static final int CMD_SCAN_ALWAYS_MODE_CHANGED = BASE + 7; + static final int CMD_WIFI_TOGGLED = BASE + 8; + static final int CMD_AIRPLANE_TOGGLED = BASE + 9; + static final int CMD_SET_AP = BASE + 10; + static final int CMD_DEFERRED_TOGGLE = BASE + 11; + static final int CMD_USER_PRESENT = BASE + 12; + static final int CMD_AP_START_FAILURE = BASE + 13; + static final int CMD_EMERGENCY_CALL_STATE_CHANGED = BASE + 14; + static final int CMD_AP_STOPPED = BASE + 15; + static final int CMD_STA_START_FAILURE = BASE + 16; // Command used to trigger a wifi stack restart when in active mode - static final int CMD_RESTART_WIFI = BASE + 17; + static final int CMD_RECOVERY_RESTART_WIFI = BASE + 17; // Internal command used to complete wifi stack restart - private static final int CMD_RESTART_WIFI_CONTINUE = BASE + 18; + private static final int CMD_RECOVERY_RESTART_WIFI_CONTINUE = BASE + 18; + // Command to disable wifi when SelfRecovery is throttled or otherwise not doing full recovery + static final int CMD_RECOVERY_DISABLE_WIFI = BASE + 19; private DefaultState mDefaultState = new DefaultState(); private StaEnabledState mStaEnabledState = new StaEnabledState(); @@ -215,8 +217,9 @@ public class WifiController extends StateMachine { case CMD_AP_START_FAILURE: case CMD_AP_STOPPED: case CMD_STA_START_FAILURE: - case CMD_RESTART_WIFI: - case CMD_RESTART_WIFI_CONTINUE: + case CMD_RECOVERY_RESTART_WIFI: + case CMD_RECOVERY_RESTART_WIFI_CONTINUE: + case CMD_RECOVERY_DISABLE_WIFI: break; case CMD_USER_PRESENT: mFirstUserSignOnSeen = true; @@ -297,7 +300,7 @@ public class WifiController extends StateMachine { log("DEFERRED_TOGGLE handled"); sendMessage((Message)(msg.obj)); break; - case CMD_RESTART_WIFI_CONTINUE: + case CMD_RECOVERY_RESTART_WIFI_CONTINUE: transitionTo(mDeviceActiveState); break; default: @@ -649,7 +652,7 @@ public class WifiController extends StateMachine { } mFirstUserSignOnSeen = true; return HANDLED; - } else if (msg.what == CMD_RESTART_WIFI) { + } else if (msg.what == CMD_RECOVERY_RESTART_WIFI) { final String bugTitle = "Wi-Fi BugReport"; final String bugDetail; if (msg.obj != null && msg.arg1 < SelfRecovery.REASON_STRINGS.length @@ -663,9 +666,12 @@ public class WifiController extends StateMachine { mWifiStateMachine.takeBugReport(bugTitle, bugDetail); }); } - deferMessage(obtainMessage(CMD_RESTART_WIFI_CONTINUE)); + deferMessage(obtainMessage(CMD_RECOVERY_RESTART_WIFI_CONTINUE)); transitionTo(mApStaDisabledState); return HANDLED; + } else if (msg.what == CMD_RECOVERY_DISABLE_WIFI) { + loge("Recovery has been throttled, disable wifi"); + transitionTo(mApStaDisabledState); } return NOT_HANDLED; } diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java index eb3848dc6..e2345e9b1 100644 --- a/service/java/com/android/server/wifi/WifiStateMachine.java +++ b/service/java/com/android/server/wifi/WifiStateMachine.java @@ -229,15 +229,23 @@ public class WifiStateMachine extends StateMachine { private final InterfaceCallback mWifiNativeInterfaceCallback = new InterfaceCallback() { @Override public void onDestroyed(String ifaceName) { - sendMessage(CMD_INTERFACE_DESTROYED); + if (mInterfaceName != null && mInterfaceName.equals(ifaceName)) { + sendMessage(CMD_INTERFACE_DESTROYED); + } } @Override public void onUp(String ifaceName) { + if (mInterfaceName != null && mInterfaceName.equals(ifaceName)) { + sendMessage(CMD_INTERFACE_STATUS_CHANGED, 1); + } } @Override public void onDown(String ifaceName) { + if (mInterfaceName != null && mInterfaceName.equals(ifaceName)) { + sendMessage(CMD_INTERFACE_STATUS_CHANGED, 0); + } } }; private boolean mIpReachabilityDisconnectEnabled = true; @@ -326,8 +334,6 @@ public class WifiStateMachine extends StateMachine { */ public static final String SUPPLICANT_BSSID_ANY = "any"; - private int mSupplicantRestartCount = 0; - /** * The link properties of the wifi interface. * Do not modify this directly; use updateLinkProperties instead. @@ -459,12 +465,14 @@ public class WifiStateMachine extends StateMachine { static final int CMD_STOP_SUPPLICANT = BASE + 12; /* STA interface destroyed */ static final int CMD_INTERFACE_DESTROYED = BASE + 13; + /* STA interface down */ + static final int CMD_INTERFACE_DOWN = BASE + 14; /* Indicates Static IP succeeded */ static final int CMD_STATIC_IP_SUCCESS = BASE + 15; /* Indicates Static IP failed */ static final int CMD_STATIC_IP_FAILURE = BASE + 16; - /* A delayed message sent to start driver when it fail to come up */ - static final int CMD_DRIVER_START_TIMED_OUT = BASE + 19; + /* Interface status change */ + static final int CMD_INTERFACE_STATUS_CHANGED = BASE + 20; /* Start the soft access point */ static final int CMD_START_AP = BASE + 21; @@ -769,8 +777,6 @@ public class WifiStateMachine extends StateMachine { private State mDefaultState = new DefaultState(); /* Temporary initial state */ private State mInitialState = new InitialState(); - /* Driver loaded, waiting for supplicant to start */ - private State mSupplicantStartingState = new SupplicantStartingState(); /* Driver loaded and supplicant ready */ private State mSupplicantStartedState = new SupplicantStartedState(); /* Scan for networks, no connection will be established */ @@ -992,7 +998,6 @@ public class WifiStateMachine extends StateMachine { // CHECKSTYLE:OFF IndentationCheck addState(mDefaultState); addState(mInitialState, mDefaultState); - addState(mSupplicantStartingState, mInitialState); addState(mSupplicantStartedState, mInitialState); addState(mConnectModeState, mSupplicantStartedState); addState(mL2ConnectedState, mConnectModeState); @@ -1045,9 +1050,6 @@ public class WifiStateMachine extends StateMachine { getHandler()); mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.RX_HS20_ANQP_ICON_EVENT, getHandler()); - mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SUP_CONNECTION_EVENT, getHandler()); - mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SUP_DISCONNECTION_EVENT, - getHandler()); mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, getHandler()); mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SUP_REQUEST_IDENTITY, @@ -3380,6 +3382,25 @@ public class WifiStateMachine extends StateMachine { + macRandomizationEnabled); } + /** + * Handle the error case where our underlying interface went down (if we do not have mac + * randomization enabled (b/72459123). + * + * This method triggers SelfRecovery with the error of REASON_STA_IFACE_DOWN. SelfRecovery then + * decides if wifi should be restarted or disabled. + */ + private void handleInterfaceDown() { + if (mEnableConnectedMacRandomization.get()) { + // interface will go down when mac randomization is active, skip + Log.d(TAG, "MacRandomization enabled, ignoring iface down"); + return; + } + + Log.e(TAG, "Detected an interface down, report failure to SelfRecovery"); + // report a failure + mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_STA_IFACE_DOWN); + } + /******************************************************** * HSM states *******************************************************/ @@ -3488,7 +3509,6 @@ public class WifiStateMachine extends StateMachine { break; case CMD_START_SUPPLICANT: case CMD_STOP_SUPPLICANT: - case CMD_DRIVER_START_TIMED_OUT: case CMD_START_AP_FAILURE: case CMD_STOP_AP: case CMD_AP_STOPPED: @@ -3496,8 +3516,6 @@ public class WifiStateMachine extends StateMachine { case CMD_RECONNECT: case CMD_REASSOCIATE: case CMD_RELOAD_TLS_AND_RECONNECT: - case WifiMonitor.SUP_CONNECTION_EVENT: - case WifiMonitor.SUP_DISCONNECTION_EVENT: case WifiMonitor.NETWORK_CONNECTION_EVENT: case WifiMonitor.NETWORK_DISCONNECTION_EVENT: case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: @@ -3524,6 +3542,8 @@ public class WifiStateMachine extends StateMachine { case CMD_SELECT_TX_POWER_SCENARIO: case CMD_WIFINATIVE_FAILURE: case CMD_INTERFACE_DESTROYED: + case CMD_INTERFACE_DOWN: + case CMD_INTERFACE_STATUS_CHANGED: messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD; break; case CMD_START_AP: @@ -3712,6 +3732,23 @@ public class WifiStateMachine extends StateMachine { } class InitialState extends State { + private boolean mIfaceIsUp; + + private void onUpChanged(boolean isUp) { + if (isUp == mIfaceIsUp) { + return; // no change + } + mIfaceIsUp = isUp; + if (isUp) { + Log.d(TAG, "Client mode interface is up"); + // for now, do nothing - client mode has never waited for iface up + } else { + // A driver/firmware hang can now put the interface in a down state. + // We detect the interface going down and recover from it + handleInterfaceDown(); + } + } + private void cleanup() { // tell scanning service that scans are not available - about to kill the interface and // supplicant @@ -3727,10 +3764,12 @@ public class WifiStateMachine extends StateMachine { // TODO: Remove this big hammer. We cannot support concurrent interfaces with this! mWifiNative.teardownAllInterfaces(); mInterfaceName = null; + mIfaceIsUp = false; } @Override public void enter() { + mIfaceIsUp = false; mWifiMonitor.stopAllMonitoring(); mWifiStateTracker.updateState(WifiStateTracker.INVALID); cleanup(); @@ -3751,6 +3790,9 @@ public class WifiStateMachine extends StateMachine { transitionTo(mDefaultState); break; } + // now that we have the interface, initialize our up/down status + onUpChanged(mWifiNative.isInterfaceUp(mInterfaceName)); + mIpClient = mFacade.makeIpClient( mContext, mInterfaceName, new IpClientCallback()); mIpClient.setMulticastFilter(true); @@ -3759,7 +3801,7 @@ public class WifiStateMachine extends StateMachine { mWifiMonitor.startMonitoring(mInterfaceName); mWifiInjector.getWifiLastResortWatchdog().clearAllFailureCounts(); setSupplicantLogLevel(); - transitionTo(mSupplicantStartingState); + transitionTo(mSupplicantStartedState); break; case CMD_SET_OPERATIONAL_MODE: if (message.arg1 == CONNECT_MODE) { @@ -3773,46 +3815,20 @@ public class WifiStateMachine extends StateMachine { WifiDiagnostics.REPORT_REASON_WIFINATIVE_FAILURE); mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE); break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - } - - class SupplicantStartingState extends State { - - @Override - public boolean processMessage(Message message) { - logStateAndMessage(message, this); - - switch(message.what) { - case WifiMonitor.SUP_CONNECTION_EVENT: - if (mVerboseLoggingEnabled) log("Supplicant connection established"); - - mSupplicantRestartCount = 0; - /* Reset the supplicant state to indicate the supplicant - * state is not known at this time */ - mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE); - /* Initialize data structures */ - mLastBssid = null; - mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID; - mLastSignalLevel = -1; - mWifiInfo.setMacAddress(mWifiNative.getMacAddress(mInterfaceName)); - // Attempt to migrate data out of legacy store. - if (!mWifiConfigManager.migrateFromLegacyStore()) { - Log.e(TAG, "Failed to migrate from legacy config store"); + case CMD_INTERFACE_STATUS_CHANGED: + boolean isUp = message.arg1 == 1; + // For now, this message can be triggered due to link state and/or interface + // status changes (b/77218676). First check if we really see an iface down by + // consulting our view of supplicant state. + if (!isUp && SupplicantState.isDriverActive(mWifiInfo.getSupplicantState())) { + // the driver is active, so this could just be part of normal operation, do + // not disable wifi in these cases (ex, a network was removed) or worry + // about the link status + break; } - sendSupplicantConnectionChangedBroadcast(true); - transitionTo(mSupplicantStartedState); - break; - case WifiMonitor.SUP_DISCONNECTION_EVENT: - // since control is split between WSM and WSMP - do not worry about supplicant - // dying if we haven't seen it up yet + + onUpChanged(isUp); break; - case CMD_START_SUPPLICANT: - case CMD_STOP_SUPPLICANT: - case CMD_STOP_AP: default: return NOT_HANDLED; } @@ -3827,6 +3843,19 @@ public class WifiStateMachine extends StateMachine { logd("SupplicantStartedState enter"); } + // reset state related to supplicant starting + mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE); + // Initialize data structures + mLastBssid = null; + mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID; + mLastSignalLevel = -1; + mWifiInfo.setMacAddress(mWifiNative.getMacAddress(mInterfaceName)); + // Attempt to migrate data out of legacy store. + if (!mWifiConfigManager.migrateFromLegacyStore()) { + Log.e(TAG, "Failed to migrate from legacy config store"); + } + sendSupplicantConnectionChangedBroadcast(true); + mWifiNative.setExternalSim(mInterfaceName, true); setRandomMacOui(); @@ -3897,19 +3926,6 @@ public class WifiStateMachine extends StateMachine { logStateAndMessage(message, this); switch(message.what) { - case WifiMonitor.SUP_DISCONNECTION_EVENT: /* Supplicant connection lost */ - // first check if we are expecting a mode switch - if (mModeChange) { - logd("expecting a mode change, do not restart supplicant"); - return HANDLED; - } - loge("Connection lost, restart supplicant"); - handleSupplicantConnectionLoss(true); - handleNetworkDisconnect(); - mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE); - sendMessage(CMD_START_SUPPLICANT); - transitionTo(mInitialState); - break; case CMD_TARGET_BSSID: // Trying to associate to this BSSID if (message.obj != null) { @@ -4082,12 +4098,6 @@ public class WifiStateMachine extends StateMachine { case WifiManager.FORGET_NETWORK: s = "FORGET_NETWORK"; break; - case WifiMonitor.SUP_CONNECTION_EVENT: - s = "SUP_CONNECTION_EVENT"; - break; - case WifiMonitor.SUP_DISCONNECTION_EVENT: - s = "SUP_DISCONNECTION_EVENT"; - break; case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: s = "SUPPLICANT_STATE_CHANGE_EVENT"; break; @@ -4336,20 +4346,6 @@ public class WifiStateMachine extends StateMachine { break; case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: SupplicantState state = handleSupplicantStateChange(message); - // A driver/firmware hang can now put the interface in a down state. - // We detect the interface going down and recover from it - if (!SupplicantState.isDriverActive(state) && !mModeChange - && !mEnableConnectedMacRandomization.get()) { - if (mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) { - handleNetworkDisconnect(); - } - log("Detected an interface down, restart driver"); - // Rely on the fact that this will force us into killing supplicant and then - // restart supplicant from a clean state. - sendMessage(CMD_START_SUPPLICANT); - transitionTo(mInitialState); - break; - } // Supplicant can fail to report a NETWORK_DISCONNECTION_EVENT // when authentication times out after a successful connection, @@ -4371,6 +4367,18 @@ public class WifiStateMachine extends StateMachine { if (state == SupplicantState.COMPLETED) { mIpClient.confirmConfiguration(); } + + if (!SupplicantState.isDriverActive(state)) { + // still use supplicant to detect interface down while work to + // mitigate b/77218676 is in progress + // note: explicitly using this command to dedup iface down notification + // paths (onUpChanged filters out duplicate updates) + sendMessage(CMD_INTERFACE_STATUS_CHANGED, 0); + if (mVerboseLoggingEnabled) { + Log.d(TAG, "detected interface down via supplicant"); + } + } + break; case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST: if (message.arg1 == 1) { diff --git a/tests/wifitests/src/com/android/server/wifi/SelfRecoveryTest.java b/tests/wifitests/src/com/android/server/wifi/SelfRecoveryTest.java index c8dafdd69..9b50517f8 100644 --- a/tests/wifitests/src/com/android/server/wifi/SelfRecoveryTest.java +++ b/tests/wifitests/src/com/android/server/wifi/SelfRecoveryTest.java @@ -47,13 +47,13 @@ public class SelfRecoveryTest { @Test public void testValidTriggerReasonsSendMessageToWifiController() { mSelfRecovery.trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RESTART_WIFI), anyInt()); + verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), anyInt()); reset(mWifiController); when(mClock.getElapsedSinceBootMillis()) .thenReturn(SelfRecovery.MAX_RESTARTS_TIME_WINDOW_MILLIS + 1); mSelfRecovery.trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RESTART_WIFI), anyInt()); + verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), anyInt()); reset(mWifiController); } @@ -71,6 +71,15 @@ public class SelfRecoveryTest { } /** + * Verifies that a STA interface down event will trigger WifiController to disable wifi. + */ + @Test + public void testStaIfaceDownDisablesWifi() { + mSelfRecovery.trigger(SelfRecovery.REASON_STA_IFACE_DOWN); + verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_DISABLE_WIFI)); + } + + /** * Verifies that invocations of {@link SelfRecovery#trigger(int)} for REASON_HAL_CRASH & * REASON_WIFICOND_CRASH are limited to {@link SelfRecovery#MAX_RESTARTS_IN_TIME_WINDOW} in a * {@link SelfRecovery#MAX_RESTARTS_TIME_WINDOW_MILLIS} millisecond time window. @@ -82,46 +91,57 @@ public class SelfRecoveryTest { // aren't ignored for (int i = 0; i < SelfRecovery.MAX_RESTARTS_IN_TIME_WINDOW / 2; i++) { mSelfRecovery.trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RESTART_WIFI), anyInt()); + verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), + anyInt()); reset(mWifiController); mSelfRecovery.trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RESTART_WIFI), anyInt()); + verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), + anyInt()); reset(mWifiController); } if ((SelfRecovery.MAX_RESTARTS_IN_TIME_WINDOW % 2) == 1) { mSelfRecovery.trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RESTART_WIFI), anyInt()); + verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), + anyInt()); reset(mWifiController); } - // Verify that further attempts to trigger restarts for are ignored + // Verify that further attempts to trigger restarts disable wifi mSelfRecovery.trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE); - verify(mWifiController, never()).sendMessage(eq(WifiController.CMD_RESTART_WIFI), + verify(mWifiController, never()).sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), anyString()); + verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_DISABLE_WIFI)); reset(mWifiController); mSelfRecovery.trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE); - verify(mWifiController, never()).sendMessage(eq(WifiController.CMD_RESTART_WIFI), + verify(mWifiController, never()).sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), anyString()); + verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_DISABLE_WIFI)); reset(mWifiController); // Verify L.R.Watchdog can still restart things (It has its own complex limiter) mSelfRecovery.trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RESTART_WIFI), anyInt()); + verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), + anyInt()); + reset(mWifiController); + + // Verify Sta Interface Down will still disable wifi + mSelfRecovery.trigger(SelfRecovery.REASON_STA_IFACE_DOWN); + verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_DISABLE_WIFI)); reset(mWifiController); // now TRAVEL FORWARDS IN TIME and ensure that more restarts can occur when(mClock.getElapsedSinceBootMillis()) .thenReturn(SelfRecovery.MAX_RESTARTS_TIME_WINDOW_MILLIS + 1); mSelfRecovery.trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RESTART_WIFI), anyInt()); + verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), anyInt()); reset(mWifiController); when(mClock.getElapsedSinceBootMillis()) .thenReturn(SelfRecovery.MAX_RESTARTS_TIME_WINDOW_MILLIS + 1); mSelfRecovery.trigger(SelfRecovery.REASON_WIFINATIVE_FAILURE); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RESTART_WIFI), anyInt()); + verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), anyInt()); reset(mWifiController); } @@ -136,7 +156,25 @@ public class SelfRecoveryTest { for (int i = 0; i < SelfRecovery.MAX_RESTARTS_IN_TIME_WINDOW * 2; i++) { // Verify L.R.Watchdog can still restart things (It has it's own complex limiter) mSelfRecovery.trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG); - verify(mWifiController).sendMessage(eq(WifiController.CMD_RESTART_WIFI), anyInt()); + verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), + anyInt()); + reset(mWifiController); + } + } + + /** + * Verifies that invocations of {@link SelfRecovery#trigger(int)} for + * REASON_STA_IFACE_DOWN are NOT limited to + * {@link SelfRecovery#MAX_RESTARTS_IN_TIME_WINDOW} in a + * {@link SelfRecovery#MAX_RESTARTS_TIME_WINDOW_MILLIS} millisecond time window. + */ + @Test + public void testTimeWindowLimiting_staIfaceDown_noEffect() { + for (int i = 0; i < SelfRecovery.MAX_RESTARTS_IN_TIME_WINDOW * 2; i++) { + mSelfRecovery.trigger(SelfRecovery.REASON_STA_IFACE_DOWN); + verify(mWifiController).sendMessage(eq(WifiController.CMD_RECOVERY_DISABLE_WIFI)); + verify(mWifiController, never()) + .sendMessage(eq(WifiController.CMD_RECOVERY_RESTART_WIFI), anyInt()); reset(mWifiController); } } diff --git a/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java index 50be8e44b..279fb670b 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java @@ -19,7 +19,8 @@ package com.android.server.wifi; import static com.android.server.wifi.WifiController.CMD_AP_STOPPED; import static com.android.server.wifi.WifiController.CMD_EMERGENCY_CALL_STATE_CHANGED; import static com.android.server.wifi.WifiController.CMD_EMERGENCY_MODE_CHANGED; -import static com.android.server.wifi.WifiController.CMD_RESTART_WIFI; +import static com.android.server.wifi.WifiController.CMD_RECOVERY_DISABLE_WIFI; +import static com.android.server.wifi.WifiController.CMD_RECOVERY_RESTART_WIFI; import static com.android.server.wifi.WifiController.CMD_SCAN_ALWAYS_MODE_CHANGED; import static com.android.server.wifi.WifiController.CMD_SET_AP; import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED; @@ -402,7 +403,8 @@ public class WifiControllerTest { @Test public void testRestartWifiStackInStaEnabledStateTriggersBugReport() throws Exception { enableWifi(); - mWifiController.sendMessage(CMD_RESTART_WIFI, SelfRecovery.REASON_WIFINATIVE_FAILURE); + mWifiController.sendMessage(CMD_RECOVERY_RESTART_WIFI, + SelfRecovery.REASON_WIFINATIVE_FAILURE); mLooper.dispatchAll(); verify(mWifiStateMachine).takeBugReport(anyString(), anyString()); } @@ -410,12 +412,37 @@ public class WifiControllerTest { @Test public void testRestartWifiWatchdogDoesNotTriggerBugReport() throws Exception { enableWifi(); - mWifiController.sendMessage(CMD_RESTART_WIFI, SelfRecovery.REASON_LAST_RESORT_WATCHDOG); + mWifiController.sendMessage(CMD_RECOVERY_RESTART_WIFI, + SelfRecovery.REASON_LAST_RESORT_WATCHDOG); mLooper.dispatchAll(); verify(mWifiStateMachine, never()).takeBugReport(anyString(), anyString()); } /** + * When in sta mode, CMD_RECOVERY_DISABLE_WIFI messages should trigger wifi to disable. + */ + @Test + public void testRecoveryDisabledTurnsWifiOff() throws Exception { + enableWifi(); + reset(mWifiStateMachine); + mWifiController.sendMessage(CMD_RECOVERY_DISABLE_WIFI); + mLooper.dispatchAll(); + verify(mWifiStateMachine).setOperationalMode(WifiStateMachine.DISABLED_MODE); + } + + /** + * When wifi is disabled, CMD_RECOVERY_DISABLE_WIFI should not trigger a state change. + */ + @Test + public void testRecoveryDisabledWhenWifiAlreadyOff() throws Exception { + reset(mWifiStateMachine, mWifiStateMachinePrime); + assertEquals("StaDisabledWithScanState", getCurrentState().getName()); + mWifiController.sendMessage(CMD_RECOVERY_DISABLE_WIFI); + mLooper.dispatchAll(); + verifyZeroInteractions(mWifiStateMachine, mWifiStateMachinePrime); + } + + /** * The command to trigger a WiFi reset should not trigger any action by WifiController if we * are not in STA mode. * WiFi is not in connect mode, so any calls to reset the wifi stack due to connection failures @@ -439,7 +466,7 @@ public class WifiControllerTest { reset(mWifiStateMachine); assertEquals("ApStaDisabledState", getCurrentState().getName()); - mWifiController.sendMessage(CMD_RESTART_WIFI); + mWifiController.sendMessage(CMD_RECOVERY_RESTART_WIFI); mLooper.dispatchAll(); verifyZeroInteractions(mWifiStateMachine); } @@ -457,7 +484,7 @@ public class WifiControllerTest { public void testRestartWifiStackInStaDisabledWithScanState() throws Exception { reset(mWifiStateMachine); assertEquals("StaDisabledWithScanState", getCurrentState().getName()); - mWifiController.sendMessage(CMD_RESTART_WIFI); + mWifiController.sendMessage(CMD_RECOVERY_RESTART_WIFI); mLooper.dispatchAll(); verifyZeroInteractions(mWifiStateMachine); } @@ -478,7 +505,7 @@ public class WifiControllerTest { reset(mWifiStateMachine); assertEquals("DeviceActiveState", getCurrentState().getName()); - mWifiController.sendMessage(CMD_RESTART_WIFI); + mWifiController.sendMessage(CMD_RECOVERY_RESTART_WIFI); mLooper.dispatchAll(); InOrder inOrder = inOrder(mWifiStateMachine); inOrder.verify(mWifiStateMachine).setOperationalMode(WifiStateMachine.CONNECT_MODE); @@ -503,7 +530,7 @@ public class WifiControllerTest { assertInEcm(true); reset(mWifiStateMachine); - mWifiController.sendMessage(CMD_RESTART_WIFI); + mWifiController.sendMessage(CMD_RECOVERY_RESTART_WIFI); mLooper.dispatchAll(); assertInEcm(true); verifyZeroInteractions(mWifiStateMachine); @@ -524,7 +551,7 @@ public class WifiControllerTest { assertEquals("ApEnabledState", getCurrentState().getName()); reset(mWifiStateMachine); - mWifiController.sendMessage(CMD_RESTART_WIFI); + mWifiController.sendMessage(CMD_RECOVERY_RESTART_WIFI); mLooper.dispatchAll(); verifyZeroInteractions(mWifiStateMachine); verify(mWifiStateMachinePrime, never()).disableWifi(); diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java index 2768af6db..c90bde3bb 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java @@ -638,7 +638,7 @@ public class WifiStateMachineTest { // But if someone tells us to enter connect mode, we start up supplicant mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE); mLooper.dispatchAll(); - assertEquals("SupplicantStartingState", getCurrentState().getName()); + assertEquals("DisconnectedState", getCurrentState().getName()); } /** @@ -853,7 +853,7 @@ public class WifiStateMachineTest { } /** - * Helper method to move through SupplicantStarting and SupplicantStarted states. + * Helper method to move through startup states. */ private void startSupplicantAndDispatchMessages() throws Exception { mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE); @@ -862,25 +862,11 @@ public class WifiStateMachineTest { // this will be removed when interface management is dynamic verify(mWifiNative, atLeastOnce()).teardownAllInterfaces(); - assertEquals("SupplicantStartingState", getCurrentState().getName()); - - when(mWifiNative.setDeviceName(eq(WIFI_IFACE_NAME), anyString())).thenReturn(true); - when(mWifiNative.setManufacturer(eq(WIFI_IFACE_NAME), anyString())).thenReturn(true); - when(mWifiNative.setModelName(eq(WIFI_IFACE_NAME), anyString())).thenReturn(true); - when(mWifiNative.setModelNumber(eq(WIFI_IFACE_NAME), anyString())).thenReturn(true); - when(mWifiNative.setSerialNumber(eq(WIFI_IFACE_NAME), anyString())).thenReturn(true); - when(mWifiNative.setConfigMethods(eq(WIFI_IFACE_NAME), anyString())).thenReturn(true); - when(mWifiNative.setDeviceType(eq(WIFI_IFACE_NAME), anyString())).thenReturn(true); - when(mWifiNative.setSerialNumber(eq(WIFI_IFACE_NAME), anyString())).thenReturn(true); - when(mWifiNative.setScanningMacOui(eq(WIFI_IFACE_NAME), any(byte[].class))) - .thenReturn(true); - - mWsm.sendMessage(WifiMonitor.SUP_CONNECTION_EVENT); - mLooper.dispatchAll(); - verify(mWifiNative, atLeastOnce()) .setupInterfaceForClientMode(eq(false), mInterfaceCallbackCaptor.capture()); verify(mWifiLastResortWatchdog, atLeastOnce()).clearAllFailureCounts(); + + assertEquals("DisconnectedState", getCurrentState().getName()); } private void addNetworkAndVerifySuccess(boolean isHidden) throws Exception { @@ -1834,18 +1820,127 @@ public class WifiStateMachineTest { } /** - * Test verifying that interface onDown callbacks are not currently hooked up. + * Test verifying that interface onDown callback triggers SelfRecovery when Supplicant has + * already reported the driver is not active. */ @Test - public void testInterfaceOnDownDoesNotTriggerClientModeShutdown() throws Exception { - connect(); + public void testInterfaceOnDownInClientModeTriggersSelfRecovery() throws Exception { + // Trigger initialize to capture the death handler registration. + loadComponentsInStaMode(); + + // make sure we mark the iface up + mInterfaceCallbackCaptor.getValue().onUp(WIFI_IFACE_NAME); + + // make sure supplicant has been reported as inactive + when(mWifiNative.isInterfaceUp(eq(WIFI_IFACE_NAME))).thenReturn(true); + mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, + new StateChangeResult(0, WifiSsid.createFromAsciiEncoded(""), null, + SupplicantState.INTERFACE_DISABLED)); + mLooper.dispatchAll(); // trigger onDown for the client interface mInterfaceCallbackCaptor.getValue().onDown(WIFI_IFACE_NAME); mLooper.dispatchAll(); - // since this is not handled yet, should not trigger a disconnect - assertEquals("ConnectedState", getCurrentState().getName()); + // WSM should trigger self recovery, but not disconnect until externally triggered + verify(mSelfRecovery).trigger(eq(SelfRecovery.REASON_STA_IFACE_DOWN)); + } + + /** + * Test verifying that interface onDown callback does not trigger SelfRecovery when + * Supplicant reports that the driver is active. + */ + @Test + public void testInterfaceOnDownInClientModeDoesNotTriggerSelfRecoveryIfDriverActive() + throws Exception { + // Trigger initialize to capture the death handler registration. + loadComponentsInStaMode(); + + // make sure we mark the iface up + mInterfaceCallbackCaptor.getValue().onUp(WIFI_IFACE_NAME); + mLooper.dispatchAll(); + + // make sure supplicant has been reported as active + mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, + new StateChangeResult(0, WifiSsid.createFromAsciiEncoded(""), null, + SupplicantState.DISCONNECTED)); + mLooper.dispatchAll(); + + // trigger onDown for the client interface + mInterfaceCallbackCaptor.getValue().onDown(WIFI_IFACE_NAME); + mLooper.dispatchAll(); + + // WSM should trigger self recovery, but not disconnect until externally triggered + verify(mSelfRecovery, never()).trigger(eq(SelfRecovery.REASON_STA_IFACE_DOWN)); + } + + /** + * Test verifying that Supplicant update for inactive driver does not trigger SelfRecovery + * when the interface is reported down. + */ + @Test + public void testSupplicantUpdateDriverInactiveInClientModeTriggersSelfRecovery() + throws Exception { + // Trigger initialize to capture the death handler registration. + loadComponentsInStaMode(); + + when(mWifiNative.isInterfaceUp(eq(WIFI_IFACE_NAME))).thenReturn(false); + + mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, + new StateChangeResult(0, WifiSsid.createFromAsciiEncoded(""), null, + SupplicantState.INTERFACE_DISABLED)); + mLooper.dispatchAll(); + + // WSM should trigger self recovery, but not disconnect until externally triggered + verify(mSelfRecovery, never()).trigger(eq(SelfRecovery.REASON_STA_IFACE_DOWN)); + } + + /** + * Test verifying that interface Supplicant update for inactive driver does not trigger + * SelfRecovery when WifiNative reports the interface is up. + */ + @Test + public void testSupplicantUpdateDriverInactiveIfaceUpClientModeDoesNotTriggerSelfRecovery() + throws Exception { + // Trigger initialize to capture the death handler registration. + loadComponentsInStaMode(); + + when(mWifiNative.isInterfaceUp(eq(WIFI_IFACE_NAME))).thenReturn(true); + + // make sure supplicant has been reported as inactive + mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0, + new StateChangeResult(0, WifiSsid.createFromAsciiEncoded(""), null, + SupplicantState.INTERFACE_DISABLED)); + mLooper.dispatchAll(); + + // WSM should trigger self recovery, but not disconnect until externally triggered + verify(mSelfRecovery, never()).trigger(eq(SelfRecovery.REASON_STA_IFACE_DOWN)); + } + + /** + * Test verifying that interface onDown callback does not trigger SelfRecovery when + * MacRandomization is enabled. + */ + @Test + public void testInterfaceOnDownInClientModeDoesNotTriggerSelfRecoveryWithMacRand() + throws Exception { + when(mFrameworkFacade.getIntegerSetting(mContext, + Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, 0)).thenReturn(1); + mContentObserver.onChange(false); + + // Trigger initialize to capture the death handler registration. + loadComponentsInStaMode(); + + // make sure we mark the iface up + mInterfaceCallbackCaptor.getValue().onUp(WIFI_IFACE_NAME); + mLooper.dispatchAll(); + + // trigger onDown for the client interface + mInterfaceCallbackCaptor.getValue().onDown(WIFI_IFACE_NAME); + mLooper.dispatchAll(); + + // WSM should trigger self recovery, but not disconnect until externally triggered + verify(mSelfRecovery, never()).trigger(eq(SelfRecovery.REASON_STA_IFACE_DOWN)); } /** |