Merge "Fix quickly toggling airplane mode on then off could leave Wifi disabled" into rvc-dev
diff --git a/service/java/com/android/server/wifi/ActiveModeWarden.java b/service/java/com/android/server/wifi/ActiveModeWarden.java
index e0a0a3d..cf54e32 100644
--- a/service/java/com/android/server/wifi/ActiveModeWarden.java
+++ b/service/java/com/android/server/wifi/ActiveModeWarden.java
@@ -850,6 +850,9 @@
         }
 
         class EnabledState extends BaseState {
+
+            private boolean mIsDisablingDueToAirplaneMode;
+
             @Override
             public void enter() {
                 log("EnabledState.enter()");
@@ -857,6 +860,7 @@
                 if (!hasAnyModeManager()) {
                     Log.e(TAG, "Entered EnabledState, but no active mode managers");
                 }
+                mIsDisablingDueToAirplaneMode = false;
             }
 
             @Override
@@ -894,11 +898,15 @@
                     case CMD_AIRPLANE_TOGGLED:
                         // airplane mode toggled on is handled in the default state
                         if (mSettingsStore.isAirplaneModeOn()) {
+                            mIsDisablingDueToAirplaneMode = true;
                             return NOT_HANDLED;
                         } else {
-                            if (hasAnyModeManagerStopping()) {
-                                // previous airplane mode toggle on is being processed, defer the
+                            if (mIsDisablingDueToAirplaneMode) {
+                                // Previous airplane mode toggle on is being processed, defer the
                                 // message toggle off until previous processing is completed.
+                                // Once previous airplane mode toggle is complete, we should
+                                // transition to DisabledState. There, we will process the deferred
+                                // airplane mode toggle message to disable airplane mode.
                                 deferMessage(msg);
                             } else {
                                 // when airplane mode is toggled off, but wifi is on, we can keep it
diff --git a/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java b/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java
index ceaf76d..8294bdb 100644
--- a/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java
@@ -2240,6 +2240,43 @@
     }
 
     @Test
+    public void airplaneModeToggleOffIsDeferredWhileProcessingToggleOnWithOneModeManager2()
+            throws Exception {
+        enterClientModeActiveState();
+        assertInEnabledState();
+
+        // APM toggle on
+        assertWifiShutDown(() -> {
+            when(mSettingsStore.isAirplaneModeOn()).thenReturn(true);
+            mActiveModeWarden.airplaneModeToggled();
+            mLooper.dispatchAll();
+        });
+
+
+        // APM toggle off before the stop is complete.
+        assertInEnabledState();
+        when(mClientModeManager.isStopping()).thenReturn(true);
+        when(mSettingsStore.isAirplaneModeOn()).thenReturn(false);
+        mActiveModeWarden.airplaneModeToggled();
+        // This test is identical to
+        // airplaneModeToggleOffIsDeferredWhileProcessingToggleOnWithOneModeManager, except the
+        // dispatchAll() here is removed. There could be a race between airplaneModeToggled and
+        // mClientListener.onStopped(). See b/160105640#comment5.
+
+        mClientListener.onStopped();
+        mLooper.dispatchAll();
+
+        verify(mClientModeManager, times(2)).start();
+        verify(mClientModeManager, times(2)).setRole(ROLE_CLIENT_PRIMARY);
+
+        mClientListener.onStarted();
+        mLooper.dispatchAll();
+
+        // We should be back to enabled state.
+        assertInEnabledState();
+    }
+
+    @Test
     public void airplaneModeToggleOffIsDeferredWhileProcessingToggleOnWithTwoModeManager()
             throws Exception {
         enterClientModeActiveState();