Merge "Implement HAL updates for DSDA support in Telephony." 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 befb38d..267048e 100644
--- a/flags/data.aconfig
+++ b/flags/data.aconfig
@@ -1,6 +1,34 @@
 package: "com.android.internal.telephony.flags"
 
 flag {
+  name: "auto_switch_allow_roaming"
+  namespace: "telephony"
+  description: "Allow using roaming network as target if user allows it from settings."
+  bug: "306488039"
+}
+
+flag {
+  name: "auto_data_switch_rat_ss"
+  namespace: "telephony"
+  description: "Whether switch for better rat and signal strength"
+  bug:"260928808"
+}
+
+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."
@@ -49,3 +77,16 @@
   bug:"286171724"
 }
 
+flag {
+ name: "notify_data_activity_changed_with_slot"
+  namespace: "telephony"
+  description: "notify data activity changed for slot id"
+  bug: "309896936"
+}
+
+flag {
+  name: "vonr_enabled_metric"
+  namespace: "telephony"
+  description: "Collect vonr status in voice call metric"
+  bug:"288449751"
+}
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/ims.aconfig b/flags/ims.aconfig
index 0fa1f1e..92d003d 100644
--- a/flags/ims.aconfig
+++ b/flags/ims.aconfig
@@ -27,3 +27,24 @@
     description: "This flag is created to prevent unnecessary updates when multiple provisioning items to update ims service are changed."
     bug:"302281114"
 }
+
+flag {
+    name: "add_rat_related_suggested_action_to_ims_registration"
+    namespace: "telephony"
+    description: "This flag is for adding suggested actions related to RAT to ims registration"
+    bug:"290573256"
+}
+
+flag {
+    name: "terminate_active_video_call_when_accepting_second_video_call_as_audio_only"
+    namespace: "telephony"
+    description: "This flag terminates active video call instead holding when accepting 2nd incoming video call as audio only"
+    bug:"309548300"
+}
+
+flag {
+    name: "emergency_registration_state"
+    namespace: "telephony"
+    description: "This flag is created to notify emergency registration state changed."
+    bug:"312101946"
+}
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..ba9772b 100644
--- a/flags/misc.aconfig
+++ b/flags/misc.aconfig
@@ -51,8 +51,15 @@
 }
 
 flag {
-  name: "load_dds_on_create"
+  name: "enable_telephony_analytics"
   namespace: "telephony"
-  description: "Load default data subid on create in PhoneGlobals."
-  bug: "310591561"
+  description: "Enable Telephony Analytics information of Service State , Sms and Call scenarios"
+  bug: "309896524"
+}
+
+flag {
+  name: "show_call_id_and_call_waiting_in_additional_settings_menu"
+  namespace: "telephony"
+  description: "Expose carrier config KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL and KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL."
+  bug: "310264981"
 }
diff --git a/flags/network.aconfig b/flags/network.aconfig
index 299a2a0..27489f4 100644
--- a/flags/network.aconfig
+++ b/flags/network.aconfig
@@ -13,3 +13,10 @@
   description: "Allow carriers to hide the roaming (R) icon when roaming."
   bug: "301467052"
 }
+
+flag {
+  name: "enable_identifier_disclosure_transparency"
+  namespace: "telephony"
+  description: "Allows the framework to register for CellularIdentifierDisclosure events and emit notifications to the user about them"
+  bug: "276752426"
+}
diff --git a/flags/telephony.aconfig b/flags/telephony.aconfig
index f60885b..b849d53 100644
--- a/flags/telephony.aconfig
+++ b/flags/telephony.aconfig
@@ -5,4 +5,11 @@
     namespace: "telephony"
     description: "This flag controls telephony feature flags mapping."
     bug:"297989574"
-}
\ No newline at end of file
+}
+
+flag {
+    name: "enforce_telephony_feature_mapping_for_public_apis"
+    namespace: "telephony"
+    description: "This flag controls telephony feature flags mapping for public APIs and CTS."
+    bug:"297989574"
+}
diff --git a/flags/uicc.aconfig b/flags/uicc.aconfig
index 3ef2301..b2024b0 100644
--- a/flags/uicc.aconfig
+++ b/flags/uicc.aconfig
@@ -11,4 +11,10 @@
     namespace: "telephony"
     description: "This flag controls to download the IMSI encryption keys after user unlocks the phone."
     bug:"303780982"
+}
+flag {
+    name: "carrier_restriction_status"
+    namespace: "telephony"
+    description: "This flag control the visibility of the getCarrierRestrictionStatus in carrierRestrictionRules class."
+    bug:"313553044"
 }
\ No newline at end of file
diff --git a/proto/src/persist_atoms.proto b/proto/src/persist_atoms.proto
index 537f824..d7a6062 100644
--- a/proto/src/persist_atoms.proto
+++ b/proto/src/persist_atoms.proto
@@ -277,6 +277,10 @@
     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;
+    optional bool vonr_enabled = 40;
 
     // Internal use only
     optional int64 setup_begin_millis = 10001;
@@ -365,6 +369,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 +387,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 +414,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 +436,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/BaseCommands.java b/src/java/com/android/internal/telephony/BaseCommands.java
index 3f3d297..b33b732 100644
--- a/src/java/com/android/internal/telephony/BaseCommands.java
+++ b/src/java/com/android/internal/telephony/BaseCommands.java
@@ -119,6 +119,7 @@
     protected RegistrantList mNotifyAnbrRegistrants = new RegistrantList();
     protected RegistrantList mTriggerImsDeregistrationRegistrants = new RegistrantList();
     protected RegistrantList mImeiInfoRegistrants = new RegistrantList();
+    protected RegistrantList mCellularIdentifierDisclosedRegistrants = new RegistrantList();
 
     @UnsupportedAppUsage
     protected Registrant mGsmSmsRegistrant;
@@ -1183,4 +1184,14 @@
     public void registerForImeiMappingChanged(Handler h, int what, Object obj) {
         mImeiInfoRegistrants.add(h, what, obj);
     }
+
+    @Override
+    public void registerForCellularIdentifierDisclosures(Handler h, int what, Object obj) {
+        mCellularIdentifierDisclosedRegistrants.add(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForCellularIdentifierDisclosures(Handler h) {
+        mCellularIdentifierDisclosedRegistrants.remove(h);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java b/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
index ab7ebc4..67be1b6 100644
--- a/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
@@ -560,7 +560,7 @@
     }
 
     private void updateCertsForPackage(@NonNull PackageInfo pkg) {
-        Set<String> certs = new ArraySet<>();
+        Set<String> certs = new ArraySet<>(1);
         List<Signature> signatures = UiccAccessRule.getSignatures(pkg);
         for (Signature signature : signatures) {
             byte[] sha1 = UiccAccessRule.getCertHash(signature, SHA_1);
@@ -773,7 +773,7 @@
             return mCachedUids.get(pkgName);
         }
 
-        Set<Integer> uids = new ArraySet<>();
+        Set<Integer> uids = new ArraySet<>(1);
         List<UserInfo> users = mUserManager.getUsers();
         for (UserInfo user : users) {
             int userId = user.getUserHandle().getIdentifier();
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index 4b977ff..91e6fab 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -2912,4 +2912,32 @@
      * @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) {}
+
+    /**
+     * Registers for cellular identifier disclosure events.
+     */
+    default void registerForCellularIdentifierDisclosures(
+            @NonNull Handler h, int what, @Nullable Object obj) {}
+
+    /**
+     * Unregisters for cellular identifier disclosure events.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    default void unregisterForCellularIdentifierDisclosures(@NonNull Handler h) {}
+}
diff --git a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index c9db985..c035329 100644
--- a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -41,6 +41,7 @@
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.MediaQualityStatus;
 
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.telephony.Rlog;
 
 import java.util.List;
@@ -55,10 +56,15 @@
 
     private TelephonyRegistryManager mTelephonyRegistryMgr;
 
+    /** Feature flags */
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
 
-    public DefaultPhoneNotifier(Context context) {
+
+    public DefaultPhoneNotifier(Context context, @NonNull FeatureFlags featureFlags) {
         mTelephonyRegistryMgr = (TelephonyRegistryManager) context.getSystemService(
             Context.TELEPHONY_REGISTRY_SERVICE);
+        mFeatureFlags = featureFlags;
     }
 
     @Override
@@ -125,10 +131,16 @@
 
     @Override
     public void notifyDataActivity(Phone sender) {
-        int phoneId = sender.getPhoneId();
+
         int subId = sender.getSubId();
-        mTelephonyRegistryMgr.notifyDataActivityChanged(phoneId, subId,
-                sender.getDataActivityState());
+
+        if (mFeatureFlags.notifyDataActivityChangedWithSlot()) {
+            int phoneId = sender.getPhoneId();
+            mTelephonyRegistryMgr.notifyDataActivityChanged(phoneId, subId,
+                    sender.getDataActivityState());
+        } else {
+            mTelephonyRegistryMgr.notifyDataActivityChanged(subId, sender.getDataActivityState());
+        }
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index d912187..620b871 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -80,6 +80,7 @@
 import android.telephony.CarrierConfigManager;
 import android.telephony.CellBroadcastIdRange;
 import android.telephony.CellIdentity;
+import android.telephony.CellularIdentifierDisclosure;
 import android.telephony.ImsiEncryptionInfo;
 import android.telephony.LinkCapacityEstimate;
 import android.telephony.NetworkScanRequest;
@@ -116,6 +117,7 @@
 import com.android.internal.telephony.imsphone.ImsPhoneMmiCode;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.metrics.VoiceCallSessionStats;
+import com.android.internal.telephony.security.CellularIdentifierDisclosureNotifier;
 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
 import com.android.internal.telephony.subscription.SubscriptionManagerService.SubscriptionManagerServiceCallback;
 import com.android.internal.telephony.test.SimulatedRadioControl;
@@ -252,6 +254,7 @@
             CellBroadcastConfigTracker.make(this, null, true);
 
     private boolean mIsNullCipherAndIntegritySupported = false;
+    private boolean mIsIdentifierDisclosureTransparencySupported = false;
 
     // Create Cfu (Call forward unconditional) so that dialing number &
     // mOnComplete (Message object passed by client) can be packed &
@@ -298,6 +301,8 @@
     private final SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionsChangedListener;
     private final CallWaitingController mCallWaitingController;
 
+    private CellularIdentifierDisclosureNotifier mIdentifierDisclosureNotifier;
+
     // Set via Carrier Config
     private boolean mIsN1ModeAllowedByCarrier = true;
     // Set via a call to the method on Phone; the only caller is IMS, and all of this code will
@@ -337,7 +342,7 @@
 
         // phone type needs to be set before other initialization as other objects rely on it
         mPrecisePhoneType = precisePhoneType;
-        mVoiceCallSessionStats = new VoiceCallSessionStats(mPhoneId, this);
+        mVoiceCallSessionStats = new VoiceCallSessionStats(mPhoneId, this, featureFlags);
         mImsManagerFactory = imsManagerFactory;
         initOnce(ci);
         initRatSpecific(precisePhoneType);
@@ -515,6 +520,20 @@
         mCIM = new CarrierInfoManager();
 
         mCi.registerForImeiMappingChanged(this, EVENT_IMEI_MAPPING_CHANGED, null);
+
+        if (mFeatureFlags.enableIdentifierDisclosureTransparency()) {
+            logi(
+                    "enable_identifier_disclosure_transparency is on. Registering for cellular "
+                            + "identifier disclosures from phone "
+                            + getPhoneId());
+            mIdentifierDisclosureNotifier =
+                    mTelephonyComponentFactory
+                            .inject(CellularIdentifierDisclosureNotifier.class.getName())
+                            .makeIdentifierDisclosureNotifier();
+            mCi.registerForCellularIdentifierDisclosures(
+                    this, EVENT_CELL_IDENTIFIER_DISCLOSURE, null);
+        }
+
         initializeCarrierApps();
     }
 
@@ -3126,6 +3145,7 @@
         mCi.areUiccApplicationsEnabled(obtainMessage(EVENT_GET_UICC_APPS_ENABLEMENT_DONE));
 
         handleNullCipherEnabledChange();
+        handleIdentifierDisclosureNotificationPreferenceChange();
     }
 
     private void handleRadioOn() {
@@ -3592,14 +3612,7 @@
             case EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE:
                 logd("EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE");
                 ar = (AsyncResult) msg.obj;
-                // Only test for a success here in order to flip the support flag.
-                // Testing for the negative case, e.g. REQUEST_NOT_SUPPORTED, is insufficient
-                // because the modem or the RIL could still return exceptions for temporary
-                // failures even when the feature is unsupported.
-                if (ar == null || ar.exception == null) {
-                    mIsNullCipherAndIntegritySupported = true;
-                    return;
-                }
+                mIsNullCipherAndIntegritySupported = doesResultIndicateModemSupport(ar);
                 break;
 
             case EVENT_IMS_DEREGISTRATION_TRIGGERED:
@@ -3663,11 +3676,45 @@
                 parseImeiInfo(msg);
                 break;
 
+            case EVENT_CELL_IDENTIFIER_DISCLOSURE:
+                logd("EVENT_CELL_IDENTIFIER_DISCLOSURE phoneId = " + getPhoneId());
+
+                ar = (AsyncResult) msg.obj;
+                if (ar == null || ar.result == null || ar.exception != null) {
+                    Rlog.e(
+                            LOG_TAG,
+                            "Failed to process cellular identifier disclosure",
+                            ar.exception);
+                    break;
+                }
+
+                CellularIdentifierDisclosure disclosure = (CellularIdentifierDisclosure) ar.result;
+                if (mFeatureFlags.enableIdentifierDisclosureTransparency()
+                        && mIdentifierDisclosureNotifier != null
+                        && disclosure != null) {
+                    mIdentifierDisclosureNotifier.addDisclosure(disclosure);
+                }
+                break;
+
+            case EVENT_SET_IDENTIFIER_DISCLOSURE_ENABLED_DONE:
+                logd("EVENT_SET_IDENTIFIER_DISCLOSURE_ENABLED_DONE");
+                ar = (AsyncResult) msg.obj;
+                mIsIdentifierDisclosureTransparencySupported = doesResultIndicateModemSupport(ar);
+                break;
+
             default:
                 super.handleMessage(msg);
         }
     }
 
+    private boolean doesResultIndicateModemSupport(AsyncResult ar) {
+        // We can only say that the modem supports a call without ambiguity if there
+        // is no exception set on the response.  Testing for REQUEST_NOT_SUPPORTED, is
+        // insufficient because the modem or the RIL could still return exceptions for temporary
+        // failures even when the feature is unsupported.
+        return (ar == null || ar.exception == null);
+    }
+
     private void parseImeiInfo(Message msg) {
         AsyncResult ar = (AsyncResult)msg.obj;
         if (ar.exception != null || ar.result == null) {
@@ -5260,7 +5307,31 @@
     }
 
     @Override
+    public void handleIdentifierDisclosureNotificationPreferenceChange() {
+        if (!mFeatureFlags.enableIdentifierDisclosureTransparency()) {
+            logi("Not handling identifier disclosure preference change. Feature flag "
+                    + "ENABLE_IDENTIFIER_DISCLOSURE_TRANSPARENCY disabled");
+            return;
+        }
+        boolean prefEnabled = getIdentifierDisclosureNotificationsPreferenceEnabled();
+
+        if (prefEnabled) {
+            mIdentifierDisclosureNotifier.enable();
+        } else {
+            mIdentifierDisclosureNotifier.disable();
+        }
+
+        mCi.setCellularIdentifierTransparencyEnabled(prefEnabled,
+                obtainMessage(EVENT_SET_IDENTIFIER_DISCLOSURE_ENABLED_DONE));
+    }
+
+    @Override
     public boolean isNullCipherAndIntegritySupported() {
         return mIsNullCipherAndIntegritySupported;
     }
+
+    @Override
+    public boolean isIdentifierDisclosureTransparencySupported() {
+        return mIsIdentifierDisclosureTransparencySupported;
+    }
 }
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..d86c090 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;
@@ -43,6 +44,7 @@
 import android.telephony.BarringInfo;
 import android.telephony.CellIdentity;
 import android.telephony.CellInfo;
+import android.telephony.CellularIdentifierDisclosure;
 import android.telephony.EmergencyRegResult;
 import android.telephony.LinkCapacityEstimate;
 import android.telephony.NetworkRegistrationInfo;
@@ -435,7 +437,25 @@
             mRil.unsljLogRet(RIL_UNSOL_CELLULAR_IDENTIFIER_DISCLOSED, identifierDisclsoure);
         }
 
-        // TODO (b/276752426) notify registrants of identifier disclosure
+        CellularIdentifierDisclosure disclosure =
+                RILUtils.convertCellularIdentifierDisclosure(identifierDisclsoure);
+
+        mRil.mCellularIdentifierDisclosedRegistrants.notifyRegistrants(
+                new AsyncResult(null, disclosure, null));
+    }
+
+    /**
+     * 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
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..d944738 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -254,7 +254,9 @@
     protected static final int EVENT_GET_N1_MODE_ENABLED_DONE = 69;
     protected static final int EVENT_SET_N1_MODE_ENABLED_DONE = 70;
     protected static final int EVENT_IMEI_MAPPING_CHANGED = 71;
-    protected static final int EVENT_LAST = EVENT_IMEI_MAPPING_CHANGED;
+    protected static final int EVENT_CELL_IDENTIFIER_DISCLOSURE = 72;
+    protected static final int EVENT_SET_IDENTIFIER_DISCLOSURE_ENABLED_DONE = 73;
+    protected static final int EVENT_LAST = EVENT_SET_IDENTIFIER_DISCLOSURE_ENABLED_DONE;
 
     // For shared prefs.
     private static final String GSM_ROAMING_LIST_OVERRIDE_PREFIX = "gsm_roaming_list_";
@@ -285,6 +287,9 @@
             "pref_null_cipher_and_integrity_enabled";
     private final TelephonyAdminReceiver m2gAdminUpdater;
 
+    public static final String PREF_IDENTIFIER_DISCLOSURE_NOTIFICATIONS_ENABLED =
+            "pref_identifier_disclosure_notifications_enabled";
+
     protected final FeatureFlags mFeatureFlags;
 
     /**
@@ -660,7 +665,7 @@
             mCi.registerForSrvccStateChanged(this, EVENT_SRVCC_STATE_CHANGED, null);
         }
         //Initialize Telephony Analytics
-        if (isTelephonyAnalyticsEnabled()) {
+        if (mFeatureFlags.enableTelephonyAnalytics()) {
             mTelephonyAnalytics = new TelephonyAnalytics(this);
         }
     }
@@ -4780,11 +4785,6 @@
         return mTelephonyAnalytics;
     }
 
-    public boolean isTelephonyAnalyticsEnabled() {
-        return mContext.getResources().getBoolean(
-                com.android.internal.R.bool.telephony_analytics_switch);
-    }
-
     /** @hide */
     public CarrierPrivilegesTracker getCarrierPrivilegesTracker() {
         return null;
@@ -5153,6 +5153,28 @@
     }
 
     /**
+     * @return whether or not this Phone interacts with a modem that supports the cellular
+     * identifier disclosure transparency feature.
+     */
+    public boolean isIdentifierDisclosureTransparencySupported() {
+        return false;
+    }
+
+    /**
+     * @return global cellular identifier disclosure transparency enabled preference
+     */
+    public boolean getIdentifierDisclosureNotificationsPreferenceEnabled() {
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
+        return sp.getBoolean(PREF_IDENTIFIER_DISCLOSURE_NOTIFICATIONS_ENABLED, false);
+    }
+
+    /**
+     * Override to handle an update to the cellular identifier disclosure transparency preference.
+     */
+    public void handleIdentifierDisclosureNotificationPreferenceChange() {
+    }
+
+    /**
      * Notifies the IMS call status to the modem.
      *
      * @param imsCallInfo The list of {@link ImsCallInfo}.
diff --git a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
index 455555e..26aeaca 100644
--- a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
+++ b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
@@ -19,6 +19,7 @@
 import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED;
 import static android.telephony.TelephonyManager.EXTRA_ACTIVE_SIM_SUPPORTED_COUNT;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.content.Intent;
 import android.os.AsyncResult;
@@ -37,6 +38,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.telephony.Rlog;
 
@@ -81,6 +83,10 @@
     private final Map<Integer, Boolean> mPhoneStatusMap;
     private MockableInterface mMi = new MockableInterface();
     private TelephonyManager mTelephonyManager;
+
+    /** Feature flags */
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
     /**
      * True if 'Virtual DSDA' i.e., in-call IMS connectivity on both subs with only single logical
      * modem, is enabled.
@@ -94,10 +100,11 @@
      * Init method to instantiate the object
      * Should only be called once.
      */
-    public static PhoneConfigurationManager init(Context context) {
+    public static PhoneConfigurationManager init(Context context,
+            @NonNull FeatureFlags featureFlags) {
         synchronized (PhoneConfigurationManager.class) {
             if (sInstance == null) {
-                sInstance = new PhoneConfigurationManager(context);
+                sInstance = new PhoneConfigurationManager(context, featureFlags);
             } else {
                 Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
             }
@@ -109,8 +116,9 @@
      * Constructor.
      * @param context context needed to send broadcast.
      */
-    private PhoneConfigurationManager(Context context) {
+    private PhoneConfigurationManager(Context context, @NonNull FeatureFlags featureFlags) {
         mContext = context;
+        mFeatureFlags = featureFlags;
         // TODO: send commands to modem once interface is ready.
         mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
         //initialize with default, it'll get updated when RADIO is ON/AVAILABLE
@@ -441,7 +449,7 @@
     }
 
     private void notifyCapabilityChanged() {
-        PhoneNotifier notifier = new DefaultPhoneNotifier(mContext);
+        PhoneNotifier notifier = new DefaultPhoneNotifier(mContext, mFeatureFlags);
 
         notifier.notifyPhoneCapabilityChanged(mStaticCapability);
     }
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index 625a937..4705d43 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -161,9 +161,9 @@
                 }
 
                 // register statsd pullers.
-                sMetricsCollector = new MetricsCollector(context);
+                sMetricsCollector = new MetricsCollector(context, sFeatureFlags);
 
-                sPhoneNotifier = new DefaultPhoneNotifier(context);
+                sPhoneNotifier = new DefaultPhoneNotifier(context, featureFlags);
 
                 int cdmaSubscription = CdmaSubscriptionSourceManager.getDefault(context);
                 Rlog.i(LOG_TAG, "Cdma Subscription set to " + cdmaSubscription);
@@ -256,7 +256,7 @@
                     Rlog.i(LOG_TAG, "IMS is not supported on this device, skipping ImsResolver.");
                 }
 
-                sPhoneConfigurationManager = PhoneConfigurationManager.init(sContext);
+                sPhoneConfigurationManager = PhoneConfigurationManager.init(sContext, featureFlags);
 
                 sCellularNetworkValidator = CellularNetworkValidator.make(sContext);
 
@@ -265,9 +265,10 @@
 
                 sPhoneSwitcher = TelephonyComponentFactory.getInstance().inject(
                         PhoneSwitcher.class.getName()).
-                        makePhoneSwitcher(maxActivePhones, sContext, Looper.myLooper());
+                        makePhoneSwitcher(maxActivePhones, sContext, Looper.myLooper(),
+                                featureFlags);
 
-                sProxyController = ProxyController.getInstance(context);
+                sProxyController = ProxyController.getInstance(context, featureFlags);
 
                 sIntentBroadcaster = IntentBroadcaster.getInstance(context);
 
@@ -275,7 +276,7 @@
 
                 for (int i = 0; i < numPhones; i++) {
                     sTelephonyNetworkFactories[i] = new TelephonyNetworkFactory(
-                            Looper.myLooper(), sPhones[i]);
+                            Looper.myLooper(), sPhones[i], featureFlags);
                 }
             }
         }
@@ -284,8 +285,9 @@
     /**
      * Upon single SIM to dual SIM switch or vice versa, we dynamically allocate or de-allocate
      * Phone and CommandInterface objects.
-     * @param context
-     * @param activeModemCount
+     *
+     * @param context The context
+     * @param activeModemCount The number of active modems
      */
     public static void onMultiSimConfigChanged(Context context, int activeModemCount) {
         synchronized (sLockProxyPhones) {
@@ -312,7 +314,7 @@
                     sPhones[i].createImsPhone();
                 }
                 sTelephonyNetworkFactories[i] = new TelephonyNetworkFactory(
-                        Looper.myLooper(), sPhones[i]);
+                        Looper.myLooper(), sPhones[i], sFeatureFlags);
             }
         }
     }
diff --git a/src/java/com/android/internal/telephony/ProxyController.java b/src/java/com/android/internal/telephony/ProxyController.java
index ed9982e..01aed07 100644
--- a/src/java/com/android/internal/telephony/ProxyController.java
+++ b/src/java/com/android/internal/telephony/ProxyController.java
@@ -18,6 +18,7 @@
 
 import static java.util.Arrays.copyOf;
 
+import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
@@ -33,6 +34,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.data.PhoneSwitcher;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.telephony.Rlog;
 
 import java.util.ArrayList;
@@ -111,11 +113,12 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private int[] mOldRadioAccessFamily;
 
+    @NonNull
+    private final FeatureFlags mFlags;
 
-    //***** Class Methods
-    public static ProxyController getInstance(Context context) {
+    public static ProxyController getInstance(Context context, FeatureFlags flags) {
         if (sProxyController == null) {
-            sProxyController = new ProxyController(context);
+            sProxyController = new ProxyController(context, flags);
         }
         return sProxyController;
     }
@@ -125,16 +128,23 @@
         return sProxyController;
     }
 
