Merge "Flagging requirement for notifyDataActivityChanged based on Slot." into main
diff --git a/flags/Android.bp b/flags/Android.bp
index 99c99c5..3c0deee 100644
--- a/flags/Android.bp
+++ b/flags/Android.bp
@@ -23,6 +23,7 @@
     package: "com.android.internal.telephony.flags",
     srcs: [
       "data.aconfig",
+      "domainselection.aconfig",
       "ims.aconfig",
       "messaging.aconfig",
       "misc.aconfig",
diff --git a/flags/data.aconfig b/flags/data.aconfig
index 52cd486..e65f8e4 100644
--- a/flags/data.aconfig
+++ b/flags/data.aconfig
@@ -1,6 +1,20 @@
 package: "com.android.internal.telephony.flags"
 
 flag {
+  name: "use_alarm_callback"
+  namespace: "telephony"
+  description: "Use alarm callback instead of broadcast."
+  bug: "311476875"
+}
+
+flag {
+  name: "refine_preferred_data_profile_selection"
+  namespace: "telephony"
+  description: "Upon internet network connect, refine selection of preferred data profile."
+  bug: "311476883"
+}
+
+flag {
   name: "unthrottle_check_transport"
   namespace: "telephony"
   description: "Check transport when unthrottle."
diff --git a/flags/domainselection.aconfig b/flags/domainselection.aconfig
new file mode 100644
index 0000000..2e1dfc8
--- /dev/null
+++ b/flags/domainselection.aconfig
@@ -0,0 +1,29 @@
+package: "com.android.internal.telephony.flags"
+
+flag {
+    name: "ap_domain_selection_enabled"
+    namespace: "telephony"
+    description: "This flag controls AP domain selection feature."
+    bug:"258112541"
+}
+
+flag {
+    name: "use_aosp_domain_selection_service"
+    namespace: "telephony"
+    description: "This flag controls AOSP's domain selection service supported."
+    bug:"258112541"
+}
+
+flag {
+    name: "use_oem_domain_selection_service"
+    namespace: "telephony"
+    description: "This flag controls OEMs' domain selection service supported."
+    bug:"258112541"
+}
+
+flag {
+    name: "domain_selection_metrics_enabled"
+    namespace: "telephony"
+    description: "This flag controls domain selection metrics."
+    bug:"258112541"
+}
diff --git a/flags/iwlan.aconfig b/flags/iwlan.aconfig
index efd43e4..0dc9f8d 100644
--- a/flags/iwlan.aconfig
+++ b/flags/iwlan.aconfig
@@ -6,3 +6,9 @@
     description: "Add AEAD algorithms AES-GCM-8, AES-GCM-12 and AES-GCM-16 to IWLAN"
     bug:"306119890"
 }
+flag {
+    name: "enable_multiple_sa_proposals"
+    namespace: "telephony"
+    description: "Add multiple proposals of cipher suites in IKE SA and Child SA"
+    bug:"287296642"
+}
diff --git a/flags/misc.aconfig b/flags/misc.aconfig
index deda579..159d462 100644
--- a/flags/misc.aconfig
+++ b/flags/misc.aconfig
@@ -56,3 +56,10 @@
   description: "Load default data subid on create in PhoneGlobals."
   bug: "310591561"
 }
+
+flag {
+  name: "enable_telephony_analytics"
+  namespace: "telephony"
+  description: "Enable Telephony Analytics information of Service State , Sms and Call scenarios"
+  bug: "309896524"
+}
diff --git a/proto/src/persist_atoms.proto b/proto/src/persist_atoms.proto
index 537f824..6d1008b 100644
--- a/proto/src/persist_atoms.proto
+++ b/proto/src/persist_atoms.proto
@@ -277,6 +277,9 @@
     optional int32 fold_state = 34;
     optional int64 rat_switch_count_after_connected = 35;
     optional bool handover_in_progress = 36;
+    optional bool is_iwlan_cross_sim_at_start = 37;
+    optional bool is_iwlan_cross_sim_at_end = 38;
+    optional bool is_iwlan_cross_sim_at_connected = 39;
 
     // Internal use only
     optional int64 setup_begin_millis = 10001;
@@ -365,6 +368,7 @@
     repeated int32 handover_failure_causes = 20;
     repeated int32 handover_failure_rat = 21;
     optional bool is_non_dds = 22;
+    optional bool is_iwlan_cross_sim = 23;
 }
 
 message CellularServiceState {
@@ -382,6 +386,7 @@
     optional int32 fold_state = 12;
     optional bool override_voice_service = 13;
     optional bool isDataEnabled = 14;
+    optional bool is_iwlan_cross_sim = 15;
 
     // Internal use only
     optional int64 last_used_millis = 10001;
@@ -408,6 +413,7 @@
     optional int32 extra_code = 6;
     optional string extra_message = 7;
     optional int32 count = 8;
+    optional bool is_iwlan_cross_sim = 9;
 
     // Internal use only
     optional int64 last_used_millis = 10001;
@@ -429,6 +435,7 @@
     optional int64 ut_available_millis = 12;
     optional int64 registering_millis = 13;
     optional int64 unregistered_millis = 14;
+    optional bool is_iwlan_cross_sim = 15;
 
     // Internal use only
     optional int64 last_used_millis = 10001;
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index 4b977ff..6b40040 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -2912,4 +2912,19 @@
      * @param result Callback message to receive the result.
      */
     default void isCellularIdentifierTransparencyEnabled(Message result) {}
-}
\ No newline at end of file
+
+    /**
+     * Enables or disables security algorithm update reports.
+     *
+     * @param enable {@code true} to enable, {@code false} to disable.
+     * @param result Callback message to receive the result.
+     */
+    default void setSecurityAlgorithmsUpdatedEnabled(boolean enable, Message result) {}
+
+    /**
+     * Check whether security algorithm update reports are enabled.
+     *
+     * @param result Callback message to receive the result.
+     */
+    default void isSecurityAlgorithmsUpdatedEnabled(Message result) {}
+}
diff --git a/src/java/com/android/internal/telephony/MultiSimSettingController.java b/src/java/com/android/internal/telephony/MultiSimSettingController.java
index d6b0930..d07e731 100644
--- a/src/java/com/android/internal/telephony/MultiSimSettingController.java
+++ b/src/java/com/android/internal/telephony/MultiSimSettingController.java
@@ -541,7 +541,7 @@
         boolean setDefaultData = true;
         List<SubscriptionInfo> activeSubList = mSubscriptionManagerService
                 .getActiveSubscriptionInfoList(mContext.getOpPackageName(),
-                        mContext.getAttributionTag());
+                        mContext.getAttributionTag(), true/*isForAllProfile*/);
         for (SubscriptionInfo activeInfo : activeSubList) {
             if (!(groupUuid.equals(activeInfo.getGroupUuid()))) {
                 // Do not set refSubId as defaultDataSubId if there are other active
@@ -588,7 +588,7 @@
 
         List<SubscriptionInfo> activeSubInfos = mSubscriptionManagerService
                 .getActiveSubscriptionInfoList(mContext.getOpPackageName(),
-                        mContext.getAttributionTag());
+                        mContext.getAttributionTag(), true/*isForAllProfile*/);
 
         if (ArrayUtils.isEmpty(activeSubInfos)) {
             mPrimarySubList.clear();
diff --git a/src/java/com/android/internal/telephony/NetworkIndication.java b/src/java/com/android/internal/telephony/NetworkIndication.java
index dc3fbf1..3f345e0 100644
--- a/src/java/com/android/internal/telephony/NetworkIndication.java
+++ b/src/java/com/android/internal/telephony/NetworkIndication.java
@@ -30,6 +30,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESTRICTED_STATE_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SECURITY_ALGORITHMS_UPDATED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SIGNAL_STRENGTH;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SUPP_SVC_NOTIFICATION;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_VOICE_RADIO_TECH_CHANGED;
@@ -438,6 +439,20 @@
         // TODO (b/276752426) notify registrants of identifier disclosure
     }
 
+    /**
+     * Security algorithm update events
+     * @param indicationType Type of radio indication
+     * @param securityAlgorithmUpdate details of what changed
+     */
+    public void securityAlgorithmsUpdated(int indicationType,
+            android.hardware.radio.network.SecurityAlgorithmUpdate securityAlgorithmUpdate) {
+        mRil.processIndication(HAL_SERVICE_NETWORK, indicationType);
+
+        if (mRil.isLogOrTrace()) {
+            mRil.unsljLogRet(RIL_UNSOL_SECURITY_ALGORITHMS_UPDATED, securityAlgorithmUpdate);
+        }
+    }
+
     @Override
     public String getInterfaceHash() {
         return IRadioNetworkIndication.HASH;
diff --git a/src/java/com/android/internal/telephony/NetworkResponse.java b/src/java/com/android/internal/telephony/NetworkResponse.java
index e4e2b1b..eb2cd16 100644
--- a/src/java/com/android/internal/telephony/NetworkResponse.java
+++ b/src/java/com/android/internal/telephony/NetworkResponse.java
@@ -528,6 +528,29 @@
         }
     }
 
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setSecurityAlgorithmsUpdatedEnabledResponse(RadioResponseInfo responseInfo) {
+        RadioResponse.responseVoid(HAL_SERVICE_NETWORK, mRil, responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error.
+     * @param isEnabled Indicates whether security algorithm updates from the modem are enabled.
+     */
+    public void isSecurityAlgorithmsUpdatedEnabledResponse(RadioResponseInfo responseInfo,
+                                                        boolean isEnabled) {
+        RILRequest rr = mRil.processResponse(HAL_SERVICE_NETWORK, responseInfo);
+
+        if (rr != null) {
+            if (responseInfo.error == RadioError.NONE) {
+                RadioResponse.sendMessageResponse(rr.mResult, isEnabled);
+            }
+            mRil.processResponseDone(rr, responseInfo, isEnabled);
+        }
+    }
+
     @Override
     public String getInterfaceHash() {
         return IRadioNetworkResponse.HASH;
diff --git a/src/java/com/android/internal/telephony/NetworkTypeController.java b/src/java/com/android/internal/telephony/NetworkTypeController.java
index d1c8359..e6fb84e 100644
--- a/src/java/com/android/internal/telephony/NetworkTypeController.java
+++ b/src/java/com/android/internal/telephony/NetworkTypeController.java
@@ -594,9 +594,6 @@
                             log("Reset timers since physical channel config indications are off.");
                         }
                         resetAllTimers();
-                        mRatchetedNrBands.clear();
-                        mRatchetedNrBandwidths = 0;
-                        mLastAnchorNrCellId = PhysicalChannelConfig.PHYSICAL_CELL_ID_UNKNOWN;
                     }
                     transitionToCurrentState();
                     break;
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 72d0622..b991b8e 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -660,7 +660,7 @@
             mCi.registerForSrvccStateChanged(this, EVENT_SRVCC_STATE_CHANGED, null);
         }
         //Initialize Telephony Analytics
-        if (isTelephonyAnalyticsEnabled()) {
+        if (mFeatureFlags.enableTelephonyAnalytics()) {
             mTelephonyAnalytics = new TelephonyAnalytics(this);
         }
     }
@@ -4780,11 +4780,6 @@
         return mTelephonyAnalytics;
     }
 
-    public boolean isTelephonyAnalyticsEnabled() {
-        return mContext.getResources().getBoolean(
-                com.android.internal.R.bool.telephony_analytics_switch);
-    }
-
     /** @hide */
     public CarrierPrivilegesTracker getCarrierPrivilegesTracker() {
         return null;
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 56cb808..7ed7dcb 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -5134,6 +5134,61 @@
         });
     }
 
+   /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setSecurityAlgorithmsUpdatedEnabled(boolean enable, Message result) {
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest(
+                "setSecurityAlgorithmsUpdatedEnabled",
+                networkProxy,
+                result,
+                RADIO_HAL_VERSION_2_2)) {
+            return;
+        }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_SET_SECURITY_ALGORITHMS_UPDATED_ENABLED, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
+                    + " enable=" + enable);
+        }
+
+        radioServiceInvokeHelper(HAL_SERVICE_NETWORK, rr, "setSecurityAlgorithmsUpdatedEnabled",
+                () -> {
+                    networkProxy.setSecurityAlgorithmsUpdatedEnabled(rr.mSerial, enable);
+            });
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void isSecurityAlgorithmsUpdatedEnabled(Message result) {
+        RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
+        if (!canMakeRequest(
+                "isSecurityAlgorithmsUpdatedEnabled",
+                networkProxy,
+                result,
+                RADIO_HAL_VERSION_2_2)) {
+            return;
+        }
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_IS_SECURITY_ALGORITHMS_UPDATED_ENABLED, result,
+                mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest));
+        }
+
+        radioServiceInvokeHelper(
+                HAL_SERVICE_NETWORK, rr, "isSecurityAlgorithmsUpdatedEnabled", () -> {
+                networkProxy.isSecurityAlgorithmsUpdatedEnabled(rr.mSerial);
+            });
+    }
+
     //***** Private Methods
     /**
      * This is a helper function to be called when an indication callback is called for any radio
@@ -6015,4 +6070,4 @@
                 return "UNKNOWN:" + service;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/java/com/android/internal/telephony/RadioNetworkProxy.java b/src/java/com/android/internal/telephony/RadioNetworkProxy.java
index fc308ca..4acc71a 100644
--- a/src/java/com/android/internal/telephony/RadioNetworkProxy.java
+++ b/src/java/com/android/internal/telephony/RadioNetworkProxy.java
@@ -949,4 +949,34 @@
         }
         // Only supported on AIDL.
     }
+
+    /**
+     * Checks security algorithm update reports are enabled.
+     *
+     * @param serial Serial number of the request.
+     * @throws RemoteException
+     */
+    public void isSecurityAlgorithmsUpdatedEnabled(int serial) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mNetworkProxy.isSecurityAlgorithmsUpdatedEnabled(serial);
+        }
+        // Only supported on AIDL.
+    }
+
+    /**
+     * Enables or disables security algorithm update reports.
+     *
+     * @param serial Serial number of request.
+     * @param enable Indicates whether to enable or disable security algorithm update reports.
+     * @throws RemoteException
+     */
+    public void setSecurityAlgorithmsUpdatedEnabled(int serial,
+            boolean enable) throws RemoteException {
+        if (isEmpty()) return;
+        if (isAidl()) {
+            mNetworkProxy.setSecurityAlgorithmsUpdatedEnabled(serial, enable);
+        }
+        // Only supported on AIDL.
+    }
 }
