Do not destroy MBB secondary when captive portal is detected

Captive portal is always detected with failed internet validation. The
current logic is to disconnect the primary and have MakeBeforeBreak
manager upgrade the secondary STA with capport to primary automatically.
But because captive portal detection and no internet basically happen
back to back, sometimes a race happens causing the secondary STA to
disconnect before it could switch to primary.

Therefore, we need to avoid disconnecting the capport secondary due to
no internet, so that it can get correctly transitioned to primary STA.

Bug: 205093620
Test: atest com.android.server.wifi
Change-Id: Ia9cf7ec76b54d95baf483bbff331d8ed3bdc04f4
diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java
index d03c156..4fcf070 100644
--- a/service/java/com/android/server/wifi/ClientModeImpl.java
+++ b/service/java/com/android/server/wifi/ClientModeImpl.java
@@ -270,6 +270,7 @@
     private WifiNative.ConnectionCapabilities mLastConnectionCapabilities;
     private int mPowerSaveDisableRequests = 0; // mask based on @PowerSaveClientType
     private boolean mIsUserSelected = false;
+    private boolean mCurrentConnectionDetectedCaptivePortal;
 
     private String getTag() {
         return TAG + "[" + (mInterfaceName == null ? "unknown" : mInterfaceName) + "]";
@@ -2791,6 +2792,7 @@
         registerDisconnected();
         mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
         mLastSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        mCurrentConnectionDetectedCaptivePortal = false;
         mLastSimBasedConnectionCarrierName = null;
         checkAbnormalDisconnectionAndTakeBugReport();
         mWifiScoreCard.resetConnectionState(mInterfaceName);
@@ -3738,6 +3740,7 @@
                         loge("CMD_START_CONNECT and no config, bail out...");
                         break;
                     }
+                    mCurrentConnectionDetectedCaptivePortal = false;
                     mTargetNetworkId = netId;
                     // Update scorecard while there is still state from existing connection
                     mLastScanRssi = mWifiConfigManager.findScanRssi(netId,
@@ -4306,6 +4309,7 @@
                         + ", redirectUri=" + redirectUri);
                 mWifiConfigManager.noteCaptivePortalDetected(mWifiInfo.getNetworkId());
                 mCmiMonitor.onCaptivePortalDetected(mClientModeManager);
+                mCurrentConnectionDetectedCaptivePortal = true;
             }
         }
 
@@ -5830,7 +5834,8 @@
                                 mWifiScoreCard.noteValidationFailure(mWifiInfo);
                             }
                         }
-                        if (mClientModeManager.getRole() == ROLE_CLIENT_SECONDARY_TRANSIENT) {
+                        if (mClientModeManager.getRole() == ROLE_CLIENT_SECONDARY_TRANSIENT
+                                && !mCurrentConnectionDetectedCaptivePortal) {
                             Log.d(getTag(), "Internet validation failed during MBB,"
                                     + " disconnecting ClientModeManager=" + mClientModeManager);
                             mWifiMetrics.logStaEvent(
diff --git a/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java
index 4f57791..5da39bc 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java
@@ -4294,7 +4294,7 @@
         // Make Before Break CMM
         when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_TRANSIENT);
 
-        // internet validation failure
+        // internet validation failure without detecting captive portal
         mWifiNetworkAgentCallbackCaptor.getValue().onValidationStatus(
                 NetworkAgent.VALIDATION_STATUS_NOT_VALID, null /* captivePortalUr; */);
         mLooper.dispatchAll();
@@ -4304,6 +4304,25 @@
     }
 
     @Test
+    public void mbb_internetValidationError_captivePortalNoDisconnect() throws Exception {
+        connect();
+        verify(mWifiInjector).makeWifiNetworkAgent(any(), any(), any(), any(),
+                mWifiNetworkAgentCallbackCaptor.capture());
+
+        // Make Before Break CMM
+        when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_TRANSIENT);
+
+        // mock internet validation failure with captive portal detected
+        when(mMockUri.toString()).thenReturn("TEST_URI");
+        mWifiNetworkAgentCallbackCaptor.getValue().onValidationStatus(
+                NetworkAgent.VALIDATION_STATUS_NOT_VALID, mMockUri);
+        mLooper.dispatchAll();
+
+        // expect no disconnection
+        verify(mWifiNative, never()).disconnect(WIFI_IFACE_NAME);
+    }
+
+    @Test
     public void captivePortalDetected_notifiesCmiMonitor() throws Exception {
         connect();
         verify(mWifiInjector).makeWifiNetworkAgent(any(), any(), any(), any(),