-    private ProxyController(Context context) {
+    /**
+     * Constructor
+     *
+     * @param context The context
+     * @param featureFlags Feature flags
+     */
+    public ProxyController(@NonNull Context context, @NonNull FeatureFlags featureFlags) {
         logd("Constructor - Enter");
 
         mContext = context;
+        mFlags = featureFlags;
         mPhones = PhoneFactory.getPhones();
         mPhoneSwitcher = PhoneSwitcher.getInstance();
 
         mUiccPhoneBookController = new UiccPhoneBookController();
         mPhoneSubInfoController = new PhoneSubInfoController(mContext);
-        mSmsController = new SmsController(mContext);
+        mSmsController = new SmsController(mContext, featureFlags);
         mSetRadioAccessFamilyStatus = new int[mPhones.length];
         mNewRadioAccessFamily = new int[mPhones.length];
         mOldRadioAccessFamily = new int[mPhones.length];
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 56cb808..5177adb 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -1179,7 +1179,8 @@
     private void addRequest(RILRequest rr) {
         acquireWakeLock(rr, FOR_WAKELOCK);
         Trace.asyncTraceForTrackBegin(
-                Trace.TRACE_TAG_NETWORK, "RIL", RILUtils.requestToString(rr.mRequest), rr.mSerial);
+                Trace.TRACE_TAG_NETWORK, "RIL", rr.mSerial + "> "
+                + RILUtils.requestToString(rr.mRequest), rr.mSerial);
         synchronized (mRequestList) {
             rr.mStartTimeMs = SystemClock.elapsedRealtime();
             mRequestList.append(rr.mSerial, rr);
@@ -1558,7 +1559,7 @@
     public void getSystemSelectionChannels(Message result) {
         RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
         if (!canMakeRequest("getSystemSelectionChannels", networkProxy, result,
-                RADIO_HAL_VERSION_1_4)) {
+                RADIO_HAL_VERSION_1_6)) {
             return;
         }
 
@@ -5086,7 +5087,7 @@
     public void setCellularIdentifierTransparencyEnabled(boolean enable, Message result) {
         RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
         if (!canMakeRequest(
-                "setCellularIdentifierDisclosedEnabled",
+                "setCellularIdentifierTransparencyEnabled",
                 networkProxy,
                 result,
                 RADIO_HAL_VERSION_2_2)) {
@@ -5102,9 +5103,12 @@
         }
 
         radioServiceInvokeHelper(
-            HAL_SERVICE_NETWORK, rr, "setCellularIdentifierDisclosedEnabled", () -> {
-              networkProxy.setCellularIdentifierTransparencyEnabled(rr.mSerial, enable);
-        });
+                HAL_SERVICE_NETWORK,
+                rr,
+                "setCellularIdentifierTransparencyEnabled",
+                () -> {
+                    networkProxy.setCellularIdentifierTransparencyEnabled(rr.mSerial, enable);
+                });
     }
 
     /**
@@ -5114,7 +5118,7 @@
     public void isCellularIdentifierTransparencyEnabled(Message result) {
         RadioNetworkProxy networkProxy = getRadioServiceProxy(RadioNetworkProxy.class);
         if (!canMakeRequest(
-                "isCellularIdentifierDisclosedEnabled",
+                "isCellularIdentifierTransparencyEnabled",
                 networkProxy,
                 result,
                 RADIO_HAL_VERSION_2_2)) {
@@ -5129,9 +5133,67 @@
         }
 
         radioServiceInvokeHelper(
-            HAL_SERVICE_NETWORK, rr, "isCellularIdentifierDisclosedEnabled", () -> {
-              networkProxy.isCellularIdentifierTransparencyEnabled(rr.mSerial);
-        });
+                HAL_SERVICE_NETWORK,
+                rr,
+                "isCellularIdentifierTransparencyEnabled",
+                () -> {
+                    networkProxy.isCellularIdentifierTransparencyEnabled(rr.mSerial);
+                });
+    }
+
+   /**
+     * {@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
@@ -5349,14 +5411,22 @@
     private void processResponseDoneInternal(RILRequest rr, int rilError, int responseType,
             Object ret) {
         if (rilError == 0) {
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "< " + RILUtils.requestToString(rr.mRequest)
-                        + " " + retToString(rr.mRequest, ret));
+            if (isLogOrTrace()) {
+                String logStr = rr.serialString() + "< " + RILUtils.requestToString(rr.mRequest)
+                        + " " + retToString(rr.mRequest, ret);
+                if (RILJ_LOGD) {
+                    riljLog(logStr);
+                }
+                Trace.instantForTrack(Trace.TRACE_TAG_NETWORK, "RIL", logStr);
             }
         } else {
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "< " + RILUtils.requestToString(rr.mRequest)
-                        + " error " + rilError);
+            if (isLogOrTrace()) {
+                String logStr = rr.serialString() + "< " + RILUtils.requestToString(rr.mRequest)
+                        + " error " + rilError;
+                if (RILJ_LOGD) {
+                    riljLog(logStr);
+                }
+                Trace.instantForTrack(Trace.TRACE_TAG_NETWORK, "RIL", logStr);
             }
             rr.onError(rilError, ret);
         }
@@ -6015,4 +6085,4 @@
                 return "UNKNOWN:" + service;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/java/com/android/internal/telephony/RILUtils.java b/src/java/com/android/internal/telephony/RILUtils.java
index 0297e4d..dff8997 100644
--- a/src/java/com/android/internal/telephony/RILUtils.java
+++ b/src/java/com/android/internal/telephony/RILUtils.java
@@ -325,6 +325,7 @@
 import android.telephony.CellSignalStrengthNr;
 import android.telephony.CellSignalStrengthTdscdma;
 import android.telephony.CellSignalStrengthWcdma;
+import android.telephony.CellularIdentifierDisclosure;
 import android.telephony.ClosedSubscriberGroupInfo;
 import android.telephony.DomainSelectionService;
 import android.telephony.EmergencyRegResult;
@@ -5686,6 +5687,20 @@
         }
     }
 
+    /** Convert an AIDL-based CellularIdentifierDisclosure to its Java wrapper. */
+    public static CellularIdentifierDisclosure convertCellularIdentifierDisclosure(
+            android.hardware.radio.network.CellularIdentifierDisclosure identifierDisclsoure) {
+        if (identifierDisclsoure == null) {
+            return null;
+        }
+
+        return new CellularIdentifierDisclosure(
+                identifierDisclsoure.protocolMessage,
+                identifierDisclsoure.identifier,
+                identifierDisclsoure.plmn,
+                identifierDisclsoure.isEmergency);
+    }
+
     private static void logd(String log) {
         Rlog.d("RILUtils", log);
     }
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/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index 01d6e4c..d19e4cb 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -94,6 +94,8 @@
 import com.android.internal.telephony.data.AccessNetworksManager.AccessNetworksManagerCallback;
 import com.android.internal.telephony.data.DataNetwork;
 import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback;
+import com.android.internal.telephony.domainselection.DomainSelectionResolver;
+import com.android.internal.telephony.emergency.EmergencyStateTracker;
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.metrics.RadioPowerStateStats;
@@ -4969,6 +4971,9 @@
     public void powerOffRadioSafely() {
         synchronized (this) {
             SatelliteController.getInstance().onCellularRadioPowerOffRequested();
+            if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
+                EmergencyStateTracker.getInstance().onCellularRadioPowerOffRequested();
+            }
             if (!mPendingRadioPowerOffAfterDataOff) {
                 // hang up all active voice calls first
                 if (mPhone.isPhoneTypeGsm() && mPhone.isInCall()) {
diff --git a/src/java/com/android/internal/telephony/SignalStrengthController.java b/src/java/com/android/internal/telephony/SignalStrengthController.java
index 383ffcd..b11d7e5 100644
--- a/src/java/com/android/internal/telephony/SignalStrengthController.java
+++ b/src/java/com/android/internal/telephony/SignalStrengthController.java
@@ -64,6 +64,7 @@
 import java.util.TreeSet;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.regex.PatternSyntaxException;
 
 /**
@@ -101,6 +102,7 @@
     private static final int EVENT_POLL_SIGNAL_STRENGTH                     = 7;
     private static final int EVENT_SIGNAL_STRENGTH_UPDATE                   = 8;
     private static final int EVENT_POLL_SIGNAL_STRENGTH_DONE                = 9;
+    private static final int EVENT_SERVICE_STATE_CHANGED                    = 10;
 
     @NonNull
     private final Phone mPhone;
@@ -146,6 +148,8 @@
     @NonNull
     private final LocalLog mLocalLog = new LocalLog(64);
 
+    private final AtomicBoolean mNTNConnected = new AtomicBoolean(false);
+
     public SignalStrengthController(@NonNull Phone phone) {
         mPhone = phone;
         mCi = mPhone.mCi;
@@ -161,6 +165,8 @@
         ccm.registerCarrierConfigChangeListener(this::post,
                 (slotIndex, subId, carrierId, specificCarrierId) ->
                         onCarrierConfigurationChanged(slotIndex));
+
+        mPhone.registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null);
     }
 
     @Override
@@ -273,6 +279,11 @@
                 break;
             }
 
+            case EVENT_SERVICE_STATE_CHANGED: {
+                onServiceStateChanged((ServiceState) ((AsyncResult) msg.obj).result);
+                break;
+            }
+
             default:
                 log("Unhandled message with number: " + msg.what);
                 break;
@@ -353,7 +364,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) {
@@ -406,10 +417,13 @@
                             true));
         }
 
-        int lteMeasurementEnabled = mCarrierConfig.getInt(CarrierConfigManager
-                .KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT, CellSignalStrengthLte.USE_RSRP);
-        int[] lteRsrpThresholds = mCarrierConfig.getIntArray(
-                CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY);
+        int lteMeasurementEnabled = mCarrierConfig.getInt(isUsingNonTerrestrialNetwork()
+                        ? CarrierConfigManager.KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT
+                        : CarrierConfigManager.KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT,
+                CellSignalStrengthLte.USE_RSRP);
+        int[] lteRsrpThresholds = mCarrierConfig.getIntArray(isUsingNonTerrestrialNetwork()
+                ? CarrierConfigManager.KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY
+                : CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY);
         if (lteRsrpThresholds != null) {
             signalThresholdInfos.add(
                     validateAndCreateSignalThresholdInfo(
@@ -421,7 +435,8 @@
         }
 
         if (mPhone.getHalVersion(HAL_SERVICE_NETWORK).greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
-            int[] lteRsrqThresholds = mCarrierConfig.getIntArray(
+            int[] lteRsrqThresholds = mCarrierConfig.getIntArray(isUsingNonTerrestrialNetwork()
+                    ? CarrierConfigManager.KEY_NTN_LTE_RSRQ_THRESHOLDS_INT_ARRAY :
                     CarrierConfigManager.KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY);
             if (lteRsrqThresholds != null) {
                 signalThresholdInfos.add(
@@ -433,7 +448,8 @@
                                 (lteMeasurementEnabled & CellSignalStrengthLte.USE_RSRQ) != 0));
             }
 
-            int[] lteRssnrThresholds = mCarrierConfig.getIntArray(
+            int[] lteRssnrThresholds = mCarrierConfig.getIntArray(isUsingNonTerrestrialNetwork()
+                    ? CarrierConfigManager.KEY_NTN_LTE_RSSNR_THRESHOLDS_INT_ARRAY :
                     CarrierConfigManager.KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY);
             if (lteRssnrThresholds != null) {
                 signalThresholdInfos.add(
@@ -1322,6 +1338,25 @@
         };
     }
 
+    private void onServiceStateChanged(ServiceState state) {
+        if (state.getState() != ServiceState.STATE_IN_SERVICE) {
+            return;
+        }
+
+        if (mNTNConnected.get() != state.isUsingNonTerrestrialNetwork()) {
+            log("onServiceStateChanged: update it to " + state.isUsingNonTerrestrialNetwork());
+            updateReportingCriteria();
+            mNTNConnected.set(state.isUsingNonTerrestrialNetwork());
+        }
+    }
+
+    private boolean isUsingNonTerrestrialNetwork() {
+        if (mPhone.getServiceState() == null) {
+            return false;
+        }
+        return mPhone.getServiceState().isUsingNonTerrestrialNetwork();
+    }
+
     private static void log(String msg) {
         if (DBG) Rlog.d(TAG, msg);
     }
diff --git a/src/java/com/android/internal/telephony/SmsController.java b/src/java/com/android/internal/telephony/SmsController.java
index 97161f8..251a1fd 100644
--- a/src/java/com/android/internal/telephony/SmsController.java
+++ b/src/java/com/android/internal/telephony/SmsController.java
@@ -22,6 +22,7 @@
 
 import static com.android.internal.telephony.util.TelephonyUtils.checkDumpPermission;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
@@ -44,6 +45,7 @@
 import android.telephony.TelephonyManager;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.internal.util.IndentingPrintWriter;
@@ -62,10 +64,11 @@
     static final String LOG_TAG = "SmsController";
 
     private final Context mContext;
-
+    private final FeatureFlags mFlags;
     @VisibleForTesting
-    public SmsController(Context context) {
+    public SmsController(Context context, @NonNull FeatureFlags flags) {
         mContext = context;
+        mFlags = flags;
         ServiceRegisterer smsServiceRegisterer = TelephonyFrameworkInitializer
                 .getTelephonyServiceManager()
                 .getSmsServiceRegisterer();
@@ -1114,4 +1117,20 @@
         // Check if smscAddr is present in FDN list
         return FdnUtils.isNumberBlockedByFDN(phoneId, smscAddr, defaultCountryIso);
     }
+
+    /**
+     * Gets the message size of WAP from the cache.
+     *
+     * @param locationUrl the location to use as a key for looking up the size in the cache.
+     * The locationUrl may or may not have the transactionId appended to the url.
+     *
+     * @return long representing the message size
+     * @throws java.util.NoSuchElementException if the WAP push doesn't exist in the cache
+     * @throws IllegalArgumentException if the locationUrl is empty
+     */
+    @Override
+    public long getWapMessageSize(@NonNull String locationUrl) {
+        byte[] bytes = locationUrl.getBytes(StandardCharsets.ISO_8859_1);
+        return WapPushCache.getWapMessageSize(bytes);
+    }
 }
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index 74d1786..f09b9ae 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -47,6 +47,7 @@
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
 import com.android.internal.telephony.nitz.NitzStateMachineImpl;
+import com.android.internal.telephony.security.CellularIdentifierDisclosureNotifier;
 import com.android.internal.telephony.uicc.IccCardStatus;
 import com.android.internal.telephony.uicc.UiccCard;
 import com.android.internal.telephony.uicc.UiccProfile;
@@ -341,8 +342,9 @@
      * Create a new UiccProfile object.
      */
     public UiccProfile makeUiccProfile(Context context, CommandsInterface ci, IccCardStatus ics,
-                                       int phoneId, UiccCard uiccCard, Object lock) {
-        return new UiccProfile(context, ci, ics, phoneId, uiccCard, lock);
+                                       int phoneId, UiccCard uiccCard, Object lock,
+            @NonNull FeatureFlags flags) {
+        return new UiccProfile(context, ci, ics, phoneId, uiccCard, lock, flags);
     }
 
     public EriManager makeEriManager(Phone phone, int eriFileSource) {
@@ -480,8 +482,8 @@
     }
 
     public PhoneSwitcher makePhoneSwitcher(int maxDataAttachModemCount, Context context,
-            Looper looper) {
-        return PhoneSwitcher.make(maxDataAttachModemCount, context, looper);
+            Looper looper, @NonNull FeatureFlags featureFlags) {
+        return PhoneSwitcher.make(maxDataAttachModemCount, context, looper, featureFlags);
     }
 
     /**
@@ -565,4 +567,9 @@
             @NonNull DataSettingsManager.DataSettingsManagerCallback callback) {
         return new DataSettingsManager(phone, dataNetworkController, looper, callback);
     }
+
+    /** Create CellularIdentifierDisclosureNotifier. */
+    public CellularIdentifierDisclosureNotifier makeIdentifierDisclosureNotifier() {
+        return CellularIdentifierDisclosureNotifier.getInstance();
+    }
 }
diff --git a/src/java/com/android/internal/telephony/WapPushCache.java b/src/java/com/android/internal/telephony/WapPushCache.java
new file mode 100644
index 0000000..70d12e2
--- /dev/null
+++ b/src/java/com/android/internal/telephony/WapPushCache.java
@@ -0,0 +1,172 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.telephony.Rlog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.NoSuchElementException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Caches WAP push PDU data for retrieval during MMS downloading.
+ * When on a satellite connection, the cached message size will be used to prevent downloading
+ * messages that exceed a threshold.
+ *
+ * The cache uses a circular buffer and will start invalidating the oldest entries after 250
+ * message sizes have been inserted.
+ * The cache also invalidates entries that have been in the cache for over 14 days.
+ */
+public class WapPushCache {
+    private static final String TAG = "WAP PUSH CACHE";
+
+    // Because we store each size twice, this represents 250 messages. That limit is chosen so
+    // that the memory footprint of the cache stays reasonably small while still supporting what
+    // we guess will be the vast majority of real use cases.
+    private static final int MAX_CACHE_SIZE = 500;
+
+    // WAP push PDUs have an expiry property, but we can't be certain that it is set accurately
+    // by the carrier. We will use our own expiry for this cache to keep it small. One example
+    // carrier has an expiry of 7 days so 14 will give us room for those with longer times as well.
+    private static final long CACHE_EXPIRY_TIME = TimeUnit.DAYS.toMillis(14);
+
+    private static final HashMap<String, CacheEntry> sMessageSizes = new LinkedHashMap<>() {
+        @Override
+        protected boolean removeEldestEntry(Entry<String, CacheEntry> eldest) {
+            return size() > MAX_CACHE_SIZE;
+        }
+    };
+
+    @VisibleForTesting
+    public static TelephonyFacade sTelephonyFacade = new TelephonyFacade();
+
+    /**
+     * Puts a WAP push PDU's messageSize in the cache.
+     *
+     * The data is stored twice, once using just locationUrl as the key and once
+     * using transactionId appended to the locationUrl. For some carriers, xMS apps
+     * append the transactionId to the location and we need to support lookup using either the
+     * original location or one modified in this way.
+
+     *
+     * @param locationUrl location of the message used as part of the cache key.
+     * @param transactionId message transaction ID used as part of the cache key.
+     * @param messageSize size of the message to be stored in the cache.
+     */
+    public static void putWapMessageSize(
+            @NonNull byte[] locationUrl,
+            @NonNull byte[] transactionId,
+            long messageSize
+    ) {
+        long expiry = sTelephonyFacade.getElapsedSinceBootMillis() + CACHE_EXPIRY_TIME;
+        if (messageSize <= 0) {
+            Rlog.e(TAG, "Invalid message size of " + messageSize + ". Not inserting.");
+            return;
+        }
+        synchronized (sMessageSizes) {
+            sMessageSizes.put(Arrays.toString(locationUrl), new CacheEntry(messageSize, expiry));
+
+            // concatenate the locationUrl and transactionId
+            byte[] joinedKey = ByteBuffer
+                    .allocate(locationUrl.length + transactionId.length)
+                    .put(locationUrl)
+                    .put(transactionId)
+                    .array();
+            sMessageSizes.put(Arrays.toString(joinedKey), new CacheEntry(messageSize, expiry));
+            invalidateOldEntries();
+        }
+    }
+
+    /**
+     * Remove entries from the cache that are older than CACHE_EXPIRY_TIME
+     */
+    private static void invalidateOldEntries() {
+        long currentTime = sTelephonyFacade.getElapsedSinceBootMillis();
+
+        // We can just remove elements from the start until one is found that does not exceed the
+        // expiry since the elements are in order of insertion.
+        for (Iterator<CacheEntry> it = sMessageSizes.values().iterator(); it.hasNext(); ) {
+            CacheEntry entry = it.next();
+            if (entry.mExpiry < currentTime) {
+                it.remove();
+            } else {
+                break;
+            }
+        }
+    }
+
+    /**
+     * Gets the message size of a WAP from the cache.
+     *
+     * Because we stored the size both using the location+transactionId key and using the
+     * location only key, we should be able to find the size whether the xMS app modified
+     * the location or not.
+     *
+     * @param locationUrl the location to use as a key for looking up the size in the cache.
+     *
+     * @return long representing the message size of the WAP
+     * @throws NoSuchElementException if the WAP doesn't exist in the cache
+     * @throws IllegalArgumentException if the locationUrl is empty
+     */
+    public static long getWapMessageSize(@NonNull byte[] locationUrl) {
+        if (locationUrl.length == 0) {
+            throw new IllegalArgumentException("Found empty locationUrl");
+        }
+        CacheEntry entry = sMessageSizes.get(Arrays.toString(locationUrl));
+        if (entry == null) {
+            throw new NoSuchElementException(
+                "No cached WAP size for locationUrl " + Arrays.toString(locationUrl)
+            );
+        }
+        return entry.mSize;
+    }
+
+    /**
+     * Clears all elements from the cache
+     */
+    @VisibleForTesting
+    public static void clear() {
+        sMessageSizes.clear();
+    }
+
+    /**
+     * Returns a count of the number of elements in the cache
+     * @return count of elements
+     */
+    @VisibleForTesting
+    public static int size() {
+        return sMessageSizes.size();
+    }
+
+
+
+    private static class CacheEntry {
+        CacheEntry(long size, long expiry) {
+            mSize = size;
+            mExpiry = expiry;
+        }
+        private final long mSize;
+        private final long mExpiry;
+    }
+}
diff --git a/src/java/com/android/internal/telephony/WapPushOverSms.java b/src/java/com/android/internal/telephony/WapPushOverSms.java
index d6ea4ad..7669411 100644
--- a/src/java/com/android/internal/telephony/WapPushOverSms.java
+++ b/src/java/com/android/internal/telephony/WapPushOverSms.java
@@ -262,6 +262,13 @@
 
             if (parsedPdu != null && parsedPdu.getMessageType() == MESSAGE_TYPE_NOTIFICATION_IND) {
                 final NotificationInd nInd = (NotificationInd) parsedPdu;
+                // save the WAP push message size so that if a download request is made for it
+                // while on a satellite connection we can check if the size is under the threshold
+                WapPushCache.putWapMessageSize(
+                        nInd.getContentLocation(),
+                        nInd.getTransactionId(),
+                        nInd.getMessageSize()
+                );
                 if (nInd.getFrom() != null
                         && BlockChecker.isBlocked(mContext, nInd.getFrom().getString(), null)) {
                     result.statusCode = Intents.RESULT_SMS_HANDLED;
diff --git a/src/java/com/android/internal/telephony/cat/CatService.java b/src/java/com/android/internal/telephony/cat/CatService.java
index fa2b19b..cadb02e 100644
--- a/src/java/com/android/internal/telephony/cat/CatService.java
+++ b/src/java/com/android/internal/telephony/cat/CatService.java
@@ -49,6 +49,8 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.ProxyController;
 import com.android.internal.telephony.SmsController;
+import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.flags.FeatureFlagsImpl;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.uicc.IccCardStatus.CardState;
 import com.android.internal.telephony.uicc.IccFileHandler;
@@ -104,6 +106,8 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private static CatService[] sInstance = null;
     @UnsupportedAppUsage
+    private static final FeatureFlags sFlags = new FeatureFlagsImpl();
+    @UnsupportedAppUsage
     private CommandsInterface mCmdIf;
     @UnsupportedAppUsage
     private Context mContext;
@@ -487,7 +491,8 @@
                         destAddr = ((SendSMSParams) cmdParams).mDestAddress.text;
                     }
                     if (text != null && destAddr != null) {
-                        ProxyController proxyController = ProxyController.getInstance(mContext);
+                        ProxyController proxyController = ProxyController
+                                .getInstance(mContext, sFlags);
                         SubscriptionManager subscriptionManager = (SubscriptionManager)
                                 mContext.getSystemService(
                                         Context.TELEPHONY_SUBSCRIPTION_SERVICE);
diff --git a/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java b/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
index 0ea0598..e8cd8f0 100644
--- a/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
+++ b/src/java/com/android/internal/telephony/data/AutoDataSwitchController.java
@@ -47,6 +47,7 @@
 
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.util.NotificationChannelController;
@@ -125,6 +126,7 @@
 
     private final @NonNull LocalLog mLocalLog = new LocalLog(128);
     private final @NonNull Context mContext;
+    private final @NonNull FeatureFlags mFlags;
     private final @NonNull SubscriptionManagerService mSubscriptionManagerService;
     private final @NonNull PhoneSwitcher mPhoneSwitcher;
     private final @NonNull AutoDataSwitchControllerCallback mPhoneSwitcherCallback;
@@ -167,15 +169,29 @@
      * switch to.
      */
     private static class PhoneSignalStatus {
+        /**
+         * How preferred the current phone is.
+         */
+        enum UsableState {
+            HOME(1), ROAMING_ENABLED(0), NOT_USABLE(-1);
+            /**
+             * The higher the score, the more preferred.
+             * HOME is preferred over ROAMING assuming roaming is metered.
+             */
+            final int mScore;
+            UsableState(int score) {
+                this.mScore = score;
+            }
+        }
         /** The phone */
-        private final @NonNull Phone mPhone;
+        @NonNull private final Phone mPhone;
         /** Data registration state of the phone */
-        private @RegistrationState int mDataRegState = NetworkRegistrationInfo
+        @RegistrationState private int mDataRegState = NetworkRegistrationInfo
                 .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING;
         /** Current Telephony display info of the phone */
-        private @NonNull TelephonyDisplayInfo mDisplayInfo;
+        @NonNull private TelephonyDisplayInfo mDisplayInfo;
         /** Signal strength of the phone */
-        private @NonNull SignalStrength mSignalStrength;
+        @NonNull private SignalStrength mSignalStrength;
         /** {@code true} if this slot is listening for events. */
         private boolean mListeningForEvents;
         private PhoneSignalStatus(@NonNull Phone phone) {
@@ -193,11 +209,28 @@
                     ? mPhone.getDataNetworkController().getDataConfigManager()
                             .getAutoDataSwitchScore(mDisplayInfo, mSignalStrength) : 0;
         }
+
+        /**
+         * @return The current usable state of the phone.
+         */
+        private UsableState getUsableState() {
+            switch (mDataRegState) {
+                case NetworkRegistrationInfo.REGISTRATION_STATE_HOME:
+                    return UsableState.HOME;
+                case NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING:
+                    return mPhone.getDataRoamingEnabled()
+                            ? UsableState.ROAMING_ENABLED : UsableState.NOT_USABLE;
+                default:
+                    return UsableState.NOT_USABLE;
+            }
+        }
+
         @Override
         public String toString() {
             return "{phone " + mPhone.getPhoneId()
                     + " score=" + getRatSignalScore() + " dataRegState="
                     + NetworkRegistrationInfo.registrationStateToString(mDataRegState)
+                    + " " + getUsableState()
                     + " display=" + mDisplayInfo + " signalStrength=" + mSignalStrength.getLevel()
                     + " listeningForEvents=" + mListeningForEvents
                     + "}";
@@ -238,10 +271,11 @@
      * @param phoneSwitcherCallback Callback for phone switcher to execute.
      */
     public AutoDataSwitchController(@NonNull Context context, @NonNull Looper looper,
-            @NonNull PhoneSwitcher phoneSwitcher,
+            @NonNull PhoneSwitcher phoneSwitcher, @NonNull FeatureFlags featureFlags,
             @NonNull AutoDataSwitchControllerCallback phoneSwitcherCallback) {
         super(looper);
         mContext = context;
+        mFlags = featureFlags;
         mSubscriptionManagerService = SubscriptionManagerService.getInstance();
         mPhoneSwitcher = phoneSwitcher;
         mPhoneSwitcherCallback = phoneSwitcherCallback;
@@ -275,7 +309,7 @@
     }
 
     /** Notify subscriptions changed. */
-    public void notifySubscriptionsChanged() {
+    public void notifySubscriptionsMappingChanged() {
         sendEmptyMessage(EVENT_SUBSCRIPTIONS_CHANGED);
     }
 
@@ -291,16 +325,19 @@
                 .collect(Collectors.toSet());
         // Track events only if there are at least two active visible subscriptions.
         if (activePhoneIds.size() < 2) activePhoneIds.clear();
+        boolean changed = false;
         for (int phoneId = 0; phoneId < mPhonesSignalStatus.length; phoneId++) {
             if (activePhoneIds.contains(phoneId)
                     && !mPhonesSignalStatus[phoneId].mListeningForEvents) {
                 registerAllEventsForPhone(phoneId);
+                changed = true;
             } else if (!activePhoneIds.contains(phoneId)
                     && mPhonesSignalStatus[phoneId].mListeningForEvents) {
                 unregisterAllEventsForPhone(phoneId);
+                changed = true;
             }
         }
-        logl("onSubscriptionChanged: " + Arrays.toString(mPhonesSignalStatus));
+        if (changed) logl("onSubscriptionChanged: " + Arrays.toString(mPhonesSignalStatus));
     }
 
     /**
@@ -362,7 +399,7 @@
             case EVENT_SERVICE_STATE_CHANGED:
                 ar = (AsyncResult) msg.obj;
                 phoneId = (int) ar.userObj;
-                onRegistrationStateChanged(phoneId);
+                onServiceStateChanged(phoneId);
                 break;
             case EVENT_DISPLAY_INFO_CHANGED:
                 ar = (AsyncResult) msg.obj;
@@ -396,7 +433,7 @@
     /**
      * Called when registration state changed.
      */
-    private void onRegistrationStateChanged(int phoneId) {
+    private void onServiceStateChanged(int phoneId) {
         Phone phone = PhoneFactory.getPhone(phoneId);
         if (phone != null && isActiveModemPhone(phoneId)) {
             int oldRegState = mPhonesSignalStatus[phoneId].mDataRegState;
@@ -409,7 +446,7 @@
                 mPhonesSignalStatus[phoneId].mDataRegState = newRegState;
                 if (isInService(oldRegState) != isInService(newRegState)
                         || isHomeService(oldRegState) != isHomeService(newRegState)) {
-                    log("onRegistrationStateChanged: phone " + phoneId + " "
+                    log("onServiceStateChanged: phone " + phoneId + " "
                             + NetworkRegistrationInfo.registrationStateToString(oldRegState)
                             + " -> "
                             + NetworkRegistrationInfo.registrationStateToString(newRegState));
@@ -562,35 +599,92 @@
             boolean backToDefault = false;
             boolean needValidation = true;
 
-            if (mDefaultNetworkIsOnNonCellular) {
-                log(debugMessage.append(", back to default as default network")
-                        .append(" is active on nonCellular transport").toString());
-                backToDefault = true;
-                needValidation = false;
-            } else if (!isHomeService(mPhonesSignalStatus[preferredPhoneId].mDataRegState)) {
-                log(debugMessage.append(", back to default as backup phone lost HOME registration")
-                        .toString());
-                backToDefault = true;
-                needValidation = false;
-            } else if (isRatSignalStrengthBasedSwitchEnabled()) {
-                int defaultScore = mPhonesSignalStatus[defaultDataPhoneId].getRatSignalScore();
-                int currentScore = mPhonesSignalStatus[preferredPhoneId].getRatSignalScore();
-                if ((defaultScore - currentScore) > mScoreTolerance) {
-                    log(debugMessage
-                            .append(", back to default as default phone has higher score ")
-                            .append(defaultScore).append(" versus current ")
-                            .append(currentScore).toString());
+            if (mFlags.autoSwitchAllowRoaming()) {
+                if (mDefaultNetworkIsOnNonCellular) {
+                    debugMessage.append(", back to default as default network")
+                            .append(" is active on nonCellular transport");
+                    backToDefault = true;
+                    needValidation = false;
+                } else {
+                    PhoneSignalStatus.UsableState defaultUsableState =
+                            mPhonesSignalStatus[defaultDataPhoneId].getUsableState();
+                    PhoneSignalStatus.UsableState currentUsableState =
+                            mPhonesSignalStatus[preferredPhoneId].getUsableState();
+
+                    boolean isCurrentUsable = currentUsableState.mScore
+                            > PhoneSignalStatus.UsableState.NOT_USABLE.mScore;
+
+                    if (currentUsableState.mScore < defaultUsableState.mScore) {
+                        debugMessage.append(", back to default phone ").append(preferredPhoneId)
+                                .append(" : ").append(defaultUsableState)
+                                .append(" , backup phone: ").append(currentUsableState);
+
+                        backToDefault = true;
+                        // Require validation if the current preferred phone is usable.
+                        needValidation = isCurrentUsable && mRequirePingTestBeforeSwitch;
+                    } else if (defaultUsableState.mScore == currentUsableState.mScore) {
+                        debugMessage.append(", default phone ").append(preferredPhoneId)
+                                .append(" : ").append(defaultUsableState)
+                                .append(" , backup phone: ").append(currentUsableState);
+
+                        if (isCurrentUsable) {
+                            // Both phones are usable.
+                            if (isRatSignalStrengthBasedSwitchEnabled()) {
+                                int defaultScore = mPhonesSignalStatus[defaultDataPhoneId]
+                                        .getRatSignalScore();
+                                int currentScore = mPhonesSignalStatus[preferredPhoneId]
+                                        .getRatSignalScore();
+                                if ((defaultScore - currentScore) > mScoreTolerance) {
+                                    debugMessage
+                                            .append(", back to default for higher score ")
+                                            .append(defaultScore).append(" versus current ")
+                                            .append(currentScore);
+                                    backToDefault = true;
+                                    needValidation = mRequirePingTestBeforeSwitch;
+                                }
+                            } else {
+                                // Only OOS/in service switch is enabled, switch back.
+                                debugMessage.append(", back to default as it's usable. ");
+                                backToDefault = true;
+                                needValidation = mRequirePingTestBeforeSwitch;
+                            }
+                        } else {
+                            debugMessage.append(", back to default as both phones are unusable.");
+                            backToDefault = true;
+                            needValidation = false;
+                        }
+                    }
+                }
+            } else {
+                if (mDefaultNetworkIsOnNonCellular) {
+                    debugMessage.append(", back to default as default network")
+                            .append(" is active on nonCellular transport");
+                    backToDefault = true;
+                    needValidation = false;
+                } else if (!isHomeService(mPhonesSignalStatus[preferredPhoneId].mDataRegState)) {
+                    debugMessage.append(", back to default as backup phone lost HOME registration");
+                    backToDefault = true;
+                    needValidation = false;
+                } else if (isRatSignalStrengthBasedSwitchEnabled()) {
+                    int defaultScore = mPhonesSignalStatus[defaultDataPhoneId].getRatSignalScore();
+                    int currentScore = mPhonesSignalStatus[preferredPhoneId].getRatSignalScore();
+                    if ((defaultScore - currentScore) > mScoreTolerance) {
+                        debugMessage
+                                .append(", back to default as default phone has higher score ")
+                                .append(defaultScore).append(" versus current ")
+                                .append(currentScore);
+                        backToDefault = true;
+                        needValidation = mRequirePingTestBeforeSwitch;
+                    }
+                } else if (isInService(mPhonesSignalStatus[defaultDataPhoneId].mDataRegState)) {
+                    debugMessage.append(", back to default as the default is back to service ");
                     backToDefault = true;
                     needValidation = mRequirePingTestBeforeSwitch;
                 }
-            } else if (isInService(mPhonesSignalStatus[defaultDataPhoneId].mDataRegState)) {
-                log(debugMessage.append(", back to default as the default is back to service ")
-                        .toString());
-                backToDefault = true;
-                needValidation = mRequirePingTestBeforeSwitch;
             }
 
             if (backToDefault) {
+                log(debugMessage.toString());
                 mSelectedTargetPhoneId = defaultDataPhoneId;
                 startStabilityCheck(DEFAULT_PHONE_INDEX, needValidation);
             } else {
@@ -625,20 +719,60 @@
             return INVALID_PHONE_INDEX;
         }
 
-        // check whether primary and secondary signal status are worth switching
-        if (!isRatSignalStrengthBasedSwitchEnabled()
-                && isInService(mPhonesSignalStatus[defaultPhoneId].mDataRegState)) {
-            debugMessage.append(", no candidate as default phone is in service");
-            return INVALID_PHONE_INDEX;
+        if (mFlags.autoSwitchAllowRoaming()) {
+            // check whether primary and secondary signal status are worth switching
+            if (!isRatSignalStrengthBasedSwitchEnabled()
+                    && isHomeService(mPhonesSignalStatus[defaultPhoneId].mDataRegState)) {
+                debugMessage.append(", no candidate as default phone is in HOME service");
+                return INVALID_PHONE_INDEX;
+            }
+        } else {
+            // check whether primary and secondary signal status are worth switching
+            if (!isRatSignalStrengthBasedSwitchEnabled()
+                    && isInService(mPhonesSignalStatus[defaultPhoneId].mDataRegState)) {
+                debugMessage.append(", no candidate as default phone is in service");
+                return INVALID_PHONE_INDEX;
+            }
         }
-        for (int phoneId = 0; phoneId < mPhonesSignalStatus.length; phoneId++) {
-            if (phoneId != defaultPhoneId
-                    // the alternative phone must have HOME availability
-                    && isHomeService(mPhonesSignalStatus[phoneId].mDataRegState)) {
-                Phone secondaryDataPhone = null;
-                debugMessage.append(", found phone ").append(phoneId).append(" in HOME reg");
 
-                if (isInService(mPhonesSignalStatus[defaultPhoneId].mDataRegState)) {
+        PhoneSignalStatus defaultPhoneStatus = mPhonesSignalStatus[defaultPhoneId];
+        for (int phoneId = 0; phoneId < mPhonesSignalStatus.length; phoneId++) {
+            if (phoneId == defaultPhoneId) continue;
+
+            Phone secondaryDataPhone = null;
+            PhoneSignalStatus candidatePhoneStatus = mPhonesSignalStatus[phoneId];
+            if (mFlags.autoSwitchAllowRoaming()) {
+                PhoneSignalStatus.UsableState currentUsableState =
+                        mPhonesSignalStatus[defaultPhoneId].getUsableState();
+                PhoneSignalStatus.UsableState candidatePhoneUsableRank =
+                        mPhonesSignalStatus[phoneId].getUsableState();
+                debugMessage.append(", found phone ").append(phoneId).append(" is ").append(
+                        candidatePhoneUsableRank)
+                        .append(", current is ").append(currentUsableState);
+                if (candidatePhoneUsableRank.mScore > currentUsableState.mScore) {
+                    secondaryDataPhone = PhoneFactory.getPhone(phoneId);
+                } else if (isRatSignalStrengthBasedSwitchEnabled()
+                        && currentUsableState.mScore == candidatePhoneUsableRank.mScore) {
+                    // Both phones are home or both roaming enabled, so compare RAT/signal score.
+
+                    int defaultScore = defaultPhoneStatus.getRatSignalScore();
+                    int candidateScore = candidatePhoneStatus.getRatSignalScore();
+                    if ((candidateScore - defaultScore) > mScoreTolerance) {
+                        debugMessage.append(" with higher score ").append(
+                                        candidateScore)
+                                .append(" versus current ").append(defaultScore);
+                        secondaryDataPhone = PhoneFactory.getPhone(phoneId);
+                    } else {
+                        debugMessage.append(", but its score ").append(candidateScore)
+                                .append(" doesn't meet the bar to switch given the current ")
+                                .append(defaultScore);
+                    }
+                }
+            } else if (isHomeService(candidatePhoneStatus.mDataRegState)) {
+                // the alternative phone must have HOME availability
+                debugMessage.append(", found phone ").append(phoneId).append(" in HOME service");
+
+                if (isInService(defaultPhoneStatus.mDataRegState)) {
                     // Use score if RAT/signal strength based switch is enabled and both phone are
                     // in service.
                     if (isRatSignalStrengthBasedSwitchEnabled()) {
@@ -658,13 +792,14 @@
                     // Only OOS/in service switch is enabled.
                     secondaryDataPhone = PhoneFactory.getPhone(phoneId);
                 }
-                if (secondaryDataPhone != null) {
-                    // check auto switch feature enabled
-                    if (secondaryDataPhone.isDataAllowed()) {
-                        return phoneId;
-                    } else {
-                        debugMessage.append(", but its data is not allowed");
-                    }
+            }
+
+            if (secondaryDataPhone != null) {
+                // check auto switch feature enabled
+                if (secondaryDataPhone.isDataAllowed()) {
+                    return phoneId;
+                } else {
+                    debugMessage.append(", but its data is not allowed");
                 }
             }
         }
@@ -676,7 +811,7 @@
      * @return {@code true} If the feature of switching base on RAT and signal strength is enabled.
      */
     private boolean isRatSignalStrengthBasedSwitchEnabled() {
-        return mScoreTolerance >= 0;
+        return mFlags.autoDataSwitchRatSs() && mScoreTolerance >= 0;
     }
 
     /**
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/data/PhoneSwitcher.java b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
index 36e6587..2c84f5e 100644
--- a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
@@ -28,6 +28,7 @@
 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_NONE;
+
 import static java.util.Arrays.copyOf;
 
 import android.annotation.NonNull;
@@ -83,6 +84,7 @@
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.data.DataNetworkController.NetworkRequestList;
 import com.android.internal.telephony.data.DataSettingsManager.DataSettingsManagerCallback;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.DataSwitch;
@@ -184,6 +186,7 @@
     private final @NonNull NetworkRequestList mNetworkRequestList = new NetworkRequestList();
     protected final RegistrantList mActivePhoneRegistrants;
     private final SubscriptionManagerService mSubscriptionManagerService;
+    private final @NonNull FeatureFlags mFlags;
     protected final Context mContext;
     private final LocalLog mLocalLog;
     protected PhoneState[] mPhoneStates;
@@ -397,9 +400,10 @@
     /**
      * Method to create singleton instance.
      */
-    public static PhoneSwitcher make(int maxDataAttachModemCount, Context context, Looper looper) {
+    public static PhoneSwitcher make(int maxDataAttachModemCount, Context context, Looper looper,
+            @NonNull FeatureFlags flags) {
         if (sPhoneSwitcher == null) {
-            sPhoneSwitcher = new PhoneSwitcher(maxDataAttachModemCount, context, looper);
+            sPhoneSwitcher = new PhoneSwitcher(maxDataAttachModemCount, context, looper, flags);
         }
 
         return sPhoneSwitcher;
@@ -457,9 +461,11 @@
     }
 
     @VisibleForTesting
-    public PhoneSwitcher(int maxActivePhones, Context context, Looper looper) {
+    public PhoneSwitcher(int maxActivePhones, Context context, Looper looper,
+            @NonNull FeatureFlags featureFlags) {
         super(looper);
         mContext = context;
+        mFlags = featureFlags;
         mActiveModemCount = getTm().getActiveModemCount();
         mPhoneSubscriptions = new int[mActiveModemCount];
         mPhoneStates = new PhoneState[mActiveModemCount];
@@ -497,6 +503,12 @@
                                     @TelephonyManager.DataEnabledChangedReason int reason,
                                     @NonNull String callingPackage) {
                                 PhoneSwitcher.this.onDataEnabledChanged();
+                            }
+                            @Override
+                            public void onDataRoamingEnabledChanged(boolean enabled) {
+                                PhoneSwitcher.this.mAutoDataSwitchController.evaluateAutoDataSwitch(
+                                        AutoDataSwitchController
+                                                .EVALUATION_REASON_DATA_SETTINGS_CHANGED);
                             }});
                 phone.getDataSettingsManager().registerCallback(
                         mDataSettingsManagerCallbacks.get(phoneId));
@@ -546,7 +558,7 @@
             }
         };
         mAutoDataSwitchController = new AutoDataSwitchController(context, looper, this,
-                mAutoDataSwitchCallback);
+                mFlags, mAutoDataSwitchCallback);
 
         mContext.registerReceiver(mDefaultDataChangedReceiver,
                 new IntentFilter(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED));
@@ -647,7 +659,6 @@
     public void handleMessage(Message msg) {
         switch (msg.what) {
             case EVENT_SUBSCRIPTION_CHANGED: {
-                mAutoDataSwitchController.notifySubscriptionsChanged();
                 onEvaluate(REQUESTS_UNCHANGED, "subscription changed");
                 break;
             }
@@ -902,6 +913,12 @@
                                 @NonNull String callingPackage) {
                             PhoneSwitcher.this.onDataEnabledChanged();
                         }
+                        @Override
+                        public void onDataRoamingEnabledChanged(boolean enabled) {
+                            PhoneSwitcher.this.mAutoDataSwitchController.evaluateAutoDataSwitch(
+                                    AutoDataSwitchController
+                                            .EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+                        }
                     });
             phone.getDataSettingsManager().registerCallback(
                     mDataSettingsManagerCallbacks.get(phone.getPhoneId()));
@@ -1080,6 +1097,7 @@
                     registerForImsRadioTechChange(mContext, i);
                 }
                 diffDetected = true;
+                mAutoDataSwitchController.notifySubscriptionsMappingChanged();
             }
         }
 
diff --git a/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java b/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java
index 2cdf807..377c219 100644
--- a/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java
+++ b/src/java/com/android/internal/telephony/data/TelephonyNetworkFactory.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony.data;
 
+import android.annotation.NonNull;
 import android.net.NetworkCapabilities;
 import android.net.NetworkFactory;
 import android.net.NetworkRequest;
@@ -29,6 +30,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.metrics.NetworkRequestsStats;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.telephony.Rlog;
@@ -77,11 +79,22 @@
     @VisibleForTesting
     public final Handler mInternalHandler;
 
+    private final @NonNull FeatureFlags mFlags;
 
-    public TelephonyNetworkFactory(Looper looper, Phone phone) {
+
+    /**
+     * Constructor
+     *
+     * @param looper The looper for the handler
+     * @param phone The phone instance
+     * @param featureFlags The feature flags
+     */
+    public TelephonyNetworkFactory(@NonNull Looper looper, @NonNull Phone phone,
+            @NonNull FeatureFlags featureFlags) {
         super(looper, phone.getContext(), "TelephonyNetworkFactory[" + phone.getPhoneId()
                 + "]", null);
         mPhone = phone;
+        mFlags = featureFlags;
         mInternalHandler = new InternalHandler(looper);
 
         mAccessNetworksManager = mPhone.getAccessNetworksManager();
diff --git a/src/java/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnection.java
index 516d6b9..0532a05 100644
--- a/src/java/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnection.java
+++ b/src/java/com/android/internal/telephony/domainselection/NormalCallDomainSelectionConnection.java
@@ -22,9 +22,11 @@
 import android.annotation.Nullable;
 import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
 import android.telephony.Annotation.DisconnectCauses;
+import android.telephony.DisconnectCause;
 import android.telephony.DomainSelectionService;
 import android.telephony.DomainSelectionService.EmergencyScanType;
 import android.telephony.NetworkRegistrationInfo;
+import android.telephony.PreciseDisconnectCause;
 import android.telephony.ims.ImsReasonInfo;
 
 import com.android.internal.telephony.Phone;
@@ -37,6 +39,9 @@
 public class NormalCallDomainSelectionConnection extends DomainSelectionConnection {
 
     private static final boolean DBG = false;
+    private int mDisconnectCause = DisconnectCause.NOT_VALID;
+    private int mPreciseDisconnectCause = PreciseDisconnectCause.NOT_VALID;
+    private String mReasonMessage = null;
 
     private @Nullable DomainSelectionConnectionCallback mCallback;
 
@@ -123,4 +128,49 @@
         }
         return builder.build();
     }
+
+    /**
+     * Save call disconnect info for error propagation.
+     * @param disconnectCause The code for the reason for the disconnect.
+     * @param preciseDisconnectCause The code for the precise reason for the disconnect.
+     * @param reasonMessage Description of the reason for the disconnect, not intended for the user
+     *                      to see.
+     */
+    public void setDisconnectCause(int disconnectCause, int preciseDisconnectCause,
+                                String reasonMessage) {
+        mDisconnectCause = disconnectCause;
+        mPreciseDisconnectCause = preciseDisconnectCause;
+        mReasonMessage = reasonMessage;
+    }
+
+    public int getDisconnectCause() {
+        return mDisconnectCause;
+    }
+
+    public int getPreciseDisconnectCause() {
+        return mPreciseDisconnectCause;
+    }
+
+    public String getReasonMessage() {
+        return mReasonMessage;
+    }
+
+    /**
+     * @return imsReasonInfo Reason for the IMS call failure.
+     */
+    public @Nullable ImsReasonInfo getImsReasonInfo() {
+        if (getSelectionAttributes() == null) {
+            // Neither selectDomain(...) nor reselectDomain(...) has been called yet.
+            return null;
+        }
+
+        return getSelectionAttributes().getPsDisconnectCause();
+    }
+
+    /**
+     * @return phoneId To support localized message based on phoneId
+     */
+    public int getPhoneId() {
+        return getPhone().getPhoneId();
+    }
 }
diff --git a/src/java/com/android/internal/telephony/domainselection/OWNERS b/src/java/com/android/internal/telephony/domainselection/OWNERS
new file mode 100644
index 0000000..b9112be
--- /dev/null
+++ b/src/java/com/android/internal/telephony/domainselection/OWNERS
@@ -0,0 +1,8 @@
+# automatically inherit owners from fw/opt/telephony
+
+hwangoo@google.com
+forestchoi@google.com
+avinashmp@google.com
+mkoon@google.com
+seheele@google.com
+radhikaagrawal@google.com
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
index 6773dca..0692f7d 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()}. */
@@ -785,6 +892,18 @@
         }
     }
 
+    /**
+     * Handles the radio power off request.
+     */
+    public void onCellularRadioPowerOffRequested() {
+        synchronized (mLock) {
+            if (isInEcm()) {
+                exitEmergencyCallbackMode(null);
+            }
+            exitEmergencyModeIfDelayed();
+        }
+    }
+
     private static boolean isVoWiFi(int properties) {
         return (properties & android.telecom.Connection.PROPERTY_WIFI) > 0
                 || (properties & android.telecom.Connection.PROPERTY_CROSS_SIM) > 0;
@@ -887,7 +1006,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 +1147,7 @@
                             MSG_SET_EMERGENCY_CALLBACK_MODE_DONE);
                 }
             } else {
-                exitEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS);
+                exitEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS, false);
             }
 
             clearEmergencySmsInfo();
@@ -1078,8 +1199,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 +1213,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 +1233,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 +1464,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..b21d45d 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -23,6 +23,8 @@
 import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_NONE;
 import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK;
 import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT;
+import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_RAT_BLOCK;
+import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCK;
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
 
 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAIC;
@@ -2481,7 +2483,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,
@@ -2527,6 +2529,11 @@
                 if ((suggestedAction == SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK)
                         || (suggestedAction == SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT)) {
                     suggestedModemAction = suggestedAction;
+                } else if (mFeatureFlags.addRatRelatedSuggestedActionToImsRegistration()) {
+                    if ((suggestedAction == SUGGESTED_ACTION_TRIGGER_RAT_BLOCK)
+                            || (suggestedAction == SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCK)) {
+                        suggestedModemAction = suggestedAction;
+                    }
                 }
             }
             updateImsRegistrationInfo(REGISTRATION_STATE_NOT_REGISTERED,
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index 4f9b69d..e95433c 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -311,8 +311,11 @@
                         // activeCall could be null if the foreground call is in a disconnected
                         // state.  If either of the calls is null there is no need to check if
                         // one will be disconnected on answer.
+                        // Use VideoProfile.STATE_BIDIRECTIONAL to not affect existing
+                        // implementation. Video state of user response is handled in acceptCall().
                         boolean answeringWillDisconnect =
-                                shouldDisconnectActiveCallOnAnswer(activeCall, imsCall);
+                                shouldDisconnectActiveCallOnAnswer(activeCall, imsCall,
+                                        VideoProfile.STATE_BIDIRECTIONAL);
                         conn.setActiveCallDisconnectedOnAnswer(answeringWillDisconnect);
                     }
                 }
@@ -2215,7 +2218,7 @@
             ImsCall ringingCall = mRingingCall.getImsCall();
             if (mForegroundCall.hasConnections() && mRingingCall.hasConnections()) {
                 answeringWillDisconnect =
-                        shouldDisconnectActiveCallOnAnswer(activeCall, ringingCall);
+                        shouldDisconnectActiveCallOnAnswer(activeCall, ringingCall, videoState);
             }
 
             // Cache video state for pending MT call.
@@ -5499,11 +5502,13 @@
      *
      * @param activeCall The active call.
      * @param incomingCall The incoming call.
+     * @param incomingCallVideoState The media type of incoming call acceptance.
+     *                              {@link VideoProfile.VideoState}
      * @return {@code true} if answering the incoming call will cause the active call to be
      *      disconnected, {@code false} otherwise.
      */
     private boolean shouldDisconnectActiveCallOnAnswer(ImsCall activeCall,
-            ImsCall incomingCall) {
+            ImsCall incomingCall, int incomingCallVideoState) {
 
         if (activeCall == null || incomingCall == null) {
             return false;
@@ -5518,7 +5523,14 @@
         boolean isActiveCallOnWifi = activeCall.isWifiCall();
         boolean isVoWifiEnabled = mImsManager.isWfcEnabledByPlatform()
                 && mImsManager.isWfcEnabledByUser();
-        boolean isIncomingCallAudio = !incomingCall.isVideoCall();
+        boolean isIncomingCallAudio = true;
+        if (!mFeatureFlags.terminateActiveVideoCallWhenAcceptingSecondVideoCallAsAudioOnly()) {
+            isIncomingCallAudio = !incomingCall.isVideoCall();
+        } else {
+            isIncomingCallAudio = !incomingCall.isVideoCall()
+                    || incomingCallVideoState == VideoProfile.STATE_AUDIO_ONLY;
+        }
+
         log("shouldDisconnectActiveCallOnAnswer : isActiveCallVideo=" + isActiveCallVideo +
                 " isActiveCallOnWifi=" + isActiveCallOnWifi + " isIncomingCallAudio=" +
                 isIncomingCallAudio + " isVowifiEnabled=" + isVoWifiEnabled);
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..8bd2547 100644
--- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
@@ -53,7 +53,9 @@
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_RAT_USAGE;
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION;
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_UNKNOWN;
+import static com.android.internal.telephony.util.TelephonyUtils.IS_DEBUGGABLE;
 
+import android.annotation.NonNull;
 import android.app.StatsManager;
 import android.content.Context;
 import android.telephony.SubscriptionManager;
@@ -65,6 +67,7 @@
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.TelephonyStatsLog;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
@@ -136,34 +139,55 @@
             DBG ? 10L * MILLIS_PER_SECOND : 23L * MILLIS_PER_HOUR;
 
     /**
+     * Sets atom pull cool down to 4 minutes for userdebug build.
+     *
+     * <p>Applies to certain atoms: CellularServiceState.
+     */
+    private static final long CELL_SERVICE_MIN_COOLDOWN_MILLIS =
+            DBG ? 10L * MILLIS_PER_SECOND :
+                    IS_DEBUGGABLE ? 4L * MILLIS_PER_MINUTE : 23L * MILLIS_PER_HOUR;
+
+    /**
      * Buckets with less than these many calls will be dropped.
      *
      * <p>Applies to metrics with duration fields. Currently used by voice call RAT usages.
      */
     private static final long MIN_CALLS_PER_BUCKET = DBG ? 0L : 5L;
 
-    /** Bucket size in milliseconds to round call durations into. */
+    /** Bucket size in milliseconds to round call durations info. */
     private static final long DURATION_BUCKET_MILLIS =
             DBG ? 2L * MILLIS_PER_SECOND : 5L * MILLIS_PER_MINUTE;
 
+    /**
+     * Sets smaller bucket size to round call durations for userdebug build.
+     *
+     * <p>Applies to certain atoms: CellularServiceState.
+     */
+    private static final long CELL_SERVICE_DURATION_BUCKET_MILLIS =
+            DBG || IS_DEBUGGABLE ? 2L * MILLIS_PER_SECOND : 5L * MILLIS_PER_MINUTE;
+
     private final PersistAtomsStorage mStorage;
     private final DeviceStateHelper mDeviceStateHelper;
     private final StatsManager mStatsManager;
+    private final VonrHelper mVonrHelper;
     private final AirplaneModeStats mAirplaneModeStats;
     private final Set<DataCallSessionStats> mOngoingDataCallStats = ConcurrentHashMap.newKeySet();
     private static final Random sRandom = new Random();
 
-    public MetricsCollector(Context context) {
-        this(context, new PersistAtomsStorage(context), new DeviceStateHelper(context));
+    public MetricsCollector(Context context, @NonNull FeatureFlags featureFlags) {
+        this(context, new PersistAtomsStorage(context),
+                new DeviceStateHelper(context), new VonrHelper(featureFlags));
     }
 
     /** Allows dependency injection. Used during unit tests. */
     @VisibleForTesting
     public MetricsCollector(
-            Context context, PersistAtomsStorage storage, DeviceStateHelper deviceStateHelper) {
+            Context context, PersistAtomsStorage storage, DeviceStateHelper deviceStateHelper,
+                    VonrHelper vonrHelper) {
         mStorage = storage;
         mDeviceStateHelper = deviceStateHelper;
         mStatsManager = (StatsManager) context.getSystemService(Context.STATS_MANAGER);
+        mVonrHelper = vonrHelper;
         if (mStatsManager != null) {
             // Most (but not all) of these are subject to cooldown specified by MIN_COOLDOWN_MILLIS.
             registerAtom(CELLULAR_DATA_SERVICE_SWITCH);
@@ -308,6 +332,11 @@
         return mDeviceStateHelper;
     }
 
+    /** Returns the {@link VonrHelper}. */
+    public VonrHelper getVonrHelper() {
+        return mVonrHelper;
+    }
+
     /** Updates duration segments and calls {@link PersistAtomsStorage#flushAtoms()}. */
     public void flushAtomsStorage() {
         concludeAll();
@@ -510,7 +539,7 @@
         // Include the latest durations
         concludeServiceStateStats();
         CellularServiceState[] persistAtoms =
-                mStorage.getCellularServiceStates(MIN_COOLDOWN_MILLIS);
+                mStorage.getCellularServiceStates(CELL_SERVICE_MIN_COOLDOWN_MILLIS);
         if (persistAtoms != null) {
             // list is already shuffled when instances were inserted
             Arrays.stream(persistAtoms)
@@ -786,7 +815,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;
         }
@@ -929,12 +959,14 @@
                 state.simSlotIndex,
                 state.isMultiSim,
                 state.carrierId,
-                roundAndConvertMillisToSeconds(state.totalTimeMillis),
+                roundAndConvertMillisToSeconds(state.totalTimeMillis,
+                        CELL_SERVICE_DURATION_BUCKET_MILLIS),
                 state.isEmergencyOnly,
                 state.isInternetPdnUp,
                 state.foldState,
                 state.overrideVoiceService,
-                state.isDataEnabled);
+                state.isDataEnabled,
+                state.isIwlanCrossSim);
     }
 
     private static StatsEvent buildStatsEvent(VoiceCallRatUsage usage) {
@@ -988,7 +1020,12 @@
                 session.lastKnownRat,
                 session.foldState,
                 session.ratSwitchCountAfterConnected,
-                session.handoverInProgress);
+                session.handoverInProgress,
+                session.isIwlanCrossSimAtStart,
+                session.isIwlanCrossSimAtEnd,
+                session.isIwlanCrossSimAtConnected,
+                session.vonrEnabled);
+
     }
 
     private static StatsEvent buildStatsEvent(IncomingSms sms) {
@@ -1060,7 +1097,8 @@
                 dataCallSession.bandAtEnd,
                 dataCallSession.handoverFailureCauses,
                 dataCallSession.handoverFailureRat,
-                dataCallSession.isNonDds);
+                dataCallSession.isNonDds,
+                dataCallSession.isIwlanCrossSim);
     }
 
     private static StatsEvent buildStatsEvent(ImsRegistrationStats stats) {
@@ -1079,7 +1117,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 +1131,8 @@
                 termination.reasonCode,
                 termination.extraCode,
                 termination.extraMessage,
-                termination.count);
+                termination.count,
+                termination.isIwlanCrossSim);
     }
 
     private static StatsEvent buildStatsEvent(NetworkRequestsV2 networkRequests) {
@@ -1356,8 +1396,15 @@
      * Rounds the duration and converts it from milliseconds to seconds.
      */
     private static int roundAndConvertMillisToSeconds(long valueMillis) {
-        long roundedValueMillis = Math.round((double) valueMillis / DURATION_BUCKET_MILLIS)
-                * DURATION_BUCKET_MILLIS;
+        return roundAndConvertMillisToSeconds(valueMillis, DURATION_BUCKET_MILLIS);
+    }
+
+    /**
+     * Rounds the duration and converts it from milliseconds to seconds.
+     */
+    private static int roundAndConvertMillisToSeconds(long valueMillis, long durationBucketSize) {
+        long roundedValueMillis = Math.round((double) valueMillis / durationBucketSize)
+                * durationBucketSize;
         return (int) (roundedValueMillis / MILLIS_PER_SECOND);
     }
 
diff --git a/src/java/com/android/internal/telephony/metrics/PerSimStatus.java b/src/java/com/android/internal/telephony/metrics/PerSimStatus.java
index bc1edc3..70ff491 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
@@ -278,7 +303,7 @@
     }
 
     /** Returns true if VoNR is enabled */
-    private static boolean isVonrEnabled(Phone phone) {
+    static boolean isVonrEnabled(Phone phone) {
         TelephonyManager telephonyManager =
                 phone.getContext()
                         .getSystemService(TelephonyManager.class);
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..9cf53c9 100644
--- a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
+++ b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
@@ -36,6 +36,7 @@
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SIGNAL_STRENGTH_AT_END__SIGNAL_STRENGTH_GREAT;
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SIGNAL_STRENGTH_AT_END__SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.net.wifi.WifiInfo;
@@ -53,6 +54,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;
@@ -68,6 +70,7 @@
 import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.analytics.TelephonyAnalytics;
 import com.android.internal.telephony.analytics.TelephonyAnalytics.CallAnalytics;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
@@ -155,6 +158,7 @@
      */
     private final VoiceCallRatTracker mRatUsage = new VoiceCallRatTracker();
 
+    private final @NonNull FeatureFlags mFlags;
     private final int mPhoneId;
     private final Phone mPhone;
 
@@ -164,10 +168,13 @@
     private final DeviceStateHelper mDeviceStateHelper =
             PhoneFactory.getMetricsCollector().getDeviceStateHelper();
 
-    public VoiceCallSessionStats(int phoneId, Phone phone) {
+    private final VonrHelper mVonrHelper =
+            PhoneFactory.getMetricsCollector().getVonrHelper();
+
+    public VoiceCallSessionStats(int phoneId, Phone phone, @NonNull FeatureFlags featureFlags) {
         mPhoneId = phoneId;
         mPhone = phone;
-
+        mFlags = featureFlags;
         DataConnectionStateTracker.getInstance(phoneId).start(phone);
     }
 
@@ -456,6 +463,10 @@
         @VideoState int videoState = conn.getVideoState();
         VoiceCallSession proto = new VoiceCallSession();
 
+        if (mFlags.vonrEnabledMetric()) {
+            mVonrHelper.updateVonrEnabledState();
+        }
+
         proto.bearerAtStart = bearer;
         proto.bearerAtEnd = bearer;
         proto.direction = getDirection(conn);
@@ -484,6 +495,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
@@ -550,6 +566,10 @@
         // Set device fold state
         proto.foldState = mDeviceStateHelper.getFoldState();
 
+        if (mFlags.vonrEnabledMetric()) {
+            proto.vonrEnabled = mVonrHelper.getVonrEnabled(mPhone.getSubId());
+        }
+
         mAtomsStorage.addVoiceCallSession(proto);
 
         // merge RAT usages to PersistPullers when the call session ends (i.e. no more active calls)
@@ -609,7 +629,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 +668,7 @@
                 proto.lastKnownRat = rat;
             }
         }
+        proto.isIwlanCrossSimAtEnd = isCrossSimCall(mPhone);
     }
 
     private void finishImsCall(int id, ImsReasonInfo reasonInfo, long durationMillis) {
@@ -905,6 +928,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/metrics/VonrHelper.java b/src/java/com/android/internal/telephony/metrics/VonrHelper.java
new file mode 100644
index 0000000..8f14a86
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/VonrHelper.java
@@ -0,0 +1,80 @@
+/*
+ * 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 com.android.internal.telephony.metrics.MetricsCollector.getPhonesIfAny;
+import static com.android.internal.telephony.metrics.PerSimStatus.isVonrEnabled;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.os.HandlerThread;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.flags.FeatureFlags;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Vonr state handler.
+ *
+ * <p>This class is instantiated in {@link MetricsCollector}.
+*/
+public class VonrHelper {
+    private final @NonNull FeatureFlags mFlags;
+
+    private Handler mHandler;
+    private Map<Integer, Boolean> mPhoneVonrState = new ConcurrentHashMap<>();
+
+    public VonrHelper(@NonNull FeatureFlags featureFlags) {
+        this.mFlags = featureFlags;
+        if (mFlags.vonrEnabledMetric()) {
+            HandlerThread mHandlerThread = new HandlerThread("VonrHelperThread");
+            mHandlerThread.start();
+            mHandler = new Handler(mHandlerThread.getLooper());
+        }
+    }
+
+    /** Update vonr_enabled state */
+    public void updateVonrEnabledState() {
+        if (mFlags.vonrEnabledMetric()) {
+            mHandler.post(mVonrRunnable);
+        }
+    }
+
+    @VisibleForTesting
+    protected Runnable mVonrRunnable =
+            new Runnable() {
+                @Override
+                public void run() {
+                    mPhoneVonrState.clear();
+                    for (Phone phone : getPhonesIfAny()) {
+                        mPhoneVonrState.put(phone.getSubId(), isVonrEnabled(phone));
+                    }
+                }
+            };
+
+    /** Get vonr_enabled per subId */
+    public boolean getVonrEnabled(int subId) {
+        if (mFlags.vonrEnabledMetric()) {
+            return mPhoneVonrState.getOrDefault(subId, false);
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/nitz/TEST_MAPPING b/src/java/com/android/internal/telephony/nitz/TEST_MAPPING
index 2bcb04e..4063803 100644
--- a/src/java/com/android/internal/telephony/nitz/TEST_MAPPING
+++ b/src/java/com/android/internal/telephony/nitz/TEST_MAPPING
@@ -1,6 +1,5 @@
 {
-  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
-  "postsubmit": [
+  "presubmit": [
     {
       "name": "FrameworksTelephonyTests",
       "options": [
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..b4ce9c7 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -56,6 +56,7 @@
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.ServiceSpecificException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.provider.Settings;
@@ -1735,6 +1736,7 @@
         if (!mSatelliteModemInterface.isSatelliteServiceSupported()) {
             return SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED;
         }
+        logd("registerForSatelliteDatagram: callback=" + callback);
         return mDatagramController.registerForSatelliteDatagram(subId, callback);
     }
 
@@ -1755,6 +1757,7 @@
         if (!mSatelliteModemInterface.isSatelliteServiceSupported()) {
             return;
         }
+        logd("unregisterForSatelliteDatagram: callback=" + callback);
         mDatagramController.unregisterForSatelliteDatagram(subId, callback);
     }
 
@@ -2003,23 +2006,29 @@
     }
 
     /**
-     * Registers for NTN signal strength changed from satellite modem.
+     * Registers for NTN signal strength changed from satellite modem. If the registration operation
+     * is not successful, a {@link ServiceSpecificException} that contains
+     * {@link SatelliteManager.SatelliteResult} will be thrown.
      *
      * @param subId The id of the subscription to request for.
-     * @param callback The callback to handle the non-terrestrial network signal strength changed
-     * event.
+     * @param callback The callback to handle the NTN signal strength changed event. If the
+     * operation is successful, {@link INtnSignalStrengthCallback#onNtnSignalStrengthChanged(
+     * NtnSignalStrength)} will return an instance of {@link NtnSignalStrength} with a value of
+     * {@link NtnSignalStrength.NtnSignalStrengthLevel} when the signal strength of non-terrestrial
+     * network has changed.
      *
-     * @return The {@link SatelliteManager.SatelliteResult} result of the operation.
+     * @throws ServiceSpecificException If the callback registration operation fails.
      */
-    @SatelliteManager.SatelliteResult public int registerForNtnSignalStrengthChanged(
-            int subId, @NonNull INtnSignalStrengthCallback callback) {
+    public void registerForNtnSignalStrengthChanged(int subId,
+            @NonNull INtnSignalStrengthCallback callback) throws RemoteException {
         if (DBG) logd("registerForNtnSignalStrengthChanged()");
 
         int error = evaluateOemSatelliteRequestAllowed(true);
-        if (error != SATELLITE_RESULT_SUCCESS) return error;
-
-        mNtnSignalStrengthChangedListeners.put(callback.asBinder(), callback);
-        return SATELLITE_RESULT_SUCCESS;
+        if (error == SATELLITE_RESULT_SUCCESS) {
+            mNtnSignalStrengthChangedListeners.put(callback.asBinder(), callback);
+        } else {
+            throw new ServiceSpecificException(error);
+        }
     }
 
     /**
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/security/CellularIdentifierDisclosureNotifier.java b/src/java/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifier.java
new file mode 100644
index 0000000..f1845c5
--- /dev/null
+++ b/src/java/com/android/internal/telephony/security/CellularIdentifierDisclosureNotifier.java
@@ -0,0 +1,86 @@
+/*
+ * 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.security;
+
+import android.telephony.CellularIdentifierDisclosure;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.telephony.Rlog;
+
+/**
+ * Encapsulates logic to emit notifications to the user that their cellular identifiers were
+ * disclosed in the clear.
+ *
+ * <p>This class will either emit notifications through SafetyCenterManager if SafetyCenter exists
+ * on a device, or it will emit system notifications otherwise.
+ *
+ * @hide
+ */
+public class CellularIdentifierDisclosureNotifier {
+
+    private static final String TAG = "CellularIdentifierDisclosureNotifier";
+    private static CellularIdentifierDisclosureNotifier sInstance = null;
+    private boolean mEnabled = false;
+
+    @VisibleForTesting
+    public CellularIdentifierDisclosureNotifier() {}
+
+    /**
+     * Add a CellularIdentifierDisclosure to be tracked by this instance.
+     * If appropriate, this will trigger a user notification.
+     */
+    public void addDisclosure(CellularIdentifierDisclosure disclosure) {
+        // TODO (b/308985417) this is a stub method for now. Logic
+        // for tracking disclosures and emitting notifications will flow
+        // from here.
+        Rlog.d(TAG, "Identifier disclosure reported: " + disclosure);
+    }
+
+    /**
+     * Get a singleton CellularIdentifierDisclosureNotifier.
+     */
+    public static synchronized CellularIdentifierDisclosureNotifier getInstance() {
+        if (sInstance == null) {
+            sInstance = new CellularIdentifierDisclosureNotifier();
+        }
+
+        return sInstance;
+    }
+
+    /**
+     * Re-enable if previously disabled. This means that {@code addDisclsoure} will start tracking
+     * disclosures again and potentially emitting notifications.
+     */
+    public void enable() {
+        Rlog.d(TAG, "enabled");
+        mEnabled = true;
+    }
+
+    /**
+     * Clear all internal state and prevent further notifications until optionally re-enabled.
+     * This can be used to in response to a user disabling the feature to emit notifications.
+     * If {@code addDisclosure} is called while in a disabled state, disclosures will be dropped.
+     */
+    public void disable() {
+        Rlog.d(TAG, "disabled");
+        mEnabled = false;
+    }
+
+    public boolean isEnabled() {
+        return mEnabled;
+    }
+}
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
index 1bf866b..a8d05a3 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);
     }
 
     /**
@@ -2856,12 +2879,14 @@
      * @return The subscription Id default to use.
      */
     private int getDefaultAsUser(@UserIdInt int userId, int defaultValue) {
-        List<SubscriptionInfoInternal> subInfos =
-                getSubscriptionInfoStreamAsUser(UserHandle.of(userId))
-                        .filter(SubscriptionInfoInternal::isActive)
-                        .toList();
-        if (subInfos.size() == 1) {
-            return subInfos.get(0).getSubscriptionId();
+        if (mFeatureFlags.workProfileApiSplit()) {
+            List<SubscriptionInfoInternal> subInfos =
+                    getSubscriptionInfoStreamAsUser(UserHandle.of(userId))
+                            .filter(SubscriptionInfoInternal::isActive)
+                            .toList();
+            if (subInfos.size() == 1) {
+                return subInfos.get(0).getSubscriptionId();
+            }
         }
         return defaultValue;
     }
@@ -3885,16 +3910,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 +4151,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/src/java/com/android/internal/telephony/uicc/UiccPort.java b/src/java/com/android/internal/telephony/uicc/UiccPort.java
index d89eab1..fd8f1c4 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccPort.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccPort.java
@@ -29,6 +29,8 @@
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.IccLogicalChannelRequest;
 import com.android.internal.telephony.TelephonyComponentFactory;
+import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.flags.FeatureFlagsImpl;
 import com.android.telephony.Rlog;
 
 import java.io.FileDescriptor;
@@ -39,6 +41,7 @@
 public class UiccPort {
     protected static final String LOG_TAG = "UiccPort";
     protected static final boolean DBG = true;
+    private static @NonNull FeatureFlags sFlags = new FeatureFlagsImpl();
 
     // The lock object is created by UiccSlot that owns this UiccCard - this is to share the lock
     // between UiccSlot, UiccCard, EuiccCard, UiccPort, EuiccPort and UiccProfile for now.
@@ -80,7 +83,7 @@
             if (mUiccProfile == null) {
                 mUiccProfile = TelephonyComponentFactory.getInstance()
                         .inject(UiccProfile.class.getName()).makeUiccProfile(
-                                mContext, mCi, ics, mPhoneId, uiccCard, mLock);
+                                mContext, mCi, ics, mPhoneId, uiccCard, mLock, sFlags);
             } else {
                 mUiccProfile.update(mContext, mCi, ics);
             }
diff --git a/src/java/com/android/internal/telephony/uicc/UiccProfile.java b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
index 83db022..0457971 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccProfile.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
@@ -20,6 +20,7 @@
 import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_VERIFICATION_FAILURE;
 import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_VERIFICATION_SUCCESS;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.usage.UsageStatsManager;
@@ -65,6 +66,7 @@
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.TelephonyStatsLog;
 import com.android.internal.telephony.cat.CatService;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
@@ -103,6 +105,7 @@
 
     private static final String OPERATOR_BRAND_OVERRIDE_PREFIX = "operator_branding_";
 
+    private final @NonNull FeatureFlags mFlags;
     // The lock object is created by UiccSlot that owns the UiccCard that owns this UiccProfile.
     // This is to share the lock between UiccSlot, UiccCard and UiccProfile for now.
     private final Object mLock;
@@ -326,8 +329,9 @@
     };
 
     public UiccProfile(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId,
-            UiccCard uiccCard, Object lock) {
+            UiccCard uiccCard, Object lock, @NonNull FeatureFlags flags) {
         if (DBG) log("Creating profile");
+        mFlags = flags;
         mLock = lock;
         mUiccCard = uiccCard;
         mPhoneId = phoneId;
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/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index 2dac867..da3920e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -779,7 +779,7 @@
         doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
         doAnswer(invocation -> mNetworkId++).when(mNetwork).getNetId();
         doReturn(mNetwork).when(mConnectivityManager).registerNetworkAgent(
-                any(), any(), any(), any(), any(), any(), any(), anyInt());
+                any(), any(), any(), any(), any(), any(), anyInt());
 
         doReturn(true).when(mEuiccManager).isEnabled();
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java b/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
index 827511d..f7f67a5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
@@ -23,6 +23,7 @@
 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.telephony.CellIdentityGsm;
 import android.telephony.CellInfo;
@@ -34,12 +35,14 @@
 import android.telephony.ims.ImsCallProfile;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.imsphone.ImsPhoneCall;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -51,6 +54,8 @@
 
     private DefaultPhoneNotifier mDefaultPhoneNotifierUT;
 
+    private FeatureFlags mFeatureFlags;
+
     // Mocked classes
     SignalStrength mSignalStrength;
     CellInfo mCellInfo;
@@ -66,13 +71,14 @@
         super.setUp(getClass().getSimpleName());
         mSignalStrength = mock(SignalStrength.class);
         mCellInfo = mock(CellInfo.class);
+        mFeatureFlags = Mockito.mock(FeatureFlags.class);
         mForeGroundCall = mock(GsmCdmaCall.class);
         mBackGroundCall = mock(GsmCdmaCall.class);
         mRingingCall = mock(GsmCdmaCall.class);
         mImsForeGroundCall = mock(ImsPhoneCall.class);
         mImsBackGroundCall = mock(ImsPhoneCall.class);
         mImsRingingCall = mock(ImsPhoneCall.class);
-        mDefaultPhoneNotifierUT = new DefaultPhoneNotifier(mContext);
+        mDefaultPhoneNotifierUT = new DefaultPhoneNotifier(mContext, mFeatureFlags);
     }
 
     @After
@@ -94,6 +100,22 @@
 
     @Test @SmallTest
     public void testNotifyDataActivity() throws Exception {
+        when(mFeatureFlags.notifyDataActivityChangedWithSlot()).thenReturn(false);
+        //mock data activity state
+        doReturn(TelephonyManager.DATA_ACTIVITY_NONE).when(mPhone).getDataActivityState();
+        mDefaultPhoneNotifierUT.notifyDataActivity(mPhone);
+        verify(mTelephonyRegistryManager).notifyDataActivityChanged(eq(0),
+                eq(TelephonyManager.DATA_ACTIVITY_NONE));
+
+        doReturn(1).when(mPhone).getSubId();
+        doReturn(TelephonyManager.DATA_ACTIVITY_IN).when(mPhone).getDataActivityState();
+        mDefaultPhoneNotifierUT.notifyDataActivity(mPhone);
+        verify(mTelephonyRegistryManager).notifyDataActivityChanged(eq(1),
+                eq(TelephonyManager.DATA_ACTIVITY_IN));
+    }
+    @Test @SmallTest
+    public void testNotifyDataActivityWithSlot() throws Exception {
+        when(mFeatureFlags.notifyDataActivityChangedWithSlot()).thenReturn(true);
         //mock data activity state
         doReturn(TelephonyManager.DATA_ACTIVITY_NONE).when(mPhone).getDataActivityState();
         doReturn(PHONE_ID).when(mPhone).getPhoneId();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
index 2fdff9e..369980c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
@@ -41,7 +41,6 @@
 import androidx.test.filters.FlakyTest;
 
 import com.android.internal.telephony.PhoneInternalInterface.DialArgs;
-import com.android.internal.telephony.domainselection.DomainSelectionResolver;
 
 import org.junit.After;
 import org.junit.Assert;
@@ -67,16 +66,12 @@
     // Mocked classes
     private GsmCdmaConnection mConnection;
     private Handler mHandler;
-    private DomainSelectionResolver mDomainSelectionResolver;
 
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
         mConnection = mock(GsmCdmaConnection.class);
         mHandler = mock(Handler.class);
-        mDomainSelectionResolver = mock(DomainSelectionResolver.class);
-        doReturn(false).when(mDomainSelectionResolver).isDomainSelectionSupported();
-        DomainSelectionResolver.setDomainSelectionResolver(mDomainSelectionResolver);
         mSimulatedCommands.setRadioPower(true, null);
         mPhone.mCi = this.mSimulatedCommands;
 
@@ -91,7 +86,6 @@
     @After
     public void tearDown() throws Exception {
         mCTUT = null;
-        DomainSelectionResolver.setDomainSelectionResolver(null);
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index 2563fdf..935da5a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -21,6 +21,7 @@
 import static com.android.internal.telephony.Phone.EVENT_ICC_CHANGED;
 import static com.android.internal.telephony.Phone.EVENT_IMS_DEREGISTRATION_TRIGGERED;
 import static com.android.internal.telephony.Phone.EVENT_RADIO_AVAILABLE;
+import static com.android.internal.telephony.Phone.EVENT_SET_IDENTIFIER_DISCLOSURE_ENABLED_DONE;
 import static com.android.internal.telephony.Phone.EVENT_SET_NULL_CIPHER_AND_INTEGRITY_DONE;
 import static com.android.internal.telephony.Phone.EVENT_SRVCC_STATE_CHANGED;
 import static com.android.internal.telephony.Phone.EVENT_UICC_APPS_ENABLEMENT_STATUS_CHANGED;
@@ -72,6 +73,7 @@
 import android.telephony.CellIdentity;
 import android.telephony.CellIdentityCdma;
 import android.telephony.CellIdentityGsm;
+import android.telephony.CellularIdentifierDisclosure;
 import android.telephony.LinkCapacityEstimate;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.RadioAccessFamily;
@@ -131,7 +133,6 @@
     private UiccSlot mUiccSlot;
     private CommandsInterface mMockCi;
     private AdnRecordCache adnRecordCache;
-    private DomainSelectionResolver mDomainSelectionResolver;
 
     //mPhoneUnderTest
     private GsmCdmaPhone mPhoneUT;
@@ -172,13 +173,10 @@
         mUiccPort = Mockito.mock(UiccPort.class);
         mMockCi = Mockito.mock(CommandsInterface.class);
         adnRecordCache = Mockito.mock(AdnRecordCache.class);
-        mDomainSelectionResolver = Mockito.mock(DomainSelectionResolver.class);
         mFeatureFlags = Mockito.mock(FeatureFlags.class);
 
         doReturn(false).when(mSST).isDeviceShuttingDown();
         doReturn(true).when(mImsManager).isVolteEnabledByPlatform();
-        doReturn(false).when(mDomainSelectionResolver).isDomainSelectionSupported();
-        DomainSelectionResolver.setDomainSelectionResolver(mDomainSelectionResolver);
 
         mPhoneUT = new GsmCdmaPhone(mContext, mSimulatedCommands, mNotifier, true, 0,
             PhoneConstants.PHONE_TYPE_GSM, mTelephonyComponentFactory, (c, p) -> mImsManager,
@@ -197,7 +195,6 @@
     public void tearDown() throws Exception {
         mPhoneUT.removeCallbacksAndMessages(null);
         mPhoneUT = null;
-        DomainSelectionResolver.setDomainSelectionResolver(null);
         try {
             DeviceConfig.setProperties(mPreTestProperties);
         } catch (DeviceConfig.BadConfigException e) {
@@ -2761,4 +2758,183 @@
         assertEquals(Phone.IMEI_TYPE_SECONDARY, mPhoneUT.getImeiType());
         assertEquals(FAKE_IMEI, mPhoneUT.getImei());
     }
+
+    @Test
+    public void testCellularIdentifierDisclosureFlagOff() {
+        when(mFeatureFlags.enableIdentifierDisclosureTransparency()).thenReturn(false);
+
+        GsmCdmaPhone phoneUT =
+                new GsmCdmaPhone(
+                        mContext,
+                        mSimulatedCommands,
+                        mNotifier,
+                        true,
+                        0,
+                        PhoneConstants.PHONE_TYPE_GSM,
+                        mTelephonyComponentFactory,
+                        (c, p) -> mImsManager,
+                        mFeatureFlags);
+        phoneUT.mCi = mMockCi;
+
+        verify(mMockCi, never())
+                .registerForCellularIdentifierDisclosures(
+                        any(Handler.class), anyInt(), any(Object.class));
+    }
+
+    @Test
+    public void testCellularIdentifierDisclosureFlagOn() {
+        when(mFeatureFlags.enableIdentifierDisclosureTransparency()).thenReturn(true);
+
+        Phone phoneUT =
+                new GsmCdmaPhone(
+                        mContext,
+                        mMockCi,
+                        mNotifier,
+                        true,
+                        0,
+                        PhoneConstants.PHONE_TYPE_GSM,
+                        mTelephonyComponentFactory,
+                        (c, p) -> mImsManager,
+                        mFeatureFlags);
+
+        verify(mMockCi, times(1))
+                .registerForCellularIdentifierDisclosures(
+                        eq(phoneUT),
+                        eq(Phone.EVENT_CELL_IDENTIFIER_DISCLOSURE),
+                        nullable(Object.class));
+    }
+
+    @Test
+    public void testCellularIdentifierDisclosure_disclosureEventAddedToNotifier() {
+        when(mFeatureFlags.enableIdentifierDisclosureTransparency()).thenReturn(true);
+
+        Phone phoneUT =
+                new GsmCdmaPhone(
+                        mContext,
+                        mMockCi,
+                        mNotifier,
+                        true,
+                        0,
+                        PhoneConstants.PHONE_TYPE_GSM,
+                        mTelephonyComponentFactory,
+                        (c, p) -> mImsManager,
+                        mFeatureFlags);
+
+        CellularIdentifierDisclosure disclosure =
+                new CellularIdentifierDisclosure(
+                        CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST,
+                        CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMSI,
+                        "001001",
+                        false);
+        phoneUT.sendMessage(
+                mPhoneUT.obtainMessage(
+                        Phone.EVENT_CELL_IDENTIFIER_DISCLOSURE,
+                        new AsyncResult(null, disclosure, null)));
+        processAllMessages();
+
+        verify(mIdentifierDisclosureNotifier, times(1)).addDisclosure(eq(disclosure));
+    }
+
+    @Test
+    public void testCellularIdentifierDisclosure_disclosureEventNull() {
+        when(mFeatureFlags.enableIdentifierDisclosureTransparency()).thenReturn(true);
+
+        Phone phoneUT =
+                new GsmCdmaPhone(
+                        mContext,
+                        mMockCi,
+                        mNotifier,
+                        true,
+                        0,
+                        PhoneConstants.PHONE_TYPE_GSM,
+                        mTelephonyComponentFactory,
+                        (c, p) -> mImsManager,
+                        mFeatureFlags);
+
+        phoneUT.sendMessage(
+                mPhoneUT.obtainMessage(
+                        Phone.EVENT_CELL_IDENTIFIER_DISCLOSURE, new AsyncResult(null, null, null)));
+        processAllMessages();
+
+        verify(mIdentifierDisclosureNotifier, never())
+                .addDisclosure(any(CellularIdentifierDisclosure.class));
+    }
+
+    @Test
+    public void testCellularIdentifierDisclosure_noModemCallOnRadioAvailable_FlagOff() {
+        when(mFeatureFlags.enableIdentifierDisclosureTransparency()).thenReturn(false);
+        GsmCdmaPhone phoneUT = makeNewPhoneUT();
+        assertFalse(phoneUT.isIdentifierDisclosureTransparencySupported());
+
+        sendRadioAvailableToPhone(phoneUT);
+
+        verify(mMockCi, never()).setCellularIdentifierTransparencyEnabled(anyBoolean(),
+                any(Message.class));
+        assertFalse(phoneUT.isIdentifierDisclosureTransparencySupported());
+    }
+
+    @Test
+    public void testCellularIdentifierDisclosure_unsupportedByModemOnRadioAvailable() {
+        when(mFeatureFlags.enableIdentifierDisclosureTransparency()).thenReturn(true);
+        GsmCdmaPhone phoneUT = makeNewPhoneUT();
+        assertFalse(phoneUT.isIdentifierDisclosureTransparencySupported());
+
+        // The following block emulates incoming messages from the modem in the case that
+        // the modem does not support the new HAL APIs. We expect the phone instance to attempt
+        // to set cipher-identifier-transparency-enabled state when the radio becomes available.
+        sendRadioAvailableToPhone(phoneUT);
+        verify(mMockCi, times(1)).setCellularIdentifierTransparencyEnabled(anyBoolean(),
+                any(Message.class));
+        sendRequestNotSupportedToPhone(phoneUT, EVENT_SET_IDENTIFIER_DISCLOSURE_ENABLED_DONE);
+
+        assertFalse(phoneUT.isIdentifierDisclosureTransparencySupported());
+    }
+
+    @Test
+    public void testCellularIdentifierDisclosure_supportedByModem() {
+        when(mFeatureFlags.enableIdentifierDisclosureTransparency()).thenReturn(true);
+        GsmCdmaPhone phoneUT = makeNewPhoneUT();
+        assertFalse(phoneUT.isIdentifierDisclosureTransparencySupported());
+
+        // The following block emulates incoming messages from the modem in the case that
+        // the modem supports the new HAL APIs. We expect the phone instance to attempt
+        // to set cipher-identifier-transparency-enabled state when the radio becomes available.
+        sendRadioAvailableToPhone(phoneUT);
+        verify(mMockCi, times(1)).setCellularIdentifierTransparencyEnabled(anyBoolean(),
+                any(Message.class));
+        sendIdentifierDisclosureEnabledSuccessToPhone(phoneUT);
+
+        assertTrue(phoneUT.isIdentifierDisclosureTransparencySupported());
+    }
+
+    private void sendRadioAvailableToPhone(GsmCdmaPhone phone) {
+        phone.sendMessage(phone.obtainMessage(EVENT_RADIO_AVAILABLE,
+                new AsyncResult(null, new int[]{ServiceState.RIL_RADIO_TECHNOLOGY_GSM}, null)));
+        processAllMessages();
+    }
+
+    private void sendRequestNotSupportedToPhone(GsmCdmaPhone phone, int eventId) {
+        phone.sendMessage(phone.obtainMessage(eventId, new AsyncResult(null, null,
+                new CommandException(CommandException.Error.REQUEST_NOT_SUPPORTED))));
+        processAllMessages();
+    }
+
+    private void sendIdentifierDisclosureEnabledSuccessToPhone(GsmCdmaPhone phone) {
+        phone.sendMessage(phone.obtainMessage(EVENT_SET_IDENTIFIER_DISCLOSURE_ENABLED_DONE,
+                new AsyncResult(null, null, null)));
+        processAllMessages();
+    }
+
+    private GsmCdmaPhone makeNewPhoneUT() {
+        return new GsmCdmaPhone(
+                mContext,
+                mMockCi,
+                mNotifier,
+                true,
+                0,
+                PhoneConstants.PHONE_TYPE_GSM,
+                mTelephonyComponentFactory,
+                (c, p) -> mImsManager,
+                mFeatureFlags);
+    }
 }
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/PhoneConfigurationManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
index 361574e..ea4705c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneConfigurationManagerTest.java
@@ -45,6 +45,8 @@
 import android.testing.TestableLooper;
 import android.util.Log;
 
+import com.android.internal.telephony.flags.FeatureFlags;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -67,6 +69,7 @@
 
     private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 1;
     PhoneConfigurationManager mPcm;
+    private FeatureFlags mFeatureFlags;
 
     @Before
     public void setUp() throws Exception {
@@ -74,6 +77,7 @@
         mHandler = mock(Handler.class);
         mMockCi0 = mock(CommandsInterface.class);
         mMockCi1 = mock(CommandsInterface.class);
+        mFeatureFlags = Mockito.mock(FeatureFlags.class);
         mPhone1 = mock(Phone.class);
         mMi = mock(PhoneConfigurationManager.MockableInterface.class);
         mPhone.mCi = mMockCi0;
@@ -95,7 +99,7 @@
     private void init(int numOfSim) throws Exception {
         doReturn(numOfSim).when(mTelephonyManager).getActiveModemCount();
         replaceInstance(PhoneConfigurationManager.class, "sInstance", null, null);
-        mPcm = PhoneConfigurationManager.init(mContext);
+        mPcm = PhoneConfigurationManager.init(mContext, mFeatureFlags);
         replaceInstance(PhoneConfigurationManager.class, "mMi", mPcm, mMi);
         processAllMessages();
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ProxyControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ProxyControllerTest.java
index 65ab664..180a5e7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ProxyControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ProxyControllerTest.java
@@ -55,7 +55,7 @@
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
         replaceInstance(ProxyController.class, "sProxyController", null, null);
-        mProxyController = ProxyController.getInstance(mContext);
+        mProxyController = new ProxyController(mContext, mFeatureFlags);
     }
 
     @After
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 9e067a2..c542857 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -99,6 +99,7 @@
 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
 import com.android.internal.telephony.data.AccessNetworksManager;
 import com.android.internal.telephony.data.DataNetworkController;
+import com.android.internal.telephony.emergency.EmergencyStateTracker;
 import com.android.internal.telephony.metrics.ServiceStateStats;
 import com.android.internal.telephony.satellite.SatelliteController;
 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
@@ -145,6 +146,7 @@
     private ServiceStateTrackerTestHandler mSSTTestHandler;
     private PersistableBundle mBundle;
     private SatelliteController mSatelliteController;
+    private EmergencyStateTracker mEmergencyStateTracker;
 
     private static final int EVENT_REGISTERED_TO_NETWORK = 1;
     private static final int EVENT_SUBSCRIPTION_INFO_READY = 2;
@@ -390,6 +392,9 @@
         sendCarrierConfigUpdate(PHONE_ID);
         waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
 
+        mEmergencyStateTracker = Mockito.mock(EmergencyStateTracker.class);
+        replaceInstance(EmergencyStateTracker.class, "INSTANCE", null, mEmergencyStateTracker);
+
         logd("ServiceStateTrackerTest -Setup!");
     }
 
@@ -480,6 +485,66 @@
         waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
         verify(mDataNetworkController, times(1)).unregisterDataNetworkControllerCallback(any());
         assertEquals(TelephonyManager.RADIO_POWER_OFF, mSimulatedCommands.getRadioState());
+        verify(mEmergencyStateTracker, never()).onCellularRadioPowerOffRequested();
+    }
+
+    @Test
+    public void testSetRadioPowerExitEmergencyMode() throws Exception {
+        doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
+
+        // Set up DSDS environment
+        GsmCdmaPhone phone2 = Mockito.mock(GsmCdmaPhone.class);
+        DataNetworkController dataNetworkController_phone2 =
+                Mockito.mock(DataNetworkController.class);
+        mPhones = new Phone[] {mPhone, phone2};
+        replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+        doReturn(dataNetworkController_phone2).when(phone2).getDataNetworkController();
+        doReturn(mSST).when(phone2).getServiceStateTracker();
+        doReturn(false).when(mDataNetworkController).areAllDataDisconnected();
+        doReturn(false).when(dataNetworkController_phone2).areAllDataDisconnected();
+        doReturn(1).when(mPhone).getSubId();
+        doReturn(2).when(phone2).getSubId();
+
+        // Start with radio on
+        sst.setRadioPower(true);
+        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+        assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
+
+        // Turn on APM
+        sst.setRadioPower(false);
+        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+        assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
+
+        // Verify checking emergency mode
+        verify(mEmergencyStateTracker).onCellularRadioPowerOffRequested();
+
+        // Verify that both subs are waiting for all data disconnected
+        verify(mDataNetworkController).tearDownAllDataNetworks(
+                eq(3 /* TEAR_DOWN_REASON_AIRPLANE_MODE_ON */));
+        verify(dataNetworkController_phone2, never()).tearDownAllDataNetworks(anyInt());
+        ArgumentCaptor<DataNetworkController.DataNetworkControllerCallback> callback1 =
+                ArgumentCaptor.forClass(DataNetworkController.DataNetworkControllerCallback.class);
+        ArgumentCaptor<DataNetworkController.DataNetworkControllerCallback> callback2 =
+                ArgumentCaptor.forClass(DataNetworkController.DataNetworkControllerCallback.class);
+        verify(mDataNetworkController, times(1)).registerDataNetworkControllerCallback(
+                callback1.capture());
+        verify(dataNetworkController_phone2, times(1)).registerDataNetworkControllerCallback(
+                callback2.capture());
+
+        // Data disconnected on sub 2, still waiting for data disconnected on sub 1
+        doReturn(true).when(dataNetworkController_phone2).areAllDataDisconnected();
+        callback2.getValue().onAnyDataNetworkExistingChanged(false);
+        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+        assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
+        verify(dataNetworkController_phone2, times(1)).unregisterDataNetworkControllerCallback(
+                any());
+
+        // Data disconnected on sub 1, radio should power off now
+        doReturn(true).when(mDataNetworkController).areAllDataDisconnected();
+        callback1.getValue().onAnyDataNetworkExistingChanged(false);
+        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+        verify(mDataNetworkController, times(1)).unregisterDataNetworkControllerCallback(any());
+        assertEquals(TelephonyManager.RADIO_POWER_OFF, mSimulatedCommands.getRadioState());
     }
 
     @Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthControllerTest.java
index 98980a1..fa89082 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthControllerTest.java
@@ -16,6 +16,9 @@
 
 package com.android.internal.telephony;
 
+import static android.telephony.ServiceState.STATE_IN_SERVICE;
+import static android.telephony.ServiceState.STATE_OUT_OF_SERVICE;
+import static android.telephony.ServiceState.STATE_POWER_OFF;
 import static android.telephony.SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP;
 import static android.telephony.SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI;
 import static android.telephony.SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP;
@@ -27,14 +30,19 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyList;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
 import android.os.PersistableBundle;
@@ -1269,6 +1277,238 @@
         sendCarrierConfigUpdate();
     }
 
+    @Test
+    public void testInvalidCarrierConfig_NTN_LTE_RSRP_thresholdIsTooSmall() {
+        // 4 threshold integers must be within the boundaries [-140, -44]
+        mBundle.putIntArray(CarrierConfigManager.KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -141, /* SIGNAL_STRENGTH_POOR */
+                        -118, /* SIGNAL_STRENGTH_MODERATE */
+                        -108, /* SIGNAL_STRENGTH_GOOD */
+                        -98  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+
+    @Test
+    public void testInvalidCarrierConfig_NTN_LTE_RSRP_thresholdIsTooLarge() {
+        // 4 threshold integers must be within the boundaries [-140, -44]
+        mBundle.putIntArray(CarrierConfigManager.KEY_NTN_LTE_RSRQ_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -128, /* SIGNAL_STRENGTH_POOR */
+                        -118, /* SIGNAL_STRENGTH_MODERATE */
+                        -108, /* SIGNAL_STRENGTH_GOOD */
+                        -43,  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testInvalidCarrierConfig_NTN_LTE_RSRQ_thresholdIsTooSmall() {
+        // 4 threshold integers must be within the boundaries [-34, 3]
+        mBundle.putIntArray(CarrierConfigManager.KEY_NTN_LTE_RSRQ_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -35, /* SIGNAL_STRENGTH_POOR */
+                        -17, /* SIGNAL_STRENGTH_MODERATE */
+                        -14, /* SIGNAL_STRENGTH_GOOD */
+                        -11  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+
+    @Test
+    public void testInvalidCarrierConfig_NTN_LTE_RSRQ_thresholdIsTooLarge() {
+        // 4 threshold integers must be within the boundaries [-34, 3]
+        mBundle.putIntArray(CarrierConfigManager.KEY_NTN_LTE_RSRQ_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -20, /* SIGNAL_STRENGTH_POOR */
+                        -17, /* SIGNAL_STRENGTH_MODERATE */
+                        -14, /* SIGNAL_STRENGTH_GOOD */
+                        4  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testInvalidCarrierConfig_NTN_LTE_RSSNR_thresholdIsTooSmall() {
+        // 4 threshold integers must be within the boundaries [-20, 30]
+        mBundle.putIntArray(CarrierConfigManager.KEY_NTN_LTE_RSSNR_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -21, /* SIGNAL_STRENGTH_POOR */
+                        1,  /* SIGNAL_STRENGTH_MODERATE */
+                        5,  /* SIGNAL_STRENGTH_GOOD */
+                        13  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testInvalidCarrierConfig_NTN_LTE_RSSNR_thresholdIsTooLarge() {
+        // 4 threshold integers must be within the boundaries [-20, 30]
+        mBundle.putIntArray(CarrierConfigManager.KEY_NTN_LTE_RSSNR_THRESHOLDS_INT_ARRAY,
+                new int[]{
+                        -3, /* SIGNAL_STRENGTH_POOR */
+                        1,  /* SIGNAL_STRENGTH_MODERATE */
+                        5,  /* SIGNAL_STRENGTH_GOOD */
+                        31  /* SIGNAL_STRENGTH_GREAT */
+                });
+        sendCarrierConfigUpdate();
+    }
+
+    @Test
+    public void testLteSignalStrengthReportingCriteriaWhenServiceStateChanged() {
+        SignalStrength ss = new SignalStrength(
+                new CellSignalStrengthCdma(),
+                new CellSignalStrengthGsm(),
+                new CellSignalStrengthWcdma(),
+                new CellSignalStrengthTdscdma(),
+                new CellSignalStrengthLte(
+                        -110, /* rssi */
+                        -114, /* rsrp */
+                        -5, /* rsrq */
+                        0, /* rssnr */
+                        SignalStrength.INVALID, /* cqi */
+                        SignalStrength.INVALID /* ta */),
+                new CellSignalStrengthNr());
+
+        // RSRP NTN_LTE threshold set to Good and LTE threshold set to poor.
+        mBundle.putInt(CarrierConfigManager.KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT,
+                CellSignalStrengthLte.USE_RSRP);
+        mBundle.putIntArray(CarrierConfigManager.KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY,
+                new int[]{-125 /* SIGNAL_STRENGTH_POOR */, -120 /* SIGNAL_STRENGTH_MODERATE */,
+                        -115 /* SIGNAL_STRENGTH_GOOD */, -110/* SIGNAL_STRENGTH_GREAT */});
+        mBundle.putIntArray(CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
+                new int[]{-114, /* SIGNAL_STRENGTH_POOR */ -110, /* SIGNAL_STRENGTH_MODERATE */
+                        -105, /* SIGNAL_STRENGTH_GOOD */ -100, /* SIGNAL_STRENGTH_GREAT */});
+        CarrierConfigManager mockConfigManager = Mockito.mock(CarrierConfigManager.class);
+        when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
+                .thenReturn(mockConfigManager);
+        when(mockConfigManager.getConfigForSubId(anyInt())).thenReturn(mBundle);
+
+        // When NTN is connected, check the signal strength is GOOD
+        AsyncResult asyncResult = mock(AsyncResult.class);
+        asyncResult.result = mServiceState;
+        doReturn(true).when(mServiceState).isUsingNonTerrestrialNetwork();
+        mSsc.handleMessage(mSsc.obtainMessage(10/*EVENT_SERVICE_STATE_CHANGED*/, asyncResult));
+        processAllMessages();
+
+        mSimulatedCommands.setSignalStrength(ss);
+        mSimulatedCommands.notifySignalStrength();
+        processAllMessages();
+        assertEquals(CellSignalStrength.SIGNAL_STRENGTH_GOOD, mSsc.getSignalStrength().getLevel());
+
+        // When TN connected, check the signal strength is POOR
+        doReturn(false).when(mServiceState).isUsingNonTerrestrialNetwork();
+        mSsc.handleMessage(mSsc.obtainMessage(10/*EVENT_SERVICE_STATE_CHANGED*/, asyncResult));
+        processAllMessages();
+
+        mSimulatedCommands.setSignalStrength(ss);
+        mSimulatedCommands.notifySignalStrength();
+        processAllMessages();
+        assertEquals(CellSignalStrength.SIGNAL_STRENGTH_POOR, mSsc.getSignalStrength().getLevel());
+
+        // RSRP NTN_LTE threshold set to Moderate and LTE threshold set to poor.
+        // When TN connected, check the signal strength is POOR.
+        mBundle.putIntArray(CarrierConfigManager.KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY,
+                new int[]{-130 /* SIGNAL_STRENGTH_POOR */, -120 /* SIGNAL_STRENGTH_MODERATE */,
+                        -110 /* SIGNAL_STRENGTH_GOOD */, -100/* SIGNAL_STRENGTH_GREAT */});
+        mSsc.handleMessage(mSsc.obtainMessage(10/*EVENT_SERVICE_STATE_CHANGED*/, asyncResult));
+        processAllMessages();
+
+        mSimulatedCommands.setSignalStrength(ss);
+        mSimulatedCommands.notifySignalStrength();
+        processAllMessages();
+        assertEquals(CellSignalStrength.SIGNAL_STRENGTH_POOR, mSsc.getSignalStrength().getLevel());
+
+        // Service State Changed with OUT_OF_SERVICE, then no update
+        // SignalStrengthReportingCriteria.
+        reset(mSimulatedCommandsVerifier);
+        doReturn(STATE_OUT_OF_SERVICE).when(mServiceState).getState();
+        mSsc.handleMessage(mSsc.obtainMessage(10/*EVENT_SERVICE_STATE_CHANGED*/, asyncResult));
+        processAllMessages();
+
+        mSimulatedCommands.setSignalStrength(ss);
+        mSimulatedCommands.notifySignalStrength();
+        processAllMessages();
+        assertEquals(CellSignalStrength.SIGNAL_STRENGTH_POOR, mSsc.getSignalStrength().getLevel());
+        verify(mSimulatedCommandsVerifier, never()).setSignalStrengthReportingCriteria(anyList(),
+                isNull());
+
+        // Service State Changed with POWER_OFF, then no update SignalStrengthReportingCriteria.
+        reset(mSimulatedCommandsVerifier);
+        doReturn(STATE_POWER_OFF).when(mServiceState).getState();
+        mSsc.handleMessage(mSsc.obtainMessage(10/*EVENT_SERVICE_STATE_CHANGED*/, asyncResult));
+        processAllMessages();
+
+        mSimulatedCommands.setSignalStrength(ss);
+        mSimulatedCommands.notifySignalStrength();
+        processAllMessages();
+        assertEquals(CellSignalStrength.SIGNAL_STRENGTH_POOR, mSsc.getSignalStrength().getLevel());
+        verify(mSimulatedCommandsVerifier, never()).setSignalStrengthReportingCriteria(anyList(),
+                isNull());
+
+        // Service State Changed with IN_SERVICE, then update SignalStrengthReportingCriteria.
+        // When NTN is connected, check the signal strength is MODERATE
+        reset(mSimulatedCommandsVerifier);
+        doReturn(true).when(mServiceState).isUsingNonTerrestrialNetwork();
+        doReturn(STATE_IN_SERVICE).when(mServiceState).getState();
+        mSsc.handleMessage(mSsc.obtainMessage(10/*EVENT_SERVICE_STATE_CHANGED*/, asyncResult));
+        processAllMessages();
+
+        mSimulatedCommands.setSignalStrength(ss);
+        mSimulatedCommands.notifySignalStrength();
+        processAllMessages();
+        assertEquals(CellSignalStrength.SIGNAL_STRENGTH_MODERATE,
+                mSsc.getSignalStrength().getLevel());
+        verify(mSimulatedCommandsVerifier).setSignalStrengthReportingCriteria(anyList(), isNull());
+
+        // Service State Changed with IN_SERVICE and still NTN is connected,
+        // verify not update SignalStrengthReportingCriteria and the signal strength is MODERATE.
+        reset(mSimulatedCommandsVerifier);
+        mSsc.handleMessage(mSsc.obtainMessage(10/*EVENT_SERVICE_STATE_CHANGED*/, asyncResult));
+        processAllMessages();
+
+        mSimulatedCommands.setSignalStrength(ss);
+        mSimulatedCommands.notifySignalStrength();
+        processAllMessages();
+        assertEquals(CellSignalStrength.SIGNAL_STRENGTH_MODERATE,
+                mSsc.getSignalStrength().getLevel());
+        verify(mSimulatedCommandsVerifier, never()).setSignalStrengthReportingCriteria(anyList(),
+                isNull());
+
+        // Service State Changed with IN_SERVICE, then update SignalStrengthReportingCriteria.
+        // When TN is connected, check the signal strength is POOR.
+        reset(mSimulatedCommandsVerifier);
+        doReturn(false).when(mServiceState).isUsingNonTerrestrialNetwork();
+        mSsc.handleMessage(mSsc.obtainMessage(10/*EVENT_SERVICE_STATE_CHANGED*/, asyncResult));
+        processAllMessages();
+
+        mSimulatedCommands.setSignalStrength(ss);
+        mSimulatedCommands.notifySignalStrength();
+        processAllMessages();
+        assertEquals(CellSignalStrength.SIGNAL_STRENGTH_POOR,
+                mSsc.getSignalStrength().getLevel());
+        verify(mSimulatedCommandsVerifier).setSignalStrengthReportingCriteria(anyList(), isNull());
+
+        // Service State Changed with IN_SERVICE and still TN is connected,
+        // verify not update SignalStrengthReportingCriteria and the signal strength is POOR.
+        reset(mSimulatedCommandsVerifier);
+        mSsc.handleMessage(mSsc.obtainMessage(10/*EVENT_SERVICE_STATE_CHANGED*/, asyncResult));
+        processAllMessages();
+
+        mSimulatedCommands.setSignalStrength(ss);
+        mSimulatedCommands.notifySignalStrength();
+        processAllMessages();
+        assertEquals(CellSignalStrength.SIGNAL_STRENGTH_POOR,
+                mSsc.getSignalStrength().getLevel());
+        verify(mSimulatedCommandsVerifier, never()).setSignalStrengthReportingCriteria(anyList(),
+                isNull());
+
+        reset(mSimulatedCommandsVerifier);
+    }
+
     private void verifyAllEmptyThresholdAreDisabledWhenSetSignalStrengthReportingCriteria(
             int expectedNonEmptyThreshold) {
         ArgumentCaptor<List<SignalThresholdInfo>> signalThresholdInfoCaptor =
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthTest.java b/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthTest.java
index 96184c5..8df4052 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SignalStrengthTest.java
@@ -21,6 +21,7 @@
 
 import android.os.Parcel;
 import android.os.PersistableBundle;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CellInfo;
 import android.telephony.CellSignalStrength;
@@ -30,10 +31,16 @@
 import android.telephony.CellSignalStrengthNr;
 import android.telephony.CellSignalStrengthTdscdma;
 import android.telephony.CellSignalStrengthWcdma;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.telephony.flags.Flags;
+
+import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -46,6 +53,7 @@
 @SmallTest
 @RunWith(JUnit4.class)
 public class SignalStrengthTest {
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
     private static final int[] DEFAULT_LTE_RSRP_THRESHOLDS = {
             -128,  // SIGNAL_STRENGTH_POOR
             -118,  // SIGNAL_STRENGTH_MODERATE
@@ -70,6 +78,42 @@
             -105,  // SIGNAL_STRENGTH_GOOD
             -95 }; // SIGNAL_STRENGTH_GREAT
 
+    private static final int[] DEFAULT_NTN_LTE_RSRP_THRESHOLDS = {
+            -118,  // SIGNAL_STRENGTH_POOR
+            -108,  // SIGNAL_STRENGTH_MODERATE
+            -98,  // SIGNAL_STRENGTH_GOOD
+            -88 }; // SIGNAL_STRENGTH_GREAT
+
+    private static final int[] DEFAULT_NTN_LTE_RSRQ_THRESHOLDS = {
+            -17,   // SIGNAL_STRENGTH_POOR
+            -14,   // SIGNAL_STRENGTH_MODERATE
+            -12,   // SIGNAL_STRENGTH_GOOD
+            -10 }; // SIGNAL_STRENGTH_GREAT
+
+    private static final int[] DEFAULT_NTN_LTE_RSSNR_THRESHOLDS = {
+            1,   // SIGNAL_STRENGTH_POOR
+            5,    // SIGNAL_STRENGTH_MODERATE
+            13,    // SIGNAL_STRENGTH_GOOD
+            17 }; // SIGNAL_STRENGTH_GREAT
+
+    // RSRP, RSSNR thresholds boundaries
+    private static final int MIN_RSRP = -140;
+    private static final int MIN_RSRQ = -34;
+    private static final int MAX_RSRQ = 3;
+    private static final int MIN_RSSNR = -20;
+    private static final int MAX_RSSNR = 30;
+
+    // Default NTN & TN LTE thresholds's index
+    private static final int INDEX_SIGNAL_STRENGTH_POOR = 0;
+    private static final int INDEX_SIGNAL_STRENGTH_MODERATE = 1;
+    private static final int INDEX_SIGNAL_STRENGTH_GOOD = 2;
+    private static final int INDEX_SIGNAL_STRENGTH_GREAT = 3;
+
+    @Before
+    public void setUp() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG);
+    }
+
     @Test
     public void testDefaults() throws Exception {
         SignalStrength s = new SignalStrength();
@@ -229,6 +273,58 @@
         return signalStrength;
     }
 
+    private static SignalStrength createSignalStrengthLteReport(int lteRsrp, int lteRsrq,
+            int lteRssnr, boolean isNTN) {
+        CellSignalStrengthLte lte = new CellSignalStrengthLte(
+                -89,               // rssi
+                lteRsrp,               // rsrp
+                lteRsrq,               // rsrq
+                lteRssnr,              // rssnr
+                CellInfo.UNAVAILABLE,  // cqiTableIndex
+                CellInfo.UNAVAILABLE,  // cqi
+                CellInfo.UNAVAILABLE); // timingAdvance
+
+        SignalStrength signalStrength = new SignalStrength(
+                new CellSignalStrengthCdma(),
+                new CellSignalStrengthGsm(),
+                new CellSignalStrengthWcdma(),
+                new CellSignalStrengthTdscdma(),
+                lte,
+                new CellSignalStrengthNr());
+
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putInt(
+                CarrierConfigManager.KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT,
+                CellSignalStrengthLte.USE_RSRP | CellSignalStrengthLte.USE_RSRQ
+                        | CellSignalStrengthLte.USE_RSSNR);
+        bundle.putIntArray(CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
+                DEFAULT_LTE_RSRP_THRESHOLDS);
+        bundle.putIntArray(CarrierConfigManager.KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY,
+                DEFAULT_LTE_RSRQ_THRESHOLDS);
+        bundle.putIntArray(CarrierConfigManager.KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY,
+                DEFAULT_LTE_RSSNR_THRESHOLDS);
+        bundle.putInt(
+                CarrierConfigManager.KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT,
+                CellSignalStrengthLte.USE_RSRP | CellSignalStrengthLte.USE_RSRQ
+                        | CellSignalStrengthLte.USE_RSSNR);
+        bundle.putIntArray(
+                CarrierConfigManager.KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY,
+                DEFAULT_NTN_LTE_RSRP_THRESHOLDS);
+        bundle.putIntArray(
+                CarrierConfigManager.KEY_NTN_LTE_RSRQ_THRESHOLDS_INT_ARRAY,
+                DEFAULT_NTN_LTE_RSRQ_THRESHOLDS);
+        bundle.putIntArray(
+                CarrierConfigManager.KEY_NTN_LTE_RSSNR_THRESHOLDS_INT_ARRAY,
+                DEFAULT_NTN_LTE_RSSNR_THRESHOLDS);
+        ServiceState serviceState = new ServiceState();
+        NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+                .setIsNonTerrestrialNetwork(isNTN)
+                .build();
+        serviceState.addNetworkRegistrationInfo(nri);
+        signalStrength.updateLevel(bundle, serviceState);
+        return signalStrength;
+    }
+
     @Test
     public void testValidateInput() throws Exception {
 
@@ -265,6 +361,44 @@
         // Input value of RSSNR: -21[dB]
         ss = createSignalStrengthLteReportRssnr(60, -21);
         assertEquals(SignalStrength.INVALID, ss.getLteRssnr());
+
+        // Test for NTN LTE RSRQ Thresholds based on Boundaries [-34 dB, 3 dB]
+        boolean isNTN = true;
+        int rsrp = DEFAULT_NTN_LTE_RSRP_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        int rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        int rsrq = MAX_RSRQ + 1;
+        ss = createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN);
+        assertEquals(SignalStrength.INVALID, ss.getLteRsrq());
+
+        rsrq = MAX_RSRQ;
+        ss = createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN);
+        assertEquals(3, ss.getLteRsrq());
+
+        rsrq = MIN_RSRQ - 1;
+        ss = createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN);
+        assertEquals(SignalStrength.INVALID, ss.getLteRsrq());
+
+        rsrq = MIN_RSRQ;
+        ss = createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN);
+        assertEquals(-34, ss.getLteRsrq());
+
+        // Test for NTN LTE RSSNR Thresholds based on Boundaries [-20 dBm, 30 dBm]
+        rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT]; // or 3 ?
+        rssnr = MAX_RSSNR + 1;
+        ss = createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN);
+        assertEquals(SignalStrength.INVALID, ss.getLteRssnr());
+
+        rssnr = MAX_RSSNR;
+        ss = createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN);
+        assertEquals(30, ss.getLteRssnr());
+
+        rssnr = MIN_RSSNR - 1;
+        ss = createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN);
+        assertEquals(SignalStrength.INVALID, ss.getLteRssnr());
+
+        rssnr = MIN_RSSNR;
+        ss = createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN);
+        assertEquals(-20, ss.getLteRssnr());
     }
 
     @Test
@@ -279,6 +413,46 @@
                 createSignalStrengthLteReportRsrq(-98, -14).getLteLevel());
         assertEquals(SignalStrength.SIGNAL_STRENGTH_GREAT,
                 createSignalStrengthLteReportRsrq(-98, -12).getLteLevel());
+
+        // When NTN is connected, check the signal strength
+        boolean isNTN = true;
+        int rsrp = DEFAULT_NTN_LTE_RSRP_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        int rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        int rsrq = MIN_RSRQ;
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_MODERATE,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_GOOD,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_GREAT,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+
+        // When NTN is disconnected, check the signal strength
+        isNTN = false;
+        rsrp = DEFAULT_LTE_RSRP_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        rsrq = MIN_RSRQ;
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_MODERATE,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_GOOD,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_GREAT,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
     }
 
     @Test
@@ -293,6 +467,46 @@
                 createSignalStrengthLteReportRsrq(-108, -14).getLteLevel());
         assertEquals(SignalStrength.SIGNAL_STRENGTH_GOOD,
                 createSignalStrengthLteReportRsrq(-108, -12).getLteLevel());
+
+        // When NTN is connected, check the signal strength
+        boolean isNTN = true;
+        int rsrp = DEFAULT_NTN_LTE_RSRP_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        int rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        int rsrq = MIN_RSRQ;
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_MODERATE,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_GOOD,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_GOOD,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+
+        // When NTN is disconnected, check the signal strength
+        isNTN = false;
+        rsrp = DEFAULT_LTE_RSRP_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        rsrq = MIN_RSRQ;
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_MODERATE,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_GOOD,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_GOOD,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
     }
 
     @Test
@@ -307,6 +521,46 @@
                 createSignalStrengthLteReportRsrq(-118, -14).getLteLevel());
         assertEquals(SignalStrength.SIGNAL_STRENGTH_MODERATE,
                 createSignalStrengthLteReportRsrq(-118, -12).getLteLevel());
+
+        // When NTN is connected, check the signal strength
+        boolean isNTN = true;
+        int rsrp = DEFAULT_NTN_LTE_RSRP_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        int rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        int rsrq = MIN_RSRQ;
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_MODERATE,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_MODERATE,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_MODERATE,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+
+        // When NTN is disconnected, check the signal strength
+        isNTN = false;
+        rsrp = DEFAULT_LTE_RSRP_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        rsrq = MIN_RSRQ;
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_MODERATE,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_MODERATE,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_MODERATE,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
     }
 
     @Test
@@ -321,6 +575,46 @@
                 createSignalStrengthLteReportRsrq(-128, -14).getLteLevel());
         assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
                 createSignalStrengthLteReportRsrq(-128, -12).getLteLevel());