diff --git a/src/java/com/android/internal/telephony/SignalStrengthController.java b/src/java/com/android/internal/telephony/SignalStrengthController.java
index 383ffcd..387c501 100644
--- a/src/java/com/android/internal/telephony/SignalStrengthController.java
+++ b/src/java/com/android/internal/telephony/SignalStrengthController.java
@@ -353,7 +353,7 @@
 
         List<SubscriptionInfo> subInfoList = SubscriptionManagerService.getInstance()
                 .getActiveSubscriptionInfoList(mPhone.getContext().getOpPackageName(),
-                        mPhone.getContext().getAttributionTag());
+                        mPhone.getContext().getAttributionTag(), true/*isForAllProfile*/);
 
         if (!ArrayUtils.isEmpty(subInfoList)) {
             for (SubscriptionInfo info : subInfoList) {
diff --git a/src/java/com/android/internal/telephony/data/DataNetwork.java b/src/java/com/android/internal/telephony/data/DataNetwork.java
index 48a18ee..bba0f19 100644
--- a/src/java/com/android/internal/telephony/data/DataNetwork.java
+++ b/src/java/com/android/internal/telephony/data/DataNetwork.java
@@ -406,6 +406,10 @@
     /** Data network tear down due to preferred data switched to another phone. */
     public static final int TEAR_DOWN_REASON_PREFERRED_DATA_SWITCHED = 30;
 
+    //********************************************************************************************//
+    // WHENEVER ADD A NEW TEAR DOWN REASON, PLEASE UPDATE DataDeactivateReasonEnum in enums.proto //
+    //********************************************************************************************//
+
     @IntDef(prefix = {"BANDWIDTH_SOURCE_"},
             value = {
                     BANDWIDTH_SOURCE_UNKNOWN,
diff --git a/src/java/com/android/internal/telephony/data/DataProfileManager.java b/src/java/com/android/internal/telephony/data/DataProfileManager.java
index df1fc92..b4055a3 100644
--- a/src/java/com/android/internal/telephony/data/DataProfileManager.java
+++ b/src/java/com/android/internal/telephony/data/DataProfileManager.java
@@ -419,18 +419,36 @@
      * @param internetNetworks The connected internet data networks.
      */
     private void onInternetDataNetworkConnected(@NonNull Set<DataNetwork> internetNetworks) {
-        // Most of the cases there should be only one.
-        // but in case there are multiple, find the default internet network, and choose the
-        // one which has longest life cycle.
-        DataProfile defaultProfile = internetNetworks.stream()
-                .filter(network -> mPreferredDataProfile == null
-                        // Find the one most resembles the current preferred profile,
-                        // avoiding e.g. DUN default network.
-                        || canPreferredDataProfileSatisfy(
-                        network.getAttachedNetworkRequestList()))
-                .map(DataNetwork::getDataProfile)
-                .min(Comparator.comparingLong(DataProfile::getLastSetupTimestamp))
-                .orElse(null);
+        DataProfile defaultProfile = null;
+        if (mFeatureFlags.refinePreferredDataProfileSelection()) {
+            // Most of the cases there should be only one.
+            // but in case there are multiple, find the default internet network, and choose the
+            // one which has longest life cycle.
+            defaultProfile = internetNetworks.stream()
+                    .filter(network -> mPreferredDataProfile == null
+                            // Find the one most resembles the current preferred profile,
+                            // avoiding e.g. DUN default network.
+                            || canPreferredDataProfileSatisfy(
+                            network.getAttachedNetworkRequestList()))
+                    .map(DataNetwork::getDataProfile)
+                    .min(Comparator.comparingLong(DataProfile::getLastSetupTimestamp))
+                    .orElse(null);
+        } else {
+            if (internetNetworks.size() == 1) {
+                // Most of the cases there should be only one.
+                defaultProfile = internetNetworks.stream().findFirst().get().getDataProfile();
+            } else if (internetNetworks.size() > 1) {
+                // but in case there are multiple, find the default internet network, and choose the
+                // one which has longest life cycle.
+                defaultProfile = internetNetworks.stream()
+                        .filter(network -> mPreferredDataProfile == null
+                                || canPreferredDataProfileSatisfy(
+                                network.getAttachedNetworkRequestList()))
+                        .map(DataNetwork::getDataProfile)
+                        .min(Comparator.comparingLong(DataProfile::getLastSetupTimestamp))
+                        .orElse(null);
+            }
+        }
 
         // Update a working internet data profile as a future candidate for preferred data profile
         // after APNs are reset to default
diff --git a/src/java/com/android/internal/telephony/data/DataRetryManager.java b/src/java/com/android/internal/telephony/data/DataRetryManager.java
index 754400e..5933463 100644
--- a/src/java/com/android/internal/telephony/data/DataRetryManager.java
+++ b/src/java/com/android/internal/telephony/data/DataRetryManager.java
@@ -22,6 +22,11 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.net.NetworkCapabilities;
 import android.os.AsyncResult;
 import android.os.Handler;
@@ -74,6 +79,11 @@
 public class DataRetryManager extends Handler {
     private static final boolean VDBG = false;
 
+    /** Intent of Alarm Manager for long retry timer. */
+    private static final String ACTION_RETRY = "com.android.internal.telephony.data.ACTION_RETRY";
+    /** The extra key for the hashcode of the retry entry for Alarm Manager. */
+    private static final String ACTION_RETRY_EXTRA_HASHCODE = "extra_retry_hashcode";
+
     /** Event for data setup retry. */
     private static final int EVENT_DATA_SETUP_RETRY = 3;
 
@@ -1014,6 +1024,22 @@
         mRil.registerForOn(this, EVENT_RADIO_ON, null);
         mRil.registerForModemReset(this, EVENT_MODEM_RESET, null);
 
+        if (!mFlags.useAlarmCallback()) {
+            // Register intent of alarm manager for long retry timer
+            IntentFilter intentFilter = new IntentFilter();
+            intentFilter.addAction(ACTION_RETRY);
+            mPhone.getContext().registerReceiver(new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    if (ACTION_RETRY.equals(intent.getAction())) {
+                        DataRetryManager.this.onAlarmIntentRetry(
+                                intent.getIntExtra(ACTION_RETRY_EXTRA_HASHCODE,
+                                        -1 /*Bad hashcode*/));
+                    }
+                }
+            }, intentFilter);
+        }
+
         if (mDataConfigManager.shouldResetDataThrottlingWhenTacChanges()) {
             mPhone.getServiceStateTracker().registerForAreaCodeChanged(this, EVENT_TAC_CHANGED,
                     null);
@@ -1449,19 +1475,48 @@
                             ? EVENT_DATA_SETUP_RETRY : EVENT_DATA_HANDOVER_RETRY, dataRetryEntry),
                     dataRetryEntry.retryDelayMillis);
         } else {
-            // No need to wake up the device, the retry can wait util next time the device wake up
-            // to save power.
-            mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME,
-                    dataRetryEntry.retryElapsedTime,
-                    "dataRetryHash-" + dataRetryEntry.hashCode() /*debug tag*/,
-                    Runnable::run,
-                    null /*worksource*/,
-                    () -> {
-                        logl("onAlarm retry " + dataRetryEntry);
-                        sendMessage(obtainMessage(dataRetryEntry instanceof DataSetupRetryEntry
-                                ? EVENT_DATA_SETUP_RETRY : EVENT_DATA_HANDOVER_RETRY,
-                                dataRetryEntry));
-                    });
+            if (mFlags.useAlarmCallback()) {
+                // No need to wake up the device, the retry can wait util next time the device wake
+                // up to save power.
+                mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME,
+                        dataRetryEntry.retryElapsedTime,
+                        "dataRetryHash-" + dataRetryEntry.hashCode() /*debug tag*/,
+                        Runnable::run,
+                        null /*worksource*/,
+                        () -> {
+                            logl("onAlarm retry " + dataRetryEntry);
+                            sendMessage(obtainMessage(dataRetryEntry instanceof DataSetupRetryEntry
+                                            ? EVENT_DATA_SETUP_RETRY : EVENT_DATA_HANDOVER_RETRY,
+                                    dataRetryEntry));
+                        });
+            } else {
+                Intent intent = new Intent(ACTION_RETRY);
+                intent.putExtra(ACTION_RETRY_EXTRA_HASHCODE, dataRetryEntry.hashCode());
+                // No need to wake up the device, the retry can wait util next time the device wake
+                // up  to save power.
+                mAlarmManager.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME,
+                        dataRetryEntry.retryElapsedTime,
+                        PendingIntent.getBroadcast(mPhone.getContext(),
+                                dataRetryEntry.hashCode()/*Unique identifier of the retry attempt*/,
+                                intent,
+                                PendingIntent.FLAG_IMMUTABLE));
+            }
+        }
+    }
+
+    /**
+     * Called when it's time to retry scheduled by Alarm Manager.
+     * @param retryHashcode The hashcode is the unique identifier of which retry entry to retry.
+     */
+    private void onAlarmIntentRetry(int retryHashcode) {
+        DataRetryEntry dataRetryEntry = mDataRetryEntries.stream()
+                .filter(entry -> entry.hashCode() == retryHashcode)
+                .findAny()
+                .orElse(null);
+        logl("onAlarmIntentRetry: found " + dataRetryEntry + " with hashcode " + retryHashcode);
+        if (dataRetryEntry != null) {
+            sendMessage(obtainMessage(dataRetryEntry instanceof DataSetupRetryEntry
+                    ? EVENT_DATA_SETUP_RETRY : EVENT_DATA_HANDOVER_RETRY, dataRetryEntry));
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
index 6773dca..3023501 100644
--- a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
@@ -39,14 +39,18 @@
 import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.sysprop.TelephonyProperties;
+import android.telephony.AccessNetworkConstants;
 import android.telephony.Annotation.DisconnectCauses;
 import android.telephony.CarrierConfigManager;
 import android.telephony.DisconnectCause;
 import android.telephony.EmergencyRegResult;
 import android.telephony.NetworkRegistrationInfo;
+import android.telephony.PreciseDataConnectionState;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
 import android.util.ArraySet;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -57,6 +61,7 @@
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.data.PhoneSwitcher;
+import com.android.internal.telephony.satellite.SatelliteController;
 import com.android.telephony.Rlog;
 
 import java.lang.annotation.Retention;
@@ -65,6 +70,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
 /**
@@ -83,6 +89,7 @@
     private static final boolean DEFAULT_EMERGENCY_CALLBACK_MODE_SUPPORTED = true;
     /** Default Emergency Callback Mode exit timeout value. */
     private static final long DEFAULT_ECM_EXIT_TIMEOUT_MS = 300000;
+    private static final int DEFAULT_EPDN_DISCONNECTION_TIMEOUT_MS = 500;
 
     /** The emergency types used when setting the emergency mode on modem. */
     @Retention(RetentionPolicy.SOURCE)
@@ -120,6 +127,9 @@
     private final Runnable mExitEcmRunnable = this::exitEmergencyCallbackMode;
     // Tracks emergency calls by callId that have reached {@link Call.State#ACTIVE}.
     private final Set<String> mActiveEmergencyCalls = new ArraySet<>();
+    private Phone mPhoneToExit;
+    private int mPdnDisconnectionTimeoutMs = DEFAULT_EPDN_DISCONNECTION_TIMEOUT_MS;
+    private final Object mLock = new Object();
     private Phone mPhone;
     // Tracks ongoing emergency callId to handle a second emergency call
     private String mOngoingCallId;
@@ -165,6 +175,29 @@
         }
     };
 
+    /**
+     * TelephonyCallback used to monitor whether ePDN on cellular network is disconnected or not.
+     */
+    private final class PreciseDataConnectionStateListener extends TelephonyCallback implements
+            TelephonyCallback.PreciseDataConnectionStateListener {
+        @Override
+        public void onPreciseDataConnectionStateChanged(
+                @NonNull PreciseDataConnectionState dataConnectionState) {
+            ApnSetting apnSetting = dataConnectionState.getApnSetting();
+            if ((apnSetting == null)
+                    || ((apnSetting.getApnTypeBitmask() | ApnSetting.TYPE_EMERGENCY) == 0)
+                    || (dataConnectionState.getTransportType()
+                            != AccessNetworkConstants.TRANSPORT_TYPE_WWAN)) {
+                return;
+            }
+            int state = dataConnectionState.getState();
+            Rlog.d(TAG, "onPreciseDataConnectionStateChanged ePDN state=" + state);
+            if (state == TelephonyManager.DATA_DISCONNECTED) exitEmergencyModeIfDelayed();
+        }
+    }
+
+    private PreciseDataConnectionStateListener mDataConnectionStateListener;
+
     /** PhoneFactory Dependencies for testing. */
     @VisibleForTesting
     public interface PhoneFactoryProxy {
@@ -188,6 +221,8 @@
     @VisibleForTesting
     public interface TelephonyManagerProxy {
         int getPhoneCount();
+        void registerTelephonyCallback(int subId, Executor executor, TelephonyCallback callback);
+        void unregisterTelephonyCallback(TelephonyCallback callback);
     }
 
     private final TelephonyManagerProxy mTelephonyManagerProxy;
@@ -195,7 +230,6 @@
     private static class TelephonyManagerProxyImpl implements TelephonyManagerProxy {
         private final TelephonyManager mTelephonyManager;
 
-
         TelephonyManagerProxyImpl(Context context) {
             mTelephonyManager = new TelephonyManager(context);
         }
@@ -204,6 +238,18 @@
         public int getPhoneCount() {
             return mTelephonyManager.getActiveModemCount();
         }
+
+        @Override
+        public void registerTelephonyCallback(int subId,
+                Executor executor, TelephonyCallback callback) {
+            TelephonyManager tm = mTelephonyManager.createForSubscriptionId(subId);
+            tm.registerTelephonyCallback(executor, callback);
+        }
+
+        @Override
+        public void unregisterTelephonyCallback(TelephonyCallback callback) {
+            mTelephonyManager.unregisterTelephonyCallback(callback);
+        }
     }
 
     /**
@@ -215,11 +261,15 @@
     }
 
     @VisibleForTesting
-    public static final int MSG_SET_EMERGENCY_MODE_DONE = 1;
+    public static final int MSG_SET_EMERGENCY_MODE = 1;
     @VisibleForTesting
-    public static final int MSG_EXIT_EMERGENCY_MODE_DONE = 2;
+    public static final int MSG_EXIT_EMERGENCY_MODE = 2;
     @VisibleForTesting
-    public static final int MSG_SET_EMERGENCY_CALLBACK_MODE_DONE = 3;
+    public static final int MSG_SET_EMERGENCY_MODE_DONE = 3;
+    @VisibleForTesting
+    public static final int MSG_EXIT_EMERGENCY_MODE_DONE = 4;
+    @VisibleForTesting
+    public static final int MSG_SET_EMERGENCY_CALLBACK_MODE_DONE = 5;
 
     private class MyHandler extends Handler {
 
@@ -263,7 +313,7 @@
                             if (mIsEmergencyCallStartedDuringEmergencySms) {
                                 Phone phone = mPhone;
                                 mPhone = null;
-                                exitEmergencyMode(mSmsPhone, emergencyType);
+                                exitEmergencyMode(mSmsPhone, emergencyType, false);
                                 // Restore call phone for further use.
                                 mPhone = phone;
 
@@ -322,6 +372,27 @@
                     }
                     break;
                 }
+                case MSG_EXIT_EMERGENCY_MODE: {
+                    Rlog.v(TAG, "MSG_EXIT_EMERGENCY_MODE");
+                    exitEmergencyModeIfDelayed();
+                    break;
+                }
+                case MSG_SET_EMERGENCY_MODE: {
+                    AsyncResult ar = (AsyncResult) msg.obj;
+                    Integer emergencyType = (Integer) ar.userObj;
+                    Rlog.v(TAG, "MSG_SET_EMERGENCY_MODE for "
+                            + emergencyTypeToString(emergencyType) + ", " + mEmergencyMode);
+                    // Should be reached here only when starting a new emergency service
+                    // while exiting emergency callback mode on the other slot.
+                    if (mEmergencyMode != MODE_EMERGENCY_WWAN) return;
+                    final Phone phone = (mPhone != null) ? mPhone : mSmsPhone;
+                    if (phone != null) {
+                        mWasEmergencyModeSetOnModem = true;
+                        phone.setEmergencyMode(MODE_EMERGENCY_WWAN,
+                                mHandler.obtainMessage(MSG_SET_EMERGENCY_MODE_DONE, emergencyType));
+                    }
+                    break;
+                }
                 default:
                     break;
             }
@@ -459,7 +530,7 @@
             // exit the emergency mode when receiving the result of setting the emergency mode and
             // the emergency mode for this call will be restarted after the exit complete.
             if (isInEmergencyMode() && !isEmergencyModeInProgress()) {
-                exitEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS);
+                exitEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS, false);
             }
 
             mPhone = phone;
@@ -511,7 +582,7 @@
                             MSG_SET_EMERGENCY_CALLBACK_MODE_DONE);
                 }
             } else {
-                exitEmergencyMode(mPhone, EMERGENCY_TYPE_CALL);
+                exitEmergencyMode(mPhone, EMERGENCY_TYPE_CALL, false);
                 clearEmergencyCallInfo();
             }
         }
@@ -576,8 +647,27 @@
             return;
         }
 
-        mWasEmergencyModeSetOnModem = true;
-        phone.setEmergencyMode(mode, m);
+        synchronized (mLock) {
+            unregisterForDataConnectionStateChanges();
+            if (mPhoneToExit != null) {
+                if (emergencyType != EMERGENCY_TYPE_CALL) {
+                    setIsInEmergencyCall(false);
+                }
+                mOnEcmExitCompleteRunnable = null;
+                if (mPhoneToExit != phone) {
+                    // Exit emergency mode on the other phone first,
+                    // then set emergency mode on the given phone.
+                    mPhoneToExit.exitEmergencyMode(
+                            mHandler.obtainMessage(MSG_SET_EMERGENCY_MODE,
+                            Integer.valueOf(emergencyType)));
+                    mPhoneToExit = null;
+                    return;
+                }
+                mPhoneToExit = null;
+            }
+            mWasEmergencyModeSetOnModem = true;
+            phone.setEmergencyMode(mode, m);
+        }
     }
 
     private void completeEmergencyMode(@EmergencyType int emergencyType) {
@@ -650,8 +740,10 @@
      *
      * @param phone the {@code Phone} to exit the emergency mode.
      * @param emergencyType the emergency type to identify an emergency call or SMS.
+     * @param waitForPdnDisconnect indicates whether it shall wait for the disconnection of ePDN.
      */
-    private void exitEmergencyMode(Phone phone, @EmergencyType int emergencyType) {
+    private void exitEmergencyMode(Phone phone, @EmergencyType int emergencyType,
+            boolean waitForPdnDisconnect) {
         Rlog.i(TAG, "exitEmergencyMode for " + emergencyTypeToString(emergencyType));
 
         if (emergencyType == EMERGENCY_TYPE_CALL) {
@@ -687,8 +779,23 @@
             return;
         }
 
-        mWasEmergencyModeSetOnModem = false;
-        phone.exitEmergencyMode(m);
+        synchronized (mLock) {
+            mWasEmergencyModeSetOnModem = false;
+            if (waitForPdnDisconnect) {
+                registerForDataConnectionStateChanges(phone);
+                mPhoneToExit = phone;
+                if (mPdnDisconnectionTimeoutMs > 0) {
+                    // To avoid waiting for the disconnection indefinitely.
+                    mHandler.sendEmptyMessageDelayed(MSG_EXIT_EMERGENCY_MODE,
+                            mPdnDisconnectionTimeoutMs);
+                }
+                return;
+            } else {
+                unregisterForDataConnectionStateChanges();
+                mPhoneToExit = null;
+            }
+            phone.exitEmergencyMode(m);
+        }
     }
 
     /** Returns last {@link EmergencyRegResult} as set by {@code setEmergencyMode()}. */
@@ -887,7 +994,9 @@
             gsmCdmaPhone.notifyEmergencyCallRegistrants(false);
 
             // Exit emergency mode on modem.
-            exitEmergencyMode(gsmCdmaPhone, EMERGENCY_TYPE_CALL);
+            // b/299866883: Wait for the disconnection of ePDN before calling exitEmergencyMode.
+            exitEmergencyMode(gsmCdmaPhone, EMERGENCY_TYPE_CALL,
+                    mEmergencyCallDomain == NetworkRegistrationInfo.DOMAIN_PS);
         }
 
         mEmergencyCallDomain = NetworkRegistrationInfo.DOMAIN_UNKNOWN;
@@ -1026,7 +1135,7 @@
                             MSG_SET_EMERGENCY_CALLBACK_MODE_DONE);
                 }
             } else {
-                exitEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS);
+                exitEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS, false);
             }
 
             clearEmergencySmsInfo();
