Merge "Gracefully tear down networks when SIM is disabled" into main
diff --git a/flags/data.aconfig b/flags/data.aconfig
index a993d51..7b7bff9 100644
--- a/flags/data.aconfig
+++ b/flags/data.aconfig
@@ -121,3 +121,14 @@
   }
 }
 
+# OWNER=jackyu TARGET=25Q1
+flag {
+  name: "sim_disabled_graceful_tear_down"
+  namespace: "telephony"
+  description: "Gracefully tear down the networks when SIM is disabled."
+  bug: ""
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
diff --git a/src/java/com/android/internal/telephony/data/DataEvaluation.java b/src/java/com/android/internal/telephony/data/DataEvaluation.java
index f5eae91..40c0081 100644
--- a/src/java/com/android/internal/telephony/data/DataEvaluation.java
+++ b/src/java/com/android/internal/telephony/data/DataEvaluation.java
@@ -225,6 +225,8 @@
         SIM_LOADED(true),
         /** SIM is removed. */
         SIM_REMOVAL(true),
+        /** SIM is disabled. */
+        SIM_DISABLED(true),
         /** Data profiles changed. */
         DATA_PROFILES_CHANGED(true),
         /** When service state changes.(For now only considering data RAT and data registration). */
diff --git a/src/java/com/android/internal/telephony/data/DataNetworkController.java b/src/java/com/android/internal/telephony/data/DataNetworkController.java
index 89153ab..5d31283 100644
--- a/src/java/com/android/internal/telephony/data/DataNetworkController.java
+++ b/src/java/com/android/internal/telephony/data/DataNetworkController.java
@@ -3543,15 +3543,6 @@
     }
 
     /**
-     * Called when SIM is absent.
-     */
-    private void onSimAbsent() {
-        log("onSimAbsent");
-        sendMessage(obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS,
-                DataEvaluationReason.SIM_REMOVAL));
-    }
-
-    /**
      * Called when SIM state changes.
      *
      * @param simState SIM state. (Note this is mixed with card state and application state.)
@@ -3559,13 +3550,22 @@
     private void onSimStateChanged(@SimState int simState) {
         log("onSimStateChanged: state=" + TelephonyManager.simStateToString(simState));
         if (mSimState != simState) {
-            mSimState = simState;
             if (simState == TelephonyManager.SIM_STATE_ABSENT) {
-                onSimAbsent();
+                log("onSimStateChanged: SIM absent.");
+                sendMessage(obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS,
+                        DataEvaluationReason.SIM_REMOVAL));
+            } else if (simState == TelephonyManager.SIM_STATE_NOT_READY
+                    && mSimState == TelephonyManager.SIM_STATE_LOADED) {
+                if (mFeatureFlags.simDisabledGracefulTearDown()) {
+                    log("onSimStateChanged: SIM disabled.");
+                    sendMessage(obtainMessage(EVENT_REEVALUATE_EXISTING_DATA_NETWORKS,
+                            DataEvaluationReason.SIM_DISABLED));
+                }
             } else if (simState == TelephonyManager.SIM_STATE_LOADED) {
                 sendMessage(obtainMessage(EVENT_REEVALUATE_UNSATISFIED_NETWORK_REQUESTS,
                         DataEvaluationReason.SIM_LOADED));
             }
+            mSimState = simState;
             mDataNetworkControllerCallbacks.forEach(callback -> callback.invokeFromExecutor(
                     () -> callback.onSimStateChanged(mSimState)));
         }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
index 5dce5c2..0b66459 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
@@ -888,8 +888,11 @@
         doReturn(PhoneConstants.State.IDLE).when(mCT).getState();
         doReturn(new SubscriptionInfoInternal.Builder().setId(1).build())
                 .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
+
         doReturn(true).when(mFeatureFlags).carrierEnabledSatelliteFlag();
         doReturn(true).when(mFeatureFlags).satelliteInternet();
+        doReturn(true).when(mFeatureFlags).simDisabledGracefulTearDown();
+
         when(mContext.getPackageManager()).thenReturn(mMockPackageManager);
         doReturn(true).when(mMockPackageManager).hasSystemFeature(anyString());
 
@@ -4196,7 +4199,7 @@
     }
 
     @Test
-    public void testImsGracefulTearDown() throws Exception {
+    public void testImsGracefulTearDownSimRemoval() throws Exception {
         setImsRegistered(true);
         setRcsRegistered(true);
 
@@ -4242,6 +4245,52 @@
     }
 
     @Test
+    public void testImsGracefulTearDownSimDisabled() throws Exception {
+        setImsRegistered(true);
+        setRcsRegistered(true);
+
+        NetworkCapabilities netCaps = new NetworkCapabilities();
+        netCaps.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
+        netCaps.maybeMarkCapabilitiesRestricted();
+        netCaps.setRequestorPackageName(FAKE_MMTEL_PACKAGE);
+
+        NetworkRequest nativeNetworkRequest = new NetworkRequest(netCaps,
+                ConnectivityManager.TYPE_MOBILE, ++mNetworkRequestId, NetworkRequest.Type.REQUEST);
+        TelephonyNetworkRequest networkRequest = new TelephonyNetworkRequest(
+                nativeNetworkRequest, mPhone, mFeatureFlags);
+
+        mDataNetworkControllerUT.addNetworkRequest(networkRequest);
+
+        processAllMessages();
+        Mockito.clearInvocations(mPhone);
+
+        // SIM disabled
+        mDataNetworkControllerUT.obtainMessage(9/*EVENT_SIM_STATE_CHANGED*/,
+                TelephonyManager.SIM_STATE_NOT_READY, 0).sendToTarget();
+        processAllMessages();
+
+        // Make sure data network enters disconnecting state
+        ArgumentCaptor<PreciseDataConnectionState> pdcsCaptor =
+                ArgumentCaptor.forClass(PreciseDataConnectionState.class);
+        verify(mPhone).notifyDataConnection(pdcsCaptor.capture());
+        PreciseDataConnectionState pdcs = pdcsCaptor.getValue();
+        assertThat(pdcs.getState()).isEqualTo(TelephonyManager.DATA_DISCONNECTING);
+
+        // IMS de-registered. Now data network is safe to be torn down.
+        Mockito.clearInvocations(mPhone);
+        setImsRegistered(false);
+        setRcsRegistered(false);
+        processAllMessages();
+
+        // All data should be disconnected.
+        verifyAllDataDisconnected();
+        verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
+        verify(mPhone).notifyDataConnection(pdcsCaptor.capture());
+        pdcs = pdcsCaptor.getValue();
+        assertThat(pdcs.getState()).isEqualTo(TelephonyManager.DATA_DISCONNECTED);
+    }
+
+    @Test
     public void testNoGracefulTearDownForEmergencyDataNetwork() throws Exception {
         setImsRegistered(true);