+
+        // When NTN is connected, check the signal strength
+        boolean isNTN = true;
+        int rsrp = DEFAULT_NTN_LTE_RSRP_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        int rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        int rsrq = MIN_RSRQ;
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+
+        // When NTN is disconnected, check the signal strength
+        isNTN = false;
+        rsrp = DEFAULT_LTE_RSRP_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        rsrq = MIN_RSRQ;
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
     }
 
     @Test
@@ -335,6 +629,45 @@
                 createSignalStrengthLteReportRsrq(-138, -14).getLteLevel());
         assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
                 createSignalStrengthLteReportRsrq(-138, -12).getLteLevel());
+
+        // When NTN is connected, check the signal strength
+        boolean isNTN = true;
+        int rsrp = MIN_RSRP;
+        int rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        int rsrq = MIN_RSRQ;
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+
+        // When NTN is disconnected, check the signal strength
+        isNTN = false;
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        rsrq = MIN_RSRQ;
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
     }
 
     @Test
@@ -349,6 +682,46 @@
                 createSignalStrengthLteReportRssnr(-98, 5).getLteLevel());
         assertEquals(SignalStrength.SIGNAL_STRENGTH_GREAT,
                 createSignalStrengthLteReportRssnr(-98, 13).getLteLevel());
+
+        // When NTN is connected, check the signal strength
+        boolean isNTN = true;
+        int rsrp = DEFAULT_NTN_LTE_RSRP_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        int rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        int rssnr = MIN_RSSNR;
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_MODERATE,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_GOOD,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_GREAT,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+
+        // When NTN is disconnected, check the signal strength
+        isNTN = false;
+        rsrp = DEFAULT_LTE_RSRP_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        rssnr = MIN_RSSNR;
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_MODERATE,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_GOOD,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_GREAT,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
     }
 
     @Test