@@ -1078,8 +1187,10 @@
             boolean isTestEmergencyNumber) {
         final boolean isAirplaneModeOn = isAirplaneModeOn(mContext);
         boolean needToTurnOnRadio = !isRadioOn() || isAirplaneModeOn;
+        final SatelliteController satelliteController = SatelliteController.getInstance();
+        boolean needToTurnOffSatellite = satelliteController.isSatelliteEnabled();
 
-        if (needToTurnOnRadio) {
+        if (needToTurnOnRadio || needToTurnOffSatellite) {
             Rlog.i(TAG, "turnOnRadioAndSwitchDds: phoneId=" + phone.getPhoneId() + " for "
                     + emergencyTypeToString(emergencyType));
             if (mRadioOnHelper == null) {
@@ -1090,9 +1201,15 @@
                 @Override
                 public void onComplete(RadioOnStateListener listener, boolean isRadioReady) {
                     if (!isRadioReady) {
-                        // Could not turn radio on
-                        Rlog.e(TAG, "Failed to turn on radio.");
-                        completeEmergencyMode(emergencyType, DisconnectCause.POWER_OFF);
+                        if (satelliteController.isSatelliteEnabled()) {
+                            // Could not turn satellite off
+                            Rlog.e(TAG, "Failed to turn off satellite modem.");
+                            completeEmergencyMode(emergencyType, DisconnectCause.SATELLITE_ENABLED);
+                        } else {
+                            // Could not turn radio on
+                            Rlog.e(TAG, "Failed to turn on radio.");
+                            completeEmergencyMode(emergencyType, DisconnectCause.POWER_OFF);
+                        }
                     } else {
                         switchDdsAndSetEmergencyMode(phone, emergencyType);
                     }
@@ -1104,7 +1221,8 @@
                     // should be able to make emergency calls at any time after the radio has been
                     // powered on and isn't in the UNAVAILABLE state, even if it is reporting the
                     // OUT_OF_SERVICE state.
-                    return phone.getServiceStateTracker().isRadioOn();
+                    return phone.getServiceStateTracker().isRadioOn()
+                            && !satelliteController.isSatelliteEnabled();
                 }
 
                 @Override
@@ -1334,4 +1452,49 @@
         Rlog.i(TAG, "updateNoSimEcbmSupported preference updated slotIndex=" + slotIndex
                 + ", supported=" + carrierConfig);
     }
+
+    /** For test purpose only */
+    @VisibleForTesting
+    public void setPdnDisconnectionTimeoutMs(int timeout) {
+        mPdnDisconnectionTimeoutMs = timeout;
+    }
+
+    private void exitEmergencyModeIfDelayed() {
+        synchronized (mLock) {
+            if (mPhoneToExit != null) {
+                unregisterForDataConnectionStateChanges();
+                mPhoneToExit.exitEmergencyMode(
+                        mHandler.obtainMessage(MSG_EXIT_EMERGENCY_MODE_DONE,
+                                Integer.valueOf(EMERGENCY_TYPE_CALL)));
+                mPhoneToExit = null;
+            }
+        }
+    }
+
+    /**
+     * Registers for changes to data connection state.
+     */
+    private void registerForDataConnectionStateChanges(Phone phone) {
+        if ((mDataConnectionStateListener != null) || (phone == null)) {
+            return;
+        }
+        Rlog.i(TAG, "registerForDataConnectionStateChanges");
+
+        mDataConnectionStateListener = new PreciseDataConnectionStateListener();
+        mTelephonyManagerProxy.registerTelephonyCallback(phone.getSubId(),
+                mHandler::post, mDataConnectionStateListener);
+    }
+
+    /**
+     * Unregisters for changes to data connection state.
+     */
+    private void unregisterForDataConnectionStateChanges() {
+        if (mDataConnectionStateListener == null) {
+            return;
+        }
+        Rlog.i(TAG, "unregisterForDataConnectionStateChanges");
+
+        mTelephonyManagerProxy.unregisterTelephonyCallback(mDataConnectionStateListener);
+        mDataConnectionStateListener = null;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index 1ee8447..4943ba3 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -2481,7 +2481,7 @@
             setServiceState(ServiceState.STATE_IN_SERVICE);
             getDefaultPhone().setImsRegistrationState(true);
             mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.CONNECTED, null);
-            mImsStats.onImsRegistered(imsRadioTech);
+            mImsStats.onImsRegistered(attributes);
             mImsNrSaModeHandler.onImsRegistered(
                     attributes.getRegistrationTechnology(), attributes.getFeatureTags());
             updateImsRegistrationInfo(REGISTRATION_STATE_REGISTERED,
diff --git a/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java b/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
index afb87dd..2b3ed82 100644
--- a/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
+++ b/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
@@ -18,7 +18,13 @@
 
 import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__IP_TYPE__APN_PROTOCOL_IPV4;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.SystemClock;
 import android.telephony.Annotation.ApnType;
 import android.telephony.Annotation.DataFailureCause;
@@ -55,6 +61,8 @@
     private long mStartTime;
     @Nullable private DataCallSession mDataCallSession;
 
+    private Network mSystemDefaultNetwork;
+    private boolean mIsSystemDefaultNetworkMobile;
     private final PersistAtomsStorage mAtomsStorage =
             PhoneFactory.getMetricsCollector().getAtomsStorage();
 
@@ -62,8 +70,47 @@
 
     public static final int SIZE_LIMIT_HANDOVER_FAILURES = 15;
 
+    final class DefaultNetworkCallback extends ConnectivityManager.NetworkCallback {
+        @Override
+        public void onAvailable(@NonNull Network network) {
+            mSystemDefaultNetwork = network;
+        }
+
+        @Override
+        public void onCapabilitiesChanged(@NonNull Network network,
+                                          @NonNull NetworkCapabilities nc) {
+            if (network == mSystemDefaultNetwork) {
+                mIsSystemDefaultNetworkMobile = nc.hasTransport(
+                        NetworkCapabilities.TRANSPORT_CELLULAR);
+            }
+        }
+
+        @Override
+        public void onLost(@NonNull Network network) {
+            mIsSystemDefaultNetworkMobile = false;
+            mSystemDefaultNetwork = null;
+        }
+    }
+
     public DataCallSessionStats(Phone phone) {
         mPhone = phone;
+        registerSystemDefaultNetworkCallback(phone);
+    }
+
+    private void registerSystemDefaultNetworkCallback(@NonNull Phone phone) {
+        ConnectivityManager connectivityManager = phone.getContext()
+                .getSystemService(ConnectivityManager.class);
+        if (connectivityManager != null) {
+            HandlerThread handlerThread = new HandlerThread(
+                    DataCallSessionStats.class.getSimpleName());
+            handlerThread.start();
+            Handler callbackHandler = new Handler(handlerThread.getLooper());
+            DefaultNetworkCallback mDefaultNetworkCallback = new DefaultNetworkCallback();
+            connectivityManager.registerSystemDefaultNetworkCallback(
+                    mDefaultNetworkCallback, callbackHandler);
+        } else {
+            loge("registerSystemDefaultNetworkCallback: ConnectivityManager is null!");
+        }
     }
 
     /** Creates a new ongoing atom when data call is set up. */
@@ -101,6 +148,9 @@
                     (currentRat == TelephonyManager.NETWORK_TYPE_IWLAN)
                             ? 0
                             : ServiceStateStats.getBand(mPhone);
+            // Limitation: Will not capture IKE mobility between Backup Calling <-> WiFi Calling.
+            mDataCallSession.isIwlanCrossSim = currentRat == TelephonyManager.NETWORK_TYPE_IWLAN
+                    && mIsSystemDefaultNetworkMobile;
         }
 
         // only set if apn hasn't been set during setup
@@ -199,6 +249,8 @@
             if (mDataCallSession.ratAtEnd != currentRat) {
                 mDataCallSession.ratSwitchCount++;
                 mDataCallSession.ratAtEnd = currentRat;
+                mDataCallSession.isIwlanCrossSim = currentRat == TelephonyManager.NETWORK_TYPE_IWLAN
+                        && mIsSystemDefaultNetworkMobile;
             }
             // band may have changed even if RAT was the same
             mDataCallSession.bandAtEnd =
@@ -288,6 +340,7 @@
         copy.handoverFailureRat = Arrays.copyOf(call.handoverFailureRat,
                 call.handoverFailureRat.length);
         copy.isNonDds = call.isNonDds;
+        copy.isIwlanCrossSim = call.isIwlanCrossSim;
         return copy;
     }
 
@@ -313,6 +366,7 @@
         proto.handoverFailureCauses = new int[0];
         proto.handoverFailureRat = new int[0];
         proto.isNonDds = false;
+        proto.isIwlanCrossSim = false;
         return proto;
     }
 
diff --git a/src/java/com/android/internal/telephony/metrics/ImsStats.java b/src/java/com/android/internal/telephony/metrics/ImsStats.java
index 2420602..04c9677 100644
--- a/src/java/com/android/internal/telephony/metrics/ImsStats.java
+++ b/src/java/com/android/internal/telephony/metrics/ImsStats.java
@@ -23,6 +23,7 @@
 import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT;
 import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO;
 import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM;
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
@@ -39,6 +40,7 @@
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
 import android.telephony.ims.ProvisioningManager;
 import android.telephony.ims.RegistrationManager.ImsRegistrationState;
 import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities;
@@ -231,6 +233,12 @@
         } else {
             ImsRegistrationStats stats = copyOfDimensionsOnly(mLastRegistrationStats);
 
+            if (stats.rat == TelephonyManager.NETWORK_TYPE_UNKNOWN) {
+                logw("conclude: discarding UNKNOWN RAT, duration=%d", duration);
+                mLastTimestamp = now;
+                return;
+            }
+
             switch (mLastRegistrationState) {
                 case REGISTRATION_STATE_REGISTERED:
                     stats.registeredMillis = duration;
@@ -285,6 +293,7 @@
             mLastRegistrationStats.rat = newRat;
             ratChanged = true;
         }
+        mLastRegistrationStats.isIwlanCrossSim = radioTech == REGISTRATION_TECH_CROSS_SIM;
 
         boolean voiceAvailableNow = capabilities.isCapable(CAPABILITY_TYPE_VOICE);
         boolean voiceAvailabilityChanged =
@@ -324,15 +333,18 @@
     }
 
     /** Updates the stats when IMS registration succeeds. */
-    public synchronized void onImsRegistered(@TransportType int imsRadioTech) {
+    public synchronized void onImsRegistered(ImsRegistrationAttributes attributes) {
         conclude();
 
-        mLastTransportType = imsRadioTech;
+        mLastTransportType = attributes.getTransportType();
         // NOTE: status can be unregistered (no registering phase)
         if (mLastRegistrationState == REGISTRATION_STATE_NOT_REGISTERED) {
             updateImsRegistrationStats();
         }
-        mLastRegistrationStats.rat = convertTransportTypeToNetworkType(imsRadioTech);
+        mLastRegistrationStats.rat =
+                convertTransportTypeToNetworkType(attributes.getTransportType());
+        mLastRegistrationStats.isIwlanCrossSim = attributes.getRegistrationTechnology()
+                == REGISTRATION_TECH_CROSS_SIM;
         mLastRegistrationState = REGISTRATION_STATE_REGISTERED;
     }
 
@@ -344,6 +356,8 @@
         ImsRegistrationTermination termination = new ImsRegistrationTermination();
         if (mLastRegistrationState != REGISTRATION_STATE_NOT_REGISTERED) {
             termination.carrierId = mLastRegistrationStats.carrierId;
+            termination.ratAtEnd = getRatAtEnd(mLastRegistrationStats.rat);
+            termination.isIwlanCrossSim = mLastRegistrationStats.isIwlanCrossSim;
         } else {
             // if the registration state is from unregistered to unregistered.
             termination.carrierId = mPhone.getDefaultPhone().getCarrierId();
@@ -359,16 +373,20 @@
 
         // Reset state to unregistered.
         mLastRegistrationState = REGISTRATION_STATE_NOT_REGISTERED;
-        mLastRegistrationStats.rat = TelephonyManager.NETWORK_TYPE_UNKNOWN;
         mLastAvailableFeatures = new MmTelCapabilities();
     }
 
     /** Updates the RAT when service state changes. */
     public synchronized void onServiceStateChanged(ServiceState state) {
-        if (mLastTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN
-                && mLastRegistrationState != REGISTRATION_STATE_NOT_REGISTERED) {
-            mLastRegistrationStats.rat =
-                    ServiceStateStats.getRat(state, NetworkRegistrationInfo.DOMAIN_PS);
+        conclude();
+
+        @NetworkType int newRat = state.getDataNetworkType();
+        MmTelCapabilities lastCapableFeatures = getLastCapableFeaturesForNetworkType(newRat);
+
+        if (lastCapableFeatures != null) {
+            mLastRegistrationStats.rat = newRat;
+        } else {
+            mLastRegistrationStats.rat = TelephonyManager.NETWORK_TYPE_UNKNOWN;
         }
     }
 
@@ -428,6 +446,19 @@
             case REGISTRATION_TECH_NONE:
                 return null;
             case REGISTRATION_TECH_IWLAN:
+            case REGISTRATION_TECH_CROSS_SIM:
+                return mLastWlanCapableFeatures;
+            default:
+                return mLastWwanCapableFeatures;
+        }
+    }
+
+    @Nullable
+    private MmTelCapabilities getLastCapableFeaturesForNetworkType(@NetworkType int netType) {
+        switch (netType) {
+            case TelephonyManager.NETWORK_TYPE_UNKNOWN:
+                return null;
+            case TelephonyManager.NETWORK_TYPE_IWLAN:
                 return mLastWlanCapableFeatures;
             default:
                 return mLastWwanCapableFeatures;
@@ -442,6 +473,7 @@
             case REGISTRATION_TECH_LTE:
                 return TelephonyManager.NETWORK_TYPE_LTE;
             case REGISTRATION_TECH_IWLAN:
+            case REGISTRATION_TECH_CROSS_SIM:
                 return TelephonyManager.NETWORK_TYPE_IWLAN;
             case REGISTRATION_TECH_NR:
                 return TelephonyManager.NETWORK_TYPE_NR;
@@ -457,6 +489,7 @@
         dest.carrierId = source.carrierId;
         dest.simSlotIndex = source.simSlotIndex;
         dest.rat = source.rat;
+        dest.isIwlanCrossSim = source.isIwlanCrossSim;
 
         return dest;
     }
diff --git a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
index 8c1aae3..9866c74 100644
--- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
@@ -786,7 +786,8 @@
                     perSimStatus.minimumVoltageClass, // simVoltageClass
                     perSimStatus.userModifiedApnTypes, // userModifiedApnTypeBitmask
                     perSimStatus.unmeteredNetworks, // unmeteredNetworks
-                    perSimStatus.vonrEnabled); // vonrEnabled
+                    perSimStatus.vonrEnabled, // vonrEnabled
+                    perSimStatus.crossSimCallingEnabled); // crossSimCallingEnabled
             data.add(statsEvent);
             result = StatsManager.PULL_SUCCESS;
         }
@@ -934,7 +935,8 @@
                 state.isInternetPdnUp,
                 state.foldState,
                 state.overrideVoiceService,
