Refactor validation before data switch

refactor the validation before data switch flow to be subId generic
instead of specific to opportunistic subId.

Bug: 244064524
Test: existing cts+atest, manual data during phone call
Merged-In: I0cbfaa195d42140426bea533fe83f1d73ac806e2
Change-Id: I0cbfaa195d42140426bea533fe83f1d73ac806e2
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index e7b2c69..bad461a 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -3636,7 +3636,7 @@
                 return SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
             }
 
-            return phoneSwitcher.getOpportunisticDataSubscriptionId();
+            return phoneSwitcher.getAutoSelectedDataSubId();
         } finally {
             Binder.restoreCallingIdentity(token);
         }
diff --git a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
index cd81b0c..ee8d157 100644
--- a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
@@ -31,6 +31,7 @@
 import static java.util.Arrays.copyOf;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -195,6 +196,7 @@
     @VisibleForTesting
     protected final CellularNetworkValidator mValidator;
     private int mPendingSwitchSubId = INVALID_SUBSCRIPTION_ID;
+    private int mLastAutoSelectedSwitchReason = -1;
     private boolean mPendingSwitchNeedValidation;
     @VisibleForTesting
     public final CellularNetworkValidator.ValidationCallback mValidationCallback =
@@ -227,9 +229,12 @@
     // Internet data if mOpptDataSubId is not set.
     protected int mPrimaryDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
-    // mOpptDataSubId must be an active subscription. If it's set, it overrides mPrimaryDataSubId
-    // to be used for Internet data.
-    private int mOpptDataSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+    // The automatically suggested preferred data subId (by e.g. CBRS or auto data switch), a
+    // candidate for preferred data subId, which is eventually presided by
+    // updatePreferredDataPhoneId().
+    // If CBRS/auto switch feature selects the primary data subId as the preferred data subId,
+    // its value will be DEFAULT_SUBSCRIPTION_ID.
+    private int mAutoSelectedDataSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
 
     // The phone ID that has an active voice call. If set, and its mobile data setting is on,
     // it will become the mPreferredDataPhoneId.