@@ -363,6 +736,46 @@
                 createSignalStrengthLteReportRssnr(-108, 5).getLteLevel());
         assertEquals(SignalStrength.SIGNAL_STRENGTH_GOOD,
                 createSignalStrengthLteReportRssnr(-108, 13).getLteLevel());
+
+        // When NTN is connected, check the signal strength
+        boolean isNTN = true;
+        int rsrp = DEFAULT_NTN_LTE_RSRP_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        int rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        int rssnr = MIN_RSSNR;
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_MODERATE,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_GOOD,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_GOOD,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+
+        // When NTN is disconnected, check the signal strength
+        isNTN = false;
+        rsrp = DEFAULT_LTE_RSRP_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        rssnr = MIN_RSSNR;
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_MODERATE,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_GOOD,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_GOOD,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
     }
 
     @Test
@@ -377,6 +790,46 @@
                 createSignalStrengthLteReportRssnr(-118, 5).getLteLevel());
         assertEquals(SignalStrength.SIGNAL_STRENGTH_MODERATE,
                 createSignalStrengthLteReportRssnr(-118, 13).getLteLevel());
+
+        // When NTN is connected, check the signal strength
+        boolean isNTN = true;
+        int rsrp = DEFAULT_NTN_LTE_RSRP_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        int rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        int rssnr = MIN_RSSNR;
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_MODERATE,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_MODERATE,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_MODERATE,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+
+        // When NTN is disconnected, check the signal strength
+        isNTN = false;
+        rsrp = DEFAULT_LTE_RSRP_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        rssnr = MIN_RSSNR;
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_MODERATE,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_MODERATE,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_MODERATE,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
     }
 
     @Test