-                state.isDataEnabled);
+                state.isDataEnabled,
+                state.isIwlanCrossSim);
     }
 
     private static StatsEvent buildStatsEvent(VoiceCallRatUsage usage) {
@@ -988,7 +990,10 @@
                 session.lastKnownRat,
                 session.foldState,
                 session.ratSwitchCountAfterConnected,
-                session.handoverInProgress);
+                session.handoverInProgress,
+                session.isIwlanCrossSimAtStart,
+                session.isIwlanCrossSimAtEnd,
+                session.isIwlanCrossSimAtConnected);
     }
 
     private static StatsEvent buildStatsEvent(IncomingSms sms) {
@@ -1060,7 +1065,8 @@
                 dataCallSession.bandAtEnd,
                 dataCallSession.handoverFailureCauses,
                 dataCallSession.handoverFailureRat,
-                dataCallSession.isNonDds);
+                dataCallSession.isNonDds,
+                dataCallSession.isIwlanCrossSim);
     }
 
     private static StatsEvent buildStatsEvent(ImsRegistrationStats stats) {
@@ -1079,7 +1085,8 @@
                 roundAndConvertMillisToSeconds(stats.utCapableMillis),
                 roundAndConvertMillisToSeconds(stats.utAvailableMillis),
                 roundAndConvertMillisToSeconds(stats.registeringMillis),
-                roundAndConvertMillisToSeconds(stats.unregisteredMillis));
+                roundAndConvertMillisToSeconds(stats.unregisteredMillis),
+                stats.isIwlanCrossSim);
     }
 
     private static StatsEvent buildStatsEvent(ImsRegistrationTermination termination) {
@@ -1092,7 +1099,8 @@
                 termination.reasonCode,
                 termination.extraCode,
                 termination.extraMessage,
-                termination.count);
+                termination.count,
+                termination.isIwlanCrossSim);
     }
 
     private static StatsEvent buildStatsEvent(NetworkRequestsV2 networkRequests) {
diff --git a/src/java/com/android/internal/telephony/metrics/PerSimStatus.java b/src/java/com/android/internal/telephony/metrics/PerSimStatus.java
index bc1edc3..a8c7421 100644
--- a/src/java/com/android/internal/telephony/metrics/PerSimStatus.java
+++ b/src/java/com/android/internal/telephony/metrics/PerSimStatus.java
@@ -32,6 +32,7 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.provider.Telephony;
+import android.telephony.AnomalyReporter;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
@@ -46,9 +47,14 @@
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.uicc.UiccController;
 import com.android.internal.telephony.uicc.UiccSlot;
+import com.android.telephony.Rlog;
+
+import java.util.UUID;
 
 /** Stores the per SIM status. */
 public class PerSimStatus {
+    private static final String TAG = "PerSimStatus";
+
     private static final long BITMASK_2G =
             TelephonyManager.NETWORK_TYPE_BITMASK_GSM
                     | TelephonyManager.NETWORK_TYPE_BITMASK_GPRS
@@ -56,6 +62,9 @@
                     | TelephonyManager.NETWORK_TYPE_BITMASK_CDMA
                     | TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT;
 
+    private static final UUID CROSS_SIM_CALLING_STATUS_ANOMALY_UUID =
+            UUID.fromString("377e1a33-d4ac-4039-9cc0-f0d8396757f3");
+
     public final int carrierId;
     public final int phoneNumberSourceUicc;
     public final int phoneNumberSourceCarrier;
@@ -74,6 +83,8 @@
     public final long unmeteredNetworks;
     public final boolean vonrEnabled;
 
+    public final boolean crossSimCallingEnabled;
+
     /** Returns the current sim status of the given {@link Phone}. */
     @Nullable
     public static PerSimStatus getCurrentState(Phone phone) {
@@ -105,7 +116,8 @@
                 getMinimumVoltageClass(phone),
                 getUserModifiedApnTypes(phone),
                 persistAtomsStorage.getUnmeteredNetworks(phone.getPhoneId(), carrierId),
-                isVonrEnabled(phone));
+                isVonrEnabled(phone),
+                isCrossSimCallingEnabled(imsMmTelManager));
     }
 
     private PerSimStatus(
@@ -125,7 +137,8 @@
             int minimumVoltageClass,
             int userModifiedApnTypes,
             long unmeteredNetworks,
-            boolean vonrEnabled) {
+            boolean vonrEnabled,
+            boolean crossSimCallingEnabled) {
         this.carrierId = carrierId;
         this.phoneNumberSourceUicc = phoneNumberSourceUicc;
         this.phoneNumberSourceCarrier = phoneNumberSourceCarrier;
@@ -143,6 +156,18 @@
         this.userModifiedApnTypes = userModifiedApnTypes;
         this.unmeteredNetworks = unmeteredNetworks;
         this.vonrEnabled = vonrEnabled;
+        this.crossSimCallingEnabled = crossSimCallingEnabled;
+    }
+
+    private static boolean isCrossSimCallingEnabled(ImsMmTelManager imsMmTelManager) {
+        try {
+            return imsMmTelManager != null && imsMmTelManager.isCrossSimCallingEnabled();
+        } catch (Exception e) {
+            AnomalyReporter.reportAnomaly(CROSS_SIM_CALLING_STATUS_ANOMALY_UUID,
+                    "Failed to query ImsMmTelManager for cross-SIM calling status!");
+            Rlog.e(TAG, e.getMessage());
+        }
+        return false;
     }
 
     @Nullable
diff --git a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
index d5d041a..101df0d 100644
--- a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
+++ b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
@@ -1713,7 +1713,8 @@
                     && state.isInternetPdnUp == key.isInternetPdnUp
                     && state.foldState == key.foldState
                     && state.overrideVoiceService == key.overrideVoiceService
-                    && state.isDataEnabled == key.isDataEnabled) {
+                    && state.isDataEnabled == key.isDataEnabled
+                    && state.isIwlanCrossSim == key.isIwlanCrossSim) {
                 return state;
             }
         }
@@ -1761,7 +1762,8 @@
         for (ImsRegistrationStats stats : mAtoms.imsRegistrationStats) {
             if (stats.carrierId == key.carrierId
                     && stats.simSlotIndex == key.simSlotIndex
-                    && stats.rat == key.rat) {
+                    && stats.rat == key.rat
+                    && stats.isIwlanCrossSim == key.isIwlanCrossSim) {
                 return stats;
             }
         }
@@ -1777,6 +1779,7 @@
             if (termination.carrierId == key.carrierId
                     && termination.isMultiSim == key.isMultiSim
                     && termination.ratAtEnd == key.ratAtEnd
+                    && termination.isIwlanCrossSim == key.isIwlanCrossSim
                     && termination.setupFailed == key.setupFailed
                     && termination.reasonCode == key.reasonCode
                     && termination.extraCode == key.extraCode
diff --git a/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java b/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java
index 14ce2cc..d400c22 100644
--- a/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java
+++ b/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java
@@ -32,6 +32,7 @@
 import android.telephony.ServiceState;
 import android.telephony.ServiceState.RoamingType;
 import android.telephony.TelephonyManager;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.Phone;
@@ -89,6 +90,7 @@
                             CellularServiceState newServiceState = copyOf(state.mServiceState);
                             newServiceState.voiceRat =
                                     getVoiceRat(mPhone, getServiceStateForPhone(mPhone));
+                            newServiceState.isIwlanCrossSim = isCrossSimCallingRegistered(mPhone);
                             return new TimestampedServiceState(newServiceState, now);
                         });
         addServiceState(lastState, now);
@@ -132,6 +134,7 @@
             newState.foldState = mDeviceStateHelper.getFoldState();
             newState.overrideVoiceService = mOverrideVoiceService.get();
             newState.isDataEnabled = mPhone.getDataSettingsManager().isDataEnabled();
+            newState.isIwlanCrossSim = isCrossSimCallingRegistered(mPhone);
             TimestampedServiceState prevState =
                     mLastState.getAndSet(new TimestampedServiceState(newState, now));
             addServiceStateAndSwitch(
@@ -302,6 +305,7 @@
         copy.foldState = state.foldState;
         copy.overrideVoiceService = state.overrideVoiceService;
         copy.isDataEnabled = state.isDataEnabled;
+        copy.isIwlanCrossSim = state.isIwlanCrossSim;
         return copy;
     }
 
@@ -360,6 +364,14 @@
         }
     }
 
+    private boolean isCrossSimCallingRegistered(Phone phone) {
+        if (phone.getImsPhone() != null) {
+            return phone.getImsPhone().getImsRegistrationTech()
+                    == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM;
+        }
+        return false;
+    }
+
     /** Returns RAT used by WWAN if WWAN is in service. */
     public static @NetworkType int getRat(
             ServiceState state, @NetworkRegistrationInfo.Domain int domain) {
diff --git a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
index 5de9902..5dbf645 100644
--- a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
+++ b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
@@ -53,6 +53,7 @@
 import android.telephony.data.ApnSetting;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.util.LongSparseArray;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
@@ -484,6 +485,11 @@
         proto.videoEnabled = videoState != VideoProfile.STATE_AUDIO_ONLY ? true : false;
         proto.handoverInProgress = isHandoverInProgress(bearer, proto.isEmergency);
 
+        boolean isCrossSimCall = isCrossSimCall(conn);
+        proto.isIwlanCrossSimAtStart = isCrossSimCall;
+        proto.isIwlanCrossSimAtEnd = isCrossSimCall;
+        proto.isIwlanCrossSimAtConnected = isCrossSimCall;
+
         // internal fields for tracking
         if (getDirection(conn) == VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT) {
             // MT call setup hasn't begun hence set to 0
@@ -609,7 +615,9 @@
             proto.setupFailed = false;
             // Track RAT when voice call is connected.
             ServiceState serviceState = getServiceState();
-            proto.ratAtConnected = getVoiceRatWithVoNRFix(mPhone, serviceState, proto.bearerAtEnd);
+            @NetworkType int rat = getVoiceRatWithVoNRFix(mPhone, serviceState, proto.bearerAtEnd);
+            proto.ratAtConnected = rat;
+            proto.isIwlanCrossSimAtConnected = isCrossSimCall(conn);
             // Reset list of codecs with the last codec at the present time. In this way, we
             // track codec quality only after call is connected and not while ringing.
             resetCodecList(conn);
@@ -646,6 +654,7 @@
                 proto.lastKnownRat = rat;
             }
         }
+        proto.isIwlanCrossSimAtEnd = isCrossSimCall(mPhone);
     }
 
     private void finishImsCall(int id, ImsReasonInfo reasonInfo, long durationMillis) {
@@ -905,6 +914,21 @@
         return conn == null ? 0 : (int) conn.getCreateTime();
     }
 
+    private boolean isCrossSimCall(Connection conn) {
+        if (conn instanceof ImsPhoneConnection) {
+            return ((ImsPhoneConnection) conn).isCrossSimCall();
+        }
+        return false;
+    }
+
+    private boolean isCrossSimCall(Phone phone) {
+        if (phone.getImsPhone() != null) {
+            return phone.getImsPhone().getImsRegistrationTech()
+                    == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM;
+        }
+        return false;
+    }
+
     @VisibleForTesting
     protected long getTimeMillis() {
         return SystemClock.elapsedRealtime();
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
index ddd0a69..e4d16e7 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
@@ -344,6 +344,10 @@
                         SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
                         getPendingDatagramCount(), SatelliteManager.SATELLITE_RESULT_SUCCESS);
                 sendRequestAsync(CMD_SEND_SATELLITE_DATAGRAM, datagramArgs, phone);
+            } else {
+                logd("sendSatelliteDatagram: mSendingDatagramInProgress="
+                        + mSendingDatagramInProgress + ", isPollingInIdleState="
+                        + mDatagramController.isPollingInIdleState());
             }
         }
     }
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
index 695a563..3ac1bbd 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
@@ -392,6 +392,10 @@
 
                 case EVENT_RECEIVED_ACK: {
                     DatagramRetryArgument argument = (DatagramRetryArgument) msg.obj;
+                    if (!sInstance.mPendingAckCountHashMap.containsKey(argument.datagramId)) {
+                        logd("The datagram " + argument.datagramId + " should have been deleted.");
+                        return;
+                    }
                     int pendingAckCount = sInstance.mPendingAckCountHashMap
                             .get(argument.datagramId);
                     pendingAckCount -= 1;
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteController.java b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
index 00e3189..b29cf26 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -1735,6 +1735,7 @@
         if (!mSatelliteModemInterface.isSatelliteServiceSupported()) {
             return SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED;
         }
+        logd("registerForSatelliteDatagram: callback=" + callback);
         return mDatagramController.registerForSatelliteDatagram(subId, callback);
     }
 
@@ -1755,6 +1756,7 @@
         if (!mSatelliteModemInterface.isSatelliteServiceSupported()) {
             return;
         }
+        logd("unregisterForSatelliteDatagram: callback=" + callback);
         mDatagramController.unregisterForSatelliteDatagram(subId, callback);
     }
 
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
index 3d629db..03481c6 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
@@ -103,12 +103,14 @@
         @Override
         public void onSatelliteDatagramReceived(
                 android.telephony.satellite.stub.SatelliteDatagram datagram, int pendingCount) {
+            logd("onSatelliteDatagramReceived: pendingCount=" + pendingCount);
             mSatelliteDatagramsReceivedRegistrants.notifyResult(new Pair<>(
                     SatelliteServiceUtils.fromSatelliteDatagram(datagram), pendingCount));
         }
 
         @Override
         public void onPendingDatagrams() {
+            logd("onPendingDatagrams");
             mPendingDatagramsRegistrants.notifyResult(null);
         }
 
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
index 1bf866b..62cb452 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
@@ -1879,6 +1879,8 @@
      *
      * @param callingPackage The package making the call.
      * @param callingFeatureId The feature in the package.
+     * @param isForAllProfiles whether the caller intends to see all subscriptions regardless
+     *                      association.
      *
      * @return Sorted list of the currently {@link SubscriptionInfo} records available on the
      * device.
@@ -1891,7 +1893,7 @@
             "carrier privileges",
     })
     public List<SubscriptionInfo> getActiveSubscriptionInfoList(@NonNull String callingPackage,
-            @Nullable String callingFeatureId) {
+            @Nullable String callingFeatureId, boolean isForAllProfiles) {
         // Check if the caller has READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or carrier
         // privilege on any active subscription. The carrier app will get full subscription infos
         // on the subs it has carrier privilege.
@@ -1905,7 +1907,13 @@
                     + "permission. Returning empty list here.");
             return Collections.emptyList();
         }
-        return getSubscriptionInfoStreamAsUser(BINDER_WRAPPER.getCallingUserHandle())
+        if (isForAllProfiles && !hasAcrossAllUsersPermission()) {
+            //TODO(b/308809058 to determine whether the permission enforcement is needed)
+            loge("getActiveSubscriptionInfoList: "
+                    + callingPackage + " has no appropriate permission.");
+        }
+        return getSubscriptionInfoStreamAsUser(isForAllProfiles
+                ? UserHandle.ALL : BINDER_WRAPPER.getCallingUserHandle())
                 .filter(SubscriptionInfoInternal::isActive)
                 // Remove the identifier if the caller does not have sufficient permission.
                 // carrier apps will get full subscription info on the subscriptions associated
@@ -1922,6 +1930,8 @@
      *
      * @param callingPackage The package making the call.
      * @param callingFeatureId The feature in the package.
+     * @param isForAllProfiles whether the caller intends to see all subscriptions regardless
+     *                        association.
      *
      * @return the number of active subscriptions.
      *