@@ -1148,7 +1153,7 @@
 
                 if (VDBG) {
                     log("mPrimaryDataSubId = " + mPrimaryDataSubId);
-                    log("mOpptDataSubId = " + mOpptDataSubId);
+                    log("mAutoSelectedDataSubId = " + mAutoSelectedDataSubId);
                     for (int i = 0; i < mActiveModemCount; i++) {
                         log(" phone[" + i + "] using sub[" + mPhoneSubscriptions[i] + "]");
                     }
@@ -1310,8 +1315,8 @@
     }
 
     private int getSubIdForDefaultNetworkRequests() {
-        if (mSubscriptionController.isActiveSubId(mOpptDataSubId)) {
-            return mOpptDataSubId;
+        if (mSubscriptionController.isActiveSubId(mAutoSelectedDataSubId)) {
+            return mAutoSelectedDataSubId;
         } else {
             return mPrimaryDataSubId;
         }
@@ -1451,45 +1456,65 @@
      */
     private void setOpportunisticDataSubscription(int subId, boolean needValidation,
             ISetOpportunisticDataCallback callback) {
-        if (!mSubscriptionController.isActiveSubId(subId)
-                && subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
-            log("Can't switch data to inactive subId " + subId);
+        validate(subId, needValidation,
+                DataSwitch.Reason.DATA_SWITCH_REASON_CBRS, callback);
+    }
+
+    /**
+     * Try setup a new internet connection on the subId that's pending validation. If the validation
+     * succeeds, this subId will be evaluated for being the preferred data subId; If fails, nothing
+     * happens.
+     * Callback will be updated with the validation result.
+     *
+     * @param subId Sub Id that's pending switch, awaiting validation.
+     * @param needValidation {@code false} if switch to the subId even if validation fails.
+     * @param switchReason The switch reason for this validation
+     * @param callback Optional - specific for external opportunistic sub validation request.
+     */
+    private void validate(int subId, boolean needValidation, int switchReason,
+            @Nullable ISetOpportunisticDataCallback callback) {
+        int subIdToValidate = (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)
+                ? mPrimaryDataSubId : subId;
+        if (!mSubscriptionController.isActiveSubId(subIdToValidate)) {
+            log("Can't switch data to inactive subId " + subIdToValidate);
+            if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+                // the default data sub is not selected yet, store the intent of switching to
+                // default subId once it becomes available.
+                mAutoSelectedDataSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+            }
             sendSetOpptCallbackHelper(callback, SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
             return;
         }
 
-        // Remove EVENT_NETWORK_VALIDATION_DONE. Don't handle validation result of previously subId
-        // if queued.
-        removeMessages(EVENT_NETWORK_VALIDATION_DONE);
-        removeMessages(EVENT_NETWORK_AVAILABLE);
-
-        int subIdToValidate = (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)
-                ? mPrimaryDataSubId : subId;
-
-        mPendingSwitchSubId = INVALID_SUBSCRIPTION_ID;
-
         if (mValidator.isValidating()) {
             mValidator.stopValidation();
             sendSetOpptCallbackHelper(mSetOpptSubCallback, SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED);
             mSetOpptSubCallback = null;
         }
 
-        if (subId == mOpptDataSubId) {
+        // Remove EVENT_NETWORK_VALIDATION_DONE. Don't handle validation result of previous subId
+        // if queued.
+        removeMessages(EVENT_NETWORK_VALIDATION_DONE);
+        removeMessages(EVENT_NETWORK_AVAILABLE);
+
+        mPendingSwitchSubId = INVALID_SUBSCRIPTION_ID;
+
+        if (subIdToValidate == mPreferredDataSubId.get()) {
             sendSetOpptCallbackHelper(callback, SET_OPPORTUNISTIC_SUB_SUCCESS);
             return;
         }
 
-        logDataSwitchEvent(subId == DEFAULT_SUBSCRIPTION_ID ? mPrimaryDataSubId : subId,
+        mLastAutoSelectedSwitchReason = switchReason;
+        logDataSwitchEvent(subIdToValidate,
                 TelephonyEvent.EventState.EVENT_STATE_START,
-                DataSwitch.Reason.DATA_SWITCH_REASON_CBRS);
-        registerDefaultNetworkChangeCallback(
-                subId == DEFAULT_SUBSCRIPTION_ID ? mPrimaryDataSubId : subId,
-                DataSwitch.Reason.DATA_SWITCH_REASON_CBRS);
+                switchReason);
+        registerDefaultNetworkChangeCallback(subIdToValidate,
+                switchReason);
 
         // If validation feature is not supported, set it directly. Otherwise,
         // start validation on the subscription first.
         if (!mValidator.isValidationFeatureSupported()) {
-            setOpportunisticSubscriptionInternal(subId);
+            setAutoSelectedDataSubIdInternal(subIdToValidate);
             sendSetOpptCallbackHelper(callback, SET_OPPORTUNISTIC_SUB_SUCCESS);
             return;
         }
@@ -1531,12 +1556,15 @@
     }
 
     /**
-     * Set opportunistic data subscription.
+     * Evaluate whether the specified sub Id can be set to be the preferred data sub Id.
+     *
+     * @param subId The subId that we tried to validate: could possibly be unvalidated if validation
+     * feature is not supported.
      */
-    private void setOpportunisticSubscriptionInternal(int subId) {
-        if (mOpptDataSubId != subId) {
-            mOpptDataSubId = subId;
-            onEvaluate(REQUESTS_UNCHANGED, "oppt data subId changed");
+    private void setAutoSelectedDataSubIdInternal(int subId) {
+        if (mAutoSelectedDataSubId != subId) {
+            mAutoSelectedDataSubId = subId;
+            onEvaluate(REQUESTS_UNCHANGED, switchReasonToString(mLastAutoSelectedSwitchReason));
         }
     }
 
@@ -1549,11 +1577,10 @@
         } else if (!confirm) {
             resultForCallBack = SET_OPPORTUNISTIC_SUB_VALIDATION_FAILED;
         } else {
-            if (mSubscriptionController.isOpportunistic(subId)) {
-                setOpportunisticSubscriptionInternal(subId);
+            if (subId == mPrimaryDataSubId) {
+                setAutoSelectedDataSubIdInternal(DEFAULT_SUBSCRIPTION_ID);
             } else {
-                // Switching data back to primary subscription.
-                setOpportunisticSubscriptionInternal(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
+                setAutoSelectedDataSubIdInternal(subId);
             }
             resultForCallBack = SET_OPPORTUNISTIC_SUB_SUCCESS;
         }
@@ -1623,10 +1650,6 @@
                 ? HAL_COMMAND_PREFERRED_DATA : HAL_COMMAND_ALLOW_DATA;
     }
 
-    public int getOpportunisticDataSubscriptionId() {
-        return mOpptDataSubId;
-    }
-
     public int getPreferredDataPhoneId() {
         return mPreferredDataPhoneId;
     }
@@ -1708,6 +1731,13 @@
         return mPreferredDataSubId.get();
     }
 
+    /**
+     * @return The auto selected data subscription id.
+     */
+    public int getAutoSelectedDataSubId() {
+        return mAutoSelectedDataSubId;
+    }
+
     // TODO (b/148396668): add an internal callback method to monitor phone capability change,
     // and hook this call to that callback.
     private void onPhoneCapabilityChanged(PhoneCapability capability) {
@@ -1732,7 +1762,7 @@
         pw.println("DefaultDataPhoneId=" + mSubscriptionController.getPhoneId(
                 mSubscriptionController.getDefaultDataSubId()));
         pw.println("mPrimaryDataSubId=" + mPrimaryDataSubId);
-        pw.println("mOpptDataSubId=" + mOpptDataSubId);
+        pw.println("mAutoSelectedDataSubId=" + mAutoSelectedDataSubId);
         pw.println("mIsRegisteredForImsRadioTechChange=" + mIsRegisteredForImsRadioTechChange);
         pw.println("mPendingSwitchNeedValidation=" + mPendingSwitchNeedValidation);
         pw.println("mMaxDataAttachModemCount=" + mMaxDataAttachModemCount);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
index 487f7e3..6465705 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
@@ -477,6 +477,65 @@
         assertFalse(mDataAllowed[1]);
     }
 
+    /**
+     * TestSetPreferredData in the event of different priorities.
+     * The following events can set preferred data subId with priority in the order of
+     * 1. Emergency call
+     * 2. Voice call (when data during call feature is enabled).
+     * 3. CBRS requests
+     */
+    @Test
+    @SmallTest
+    public void testSetPreferredDataCasePriority() throws Exception {
+        initialize();
+        setAllPhonesInactive();
+
+        // Phone 0 has sub 1, phone 1 has sub 2.
+        // Sub 1 is default data sub.
+        // Both are active subscriptions are active sub, as they are in both active slots.
+        setSlotIndexToSubId(0, 1);
+        setSlotIndexToSubId(1, 2);
+        setDefaultDataSubId(1);
+
+        // Notify phoneSwitcher about default data sub and default network request.
+        NetworkRequest internetRequest = addInternetNetworkRequest(null, 50);
+        // Phone 0 (sub 1) should be activated as it has default data sub.
+        assertEquals(1, mPhoneSwitcher.getActiveDataSubId());
+        assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+                new TelephonyNetworkRequest(internetRequest, mPhone), 0));
+        assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+                new TelephonyNetworkRequest(internetRequest, mPhone), 1));
+
+        // Set sub 2 as preferred sub should make phone 1 activated and phone 0 deactivated.
+        mPhoneSwitcher.trySetOpportunisticDataSubscription(2, false, null);
+        processAllMessages();
+        mPhoneSwitcher.mValidationCallback.onNetworkAvailable(null, 2);
+        // A higher priority event occurring E.g. Phone1 has active IMS call on LTE.
+        doReturn(mImsPhone).when(mPhone).getImsPhone();
+        doReturn(true).when(mDataSettingsManager).isDataEnabled(ApnSetting.TYPE_DEFAULT);
+        mockImsRegTech(1, REGISTRATION_TECH_LTE);
+        notifyPhoneAsInCall(mPhone);
+
+        // switch shouldn't occur due to the higher priority event
+        assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+                new TelephonyNetworkRequest(internetRequest, mPhone), 0));
+        assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+                new TelephonyNetworkRequest(internetRequest, mPhone), 1));
+        assertEquals(1, mPhoneSwitcher.getActiveDataSubId());
+        assertEquals(2, mPhoneSwitcher.getAutoSelectedDataSubId());
+
+        // The higher priority event ends, time to switch to auto selected subId.
+        notifyPhoneAsInactive(mPhone);
+
+        assertEquals(2, mPhoneSwitcher.getActiveDataSubId());
+        assertEquals(2, mPhoneSwitcher.getAutoSelectedDataSubId());
+        assertTrue(mPhoneSwitcher.shouldApplyNetworkRequest(
+                new TelephonyNetworkRequest(internetRequest, mPhone), 1));
+        assertFalse(mPhoneSwitcher.shouldApplyNetworkRequest(
+                new TelephonyNetworkRequest(internetRequest, mPhone), 0));
+
+    }
+
     @Test
     @SmallTest
     public void testSetPreferredDataModemCommand() throws Exception {
@@ -1084,7 +1143,23 @@
         setSlotIndexToSubId(1, 2);
         setDefaultDataSubId(1);
 
+        // Switch to primary before a primary is selected/inactive.
+        setDefaultDataSubId(-1);
+        mPhoneSwitcher.trySetOpportunisticDataSubscription(
+                SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false, mSetOpptDataCallback1);
+        processAllMessages();
+
+        assertEquals(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+                mPhoneSwitcher.getAutoSelectedDataSubId());
+        verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
+
+        // once the primary is selected, it becomes the active sub.
+        setDefaultDataSubId(2);
+        assertEquals(2, mPhoneSwitcher.getActiveDataSubId());
+
+        setDefaultDataSubId(1);
         // Validating on sub 10 which is inactive.
+        clearInvocations(mSetOpptDataCallback1);
         mPhoneSwitcher.trySetOpportunisticDataSubscription(10, true, mSetOpptDataCallback1);
         processAllMessages();
         verify(mSetOpptDataCallback1).onComplete(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);