@@ -391,6 +844,46 @@
                 createSignalStrengthLteReportRssnr(-128, 5).getLteLevel());
         assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
                 createSignalStrengthLteReportRssnr(-128, 13).getLteLevel());
+
+        // When NTN is connected, check the signal strength
+        boolean isNTN = true;
+        int rsrp = DEFAULT_NTN_LTE_RSRP_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        int rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        int rssnr = MIN_RSSNR;
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+
+        // When NTN is disconnected, check the signal strength
+        isNTN = false;
+        rsrp = DEFAULT_LTE_RSRP_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        rssnr = MIN_RSSNR;
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_POOR,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
     }
 
     @Test
@@ -405,6 +898,45 @@
                 createSignalStrengthLteReportRssnr(-138, 5).getLteLevel());
         assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
                 createSignalStrengthLteReportRssnr(-138, 13).getLteLevel());
+
+        // When NTN is connected, check the signal strength
+        boolean isNTN = true;
+        int rsrp = MIN_RSRP;
+        int rsrq = DEFAULT_NTN_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        int rssnr = MIN_RSSNR;
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_NTN_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+
+        // When NTN is disconnected, check the signal strength
+        isNTN = false;
+        rsrq = DEFAULT_LTE_RSRQ_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        rssnr = MIN_RSSNR;
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_POOR];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_MODERATE];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GOOD];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
+        rssnr = DEFAULT_LTE_RSSNR_THRESHOLDS[INDEX_SIGNAL_STRENGTH_GREAT];
+        assertEquals(SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                createSignalStrengthLteReport(rsrp, rsrq, rssnr, isNTN).getLteLevel());
     }
 }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsControllerTest.java