@@ -1934,14 +1944,27 @@
             "carrier privileges",
     })
     public int getActiveSubInfoCount(@NonNull String callingPackage,
-            @Nullable String callingFeatureId) {
+            @Nullable String callingFeatureId, boolean isForAllProfiles) {
         if (!TelephonyPermissions.checkReadPhoneStateOnAnyActiveSub(mContext,
                 Binder.getCallingPid(), Binder.getCallingUid(), callingPackage, callingFeatureId,
                 "getAllSubInfoList")) {
             throw new SecurityException("Need READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or "
                     + "carrier privilege");
         }
-        return getActiveSubIdListAsUser(false, BINDER_WRAPPER.getCallingUserHandle()).length;
+        if (isForAllProfiles && !hasAcrossAllUsersPermission()) {
+            //TODO(b/308809058 to determine whether the permission enforcement is needed)
+            loge("getActiveSubInfoCount: "
+                    + callingPackage + " has no appropriate permission.");
+        }
+        return getActiveSubIdListAsUser(false, isForAllProfiles
+                ? UserHandle.ALL : BINDER_WRAPPER.getCallingUserHandle()).length;
+    }
+
+    /**@return {@code true} if the caller is permitted to see all subscriptions. */
+    private boolean hasAcrossAllUsersPermission() {
+        return hasPermissions(Manifest.permission.INTERACT_ACROSS_USERS,
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                Manifest.permission.INTERACT_ACROSS_PROFILES);
     }
 
     /**
@@ -3885,16 +3908,6 @@
     }
 
     /**
-     * @return All active subscriptions.
-     */
-    @NonNull
-    private List<SubscriptionInfoInternal> getActiveSubscriptionInfoListAllUser() {
-        return mSubscriptionDatabaseManager.getAllSubscriptions().stream()
-                .filter(SubscriptionInfoInternal::isActive)
-                .collect(Collectors.toList());
-    }
-
-    /**
      * Get subscriptions accessible to the caller user.
      *
      * @param user The user to check.
@@ -4136,7 +4149,10 @@
      */
     @VisibleForTesting
     public void updateGroupDisabled() {
-        List<SubscriptionInfoInternal> activeSubscriptions = getActiveSubscriptionInfoListAllUser();
+        List<SubscriptionInfoInternal> activeSubscriptions = mSubscriptionDatabaseManager
+                .getAllSubscriptions().stream()
+                .filter(SubscriptionInfoInternal::isActive)
+                .collect(Collectors.toList());
         for (SubscriptionInfo oppSubInfo : getOpportunisticSubscriptions(
                 mContext.getOpPackageName(), mContext.getFeatureId())) {
             boolean groupDisabled = activeSubscriptions.stream()
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellBroadcastConfigTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellBroadcastConfigTrackerTest.java
index 21a14b2..722f6ac 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CellBroadcastConfigTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellBroadcastConfigTrackerTest.java
@@ -51,6 +51,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -67,6 +68,7 @@
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
+        mFeatureFlags = Mockito.mock(FeatureFlags.class);
         mSpyCi = spy(mSimulatedCommands);
         mPhone = new GsmCdmaPhone(mContext, mSpyCi, mNotifier, true, 0,
             PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory, (c, p) -> mImsManager,
diff --git a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
index a41dbe1..7893e78 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/MultiSimSettingControllerTest.java
@@ -198,7 +198,7 @@
             }
             return subscriptionInfoList;
         }).when(mSubscriptionManagerService).getActiveSubscriptionInfoList(
-                anyString(), nullable(String.class));
+                anyString(), nullable(String.class), anyBoolean());
 
         doAnswer(invocation -> {
             final boolean visibleOnly = (boolean) invocation.getArguments()[0];
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
index f8d22cd..ff14f9a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
@@ -54,7 +54,6 @@
 import com.android.internal.telephony.data.DataConfigManager.DataConfigManagerCallback;
 import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
 import com.android.internal.telephony.data.DataProfileManager.DataProfileManagerCallback;
-import com.android.internal.telephony.flags.FeatureFlags;
 
 import org.junit.After;
 import org.junit.Before;
@@ -100,8 +99,6 @@
 
     private DataProfileManager mDataProfileManagerUT;
 
-    private FeatureFlags mFeatureFlags;
-
     private final ApnSettingContentProvider mApnSettingContentProvider =
             new ApnSettingContentProvider();
 
@@ -835,7 +832,6 @@
         logd("DataProfileManagerTest +Setup!");
         super.setUp(getClass().getSimpleName());
         mDataProfileManagerCallback = Mockito.mock(DataProfileManagerCallback.class);
-        mFeatureFlags = Mockito.mock(FeatureFlags.class);
         ((MockContentResolver) mContext.getContentResolver()).addProvider(
                 Telephony.Carriers.CONTENT_URI.getAuthority(), mApnSettingContentProvider);
 
@@ -1035,6 +1031,7 @@
 
     @Test
     public void testSetPreferredDataProfile() {
+        doReturn(true).when(mFeatureFlags).refinePreferredDataProfileSelection();
         TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(
                 new NetworkRequest.Builder()
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java
index dd95ff0..2541bd1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataRetryManagerTest.java
@@ -786,6 +786,7 @@
 
     @Test
     public void testDataRetryLongTimer() {
+        doReturn(true).when(mFeatureFlags).useAlarmCallback();
         // Rule requires a long timer
         DataSetupRetryRule retryRule = new DataSetupRetryRule(
                 "capabilities=internet, retry_interval=120000, maximum_retries=2");
diff --git a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
index 7da79a6..1eff979 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
@@ -57,6 +57,7 @@
 import android.telephony.EmergencyRegResult;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -206,6 +207,83 @@
     }
 
     /**
+     * Test that the EmergencyStateTracker turns off satellite modem, performs a DDS switch and
+     * sets emergency mode switch when we are not roaming and the carrier only supports SUPL over
+     * the data plane.
+     */
+    @Test
+    @SmallTest
+    public void startEmergencyCall_satelliteEnabled_turnOnRadioSwitchDdsAndSetEmergencyMode() {
+        EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+                true /* isSuplDdsSwitchRequiredForEmergencyCall */);
+        // Create test Phones and set radio on
+        Phone testPhone = setupTestPhoneForEmergencyCall(false /* isRoaming */,
+                true /* isRadioOn */);
+        when(mSST.isRadioOn()).thenReturn(true);
+        // Satellite enabled
+        when(mSatelliteController.isSatelliteEnabled()).thenReturn(true);
+
+        setConfigForDdsSwitch(testPhone, null,
+                CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY, "150");
+        // Spy is used to capture consumer in delayDialForDdsSwitch
+        EmergencyStateTracker spyEst = spy(emergencyStateTracker);
+        CompletableFuture<Integer> unused = spyEst.startEmergencyCall(testPhone, TEST_CALL_ID,
+                false);
+
+        // startEmergencyCall should trigger radio on
+        ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor
+                .forClass(RadioOnStateListener.Callback.class);
+        verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone),
+                eq(false), eq(0));
+        // isOkToCall() should return true once satellite modem is off
+        assertFalse(callback.getValue()
+                .isOkToCall(testPhone, ServiceState.STATE_IN_SERVICE, false));
+        when(mSatelliteController.isSatelliteEnabled()).thenReturn(false);
+        assertTrue(callback.getValue()
+                .isOkToCall(testPhone, ServiceState.STATE_IN_SERVICE, false));
+        // Once radio on is complete, trigger delay dial
+        callback.getValue().onComplete(null, true);
+        ArgumentCaptor<Consumer<Boolean>> completeConsumer = ArgumentCaptor
+                .forClass(Consumer.class);
+        verify(spyEst).switchDdsDelayed(eq(testPhone), completeConsumer.capture());
+        verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(testPhone.getPhoneId()),
+                eq(150) /* extensionTime */, any());
+        // After dds switch completes successfully, set emergency mode
+        completeConsumer.getValue().accept(true);
+        verify(testPhone).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any());
+    }
+
+    /**
+     * Test that if startEmergencyCall fails to turn off satellite modem, then it's future completes
+     * with {@link DisconnectCause#SATELLITE_ENABLED}.
+     */
+    @Test
+    @SmallTest
+    public void startEmergencyCall_satelliteOffFails_returnsDisconnectCauseSatelliteEnabled() {
+        EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+                true /* isSuplDdsSwitchRequiredForEmergencyCall */);
+        // Create test Phones and set radio on
+        Phone testPhone = setupTestPhoneForEmergencyCall(false /* isRoaming */,
+                true /* isRadioOn */);
+        // Satellite enabled
+        when(mSatelliteController.isSatelliteEnabled()).thenReturn(true);
+
+        CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(testPhone,
+                TEST_CALL_ID, false);
+
+        // startEmergencyCall should trigger satellite modem off
+        ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor
+                .forClass(RadioOnStateListener.Callback.class);
+        verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true), eq(testPhone),
+                eq(false), eq(0));
+        // Verify future completes with DisconnectCause.POWER_OFF if radio not ready
+        CompletableFuture<Void> unused = future.thenAccept((result) -> {
+            assertEquals((Integer) result, (Integer) DisconnectCause.SATELLITE_ENABLED);
+        });
+        callback.getValue().onComplete(null, false /* isRadioReady */);
+    }
+
+    /**
      * Test that the EmergencyStateTracker does not perform a DDS switch when the carrier supports
      * control-plane fallback. Radio is set to on so RadioOnHelper not triggered.
      */
@@ -598,6 +676,216 @@
     }
 
     /**
+     * Test that once endCall() for IMS call is called and we enter ECM, then we exit ECM
+     * after the specified timeout.
+     */
+    @Test
+    @SmallTest
+    public void endCall_entersEcm_thenExitsEcmAfterTimeoutImsCall() throws Exception {
+        // Setup EmergencyStateTracker
+        EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+                /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+        // Create test Phone
+        Phone testPhone = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+                /* isRadioOn= */ true);
+        setUpAsyncResultForSetEmergencyMode(testPhone, E_REG_RESULT);
+        setUpAsyncResultForExitEmergencyMode(testPhone);
+        CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
+                TEST_CALL_ID, false);
+        // Set call to ACTIVE
+        emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+        emergencyStateTracker.onEmergencyCallDomainUpdated(
+                PhoneConstants.PHONE_TYPE_IMS, TEST_CALL_ID);
+        // Set ecm as supported
+        setEcmSupportedConfig(testPhone, /* ecmSupported= */ true);
+
+        processAllMessages();
+
+        emergencyStateTracker.endCall(TEST_CALL_ID);
+
+        assertTrue(emergencyStateTracker.isInEcm());
+
+        Context mockContext = mock(Context.class);
+        replaceInstance(EmergencyStateTracker.class, "mContext",
+                emergencyStateTracker, mockContext);
+        processAllFutureMessages();
+
+        ArgumentCaptor<TelephonyCallback> callbackCaptor =
+                ArgumentCaptor.forClass(TelephonyCallback.class);
+
+        verify(mTelephonyManagerProxy).registerTelephonyCallback(eq(testPhone.getSubId()),
+                  any(), callbackCaptor.capture());
+
+        TelephonyCallback callback = callbackCaptor.getValue();
+
+        assertNotNull(callback);
+
+        // Verify exitEmergencyMode() is called after timeout
+        verify(testPhone).exitEmergencyMode(any(Message.class));
+        assertFalse(emergencyStateTracker.isInEmergencyMode());
+        assertFalse(emergencyStateTracker.isInEcm());
+        verify(mTelephonyManagerProxy).unregisterTelephonyCallback(eq(callback));
+    }
+
+    /**
+     * Test that startEmergencyCall() is called right after exiting ECM on the same slot.
+     */
+    @Test
+    @SmallTest
+    public void exitEcm_thenDialEmergencyCallOnTheSameSlotRightAfter() throws Exception {
+        // Setup EmergencyStateTracker
+        EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+                /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+        emergencyStateTracker.setPdnDisconnectionTimeoutMs(0);
+        // Create test Phone
+        Phone testPhone = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+                /* isRadioOn= */ true);
+        setUpAsyncResultForSetEmergencyMode(testPhone, E_REG_RESULT);
+        setUpAsyncResultForExitEmergencyMode(testPhone);
+
+        verify(testPhone, times(0)).setEmergencyMode(anyInt(), any(Message.class));
+        verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));
+
+        CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
+                TEST_CALL_ID, false);
+        processAllMessages();
+
+        verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+        verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));
+
+        // Set call to ACTIVE
+        emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+        emergencyStateTracker.onEmergencyCallDomainUpdated(
+                PhoneConstants.PHONE_TYPE_IMS, TEST_CALL_ID);
+        // Set ecm as supported
+        setEcmSupportedConfig(testPhone, /* ecmSupported= */ true);
+
+        processAllMessages();
+
+        emergencyStateTracker.endCall(TEST_CALL_ID);
+
+        assertTrue(emergencyStateTracker.isInEcm());
+
+        Context mockContext = mock(Context.class);
+        replaceInstance(EmergencyStateTracker.class, "mContext",
+                emergencyStateTracker, mockContext);
+        processAllFutureMessages();
+
+        ArgumentCaptor<TelephonyCallback> callbackCaptor =
+                ArgumentCaptor.forClass(TelephonyCallback.class);
+
+        verify(mTelephonyManagerProxy).registerTelephonyCallback(eq(testPhone.getSubId()),
+                  any(), callbackCaptor.capture());
+
+        TelephonyCallback callback = callbackCaptor.getValue();
+
+        assertNotNull(callback);
+        assertFalse(emergencyStateTracker.isInEmergencyMode());
+        assertFalse(emergencyStateTracker.isInEcm());
+        verify(mTelephonyManagerProxy, times(0)).unregisterTelephonyCallback(eq(callback));
+        verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+        verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK),
+                any(Message.class));
+        verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));
+
+        replaceInstance(EmergencyStateTracker.class, "mContext", emergencyStateTracker, mContext);
+
+        // dial on the same slot
+        unused = emergencyStateTracker.startEmergencyCall(testPhone, TEST_CALL_ID, false);
+        processAllMessages();
+
+        assertTrue(emergencyStateTracker.isInEmergencyMode());
+        assertFalse(emergencyStateTracker.isInEcm());
+        verify(mTelephonyManagerProxy, times(1)).unregisterTelephonyCallback(eq(callback));
+        verify(testPhone, times(2)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+        verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK),
+                any(Message.class));
+        verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));
+    }
+
+    /**
+     * Test that startEmergencyCall() is called right after exiting ECM on the other slot.
+     */
+    @Test
+    @SmallTest
+    public void exitEcm_thenDialEmergencyCallOnTheOtherSlotRightAfter() throws Exception {
+        // Setup EmergencyStateTracker
+        EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
+                /* isSuplDdsSwitchRequiredForEmergencyCall= */ true);
+        emergencyStateTracker.setPdnDisconnectionTimeoutMs(0);
+        // Create test Phone
+        Phone testPhone = setupTestPhoneForEmergencyCall(/* isRoaming= */ true,
+                /* isRadioOn= */ true);
+        setUpAsyncResultForSetEmergencyMode(testPhone, E_REG_RESULT);
+        setUpAsyncResultForExitEmergencyMode(testPhone);
+
+        verify(testPhone, times(0)).setEmergencyMode(anyInt(), any(Message.class));
+        verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));
+
+        CompletableFuture<Integer> unused = emergencyStateTracker.startEmergencyCall(testPhone,
+                TEST_CALL_ID, false);
+        processAllMessages();
+
+        verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+        verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));
+
+        // Set call to ACTIVE
+        emergencyStateTracker.onEmergencyCallStateChanged(Call.State.ACTIVE, TEST_CALL_ID);
+        emergencyStateTracker.onEmergencyCallDomainUpdated(
+                PhoneConstants.PHONE_TYPE_IMS, TEST_CALL_ID);
+        // Set ecm as supported
+        setEcmSupportedConfig(testPhone, /* ecmSupported= */ true);
+
+        processAllMessages();
+
+        emergencyStateTracker.endCall(TEST_CALL_ID);
+
+        assertTrue(emergencyStateTracker.isInEcm());
+
+        Context mockContext = mock(Context.class);
+        replaceInstance(EmergencyStateTracker.class, "mContext",
+                emergencyStateTracker, mockContext);
+        processAllFutureMessages();
+
+        ArgumentCaptor<TelephonyCallback> callbackCaptor =
+                ArgumentCaptor.forClass(TelephonyCallback.class);
+
+        verify(mTelephonyManagerProxy).registerTelephonyCallback(eq(testPhone.getSubId()),
+                  any(), callbackCaptor.capture());
+
+        TelephonyCallback callback = callbackCaptor.getValue();
+
+        assertNotNull(callback);
+        assertFalse(emergencyStateTracker.isInEmergencyMode());
+        assertFalse(emergencyStateTracker.isInEcm());
+        verify(mTelephonyManagerProxy, times(0)).unregisterTelephonyCallback(eq(callback));
+        verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+        verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK),
+                any(Message.class));
+        verify(testPhone, times(0)).exitEmergencyMode(any(Message.class));
+
+        Phone phone1 = getPhone(1);
+        verify(phone1, times(0)).setEmergencyMode(anyInt(), any(Message.class));
+        verify(phone1, times(0)).exitEmergencyMode(any(Message.class));
+
+        replaceInstance(EmergencyStateTracker.class, "mContext", emergencyStateTracker, mContext);
+
+        // dial on the other slot
+        unused = emergencyStateTracker.startEmergencyCall(phone1, TEST_CALL_ID, false);
+        processAllMessages();
+
+        assertTrue(emergencyStateTracker.isInEmergencyMode());
+        assertFalse(emergencyStateTracker.isInEcm());
+        verify(mTelephonyManagerProxy, times(1)).unregisterTelephonyCallback(eq(callback));
+        verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+        verify(testPhone, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_CALLBACK),
+                any(Message.class));
+        verify(testPhone, times(1)).exitEmergencyMode(any(Message.class));
+        verify(phone1, times(1)).setEmergencyMode(eq(MODE_EMERGENCY_WWAN), any(Message.class));
+        verify(phone1, times(0)).exitEmergencyMode(any(Message.class));
+    }
+
+    /**
      * Test that after exitEmergencyCallbackMode() is called, the correct intents are sent and
      * emergency mode is exited on the modem.
      */
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java
index 1f2cb15..17a428b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmMmiCodeTest.java
@@ -43,6 +43,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 
 import java.util.ArrayList;
 import java.util.concurrent.Executor;
@@ -65,6 +66,7 @@
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
+        mFeatureFlags = Mockito.mock(FeatureFlags.class);
         doReturn(mExecutor).when(mContext).getMainExecutor();
         mGsmCdmaPhoneUT = new GsmCdmaPhone(mContext, mSimulatedCommands, mNotifier, true, 0,
                 PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory, (c, p) -> mImsManager,
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/DataCallSessionStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/DataCallSessionStatsTest.java
new file mode 100644
index 0000000..700b4cf
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/DataCallSessionStatsTest.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.telephony.DataFailCause;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
+import android.telephony.data.DataCallResponse;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.nano.PersistAtomsProto.DataCallSession;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+public class DataCallSessionStatsTest extends TelephonyTest {
+
+    private ArgumentCaptor<NetworkCallback> mNetworkCallbackCaptor =
+            ArgumentCaptor.forClass(NetworkCallback.class);
+    private DataCallResponse mDefaultImsResponse = buildDataCallResponse("ims", 0);
+    private Network mMockNetwork;
+    private NetworkCapabilities mCellularNetworkCapabilities = new NetworkCapabilities.Builder()
+            .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).build();
+
+    private static class TestableDataCallSessionStats extends DataCallSessionStats {
+        private long mTimeMillis = 0L;
+
+        TestableDataCallSessionStats(Phone phone) {
+            super(phone);
+        }
+
+        @Override
+        protected long getTimeMillis() {
+            return mTimeMillis;
+        }
+
+        private void setTimeMillis(long timeMillis) {
+            mTimeMillis = timeMillis;
+        }
+    }
+
+    private TestableDataCallSessionStats mDataCallSessionStats;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+        doNothing().when(mConnectivityManager).registerSystemDefaultNetworkCallback(
+                mNetworkCallbackCaptor.capture(), any());
+        mMockNetwork = mock(Network.class);
+        when(mServiceState.getDataRegistrationState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+        mDataCallSessionStats = new TestableDataCallSessionStats(mPhone);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mDataCallSessionStats = null;
+        super.tearDown();
+    }
+
+    private NetworkCallback getNetworkMonitorCallback() {
+        return mNetworkCallbackCaptor.getValue();
+    }
+
+    private DataCallResponse buildDataCallResponse(String apn, long retryDurationMillis) {
+        return new DataCallResponse.Builder()
+                .setId(apn.hashCode())
+                .setRetryDurationMillis(retryDurationMillis)
+                .build();
+    }
+
+    @Test
+    @SmallTest
+    public void testSetupDataCallOnCellularIms_success() {
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCallResponse(
+                mDefaultImsResponse,
+                TelephonyManager.NETWORK_TYPE_LTE,
+                ApnSetting.TYPE_IMS,
+                ApnSetting.PROTOCOL_IP,
+                DataFailCause.NONE);
+
+        mDataCallSessionStats.setTimeMillis(60000L);
+        mDataCallSessionStats.conclude();
+
+        ArgumentCaptor<DataCallSession> callCaptor =
+                ArgumentCaptor.forClass(DataCallSession.class);
+        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
+                callCaptor.capture());
+        DataCallSession stats = callCaptor.getValue();
+
+        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
+        assertEquals(1, stats.durationMinutes);
+        assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.ratAtEnd);
+        assertTrue(stats.ongoing);
+    }
+
+    @Test
+    @SmallTest
+    public void testSetupDataCallOnIwlan_success() {
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCallResponse(
+                mDefaultImsResponse,
+                TelephonyManager.NETWORK_TYPE_IWLAN,
+                ApnSetting.TYPE_IMS,
+                ApnSetting.PROTOCOL_IP,
+                DataFailCause.NONE);
+
+        mDataCallSessionStats.setTimeMillis(120000L);
+        mDataCallSessionStats.conclude();
+
+        ArgumentCaptor<DataCallSession> callCaptor =
+                ArgumentCaptor.forClass(DataCallSession.class);
+        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
+                callCaptor.capture());
+        DataCallSession stats = callCaptor.getValue();
+
+        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
+        assertEquals(2, stats.durationMinutes);
+        assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, stats.ratAtEnd);
+        assertFalse(stats.isIwlanCrossSim);
+        assertTrue(stats.ongoing);
+    }
+
+    @Test
+    @SmallTest
+    public void testSetupDataCallOnCrossSimCalling_success() {
+        getNetworkMonitorCallback().onAvailable(mMockNetwork);
+        getNetworkMonitorCallback().onCapabilitiesChanged(
+                mMockNetwork, mCellularNetworkCapabilities);
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCallResponse(
+                mDefaultImsResponse,
+                TelephonyManager.NETWORK_TYPE_IWLAN,
+                ApnSetting.TYPE_IMS,
+                ApnSetting.PROTOCOL_IP,
+                DataFailCause.NONE);
+
+        mDataCallSessionStats.setTimeMillis(60000L);
+        mDataCallSessionStats.conclude();
+
+        ArgumentCaptor<DataCallSession> callCaptor =
+                ArgumentCaptor.forClass(DataCallSession.class);
+        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
+                callCaptor.capture());
+        DataCallSession stats = callCaptor.getValue();
+
+        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
+        assertEquals(1, stats.durationMinutes);
+        assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, stats.ratAtEnd);
+        assertTrue(stats.isIwlanCrossSim);
+        assertTrue(stats.ongoing);
+    }
+
+    @Test
+    @SmallTest
+    public void testSetupDataCallOnCellularIms_failure() {
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCallResponse(
+                mDefaultImsResponse,
+                TelephonyManager.NETWORK_TYPE_LTE,
+                ApnSetting.TYPE_IMS,
+                ApnSetting.PROTOCOL_IP,
+                DataFailCause.NETWORK_FAILURE);
+
+        ArgumentCaptor<DataCallSession> callCaptor =
+                ArgumentCaptor.forClass(DataCallSession.class);
+        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
+                callCaptor.capture());
+        DataCallSession stats = callCaptor.getValue();
+
+        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
+        assertEquals(0, stats.durationMinutes);
+        assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.ratAtEnd);
+        assertFalse(stats.ongoing);
+    }
+
+    @Test
+    @SmallTest
+    public void testHandoverFromCellularToIwlan_success() {
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCallResponse(
+                mDefaultImsResponse,
+                TelephonyManager.NETWORK_TYPE_LTE,
+                ApnSetting.TYPE_IMS,
+                ApnSetting.PROTOCOL_IP,
+                DataFailCause.NONE);
+
+        mDataCallSessionStats.onDrsOrRatChanged(TelephonyManager.NETWORK_TYPE_IWLAN);
+        mDataCallSessionStats.conclude();
+
+        ArgumentCaptor<DataCallSession> callCaptor =
+                ArgumentCaptor.forClass(DataCallSession.class);
+        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
+                callCaptor.capture());
+        DataCallSession stats = callCaptor.getValue();
+
+        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
+        assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, stats.ratAtEnd);
+        assertEquals(1, stats.ratSwitchCount);
+        assertTrue(stats.ongoing);
+    }
+
+    @Test
+    @SmallTest
+    public void testHandoverFromCellularToCrossSimCalling_success() {
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCallResponse(
+                mDefaultImsResponse,
+                TelephonyManager.NETWORK_TYPE_LTE,
+                ApnSetting.TYPE_IMS,
+                ApnSetting.PROTOCOL_IP,
+                DataFailCause.NONE);
+
+        getNetworkMonitorCallback().onAvailable(mMockNetwork);
+        getNetworkMonitorCallback().onCapabilitiesChanged(
+                mMockNetwork, mCellularNetworkCapabilities);
+        mDataCallSessionStats.onDrsOrRatChanged(TelephonyManager.NETWORK_TYPE_IWLAN);
+        mDataCallSessionStats.conclude();
+
+        ArgumentCaptor<DataCallSession> callCaptor =
+                ArgumentCaptor.forClass(DataCallSession.class);
+        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
+                callCaptor.capture());
+        DataCallSession stats = callCaptor.getValue();
+
+        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
+        assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, stats.ratAtEnd);
+        assertEquals(1, stats.ratSwitchCount);
+        assertTrue(stats.isIwlanCrossSim);
+        assertTrue(stats.ongoing);
+    }
+
+    @Test
+    @SmallTest
+    public void testHandoverFromCellularToIwlan_failure() {
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCallResponse(
+                mDefaultImsResponse,
+                TelephonyManager.NETWORK_TYPE_LTE,
+                ApnSetting.TYPE_IMS,
+                ApnSetting.PROTOCOL_IP,
+                DataFailCause.NONE);
+
+        mDataCallSessionStats.onHandoverFailure(DataFailCause.IWLAN_DNS_RESOLUTION_TIMEOUT,
+                TelephonyManager.NETWORK_TYPE_LTE, TelephonyManager.NETWORK_TYPE_IWLAN);
+        mDataCallSessionStats.conclude();
+
+        ArgumentCaptor<DataCallSession> callCaptor =
+                ArgumentCaptor.forClass(DataCallSession.class);
+        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
+                callCaptor.capture());
+        DataCallSession stats = callCaptor.getValue();
+
+        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
+        assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.ratAtEnd);
+        assertTrue(stats.ongoing);
+        assertEquals(DataFailCause.IWLAN_DNS_RESOLUTION_TIMEOUT,
+                stats.handoverFailureCauses[0]);
+
+        int cellularToIwlanFailureDirection = TelephonyManager.NETWORK_TYPE_LTE
+                | (TelephonyManager.NETWORK_TYPE_IWLAN << 16);
+        assertEquals(cellularToIwlanFailureDirection, stats.handoverFailureRat[0]);
+    }
+
+    @Test
+    @SmallTest
+    public void testSetupDataCallOnIwlan_success_thenOOS() {
+        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
+        mDataCallSessionStats.onSetupDataCallResponse(
+                mDefaultImsResponse,
+                TelephonyManager.NETWORK_TYPE_IWLAN,
+                ApnSetting.TYPE_IMS,
+                ApnSetting.PROTOCOL_IP,
+                DataFailCause.NONE);
+        when(mServiceState.getDataRegistrationState())
+                .thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+        mDataCallSessionStats.onDataCallDisconnected(DataFailCause.IWLAN_IKE_DPD_TIMEOUT);
+
+        ArgumentCaptor<DataCallSession> callCaptor =
+                ArgumentCaptor.forClass(DataCallSession.class);
+        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
+                callCaptor.capture());
+        DataCallSession stats = callCaptor.getValue();
+
+        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
+        assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, stats.ratAtEnd);
+        assertTrue(stats.oosAtEnd);
+        assertFalse(stats.ongoing);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/ImsStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/ImsStatsTest.java
index 7c7c5b2..df8bd85 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/ImsStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/ImsStatsTest.java
@@ -23,6 +23,7 @@
 import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT;
 import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO;
 import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM;
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NR;
@@ -40,6 +41,7 @@
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.TelephonyManager;
 import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
 import android.telephony.ims.ProvisioningManager;
 import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities;
 import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability;
@@ -67,6 +69,13 @@
     private static final int CARRIER1_ID = 1;
     private static final int CARRIER2_ID = 1187;
 
+    private ImsRegistrationAttributes mWwanAttributes =
+            new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_LTE).build();
+    private ImsRegistrationAttributes mIwlanAttributes =
+            new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_IWLAN).build();
+    private ImsRegistrationAttributes mCrossSimAttributes =
+            new ImsRegistrationAttributes.Builder(REGISTRATION_TECH_CROSS_SIM).build();
+
     @MmTelCapability
     private static final int CAPABILITY_TYPE_ALL =
             MmTelCapabilities.CAPABILITY_TYPE_VOICE
@@ -164,7 +173,7 @@
                 CAPABILITY_TYPE_SMS,
                 REGISTRATION_TECH_LTE,
                 ProvisioningManager.PROVISIONING_VALUE_ENABLED);
-        mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+        mImsStats.onImsRegistered(mWwanAttributes);
         mImsStats.onImsCapabilitiesChanged(
                 REGISTRATION_TECH_LTE, new MmTelCapabilities(CAPABILITY_TYPE_ALL));
 
@@ -211,7 +220,7 @@
                 CAPABILITY_TYPE_SMS,
                 REGISTRATION_TECH_LTE,
                 ProvisioningManager.PROVISIONING_VALUE_ENABLED);
-        mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+        mImsStats.onImsRegistered(mWwanAttributes);
         mImsStats.onImsCapabilitiesChanged(
                 REGISTRATION_TECH_LTE, new MmTelCapabilities(CAPABILITY_TYPE_VOICE));
 
@@ -258,7 +267,7 @@
                 CAPABILITY_TYPE_UT,
                 REGISTRATION_TECH_LTE,
                 ProvisioningManager.PROVISIONING_VALUE_ENABLED);
-        mImsStats.onImsRegistered(TRANSPORT_TYPE_WLAN);
+        mImsStats.onImsRegistered(mIwlanAttributes);
         mImsStats.onImsCapabilitiesChanged(
                 REGISTRATION_TECH_IWLAN, new MmTelCapabilities(CAPABILITY_TYPE_VOICE));
 
@@ -287,6 +296,35 @@
 
     @Test
     @SmallTest
+    public void conclude_registeredVoiceOnly_crossSimCalling() throws Exception {
+        mImsStats.onSetFeatureResponse(
+                CAPABILITY_TYPE_VOICE,
+                REGISTRATION_TECH_CROSS_SIM,
+                ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+        mImsStats.onImsRegistered(mCrossSimAttributes);
+        mImsStats.onImsCapabilitiesChanged(
+                REGISTRATION_TECH_CROSS_SIM, new MmTelCapabilities(CAPABILITY_TYPE_VOICE));
+
+        mImsStats.incTimeMillis(2000L);
+        mImsStats.conclude();
+
+        // Duration should be counted
+        ArgumentCaptor<ImsRegistrationStats> captor =
+                ArgumentCaptor.forClass(ImsRegistrationStats.class);
+        verify(mPersistAtomsStorage).addImsRegistrationStats(captor.capture());
+        ImsRegistrationStats stats = captor.getValue();
+        assertEquals(CARRIER1_ID, stats.carrierId);
+        assertEquals(0, stats.simSlotIndex);
+        assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, stats.rat);
+        assertEquals(true, stats.isIwlanCrossSim);
+        assertEquals(2000L, stats.registeredMillis);
+        assertEquals(2000L, stats.voiceCapableMillis);
+        assertEquals(2000L, stats.voiceAvailableMillis);
+        verifyNoMoreInteractions(mPersistAtomsStorage);
+    }
+
+    @Test
+    @SmallTest
     public void conclude_notRegistered() throws Exception {
         // IMS over LTE
         mImsStats.onSetFeatureResponse(
@@ -333,7 +371,7 @@
         ImsRegistrationStats stats = statsCaptor.getValue();
         assertEquals(CARRIER1_ID, stats.carrierId);
         assertEquals(0, stats.simSlotIndex);
-        assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, stats.rat);
+        assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.rat);
         assertEquals(2000L, stats.unregisteredMillis);
         verifyNoMoreInteractions(mPersistAtomsStorage);
     }
@@ -380,12 +418,129 @@
 
     @Test
     @SmallTest