index 09c4173..972df5d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsControllerTest.java
@@ -16,7 +16,10 @@
 
 package com.android.internal.telephony;
 
+import static junit.framework.Assert.assertEquals;
+
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.anyInt;
@@ -40,7 +43,9 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mockito;
 
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.NoSuchElementException;
 
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -58,13 +63,14 @@
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
         mAdnRecordCache = Mockito.mock(AdnRecordCache.class);
-        mSmsControllerUT = new SmsController(mContext);
+        mSmsControllerUT = new SmsController(mContext, mFeatureFlags);
         mCallingPackage = mContext.getOpPackageName();
     }
 
     @After
     public void tearDown() throws Exception {
         mAdnRecordCache = null;
+        WapPushCache.clear();
         super.tearDown();
     }
 
@@ -239,4 +245,40 @@
         verify(mIccSmsInterfaceManager, Mockito.times(0))
                 .sendText(mCallingPackage, "1234", null, "text", null, null, false, 0L, true);
     }
+
+    @Test
+    public void testGetWapMessageSize() {
+        long expectedSize = 100L;
+        String location = "content://mms";
+        byte[] locationBytes = location.getBytes(StandardCharsets.ISO_8859_1);
+        byte[] transactionId = "123".getBytes(StandardCharsets.ISO_8859_1);
+
+        WapPushCache.putWapMessageSize(locationBytes, transactionId, expectedSize);
+        long size = mSmsControllerUT.getWapMessageSize(location);
+
+        assertEquals(expectedSize, size);
+    }
+
+    @Test
+    public void testGetWapMessageSize_withTransactionIdAppended() {
+        long expectedSize = 100L;
+        byte[] location = "content://mms".getBytes(StandardCharsets.ISO_8859_1);
+        byte[] transactionId = "123".getBytes(StandardCharsets.ISO_8859_1);
+        byte[] joinedKey = new byte[location.length + transactionId.length];
+        System.arraycopy(location, 0, joinedKey, 0, location.length);
+        System.arraycopy(transactionId, 0, joinedKey, location.length, transactionId.length);
+        String joinedKeyString = new String(joinedKey, StandardCharsets.ISO_8859_1);
+
+        WapPushCache.putWapMessageSize(location, transactionId, expectedSize);
+        long size = mSmsControllerUT.getWapMessageSize(joinedKeyString);
+
+        assertEquals(expectedSize, size);
+    }
+
+    @Test
+    public void testGetWapMessageSize_nonexistentThrows() {
+        assertThrows(NoSuchElementException.class, () ->
+                mSmsControllerUT.getWapMessageSize("content://mms")
+        );
+    }
 }
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
index 95361b3..5a5e11f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
@@ -1499,7 +1499,7 @@
     }
 
     @Test
-    public void testNotifyDataActivityForSubscriber() {
+    public void testNotifyDataActivityForSubscriberWithSlot() {
         final int subId = 1;
         int[] events = {TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED};
         doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
@@ -1509,14 +1509,14 @@
         mTelephonyRegistry.listenWithEventList(false, false, subId, mContext.getOpPackageName(),
                 mContext.getAttributionTag(), mTelephonyCallback.callback, events, true);
 
-        mTelephonyRegistry.notifyDataActivityForSubscriber(0/*phoneId*/, subId,
+        mTelephonyRegistry.notifyDataActivityForSubscriberWithSlot(0/*phoneId*/, subId,
                 TelephonyManager.DATA_ACTIVITY_INOUT);
         processAllMessages();
         assertEquals(TelephonyManager.DATA_ACTIVITY_INOUT, mDataActivity);
     }
 
     @Test
-    public void testNotifyDataActivityForSubscriberForInvalidSubId() {
+    public void testNotifyDataActivityForSubscriberWithSlotForInvalidSubId() {
         final int subId = INVALID_SUBSCRIPTION_ID;
         int[] events = {TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED};
         doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
@@ -1526,7 +1526,7 @@
         mTelephonyRegistry.listenWithEventList(false, false, subId, mContext.getOpPackageName(),
                 mContext.getAttributionTag(), mTelephonyCallback.callback, events, true);
 
-        mTelephonyRegistry.notifyDataActivityForSubscriber(0/*phoneId*/, subId,
+        mTelephonyRegistry.notifyDataActivityForSubscriberWithSlot(0/*phoneId*/, subId,
                 TelephonyManager.DATA_ACTIVITY_OUT);
         processAllMessages();
         assertEquals(TelephonyManager.DATA_ACTIVITY_OUT, mDataActivity);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 3c01c85..89e9075 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -108,6 +108,7 @@
 import com.android.internal.telephony.data.DataSettingsManager;
 import com.android.internal.telephony.data.LinkBandwidthEstimator;
 import com.android.internal.telephony.data.PhoneSwitcher;
+import com.android.internal.telephony.domainselection.DomainSelectionResolver;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
@@ -122,6 +123,7 @@
 import com.android.internal.telephony.metrics.SmsStats;
 import com.android.internal.telephony.metrics.VoiceCallSessionStats;
 import com.android.internal.telephony.satellite.SatelliteController;
+import com.android.internal.telephony.security.CellularIdentifierDisclosureNotifier;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.test.SimulatedCommands;
 import com.android.internal.telephony.test.SimulatedCommandsVerifier;
@@ -280,6 +282,8 @@
     protected ServiceStateStats mServiceStateStats;
     protected SatelliteController mSatelliteController;
     protected DeviceStateHelper mDeviceStateHelper;
+    protected CellularIdentifierDisclosureNotifier mIdentifierDisclosureNotifier;
+    protected DomainSelectionResolver mDomainSelectionResolver;
 
     // Initialized classes
     protected ActivityManager mActivityManager;
@@ -551,6 +555,8 @@
         mServiceStateStats = Mockito.mock(ServiceStateStats.class);
         mSatelliteController = Mockito.mock(SatelliteController.class);
         mDeviceStateHelper = Mockito.mock(DeviceStateHelper.class);
+        mIdentifierDisclosureNotifier = Mockito.mock(CellularIdentifierDisclosureNotifier.class);
+        mDomainSelectionResolver = Mockito.mock(DomainSelectionResolver.class);
 
         TelephonyManager.disableServiceHandleCaching();
         PropertyInvalidatedCache.disableForTestMode();
@@ -621,7 +627,7 @@
         doReturn(mUiccProfile).when(mTelephonyComponentFactory)
                 .makeUiccProfile(nullable(Context.class), nullable(CommandsInterface.class),
                         nullable(IccCardStatus.class), anyInt(), nullable(UiccCard.class),
-                        nullable(Object.class));
+                        nullable(Object.class), any(FeatureFlags.class));
         doReturn(mCT).when(mTelephonyComponentFactory)
                 .makeGsmCdmaCallTracker(nullable(GsmCdmaPhone.class));
         doReturn(mIccPhoneBookIntManager).when(mTelephonyComponentFactory)
@@ -664,6 +670,9 @@
                         any(DataServiceManager.class), any(Looper.class),
                         any(FeatureFlags.class),
                         any(DataProfileManager.DataProfileManagerCallback.class));
+        doReturn(mIdentifierDisclosureNotifier)
+                .when(mTelephonyComponentFactory)
+                .makeIdentifierDisclosureNotifier();
 
         //mPhone
         doReturn(mContext).when(mPhone).getContext();
@@ -885,6 +894,9 @@
                 .getFoldState();
         doReturn(null).when(mContext).getSystemService(eq(Context.DEVICE_STATE_SERVICE));
 
+        doReturn(false).when(mDomainSelectionResolver).isDomainSelectionSupported();
+        DomainSelectionResolver.setDomainSelectionResolver(mDomainSelectionResolver);
+
         //Use reflection to mock singletons
         replaceInstance(CallManager.class, "INSTANCE", null, mCallManager);
         replaceInstance(TelephonyComponentFactory.class, "sInstance", null,
@@ -987,6 +999,7 @@
         mTestableLoopers.clear();
         mTestableLoopers = null;
         mTestableLooper = null;
+        DomainSelectionResolver.setDomainSelectionResolver(null);
     }
 
     protected static void logd(String s) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/WapPushCacheTest.java b/tests/telephonytests/src/com/android/internal/telephony/WapPushCacheTest.java
new file mode 100644
index 0000000..f572c08
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/WapPushCacheTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.time.Clock;
+import java.util.NoSuchElementException;
+import java.util.concurrent.TimeUnit;
+
+public class WapPushCacheTest extends TelephonyTest {
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        WapPushCache.clear();
+        WapPushCache.sTelephonyFacade = new TelephonyFacade();
+        super.tearDown();
+    }
+
+    @Test
+    public void testGetWapMessageSize() {
+        long expectedSize = 100L;
+        byte[] location = "content://mms".getBytes();
+        byte[] transactionId = "123".getBytes();
+
+        WapPushCache.putWapMessageSize(location, transactionId, expectedSize);
+        long size = WapPushCache.getWapMessageSize(location);
+
+        assertEquals(expectedSize, size);
+    }
+
+    @Test
+    public void testGetWapMessageSize_withTransactionIdAppended() {
+        long expectedSize = 100L;
+        byte[] location = "content://mms".getBytes();
+        byte[] transactionId = "123".getBytes();
+        byte[] joinedKey = new byte[location.length + transactionId.length];
+        System.arraycopy(location, 0, joinedKey, 0, location.length);
+        System.arraycopy(transactionId, 0, joinedKey, location.length, transactionId.length);
+
+        WapPushCache.putWapMessageSize(location, transactionId, expectedSize);
+        long size = WapPushCache.getWapMessageSize(joinedKey);
+
+        assertEquals(expectedSize, size);
+    }
+
+    @Test
+    public void testGetWapMessageSize_nonexistentThrows() {
+        assertThrows(NoSuchElementException.class, () ->
+                WapPushCache.getWapMessageSize("content://mms".getBytes())
+        );
+    }
+    @Test
+    public void testGetWapMessageSize_emptyLocationUrlThrows() {
+        assertThrows(IllegalArgumentException.class, () ->
+                WapPushCache.getWapMessageSize(new byte[0])
+        );
+    }
+
+    @Test
+    public void testPutWapMessageSize_invalidValuePreventsInsert() {
+        long expectedSize = 0L;
+        byte[] location = "content://mms".getBytes();
+        byte[] transactionId = "123".getBytes();
+
+        WapPushCache.putWapMessageSize(location, transactionId, expectedSize);
+
+        assertEquals(0, WapPushCache.size());
+    }
+
+    @Test
+    public void testPutWapMessageSize_sizeLimitExceeded_oldestEntryRemoved() {
+        long expectedSize = 100L;
+        for (int i = 0; i < 251; i++) {
+            byte[] location = ("" + i).getBytes();
+            byte[] transactionId = "abc".getBytes();
+            WapPushCache.putWapMessageSize(location, transactionId, expectedSize);
+        }
+
+        // assert one of the entries inserted above has been removed
+        assertEquals(500, WapPushCache.size());
+        // assert last entry added exists
+        assertEquals(expectedSize, WapPushCache.getWapMessageSize("250".getBytes()));
+        // assert the first entry added was removed
+        assertThrows(NoSuchElementException.class, () ->
+                WapPushCache.getWapMessageSize("0".getBytes())
+        );
+    }
+
+    @Test
+    public void testPutWapMessageSize_expiryExceeded_entryRemoved() {
+        long currentTime = Clock.systemUTC().millis();
+        TelephonyFacade facade = mock(TelephonyFacade.class);
+        when(facade.getElapsedSinceBootMillis()).thenReturn(currentTime);
+        WapPushCache.sTelephonyFacade = facade;
+
+        long expectedSize = 100L;
+        byte[] transactionId = "abc".getBytes();
+        byte[] location1 = "old".getBytes();
+        byte[] location2 = "new".getBytes();
+
+        WapPushCache.putWapMessageSize(location1, transactionId, expectedSize);
+        assertEquals(2, WapPushCache.size());
+
+        // advance time
+        when(facade.getElapsedSinceBootMillis())
+                .thenReturn(currentTime + TimeUnit.DAYS.toMillis(14) + 1);
+
+        WapPushCache.putWapMessageSize(location2, transactionId, expectedSize);
+
+        assertEquals(2, WapPushCache.size());
+        assertEquals(expectedSize, WapPushCache.getWapMessageSize(location2));
+        assertThrows(NoSuchElementException.class, () ->
+                WapPushCache.getWapMessageSize(location1)
+        );
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/WapPushOverSmsTest.java b/tests/telephonytests/src/com/android/internal/telephony/WapPushOverSmsTest.java
index 8e40271..f5d4e95 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/WapPushOverSmsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/WapPushOverSmsTest.java
@@ -62,6 +62,7 @@
 
     @After
     public void tearDown() throws Exception {
+        WapPushCache.clear();
         mWapPushOverSmsUT = null;
         super.tearDown();
     }
@@ -150,4 +151,29 @@
                 any(UserHandle.class),
                 anyInt());
     }
+
+    @Test @SmallTest
+    public void testDispatchWapPdu_notificationIndInsertedToCache() throws Exception {
+        assertEquals(0, WapPushCache.size());
+        when(mISmsStub.getCarrierConfigValuesForSubscriber(anyInt())).thenReturn(new Bundle());
+
+        doReturn(true).when(mWspTypeDecoder).decodeUintvarInteger(anyInt());
+        doReturn(true).when(mWspTypeDecoder).decodeContentType(anyInt());
+        doReturn((long) 2).when(mWspTypeDecoder).getValue32();
+        doReturn(2).when(mWspTypeDecoder).getDecodedDataLength();
+        doReturn(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO).when(mWspTypeDecoder).getValueString();
+
+        byte[] pdu = {1, 6, 0, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 47,
+                118, 110, 100, 46, 119, 97, 112, 46, 109, 109, 115, 45, 109, 101, 115, 115,
+                97, 103, 101, 0, -116, -126, -104, 77, 109, 115, 84, 114, 97, 110, 115, 97,
+                99, 116, 105, 111, 110, 73, 68, 0, -115, 18, -119, 8, -128, 49, 54, 49, 55,
+                56, 50, 54, 57, 49, 54, 56, 47, 84, 89, 80, 69, 61, 80, 76, 77, 78, 0, -118,
+                -128, -114, 2, 3, -24, -120, 3, -127, 3, 3, -12, -128, -106, 84, 101, 115,
+                116, 32, 77, 109, 115, 32, 83, 117, 98, 106, 101, 99, 116, 0, -125, 104, 116,
+                116, 112, 58, 47, 47, 119, 119, 119, 46, 103, 111, 111, 103, 108, 101, 46, 99,
+                111, 109, 47, 115, 97, 100, 102, 100, 100, 0};
+
+        mWapPushOverSmsUT.dispatchWapPdu(pdu, null, mInboundSmsHandler, null, 0, 0L);
+        assertEquals(2, WapPushCache.size());
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cat/CATServiceTest.java b/tests/telephonytests/src/com/android/internal/telephony/cat/CATServiceTest.java
index f2c1870..a07ddbe 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/cat/CATServiceTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/cat/CATServiceTest.java
@@ -144,7 +144,7 @@
         mSimulatedCommands = mock(SimulatedCommands.class);
         mSimulatedCommands.setIccIoResultForApduLogicalChannel(mIccIoResult);
         mUiccProfile = new UiccProfile(mContext, mSimulatedCommands, mIccCardStatus,
-                0 /* phoneId */, mUiccCard, new Object());
+                0 /* phoneId */, mUiccCard, new Object(), mFeatureFlags);
         processAllMessages();
         logd("Created UiccProfile");
         processAllMessages();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/ApnSettingTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/ApnSettingTest.java
index 98d3ce5..378df4b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/ApnSettingTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/ApnSettingTest.java
@@ -16,6 +16,9 @@
 
 package com.android.internal.telephony.data;
 
+import static android.telephony.data.ApnSetting.INFRASTRUCTURE_CELLULAR;
+import static android.telephony.data.ApnSetting.INFRASTRUCTURE_SATELLITE;
+
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
@@ -381,4 +384,34 @@
                 .build();
         assertEquals("proxy.mobile.att.net", apn3.getMmsProxyAddressAsString());
     }
+
+    @Test
+    public void testBuild_InfrastructureBitmask() {
+        int infrastructureBitmask = INFRASTRUCTURE_CELLULAR | INFRASTRUCTURE_SATELLITE;
+        ApnSetting apn1 = new ApnSetting.Builder()
+                .setId(1234)
+                .setOperatorNumeric("310260")
+                .setEntryName("mms")
+                .setApnName("mms")
+                .setApnTypeBitmask(ApnSetting.TYPE_MMS | ApnSetting.TYPE_DEFAULT)
+                .setProtocol(ApnSetting.PROTOCOL_IPV4V6)
+                .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE))
+                .build();
+        // InfrastructureBitmask default value set to '3(cellular|satellite)'
+        assertEquals(infrastructureBitmask, apn1.getInfrastructureBitmask());
+
+        infrastructureBitmask = INFRASTRUCTURE_CELLULAR;
+        ApnSetting apn2 = new ApnSetting.Builder()
+                .setId(1235)
+                .setOperatorNumeric("310260")
+                .setEntryName("mms")
+                .setApnName("mms")
+                .setApnTypeBitmask(ApnSetting.TYPE_MMS | ApnSetting.TYPE_DEFAULT)
+                .setProtocol(ApnSetting.PROTOCOL_IPV4V6)
+                .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_LTE))
+                .setInfrastructureBitmask(infrastructureBitmask)
+                .build();
+        // InfrastructureBitmask value set to '1(cellular)'
+        assertEquals(infrastructureBitmask, apn2.getInfrastructureBitmask());
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
index 4b88f0a..6462d73 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/AutoDataSwitchControllerTest.java
@@ -152,12 +152,16 @@
         doReturn(PHONE_1).when(mPhoneSwitcher).getPreferredDataPhoneId();
 
         mAutoDataSwitchControllerUT = new AutoDataSwitchController(mContext, Looper.myLooper(),
-                mPhoneSwitcher, mMockedPhoneSwitcherCallback);
+                mPhoneSwitcher, mFeatureFlags, mMockedPhoneSwitcherCallback);
+
+        doReturn(true).when(mFeatureFlags).autoSwitchAllowRoaming();
     }
 
     @After
     public void tearDown() throws Exception {
         mAutoDataSwitchControllerUT = null;
+        mGoodTelephonyDisplayInfo = null;
+        mBadTelephonyDisplayInfo = null;
         super.tearDown();
     }
 