+    public void conclude_serviceStateChanged_afterRatUnknown() throws Exception {
+        // IMS over LTE
+        mImsStats.onSetFeatureResponse(
+                CAPABILITY_TYPE_VOICE,
+                REGISTRATION_TECH_LTE,
+                ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+        mImsStats.onSetFeatureResponse(
+                CAPABILITY_TYPE_VIDEO,
+                REGISTRATION_TECH_LTE,
+                ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+        mImsStats.onSetFeatureResponse(
+                CAPABILITY_TYPE_UT,
+                REGISTRATION_TECH_LTE,
+                ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+        mImsStats.onSetFeatureResponse(
+                CAPABILITY_TYPE_SMS,
+                REGISTRATION_TECH_LTE,
+                ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+        mImsStats.onImsCapabilitiesChanged(
+                REGISTRATION_TECH_LTE, new MmTelCapabilities(CAPABILITY_TYPE_ALL));
+
+        mImsStats.onImsUnregistered(
+                new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 999, "Timeout"));
+
+        doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN)
+                .when(mServiceState)
+                .getDataNetworkType();
+        mImsStats.onServiceStateChanged(mServiceState);
+
+        mImsStats.incTimeMillis(2000L);
+
+        doReturn(TelephonyManager.NETWORK_TYPE_LTE)
+                .when(mServiceState)
+                .getDataNetworkType();
+        mImsStats.onServiceStateChanged(mServiceState);
+        mImsStats.conclude();
+
+        // Atom with termination info should be generated
+        ArgumentCaptor<ImsRegistrationTermination> terminationCaptor =
+                ArgumentCaptor.forClass(ImsRegistrationTermination.class);
+        verify(mPersistAtomsStorage).addImsRegistrationTermination(terminationCaptor.capture());
+        ImsRegistrationTermination termination = terminationCaptor.getValue();
+        assertEquals(CARRIER1_ID, termination.carrierId);
+        assertFalse(termination.isMultiSim);
+        assertEquals(TelephonyManager.NETWORK_TYPE_LTE, termination.ratAtEnd);
+        assertTrue(termination.setupFailed);
+        assertEquals(ImsReasonInfo.CODE_REGISTRATION_ERROR, termination.reasonCode);
+        assertEquals(999, termination.extraCode);
+        assertEquals("Timeout", termination.extraMessage);
+        verifyNoMoreInteractions(mPersistAtomsStorage);
+    }
+
+    @Test
+    @SmallTest
+    public void conclude_serviceStateChanged_afterRatLte() throws Exception {
+        // IMS over LTE
+        mImsStats.onSetFeatureResponse(
+                CAPABILITY_TYPE_VOICE,
+                REGISTRATION_TECH_LTE,
+                ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+        mImsStats.onSetFeatureResponse(
+                CAPABILITY_TYPE_VIDEO,
+                REGISTRATION_TECH_LTE,
+                ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+        mImsStats.onSetFeatureResponse(
+                CAPABILITY_TYPE_UT,
+                REGISTRATION_TECH_LTE,
+                ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+        mImsStats.onSetFeatureResponse(
+                CAPABILITY_TYPE_SMS,
+                REGISTRATION_TECH_LTE,
+                ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+        mImsStats.onImsCapabilitiesChanged(
+                REGISTRATION_TECH_LTE, new MmTelCapabilities(CAPABILITY_TYPE_ALL));
+
+        mImsStats.onImsUnregistered(
+                new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 999, "Timeout"));
+
+        doReturn(TelephonyManager.NETWORK_TYPE_LTE)
+                .when(mServiceState)
+                .getDataNetworkType();
+        mImsStats.onServiceStateChanged(mServiceState);
+
+        mImsStats.incTimeMillis(2000L);
+
+        doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN)
+                .when(mServiceState)
+                .getDataNetworkType();
+        mImsStats.onServiceStateChanged(mServiceState);
+        mImsStats.conclude();
+
+        // Atom with termination info and durations should be generated
+        ArgumentCaptor<ImsRegistrationTermination> terminationCaptor =
+                ArgumentCaptor.forClass(ImsRegistrationTermination.class);
+        verify(mPersistAtomsStorage).addImsRegistrationTermination(terminationCaptor.capture());
+        ImsRegistrationTermination termination = terminationCaptor.getValue();
+        assertEquals(CARRIER1_ID, termination.carrierId);
+        assertFalse(termination.isMultiSim);
+        assertEquals(TelephonyManager.NETWORK_TYPE_LTE, termination.ratAtEnd);
+        assertTrue(termination.setupFailed);
+        assertEquals(ImsReasonInfo.CODE_REGISTRATION_ERROR, termination.reasonCode);
+        assertEquals(999, termination.extraCode);
+        assertEquals("Timeout", termination.extraMessage);
+
+        ArgumentCaptor<ImsRegistrationStats> statsCaptor =
+                ArgumentCaptor.forClass(ImsRegistrationStats.class);
+        verify(mPersistAtomsStorage).addImsRegistrationStats(statsCaptor.capture());
+        ImsRegistrationStats stats = statsCaptor.getValue();
+        assertEquals(CARRIER1_ID, stats.carrierId);
+        assertEquals(0, stats.simSlotIndex);
+        assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.rat);
+        assertEquals(2000L, stats.unregisteredMillis);
+        verifyNoMoreInteractions(mPersistAtomsStorage);
+    }
+
+    @Test
+    @SmallTest
     public void onImsCapabilitiesChanged_sameTech() throws Exception {
         mImsStats.onSetFeatureResponse(
                 CAPABILITY_TYPE_VOICE,
                 REGISTRATION_TECH_LTE,
                 ProvisioningManager.PROVISIONING_VALUE_ENABLED);
-        mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+        mImsStats.onImsRegistered(mWwanAttributes);
 
         mImsStats.incTimeMillis(2000L);
         mImsStats.onImsCapabilitiesChanged(
@@ -420,7 +575,7 @@
                 CAPABILITY_TYPE_VOICE,
                 REGISTRATION_TECH_LTE,
                 ProvisioningManager.PROVISIONING_VALUE_ENABLED);
-        mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+        mImsStats.onImsRegistered(mWwanAttributes);
         mImsStats.onImsCapabilitiesChanged(
                 REGISTRATION_TECH_LTE, new MmTelCapabilities(CAPABILITY_TYPE_VOICE));
 
@@ -459,7 +614,7 @@
                 CAPABILITY_TYPE_SMS,
                 REGISTRATION_TECH_LTE,
                 ProvisioningManager.PROVISIONING_VALUE_ENABLED);
-        mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+        mImsStats.onImsRegistered(mWwanAttributes);
         mImsStats.onImsCapabilitiesChanged(
                 REGISTRATION_TECH_LTE, new MmTelCapabilities(CAPABILITY_TYPE_SMS));
 
@@ -498,7 +653,7 @@
                 CAPABILITY_TYPE_VOICE,
                 REGISTRATION_TECH_LTE,
                 ProvisioningManager.PROVISIONING_VALUE_ENABLED);
-        mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+        mImsStats.onImsRegistered(mWwanAttributes);
 
         mImsStats.incTimeMillis(2000L);
         mImsStats.onSetFeatureResponse(
@@ -529,11 +684,11 @@
     @Test
     @SmallTest
     public void onImsRegistered_differentTech() throws Exception {
-        mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+        mImsStats.onImsRegistered(mWwanAttributes);
         mImsStats.incTimeMillis(2000L);
-        mImsStats.onImsRegistered(TRANSPORT_TYPE_WLAN);
+        mImsStats.onImsRegistered(mIwlanAttributes);
         mImsStats.incTimeMillis(2000L);
-        mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+        mImsStats.onImsRegistered(mWwanAttributes);
 
         // At this point, the first 2 registrations should have their durations counted
         ArgumentCaptor<ImsRegistrationStats> captor =
@@ -574,7 +729,7 @@
     public void onImsRegistered_afterImsRegistering() throws Exception {
         mImsStats.onImsRegistering(TRANSPORT_TYPE_WWAN);
         mImsStats.incTimeMillis(2000L);
-        mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+        mImsStats.onImsRegistered(mWwanAttributes);
 
         // Registering duration should be counted
         ArgumentCaptor<ImsRegistrationStats> captor =
@@ -591,6 +746,11 @@
     @Test
     @SmallTest
     public void onImsRegistering_afterImsUnregistered() throws Exception {
+        doReturn(TelephonyManager.NETWORK_TYPE_LTE)
+                .when(mServiceState)
+                .getDataNetworkType();
+        mImsStats.onServiceStateChanged(mServiceState);
+
         mImsStats.onImsUnregistered(
                 new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 999, "Timeout"));
         mImsStats.incTimeMillis(2000L);
@@ -615,7 +775,7 @@
         ImsRegistrationStats stats = statsCaptor.getValue();
         assertEquals(CARRIER1_ID, stats.carrierId);
         assertEquals(0, stats.simSlotIndex);
-        assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, stats.rat);
+        assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.rat);
         assertEquals(2000L, stats.unregisteredMillis);
         verifyNoMoreInteractions(mPersistAtomsStorage);
     }
@@ -649,7 +809,7 @@
         mImsStats.onImsUnregistered(
                 new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 999, "Timeout"));
 
-        // Atom with termination info should be generated
+        // Atom with termination info and durations should be generated
         ArgumentCaptor<ImsRegistrationStats> statsCaptor =
                 ArgumentCaptor.forClass(ImsRegistrationStats.class);
         verify(mPersistAtomsStorage).addImsRegistrationStats(statsCaptor.capture());
@@ -677,7 +837,7 @@
     @Test
     @SmallTest
     public void onImsUnregistered_afterRegistered() throws Exception {
-        mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+        mImsStats.onImsRegistered(mWwanAttributes);
         mImsStats.incTimeMillis(2000L);
         mImsStats.onImsUnregistered(
                 new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 999, "Timeout"));
@@ -879,9 +1039,7 @@
         }
     }
 
-    @Test
-    @SmallTest
-    public void onImsUnregistered_multiSim() throws Exception {
+    void secondSimMockSetup() {
         doReturn(mSecondImsPhone).when(mSecondPhone).getImsPhone();
         doReturn(mSecondPhone).when(mSecondImsPhone).getDefaultPhone();
         doReturn(1).when(mSecondPhone).getPhoneId();
@@ -898,8 +1056,14 @@
         // Reusing service state tracker from phone 0 for simplicity
         doReturn(mSST).when(mSecondPhone).getServiceStateTracker();
         doReturn(mSST).when(mSecondImsPhone).getServiceStateTracker();
+    }
+
+    @Test
+    @SmallTest
+    public void onImsUnregistered_multiSim() throws Exception {
+        secondSimMockSetup();
         mImsStats = new TestableImsStats(mSecondImsPhone);
-        mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+        mImsStats.onImsRegistered(mWwanAttributes);
         mImsStats.incTimeMillis(2000L);
         mImsStats.onImsUnregistered(
                 new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 999, "Timeout"));
@@ -937,6 +1101,42 @@
 
     @Test
     @SmallTest
+    public void onImsUnregistered_crossSim() throws Exception {
+        secondSimMockSetup();
+        mImsStats = new TestableImsStats(mSecondImsPhone);
+        mImsStats.onImsRegistered(mCrossSimAttributes);
+        mImsStats.incTimeMillis(2000L);
+        mImsStats.onImsUnregistered(
+                new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 999, "Timeout"));
+
+        // Atom with termination info and durations should be generated
+        ArgumentCaptor<ImsRegistrationStats> statsCaptor =
+                ArgumentCaptor.forClass(ImsRegistrationStats.class);
+        verify(mPersistAtomsStorage).addImsRegistrationStats(statsCaptor.capture());
+        ImsRegistrationStats stats = statsCaptor.getValue();
+        assertEquals(CARRIER2_ID, stats.carrierId);
+        assertEquals(1, stats.simSlotIndex);
+        assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, stats.rat);
+        assertEquals(true, stats.isIwlanCrossSim);
+        assertEquals(2000L, stats.registeredMillis);
+
+        ArgumentCaptor<ImsRegistrationTermination> terminationCaptor =
+                ArgumentCaptor.forClass(ImsRegistrationTermination.class);
+        verify(mPersistAtomsStorage).addImsRegistrationTermination(terminationCaptor.capture());
+        ImsRegistrationTermination termination = terminationCaptor.getValue();
+        assertEquals(CARRIER2_ID, termination.carrierId);
+        assertTrue(termination.isMultiSim);
+        assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, termination.ratAtEnd);
+        assertEquals(true, termination.isIwlanCrossSim);
+        assertFalse(termination.setupFailed);
+        assertEquals(ImsReasonInfo.CODE_REGISTRATION_ERROR, termination.reasonCode);
+        assertEquals(999, termination.extraCode);
+        assertEquals("Timeout", termination.extraMessage);
+        verifyNoMoreInteractions(mPersistAtomsStorage);
+    }
+
+    @Test
+    @SmallTest
     public void getImsVoiceRadioTech_noRegistration() throws Exception {
         // Do nothing
 
@@ -946,7 +1146,7 @@
     @Test
     @SmallTest
     public void getImsVoiceRadioTech_noVoiceRegistration() throws Exception {
-        mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+        mImsStats.onImsRegistered(mWwanAttributes);
         mImsStats.onImsCapabilitiesChanged(
                 REGISTRATION_TECH_LTE, new MmTelCapabilities(CAPABILITY_TYPE_SMS));
 
@@ -956,7 +1156,7 @@
     @Test
     @SmallTest
     public void getImsVoiceRadioTech_cellularRegistration() throws Exception {
-        mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+        mImsStats.onImsRegistered(mWwanAttributes);
         mImsStats.onImsCapabilitiesChanged(
                 REGISTRATION_TECH_LTE, new MmTelCapabilities(CAPABILITY_TYPE_VOICE));
 
@@ -966,7 +1166,7 @@
     @Test
     @SmallTest
     public void getImsVoiceRadioTech_wifiRegistration() throws Exception {
-        mImsStats.onImsRegistered(TRANSPORT_TYPE_WLAN);
+        mImsStats.onImsRegistered(mIwlanAttributes);
         mImsStats.onImsCapabilitiesChanged(
                 REGISTRATION_TECH_IWLAN, new MmTelCapabilities(CAPABILITY_TYPE_VOICE));
 
@@ -975,8 +1175,18 @@
 
     @Test
     @SmallTest
+    public void getImsVoiceRadioTech_crossSimRegistration() throws Exception {
+        mImsStats.onImsRegistered(mCrossSimAttributes);
+        mImsStats.onImsCapabilitiesChanged(
+                REGISTRATION_TECH_CROSS_SIM, new MmTelCapabilities(CAPABILITY_TYPE_VOICE));
+
+        assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, mImsStats.getImsVoiceRadioTech());
+    }
+
+    @Test
+    @SmallTest
     public void getImsVoiceRadioTech_unregistered() throws Exception {
-        mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+        mImsStats.onImsRegistered(mWwanAttributes);
         mImsStats.onImsCapabilitiesChanged(
                 REGISTRATION_TECH_LTE, new MmTelCapabilities(CAPABILITY_TYPE_VOICE));
         mImsStats.onImsUnregistered(
@@ -995,17 +1205,14 @@
     @Test
     @SmallTest
     public void getImsVoiceRadioTech_serviceStateChanged() throws Exception {
-        mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+        mImsStats.onImsRegistered(mWwanAttributes);
         mImsStats.onImsCapabilitiesChanged(
                 REGISTRATION_TECH_LTE, new MmTelCapabilities(CAPABILITY_TYPE_VOICE));
-        doReturn(
-                        new NetworkRegistrationInfo.Builder()
-                                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
-                                .setRegistrationState(
-                                        NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
-                                .build())
+
+        doReturn(TelephonyManager.NETWORK_TYPE_NR)
                 .when(mServiceState)
-                .getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN);
+                .getDataNetworkType();
+
         mImsStats.onServiceStateChanged(mServiceState);
         assertEquals(TelephonyManager.NETWORK_TYPE_NR, mImsStats.getImsVoiceRadioTech());
     }
@@ -1013,17 +1220,14 @@
     @Test
     @SmallTest
     public void getImsVoiceRadioTech_serviceStateChanged_wlan() throws Exception {
-        mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+        mImsStats.onImsRegistered(mWwanAttributes);
         mImsStats.onImsCapabilitiesChanged(
                 REGISTRATION_TECH_IWLAN, new MmTelCapabilities(CAPABILITY_TYPE_VOICE));
-        doReturn(
-                        new NetworkRegistrationInfo.Builder()
-                                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_NR)
-                                .setRegistrationState(
-                                        NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
-                                .build())
+
+        doReturn(TelephonyManager.NETWORK_TYPE_IWLAN)
                 .when(mServiceState)
-                .getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN);
+                .getDataNetworkType();
+
         mImsStats.onServiceStateChanged(mServiceState);
         assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, mImsStats.getImsVoiceRadioTech());
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/PerSimStatusTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/PerSimStatusTest.java
index dc9be1c..e03dfe4 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/PerSimStatusTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/PerSimStatusTest.java
@@ -107,6 +107,7 @@
                 .when(imsMmTelManager1)
                 .getVoWiFiRoamingModeSetting();
         doReturn(false).when(imsMmTelManager1).isVtSettingEnabled();
+        doReturn(true).when(imsMmTelManager1).isCrossSimCallingEnabled();
         doReturn(false).when(mPhone).getDataRoamingEnabled();
         doReturn(1L)
                 .when(mPhone)
@@ -152,6 +153,7 @@
                 .when(imsMmTelManager2)
                 .getVoWiFiRoamingModeSetting();
         doReturn(true).when(imsMmTelManager2).isVtSettingEnabled();
+        doReturn(false).when(imsMmTelManager2).isCrossSimCallingEnabled();
         doReturn(false).when(mSecondPhone).getDataRoamingEnabled();
         doReturn(1L)
                 .when(mSecondPhone)
@@ -191,6 +193,8 @@
                 perSimStatus1.minimumVoltageClass);
         assertEquals(NETWORK_TYPE_BITMASK_GSM, perSimStatus1.unmeteredNetworks);
         assertEquals(false, perSimStatus1.vonrEnabled);
+        assertEquals(true, perSimStatus1.crossSimCallingEnabled);
+
         assertEquals(101, perSimStatus2.carrierId);
         assertEquals(1, perSimStatus2.phoneNumberSourceUicc);
         assertEquals(2, perSimStatus2.phoneNumberSourceCarrier);
@@ -210,6 +214,7 @@
                 perSimStatus2.minimumVoltageClass);
         assertEquals(NETWORK_TYPE_BITMASK_GSM, perSimStatus2.unmeteredNetworks);
         assertEquals(false, perSimStatus2.vonrEnabled);
+        assertEquals(false, perSimStatus2.crossSimCallingEnabled);
     }
 
     @Test
@@ -279,6 +284,7 @@
                 perSimStatus.minimumVoltageClass);
         assertEquals(NETWORK_TYPE_BITMASK_GSM, perSimStatus.unmeteredNetworks);
         assertEquals(true, perSimStatus.vonrEnabled);
+        assertEquals(false, perSimStatus.crossSimCallingEnabled);
     }
 
     @Test
@@ -340,6 +346,7 @@
                 perSimStatus.minimumVoltageClass);
         assertEquals(NETWORK_TYPE_BITMASK_GSM, perSimStatus.unmeteredNetworks);
         assertEquals(true, perSimStatus.vonrEnabled);
+        assertEquals(false, perSimStatus.crossSimCallingEnabled);
     }
 
     @Test
@@ -397,5 +404,6 @@
                 perSimStatus.minimumVoltageClass);
         assertEquals(NETWORK_TYPE_BITMASK_GSM, perSimStatus.unmeteredNetworks);
         assertEquals(true, perSimStatus.vonrEnabled);
+        assertEquals(false, perSimStatus.crossSimCallingEnabled);
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java
index 2509b8c..c66cfa7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java
@@ -44,6 +44,7 @@
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.internal.telephony.Phone;
@@ -401,6 +402,30 @@
 
     @Test
     @SmallTest
+    public void onImsVoiceRegistrationChanged_crossSimCalling() throws Exception {
+        mServiceStateStats.onServiceStateChanged(mServiceState);
+        mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+        doReturn(TelephonyManager.NETWORK_TYPE_IWLAN).when(mImsStats).getImsVoiceRadioTech();
+        doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM).when(mImsPhone)
+                .getImsRegistrationTech();
+        mServiceStateStats.incTimeMillis(100L);
+        mServiceStateStats.onImsVoiceRegistrationChanged();
+        mServiceStateStats.incTimeMillis(200L);
+        mServiceStateStats.conclude();
+
+        ArgumentCaptor<CellularServiceState> captor =
+                ArgumentCaptor.forClass(CellularServiceState.class);
+        verify(mPersistAtomsStorage, times(2))
+                .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+        CellularServiceState state = captor.getAllValues().get(1);
+
+        assertEquals(200L, state.totalTimeMillis);
+        assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, state.voiceRat);
+        assertTrue(state.isIwlanCrossSim);
+    }
+
+    @Test
+    @SmallTest
     public void onInternetDataNetworkDisconnected() throws Exception {
          // Using default service state for LTE
         mServiceStateStats.onServiceStateChanged(mServiceState);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
index 5412992..00ba0f8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
@@ -56,6 +56,7 @@
 import android.telephony.data.ApnSetting;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.ImsStreamMediaProfile;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.internal.telephony.Call;
@@ -196,6 +197,7 @@
 
         doReturn(PhoneConstants.PHONE_TYPE_IMS).when(mImsConnection0).getPhoneType();
         doReturn(false).when(mImsConnection0).isEmergencyCall();
+        doReturn(false).when(mImsConnection0).isCrossSimCall();
         doReturn(PhoneConstants.PHONE_TYPE_IMS).when(mImsConnection1).getPhoneType();
         doReturn(false).when(mImsConnection1).isEmergencyCall();
         doReturn(PhoneConstants.PHONE_TYPE_GSM).when(mGsmConnection0).getPhoneType();
@@ -203,6 +205,9 @@
         doReturn(PhoneConstants.PHONE_TYPE_GSM).when(mGsmConnection1).getPhoneType();
         doReturn(false).when(mGsmConnection1).isEmergencyCall();
 
+        doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_LTE).when(mImsPhone)
+                .getImsRegistrationTech();
+
         if (Looper.myLooper() == null) {
             Looper.prepare();
         }
@@ -241,7 +246,6 @@
                         ImsReasonInfo.CODE_REMOTE_CALL_DECLINE);
         expectedCall.setupDurationMillis = 200;
         expectedCall.setupFailed = true;
-        expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
         expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_EVS_SWB;
         expectedCall.mainCodecQuality =
                 VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_SUPER_WIDEBAND;
@@ -1519,6 +1523,75 @@
 
     @Test
     @SmallTest
+    public void singleCrossSimCall_moAccepted() {
+        setServiceStateWithWifiCalling(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+        doReturn(mImsPhone).when(mPhone).getImsPhone();
+        doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM).when(mImsPhone)
+                .getImsRegistrationTech();
+        doReturn(TelephonyManager.NETWORK_TYPE_IWLAN).when(mImsStats).getImsVoiceRadioTech();
+        doReturn(false).when(mImsConnection0).isIncoming();
+        doReturn(true).when(mImsConnection0).isCrossSimCall();
+        doReturn(2000L).when(mImsConnection0).getCreateTime();
+        doReturn(1000L).when(mImsConnection0).getDurationMillis();
+        doReturn(mImsCall0).when(mImsConnection0).getCall();
+        doReturn(new ArrayList(List.of(mImsConnection0))).when(mImsCall0).getConnections();
+        VoiceCallSession expectedCall =
+                makeSlot0CallProto(
+                        VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS,
+                        VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO,
+                        TelephonyManager.NETWORK_TYPE_IWLAN,
+                        ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE);
+        expectedCall.isIwlanCrossSimAtStart = true;
+        expectedCall.isIwlanCrossSimAtEnd = true;
+        expectedCall.isIwlanCrossSimAtConnected = true;
+
+        expectedCall.bandAtEnd = 0; // not configured for IWLAN
+        expectedCall.setupDurationMillis = 200;
+        expectedCall.setupFailed = false;
+        expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_EVS_SWB;
+        expectedCall.mainCodecQuality =
+                VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_SUPER_WIDEBAND;
+        expectedCall.disconnectExtraMessage = "normal call clearing";
+        expectedCall.callDuration =
+                VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_ONE_MINUTE;
+        VoiceCallRatUsage expectedRatUsage =
+                makeRatUsageProto(
+                        CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_IWLAN, 2000L, 100000L, 1L);
+        final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+
+        mVoiceCallSessionStats0.setTimeMillis(2000L);
+        doReturn(Call.State.DIALING).when(mImsCall0).getState();
+        doReturn(Call.State.DIALING).when(mImsConnection0).getState();
+        mVoiceCallSessionStats0.onImsDial(mImsConnection0);
+        mVoiceCallSessionStats0.setTimeMillis(2100L);
+        mVoiceCallSessionStats0.onAudioCodecChanged(
+                mImsConnection0, ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB);
+        mVoiceCallSessionStats0.setTimeMillis(2200L);
+        doReturn(Call.State.ALERTING).when(mImsCall0).getState();
+        doReturn(Call.State.ALERTING).when(mImsConnection0).getState();
+        mVoiceCallSessionStats0.onCallStateChanged(mImsCall0);
+        mVoiceCallSessionStats0.setTimeMillis(12000L);
+        doReturn(Call.State.ACTIVE).when(mImsCall0).getState();
+        doReturn(Call.State.ACTIVE).when(mImsConnection0).getState();
+        mVoiceCallSessionStats0.onCallStateChanged(mImsCall0);
+        mVoiceCallSessionStats0.setTimeMillis(100000L);
+        mVoiceCallSessionStats0.onImsCallTerminated(
+                mImsConnection0,
+                new ImsReasonInfo(
+                        ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE, 0, "normal call clearing"));
+
+        ArgumentCaptor<VoiceCallSession> callCaptor =
+                ArgumentCaptor.forClass(VoiceCallSession.class);
+        verify(mPersistAtomsStorage, times(1)).addVoiceCallSession(callCaptor.capture());
+        verify(mPersistAtomsStorage, times(1)).addVoiceCallRatUsage(any());
+        verifyNoMoreInteractions(mPersistAtomsStorage);
+        assertProtoEquals(expectedCall, callCaptor.getValue());
+        assertThat(ratUsage.get()).hasLength(1);
+        assertProtoEquals(expectedRatUsage, ratUsage.get()[0]);
+    }
+
+    @Test
+    @SmallTest
     public void singleCsCall_moRejected() {
         doReturn(false).when(mGsmConnection0).isIncoming();
         doReturn(2000L).when(mGsmConnection0).getCreateTime();
@@ -2518,6 +2591,8 @@
     public void singleWifiCall_preferred() {
         setServiceStateWithWifiCalling(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
         doReturn(mImsPhone).when(mPhone).getImsPhone();
+        doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN).when(mImsPhone)
+                .getImsRegistrationTech();
         doReturn(TelephonyManager.NETWORK_TYPE_IWLAN).when(mImsStats).getImsVoiceRadioTech();
         doReturn(true).when(mImsConnection0).isIncoming();
         doReturn(2000L).when(mImsConnection0).getCreateTime();
@@ -2566,6 +2641,8 @@
     public void singleWifiCall_airPlaneMode() {
         setServiceStateWithWifiCalling(mServiceState, TelephonyManager.NETWORK_TYPE_UNKNOWN);
         doReturn(mImsPhone).when(mPhone).getImsPhone();
+        doReturn(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN).when(mImsPhone)
+                .getImsRegistrationTech();
         doReturn(TelephonyManager.NETWORK_TYPE_IWLAN).when(mImsStats).getImsVoiceRadioTech();
         doReturn(true).when(mImsConnection0).isIncoming();
         doReturn(2000L).when(mImsConnection0).getCreateTime();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
index 0a1ab02..fd5f6ab 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
@@ -428,7 +428,7 @@
         processAllMessages();
         // As modem is busy receiving datagrams, sending datagram did not proceed further.
         mInOrder.verify(mMockDatagramController).needsWaitingForSatelliteConnected();
-        mInOrder.verify(mMockDatagramController).isPollingInIdleState();
+        mInOrder.verify(mMockDatagramController, times(2)).isPollingInIdleState();
         verifyNoMoreInteractions(mMockDatagramController);
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
index 94f56b4..0e16e25 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
@@ -23,6 +23,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.anyInt;
@@ -38,6 +39,7 @@
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.os.AsyncResult;
 import android.os.IBinder;
@@ -66,6 +68,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidTestingRunner.class)
@@ -272,10 +275,13 @@
 
     @Test
     public void testSatelliteDatagramReceived_success_zeroPendingCount() {
+        TestSatelliteDatagramCallback testSatelliteDatagramCallback =
+                new TestSatelliteDatagramCallback();
+
+        mSatelliteDatagramListenerHandler.addListener(testSatelliteDatagramCallback);
         mSatelliteDatagramListenerHandler.obtainMessage(1 /*EVENT_SATELLITE_DATAGRAM_RECEIVED*/,
                         new AsyncResult(null, new Pair<>(mDatagram, 0), null))
                 .sendToTarget();
-
         processAllMessages();
 
         mInOrder.verify(mMockDatagramController)
@@ -286,6 +292,14 @@
                 .updateReceiveStatus(eq(SUB_ID),
                         eq(SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE),
                         eq(0), eq(SatelliteManager.SATELLITE_RESULT_SUCCESS));
+        assertTrue(testSatelliteDatagramCallback.waitForOnSatelliteDatagramReceived());
+
+        assertTrue(testSatelliteDatagramCallback.sendInternalAck());
+        try {
+            processAllFutureMessages();
+        } catch (Exception e) {
+            fail("Unexpected exception e=" + e);
+        }
     }
 
     @Test
@@ -475,4 +489,57 @@
             mLong = duration;
         }
     }
+
+    private static class TestSatelliteDatagramCallback extends ISatelliteDatagramCallback.Stub {
+        @Nullable private IVoidConsumer mInternalAck;
+        private final Semaphore mSemaphore = new Semaphore(0);
+
+        @Override
+        public void onSatelliteDatagramReceived(long datagramId,
+                @NonNull SatelliteDatagram datagram, int pendingCount,
+                @NonNull IVoidConsumer internalAck) {
+            logd("onSatelliteDatagramReceived");
+            mInternalAck = internalAck;
+            try {
+                internalAck.accept();
+            } catch (RemoteException e) {
+                logd("onSatelliteDatagramReceived: accept e=" + e);
+                return;
+            }
+
+            try {
+                mSemaphore.release();
+            } catch (Exception e) {
+                logd("onSatelliteDatagramReceived: release e=" + e);
+            }
+        }
+
+        public boolean waitForOnSatelliteDatagramReceived() {
+            logd("waitForOnSatelliteDatagramReceived");
+            try {
+                if (!mSemaphore.tryAcquire(1000, TimeUnit.MILLISECONDS)) {
+                    logd("Timed out to receive onSatelliteDatagramReceived");
+                    return false;
+                }
+            } catch (InterruptedException e) {
+                logd("waitForOnSatelliteDatagramReceived: e=" + e);
+                return false;
+            }
+            return true;
+        }
+
+        public boolean sendInternalAck() {
+            if (mInternalAck == null) {
+                logd("sendInternalAck: mInternalAck is null");
+                return false;
+            }
+            try {
+                mInternalAck.accept();
+            } catch (RemoteException e) {
+                logd("sendInternalAck: accept e=" + e);
+                return false;
+            }
+            return true;
+        }
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
index e4e2434..eecaf90 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java
@@ -766,13 +766,13 @@
 
         // Should get an empty list without READ_PHONE_STATE.
         assertThat(mSubscriptionManagerServiceUT.getActiveSubscriptionInfoList(
-                CALLING_PACKAGE, CALLING_FEATURE)).isEmpty();
+                CALLING_PACKAGE, CALLING_FEATURE, true)).isEmpty();
 
         // Grant READ_PHONE_STATE permission for insertion.
         mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE);
 
         List<SubscriptionInfo> subInfos = mSubscriptionManagerServiceUT
-                .getActiveSubscriptionInfoList(CALLING_PACKAGE, CALLING_FEATURE);
+                .getActiveSubscriptionInfoList(CALLING_PACKAGE, CALLING_FEATURE, true);
         assertThat(subInfos).hasSize(1);
         assertThat(subInfos.get(0).getIccId()).isEmpty();
         assertThat(subInfos.get(0).getCardString()).isEmpty();
@@ -783,7 +783,7 @@
         setCarrierPrivilegesForSubId(true, 1);
 
         subInfos = mSubscriptionManagerServiceUT
-                .getActiveSubscriptionInfoList(CALLING_PACKAGE, CALLING_FEATURE);
+                .getActiveSubscriptionInfoList(CALLING_PACKAGE, CALLING_FEATURE, true);
         assertThat(subInfos).hasSize(1);
         assertThat(subInfos.get(0)).isEqualTo(FAKE_SUBSCRIPTION_INFO1.toSubscriptionInfo());
     }
@@ -963,11 +963,11 @@
 
         // Should fail without READ_PHONE_STATE
         assertThrows(SecurityException.class, () -> mSubscriptionManagerServiceUT
-                .getActiveSubInfoCount(CALLING_PACKAGE, CALLING_FEATURE));
+                .getActiveSubInfoCount(CALLING_PACKAGE, CALLING_FEATURE, true));
 
         mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
         assertThat(mSubscriptionManagerServiceUT.getActiveSubInfoCount(
-                CALLING_PACKAGE, CALLING_FEATURE)).isEqualTo(2);
+                CALLING_PACKAGE, CALLING_FEATURE, true)).isEqualTo(2);
     }
 
     @Test
@@ -1252,7 +1252,7 @@
                 .isEqualTo(new int[]{subId1, subId2});
         // Test get getActiveSubInfoCount
         assertThat(mSubscriptionManagerServiceUT.getActiveSubInfoCount(
-                CALLING_PACKAGE, CALLING_FEATURE)).isEqualTo(1);
+                CALLING_PACKAGE, CALLING_FEATURE, false)).isEqualTo(1);
         // Test getActiveSubscriptionInfo
         assertThat(mSubscriptionManagerServiceUT.getActiveSubscriptionInfo(
                 subId1, CALLING_PACKAGE, CALLING_FEATURE).getSubscriptionId()).isEqualTo(subId1);
@@ -1274,7 +1274,8 @@
                 .isEqualTo(subId2);
         // Test getActiveSubscriptionInfoList
         assertThat(mSubscriptionManagerServiceUT.getActiveSubscriptionInfoList(
-                CALLING_PACKAGE, CALLING_FEATURE).stream().map(SubscriptionInfo::getSubscriptionId)
+                CALLING_PACKAGE, CALLING_FEATURE, false)
+                .stream().map(SubscriptionInfo::getSubscriptionId)
                 .toList()).isEqualTo(List.of(subId1));
         // Test getAllSubInfoList
         assertThat(mSubscriptionManagerServiceUT.getAllSubInfoList(CALLING_PACKAGE,
@@ -1368,7 +1369,7 @@
                 .isEqualTo(new int[]{subId1, subId2});
         // Test get getActiveSubInfoCount
         assertThat(mSubscriptionManagerServiceUT.getActiveSubInfoCount(
-                CALLING_PACKAGE, CALLING_FEATURE)).isEqualTo(1);
+                CALLING_PACKAGE, CALLING_FEATURE, false)).isEqualTo(1);
         // Test getActiveSubscriptionInfo
         assertThat(mSubscriptionManagerServiceUT.getActiveSubscriptionInfo(
                 subId1, CALLING_PACKAGE, CALLING_FEATURE).getSubscriptionId()).isEqualTo(subId1);
@@ -1390,7 +1391,7 @@
                 .isEqualTo(subId2);
         // Test getActiveSubscriptionInfoList
         assertThat(mSubscriptionManagerServiceUT.getActiveSubscriptionInfoList(
-                        CALLING_PACKAGE, CALLING_FEATURE).stream()
+                        CALLING_PACKAGE, CALLING_FEATURE, false).stream()
                 .map(SubscriptionInfo::getSubscriptionId)
                 .toList()).isEqualTo(List.of(subId1));
         // Test getAllSubInfoList
@@ -2151,7 +2152,7 @@
         assertThat(mSubscriptionManagerServiceUT.getAllSubInfoList(
                 CALLING_PACKAGE, CALLING_FEATURE).isEmpty()).isTrue();
         assertThat(mSubscriptionManagerServiceUT.getActiveSubscriptionInfoList(
-                CALLING_PACKAGE, CALLING_FEATURE)).isEmpty();
+                CALLING_PACKAGE, CALLING_FEATURE, true)).isEmpty();
     }
 
     @Test
@@ -2357,7 +2358,7 @@
         verify(mEuiccController).blockingGetEuiccProfileInfoList(eq(1));
 
         List<SubscriptionInfo> subInfoList = mSubscriptionManagerServiceUT
-                .getActiveSubscriptionInfoList(CALLING_PACKAGE, CALLING_FEATURE);
+                .getActiveSubscriptionInfoList(CALLING_PACKAGE, CALLING_FEATURE, true);
         assertThat(subInfoList).hasSize(1);
         assertThat(subInfoList.get(0).getSimSlotIndex()).isEqualTo(1);
         assertThat(subInfoList.get(0).getSubscriptionId()).isEqualTo(1);
@@ -2475,7 +2476,7 @@
         processAllMessages();
 
         List<SubscriptionInfo> subInfoList = mSubscriptionManagerServiceUT
-                .getActiveSubscriptionInfoList(CALLING_PACKAGE, CALLING_FEATURE);
+                .getActiveSubscriptionInfoList(CALLING_PACKAGE, CALLING_FEATURE, true);
 
         assertThat(subInfoList).hasSize(1);
         assertThat(subInfoList.get(0).isActive()).isTrue();
@@ -2595,16 +2596,17 @@
                 CALLING_PACKAGE, CALLING_FEATURE)).isEmpty();
         assertThat(mSubscriptionManagerServiceUT.getActiveSubIdList(false)).isEmpty();
         assertThat(mSubscriptionManagerServiceUT.getActiveSubscriptionInfoList(
-                CALLING_PACKAGE, CALLING_FEATURE)).isEmpty();
+                CALLING_PACKAGE, CALLING_FEATURE, true)).isEmpty();
 
         setIdentifierAccess(true);
         mSubscriptionManagerServiceUT.addSubInfo(FAKE_MAC_ADDRESS2, FAKE_CARRIER_NAME2,
                 0, SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM);
         assertThat(mSubscriptionManagerServiceUT.getActiveSubIdList(false)).isNotEmpty();
         assertThat(mSubscriptionManagerServiceUT.getActiveSubscriptionInfoList(
-                CALLING_PACKAGE, CALLING_FEATURE)).isNotEmpty();
+                CALLING_PACKAGE, CALLING_FEATURE, true)).isNotEmpty();
         assertThat(mSubscriptionManagerServiceUT.getActiveSubscriptionInfoList(
-                CALLING_PACKAGE, CALLING_FEATURE).get(0).getIccId()).isEqualTo(FAKE_MAC_ADDRESS2);
+                CALLING_PACKAGE, CALLING_FEATURE, true).get(0).getIccId())
+                .isEqualTo(FAKE_MAC_ADDRESS2);
     }
 
     @Test