@@ -222,6 +226,97 @@
     }
 
     @Test
+    public void testRoaming_prefer_home_over_roam() {
+        // DDS -> nDDS: Prefer Home over Roaming
+        prepareIdealUsesNonDdsCondition();
+        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+        serviceStateChanged(PHONE_2, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+        processAllFutureMessages();
+
+        verify(mMockedPhoneSwitcherCallback).onRequireValidation(PHONE_2, true/*needValidation*/);
+
+        // nDDS -> DDS: Prefer Home over Roaming
+        doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();
+        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+        serviceStateChanged(PHONE_2, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+        processAllFutureMessages();
+
+        verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
+                true/*needValidation*/);
+    }
+
+    @Test
+    public void testRoaming_roaming_but_roam_disabled() {
+        // Disable RAT + signalStrength base switching.
+        doReturn(-1).when(mDataConfigManager).getAutoDataSwitchScoreTolerance();
+        mAutoDataSwitchControllerUT = new AutoDataSwitchController(mContext, Looper.myLooper(),
+                mPhoneSwitcher, mFeatureFlags, mMockedPhoneSwitcherCallback);
+
+        // On primary phone
+        // 1.1 Both roaming, user allow roaming on both phone, no need to switch.
+        prepareIdealUsesNonDdsCondition();
+        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+        serviceStateChanged(PHONE_2, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+        processAllFutureMessages();
+        clearInvocations(mMockedPhoneSwitcherCallback);
+
+        mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+        processAllFutureMessages();
+        verify(mMockedPhoneSwitcherCallback, never()).onRequireValidation(anyInt(),
+                anyBoolean()/*needValidation*/);
+
+        // 1.2 Both roaming, but roaming is only allowed on the backup phone.
+        doReturn(false).when(mPhone).getDataRoamingEnabled();
+        mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+        processAllFutureMessages();
+
+        verify(mMockedPhoneSwitcherCallback).onRequireValidation(PHONE_2, true/*needValidation*/);
+
+        // On backup phone
+        doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();
+        // 2.1 Both roaming, user allow roaming on both phone, prefer default.
+        doReturn(true).when(mPhone).getDataRoamingEnabled();
+        mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+        processAllFutureMessages();
+
+        verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
+                true/*needValidation*/);
+
+        // 2.1 Both roaming, but roaming is only allowed on the default phone.
+        doReturn(false).when(mPhone2).getDataRoamingEnabled();
+        mAutoDataSwitchControllerUT.evaluateAutoDataSwitch(EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+        processAllFutureMessages();
+
+        verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
+                false/*needValidation*/);
+    }
+
+    @Test
+    public void testRoaming_same_roaming_condition_uses_rat_signalStrength() {
+        doReturn(true).when(mFeatureFlags).autoDataSwitchRatSs();
+        // On primary phone
+        // 1. Both roaming, user allow roaming on both phone, uses RAT score to decide switch.
+        prepareIdealUsesNonDdsCondition();
+        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+        serviceStateChanged(PHONE_2, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+        processAllFutureMessages();
+
+        verify(mMockedPhoneSwitcherCallback).onRequireValidation(PHONE_2, true/*needValidation*/);
+
+        // On backup phone
+        doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();
+        // 2. Both roaming, user allow roaming on both phone, uses RAT score to decide switch.
+        signalStrengthChanged(PHONE_1, SignalStrength.SIGNAL_STRENGTH_GREAT);
+        signalStrengthChanged(PHONE_2, SignalStrength.SIGNAL_STRENGTH_POOR);
+        displayInfoChanged(PHONE_1, mGoodTelephonyDisplayInfo);
+        displayInfoChanged(PHONE_2, mBadTelephonyDisplayInfo);
+        processAllFutureMessages();
+
+        verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
+                true/*needValidation*/);
+    }
+
+    @Test
     public void testCancelSwitch_onPrimary_rat_signalStrength() {
         // 4.1.1 Display info and signal strength on secondary phone became bad,
         // but primary is still OOS, so still switch to the secondary.
@@ -269,13 +364,13 @@
         // Disable Rat/SignalStrength based switch to test primary OOS based switch
         doReturn(-1).when(mDataConfigManager).getAutoDataSwitchScoreTolerance();
         mAutoDataSwitchControllerUT = new AutoDataSwitchController(mContext, Looper.myLooper(),
-                mPhoneSwitcher, mMockedPhoneSwitcherCallback);
+                mPhoneSwitcher, mFeatureFlags, mMockedPhoneSwitcherCallback);
         doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();
 
         prepareIdealUsesNonDdsCondition();
         // 1.1 service state changes - primary becomes available again, require validation
         serviceStateChanged(PHONE_1,
-                NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING/*need validate*/);
+                NetworkRegistrationInfo.REGISTRATION_STATE_HOME/*need validate*/);
         processAllFutureMessages();
         verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
                 true/*needValidation*/);
@@ -285,7 +380,7 @@
         // 1.2 service state changes - secondary becomes unavailable, NO need validation
         serviceStateChanged(PHONE_1,
                 NetworkRegistrationInfo.REGISTRATION_STATE_HOME/*need validate*/);
-        serviceStateChanged(PHONE_2, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING/*no need*/);
+        serviceStateChanged(PHONE_2, NetworkRegistrationInfo.REGISTRATION_STATE_DENIED/*no need*/);
         processAllFutureMessages();
         // The later validation requirement overrides the previous
         verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
@@ -325,6 +420,7 @@
 
     @Test
     public void testOnNonDdsSwitchBackToPrimary_rat_signalStrength() {
+        doReturn(true).when(mFeatureFlags).autoDataSwitchRatSs();
         doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();
 
         prepareIdealUsesNonDdsCondition();
@@ -354,8 +450,8 @@
         doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();
         prepareIdealUsesNonDdsCondition();
 
-        // attempts the switch back due to secondary becomes ROAMING
-        serviceStateChanged(PHONE_2, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+        // attempts the switch back due to secondary not usable
+        serviceStateChanged(PHONE_2, NetworkRegistrationInfo.REGISTRATION_STATE_DENIED);
         processAllFutureMessages();
 
         verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
@@ -413,7 +509,7 @@
         doReturn(-1 /*Disable signal based switch for easy mock*/).when(mDataConfigManager)
                 .getAutoDataSwitchScoreTolerance();
         mAutoDataSwitchControllerUT = new AutoDataSwitchController(mContext, Looper.myLooper(),
-                mPhoneSwitcher, mMockedPhoneSwitcherCallback);
+                mPhoneSwitcher, mFeatureFlags, mMockedPhoneSwitcherCallback);
 
         //1. DDS -> nDDS, verify callback doesn't require validation
         prepareIdealUsesNonDdsCondition();
@@ -423,7 +519,7 @@
 
         //2. nDDS -> DDS, verify callback doesn't require validation
         doReturn(PHONE_2).when(mPhoneSwitcher).getPreferredDataPhoneId();
-        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING);
+        serviceStateChanged(PHONE_1, NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
         processAllFutureMessages();
         verify(mMockedPhoneSwitcherCallback).onRequireValidation(DEFAULT_PHONE_INDEX,
                 false/*needValidation*/);
@@ -478,7 +574,7 @@
         int modemCount = 2;
         doReturn(new int[]{SUB_2}).when(mSubscriptionManagerService)
                 .getActiveSubIdList(true);
-        mAutoDataSwitchControllerUT.notifySubscriptionsChanged();
+        mAutoDataSwitchControllerUT.notifySubscriptionsMappingChanged();
         processAllMessages();
 
         // Verify unregister from both slots since only 1 visible SIM is insufficient for switching
@@ -492,7 +588,7 @@
         clearInvocations(mDisplayInfoController, mSignalStrengthController, mSST);
         doReturn(new int[]{SUB_1, SUB_2}).when(mSubscriptionManagerService)
                 .getActiveSubIdList(true);
-        mAutoDataSwitchControllerUT.notifySubscriptionsChanged();
+        mAutoDataSwitchControllerUT.notifySubscriptionsMappingChanged();
         processAllMessages();
 
         // Verify register on both slots
@@ -544,8 +640,10 @@
 
         // 4.1 User data enabled on primary SIM
         doReturn(true).when(mPhone).isUserDataEnabled();
+        doReturn(true).when(mPhone).getDataRoamingEnabled();
 
         // 4.2 Auto switch feature is enabled
+        doReturn(true).when(mPhone2).getDataRoamingEnabled();
         doReturn(true).when(mPhone2).isDataAllowed();
 
         // 5. No default network
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
index ffaf8fc..5dd83bf 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
@@ -445,7 +445,7 @@
 
         verify(mConnectivityManager).registerNetworkAgent(any(), any(NetworkInfo.class),
                 linkPropertiesCaptor.capture(), networkCapabilitiesCaptor.capture(), any(), any(),
-                any(), anyInt());
+                anyInt());
         // The very first link properties from telephony is an empty link properties. It will be
         // updated later.
         assertThat(linkPropertiesCaptor.getValue()).isEqualTo(new LinkProperties());
@@ -552,7 +552,7 @@
 
         verify(mConnectivityManager).registerNetworkAgent(any(), any(NetworkInfo.class),
                 any(LinkProperties.class), networkCapabilitiesCaptor.capture(), any(), any(),
-                any(), anyInt());
+                anyInt());
 
         // Make sure the initial network capability has NOT_SUSPENDED
         assertThat(networkCapabilitiesCaptor.getValue().hasCapability(
@@ -616,7 +616,7 @@
         // Agent re-created, so register should be called twice.
         verify(mConnectivityManager, times(2)).registerNetworkAgent(any(), any(NetworkInfo.class),
                 any(LinkProperties.class), networkCapabilitiesCaptor.capture(), any(), any(),
-                any(), anyInt());
+                anyInt());
 
         // Make sure the 2nd network agent was created with NOT_SUSPENDED.
         assertThat(networkCapabilitiesCaptor.getValue().hasCapability(
@@ -663,7 +663,7 @@
 
         verify(mConnectivityManager).registerNetworkAgent(any(), any(NetworkInfo.class),
                 linkPropertiesCaptor.capture(), networkCapabilitiesCaptor.capture(), any(), any(),
-                any(), anyInt());
+                anyInt());
         // The very first link properties from telephony is an empty link properties. It will be
         // updated later.
         assertThat(linkPropertiesCaptor.getValue()).isEqualTo(new LinkProperties());
@@ -1424,8 +1424,8 @@
                 .forClass(NetworkAgentConfig.class);
 
         verify(mConnectivityManager).registerNetworkAgent(any(), any(NetworkInfo.class),
-                any(LinkProperties.class), any(NetworkCapabilities.class), any(), any(),
-                captor.capture(), anyInt());
+                any(LinkProperties.class), any(NetworkCapabilities.class), any(), captor.capture(),
+                anyInt());
 
         NetworkAgentConfig networkAgentConfig = captor.getValue();
 
@@ -1508,7 +1508,7 @@
         // Agent re-created, so register should be called twice.
         verify(mConnectivityManager, times(2)).registerNetworkAgent(any(), any(NetworkInfo.class),
                 any(LinkProperties.class), any(NetworkCapabilities.class), any(), any(),
-                any(), anyInt());
+                anyInt());
 
         assertThat(mDataNetworkUT.getNetworkCapabilities().hasCapability(
                 NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)).isTrue();
@@ -1530,7 +1530,7 @@
         // Agent not re-created, so register should be called once.
         verify(mConnectivityManager, times(1)).registerNetworkAgent(any(), any(NetworkInfo.class),
                 any(LinkProperties.class), any(NetworkCapabilities.class), any(), any(),
-                any(), anyInt());
+                anyInt());
 
         assertThat(mDataNetworkUT.getNetworkCapabilities().hasCapability(
                 NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)).isTrue();
@@ -1730,7 +1730,7 @@
         // Agent re-created, so register should be called twice.
         verify(mConnectivityManager, times(2)).registerNetworkAgent(any(), any(NetworkInfo.class),
                 linkPropertiesCaptor.capture(), any(NetworkCapabilities.class), any(), any(),
-                any(), anyInt());
+                anyInt());
         // The new agent should have the new IP address.
         assertThat(linkPropertiesCaptor.getValue().getAllAddresses()).containsExactly(
                 InetAddresses.parseNumericAddress(IPV4_ADDRESS1),
@@ -1779,7 +1779,7 @@
         // Agent re-created, so register should be called twice.
         verify(mConnectivityManager, times(2)).registerNetworkAgent(any(), any(NetworkInfo.class),
                 linkPropertiesCaptor.capture(), any(NetworkCapabilities.class), any(), any(),
-                any(), anyInt());
+                anyInt());
         // The new agent should have the new IP address.
         assertThat(linkPropertiesCaptor.getValue().getAllAddresses()).containsExactly(
                 InetAddresses.parseNumericAddress(IPV6_ADDRESS1));
@@ -1870,7 +1870,7 @@
         // Agent should not be re-created, so register should be called ony once.
         verify(mConnectivityManager, times(1)).registerNetworkAgent(any(), any(NetworkInfo.class),
                 any(LinkProperties.class), any(NetworkCapabilities.class), any(), any(),
-                any(), anyInt());
+                anyInt());
 
         // The network should have IPv6 address now
         assertThat(mDataNetworkUT.getLinkProperties().getAllAddresses()).containsExactly(
@@ -1914,7 +1914,7 @@
         // Agent should not be re-created, so register should be called ony once.
         verify(mConnectivityManager, times(1)).registerNetworkAgent(any(), any(NetworkInfo.class),
                 any(LinkProperties.class), any(NetworkCapabilities.class), any(), any(),
-                any(), anyInt());
+                anyInt());
 
         // The network should have IPv6 address now
         assertThat(mDataNetworkUT.getLinkProperties().getAllAddresses()).containsExactly(
@@ -1959,7 +1959,7 @@
         // Agent should not be re-created, so register should be called ony once.
         verify(mConnectivityManager, times(1)).registerNetworkAgent(any(), any(NetworkInfo.class),
                 any(LinkProperties.class), any(NetworkCapabilities.class), any(), any(),
-                any(), anyInt());
+                anyInt());
 
         // The network should have IPv6 address now
         assertThat(mDataNetworkUT.getLinkProperties().getAllAddresses()).containsExactly(
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..e556cb8 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)
@@ -1614,6 +1611,7 @@
                                 | TelephonyManager.NETWORK_TYPE_BITMASK_NR))
                         .setMvnoMatchData("")
                         .setApnSetId(DEFAULT_APN_SET_ID)
+                        .setInfrastructureBitmask(1)
                         .build())
                 .build();
 
@@ -1649,6 +1647,7 @@
                         .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_NR))
                         .setMvnoMatchData("")
                         .setApnSetId(DEFAULT_APN_SET_ID)
+                        .setInfrastructureBitmask(1)
                         .build())
                 .build();
 
@@ -1677,6 +1676,7 @@
                                 | TelephonyManager.NETWORK_TYPE_BITMASK_NR))
                         .setMvnoMatchData("")
                         .setApnSetId(APN_SET_ID_1)
+                        .setInfrastructureBitmask(1)
                         .build())
                 .build();
 
@@ -1705,6 +1705,7 @@
                                 | TelephonyManager.NETWORK_TYPE_BITMASK_NR))
                         .setMvnoMatchData("")
                         .setApnSetId(MATCH_ALL_APN_SET_ID)
+                        .setInfrastructureBitmask(1)
                         .build())
                 .build();
 
@@ -1740,6 +1741,7 @@
                                 | TelephonyManager.NETWORK_TYPE_BITMASK_NR))
                         .setMvnoMatchData("")
                         .setApnSetId(APN_SET_ID_1)
+                        .setInfrastructureBitmask(1)
                         .build())
                 .build();
 
@@ -1768,6 +1770,7 @@
                         .setNetworkTypeBitmask((int) (TelephonyManager.NETWORK_TYPE_BITMASK_NR))
                         .setMvnoMatchData("")
                         .setApnSetId(DEFAULT_APN_SET_ID)
+                        .setInfrastructureBitmask(1)
                         .build())
                 .build();
 
@@ -1796,6 +1799,7 @@
                                 | TelephonyManager.NETWORK_TYPE_BITMASK_NR))
                         .setMvnoMatchData("")
                         .setApnSetId(MATCH_ALL_APN_SET_ID)
+                        .setInfrastructureBitmask(1)
                         .build())
                 .build();
 
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/data/PhoneSwitcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
index 94fd934..13ab611 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/PhoneSwitcherTest.java
@@ -26,8 +26,10 @@
 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 com.android.internal.telephony.data.AutoDataSwitchController.EVALUATION_REASON_VOICE_CALL_END;
 import static com.android.internal.telephony.data.PhoneSwitcher.ECBM_DEFAULT_DATA_SWITCH_BASE_TIME_MS;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -267,12 +269,14 @@
         assertFalse("data allowed", mDataAllowed[0]);
 
         setSlotIndexToSubId(1, 1);
+        clearInvocations(mAutoDataSwitchController);
         mSubChangedListener.onSubscriptionsChanged();
         processAllMessages();
 
         Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
         processAllMessages();
         verify(mActivePhoneSwitchHandler, times(1)).sendMessageAtTime(any(), anyLong());
+        verify(mAutoDataSwitchController).notifySubscriptionsMappingChanged();
         clearInvocations(mActivePhoneSwitchHandler);
         assertFalse("data allowed", mDataAllowed[0]);
         assertTrue("data not allowed", mDataAllowed[1]);
@@ -289,8 +293,10 @@
 
         // 3 lose default via sub->phone change
         setSlotIndexToSubId(0, 2);
+        clearInvocations(mAutoDataSwitchController);
         mSubChangedListener.onSubscriptionsChanged();
         processAllMessages();
+        verify(mAutoDataSwitchController).notifySubscriptionsMappingChanged();
         Message.obtain(mPhoneSwitcherUT, EVENT_MODEM_COMMAND_DONE, res).sendToTarget();
         processAllMessages();
 
@@ -1097,7 +1103,6 @@
     }
 
     @Test
-    @SmallTest
     public void testDataEnabledChangedDuringVoiceCall() throws Exception {
         doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
         initialize();
@@ -1135,6 +1140,18 @@
     }
 
     @Test
+    public void testRoamingToggle() throws Exception {
+        initialize();
+        setSlotIndexToSubId(0, 1);
+
+        mDataSettingsManagerCallbacks.get(0).onDataRoamingEnabledChanged(true);
+        processAllMessages();
+
+        verify(mAutoDataSwitchController).evaluateAutoDataSwitch(AutoDataSwitchController
+                .EVALUATION_REASON_DATA_SETTINGS_CHANGED);
+    }
+
+    @Test
     @SmallTest
     public void testNetworkRequestOnNonDefaultData() throws Exception {
         doReturn(true).when(mMockRadioConfig).isSetPreferredDataCommandSupported();
@@ -1858,7 +1875,8 @@
         initializeConnManagerMock();
         initializeConfigMock();
 
-        mPhoneSwitcherUT = new PhoneSwitcher(mMaxDataAttachModemCount, mContext, Looper.myLooper());
+        mPhoneSwitcherUT = new PhoneSwitcher(mMaxDataAttachModemCount, mContext, Looper.myLooper(),
+                mFeatureFlags);
 
         Field field = PhoneSwitcher.class.getDeclaredField("mDataSettingsManagerCallbacks");
         field.setAccessible(true);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkFactoryTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkFactoryTest.java
index 5941f06..eff274c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkFactoryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/TelephonyNetworkFactoryTest.java
@@ -190,7 +190,8 @@
     private void createMockedTelephonyComponents() throws Exception {
         replaceInstance(PhoneSwitcher.class, "sPhoneSwitcher", null, mPhoneSwitcher);
 
-        mTelephonyNetworkFactoryUT = new TelephonyNetworkFactory(Looper.myLooper(), mPhone);
+        mTelephonyNetworkFactoryUT = new TelephonyNetworkFactory(Looper.myLooper(), mPhone,
+                mFeatureFlags);
         final ArgumentCaptor<NetworkProvider> providerCaptor =
                 ArgumentCaptor.forClass(NetworkProvider.class);
         verify(mConnectivityManager).registerNetworkProvider(providerCaptor.capture());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionResolverTest.java
index 7095909..eaf11a4 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionResolverTest.java
@@ -83,6 +83,8 @@
     @Test
     @SmallTest
     public void testGetInstance() throws IllegalStateException {
+        DomainSelectionResolver.setDomainSelectionResolver(null);
+
         assertThrows(IllegalStateException.class, () -> {
             DomainSelectionResolver.getInstance();
         });
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/OWNERS b/tests/telephonytests/src/com/android/internal/telephony/domainselection/OWNERS
new file mode 100644
index 0000000..b9112be
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/OWNERS
@@ -0,0 +1,8 @@
+# automatically inherit owners from fw/opt/telephony
+
+hwangoo@google.com
+forestchoi@google.com
+avinashmp@google.com
+mkoon@google.com
+seheele@google.com
+radhikaagrawal@google.com
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..fff1b68 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,252 @@
     }
 
     /**
+     * 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 once endCall() is called and we enter ECM, then we exit ECM when turning on
+     * airplane mode.
+     */
+    @Test
+    @SmallTest
+    public void endCall_entersEcm_thenExitsEcmWhenTurnOnAirplaneMode() {
+        // 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);
+        // Set ecm as supported
+        setEcmSupportedConfig(testPhone, /* ecmSupported= */ true);
+
+        processAllMessages();
+
+        emergencyStateTracker.endCall(TEST_CALL_ID);
+
+        assertTrue(emergencyStateTracker.isInEcm());
+
+        emergencyStateTracker.onCellularRadioPowerOffRequested();
+
+        // Verify exitEmergencyMode() is called.
+        verify(testPhone).exitEmergencyMode(any(Message.class));
+        assertFalse(emergencyStateTracker.isInEcm());
+        assertFalse(emergencyStateTracker.isInEmergencyMode());
+    }
+
+    /**
      * 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/imsphone/ImsPhoneCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
index 04ae9d0..bfa702c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
@@ -126,7 +126,6 @@
 import com.android.internal.telephony.SrvccConnection;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.d2d.RtpTransport;
-import com.android.internal.telephony.domainselection.DomainSelectionResolver;
 import com.android.internal.telephony.imsphone.ImsPhoneCallTracker.VtDataUsageProvider;
 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
 
@@ -174,7 +173,6 @@
     private INetworkStatsProviderCallback mVtDataUsageProviderCb;
     private ImsPhoneCallTracker.ConnectorFactory mConnectorFactory;
     private CommandsInterface mMockCi;
-    private DomainSelectionResolver mDomainSelectionResolver;
     private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
 
     private final Executor mExecutor = Runnable::run;
@@ -242,7 +240,6 @@
         doReturn(ImsFeature.STATE_READY).when(mImsManager).getImsServiceState();
         doReturn(mImsCallProfile).when(mImsManager).createCallProfile(anyInt(), anyInt());
         mContextFixture.addSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS);
-        mDomainSelectionResolver = mock(DomainSelectionResolver.class);
 
         doReturn(new SubscriptionInfoInternal.Builder().setSimSlotIndex(0).setId(1).build())
                 .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
@@ -281,9 +278,6 @@
             return mMockConnector;
         }).when(mConnectorFactory).create(any(), anyInt(), anyString(), any(), any());
 
-        DomainSelectionResolver.setDomainSelectionResolver(mDomainSelectionResolver);
-        doReturn(false).when(mDomainSelectionResolver).isDomainSelectionSupported();
-
         doReturn(false)
                 .when(mFeatureFlags).updateImsServiceByGatheringProvisioningChanges();
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
index 4e61c67..f491041 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
@@ -24,6 +24,8 @@
 import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_NONE;
 import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK;
 import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT;
+import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_RAT_BLOCK;
+import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCK;
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_3G;
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
@@ -123,7 +125,6 @@
     private ImsPhoneCall mBackgroundCall;
     private ImsPhoneCall mRingingCall;
     private Handler mTestHandler;
-    private DomainSelectionResolver mDomainSelectionResolver;
     Connection mConnection;
     ImsUtInterface mImsUtInterface;
     private FeatureFlags mFeatureFlags;
@@ -150,10 +151,7 @@
         mTestHandler = mock(Handler.class);
         mConnection = mock(Connection.class);
         mImsUtInterface = mock(ImsUtInterface.class);
-        mDomainSelectionResolver = mock(DomainSelectionResolver.class);
         mFeatureFlags = mock(FeatureFlags.class);
-        doReturn(false).when(mDomainSelectionResolver).isDomainSelectionSupported();
-        DomainSelectionResolver.setDomainSelectionResolver(mDomainSelectionResolver);
 
         mImsCT.mForegroundCall = mForegroundCall;
         mImsCT.mBackgroundCall = mBackgroundCall;
@@ -198,7 +196,6 @@
     public void tearDown() throws Exception {
         mImsPhoneUT = null;
         mBundle = null;
-        DomainSelectionResolver.setDomainSelectionResolver(null);
         super.tearDown();
     }
 
@@ -1484,6 +1481,81 @@
         assertTrue(regInfo[0] == 1 && regInfo[1] == 1 && regInfo[2] == 1);
     }
 
+    /**
+     * Verifies that valid state and reason is passed to RIL with RAT suggested actions
+     * when IMS registration state changes to unregistered.
+     */
+    @Test
+    @SmallTest
+    public void testUpdateImsRegistrationInfoWithRatSuggestedAction() {
+        doReturn(true).when(mFeatureFlags)
+                .addRatRelatedSuggestedActionToImsRegistration();
+
+        mSimulatedCommands.updateImsRegistrationInfo(0, 0, 0, 0, null);
+
+        int[] regInfo = mSimulatedCommands.getImsRegistrationInfo();
+        assertNotNull(regInfo);
+        assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[2] == 0);
+
+        RegistrationManager.RegistrationCallback registrationCallback =
+                mImsPhoneUT.getImsMmTelRegistrationCallback();
+
+        ImsReasonInfo reasonInfo = new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR,
+                ImsReasonInfo.CODE_UNSPECIFIED, "");
+
+        // unregistered with rat block
+        registrationCallback.onUnregistered(reasonInfo,
+                SUGGESTED_ACTION_TRIGGER_RAT_BLOCK,
+                REGISTRATION_TECH_LTE);
+        regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+        assertTrue(regInfo[0] == RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED
+                && regInfo[1] == REGISTRATION_TECH_LTE
+                && regInfo[2] == SUGGESTED_ACTION_TRIGGER_RAT_BLOCK);
+
+        // reset the registration info saved in the SimulatedCommands
+        mSimulatedCommands.updateImsRegistrationInfo(0, 0, 0, 0, null);
+        regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+        assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[2] == 0);
+
+        // verfies that duplicated notification with the same suggested action is invoked
+        registrationCallback.onUnregistered(reasonInfo,
+                SUGGESTED_ACTION_TRIGGER_RAT_BLOCK,
+                REGISTRATION_TECH_LTE);
+        regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+        assertTrue(regInfo[0] == RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED
+                && regInfo[1] == REGISTRATION_TECH_LTE
+                && regInfo[2] == SUGGESTED_ACTION_TRIGGER_RAT_BLOCK);
+
+        // unregistered with rat block clear
+        registrationCallback.onUnregistered(reasonInfo,
+                SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCK,
+                REGISTRATION_TECH_LTE);
+        regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+        assertTrue(regInfo[0] == RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED
+                && regInfo[1] == REGISTRATION_TECH_LTE
+                && regInfo[2] == SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCK);
+
+        // reset the registration info saved in the SimulatedCommands
+        mSimulatedCommands.updateImsRegistrationInfo(0, 0, 0, 0, null);
+        regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+        assertTrue(regInfo[0] == 0 && regInfo[1] == 0 && regInfo[2] == 0);
+
+        // verfies that duplicated notification with the same suggested action is invoked
+        registrationCallback.onUnregistered(reasonInfo,
+                SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCK,
+                REGISTRATION_TECH_LTE);
+        regInfo = mSimulatedCommands.getImsRegistrationInfo();
+
+        assertTrue(regInfo[0] == RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED
+                && regInfo[1] == REGISTRATION_TECH_LTE
+                && regInfo[2] == SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCK);
+    }
+
     @Test
     @SmallTest
     public void testImsDialArgsBuilderFromForAlternateService() {
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/MetricsCollectorTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
index 66bf482..0e1135e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
@@ -23,6 +23,7 @@
 import static com.android.internal.telephony.TelephonyStatsLog.SUPPORTED_RADIO_ACCESS_FAMILY;
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_RAT_USAGE;
 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION;
+import static com.android.internal.telephony.util.TelephonyUtils.IS_DEBUGGABLE;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -67,6 +68,8 @@
                     .setCoolDownMillis(24L * 3600L * 1000L)
                     .build();
     private static final long MIN_COOLDOWN_MILLIS = 23L * 3600L * 1000L;
+    private static final long CELL_SERVICE_MIN_COOLDOWN_MILLIS =
+            IS_DEBUGGABLE ? 4L *  60L * 1000L : MIN_COOLDOWN_MILLIS;
     private static final long MIN_CALLS_PER_BUCKET = 5L;
 
     // NOTE: these fields are currently 32-bit internally and padded to 64-bit by TelephonyManager
@@ -92,6 +95,7 @@
     private UiccCard mActiveCard;
     private UiccPort mActivePort;
     private ServiceStateStats mServiceStateStats;
+    private VonrHelper mVonrHelper;
 
     private MetricsCollector mMetricsCollector;
 
@@ -104,8 +108,10 @@
         mActiveCard = mock(UiccCard.class);
         mActivePort = mock(UiccPort.class);
         mServiceStateStats = mock(ServiceStateStats.class);
+        mVonrHelper = mock(VonrHelper.class);
         mMetricsCollector =
-                new MetricsCollector(mContext, mPersistAtomsStorage, mDeviceStateHelper);
+                new MetricsCollector(mContext, mPersistAtomsStorage,
+                        mDeviceStateHelper, mVonrHelper);
         doReturn(mSST).when(mSecondPhone).getServiceStateTracker();
         doReturn(mServiceStateStats).when(mSST).getServiceStateStats();
     }
@@ -398,7 +404,8 @@
 
         assertThat(actualAtoms).hasSize(0);
         assertThat(result).isEqualTo(StatsManager.PULL_SKIP);
-        verify(mPersistAtomsStorage, times(1)).getCellularServiceStates(eq(MIN_COOLDOWN_MILLIS));
+        verify(mPersistAtomsStorage, times(1)).getCellularServiceStates(
+                eq(CELL_SERVICE_MIN_COOLDOWN_MILLIS));
         verifyNoMoreInteractions(mPersistAtomsStorage);
     }
 
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..1498eb4 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
@@ -44,6 +44,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
+import android.annotation.NonNull;
 import android.os.Looper;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.Annotation.NetworkType;
@@ -56,6 +57,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;
@@ -67,6 +69,7 @@
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.imsphone.ImsPhoneCall;
 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage;
@@ -113,12 +116,14 @@
     private GsmCdmaCall mCsCall1;
     private ImsPhoneCall mImsCall0;
     private ImsPhoneCall mImsCall1;
+    private VonrHelper mVonrHelper;
 
     private static class TestableVoiceCallSessionStats extends VoiceCallSessionStats {
         private long mTimeMillis = 0L;
 
-        TestableVoiceCallSessionStats(int phoneId, Phone phone) {
-            super(phoneId, phone);
+        TestableVoiceCallSessionStats(int phoneId, Phone phone,
+                @NonNull FeatureFlags featureFlags) {
+            super(phoneId, phone, featureFlags);
         }
 
         @Override
@@ -159,6 +164,7 @@
         mCsCall1 = mock(GsmCdmaCall.class);
         mImsCall0 = mock(ImsPhoneCall.class);
         mImsCall1 = mock(ImsPhoneCall.class);
+        mVonrHelper = mock(VonrHelper.class);
         replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[] {mPhone, mSecondPhone});
         doReturn(CARRIER_ID_SLOT_0).when(mPhone).getCarrierId();
         // mPhone's mContext/mSST/mServiceState has been set up by TelephonyTest
@@ -196,6 +202,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,14 +210,21 @@
         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();
         }
 
-        mVoiceCallSessionStats0 = new TestableVoiceCallSessionStats(0, mPhone);
+        doReturn(mVonrHelper).when(mMetricsCollector).getVonrHelper();
+
+        mVoiceCallSessionStats0 = new TestableVoiceCallSessionStats(0, mPhone, mFeatureFlags);
         mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
-        mVoiceCallSessionStats1 = new TestableVoiceCallSessionStats(1, mSecondPhone);
+        mVoiceCallSessionStats1 = new TestableVoiceCallSessionStats(1, mSecondPhone, mFeatureFlags);
         mVoiceCallSessionStats1.onServiceStateChanged(mSecondServiceState);
+
+        doReturn(true).when(mFeatureFlags).vonrEnabledMetric();
     }
 
     @After
@@ -241,7 +255,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 +1532,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 +2600,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 +2650,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();
@@ -2609,6 +2695,63 @@
         assertProtoEquals(expectedRatUsage, ratUsage.get()[0]);
     }
 
+    @Test
+    @SmallTest
+    public void singleCall_vonrEnabled() {
+        setServiceState(mServiceState, TelephonyManager.NETWORK_TYPE_LTE);
+        doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mImsStats).getImsVoiceRadioTech();
+        doReturn(mImsPhone).when(mPhone).getImsPhone();
+        doReturn(false).when(mImsConnection0).isIncoming();
+        doReturn(2000L).when(mImsConnection0).getCreateTime();
+        doReturn(0L).when(mImsConnection0).getDurationMillis();
+        doReturn(mImsCall0).when(mImsConnection0).getCall();
+        doReturn(true).when(mVonrHelper).getVonrEnabled(anyInt());
+        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_LTE,
+                        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;
+        expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+        expectedCall.callDuration = VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_UNKNOWN;
+        expectedCall.vonrEnabled = true;
+        VoiceCallRatUsage expectedRatUsage =
+                makeRatUsageProto(
+                        CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 12000L, 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);
+        mVoiceCallSessionStats0.onImsCallTerminated(
+                mImsConnection0, new ImsReasonInfo(ImsReasonInfo.CODE_REMOTE_CALL_DECLINE, 0));
+
+        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]);
+    }
+
     private AtomicReference<VoiceCallRatUsage[]> setupRatUsageCapture() {
         final AtomicReference<VoiceCallRatUsage[]> ratUsage = new AtomicReference<>(null);
         doAnswer(
@@ -2696,6 +2839,7 @@
         call.isRoaming = false;
         call.setupBeginMillis = 0L;
         call.signalStrengthAtEnd = 2;
+        call.vonrEnabled = false;
         return call;
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/VonrHelperTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/VonrHelperTest.java
new file mode 100644
index 0000000..04cd925
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/VonrHelperTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 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 com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.annotation.NonNull;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.flags.FeatureFlags;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class VonrHelperTest extends TelephonyTest {
+    private static final int SUBID = 1;
+
+    private static class TestableVonrHelper extends VonrHelper {
+        TestableVonrHelper(@NonNull FeatureFlags featureFlags) {
+            super(featureFlags);
+        }
+
+        @Override
+        public void updateVonrEnabledState() {
+            mVonrRunnable.run();
+        }
+    }
+
+    private TestableVonrHelper mVonrHelper;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+        doReturn(SUBID).when(mPhone).getSubId();
+        doReturn(false).when(mTelephonyManager).isVoNrEnabled();
+        mVonrHelper = new TestableVonrHelper(mFeatureFlags);
+        doReturn(true).when(mFeatureFlags).vonrEnabledMetric();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Test
+    @SmallTest
+    public void vonr_enabled() {
+        doReturn(true).when(mTelephonyManager).isVoNrEnabled();
+
+        mVonrHelper.updateVonrEnabledState();
+
+        assertThat(mVonrHelper.getVonrEnabled(SUBID)).isTrue();
+    }
+
+    @Test
+    @SmallTest
+    public void vonr_disabled() {
+        mVonrHelper.updateVonrEnabledState();
+
+        assertThat(mVonrHelper.getVonrEnabled(SUBID)).isFalse();
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/nitz/TEST_MAPPING b/tests/telephonytests/src/com/android/internal/telephony/nitz/TEST_MAPPING
index 2bcb04e..4063803 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/nitz/TEST_MAPPING
+++ b/tests/telephonytests/src/com/android/internal/telephony/nitz/TEST_MAPPING
@@ -1,6 +1,5 @@
 {
-  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
-  "postsubmit": [
+  "presubmit": [
     {
       "name": "FrameworksTelephonyTests",
       "options": [
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/satellite/SatelliteControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
index eebd04c..55532cc 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
@@ -61,6 +61,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -91,7 +92,9 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.PersistableBundle;
+import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.ServiceSpecificException;
 import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
@@ -491,7 +494,7 @@
         doReturn(mMockProvisionMetricsStats).when(mMockProvisionMetricsStats)
                 .setResultCode(anyInt());
         doReturn(mMockProvisionMetricsStats).when(mMockProvisionMetricsStats)
-                    .setIsProvisionRequest(eq(false));
+                .setIsProvisionRequest(eq(false));
         doNothing().when(mMockProvisionMetricsStats).reportProvisionMetrics();
         doNothing().when(mMockControllerMetricsStats).reportDeprovisionCount(anyInt());
         when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
@@ -1036,7 +1039,7 @@
         assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
                 (long) mIIntegerConsumerResults.get(0));
         verify(mMockPointingAppController).unregisterForSatelliteTransmissionUpdates(anyInt(),
-                any(),  eq(mStartTransmissionUpdateCallback));
+                any(), eq(mStartTransmissionUpdateCallback));
         verify(mMockPointingAppController).setStartedSatelliteTransmissionUpdates(eq(false));
     }
 
@@ -1095,7 +1098,7 @@
         mSatelliteControllerUT.stopSatelliteTransmissionUpdates(SUB_ID, mIIntegerConsumer,
                 mStopTransmissionUpdateCallback);
         verify(mMockPointingAppController).unregisterForSatelliteTransmissionUpdates(anyInt(),
-                any(),  eq(mStopTransmissionUpdateCallback));
+                any(), eq(mStopTransmissionUpdateCallback));
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
@@ -1519,7 +1522,7 @@
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
         mSatelliteControllerUT.deprovisionSatelliteService(SUB_ID,
-                 TEST_SATELLITE_TOKEN, mIIntegerConsumer);
+                TEST_SATELLITE_TOKEN, mIIntegerConsumer);
         processAllMessages();
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
@@ -2156,16 +2159,16 @@
                     }
                 };
 
-        int errorCode = mSatelliteControllerUT.registerForNtnSignalStrengthChanged(SUB_ID,
-                callback);
-        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE, errorCode);
-        @NtnSignalStrength.NtnSignalStrengthLevel int expectedLevel = NTN_SIGNAL_STRENGTH_NONE;
+        verifyRegisterForNtnSignalStrengthChanged(SUB_ID, callback,
+                SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
 
         setUpResponseForRequestIsSatelliteSupported(false,
                 SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
-        errorCode = mSatelliteControllerUT.registerForNtnSignalStrengthChanged(SUB_ID, callback);
-        assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, errorCode);
+        verifyRegisterForNtnSignalStrengthChanged(SUB_ID, callback,
+                SATELLITE_RESULT_NOT_SUPPORTED);
+
+        @NtnSignalStrength.NtnSignalStrengthLevel int expectedLevel = NTN_SIGNAL_STRENGTH_NONE;
         verifyRequestNtnSignalStrength(expectedLevel, SATELLITE_RESULT_NOT_SUPPORTED);
 
         resetSatelliteControllerUT();
@@ -2173,8 +2176,8 @@
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         setUpResponseForRequestNtnSignalStrength(expectedLevel, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        errorCode = mSatelliteControllerUT.registerForNtnSignalStrengthChanged(SUB_ID, callback);
-        assertEquals(SATELLITE_RESULT_SUCCESS, errorCode);
+        verifyRegisterForNtnSignalStrengthChanged(SUB_ID, callback,
+                SATELLITE_RESULT_SUCCESS);
         verifyRequestNtnSignalStrength(expectedLevel, SATELLITE_RESULT_SUCCESS);
 
         expectedLevel = NTN_SIGNAL_STRENGTH_GOOD;
@@ -2225,15 +2228,14 @@
                     }
                 };
 
-        int errorCode = mSatelliteControllerUT.registerForNtnSignalStrengthChanged(SUB_ID,
-                callback);
-        assertEquals(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, errorCode);
+        verifyRegisterForNtnSignalStrengthChanged(SUB_ID, callback,
+                SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
 
         setUpResponseForRequestIsSatelliteSupported(false,
                 SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(false, SATELLITE_RESULT_NOT_SUPPORTED);
-        errorCode = mSatelliteControllerUT.registerForNtnSignalStrengthChanged(SUB_ID, callback);
-        assertEquals(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, errorCode);
+        verifyRegisterForNtnSignalStrengthChanged(SUB_ID, callback,
+                SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
         setUpResponseForRequestNtnSignalStrength(NTN_SIGNAL_STRENGTH_NONE,
                 SATELLITE_RESULT_SUCCESS);
         verifyRequestNtnSignalStrength(NTN_SIGNAL_STRENGTH_NONE,
@@ -2242,8 +2244,8 @@
         resetSatelliteControllerUT();
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         verifySatelliteSupported(false, SATELLITE_RESULT_NOT_SUPPORTED);
-        errorCode = mSatelliteControllerUT.registerForNtnSignalStrengthChanged(SUB_ID, callback);
-        assertEquals(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, errorCode);
+        verifyRegisterForNtnSignalStrengthChanged(SUB_ID, callback,
+                SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
         verifyRequestNtnSignalStrength(NTN_SIGNAL_STRENGTH_NONE,
                 SATELLITE_RESULT_REQUEST_NOT_SUPPORTED);
 
@@ -2660,7 +2662,7 @@
             int satelliteVisibilityTime, @SatelliteManager.SatelliteResult int error) {
         SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
                 ? null : new SatelliteException(error);
-        int[] visibilityTime = new int[] {satelliteVisibilityTime};
+        int[] visibilityTime = new int[]{satelliteVisibilityTime};
         doAnswer(invocation -> {
             Message message = (Message) invocation.getArguments()[0];
             AsyncResult.forMessage(message, visibilityTime, exception);
@@ -2687,7 +2689,7 @@
             boolean isSatelliteProvisioned, @SatelliteManager.SatelliteResult int error) {
         SatelliteException exception = (error == SATELLITE_RESULT_SUCCESS)
                 ? null : new SatelliteException(error);
-        int[] provisioned = new int[] {isSatelliteProvisioned ? 1 : 0};
+        int[] provisioned = new int[]{isSatelliteProvisioned ? 1 : 0};
         doAnswer(invocation -> {
             Message message = (Message) invocation.getArguments()[0];
             AsyncResult.forMessage(message, provisioned, exception);
@@ -3101,6 +3103,22 @@
         mSimulatedCommands.setRadioPower(on, false, false, null);
     }
 
+    private void verifyRegisterForNtnSignalStrengthChanged(int subId,
+            INtnSignalStrengthCallback callback, int expectedError) {
+        if (expectedError == SATELLITE_RESULT_SUCCESS) {
+            try {
+                mSatelliteControllerUT.registerForNtnSignalStrengthChanged(subId, callback);
+            } catch (RemoteException ex) {
+                throw new AssertionError();
+            }
+        } else {
+            ServiceSpecificException ex = assertThrows(ServiceSpecificException.class,
+                    () -> mSatelliteControllerUT.registerForNtnSignalStrengthChanged(subId,
+                            callback));
+            assertEquals(expectedError, ex.errorCode);
+        }
+    }
+
     private static void loge(String message) {
         Rlog.e(TAG, message);
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/security/CellularIdentifierDisclosureTest.java b/tests/telephonytests/src/com/android/internal/telephony/security/CellularIdentifierDisclosureTest.java
new file mode 100644
index 0000000..327a1cb
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/security/CellularIdentifierDisclosureTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.security;
+
+import static android.telephony.CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMEI;
+import static android.telephony.CellularIdentifierDisclosure.CELLULAR_IDENTIFIER_IMSI;
+import static android.telephony.CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST;
+import static android.telephony.CellularIdentifierDisclosure.NAS_PROTOCOL_MESSAGE_IDENTITY_RESPONSE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.hardware.radio.network.CellularIdentifier;
+import android.hardware.radio.network.NasProtocolMessage;
+import android.os.Parcel;
+
+import com.android.internal.telephony.RILUtils;
+
+import org.junit.Test;
+
+public class CellularIdentifierDisclosureTest {
+
+    @Test
+    public void testEqualsAndHash() {
+        android.telephony.CellularIdentifierDisclosure disclosure =
+                new android.telephony.CellularIdentifierDisclosure(
+                        NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST,
+                        CELLULAR_IDENTIFIER_IMSI,
+                        "001001",
+                        false);
+
+        android.telephony.CellularIdentifierDisclosure anotherDislcosure =
+                new android.telephony.CellularIdentifierDisclosure(
+                        NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST,
+                        CELLULAR_IDENTIFIER_IMSI,
+                        "001001",
+                        false);
+        assertEquals(disclosure, anotherDislcosure);
+        assertEquals(disclosure.hashCode(), anotherDislcosure.hashCode());
+    }
+
+    @Test
+    public void testNotEqualsAndHash() {
+        android.telephony.CellularIdentifierDisclosure imsiDisclosure =
+                new android.telephony.CellularIdentifierDisclosure(
+                        NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST,
+                        CELLULAR_IDENTIFIER_IMSI,
+                        "001001",
+                        false);
+
+        android.telephony.CellularIdentifierDisclosure imeiDisclosure =
+                new android.telephony.CellularIdentifierDisclosure(
+                        NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST,
+                        CELLULAR_IDENTIFIER_IMEI,
+                        "001001",
+                        false);
+
+        assertNotEquals(imsiDisclosure, imeiDisclosure);
+        assertNotEquals(imsiDisclosure.hashCode(), imeiDisclosure.hashCode());
+    }
+
+    @Test
+    public void testGetters() {
+        android.telephony.CellularIdentifierDisclosure disclosure =
+                new android.telephony.CellularIdentifierDisclosure(
+                        NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST,
+                        CELLULAR_IDENTIFIER_IMSI,
+                        "001001",
+                        false);
+
+        assertEquals(NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST, disclosure.getNasProtocolMessage());
+        assertEquals(CELLULAR_IDENTIFIER_IMSI, disclosure.getCellularIdentifier());
+        assertEquals(false, disclosure.isEmergency());
+        assertEquals("001001", disclosure.getPlmn());
+    }
+
+    @Test
+    public void testParcel() {
+        android.telephony.CellularIdentifierDisclosure disclosure =
+                new android.telephony.CellularIdentifierDisclosure(
+                        NAS_PROTOCOL_MESSAGE_ATTACH_REQUEST,
+                        CELLULAR_IDENTIFIER_IMSI,
+                        "001001",
+                        false);
+
+        Parcel p = Parcel.obtain();
+        disclosure.writeToParcel(p, 0);
+        p.setDataPosition(0);
+
+        android.telephony.CellularIdentifierDisclosure fromParcel =
+                android.telephony.CellularIdentifierDisclosure.CREATOR.createFromParcel(p);
+        assertEquals(disclosure, fromParcel);
+    }
+
+    @Test
+    public void testConvertCellularIdentifierDisclosure() {
+        android.hardware.radio.network.CellularIdentifierDisclosure aidlDisclsoure =
+                new android.hardware.radio.network.CellularIdentifierDisclosure();
+        aidlDisclsoure.plmn = "001001";
+        aidlDisclsoure.identifier = NasProtocolMessage.IDENTITY_RESPONSE;
+        aidlDisclsoure.protocolMessage = CellularIdentifier.IMEI;
+        aidlDisclsoure.isEmergency = true;
+
+        android.telephony.CellularIdentifierDisclosure expectedDisclosure =
+                new android.telephony.CellularIdentifierDisclosure(
+                        NAS_PROTOCOL_MESSAGE_IDENTITY_RESPONSE,
+                        CELLULAR_IDENTIFIER_IMEI,
+                        "001001",
+                        true);
+
+        assertEquals(
+                expectedDisclosure, RILUtils.convertCellularIdentifierDisclosure(aidlDisclsoure));
+    }
+}
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
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
index a9034eb..ca322e0 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
@@ -129,7 +129,7 @@
         ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
                 ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
         mUiccProfile = new UiccProfile(mContext, mSimulatedCommands, mIccCardStatus,
-              0 /* phoneId */, mUiccCard, new Object());
+              0 /* phoneId */, mUiccCard, new Object(), mFeatureFlags);
         verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
                 listenerArgumentCaptor.capture());
         mCarrierConfigChangeListener = listenerArgumentCaptor.getAllValues().get(0);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
index 1d320a3..671f273 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
@@ -428,7 +428,7 @@
         mUiccSlot.update(mSimulatedCommands, mIccCardStatus, phoneId, slotIndex);
         verify(mTelephonyComponentFactory).makeUiccProfile(
                 anyObject(), eq(mSimulatedCommands), eq(mIccCardStatus), anyInt(), anyObject(),
-                anyObject());
+                anyObject(), anyObject());
         assertEquals(IccCardStatus.CardState.CARDSTATE_PRESENT, mUiccSlot.getCardState());
         assertNotNull(mUiccSlot.getUiccCard());
 
@@ -451,7 +451,7 @@
         mUiccSlot.update(mSimulatedCommands, mIccCardStatus, phoneId, slotIndex);
         verify(mTelephonyComponentFactory).makeUiccProfile(
                 anyObject(), eq(mSimulatedCommands), eq(mIccCardStatus), anyInt(), anyObject(),
-                anyObject());
+                anyObject(), anyObject());
         assertEquals(IccCardStatus.CardState.CARDSTATE_PRESENT, mUiccSlot.getCardState());
         assertNotNull(mUiccSlot.getUiccCard());