Merge "Add feature flag to public API for IMS service." into main
diff --git a/flags/Android.bp b/flags/Android.bp
index 1885032..edcfc3f 100644
--- a/flags/Android.bp
+++ b/flags/Android.bp
@@ -33,6 +33,7 @@
         "subscription.aconfig",
         "uicc.aconfig",
         "satellite.aconfig",
-        "iwlan.aconfig"
+        "iwlan.aconfig",
+        "carrier.aconfig",
     ],
 }
diff --git a/flags/calling.aconfig b/flags/calling.aconfig
index a5757d8..906abfc 100644
--- a/flags/calling.aconfig
+++ b/flags/calling.aconfig
@@ -62,3 +62,11 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+# OWNER=yongnamcha TARGET=25Q2
+flag {
+    name: "emergency_callback_mode_notification"
+    namespace: "telephony"
+    description: "Used to notify the emergency callback mode for call/SMS to other applications."
+    bug:"359064059"
+}
diff --git a/flags/carrier.aconfig b/flags/carrier.aconfig
new file mode 100644
index 0000000..265d258
--- /dev/null
+++ b/flags/carrier.aconfig
@@ -0,0 +1,23 @@
+package: "com.android.internal.telephony.flags"
+container: "system"
+
+# OWNER=nharold TARGET=24Q4
+flag {
+    name: "async_init_carrier_privileges_tracker"
+    is_exported: true
+    namespace: "telephony"
+    description: "Offload the heavyweight initialization of CarrierPrivilegesTracker to a worker thread"
+    bug:"357096337"
+}
+
+# OWNER=melhuishj TARGET=25Q1
+flag {
+    name: "cleanup_carrier_app_update_enabled_state_logic"
+    is_exported: true
+    namespace: "telephony"
+    description: "Improve readability of update state logic"
+    bug:"232141900"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/flags/data.aconfig b/flags/data.aconfig
index 0fd094d..ccd5db4 100644
--- a/flags/data.aconfig
+++ b/flags/data.aconfig
@@ -132,3 +132,10 @@
   }
 }
 
+# OWNER=TBD TARGET=TBD
+flag {
+  name: "oem_paid_private"
+  namespace: "telephony"
+  description: "Support OEM_PAID and OEM_PRIVATE networks"
+  bug: "366194627"
+}
diff --git a/flags/misc.aconfig b/flags/misc.aconfig
index 053b328..860c864 100644
--- a/flags/misc.aconfig
+++ b/flags/misc.aconfig
@@ -96,17 +96,6 @@
     bug:"271921464"
 }
 
-# OWNER=rambowang TARGET=24Q3
-flag {
-    name: "add_anomaly_when_notify_config_changed_with_invalid_phone"
-    namespace: "telephony"
-    description: "Report anomaly when CarrierConfigLoader received config change with sub that maps to invalid phoneId"
-    bug:"270757342"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
 # OWNER=sangyun TARGET=24Q3
 flag {
     name: "roaming_notification_for_single_data_network"
@@ -206,3 +195,37 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+# OWNER=jackyu TARGET=25Q2
+flag {
+    name: "hsum_broadcast"
+    namespace: "telephony"
+    description: "Fixed the bug that broadcast intent is only sent to the system user."
+    bug:"362554272"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+# OWNER=rambowang TARGET=25Q2
+flag {
+    name: "support_carrier_services_for_hsum"
+    namespace: "telephony"
+    description: "Support Carrier Services (APIs) for HSUM."
+    bug:"345522246"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+# OWNER=jackyu TARGET=25Q2
+flag {
+    name: "hsum_package_manager"
+    namespace: "telephony"
+    description: "Fixed the bug that package manager is not for the right user"
+    bug:"356827794"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
diff --git a/flags/subscription.aconfig b/flags/subscription.aconfig
index dc17a61..76485be 100644
--- a/flags/subscription.aconfig
+++ b/flags/subscription.aconfig
@@ -28,14 +28,6 @@
   bug: "296097429"
 }
 
-# OWNER=rambowang TARGET=24Q3
-flag {
-  name: "data_only_service_allow_emergency_call_only"
-  namespace: "telephony"
-  description: "Support emergency call only for data only cellular service."
-  bug: "296097429"
-}
-
 # OWNER=hhshin TARGET=24Q3
 flag {
   name: "support_psim_to_esim_conversion"
diff --git a/flags/uicc.aconfig b/flags/uicc.aconfig
index 14341d9..ad0c59f 100644
--- a/flags/uicc.aconfig
+++ b/flags/uicc.aconfig
@@ -69,3 +69,14 @@
     description: "This flag controls optimization of apdu sender class."
     bug:"335257880"
 }
+
+# OWNER=arunvoddu TARGET=24Q4
+flag {
+    name: "ignore_carrierid_reset_for_sim_removal"
+    namespace: "telephony"
+    description: "This flag controls the carrierId reset while imsi key deletion time upon sim ejection."
+    bug:"366178705"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
\ No newline at end of file
diff --git a/proto/src/persist_atoms.proto b/proto/src/persist_atoms.proto
index d4fbd1b..9dbdcb0 100644
--- a/proto/src/persist_atoms.proto
+++ b/proto/src/persist_atoms.proto
@@ -719,6 +719,10 @@
     optional int32 count_of_disallowed_satellite_access = 27;
     optional int32 count_of_satellite_access_check_fail = 28;
     optional bool is_provisioned = 29;
+    optional int32 carrier_id = 30;
+    optional int32 count_of_satellite_allowed_state_changed_events = 31;
+    optional int32 count_of_successful_location_queries = 32;
+    optional int32 count_of_failed_location_queries = 33;
 }
 
 message SatelliteSession {
@@ -735,6 +739,10 @@
     optional int32 count_of_incoming_datagram_failed = 11;
     optional bool is_demo_mode = 12;
     optional int32 max_ntn_signal_strength_level = 13;
+    optional int32 carrier_id = 14;
+    optional int32 count_of_satellite_notification_displayed = 15;
+    optional int32 count_of_auto_exit_due_to_screen_off = 16;
+    optional int32 count_of_auto_exit_due_to_tn_network = 17;
 }
 
 message SatelliteIncomingDatagram {
@@ -742,6 +750,7 @@
     optional int32 datagram_size_bytes = 2;
     optional int64 datagram_transfer_time_millis = 3;
     optional bool is_demo_mode = 4;
+    optional int32 carrier_id = 5;
 }
 
 message SatelliteOutgoingDatagram {
@@ -750,6 +759,7 @@
     optional int32 datagram_size_bytes = 3;
     optional int64 datagram_transfer_time_millis = 4;
     optional bool is_demo_mode = 5;
+    optional int32 carrier_id = 6;
 }
 
 message SatelliteProvision {
@@ -757,6 +767,7 @@
     optional int32 provisioning_time_sec = 2;
     optional bool is_provision_request = 3;
     optional bool is_canceled = 4;
+    optional int32 carrier_id = 5;
 }
 
 message SatelliteSosMessageRecommender {
@@ -809,6 +820,8 @@
     optional int32 satellite_session_gap_min_sec = 5;
     optional int32 satellite_session_gap_avg_sec = 6;
     optional int32 satellite_session_gap_max_sec = 7;
+    optional int32 carrier_id = 8;
+    optional bool is_device_entitled = 9;
 }
 
 message SatelliteEntitlement {
@@ -836,4 +849,6 @@
     optional int32 result_code = 7;
     repeated string country_codes = 8;
     optional int32 config_data_source = 9;
+    optional int32 carrier_id = 10;
+    optional int32 triggering_event = 11;
 }
diff --git a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
index 6f84521..7928e4b 100644
--- a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
+++ b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
@@ -170,7 +170,13 @@
                                                 TelephonyManager.class)
                                         .createForSubscriptionId(subId);
                             }
-                            mCarrierId = carrierId;
+                            if (Flags.ignoreCarrieridResetForSimRemoval()) {
+                                if (carrierId > 0) {
+                                    mCarrierId = carrierId;
+                                }
+                            } else {
+                                mCarrierId = carrierId;
+                            }
                             updateSimOperator();
                             // If device is screen locked do not proceed to handle
                             // EVENT_ALARM_OR_CONFIG_CHANGE
diff --git a/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java b/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
index 98d0536..6326d6c 100644
--- a/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierPrivilegesTracker.java
@@ -31,6 +31,7 @@
 import android.annotation.ElapsedRealtimeLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -45,6 +46,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
 import android.os.PersistableBundle;
@@ -66,7 +68,7 @@
 import android.util.Pair;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.uicc.UiccPort;
 import com.android.internal.telephony.uicc.UiccProfile;
 import com.android.telephony.Rlog;
@@ -78,7 +80,6 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -93,6 +94,7 @@
  * Registered Telephony entities will receive notifications when the UIDs with these privileges
  * change.
  */
+@SuppressLint("MissingPermission")
 public class CarrierPrivilegesTracker extends Handler {
     private static final String TAG = CarrierPrivilegesTracker.class.getSimpleName();
 
@@ -176,6 +178,8 @@
     private static final int ACTION_SET_TEST_OVERRIDE_CARRIER_SERVICE_PACKAGE = 11;
 
     private final Context mContext;
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
     private final Phone mPhone;
     private final PackageManager mPackageManager;
     private final UserManager mUserManager;
@@ -195,7 +199,7 @@
     @Nullable private List<UiccAccessRule> mTestOverrideRules = null;
     @Nullable private String mTestOverrideCarrierServicePackage = null;
     // Map of PackageName -> Certificate hashes for that Package
-    @NonNull private final Map<String, Set<String>> mInstalledPackageCerts = new ArrayMap<>();
+    @NonNull private final Map<String, Set<Integer>> mInstalledPackageCertHashes = new ArrayMap<>();
     // Map of PackageName -> UIDs for that Package
     @NonNull private final Map<String, Set<Integer>> mCachedUids = new ArrayMap<>();
 
@@ -223,6 +227,8 @@
             "mPrivilegedPackageInfoLock.writeLock()"})
     private boolean mSimIsReadyButNotLoaded = false;
 
+    private volatile Handler mCurrentHandler;
+
     /** Small snapshot to hold package names and UIDs of privileged packages. */
     private static final class PrivilegedPackageInfo {
         @NonNull final Set<String> mPackageNames;
@@ -297,7 +303,9 @@
                                 return;
                             }
 
-                            sendMessage(obtainMessage(ACTION_SIM_STATE_UPDATED, slotId, simState));
+                            mCurrentHandler.sendMessage(
+                                    mCurrentHandler.obtainMessage(
+                                            ACTION_SIM_STATE_UPDATED, slotId, simState));
                             break;
                         }
                         case Intent.ACTION_PACKAGE_ADDED: // fall through
@@ -316,7 +324,7 @@
                             boolean notExist = false;
                             try {
                                 disabledByUser = action.equals(Intent.ACTION_PACKAGE_CHANGED)
-                                        && mPackageManager.getApplicationEnabledSetting(pkgName)
+                                        && getApplicationEnabledSetting(pkgName)
                                         == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
                             } catch (IllegalArgumentException iae) {
                                 // Very rare case when package changed race with package removed
@@ -329,7 +337,8 @@
                                     ? ACTION_PACKAGE_REMOVED_OR_DISABLED_BY_USER
                                     : ACTION_PACKAGE_ADDED_REPLACED_OR_CHANGED;
 
-                            sendMessage(obtainMessage(what, pkgName));
+                            mCurrentHandler.sendMessage(
+                                    mCurrentHandler.obtainMessage(what, pkgName));
                             break;
                         }
                     }
@@ -337,10 +346,12 @@
             };
 
     public CarrierPrivilegesTracker(
-            @NonNull Looper looper, @NonNull Phone phone, @NonNull Context context) {
+            @NonNull Looper looper, @NonNull Phone phone,
+            @NonNull Context context, @NonNull FeatureFlags featureFlags) {
         super(looper);
-        mContext = context;
         mPhone = phone;
+        mContext = context;
+        mFeatureFlags = featureFlags;
         mPackageManager = mContext.getPackageManager();
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         mCarrierConfigManager =
@@ -356,10 +367,66 @@
                 (TelephonyRegistryManager)
                         mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
 
+        if (mFeatureFlags.asyncInitCarrierPrivilegesTracker()) {
+            final Object localLock = new Object();
+            HandlerThread initializerThread =
+                    new HandlerThread("CarrierPrivilegesTracker Initializer") {
+                        @Override
+                        protected void onLooperPrepared() {
+                            synchronized (localLock) {
+                                localLock.notifyAll();
+                            }
+                        }
+                    };
+            synchronized (localLock) {
+                initializerThread.start();
+                while (true) {
+                    try {
+                        localLock.wait();
+                        break;
+                    } catch (InterruptedException ie) {
+                    }
+                }
+            }
+            mCurrentHandler = new Handler(initializerThread.getLooper()) {
+                @Override
+                public void handleMessage(Message msg) {
+                    switch(msg.what) {
+                        case ACTION_INITIALIZE_TRACKER:
+                            handleInitializeTracker();
+                            if (!hasMessagesOrCallbacks()) {
+                                mCurrentHandler = CarrierPrivilegesTracker.this;
+                                initializerThread.quitSafely();
+                            }
+                            break;
+                        default:
+                            Message m = CarrierPrivilegesTracker.this.obtainMessage();
+                            m.copyFrom(msg);
+                            m.sendToTarget();
+                            if (!hasMessagesOrCallbacks()) {
+                                mCurrentHandler = CarrierPrivilegesTracker.this;
+                                initializerThread.quitSafely();
+                            }
+                            break;
+                    }
+                }
+            };
+        } else {
+            mCurrentHandler = this;
+        }
+
+        mCurrentHandler.sendMessage(obtainMessage(ACTION_INITIALIZE_TRACKER));
+
         IntentFilter certFilter = new IntentFilter();
         certFilter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED);
         certFilter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED);
-        mContext.registerReceiver(mIntentReceiver, certFilter);
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            mContext.registerReceiverAsUser(
+                    mIntentReceiver, UserHandle.of(ActivityManager.getCurrentUser()), certFilter,
+                    /* broadcastPermission= */ null, /* scheduler= */ null);
+        } else {
+            mContext.registerReceiver(mIntentReceiver, certFilter);
+        }
 
         IntentFilter packageFilter = new IntentFilter();
         packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
@@ -370,9 +437,14 @@
         // For package-related broadcasts, specify the data scheme for "package" to receive the
         // package name along with the broadcast
         packageFilter.addDataScheme("package");
-        mContext.registerReceiver(mIntentReceiver, packageFilter);
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            mContext.registerReceiverAsUser(
+                    mIntentReceiver, UserHandle.of(ActivityManager.getCurrentUser()), packageFilter,
+                    /* broadcastPermission= */ null, /* scheduler= */ null);
+        } else {
+            mContext.registerReceiver(mIntentReceiver, packageFilter);
+        }
 
-        sendMessage(obtainMessage(ACTION_INITIALIZE_TRACKER));
     }
 
     @Override
@@ -480,7 +552,7 @@
                     && mClearUiccRulesUptimeMillis == CLEAR_UICC_RULE_NOT_SCHEDULED) {
                 mClearUiccRulesUptimeMillis =
                         SystemClock.uptimeMillis() + CLEAR_UICC_RULES_DELAY_MILLIS;
-                sendMessageAtTime(obtainMessage(ACTION_CLEAR_UICC_RULES),
+                mCurrentHandler.sendMessageAtTime(obtainMessage(ACTION_CLEAR_UICC_RULES),
                         mClearUiccRulesUptimeMillis);
                 mLocalLog.log("SIM is gone, simState=" + TelephonyManager.simStateToString(simState)
                         + ". Delay " + TimeUnit.MILLISECONDS.toSeconds(
@@ -537,18 +609,29 @@
         return uiccProfile.getCarrierPrivilegeAccessRules();
     }
 
-    private void handlePackageAddedReplacedOrChanged(@Nullable String pkgName) {
-        if (pkgName == null) return;
+    private PackageInfo getPackageInfoForPackage(@Nullable String pkgName) {
+        if (pkgName == null) return null;
 
         PackageInfo pkg;
         try {
-            pkg = mPackageManager.getPackageInfo(pkgName, INSTALLED_PACKAGES_QUERY_FLAGS);
+            return mFeatureFlags.supportCarrierServicesForHsum()
+                        ? mPackageManager.getPackageInfoAsUser(
+                                pkgName,
+                                INSTALLED_PACKAGES_QUERY_FLAGS,
+                                ActivityManager.getCurrentUser())
+                        : mPackageManager.getPackageInfo(
+                                pkgName, INSTALLED_PACKAGES_QUERY_FLAGS);
         } catch (NameNotFoundException e) {
             Rlog.e(TAG, "Error getting installed package: " + pkgName, e);
-            return;
+            return null;
         }
+    }
 
-        updateCertsForPackage(pkg);
+    private void handlePackageAddedReplacedOrChanged(@Nullable String pkgName) {
+        PackageInfo pkg = getPackageInfoForPackage(pkgName);
+        if (pkg == null) return;
+
+        updateCertHashHashesForPackage(pkg);
         // Invalidate cache because this may be a package already on the device but getting
         // installed for a user it wasn't installed in before, which means there will be an
         // additional UID.
@@ -556,30 +639,46 @@
         if (VDBG) {
             Rlog.d(TAG, "Package added/replaced/changed:"
                     + " pkg=" + Rlog.pii(TAG, pkgName)
-                    + " cert hashes=" + mInstalledPackageCerts.get(pkgName));
+                    + " cert hashes=" + mInstalledPackageCertHashes.get(pkgName));
         }
 
         maybeUpdatePrivilegedPackagesAndNotifyRegistrants();
     }
 
-    private void updateCertsForPackage(@NonNull PackageInfo pkg) {
-        Set<String> certs = new ArraySet<>(1);
+    private void updateCertHashHashesForPackage(@NonNull PackageInfo pkg) {
+        Set<Integer> certs = new ArraySet<>(2);
         List<Signature> signatures = UiccAccessRule.getSignatures(pkg);
         for (Signature signature : signatures) {
             byte[] sha1 = UiccAccessRule.getCertHash(signature, SHA_1);
-            certs.add(IccUtils.bytesToHexString(sha1).toUpperCase(Locale.ROOT));
+            certs.add(UiccAccessRule.getCertificateHashHashCode(sha1));
 
             byte[] sha256 = UiccAccessRule.getCertHash(signature, SHA_256);
-            certs.add(IccUtils.bytesToHexString(sha256).toUpperCase(Locale.ROOT));
+            certs.add(UiccAccessRule.getCertificateHashHashCode(sha256));
         }
 
-        mInstalledPackageCerts.put(pkg.packageName, certs);
+        mInstalledPackageCertHashes.put(pkg.packageName, certs);
+    }
+
+    private Set<byte[]> getCertsForPackage(@NonNull String pkgName) {
+        PackageInfo pkg = getPackageInfoForPackage(pkgName);
+        if (pkg == null) return Collections.emptySet();
+
+        List<Signature> signatures = UiccAccessRule.getSignatures(pkg);
+
+        ArraySet<byte[]> certs = new ArraySet<>(2);
+        for (Signature signature : signatures) {
+            certs.add(UiccAccessRule.getCertHash(signature, SHA_1));
+            certs.add(UiccAccessRule.getCertHash(signature, SHA_256));
+        }
+
+        return certs;
     }
 
     private void handlePackageRemovedOrDisabledByUser(@Nullable String pkgName) {
         if (pkgName == null) return;
 
-        if (mInstalledPackageCerts.remove(pkgName) == null || mCachedUids.remove(pkgName) == null) {
+        if (mInstalledPackageCertHashes.remove(pkgName) == null
+                || mCachedUids.remove(pkgName) == null) {
             Rlog.e(TAG, "Unknown package was uninstalled or disabled by user: " + pkgName);
             return;
         }
@@ -611,7 +710,7 @@
             msg +=
                     " installed pkgs="
                             + getObfuscatedPackages(
-                                    mInstalledPackageCerts.entrySet(),
+                                    mInstalledPackageCertHashes.entrySet(),
                                     e -> "pkg(" + Rlog.pii(TAG, e.getKey()) + ")=" + e.getValue());
         }
         mLocalLog.log(msg);
@@ -620,9 +719,12 @@
     private void refreshInstalledPackageCache() {
         List<PackageInfo> installedPackages =
                 mPackageManager.getInstalledPackagesAsUser(
-                        INSTALLED_PACKAGES_QUERY_FLAGS, UserHandle.SYSTEM.getIdentifier());
+                        INSTALLED_PACKAGES_QUERY_FLAGS,
+                        mFeatureFlags.supportCarrierServicesForHsum()
+                                ? ActivityManager.getCurrentUser()
+                                : UserHandle.SYSTEM.getIdentifier());
         for (PackageInfo pkg : installedPackages) {
-            updateCertsForPackage(pkg);
+            updateCertHashHashesForPackage(pkg);
             // This may be unnecessary before initialization, but invalidate the cache all the time
             // just in case to ensure consistency.
             getUidsForPackage(pkg.packageName, /* invalidateCache= */ true);
@@ -711,8 +813,12 @@
         Set<String> carrierServiceEligiblePackages = new ArraySet<>();
         Set<String> privilegedPackageNames = new ArraySet<>();
         Set<Integer> privilegedUids = new ArraySet<>();
-        for (Map.Entry<String, Set<String>> e : mInstalledPackageCerts.entrySet()) {
-            final int priv = getPackagePrivilegedStatus(e.getKey(), e.getValue());
+        for (Map.Entry<String, Set<Integer>> e : mInstalledPackageCertHashes.entrySet()) {
+            if (!isPackageMaybePrivileged(e.getKey(), e.getValue())) continue;
+
+            Set<byte[]> fullCerts = getCertsForPackage(e.getKey());
+
+            final int priv = getPackagePrivilegedStatus(e.getKey(), fullCerts);
             switch (priv) {
                 case PACKAGE_PRIVILEGED_FROM_SIM:
                 case PACKAGE_PRIVILEGED_FROM_CARRIER_SERVICE_TEST_OVERRIDE: // fallthrough
@@ -731,32 +837,58 @@
                 getCarrierService(carrierServiceEligiblePackages));
     }
 
+    private boolean isPackageMaybePrivileged(
+            @NonNull String pkgName, @NonNull Set<Integer> hashHashes) {
+        for (Integer hashHash : hashHashes) {
+            // Non-null (whether empty or not) test override rule will ignore the UICC and CC rules
+            if (mTestOverrideRules != null) {
+                for (UiccAccessRule rule : mTestOverrideRules) {
+                    if (rule.hasMatchingCertificateHashHashAndPackageName(hashHash, pkgName)) {
+                        return true;
+                    }
+                }
+            } else {
+                for (UiccAccessRule rule : mUiccRules) {
+                    if (rule.hasMatchingCertificateHashHashAndPackageName(hashHash, pkgName)) {
+                        return true;
+                    }
+                }
+                for (UiccAccessRule rule : mCarrierConfigRules) {
+                    if (rule.hasMatchingCertificateHashHashAndPackageName(hashHash, pkgName)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
     /**
      * Returns the privilege status of the provided package.
      *
      * <p>Returned privilege status depends on whether a package matches the certificates from
      * carrier config, from test overrides or from certificates stored on the SIM.
      */
-    private int getPackagePrivilegedStatus(@NonNull String pkgName, @NonNull Set<String> certs) {
+    private int getPackagePrivilegedStatus(@NonNull String pkgName, @NonNull Set<byte[]> certs) {
         // Double-nested for loops, but each collection should contain at most 2 elements in nearly
         // every case.
         // TODO(b/184382310) find a way to speed this up
-        for (String cert : certs) {
+        for (byte[] cert : certs) {
             // Non-null (whether empty or not) test override rule will ignore the UICC and CC rules
             if (mTestOverrideRules != null) {
                 for (UiccAccessRule rule : mTestOverrideRules) {
-                    if (rule.matches(cert, pkgName)) {
+                    if (rule.hasMatchingCertificateHashAndPackageName(cert, pkgName)) {
                         return PACKAGE_PRIVILEGED_FROM_SIM;
                     }
                 }
             } else {
                 for (UiccAccessRule rule : mUiccRules) {
-                    if (rule.matches(cert, pkgName)) {
+                    if (rule.hasMatchingCertificateHashAndPackageName(cert, pkgName)) {
                         return PACKAGE_PRIVILEGED_FROM_SIM;
                     }
                 }
                 for (UiccAccessRule rule : mCarrierConfigRules) {
-                    if (rule.matches(cert, pkgName)) {
+                    if (rule.hasMatchingCertificateHashAndPackageName(cert, pkgName)) {
                         return pkgName.equals(mTestOverrideCarrierServicePackage)
                                 ? PACKAGE_PRIVILEGED_FROM_CARRIER_SERVICE_TEST_OVERRIDE
                                 : PACKAGE_PRIVILEGED_FROM_CARRIER_CONFIG;
@@ -794,7 +926,9 @@
     private int getPackageUid(@Nullable String pkgName) {
         int uid = Process.INVALID_UID;
         try {
-            uid = mPackageManager.getPackageUid(pkgName, /* flags= */0);
+            uid = mFeatureFlags.supportCarrierServicesForHsum()
+                    ? mPackageManager.getPackageUidAsUser(pkgName, ActivityManager.getCurrentUser())
+                    : mPackageManager.getPackageUid(pkgName, /* flags= */0);
         } catch (NameNotFoundException e) {
             Rlog.e(TAG, "Unable to find uid for package " + pkgName);
         }
@@ -825,7 +959,7 @@
             pw.println(
                     "CarrierPrivilegesTracker - Obfuscated Pkgs + Certs: "
                             + getObfuscatedPackages(
-                                    mInstalledPackageCerts.entrySet(),
+                                    mInstalledPackageCertHashes.entrySet(),
                                     e -> "pkg(" + Rlog.pii(TAG, e.getKey()) + ")=" + e.getValue()));
         }
         pw.println("mClearUiccRulesUptimeMillis: " + mClearUiccRulesUptimeMillis);
@@ -841,7 +975,8 @@
      * @see TelephonyManager#setCarrierTestOverride
      */
     public void setTestOverrideCarrierPrivilegeRules(@Nullable String carrierPrivilegeRules) {
-        sendMessage(obtainMessage(ACTION_SET_TEST_OVERRIDE_RULE, carrierPrivilegeRules));
+        mCurrentHandler.sendMessage(
+                obtainMessage(ACTION_SET_TEST_OVERRIDE_RULE, carrierPrivilegeRules));
     }
 
     /**
@@ -857,7 +992,7 @@
      * @see TelephonyManager#setCarrierServicePackageOverride
      */
     public void setTestOverrideCarrierServicePackage(@Nullable String carrierServicePackage) {
-        sendMessage(obtainMessage(
+        mCurrentHandler.sendMessage(obtainMessage(
                 ACTION_SET_TEST_OVERRIDE_CARRIER_SERVICE_PACKAGE, carrierServicePackage));
     }
 
@@ -994,10 +1129,25 @@
         // Do the PackageManager queries before we take the lock, as these are the longest-running
         // pieces of this method and don't depend on the set of carrier apps.
         List<ResolveInfo> resolveInfos = new ArrayList<>();
-        resolveInfos.addAll(mPackageManager.queryBroadcastReceivers(intent, 0));
-        resolveInfos.addAll(mPackageManager.queryIntentActivities(intent, 0));
-        resolveInfos.addAll(mPackageManager.queryIntentServices(intent, 0));
-        resolveInfos.addAll(mPackageManager.queryIntentContentProviders(intent, 0));
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            resolveInfos.addAll(
+                    mPackageManager.queryBroadcastReceiversAsUser(
+                            intent, /* flags= */ 0, ActivityManager.getCurrentUser()));
+            resolveInfos.addAll(
+                    mPackageManager.queryIntentActivitiesAsUser(
+                            intent, /* flags= */ 0, ActivityManager.getCurrentUser()));
+            resolveInfos.addAll(
+                    mPackageManager.queryIntentServicesAsUser(
+                            intent, /* flags= */ 0, ActivityManager.getCurrentUser()));
+            resolveInfos.addAll(
+                    mPackageManager.queryIntentContentProvidersAsUser(
+                            intent, /* flags= */ 0, ActivityManager.getCurrentUser()));
+        } else {
+            resolveInfos.addAll(mPackageManager.queryBroadcastReceivers(intent, 0));
+            resolveInfos.addAll(mPackageManager.queryIntentActivities(intent, 0));
+            resolveInfos.addAll(mPackageManager.queryIntentServices(intent, 0));
+            resolveInfos.addAll(mPackageManager.queryIntentContentProviders(intent, 0));
+        }
 
         // Now actually check which of the resolved packages have carrier privileges.
         mPrivilegedPackageInfoLock.readLock().lock();
@@ -1031,8 +1181,15 @@
 
     @NonNull
     private Pair<String, Integer> getCarrierService(@NonNull Set<String> simPrivilegedPackages) {
-        List<ResolveInfo> carrierServiceResolveInfos = mPackageManager.queryIntentServices(
-                new Intent(CarrierService.CARRIER_SERVICE_INTERFACE), /* flags= */ 0);
+        List<ResolveInfo> carrierServiceResolveInfos =
+                mFeatureFlags.supportCarrierServicesForHsum()
+                        ? mPackageManager.queryIntentServicesAsUser(
+                                new Intent(CarrierService.CARRIER_SERVICE_INTERFACE),
+                                /* flags= */ 0,
+                                ActivityManager.getCurrentUser())
+                        : mPackageManager.queryIntentServices(
+                                new Intent(CarrierService.CARRIER_SERVICE_INTERFACE),
+                                /* flags= */ 0);
         String carrierServicePackageName = null;
         for (ResolveInfo resolveInfo : carrierServiceResolveInfos) {
             String packageName = getPackageName(resolveInfo);
@@ -1050,4 +1207,16 @@
                 ? new Pair<>(null, Process.INVALID_UID)
                 : new Pair<>(carrierServicePackageName, getPackageUid(carrierServicePackageName));
     }
+
+    private @PackageManager.EnabledState int getApplicationEnabledSetting(
+            @NonNull String packageName) {
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            return mContext.createContextAsUser(
+                            UserHandle.of(ActivityManager.getCurrentUser()), /* flags= */ 0)
+                    .getPackageManager()
+                    .getApplicationEnabledSetting(packageName);
+        } else {
+            return mPackageManager.getApplicationEnabledSetting(packageName);
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/CarrierResolver.java b/src/java/com/android/internal/telephony/CarrierResolver.java
index 8a9b3e3..829bf6c 100644
--- a/src/java/com/android/internal/telephony/CarrierResolver.java
+++ b/src/java/com/android/internal/telephony/CarrierResolver.java
@@ -30,6 +30,7 @@
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
+import android.os.UserHandle;
 import android.provider.Telephony;
 import android.service.carrier.CarrierIdentifier;
 import android.telephony.CarrierConfigManager;
@@ -41,6 +42,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.metrics.CarrierIdMatchStats;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
@@ -112,6 +114,9 @@
     private final LocalLog mCarrierIdLocalLog = new LocalLog(16);
     private final TelephonyManager mTelephonyMgr;
 
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
+
     private final ContentObserver mContentObserver = new ContentObserver(this) {
         @Override
         public void onChange(boolean selfChange, Uri uri) {
@@ -172,9 +177,10 @@
         }
     };
 
-    public CarrierResolver(Phone phone) {
+    public CarrierResolver(Phone phone, @NonNull FeatureFlags flags) {
         logd("Creating CarrierResolver[" + phone.getPhoneId() + "]");
         mContext = phone.getContext();
+        mFeatureFlags = flags;
         mPhone = phone;
         mTelephonyMgr = TelephonyManager.from(mContext);
 
@@ -500,7 +506,11 @@
             intent.putExtra(TelephonyManager.EXTRA_SPECIFIC_CARRIER_ID, mSpecificCarrierId);
             intent.putExtra(TelephonyManager.EXTRA_SPECIFIC_CARRIER_NAME, mSpecificCarrierName);
             intent.putExtra(TelephonyManager.EXTRA_SUBSCRIPTION_ID, mPhone.getSubId());
-            mContext.sendBroadcast(intent);
+            if (mFeatureFlags.hsumBroadcast()) {
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            } else {
+                mContext.sendBroadcast(intent);
+            }
 
             // notify content observers for specific carrier id change event.
             ContentValues cv = new ContentValues();
@@ -535,7 +545,11 @@
             intent.putExtra(TelephonyManager.EXTRA_CARRIER_ID, mCarrierId);
             intent.putExtra(TelephonyManager.EXTRA_CARRIER_NAME, mCarrierName);
             intent.putExtra(TelephonyManager.EXTRA_SUBSCRIPTION_ID, mPhone.getSubId());
-            mContext.sendBroadcast(intent);
+            if (mFeatureFlags.hsumBroadcast()) {
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            } else {
+                mContext.sendBroadcast(intent);
+            }
 
             // notify content observers for carrier id change event
             ContentValues cv = new ContentValues();
diff --git a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
index 960d794..4f9d84d 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
@@ -17,6 +17,7 @@
 package com.android.internal.telephony;
 
 import android.annotation.NonNull;
+import android.app.ActivityManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -45,6 +46,7 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.util.TelephonyUtils;
 
 import java.io.FileDescriptor;
@@ -160,7 +162,11 @@
     };
 
     public CarrierServiceBindHelper(Context context) {
-        mContext = context.createContextAsUser(Process.myUserHandle(), 0);
+        mContext =
+                context.createContextAsUser(
+                        Flags.supportCarrierServicesForHsum()
+                        ? UserHandle.of(ActivityManager.getCurrentUser())
+                        : Process.myUserHandle(), 0);
 
         updateBindingsAndSimStates();
 
@@ -171,7 +177,7 @@
                 context, mHandler.getLooper(), UserHandle.ALL);
         try {
             Context contextAsUser = mContext.createPackageContextAsUser(mContext.getPackageName(),
-                0, UserHandle.SYSTEM);
+                0, Flags.supportCarrierServicesForHsum() ? UserHandle.CURRENT : UserHandle.SYSTEM);
             contextAsUser.registerReceiver(mUserUnlockedReceiver,
                 new IntentFilter(Intent.ACTION_USER_UNLOCKED), null /* broadcastPermission */,
                 mHandler);
diff --git a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
index d47c2c4..b470e2e 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
@@ -75,7 +75,8 @@
 
 
     @VisibleForTesting
-    public static final String ACTION_NEVER_ASK_AGAIN = "SilenceNoWifiEmrgCallingNotification";
+    public static final String ACTION_NEVER_ASK_AGAIN =
+            "com.android.internal.telephony.action.SILENCE_WIFI_CALLING_NOTIFICATION";
     public final NotificationActionReceiver mActionReceiver = new NotificationActionReceiver();
 
     @VisibleForTesting
@@ -733,6 +734,7 @@
         public void onReceive(Context context, Intent intent) {
             if (intent.getAction().equals(ACTION_NEVER_ASK_AGAIN)) {
                 Rlog.i(LOG_TAG, "NotificationActionReceiver: ACTION_NEVER_ASK_AGAIN");
+                dismissEmergencyCallingNotification();
                 // insert a key to silence future notifications
                 SharedPreferences.Editor editor =
                         PreferenceManager.getDefaultSharedPreferences(context).edit();
@@ -743,5 +745,22 @@
                 context.unregisterReceiver(mActionReceiver);
             }
         }
+
+        /**
+         * Dismiss the notification when the "Do Not Ask Again" button is clicked
+         */
+        private void dismissEmergencyCallingNotification() {
+            if (!mFeatureFlags.stopSpammingEmergencyNotification()) {
+                return;
+            }
+            try {
+                NotificationType t = mNotificationTypeMap.get(NOTIFICATION_EMERGENCY_NETWORK);
+                if (t != null) {
+                    cancelNotification(t);
+                }
+            } catch (Exception e) {
+                Rlog.e(LOG_TAG, "dismissEmergencyCallingNotification", e);
+            }
+        }
     }
 }
diff --git a/src/java/com/android/internal/telephony/Connection.java b/src/java/com/android/internal/telephony/Connection.java
index 82b4c2b8e..7a1c8fe 100644
--- a/src/java/com/android/internal/telephony/Connection.java
+++ b/src/java/com/android/internal/telephony/Connection.java
@@ -692,8 +692,18 @@
                     && mEmergencyNumberInfo.getEmergencyCallRouting()
                         != EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY) {
                 int eccCategory = dialArgs.intentExtras.getInt(
-                        PhoneConstants.EXTRA_EMERGENCY_SERVICE_CATEGORY,
-                        mEmergencyNumberInfo.getEmergencyServiceCategoryBitmask());
+                    PhoneConstants.EXTRA_EMERGENCY_SERVICE_CATEGORY,
+                    mEmergencyNumberInfo.getEmergencyServiceCategoryBitmask());
+                // According to 3gpp 23.167 section 7.1.2, when CS domain is selected,
+                // emergency routing is performed only if the emergency category is provided.
+                if (this instanceof GsmCdmaConnection
+                        && dialArgs.intentExtras.getInt(
+                                PhoneConstants.EXTRA_EMERGENCY_SERVICE_CATEGORY,
+                                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED)
+                                == EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED) {
+                    Rlog.d(TAG, "setEmergencyCallInfo: specific eccCategory is required");
+                    return;
+                }
                 Rlog.d(TAG, "setEmergencyCallInfo: enforce emergency routing eccCategory="
                         + eccCategory);
                 List<String> emergencyUrns = dialArgs.intentExtras.getStringArrayList(
@@ -706,13 +716,32 @@
                         mEmergencyNumberInfo.getMnc(),
                         eccCategory,
                         emergencyUrns,
-                        mEmergencyNumberInfo.getEmergencyNumberSourceBitmask(),
+                        getEmergencyNumberSourceForEmergencyRouting(),
                         EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
             }
         }
     }
 
     /**
+     * Get the emergency number source to be used for emergency routing calls.
+     * This is not getting actual source, instead its forcing the source to
+     * EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING.
+     * Even when the source is EMERGENCY_NUMBER_SOURCE_DATABASE,
+     * to allow the category information delivered by the network to be used,
+     * the source is set to EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING.
+     */
+    private int getEmergencyNumberSourceForEmergencyRouting() {
+        int source = mEmergencyNumberInfo.getEmergencyNumberSourceBitmask();
+        Rlog.d(TAG, "getEmergencyNumberSourceForEmergencyRouting: source=" + source);
+
+        if (source != EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST) {
+            source = EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING;
+        }
+
+        return source;
+    }
+
+    /**
      * Set the non-detectable emergency number information.
      */
     public void setNonDetectableEmergencyCallInfo(int eccCategory,
diff --git a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index da17f60..47c701c 100644
--- a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -303,14 +303,28 @@
     }
 
     @Override
-    public void notifyCallbackModeStarted(Phone sender, @EmergencyCallbackModeType int type) {
-        mTelephonyRegistryMgr.notifyCallBackModeStarted(sender.getPhoneId(),
-                sender.getSubId(), type);
+    public void notifyCallbackModeStarted(Phone sender, @EmergencyCallbackModeType int type,
+            long durationMillis) {
+        if (!mFeatureFlags.emergencyCallbackModeNotification()) return;
+
+        mTelephonyRegistryMgr.notifyCallbackModeStarted(sender.getPhoneId(),
+                sender.getSubId(), type, durationMillis);
+    }
+
+    @Override
+    public void notifyCallbackModeRestarted(Phone sender, @EmergencyCallbackModeType int type,
+            long durationMillis) {
+        if (!mFeatureFlags.emergencyCallbackModeNotification()) return;
+
+        mTelephonyRegistryMgr.notifyCallbackModeRestarted(sender.getPhoneId(),
+                sender.getSubId(), type, durationMillis);
     }
 
     @Override
     public void notifyCallbackModeStopped(Phone sender, @EmergencyCallbackModeType int type,
             @EmergencyCallbackModeStopReason int reason) {
+        if (!mFeatureFlags.emergencyCallbackModeNotification()) return;
+
         mTelephonyRegistryMgr.notifyCallbackModeStopped(sender.getPhoneId(),
                 sender.getSubId(), type, reason);
     }
diff --git a/src/java/com/android/internal/telephony/FdnUtils.java b/src/java/com/android/internal/telephony/FdnUtils.java
index 23cab44..453b3fc 100644
--- a/src/java/com/android/internal/telephony/FdnUtils.java
+++ b/src/java/com/android/internal/telephony/FdnUtils.java
@@ -121,7 +121,8 @@
         try {
             PhoneNumber phoneNumber = phoneNumberUtil.parse(dialStr, defaultCountryIso);
             dialStrE164 = phoneNumberUtil.format(phoneNumber, PhoneNumberFormat.E164);
-            dialStrNational = String.valueOf(phoneNumber.getNationalNumber());
+            dialStrNational = String.valueOf(
+                    phoneNumberUtil.getNationalSignificantNumber(phoneNumber));
         } catch (NumberParseException ignored) {
             Rlog.w(LOG_TAG, "isFDN: could not parse dialStr");
             dialStr = extractSMSC(dialStr);
diff --git a/src/java/com/android/internal/telephony/GbaManager.java b/src/java/com/android/internal/telephony/GbaManager.java
index 7c5f636..047d5d5 100644
--- a/src/java/com/android/internal/telephony/GbaManager.java
+++ b/src/java/com/android/internal/telephony/GbaManager.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony;
 
+import android.annotation.UserIdInt;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -27,6 +28,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.telephony.IBootstrapAuthenticationCallback;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -75,7 +77,8 @@
     private Handler mHandler;
 
     private String mServicePackageName;
-    private String mServicePackageNameOverride;
+    @UserIdInt
+    private int mUserId = UserHandle.USER_SYSTEM;
     private int mReleaseTime;
     private int mRetryTimes = 0;
 
@@ -426,8 +429,9 @@
         try {
             logv("Trying to bind " + servicePackage);
             mServiceConnection = new GbaServiceConnection();
-            if (!mContext.bindService(intent, mServiceConnection,
-                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE)) {
+            if (!mContext.bindServiceAsUser(intent, mServiceConnection,
+                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+                    UserHandle.of(mUserId))) {
                 logd("Cannot bind to the service.");
                 retryBind();
                 return;
@@ -462,12 +466,13 @@
     }
 
     /** override GBA service package name to be connected */
-    public boolean overrideServicePackage(String packageName) {
+    public boolean overrideServicePackage(String packageName, @UserIdInt int userId) {
         synchronized (this) {
-            if (!TextUtils.equals(mServicePackageName, packageName)) {
+            if (!TextUtils.equals(mServicePackageName, packageName) || userId != mUserId) {
                 logv("Service package name is changed from " + mServicePackageName
-                        + " to " + packageName);
+                        + " to " + packageName + ", user id from " + mUserId + " to " + userId);
                 mServicePackageName = packageName;
+                mUserId = userId;
                 if (!mHandler.hasMessages(EVENT_CONFIG_CHANGED)) {
                     mHandler.sendEmptyMessage(EVENT_CONFIG_CHANGED);
                 }
diff --git a/src/java/com/android/internal/telephony/GsmCdmaConnection.java b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
index cc07047..d96663a 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaConnection.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
@@ -30,6 +30,7 @@
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.ServiceState;
+import android.telephony.emergency.EmergencyNumber;
 import android.text.TextUtils;
 
 import com.android.internal.telephony.PhoneInternalInterface.DialArgs;
@@ -186,7 +187,7 @@
 
         mAddress = PhoneNumberUtils.extractNetworkPortionAlt(dialString);
         if (dialArgs.isEmergency) {
-            setEmergencyCallInfo(mOwner, null);
+            setEmergencyCallInfo(mOwner, dialArgs);
 
             // There was no emergency number info found for this call, however it is
             // still marked as an emergency number. This may happen if it was a redialed
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 6fe1232..2e1a89f 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -390,8 +390,9 @@
                 .makeDataNetworkController(this, getLooper(), featureFlags);
 
         mCarrierResolver = mTelephonyComponentFactory.inject(CarrierResolver.class.getName())
-                .makeCarrierResolver(this);
-        mCarrierPrivilegesTracker = new CarrierPrivilegesTracker(Looper.myLooper(), this, context);
+                .makeCarrierResolver(this, featureFlags);
+        mCarrierPrivilegesTracker = new CarrierPrivilegesTracker(
+                Looper.myLooper(), this, context, featureFlags);
 
         getCarrierActionAgent().registerForCarrierAction(
                 CarrierActionAgent.CARRIER_ACTION_SET_METERED_APNS_ENABLED, this,
diff --git a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
index c94480e..6955a55 100644
--- a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
+++ b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
@@ -202,8 +202,7 @@
                         tracker.onSent(mContext);
                         mTrackers.remove(token);
                         mPhone.notifySmsSent(tracker.mDestAddress);
-                        mSmsDispatchersController.notifySmsSent(
-                                tracker.mDestAddress, tracker.mMessageId, true,
+                        mSmsDispatchersController.notifySmsSent(tracker, true,
                                 tracker.isSinglePartOrLastPart(), true /*success*/);
                         break;
                     case ImsSmsImplBase.SEND_STATUS_ERROR:
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index 891f3f4..ca03f5d 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -75,6 +75,7 @@
 import com.android.internal.telephony.analytics.TelephonyAnalytics;
 import com.android.internal.telephony.analytics.TelephonyAnalytics.SmsMmsAnalytics;
 import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.satellite.metrics.CarrierRoamingSatelliteSessionStats;
 import com.android.internal.telephony.util.NotificationChannelController;
@@ -806,7 +807,12 @@
             Intent intent = new Intent(Intents.SMS_REJECTED_ACTION);
             intent.putExtra("result", result);
             intent.putExtra("subId", mPhone.getSubId());
-            mContext.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS);
+            if (mFeatureFlags.hsumBroadcast()) {
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                        android.Manifest.permission.RECEIVE_SMS);
+            } else {
+                mContext.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS);
+            }
         }
         acknowledgeLastIncomingSms(success, result, response);
     }
@@ -2148,9 +2154,13 @@
                 // TODO(b/355049884): This is looking at sms package of the wrong user!
                 UserManager userManager =
                         (UserManager) context.getSystemService(Context.USER_SERVICE);
+                PackageManager pm = context.getPackageManager();
+                if (Flags.hsumPackageManager()) {
+                    pm = context.createContextAsUser(UserHandle.CURRENT, 0).getPackageManager();
+                }
                 if (userManager.isUserUnlocked()) {
-                    context.startActivity(context.getPackageManager().getLaunchIntentForPackage(
-                            Telephony.Sms.getDefaultSmsPackage(context)));
+                    context.startActivityAsUser(pm.getLaunchIntentForPackage(
+                            Telephony.Sms.getDefaultSmsPackage(context)), UserHandle.CURRENT);
                 }
             }
         }
diff --git a/src/java/com/android/internal/telephony/MultiSimSettingController.java b/src/java/com/android/internal/telephony/MultiSimSettingController.java
index e98b996..881adde 100644
--- a/src/java/com/android/internal/telephony/MultiSimSettingController.java
+++ b/src/java/com/android/internal/telephony/MultiSimSettingController.java
@@ -43,6 +43,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelUuid;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.telephony.CarrierConfigManager;
@@ -512,8 +513,8 @@
         SatelliteController satelliteController = SatelliteController.getInstance();
         boolean isSatelliteEnabledOrBeingEnabled = false;
         if (satelliteController != null) {
-            isSatelliteEnabledOrBeingEnabled = satelliteController.isSatelliteEnabled()
-                    || satelliteController.isSatelliteBeingEnabled();
+            isSatelliteEnabledOrBeingEnabled =
+                    satelliteController.isSatelliteEnabledOrBeingEnabled();
         }
 
         if (DBG) {
@@ -792,7 +793,11 @@
         intent.putExtra(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE, type);
         intent.putExtra(EXTRA_SUBSCRIPTION_ID, defaultSubId);
 
-        mContext.sendBroadcast(intent);
+        if (mFeatureFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        } else {
+            mContext.sendBroadcast(intent);
+        }
     }
 
     private void sendSubChangeNotificationIfNeeded(int change, boolean dataSelected,
@@ -830,7 +835,11 @@
             if (simCombinationParams.mWarningType == EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA) {
                 intent.putExtra(EXTRA_SIM_COMBINATION_NAMES, simCombinationParams.mSimNames);
             }
-            mContext.sendBroadcast(intent);
+            if (mFeatureFlags.hsumBroadcast()) {
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            } else {
+                mContext.sendBroadcast(intent);
+            }
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
index c09f8fb..9b2b847 100644
--- a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
+++ b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
@@ -33,6 +33,7 @@
 import android.os.Messenger;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.telephony.CellInfo;
 import android.telephony.LocationAccessPolicy;
 import android.telephony.NetworkScan;
@@ -567,8 +568,8 @@
         //   3. The live scan is not requested by mobile network setting menu
         private synchronized boolean interruptLiveScan(NetworkScanRequestInfo nsri) {
             if (mLiveRequestInfo != null && mPendingRequestInfo == null
-                    && nsri.mUid == Process.SYSTEM_UID
-                            && mLiveRequestInfo.mUid != Process.SYSTEM_UID) {
+                    && UserHandle.isSameApp(nsri.mUid, Process.SYSTEM_UID)
+                            && !UserHandle.isSameApp(mLiveRequestInfo.mUid, Process.SYSTEM_UID)) {
                 doInterruptScan(mLiveRequestInfo.mScanId);
                 mPendingRequestInfo = nsri;
                 notifyMessenger(mLiveRequestInfo, TelephonyScanManager.CALLBACK_SCAN_ERROR,
diff --git a/src/java/com/android/internal/telephony/NetworkTypeController.java b/src/java/com/android/internal/telephony/NetworkTypeController.java
index 9b1bd24..9d27146 100644
--- a/src/java/com/android/internal/telephony/NetworkTypeController.java
+++ b/src/java/com/android/internal/telephony/NetworkTypeController.java
@@ -233,6 +233,8 @@
     private int mLastAnchorNrCellId = PhysicalChannelConfig.PHYSICAL_CELL_ID_UNKNOWN;
     private boolean mDoesPccListIndicateIdle = false;
 
+    private boolean mInVoiceCall = false;
+
     /**
      * NetworkTypeController constructor.
      *
@@ -729,6 +731,7 @@
                             break;
                         }
                     }
+                    mInVoiceCall = inVoiceCall;
                     break;
                 default:
                     throw new RuntimeException("Received invalid event: " + msg.what);
@@ -1314,6 +1317,7 @@
             mRatchetedNrBands.addAll(nrBands);
         } else {
             if (mFeatureFlags.supportNrSaRrcIdle() && mDoesPccListIndicateIdle
+                    && anchorNrCellId != mLastAnchorNrCellId
                     && isUsingPhysicalChannelConfigForRrcDetection()
                     && !mPrimaryCellChangedWhileIdle
                     && !isNrAdvancedForPccFields(nrBandwidths, nrBands)) {
@@ -1352,11 +1356,13 @@
         if (secondaryRule != null) {
             int secondaryDuration = secondaryRule.getSecondaryTimer(mSecondaryTimerState);
             long durationMillis = secondaryDuration * 1000L;
-            if ((mSecondaryTimerExpireTimestamp - SystemClock.uptimeMillis()) > durationMillis) {
+            long now = SystemClock.uptimeMillis();
+            if ((mSecondaryTimerExpireTimestamp - now) > durationMillis) {
                 if (DBG) log("Due to PCI change, reduce the secondary timer to " + durationMillis);
                 removeMessages(EVENT_SECONDARY_TIMER_EXPIRED);
                 sendMessageDelayed(EVENT_SECONDARY_TIMER_EXPIRED, mSecondaryTimerState,
                         durationMillis);
+                mSecondaryTimerExpireTimestamp = now + durationMillis;
             }
         } else {
             loge("!! Secondary timer is active, but found no rule for " + mPrimaryTimerState);
@@ -1368,6 +1374,8 @@
         if (mIsPrimaryTimerActive) {
             log("Transition without timer from " + getCurrentState().getName() + " to " + destName
                     + " due to existing " + mPrimaryTimerState + " primary timer.");
+        } else if (mInVoiceCall) {
+            log("Skip primary timer to " + destName + " due to in call");
         } else {
             if (DBG) {
                 log("Transition with primary timer from " + mPreviousState + " to " + destName);
@@ -1392,7 +1400,10 @@
             log("Transition with secondary timer from " + currentName + " to "
                     + destState.getName());
         }
-        if (!mIsDeviceIdleMode && rule != null && rule.getSecondaryTimer(currentName) > 0) {
+        if (mInVoiceCall) {
+            log("Skip secondary timer from " + currentName + " to "
+                    + destState.getName() + " due to in call");
+        } else if (!mIsDeviceIdleMode && rule != null && rule.getSecondaryTimer(currentName) > 0) {
             int duration = rule.getSecondaryTimer(currentName);
             if (mLastShownNrDueToAdvancedBand && mNrAdvancedBandsSecondaryTimer > 0) {
                 duration = mNrAdvancedBandsSecondaryTimer;
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index fb9b2c3..61f1d45 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -657,7 +657,7 @@
         // Initialize device storage and outgoing SMS usage monitors for SMSDispatchers.
         mTelephonyComponentFactory = telephonyComponentFactory;
         mSmsStorageMonitor = mTelephonyComponentFactory.inject(SmsStorageMonitor.class.getName())
-                .makeSmsStorageMonitor(this);
+                .makeSmsStorageMonitor(this, mFeatureFlags);
         mSmsUsageMonitor = mTelephonyComponentFactory.inject(SmsUsageMonitor.class.getName())
                 .makeSmsUsageMonitor(context);
         mUiccController = UiccController.getInstance();
@@ -898,6 +898,8 @@
                         Rlog.e(mLogTag, "Invalid Exception for usage setting " + ar.exception);
                         break; // technically extraneous, but good hygiene
                     }
+                } else {
+                    mUsageSettingFromModem = msg.arg1;
                 }
                 break;
             default:
@@ -3179,7 +3181,11 @@
             Intent intent = new Intent(TelephonyIntents.SECRET_CODE_ACTION,
                     Uri.parse("android_secret_code://" + code));
             intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-            mContext.sendBroadcast(intent, null, options.toBundle());
+            if (mFeatureFlags.hsumBroadcast()) {
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL, null, options.toBundle());
+            } else {
+                mContext.sendBroadcast(intent, null, options.toBundle());
+            }
 
             // {@link TelephonyManager.ACTION_SECRET_CODE} will replace {@link
             // TelephonyIntents#SECRET_CODE_ACTION} in the next Android version. Before
@@ -3187,7 +3193,12 @@
             Intent secrectCodeIntent = new Intent(TelephonyManager.ACTION_SECRET_CODE,
                     Uri.parse("android_secret_code://" + code));
             secrectCodeIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-            mContext.sendBroadcast(secrectCodeIntent, null, options.toBundle());
+            if (mFeatureFlags.hsumBroadcast()) {
+                mContext.sendBroadcastAsUser(secrectCodeIntent, UserHandle.ALL, null,
+                        options.toBundle());
+            } else {
+                mContext.sendBroadcast(secrectCodeIntent, null, options.toBundle());
+            }
         }
     }
 
@@ -4475,7 +4486,8 @@
             mCi.getUsageSetting(obtainMessage(EVENT_GET_USAGE_SETTING_DONE));
             // If the modem value is already known, and the value has changed, proceed to update.
         } else if (mPreferredUsageSetting != mUsageSettingFromModem) {
-            mCi.setUsageSetting(obtainMessage(EVENT_SET_USAGE_SETTING_DONE),
+            mCi.setUsageSetting(obtainMessage(EVENT_SET_USAGE_SETTING_DONE,
+                        mPreferredUsageSetting, 0 /* unused */),
                     mPreferredUsageSetting);
         }
         return true;
@@ -5283,22 +5295,43 @@
     }
 
     /**
-     * Start callback mode
+     * Start the emergency callback mode
      * @param type for callback mode entry.
+     * @param durationMillis is the number of milliseconds remaining in the emergency callback
+     *                        mode.
      */
-    public void startCallbackMode(@TelephonyManager.EmergencyCallbackModeType int type) {
-        Rlog.d(mLogTag, "startCallbackMode:type=" + type);
-        mNotifier.notifyCallbackModeStarted(this, type);
+    public void startEmergencyCallbackMode(@TelephonyManager.EmergencyCallbackModeType int type,
+            long durationMillis) {
+        if (!mFeatureFlags.emergencyCallbackModeNotification()) return;
+
+        Rlog.d(mLogTag, "startEmergencyCallbackMode:type=" + type);
+        mNotifier.notifyCallbackModeStarted(this, type, durationMillis);
     }
 
     /**
-     * Stop callback mode
+     * Restart the emergency callback mode
+     * @param type for callback mode entry.
+     * @param durationMillis is the number of milliseconds remaining in the emergency callback
+     *                        mode.
+     */
+    public void restartEmergencyCallbackMode(@TelephonyManager.EmergencyCallbackModeType int type,
+            long durationMillis) {
+        if (!mFeatureFlags.emergencyCallbackModeNotification()) return;
+
+        Rlog.d(mLogTag, "restartEmergencyCallbackMode:type=" + type);
+        mNotifier.notifyCallbackModeRestarted(this, type, durationMillis);
+    }
+
+    /**
+     * Stop the emergency callback mode
      * @param type for callback mode exit.
      * @param reason for stopping callback mode.
      */
-    public void stopCallbackMode(@TelephonyManager.EmergencyCallbackModeType int type,
+    public void stopEmergencyCallbackMode(@TelephonyManager.EmergencyCallbackModeType int type,
             @TelephonyManager.EmergencyCallbackModeStopReason int reason) {
-        Rlog.d(mLogTag, "stopCallbackMode:type=" + type + ", reason=" + reason);
+        if (!mFeatureFlags.emergencyCallbackModeNotification()) return;
+
+        Rlog.d(mLogTag, "stopEmergencyCallbackMode:type=" + type + ", reason=" + reason);
         mNotifier.notifyCallbackModeStopped(this, type, reason);
     }
 
diff --git a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
index b96887c..dd96594 100644
--- a/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
+++ b/src/java/com/android/internal/telephony/PhoneConfigurationManager.java
@@ -29,6 +29,7 @@
 import android.os.PowerManager;
 import android.os.RegistrantList;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.sysprop.TelephonyProperties;
 import android.telephony.PhoneCapability;
@@ -773,7 +774,11 @@
 
         Intent intent = new Intent(ACTION_MULTI_SIM_CONFIG_CHANGED);
         intent.putExtra(EXTRA_ACTIVE_SIM_SUPPORTED_COUNT, numOfActiveModems);
-        mContext.sendBroadcast(intent);
+        if (mFeatureFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        } else {
+            mContext.sendBroadcast(intent);
+        }
     }
     /**
      * This is invoked from shell commands during CTS testing only.
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index f7ce388..c077ca9 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -207,7 +207,7 @@
 
                 // Instantiate UiccController so that all other classes can just
                 // call getInstance()
-                sUiccController = UiccController.make(context);
+                sUiccController = UiccController.make(context, featureFlags);
 
                 Rlog.i(LOG_TAG, "Creating SubscriptionManagerService");
                 sSubscriptionManagerService = new SubscriptionManagerService(context,
diff --git a/src/java/com/android/internal/telephony/PhoneNotifier.java b/src/java/com/android/internal/telephony/PhoneNotifier.java
index 3f388fb..a43ff72 100644
--- a/src/java/com/android/internal/telephony/PhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/PhoneNotifier.java
@@ -145,7 +145,12 @@
             List<LinkCapacityEstimate> linkCapacityEstimateList);
 
     /** Notify callback mode started. */
-    void notifyCallbackModeStarted(Phone sender, @EmergencyCallbackModeType int type);
+    void notifyCallbackModeStarted(Phone sender, @EmergencyCallbackModeType int type,
+            long durationMillis);
+
+    /** Notify callback mode restarted. */
+    void notifyCallbackModeRestarted(Phone sender, @EmergencyCallbackModeType int type,
+            long durationMillis);
 
     /** Notify callback mode stopped. */
     void notifyCallbackModeStopped(Phone sender, @EmergencyCallbackModeType int type,
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoController.java b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
index 073e242..7ee3de2 100644
--- a/src/java/com/android/internal/telephony/PhoneSubInfoController.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
@@ -785,9 +785,16 @@
      */
     @Nullable
     private String getCurrentPackageName() {
+        if (mFeatureFlags.hsumPackageManager()) {
+            PackageManager pm = mContext.createContextAsUser(Binder.getCallingUserHandle(), 0)
+                    .getPackageManager();
+            if (pm == null) return null;
+            String[] callingPackageNames = pm.getPackagesForUid(Binder.getCallingUid());
+            return (callingPackageNames == null) ? null : callingPackageNames[0];
+        }
         if (mPackageManager == null) return null;
-        String[] callingUids = mPackageManager.getPackagesForUid(Binder.getCallingUid());
-        return (callingUids == null) ? null : callingUids[0];
+        String[] callingPackageNames = mPackageManager.getPackagesForUid(Binder.getCallingUid());
+        return (callingPackageNames == null) ? null : callingPackageNames[0];
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/ProxyController.java b/src/java/com/android/internal/telephony/ProxyController.java
index 01aed07..3b30d2f 100644
--- a/src/java/com/android/internal/telephony/ProxyController.java
+++ b/src/java/com/android/internal/telephony/ProxyController.java
@@ -28,6 +28,7 @@
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
+import android.os.UserHandle;
 import android.telephony.RadioAccessFamily;
 import android.telephony.TelephonyManager;
 import android.util.Log;
@@ -364,7 +365,11 @@
                     logd("onStartRadioCapabilityResponse got exception=" + ar.exception);
                     mRadioCapabilitySessionId = mUniqueIdGenerator.getAndIncrement();
                     Intent intent = new Intent(TelephonyIntents.ACTION_SET_RADIO_CAPABILITY_FAILED);
-                    mContext.sendBroadcast(intent);
+                    if (mFlags.hsumBroadcast()) {
+                        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+                    } else {
+                        mContext.sendBroadcast(intent);
+                    }
                     clearTransaction();
                     return;
                 }
@@ -611,7 +616,12 @@
         }
 
         // Broadcast that we're done
-        mContext.sendBroadcast(intent, android.Manifest.permission.READ_PHONE_STATE);
+        if (mFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                    android.Manifest.permission.READ_PHONE_STATE);
+        } else {
+            mContext.sendBroadcast(intent, android.Manifest.permission.READ_PHONE_STATE);
+        }
     }
 
     // Clear this transaction
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index 2f2b62a..8a2d5bf 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -20,6 +20,7 @@
 
 import static com.android.internal.telephony.IccSmsInterfaceManager.SMS_MESSAGE_PERIOD_NOT_SPECIFIED;
 import static com.android.internal.telephony.IccSmsInterfaceManager.SMS_MESSAGE_PRIORITY_NOT_SPECIFIED;
+import static com.android.internal.telephony.SmsDispatchersController.PendingRequest;
 import static com.android.internal.telephony.SmsResponse.NO_ERROR_CODE;
 
 import android.annotation.UserIdInt;
@@ -1016,8 +1017,8 @@
      */
     protected void notifySmsSentFailedToEmergencyStateTracker(SmsTracker tracker,
             boolean isOverIms) {
-        mSmsDispatchersController.notifySmsSent(tracker.mDestAddress, tracker.mMessageId,
-                isOverIms, true /*isLastSmsPart*/, false /*success*/);
+        mSmsDispatchersController.notifySmsSent(tracker, isOverIms,
+            true /*isLastSmsPart*/, false /*success*/);
     }
 
     /**
@@ -1052,9 +1053,8 @@
             }
             tracker.onSent(mContext);
             mPhone.notifySmsSent(tracker.mDestAddress);
-            mSmsDispatchersController.notifySmsSent(
-                    tracker.mDestAddress, tracker.mMessageId, false,
-                    tracker.isSinglePartOrLastPart(), true /*success*/);
+            mSmsDispatchersController.notifySmsSent(tracker, false,
+                tracker.isSinglePartOrLastPart(), true /*success*/);
 
             mPhone.getSmsStats().onOutgoingSms(
                     tracker.mImsRetry > 0 /* isOverIms */,
@@ -1399,7 +1399,7 @@
     @UnsupportedAppUsage
     protected void sendData(String callingPackage, int callingUser, String destAddr, String scAddr,
             int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent,
-            boolean isForVvm) {
+            boolean isForVvm, long uniqueMessageId) {
         int messageRef = nextMessageRef();
         SmsMessageBase.SubmitPduBase pdu = getSubmitPdu(
                 scAddr, destAddr, destPort, data, (deliveryIntent != null), messageRef);
@@ -1408,7 +1408,8 @@
             SmsTracker tracker = getSmsTracker(callingPackage, callingUser, map, sentIntent,
                     deliveryIntent, getFormat(), null /*messageUri*/, false /*expectMore*/,
                     null /*fullMessageText*/, false /*isText*/,
-                    true /*persistMessage*/, isForVvm, 0L /* messageId */, messageRef);
+                    true /*persistMessage*/, isForVvm, 0L /* messageId */, messageRef,
+                    uniqueMessageId);
 
             if (!sendSmsByCarrierApp(true /* isDataSms */, tracker)) {
                 sendSubmitPdu(tracker);
@@ -1526,7 +1527,7 @@
             long messageId) {
         sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, messageUri, callingPkg,
                 callingUser, persistMessage, priority, expectMore, validityPeriod, isForVvm,
-                messageId, false);
+                messageId, false, PendingRequest.getNextUniqueMessageId());
     }
 
     /**
@@ -1634,7 +1635,7 @@
             PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri,
             String callingPkg, int callingUser, boolean persistMessage, int priority,
             boolean expectMore, int validityPeriod, boolean isForVvm,
-            long messageId, boolean skipShortCodeCheck) {
+            long messageId, boolean skipShortCodeCheck, long uniqueMessageId) {
         Rlog.d(TAG, "sendText id: " + SmsController.formatCrossStackMessageId(messageId));
         int messageRef = nextMessageRef();
         SmsMessageBase.SubmitPduBase pdu = getSubmitPdu(
@@ -1645,7 +1646,7 @@
             SmsTracker tracker = getSmsTracker(callingPkg, callingUser, map, sentIntent,
                     deliveryIntent, getFormat(), messageUri, expectMore, text, true /*isText*/,
                     persistMessage, priority, validityPeriod, isForVvm, messageId, messageRef,
-                    skipShortCodeCheck);
+                    skipShortCodeCheck, uniqueMessageId);
 
             if (!sendSmsByCarrierApp(false /* isDataSms */, tracker)) {
                 sendSubmitPdu(tracker);
@@ -1827,7 +1828,7 @@
             ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
             ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg,
             int callingUser, boolean persistMessage, int priority, boolean expectMore,
-            int validityPeriod, long messageId) {
+            int validityPeriod, long messageId, long uniqueMessageId) {
         final String fullMessageText = getMultipartMessageText(parts);
         int refNumber = getNextConcatenatedRef() & 0x00FF;
         int encoding = SmsConstants.ENCODING_UNKNOWN;
@@ -1890,7 +1891,7 @@
                         smsHeader, encoding, sentIntent, deliveryIntent, (i == (msgCount - 1)),
                         unsentPartCount, anyPartFailed, messageUri,
                         fullMessageText, priority, expectMore, validityPeriod, messageId,
-                        messageRef);
+                        messageRef, uniqueMessageId);
             if (trackers[i] == null) {
                 triggerSentIntentForFailure(sentIntents);
                 return;
@@ -1926,7 +1927,7 @@
             int encoding, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart,
             AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
             String fullMessageText, int priority, boolean expectMore, int validityPeriod,
-            long messageId, int messageRef) {
+            long messageId, int messageRef, long uniqueMessageId) {
         if (isCdmaMo()) {
             UserData uData = new UserData();
             uData.payloadStr = message;
@@ -1956,7 +1957,7 @@
                         deliveryIntent, getFormat(), unsentPartCount, anyPartFailed, messageUri,
                         smsHeader, (!lastPart || expectMore), fullMessageText,  /*isText*/
                         true,  /*persistMessage*/ true, priority, validityPeriod,  /* isForVvm */
-                        false, messageId, messageRef, false);
+                        false, messageId, messageRef, false, uniqueMessageId);
             } else {
                 Rlog.e(TAG, "CdmaSMSDispatcher.getNewSubmitPduTracker(): getSubmitPdu() returned "
                         + "null " + SmsController.formatCrossStackMessageId(messageId));
@@ -1975,7 +1976,7 @@
                         messageUri, smsHeader, (!lastPart || expectMore),
                         fullMessageText,  /*isText*/
                         true,  /*persistMessage*/ false, priority, validityPeriod,  /* isForVvm */
-                        false, messageId, messageRef, false);
+                        false, messageId, messageRef, false, uniqueMessageId);
             } else {
                 Rlog.e(TAG, "GsmSMSDispatcher.getNewSubmitPduTracker(): getSubmitPdu() returned "
                         + "null " + SmsController.formatCrossStackMessageId(messageId));
@@ -2492,6 +2493,7 @@
 
         private int mCarrierId;
         private boolean mSkipShortCodeDestAddrCheck;
+        public final long mUniqueMessageId;
         // SMS anomaly uuid -- unexpected error from RIL
         private final UUID mAnomalyUnexpectedErrorFromRilUUID =
                 UUID.fromString("43043600-ea7a-44d2-9ae6-a58567ac7886");
@@ -2502,7 +2504,8 @@
                 SmsHeader smsHeader, boolean expectMore, String fullMessageText, int subId,
                 boolean isText, boolean persistMessage, int userId, int priority,
                 int validityPeriod, boolean isForVvm, long messageId, int carrierId,
-                int messageRef, boolean skipShortCodeDestAddrCheck) {
+                int messageRef, boolean skipShortCodeDestAddrCheck,
+                long uniqueMessageId) {
             mData = data;
             mSentIntent = sentIntent;
             mDeliveryIntent = deliveryIntent;
@@ -2529,6 +2532,26 @@
             mMessageId = messageId;
             mCarrierId = carrierId;
             mSkipShortCodeDestAddrCheck = skipShortCodeDestAddrCheck;
+            mUniqueMessageId = uniqueMessageId;
+        }
+
+        @VisibleForTesting
+        public SmsTracker(String destAddr, long messageId) {
+            mData = null;
+            mSentIntent = null;
+            mDeliveryIntent = null;
+            mAppInfo = null;
+            mDestAddress = destAddr;
+            mUsesImsServiceForIms = false;
+            mSmsHeader = null;
+            mMessageId = messageId;
+            mUserId = 0;
+            mPriority = 0;
+            mValidityPeriod = 0;
+            mIsForVvm = false;
+            mCarrierId = 0;
+            mSkipShortCodeDestAddrCheck = false;
+            mUniqueMessageId = 0;
         }
 
         public HashMap<String, Object> getData() {
@@ -2820,7 +2843,8 @@
             AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
             SmsHeader smsHeader, boolean expectMore, String fullMessageText, boolean isText,
             boolean persistMessage, int priority, int validityPeriod, boolean isForVvm,
-            long messageId, int messageRef, boolean skipShortCodeCheck) {
+            long messageId, int messageRef, boolean skipShortCodeCheck,
+            long uniqueMessageId) {
         if (!Flags.smsMmsDeliverBroadcastsRedirectToMainUser()) {
             callingUser = UserHandle.getUserHandleForUid(Binder.getCallingUid()).getIdentifier();
         }
@@ -2841,31 +2865,33 @@
                 unsentPartCount, anyPartFailed, messageUri, smsHeader, expectMore,
                 fullMessageText, getSubId(), isText, persistMessage, callingUser, priority,
                 validityPeriod, isForVvm, messageId, mPhone.getCarrierId(), messageRef,
-                skipShortCodeCheck);
+                skipShortCodeCheck, uniqueMessageId);
     }
 
     protected SmsTracker getSmsTracker(String callingPackage, int callingUser,
             HashMap<String, Object> data, PendingIntent sentIntent, PendingIntent deliveryIntent,
             String format, Uri messageUri, boolean expectMore, String fullMessageText,
             boolean isText, boolean persistMessage, boolean isForVvm,
-            long messageId, int messageRef) {
+            long messageId, int messageRef, long uniqueMessageId) {
         return getSmsTracker(callingPackage, callingUser , data, sentIntent, deliveryIntent,
                 format, /*unsentPartCount*/ null, /*anyPartFailed*/ null, messageUri, /*smsHeader*/
                 null, expectMore, fullMessageText, isText,
                 persistMessage, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
                 SMS_MESSAGE_PERIOD_NOT_SPECIFIED,
-                isForVvm, messageId, messageRef, false);
+                isForVvm, messageId, messageRef, false, uniqueMessageId);
     }
 
     protected SmsTracker getSmsTracker(String callingPackage, int callingUser,
             HashMap<String, Object> data, PendingIntent sentIntent, PendingIntent deliveryIntent,
             String format, Uri messageUri, boolean expectMore, String fullMessageText,
             boolean isText, boolean persistMessage, int priority, int validityPeriod,
-            boolean isForVvm, long messageId, int messageRef, boolean skipShortCodeCheck) {
+            boolean isForVvm, long messageId, int messageRef, boolean skipShortCodeCheck,
+            long uniqueMessageId) {
         return getSmsTracker(callingPackage, callingUser, data, sentIntent, deliveryIntent,
                 format, /*unsentPartCount*/ null, /*anyPartFailed*/ null, messageUri, /*smsHeader*/
                 null, expectMore, fullMessageText, isText, persistMessage, priority,
-                validityPeriod, isForVvm, messageId, messageRef, skipShortCodeCheck);
+                validityPeriod, isForVvm, messageId, messageRef, skipShortCodeCheck,
+                uniqueMessageId);
     }
 
     protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index e7500a2..3309d21 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -327,18 +327,10 @@
     private boolean mImsRegistrationOnOff = false;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private boolean mDeviceShuttingDown = false;
+    private CarrierDisplayNameData mCarrierDisplayNameData =
+            new CarrierDisplayNameData.Builder().build();
     /** Keep track of SPN display rules, so we only broadcast intent if something changes. */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private String mCurSpn = null;
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private String mCurDataSpn = null;
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private String mCurPlmn = null;
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private boolean mCurShowPlmn = false;
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private boolean mCurShowSpn = false;
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     @VisibleForTesting
     public int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private int mPrevSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -452,7 +444,7 @@
                 // Once sub id becomes valid, we need to update the service provider name
                 // displayed on the UI again. The old SPN update intents sent to
                 // MobileSignalController earlier were actually ignored due to invalid sub id.
-                updateSpnDisplay();
+                updateCarrierDisplayName();
             }
             mSubId = curSubId;
         }
@@ -559,12 +551,12 @@
                 pollState();
                 // Depends on modem, ServiceState is not necessarily updated, so make sure updating
                 // SPN.
-                updateSpnDisplay();
+                updateCarrierDisplayName();
             } else if (action.equals(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED)) {
                 String lastKnownNetworkCountry = intent.getStringExtra(
                         TelephonyManager.EXTRA_LAST_KNOWN_NETWORK_COUNTRY);
                 if (!mLastKnownNetworkCountry.equals(lastKnownNetworkCountry)) {
-                    updateSpnDisplay();
+                    updateCarrierDisplayName();
                 }
             }
         }
@@ -1245,7 +1237,7 @@
                 if (mUiccApplication == null
                         || mUiccApplication.getState() != AppState.APPSTATE_READY) {
                     mIsSimReady = false;
-                    updateSpnDisplay();
+                    updateCarrierDisplayName();
                 }
                 break;
 
@@ -1424,7 +1416,7 @@
                 updateOtaspState();
                 if (mPhone.isPhoneTypeGsm()) {
                     mCdnr.updateEfFromUsim((SIMRecords) mIccRecords);
-                    updateSpnDisplay();
+                    updateCarrierDisplayName();
                 }
                 break;
 
@@ -1531,7 +1523,7 @@
 
             case EVENT_IMS_CAPABILITY_CHANGED:
                 if (DBG) log("EVENT_IMS_CAPABILITY_CHANGED");
-                updateSpnDisplay();
+                updateCarrierDisplayName();
                 mImsCapabilityChangedRegistrants.notifyRegistrants();
                 break;
 
@@ -1623,7 +1615,7 @@
                     mCdnr.updateEfFromRuim((RuimRecords) mIccRecords);
                     updatePhoneObject();
                     if (mPhone.isPhoneTypeCdma()) {
-                        updateSpnDisplay();
+                        updateCarrierDisplayName();
                     } else {
                         RuimRecords ruim = (RuimRecords) mIccRecords;
                         if (ruim != null) {
@@ -2761,53 +2753,34 @@
         }
     }
 
-    private void notifySpnDisplayUpdate(CarrierDisplayNameData data) {
-        int subId = mPhone.getSubId();
-        // Update ACTION_SERVICE_PROVIDERS_UPDATED if any value changes
-        if (mSubId != subId
-                || data.shouldShowPlmn() != mCurShowPlmn
-                || data.shouldShowSpn() != mCurShowSpn
-                || !TextUtils.equals(data.getSpn(), mCurSpn)
-                || !TextUtils.equals(data.getDataSpn(), mCurDataSpn)
-                || !TextUtils.equals(data.getPlmn(), mCurPlmn)) {
+    private void notifyCarrierDisplayNameDataChanged() {
+        final String log = String.format("notifyCarrierDisplayNameDataChanged: "
+                        + "changed sending intent, "
+                        + "rule=%d, CarrierDisplayNameData=%s, subId=%d",
+                getCarrierNameDisplayBitmask(mSS),
+                mCarrierDisplayNameData,
+                mPhone.getSubId());
+        mCdnrLogs.log(log);
+        if (DBG) log(log);
 
-            final String log = String.format("updateSpnDisplay: changed sending intent, "
-                            + "rule=%d, showPlmn='%b', plmn='%s', showSpn='%b', spn='%s', "
-                            + "dataSpn='%s', subId='%d'",
-                    getCarrierNameDisplayBitmask(mSS),
-                    data.shouldShowPlmn(),
-                    data.getPlmn(),
-                    data.shouldShowSpn(),
-                    data.getSpn(),
-                    data.getDataSpn(),
-                    subId);
-            mCdnrLogs.log(log);
-            if (DBG) log("updateSpnDisplay: " + log);
 
-            Intent intent = new Intent(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
-            intent.putExtra(TelephonyManager.EXTRA_SHOW_SPN, data.shouldShowSpn());
-            intent.putExtra(TelephonyManager.EXTRA_SPN, data.getSpn());
-            intent.putExtra(TelephonyManager.EXTRA_DATA_SPN, data.getDataSpn());
-            intent.putExtra(TelephonyManager.EXTRA_SHOW_PLMN, data.shouldShowPlmn());
-            intent.putExtra(TelephonyManager.EXTRA_PLMN, data.getPlmn());
-            SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
-            mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
-
-            if (SubscriptionManager.isValidSubscriptionId(subId)) {
-                mSubscriptionManagerService.setCarrierName(subId, TextUtils.emptyIfNull(
-                        getCarrierName(data.shouldShowPlmn(), data.getPlmn(),
-                                data.shouldShowSpn(), data.getSpn())));
-            }
-        }
-        mCurShowSpn = data.shouldShowSpn();
-        mCurShowPlmn = data.shouldShowPlmn();
-        mCurSpn = data.getSpn();
-        mCurDataSpn = data.getDataSpn();
-        mCurPlmn = data.getPlmn();
+        Intent intent = new Intent(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
+        intent.putExtra(TelephonyManager.EXTRA_SHOW_SPN, mCarrierDisplayNameData.shouldShowSpn());
+        intent.putExtra(TelephonyManager.EXTRA_SPN, mCarrierDisplayNameData.getSpn());
+        intent.putExtra(TelephonyManager.EXTRA_DATA_SPN, mCarrierDisplayNameData.getDataSpn());
+        intent.putExtra(TelephonyManager.EXTRA_SHOW_PLMN, mCarrierDisplayNameData.shouldShowPlmn());
+        intent.putExtra(TelephonyManager.EXTRA_PLMN, mCarrierDisplayNameData.getPlmn());
+        SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
+        mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
     @NonNull
-    private String getCarrierName(boolean showPlmn, String plmn, boolean showSpn, String spn) {
+    private String getCarrierName(CarrierDisplayNameData cdnd) {
+        boolean showPlmn = cdnd.shouldShowPlmn();
+        boolean showSpn = cdnd.shouldShowSpn();
+        String plmn = cdnd.getPlmn();
+        String spn = cdnd.getSpn();
+
         String carrierName = "";
         if (showPlmn) {
             carrierName = plmn;
@@ -2825,29 +2798,34 @@
         } else if (showSpn) {
             carrierName = spn;
         }
-        return carrierName;
+        return TextUtils.emptyIfNull(carrierName);
     }
 
-    private void updateSpnDisplayCdnr() {
-        log("updateSpnDisplayCdnr+");
-        CarrierDisplayNameData data = mCdnr.getCarrierDisplayNameData();
-        notifySpnDisplayUpdate(data);
-        log("updateSpnDisplayCdnr-");
-    }
-
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     @VisibleForTesting
-    public void updateSpnDisplay() {
-        if (mCarrierConfig.getBoolean(
-                CarrierConfigManager.KEY_ENABLE_CARRIER_DISPLAY_NAME_RESOLVER_BOOL)) {
-            updateSpnDisplayCdnr();
-        } else {
-            updateSpnDisplayLegacy();
+    public void updateCarrierDisplayName() {
+        final boolean useCdnr = mCarrierConfig.getBoolean(
+                CarrierConfigManager.KEY_ENABLE_CARRIER_DISPLAY_NAME_RESOLVER_BOOL);
+
+        final CarrierDisplayNameData cdnd = useCdnr
+                ? mCdnr.getCarrierDisplayNameData()
+                : getCarrierDisplayNameLegacy();
+
+        final int subId = mPhone.getSubId();
+
+        // Avoid sending unnecessary updates
+        if (subId == mSubId && cdnd.equals(mCarrierDisplayNameData)) return;
+
+        if (SubscriptionManager.isValidSubscriptionId(subId)) {
+            mSubscriptionManagerService.setCarrierName(subId, getCarrierName(cdnd));
         }
+
+        mCarrierDisplayNameData = cdnd; // notify...() relies on the updated value
+        notifyCarrierDisplayNameDataChanged();
+
     }
 
-    private void updateSpnDisplayLegacy() {
-        log("updateSpnDisplayLegacy+");
+    private @NonNull CarrierDisplayNameData getCarrierDisplayNameLegacy() {
+        log("getCarrierDisplayNameLegacy+");
 
         String spn = null;
         String dataSpn = null;
@@ -2888,11 +2866,12 @@
                     .getStringArray(com.android.internal.R.array.wfcSpnFormats);
 
             if (voiceIdx < 0 || voiceIdx >= wfcSpnFormats.length) {
-                loge("updateSpnDisplay: KEY_WFC_SPN_FORMAT_IDX_INT out of bounds: " + voiceIdx);
+                loge("updateCarrierDisplayName: KEY_WFC_SPN_FORMAT_IDX_INT out of bounds: "
+                        + voiceIdx);
                 voiceIdx = 0;
             }
             if (dataIdx < 0 || dataIdx >= wfcSpnFormats.length) {
-                loge("updateSpnDisplay: KEY_WFC_DATA_SPN_FORMAT_IDX_INT out of bounds: "
+                loge("updateCarrierDisplayName: KEY_WFC_DATA_SPN_FORMAT_IDX_INT out of bounds: "
                         + dataIdx);
                 dataIdx = 0;
             }
@@ -2926,7 +2905,7 @@
                     .getStringArray(R.array.crossSimSpnFormats);
 
             if (crossSimSpnFormatIdx < 0 || crossSimSpnFormatIdx >= crossSimSpnFormats.length) {
-                loge("updateSpnDisplay: KEY_CROSS_SIM_SPN_FORMAT_INT out of bounds: "
+                loge("updateCarrierDisplayName: KEY_CROSS_SIM_SPN_FORMAT_INT out of bounds: "
                         + crossSimSpnFormatIdx);
                 crossSimSpnFormatIdx = 0;
             }
@@ -2971,21 +2950,25 @@
                             .toString();
                     noService = true;
                 }
-                if (DBG) log("updateSpnDisplay: radio is on but out " +
-                        "of service, set plmn='" + plmn + "'");
+                if (DBG) {
+                    log("updateCarrierDisplayName: radio is on but out "
+                            + "of service, set plmn='" + plmn + "'");
+                }
             } else if (combinedRegState == ServiceState.STATE_IN_SERVICE) {
                 // In either home or roaming service
                 plmn = mSS.getOperatorAlpha();
                 showPlmn = !TextUtils.isEmpty(plmn) &&
                         ((rule & CARRIER_NAME_DISPLAY_BITMASK_SHOW_PLMN)
                                 == CARRIER_NAME_DISPLAY_BITMASK_SHOW_PLMN);
-                if (DBG) log("updateSpnDisplay: rawPlmn = " + plmn);
+                if (DBG) log("updateCarrierDisplayName: rawPlmn = " + plmn);
             } else {
                 // Power off state, such as airplane mode, show plmn as null
                 showPlmn = true;
                 plmn = null;
-                if (DBG) log("updateSpnDisplay: radio is off w/ showPlmn="
-                        + showPlmn + " plmn=" + plmn);
+                if (DBG) {
+                    log("updateCarrierDisplayName: radio is off w/ showPlmn="
+                            + showPlmn + " plmn=" + plmn);
+                }
             }
 
             // The value of spn/showSpn are same in different scenarios.
@@ -2997,7 +2980,7 @@
             showSpn = !noService && !TextUtils.isEmpty(spn)
                     && ((rule & CARRIER_NAME_DISPLAY_BITMASK_SHOW_SPN)
                     == CARRIER_NAME_DISPLAY_BITMASK_SHOW_SPN);
-            if (DBG) log("updateSpnDisplay: rawSpn = " + spn);
+            if (DBG) log("updateCarrierDisplayName: rawSpn = " + spn);
             if (!TextUtils.isEmpty(crossSimSpnFormat)) {
                 if (!TextUtils.isEmpty(spn)) {
                     // Show SPN + Cross-SIM Calling If SIM has SPN and SPN display condition
@@ -3056,7 +3039,7 @@
 
             // mOperatorAlpha contains the ERI text
             plmn = mSS.getOperatorAlpha();
-            if (DBG) log("updateSpnDisplay: cdma rawPlmn = " + plmn);
+            if (DBG) log("updateCarrierDisplayName: cdma rawPlmn = " + plmn);
 
             showPlmn = plmn != null;
 
@@ -3068,8 +3051,8 @@
                 // todo: temporary hack; should have a better fix. This is to avoid using operator
                 // name from ServiceState (populated in processIwlanRegistrationInfo()) until
                 // wifi calling is actually enabled
-                log("updateSpnDisplay: overwriting plmn from " + plmn + " to null as radio " +
-                        "state is off");
+                log("updateCarrierDisplayName: overwriting plmn from "
+                        + plmn + " to null as radio " + "state is off");
                 plmn = null;
             }
 
@@ -3077,20 +3060,21 @@
                 plmn = Resources.getSystem().getText(com.android.internal.R.string
                         .lockscreen_carrier_default).toString();
                 if (DBG) {
-                    log("updateSpnDisplay: radio is on but out of svc, set plmn='" + plmn + "'");
+                    log("updateCarrierDisplayName: radio is on but out of svc, set plmn='"
+                            + plmn + "'");
                 }
             }
-
         }
 
-        notifySpnDisplayUpdate(new CarrierDisplayNameData.Builder()
+        log("getCarrierDisplayNameLegacy-");
+
+        return new CarrierDisplayNameData.Builder()
                 .setSpn(spn)
                 .setDataSpn(dataSpn)
                 .setShowSpn(showSpn)
                 .setPlmn(plmn)
                 .setShowPlmn(showPlmn)
-                .build());
-        log("updateSpnDisplayLegacy-");
+                .build();
     }
 
     /**
@@ -3299,7 +3283,7 @@
         mImsRegistrationOnOff = registered;
 
         // It's possible ServiceState changes did not trigger SPN display update; we update it here.
-        updateSpnDisplay();
+        updateCarrierDisplayName();
     }
 
     public void onImsCapabilityChanged() {
@@ -3713,11 +3697,11 @@
         String eriText = mPhone.getCdmaEriText();
         boolean hasEriChanged = !TextUtils.equals(mEriText, eriText);
         mEriText = eriText;
-        // Trigger updateSpnDisplay when
+        // Trigger updateCarrierDisplayName when
         // 1. Service state is changed.
         // 2. phone type is Cdma or CdmaLte and ERI text has changed.
         if (hasChanged || (!mPhone.isPhoneTypeGsm() && hasEriChanged)) {
-            updateSpnDisplay();
+            updateCarrierDisplayName();
         }
 
         if (hasChanged) {
@@ -5279,11 +5263,7 @@
         pw.println(" mStartedGprsRegCheck=" + mStartedGprsRegCheck);
         pw.println(" mReportedGprsNoReg=" + mReportedGprsNoReg);
         pw.println(" mNotification=" + mNotification);
-        pw.println(" mCurSpn=" + mCurSpn);
-        pw.println(" mCurDataSpn=" + mCurDataSpn);
-        pw.println(" mCurShowSpn=" + mCurShowSpn);
-        pw.println(" mCurPlmn=" + mCurPlmn);
-        pw.println(" mCurShowPlmn=" + mCurShowPlmn);
+        pw.println(" mCarrierDisplayNameData=" + mCarrierDisplayNameData);
         pw.flush();
         pw.println(" mCurrentOtaspMode=" + mCurrentOtaspMode);
         pw.println(" mRoamingIndicator=" + mRoamingIndicator);
diff --git a/src/java/com/android/internal/telephony/SmsController.java b/src/java/com/android/internal/telephony/SmsController.java
index 1295f38..e3c409d 100644
--- a/src/java/com/android/internal/telephony/SmsController.java
+++ b/src/java/com/android/internal/telephony/SmsController.java
@@ -224,7 +224,15 @@
         }
     }
 
+    @NonNull
     private String getCallingPackage() {
+        if (mFlags.hsumPackageManager()) {
+            PackageManager pm = mContext.createContextAsUser(Binder.getCallingUserHandle(), 0)
+                    .getPackageManager();
+            String[] packages = pm.getPackagesForUid(Binder.getCallingUid());
+            if (packages == null || packages.length == 0) return "";
+            return packages[0];
+        }
         return mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid())[0];
     }
 
@@ -412,9 +420,7 @@
             boolean persistMessageForNonDefaultSmsApp, long messageId) {
         // This is different from the checking of other method. It prefers the package name
         // returned by getCallPackage() for backward-compatibility.
-        if (getCallingPackage() != null) {
-            callingPackage = getCallingPackage();
-        }
+        callingPackage = getCallingPackage();
         UserHandle callingUser = Binder.getCallingUserHandle();
 
         Rlog.d(LOG_TAG, "sendMultipartTextForSubscriber caller=" + callingPackage);
diff --git a/src/java/com/android/internal/telephony/SmsDispatchersController.java b/src/java/com/android/internal/telephony/SmsDispatchersController.java
index 7795955..b60ded5 100644
--- a/src/java/com/android/internal/telephony/SmsDispatchersController.java
+++ b/src/java/com/android/internal/telephony/SmsDispatchersController.java
@@ -70,6 +70,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicLong;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
@@ -220,6 +221,7 @@
         public static final int TYPE_TEXT = 2;
         public static final int TYPE_MULTIPART_TEXT = 3;
         public static final int TYPE_RETRY_SMS = 4;
+        private static final AtomicLong sNextUniqueMessageId = new AtomicLong(0);
 
         public final int type;
         public final SMSDispatcher.SmsTracker tracker;
@@ -242,6 +244,7 @@
         public final int validityPeriod;
         public final long messageId;
         public final boolean skipShortCodeCheck;
+        public final long uniqueMessageId;
 
         public PendingRequest(int type, SMSDispatcher.SmsTracker tracker, String callingPackage,
                 int callingUser, String destAddr, String scAddr,
@@ -270,6 +273,16 @@
             this.validityPeriod = validityPeriod;
             this.messageId = messageId;
             this.skipShortCodeCheck = skipShortCodeCheck;
+            if (tracker != null) {
+                this.uniqueMessageId = tracker.mUniqueMessageId;
+            } else {
+                this.uniqueMessageId = getNextUniqueMessageId();
+            }
+        }
+
+        public static long getNextUniqueMessageId() {
+            return sNextUniqueMessageId.getAndUpdate(
+                id -> ((id + 1) % Long.MAX_VALUE));
         }
     }
 
@@ -867,8 +880,8 @@
                 // should never come here...
                 Rlog.e(TAG, "sendRetrySms failed to re-encode per missing fields!");
                 tracker.onFailed(mContext, SmsManager.RESULT_SMS_SEND_RETRY_FAILED, NO_ERROR_CODE);
-                notifySmsSent(tracker.mDestAddress, tracker.mMessageId,
-                        !retryUsingImsService, true /*isLastSmsPart*/, false /*success*/);
+                notifySmsSent(tracker, !retryUsingImsService,
+                        true /*isLastSmsPart*/, false /*success*/);
                 return;
             }
             String scAddr = (String) map.get("scAddr");
@@ -876,8 +889,8 @@
             if (destAddr == null) {
                 Rlog.e(TAG, "sendRetrySms failed due to null destAddr");
                 tracker.onFailed(mContext, SmsManager.RESULT_SMS_SEND_RETRY_FAILED, NO_ERROR_CODE);
-                notifySmsSent(tracker.mDestAddress, tracker.mMessageId,
-                        !retryUsingImsService, true /*isLastSmsPart*/, false /*success*/);
+                notifySmsSent(tracker, !retryUsingImsService,
+                        true /*isLastSmsPart*/, false /*success*/);
                 return;
             }
 
@@ -918,8 +931,8 @@
                         + "scAddr: %s, "
                         + "destPort: %s", scAddr, map.get("destPort")));
                 tracker.onFailed(mContext, SmsManager.RESULT_SMS_SEND_RETRY_FAILED, NO_ERROR_CODE);
-                notifySmsSent(tracker.mDestAddress, tracker.mMessageId,
-                        !retryUsingImsService, true /*isLastSmsPart*/, false /*success*/);
+                notifySmsSent(tracker, !retryUsingImsService,
+                    true /*isLastSmsPart*/, false /*success*/);
                 return;
             }
             // replace old smsc and pdu with newly encoded ones
@@ -1214,11 +1227,11 @@
     /**
      * Called when MO SMS is sent.
      */
-    protected void notifySmsSent(@NonNull String destAddr, long messageId, boolean isOverIms,
-            boolean isLastSmsPart, boolean success) {
-        notifySmsSentToEmergencyStateTracker(
-                destAddr, messageId, isOverIms, isLastSmsPart, success);
-        notifySmsSentToDatagramDispatcher(messageId, success);
+    protected void notifySmsSent(@NonNull SMSDispatcher.SmsTracker tracker,
+            boolean isOverIms, boolean isLastSmsPart, boolean success) {
+        notifySmsSentToEmergencyStateTracker(tracker.mDestAddress,
+            tracker.mMessageId, isOverIms, isLastSmsPart, success);
+        notifySmsSentToDatagramDispatcher(tracker.mUniqueMessageId, success);
     }
 
     /**
@@ -1239,7 +1252,7 @@
     }
 
     private void notifySmsSentToDatagramDispatcher(long messageId, boolean success) {
-        if (SatelliteController.getInstance().isInCarrierRoamingNbIotNtn()) {
+        if (SatelliteController.getInstance().isInCarrierRoamingNbIotNtn(mPhone)) {
             DatagramDispatcher.getInstance().onSendSmsDone(mPhone.getSubId(), messageId, success);
         }
     }
@@ -1402,15 +1415,18 @@
         if (domain == NetworkRegistrationInfo.DOMAIN_PS) {
             mImsSmsDispatcher.sendData(request.callingPackage, request.callingUser,
                     request.destAddr, request.scAddr, request.destPort, request.data,
-                    request.sentIntents.get(0), request.deliveryIntents.get(0), request.isForVvm);
+                    request.sentIntents.get(0), request.deliveryIntents.get(0), request.isForVvm,
+                    request.uniqueMessageId);
         } else if (isCdmaMo(domain)) {
             mCdmaDispatcher.sendData(request.callingPackage, request.callingUser, request.destAddr,
                     request.scAddr, request.destPort, request.data,
-                    request.sentIntents.get(0), request.deliveryIntents.get(0), request.isForVvm);
+                    request.sentIntents.get(0), request.deliveryIntents.get(0), request.isForVvm,
+                    request.uniqueMessageId);
         } else {
             mGsmDispatcher.sendData(request.callingPackage, request.callingUser, request.destAddr,
                     request.scAddr, request.destPort, request.data,
-                    request.sentIntents.get(0), request.deliveryIntents.get(0), request.isForVvm);
+                    request.sentIntents.get(0), request.deliveryIntents.get(0), request.isForVvm,
+                    request.uniqueMessageId);
         }
     }
 
@@ -1430,7 +1446,7 @@
                     request.messageUri, request.callingPackage, request.callingUser,
                     request.persistMessage, request.priority,  /*request.expectMore*/ false,
                     request.validityPeriod, request.isForVvm, request.messageId,
-                    request.skipShortCodeCheck);
+                    request.skipShortCodeCheck, request.uniqueMessageId);
         } else {
             if (isCdmaMo(domain)) {
                 mCdmaDispatcher.sendText(request.destAddr, request.scAddr, request.texts.get(0),
@@ -1438,14 +1454,14 @@
                         request.messageUri, request.callingPackage, request.callingUser,
                         request.persistMessage, request.priority, request.expectMore,
                         request.validityPeriod, request.isForVvm, request.messageId,
-                        request.skipShortCodeCheck);
+                        request.skipShortCodeCheck, request.uniqueMessageId);
             } else {
                 mGsmDispatcher.sendText(request.destAddr, request.scAddr, request.texts.get(0),
                         request.sentIntents.get(0), request.deliveryIntents.get(0),
                         request.messageUri, request.callingPackage, request.callingUser,
                         request.persistMessage, request.priority, request.expectMore,
                         request.validityPeriod, request.isForVvm, request.messageId,
-                        request.skipShortCodeCheck);
+                        request.skipShortCodeCheck, request.uniqueMessageId);
             }
         }
     }
@@ -1465,25 +1481,29 @@
                     request.sentIntents, request.deliveryIntents, request.messageUri,
                     request.callingPackage, request.callingUser, request.persistMessage,
                     request.priority, false /*request.expectMore*/, request.validityPeriod,
-                    request.messageId);
+                    request.messageId, request.uniqueMessageId);
         } else {
             if (isCdmaMo(domain)) {
                 mCdmaDispatcher.sendMultipartText(request.destAddr, request.scAddr, request.texts,
                         request.sentIntents, request.deliveryIntents, request.messageUri,
                         request.callingPackage, request.callingUser, request.persistMessage,
                         request.priority, request.expectMore, request.validityPeriod,
-                        request.messageId);
+                        request.messageId, request.uniqueMessageId);
             } else {
                 mGsmDispatcher.sendMultipartText(request.destAddr, request.scAddr, request.texts,
                         request.sentIntents, request.deliveryIntents, request.messageUri,
                         request.callingPackage, request.callingUser, request.persistMessage,
                         request.priority, request.expectMore, request.validityPeriod,
-                        request.messageId);
+                        request.messageId, request.uniqueMessageId);
             }
         }
     }
 
-    private void triggerSentIntentForFailure(@NonNull PendingIntent sentIntent) {
+    private void triggerSentIntentForFailure(PendingIntent sentIntent) {
+        if (sentIntent == null) {
+            logd("sentIntent is null");
+            return;
+        }
         try {
             sentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE);
         } catch (CanceledException e) {
@@ -1491,7 +1511,11 @@
         }
     }
 
-    private void triggerSentIntentForFailure(@NonNull List<PendingIntent> sentIntents) {
+    private void triggerSentIntentForFailure(List<PendingIntent> sentIntents) {
+        if (sentIntents == null) {
+            logd("sentIntents is null");
+            return;
+        }
         for (PendingIntent sentIntent : sentIntents) {
             triggerSentIntentForFailure(sentIntent);
         }
@@ -1605,13 +1629,16 @@
 
         if (mImsSmsDispatcher.isAvailable()) {
             mImsSmsDispatcher.sendData(callingPackage, callingUser, destAddr, scAddr, destPort,
-                    data, sentIntent, deliveryIntent, isForVvm);
+                    data, sentIntent, deliveryIntent, isForVvm,
+                    PendingRequest.getNextUniqueMessageId());
         } else if (isCdmaMo()) {
             mCdmaDispatcher.sendData(callingPackage, callingUser, destAddr, scAddr, destPort, data,
-                    sentIntent, deliveryIntent, isForVvm);
+                    sentIntent, deliveryIntent, isForVvm,
+                    PendingRequest.getNextUniqueMessageId());
         } else {
             mGsmDispatcher.sendData(callingPackage, callingUser, destAddr, scAddr, destPort, data,
-                    sentIntent, deliveryIntent, isForVvm);
+                    sentIntent, deliveryIntent, isForVvm,
+                    PendingRequest.getNextUniqueMessageId());
         }
     }
 
@@ -1833,7 +1860,7 @@
                 messageUri, persistMessage, priority, expectMore, validityPeriod, messageId,
                 skipShortCodeCheck);
 
-        if (SatelliteController.getInstance().isInCarrierRoamingNbIotNtn()) {
+        if (SatelliteController.getInstance().isInCarrierRoamingNbIotNtn(mPhone)) {
             // Send P2P SMS using carrier roaming NB IOT NTN
             DatagramDispatcher.getInstance().sendSms(pendingRequest);
             return;
@@ -1843,7 +1870,8 @@
     }
 
     private void sendTextInternal(PendingRequest request) {
-        logd("sendTextInternal: messageId=" + request.messageId);
+        logd("sendTextInternal: messageId=" + request.messageId
+                 + ", uniqueMessageId=" + request.uniqueMessageId);
         if (isSmsDomainSelectionEnabled()) {
             TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
             boolean isEmergency = tm.isEmergencyNumber(request.destAddr);
@@ -1859,7 +1887,7 @@
                     request.messageUri, request.callingPackage, request.callingUser,
                     request.persistMessage, request.priority, false /*expectMore*/,
                     request.validityPeriod, request.isForVvm, request.messageId,
-                    request.skipShortCodeCheck);
+                    request.skipShortCodeCheck, request.uniqueMessageId);
         } else {
             if (isCdmaMo()) {
                 mCdmaDispatcher.sendText(request.destAddr, request.scAddr, request.texts.get(0),
@@ -1867,14 +1895,14 @@
                         request.messageUri, request.callingPackage, request.callingUser,
                         request.persistMessage, request.priority, request.expectMore,
                         request.validityPeriod, request.isForVvm, request.messageId,
-                        request.skipShortCodeCheck);
+                        request.skipShortCodeCheck, request.uniqueMessageId);
             } else {
                 mGsmDispatcher.sendText(request.destAddr, request.scAddr, request.texts.get(0),
                         request.sentIntents.get(0), request.deliveryIntents.get(0),
                         request.messageUri, request.callingPackage, request.callingUser,
                         request.persistMessage, request.priority, request.expectMore,
                         request.validityPeriod, request.isForVvm, request.messageId,
-                        request.skipShortCodeCheck);
+                        request.skipShortCodeCheck, request.uniqueMessageId);
             }
         }
     }
@@ -1997,7 +2025,7 @@
                 null, 0, parts, messageUri, persistMessage, priority, expectMore,
                 validityPeriod, messageId, false);
 
-        if (SatelliteController.getInstance().isInCarrierRoamingNbIotNtn()) {
+        if (SatelliteController.getInstance().isInCarrierRoamingNbIotNtn(mPhone)) {
             // Send multipart P2P SMS using carrier roaming NB IOT NTN
             DatagramDispatcher.getInstance().sendSms(pendingRequest);
             return;
@@ -2021,20 +2049,20 @@
                     request.sentIntents, request.deliveryIntents, request.messageUri,
                     request.callingPackage, request.callingUser, request.persistMessage,
                     request.priority, false /*expectMore*/, request.validityPeriod,
-                    request.messageId);
+                    request.messageId, request.uniqueMessageId);
         } else {
             if (isCdmaMo()) {
                 mCdmaDispatcher.sendMultipartText(request.destAddr, request.scAddr, request.texts,
                         request.sentIntents, request.deliveryIntents, request.messageUri,
                         request.callingPackage, request.callingUser, request.persistMessage,
                         request.priority, request.expectMore, request.validityPeriod,
-                        request.messageId);
+                        request.messageId, request.uniqueMessageId);
             } else {
                 mGsmDispatcher.sendMultipartText(request.destAddr, request.scAddr, request.texts,
                         request.sentIntents, request.deliveryIntents, request.messageUri,
                         request.callingPackage, request.callingUser, request.persistMessage,
                         request.priority, request.expectMore, request.validityPeriod,
-                        request.messageId);
+                        request.messageId, request.uniqueMessageId);
             }
         }
     }
diff --git a/src/java/com/android/internal/telephony/SmsStorageMonitor.java b/src/java/com/android/internal/telephony/SmsStorageMonitor.java
index 2736f7a..07b7a18 100644
--- a/src/java/com/android/internal/telephony/SmsStorageMonitor.java
+++ b/src/java/com/android/internal/telephony/SmsStorageMonitor.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony;
 
+import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -33,6 +34,7 @@
 import android.telephony.SubscriptionManager;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.telephony.Rlog;
 
@@ -91,6 +93,9 @@
 
     private boolean mMemoryStatusOverrideFlag = false;
 
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
+
     /**
      * Hold the wake lock for 5 seconds, which should be enough time for
      * any receiver(s) to grab its own wake lock.
@@ -100,9 +105,11 @@
     /**
      * Creates an SmsStorageMonitor and registers for events.
      * @param phone the Phone to use
+     * @param flags The Android feature flags
      */
-    public SmsStorageMonitor(Phone phone) {
+    public SmsStorageMonitor(@NonNull Phone phone, @NonNull FeatureFlags flags) {
         mPhone = phone;
+        mFeatureFlags = flags;
         mContext = phone.getContext();
         mCi = phone.mCi;
 
@@ -278,7 +285,12 @@
         intent.setComponent(componentName);
         mWakeLock.acquire(WAKE_LOCK_TIMEOUT);
         SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
-        mContext.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS);
+        if (mFeatureFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                    android.Manifest.permission.RECEIVE_SMS);
+        } else {
+            mContext.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS);
+        }
     }
 
     /** Returns whether or not there is storage available for an incoming SMS. */
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index 0b0f9d3..10e97b6 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -289,8 +289,16 @@
         return new GsmCdmaCallTracker(phone, featureFlags);
     }
 
-    public SmsStorageMonitor makeSmsStorageMonitor(Phone phone) {
-        return new SmsStorageMonitor(phone);
+    /**
+     * Create {@link SmsStorageMonitor} instance.
+     *
+     * @param phone Phone instance
+     * @param flags Android feature flags
+     *
+     * @return The created instance
+     */
+    public SmsStorageMonitor makeSmsStorageMonitor(Phone phone, @NonNull FeatureFlags flags) {
+        return new SmsStorageMonitor(phone, flags);
     }
 
     public SmsUsageMonitor makeSmsUsageMonitor(Context context) {
@@ -331,8 +339,16 @@
         return new CarrierActionAgent(phone);
     }
 
-    public CarrierResolver makeCarrierResolver(Phone phone) {
-        return new CarrierResolver(phone);
+    /**
+     * Create {@link CarrierResolver} instance
+     *
+     * @param phone The phone instance
+     * @param flags Android feature flags
+     *
+     * @return The created instance
+     */
+    public CarrierResolver makeCarrierResolver(Phone phone, FeatureFlags flags) {
+        return new CarrierResolver(phone, flags);
     }
 
     public IccPhoneBookInterfaceManager makeIccPhoneBookInterfaceManager(Phone phone) {
diff --git a/src/java/com/android/internal/telephony/TelephonyCountryDetector.java b/src/java/com/android/internal/telephony/TelephonyCountryDetector.java
index c2d6018..1e07bc3 100644
--- a/src/java/com/android/internal/telephony/TelephonyCountryDetector.java
+++ b/src/java/com/android/internal/telephony/TelephonyCountryDetector.java
@@ -16,7 +16,6 @@
 
 package com.android.internal.telephony;
 
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -111,6 +110,7 @@
     @GuardedBy("mLock")
     private boolean mIsCountryCodesOverridden = false;
     private final RegistrantList mCountryCodeChangedRegistrants = new RegistrantList();
+    private boolean mIsWifiNetworkConnected = false;
 
     private FeatureFlags mFeatureFlags = null;
 
@@ -298,7 +298,7 @@
                 handleNetworkCountryCodeChangedEvent((NetworkCountryCodeInfo) msg.obj);
                 break;
             case EVENT_WIFI_CONNECTIVITY_STATE_CHANGED:
-                handleEventWifiConnectivityStateChanged();
+                handleEventWifiConnectivityStateChanged((boolean) msg.obj);
                 break;
             case EVENT_LOCATION_UPDATE_REQUEST_QUOTA_RESET:
                 evaluateRequestingLocationUpdates();
@@ -490,11 +490,21 @@
         }
     }
 
-    private void handleEventWifiConnectivityStateChanged() {
-        mWifiConnectivityStateChangedRegistrantList.notifyResult(isWifiNetworkConnected());
+    private void handleEventWifiConnectivityStateChanged(boolean connected) {
+        logd("handleEventWifiConnectivityStateChanged: " + connected);
+        evaluateNotifyWifiConnectivityStateChangedEvent(connected);
         evaluateRequestingLocationUpdates();
     }
 
+    private void evaluateNotifyWifiConnectivityStateChangedEvent(boolean connected) {
+        if (connected != mIsWifiNetworkConnected) {
+            mIsWifiNetworkConnected = connected;
+            mWifiConnectivityStateChangedRegistrantList.notifyResult(mIsWifiNetworkConnected);
+            logd("evaluateNotifyWifiConnectivityStateChangedEvent: wifi connectivity state has "
+                    + "changed to " + connected);
+        }
+    }
+
     private void setLocationCountryCode(@NonNull Pair<String, Long> countryCodeInfo) {
         logd("Set location country code to: " + countryCodeInfo.first);
         if (!isValid(countryCodeInfo.first)) {
@@ -526,25 +536,23 @@
     private void registerForWifiConnectivityStateChanged() {
         logd("registerForWifiConnectivityStateChanged");
         NetworkRequest.Builder builder = new NetworkRequest.Builder();
-        builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+        builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+
         ConnectivityManager.NetworkCallback networkCallback =
                 new ConnectivityManager.NetworkCallback() {
                     @Override
-                    public void onAvailable(Network network) {
-                        logd("Wifi network available: " + network);
-                        sendRequestAsync(EVENT_WIFI_CONNECTIVITY_STATE_CHANGED, null);
+                    public void onCapabilitiesChanged(Network network,
+                            NetworkCapabilities networkCapabilities) {
+                        logd("onCapabilitiesChanged: " + networkCapabilities);
+                        sendRequestAsync(EVENT_WIFI_CONNECTIVITY_STATE_CHANGED,
+                                isInternetAvailable(networkCapabilities));
                     }
 
                     @Override
                     public void onLost(Network network) {
                         logd("Wifi network lost: " + network);
-                        sendRequestAsync(EVENT_WIFI_CONNECTIVITY_STATE_CHANGED, null);
-                    }
-
-                    @Override
-                    public void onUnavailable() {
-                        logd("Wifi network unavailable");
-                        sendRequestAsync(EVENT_WIFI_CONNECTIVITY_STATE_CHANGED, null);
+                        sendRequestAsync(EVENT_WIFI_CONNECTIVITY_STATE_CHANGED, false);
                     }
                 };
         mConnectivityManager.registerNetworkCallback(builder.build(), networkCallback);
@@ -560,15 +568,20 @@
 
     /**
      * Check whether Wi-Fi network is connected or not.
-     * @return {@code true} is Wi-Fi is connected, {@code false} otherwise.
+     * @return {@code true} is Wi-Fi is connected, and internet is available, {@code false}
+     * otherwise.
      */
     public boolean isWifiNetworkConnected() {
-        Network activeNetwork = mConnectivityManager.getActiveNetwork();
-        NetworkCapabilities networkCapabilities =
-                mConnectivityManager.getNetworkCapabilities(activeNetwork);
-        return networkCapabilities != null
-                && networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
-                && networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+        logd("isWifiNetworkConnected: " + mIsWifiNetworkConnected);
+        return mIsWifiNetworkConnected;
+    }
+
+    private boolean isInternetAvailable(NetworkCapabilities networkCapabilities) {
+        boolean isWifiConnected =
+                networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                && networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+        logd("isWifiConnected: " + isWifiConnected);
+        return isWifiConnected;
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java b/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java
index e374811..64ecdc9 100644
--- a/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java
+++ b/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java
@@ -35,6 +35,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.VisualVoicemailSmsParser.WrappedMessageData;
+import com.android.internal.telephony.flags.Flags;
 
 import java.nio.ByteBuffer;
 import java.nio.charset.CharacterCodingException;
@@ -286,7 +287,11 @@
         intent.putExtra(VoicemailContract.EXTRA_VOICEMAIL_SMS, builder.build());
         intent.putExtra(VoicemailContract.EXTRA_TARGET_PACKAGE, filterSettings.packageName);
         intent.setPackage(TELEPHONY_SERVICE_PACKAGE);
-        context.sendBroadcast(intent);
+        if (Flags.hsumBroadcast()) {
+            context.sendBroadcastAsUser(intent, UserHandle.ALL);
+        } else {
+            context.sendBroadcast(intent);
+        }
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/cat/CatService.java b/src/java/com/android/internal/telephony/cat/CatService.java
index 4da1622..5fdb8ce 100644
--- a/src/java/com/android/internal/telephony/cat/CatService.java
+++ b/src/java/com/android/internal/telephony/cat/CatService.java
@@ -40,6 +40,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -712,7 +713,12 @@
         intent.putExtra("SLOT_ID", mSlotId);
         intent.setComponent(AppInterface.getDefaultSTKApplication());
         CatLog.d(this, "Sending CmdMsg: " + cmdMsg+ " on slotid:" + mSlotId);
-        mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
+
+        if (sFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL, AppInterface.STK_PERMISSION);
+        } else {
+            mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
+        }
     }
 
     /**
@@ -726,7 +732,11 @@
         Intent intent = new Intent(AppInterface.CAT_SESSION_END_ACTION);
         intent.putExtra("SLOT_ID", mSlotId);
         intent.setComponent(AppInterface.getDefaultSTKApplication());
-        mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
+        if (sFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL, AppInterface.STK_PERMISSION);
+        } else {
+            mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
+        }
     }
 
 
@@ -1090,7 +1100,11 @@
         intent.putExtra("SLOT_ID", mSlotId);
         CatLog.d(this, "Sending Card Status: "
                 + cardState + " " + "cardPresent: " + cardPresent +  "SLOT_ID: " +  mSlotId);
-        mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
+        if (sFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL, AppInterface.STK_PERMISSION);
+        } else {
+            mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
+        }
     }
 
     private void broadcastAlphaMessage(String alphaString) {
@@ -1100,7 +1114,11 @@
         intent.putExtra(AppInterface.ALPHA_STRING, alphaString);
         intent.putExtra("SLOT_ID", mSlotId);
         intent.setComponent(AppInterface.getDefaultSTKApplication());
-        mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
+        if (sFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL, AppInterface.STK_PERMISSION);
+        } else {
+            mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
+        }
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameData.java b/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameData.java
index 59745dc..597bad3 100644
--- a/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameData.java
+++ b/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameData.java
@@ -18,6 +18,7 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.Rlog;
 
 import java.util.Objects;
 
@@ -25,6 +26,9 @@
  * A container of carrier display name.
  */
 public class CarrierDisplayNameData implements Parcelable {
+
+    private static final String LOG_TAG = "CarrierDisplayNameData";
+
     /** Service provider name. */
     private final String mSpn;
 
@@ -40,8 +44,18 @@
     /** {@code True} if display PLMN network name is required. */
     private final boolean mShowPlmn;
 
-    private CarrierDisplayNameData(String spn, String dataSpn, boolean showSpn, String plmn,
-            boolean showPlmn) {
+    private CarrierDisplayNameData(
+            String spn, String dataSpn, boolean showSpn, String plmn, boolean showPlmn) {
+        final String logString = "Data SPN must be provided if SPN is provided";
+        if (spn != null && dataSpn == null) {
+            Rlog.e(LOG_TAG, logString);
+            if (android.os.Build.isDebuggable()) {
+                throw new IllegalArgumentException(logString);
+            } else {
+                dataSpn = spn;
+            }
+        }
+
         this.mSpn = spn;
         this.mDataSpn = dataSpn;
         this.mShowSpn = showSpn;
diff --git a/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java b/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
index 30e5aa1..8646981 100644
--- a/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
+++ b/src/java/com/android/internal/telephony/cdnr/CarrierDisplayNameResolver.java
@@ -190,7 +190,7 @@
     }
 
     /** Get the resolved carrier display name. */
-    public CarrierDisplayNameData getCarrierDisplayNameData() {
+    public @NonNull CarrierDisplayNameData getCarrierDisplayNameData() {
         resolveCarrierDisplayName();
         return mCarrierDisplayNameData;
     }
@@ -340,6 +340,7 @@
 
         return new CarrierDisplayNameData.Builder()
                 .setSpn(spn)
+                .setDataSpn(spn)
                 .setShowSpn(showSpn)
                 .setPlmn(plmn)
                 .setShowPlmn(showPlmn)
diff --git a/src/java/com/android/internal/telephony/data/DataNetworkController.java b/src/java/com/android/internal/telephony/data/DataNetworkController.java
index 5d31283..9e432e1 100644
--- a/src/java/com/android/internal/telephony/data/DataNetworkController.java
+++ b/src/java/com/android/internal/telephony/data/DataNetworkController.java
@@ -3195,7 +3195,7 @@
                 telephonyNetworkRequest, DataEvaluationReason.DATA_RETRY);
         if (!evaluation.containsDisallowedReasons()) {
             DataProfile dataProfile = dataSetupRetryEntry.dataProfile;
-            if (dataProfile == null) {
+            if (dataProfile == null || !mDataProfileManager.isDataProfileCompatible(dataProfile)) {
                 dataProfile = evaluation.getCandidateDataProfile();
             }
             if (dataProfile != null) {
diff --git a/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java b/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
index b9b60a0..42e1a78 100644
--- a/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
+++ b/src/java/com/android/internal/telephony/data/DataStallRecoveryManager.java
@@ -32,6 +32,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.telephony.Annotation.RadioPowerState;
 import android.telephony.Annotation.ValidationStatus;
@@ -714,7 +715,12 @@
         // Put the bundled stats extras on the intent.
         intent.putExtra("EXTRA_DSRS_STATS_BUNDLE", bundle);
 
-        mPhone.getContext().sendBroadcast(intent, READ_PRIVILEGED_PHONE_STATE);
+        if (mFeatureFlags.hsumBroadcast()) {
+            mPhone.getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
+                    READ_PRIVILEGED_PHONE_STATE);
+        } else {
+            mPhone.getContext().sendBroadcast(intent, READ_PRIVILEGED_PHONE_STATE);
+        }
     }
 
     /** Recovery Action: RECOVERY_ACTION_GET_DATA_CALL_LIST */
diff --git a/src/java/com/android/internal/telephony/data/DataUtils.java b/src/java/com/android/internal/telephony/data/DataUtils.java
index 20da97f..c88e0b3 100644
--- a/src/java/com/android/internal/telephony/data/DataUtils.java
+++ b/src/java/com/android/internal/telephony/data/DataUtils.java
@@ -287,6 +287,8 @@
             case NetworkCapabilities.NET_CAPABILITY_VSIM -> ApnSetting.TYPE_VSIM;
             case NetworkCapabilities.NET_CAPABILITY_BIP -> ApnSetting.TYPE_BIP;
             case NetworkCapabilities.NET_CAPABILITY_RCS -> ApnSetting.TYPE_RCS;
+            case NetworkCapabilities.NET_CAPABILITY_OEM_PAID -> ApnSetting.TYPE_OEM_PAID;
+            case NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE -> ApnSetting.TYPE_OEM_PRIVATE;
             default -> ApnSetting.TYPE_NONE;
         };
     }
@@ -315,6 +317,8 @@
             case ApnSetting.TYPE_VSIM -> NetworkCapabilities.NET_CAPABILITY_VSIM;
             case ApnSetting.TYPE_ENTERPRISE -> NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
             case ApnSetting.TYPE_RCS -> NetworkCapabilities.NET_CAPABILITY_RCS;
+            case ApnSetting.TYPE_OEM_PAID -> NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
+            case ApnSetting.TYPE_OEM_PRIVATE -> NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE;
             default -> -1;
         };
     }
diff --git a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
index 1005bb7..62d33d9 100644
--- a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
@@ -1491,14 +1491,18 @@
             mPreferredDataPhoneId = mEmergencyOverride.mPhoneId;
             mLastSwitchPreferredDataReason = DataSwitch.Reason.DATA_SWITCH_REASON_UNKNOWN;
         } else {
-            int imsRegTech = mImsRegTechProvider.get(mContext, mPhoneIdInVoiceCall);
-            if (isAnyVoiceCallActiveOnDevice() && imsRegTech != REGISTRATION_TECH_IWLAN) {
-                if (imsRegTech != REGISTRATION_TECH_CROSS_SIM) {
-                    mPreferredDataPhoneId = shouldSwitchDataDueToInCall()
-                            ? mPhoneIdInVoiceCall : getFallbackDataPhoneIdForInternetRequests();
+            if (isAnyVoiceCallActiveOnDevice()) {
+                int imsRegTech = mImsRegTechProvider.get(mContext, mPhoneIdInVoiceCall);
+                if (imsRegTech != REGISTRATION_TECH_IWLAN) {
+                    if (imsRegTech != REGISTRATION_TECH_CROSS_SIM) {
+                        mPreferredDataPhoneId = shouldSwitchDataDueToInCall()
+                                ? mPhoneIdInVoiceCall : getFallbackDataPhoneIdForInternetRequests();
+                    } else {
+                        logl("IMS call on cross-SIM, skip switching data to phone "
+                                + mPhoneIdInVoiceCall);
+                    }
                 } else {
-                    logl("IMS call on cross-SIM, skip switching data to phone "
-                            + mPhoneIdInVoiceCall);
+                    mPreferredDataPhoneId = getFallbackDataPhoneIdForInternetRequests();
                 }
             } else {
                 mPreferredDataPhoneId = getFallbackDataPhoneIdForInternetRequests();
diff --git a/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java b/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java
index 677db87..681ca12 100644
--- a/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java
+++ b/src/java/com/android/internal/telephony/domainselection/DomainSelectionConnection.java
@@ -783,6 +783,20 @@
             dispose();
             return;
         }
+
+        if (scanType == DomainSelectionService.SCAN_TYPE_FULL_SERVICE) {
+            // Handle as unknown network.
+            EmergencyRegistrationResult result = new EmergencyRegistrationResult(
+                    AccessNetworkConstants.AccessNetworkType.UNKNOWN,
+                    NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN,
+                    NetworkRegistrationInfo.DOMAIN_UNKNOWN, false, false, 0, 0, "", "", "");
+
+            if (mHandler != null) {
+                Message msg = mHandler.obtainMessage(EVENT_EMERGENCY_NETWORK_SCAN_RESULT,
+                        new AsyncResult(null, result, null));
+                msg.sendToTarget();
+            }
+        }
     }
 
     private void onModemReset() {
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
index f983108..53a3fe8 100644
--- a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
@@ -1588,7 +1588,7 @@
         final boolean isAirplaneModeOn = isAirplaneModeOn(mContext);
         boolean needToTurnOnRadio = !isRadioOn() || isAirplaneModeOn;
         final SatelliteController satelliteController = SatelliteController.getInstance();
-        boolean needToTurnOffSatellite = satelliteController.isSatelliteEnabled();
+        boolean needToTurnOffSatellite = satelliteController.isSatelliteEnabledOrBeingEnabled();
 
         if (isAirplaneModeOn && !isPowerOff()
                 && !phone.getServiceStateTracker().getDesiredPowerState()) {
@@ -1614,7 +1614,7 @@
                 @Override
                 public void onComplete(RadioOnStateListener listener, boolean isRadioReady) {
                     if (!isRadioReady) {
-                        if (satelliteController.isSatelliteEnabled()) {
+                        if (satelliteController.isSatelliteEnabledOrBeingEnabled()) {
                             // Could not turn satellite off
                             Rlog.e(TAG, "Failed to turn off satellite modem.");
                             completeEmergencyMode(emergencyType, DisconnectCause.SATELLITE_ENABLED);
@@ -1647,7 +1647,7 @@
                         return false;
                     }
                     return phone.getServiceStateTracker().isRadioOn()
-                            && !satelliteController.isSatelliteEnabled();
+                            && !satelliteController.isSatelliteEnabledOrBeingEnabled();
                 }
 
                 @Override
@@ -1659,7 +1659,7 @@
                     }
                     // onTimeout shall be called only with the Phone for emergency
                     return phone.getServiceStateTracker().isRadioOn()
-                            && !satelliteController.isSatelliteEnabled();
+                            && !satelliteController.isSatelliteEnabledOrBeingEnabled();
                 }
             }, !isTestEmergencyNumber, phone, isTestEmergencyNumber, waitForInServiceTimeout,
                     /* forNormalRoutingEmergencyCall */ false);
diff --git a/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java b/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java
index 26da1c2..11162b8 100644
--- a/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java
+++ b/src/java/com/android/internal/telephony/emergency/RadioOnHelper.java
@@ -104,11 +104,8 @@
         }
         powerOnRadio(forEmergencyCall, phoneForEmergencyCall, isTestEmergencyNumber,
                 forNormalRoutingEmergencyCall);
-        if (SatelliteController.getInstance().isSatelliteEnabled()
-                || SatelliteController.getInstance().isSatelliteBeingEnabled()) {
-            // TODO: phoneForEmergencyCall is actually ignored, SatelliteController#mSatelliePhone
-            //  is being used instead.
-            powerOffSatellite(phoneForEmergencyCall);
+        if (SatelliteController.getInstance().isSatelliteEnabledOrBeingEnabled()) {
+            powerOffSatellite();
         }
     }
 
@@ -162,7 +159,7 @@
      * Attempt to power off the satellite modem. We'll eventually get an
      * onSatelliteModemStateChanged() callback when the satellite modem is successfully disabled.
      */
-    private void powerOffSatellite(Phone phoneForEmergencyCall) {
+    private void powerOffSatellite() {
         SatelliteController satelliteController = SatelliteController.getInstance();
         satelliteController.requestSatelliteEnabled(
                 false /* enableSatellite */, false /* enableDemoMode */, false /* isEmergency */,
diff --git a/src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java b/src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java
index eaff539..3a3f6c8 100644
--- a/src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java
+++ b/src/java/com/android/internal/telephony/emergency/RadioOnStateListener.java
@@ -152,6 +152,11 @@
         public void onEmergencyModeChanged(boolean isEmergency) {
             Rlog.d(TAG, "onEmergencyModeChanged: ignored " + isEmergency);
         }
+
+        @Override
+        public void onRegistrationFailure(int causeCode) {
+            Rlog.d(TAG, "onRegistrationFailure: causeCode " + causeCode);
+        }
     };
 
     private Callback mCallback; // The callback to notify upon completion.
@@ -225,7 +230,7 @@
         // Register for RADIO_OFF to handle cases where emergency call is dialed before
         // we receive UNSOL_RESPONSE_RADIO_STATE_CHANGED with RADIO_OFF.
         registerForRadioOff();
-        if (mSatelliteController.isSatelliteEnabled()) {
+        if (mSatelliteController.isSatelliteEnabledOrBeingEnabled()) {
             // Register for satellite modem state changed to notify when satellite is disabled.
             registerForSatelliteEnabledChanged();
         }
@@ -396,7 +401,7 @@
                 Rlog.d(TAG, "Trying (again) to turn the radio on and satellite modem off.");
                 mPhone.setRadioPower(true, mForEmergencyCall, mSelectedPhoneForEmergencyCall,
                         false);
-                if (mSatelliteController.isSatelliteEnabled()) {
+                if (mSatelliteController.isSatelliteEnabledOrBeingEnabled()) {
                     mSatelliteController.requestSatelliteEnabled(
                             false /* enableSatellite */, false /* enableDemoMode */,
                             false /* isEmergency*/,
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
index 4e773f3..c5cc769 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
@@ -749,10 +749,12 @@
         @Override
         public boolean processMessage(Message message) {
             if (message.what == CMD_SERVICE_DISCONNECTED) {
+                EuiccSession.get().endAllSessions();
                 mEuiccService = null;
                 transitionTo(mDisconnectedState);
                 return HANDLED;
             } else if (message.what == CMD_LINGER_TIMEOUT) {
+                EuiccSession.get().endAllSessions();
                 unbind();
                 transitionTo(mAvailableState);
                 return HANDLED;
@@ -831,6 +833,7 @@
                         }
                         case CMD_DOWNLOAD_SUBSCRIPTION: {
                             DownloadRequest request = (DownloadRequest) message.obj;
+                            EuiccSession.get().startSession(EuiccSession.DOWNLOAD);
                             mEuiccService.downloadSubscription(slotId,
                                     request.mPortIndex,
                                     request.mSubscription,
@@ -845,6 +848,7 @@
                                                     .onDownloadComplete(result);
                                                 onCommandEnd(callback);
                                             });
+                                            EuiccSession.get().endSession(EuiccSession.DOWNLOAD);
                                         }
                                     });
                             break;
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccController.java b/src/java/com/android/internal/telephony/euicc/EuiccController.java
index 5ed3eaa..715116e 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccController.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccController.java
@@ -1855,7 +1855,12 @@
         if (bestComponent != null) {
             intent.setPackage(bestComponent.packageName);
         }
-        mContext.sendBroadcast(intent, permission.WRITE_EMBEDDED_SUBSCRIPTIONS);
+        if (mFeatureFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                    permission.WRITE_EMBEDDED_SUBSCRIPTIONS);
+        } else {
+            mContext.sendBroadcast(intent, permission.WRITE_EMBEDDED_SUBSCRIPTIONS);
+        }
     }
 
     @Nullable
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccSession.java b/src/java/com/android/internal/telephony/euicc/EuiccSession.java
new file mode 100644
index 0000000..f17789f
--- /dev/null
+++ b/src/java/com/android/internal/telephony/euicc/EuiccSession.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2024 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.euicc;
+
+import android.annotation.Nullable;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.uicc.euicc.apdu.ApduSender;
+import com.android.telephony.Rlog;
+
+import java.util.Set;
+
+/**
+ * A eUICC transaction session aims to optimize multiple back-to-back EuiccPort API calls by only
+ * open and close a logical channel once.
+ *
+ * <p>This class is thread-safe.
+ */
+public class EuiccSession {
+    private static final String TAG = "EuiccSession";
+
+    // **** Well known session IDs, see #startSession() ****
+    public static final String DOWNLOAD = "DOWNLOAD";
+
+    @GuardedBy("EuiccSession.class")
+    private static EuiccSession sInstance;
+
+    public static synchronized EuiccSession get() {
+        if (sInstance == null) {
+            sInstance = new EuiccSession();
+        }
+        return sInstance;
+    }
+
+    @GuardedBy("this")
+    private final Set<String> mSessions = new ArraySet<>();
+
+    @GuardedBy("this")
+    private final Set<ApduSender> mApduSenders = new ArraySet<>();
+
+    /**
+     * Marks the start of a eUICC transaction session.
+     *
+     * <p>A session means a long-open logical channel (see {@link ApduSender}) used to
+     * send multiple APDUs for one action e.g. {@link EuiccController#downloadSubscription()}.
+     * Those APDUs can be send by one or multiple {@link EuiccCardController} methods.
+     *
+     * <p>Ideally a session should correespond to one phoneId and hence just one logical channel.
+     * But many {@link EuiccCardController} methods uses first available port and is not specific
+     * to a phoneId. So EuiccController cannot choose one phoneId to use. Hence a session has to
+     * be not specific to phoneId, i.e. for DSDS device both phoneId's will be in a session.
+     *
+     * <p>If called multiple times with different {@code sessionId}'s, the session is truly closed
+     * when the all sessions are ended. See {@link #endSession()}.
+     *
+     * @param sessionId The session ID.
+     */
+    public void startSession(String sessionId) {
+        if (!Flags.optimizationApduSender()) {
+            // Other methods in this class is no-op if no session started.
+            // Do not add flag to other methods, so if the flag gets turned off,
+            // the session can be ended properly.
+            return;
+        }
+        Rlog.i(TAG, "startSession: " + sessionId);
+        synchronized(this) {
+            mSessions.add(sessionId);
+        }
+    }
+
+    /** Returns {@code true} if there is at least one session ongoing. */
+    public boolean hasSession() {
+        boolean hasSession = hasSessionInternal();
+        Rlog.i(TAG, "hasSession: " + hasSession);
+        return hasSession;
+    }
+
+    // The bare metal implementation of hasSession() without logging.
+    private boolean hasSessionInternal() {
+        synchronized(this) {
+            return !mSessions.isEmpty();
+        }
+    }
+
+    /**
+     * Notes that a logical channel may be opened by the {@code apduSender}, which will
+     * be used to close the channel when session ends (see {@link #endSession()}).
+     *
+     * <p>No-op if no session ongoing (see {@link #hasSession()}).
+     *
+     * @param apduSender The ApduSender that will open the channel.
+     */
+    public void noteChannelOpen(ApduSender apduSender) {
+        Rlog.i(TAG, "noteChannelOpen: " + apduSender);
+        synchronized(this) {
+            if (hasSessionInternal()) {
+                mApduSenders.add(apduSender);
+            }
+        }
+    }
+
+    /**
+     * Marks the end of a eUICC transaction session. If this ends the last ongoing session,
+     * try to close the logical channel using the noted {@code apduSender}s
+     * (see {@link #noteChannelOpen()}).
+     *
+     * @param sessionId The session ID.
+     */
+    public void endSession(String sessionId) {
+        Rlog.i(TAG, "endSession: " + sessionId);
+        endSessionInternal(sessionId);
+    }
+
+    /**
+     * Marks the end of all eUICC transaction sessions and close the logical
+     * channels using the noted {@code apduSender}s
+     * (see {@link #noteChannelOpen()}).
+     *
+     * <p>This is useful in global cleanup e.g. when EuiccService
+     * implementation app crashes and indivial {@link #endSession()} calls
+     * won't happen in {@link EuiccConnector}.
+     */
+    public void endAllSessions() {
+        Rlog.i(TAG, "endAllSessions");
+        endSessionInternal(null);
+    }
+
+    // The implementation of endSession(sessionId) or endAllSessions() when the sessionId is null,
+    // without logging.
+    private void endSessionInternal(@Nullable String sessionId) {
+        ApduSender[] apduSenders = new ApduSender[0];
+        synchronized(this) {
+            boolean sessionRemoved = removeOrClear(mSessions, sessionId);
+            // 1. sessionRemoved is false if the `sessionId` was never started or there was
+            // no session. Don't bother invoke `apduSender`.
+            // 2. If some session is removed, and as a result there is no more session, we
+            // can clsoe channels.
+            if (sessionRemoved && !hasSessionInternal()) {
+                // copy mApduSenders to a local variable so we don't call closeAnyOpenChannel()
+                // which can take time in synchronized block.
+                apduSenders = mApduSenders.toArray(apduSenders);
+                mApduSenders.clear();
+            }
+        }
+        for (ApduSender apduSender : apduSenders) {
+            apduSender.closeAnyOpenChannel();
+        }
+    }
+
+    /**
+     * Removes the given element from the set. If the element is null, clears the set.
+     *
+     * @return true if the set changed as a result of the call
+     */
+    private static boolean removeOrClear(Set<String> collection, @Nullable String element) {
+        if (element == null) {
+            boolean collectionChanged = !collection.isEmpty();
+            collection.clear();
+            return collectionChanged;
+        } else {
+            return collection.remove(element);
+        }
+    }
+
+    @VisibleForTesting
+    public EuiccSession() {}
+}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index 1b2aee0..e802b3e 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -70,6 +70,7 @@
 import android.os.RegistrantList;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.sysprop.TelephonyProperties;
@@ -1543,7 +1544,11 @@
         Intent intent = new Intent(intentAction);
         intent.putExtra(ImsManager.EXTRA_PHONE_ID, mPhone.getPhoneId());
         if (mPhone != null && mPhone.getContext() != null) {
-            mPhone.getContext().sendBroadcast(intent);
+            if (mFeatureFlags.hsumBroadcast()) {
+                mPhone.getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
+            } else {
+                mPhone.getContext().sendBroadcast(intent);
+            }
         }
     }
 
@@ -2879,7 +2884,7 @@
         boolean rejectCall = false;
 
         if (mFeatureFlags.preventHangupDuringCallMerge()) {
-            if (imsCall.isCallSessionMergePending()) {
+            if (imsCall != null && imsCall.isCallSessionMergePending()) {
                 if (DBG) log("hangup call failed during call merge");
 
                 throw new CallStateException("can not hangup during call merge");
@@ -4680,8 +4685,14 @@
                     configChangedIntent.putExtra(ImsConfig.EXTRA_CHANGED_ITEM, item);
                     configChangedIntent.putExtra(ImsConfig.EXTRA_NEW_VALUE, value);
                     if (mPhone != null && mPhone.getContext() != null) {
-                        mPhone.getContext().sendBroadcast(configChangedIntent,
-                                Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+                        if (mFeatureFlags.hsumBroadcast()) {
+                            mPhone.getContext().sendBroadcastAsUser(configChangedIntent,
+                                    UserHandle.ALL,
+                                    Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+                        } else {
+                            mPhone.getContext().sendBroadcast(configChangedIntent,
+                                    Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+                        }
                     }
                 }
 
@@ -6341,8 +6352,13 @@
         configChangedIntent.putExtra(ImsConfig.EXTRA_CHANGED_ITEM, item);
         configChangedIntent.putExtra(ImsConfig.EXTRA_NEW_VALUE, value);
         if (mPhone != null && mPhone.getContext() != null) {
-            mPhone.getContext().sendBroadcast(
-                    configChangedIntent, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+            if (mFeatureFlags.hsumBroadcast()) {
+                mPhone.getContext().sendBroadcastAsUser(configChangedIntent, UserHandle.ALL,
+                        Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+            } else {
+                mPhone.getContext().sendBroadcast(
+                        configChangedIntent, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+            }
         }
     }
 }
diff --git a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
index a16545a..e43bf3c 100644
--- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
@@ -1476,7 +1476,11 @@
                 satelliteController.countOfAllowedSatelliteAccess,
                 satelliteController.countOfDisallowedSatelliteAccess,
                 satelliteController.countOfSatelliteAccessCheckFail,
-                satelliteController.isProvisioned);
+                satelliteController.isProvisioned,
+                satelliteController.carrierId,
+                satelliteController.countOfSatelliteAllowedStateChangedEvents,
+                satelliteController.countOfSuccessfulLocationQueries,
+                satelliteController.countOfFailedLocationQueries);
     }
 
     private static StatsEvent buildStatsEvent(SatelliteSession satelliteSession) {
@@ -1494,7 +1498,11 @@
                 satelliteSession.countOfIncomingDatagramSuccess,
                 satelliteSession.countOfIncomingDatagramFailed,
                 satelliteSession.isDemoMode,
-                satelliteSession.maxNtnSignalStrengthLevel);
+                satelliteSession.maxNtnSignalStrengthLevel,
+                satelliteSession.carrierId,
+                satelliteSession.countOfSatelliteNotificationDisplayed,
+                satelliteSession.countOfAutoExitDueToScreenOff,
+                satelliteSession.countOfAutoExitDueToTnNetwork);
     }
 
     private static StatsEvent buildStatsEvent(SatelliteIncomingDatagram stats) {
@@ -1503,7 +1511,8 @@
                 stats.resultCode,
                 stats.datagramSizeBytes,
                 stats.datagramTransferTimeMillis,
-                stats.isDemoMode);
+                stats.isDemoMode,
+                stats.carrierId);
     }
 
     private static StatsEvent buildStatsEvent(SatelliteOutgoingDatagram stats) {
@@ -1513,7 +1522,8 @@
                 stats.resultCode,
                 stats.datagramSizeBytes,
                 stats.datagramTransferTimeMillis,
-                stats.isDemoMode);
+                stats.isDemoMode,
+                stats.carrierId);
     }
 
     private static StatsEvent buildStatsEvent(SatelliteProvision stats) {
@@ -1522,7 +1532,8 @@
                 stats.resultCode,
                 stats.provisioningTimeSec,
                 stats.isProvisionRequest,
-                stats.isCanceled);
+                stats.isCanceled,
+                stats.carrierId);
     }
 
     private static StatsEvent buildStatsEvent(SatelliteSosMessageRecommender stats) {
@@ -1582,7 +1593,9 @@
                 stats.countOfSatelliteNotificationDisplayed,
                 stats.satelliteSessionGapMinSec,
                 stats.satelliteSessionGapAvgSec,
-                stats.satelliteSessionGapMaxSec);
+                stats.satelliteSessionGapMaxSec,
+                stats.carrierId,
+                stats.isDeviceEntitled);
     }
 
     private static StatsEvent buildStatsEvent(SatelliteEntitlement stats) {
@@ -1614,7 +1627,9 @@
                 stats.isEmergency,
                 stats.resultCode,
                 stats.countryCodes,
-                stats.configDataSource);
+                stats.configDataSource,
+                stats.carrierId,
+                stats.triggeringEvent);
     }
 
     /** Returns all phones in {@link PhoneFactory}, or an empty array if phones not made yet. */
diff --git a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
index 780af82..9f74890 100644
--- a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
+++ b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
@@ -770,7 +770,14 @@
         atom.countOfAllowedSatelliteAccess += stats.countOfAllowedSatelliteAccess;
         atom.countOfDisallowedSatelliteAccess += stats.countOfDisallowedSatelliteAccess;
         atom.countOfSatelliteAccessCheckFail += stats.countOfSatelliteAccessCheckFail;
+
         atom.isProvisioned = stats.isProvisioned;
+        atom.carrierId = stats.carrierId;
+
+        atom.countOfSatelliteAllowedStateChangedEvents
+                += stats.countOfSatelliteAllowedStateChangedEvents;
+        atom.countOfSuccessfulLocationQueries += stats.countOfSuccessfulLocationQueries;
+        atom.countOfFailedLocationQueries += stats.countOfFailedLocationQueries;
 
         mAtoms.satelliteController = atomArray;
         saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
@@ -854,24 +861,24 @@
     /** Adds a new {@link CarrierRoamingSatelliteControllerStats} to the storage. */
     public synchronized void addCarrierRoamingSatelliteControllerStats(
             CarrierRoamingSatelliteControllerStats stats) {
-        // CarrierRoamingSatelliteController is a single data point
-        CarrierRoamingSatelliteControllerStats[] atomArray =
-                mAtoms.carrierRoamingSatelliteControllerStats;
-        if (atomArray == null || atomArray.length == 0) {
-            atomArray = new CarrierRoamingSatelliteControllerStats[] {new
-                    CarrierRoamingSatelliteControllerStats()};
+        CarrierRoamingSatelliteControllerStats existingStats = find(stats);
+        if (existingStats != null) {
+            existingStats.configDataSource = stats.configDataSource;
+            existingStats.countOfEntitlementStatusQueryRequest +=
+                    stats.countOfEntitlementStatusQueryRequest;
+            existingStats.countOfSatelliteConfigUpdateRequest +=
+                    stats.countOfSatelliteConfigUpdateRequest;
+            existingStats.countOfSatelliteNotificationDisplayed +=
+                    stats.countOfSatelliteNotificationDisplayed;
+            existingStats.satelliteSessionGapMinSec = stats.satelliteSessionGapMinSec;
+            existingStats.satelliteSessionGapAvgSec = stats.satelliteSessionGapAvgSec;
+            existingStats.satelliteSessionGapMaxSec = stats.satelliteSessionGapMaxSec;
+            existingStats.isDeviceEntitled = stats.isDeviceEntitled;
+        } else {
+            mAtoms.carrierRoamingSatelliteControllerStats = insertAtRandomPlace(
+                    mAtoms.carrierRoamingSatelliteControllerStats, stats, mMaxNumSatelliteStats);
         }
 
-        CarrierRoamingSatelliteControllerStats atom = atomArray[0];
-        atom.configDataSource = stats.configDataSource;
-        atom.countOfEntitlementStatusQueryRequest += stats.countOfEntitlementStatusQueryRequest;
-        atom.countOfSatelliteConfigUpdateRequest += stats.countOfSatelliteConfigUpdateRequest;
-        atom.countOfSatelliteNotificationDisplayed += stats.countOfSatelliteNotificationDisplayed;
-        atom.satelliteSessionGapMinSec = stats.satelliteSessionGapMinSec;
-        atom.satelliteSessionGapAvgSec = stats.satelliteSessionGapAvgSec;
-        atom.satelliteSessionGapMaxSec = stats.satelliteSessionGapMaxSec;
-
-        mAtoms.carrierRoamingSatelliteControllerStats = atomArray;
         saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
     }
 
@@ -2332,7 +2339,12 @@
                     && stats.countOfIncomingDatagramSuccess == key.countOfIncomingDatagramSuccess
                     && stats.countOfIncomingDatagramFailed == key.countOfIncomingDatagramFailed
                     && stats.isDemoMode == key.isDemoMode
-                    && stats.maxNtnSignalStrengthLevel == key.maxNtnSignalStrengthLevel) {
+                    && stats.maxNtnSignalStrengthLevel == key.maxNtnSignalStrengthLevel
+                    && stats.carrierId == key.carrierId
+                    && stats.countOfSatelliteNotificationDisplayed
+                    == key.countOfSatelliteNotificationDisplayed
+                    && stats.countOfAutoExitDueToScreenOff == key.countOfAutoExitDueToScreenOff
+                    && stats.countOfAutoExitDueToTnNetwork == key.countOfAutoExitDueToTnNetwork) {
                 return stats;
             }
         }
@@ -2378,6 +2390,21 @@
     }
 
     /**
+     * Returns CarrierRoamingSatelliteControllerStats atom that has same carrier_id value or
+     * {@code null} if does not exist.
+     */
+    private @Nullable CarrierRoamingSatelliteControllerStats find(
+            CarrierRoamingSatelliteControllerStats key) {
+        for (CarrierRoamingSatelliteControllerStats stats :
+                mAtoms.carrierRoamingSatelliteControllerStats) {
+            if (stats.carrierId == key.carrierId) {
+                return stats;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Returns SatelliteEntitlement atom that has same values or {@code null} if it does not exist.
      */
     private @Nullable SatelliteEntitlement find(SatelliteEntitlement key) {
diff --git a/src/java/com/android/internal/telephony/metrics/SatelliteStats.java b/src/java/com/android/internal/telephony/metrics/SatelliteStats.java
index 2e58159..71c1bf3 100644
--- a/src/java/com/android/internal/telephony/metrics/SatelliteStats.java
+++ b/src/java/com/android/internal/telephony/metrics/SatelliteStats.java
@@ -17,8 +17,10 @@
 package com.android.internal.telephony.metrics;
 
 import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE;
+import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
 
-import android.telephony.TelephonyManager;
+import static com.android.internal.telephony.satellite.SatelliteConstants.TRIGGERING_EVENT_UNKNOWN;
+
 import android.telephony.satellite.NtnSignalStrength;
 import android.telephony.satellite.SatelliteManager;
 
@@ -94,6 +96,10 @@
         private final int mCountOfDisallowedSatelliteAccess;
         private final int mCountOfSatelliteAccessCheckFail;
         private static boolean sIsProvisioned;
+        private static int sCarrierId = UNKNOWN_CARRIER_ID;
+        private final int mCountOfSatelliteAllowedStateChangedEvents;
+        private final int mCountOfSuccessfulLocationQueries;
+        private final int mCountOfFailedLocationQueries;
 
         private SatelliteControllerParams(Builder builder) {
             this.mCountOfSatelliteServiceEnablementsSuccess =
@@ -144,6 +150,18 @@
             if (builder.mIsProvisioned.isPresent()) {
                 this.sIsProvisioned = builder.mIsProvisioned.get();
             }
+
+            // Carrier ID value should be updated only when it is meaningful.
+            if (builder.mCarrierId.isPresent()) {
+                this.sCarrierId = builder.mCarrierId.get();
+            }
+
+            this.mCountOfSatelliteAllowedStateChangedEvents =
+                    builder.mCountOfSatelliteAllowedStateChangedEvents;
+            this.mCountOfSuccessfulLocationQueries =
+                    builder.mCountOfSuccessfulLocationQueries;
+            this.mCountOfFailedLocationQueries =
+                    builder.mCountOfFailedLocationQueries;
         }
 
         public int getCountOfSatelliteServiceEnablementsSuccess() {
@@ -262,6 +280,22 @@
             return sIsProvisioned;
         }
 
+        public int getCarrierId() {
+            return sCarrierId;
+        }
+
+        public int getCountOfSatelliteAllowedStateChangedEvents() {
+            return mCountOfSatelliteAllowedStateChangedEvents;
+        }
+
+        public int getCountOfSuccessfulLocationQueries() {
+            return mCountOfSuccessfulLocationQueries;
+        }
+
+        public int getCountOfFailedLocationQueries() {
+            return mCountOfFailedLocationQueries;
+        }
+
         /**
          * A builder class to create {@link SatelliteControllerParams} data structure class
          */
@@ -295,6 +329,10 @@
             private int mCountOfDisallowedSatelliteAccess = 0;
             private int mCountOfSatelliteAccessCheckFail = 0;
             private Optional<Boolean> mIsProvisioned = Optional.empty();
+            private Optional<Integer> mCarrierId = Optional.empty();
+            private int mCountOfSatelliteAllowedStateChangedEvents = 0;
+            private int mCountOfSuccessfulLocationQueries = 0;
+            private int mCountOfFailedLocationQueries = 0;
 
             /**
              * Sets countOfSatelliteServiceEnablementsSuccess value of {@link SatelliteController}
@@ -583,6 +621,46 @@
             }
 
             /**
+             * Sets Carrier ID value of {@link SatelliteController} atom
+             * then returns Builder class
+             */
+            public Builder setCarrierId(int carrierId) {
+                this.mCarrierId = Optional.of(carrierId);
+                return this;
+            }
+
+            /**
+             * Sets countOfSatelliteAllowedStateChangedEvents value of {@link SatelliteController}
+             * atom
+             * then returns Builder class
+             */
+            public Builder setCountOfSatelliteAllowedStateChangedEvents(
+                    int countOfSatelliteAllowedStateChangedEvents) {
+                this.mCountOfSatelliteAllowedStateChangedEvents =
+                        countOfSatelliteAllowedStateChangedEvents;
+                return this;
+            }
+
+            /**
+             * Sets countOfSuccessfulLocationQueries value of {@link SatelliteController} atom
+             * then returns Builder class
+             */
+            public Builder setCountOfSuccessfulLocationQueries(
+                    int countOfSuccessfulLocationQueries) {
+                this.mCountOfSuccessfulLocationQueries = countOfSuccessfulLocationQueries;
+                return this;
+            }
+
+            /**
+             * Sets countOfFailedLocationQueries value of {@link SatelliteController} atom
+             * then returns Builder class
+             */
+            public Builder setCountOfFailedLocationQueries(int countOfFailedLocationQueries) {
+                this.mCountOfFailedLocationQueries = countOfFailedLocationQueries;
+                return this;
+            }
+
+            /**
              * Returns ControllerParams, which contains whole component of
              * {@link SatelliteController} atom
              */
@@ -632,6 +710,11 @@
                     + ", countOfDisallowedSatelliteAccess=" + mCountOfDisallowedSatelliteAccess
                     + ", countOfSatelliteAccessCheckFail=" + mCountOfSatelliteAccessCheckFail
                     + ", isProvisioned=" + sIsProvisioned
+                    + ", carrierId=" + sCarrierId
+                    + ", countOfSatelliteAllowedStateChangedEvents="
+                    + mCountOfSatelliteAllowedStateChangedEvents
+                    + ", countOfSuccessfulLocationQueries=" + mCountOfSuccessfulLocationQueries
+                    + ", countOfFailedLocationQueries=" + mCountOfFailedLocationQueries
                     + ")";
         }
     }
@@ -653,6 +736,11 @@
         private final int mCountOfIncomingDatagramFailed;
         private final boolean mIsDemoMode;
         private final @NtnSignalStrength.NtnSignalStrengthLevel int mMaxNtnSignalStrengthLevel;
+        private final int mCarrierId;
+        private final int mCountOfSatelliteNotificationDisplayed;
+        private final int mCountOfAutoExitDueToScreenOff;
+        private final int mCountOfAutoExitDueToTnNetwork;
+
 
         private SatelliteSessionParams(Builder builder) {
             this.mSatelliteServiceInitializationResult =
@@ -669,6 +757,11 @@
             this.mCountOfIncomingDatagramFailed = builder.mCountOfIncomingDatagramFailed;
             this.mIsDemoMode = builder.mIsDemoMode;
             this.mMaxNtnSignalStrengthLevel = builder.mMaxNtnSignalStrengthLevel;
+            this.mCarrierId = builder.mCarrierId;
+            this.mCountOfSatelliteNotificationDisplayed =
+                    builder.mCountOfSatelliteNotificationDisplayed;
+            this.mCountOfAutoExitDueToScreenOff = builder.mCountOfAutoExitDueToScreenOff;
+            this.mCountOfAutoExitDueToTnNetwork = builder.mCountOfAutoExitDueToTnNetwork;
         }
 
         public int getSatelliteServiceInitializationResult() {
@@ -719,6 +812,22 @@
             return mMaxNtnSignalStrengthLevel;
         }
 
+        public int getCarrierId() {
+            return mCarrierId;
+        }
+
+        public int getCountOfSatelliteNotificationDisplayed() {
+            return mCountOfSatelliteNotificationDisplayed;
+        }
+
+        public int getCountOfAutoExitDueToScreenOff() {
+            return mCountOfAutoExitDueToScreenOff;
+        }
+
+        public int getCountOfAutoExitDueToTnNetwork() {
+            return mCountOfAutoExitDueToTnNetwork;
+        }
+
         /**
          * A builder class to create {@link SatelliteSessionParams} data structure class
          */
@@ -736,6 +845,10 @@
             private boolean mIsDemoMode = false;
             private @NtnSignalStrength.NtnSignalStrengthLevel int mMaxNtnSignalStrengthLevel =
                     NTN_SIGNAL_STRENGTH_NONE;
+            private int mCarrierId = UNKNOWN_CARRIER_ID;
+            private int mCountOfSatelliteNotificationDisplayed = -1;
+            private int mCountOfAutoExitDueToScreenOff = -1;
+            private int mCountOfAutoExitDueToTnNetwork = -1;
 
             /**
              * Sets satelliteServiceInitializationResult value of {@link SatelliteSession}
@@ -811,13 +924,49 @@
                 return this;
             }
 
-            /** Sets the max ntn signal strength for the satellite session */
+            /** Sets the max ntn signal strength for the satellite session. */
             public Builder setMaxNtnSignalStrengthLevel(
                     @NtnSignalStrength.NtnSignalStrengthLevel int maxNtnSignalStrengthLevel) {
                 this.mMaxNtnSignalStrengthLevel = maxNtnSignalStrengthLevel;
                 return this;
             }
 
+            /** Sets the currently active NB-IoT NTN carrier ID. */
+            public Builder setCarrierId(int carrierId) {
+                this.mCarrierId = carrierId;
+                return this;
+            }
+
+            /**
+             * Sets Total number of times the user is notified that the device is eligible for
+             * satellite service for this session.
+             */
+            public Builder setCountOfSatelliteNotificationDisplayed(
+                    int countOfSatelliteNotificationDisplayed) {
+                this.mCountOfSatelliteNotificationDisplayed = countOfSatelliteNotificationDisplayed;
+                return this;
+            }
+
+            /**
+             * Sets Total number of times exit P2P message service automatically due to screen is
+             * off and timer is expired.
+             */
+            public Builder setCountOfAutoExitDueToScreenOff(
+                    int countOfAutoExitDueToScreenOff) {
+                this.mCountOfAutoExitDueToScreenOff = countOfAutoExitDueToScreenOff;
+                return this;
+            }
+
+            /**
+             * Sets Total number of times times exit P2P message service automatically due to
+             * scan TN network.
+             */
+            public Builder setCountOfAutoExitDueToTnNetwork(
+                    int countOfAutoExitDueToTnNetwork) {
+                this.mCountOfAutoExitDueToTnNetwork = countOfAutoExitDueToTnNetwork;
+                return this;
+            }
+
             /**
              * Returns SessionParams, which contains whole component of
              * {@link SatelliteSession} atom
@@ -843,6 +992,11 @@
                     + ", CountOfIncomingDatagramFailed=" + mCountOfIncomingDatagramFailed
                     + ", IsDemoMode=" + mIsDemoMode
                     + ", MaxNtnSignalStrengthLevel=" + mMaxNtnSignalStrengthLevel
+                    + ", CarrierId=" + mCarrierId
+                    + ", CountOfSatelliteNotificationDisplayed"
+                    + mCountOfSatelliteNotificationDisplayed
+                    + ", CountOfAutoExitDueToScreenOff" + mCountOfAutoExitDueToScreenOff
+                    + ", CountOfAutoExitDueToTnNetwork" + mCountOfAutoExitDueToTnNetwork
                     + ")";
         }
     }
@@ -856,12 +1010,14 @@
         private final int mDatagramSizeBytes;
         private final long mDatagramTransferTimeMillis;
         private final boolean mIsDemoMode;
+        private final int mCarrierId;
 
         private SatelliteIncomingDatagramParams(Builder builder) {
             this.mResultCode = builder.mResultCode;
             this.mDatagramSizeBytes = builder.mDatagramSizeBytes;
             this.mDatagramTransferTimeMillis = builder.mDatagramTransferTimeMillis;
             this.mIsDemoMode = builder.mIsDemoMode;
+            this.mCarrierId = builder.mCarrierId;
         }
 
         public int getResultCode() {
@@ -880,6 +1036,10 @@
             return mIsDemoMode;
         }
 
+        public int getCarrierId() {
+            return mCarrierId;
+        }
+
         /**
          * A builder class to create {@link SatelliteIncomingDatagramParams} data structure class
          */
@@ -888,6 +1048,7 @@
             private int mDatagramSizeBytes = -1;
             private long mDatagramTransferTimeMillis = -1;
             private boolean mIsDemoMode = false;
+            private int mCarrierId = UNKNOWN_CARRIER_ID;
 
             /**
              * Sets resultCode value of {@link SatelliteIncomingDatagram} atom
@@ -925,6 +1086,12 @@
                 return this;
             }
 
+            /** Sets the currently active NB-IoT NTN carrier ID. */
+            public Builder setCarrierId(int carrierId) {
+                this.mCarrierId = carrierId;
+                return this;
+            }
+
             /**
              * Returns IncomingDatagramParams, which contains whole component of
              * {@link SatelliteIncomingDatagram} atom
@@ -942,6 +1109,7 @@
                     + ", datagramSizeBytes=" + mDatagramSizeBytes
                     + ", datagramTransferTimeMillis=" + mDatagramTransferTimeMillis
                     + ", isDemoMode=" + mIsDemoMode
+                    + ", CarrierId=" + mCarrierId
                     + ")";
         }
     }
@@ -956,6 +1124,7 @@
         private final int mDatagramSizeBytes;
         private final long mDatagramTransferTimeMillis;
         private final boolean mIsDemoMode;
+        private final int mCarrierId;
 
         private SatelliteOutgoingDatagramParams(Builder builder) {
             this.mDatagramType = builder.mDatagramType;
@@ -963,6 +1132,7 @@
             this.mDatagramSizeBytes = builder.mDatagramSizeBytes;
             this.mDatagramTransferTimeMillis = builder.mDatagramTransferTimeMillis;
             this.mIsDemoMode = builder.mIsDemoMode;
+            this.mCarrierId = builder.mCarrierId;
         }
 
         public int getDatagramType() {
@@ -985,6 +1155,10 @@
             return mIsDemoMode;
         }
 
+        public int getCarrierId() {
+            return mCarrierId;
+        }
+
         /**
          * A builder class to create {@link SatelliteOutgoingDatagramParams} data structure class
          */
@@ -994,6 +1168,7 @@
             private int mDatagramSizeBytes = -1;
             private long mDatagramTransferTimeMillis = -1;
             private boolean mIsDemoMode = false;
+            private int mCarrierId = UNKNOWN_CARRIER_ID;
 
             /**
              * Sets datagramType value of {@link SatelliteOutgoingDatagram} atom
@@ -1040,6 +1215,12 @@
                 return this;
             }
 
+            /** Sets the currently active NB-IoT NTN carrier ID. */
+            public Builder setCarrierId(int carrierId) {
+                this.mCarrierId = carrierId;
+                return this;
+            }
+
             /**
              * Returns OutgoingDatagramParams, which contains whole component of
              * {@link SatelliteOutgoingDatagram} atom
@@ -1058,6 +1239,7 @@
                     + ", datagramSizeBytes=" + mDatagramSizeBytes
                     + ", datagramTransferTimeMillis=" + mDatagramTransferTimeMillis
                     + ", isDemoMode=" + mIsDemoMode
+                    + ", CarrierId=" + mCarrierId
                     + ")";
         }
     }
@@ -1071,12 +1253,14 @@
         private final int mProvisioningTimeSec;
         private final boolean mIsProvisionRequest;
         private final boolean mIsCanceled;
+        private final int mCarrierId;
 
         private SatelliteProvisionParams(Builder builder) {
             this.mResultCode = builder.mResultCode;
             this.mProvisioningTimeSec = builder.mProvisioningTimeSec;
             this.mIsProvisionRequest = builder.mIsProvisionRequest;
             this.mIsCanceled = builder.mIsCanceled;
+            this.mCarrierId = builder.mCarrierId;
         }
 
         public int getResultCode() {
@@ -1095,6 +1279,10 @@
             return mIsCanceled;
         }
 
+        public int getCarrierId() {
+            return mCarrierId;
+        }
+
         /**
          * A builder class to create {@link SatelliteProvisionParams} data structure class
          */
@@ -1103,6 +1291,7 @@
             private int mProvisioningTimeSec = -1;
             private boolean mIsProvisionRequest = false;
             private boolean mIsCanceled = false;
+            private int mCarrierId = UNKNOWN_CARRIER_ID;
 
             /**
              * Sets resultCode value of {@link SatelliteProvision} atom
@@ -1140,6 +1329,12 @@
                 return this;
             }
 
+            /** Sets the currently active NB-IoT NTN carrier ID. */
+            public Builder setCarrierId(int carrierId) {
+                this.mCarrierId = carrierId;
+                return this;
+            }
+
             /**
              * Returns ProvisionParams, which contains whole component of
              * {@link SatelliteProvision} atom
@@ -1156,7 +1351,9 @@
                     + "resultCode=" + mResultCode
                     + ", provisioningTimeSec=" + mProvisioningTimeSec
                     + ", isProvisionRequest=" + mIsProvisionRequest
-                    + ", isCanceled" + mIsCanceled + ")";
+                    + ", isCanceled" + mIsCanceled
+                    + ", CarrierId=" + mCarrierId
+                    + ")";
         }
     }
 
@@ -1236,7 +1433,7 @@
             private int mRecommendingHandoverType = -1;
             private boolean mIsSatelliteAllowedInCurrentLocation = false;
             private boolean mIsWifiConnected = false;
-            private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+            private int mCarrierId = UNKNOWN_CARRIER_ID;
 
 
             /**
@@ -1672,6 +1869,8 @@
         private final int mSatelliteSessionGapMinSec;
         private final int mSatelliteSessionGapAvgSec;
         private final int mSatelliteSessionGapMaxSec;
+        private static int sCarrierId;
+        private static boolean sIsDeviceEntitled;
 
         private CarrierRoamingSatelliteControllerStatsParams(Builder builder) {
             this.mConfigDataSource = builder.mConfigDataSource;
@@ -1684,6 +1883,16 @@
             this.mSatelliteSessionGapMinSec = builder.mSatelliteSessionGapMinSec;
             this.mSatelliteSessionGapAvgSec = builder.mSatelliteSessionGapAvgSec;
             this.mSatelliteSessionGapMaxSec = builder.mSatelliteSessionGapMaxSec;
+
+            // Carrier ID value should be updated only when it is meaningful.
+            if (builder.mCarrierId.isPresent()) {
+                this.sCarrierId = builder.mCarrierId.get();
+            }
+
+            // isDeviceEntitled value should be updated only when it is meaningful.
+            if (builder.mIsDeviceEntitled.isPresent()) {
+                this.sIsDeviceEntitled = builder.mIsDeviceEntitled.get();
+            }
         }
 
         public int getConfigDataSource() {
@@ -1715,6 +1924,14 @@
             return mSatelliteSessionGapMaxSec;
         }
 
+        public int getCarrierId() {
+            return sCarrierId;
+        }
+
+        public boolean isDeviceEntitled() {
+            return sIsDeviceEntitled;
+        }
+
         /**
          * A builder class to create {@link CarrierRoamingSatelliteControllerStatsParams}
          * data structure class
@@ -1727,6 +1944,8 @@
             private int mSatelliteSessionGapMinSec = 0;
             private int mSatelliteSessionGapAvgSec = 0;
             private int mSatelliteSessionGapMaxSec = 0;
+            private Optional<Integer> mCarrierId = Optional.empty();
+            private Optional<Boolean> mIsDeviceEntitled = Optional.empty();
 
             /**
              * Sets configDataSource value of {@link CarrierRoamingSatelliteControllerStats} atom
@@ -1794,6 +2013,18 @@
                 return this;
             }
 
+            /** Sets the currently active NB-IoT NTN carrier ID. */
+            public Builder setCarrierId(int carrierId) {
+                this.mCarrierId = Optional.of(carrierId);
+                return this;
+            }
+
+            /** Sets whether the device is currently entitled or not. */
+            public Builder setIsDeviceEntitled(boolean isDeviceEntitled) {
+                this.mIsDeviceEntitled = Optional.of(isDeviceEntitled);
+                return this;
+            }
+
             /**
              * Returns CarrierRoamingSatelliteControllerStatsParams, which contains whole component
              * of {@link CarrierRoamingSatelliteControllerStats} atom
@@ -1817,6 +2048,8 @@
                     + ", satelliteSessionGapMinSec=" + mSatelliteSessionGapMinSec
                     + ", satelliteSessionGapAvgSec=" + mSatelliteSessionGapAvgSec
                     + ", satelliteSessionGapMaxSec=" + mSatelliteSessionGapMaxSec
+                    + ", carrierId=" + sCarrierId
+                    + ", isDeviceEntitled=" + sIsDeviceEntitled
                     + ")";
         }
     }
@@ -2048,6 +2281,8 @@
         private final @SatelliteManager.SatelliteResult int mResultCode;
         private final String[] mCountryCodes;
         private final @SatelliteConstants.ConfigDataSource int mConfigDataSource;
+        private final int mCarrierId;
+        private final int mTriggeringEvent;
 
         private SatelliteAccessControllerParams(Builder builder) {
             this.mAccessControlType = builder.mAccessControlType;
@@ -2059,6 +2294,8 @@
             this.mResultCode = builder.mResultCode;
             this.mCountryCodes = builder.mCountryCodes;
             this.mConfigDataSource = builder.mConfigDataSource;
+            this.mCarrierId = builder.mCarrierId;
+            this.mTriggeringEvent = builder.mTriggeringEvent;
         }
 
         public @SatelliteConstants.AccessControlType int getAccessControlType() {
@@ -2097,6 +2334,14 @@
             return mConfigDataSource;
         }
 
+        public int getCarrierId() {
+            return mCarrierId;
+        }
+
+        @SatelliteConstants.TriggeringEvent public int getTriggeringEvent() {
+            return mTriggeringEvent;
+        }
+
         /**
          * A builder class to create {@link SatelliteAccessControllerParams} data structure class
          */
@@ -2110,6 +2355,9 @@
             private @SatelliteManager.SatelliteResult int mResultCode;
             private String[] mCountryCodes;
             private @SatelliteConstants.ConfigDataSource int mConfigDataSource;
+            private int mCarrierId = UNKNOWN_CARRIER_ID;
+            private @SatelliteConstants.TriggeringEvent int mTriggeringEvent =
+                    TRIGGERING_EVENT_UNKNOWN;
 
             /**
              * Sets AccessControlType value of {@link #SatelliteAccessController}
@@ -2171,6 +2419,19 @@
                 return this;
             }
 
+            /** Sets the currently active NB-IoT NTN carrier ID. */
+            public Builder setCarrierId(int carrierId) {
+                this.mCarrierId = carrierId;
+                return this;
+            }
+
+            /** Sets the triggering evenr for current satellite access controller metric. */
+            public Builder setTriggeringEvent(
+                    @SatelliteConstants.TriggeringEvent int triggeringEvent) {
+                this.mTriggeringEvent = triggeringEvent;
+                return this;
+            }
+
             /**
              * Returns AccessControllerParams, which contains whole component of
              * {@link #SatelliteAccessController} atom
@@ -2193,6 +2454,8 @@
                     + ", ResultCode=" + mResultCode
                     + ", CountryCodes=" + Arrays.toString(mCountryCodes)
                     + ", ConfigDataSource=" + mConfigDataSource
+                    + ", CarrierId=" + mCarrierId
+                    + ", TriggeringEvent=" + mTriggeringEvent
                     + ")";
         }
     }
@@ -2237,6 +2500,12 @@
         proto.countOfDisallowedSatelliteAccess = param.getCountOfDisallowedSatelliteAccess();
         proto.countOfSatelliteAccessCheckFail = param.getCountOfSatelliteAccessCheckFail();
         proto.isProvisioned = param.isProvisioned();
+        proto.carrierId = param.getCarrierId();
+        proto.countOfSatelliteAllowedStateChangedEvents =
+                param.getCountOfSatelliteAllowedStateChangedEvents();
+        proto.countOfSuccessfulLocationQueries = param.getCountOfSuccessfulLocationQueries();
+        proto.countOfFailedLocationQueries = param.getCountOfFailedLocationQueries();
+
         mAtomsStorage.addSatelliteControllerStats(proto);
     }
 
@@ -2257,6 +2526,11 @@
         proto.countOfIncomingDatagramFailed = param.getCountOfOutgoingDatagramFailed();
         proto.isDemoMode = param.getIsDemoMode();
         proto.maxNtnSignalStrengthLevel = param.getMaxNtnSignalStrengthLevel();
+        proto.carrierId = param.getCarrierId();
+        proto.countOfSatelliteNotificationDisplayed =
+                param.getCountOfSatelliteNotificationDisplayed();
+        proto.countOfAutoExitDueToScreenOff = param.getCountOfAutoExitDueToScreenOff();
+        proto.countOfAutoExitDueToTnNetwork = param.getCountOfAutoExitDueToTnNetwork();
         mAtomsStorage.addSatelliteSessionStats(proto);
     }
 
@@ -2268,6 +2542,7 @@
         proto.datagramSizeBytes = param.getDatagramSizeBytes();
         proto.datagramTransferTimeMillis = param.getDatagramTransferTimeMillis();
         proto.isDemoMode = param.getIsDemoMode();
+        proto.carrierId = param.getCarrierId();
         mAtomsStorage.addSatelliteIncomingDatagramStats(proto);
     }
 
@@ -2280,6 +2555,7 @@
         proto.datagramSizeBytes = param.getDatagramSizeBytes();
         proto.datagramTransferTimeMillis = param.getDatagramTransferTimeMillis();
         proto.isDemoMode = param.getIsDemoMode();
+        proto.carrierId = param.getCarrierId();
         mAtomsStorage.addSatelliteOutgoingDatagramStats(proto);
     }
 
@@ -2290,6 +2566,7 @@
         proto.provisioningTimeSec = param.getProvisioningTimeSec();
         proto.isProvisionRequest = param.getIsProvisionRequest();
         proto.isCanceled = param.getIsCanceled();
+        proto.carrierId = param.getCarrierId();
         mAtomsStorage.addSatelliteProvisionStats(proto);
     }
 
@@ -2344,6 +2621,8 @@
         proto.satelliteSessionGapMinSec = param.mSatelliteSessionGapMinSec;
         proto.satelliteSessionGapAvgSec = param.mSatelliteSessionGapAvgSec;
         proto.satelliteSessionGapMaxSec = param.mSatelliteSessionGapMaxSec;
+        proto.carrierId = param.getCarrierId();
+        proto.isDeviceEntitled = param.isDeviceEntitled();
         mAtomsStorage.addCarrierRoamingSatelliteControllerStats(proto);
     }
 
@@ -2381,6 +2660,8 @@
         proto.resultCode = param.getResultCode();
         proto.countryCodes = param.getCountryCodes();
         proto.configDataSource = param.getConfigDataSource();
+        proto.carrierId = param.getCarrierId();
+        proto.triggeringEvent = param.getTriggeringEvent();
         mAtomsStorage.addSatelliteAccessControllerStats(proto);
     }
 }
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramController.java b/src/java/com/android/internal/telephony/satellite/DatagramController.java
index 9b483b8..5cd2f99 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramController.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramController.java
@@ -252,6 +252,7 @@
             @NonNull Consumer<Integer> callback) {
         mDatagramDispatcher.sendSatelliteDatagram(subId, datagramType, datagram,
                 needFullScreenPointingUI, callback);
+        mPointingAppController.onSendDatagramRequested(subId, datagramType);
     }
 
     /**
@@ -489,6 +490,16 @@
         }
     }
 
+    /** Return the datagram type indicating that the message to be sent or received.
+     * {@link SatelliteManager.DatagramType}
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public int getDatagramType() {
+        synchronized (mLock) {
+            return mDatagramType;
+        }
+    }
+
     /**
      * This API can be used by only CTS to timeout durations used by DatagramController module.
      *
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
index 40183c1..d1b4471 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
@@ -729,6 +729,7 @@
                         .setDatagramTransferTimeMillis(argument.datagramStartTime > 0
                                 ? (System.currentTimeMillis() - argument.datagramStartTime) : 0)
                         .setIsDemoMode(mIsDemoMode)
+                        .setCarrierId(SatelliteController.getInstance().getSatelliteCarrierId())
                         .build());
         if (resultCode == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
             mControllerMetricsStats.reportOutgoingDatagramSuccessCount(argument.datagramType,
@@ -1059,7 +1060,7 @@
         SatelliteController.getInstance().startPointingUI();
 
         int subId = satellitePhone.getSubId();
-        long messageId = pendingSms.messageId;
+        long messageId = pendingSms.uniqueMessageId;
         plogd("sendSms: subId=" + subId + " messageId:" + messageId);
 
         synchronized (mLock) {
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
index 7a92286..5a89c40 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
@@ -40,6 +40,7 @@
 import android.telephony.DropBoxManagerLoggerBackend;
 import android.telephony.PersistentLogger;
 import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
 import android.telephony.satellite.ISatelliteDatagramCallback;
 import android.telephony.satellite.SatelliteDatagram;
 import android.telephony.satellite.SatelliteManager;
@@ -454,8 +455,7 @@
 
                 if (mIsDemoMode && error == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
                     SatelliteDatagram datagram = mDatagramController.popDemoModeDatagram();
-                    final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(
-                            request.subId, mContext);
+                    final int validSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
                     SatelliteDatagramListenerHandler listenerHandler =
                             mSatelliteDatagramListenerHandlers.get(validSubId);
                     if (listenerHandler != null) {
@@ -517,7 +517,7 @@
             return SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED;
         }
 
-        final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(subId, mContext);
+        final int validSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
         SatelliteDatagramListenerHandler satelliteDatagramListenerHandler =
                 mSatelliteDatagramListenerHandlers.get(validSubId);
         if (satelliteDatagramListenerHandler == null) {
@@ -543,7 +543,7 @@
      */
     public void unregisterForSatelliteDatagram(int subId,
             @NonNull ISatelliteDatagramCallback callback) {
-        final int validSubId = SatelliteServiceUtils.getValidSatelliteSubId(subId, mContext);
+        final int validSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
         SatelliteDatagramListenerHandler handler =
                 mSatelliteDatagramListenerHandlers.get(validSubId);
         if (handler != null) {
@@ -575,7 +575,8 @@
             callback.accept(SatelliteManager.SATELLITE_RESULT_MODEM_BUSY);
             return;
         }
-        pollPendingSatelliteDatagramsInternal(subId, callback);
+        pollPendingSatelliteDatagramsInternal(
+                SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, callback);
     }
 
     private void handleSatelliteConnectedEvent() {
@@ -747,6 +748,7 @@
                         .setDatagramSizeBytes(datagramSizeRoundedBytes)
                         .setDatagramTransferTimeMillis(datagramTransferTime)
                         .setIsDemoMode(mIsDemoMode)
+                        .setCarrierId(SatelliteController.getInstance().getSatelliteCarrierId())
                         .build());
 
         mControllerMetricsStats.reportIncomingDatagramCount(resultCode, mIsDemoMode);
diff --git a/src/java/com/android/internal/telephony/satellite/PointingAppController.java b/src/java/com/android/internal/telephony/satellite/PointingAppController.java
index 06281c7..9606150 100644
--- a/src/java/com/android/internal/telephony/satellite/PointingAppController.java
+++ b/src/java/com/android/internal/telephony/satellite/PointingAppController.java
@@ -34,9 +34,11 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.telephony.DropBoxManagerLoggerBackend;
 import android.telephony.PersistentLogger;
 import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
 import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
 import android.telephony.satellite.PointingInfo;
 import android.telephony.satellite.SatelliteManager;
@@ -189,6 +191,7 @@
         public static final int EVENT_SEND_DATAGRAM_STATE_CHANGED = 2;
         public static final int EVENT_RECEIVE_DATAGRAM_STATE_CHANGED = 3;
         public static final int EVENT_DATAGRAM_TRANSFER_STATE_CHANGED = 4;
+        public static final int EVENT_SEND_DATAGRAM_REQUESTED = 5;
 
         private final ConcurrentHashMap<IBinder, ISatelliteTransmissionUpdateCallback> mListeners;
 
@@ -277,6 +280,24 @@
                     break;
                 }
 
+                case EVENT_SEND_DATAGRAM_REQUESTED: {
+                    logd("Received EVENT_SEND_DATAGRAM_REQUESTED");
+                    int datagramType = (int) msg.obj;
+                    List<IBinder> toBeRemoved = new ArrayList<>();
+                    mListeners.values().forEach(listener -> {
+                        try {
+                            listener.onSendDatagramRequested(datagramType);
+                        } catch (RemoteException e) {
+                            logd("EVENT_SEND_DATAGRAM_REQUESTED RemoteException: " + e);
+                            toBeRemoved.add(listener.asBinder());
+                        }
+                    });
+                    toBeRemoved.forEach(listener -> {
+                        mListeners.remove(listener);
+                    });
+                    break;
+                }
+
                 default:
                     loge("SatelliteTransmissionUpdateHandler unknown event: " + msg.what);
             }
@@ -290,6 +311,7 @@
      */
     public void registerForSatelliteTransmissionUpdates(int subId,
             ISatelliteTransmissionUpdateCallback callback) {
+        subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
         SatelliteTransmissionUpdateHandler handler =
                 mSatelliteTransmissionUpdateHandlers.get(subId);
         if (handler != null) {
@@ -318,6 +340,7 @@
      */
     public void unregisterForSatelliteTransmissionUpdates(int subId, Consumer<Integer> result,
             ISatelliteTransmissionUpdateCallback callback) {
+        subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
         SatelliteTransmissionUpdateHandler handler =
                 mSatelliteTransmissionUpdateHandlers.get(subId);
         if (handler != null) {
@@ -404,7 +427,7 @@
             mLastNeedFullScreenPointingUI = needFullScreenPointingUI;
             mLastIsDemoMode = isDemoMode;
             mLastIsEmergency = isEmergency;
-            mContext.startActivity(launchIntent);
+            mContext.startActivityAsUser(launchIntent, UserHandle.CURRENT);
         } catch (ActivityNotFoundException ex) {
             ploge("startPointingUI: Pointing UI app activity is not found, ex=" + ex);
         }
@@ -426,6 +449,7 @@
             int sendPendingCount, int errorCode) {
         DatagramTransferStateHandlerRequest request = new DatagramTransferStateHandlerRequest(
                 datagramType, datagramTransferState, sendPendingCount, errorCode);
+        subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
         SatelliteTransmissionUpdateHandler handler =
                 mSatelliteTransmissionUpdateHandlers.get(subId);
 
@@ -439,12 +463,32 @@
         }
     }
 
+    /**
+     * This API is used to notify PointingAppController that a send datagram has just been
+     * requested.
+     */
+    public void onSendDatagramRequested(
+            int subId, @SatelliteManager.DatagramType int datagramType) {
+        subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+        SatelliteTransmissionUpdateHandler handler =
+                mSatelliteTransmissionUpdateHandlers.get(subId);
+        if (handler != null) {
+            Message msg = handler.obtainMessage(
+                    SatelliteTransmissionUpdateHandler.EVENT_SEND_DATAGRAM_REQUESTED,
+                    datagramType);
+            msg.sendToTarget();
+        } else {
+            ploge("SatelliteTransmissionUpdateHandler not found for subId: " + subId);
+        }
+    }
+
     public void updateReceiveDatagramTransferState(int subId,
             @SatelliteManager.SatelliteDatagramTransferState int datagramTransferState,
             int receivePendingCount, int errorCode) {
         DatagramTransferStateHandlerRequest request = new DatagramTransferStateHandlerRequest(
                 SatelliteManager.DATAGRAM_TYPE_UNKNOWN, datagramTransferState, receivePendingCount,
                 errorCode);
+        subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
         SatelliteTransmissionUpdateHandler handler =
                 mSatelliteTransmissionUpdateHandlers.get(subId);
 
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteConstants.java b/src/java/com/android/internal/telephony/satellite/SatelliteConstants.java
index 384dfa5..a5afe4a 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteConstants.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteConstants.java
@@ -104,4 +104,22 @@
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AccessControlType {}
+
+    //// Unknown reason.
+    public static final int TRIGGERING_EVENT_UNKNOWN = 0;
+    // Satellite Access Controller has been triggered by an external event.
+    public static final int TRIGGERING_EVENT_EXTERNAL_REQUEST = 1;
+    // Satellite Access Controller has been triggered by an MCC change event.
+    public static final int TRIGGERING_EVENT_MCC_CHANGED = 2;
+    //Satellite Access Controller has been triggered due to the location setting being enabled.
+    public static final int TRIGGERING_EVENT_LOCATION_SETTINGS_ENABLED = 3;
+
+    @IntDef(prefix = {"TRIGGERING_EVENT_"}, value = {
+            TRIGGERING_EVENT_UNKNOWN,
+            TRIGGERING_EVENT_EXTERNAL_REQUEST,
+            TRIGGERING_EVENT_MCC_CHANGED,
+            TRIGGERING_EVENT_LOCATION_SETTINGS_ENABLED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TriggeringEvent {}
 }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteController.java b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
index 36215a4..41a1825 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
 import static android.provider.Settings.ACTION_SATELLITE_SETTING;
 import static android.telephony.CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_MANUAL;
 import static android.telephony.CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_TYPE;
@@ -29,16 +31,17 @@
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL;
-import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ESOS_SUPPORTED_BOOL;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_NIDD_APN_NAME_STRING;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL;
-import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL;
 import static android.telephony.SubscriptionManager.SATELLITE_ATTACH_ENABLED_FOR_CARRIER;
 import static android.telephony.SubscriptionManager.SATELLITE_ENTITLEMENT_STATUS;
 import static android.telephony.SubscriptionManager.isValidSubscriptionId;
+import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
 import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE;
 import static android.telephony.satellite.SatelliteManager.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS;
 import static android.telephony.satellite.SatelliteManager.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911;
@@ -61,6 +64,7 @@
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.StatusBarManager;
 import android.bluetooth.BluetoothAdapter;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -72,6 +76,8 @@
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.hardware.devicestate.DeviceState;
+import android.hardware.devicestate.DeviceStateManager;
 import android.net.Uri;
 import android.net.wifi.WifiManager;
 import android.nfc.NfcAdapter;
@@ -125,6 +131,7 @@
 import android.telephony.satellite.SatelliteSubscriberInfo;
 import android.telephony.satellite.SatelliteSubscriberProvisionStatus;
 import android.telephony.satellite.SatelliteSubscriptionInfo;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
@@ -163,6 +170,7 @@
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.TreeMap;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
@@ -197,7 +205,8 @@
     public static final int TIMEOUT_TYPE_DEMO_POINTING_ALIGNED_DURATION_MILLIS = 2;
     /** This is used by CTS to override demo pointing not aligned duration. */
     public static final int TIMEOUT_TYPE_DEMO_POINTING_NOT_ALIGNED_DURATION_MILLIS = 3;
-
+    /** This is used by CTS to override evaluate esos profiles prioritization duration. */
+    public static final int TIMEOUT_TYPE_EVALUATE_ESOS_PROFILES_PRIORITIZATION_DURATION_MILLIS = 4;
     /** Key used to read/write OEM-enabled satellite provision status in shared preferences. */
     private static final String OEM_ENABLED_SATELLITE_PROVISION_STATUS_KEY =
             "oem_enabled_satellite_provision_status_key";
@@ -205,6 +214,10 @@
     public static final long DEFAULT_CARRIER_EMERGENCY_CALL_WAIT_FOR_CONNECTION_TIMEOUT_MILLIS =
             TimeUnit.SECONDS.toMillis(30);
 
+    /** Sets report entitled metrics cool down to 23 hours to help enforcing privacy requirement.*/
+    private static final long WAIT_FOR_REPORT_ENTITLED_MERTICS_TIMEOUT_MILLIS =
+            TimeUnit.HOURS.toMillis(23);
+
     /** Message codes used in handleMessage() */
     //TODO: Move the Commands and events related to position updates to PointingAppController
     private static final int CMD_START_SATELLITE_TRANSMISSION_UPDATES = 1;
@@ -255,6 +268,8 @@
     private static final int EVENT_UPDATE_SATELLITE_ENABLE_ATTRIBUTES_DONE = 51;
     protected static final int
             EVENT_WAIT_FOR_UPDATE_SATELLITE_ENABLE_ATTRIBUTES_RESPONSE_TIMED_OUT = 52;
+    private static final int EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT = 53;
+    protected static final int EVENT_SATELLITE_REGISTRATION_FAILURE = 54;
 
     @NonNull private static SatelliteController sInstance;
     @NonNull private final Context mContext;
@@ -335,6 +350,8 @@
             new AtomicBoolean(false);
     private final AtomicBoolean mRegisteredForSatelliteSupportedStateChanged =
             new AtomicBoolean(false);
+    private final AtomicBoolean mRegisteredForSatelliteRegistrationFailure =
+            new AtomicBoolean(false);
     /**
      * Map key: subId, value: callback to get error code of the provision request.
      */
@@ -363,6 +380,12 @@
      */
     private final ConcurrentHashMap<IBinder, ISatelliteSupportedStateCallback>
             mSatelliteSupportedStateChangedListeners = new ConcurrentHashMap<>();
+
+    /**
+     * Map key: binder of the callback, value: callback to satellite registration failure
+     */
+    private final ConcurrentHashMap<IBinder, ISatelliteModemStateCallback>
+            mSatelliteRegistrationFailureListeners = new ConcurrentHashMap<>();
     private final Object mIsSatelliteSupportedLock = new Object();
     @GuardedBy("mIsSatelliteSupportedLock")
     private Boolean mIsSatelliteSupported = null;
@@ -504,7 +527,10 @@
     // key : priority, low value is high, value : List<SubscriptionInfo>
     @GuardedBy("mSatelliteTokenProvisionedLock")
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    protected Map<Integer, List<SubscriptionInfo>> mSubsInfoListPerPriority = new HashMap<>();
+    protected TreeMap<Integer, List<SubscriptionInfo>> mSubsInfoListPerPriority = new TreeMap<>();
+    // The ID of the satellite subscription that has highest priority and is provisioned.
+    @GuardedBy("mSatelliteTokenProvisionedLock")
+    private int mSelectedSatelliteSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     // The last ICC ID that framework configured to modem.
     @GuardedBy("mSatelliteTokenProvisionedLock")
     private String mLastConfiguredIccId;
@@ -513,6 +539,7 @@
     private long mWaitTimeForSatelliteEnablingResponse;
     private long mDemoPointingAlignedDurationMillis;
     private long mDemoPointingNotAlignedDurationMillis;
+    private long mEvaluateEsosProfilesPrioritizationDurationMillis;
     private final Object mLock = new Object();
     @GuardedBy("mLock")
     private long mLastEmergencyCallTime;
@@ -546,6 +573,17 @@
     @GuardedBy("mIsWifiConnectedLock")
     private boolean mIsWifiConnected = false;
     private boolean mHasSentBroadcast = false;
+    // For satellite CTS test which to configure intent component with the necessary values.
+    private boolean mChangeIntentComponent = false;
+    private String mConfigSatelliteGatewayServicePackage = "";
+    private String mConfigSatelliteCarrierRoamingEsosProvisionedClass = "";
+
+    private boolean mIsNotificationShowing = false;
+    private static final String OPEN_MESSAGE_BUTTON = "open_message_button";
+    private static final String HOW_IT_WORKS_BUTTON = "how_it_works_button";
+    private static final String ACTION_NOTIFICATION_CLICK = "action_notification_click";
+    private static final String ACTION_NOTIFICATION_DISMISS = "action_notification_dismiss";
+    private AtomicBoolean mOverrideNtnEligibility;
     private BroadcastReceiver
             mDefaultSmsSubscriptionChangedBroadcastReceiver = new BroadcastReceiver() {
                 @Override
@@ -558,6 +596,10 @@
                 }
             };
 
+    // List of device states returned from DeviceStateManager to determine if running on a foldable
+    // device.
+    private List<DeviceState> mDeviceStates = new ArrayList();
+
     /**
      * @return The singleton instance of SatelliteController.
      */
@@ -692,8 +734,10 @@
                 getDemoPointingNotAlignedDurationMillisFromResources();
         mSatelliteEmergencyModeDurationMillis =
                 getSatelliteEmergencyModeDurationFromOverlayConfig(context);
+        mEvaluateEsosProfilesPrioritizationDurationMillis =
+                getEvaluateEsosProfilesPrioritizationDurationMillis();
         sendMessageDelayed(obtainMessage(CMD_EVALUATE_ESOS_PROFILES_PRIORITIZATION),
-                /* delayMillis= */ TimeUnit.MINUTES.toMillis(1));
+                mEvaluateEsosProfilesPrioritizationDurationMillis);
 
         SubscriptionManager subscriptionManager = mContext.getSystemService(
                 SubscriptionManager.class);
@@ -703,6 +747,10 @@
                     new HandlerExecutor(new Handler(looper)), mSubscriptionsChangedListener);
         }
         registerDefaultSmsSubscriptionChangedBroadcastReceiver();
+        updateSatelliteProvisionedStatePerSubscriberId();
+        if (android.hardware.devicestate.feature.flags.Flags.deviceStatePropertyMigration()) {
+            mDeviceStates = getSupportedDeviceStates();
+        }
     }
 
     class SatelliteSubscriptionsChangedListener
@@ -1197,7 +1245,8 @@
                             .setSatelliteTechnology(getSupportedNtnRadioTechnology())
                             .setInitializationProcessingTime(
                                     System.currentTimeMillis() - mSessionProcessingTimeStamp)
-                            .setIsDemoMode(mIsDemoModeEnabled);
+                            .setIsDemoMode(mIsDemoModeEnabled)
+                            .setCarrierId(getSatelliteCarrierId());
                     mSessionProcessingTimeStamp = 0;
 
                     if (error == SATELLITE_RESULT_SUCCESS) {
@@ -1225,28 +1274,39 @@
                 break;
             }
 
-            case EVENT_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMED_OUT:
+            case EVENT_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMED_OUT: {
                 handleEventWaitForSatelliteEnablingResponseTimedOut(
                         (RequestSatelliteEnabledArgument) msg.obj);
                 break;
+            }
 
             case CMD_UPDATE_SATELLITE_ENABLE_ATTRIBUTES: {
                 request = (SatelliteControllerHandlerRequest) msg.obj;
                 RequestSatelliteEnabledArgument argument =
                         (RequestSatelliteEnabledArgument) request.argument;
 
+                if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+                    plogd("UpdateEnableAttributes: carrierRoamingNbIotNtn flag is disabled");
+                    sendErrorAndReportSessionMetrics(
+                            SatelliteManager.SATELLITE_RESULT_INVALID_ARGUMENTS, argument.callback);
+                    synchronized (mSatelliteEnabledRequestLock) {
+                        mSatelliteEnableAttributesUpdateRequest = null;
+                    }
+                    break;
+                }
+
                 synchronized (mSatelliteEnabledRequestLock) {
                     if (mSatelliteEnabledRequest != null) {
                         plogd("UpdateEnableAttributes: Satellite is being enabled. Need to "
                                 + "wait until enable complete before updating attributes");
-                        return;
+                        break;
                     }
                     if (isSatelliteBeingDisabled()) {
                         plogd("UpdateEnableAttributes: Satellite is being disabled. Aborting the "
                                 + "enable attributes update request");
                         mSatelliteEnableAttributesUpdateRequest = null;
                         argument.callback.accept(SATELLITE_RESULT_REQUEST_ABORTED);
-                        return;
+                        break;
                     }
                 }
                 onCompleted = obtainMessage(EVENT_UPDATE_SATELLITE_ENABLE_ATTRIBUTES_DONE, request);
@@ -1677,35 +1737,30 @@
                 RequestProvisionSatelliteArgument argument =
                         (RequestProvisionSatelliteArgument) request.argument;
                 onCompleted = obtainMessage(EVENT_UPDATE_PROVISION_SATELLITE_TOKEN_DONE, request);
-                // only pass to index 0.
-                // TODO: Select the subscription with highest priority and set it to mSatelliteSubId
-                int subId = -1;
-                synchronized (mSatelliteTokenProvisionedLock) {
-                    subId = mSubscriberIdPerSub.getOrDefault(
-                            argument.mSatelliteSubscriberInfoList.get(0).getSubscriberId(), -1);
-                }
+                boolean provisionChanged = updateSatelliteSubscriptionProvisionState(
+                        argument.mSatelliteSubscriberInfoList, argument.mProvisioned);
+                int subId = getSelectedSatelliteSubId();
                 setSatellitePhone(subId);
                 String iccId = mSubscriptionManagerService.getSubscriptionInfo(subId).getIccId();
                 argument.setIccId(iccId);
-                boolean sendResponse = false;
                 synchronized (mSatelliteTokenProvisionedLock) {
                     if (!iccId.equals(mLastConfiguredIccId)) {
                         logd("updateSatelliteSubscription subId=" + subId + ", iccId=" + iccId
                                 + " to modem");
                         mSatelliteModemInterface.updateSatelliteSubscription(iccId, onCompleted);
-                    } else {
-                        sendResponse = true;
                     }
                 }
-                handleEventSatelliteSubscriptionProvisionStateChanged(
-                        argument.mSatelliteSubscriberInfoList, true);
-                if (sendResponse) {
-                    // The response is sent immediately because the ICCID has already been
-                    // delivered to the modem.
-                    Bundle bundle = new Bundle();
-                    bundle.putBoolean(SatelliteManager.KEY_PROVISION_SATELLITE_TOKENS, true);
-                    argument.mResult.send(SATELLITE_RESULT_SUCCESS, bundle);
+                if (provisionChanged) {
+                    handleEventSatelliteSubscriptionProvisionStateChanged();
                 }
+
+                // The response is sent immediately because the ICCID has already been
+                // delivered to the modem.
+                Bundle bundle = new Bundle();
+                bundle.putBoolean(
+                        argument.mProvisioned ? SatelliteManager.KEY_PROVISION_SATELLITE_TOKENS
+                                : SatelliteManager.KEY_DEPROVISION_SATELLITE_TOKENS, true);
+                argument.mResult.send(SATELLITE_RESULT_SUCCESS, bundle);
                 break;
             }
 
@@ -1722,10 +1777,6 @@
                     }
                 }
                 logd("updateSatelliteSubscription result=" + error);
-                Bundle bundle = new Bundle();
-                bundle.putBoolean(SatelliteManager.KEY_PROVISION_SATELLITE_TOKENS,
-                        error == SATELLITE_RESULT_SUCCESS);
-                argument.mResult.send(error, bundle);
                 break;
             }
 
@@ -1749,8 +1800,32 @@
                 synchronized (mIsRadioOnLock) {
                     mRadioOffRequested = false;
                 }
+                break;
             }
 
+            case EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT: {
+                // TODO: b/366329504 report carrier roaming metrics for multiple subscription IDs.
+                synchronized (mSupportedSatelliteServicesLock) {
+                    int defaultSubId = mSubscriptionManagerService.getDefaultSubId();
+                    boolean isEntitled = mSatelliteEntitlementStatusPerCarrier.get(defaultSubId,
+                            false);
+                    mCarrierRoamingSatelliteControllerStats.reportIsDeviceEntitled(isEntitled);
+                }
+                sendMessageDelayed(obtainMessage(
+                                EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT),
+                        WAIT_FOR_REPORT_ENTITLED_MERTICS_TIMEOUT_MILLIS);
+                break;
+            }
+
+            case EVENT_SATELLITE_REGISTRATION_FAILURE:
+                ar = (AsyncResult) msg.obj;
+                if (ar.result == null) {
+                    loge("EVENT_SATELLITE_REGISTRATION_FAILURE: result is null");
+                } else {
+                    handleEventSatelliteRegistrationFailure((int) ar.result);
+                }
+                break;
+
             default:
                 Log.w(TAG, "SatelliteControllerHandler: unexpected message code: " +
                         msg.what);
@@ -1764,11 +1839,13 @@
         public ResultReceiver mResult;
         public long mRequestId;
         public String mIccId;
+        public boolean mProvisioned;
 
         RequestProvisionSatelliteArgument(List<SatelliteSubscriberInfo> satelliteSubscriberInfoList,
-                ResultReceiver result) {
+                ResultReceiver result, boolean provisioned) {
             this.mSatelliteSubscriberInfoList = satelliteSubscriberInfoList;
             this.mResult = result;
+            this.mProvisioned = provisioned;
             this.mRequestId = sNextSatelliteEnableRequestId.getAndUpdate(
                     n -> ((n + 1) % Long.MAX_VALUE));
         }
@@ -1916,6 +1993,12 @@
                      * carriers want to disable satellite for prioritizing emergency calls. Thus,
                      * we need to push the disable request to modem while enable is in progress.
                      */
+                    if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+                        plogd("requestSatelliteEnabled: carrierRoamingNbIotNtn flag is disabled");
+                        sendErrorAndReportSessionMetrics(
+                                SatelliteManager.SATELLITE_RESULT_ENABLE_IN_PROGRESS, result);
+                        return;
+                    }
                     mSatelliteDisabledRequest = request;
                 }
             }
@@ -2024,13 +2107,15 @@
      *
      * @return {@code true} if the satellite modem is enabled and {@code false} otherwise.
      */
-    public boolean isSatelliteEnabled() {
+    private boolean isSatelliteEnabled() {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
             plogd("isSatelliteEnabled: oemEnabledSatelliteFlag is disabled");
             return false;
         }
-        if (mIsSatelliteEnabled == null) return false;
-        return mIsSatelliteEnabled;
+        synchronized (mIsSatelliteEnabledLock) {
+            if (mIsSatelliteEnabled == null) return false;
+            return mIsSatelliteEnabled;
+        }
     }
 
     /**
@@ -2038,7 +2123,7 @@
      *
      * @return {@code true} if the satellite modem is being enabled and {@code false} otherwise.
      */
-    public boolean isSatelliteBeingEnabled() {
+    private boolean isSatelliteBeingEnabled() {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
             plogd("isSatelliteBeingEnabled: oemEnabledSatelliteFlag is disabled");
             return false;
@@ -2051,6 +2136,17 @@
     }
 
     /**
+     * Get whether the satellite modem is enabled or being enabled.
+     * This will return the cached value instead of querying the satellite modem.
+     *
+     * @return {@code true} if the satellite modem is enabled or being enabled, {@code false}
+     * otherwise.
+     */
+    public boolean isSatelliteEnabledOrBeingEnabled() {
+        return isSatelliteEnabled() || isSatelliteBeingEnabled();
+    }
+
+    /**
      * Get whether satellite modem is being disabled.
      *
      * @return {@code true} if the satellite modem is being disabled and {@code false} otherwise.
@@ -2201,12 +2297,6 @@
     public void stopSatelliteTransmissionUpdates(@NonNull IIntegerConsumer errorCallback,
             @NonNull ISatelliteTransmissionUpdateCallback callback) {
         Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(errorCallback::accept);
-        int error = evaluateOemSatelliteRequestAllowed(true);
-        if (error != SATELLITE_RESULT_SUCCESS) {
-            result.accept(error);
-            return;
-        }
-
         mPointingAppController.unregisterForSatelliteTransmissionUpdates(
                 getHighestPrioritySubscrption(), result, callback);
 
@@ -2381,6 +2471,10 @@
             plogd("registerForSatelliteModemStateChanged: oemEnabledSatelliteFlag is disabled");
             return SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED;
         }
+        if (mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("registerForSatelliteModemStateChanged: add RegistrationFailure Listeners");
+            mSatelliteRegistrationFailureListeners.put(callback.asBinder(), callback);
+        }
         if (mSatelliteSessionController != null) {
             mSatelliteSessionController.registerForSatelliteModemStateChanged(callback);
         } else {
@@ -2410,6 +2504,10 @@
             ploge("unregisterForModemStateChanged: mSatelliteSessionController"
                     + " is not initialized yet");
         }
+        if (mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("unregisterForModemStateChanged: remove RegistrationFailure Listeners");
+            mSatelliteRegistrationFailureListeners.remove(callback.asBinder());
+        }
     }
 
     /**
@@ -2534,7 +2632,7 @@
     }
 
     /**
-     * Inform whether the device is aligned with satellite for demo mode.
+     * Inform whether the device is aligned with the satellite in both real and demo mode.
      *
      * @param isAligned {@true} means device is aligned with the satellite, otherwise {@false}.
      */
@@ -2702,7 +2800,7 @@
             @NonNull INtnSignalStrengthCallback callback) throws RemoteException {
         if (DBG) plogd("registerForNtnSignalStrengthChanged()");
 
-        int error = evaluateOemSatelliteRequestAllowed(true);
+        int error = evaluateOemSatelliteRequestAllowed(false);
         if (error == SATELLITE_RESULT_SUCCESS) {
             mNtnSignalStrengthChangedListeners.put(callback.asBinder(), callback);
             synchronized (mNtnSignalsStrengthLock) {
@@ -2730,11 +2828,7 @@
     public void unregisterForNtnSignalStrengthChanged(
             @NonNull INtnSignalStrengthCallback callback) {
         if (DBG) plogd("unregisterForNtnSignalStrengthChanged()");
-
-        int error = evaluateOemSatelliteRequestAllowed(true);
-        if (error == SATELLITE_RESULT_SUCCESS) {
-            mNtnSignalStrengthChangedListeners.remove(callback.asBinder());
-        }
+        mNtnSignalStrengthChangedListeners.remove(callback.asBinder());
     }
 
     /**
@@ -2748,7 +2842,7 @@
             @NonNull ISatelliteCapabilitiesCallback callback) {
         if (DBG) plogd("registerForCapabilitiesChanged()");
 
-        int error = evaluateOemSatelliteRequestAllowed(true);
+        int error = evaluateOemSatelliteRequestAllowed(false);
         if (error != SATELLITE_RESULT_SUCCESS) return error;
 
         mSatelliteCapabilitiesChangedListeners.put(callback.asBinder(), callback);
@@ -2766,11 +2860,7 @@
     public void unregisterForCapabilitiesChanged(
             @NonNull ISatelliteCapabilitiesCallback callback) {
         if (DBG) plogd("unregisterForCapabilitiesChanged()");
-
-        int error = evaluateOemSatelliteRequestAllowed(true);
-        if (error == SATELLITE_RESULT_SUCCESS) {
-            mSatelliteCapabilitiesChangedListeners.remove(callback.asBinder());
-        }
+        mSatelliteCapabilitiesChangedListeners.remove(callback.asBinder());
     }
 
     /**
@@ -2866,6 +2956,21 @@
     }
 
     /**
+     * This API can be used by only CTS to control ingoring cellular service state event.
+     *
+     * @param enabled Whether to enable boolean config.
+     * @return {@code true} if the value is set successfully, {@code false} otherwise.
+     */
+    public boolean setSatelliteIgnoreCellularServiceState(boolean enabled) {
+        plogd("setSatelliteIgnoreCellularServiceState - " + enabled);
+        if (mSatelliteSessionController == null) {
+            ploge("setSatelliteIgnoreCellularServiceState is not initialized yet");
+            return false;
+        }
+        return mSatelliteSessionController.setSatelliteIgnoreCellularServiceState(enabled);
+    }
+
+    /**
      * This API can be used by only CTS to override timeout durations used by DatagramController
      * module.
      *
@@ -2944,6 +3049,14 @@
             } else {
                 mDemoPointingNotAlignedDurationMillis = timeoutMillis;
             }
+        } else if (timeoutType
+                == TIMEOUT_TYPE_EVALUATE_ESOS_PROFILES_PRIORITIZATION_DURATION_MILLIS) {
+            if (reset) {
+                mEvaluateEsosProfilesPrioritizationDurationMillis =
+                        getEvaluateEsosProfilesPrioritizationDurationMillis();
+            } else {
+                mEvaluateEsosProfilesPrioritizationDurationMillis = timeoutMillis;
+            }
         } else {
             plogw("Invalid timeoutType=" + timeoutType);
             return false;
@@ -3433,7 +3546,34 @@
             plogd("isInCarrierRoamingNbIotNtn: not carrier roaming ntn eligible.");
             return false;
         }
+        plogd("isInCarrierRoamingNbIotNtn: carrier roaming ntn eligible.");
+        return true;
+    }
 
+    /**
+     * @return {@code true} if phone is in carrier roaming nb iot ntn mode,
+     * else {@return false}
+     */
+    public boolean isInCarrierRoamingNbIotNtn(@NonNull Phone phone) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("isInCarrierRoamingNbIotNtn: carrier roaming nb iot ntn "
+                    + "feature flag is disabled");
+            return false;
+        }
+
+        if (!isSatelliteEnabled()) {
+            plogd("iisInCarrierRoamingNbIotNtn: satellite is disabled");
+            return false;
+        }
+
+        if (!isCarrierRoamingNtnEligible(phone)) {
+            plogd("isInCarrierRoamingNbIotNtn: phone associated with subId "
+                      + phone.getSubId()
+                      + " is not carrier roaming ntn eligible.");
+            return false;
+        }
+        plogd("isInCarrierRoamingNbIotNtn: carrier roaming ntn eligible for phone"
+                  + " associated with subId " + phone.getSubId());
         return true;
     }
 
@@ -3577,6 +3717,13 @@
             if (mSatelliteEntitlementStatusPerCarrier.get(subId, false) != entitlementEnabled) {
                 logd("update the carrier satellite enabled to " + entitlementEnabled);
                 mSatelliteEntitlementStatusPerCarrier.put(subId, entitlementEnabled);
+                mCarrierRoamingSatelliteControllerStats.reportIsDeviceEntitled(entitlementEnabled);
+                if (hasMessages(EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT)) {
+                    removeMessages(EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT);
+                    sendMessageDelayed(obtainMessage(
+                                    EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT),
+                            WAIT_FOR_REPORT_ENTITLED_MERTICS_TIMEOUT_MILLIS);
+                }
                 try {
                     mSubscriptionManagerService.setSubscriptionProperty(subId,
                             SATELLITE_ENTITLEMENT_STATUS, entitlementEnabled ? "1" : "0");
@@ -3660,6 +3807,7 @@
             mProvisionMetricsStats
                     .setResultCode(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE)
                     .setIsProvisionRequest(true)
+                    .setCarrierId(getSatelliteCarrierId())
                     .reportProvisionMetrics();
             mControllerMetricsStats.reportProvisionCount(
                     SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
@@ -3678,6 +3826,7 @@
         }
         mProvisionMetricsStats.setResultCode(result)
                 .setIsProvisionRequest(true)
+                .setCarrierId(getSatelliteCarrierId())
                 .reportProvisionMetrics();
         mControllerMetricsStats.reportProvisionCount(result);
     }
@@ -3707,6 +3856,7 @@
         }
         mProvisionMetricsStats.setResultCode(result)
                 .setIsProvisionRequest(false)
+                .setCarrierId(getSatelliteCarrierId())
                 .reportProvisionMetrics();
         mControllerMetricsStats.reportDeprovisionCount(result);
     }
@@ -3837,6 +3987,7 @@
             registerForSatelliteModemStateChanged();
             registerForNtnSignalStrengthChanged();
             registerForCapabilitiesChanged();
+            registerForSatelliteRegistrationFailure();
 
             requestIsSatelliteProvisioned(
                     new ResultReceiver(this) {
@@ -3864,6 +4015,7 @@
                     });
         }
         registerForSatelliteSupportedStateChanged();
+        selectBindingSatelliteSubscription();
     }
 
     private void updateSatelliteEnabledState(boolean enabled, String caller) {
@@ -3941,6 +4093,16 @@
         }
     }
 
+    private void registerForSatelliteRegistrationFailure() {
+        if (mFeatureFlags.carrierRoamingNbIotNtn()) {
+            if (!mRegisteredForSatelliteRegistrationFailure.get()) {
+                mSatelliteModemInterface.registerForSatelliteRegistrationFailure(this,
+                        EVENT_SATELLITE_REGISTRATION_FAILURE, null);
+                mRegisteredForSatelliteRegistrationFailure.set(true);
+            }
+        }
+    }
+
     private void handleEventSatelliteProvisionStateChanged(boolean provisioned) {
         plogd("handleSatelliteProvisionStateChangedEvent: provisioned=" + provisioned);
 
@@ -3963,10 +4125,10 @@
         });
     }
 
-    private void handleEventSatelliteSubscriptionProvisionStateChanged(
-            List<SatelliteSubscriberInfo> newList, boolean provisioned) {
-        logd("handleEventSatelliteSubscriptionProvisionStateChanged: newList=" + newList
-                + " , provisioned=" + provisioned);
+    private boolean updateSatelliteSubscriptionProvisionState(List<SatelliteSubscriberInfo> newList,
+            boolean provisioned) {
+        logd("updateSatelliteSubscriptionProvisionState: List=" + newList + " , provisioned="
+                + provisioned);
         boolean provisionChanged = false;
         synchronized (mSatelliteTokenProvisionedLock) {
             for (SatelliteSubscriberInfo subscriberInfo : newList) {
@@ -3976,13 +4138,23 @@
                 }
                 provisionChanged = true;
                 mProvisionedSubscriberId.put(subscriberInfo.getSubscriberId(), provisioned);
+                int subId = mSubscriberIdPerSub.getOrDefault(subscriberInfo.getSubscriberId(),
+                        SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+                try {
+                    mSubscriptionManagerService.setIsSatelliteProvisionedForNonIpDatagram(subId,
+                            provisioned);
+                    plogd("updateSatelliteSubscriptionProvisionState: set Provision state to db "
+                            + "subId=" + subId);
+                } catch (IllegalArgumentException | SecurityException ex) {
+                    ploge("setIsSatelliteProvisionedForNonIpDatagram: subId=" + subId + ", ex="
+                            + ex);
+                }
             }
         }
-        if (!provisionChanged) {
-            logd("handleEventSatelliteSubscriptionProvisionStateChanged: provision state nothing "
-                    + "changed.");
-            return;
-        }
+        return provisionChanged;
+    }
+
+    private void handleEventSatelliteSubscriptionProvisionStateChanged() {
         List<SatelliteSubscriberProvisionStatus> informList =
                 getPrioritizedSatelliteSubscriberProvisionStatusList();
         plogd("handleEventSatelliteSubscriptionProvisionStateChanged: " + informList);
@@ -3993,6 +4165,8 @@
                     && mProvisionedSubscriberId.containsValue(Boolean.TRUE);
             mControllerMetricsStats.setIsProvisioned(isProvisioned);
         }
+        selectBindingSatelliteSubscription();
+        handleStateChangedForCarrierRoamingNtnEligibility();
     }
 
     private void notifySatelliteSubscriptionProvisionStateChanged(
@@ -4027,7 +4201,7 @@
                 mWaitingForSatelliteModemOff = false;
             }
         } else {
-            if (isSatelliteEnabled() || isSatelliteBeingEnabled() || isSatelliteBeingDisabled()) {
+            if (isSatelliteEnabledOrBeingEnabled() || isSatelliteBeingDisabled()) {
                 notifyModemStateChangedToSessionController(state);
             } else {
                 // Telephony framework and modem are out of sync. We need to disable modem
@@ -4177,7 +4351,8 @@
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     protected void setSettingsKeyToAllowDeviceRotation(int val) {
         // Only allows on a foldable device type.
-        if (!isFoldable(mContext)) {
+        if (!isFoldable(mContext, mDeviceStates)) {
+            logd("setSettingsKeyToAllowDeviceRotation(" + val + "), device was not a foldable");
             return;
         }
 
@@ -4218,10 +4393,19 @@
      * If the device type is foldable.
      *
      * @param context context
+     * @param deviceStates list of {@link DeviceState}s provided from {@link DeviceStateManager}
      * @return {@code true} if device type is foldable. {@code false} for otherwise.
      */
-    private boolean isFoldable(Context context) {
-        return context.getResources().getIntArray(R.array.config_foldedDeviceStates).length > 0;
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public boolean isFoldable(Context context, List<DeviceState> deviceStates) {
+        if (android.hardware.devicestate.feature.flags.Flags.deviceStatePropertyMigration()) {
+            return deviceStates.stream().anyMatch(deviceState -> deviceState.hasProperty(
+                    PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY)
+                    || deviceState.hasProperty(
+                    PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY));
+        } else {
+            return context.getResources().getIntArray(R.array.config_foldedDeviceStates).length > 0;
+        }
     }
 
     /**
@@ -4354,6 +4538,7 @@
             updateSatelliteEnabledState(
                     false, "moveSatelliteToOffStateAndCleanUpResources");
         }
+        selectBindingSatelliteSubscription();
     }
 
     private void setDemoModeEnabled(boolean enabled) {
@@ -4531,25 +4716,29 @@
     @NonNull private PersistableBundle getConfigForSubId(int subId) {
         PersistableBundle config = null;
         if (mCarrierConfigManager != null) {
-            config = mCarrierConfigManager.getConfigForSubId(subId,
-                    KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
-                    KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
-                    KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL,
-                    KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT,
-                    KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL,
-                    KEY_CARRIER_ROAMING_SATELLITE_DEFAULT_SERVICES_INT_ARRAY,
-                    KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL,
-                    KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT,
-                    KEY_SATELLITE_ESOS_SUPPORTED_BOOL,
-                    KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL,
-                    KEY_SATELLITE_NIDD_APN_NAME_STRING,
-                    KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT,
-                    KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT,
-                    KEY_CARRIER_ROAMING_NTN_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_INT,
-                    KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT,
-                    KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT,
-                    KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT
-            );
+            try {
+                config = mCarrierConfigManager.getConfigForSubId(subId,
+                        KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
+                        KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
+                        KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL,
+                        KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT,
+                        KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL,
+                        KEY_CARRIER_ROAMING_SATELLITE_DEFAULT_SERVICES_INT_ARRAY,
+                        KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL,
+                        KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT,
+                        KEY_SATELLITE_ESOS_SUPPORTED_BOOL,
+                        KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL,
+                        KEY_SATELLITE_NIDD_APN_NAME_STRING,
+                        KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT,
+                        KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT,
+                        KEY_CARRIER_ROAMING_NTN_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_INT,
+                        KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT,
+                        KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT,
+                        KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT
+                );
+            } catch (Exception e) {
+                logw("getConfigForSubId: " + e);
+            }
         }
         if (config == null || config.isEmpty()) {
             config = CarrierConfigManager.getDefaultConfig();
@@ -4568,6 +4757,7 @@
 
         updateCarrierConfig(subId);
         updateSatelliteESOSSupported(subId);
+        updateSatelliteProvisionedStatePerSubscriberId();
         updateEntitlementPlmnListPerCarrier(subId);
         updateSupportedSatelliteServicesForActiveSubscriptions();
         processNewCarrierConfigData(subId);
@@ -4660,6 +4850,34 @@
         }
     }
 
+    /** If the provision state per subscriberId for the cached is not exist, check the database for
+     * the corresponding value and use it. */
+    private void updateSatelliteProvisionedStatePerSubscriberId() {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            return;
+        }
+
+        List<SubscriptionInfo> allSubInfos = mSubscriptionManagerService.getAllSubInfoList(
+                mContext.getOpPackageName(), mContext.getAttributionTag());
+        for (SubscriptionInfo info : allSubInfos) {
+            int subId = info.getSubscriptionId();
+            Pair<String, Integer> subscriberIdPair = getSubscriberIdAndType(
+                    mSubscriptionManagerService.getSubscriptionInfo(subId));
+            String subscriberId = subscriberIdPair.first;
+            synchronized (mSatelliteTokenProvisionedLock) {
+                if (mProvisionedSubscriberId.get(subscriberId) == null) {
+                    boolean Provisioned = mSubscriptionManagerService
+                            .isSatelliteProvisionedForNonIpDatagram(subId);
+                    if (Provisioned) {
+                        mProvisionedSubscriberId.put(subscriberId, true);
+                        logd("updateSatelliteProvisionStatePerSubscriberId: " + subscriberId
+                                + " set true");
+                    }
+                }
+            }
+        }
+    }
+
     @NonNull
     private String[] readStringArrayFromOverlayConfig(@ArrayRes int id) {
         String[] strArray = null;
@@ -4699,6 +4917,17 @@
         return getConfigForSubId(subId).getBoolean(KEY_SATELLITE_ESOS_SUPPORTED_BOOL);
     }
 
+    /**
+     * Return whether the device allows to turn off satellite session for emergency call.
+     *
+     * @param subId Associated subscription ID
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean turnOffSatelliteSessionForEmergencyCall(int subId) {
+        return getConfigForSubId(subId).getBoolean(
+                KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL);
+    }
+
     private int getCarrierRoamingNtnConnectType(int subId) {
         return getConfigForSubId(subId).getInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT);
     }
@@ -4815,6 +5044,13 @@
                 }
                 boolean result = entitlementStatus.equals("1");
                 mSatelliteEntitlementStatusPerCarrier.put(subId, result);
+                mCarrierRoamingSatelliteControllerStats.reportIsDeviceEntitled(result);
+                if (hasMessages(EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT)) {
+                    removeMessages(EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT);
+                    sendMessageDelayed(obtainMessage(
+                                    EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT),
+                            WAIT_FOR_REPORT_ENTITLED_MERTICS_TIMEOUT_MILLIS);
+                }
             }
 
             if (!mSatelliteEntitlementStatusPerCarrier.get(subId, false)) {
@@ -4988,6 +5224,7 @@
         mSessionMetricsStats.setInitializationResult(error)
                 .setSatelliteTechnology(getSupportedNtnRadioTechnology())
                 .setIsDemoMode(mIsDemoModeEnabled)
+                .setCarrierId(getSatelliteCarrierId())
                 .reportSessionMetrics();
         mSessionStartTimeStamp = 0;
         mSessionProcessingTimeStamp = 0;
@@ -5011,14 +5248,18 @@
         for (Phone phone : PhoneFactory.getPhones()) {
             int subId = phone.getSubId();
             ServiceState serviceState = phone.getServiceState();
-            if (serviceState == null) {
+            if (serviceState == null || subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
                 continue;
             }
 
             synchronized (mSatelliteConnectedLock) {
                 CarrierRoamingSatelliteSessionStats sessionStats =
                         mCarrierRoamingSatelliteSessionStatsMap.get(subId);
-
+                if (DEBUG) {
+                    plogd("handleServiceStateForSatelliteConnectionViaCarrier : SubId = " + subId
+                            + "  isUsingNonTerrestrialNetwork = "
+                            + serviceState.isUsingNonTerrestrialNetwork());
+                }
                 if (serviceState.isUsingNonTerrestrialNetwork()) {
                     if (sessionStats != null) {
                         sessionStats.onSignalStrength(phone);
@@ -5070,7 +5311,6 @@
 
     private void updateLastNotifiedNtnModeAndNotify(@Nullable Phone phone) {
         if (!mFeatureFlags.carrierEnabledSatelliteFlag()) return;
-
         if (phone == null) {
             return;
         }
@@ -5085,6 +5325,9 @@
                 mLastNotifiedNtnMode.put(subId, currNtnMode);
                 phone.notifyCarrierRoamingNtnModeChanged(currNtnMode);
                 logCarrierRoamingSatelliteSessionStats(phone, lastNotifiedNtnMode, currNtnMode);
+                if(mIsNotificationShowing && !currNtnMode) {
+                    dismissSatelliteNotification();
+                }
             }
         }
     }
@@ -5181,6 +5424,11 @@
             return;
         }
 
+        if (mOverrideNtnEligibility != null) {
+            mSatellitePhone.notifyCarrierRoamingNtnEligibleStateChanged(currentNtnEligibility);
+            return;
+        }
+
         synchronized (mSatellitePhoneLock) {
             if (mSatellitePhone == null) {
                 ploge("notifyNtnEligibility: mSatellitePhone is null");
@@ -5438,6 +5686,7 @@
                         .setInitializationProcessingTime(
                                 System.currentTimeMillis() - mSessionProcessingTimeStamp)
                         .setIsDemoMode(mIsDemoModeEnabled)
+                        .setCarrierId(getSatelliteCarrierId())
                         .reportSessionMetrics();
             } else {
                 resetSatelliteDisabledRequest();
@@ -5462,7 +5711,7 @@
             return;
         }
 
-        if (!isSatelliteEnabled()) {
+        if (!isSatelliteEnabledOrBeingEnabled()) {
             plogd("handleCmdUpdateNtnSignalStrengthReporting: ignore request, satellite is "
                     + "disabled");
             return;
@@ -5531,29 +5780,31 @@
         }
 
         Pair<Boolean, Integer> isNtn = isUsingNonTerrestrialNetworkViaCarrier();
+        boolean notificationKeyStatus = mSharedPreferences.getBoolean(
+                SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY, false);
+        if (DEBUG) {
+            logd("determineAutoConnectSystemNotification: isNtn.first = " + isNtn.first
+                    + " IsNotiToShow = " + !notificationKeyStatus + " mIsNotificationShowing = "
+                    + mIsNotificationShowing);
+        }
         if (isNtn.first) {
-            if (mSharedPreferences == null) {
-                try {
-                    mSharedPreferences = mContext.getSharedPreferences(SATELLITE_SHARED_PREF,
-                            Context.MODE_PRIVATE);
-                } catch (Exception e) {
-                    loge("Cannot get default shared preferences: " + e);
-                }
-            }
-            if (mSharedPreferences == null) {
-                loge("determineSystemNotification: Cannot get default shared preferences");
-                return;
-            }
-            if (!mSharedPreferences.getBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY, false)) {
+            if (!notificationKeyStatus) {
                 updateSatelliteSystemNotification(isNtn.second,
                         CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC,
                         /*visible*/ true);
-                mSharedPreferences.edit().putBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY,
-                        true).apply();
             }
+        } else if (mIsNotificationShowing
+                && !isSatelliteConnectedViaCarrierWithinHysteresisTime()) {
+            // Dismiss the notification if it is still displaying.
+            dismissSatelliteNotification();
         }
     }
 
+    private void dismissSatelliteNotification() {
+        mIsNotificationShowing = false;
+        updateSatelliteSystemNotification(-1, -1,/*visible*/ false);
+    }
+
     /**
      * Update the system notification to reflect the current satellite status, that's either already
      * connected OR needs to be manually enabled. The device should only display one notification
@@ -5567,15 +5818,13 @@
      */
     private void updateSatelliteSystemNotification(int subId,
             @CARRIER_ROAMING_NTN_CONNECT_TYPE int carrierRoamingNtnConnectType, boolean visible) {
-        plogd("updateSatelliteSystemNotification subId=" + subId
-                + ", carrierRoamingNtnConnectType=" + SatelliteServiceUtils
-                .carrierRoamingNtnConnectTypeToString(carrierRoamingNtnConnectType)
-                + ", visible=" + visible);
+        plogd("updateSatelliteSystemNotification subId=" + subId + ", carrierRoamingNtnConnectType="
+                + SatelliteServiceUtils.carrierRoamingNtnConnectTypeToString(
+                carrierRoamingNtnConnectType) + ", visible=" + visible);
         final NotificationChannel notificationChannel = new NotificationChannel(
                 NOTIFICATION_CHANNEL_ID,
                 NOTIFICATION_CHANNEL,
-                NotificationManager.IMPORTANCE_DEFAULT
-        );
+                NotificationManager.IMPORTANCE_DEFAULT);
         notificationChannel.setSound(null, null);
         NotificationManager notificationManager = mContext.getSystemService(
                 NotificationManager.class);
@@ -5608,50 +5857,128 @@
                         com.android.internal.R.color.system_notification_accent_color))
                 .setVisibility(Notification.VISIBILITY_PUBLIC);
 
-        // Add action to invoke message application.
-        // getDefaultSmsPackage and getLaunchIntentForPackage are nullable.
-        Optional<Intent> nullableIntent = Optional.ofNullable(
-                        Telephony.Sms.getDefaultSmsPackage(mContext))
-                .flatMap(packageName -> {
-                    PackageManager pm = mContext.getPackageManager();
-                    return Optional.ofNullable(pm.getLaunchIntentForPackage(packageName));
-                });
-        // If nullableIntent is null, create new Intent for most common way to invoke message app.
-        Intent finalIntent = nullableIntent.map(intent -> {
-            // Invoke the home screen of default message application.
-            intent.setAction(Intent.ACTION_MAIN);
-            intent.addCategory(Intent.CATEGORY_HOME);
-            return intent;
-        }).orElseGet(() -> {
-            ploge("showSatelliteSystemNotification: no default sms package name, Invoke "
-                    + "default sms compose window instead");
-            Intent newIntent = new Intent(Intent.ACTION_VIEW);
-            newIntent.setData(Uri.parse("sms:"));
-            return newIntent;
-        });
-
-        PendingIntent pendingIntentOpenMessage = PendingIntent.getActivity(mContext, 0,
-                finalIntent, PendingIntent.FLAG_IMMUTABLE);
+        // Intent for `Open Messages` [Button 1]
+        Intent openMessageIntent = new Intent();
+        openMessageIntent.setAction(OPEN_MESSAGE_BUTTON);
+        PendingIntent openMessagePendingIntent = PendingIntent.getBroadcast(mContext, 0,
+                openMessageIntent, PendingIntent.FLAG_IMMUTABLE);
         Notification.Action actionOpenMessage = new Notification.Action.Builder(0,
                 mContext.getResources().getString(R.string.satellite_notification_open_message),
-                pendingIntentOpenMessage).build();
-        notificationBuilder.addAction(actionOpenMessage);
+                openMessagePendingIntent).build();
+        notificationBuilder.addAction(actionOpenMessage);   // Handle `Open Messages` button
 
-        // Add action to invoke Satellite setting activity in Settings.
-        Intent intentSatelliteSetting = new Intent(ACTION_SATELLITE_SETTING);
-        intentSatelliteSetting.putExtra("sub_id", subId);
-        PendingIntent pendingIntentSatelliteSetting = PendingIntent.getActivity(mContext, 0,
-                intentSatelliteSetting, PendingIntent.FLAG_IMMUTABLE);
-
-        Notification.Action actionOpenSatelliteSetting = new Notification.Action.Builder(null,
+        // Button for `How it works` [Button 2]
+        Intent howItWorksIntent = new Intent();
+        howItWorksIntent.setAction(HOW_IT_WORKS_BUTTON);
+        howItWorksIntent.putExtra("SUBID", subId);
+        PendingIntent howItWorksPendingIntent = PendingIntent.getBroadcast(mContext, 0,
+                howItWorksIntent, PendingIntent.FLAG_IMMUTABLE);
+        Notification.Action actionHowItWorks = new Notification.Action.Builder(0,
                 mContext.getResources().getString(R.string.satellite_notification_how_it_works),
-                pendingIntentSatelliteSetting).build();
-        notificationBuilder.addAction(actionOpenSatelliteSetting);
+                howItWorksPendingIntent).build();
+        notificationBuilder.addAction(actionHowItWorks);    // Handle `How it works` button
+
+        // Intent for clicking the main notification body
+        Intent notificationClickIntent = new Intent(ACTION_NOTIFICATION_CLICK);
+        PendingIntent notificationClickPendingIntent = PendingIntent.getBroadcast(mContext, 0,
+                notificationClickIntent, PendingIntent.FLAG_IMMUTABLE);
+        notificationBuilder.setContentIntent(
+                notificationClickPendingIntent); // Handle notification body click
+
+        // Intent for dismissing/swiping the notification
+        Intent deleteIntent = new Intent(ACTION_NOTIFICATION_DISMISS);
+        PendingIntent deletePendingIntent = PendingIntent.getBroadcast(mContext, 0, deleteIntent,
+                PendingIntent.FLAG_IMMUTABLE);
+        notificationBuilder.setDeleteIntent(
+                deletePendingIntent);  // Handle notification swipe/dismiss
 
         notificationManager.notifyAsUser(NOTIFICATION_TAG, NOTIFICATION_ID,
                 notificationBuilder.build(), UserHandle.ALL);
 
+        // The Intent filter is to receive the above four events.
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(OPEN_MESSAGE_BUTTON);
+        filter.addAction(HOW_IT_WORKS_BUTTON);
+        filter.addAction(ACTION_NOTIFICATION_CLICK);
+        filter.addAction(ACTION_NOTIFICATION_DISMISS);
+        mContext.registerReceiver(mNotificationInteractionBroadcastReceiver, filter,
+                Context.RECEIVER_EXPORTED);
+
+        mIsNotificationShowing = true;
         mCarrierRoamingSatelliteControllerStats.reportCountOfSatelliteNotificationDisplayed();
+        mCarrierRoamingSatelliteControllerStats.reportCarrierId(getSatelliteCarrierId());
+        mSessionMetricsStats.addCountOfSatelliteNotificationDisplayed();
+    }
+
+    private final BroadcastReceiver mNotificationInteractionBroadcastReceiver =
+            new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent receivedIntent) {
+                    String intentAction = receivedIntent.getAction();
+                    if (TextUtils.isEmpty(intentAction)) {
+                        loge("Received empty action from the notification");
+                        return;
+                    }
+                    if (DBG) {
+                        plogd("Notification Broadcast recvd action = "
+                                + receivedIntent.getAction());
+                    }
+                    boolean closeStatusBar = true;
+                    switch (intentAction) {
+                        case OPEN_MESSAGE_BUTTON -> {
+                            // Add action to invoke message application.
+                            // getDefaultSmsPackage and getLaunchIntentForPackage are nullable.
+                            Optional<Intent> nullableIntent = Optional.ofNullable(
+                                    Telephony.Sms.getDefaultSmsPackage(context)).flatMap(
+                                    packageName -> {
+                                        PackageManager pm = context.getPackageManager();
+                                        return Optional.ofNullable(
+                                                pm.getLaunchIntentForPackage(packageName));
+                                    });
+                            // If nullableIntent is null, create new Intent for most common way to
+                            // invoke
+                            // message app.
+                            Intent finalIntent = nullableIntent.map(intent -> {
+                                // Invoke the home screen of default message application.
+                                intent.setAction(Intent.ACTION_MAIN);
+                                intent.addCategory(Intent.CATEGORY_HOME);
+                                return intent;
+                            }).orElseGet(() -> {
+                                ploge("showSatelliteSystemNotification: no default sms package "
+                                        + "name, Invoke default sms compose window instead");
+                                Intent newIntent = new Intent(Intent.ACTION_VIEW);
+                                newIntent.setData(Uri.parse("sms:"));
+                                return newIntent;
+                            });
+                            context.startActivity(finalIntent);
+                        }
+                        case HOW_IT_WORKS_BUTTON -> {
+                            int subId = receivedIntent.getIntExtra("SUBID", -1);
+                            Intent intentSatelliteSetting = new Intent(ACTION_SATELLITE_SETTING);
+                            intentSatelliteSetting.putExtra("sub_id", subId);
+                            intentSatelliteSetting.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                            context.startActivity(intentSatelliteSetting);
+
+                        }
+                        case ACTION_NOTIFICATION_DISMISS -> closeStatusBar = false;
+                    }
+                    // Note : ACTION_NOTIFICATION_DISMISS is not required to handled
+                    dismissNotificationAndUpdatePref(closeStatusBar);
+                }
+            };
+
+    private void dismissNotificationAndUpdatePref(boolean closeStatusBar) {
+        dismissSatelliteNotification();
+        if (closeStatusBar) {
+            // Collapse the status bar once user interact with notification.
+            StatusBarManager statusBarManager = mContext.getSystemService(StatusBarManager.class);
+            if (statusBarManager != null) {
+                statusBarManager.collapsePanels();
+            }
+        }
+        // update the sharedpref only when user interacted with the notification.
+        mSharedPreferences.edit().putBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY, true).apply();
+        mContext.unregisterReceiver(mNotificationInteractionBroadcastReceiver);
     }
 
     private void resetCarrierRoamingSatelliteModeParams() {
@@ -5916,6 +6243,10 @@
         return TimeUnit.SECONDS.toMillis(duration);
     }
 
+    private long getEvaluateEsosProfilesPrioritizationDurationMillis() {
+        return TimeUnit.MINUTES.toMillis(1);
+    }
+
     /**
      * Calculate priority
      * 1. Active eSOS profiles are higher priority than inactive eSOS profiles.
@@ -5933,7 +6264,7 @@
         List<SubscriptionInfo> allSubInfos = mSubscriptionManagerService.getAllSubInfoList(
                 mContext.getOpPackageName(), mContext.getAttributionTag());
         // Key : priority - lower value has higher priority; Value : List<SubscriptionInfo>
-        Map<Integer, List<SubscriptionInfo>> newSubsInfoListPerPriority = new HashMap<>();
+        TreeMap<Integer, List<SubscriptionInfo>> newSubsInfoListPerPriority = new TreeMap<>();
         synchronized (mSatelliteTokenProvisionedLock) {
             for (SubscriptionInfo info : allSubInfos) {
                 int subId = info.getSubscriptionId();
@@ -5985,6 +6316,7 @@
                 mSubsInfoListPerPriority = newSubsInfoListPerPriority;
                 sendBroadCastForProvisionedESOSSubs();
                 mHasSentBroadcast = true;
+                selectBindingSatelliteSubscription();
             }
         }
     }
@@ -6034,7 +6366,7 @@
         for (Map.Entry<Integer, List<SubscriptionInfo>> entry : currentMap.entrySet()) {
             List<SubscriptionInfo> currentList = entry.getValue();
             List<SubscriptionInfo> newList = newMap.get(entry.getKey());
-            if (currentList == null || currentList.size() != newList.size()) {
+            if (newList == null || currentList == null || currentList.size() != newList.size()) {
                 return true;
             }
             for (int i = 0; i < currentList.size(); i++) {
@@ -6049,21 +6381,23 @@
     }
 
     private void sendBroadCastForProvisionedESOSSubs() {
-        String packageName = getStringFromOverlayConfig(
-                R.string.config_satellite_gateway_service_package);
-        String className = getStringFromOverlayConfig(
-                R.string.config_satellite_carrier_roaming_esos_provisioned_class);
+        String packageName = getConfigSatelliteGatewayServicePackage();
+        String className = getConfigSatelliteCarrierRoamingEsosProvisionedClass();
         if (packageName == null || className == null || packageName.isEmpty()
                 || className.isEmpty()) {
-            logd("sendBroadCaseToProvisionedESOSSubs: packageName or className is null or empty.");
+            logd("sendBroadCastForProvisionedESOSSubs: packageName or className is null or empty.");
             return;
         }
         String action = SatelliteManager.ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED;
 
         Intent intent = new Intent(action);
         intent.setComponent(new ComponentName(packageName, className));
-        mContext.sendBroadcast(intent);
-        logd("sendBroadCaseToProvisionedESOSSubs" + intent);
+        if (mFeatureFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        } else {
+            mContext.sendBroadcast(intent);
+        }
+        logd("sendBroadCastForProvisionedESOSSubs" + intent);
     }
 
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@@ -6142,6 +6476,57 @@
         return list;
     }
 
+    private int getSelectedSatelliteSubId() {
+        synchronized (mSatelliteTokenProvisionedLock) {
+            return mSelectedSatelliteSubId;
+        }
+    }
+
+    private void selectBindingSatelliteSubscription() {
+        if (isSatelliteEnabled() || isSatelliteBeingEnabled()) {
+            plogd("selectBindingSatelliteSubscription: satellite subscription will be selected "
+                    + "once the satellite session ends");
+            return;
+        }
+
+        int selectedSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        List<SatelliteSubscriberProvisionStatus> satelliteSubscribers =
+                getPrioritizedSatelliteSubscriberProvisionStatusList();
+        for (SatelliteSubscriberProvisionStatus status : satelliteSubscribers) {
+            // TODO: need to check if satellite is allowed at current location for the subscription
+            int subId = getSubIdFromSubscriberId(
+                    status.getSatelliteSubscriberInfo().getSubscriberId());
+            if (status.getProvisionStatus() && isActiveSubId(subId)) {
+                selectedSubId = subId;
+                break;
+            }
+        }
+
+        synchronized (mSatelliteTokenProvisionedLock) {
+            if (selectedSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
+                    && isSatelliteSupportedViaOem()) {
+                mSelectedSatelliteSubId = SatelliteServiceUtils.getNtnOnlySubscriptionId(mContext);
+            }
+            if (selectedSubId != mSelectedSatelliteSubId) {
+                mSelectedSatelliteSubId = selectedSubId;
+                setSatellitePhone(selectedSubId);
+            }
+        }
+        plogd("selectBindingSatelliteSubscription: SelectedSatelliteSubId="
+                + mSelectedSatelliteSubId);
+    }
+
+    private int getSubIdFromSubscriberId(String subscriberId) {
+        synchronized (mSatelliteTokenProvisionedLock) {
+            return mSubscriberIdPerSub.getOrDefault(subscriberId,
+                    SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        }
+    }
+
+    private boolean isActiveSubId(int subId) {
+        return mSubscriptionManagerService.getSubscriptionInfo(subId).isActive();
+    }
+
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     protected boolean isSubscriptionProvisioned(int subId) {
         plogd("isSubscriptionProvisioned: subId=" + subId);
@@ -6172,19 +6557,45 @@
             @NonNull ResultReceiver result) {
         if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
             result.send(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, null);
+            logd("provisionSatellite: carrierRoamingNbIotNtn not support");
             return;
         }
-        if (list.size() == 0) {
+        if (list.isEmpty()) {
             result.send(SATELLITE_RESULT_INVALID_ARGUMENTS, null);
+            logd("provisionSatellite: SatelliteSubscriberInfo list is empty");
             return;
         }
 
         logd("provisionSatellite:" + list);
         RequestProvisionSatelliteArgument request = new RequestProvisionSatelliteArgument(list,
-                result);
+                result, true);
         sendRequestAsync(CMD_UPDATE_PROVISION_SATELLITE_TOKEN, request, null);
     }
 
+    /**
+     * Deliver the list of deprovisioned satellite subscriber ids.
+     *
+     * @param list List of deprovisioned satellite subscriber ids.
+     * @param result The result receiver that returns whether deliver success or fail.
+     */
+    public void deprovisionSatellite(@NonNull List<SatelliteSubscriberInfo> list,
+            @NonNull ResultReceiver result) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            result.send(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, null);
+            logd("deprovisionSatellite: carrierRoamingNbIotNtn not support");
+            return;
+        }
+        if (list.isEmpty()) {
+            result.send(SATELLITE_RESULT_INVALID_ARGUMENTS, null);
+            logd("deprovisionSatellite: SatelliteSubscriberInfo list is empty");
+            return;
+        }
+
+        logd("deprovisionSatellite:" + list);
+        RequestProvisionSatelliteArgument request = new RequestProvisionSatelliteArgument(list,
+                result, false);
+        sendRequestAsync(CMD_UPDATE_PROVISION_SATELLITE_TOKEN, request, null);
+    }
 
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     protected void setSatellitePhone(int subId) {
@@ -6194,6 +6605,25 @@
                 mSatellitePhone = SatelliteServiceUtils.getPhone();
             }
             plogd("mSatellitePhone:" + (mSatellitePhone != null) + ", subId=" + subId);
+            int carrierId = mSatellitePhone.getCarrierId();
+            if (carrierId != UNKNOWN_CARRIER_ID) {
+                mControllerMetricsStats.setCarrierId(carrierId);
+            } else {
+                logd("setSatellitePhone: Carrier ID is UNKNOWN_CARRIER_ID");
+            }
+        }
+    }
+
+    /** Return the carrier ID of the binding satellite subscription. */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public int getSatelliteCarrierId() {
+        synchronized (mSatellitePhoneLock) {
+            if (mSatellitePhone != null) {
+                return mSatellitePhone.getCarrierId();
+            } else {
+                logd("getSatelliteCarrierId: returns UNKNOWN_CARRIER_ID");
+                return UNKNOWN_CARRIER_ID;
+            }
         }
     }
 
@@ -6224,6 +6654,11 @@
         }
 
         int subId = phone.getSubId();
+        if (!isSatelliteRoamingP2pSmSSupported(subId)) {
+            plogd("isCarrierRoamingNtnEligible: doesn't support P2P SMS");
+            return false;
+        }
+
         if (!isSatelliteSupportedViaCarrier(subId)) {
             plogd("isCarrierRoamingNtnEligible[phoneId=" + phone.getPhoneId()
                     + "]: satellite is not supported via carrier");
@@ -6250,6 +6685,11 @@
             return false;
         }
 
+        if (mOverrideNtnEligibility != null) {
+            // TODO need to send the value from `mOverrideNtnEligibility` or simply true ?
+            return true;
+        }
+
         if (SatelliteServiceUtils.isCellularAvailable()) {
             plogd("isCarrierRoamingNtnEligible[phoneId=" + phone.getPhoneId()
                     + "]: cellular is available");
@@ -6366,6 +6806,91 @@
         }
     }
 
+    private void handleEventSatelliteRegistrationFailure(int causeCode) {
+        plogd("handleEventSatelliteRegistrationFailure: " + causeCode);
+
+        List<ISatelliteModemStateCallback> deadCallersList = new ArrayList<>();
+        mSatelliteRegistrationFailureListeners.values().forEach(listener -> {
+            try {
+                listener.onRegistrationFailure(causeCode);
+            } catch (RemoteException e) {
+                logd("handleEventSatelliteRegistrationFailure RemoteException: " + e);
+                deadCallersList.add(listener);
+            }
+        });
+        deadCallersList.forEach(listener -> {
+            mSatelliteRegistrationFailureListeners.remove(listener.asBinder());
+        });
+    }
+
+    /**
+     * This API can be used by only CTS to override the cached value for the device overlay config
+     * value :
+     * config_satellite_gateway_service_package and
+     * config_satellite_carrier_roaming_esos_provisioned_class.
+     * These values are set before sending an intent to broadcast there are any change to list of
+     * subscriber informations.
+     *
+     * @param name the name is one of the following that constitute an intent.
+     *             component package name, or component class name.
+     * @return {@code true} if the setting is successful, {@code false} otherwise.
+     */
+    public boolean setSatelliteSubscriberIdListChangedIntentComponent(String name) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            logd("setSatelliteSubscriberIdListChangedIntentComponent: carrierRoamingNbIotNtn is "
+                    + "disabled");
+            return false;
+        }
+        if (!isMockModemAllowed()) {
+            logd("setSatelliteSubscriberIdListChangedIntentComponent: mock modem is not allowed");
+            return false;
+        }
+        logd("setSatelliteSubscriberIdListChangedIntentComponent:" + name);
+
+        if (name.contains("/")) {
+            mChangeIntentComponent = true;
+        } else {
+            mChangeIntentComponent = false;
+            return true;
+        }
+        boolean result = true;
+        String[] cmdPart = name.split("/");
+        switch (cmdPart[0]) {
+            case "-p": {
+                mConfigSatelliteGatewayServicePackage = cmdPart[1];
+                break;
+            }
+            case "-c": {
+                mConfigSatelliteCarrierRoamingEsosProvisionedClass = cmdPart[1];
+                break;
+            }
+            default:
+                logd("setSatelliteSubscriberIdListChangedIntentComponent: invalid name " + name);
+                result = false;
+                break;
+        }
+        return result;
+    }
+
+    private String getConfigSatelliteGatewayServicePackage() {
+        if (!mChangeIntentComponent) {
+            return getStringFromOverlayConfig(
+                    R.string.config_satellite_gateway_service_package);
+        }
+        logd("getConfigSatelliteGatewayServicePackage: " + mConfigSatelliteGatewayServicePackage);
+        return mConfigSatelliteGatewayServicePackage;
+    }
+
+    private String getConfigSatelliteCarrierRoamingEsosProvisionedClass() {
+        if (!mChangeIntentComponent) {
+            return getStringFromOverlayConfig(
+                    R.string.config_satellite_carrier_roaming_esos_provisioned_class);
+        }
+        logd("getConfigSatelliteCarrierRoamingEsosProvisionedClass: "
+                + mConfigSatelliteCarrierRoamingEsosProvisionedClass);
+        return mConfigSatelliteCarrierRoamingEsosProvisionedClass;
+    }
+
     private void registerDefaultSmsSubscriptionChangedBroadcastReceiver() {
         if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
             plogd("registerDefaultSmsSubscriptionChangedBroadcastReceiver: Flag "
@@ -6377,6 +6902,11 @@
         mContext.registerReceiver(mDefaultSmsSubscriptionChangedBroadcastReceiver, intentFilter);
     }
 
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected List<DeviceState> getSupportedDeviceStates() {
+        return mContext.getSystemService(DeviceStateManager.class).getSupportedDeviceStates();
+    }
+
     FeatureFlags getFeatureFlags() {
         return mFeatureFlags;
     }
@@ -6394,4 +6924,29 @@
             return !mWaitingForSatelliteModemOff;
         }
     }
+
+    /**
+     * Method to override the Carrier roaming Non-terrestrial network eligibility check
+     *
+     * @param state         flag to enable or disable the Ntn eligibility check.
+     * @param resetRequired reset overriding the check with adb command.
+     */
+    public boolean overrideCarrierRoamingNtnEligibilityChanged(boolean state,
+            boolean resetRequired) {
+        Log.d(TAG, "overrideCarrierRoamingNtnEligibilityChanged state = " + state
+                + "  resetRequired = " + resetRequired);
+        if (resetRequired) {
+            mOverrideNtnEligibility = null;
+        } else {
+            if (mOverrideNtnEligibility == null) {
+                mOverrideNtnEligibility = new AtomicBoolean(state);
+            } else {
+                mOverrideNtnEligibility.set(state);
+            }
+            if (this.mSatellitePhone != null) {
+                updateLastNotifiedNtnEligibilityAndNotify(state);
+            }
+        }
+        return true;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
index d644cc1..a19f802 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteModemInterface.java
@@ -102,6 +102,8 @@
             new RegistrantList();
     @NonNull private final RegistrantList mSatelliteSupportedStateChangedRegistrants =
             new RegistrantList();
+    @NonNull private final RegistrantList mSatelliteRegistrationFailureRegistrants =
+            new RegistrantList();
 
     private class SatelliteListener extends ISatelliteListener.Stub {
 
@@ -187,7 +189,7 @@
 
         @Override
         public void onRegistrationFailure(int causeCode) {
-            // TO-DO notify registrants
+            mSatelliteRegistrationFailureRegistrants.notifyResult(causeCode);
         }
 
         private boolean notifyResultIfExpectedListener() {
@@ -564,6 +566,27 @@
     }
 
     /**
+     * Registers for the satellite registration failed.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    public void registerForSatelliteRegistrationFailure(
+            @NonNull Handler h, int what, @Nullable Object obj) {
+        mSatelliteRegistrationFailureRegistrants.add(h, what, obj);
+    }
+
+    /**
+     * Unregisters for the satellite registration failed.
+     *
+     * @param h Handler to be removed from the registrant list.
+     */
+    public void unregisterForSatelliteRegistrationFailure(@NonNull Handler h) {
+        mSatelliteRegistrationFailureRegistrants.remove(h);
+    }
+
+    /**
      * Request to enable or disable the satellite service listening mode.
      * Listening mode allows the satellite service to listen for incoming pages.
      *
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java b/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
index c4babcb..23dcefc 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
@@ -124,6 +124,8 @@
 
     @Nullable private PersistentLogger mPersistentLogger = null;
 
+    private boolean mIsTestEmergencyNumber = false;
+
     /**
      * Create an instance of SatelliteSOSMessageRecommender.
      *
@@ -213,11 +215,13 @@
      * @param connection The connection created by TelephonyConnectionService for the emergency
      *                   call.
      */
-    public void onEmergencyCallStarted(@NonNull Connection connection) {
+    public void onEmergencyCallStarted(@NonNull Connection connection,
+            boolean isTestEmergencyNumber) {
         if (!isSatelliteSupported()) {
             plogd("onEmergencyCallStarted: satellite is not supported");
             return;
         }
+        mIsTestEmergencyNumber = isTestEmergencyNumber;
 
         if (hasMessages(EVENT_EMERGENCY_CALL_STARTED)) {
             logd("onEmergencyCallStarted: Ignoring due to ongoing event:");
@@ -259,6 +263,7 @@
     }
 
     private void handleEmergencyCallStartedEvent(@NonNull Connection connection) {
+        plogd("handleEmergencyCallStartedEvent: connection=" + connection);
         mSatelliteController.setLastEmergencyCallTime();
 
         if (sendEventDisplayEmergencyMessageForcefully(connection)) {
@@ -280,7 +285,7 @@
 
     private void handleSatelliteProvisionStateChangedEvent(boolean provisioned) {
         if (!provisioned) {
-            cleanUpResources();
+            cleanUpResources(false);
         }
     }
 
@@ -316,10 +321,12 @@
             boolean isCellularAvailable = SatelliteServiceUtils.isCellularAvailable();
             if (!isCellularAvailable
                     && isSatelliteAllowed()
-                    && (isSatelliteViaOemAvailable() || isSatelliteViaCarrierAvailable())
+                    && (isSatelliteViaOemAvailable()
+                    || isSatelliteConnectedViaCarrierWithinHysteresisTime())
                     && shouldTrackCall(mEmergencyConnection.getState())) {
                 plogd("handleTimeoutEvent: Sent EVENT_DISPLAY_EMERGENCY_MESSAGE to Dialer");
-                Bundle extras = createExtraBundleForEventDisplayEmergencyMessage();
+                Bundle extras = createExtraBundleForEventDisplayEmergencyMessage(
+                        mIsTestEmergencyNumber);
                 mEmergencyConnection.sendConnectionEvent(
                         TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE, extras);
                 isDialerNotified = true;
@@ -329,14 +336,13 @@
                     + ", isCellularAvailable=" + isCellularAvailable
                     + ", isSatelliteAllowed=" + isSatelliteAllowed()
                     + ", shouldTrackCall=" + shouldTrackCall(mEmergencyConnection.getState()));
-            reportESosRecommenderDecision(isDialerNotified);
-            cleanUpResources();
+            cleanUpResources(isDialerNotified);
         }
     }
 
     private boolean isSatelliteAllowed() {
         synchronized (mLock) {
-            if (isSatelliteViaCarrierAvailable()) return true;
+            if (isSatelliteConnectedViaCarrierWithinHysteresisTime()) return true;
             return mIsSatelliteAllowedForCurrentLocation;
         }
     }
@@ -358,7 +364,7 @@
         return satelliteProvisioned != null ? satelliteProvisioned : false;
     }
 
-    private boolean isSatelliteViaCarrierAvailable() {
+    private boolean isSatelliteConnectedViaCarrierWithinHysteresisTime() {
         return mIsSatelliteConnectedViaCarrierWithinHysteresisTime.get();
     }
 
@@ -381,13 +387,12 @@
              * we're not tracking. There must be some unexpected things happened in
              * TelephonyConnectionService. Thus, we need to clean up the resources.
              */
-            cleanUpResources();
+            cleanUpResources(false);
             return;
         }
 
         if (!shouldTrackCall(state)) {
-            reportESosRecommenderDecision(false);
-            cleanUpResources();
+            cleanUpResources(false);
         } else {
             // Location service will enter emergency mode only when connection state changes to
             // STATE_DIALING
@@ -398,7 +403,8 @@
         }
     }
 
-    private void reportESosRecommenderDecision(boolean isDialerNotified) {
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected void reportESosRecommenderDecision(boolean isDialerNotified) {
         SatelliteStats.getInstance().onSatelliteSosMessageRecommender(
                 new SatelliteStats.SatelliteSosMessageRecommenderParams.Builder()
                         .setDisplaySosMessageSent(isDialerNotified)
@@ -412,7 +418,9 @@
                         .setCarrierId(getAvailableNtnCarrierID()).build());
     }
 
-    private void cleanUpResources() {
+    private void cleanUpResources(boolean isDialerNotified) {
+        plogd("cleanUpResources");
+        reportESosRecommenderDecision(isDialerNotified);
         synchronized (mLock) {
             stopTimer();
             if (mEmergencyConnection != null) {
@@ -423,6 +431,7 @@
             mIsTimerTimedOut = false;
             mCheckingAccessRestrictionInProgress = false;
             mIsSatelliteAllowedForCurrentLocation = false;
+            mIsTestEmergencyNumber = false;
         }
     }
 
@@ -500,7 +509,8 @@
         if (!SatelliteServiceUtils.isCellularAvailable() && mEmergencyConnection != null) {
             startTimer();
         } else {
-            logv("handleStateChangedEventForHysteresisTimer stopTimer");
+            plogd("handleStateChangedEventForHysteresisTimer stopTimer, mEmergencyConnection="
+                    + mEmergencyConnection);
             stopTimer();
         }
     }
@@ -513,7 +523,7 @@
             sendMessageDelayed(obtainMessage(EVENT_TIME_OUT), mTimeoutMillis);
             mCountOfTimerStarted++;
             mIsTimerTimedOut = false;
-            logd("startTimer mCountOfTimerStarted=" + mCountOfTimerStarted);
+            plogd("startTimer mCountOfTimerStarted=" + mCountOfTimerStarted);
         }
     }
 
@@ -532,7 +542,7 @@
     }
 
     private void selectEmergencyCallWaitForConnectionTimeoutDuration() {
-        if (mSatelliteController.isSatelliteEmergencyMessagingSupportedViaCarrier()) {
+        if (isSatelliteConnectedViaCarrierWithinHysteresisTime()) {
             mTimeoutMillis =
                     mSatelliteController.getCarrierEmergencyCallWaitForConnectionTimeoutMillis();
         } else {
@@ -581,11 +591,13 @@
 
     @Nullable
     private static String getSatelliteEmergencyHandoverIntentActionFromOverlayConfig(
-            @NonNull Context context) {
+            @NonNull Context context, boolean isTestEmergencyNumber) {
         String action;
         try {
-            action = context.getResources().getString(
-                    R.string.config_satellite_emergency_handover_intent_action);
+            int actionIntent = isTestEmergencyNumber
+                    ? R.string.config_satellite_test_with_esp_replies_intent_action
+                    : R.string.config_satellite_emergency_handover_intent_action;
+            action = context.getResources().getString(actionIntent);
         } catch (Resources.NotFoundException ex) {
             loge("getSatelliteEmergencyHandoverIntentFilterActionFromOverlayConfig: ex=" + ex);
             action = null;
@@ -631,15 +643,17 @@
         return callback;
     }
 
-    @NonNull private Bundle createExtraBundleForEventDisplayEmergencyMessage() {
+    @NonNull private Bundle createExtraBundleForEventDisplayEmergencyMessage(
+            boolean isTestEmergencyNumber) {
         int handoverType = EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS;
         Pair<String, String> oemSatelliteMessagingApp =
                 getOemEnabledSatelliteHandoverAppFromOverlayConfig(mContext);
         String packageName = oemSatelliteMessagingApp.first;
         String className = oemSatelliteMessagingApp.second;
-        String action = getSatelliteEmergencyHandoverIntentActionFromOverlayConfig(mContext);
+        String action = getSatelliteEmergencyHandoverIntentActionFromOverlayConfig(mContext,
+                isTestEmergencyNumber);
 
-        if (isSatelliteViaCarrierAvailable()
+        if (isSatelliteConnectedViaCarrierWithinHysteresisTime()
                 || isEmergencyCallToSatelliteHandoverTypeT911Enforced()) {
             ComponentName defaultSmsAppComponent = getDefaultSmsApp();
             handoverType = EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911;
@@ -706,7 +720,8 @@
             @NonNull Connection connection) {
         plogd("Sent EVENT_DISPLAY_EMERGENCY_MESSAGE to Dialer forcefully.");
         mEmergencyConnection = connection;
-        Bundle extras = createExtraBundleForEventDisplayEmergencyMessage();
+        Bundle extras = createExtraBundleForEventDisplayEmergencyMessage(
+                /* isTestEmergencyNumber= */ true);
         connection.sendConnectionEvent(TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE, extras);
         mEmergencyConnection = null;
     }
@@ -723,7 +738,7 @@
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     public int getEmergencyCallToSatelliteHandoverType() {
         if (Flags.carrierRoamingNbIotNtn() && isSatelliteViaOemAvailable()
-                && isSatelliteViaCarrierAvailable()) {
+                && isSatelliteConnectedViaCarrierWithinHysteresisTime()) {
             Phone satellitePhone = mSatelliteController.getSatellitePhone();
             if (satellitePhone == null) {
                 ploge("getEmergencyCallToSatelliteHandoverType: satellitePhone is null");
@@ -732,7 +747,7 @@
             int satelliteSubId = satellitePhone.getSubId();
             return mSatelliteController.getCarrierRoamingNtnEmergencyCallToSatelliteHandoverType(
                     satelliteSubId);
-        } else if (isSatelliteViaCarrierAvailable()) {
+        } else if (isSatelliteConnectedViaCarrierWithinHysteresisTime()) {
             return EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911;
         } else {
             return EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS;
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java b/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
index 21c5c44..3936a7e 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteServiceUtils.java
@@ -481,12 +481,12 @@
                 if ((state == STATE_IN_SERVICE || state == STATE_EMERGENCY_ONLY
                         || serviceState.isEmergencyOnly())
                         && !isSatellitePlmn(phone.getSubId(), serviceState)) {
-                    logv("isCellularAvailable true");
+                    logd("isCellularAvailable true");
                     return true;
                 }
             }
         }
-        logv("isCellularAvailable false");
+        logd("isCellularAvailable false");
         return false;
     }
 
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
index eab3be7..0e21e6c 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
@@ -19,6 +19,8 @@
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT;
+import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_SMS;
+import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE;
@@ -34,6 +36,7 @@
 import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_LISTENING;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_ABORTED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -51,6 +54,7 @@
 import android.os.SystemProperties;
 import android.telephony.DropBoxManagerLoggerBackend;
 import android.telephony.PersistentLogger;
+import android.telephony.ServiceState;
 import android.telephony.satellite.ISatelliteModemStateCallback;
 import android.telephony.satellite.SatelliteManager;
 import android.telephony.satellite.stub.ISatelliteGateway;
@@ -65,7 +69,9 @@
 import com.android.internal.telephony.ExponentialBackoff;
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.satellite.metrics.SessionMetricsStats;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 import com.android.telephony.Rlog;
@@ -121,8 +127,10 @@
     private static final int EVENT_SATELLITE_ENABLEMENT_FAILED = 8;
     private static final int EVENT_SCREEN_STATE_CHANGED = 9;
     protected static final int EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT = 10;
-    protected static final int EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT = 11;
-
+    protected static final int EVENT_ESOS_INACTIVITY_TIMER_TIMED_OUT = 11;
+    private static final int EVENT_ENABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE = 12;
+    private static final int EVENT_SERVICE_STATE_CHANGED = 13;
+    protected static final int EVENT_P2P_SMS_INACTIVITY_TIMER_TIMED_OUT = 14;
     private static final long REBIND_INITIAL_DELAY = 2 * 1000; // 2 seconds
     private static final long REBIND_MAXIMUM_DELAY = 64 * 1000; // 1 minute
     private static final int REBIND_MULTIPLIER = 2;
@@ -158,6 +166,7 @@
     private long mSatelliteStayAtListeningFromSendingMillis;
     private long mSatelliteStayAtListeningFromReceivingMillis;
     private long mSatelliteNbIotInactivityTimeoutMillis;
+    private boolean mIgnoreCellularServiceState = false;
     private final ConcurrentHashMap<IBinder, ISatelliteModemStateCallback> mListeners;
     @SatelliteManager.SatelliteModemState private int mCurrentState;
     @SatelliteManager.SatelliteModemState private int mPreviousState;
@@ -173,6 +182,7 @@
     @NonNull private final DatagramController mDatagramController;
     @Nullable private PersistentLogger mPersistentLogger = null;
     @Nullable private DeviceStateMonitor mDeviceStateMonitor;
+    @NonNull private SessionMetricsStats mSessionMetricsStats;
 
     @NonNull private FeatureFlags mFeatureFlags;
 
@@ -202,8 +212,10 @@
             boolean isSatelliteSupported) {
         if (sInstance == null || isSatelliteSupported != sInstance.mIsSatelliteSupported) {
             ConcurrentHashMap<IBinder, ISatelliteModemStateCallback> existingListeners = null;
+            boolean existIgnoreCellularServiceState = false;
             if (sInstance != null) {
                 existingListeners = sInstance.mListeners;
+                existIgnoreCellularServiceState = sInstance.mIgnoreCellularServiceState;
                 sInstance.cleanUpResource();
             }
 
@@ -217,6 +229,10 @@
                 Log.d(TAG, "make() existingListeners: " + existingListeners.size());
                 sInstance.mListeners.putAll(existingListeners);
             }
+            if (existIgnoreCellularServiceState) {
+                Log.d(TAG, "make() existIgnoreCellularServiceState is true");
+                sInstance.mIgnoreCellularServiceState = true;
+            }
         }
         return sInstance;
     }
@@ -274,11 +290,26 @@
             bindService();
         });
 
-        Phone phone = mSatelliteController.getSatellitePhone();
-        if (phone == null) {
-            phone = SatelliteServiceUtils.getPhone();
+        Phone satellitePhone = mSatelliteController.getSatellitePhone();
+        if (satellitePhone == null) {
+            satellitePhone = SatelliteServiceUtils.getPhone();
         }
-        mDeviceStateMonitor = phone.getDeviceStateMonitor();
+        mDeviceStateMonitor = satellitePhone.getDeviceStateMonitor();
+        mSessionMetricsStats = SessionMetricsStats.getInstance();
+
+        if (mFeatureFlags.carrierRoamingNbIotNtn()) {
+            // Register to received Cellular service state
+            for (Phone phone : PhoneFactory.getPhones()) {
+                if (phone == null) continue;
+
+                phone.registerForServiceStateChanged(
+                        getHandler(), EVENT_SERVICE_STATE_CHANGED, null);
+                if (DBG) {
+                    plogd("SatelliteSessionController: registerForServiceStateChanged phoneId "
+                            + phone.getPhoneId());
+                }
+            }
+        }
 
         addState(mUnavailableState);
         addState(mPowerOffState);
@@ -446,6 +477,23 @@
     }
 
     /**
+     * This API can be used by only CTS to control ingoring cellular service state event.
+     *
+     * @param enabled Whether to enable boolean config.
+     * @return {@code true} if the value is set successfully, {@code false} otherwise.
+     */
+    public boolean setSatelliteIgnoreCellularServiceState(boolean enabled) {
+        plogd("setSatelliteIgnoreCellularServiceState : "
+                + "old = " + mIgnoreCellularServiceState + " new : " + enabled);
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            return false;
+        }
+
+        mIgnoreCellularServiceState = enabled;
+        return true;
+    }
+
+    /**
      * This API can be used by only CTS to update satellite gateway service package name.
      *
      * @param servicePackageName The package name of the satellite gateway service.
@@ -507,10 +555,12 @@
         mIsDeviceAlignedWithSatellite = isAligned;
 
         if (mIsDeviceAlignedWithSatellite) {
-            stopCarrierRoamingNbIotInactivityTimer();
+            stopEsosInactivityTimer();
+            stopP2pSmsInactivityTimer();
         } else {
             if (mCurrentState == SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED) {
-                evaluateStartingCarrierRoamingNbIotInactivityTimer();
+                evaluateStartingEsosInactivityTimer();
+                evaluateStartingP2pSmsInactivityTimer();
             }
         }
     }
@@ -543,9 +593,33 @@
         plogd("cleanUpResource");
         mIsDeviceAlignedWithSatellite = false;
         unregisterForScreenStateChanged();
+
+        if (mFeatureFlags.carrierRoamingNbIotNtn()) {
+            // Register to received Cellular service state
+            for (Phone phone : PhoneFactory.getPhones()) {
+                if (phone == null) continue;
+
+                phone.unregisterForServiceStateChanged(getHandler());
+                if (DBG) {
+                    plogd("cleanUpResource: unregisterForServiceStateChanged phoneId "
+                            + phone.getPhoneId());
+                }
+            }
+        }
+
         quitNow();
     }
 
+    /**
+     * Uses this function to notify that cellular service state has changed
+     *
+     * @param serviceState The state of the cellular service.
+     */
+    @VisibleForTesting
+    public void onCellularServiceStateChanged(ServiceState serviceState) {
+        sendMessage(EVENT_SERVICE_STATE_CHANGED, new AsyncResult(null, serviceState, null));
+    }
+
     private boolean isDemoMode() {
         return mIsDemoMode;
     }
@@ -786,8 +860,11 @@
             mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_IDLE;
             mIsSendingTriggeredDuringTransferringState.set(false);
             stopNbIotInactivityTimer();
+
             //Enable Cellular Modem scanning
-            mSatelliteModemInterface.enableCellularModemWhileSatelliteModeIsOn(true, null);
+            Message onCompleted =
+                    obtainMessage(EVENT_ENABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE);
+            mSatelliteModemInterface.enableCellularModemWhileSatelliteModeIsOn(true, onCompleted);
             notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
         }
 
@@ -817,6 +894,27 @@
                 case EVENT_SATELLITE_MODEM_STATE_CHANGED:
                     handleSatelliteModemStateChanged(msg);
                     break;
+                case EVENT_ENABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE:
+                    if (!mIgnoreCellularServiceState) {
+                        handleEventEnableCellularModemWhileSatelliteModeIsOnDone();
+                    } else {
+                        plogd("IdleState: processing: ignore "
+                                + "EVENT_ENABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE");
+                    }
+                    break;
+                case EVENT_SERVICE_STATE_CHANGED:
+                    if (!mIgnoreCellularServiceState) {
+                        AsyncResult ar = (msg.obj != null) ? (AsyncResult) msg.obj : null;
+                        if (ar == null || ar.result == null) {
+                            plogd("IdleState: processing: can't access ServiceState");
+                        } else {
+                            ServiceState newServiceState = (ServiceState) ar.result;
+                            handleEventServiceStateChanged(newServiceState);
+                        }
+                    } else {
+                        plogd("IdleState: processing: ignore EVENT_SERVICE_STATE_CHANGED");
+                    }
+                    break;
             }
             // Ignore all unexpected events.
             return HANDLED;
@@ -844,6 +942,61 @@
             }
         }
 
+        private void handleEventEnableCellularModemWhileSatelliteModeIsOnDone() {
+            if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+                Rlog.d(TAG, "handleEventEnableCellularModemWhileSatelliteModeIsOnDone: "
+                        + "carrierRoamingNbIotNtn is disabled");
+                return;
+            }
+
+            ServiceState serviceState = mSatelliteController.getSatellitePhone().getServiceState();
+            if (serviceState == null) {
+                plogd("handleEventEnableCellularModemWhileSatelliteModeIsOnDone: "
+                        + "can't access ServiceState");
+                return;
+            }
+            handleEventServiceStateChanged(serviceState);
+        }
+
+        private void handleEventServiceStateChanged(ServiceState serviceState) {
+            boolean isInServiceOrEmergency =
+                    serviceState.getVoiceRegState() == ServiceState.STATE_IN_SERVICE
+                    || serviceState.getDataRegState() == ServiceState.STATE_IN_SERVICE
+                    || serviceState.isEmergencyOnly();
+            if (!isInServiceOrEmergency) {
+                plogd("handleEventServiceStateChanged: is not IN_SERVICE or EMERGENCY_ONLY");
+                return;
+            }
+
+            // In emergency
+            boolean isEmergency = mSatelliteController.getRequestIsEmergency();
+            if (isEmergency) {
+                boolean isEmergencyCommunicationEstablished = (mDatagramController == null)
+                        ? false : mDatagramController.isEmergencyCommunicationEstablished();
+                boolean isTurnOffAllowed =
+                        mSatelliteController.turnOffSatelliteSessionForEmergencyCall(getSubId());
+                if (isEmergencyCommunicationEstablished || !isTurnOffAllowed) {
+                    logd("handleEventServiceStateChanged: "
+                            + "can't disable emergency satellite session");
+                    return;
+                }
+            }
+
+            mSatelliteController.requestSatelliteEnabled(
+                    false /*enableSatellite*/,
+                    false /*enableDemoMode*/,
+                    isEmergency /*isEmergency*/,
+                    new IIntegerConsumer.Stub() {
+                        @Override
+                        public void accept(int result) {
+                            plogd("requestSatelliteEnabled result=" + result);
+                            if (result == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
+                                mSessionMetricsStats.addCountOfAutoExitDueToTnNetwork();
+                            }
+                        }
+                    });
+        }
+
         private void handleEventDisableCellularModemWhileSatelliteModeIsOnDone(
                 @NonNull AsyncResult result) {
             synchronized (mLock) {
@@ -1049,14 +1202,16 @@
             mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED;
             notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
             startNbIotInactivityTimer();
-            evaluateStartingCarrierRoamingNbIotInactivityTimer();
+            evaluateStartingEsosInactivityTimer();
+            evaluateStartingP2pSmsInactivityTimer();
         }
 
         @Override
         public void exit() {
             if (DBG) plogd("Exiting NotConnectedState");
 
-            stopCarrierRoamingNbIotInactivityTimer();
+            stopEsosInactivityTimer();
+            stopP2pSmsInactivityTimer();
         }
 
         @Override
@@ -1070,8 +1225,22 @@
                 case EVENT_SATELLITE_MODEM_STATE_CHANGED:
                     handleEventSatelliteModemStateChanged(msg.arg1);
                     break;
-                case EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT:
-                    // fall through
+                case EVENT_ESOS_INACTIVITY_TIMER_TIMED_OUT:
+                    if (isP2pSmsInActivityTimerStarted()) {
+                        plogd("NotConnectedState: processing: P2P_SMS inactivity timer running "
+                                + "can not move to IDLE");
+                    } else {
+                        transitionTo(mIdleState);
+                    }
+                    break;
+                case EVENT_P2P_SMS_INACTIVITY_TIMER_TIMED_OUT:
+                    if (isEsosInActivityTimerStarted()) {
+                        plogd("NotConnectedState: processing: ESOS inactivity timer running "
+                                + "can not move to IDLE");
+                    } else {
+                        transitionTo(mIdleState);
+                    }
+                    break;
                 case EVENT_NB_IOT_INACTIVITY_TIMER_TIMED_OUT:
                     transitionTo(mIdleState);
                     break;
@@ -1108,17 +1277,30 @@
                     || datagramTransferState.receiveState
                     == SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT) {
                 stopNbIotInactivityTimer();
-                stopCarrierRoamingNbIotInactivityTimer();
+
+                if (mSatelliteController.getRequestIsEmergency()) {
+                    stopEsosInactivityTimer();
+                }
+                stopP2pSmsInactivityTimer();
             } else if (datagramTransferState.sendState == SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE
                     && datagramTransferState.receiveState
                     == SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE) {
                 startNbIotInactivityTimer();
-                evaluateStartingCarrierRoamingNbIotInactivityTimer();
+                evaluateStartingEsosInactivityTimer();
+                evaluateStartingP2pSmsInactivityTimer();
             } else if (isSending(datagramTransferState.sendState)
                     || isReceiving(datagramTransferState.receiveState)) {
-                restartNbIotInactivityTimer();
-                stopCarrierRoamingNbIotInactivityTimer();
-                evaluateStartingCarrierRoamingNbIotInactivityTimer();
+                stopNbIotInactivityTimer();
+
+                DatagramController datagramController = DatagramController.getInstance();
+                int datagramType = datagramController.getDatagramType();
+                if (datagramType == DATAGRAM_TYPE_SOS_MESSAGE) {
+                    stopEsosInactivityTimer();
+                } else if (datagramType == DATAGRAM_TYPE_SMS) {
+                    stopP2pSmsInactivityTimer();
+                } else {
+                    plogd("datagram type is not SOS_Message and SMS " + datagramType);
+                }
             }
         }
     }
@@ -1225,8 +1407,17 @@
             case EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT:
                 whatString = "EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT";
                 break;
-            case EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT:
-                whatString = "EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT";
+            case EVENT_ESOS_INACTIVITY_TIMER_TIMED_OUT:
+                whatString = "EVENT_ESOS_INACTIVITY_TIMER_TIMED_OUT";
+                break;
+            case EVENT_P2P_SMS_INACTIVITY_TIMER_TIMED_OUT:
+                whatString = "EVENT_P2P_SMS_INACTIVITY_TIMER_TIMED_OUT";
+                break;
+            case EVENT_ENABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE:
+                whatString = "EVENT_ENABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE";
+                break;
+            case EVENT_SERVICE_STATE_CHANGED:
+                whatString = "EVENT_SERVICE_STATE_CHANGED";
                 break;
             default:
                 whatString = "UNKNOWN EVENT " + what;
@@ -1403,8 +1594,13 @@
             return;
         }
 
+        if (!mSatelliteController.isInCarrierRoamingNbIotNtn()) {
+            logd("registerScreenOnOffChanged: device is not in CarrierRoamingNbIotNtn");
+            return;
+        }
+
         if (mSatelliteController.getRequestIsEmergency()) {
-            if (DBG) logd("registerScreenOnOffChanged: Emergency mode");
+            logd("registerScreenOnOffChanged: not register, device is in Emergency mode");
             // screen on/off timer is available in not emergency mode
             return;
         }
@@ -1475,13 +1671,6 @@
                 DEFAULT_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC);
     }
 
-    private int getP2pSmsInactivityTimeoutDurationSec() {
-        PersistableBundle config = mSatelliteController.getPersistableBundle(getSubId());
-
-        return config.getInt(KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT,
-                DEFAULT_P2P_SMS_INACTIVITY_TIMEOUT_SEC);
-    }
-
     private int getEsosInactivityTimeoutDurationSec() {
         PersistableBundle config = mSatelliteController.getPersistableBundle(getSubId());
 
@@ -1489,59 +1678,116 @@
                 DEFAULT_ESOS_INACTIVITY_TIMEOUT_SEC);
     }
 
-    private void evaluateStartingCarrierRoamingNbIotInactivityTimer() {
+    private void evaluateStartingEsosInactivityTimer() {
         if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
-            plogd("evaluateStartingCarrierRoamingNbIotInactivityTimer: "
+            plogd("evaluateStartingEsosInactivityTimer: "
                     + "carrierRoamingNbIotNtn is disabled");
             return;
         }
 
+        if (isEsosInActivityTimerStarted()) {
+            plogd("isEsosInActivityTimerStarted: "
+                    + "ESOS inactivity timer already started");
+            return;
+        }
+
         int subId = getSubId();
-        if (!mSatelliteController.isSatelliteRoamingP2pSmSSupported(subId)
-                && !mSatelliteController.isSatelliteEsosSupported(subId)) {
-            plogd("evaluateStartingCarrierRoamingNbIotInactivityTimer: "
-                    + "device does not support P2P SMS and ESOS are disabled");
+        if (!mSatelliteController.isSatelliteEsosSupported(subId)) {
+            plogd("evaluateStartingEsosInactivityTimer: ESOS is not supported");
+            return;
+        }
+
+        if (!mSatelliteController.getRequestIsEmergency()) {
+            plogd("evaluateStartingEsosInactivityTimer: request is not emergency");
             return;
         }
 
         if (mIsDeviceAlignedWithSatellite) {
-            plogd("evaluateStartingCarrierRoamingNbIotInactivityTimer: "
-                    + "can't start inactivity timer due to device aligned satellite");
+            plogd("evaluateStartingEsosInactivityTimer: "
+                    + "can't start ESOS inactivity timer due to device aligned satellite");
             return;
         }
 
-        if (hasMessages(EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT)) {
-            plogd("evaluateStartingCarrierRoamingNbIotInactivityTimer: already started");
-            return;
-        }
-
-        int timeOutMillis;
-        if (mSatelliteController.getRequestIsEmergency()) {
-            timeOutMillis = getEsosInactivityTimeoutDurationSec() * 1000;
-        } else if (mSatelliteController.isInCarrierRoamingNbIotNtn()) {
-            timeOutMillis = getP2pSmsInactivityTimeoutDurationSec() * 1000;
-        } else {
-            plogd("evaluateStartingCarrierRoamingNbIotInactivityTimer: "
-                    + "can't start inactivity timer device is in not P2P SMS and ESOS mode");
-            return;
-        }
-
+        int timeOutMillis = getEsosInactivityTimeoutDurationSec() * 1000;
         DatagramController datagramController = DatagramController.getInstance();
         if (datagramController.isSendingInIdleState()
                 && datagramController.isPollingInIdleState()) {
-            sendMessageDelayed(EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT,
-                    timeOutMillis);
-            plogd("evaluateStartingCarrierRoamingNbIotInactivityTimer: start inactivity timer "
+            sendMessageDelayed(EVENT_ESOS_INACTIVITY_TIMER_TIMED_OUT, timeOutMillis);
+            plogd("evaluateStartingEsosInactivityTimer: start ESOS inactivity timer "
                     + timeOutMillis);
         } else {
-            plogd("evaluateStartingCarrierRoamingNbIotInactivityTimer: "
-                    + "can't start inactivity timer");
+            plogd("evaluateStartingEsosInactivityTimer: "
+                    + "can't start ESOS inactivity timer");
         }
     }
 
-    private void stopCarrierRoamingNbIotInactivityTimer() {
-        removeMessages(EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT);
-        plogd("stopCarrierRoamingNbIotInactivityTimer:");
+    private void stopEsosInactivityTimer() {
+        if (isEsosInActivityTimerStarted()) {
+            removeMessages(EVENT_ESOS_INACTIVITY_TIMER_TIMED_OUT);
+            plogd("stopEsosInactivityTimer: ESOS inactivity timer stopped");
+        }
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public boolean isEsosInActivityTimerStarted() {
+        return hasMessages(EVENT_ESOS_INACTIVITY_TIMER_TIMED_OUT);
+    }
+
+    private int getP2pSmsInactivityTimeoutDurationSec() {
+        PersistableBundle config = mSatelliteController.getPersistableBundle(getSubId());
+
+        return config.getInt(KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT,
+                DEFAULT_P2P_SMS_INACTIVITY_TIMEOUT_SEC);
+    }
+
+    private void evaluateStartingP2pSmsInactivityTimer() {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            plogd("evaluateStartingP2pSmsInactivityTimer: "
+                    + "carrierRoamingNbIotNtn is disabled");
+            return;
+        }
+
+        if (isP2pSmsInActivityTimerStarted()) {
+            plogd("isEsosInActivityTimerStarted: "
+                    + "P2P_SMS inactivity timer already started");
+            return;
+        }
+
+        int subId = getSubId();
+        if (!mSatelliteController.isSatelliteRoamingP2pSmSSupported(subId)) {
+            plogd("evaluateStartingEsosInactivityTimer: P2P_SMS is not supported");
+            return;
+        }
+
+        if (mIsDeviceAlignedWithSatellite) {
+            plogd("evaluateStartingEsosInactivityTimer: "
+                    + "can't start P2P_SMS inactivity timer due to device aligned satellite");
+            return;
+        }
+
+        int timeOutMillis = getP2pSmsInactivityTimeoutDurationSec() * 1000;
+        DatagramController datagramController = DatagramController.getInstance();
+        if (datagramController.isSendingInIdleState()
+                && datagramController.isPollingInIdleState()) {
+            sendMessageDelayed(EVENT_P2P_SMS_INACTIVITY_TIMER_TIMED_OUT, timeOutMillis);
+            plogd("evaluateStartingEsosInactivityTimer: start P2P_SMS inactivity timer "
+                    + timeOutMillis);
+        } else {
+            plogd("evaluateStartingEsosInactivityTimer: "
+                    + "can't start P2P_SMS inactivity timer");
+        }
+    }
+
+    private void stopP2pSmsInactivityTimer() {
+        if (isP2pSmsInActivityTimerStarted()) {
+            removeMessages(EVENT_P2P_SMS_INACTIVITY_TIMER_TIMED_OUT);
+            plogd("stopP2pSmsInactivityTimer: P2P_SMS inactivity timer stopped");
+        }
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public boolean isP2pSmsInActivityTimerStarted() {
+        return hasMessages(EVENT_P2P_SMS_INACTIVITY_TIMER_TIMED_OUT);
     }
 
     private void handleEventScreenOffInactivityTimerTimedOut() {
@@ -1555,6 +1801,9 @@
                     @Override
                     public void accept(int result) {
                         plogd("requestSatelliteEnabled result=" + result);
+                        if (result == SATELLITE_RESULT_SUCCESS) {
+                            mSessionMetricsStats.addCountOfAutoExitDueToScreenOff();
+                        }
                     }
                 });
     }
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/AccessControllerMetricsStats.java b/src/java/com/android/internal/telephony/satellite/metrics/AccessControllerMetricsStats.java
index 13ba709..4333253 100644
--- a/src/java/com/android/internal/telephony/satellite/metrics/AccessControllerMetricsStats.java
+++ b/src/java/com/android/internal/telephony/satellite/metrics/AccessControllerMetricsStats.java
@@ -15,9 +15,12 @@
  */
 package com.android.internal.telephony.satellite.metrics;
 
+import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
+import static com.android.internal.telephony.satellite.SatelliteConstants.ACCESS_CONTROL_TYPE_UNKNOWN;
 import static com.android.internal.telephony.satellite.SatelliteConstants.CONFIG_DATA_SOURCE_UNKNOWN;
+import static com.android.internal.telephony.satellite.SatelliteConstants.TRIGGERING_EVENT_UNKNOWN;
 
 import android.annotation.NonNull;
 import android.telephony.satellite.SatelliteManager;
@@ -42,6 +45,8 @@
     private @SatelliteManager.SatelliteResult int mResultCode;
     private String[] mCountryCodes;
     private @SatelliteConstants.ConfigDataSource int mConfigDataSource;
+    private int mCarrierId;
+    private @SatelliteConstants.TriggeringEvent int mTriggeringEvent;
     private AccessControllerMetricsStats() {
         initializeAccessControllerMetricsParam();
     }
@@ -61,7 +66,7 @@
         return sInstance;
     }
     private void initializeAccessControllerMetricsParam() {
-        mAccessControlType = SatelliteConstants.ACCESS_CONTROL_TYPE_UNKNOWN;
+        mAccessControlType = ACCESS_CONTROL_TYPE_UNKNOWN;
         mLocationQueryTimeMillis = 0;
         mOnDeviceLookupTimeMillis = 0;
         mTotalCheckingTimeMillis = 0;
@@ -70,6 +75,8 @@
         mResultCode = SATELLITE_RESULT_SUCCESS;
         mCountryCodes = new String[0];
         mConfigDataSource = CONFIG_DATA_SOURCE_UNKNOWN;
+        mCarrierId = UNKNOWN_CARRIER_ID;
+        mTriggeringEvent = TRIGGERING_EVENT_UNKNOWN;
     }
     /**
      * Sets the Access Control Type for current satellite enablement.
@@ -161,6 +168,26 @@
         logd("setConfigDataSource: config data source = " + mConfigDataSource);
         return this;
     }
+    /**
+     * Sets the carrier id for NTN satellite service.
+     * @param carrierId Carrier ID of currently available NTN Satellite Network.
+     */
+    public AccessControllerMetricsStats setCarrierId(int carrierId) {
+        mCarrierId = carrierId;
+        logd("setCarrierId: Carrier ID = " + mCarrierId);
+        return this;
+    }
+    /**
+     * Sets the triggering event for satellite access controller operation.
+     * @param triggeringEvent triggering event.
+     */
+    public AccessControllerMetricsStats setTriggeringEvent(
+            @SatelliteConstants.TriggeringEvent int triggeringEvent) {
+        mTriggeringEvent = triggeringEvent;
+        logd("setTriggeringEvent: triggering event = " + mTriggeringEvent);
+        return this;
+    }
+
     /** Report the access controller metrics atoms to PersistAtomsStorage in telephony. */
     public void reportAccessControllerMetrics() {
         SatelliteStats.SatelliteAccessControllerParams accessControllerParams =
@@ -174,6 +201,8 @@
                         .setResult(mResultCode)
                         .setCountryCodes(mCountryCodes)
                         .setConfigDatasource(mConfigDataSource)
+                        .setCarrierId(mCarrierId)
+                        .setTriggeringEvent(mTriggeringEvent)
                         .build();
         logd("reportAccessControllerMetrics: " + accessControllerParams.toString());
         SatelliteStats.getInstance().onSatelliteAccessControllerMetrics(accessControllerParams);
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/CarrierRoamingSatelliteControllerStats.java b/src/java/com/android/internal/telephony/satellite/metrics/CarrierRoamingSatelliteControllerStats.java
index 9524b75..e97d234 100644
--- a/src/java/com/android/internal/telephony/satellite/metrics/CarrierRoamingSatelliteControllerStats.java
+++ b/src/java/com/android/internal/telephony/satellite/metrics/CarrierRoamingSatelliteControllerStats.java
@@ -80,6 +80,22 @@
                         .build());
     }
 
+    /** Capture the NB-IoT NTN carrier ID */
+    public void reportCarrierId(int carrierId) {
+        mSatelliteStats.onCarrierRoamingSatelliteControllerStatsMetrics(
+                new SatelliteStats.CarrierRoamingSatelliteControllerStatsParams.Builder()
+                        .setCarrierId(carrierId)
+                        .build());
+    }
+
+    /** Capture whether the device is satellite entitled or not */
+    public void reportIsDeviceEntitled(boolean isDeviceEntitled) {
+        mSatelliteStats.onCarrierRoamingSatelliteControllerStatsMetrics(
+                new SatelliteStats.CarrierRoamingSatelliteControllerStatsParams.Builder()
+                        .setIsDeviceEntitled(isDeviceEntitled)
+                        .build());
+    }
+
     private static void logd(@NonNull String log) {
         Log.d(TAG, log);
     }
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java b/src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java
index d81683e..56c3431 100644
--- a/src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java
+++ b/src/java/com/android/internal/telephony/satellite/metrics/ControllerMetricsStats.java
@@ -141,14 +141,13 @@
             builder.setCountOfDemoModeOutgoingDatagramSuccess(ADD_COUNT);
         } else {
             builder.setCountOfOutgoingDatagramSuccess(ADD_COUNT);
-        }
-
-        if (SatelliteServiceUtils.isSosMessage(datagramType)) {
-            builder.setCountOfDatagramTypeSosSmsSuccess(ADD_COUNT);
-        } else if (datagramType == SatelliteManager.DATAGRAM_TYPE_LOCATION_SHARING) {
-            builder.setCountOfDatagramTypeLocationSharingSuccess(ADD_COUNT);
-        } else if (datagramType == SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) {
-            builder.setCountOfDatagramTypeKeepAliveSuccess(ADD_COUNT).build();
+            if (SatelliteServiceUtils.isSosMessage(datagramType)) {
+                builder.setCountOfDatagramTypeSosSmsSuccess(ADD_COUNT);
+            } else if (datagramType == SatelliteManager.DATAGRAM_TYPE_LOCATION_SHARING) {
+                builder.setCountOfDatagramTypeLocationSharingSuccess(ADD_COUNT);
+            } else if (datagramType == SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) {
+                builder.setCountOfDatagramTypeKeepAliveSuccess(ADD_COUNT).build();
+            }
         }
 
         SatelliteStats.SatelliteControllerParams controllerParam = builder.build();
@@ -166,14 +165,13 @@
             builder.setCountOfDemoModeOutgoingDatagramFail(ADD_COUNT);
         } else {
             builder.setCountOfOutgoingDatagramFail(ADD_COUNT);
-        }
-
-        if (SatelliteServiceUtils.isSosMessage(datagramType)) {
-            builder.setCountOfDatagramTypeSosSmsFail(ADD_COUNT);
-        } else if (datagramType == SatelliteManager.DATAGRAM_TYPE_LOCATION_SHARING) {
-            builder.setCountOfDatagramTypeLocationSharingFail(ADD_COUNT);
-        } else if (datagramType == SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) {
-            builder.setCountOfDatagramTypeKeepAliveFail(ADD_COUNT);
+            if (SatelliteServiceUtils.isSosMessage(datagramType)) {
+                builder.setCountOfDatagramTypeSosSmsFail(ADD_COUNT);
+            } else if (datagramType == SatelliteManager.DATAGRAM_TYPE_LOCATION_SHARING) {
+                builder.setCountOfDatagramTypeLocationSharingFail(ADD_COUNT);
+            } else if (datagramType == SatelliteManager.DATAGRAM_TYPE_KEEP_ALIVE) {
+                builder.setCountOfDatagramTypeKeepAliveFail(ADD_COUNT);
+            }
         }
 
         SatelliteStats.SatelliteControllerParams controllerParam = builder.build();
@@ -374,12 +372,51 @@
     /** Capture the latest provisioned state for satellite service */
     @VisibleForTesting
     public void setIsProvisioned(boolean isProvisioned) {
+        logd("setIsProvisioned:" + isProvisioned);
         mSatelliteStats.onSatelliteControllerMetrics(
                 new SatelliteStats.SatelliteControllerParams.Builder()
                         .setIsProvisioned(isProvisioned)
                         .build());
     }
 
+    /** Capture the NB-IoT NTN carrier ID */
+    @VisibleForTesting
+    public void setCarrierId(int carrierId) {
+        logd("setCarrierId:" + carrierId);
+        mSatelliteStats.onSatelliteControllerMetrics(
+                new SatelliteStats.SatelliteControllerParams.Builder()
+                        .setCarrierId(carrierId)
+                        .build());
+    }
+
+    /**
+     * Report a counter when allowed state has changed.
+     */
+    public void reportAllowedStateChanged() {
+        logd("reportAllowedStateChanged:");
+        mSatelliteStats.onSatelliteControllerMetrics(
+                new SatelliteStats.SatelliteControllerParams.Builder()
+                        .setCountOfSatelliteAllowedStateChangedEvents(ADD_COUNT)
+                        .build());
+    }
+
+    /**
+     * Report a counter when location query was successful or failed.
+     */
+    public void reportLocationQuerySuccessful(boolean result) {
+        SatelliteStats.SatelliteControllerParams.Builder builder;
+        if (result) {
+            builder = new SatelliteStats.SatelliteControllerParams.Builder()
+                    .setCountOfSuccessfulLocationQueries(ADD_COUNT);
+        } else {
+            builder = new SatelliteStats.SatelliteControllerParams.Builder()
+                    .setCountOfFailedLocationQueries(ADD_COUNT);
+        }
+        SatelliteStats.SatelliteControllerParams controllerParam = builder.build();
+        logd("reportLocationQuerySuccessful:" + controllerParam);
+        mSatelliteStats.onSatelliteControllerMetrics(controllerParam);
+    }
+
     /** Receives the battery status whether it is in charging or not, update interval is 60 sec. */
     private final BroadcastReceiver mBatteryStatusReceiver = new BroadcastReceiver() {
         private long mLastUpdatedTime = 0;
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/ProvisionMetricsStats.java b/src/java/com/android/internal/telephony/satellite/metrics/ProvisionMetricsStats.java
index 0647231..73be042 100644
--- a/src/java/com/android/internal/telephony/satellite/metrics/ProvisionMetricsStats.java
+++ b/src/java/com/android/internal/telephony/satellite/metrics/ProvisionMetricsStats.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.satellite.metrics;
 
+import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
+
 import android.annotation.NonNull;
 import android.telephony.satellite.SatelliteManager;
 import android.util.Log;
@@ -37,6 +39,7 @@
     private int mProvisioningStartTimeSec;
     private boolean mIsProvisionRequest;
     private boolean mIsCanceled;
+    private int mCarrierId;
 
     private ProvisionMetricsStats() {
         initializeProvisionParams();
@@ -80,6 +83,12 @@
         return this;
     }
 
+    /** Sets the Carrier of NTN satellite */
+    public ProvisionMetricsStats setCarrierId(int carrierId) {
+        mCarrierId = carrierId;
+        return this;
+    }
+
     /** Report the provision metrics atoms to PersistAtomsStorage in telephony */
     public void reportProvisionMetrics() {
         SatelliteStats.SatelliteProvisionParams provisionParams =
@@ -89,9 +98,10 @@
                                 (System.currentTimeMillis() / 1000) - mProvisioningStartTimeSec)
                         .setIsProvisionRequest(mIsProvisionRequest)
                         .setIsCanceled(mIsCanceled)
+                        .setCarrierId(mCarrierId)
                         .build();
         SatelliteStats.getInstance().onSatelliteProvisionMetrics(provisionParams);
-        logd("reportProvisionMetrics: " + provisionParams.toString());
+        logd("reportProvisionMetrics: " + provisionParams);
         initializeProvisionParams();
     }
 
@@ -100,6 +110,7 @@
         mProvisioningStartTimeSec = INVALID_TIME;
         mIsProvisionRequest = false;
         mIsCanceled = false;
+        mCarrierId = UNKNOWN_CARRIER_ID;
     }
 
     private static void logd(@NonNull String log) {
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java b/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
index 65181c0..a234378 100644
--- a/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
+++ b/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony.satellite.metrics;
 
+import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
 import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
@@ -52,6 +53,10 @@
     private int mCountOfIncomingDatagramFailed;
     private boolean mIsDemoMode;
     private @NtnSignalStrength.NtnSignalStrengthLevel int mMaxNtnSignalStrengthLevel;
+    private int mCarrierId;
+    private int mCountOfSatelliteNotificationDisplayed;
+    private int mCountOfAutoExitDueToScreenOff;
+    private int mCountOfAutoExitDueToTnNetwork;
 
     private SessionMetricsStats() {
         initializeSessionMetricsParam();
@@ -213,6 +218,35 @@
         return this;
     }
 
+    /** Sets the Carrier ID of this NTN session. */
+    public SessionMetricsStats setCarrierId(int carrierId) {
+        mCarrierId = carrierId;
+        logd("setCarrierId(" + carrierId + ")");
+        return this;
+    }
+
+    /** Increase the count of Satellite Notification Display. */
+    public SessionMetricsStats addCountOfSatelliteNotificationDisplayed() {
+        mCountOfSatelliteNotificationDisplayed++;
+        logd("addCountOfSatelliteNotificationDisplayed: current count="
+                + mCountOfSatelliteNotificationDisplayed);
+        return this;
+    }
+
+    /** Increase the count of auto exit from P2P satellite messaging due to screen off. */
+    public SessionMetricsStats addCountOfAutoExitDueToScreenOff() {
+        mCountOfAutoExitDueToScreenOff++;
+        logd("addCountOfAutoExitDueToScreenOff: current count=" + mCountOfAutoExitDueToScreenOff);
+        return this;
+    }
+
+    /** Increase the count of auto exit from P2P satellite messaging due to scan TN network. */
+    public SessionMetricsStats addCountOfAutoExitDueToTnNetwork() {
+        mCountOfAutoExitDueToTnNetwork++;
+        logd("addCountOfAutoExitDueToTnNetwork: current count=" + mCountOfAutoExitDueToTnNetwork);
+        return this;
+    }
+
     /** Report the session metrics atoms to PersistAtomsStorage in telephony. */
     public void reportSessionMetrics() {
         SatelliteStats.SatelliteSessionParams sessionParams =
@@ -229,6 +263,11 @@
                         .setCountOfIncomingDatagramFailed(mCountOfIncomingDatagramFailed)
                         .setIsDemoMode(mIsDemoMode)
                         .setMaxNtnSignalStrengthLevel(mMaxNtnSignalStrengthLevel)
+                        .setCarrierId(mCarrierId)
+                        .setCountOfSatelliteNotificationDisplayed(
+                                mCountOfSatelliteNotificationDisplayed)
+                        .setCountOfAutoExitDueToScreenOff(mCountOfAutoExitDueToScreenOff)
+                        .setCountOfAutoExitDueToTnNetwork(mCountOfAutoExitDueToTnNetwork)
                         .build();
         logd("reportSessionMetrics: " + sessionParams.toString());
         SatelliteStats.getInstance().onSatelliteSessionMetrics(sessionParams);
@@ -277,6 +316,10 @@
         mCountOfIncomingDatagramFailed = 0;
         mIsDemoMode = false;
         mMaxNtnSignalStrengthLevel = NTN_SIGNAL_STRENGTH_NONE;
+        mCarrierId = UNKNOWN_CARRIER_ID;
+        mCountOfSatelliteNotificationDisplayed = 0;
+        mCountOfAutoExitDueToScreenOff = 0;
+        mCountOfAutoExitDueToTnNetwork = 0;
     }
 
     private static void logd(@NonNull String log) {
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
index 87d6d27..4e28d00 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
@@ -1450,8 +1450,8 @@
             SatelliteController satelliteController = SatelliteController.getInstance();
             boolean isSatelliteEnabledOrBeingEnabled = false;
             if (satelliteController != null) {
-                isSatelliteEnabledOrBeingEnabled = satelliteController.isSatelliteEnabled()
-                        || satelliteController.isSatelliteBeingEnabled();
+                isSatelliteEnabledOrBeingEnabled =
+                        satelliteController.isSatelliteEnabledOrBeingEnabled();
             }
 
             if (!isSatelliteEnabledOrBeingEnabled) {
@@ -4561,6 +4561,10 @@
             // Too many packages running with phone uid. Just return one here.
             return "com.android.phone";
         }
+        if (mFeatureFlags.hsumPackageManager()) {
+            return Arrays.toString(mContext.createContextAsUser(Binder.getCallingUserHandle(), 0)
+                    .getPackageManager().getPackagesForUid(Binder.getCallingUid()));
+        }
         return Arrays.toString(mContext.getPackageManager().getPackagesForUid(
                 Binder.getCallingUid()));
     }
@@ -4765,9 +4769,16 @@
      */
     @Nullable
     private String getCurrentPackageName() {
+        if (mFeatureFlags.hsumPackageManager()) {
+            PackageManager pm = mContext.createContextAsUser(Binder.getCallingUserHandle(), 0)
+                    .getPackageManager();
+            if (pm == null) return null;
+            String[] callingPackageNames = pm.getPackagesForUid(Binder.getCallingUid());
+            return (callingPackageNames == null) ? null : callingPackageNames[0];
+        }
         if (mPackageManager == null) return null;
-        String[] callingUids = mPackageManager.getPackagesForUid(Binder.getCallingUid());
-        return (callingUids == null) ? null : callingUids[0];
+        String[] callingPackageNames = mPackageManager.getPackagesForUid(Binder.getCallingUid());
+        return (callingPackageNames == null) ? null : callingPackageNames[0];
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
index 5f8ccb4..a3f34c4 100644
--- a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
@@ -16,14 +16,18 @@
 
 package com.android.internal.telephony.uicc;
 
+import static com.android.internal.telephony.util.TelephonyUtils.FORCE_VERBOSE_STATE_LOGGING;
+
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.AsyncResult;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CommandsInterface;
+import com.android.telephony.Rlog;
 
 import java.util.ArrayList;
 
@@ -31,7 +35,9 @@
  * {@hide}
  */
 public abstract class IccFileHandler extends Handler implements IccConstants {
-    private static final boolean VDBG = false;
+    protected static final String LOG_TAG = "IccFileHandler";
+    private static final boolean VDBG = FORCE_VERBOSE_STATE_LOGGING ||
+            Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
 
     //from TS 11.11 9.1 or elsewhere
     static protected final int COMMAND_READ_BINARY = 0xb0;
diff --git a/src/java/com/android/internal/telephony/uicc/IccRecords.java b/src/java/com/android/internal/telephony/uicc/IccRecords.java
index ae93b09..57ea9b9 100644
--- a/src/java/com/android/internal/telephony/uicc/IccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IccRecords.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.uicc;
 
+import static com.android.internal.telephony.util.TelephonyUtils.FORCE_VERBOSE_STATE_LOGGING;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -59,7 +61,6 @@
 public abstract class IccRecords extends Handler implements IccConstants {
     private static final String LOG_TAG = "IccRecords";
     protected static final boolean DBG = true;
-    private static final boolean FORCE_VERBOSE_STATE_LOGGING = false; /* stopship if true */
     protected static final boolean VDBG =  FORCE_VERBOSE_STATE_LOGGING ||
             Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
 
diff --git a/src/java/com/android/internal/telephony/uicc/InstallCarrierAppTrampolineActivity.java b/src/java/com/android/internal/telephony/uicc/InstallCarrierAppTrampolineActivity.java
index 2c29266..a07c2ec 100644
--- a/src/java/com/android/internal/telephony/uicc/InstallCarrierAppTrampolineActivity.java
+++ b/src/java/com/android/internal/telephony/uicc/InstallCarrierAppTrampolineActivity.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.os.Bundle;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
@@ -107,7 +108,8 @@
         super.onActivityResult(requestCode, resultCode, data);
         if (requestCode == INSTALL_CARRIER_APP_DIALOG_REQUEST) {
             if (resultCode == DOWNLOAD_RESULT) {
-                startActivity(InstallCarrierAppUtils.getPlayStoreIntent(mPackageName));
+                startActivityAsUser(InstallCarrierAppUtils.getPlayStoreIntent(mPackageName),
+                        UserHandle.CURRENT);
             }
             finishNoAnimation();
         }
diff --git a/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java b/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
index 9591a498..b705cbc 100644
--- a/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
@@ -16,17 +16,23 @@
 
 package com.android.internal.telephony.uicc;
 
+import android.annotation.NonNull;
+
+import static com.android.internal.telephony.util.TelephonyUtils.FORCE_VERBOSE_STATE_LOGGING;
+
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
 import android.os.AsyncResult;
 import android.os.Build;
 import android.os.Message;
+import android.os.UserHandle;
 import android.telephony.SubscriptionManager;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.gsm.SimTlv;
 import com.android.telephony.Rlog;
 
@@ -43,7 +49,6 @@
     protected static final String LOG_TAG = "IsimUiccRecords";
 
     private static final boolean DBG = true;
-    private static final boolean FORCE_VERBOSE_STATE_LOGGING = false; /* stopship if true */
     private static final boolean VDBG =  FORCE_VERBOSE_STATE_LOGGING ||
             Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
     private static final boolean DUMP_RECORDS = false;  // Note: PII is logged when this is true
@@ -64,6 +69,9 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private String auth_rsp;
 
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
+
     private static final int TAG_ISIM_VALUE = 0x80;     // From 3GPP TS 31.103
 
     @Override
@@ -78,9 +86,10 @@
                 + " mSmss TPMR=" + getSmssTpmrValue()) : "");
     }
 
-    public IsimUiccRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
+    public IsimUiccRecords(@NonNull UiccCardApplication app, @NonNull Context c,
+            @NonNull CommandsInterface ci, @NonNull FeatureFlags flags) {
         super(app, c, ci);
-
+        mFeatureFlags = flags;
         mRecordsRequested = false;  // No load request is made till SIM ready
         //todo: currently locked state for ISIM is not handled well and may cause app state to not
         //be broadcast
@@ -387,7 +396,11 @@
         Intent intent = new Intent(INTENT_ISIM_REFRESH);
         log("send ISim REFRESH: " + INTENT_ISIM_REFRESH);
         SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mParentApp.getPhoneId());
-        mContext.sendBroadcast(intent);
+        if (mFeatureFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        } else {
+            mContext.sendBroadcast(intent);
+        }
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/uicc/PinStorage.java b/src/java/com/android/internal/telephony/uicc/PinStorage.java
index e4a0cfa..e26050e 100644
--- a/src/java/com/android/internal/telephony/uicc/PinStorage.java
+++ b/src/java/com/android/internal/telephony/uicc/PinStorage.java
@@ -36,6 +36,7 @@
 import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT__EVENT__PIN_VERIFICATION_SUCCESS;
 import static com.android.internal.telephony.uicc.IccCardStatus.PinState.PINSTATE_ENABLED_NOT_VERIFIED;
 import static com.android.internal.telephony.uicc.IccCardStatus.PinState.PINSTATE_ENABLED_VERIFIED;
+import static com.android.internal.telephony.util.TelephonyUtils.FORCE_VERBOSE_STATE_LOGGING;
 
 import android.annotation.Nullable;
 import android.app.KeyguardManager;
@@ -56,6 +57,7 @@
 import android.telephony.TelephonyManager.SimState;
 import android.util.Base64;
 import android.util.IndentingPrintWriter;
+import android.util.Log;
 import android.util.SparseArray;
 
 import com.android.internal.R;
@@ -88,7 +90,8 @@
  */
 public class PinStorage extends Handler {
     private static final String TAG = "PinStorage";
-    private static final boolean VDBG = false;  // STOPSHIP if true
+    private static final boolean VDBG = FORCE_VERBOSE_STATE_LOGGING ||
+            Rlog.isLoggable(TAG, Log.VERBOSE);
 
     /**
      * Time duration in milliseconds to allow automatic PIN verification after reboot. All unused
diff --git a/src/java/com/android/internal/telephony/uicc/SIMRecords.java b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
index 34b412f..4b0e63c 100644
--- a/src/java/com/android/internal/telephony/uicc/SIMRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
@@ -19,6 +19,8 @@
 import static android.telephony.SmsManager.STATUS_ON_ICC_READ;
 import static android.telephony.SmsManager.STATUS_ON_ICC_UNREAD;
 
+import static com.android.internal.telephony.util.TelephonyUtils.FORCE_VERBOSE_STATE_LOGGING;
+
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Resources;
@@ -57,8 +59,7 @@
     protected static final String LOG_TAG = "SIMRecords";
 
     private static final boolean CRASH_RIL = false;
-    private static final boolean FORCE_VERBOSE_STATE_LOGGING = false; /* stopship if true */
-    private static final boolean VDBG =  FORCE_VERBOSE_STATE_LOGGING ||
+    private static final boolean VDBG = FORCE_VERBOSE_STATE_LOGGING ||
             Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
 
     // ***** Instance Variables
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java b/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
index fe19e99..3bda29f 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCardApplication.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony.uicc;
 
+import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.AsyncResult;
@@ -29,6 +30,7 @@
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
@@ -105,11 +107,14 @@
     private RegistrantList mPinLockedRegistrants = new RegistrantList();
     private RegistrantList mNetworkLockedRegistrants = new RegistrantList();
 
-    public UiccCardApplication(UiccProfile uiccProfile,
-                        IccCardApplicationStatus as,
-                        Context c,
-                        CommandsInterface ci) {
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
+
+    public UiccCardApplication(@NonNull UiccProfile uiccProfile,
+            @NonNull IccCardApplicationStatus as, @NonNull Context c, @NonNull CommandsInterface ci,
+            @NonNull FeatureFlags flags) {
         if (DBG) log("Creating UiccApp: " + as);
+        mFeatureFlags = flags;
         mUiccProfile = uiccProfile;
         mAppState = as.app_state;
         mAppType = as.app_type;
@@ -208,7 +213,7 @@
         } else if (type == AppType.APPTYPE_RUIM || type == AppType.APPTYPE_CSIM){
             return new RuimRecords(this, c, ci);
         } else if (type == AppType.APPTYPE_ISIM) {
-            return new IsimUiccRecords(this, c, ci);
+            return new IsimUiccRecords(this, c, ci, mFeatureFlags);
         } else {
             // Unknown app type (maybe detection is still in progress)
             return null;
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index a0ee2d1..dd71c44 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -19,6 +19,8 @@
 import static android.telephony.TelephonyManager.UNINITIALIZED_CARD_ID;
 import static android.telephony.TelephonyManager.UNSUPPORTED_CARD_ID;
 
+import static com.android.internal.telephony.util.TelephonyUtils.FORCE_VERBOSE_STATE_LOGGING;
+
 import static java.util.Arrays.copyOf;
 
 import android.Manifest;
@@ -35,6 +37,7 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.RegistrantList;
+import android.os.UserHandle;
 import android.preference.PreferenceManager;
 import android.sysprop.TelephonyProperties;
 import android.telephony.AnomalyReporter;
@@ -64,6 +67,7 @@
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.RadioConfig;
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.uicc.euicc.EuiccCard;
@@ -129,9 +133,10 @@
  * See also {@link com.android.internal.telephony.IccCard}
  */
 public class UiccController extends Handler {
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false; //STOPSHIP if true
     private static final String LOG_TAG = "UiccController";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = FORCE_VERBOSE_STATE_LOGGING ||
+            Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
 
     public static final int INVALID_SLOT_ID = -1;
 
@@ -245,22 +250,26 @@
     // LocalLog buffer to hold important SIM related events for debugging
     private static LocalLog sLocalLog = new LocalLog(TelephonyUtils.IS_DEBUGGABLE ? 256 : 64);
 
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
+
     /**
      * API to make UiccController singleton if not already created.
      */
-    public static UiccController make(Context c) {
+    public static UiccController make(@NonNull Context c, @NonNull FeatureFlags flags) {
         synchronized (mLock) {
             if (mInstance != null) {
                 throw new RuntimeException("UiccController.make() should only be called once");
             }
-            mInstance = new UiccController(c);
+            mInstance = new UiccController(c, flags);
             return mInstance;
         }
     }
 
-    private UiccController(Context c) {
+    private UiccController(@NonNull Context c, @NonNull FeatureFlags flags) {
         if (DBG) log("Creating UiccController");
         mContext = c;
+        mFeatureFlags = flags;
         mCis = PhoneFactory.getCommandsInterfaces();
         int numPhysicalSlots = c.getResources().getInteger(
                 com.android.internal.R.integer.config_num_physical_slots);
@@ -292,7 +301,7 @@
             mCis[i].registerForIccRefresh(this, EVENT_SIM_REFRESH, i);
         }
 
-        mLauncher = new UiccStateChangedLauncher(c, this);
+        mLauncher = new UiccStateChangedLauncher(c, this, mFeatureFlags);
         mCardStrings = loadCardStrings();
         mDefaultEuiccCardId = UNINITIALIZED_CARD_ID;
 
@@ -814,7 +823,12 @@
             Rlog.d(LOG_TAG, "Broadcasting intent ACTION_SIM_CARD_STATE_CHANGED "
                     + TelephonyManager.simStateToString(state) + " for phone: " + phoneId
                     + " slot: " + slotId + " port: " + portIndex + " sub: " + subId);
-            mContext.sendBroadcast(intent, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+            if (mFeatureFlags.hsumBroadcast()) {
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                        Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+            } else {
+                mContext.sendBroadcast(intent, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+            }
             TelephonyMetrics.getInstance().updateSimState(phoneId, state);
         }
     }
@@ -854,7 +868,12 @@
                     + TelephonyManager.simStateToString(state)
                     + " for phone: " + phoneId + " slot: " + slotId + " port: "
                     + slot.getPortIndexFromPhoneId(phoneId) + " sub: " + subId);
-            mContext.sendBroadcast(intent, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+            if (mFeatureFlags.hsumBroadcast()) {
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                        Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+            } else {
+                mContext.sendBroadcast(intent, Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+            }
             TelephonyMetrics.getInstance().updateSimState(phoneId, state);
         }
     }
@@ -1456,8 +1475,13 @@
         options.setBackgroundActivityStartsAllowed(true);
         Intent intent = new Intent(TelephonyManager.ACTION_SIM_SLOT_STATUS_CHANGED);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        mContext.sendBroadcast(intent, android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
-                options.toBundle());
+        if (mFeatureFlags.hsumBroadcast()) {
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                    android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, options.toBundle());
+        } else {
+            mContext.sendBroadcast(intent, android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+                    options.toBundle());
+        }
     }
 
     private boolean hasActivePort(IccSimPortInfo[] simPortInfos) {
diff --git a/src/java/com/android/internal/telephony/uicc/UiccProfile.java b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
index 0e4a5ef..2551cb8 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccProfile.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
@@ -19,6 +19,7 @@
 import static com.android.internal.telephony.TelephonyStatsLog.PIN_STORAGE_EVENT;
 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 static com.android.internal.telephony.util.TelephonyUtils.FORCE_VERBOSE_STATE_LOGGING;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -40,6 +41,7 @@
 import android.os.PersistableBundle;
 import android.os.Registrant;
 import android.os.RegistrantList;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
@@ -53,6 +55,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CarrierAppUtils;
@@ -102,11 +105,13 @@
 public class UiccProfile extends IccCard {
     protected static final String LOG_TAG = "UiccProfile";
     protected static final boolean DBG = true;
-    private static final boolean VDBG = false; //STOPSHIP if true
+    private static final boolean VDBG = FORCE_VERBOSE_STATE_LOGGING ||
+            Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
 
     private static final String OPERATOR_BRAND_OVERRIDE_PREFIX = "operator_branding_";
 
-    private final @NonNull FeatureFlags mFlags;
+    @NonNull
+    private final 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;
@@ -1140,7 +1145,7 @@
                     //Create newly added Applications
                     if (i < ics.mApplications.length) {
                         mUiccApplications[i] = new UiccCardApplication(this,
-                                ics.mApplications[i], mContext, mCi);
+                                ics.mApplications[i], mContext, mCi, mFlags);
                     }
                 } else if (i >= ics.mApplications.length) {
                     //Delete removed applications
@@ -1352,7 +1357,7 @@
     private void promptInstallCarrierApp(String pkgName) {
         Intent showDialogIntent = InstallCarrierAppTrampolineActivity.get(mContext, pkgName);
         showDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        mContext.startActivity(showDialogIntent);
+        mContext.startActivityAsUser(showDialogIntent, UserHandle.CURRENT);
     }
 
     private void onCarrierPrivilegesLoadedMessage() {
diff --git a/src/java/com/android/internal/telephony/uicc/UiccStateChangedLauncher.java b/src/java/com/android/internal/telephony/uicc/UiccStateChangedLauncher.java
index 3ef421a..ff48fd1 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccStateChangedLauncher.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccStateChangedLauncher.java
@@ -16,15 +16,18 @@
 
 package com.android.internal.telephony.uicc;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Handler;
 import android.os.Message;
+import android.os.UserHandle;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
 import com.android.internal.R;
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.uicc.IccCardStatus.CardState;
 
 /**
@@ -46,7 +49,12 @@
     private UiccController mUiccController;
     private boolean[] mIsRestricted = null;
 
-    public UiccStateChangedLauncher(Context context, UiccController controller) {
+    @NonNull
+    private final FeatureFlags mFeatureFlags;
+
+    public UiccStateChangedLauncher(Context context, UiccController controller,
+            @NonNull FeatureFlags flags) {
+        mFeatureFlags = flags;
         sDeviceProvisioningPackage = context.getResources().getString(
                 R.string.config_deviceProvisioningPackage);
         if (sDeviceProvisioningPackage != null && !sDeviceProvisioningPackage.isEmpty()) {
@@ -92,7 +100,11 @@
         Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
         intent.setPackage(sDeviceProvisioningPackage);
         try {
-            mContext.sendBroadcast(intent);
+            if (mFeatureFlags.hsumBroadcast()) {
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            } else {
+                mContext.sendBroadcast(intent);
+            }
         } catch (Exception e) {
             Log.e(TAG, e.toString());
         }
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java b/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java
index 97fb9ca..1d9dc68 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java
@@ -25,7 +25,9 @@
 import android.telephony.IccOpenLogicalChannelResponse;
 import android.util.Base64;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.euicc.EuiccSession;
 import com.android.internal.telephony.uicc.IccIoResult;
 import com.android.internal.telephony.uicc.euicc.async.AsyncResultCallback;
 import com.android.internal.telephony.uicc.euicc.async.AsyncResultHelper;
@@ -40,7 +42,14 @@
  * before sending and closed after all APDU commands are sent. The complete response of the last
  * APDU command will be returned. If any APDU command returns an error status (other than
  * {@link #STATUS_NO_ERROR}) or causing an exception, an {@link ApduException} will be returned
- * immediately without sending the rest of commands. This class is thread-safe.
+ * immediately without sending the rest of commands.
+ *
+ * <p>If {@link EuiccSession} indicates ongoing session(s), the behavior changes: 1) before
+ * sending, check if a channel is opened already. If yes, reuse the channel and send APDU commands
+ * directly. If no, open a channel before sending. 2) The channel is closed when EuiccSession
+ * class ends all sessions, independent of APDU sending.
+ *
+ * <p>This class is thread-safe.
  *
  * @hide
  */
@@ -54,6 +63,7 @@
     // Status code of APDU response
     private static final int STATUS_NO_ERROR = 0x9000;
     private static final int SW1_NO_ERROR = 0x91;
+    private static final int STATUS_CHANNEL_CLOSED = 0x6881; // b/359336875
 
     private static final int WAIT_TIME_MS = 2000;
     private static final String CHANNEL_ID_PRE = "esim-channel";
@@ -68,6 +78,10 @@
         Rlog.d(LOG_TAG, msg);
     }
 
+    private static void loge(String msg) {
+        Rlog.e(LOG_TAG, msg);
+    }
+
     private final String mAid;
     private final boolean mSupportExtendedApdu;
     private final OpenLogicalChannelInvocation mOpenChannel;
@@ -76,10 +90,16 @@
     private final Context mContext;
     private final String mChannelKey;
     private final String mChannelResponseKey;
+    // closeAnyOpenChannel() needs a handler for its async callbacks.
+    private final Handler mHandler;
 
-    // Lock for accessing mChannelOpened. We only allow to open a single logical channel at any
-    // time for an AID.
-    private final Object mChannelLock = new Object();
+    // Lock for accessing mChannelInUse. We only allow to open a single logical
+    // channel at any time for an AID and to invoke one command at any time.
+    // Only the thread (and its async callbacks) that sets mChannelInUse
+    // can open/close/send, and update mChannelOpened.
+    private final Object mChannelInUseLock = new Object();
+    @GuardedBy("mChannelInUseLock")
+    private boolean mChannelInUse;
     private boolean mChannelOpened;
 
     /**
@@ -98,7 +118,8 @@
         mTransmitApdu = new TransmitApduLogicalChannelInvocation(ci);
         mChannelKey = CHANNEL_ID_PRE + "_" + phoneId;
         mChannelResponseKey = CHANNEL_RESPONSE_ID_PRE + "_" + phoneId;
-        closeExistingChannelIfExists();
+        mHandler = new Handler();
+        mChannelInUse = false;
     }
 
     /**
@@ -117,83 +138,125 @@
             RequestProvider requestProvider,
             ApduSenderResultCallback resultCallback,
             Handler handler) {
-        synchronized (mChannelLock) {
-            if (mChannelOpened) {
-                if (!Looper.getMainLooper().equals(Looper.myLooper())) {
-                    logd("Logical channel has already been opened. Wait.");
-                    try {
-                        mChannelLock.wait(WAIT_TIME_MS);
-                    } catch (InterruptedException e) {
-                        // nothing to do
-                    }
-                    if (mChannelOpened) {
-                        AsyncResultHelper.throwException(
-                                new ApduException("The logical channel is still in use."),
-                                resultCallback, handler);
-                        return;
-                    }
-                } else {
-                    AsyncResultHelper.throwException(
-                            new ApduException("The logical channel is in use."),
-                            resultCallback, handler);
-                    return;
-                }
-            }
-            mChannelOpened = true;
+        if (!acquireChannelLock()) {
+            AsyncResultHelper.throwException(
+                    new ApduException("The logical channel is still in use."),
+                    resultCallback,
+                    handler);
+            return;
         }
 
-        mOpenChannel.invoke(mAid, new AsyncResultCallback<IccOpenLogicalChannelResponse>() {
-            @Override
-            public void onResult(IccOpenLogicalChannelResponse openChannelResponse) {
-                int channel = openChannelResponse.getChannel();
-                int status = openChannelResponse.getStatus();
-                byte[] selectResponse = openChannelResponse.getSelectResponse();
-                if (status == IccOpenLogicalChannelResponse.STATUS_NO_SUCH_ELEMENT) {
-                    channel = PreferenceManager.getDefaultSharedPreferences(mContext)
-                                .getInt(mChannelKey, IccOpenLogicalChannelResponse.INVALID_CHANNEL);
-                    if (channel != IccOpenLogicalChannelResponse.INVALID_CHANNEL) {
-                        logv("Try to use already opened channel: " + channel);
-                        status = IccOpenLogicalChannelResponse.STATUS_NO_ERROR;
-                        String storedResponse = PreferenceManager
-                                .getDefaultSharedPreferences(mContext)
-                                      .getString(mChannelResponseKey, "");
-                        selectResponse = Base64.decode(storedResponse, Base64.DEFAULT);
-                    }
-                }
-                if (channel == IccOpenLogicalChannelResponse.INVALID_CHANNEL
-                        || status != IccOpenLogicalChannelResponse.STATUS_NO_ERROR) {
-                    synchronized (mChannelLock) {
-                        mChannelOpened = false;
-                        mChannelLock.notify();
-                    }
-                    resultCallback.onException(
-                            new ApduException("Failed to open logical channel opened for AID: "
-                                    + mAid + ", with status: " + status));
-                    return;
-                }
-
-                RequestBuilder builder = new RequestBuilder(channel, mSupportExtendedApdu);
-                Throwable requestException = null;
-                PreferenceManager.getDefaultSharedPreferences(mContext)
-                        .edit().putInt(mChannelKey, channel).apply();
-                PreferenceManager.getDefaultSharedPreferences(mContext)
-                        .edit().putString(mChannelResponseKey,
-                                Base64.encodeToString(selectResponse, Base64.DEFAULT)).apply();
-                try {
-                    requestProvider.buildRequest(selectResponse, builder);
-                } catch (Throwable e) {
-                    requestException = e;
-                }
-                if (builder.getCommands().isEmpty() || requestException != null) {
-                    // Just close the channel if we don't have commands to send or an error
-                    // was encountered.
-                    closeAndReturn(channel, null /* response */, requestException, resultCallback,
-                            handler);
-                    return;
-                }
-                sendCommand(builder.getCommands(), 0 /* index */, resultCallback, handler);
+        boolean euiccSession = EuiccSession.get().hasSession();
+        // Case 1, channel was already opened AND EuiccSession is ongoing.
+        // sendCommand directly. Do not immediately close channel after sendCommand.
+        // Case 2, channel was already opened AND EuiccSession is not ongoing. This means
+        // EuiccSession#endSession is already called but closeAnyOpenChannel() is not
+        // yet executed because of waiting to acquire lock hold by this thread.
+        // sendCommand directly. Close channel immediately anyways after sendCommand.
+        // Case 3, channel is not open AND EuiccSession is ongoing. Open channel
+        // before sendCommand. Do not immediately close channel after sendCommand.
+        // Case 4, channel is not open AND EuiccSession is not ongoing. Open channel
+        // before sendCommand. Close channel immediately after sendCommand.
+        if (mChannelOpened) {  // Case 1 or 2
+            if (euiccSession) {
+                EuiccSession.get().noteChannelOpen(this);
             }
-        }, handler);
+            RequestBuilder builder = getRequestBuilderWithOpenedChannel(requestProvider,
+                    !euiccSession /* closeChannelImmediately */, resultCallback, handler);
+            if (builder == null) {
+                return;
+            }
+            sendCommand(builder.getCommands(), 0 /* index */,
+                    !euiccSession /* closeChannelImmediately */, resultCallback, handler);
+        } else {  // Case 3 or 4
+            if (euiccSession) {
+                EuiccSession.get().noteChannelOpen(this);
+            }
+            openChannel(requestProvider,
+                    !euiccSession /* closeChannelImmediately */, resultCallback, handler);
+        }
+    }
+
+    private RequestBuilder getRequestBuilderWithOpenedChannel(
+            RequestProvider requestProvider,
+            boolean closeChannelImmediately,
+            ApduSenderResultCallback resultCallback,
+            Handler handler) {
+        Throwable requestException = null;
+        int channel =
+                PreferenceManager.getDefaultSharedPreferences(mContext)
+                        .getInt(mChannelKey, IccOpenLogicalChannelResponse.INVALID_CHANNEL);
+        String storedResponse =
+                PreferenceManager.getDefaultSharedPreferences(mContext)
+                        .getString(mChannelResponseKey, "");
+        byte[] selectResponse = Base64.decode(storedResponse, Base64.DEFAULT);
+        RequestBuilder builder = new RequestBuilder(channel, mSupportExtendedApdu);
+        try {
+            requestProvider.buildRequest(selectResponse, builder);
+        } catch (Throwable e) {
+            requestException = e;
+        }
+        if (builder.getCommands().isEmpty() || requestException != null) {
+            logd("Release as commands are empty or exception occurred");
+            returnRespnseOrException(channel, closeChannelImmediately,
+                    null /* response */, requestException, resultCallback, handler);
+            return null;
+        }
+        return builder;
+    }
+
+    private void openChannel(
+            RequestProvider requestProvider,
+            boolean closeChannelImmediately,
+            ApduSenderResultCallback resultCallback,
+            Handler handler) {
+        mOpenChannel.invoke(mAid, new AsyncResultCallback<IccOpenLogicalChannelResponse>() {
+                    @Override
+                    public void onResult(IccOpenLogicalChannelResponse openChannelResponse) {
+                        int channel = openChannelResponse.getChannel();
+                        int status = openChannelResponse.getStatus();
+                        byte[] selectResponse = openChannelResponse.getSelectResponse();
+                        if (status == IccOpenLogicalChannelResponse.STATUS_NO_SUCH_ELEMENT) {
+                            channel = PreferenceManager.getDefaultSharedPreferences(mContext)
+                                            .getInt(mChannelKey,
+                                                    IccOpenLogicalChannelResponse.INVALID_CHANNEL);
+                            if (channel != IccOpenLogicalChannelResponse.INVALID_CHANNEL) {
+                                logv("Try to use already opened channel: " + channel);
+                                status = IccOpenLogicalChannelResponse.STATUS_NO_ERROR;
+                                String storedResponse = PreferenceManager
+                                        .getDefaultSharedPreferences(mContext)
+                                              .getString(mChannelResponseKey, "");
+                                selectResponse = Base64.decode(storedResponse, Base64.DEFAULT);
+                            }
+                        }
+
+                        if (channel == IccOpenLogicalChannelResponse.INVALID_CHANNEL
+                                || status != IccOpenLogicalChannelResponse.STATUS_NO_ERROR) {
+                            mChannelOpened = false;
+                            resultCallback.onException(
+                                    new ApduException("Failed to open logical channel for AID: "
+                                            + mAid + ", with status: " + status));
+                            return;
+                        }
+                        PreferenceManager.getDefaultSharedPreferences(mContext)
+                                .edit()
+                                .putInt(mChannelKey, channel)
+                                .putString(mChannelResponseKey,
+                                    Base64.encodeToString(selectResponse, Base64.DEFAULT)).apply();
+                        mChannelOpened = true;
+
+                        RequestBuilder builder =
+                                getRequestBuilderWithOpenedChannel(requestProvider,
+                                        closeChannelImmediately, resultCallback, handler);
+                        if (builder == null) {
+                            return;
+                        }
+
+                        sendCommand(builder.getCommands(), 0 /* index */,
+                                closeChannelImmediately, resultCallback, handler);
+                    }
+                },
+                handler);
     }
 
     /**
@@ -206,6 +269,7 @@
     private void sendCommand(
             List<ApduCommand> commands,
             int index,
+            boolean closeChannelImmediately,
             ApduSenderResultCallback resultCallback,
             Handler handler) {
         ApduCommand command = commands.get(index);
@@ -220,9 +284,21 @@
                             public void onResult(IccIoResult fullResponse) {
                                 logv("Full APDU response: " + fullResponse);
                                 int status = (fullResponse.sw1 << 8) | fullResponse.sw2;
-                                if (status != STATUS_NO_ERROR && fullResponse.sw1 != SW1_NO_ERROR) {
-                                    closeAndReturn(command.channel, null /* response */,
-                                            new ApduException(status), resultCallback, handler);
+                                if (status != STATUS_NO_ERROR
+                                        && fullResponse.sw1 != SW1_NO_ERROR) {
+                                    if (status == STATUS_CHANNEL_CLOSED) {
+                                        // Channel is closed by EUICC e.g. REFRESH.
+                                        tearDownPreferences();
+                                        mChannelOpened = false;
+                                        // TODO: add retry
+                                    }
+                                    returnRespnseOrException(
+                                            command.channel,
+                                            closeChannelImmediately,
+                                            null /* response */,
+                                            new ApduException(status),
+                                            resultCallback,
+                                            handler);
                                     return;
                                 }
 
@@ -232,11 +308,17 @@
                                                 fullResponse);
                                 if (continueSendCommand) {
                                     // Sends the next command
-                                    sendCommand(commands, index + 1, resultCallback, handler);
+                                    sendCommand(commands, index + 1,
+                                            closeChannelImmediately, resultCallback, handler);
                                 } else {
                                     // Returns the result of the last command
-                                    closeAndReturn(command.channel, fullResponse.payload,
-                                            null /* exception */, resultCallback, handler);
+                                    returnRespnseOrException(
+                                            command.channel,
+                                            closeChannelImmediately,
+                                            fullResponse.payload,
+                                            null /* exception */,
+                                            resultCallback,
+                                            handler);
                                 }
                             }
                         }, handler);
@@ -285,6 +367,41 @@
                 }, handler);
     }
 
+    private void tearDownPreferences() {
+        PreferenceManager.getDefaultSharedPreferences(mContext)
+                .edit()
+                .remove(mChannelKey)
+                .remove(mChannelResponseKey)
+                .apply();
+    }
+
+    /**
+     * Fires the {@code resultCallback} to return a response or exception. Also
+     * closes the open logical channel if {@code closeChannelImmediately} is {@code true}.
+     */
+    private void returnRespnseOrException(
+            int channel,
+            boolean closeChannelImmediately,
+            @Nullable byte[] response,
+            @Nullable Throwable exception,
+            ApduSenderResultCallback resultCallback,
+            Handler handler) {
+        if (closeChannelImmediately) {
+            closeAndReturn(
+                    channel,
+                    response,
+                    exception,
+                    resultCallback,
+                    handler);
+        } else {
+            releaseChannelLockAndReturn(
+                    response,
+                    exception,
+                    resultCallback,
+                    handler);
+        }
+    }
+
     /**
      * Closes the opened logical channel.
      *
@@ -302,14 +419,9 @@
         mCloseChannel.invoke(channel, new AsyncResultCallback<Boolean>() {
             @Override
             public void onResult(Boolean aBoolean) {
-                synchronized (mChannelLock) {
-                    PreferenceManager.getDefaultSharedPreferences(mContext)
-                            .edit().remove(mChannelKey).apply();
-                    PreferenceManager.getDefaultSharedPreferences(mContext)
-                            .edit().remove(mChannelResponseKey).apply();
-                    mChannelOpened = false;
-                    mChannelLock.notify();
-                }
+                tearDownPreferences();
+                mChannelOpened = false;
+                releaseChannelLock();
 
                 if (exception == null) {
                     resultCallback.onResult(response);
@@ -321,37 +433,97 @@
     }
 
     /**
-     * Cleanup the existing opened channel which was remainined opened earlier due
-     * to failure or crash.
+     * Cleanup the existing opened channel which remained opened earlier due
+     * to:
+     *
+     * <p> 1) onging EuiccSession. This will be called by {@link EuiccSession#endSession()}
+     * from non-main-thread. Or,
+     *
+     * <p> 2) telephony crash. This will be called by constructor from main-thread.
      */
-    private void closeExistingChannelIfExists() {
-        if (mCloseChannel != null) {
-            int channelId = PreferenceManager.getDefaultSharedPreferences(mContext)
+    public void closeAnyOpenChannel() {
+        if (!acquireChannelLock()) {
+            // This cannot happen for case 2) when called by constructor
+            loge("[closeAnyOpenChannel] failed to acquire channel lock");
+            return;
+        }
+        int channelId = PreferenceManager.getDefaultSharedPreferences(mContext)
                 .getInt(mChannelKey, IccOpenLogicalChannelResponse.INVALID_CHANNEL);
-            if (channelId != IccOpenLogicalChannelResponse.INVALID_CHANNEL) {
-                logv("Trying to clean up the opened channel : " +  channelId);
-                synchronized (mChannelLock) {
-                    mChannelOpened = true;
-                    mChannelLock.notify();
+        if (channelId == IccOpenLogicalChannelResponse.INVALID_CHANNEL) {
+            releaseChannelLock();
+            return;
+        }
+        logv("[closeAnyOpenChannel] closing the open channel : " +  channelId);
+        mCloseChannel.invoke(channelId, new AsyncResultCallback<Boolean>() {
+            @Override
+            public void onResult(Boolean isSuccess) {
+                if (isSuccess) {
+                    logv("[closeAnyOpenChannel] Channel closed successfully: " + channelId);
+                    tearDownPreferences();
                 }
-                mCloseChannel.invoke(channelId, new AsyncResultCallback<Boolean>() {
-                    @Override
-                    public void onResult(Boolean isSuccess) {
-                        if (isSuccess) {
-                          logv("Channel closed successfully: " +  channelId);
-                          PreferenceManager.getDefaultSharedPreferences(mContext)
-                                 .edit().remove(mChannelResponseKey).apply();
-                          PreferenceManager.getDefaultSharedPreferences(mContext)
-                                 .edit().remove(mChannelKey).apply();
-                       }
-
-                       synchronized (mChannelLock) {
-                           mChannelOpened = false;
-                           mChannelLock.notify();
-                      }
-                    }
-                }, new Handler());
+                // Even if CloseChannel failed, pretend that the channel is closed.
+                // So next send() will try open the channel again. If the channel is
+                // indeed still open, we use the channelId saved in sharedPref.
+                mChannelOpened = false;
+                releaseChannelLock();
             }
+        }, mHandler);
+    }
+
+    // releases channel and callback
+    private void releaseChannelLockAndReturn(
+            @Nullable byte[] response,
+            @Nullable Throwable exception,
+            ApduSenderResultCallback resultCallback,
+            Handler handler) {
+        handler.post(
+                () -> {
+                    releaseChannelLock();
+                    if (exception == null) {
+                        resultCallback.onResult(response);
+                    } else {
+                        resultCallback.onException(exception);
+                    }
+                });
+    }
+
+    private void releaseChannelLock() {
+        synchronized (mChannelInUseLock) {
+            logd("Channel lock released.");
+            mChannelInUse = false;
+            mChannelInUseLock.notify();
+        }
+    }
+
+    /**
+     * Acquires channel lock and returns {@code true} if successful.
+     *
+     * <p>It fails and returns {@code false} when:
+     * <ul>
+     *   <li>Called from main thread, and mChannelInUse=true, fails immediately.
+     *   <li>Called from non main thread, and mChannelInUse=true after 2 seconds waiting, fails.
+     * </ul>
+     */
+    private boolean acquireChannelLock() {
+        synchronized (mChannelInUseLock) {
+            if (mChannelInUse) {
+                if (!Looper.getMainLooper().equals(Looper.myLooper())) {
+                    logd("Logical channel is in use. Wait.");
+                    try {
+                        mChannelInUseLock.wait(WAIT_TIME_MS);
+                    } catch (InterruptedException e) {
+                        // nothing to do
+                    }
+                    if (mChannelInUse) {
+                        return false;
+                    }
+                } else {
+                    return false;
+                }
+            }
+            mChannelInUse = true;
+            logd("Channel lock acquired.");
+            return true;
         }
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierDisplayNameResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierDisplayNameResolverTest.java
index 2ccfe0c..17fb829 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierDisplayNameResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierDisplayNameResolverTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 
@@ -271,4 +272,14 @@
         assertThat(data.shouldShowPlmn()).isTrue();
         assertThat(data.getPlmn()).isEqualTo(HOME_PLMN_NUMERIC);
     }
+
+    @Test
+    public void testCarrierDisplayNameData_enforceNonNullDataSpn() {
+        try {
+            CarrierDisplayNameData cdnd = new CarrierDisplayNameData.Builder()
+                    .setSpn("testSpn").build();
+            fail("Expected IAE");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java
index 06b63a2..1c58ef2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierPrivilegesTrackerTest.java
@@ -52,6 +52,7 @@
 import android.net.Uri;
 import android.os.PersistableBundle;
 import android.os.Process;
+import android.os.UserHandle;
 import android.service.carrier.CarrierService;
 import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyManager;
@@ -187,9 +188,15 @@
             pkg.packageName = pkgCertInfo.pkgName;
             pkg.signatures = new Signature[] {new Signature(pkgCertInfo.cert)};
 
-            when(mPackageManager.getPackageInfo(
-                    eq(pkgCertInfo.pkgName), eq(PM_FLAGS)))
-                    .thenReturn(pkg);
+            if (mFeatureFlags.supportCarrierServicesForHsum()) {
+                when(mPackageManager.getPackageInfoAsUser(
+                        eq(pkgCertInfo.pkgName), eq(PM_FLAGS), anyInt()))
+                        .thenReturn(pkg);
+            } else {
+                when(mPackageManager.getPackageInfo(
+                        eq(pkgCertInfo.pkgName), eq(PM_FLAGS)))
+                        .thenReturn(pkg);
+            }
             when(mPackageManager.getPackageUidAsUser(
                     eq(pkgCertInfo.pkgName), eq(pkgCertInfo.userInfo.id)))
                     .thenReturn(pkgCertInfo.uid);
@@ -214,7 +221,8 @@
         ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
                 ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
         CarrierPrivilegesTracker cpt =
-                new CarrierPrivilegesTracker(mTestableLooper.getLooper(), mPhone, mContext);
+                new CarrierPrivilegesTracker(
+                        mTestableLooper.getLooper(), mPhone, mContext, mFeatureFlags);
         verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
                 listenerArgumentCaptor.capture());
         mCarrierConfigChangeListener = listenerArgumentCaptor.getAllValues().get(0);
@@ -579,8 +587,14 @@
 
         ResolveInfo pkg1ResolveInfo = new ResolveInfoBuilder().setActivity(PACKAGE_1).build();
         ResolveInfo pkg2ResolveInfo = new ResolveInfoBuilder().setActivity(PACKAGE_2).build();
-        when(mPackageManager.queryBroadcastReceivers(any(), anyInt())).thenReturn(
-                List.of(pkg1ResolveInfo, pkg2ResolveInfo));
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            when(mPackageManager.queryBroadcastReceiversAsUser(any(), anyInt(),
+                    anyInt())).thenReturn(
+                    List.of(pkg1ResolveInfo, pkg2ResolveInfo));
+        } else {
+            when(mPackageManager.queryBroadcastReceivers(any(), anyInt())).thenReturn(
+                    List.of(pkg1ResolveInfo, pkg2ResolveInfo));
+        }
 
         // SIM is READY
         sendSimCardStateChangedIntent(PHONE_ID, SIM_STATE_READY);
@@ -712,8 +726,13 @@
         // Update PACKAGE_1 to have no signatures
         PackageInfo pkg = new PackageInfo();
         pkg.packageName = PACKAGE_1;
-        when(mPackageManager.getPackageInfo(eq(PACKAGE_1), eq(PM_FLAGS)))
-                .thenReturn(pkg);
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            when(mPackageManager.getPackageInfoAsUser(eq(PACKAGE_1), eq(PM_FLAGS), anyInt()))
+                    .thenReturn(pkg);
+        } else {
+            when(mPackageManager.getPackageInfo(eq(PACKAGE_1), eq(PM_FLAGS)))
+                    .thenReturn(pkg);
+        }
 
         sendPackageChangedIntent(Intent.ACTION_PACKAGE_ADDED, PACKAGE_1);
         mTestableLooper.processAllMessages();
@@ -784,19 +803,35 @@
         setupInstalledPackages(
                 new PackageCertInfo(PACKAGE_1, CERT_1, USER_1, UID_1),
                 new PackageCertInfo(PACKAGE_2, CERT_2, USER_1, UID_2));
-        when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
-        when(mPackageManager.getPackageUid(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+        } else {
+            when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+            when(mPackageManager.getPackageUid(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+        }
         ResolveInfo resolveInfoPkg1 = new ResolveInfoBuilder().setService(PACKAGE_1).build();
-        doReturn(List.of(resolveInfoPkg1))
-                .when(mPackageManager)
-                .queryIntentServices(any(), anyInt());
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            doReturn(List.of(resolveInfoPkg1))
+                    .when(mPackageManager)
+                    .queryIntentServicesAsUser(any(), anyInt(), anyInt());
+        } else {
+            doReturn(List.of(resolveInfoPkg1))
+                    .when(mPackageManager)
+                    .queryIntentServices(any(), anyInt());
+        }
         mCarrierPrivilegesTracker = createCarrierPrivilegesTracker();
 
         // Package_1 is disabled
         when(mPackageManager.getApplicationEnabledSetting(eq(PACKAGE_1))).thenReturn(
                 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER);
-        doReturn(List.of()).when(
-                mPackageManager).queryIntentServices(any(), anyInt());
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            doReturn(List.of()).when(
+                    mPackageManager).queryIntentServicesAsUser(any(), anyInt(), anyInt());
+        } else {
+            doReturn(List.of()).when(
+                    mPackageManager).queryIntentServices(any(), anyInt());
+        }
         sendPackageChangedIntent(Intent.ACTION_PACKAGE_CHANGED, PACKAGE_1);
         mTestableLooper.processAllMessages();
 
@@ -807,8 +842,13 @@
         // Package_1 is re-enabled
         when(mPackageManager.getApplicationEnabledSetting(eq(PACKAGE_1))).thenReturn(
                 PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
-        doReturn(List.of(resolveInfoPkg1)).when(
-                mPackageManager).queryIntentServices(any(), anyInt());
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            doReturn(List.of(resolveInfoPkg1)).when(
+                    mPackageManager).queryIntentServicesAsUser(any(), anyInt(), anyInt());
+        } else {
+            doReturn(List.of(resolveInfoPkg1)).when(
+                    mPackageManager).queryIntentServices(any(), anyInt());
+        }
         sendPackageChangedIntent(Intent.ACTION_PACKAGE_CHANGED, PACKAGE_1);
         mTestableLooper.processAllMessages();
 
@@ -888,24 +928,45 @@
 
         ResolveInfo privilegeBroadcast = new ResolveInfoBuilder().setActivity(PACKAGE_1).build();
         ResolveInfo noPrivilegeBroadcast = new ResolveInfoBuilder().setActivity(PACKAGE_2).build();
-        when(mPackageManager.queryBroadcastReceivers(any(), anyInt())).thenReturn(
-                List.of(privilegeBroadcast, noPrivilegeBroadcast));
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            when(mPackageManager.queryBroadcastReceiversAsUser(any(), anyInt(),
+                    anyInt())).thenReturn(
+                    List.of(privilegeBroadcast, noPrivilegeBroadcast));
+        } else {
+            when(mPackageManager.queryBroadcastReceivers(any(), anyInt())).thenReturn(
+                    List.of(privilegeBroadcast, noPrivilegeBroadcast));
+        }
 
         ResolveInfo privilegeActivity = new ResolveInfoBuilder().setActivity(PACKAGE_3).build();
         ResolveInfo noPrivilegeActivity = new ResolveInfoBuilder().setActivity(PACKAGE_4).build();
-        when(mPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(
-                List.of(privilegeActivity, noPrivilegeActivity));
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            when(mPackageManager.queryIntentActivitiesAsUser(any(), anyInt(), anyInt())).thenReturn(
+                    List.of(privilegeActivity, noPrivilegeActivity));
+        } else {
+            when(mPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(
+                    List.of(privilegeActivity, noPrivilegeActivity));
+        }
 
         ResolveInfo privilegeService = new ResolveInfoBuilder().setService(PACKAGE_5).build();
         ResolveInfo noPrivilegeService = new ResolveInfoBuilder().setService(PACKAGE_6).build();
         // Use doReturn instead of when/thenReturn which has NPE with unknown reason
-        doReturn(List.of(privilegeService, noPrivilegeService)).when(
-                mPackageManager).queryIntentServices(any(), anyInt());
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            doReturn(List.of(privilegeService, noPrivilegeService)).when(
+                    mPackageManager).queryIntentServicesAsUser(any(), anyInt(), anyInt());
+        } else {
+            doReturn(List.of(privilegeService, noPrivilegeService)).when(
+                    mPackageManager).queryIntentServices(any(), anyInt());
+        }
 
         ResolveInfo privilegeProvider = new ResolveInfoBuilder().setProvider(PACKAGE_7).build();
         ResolveInfo noPrivilegeProvider = new ResolveInfoBuilder().setProvider(PACKAGE_8).build();
-        when(mPackageManager.queryIntentContentProviders(any(), anyInt())).thenReturn(
-                List.of(privilegeProvider, noPrivilegeProvider));
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            when(mPackageManager.queryIntentContentProvidersAsUser(any(), anyInt(), anyInt()))
+                    .thenReturn(List.of(privilegeProvider, noPrivilegeProvider));
+        } else {
+            when(mPackageManager.queryIntentContentProviders(any(), anyInt())).thenReturn(
+                    List.of(privilegeProvider, noPrivilegeProvider));
+        }
 
         mCarrierPrivilegesTracker = createCarrierPrivilegesTracker();
         Intent intent = new Intent(CarrierService.CARRIER_SERVICE_INTERFACE);
@@ -934,11 +995,19 @@
         ResolveInfo privilegeService = new ResolveInfoBuilder().setService(PACKAGE_1).build();
         ResolveInfo noPrivilegeService = new ResolveInfoBuilder().setService(PACKAGE_2).build();
         // Use doReturn instead of when/thenReturn which has NPE with unknown reason
-        doReturn(List.of(privilegeService, noPrivilegeService)).when(
-                mPackageManager).queryIntentServices(any(), anyInt());
-        when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
-        when(mPackageManager.getPackageUid(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
-        when(mPackageManager.getPackageUid(eq(PACKAGE_3), anyInt())).thenReturn(UID_1);
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            doReturn(List.of(privilegeService, noPrivilegeService)).when(
+                    mPackageManager).queryIntentServicesAsUser(any(), anyInt(), anyInt());
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_3), anyInt())).thenReturn(UID_1);
+        } else {
+            doReturn(List.of(privilegeService, noPrivilegeService)).when(
+                    mPackageManager).queryIntentServices(any(), anyInt());
+            when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+            when(mPackageManager.getPackageUid(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+            when(mPackageManager.getPackageUid(eq(PACKAGE_3), anyInt())).thenReturn(UID_1);
+        }
 
         // Get CS package name for the first time
         mCarrierPrivilegesTracker = createCarrierPrivilegesTracker();
@@ -947,7 +1016,11 @@
         mTestableLooper.processAllMessages();
 
         // Package manager should be queried from
-        verify(mPackageManager).queryIntentServices(any(), anyInt());
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            verify(mPackageManager).queryIntentServicesAsUser(any(), anyInt(), anyInt());
+        } else {
+            verify(mPackageManager).queryIntentServices(any(), anyInt());
+        }
         assertEquals(PACKAGE_1, carrierServicePackageName);
         assertEquals(UID_1, carrierServiceUid);
 
@@ -958,7 +1031,11 @@
         mTestableLooper.processAllMessages();
 
         // It should return the same result, but didn't query package manager
-        verify(mPackageManager, never()).queryIntentServices(any(), anyInt());
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            verify(mPackageManager, never()).queryIntentServicesAsUser(any(), anyInt(), anyInt());
+        } else {
+            verify(mPackageManager, never()).queryIntentServices(any(), anyInt());
+        }
         assertEquals(PACKAGE_1, carrierServicePackageName);
         assertEquals(UID_1, carrierServiceUid);
     }
@@ -978,12 +1055,21 @@
         ResolveInfo service1 = new ResolveInfoBuilder().setService(PACKAGE_1).build();
         ResolveInfo service2 = new ResolveInfoBuilder().setService(PACKAGE_2).build();
         // Use doReturn instead of when/thenReturn which has NPE with unknown reason
-        doReturn(List.of(service1, service2))
-                .when(mPackageManager)
-                .queryIntentServices(any(), anyInt());
-        when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
-        when(mPackageManager.getPackageUid(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
-        when(mPackageManager.getPackageUid(eq(PACKAGE_3), anyInt())).thenReturn(UID_1);
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            doReturn(List.of(service1, service2))
+                    .when(mPackageManager)
+                    .queryIntentServicesAsUser(any(), anyInt(), anyInt());
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_3), anyInt())).thenReturn(UID_1);
+        } else {
+            doReturn(List.of(service1, service2))
+                    .when(mPackageManager)
+                    .queryIntentServices(any(), anyInt());
+            when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+            when(mPackageManager.getPackageUid(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+            when(mPackageManager.getPackageUid(eq(PACKAGE_3), anyInt())).thenReturn(UID_1);
+        }
 
         // Verify that neither carrier service (no privileges, or carrier-config based privileges)
         // are accepted.
@@ -992,7 +1078,11 @@
         int carrierServiceUid = mCarrierPrivilegesTracker.getCarrierServicePackageUid();
         mTestableLooper.processAllMessages();
 
-        verify(mPackageManager).queryIntentServices(any(), anyInt());
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            verify(mPackageManager).queryIntentServicesAsUser(any(), anyInt(), anyInt());
+        } else {
+            verify(mPackageManager).queryIntentServices(any(), anyInt());
+        }
         assertNull(carrierServicePackageName);
         assertEquals(Process.INVALID_UID, carrierServiceUid);
     }
@@ -1008,11 +1098,19 @@
                 new PackageCertInfo(PACKAGE_3, CERT_1, USER_1, UID_1));
         // No CarrierService declared at all
         // Use doReturn instead of when/thenReturn which has NPE with unknown reason
-        doReturn(List.of()).when(
-                mPackageManager).queryIntentServices(any(), anyInt());
-        when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
-        when(mPackageManager.getPackageUid(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
-        when(mPackageManager.getPackageUid(eq(PACKAGE_3), anyInt())).thenReturn(UID_1);
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            doReturn(List.of()).when(
+                    mPackageManager).queryIntentServicesAsUser(any(), anyInt(), anyInt());
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_3), anyInt())).thenReturn(UID_1);
+        } else {
+            doReturn(List.of()).when(
+                    mPackageManager).queryIntentServices(any(), anyInt());
+            when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+            when(mPackageManager.getPackageUid(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+            when(mPackageManager.getPackageUid(eq(PACKAGE_3), anyInt())).thenReturn(UID_1);
+        }
 
         mCarrierPrivilegesTracker = createCarrierPrivilegesTracker();
         String carrierServicePackageName = mCarrierPrivilegesTracker.getCarrierServicePackageName();
@@ -1021,7 +1119,11 @@
 
         assertNull(carrierServicePackageName);
         assertEquals(Process.INVALID_UID, carrierServiceUid);
-        verify(mPackageManager).queryIntentServices(any(), anyInt());
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            verify(mPackageManager).queryIntentServicesAsUser(any(), anyInt(), anyInt());
+        } else {
+            verify(mPackageManager).queryIntentServices(any(), anyInt());
+        }
     }
 
     @Test
@@ -1030,10 +1132,17 @@
         setupInstalledPackages(new PackageCertInfo(PACKAGE_1, CERT_1, USER_1, UID_1));
         ResolveInfo carrierService = new ResolveInfoBuilder().setService(PACKAGE_1).build();
 
-        doReturn(List.of(carrierService))
-                .when(mPackageManager)
-                .queryIntentServices(any(), anyInt());
-        when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            doReturn(List.of(carrierService))
+                    .when(mPackageManager)
+                    .queryIntentServicesAsUser(any(), anyInt(), anyInt());
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+        } else {
+            doReturn(List.of(carrierService))
+                    .when(mPackageManager)
+                    .queryIntentServices(any(), anyInt());
+            when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+        }
 
         // Set override, and verify the carrier service package was not set due to a lack of a
         // matching cert.
@@ -1056,11 +1165,19 @@
         ResolveInfo service1 = new ResolveInfoBuilder().setService(PACKAGE_1).build();
         ResolveInfo service2 = new ResolveInfoBuilder().setService(PACKAGE_2).build();
 
-        doReturn(List.of(service1, service2))
-                .when(mPackageManager)
-                .queryIntentServices(any(), anyInt());
-        when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
-        when(mPackageManager.getPackageUid(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            doReturn(List.of(service1, service2))
+                    .when(mPackageManager)
+                    .queryIntentServicesAsUser(any(), anyInt(), anyInt());
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+        } else {
+            doReturn(List.of(service1, service2))
+                    .when(mPackageManager)
+                    .queryIntentServices(any(), anyInt());
+            when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+            when(mPackageManager.getPackageUid(eq(PACKAGE_2), anyInt())).thenReturn(UID_2);
+        }
 
         mCarrierPrivilegesTracker = createCarrierPrivilegesTracker();
 
@@ -1093,10 +1210,17 @@
         setupInstalledPackages(new PackageCertInfo(PACKAGE_1, CERT_1, USER_1, UID_1));
         ResolveInfo carrierService = new ResolveInfoBuilder().setService(PACKAGE_1).build();
 
-        doReturn(List.of(carrierService))
-                .when(mPackageManager)
-                .queryIntentServices(any(), anyInt());
-        when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            doReturn(List.of(carrierService))
+                    .when(mPackageManager)
+                    .queryIntentServicesAsUser(any(), anyInt(), anyInt());
+            when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+        } else {
+            doReturn(List.of(carrierService))
+                    .when(mPackageManager)
+                    .queryIntentServices(any(), anyInt());
+            when(mPackageManager.getPackageUid(eq(PACKAGE_1), anyInt())).thenReturn(UID_1);
+        }
 
         // Set override, and expect that an invalid package name would not be selected as the
         // carrier config service.
@@ -1114,21 +1238,40 @@
     }
 
     private void sendSimCardStateChangedIntent(int phoneId, int simState) {
-        mContext.sendBroadcast(
-                new Intent(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED)
-                        .putExtra(EXTRA_SIM_STATE, simState)
-                        .putExtra(PhoneConstants.PHONE_KEY, phoneId));
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            mContext.sendBroadcastAsUser(
+                    new Intent(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED)
+                            .putExtra(EXTRA_SIM_STATE, simState)
+                            .putExtra(PhoneConstants.PHONE_KEY, phoneId), UserHandle.ALL);
+        } else {
+            mContext.sendBroadcast(
+                    new Intent(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED)
+                            .putExtra(EXTRA_SIM_STATE, simState)
+                            .putExtra(PhoneConstants.PHONE_KEY, phoneId));
+        }
     }
 
     private void sendSimApplicationStateChangedIntent(int phoneId, int simState) {
-        mContext.sendBroadcast(
-                new Intent(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED)
-                        .putExtra(EXTRA_SIM_STATE, simState)
-                        .putExtra(PhoneConstants.PHONE_KEY, phoneId));
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            mContext.sendBroadcastAsUser(
+                    new Intent(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED)
+                            .putExtra(EXTRA_SIM_STATE, simState)
+                            .putExtra(PhoneConstants.PHONE_KEY, phoneId), UserHandle.ALL);
+        } else {
+            mContext.sendBroadcast(
+                    new Intent(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED)
+                            .putExtra(EXTRA_SIM_STATE, simState)
+                            .putExtra(PhoneConstants.PHONE_KEY, phoneId));
+        }
     }
 
     private void sendPackageChangedIntent(String action, String pkgName) {
-        mContext.sendBroadcast(new Intent(action, new Uri.Builder().path(pkgName).build()));
+        if (mFeatureFlags.supportCarrierServicesForHsum()) {
+            mContext.sendBroadcastAsUser(
+                    new Intent(action, new Uri.Builder().path(pkgName).build()), UserHandle.ALL);
+        } else {
+            mContext.sendBroadcast(new Intent(action, new Uri.Builder().path(pkgName).build()));
+        }
     }
 
     /** Returns the SHA-1 hash (as a hex String) for the given hex String. */
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierResolverTest.java
index a3fcd4e..753f85c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierResolverTest.java
@@ -98,7 +98,7 @@
         super.setUp(getClass().getSimpleName());
         ((MockContentResolver) mContext.getContentResolver()).addProvider(
                 CarrierId.AUTHORITY, new CarrierIdContentProvider());
-        mCarrierResolver = new CarrierResolver(mPhone);
+        mCarrierResolver = new CarrierResolver(mPhone, mFeatureFlags);
         mCarrierResolver.sendEmptyMessage(ICC_CHANGED_EVENT);
         processAllMessages();
         logd("CarrierResolverTest -Setup!");
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index 4612ad9..9788320 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -241,6 +241,14 @@
         }
 
         @Override
+        public boolean bindServiceAsUser(
+                Intent serviceIntent,
+                ServiceConnection connection,
+                int flags, UserHandle user) {
+            return bindService(serviceIntent, connection, flags);
+        }
+
+        @Override
         public void unbindService(
                 ServiceConnection connection) {
             IInterface service = mServiceByServiceConnection.remove(connection);
@@ -541,6 +549,12 @@
         }
 
         @Override
+        public void sendBroadcastAsUser(Intent intent, UserHandle user,
+                String receiverPermission, Bundle options) {
+            sendBroadcast(intent);
+        }
+
+        @Override
         public void sendBroadcastMultiplePermissions(Intent intent,
                 String[] includePermissions, String[] excludePermissions) {
             sendBroadcast(intent);
@@ -681,6 +695,9 @@
 
         @Override
         public void startActivity(Intent intent) {}
+
+        @Override
+        public void startActivityAsUser(Intent intent, UserHandle user) {}
     }
 
     private final Multimap<String, ComponentName> mComponentNamesByAction =
diff --git a/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java b/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
index 8dad3ec..da4bf50 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
@@ -378,6 +378,51 @@
 
     @Test
     @SmallTest
+    public void testNotifyCallbackModeStarted() {
+        doReturn(true).when(mFeatureFlags).emergencyCallbackModeNotification();
+        int phoneId = mPhone.getPhoneId();
+        int subId = mPhone.getSubId();
+        int type = 1;
+        long durationMillis = 1000;
+
+        mDefaultPhoneNotifierUT.notifyCallbackModeStarted(mPhone, type, durationMillis);
+
+        verify(mTelephonyRegistryManager).notifyCallbackModeStarted(eq(phoneId), eq(subId),
+                eq(type), eq(durationMillis));
+    }
+
+    @Test
+    @SmallTest
+    public void testNotifyCallbackModeRestarted() {
+        doReturn(true).when(mFeatureFlags).emergencyCallbackModeNotification();
+        int phoneId = mPhone.getPhoneId();
+        int subId = mPhone.getSubId();
+        int type = 1;
+        long durationMillis = 1000;
+
+        mDefaultPhoneNotifierUT.notifyCallbackModeRestarted(mPhone, type, durationMillis);
+
+        verify(mTelephonyRegistryManager).notifyCallbackModeRestarted(eq(phoneId), eq(subId),
+                eq(type), eq(durationMillis));
+    }
+
+    @Test
+    @SmallTest
+    public void testNotifyCallbackModeStopped() {
+        doReturn(true).when(mFeatureFlags).emergencyCallbackModeNotification();
+        int phoneId = mPhone.getPhoneId();
+        int subId = mPhone.getSubId();
+        int type = 1;
+        int reason = 0;
+
+        mDefaultPhoneNotifierUT.notifyCallbackModeStopped(mPhone, type, reason);
+
+        verify(mTelephonyRegistryManager).notifyCallbackModeStopped(eq(phoneId), eq(subId),
+                eq(type), eq(reason));
+    }
+
+    @Test
+    @SmallTest
     public void testCarrierRoamingNtnModeChanged() {
         int subId = mPhone.getSubId();
         mDefaultPhoneNotifierUT.notifyCarrierRoamingNtnModeChanged(mPhone, true);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/FdnUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/FdnUtilsTest.java
index 9da19bc..996fa2d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/FdnUtilsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/FdnUtilsTest.java
@@ -181,4 +181,13 @@
 
         assertTrue(FdnUtils.isFDN("1234560000@ims.mnc.org", "US", fdnList));
     }
+
+    @Test
+    public void dialStrInNationalFormat_returnsTrue() {
+        ArrayList<AdnRecord> fdnList = initializeFdnList();
+        AdnRecord adnRecord = new AdnRecord(null, "0469887529");
+        fdnList.add(8, adnRecord);
+
+        assertTrue(FdnUtils.isFDN("0469887529", "US", fdnList));
+    }
 }
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GbaManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/GbaManagerTest.java
index bad32e9..8898a0f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GbaManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GbaManagerTest.java
@@ -41,6 +41,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.telephony.IBootstrapAuthenticationCallback;
 import android.telephony.TelephonyManager;
 import android.telephony.gba.GbaAuthRequest;
@@ -51,8 +52,6 @@
 import android.testing.TestableLooper;
 import android.util.Log;
 
-import androidx.test.filters.SmallTest;
-
 import com.android.internal.telephony.metrics.RcsStats;
 
 import org.junit.After;
@@ -66,7 +65,7 @@
  */
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
-public final class GbaManagerTest {
+public final class GbaManagerTest extends TelephonyTest {
     private static final String LOG_TAG = "GbaManagerTest";
 
     private static final ComponentName TEST_DEFAULT_SERVICE_NAME = new ComponentName(
@@ -91,6 +90,7 @@
 
     @Before
     public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
         log("setUp");
         mMockContext = mock(Context.class);
         mMockBinder = mock(IBinder.class);
@@ -100,7 +100,8 @@
         if (Looper.myLooper() == null) {
             Looper.prepare();
         }
-        when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(true);
+        when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any(UserHandle.class)))
+                .thenReturn(true);
         when(mMockGbaServiceBinder.asBinder()).thenReturn(mMockBinder);
         mTestGbaManager = new GbaManager(mMockContext, TEST_SUB_ID, null, 0, mMockRcsStats);
         mHandler = mTestGbaManager.getHandler();
@@ -109,137 +110,129 @@
         } catch (Exception e) {
             fail("Unable to create looper from handler.");
         }
+        monitorTestableLooper(mLooper);
     }
 
     @After
     public void tearDown() throws Exception {
         log("tearDown");
         mTestGbaManager.destroy();
-        mTestGbaManager = null;
-        mHandler = null;
-        mLooper.destroy();
-        mLooper = null;
+        super.tearDown();
     }
 
     @Test
-    @SmallTest
     public void testFailOnRequest() throws Exception {
         GbaAuthRequest request = createDefaultRequest();
 
         mTestGbaManager.bootstrapAuthenticationRequest(request);
-        mLooper.processAllMessages();
+        processAllMessages();
 
-        verify(mMockContext, never()).bindService(any(), any(), anyInt());
+        verify(mMockContext, never()).bindServiceAsUser(any(), any(), anyInt(),
+                any(UserHandle.class));
         verify(mMockCallback).onAuthenticationFailure(anyInt(), anyInt());
         assertTrue(!mTestGbaManager.isServiceConnected());
     }
 
     @Test
-    @SmallTest
     public void testBindServiceOnRequest() throws Exception {
-        mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName());
+        mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName(), 123);
         GbaAuthRequest request = createDefaultRequest();
 
         mTestGbaManager.bootstrapAuthenticationRequest(request);
-        mLooper.processAllMessages();
+        processAllMessages();
         bindAndConnectService(TEST_DEFAULT_SERVICE_NAME);
-        mLooper.processAllMessages();
+        processAllMessages();
 
         verify(mMockGbaServiceBinder).authenticationRequest(any());
         assertTrue(mTestGbaManager.isServiceConnected());
     }
 
     @Test
-    @SmallTest
     public void testFailAndRetryOnRequest() throws RemoteException {
-        when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(false);
-        mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName());
+        when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any(UserHandle.class)))
+                .thenReturn(false);
+        mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName(), 123);
         GbaAuthRequest request = createDefaultRequest();
 
         mTestGbaManager.bootstrapAuthenticationRequest(request);
 
         for (int i = 0; i < GbaManager.MAX_RETRY; i++) {
-            mLooper.processAllMessages();
-            verify(mMockContext, times(i + 1)).bindService(any(), any(), anyInt());
-            try {
-                Thread.sleep(GbaManager.RETRY_TIME_MS + 500);
-            } catch (InterruptedException e) {
-            }
+            processAllMessages();
+            verify(mMockContext, times(i + 1)).bindServiceAsUser(any(), any(), anyInt(),
+                    any(UserHandle.class));
+            moveTimeForward(GbaManager.REQUEST_TIMEOUT_MS);
         }
         assertTrue(!mTestGbaManager.isServiceConnected());
-        mLooper.processAllMessages();
+        processAllMessages();
         verify(mMockCallback).onAuthenticationFailure(anyInt(), anyInt());
     }
 
     @Test
-    @SmallTest
     public void testBindServiceWhenPackageNameChanged() {
-        mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName());
+        mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName(), 123);
         mTestGbaManager.overrideReleaseTime(RELEASE_TIME_60S);
         GbaAuthRequest request = createDefaultRequest();
 
         mTestGbaManager.bootstrapAuthenticationRequest(request);
-        mLooper.processAllMessages();
+        processAllMessages();
         ServiceConnection conn = bindAndConnectService(TEST_DEFAULT_SERVICE_NAME);
-        mTestGbaManager.overrideServicePackage(TEST_SERVICE2_NAME.getPackageName());
+        mTestGbaManager.overrideServicePackage(TEST_SERVICE2_NAME.getPackageName(), 123);
 
         assertEquals(TEST_SERVICE2_NAME.getPackageName(), mTestGbaManager.getServicePackage());
 
-        mLooper.processAllMessages();
+        processAllMessages();
         unbindService(conn);
         bindAndConnectService(TEST_SERVICE2_NAME);
         assertTrue(mTestGbaManager.isServiceConnected());
     }
 
     @Test
-    @SmallTest
     public void testBindServiceWhenReleaseTimeChanged() {
-        mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName());
+        mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName(), 123);
         mTestGbaManager.overrideReleaseTime(RELEASE_NEVER);
 
         assertEquals(RELEASE_NEVER, mTestGbaManager.getReleaseTime());
-        mLooper.processAllMessages();
+        processAllMessages();
         bindAndConnectService(TEST_DEFAULT_SERVICE_NAME);
 
         assertTrue(mTestGbaManager.isServiceConnected());
     }
 
     @Test
-    @SmallTest
     public void testDontBindServiceWhenPackageNameChanged() {
-        mTestGbaManager.overrideServicePackage(TEST_SERVICE2_NAME.getPackageName());
+        mTestGbaManager.overrideServicePackage(TEST_SERVICE2_NAME.getPackageName(), 123);
 
-        mLooper.processAllMessages();
+        processAllMessages();
 
-        verify(mMockContext, never()).bindService(any(), any(), anyInt());
+        verify(mMockContext, never()).bindServiceAsUser(any(), any(), anyInt(),
+                any(UserHandle.class));
         assertTrue(!mTestGbaManager.isServiceConnected());
     }
 
     @Test
-    @SmallTest
     public void testDontBindServiceWhenReleaseTimeChanged() {
-        mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName());
+        mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName(), 123);
         mTestGbaManager.overrideReleaseTime(RELEASE_TIME_60S);
 
-        mLooper.processAllMessages();
+        processAllMessages();
 
-        verify(mMockContext, never()).bindService(any(), any(), anyInt());
+        verify(mMockContext, never()).bindServiceAsUser(any(), any(), anyInt(),
+                any(UserHandle.class));
         assertTrue(!mTestGbaManager.isServiceConnected());
     }
 
     @Test
-    @SmallTest
     public void testMetricsGbaEvent() throws Exception {
-        mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName());
+        mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName(), 123);
         mTestGbaManager.overrideReleaseTime(RELEASE_NEVER);
 
-        mLooper.processAllMessages();
+        processAllMessages();
         bindAndConnectService(TEST_DEFAULT_SERVICE_NAME);
         GbaAuthRequest request = createDefaultRequest();
 
         // Failure case
         mTestGbaManager.bootstrapAuthenticationRequest(request);
-        mLooper.processAllMessages();
+        processAllMessages();
 
         ArgumentCaptor<GbaAuthRequest> captor = ArgumentCaptor.forClass(GbaAuthRequest.class);
         verify(mMockGbaServiceBinder, times(1)).authenticationRequest(captor.capture());
@@ -254,7 +247,7 @@
 
         // Success case
         mTestGbaManager.bootstrapAuthenticationRequest(request);
-        mLooper.processAllMessages();
+        processAllMessages();
 
         ArgumentCaptor<GbaAuthRequest> captor2 = ArgumentCaptor.forClass(GbaAuthRequest.class);
         verify(mMockGbaServiceBinder, times(2)).authenticationRequest(captor2.capture());
@@ -280,9 +273,10 @@
                 ArgumentCaptor.forClass(Intent.class);
         ArgumentCaptor<ServiceConnection> serviceCaptor =
                 ArgumentCaptor.forClass(ServiceConnection.class);
-        verify(mMockContext, atLeastOnce()).bindService(intentCaptor.capture(),
+        verify(mMockContext, atLeastOnce()).bindServiceAsUser(intentCaptor.capture(),
                 serviceCaptor.capture(), eq(
-                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE));
+                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
+                any(UserHandle.class));
         Intent testIntent = intentCaptor.getValue();
         assertEquals(GbaService.SERVICE_INTERFACE, testIntent.getAction());
         assertEquals(component.getPackageName(), testIntent.getPackage());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java
index 45f8c12..e56ac90 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java
@@ -21,10 +21,12 @@
 import static org.junit.Assert.*;
 import static org.mockito.Mockito.*;
 
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.emergency.EmergencyNumber;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
@@ -313,4 +315,52 @@
         assertEquals(DisconnectCause.OUT_OF_SERVICE,
                 connection.disconnectCauseFromCode(CallFailCause.LOCAL_SERVICE_UNAVAILABLE));
     }
+
+    @Test
+    public void testUpdateEmergencyRouting() {
+        Bundle extras = new Bundle();
+        extras.putBoolean(PhoneConstants.EXTRA_USE_EMERGENCY_ROUTING, true);
+
+        DialArgs dialArgs = new DialArgs.Builder()
+                .setIsEmergency(true)
+                .setIntentExtras(extras)
+                .build();
+
+        doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
+
+        connection = new GsmCdmaConnection(mPhone, "911", mCT, null, dialArgs);
+        // Not updated when category is unset.
+        assertEquals(getTestEmergencyNumber(), connection.getEmergencyNumberInfo());
+
+        extras.putInt(PhoneConstants.EXTRA_EMERGENCY_SERVICE_CATEGORY,
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED);
+
+        dialArgs = new DialArgs.Builder()
+                .setIsEmergency(true)
+                .setIntentExtras(extras)
+                .build();
+
+        connection = new GsmCdmaConnection(mPhone, "911", mCT, null, dialArgs);
+        // Not updated when category is EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED.
+        assertEquals(getTestEmergencyNumber(), connection.getEmergencyNumberInfo());
+
+        extras.putInt(PhoneConstants.EXTRA_EMERGENCY_SERVICE_CATEGORY,
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE);
+
+        dialArgs = new DialArgs.Builder()
+                .setIsEmergency(true)
+                .setIntentExtras(extras)
+                .build();
+
+        connection = new GsmCdmaConnection(mPhone, "911", mCT, null, dialArgs);
+
+        EmergencyNumber expectedNumber = new EmergencyNumber("911", "us", "30",
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE,
+                new ArrayList<String>(), EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
+
+        // Updated when category is not EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED.
+        assertNotEquals(getTestEmergencyNumber(), connection.getEmergencyNumberInfo());
+        assertEquals(expectedNumber, connection.getEmergencyNumberInfo());
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index ba08f8b..7735c97 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -2018,6 +2018,42 @@
 
     @Test
     @SmallTest
+    public void testUsageSettingUpdate_ResetToDefault() {
+        setupUsageSettingResources();
+        mPhoneUT.mCi = mMockCi;
+
+        SubscriptionInfoInternal si = makeSubscriptionInfoInternal(
+                false, SubscriptionManager.USAGE_SETTING_DATA_CENTRIC);
+        doReturn(si).when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
+
+        mPhoneUT.updateUsageSetting();
+        processAllMessages();
+
+        verify(mMockCi).getUsageSetting(any());
+        mPhoneUT.sendMessage(mPhoneUT.obtainMessage(GsmCdmaPhone.EVENT_GET_USAGE_SETTING_DONE,
+                new AsyncResult(null,
+                        new int[]{SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC}, null)));
+        processAllMessages();
+
+        // Grab the message to ensure it returns the preferred value for updating the cache
+        ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+        verify(mMockCi).setUsageSetting(
+                messageCaptor.capture(), eq(SubscriptionManager.USAGE_SETTING_DATA_CENTRIC));
+        AsyncResult.forMessage(messageCaptor.getValue());
+        messageCaptor.getValue().sendToTarget();
+        processAllMessages();
+
+        si = makeSubscriptionInfoInternal(false, SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC);
+        doReturn(si).when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
+
+        mPhoneUT.updateUsageSetting();
+        processAllMessages();
+
+        verify(mMockCi).setUsageSetting(any(), eq(SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC));
+    }
+
+    @Test
+    @SmallTest
     public void testUsageSettingUpdate_DefaultOpportunistic() {
         setupUsageSettingResources();
         mPhoneUT.mCi = mMockCi;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
index 738f765..f92643a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
@@ -1649,8 +1649,23 @@
                 mNetworkTypeController.getOverrideNetworkType());
         assertTrue(mNetworkTypeController.areAnyTimersActive());
 
+
+        // the timer has been reduced from 20 - 6s(advance band) to 5s(regular). Suppose passed 1s,
+        // a new PCC shouldn't affect the timer.
+        moveTimeForward(1 * 1000);
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, Collections.emptyList(), null));
+        mNetworkTypeController.sendMessage(11 /* EVENT_PHYSICAL_CHANNEL_CONFIGS_CHANGED */,
+                new AsyncResult(null, List.of(
+                        new PhysicalChannelConfig.Builder()
+                                .setPhysicalCellId(3)
+                                .setNetworkType(TelephonyManager.NETWORK_TYPE_NR)
+                                .setCellConnectionStatus(CellInfo.CONNECTION_PRIMARY_SERVING)
+                                .build()), null));
+        processAllMessages();
+
         // Verify the timer has been reduced from 20 - 6s(advance band) to 5s(regular).
-        moveTimeForward(5 * 1000);
+        moveTimeForward(4 * 1000);
         processAllMessages();
 
         assertEquals("connected_rrc_idle", getCurrentState().getName());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
index 88c5389..48c9f9c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
@@ -175,6 +175,7 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.SparseArray;
+import android.view.Display;
 
 import androidx.test.filters.FlakyTest;
 
@@ -312,6 +313,7 @@
         } catch (RuntimeException e) {
         }
         Context context = new ContextFixture().getTestDouble();
+        doReturn(Display.DEFAULT_DISPLAY).when(context).getDisplayId();
         doReturn(true).when(mConnectionManager).isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
         doReturn(mConnectionManager).when(context)
             .getSystemService(Context.CONNECTIVITY_SERVICE);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 1465176..9bee1ac 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -2983,7 +2983,7 @@
         doReturn(ServiceState.STATE_IN_SERVICE).when(mSST).getCombinedRegState(ss);
 
         // update the spn
-        sst.updateSpnDisplay();
+        sst.updateCarrierDisplayName();
 
         // Plmn should be shown, and the string is "Emergency call only"
         Bundle b = getExtrasFromLastSpnUpdateIntent();
@@ -3005,7 +3005,7 @@
         sst.mSS = ss;
 
         // update the spn
-        sst.updateSpnDisplay();
+        sst.updateCarrierDisplayName();
 
         // Plmn should be shown, and the string is "No service"
         Bundle b = getExtrasFromLastSpnUpdateIntent();
@@ -3026,7 +3026,7 @@
         sst.mSS = ss;
 
         // update the spn
-        sst.updateSpnDisplay();
+        sst.updateCarrierDisplayName();
 
         // Plmn should be shown, and the string is null
         Bundle b = getExtrasFromLastSpnUpdateIntent();
@@ -3049,7 +3049,7 @@
         doReturn(false).when(mPhone).isWifiCallingEnabled();
 
         // update the spn
-        sst.updateSpnDisplay();
+        sst.updateCarrierDisplayName();
 
         // Show both spn & plmn
         String spn = mBundle.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING);
@@ -3091,7 +3091,7 @@
         doReturn(formats).when(r).getStringArray(anyInt());
 
         // update the spn
-        sst.updateSpnDisplay();
+        sst.updateCarrierDisplayName();
 
         // Only spn should be shown
         String spn = mBundle.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING);
@@ -3128,7 +3128,7 @@
         doReturn(true).when(mPhone).isImsRegistered();
 
         // update the spn
-        sst.updateSpnDisplay();
+        sst.updateCarrierDisplayName();
 
         // Only spn should be shown
         String spn = mBundle.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING);
@@ -3161,7 +3161,7 @@
         doReturn(true).when(mPhone).isImsRegistered();
 
         // update the spn
-        sst.updateSpnDisplay();
+        sst.updateCarrierDisplayName();
 
         // Only plmn should be shown
         String plmn = mBundle.getStringArray(CarrierConfigManager.KEY_PNN_OVERRIDE_STRING_ARRAY)[0];
@@ -3187,7 +3187,7 @@
         doReturn(false).when(mPhone).isWifiCallingEnabled();
 
         // update the spn
-        sst.updateSpnDisplay();
+        sst.updateCarrierDisplayName();
 
         // Show both spn & plmn
         String spn = mBundle.getString(CarrierConfigManager.KEY_CARRIER_NAME_STRING);
@@ -3240,7 +3240,7 @@
         doReturn(false).when(mPhone).isWifiCallingEnabled();
 
         // update the spn
-        sst.updateSpnDisplay();
+        sst.updateCarrierDisplayName();
 
         // Plmn should be shown, and the string is "No service"
         Bundle b = getExtrasFromLastSpnUpdateIntent();
@@ -3271,7 +3271,7 @@
         doReturn(true).when(mPhone).isWifiCallingEnabled();
 
         // update the spn
-        sst.updateSpnDisplay();
+        sst.updateCarrierDisplayName();
 
         // Plmn should be shown, and the string is "No service"
         Bundle b = getExtrasFromLastSpnUpdateIntent();
@@ -3303,7 +3303,7 @@
         doReturn(true).when(mPhone).isWifiCallingEnabled();
 
         // update the spn
-        sst.updateSpnDisplay();
+        sst.updateCarrierDisplayName();
 
         // Plmn should be shown, and the string is "No service"
         Bundle b = getExtrasFromLastSpnUpdateIntent();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
index 12a119c..8f0cbbb 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
@@ -25,6 +25,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.isNull;
@@ -116,17 +117,23 @@
 
         public void testNotifySmsSentToEmergencyStateTracker(String destAddr, long messageId,
                 boolean isOverIms, boolean isLastSmsPart) {
-            notifySmsSent(destAddr, messageId, isOverIms, isLastSmsPart, true/*success*/);
+            notifySmsSent(getSmsTracker(destAddr, messageId), isOverIms,
+                isLastSmsPart, true/*success*/);
         }
 
         public void testNotifySmsSentFailedToEmergencyStateTracker(String destAddr,
                 long messageId, boolean isOverIms) {
-            notifySmsSent(destAddr, messageId, isOverIms, true/*isLastSmsPart*/, false/*success*/);
+            notifySmsSent(getSmsTracker(destAddr, messageId), isOverIms,
+                true/*isLastSmsPart*/, false/*success*/);
         }
 
         public void testNotifySmsReceivedViaImsToEmergencyStateTracker(String origAddr) {
             notifySmsReceivedViaImsToEmergencyStateTracker(origAddr);
         }
+
+        private SMSDispatcher.SmsTracker getSmsTracker(String destAddr, long messageId) {
+            return new SMSDispatcher.SmsTracker(destAddr, messageId);
+        }
     }
 
     /**
@@ -140,9 +147,9 @@
         @Override
         public void sendData(String callingPackage, int callingUser, String destAddr,
                 String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
-                PendingIntent deliveryIntent, boolean isForVvm) {
+                PendingIntent deliveryIntent, boolean isForVvm, long uniqueMessageId) {
             super.sendData(callingPackage, callingUser, destAddr, scAddr, destPort,
-                    data, sentIntent, deliveryIntent, isForVvm);
+                    data, sentIntent, deliveryIntent, isForVvm, uniqueMessageId);
         }
 
         @Override
@@ -167,9 +174,9 @@
         @Override
         public void sendData(String callingPackage, int callingUser, String destAddr, String scAddr,
                 int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent,
-                boolean isForVvm) {
+                boolean isForVvm, long uniqueMessageId) {
             super.sendData(callingPackage, callingUser, destAddr, scAddr, destPort,
-                    data, sentIntent, deliveryIntent, isForVvm);
+                    data, sentIntent, deliveryIntent, isForVvm, uniqueMessageId);
         }
 
         @Override
@@ -244,7 +251,7 @@
     @Test @SmallTest
     public void testReportSmsMemoryStatus() throws Exception {
         int eventReportMemoryStatusDone = 3;
-        SmsStorageMonitor smsStorageMonnitor = new SmsStorageMonitor(mPhone);
+        SmsStorageMonitor smsStorageMonnitor = new SmsStorageMonitor(mPhone, mFeatureFlags);
         Message result = smsStorageMonnitor.obtainMessage(eventReportMemoryStatusDone);
         ImsSmsDispatcher mImsSmsDispatcher = Mockito.mock(ImsSmsDispatcher.class);
         mSmsDispatchersController.setImsSmsDispatcher(mImsSmsDispatcher);
@@ -257,7 +264,7 @@
     @Test @SmallTest
     public void testReportSmsMemoryStatusFailure() throws Exception {
         int eventReportMemoryStatusDone = 3;
-        SmsStorageMonitor smsStorageMonnitor = new SmsStorageMonitor(mPhone);
+        SmsStorageMonitor smsStorageMonnitor = new SmsStorageMonitor(mPhone, mFeatureFlags);
         Message result = smsStorageMonnitor.obtainMessage(eventReportMemoryStatusDone);
         mSmsDispatchersController.setImsSmsDispatcher(null);
         mSmsDispatchersController.reportSmsMemoryStatus(result);
@@ -491,7 +498,7 @@
         verify(mEmergencySmsDsc).finishSelection();
         verify(mImsSmsDispatcher).sendText(eq("911"), eq("2222"), eq("text"), eq(mSentIntent),
                 any(), any(), eq("test-app"), eq(mCallingUserId), eq(false),
-                eq(0), eq(false), eq(10), eq(false), eq(1L), eq(false));
+                eq(0), eq(false), eq(10), eq(false), eq(1L), eq(false), anyLong());
         assertNull(holder.getConnection());
         assertFalse(holder.isDomainSelectionRequested());
         assertEquals(0, holder.getPendingRequests().size());
@@ -542,7 +549,7 @@
         verify(mImsSmsDispatcher).sendMultipartText(eq("911"), eq("2222"), eq(parts),
                 eq(sentIntents), eq(deliveryIntents), any(), eq("test-app"),
                 eq(mCallingUserId), eq(false), eq(0), eq(false),
-                eq(10), eq(1L));
+                eq(10), eq(1L), anyLong());
         assertNull(holder.getConnection());
         assertFalse(holder.isDomainSelectionRequested());
         assertEquals(0, holder.getPendingRequests().size());
@@ -741,7 +748,7 @@
 
         verify(mImsSmsDispatcher).sendText(eq("1111"), eq("2222"), eq("text"), eq(mSentIntent),
                 any(), any(), eq("test-app"), eq(mCallingUserId),
-                eq(false), eq(0), eq(false), eq(10), eq(false), eq(1L), eq(false));
+                eq(false), eq(0), eq(false), eq(10), eq(false), eq(1L), eq(false), anyLong());
     }
 
     @Test
@@ -782,7 +789,7 @@
 
         verify(mImsSmsDispatcher).sendText(eq("911"), eq("2222"), eq("text"), eq(mSentIntent),
                 any(), any(), eq("test-app"), eq(0), eq(false), eq(0), eq(false), eq(10),
-                eq(false), eq(1L), eq(false));
+                eq(false), eq(1L), eq(false), anyLong());
     }
 
     @Test
@@ -818,7 +825,7 @@
         verify(mImsSmsDispatcher, times(2)).sendText(eq("1111"), eq("2222"),
                 eq("text"), eq(mSentIntent), any(), any(), eq("test-app"), eq(mCallingUserId),
                 eq(false), eq(0), eq(false), eq(10), eq(false), eq(1L),
-                eq(false));
+                eq(false), anyLong());
         assertNull(holder.getConnection());
         assertFalse(holder.isDomainSelectionRequested());
         assertEquals(0, holder.getPendingRequests().size());
@@ -839,7 +846,7 @@
         // ImsSmsDispatcher handles this text directly.
         verify(mImsSmsDispatcher).sendText(eq("1111"), eq("2222"), eq("text"),
                 eq(mSentIntent), any(), any(), eq("test-app"), eq(mCallingUserId), eq(false), eq(0),
-                eq(false), eq(10), eq(false), eq(1L), eq(false));
+                eq(false), eq(10), eq(false), eq(1L), eq(false), anyLong());
     }
 
     @Test
@@ -903,7 +910,7 @@
         verify(newSmsDsc).finishSelection();
         verify(mImsSmsDispatcher, times(2)).sendText(eq("1111"), eq("2222"), eq("text"),
                 eq(mSentIntent), any(), any(), eq("test-app"), eq(mCallingUserId), eq(false), eq(0),
-                eq(false), eq(10), eq(false), eq(1L), eq(false));
+                eq(false), eq(10), eq(false), eq(1L), eq(false), anyLong());
         assertNull(holder.getConnection());
         assertFalse(holder.isDomainSelectionRequested());
         assertEquals(0, holder.getPendingRequests().size());
@@ -974,7 +981,7 @@
         verify(newEmergencySmsDsc).finishSelection();
         verify(mImsSmsDispatcher, times(2)).sendText(eq("911"), eq("2222"), eq("text"),
                 eq(mSentIntent), any(), any(), eq("test-app"), eq(0), eq(false), eq(0), eq(false),
-                eq(10), eq(false), eq(1L), eq(false));
+                eq(10), eq(false), eq(1L), eq(false), anyLong());
         assertNull(holder.getConnection());
         assertFalse(holder.isDomainSelectionRequested());
         assertEquals(0, holder.getPendingRequests().size());
@@ -1017,7 +1024,7 @@
 
         verify(mImsSmsDispatcher).sendText(eq("1111"), eq("2222"), eq("text"), eq(mSentIntent),
                 any(), any(), eq("test-app"), eq(mCallingUserId), eq(false), eq(0),
-                eq(false), eq(10), eq(false), eq(1L), eq(false));
+                eq(false), eq(10), eq(false), eq(1L), eq(false), anyLong());
     }
 
     @Test
@@ -1060,7 +1067,7 @@
 
         verify(mImsSmsDispatcher).sendText(eq("911"), eq("2222"), eq("text"), eq(mSentIntent),
                 any(), any(), eq("test-app"), eq(mCallingUserId), eq(false), eq(0), eq(false),
-                eq(10), eq(false), eq(1L), eq(false));
+                eq(10), eq(false), eq(1L), eq(false), anyLong());
     }
 
     private void switchImsSmsFormat(int phoneType) {
@@ -1079,7 +1086,7 @@
 
     @Test
     public void testSendSmsToDatagramDispatcher() {
-        when(mSatelliteController.isInCarrierRoamingNbIotNtn()).thenReturn(true);
+        when(mSatelliteController.isInCarrierRoamingNbIotNtn(any(Phone.class))).thenReturn(true);
         mSmsDispatchersController.sendText("1111", "2222", "text", mSentIntent, null, null,
                 "test-app", mCallingUserId, false, 0, false, 10, false, 1L, false);
         processAllMessages();
@@ -1219,13 +1226,13 @@
         verify(mSmsDsc).finishSelection();
         if (domain == NetworkRegistrationInfo.DOMAIN_PS) {
             verify(mImsSmsDispatcher).sendData(eq("test-app"), eq(0), eq("1111"), eq("2222"),
-                    eq(8080), eq(data), eq(mSentIntent), any(), eq(false));
+                    eq(8080), eq(data), eq(mSentIntent), any(), eq(false), anyLong());
         } else if (isCdmaMo) {
             verify(mCdmaSmsDispatcher).sendData(eq("test-app"), eq(0), eq("1111"), eq("2222"),
-                    eq(8080), eq(data), eq(mSentIntent), any(), eq(false));
+                    eq(8080), eq(data), eq(mSentIntent), any(), eq(false), anyLong());
         } else {
             verify(mGsmSmsDispatcher).sendData(eq("test-app"), eq(0), eq("1111"), eq("2222"),
-                    eq(8080), eq(data), eq(mSentIntent), any(), eq(false));
+                    eq(8080), eq(data), eq(mSentIntent), any(), eq(false), anyLong());
         }
         assertNull(holder.getConnection());
         assertFalse(holder.isDomainSelectionRequested());
@@ -1256,15 +1263,15 @@
         if (domain == NetworkRegistrationInfo.DOMAIN_PS) {
             verify(mImsSmsDispatcher).sendText(eq("1111"), eq("2222"), eq("text"), eq(mSentIntent),
                     any(), any(), eq("test-app"), eq(0), eq(false), eq(0), eq(false), eq(10),
-                    eq(false), eq(1L), eq(false));
+                    eq(false), eq(1L), eq(false), anyLong());
         } else if (isCdmaMo) {
             verify(mCdmaSmsDispatcher).sendText(eq("1111"), eq("2222"), eq("text"), eq(mSentIntent),
                     any(), any(), eq("test-app"), eq(0), eq(false), eq(0), eq(false), eq(10),
-                    eq(false), eq(1L), eq(false));
+                    eq(false), eq(1L), eq(false), anyLong());
         } else {
             verify(mGsmSmsDispatcher).sendText(eq("1111"), eq("2222"), eq("text"), eq(mSentIntent),
                     any(), any(), eq("test-app"), eq(0), eq(false), eq(0), eq(false), eq(10),
-                    eq(false), eq(1L), eq(false));
+                    eq(false), eq(1L), eq(false), anyLong());
         }
         assertNull(holder.getConnection());
         assertFalse(holder.isDomainSelectionRequested());
@@ -1298,16 +1305,16 @@
         if (domain == NetworkRegistrationInfo.DOMAIN_PS) {
             verify(mImsSmsDispatcher).sendMultipartText(eq("1111"), eq("2222"), eq(parts),
                     eq(sentIntents), eq(deliveryIntents), any(), eq("test-app"), eq(mCallingUserId),
-                    eq(false), eq(0), eq(false), eq(10), eq(1L));
+                    eq(false), eq(0), eq(false), eq(10), eq(1L), anyLong());
         } else if (isCdmaMo) {
             verify(mCdmaSmsDispatcher).sendMultipartText(eq("1111"), eq("2222"), eq(parts),
                     eq(sentIntents), eq(deliveryIntents), any(), eq("test-app"), eq(mCallingUserId),
                     eq(false), eq(0),
-                    eq(false), eq(10), eq(1L));
+                    eq(false), eq(10), eq(1L), anyLong());
         } else {
             verify(mGsmSmsDispatcher).sendMultipartText(eq("1111"), eq("2222"), eq(parts),
                     eq(sentIntents), eq(deliveryIntents), any(), eq("test-app"), eq(mCallingUserId),
-                    eq(false), eq(0), eq(false), eq(10), eq(1L));
+                    eq(false), eq(0), eq(false), eq(10), eq(1L), anyLong());
         }
         assertNull(holder.getConnection());
         assertFalse(holder.isDomainSelectionRequested());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsStorageMonitorTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsStorageMonitorTest.java
index 4483c61..bdf7e78 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsStorageMonitorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsStorageMonitorTest.java
@@ -52,7 +52,7 @@
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
-        mSmsStorageMonitor = new SmsStorageMonitor(mPhone);
+        mSmsStorageMonitor = new SmsStorageMonitor(mPhone, mFeatureFlags);
         mSmsStorageMonitor.setMaxRetries(MAX_RETRIES);
         mSmsStorageMonitor.setRetryDelayInMillis(RETRY_DELAY);
         processAllMessages();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyCountryDetectorTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyCountryDetectorTest.java
index 9b3777b..5db1206 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyCountryDetectorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyCountryDetectorTest.java
@@ -112,26 +112,27 @@
         when(mSST2.getLocaleTracker()).thenReturn(mMockLocaleTracker2);
         when(mMockLocaleTracker2.getCurrentCountry()).thenReturn("");
 
-        when(mConnectivityManager.getActiveNetwork()).thenReturn(mMockNetwork);
         mNetworkCapabilities = new NetworkCapabilities();
         mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true);
         mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET, true);
-        when(mConnectivityManager.getNetworkCapabilities(any(Network.class)))
-                .thenReturn(mNetworkCapabilities);
+        mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED, true);
 
         when(mLocationManager.getProviders(true)).thenReturn(Arrays.asList("TEST_PROVIDER"));
 
         when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
         mCountryDetectorUT = new TestTelephonyCountryDetector(
                 mLooper, mContext, mLocationManager, mConnectivityManager, mMockFeatureFlags);
+        verify(mConnectivityManager).registerNetworkCallback(
+                any(NetworkRequest.class), mNetworkCallbackCaptor.capture());
+        mNetworkCallbackCaptor.getValue().onAvailable(mMockNetwork);
+        mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(mMockNetwork, mNetworkCapabilities);
+        mTestableLooper.processAllMessages();
         if (isGeoCoderImplemented()) {
             verify(mLocationManager).requestLocationUpdates(anyString(), anyLong(), anyFloat(),
                     mLocationListenerCaptor.capture());
             verify(mLocationManager).getProviders(true);
             verify(mLocationManager).getLastKnownLocation(anyString());
         }
-        verify(mConnectivityManager).registerNetworkCallback(
-                any(NetworkRequest.class), mNetworkCallbackCaptor.capture());
     }
 
     @After
@@ -285,7 +286,7 @@
 
         // Wi-fi is not available
         clearInvocations(mLocationManager);
-        mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, false);
+        mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true);
         mNetworkCallbackCaptor.getValue().onLost(mMockNetwork);
         mTestableLooper.processAllMessages();
         verify(mLocationManager, never()).removeUpdates(any(LocationListener.class));
@@ -294,6 +295,9 @@
         clearInvocations(mLocationManager);
         mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true);
         mNetworkCallbackCaptor.getValue().onAvailable(mMockNetwork);
+        mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET, true);
+        mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED, true);
+        mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(mMockNetwork, mNetworkCapabilities);
         mTestableLooper.processAllMessages();
         // Location updates were already requested
         verify(mLocationManager, never()).requestLocationUpdates(anyString(), anyLong(), anyFloat(),
@@ -301,7 +305,10 @@
 
         // Make Wi-fi not available and reset the quota
         clearInvocations(mLocationManager);
-        mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, false);
+        mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true);
+        mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET, true);
+        mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED, false);
+        mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(mMockNetwork, mNetworkCapabilities);
         mTestableLooper.moveTimeForward(
                 TestTelephonyCountryDetector.getLocationUpdateRequestQuotaResetTimeoutMillis());
         mTestableLooper.processAllMessages();
@@ -311,6 +318,9 @@
         clearInvocations(mLocationManager);
         mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true);
         mNetworkCallbackCaptor.getValue().onAvailable(mMockNetwork);
+        mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET, true);
+        mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED, true);
+        mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(mMockNetwork, mNetworkCapabilities);
         mTestableLooper.processAllMessages();
         verify(mLocationManager).requestLocationUpdates(anyString(), anyLong(), anyFloat(),
                 any(LocationListener.class));
@@ -324,16 +334,20 @@
         verify(mLocationManager, never()).requestLocationUpdates(anyString(), anyLong(), anyFloat(),
                 any(LocationListener.class));
 
-        // Wi-fi becomes not available
+        // Wi-fi lost
         clearInvocations(mLocationManager);
-        mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, false);
-        mNetworkCallbackCaptor.getValue().onUnavailable();
+        mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true);
+        mNetworkCallbackCaptor.getValue().onLost(mMockNetwork);
         mTestableLooper.processAllMessages();
         verify(mLocationManager).removeUpdates(any(LocationListener.class));
     }
 
     @Test
     public void testRegisterUnregisterForWifiConnectivityStateChanged() {
+        // Set Wi-Fi unavailable.
+        mNetworkCallbackCaptor.getValue().onLost(mMockNetwork);
+        mTestableLooper.processAllMessages();
+
         WifiConnectivityStateChangedListener listener = new WifiConnectivityStateChangedListener(
                 mLooper);
 
@@ -344,19 +358,63 @@
         clearInvocations(mLocationManager);
         mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true);
         mNetworkCallbackCaptor.getValue().onAvailable(mMockNetwork);
+        mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET, true);
+        mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED, true);
+        mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(mMockNetwork, mNetworkCapabilities);
         mTestableLooper.processAllMessages();
         assertTrue(listener.getIsWifiConnected());
 
-        // Wi-fi becomes not available
+        // Wi-fi lost
         clearInvocations(mLocationManager);
-        mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, false);
-        mNetworkCallbackCaptor.getValue().onUnavailable();
+        mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true);
+        mNetworkCallbackCaptor.getValue().onLost(mMockNetwork);
         mTestableLooper.processAllMessages();
         assertFalse(listener.getIsWifiConnected());
 
         mCountryDetectorUT.unregisterForWifiConnectivityStateChanged(listener);
     }
 
+    @Test
+    public void testReflectWifiConnectedStatusChanged() {
+        // 1. Wi-Fi is turned off, network capability is not available.
+        mNetworkCallbackCaptor.getValue().onLost(mMockNetwork);
+        mNetworkCapabilities = new NetworkCapabilities();
+        mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true);
+        mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET, false);
+        mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED, false);
+        mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(mMockNetwork, mNetworkCapabilities);
+        mTestableLooper.processAllMessages();
+        assertFalse(mCountryDetectorUT.isWifiNetworkConnected());
+
+        // 2. Wi-Fi is turned on, but network capability has not been updated.
+        mNetworkCallbackCaptor.getValue().onAvailable(mMockNetwork);
+        mTestableLooper.processAllMessages();
+        assertFalse(mCountryDetectorUT.isWifiNetworkConnected());
+
+        // 3. Network capability has been updated, not some of them still false.
+        mNetworkCapabilities = new NetworkCapabilities();
+        mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true);
+        mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET, true);
+        mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED, false);
+        mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(mMockNetwork, mNetworkCapabilities);
+        mTestableLooper.processAllMessages();
+        assertFalse(mCountryDetectorUT.isWifiNetworkConnected());
+
+        // 4. Network capability has been updated to validated.
+        mNetworkCapabilities = new NetworkCapabilities();
+        mNetworkCapabilities.setTransportType(NetworkCapabilities.TRANSPORT_WIFI, true);
+        mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET, true);
+        mNetworkCapabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED, true);
+        mNetworkCallbackCaptor.getValue().onCapabilitiesChanged(mMockNetwork, mNetworkCapabilities);
+        mTestableLooper.processAllMessages();
+        assertTrue(mCountryDetectorUT.isWifiNetworkConnected());
+
+        // 5. Wi-Fi is turned off.
+        mNetworkCallbackCaptor.getValue().onLost(mMockNetwork);
+        mTestableLooper.processAllMessages();
+        assertFalse(mCountryDetectorUT.isWifiNetworkConnected());
+    }
+
     private static boolean isGeoCoderImplemented() {
         return Geocoder.isPresent();
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
index a645439..ed45fac 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
@@ -86,6 +86,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -123,6 +124,8 @@
     private CellIdentity mCellIdentityForRegiFail;
     private int mRegistrationFailReason;
     private Set<Integer> mSimultaneousCallingSubscriptions;
+    private int mCallbackModeStopReason = TelephonyManager.STOP_REASON_UNKNOWN;
+    private long mCallbackModeDurationMillis;
     private boolean mCarrierRoamingNtnMode;
     private boolean mCarrierRoamingNtnEligible;
 
@@ -201,6 +204,7 @@
             TelephonyCallback.RegistrationFailedListener,
             TelephonyCallback.DataActivityListener,
             TelephonyCallback.SimultaneousCellularCallingSupportListener,
+            TelephonyCallback.EmergencyCallbackModeListener,
             TelephonyCallback.CarrierRoamingNtnModeListener {
         // This class isn't mockable to get invocation counts because the IBinder is null and
         // crashes the TelephonyRegistry. Make a cheesy verify(times()) alternative.
@@ -298,6 +302,27 @@
         }
 
         @Override
+        public void onCallbackModeStarted(@TelephonyManager.EmergencyCallbackModeType int type,
+                @NonNull Duration timerDuration, int subId) {
+            invocationCount.incrementAndGet();
+            mCallbackModeDurationMillis = timerDuration.toMillis();
+        }
+
+        @Override
+        public void onCallbackModeRestarted(@TelephonyManager.EmergencyCallbackModeType int type,
+                @NonNull Duration timerDuration, int subId) {
+            invocationCount.incrementAndGet();
+            mCallbackModeDurationMillis = timerDuration.toMillis();
+        }
+
+        @Override
+        public void onCallbackModeStopped(@TelephonyManager.EmergencyCallbackModeType int type,
+                @TelephonyManager.EmergencyCallbackModeStopReason int reason, int subId) {
+            invocationCount.incrementAndGet();
+            mCallbackModeStopReason = reason;
+        }
+
+        @Override
         public void onCarrierRoamingNtnModeChanged(boolean active) {
             invocationCount.incrementAndGet();
             mCarrierRoamingNtnMode = active;
@@ -981,6 +1006,8 @@
 
         mApplicationInfo.targetSdkVersion = Build.VERSION_CODES.TIRAMISU;
         doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfo(anyString(), anyInt());
+        doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfoAsUser(
+                anyString(), anyInt(), any(UserHandle.class));
         mContextFixture.addCallingOrSelfPermission("");
         mContextFixture.addCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
         mContextFixture.addCallingOrSelfPermission(
@@ -1077,6 +1104,8 @@
 
         mApplicationInfo.targetSdkVersion = Build.VERSION_CODES.TIRAMISU;
         doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfo(anyString(), anyInt());
+        doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfoAsUser(
+                anyString(), anyInt(), any(UserHandle.class));
         mContextFixture.addCallingOrSelfPermission("");
         mContextFixture.addCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
         mContextFixture.addCallingOrSelfPermission(
@@ -1589,6 +1618,57 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION)
+    public void testNotifyCallbackModeStarted() {
+        final long durationMillis = 1000;
+        int[] events = {TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED};
+
+        mTelephonyRegistry.listenWithEventList(false, false, 1/*subId*/,
+                mContext.getOpPackageName(), mContext.getAttributionTag(),
+                mTelephonyCallback.callback, events, true);
+        mTelephonyRegistry.notifyCallbackModeStarted(0/*phoneId*/, 1/*subId*/,
+                TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL, durationMillis);
+        processAllMessages();
+
+        assertEquals(1, mTelephonyCallback.invocationCount.get());
+        assertEquals(durationMillis, mCallbackModeDurationMillis);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION)
+    public void testNotifyCallbackModeReStarted() {
+        final long durationMillis = 1000;
+        int[] events = {TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED};
+
+        mTelephonyRegistry.listenWithEventList(false, false, 1/*subId*/,
+                mContext.getOpPackageName(), mContext.getAttributionTag(),
+                mTelephonyCallback.callback, events, true);
+        mTelephonyRegistry.notifyCallbackModeRestarted(0/*phoneId*/, 1/*subId*/,
+                TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL, durationMillis);
+        processAllMessages();
+
+        assertEquals(1, mTelephonyCallback.invocationCount.get());
+        assertEquals(durationMillis, mCallbackModeDurationMillis);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION)
+    public void testNotifyCallbackModeStopped() {
+        final int reason = TelephonyManager.STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED;
+        int[] events = {TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED};
+
+        mTelephonyRegistry.listenWithEventList(false, false, 1/*subId*/,
+                mContext.getOpPackageName(), mContext.getAttributionTag(),
+                mTelephonyCallback.callback, events, true);
+        mTelephonyRegistry.notifyCallbackModeStopped(0/*phoneId*/, 1/*subId*/,
+                TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL, reason);
+        processAllMessages();
+
+        assertEquals(1, mTelephonyCallback.invocationCount.get());
+        assertEquals(reason, mCallbackModeStopReason);
+    }
+
+    @Test
     public void testNotifyCarrierRoamingNtnModeChanged() {
         int subId = INVALID_SUBSCRIPTION_ID;
         doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 36ac992..d80c9a2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -581,6 +581,8 @@
 
         doReturn(true).when(mFeatureFlags).minimalTelephonyCdmCheck();
         doReturn(true).when(mFeatureFlags).supportNetworkProvider();
+        doReturn(true).when(mFeatureFlags).hsumBroadcast();
+        doReturn(true).when(mFeatureFlags).hsumPackageManager();
 
         TelephonyManager.disableServiceHandleCaching();
         PropertyInvalidatedCache.disableForTestMode();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
index 0b66459..ee713c6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
@@ -3219,6 +3219,118 @@
     }
 
     @Test
+    public void testSetupDataNetworkWithCandidateProfileWithIncompatibleRetryDataProfile() throws Exception {
+        mDataNetworkControllerUT
+                .getDataRetryManager()
+                .registerCallback(mMockedDataRetryManagerCallback);
+        setFailedSetupDataResponse(mMockedWwanDataServiceManager,
+                DataFailCause.ONLY_IPV4_ALLOWED, 2500 /* mSec */, false);
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+        processAllMessages();
+        verify(mMockedWwanDataServiceManager, times(1)).setupDataCall(anyInt(),
+                any(DataProfile.class), anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(),
+                any(), any(), anyBoolean(), any(Message.class));
+
+        moveTimeForward(2500);
+        processAllMessages();
+        ArgumentCaptor<DataRetryManager.DataSetupRetryEntry> retryEntry =
+                ArgumentCaptor.forClass(DataRetryManager.DataSetupRetryEntry.class);
+        verify(mMockedDataRetryManagerCallback, times(1))
+                .onDataNetworkSetupRetry(retryEntry.capture());
+
+        ArgumentCaptor<List<ThrottleStatus>> throttleStatusCaptor =
+                ArgumentCaptor.forClass(List.class);
+        verify(mMockedDataRetryManagerCallback)
+                .onThrottleStatusChanged(throttleStatusCaptor.capture());
+
+        assertThat(retryEntry.getValue().dataProfile).isNotNull();
+
+        assertThat(retryEntry.getValue().dataProfile).isEqualTo(mGeneralPurposeDataProfile);
+        doReturn(false)
+                .when(mDataProfileManager)
+                .isDataProfileCompatible(retryEntry.getValue().dataProfile);
+
+        doReturn(mDuplicatedGeneralPurposeDataProfile).when(mDataProfileManager)
+                .getDataProfileForNetworkRequest(any(TelephonyNetworkRequest.class),
+                anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
+
+        setSuccessfulSetupDataResponse(mMockedWwanDataServiceManager, 2);
+
+        mDataNetworkControllerUT
+                .getDataRetryManager()
+                .obtainMessage(
+                        6 /* EVENT_DATA_PROFILE_UNTHROTTLED*/,
+                        new AsyncResult(
+                                AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+                                mGeneralPurposeDataProfile,
+                                null))
+                .sendToTarget();
+        processAllFutureMessages();
+
+        verify(mMockedWwanDataServiceManager, times(2)).setupDataCall(anyInt(),
+                any(DataProfile.class), anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(),
+                any(), any(), anyBoolean(), any(Message.class));
+
+        verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+        verifyConnectedNetworkHasDataProfile(mDuplicatedGeneralPurposeDataProfile);
+    }
+
+    @Test
+    public void testSetupDataNetworkRetryWithCompatibleRetryDataProfile() throws Exception {
+        mDataNetworkControllerUT
+                .getDataRetryManager()
+                .registerCallback(mMockedDataRetryManagerCallback);
+        setFailedSetupDataResponse(mMockedWwanDataServiceManager,
+                DataFailCause.ONLY_IPV4_ALLOWED, 2500 /* mSec */, false);
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+        processAllMessages();
+        verify(mMockedWwanDataServiceManager, times(1)).setupDataCall(anyInt(),
+                any(DataProfile.class), anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(),
+                any(), any(), anyBoolean(), any(Message.class));
+
+        moveTimeForward(2500);
+        processAllMessages();
+        ArgumentCaptor<DataRetryManager.DataSetupRetryEntry> retryEntry =
+                ArgumentCaptor.forClass(DataRetryManager.DataSetupRetryEntry.class);
+        verify(mMockedDataRetryManagerCallback, times(1))
+                .onDataNetworkSetupRetry(retryEntry.capture());
+
+        ArgumentCaptor<List<ThrottleStatus>> throttleStatusCaptor =
+                ArgumentCaptor.forClass(List.class);
+        verify(mMockedDataRetryManagerCallback)
+                .onThrottleStatusChanged(throttleStatusCaptor.capture());
+
+        assertThat(retryEntry.getValue().dataProfile).isNotNull();
+
+        assertThat(retryEntry.getValue().dataProfile).isEqualTo(mGeneralPurposeDataProfile);
+
+        assertThat(mDataProfileManager.isDataProfileCompatible(retryEntry.getValue().dataProfile))
+                .isTrue();
+
+        setSuccessfulSetupDataResponse(mMockedWwanDataServiceManager, 2);
+
+        mDataNetworkControllerUT
+                .getDataRetryManager()
+                .obtainMessage(
+                        6 /* EVENT_DATA_PROFILE_UNTHROTTLED*/,
+                        new AsyncResult(
+                                AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+                                mGeneralPurposeDataProfile,
+                                null))
+                .sendToTarget();
+        processAllFutureMessages();
+
+        verify(mMockedWwanDataServiceManager, times(2)).setupDataCall(anyInt(),
+                any(DataProfile.class), anyBoolean(), anyBoolean(), anyInt(), any(), anyInt(),
+                any(), any(), anyBoolean(), any(Message.class));
+
+        verifyConnectedNetworkHasCapabilities(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+        verifyConnectedNetworkHasDataProfile(mGeneralPurposeDataProfile);
+    }
+
+    @Test
     public void testSetupDataNetworkRetryFailed() {
         mDataNetworkControllerUT.getDataRetryManager()
                 .registerCallback(mMockedDataRetryManagerCallback);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java
index 2f3fabf..ec7ad3c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/domainselection/DomainSelectionConnectionTest.java
@@ -16,6 +16,7 @@
 package com.android.internal.telephony.domainselection;
 
 import static android.telephony.AccessNetworkConstants.AccessNetworkType.EUTRAN;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.UNKNOWN;
 import static android.telephony.AccessNetworkConstants.AccessNetworkType.UTRAN;
 import static android.telephony.DomainSelectionService.SCAN_TYPE_FULL_SERVICE;
 import static android.telephony.DomainSelectionService.SCAN_TYPE_NO_PREFERENCE;
@@ -516,6 +517,131 @@
 
     @Test
     @SmallTest
+    public void testWwanSelectorCallbackOnRequestEmergencyNetworkScanAndErrorForScanTypeFull()
+            throws Exception {
+        mDsc = createConnection(mPhone, SELECTOR_TYPE_CALLING, true,
+                mDomainSelectionController);
+
+        ITransportSelectorCallback transportCallback = mDsc.getTransportSelectorCallback();
+
+        assertNotNull(transportCallback);
+
+        DomainSelectionService.SelectionAttributes attr = getSelectionAttributes(
+                mPhone.getPhoneId(), mPhone.getSubId(), SELECTOR_TYPE_CALLING, true,
+                false, 0, TELECOM_CALL_ID1, null, null, null);
+
+        mDsc.selectDomain(attr);
+
+        IDomainSelector domainSelector = Mockito.mock(IDomainSelector.class);
+        transportCallback.onCreated(domainSelector);
+
+        IWwanSelectorCallback wwanCallback = onWwanSelected(transportCallback);
+
+        assertNotNull(wwanCallback);
+
+        int[] preferredNetworks = new int[] { EUTRAN, UTRAN };
+        int scanType = SCAN_TYPE_FULL_SERVICE;
+        IWwanSelectorResultCallback resultCallback =
+                Mockito.mock(IWwanSelectorResultCallback.class);
+
+        wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType,
+                false, resultCallback);
+        processAllMessages();
+
+        ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+        ArgumentCaptor<Message> msgCaptor = ArgumentCaptor.forClass(Message.class);
+
+        verify(mPhone).registerForEmergencyNetworkScan(handlerCaptor.capture(), anyInt(), any());
+        verify(mCi).registerForModemReset(any(), anyInt(), any());
+        processAllMessages();
+
+        int[] expectedPreferredNetworks = new int[] { EUTRAN, UTRAN };
+
+        verify(mPhone).triggerEmergencyNetworkScan(eq(expectedPreferredNetworks),
+                eq(scanType), msgCaptor.capture());
+
+        Handler handler = handlerCaptor.getValue();
+        Message msg = msgCaptor.getValue();
+
+        assertNotNull(handler);
+        assertNotNull(msg);
+
+        CommandException commandException =
+                new CommandException(CommandException.Error.INTERNAL_ERR);
+        handler.sendMessage(handler.obtainMessage(msg.what,
+                new AsyncResult((Integer) scanType, null, commandException)));
+        processAllMessages();
+
+        EmergencyRegistrationResult regResult = new EmergencyRegistrationResult(UNKNOWN,
+                NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN,
+                NetworkRegistrationInfo.DOMAIN_UNKNOWN, false, false, 0, 0, "", "", "");
+        verify(resultCallback).onComplete(eq(regResult));
+        verify(mPhone, times(0)).cancelEmergencyNetworkScan(anyBoolean(), any());
+    }
+
+    @Test
+    @SmallTest
+    public void testWwanSelectorCallbackOnRequestEmergencyNetworkScanAndErrorForScanTypeNoPref()
+            throws Exception {
+        mDsc = createConnection(mPhone, SELECTOR_TYPE_CALLING, true,
+                mDomainSelectionController);
+
+        ITransportSelectorCallback transportCallback = mDsc.getTransportSelectorCallback();
+
+        assertNotNull(transportCallback);
+
+        DomainSelectionService.SelectionAttributes attr = getSelectionAttributes(
+                mPhone.getPhoneId(), mPhone.getSubId(), SELECTOR_TYPE_CALLING, true,
+                false, 0, TELECOM_CALL_ID1, null, null, null);
+
+        mDsc.selectDomain(attr);
+
+        IDomainSelector domainSelector = Mockito.mock(IDomainSelector.class);
+        transportCallback.onCreated(domainSelector);
+
+        IWwanSelectorCallback wwanCallback = onWwanSelected(transportCallback);
+
+        assertNotNull(wwanCallback);
+
+        int[] preferredNetworks = new int[] { EUTRAN, UTRAN };
+        int scanType = SCAN_TYPE_NO_PREFERENCE;
+        IWwanSelectorResultCallback resultCallback =
+                Mockito.mock(IWwanSelectorResultCallback.class);
+
+        wwanCallback.onRequestEmergencyNetworkScan(preferredNetworks, scanType,
+                false, resultCallback);
+        processAllMessages();
+
+        ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+        ArgumentCaptor<Message> msgCaptor = ArgumentCaptor.forClass(Message.class);
+
+        verify(mPhone).registerForEmergencyNetworkScan(handlerCaptor.capture(), anyInt(), any());
+        verify(mCi).registerForModemReset(any(), anyInt(), any());
+        processAllMessages();
+
+        int[] expectedPreferredNetworks = new int[] { EUTRAN, UTRAN };
+
+        verify(mPhone).triggerEmergencyNetworkScan(eq(expectedPreferredNetworks),
+                eq(scanType), msgCaptor.capture());
+
+        Handler handler = handlerCaptor.getValue();
+        Message msg = msgCaptor.getValue();
+
+        assertNotNull(handler);
+        assertNotNull(msg);
+
+        CommandException commandException =
+                new CommandException(CommandException.Error.INTERNAL_ERR);
+        handler.sendMessage(handler.obtainMessage(msg.what,
+                new AsyncResult((Integer) scanType, null, commandException)));
+        processAllMessages();
+
+        verify(resultCallback, times(0)).onComplete(any());
+        verify(mPhone, times(0)).cancelEmergencyNetworkScan(anyBoolean(), any());
+    }
+
+    @Test
+    @SmallTest
     public void testModemResetOnRequestEmergencyNetworkDuringNetworkScan()
             throws Exception {
         mDsc = createConnection(mPhone, SELECTOR_TYPE_CALLING, true,
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 25e9715..2664ff3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
@@ -346,7 +346,7 @@
                 true /* isRadioOn */);
         when(mSST.isRadioOn()).thenReturn(true);
         // Satellite enabled
-        when(mSatelliteController.isSatelliteEnabled()).thenReturn(true);
+        when(mSatelliteController.isSatelliteEnabledOrBeingEnabled()).thenReturn(true);
 
         setConfigForDdsSwitch(testPhone, null,
                 CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY, "150");
@@ -363,7 +363,7 @@
         // isOkToCall() should return true once satellite modem is off
         assertFalse(callback.getValue()
                 .isOkToCall(testPhone, ServiceState.STATE_IN_SERVICE, false));
-        when(mSatelliteController.isSatelliteEnabled()).thenReturn(false);
+        when(mSatelliteController.isSatelliteEnabledOrBeingEnabled()).thenReturn(false);
         assertTrue(callback.getValue()
                 .isOkToCall(testPhone, ServiceState.STATE_IN_SERVICE, false));
         // Once radio on is complete, trigger delay dial
@@ -391,7 +391,7 @@
         Phone testPhone = setupTestPhoneForEmergencyCall(false /* isRoaming */,
                 true /* isRadioOn */);
         // Satellite enabled
-        when(mSatelliteController.isSatelliteEnabled()).thenReturn(true);
+        when(mSatelliteController.isSatelliteEnabledOrBeingEnabled()).thenReturn(true);
 
         CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(testPhone,
                 mTestConnection1, false);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/emergency/RadioOnStateListenerTest.java b/tests/telephonytests/src/com/android/internal/telephony/emergency/RadioOnStateListenerTest.java
index bb349a7..e9bff91 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/emergency/RadioOnStateListenerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/RadioOnStateListenerTest.java
@@ -104,7 +104,7 @@
      */
     @Test
     public void testRegisterForSatelliteCallback() {
-        doReturn(true).when(mSatelliteController).isSatelliteEnabled();
+        doReturn(true).when(mSatelliteController).isSatelliteEnabledOrBeingEnabled();
         mListener.waitForRadioOn(mMockPhone, mCallback, false, false, 0);
 
         waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
@@ -229,7 +229,7 @@
 
     @Test
     public void testTimeout_RetryFailure_WithSatellite() {
-        doReturn(true).when(mSatelliteController).isSatelliteEnabled();
+        doReturn(true).when(mSatelliteController).isSatelliteEnabledOrBeingEnabled();
         ServiceState state = new ServiceState();
         state.setState(ServiceState.STATE_POWER_OFF);
         when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccSessionTest.java b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccSessionTest.java
new file mode 100644
index 0000000..6e0d383
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/euicc/EuiccSessionTest.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2024 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.euicc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.never;
+
+import android.app.PendingIntent;
+import android.compat.testing.PlatformCompatChangeRule;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.UserManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+import android.service.euicc.DownloadSubscriptionResult;
+import android.service.euicc.EuiccService;
+import android.service.euicc.GetDefaultDownloadableSubscriptionListResult;
+import android.service.euicc.GetDownloadableSubscriptionMetadataResult;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.UiccAccessRule;
+import android.telephony.UiccCardInfo;
+import android.telephony.UiccPortInfo;
+import android.telephony.euicc.DownloadableSubscription;
+import android.telephony.euicc.EuiccInfo;
+import android.telephony.euicc.EuiccManager;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.euicc.EuiccConnector.GetOtaStatusCommandCallback;
+import com.android.internal.telephony.euicc.EuiccConnector.OtaStatusChangedCallback;
+import com.android.internal.telephony.uicc.euicc.apdu.ApduSender;
+import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.uicc.UiccSlot;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.mockito.stubbing.Stubber;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+public class EuiccSessionTest extends TelephonyTest {
+    @Rule
+    public final TestRule compatChangeRule = new PlatformCompatChangeRule();
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+    @Rule
+    public final MockitoRule rule = MockitoJUnit.rule();
+
+    private static final String SESSION_ID_1 = "SESSION_ID_1";
+    private static final String SESSION_ID_2 = "SESSION_ID_2";
+
+    private EuiccSession mEuiccSession;
+    @Mock private ApduSender mApduSender;
+    @Mock private ApduSender mApduSender2;
+
+    @Before
+    public void setUp() throws Exception {
+        mEuiccSession = new EuiccSession();
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER)
+    public void startOneSession_featureDisabled_noop() throws Exception {
+        mEuiccSession.startSession(SESSION_ID_1);
+        mEuiccSession.noteChannelOpen(mApduSender);
+        mEuiccSession.noteChannelOpen(mApduSender2);
+
+        assertThat(mEuiccSession.hasSession()).isFalse();
+
+        mEuiccSession.endSession(SESSION_ID_1);
+
+        assertThat(mEuiccSession.hasSession()).isFalse();
+        verify(mApduSender, never()).closeAnyOpenChannel();
+        verify(mApduSender2, never()).closeAnyOpenChannel();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER)
+    public void startOneSession_endSession_hasSession() throws Exception {
+        mEuiccSession.startSession(SESSION_ID_1);
+        mEuiccSession.noteChannelOpen(mApduSender);
+        mEuiccSession.noteChannelOpen(mApduSender2);
+
+        assertThat(mEuiccSession.hasSession()).isTrue();
+
+        mEuiccSession.endSession(SESSION_ID_2);
+
+        assertThat(mEuiccSession.hasSession()).isTrue();
+        verify(mApduSender, never()).closeAnyOpenChannel();
+
+        mEuiccSession.endSession(SESSION_ID_1);
+
+        assertThat(mEuiccSession.hasSession()).isFalse();
+        verify(mApduSender).closeAnyOpenChannel();
+        verify(mApduSender2).closeAnyOpenChannel();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER)
+    public void startTwoSession_endSession_hasSession() throws Exception {
+        mEuiccSession.startSession(SESSION_ID_1);
+        mEuiccSession.noteChannelOpen(mApduSender);
+        mEuiccSession.startSession(SESSION_ID_2);
+
+        assertThat(mEuiccSession.hasSession()).isTrue();
+
+        mEuiccSession.endSession(SESSION_ID_1);
+        verify(mApduSender, never()).closeAnyOpenChannel();
+
+        assertThat(mEuiccSession.hasSession()).isTrue();
+
+        mEuiccSession.endSession(SESSION_ID_2);
+
+        assertThat(mEuiccSession.hasSession()).isFalse();
+        verify(mApduSender).closeAnyOpenChannel();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER)
+    public void startTwoSessions_endAllSessions_hasSession() throws Exception {
+        mEuiccSession.startSession(SESSION_ID_1);
+        mEuiccSession.noteChannelOpen(mApduSender);
+        mEuiccSession.startSession(SESSION_ID_2);
+        mEuiccSession.noteChannelOpen(mApduSender2);
+
+        assertThat(mEuiccSession.hasSession()).isTrue();
+
+        mEuiccSession.endAllSessions();
+
+        assertThat(mEuiccSession.hasSession()).isFalse();
+        verify(mApduSender).closeAnyOpenChannel();
+        verify(mApduSender2).closeAnyOpenChannel();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER)
+    public void noteChannelOpen_noSession_endSession_noop() throws Exception {
+        // noteChannelOpen called without a session started
+        mEuiccSession.noteChannelOpen(mApduSender);
+
+        assertThat(mEuiccSession.hasSession()).isFalse();
+
+        mEuiccSession.endSession(SESSION_ID_1);
+
+        assertThat(mEuiccSession.hasSession()).isFalse();
+        verify(mApduSender, never()).closeAnyOpenChannel();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER)
+    public void endAllSessions_noSession_endAllSessions_noOp() throws Exception {
+        // noteChannelOpen called without a session started
+        mEuiccSession.noteChannelOpen(mApduSender);
+
+        assertThat(mEuiccSession.hasSession()).isFalse();
+
+        mEuiccSession.endAllSessions();
+
+        assertThat(mEuiccSession.hasSession()).isFalse();
+        verify(mApduSender, never()).closeAnyOpenChannel();
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
index 58cc261..639a2a3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
@@ -313,7 +313,7 @@
         // send SMS and check sentIntent
         mReceivedTestIntent = false;
         mGsmSmsDispatcher.sendMultipartText("+123" /*destAddr*/, "222" /*scAddr*/, parts,
-                sentIntents, null, null, null, mCallingUserId, false, -1, false, -1, 0L);
+                sentIntents, null, null, null, mCallingUserId, false, -1, false, -1, 0L, 0L);
 
         waitForMs(500);
         synchronized (mLock) {
@@ -456,7 +456,7 @@
 
         mGsmSmsDispatcher.sendMultipartText("6501002000" /*destAddr*/, "222" /*scAddr*/, parts,
                 withSentIntents ? sentIntents : null, null, null, null, mCallingUserId,
-                false, -1, false, -1, 0L);
+                false, -1, false, -1, 0L, 0L);
     }
 
     @Test
@@ -549,7 +549,7 @@
             messageRef += parts.size();
         }
         mGsmSmsDispatcher.sendMultipartText("6501002000" /*destAddr*/, "222" /*scAddr*/, parts,
-                null, null, null, null, mCallingUserId, false, -1, false, -1, 0L);
+                null, null, null, null, mCallingUserId, false, -1, false, -1, 0L, 0L);
         waitForMs(150);
         ArgumentCaptor<String> pduCaptor = ArgumentCaptor.forClass(String.class);
 
@@ -565,7 +565,7 @@
     @Test
     public void testSendTextWithMessageRefNegativeBoundaryCondition() throws Exception {
         mIsimUiccRecords = new IsimUiccRecords(mUiccCardApplication3gpp, mContext,
-                mSimulatedCommands);
+                mSimulatedCommands, mFeatureFlags);
         doReturn(mIsimUiccRecords).when(mPhone).getIccRecords();
         Message msg = mGsmSmsDispatcher.obtainMessage(17);
         mPhone.getIccRecords().setSmssTpmrValue(-1, msg);
@@ -584,7 +584,7 @@
     @Test
     public void testSendTextWithMessageRefMaxBoundaryCondition() throws Exception {
         mIsimUiccRecords = new IsimUiccRecords(mUiccCardApplication3gpp, mContext,
-                mSimulatedCommands);
+                mSimulatedCommands, mFeatureFlags);
         doReturn(mIsimUiccRecords).when(mPhone).getIccRecords();
         Message msg = mGsmSmsDispatcher.obtainMessage(17);
         mPhone.getIccRecords().setSmssTpmrValue(255, msg);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
index d3a021f..110dade 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
@@ -1147,6 +1147,7 @@
         mSatelliteController1.countOfDatagramTypeKeepAliveSuccess = 1;
         mSatelliteController1.countOfDatagramTypeKeepAliveFail = 2;
         mSatelliteController1.isProvisioned = true;
+        mSatelliteController1.carrierId = 1;
 
         mSatelliteController2 = new SatelliteController();
         mSatelliteController2.countOfSatelliteServiceEnablementsSuccess = 2 + 1;
@@ -1175,6 +1176,7 @@
         mSatelliteController2.countOfDatagramTypeKeepAliveSuccess = 4;
         mSatelliteController2.countOfDatagramTypeKeepAliveFail = 5;
         mSatelliteController2.isProvisioned = false;
+        mSatelliteController2.carrierId = 10;
 
         // SatelliteController atom has one data point
         mSatelliteControllers =
@@ -1199,6 +1201,10 @@
         mSatelliteSession1.countOfIncomingDatagramFailed = 0;
         mSatelliteSession1.isDemoMode = false;
         mSatelliteSession1.maxNtnSignalStrengthLevel = 2;
+        mSatelliteSession1.carrierId = 2;
+        mSatelliteSession1.countOfSatelliteNotificationDisplayed = 4;
+        mSatelliteSession1.countOfAutoExitDueToScreenOff = 6;
+        mSatelliteSession1.countOfAutoExitDueToTnNetwork = 7;
 
         mSatelliteSession2 = new SatelliteSession();
         mSatelliteSession2.satelliteServiceInitializationResult =
@@ -1217,6 +1223,10 @@
         mSatelliteSession2.countOfIncomingDatagramFailed = 1;
         mSatelliteSession2.isDemoMode = true;
         mSatelliteSession2.maxNtnSignalStrengthLevel = 4;
+        mSatelliteSession2.carrierId = 20;
+        mSatelliteSession2.countOfSatelliteNotificationDisplayed = 40;
+        mSatelliteSession2.countOfAutoExitDueToScreenOff = 60;
+        mSatelliteSession2.countOfAutoExitDueToTnNetwork = 70;
 
         mSatelliteSessions =
                 new SatelliteSession[] {
@@ -1228,12 +1238,14 @@
         mSatelliteIncomingDatagram1.datagramSizeBytes = 1 * 1024;
         mSatelliteIncomingDatagram1.datagramTransferTimeMillis = 3 * 1000;
         mSatelliteIncomingDatagram1.isDemoMode = false;
+        mSatelliteIncomingDatagram1.carrierId = 1;
 
         mSatelliteIncomingDatagram2 = new SatelliteIncomingDatagram();
         mSatelliteIncomingDatagram2.resultCode = SatelliteProtoEnums.SATELLITE_RESULT_MODEM_ERROR;
         mSatelliteIncomingDatagram2.datagramSizeBytes = 512;
         mSatelliteIncomingDatagram2.datagramTransferTimeMillis = 1 * 1000;
         mSatelliteIncomingDatagram2.isDemoMode = true;
+        mSatelliteIncomingDatagram2.carrierId = 10;
 
         mSatelliteIncomingDatagrams =
                 new SatelliteIncomingDatagram[] {
@@ -1247,6 +1259,7 @@
         mSatelliteOutgoingDatagram1.datagramSizeBytes = 1 * 1024;
         mSatelliteOutgoingDatagram1.datagramTransferTimeMillis = 3 * 1000;
         mSatelliteOutgoingDatagram1.isDemoMode = false;
+        mSatelliteOutgoingDatagram1.carrierId = 1;
 
         mSatelliteOutgoingDatagram2 = new SatelliteOutgoingDatagram();
         mSatelliteOutgoingDatagram2.datagramType =
@@ -1255,6 +1268,7 @@
         mSatelliteOutgoingDatagram2.datagramSizeBytes = 512;
         mSatelliteOutgoingDatagram2.datagramTransferTimeMillis = 1 * 1000;
         mSatelliteOutgoingDatagram2.isDemoMode = true;
+        mSatelliteOutgoingDatagram2.carrierId = 10;
 
         mSatelliteOutgoingDatagrams =
                 new SatelliteOutgoingDatagram[] {
@@ -1266,6 +1280,7 @@
         mSatelliteProvision1.provisioningTimeSec = 3 * 60;
         mSatelliteProvision1.isProvisionRequest = true;
         mSatelliteProvision1.isCanceled = false;
+        mSatelliteProvision1.carrierId = 1;
 
         mSatelliteProvision2 = new SatelliteProvision();
         mSatelliteProvision2.resultCode =
@@ -1273,6 +1288,7 @@
         mSatelliteProvision2.provisioningTimeSec = 0;
         mSatelliteProvision2.isProvisionRequest = false;
         mSatelliteProvision2.isCanceled = true;
+        mSatelliteProvision2.carrierId = 10;
 
         mSatelliteProvisions =
                 new SatelliteProvision[] {
@@ -1359,6 +1375,8 @@
         mCarrierRoamingSatelliteControllerStats1.satelliteSessionGapMinSec = 2;
         mCarrierRoamingSatelliteControllerStats1.satelliteSessionGapAvgSec = 3;
         mCarrierRoamingSatelliteControllerStats1.satelliteSessionGapMaxSec = 4;
+        mCarrierRoamingSatelliteControllerStats1.carrierId = 1;
+        mCarrierRoamingSatelliteControllerStats1.isDeviceEntitled = true;
 
         mCarrierRoamingSatelliteControllerStats2 = new CarrierRoamingSatelliteControllerStats();
         mCarrierRoamingSatelliteControllerStats2.configDataSource =
@@ -1369,6 +1387,8 @@
         mCarrierRoamingSatelliteControllerStats2.satelliteSessionGapMinSec = 5;
         mCarrierRoamingSatelliteControllerStats2.satelliteSessionGapAvgSec = 10;
         mCarrierRoamingSatelliteControllerStats2.satelliteSessionGapMaxSec = 15;
+        mCarrierRoamingSatelliteControllerStats2.carrierId = 10;
+        mCarrierRoamingSatelliteControllerStats2.isDeviceEntitled = false;
 
         // CarrierRoamingSatelliteController has one data point
         mCarrierRoamingSatelliteControllerStats = new CarrierRoamingSatelliteControllerStats[] {
@@ -1421,6 +1441,7 @@
         mSatelliteAccessController1.resultCode = SATELLITE_RESULT_SUCCESS;
         mSatelliteAccessController1.countryCodes = new String[]{"AB", "CD"};
         mSatelliteAccessController1.configDataSource = CONFIG_DATA_SOURCE_DEVICE_CONFIG;
+        mSatelliteAccessController1.carrierId = 1;
 
         mSatelliteAccessController2 = new SatelliteAccessController();
         mSatelliteAccessController2.accessControlType = ACCESS_CONTROL_TYPE_CURRENT_LOCATION;
@@ -1432,6 +1453,7 @@
         mSatelliteAccessController2.resultCode = SATELLITE_RESULT_SUCCESS;
         mSatelliteAccessController2.countryCodes = new String[]{"EF", "GH"};
         mSatelliteAccessController2.configDataSource = CONFIG_DATA_SOURCE_CONFIG_UPDATER;
+        mSatelliteAccessController2.carrierId = 10;
 
         mSatelliteAccessControllers = new SatelliteAccessController[]{
                 mSatelliteAccessController1, mSatelliteAccessController2
@@ -4428,6 +4450,8 @@
         expected.countOfDatagramTypeKeepAliveFail =
                 mSatelliteController1.countOfDatagramTypeKeepAliveFail
                         + mSatelliteController2.countOfDatagramTypeKeepAliveFail;
+        expected.isProvisioned = mSatelliteController2.isProvisioned;
+        expected.carrierId = mSatelliteController2.carrierId;
 
         // Service state and service switch should be added successfully
         verifyCurrentStateSavedToFileOnce();
@@ -4950,41 +4974,56 @@
     }
 
     @Test
-    public void addCarrierRoamingSatelliteControllerStats_withExistingEntries() throws Exception {
+    public void addCarrierRoamingSatelliteControllerStats_withExistingCarrierId() throws Exception {
         createEmptyTestFile();
         mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
         mPersistAtomsStorage.addCarrierRoamingSatelliteControllerStats(
-                mCarrierRoamingSatelliteControllerStats1);
+                copyOf(mCarrierRoamingSatelliteControllerStats1));
         mPersistAtomsStorage.addCarrierRoamingSatelliteControllerStats(
-                mCarrierRoamingSatelliteControllerStats2);
+                copyOf(mCarrierRoamingSatelliteControllerStats1));
         mPersistAtomsStorage.incTimeMillis(100L);
 
         CarrierRoamingSatelliteControllerStats expected =
                 new CarrierRoamingSatelliteControllerStats();
-        expected.configDataSource = mCarrierRoamingSatelliteControllerStats2.configDataSource;
+        expected.configDataSource = mCarrierRoamingSatelliteControllerStats1.configDataSource;
         expected.countOfEntitlementStatusQueryRequest =
-                mCarrierRoamingSatelliteControllerStats1.countOfEntitlementStatusQueryRequest
-                        + mCarrierRoamingSatelliteControllerStats2
-                        .countOfEntitlementStatusQueryRequest;
+                mCarrierRoamingSatelliteControllerStats1.countOfEntitlementStatusQueryRequest * 2;
         expected.countOfSatelliteConfigUpdateRequest =
-                mCarrierRoamingSatelliteControllerStats1.countOfSatelliteConfigUpdateRequest
-                        + mCarrierRoamingSatelliteControllerStats2
-                        .countOfSatelliteConfigUpdateRequest;
+                mCarrierRoamingSatelliteControllerStats1.countOfSatelliteConfigUpdateRequest * 2;
         expected.countOfSatelliteNotificationDisplayed =
-                mCarrierRoamingSatelliteControllerStats1.countOfSatelliteNotificationDisplayed
-                + mCarrierRoamingSatelliteControllerStats2
-                        .countOfSatelliteNotificationDisplayed;
+                mCarrierRoamingSatelliteControllerStats1.countOfSatelliteNotificationDisplayed * 2;
         expected.satelliteSessionGapMinSec =
-                mCarrierRoamingSatelliteControllerStats2.satelliteSessionGapMinSec;
+                mCarrierRoamingSatelliteControllerStats1.satelliteSessionGapMinSec;
         expected.satelliteSessionGapAvgSec =
-                mCarrierRoamingSatelliteControllerStats2.satelliteSessionGapAvgSec;
+                mCarrierRoamingSatelliteControllerStats1.satelliteSessionGapAvgSec;
         expected.satelliteSessionGapMaxSec =
-                mCarrierRoamingSatelliteControllerStats2.satelliteSessionGapMaxSec;
-
+                mCarrierRoamingSatelliteControllerStats1.satelliteSessionGapMaxSec;
+        expected.carrierId = mCarrierRoamingSatelliteControllerStats1.carrierId;
+        expected.isDeviceEntitled = mCarrierRoamingSatelliteControllerStats1.isDeviceEntitled;
         verifyCurrentStateSavedToFileOnce();
         CarrierRoamingSatelliteControllerStats[] output =
                 mPersistAtomsStorage.getCarrierRoamingSatelliteControllerStats(0L);
-        assertHasStats(output, expected);
+        assertHasStats(output, expected, 1);
+    }
+
+    @Test
+    public void addCarrierRoamingSatelliteControllerStats_addNewCarrierId() throws Exception {
+        createEmptyTestFile();
+        mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+        mPersistAtomsStorage.addCarrierRoamingSatelliteControllerStats(
+                copyOf(mCarrierRoamingSatelliteControllerStats1));
+        mPersistAtomsStorage.addCarrierRoamingSatelliteControllerStats(
+                copyOf(mCarrierRoamingSatelliteControllerStats2));
+        mPersistAtomsStorage.incTimeMillis(100L);
+
+        CarrierRoamingSatelliteControllerStats expected1 = mCarrierRoamingSatelliteControllerStats1;
+        CarrierRoamingSatelliteControllerStats expected2 = mCarrierRoamingSatelliteControllerStats2;
+
+        CarrierRoamingSatelliteControllerStats[] output =
+                mPersistAtomsStorage.getCarrierRoamingSatelliteControllerStats(0L);
+
+        assertHasStats(output, expected1, 1);
+        assertHasStats(output, expected2, 1);
     }
 
     @Test
@@ -5843,6 +5882,7 @@
         assertEquals(tested[0].countOfSatelliteAccessCheckFail,
                 expectedStats.countOfSatelliteAccessCheckFail);
         assertEquals(tested[0].isProvisioned, expectedStats.isProvisioned);
+        assertEquals(tested[0].carrierId, expectedStats.carrierId);
     }
 
     private static void assertHasStatsAndCount(
@@ -5869,7 +5909,14 @@
                         == expectedStats.countOfIncomingDatagramSuccess
                     && stats.countOfIncomingDatagramFailed
                         == expectedStats.countOfIncomingDatagramFailed
-                    && stats.isDemoMode == expectedStats.isDemoMode) {
+                    && stats.isDemoMode == expectedStats.isDemoMode
+                    && stats.carrierId == expectedStats.carrierId
+                    && stats.countOfSatelliteNotificationDisplayed
+                    == expectedStats.countOfSatelliteNotificationDisplayed
+                    && stats.countOfAutoExitDueToScreenOff
+                    == expectedStats.countOfAutoExitDueToScreenOff
+                    && stats.countOfAutoExitDueToTnNetwork
+                    == expectedStats.countOfAutoExitDueToTnNetwork) {
                 actualCount = stats.count;
             }
         }
@@ -5886,7 +5933,8 @@
                     && stats.datagramSizeBytes == expectedStats.datagramSizeBytes
                     && stats.datagramTransferTimeMillis
                         == expectedStats.datagramTransferTimeMillis
-                    && stats.isDemoMode == expectedStats.isDemoMode) {
+                    && stats.isDemoMode == expectedStats.isDemoMode
+                    && stats.carrierId == expectedStats.carrierId) {
                 actualCount++;
             }
         }
@@ -5904,7 +5952,8 @@
                     && stats.datagramSizeBytes == expectedStats.datagramSizeBytes
                     && stats.datagramTransferTimeMillis
                         == expectedStats.datagramTransferTimeMillis
-                    && stats.isDemoMode == expectedStats.isDemoMode) {
+                    && stats.isDemoMode == expectedStats.isDemoMode
+                    && stats.carrierId == expectedStats.carrierId) {
                 actualCount++;
             }
         }
@@ -5920,7 +5969,8 @@
             if (stats.resultCode == expectedStats.resultCode
                     && stats.provisioningTimeSec == expectedStats.provisioningTimeSec
                     && stats.isProvisionRequest == expectedStats.isProvisionRequest
-                    && stats.isCanceled == expectedStats.isCanceled) {
+                    && stats.isCanceled == expectedStats.isCanceled
+                    && stats.carrierId == expectedStats.carrierId) {
                 actualCount++;
             }
         }
@@ -5965,7 +6015,8 @@
                     && stats.isEmergency == expectedStats.isEmergency
                     && stats.resultCode == expectedStats.resultCode
                     && Arrays.equals(stats.countryCodes, expectedStats.countryCodes)
-                    && stats.configDataSource == expectedStats.configDataSource) {
+                    && stats.configDataSource == expectedStats.configDataSource
+                    && stats.carrierId == expectedStats.carrierId) {
                 actualCount++;
             }
         }
@@ -6213,18 +6264,29 @@
     }
 
     private static void assertHasStats(CarrierRoamingSatelliteControllerStats[] tested,
-            @Nullable CarrierRoamingSatelliteControllerStats expectedStats) {
+            @Nullable CarrierRoamingSatelliteControllerStats expectedStats, int expectedCount) {
         assertNotNull(tested);
-        assertEquals(tested[0].configDataSource, expectedStats.configDataSource);
-        assertEquals(tested[0].countOfEntitlementStatusQueryRequest,
-                expectedStats.countOfEntitlementStatusQueryRequest);
-        assertEquals(tested[0].countOfSatelliteConfigUpdateRequest,
-                expectedStats.countOfSatelliteConfigUpdateRequest);
-        assertEquals(tested[0].countOfSatelliteNotificationDisplayed,
-                expectedStats.countOfSatelliteNotificationDisplayed);
-        assertEquals(tested[0].satelliteSessionGapMinSec, expectedStats.satelliteSessionGapMinSec);
-        assertEquals(tested[0].satelliteSessionGapAvgSec, expectedStats.satelliteSessionGapAvgSec);
-        assertEquals(tested[0].satelliteSessionGapMaxSec, expectedStats.satelliteSessionGapMaxSec);
+        int count = 0;
+        for (CarrierRoamingSatelliteControllerStats stats : tested) {
+            if (expectedStats.carrierId == stats.carrierId) {
+                assertEquals(expectedStats.configDataSource, stats.configDataSource);
+                assertEquals(expectedStats.countOfEntitlementStatusQueryRequest,
+                        stats.countOfEntitlementStatusQueryRequest);
+                assertEquals(expectedStats.countOfSatelliteConfigUpdateRequest,
+                        stats.countOfSatelliteConfigUpdateRequest);
+                assertEquals(expectedStats.countOfSatelliteNotificationDisplayed,
+                        stats.countOfSatelliteNotificationDisplayed);
+                assertEquals(expectedStats.satelliteSessionGapMinSec,
+                        stats.satelliteSessionGapMinSec);
+                assertEquals(expectedStats.satelliteSessionGapAvgSec,
+                        stats.satelliteSessionGapAvgSec);
+                assertEquals(expectedStats.satelliteSessionGapMaxSec,
+                        stats.satelliteSessionGapMaxSec);
+                assertEquals(expectedStats.isDeviceEntitled, stats.isDeviceEntitled);
+                count++;
+            }
+        }
+        assertEquals(expectedCount, count);
     }
 
     private static void assertHasStatsAndCount(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java
index da0dfe9..5740336 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java
@@ -434,6 +434,8 @@
                         .setSatelliteSessionGapMinSec(15)
                         .setSatelliteSessionGapAvgSec(30)
                         .setSatelliteSessionGapMaxSec(45)
+                        .setCarrierId(10)
+                        .setIsDeviceEntitled(true)
                         .build();
 
         mSatelliteStats.onCarrierRoamingSatelliteControllerStatsMetrics(param);
@@ -452,6 +454,70 @@
         assertEquals(param.getSatelliteSessionGapMinSec(), stats.satelliteSessionGapMinSec);
         assertEquals(param.getSatelliteSessionGapAvgSec(), stats.satelliteSessionGapAvgSec);
         assertEquals(param.getSatelliteSessionGapMaxSec(), stats.satelliteSessionGapMaxSec);
+        assertEquals(param.getCarrierId(), stats.carrierId);
+        assertEquals(param.isDeviceEntitled(), stats.isDeviceEntitled);
+
+        verifyNoMoreInteractions(mPersistAtomsStorage);
+    }
+
+    @Test
+    public void onCarrierRoamingSatelliteControllerStatsMetrics_testStaticFields()
+            throws Exception {
+        SatelliteStats.CarrierRoamingSatelliteControllerStatsParams param =
+                new SatelliteStats.CarrierRoamingSatelliteControllerStatsParams.Builder()
+                        .setConfigDataSource(4)
+                        .setCountOfEntitlementStatusQueryRequest(6)
+                        .setCountOfSatelliteConfigUpdateRequest(2)
+                        .setCountOfSatelliteNotificationDisplayed(1)
+                        .setSatelliteSessionGapMinSec(15)
+                        .setSatelliteSessionGapAvgSec(30)
+                        .setSatelliteSessionGapMaxSec(45)
+                        .setCarrierId(10)
+                        .setIsDeviceEntitled(true)
+                        .build();
+        mSatelliteStats.onCarrierRoamingSatelliteControllerStatsMetrics(param);
+
+        ArgumentCaptor<CarrierRoamingSatelliteControllerStats> captor =
+                ArgumentCaptor.forClass(CarrierRoamingSatelliteControllerStats.class);
+        verify(mPersistAtomsStorage, times(1)).addCarrierRoamingSatelliteControllerStats(
+                captor.capture());
+        CarrierRoamingSatelliteControllerStats stats = captor.getValue();
+        assertEquals(param.getCountOfEntitlementStatusQueryRequest(),
+                stats.countOfEntitlementStatusQueryRequest);
+        assertEquals(param.getCarrierId(), stats.carrierId);
+        assertEquals(param.isDeviceEntitled(), stats.isDeviceEntitled);
+
+        param = new SatelliteStats.CarrierRoamingSatelliteControllerStatsParams.Builder()
+                .setCountOfSatelliteConfigUpdateRequest(2)
+                .build();
+        mSatelliteStats.onCarrierRoamingSatelliteControllerStatsMetrics(param);
+
+        captor = ArgumentCaptor.forClass(CarrierRoamingSatelliteControllerStats.class);
+        verify(mPersistAtomsStorage, times(2)).addCarrierRoamingSatelliteControllerStats(
+                captor.capture());
+        stats = captor.getValue();
+        // count should be added
+        assertEquals(2, stats.countOfSatelliteConfigUpdateRequest);
+        // static values should not be updated
+        assertEquals(10, stats.carrierId);
+        assertEquals(true, stats.isDeviceEntitled);
+
+        param = new SatelliteStats.CarrierRoamingSatelliteControllerStatsParams.Builder()
+                .setCountOfSatelliteConfigUpdateRequest(2)
+                .setCarrierId(20)
+                .setIsDeviceEntitled(false)
+                .build();
+        mSatelliteStats.onCarrierRoamingSatelliteControllerStatsMetrics(param);
+
+        captor = ArgumentCaptor.forClass(CarrierRoamingSatelliteControllerStats.class);
+        verify(mPersistAtomsStorage, times(3)).addCarrierRoamingSatelliteControllerStats(
+                captor.capture());
+        stats = captor.getValue();
+        // count should be added
+        assertEquals(2, stats.countOfSatelliteConfigUpdateRequest);
+        // static values should be updated
+        assertEquals(20, stats.carrierId);
+        assertEquals(false, stats.isDeviceEntitled);
 
         verifyNoMoreInteractions(mPersistAtomsStorage);
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/ControllerMetricsStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/ControllerMetricsStatsTest.java
index 8173bbc..fdb94c9 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/ControllerMetricsStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/ControllerMetricsStatsTest.java
@@ -180,7 +180,7 @@
         assertEquals(0, mTestStats.mCountOfIncomingDatagramFail);
         assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsSuccess);
         assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsFail);
-        assertEquals(10, mTestStats.mCountOfDatagramTypeLocationSharingSuccess);
+        assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingSuccess);
         assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingFail);
         assertEquals(0, mTestStats.mCountOfProvisionSuccess);
         assertEquals(0, mTestStats.mCountOfProvisionFail);
@@ -282,7 +282,7 @@
         assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsSuccess);
         assertEquals(0, mTestStats.mCountOfDatagramTypeSosSmsFail);
         assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingSuccess);
-        assertEquals(10, mTestStats.mCountOfDatagramTypeLocationSharingFail);
+        assertEquals(0, mTestStats.mCountOfDatagramTypeLocationSharingFail);
         assertEquals(0, mTestStats.mCountOfProvisionSuccess);
         assertEquals(0, mTestStats.mCountOfProvisionFail);
         assertEquals(0, mTestStats.mCountOfDeprovisionSuccess);
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 e51cdc7..142e70a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramDispatcherTest.java
@@ -16,11 +16,12 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_MODEM_TIMEOUT;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
-import static com.android.internal.telephony.satellite.DatagramController.SATELLITE_ALIGN_TIMEOUT;
 import static com.android.internal.telephony.SmsDispatchersController.PendingRequest;
+import static com.android.internal.telephony.satellite.DatagramController.SATELLITE_ALIGN_TIMEOUT;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -176,6 +177,7 @@
         when(mMockDatagramController.isPollingInIdleState()).thenReturn(true);
         when(mMockSatelliteController.getSatellitePhone()).thenReturn(mPhone);
         when(mPhone.getSmsDispatchersController()).thenReturn(mMockSmsDispatchersController);
+        when(mMockSatelliteController.getSatelliteCarrierId()).thenReturn(UNKNOWN_CARRIER_ID);
         mPendingSms = createPendingRequest();
     }
 
@@ -822,7 +824,7 @@
                         eq(SATELLITE_RESULT_SUCCESS));
         verify(mMockSmsDispatchersController).sendCarrierRoamingNbIotNtnText(eq(mPendingSms));
 
-        mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.messageId, true);
+        mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.uniqueMessageId, true);
         processAllMessages();
 
         mInOrder.verify(mMockDatagramController)
@@ -856,7 +858,7 @@
                         eq(SATELLITE_RESULT_SUCCESS));
         verify(mMockSmsDispatchersController).sendCarrierRoamingNbIotNtnText(eq(mPendingSms));
 
-        mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.messageId, false);
+        mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.uniqueMessageId, false);
         processAllMessages();
 
         mInOrder.verify(mMockDatagramController)
@@ -1054,7 +1056,7 @@
                         eq(SATELLITE_RESULT_SUCCESS));
         verify(mMockSmsDispatchersController).sendCarrierRoamingNbIotNtnText(eq(mPendingSms));
 
-        mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.messageId, true);
+        mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.uniqueMessageId, true);
         processAllMessages();
 
         mInOrder.verify(mMockDatagramController)
@@ -1105,7 +1107,7 @@
         processAllMessages();
         verifyZeroInteractions(mMockSatelliteModemInterface);
 
-        mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.messageId, true);
+        mDatagramDispatcherUT.onSendSmsDone(mPhone.getSubId(), mPendingSms.uniqueMessageId, true);
         processAllMessages();
         mInOrder.verify(mMockDatagramController)
                 .updateSendStatus(eq(SUB_ID), eq(datagramTypeSms),
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 947661b..e964ced 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
+
 import static com.android.internal.telephony.satellite.DatagramController.SATELLITE_ALIGN_TIMEOUT;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -48,6 +50,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.provider.Telephony;
+import android.telephony.SubscriptionManager;
 import android.telephony.satellite.ISatelliteDatagramCallback;
 import android.telephony.satellite.SatelliteDatagram;
 import android.telephony.satellite.SatelliteManager;
@@ -78,7 +81,7 @@
 @TestableLooper.RunWithLooper
 public class DatagramReceiverTest extends TelephonyTest {
     private static final String TAG = "DatagramReceiverTest";
-    private static final int SUB_ID = 0;
+    private static final int SUB_ID = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
     private static final String TEST_MESSAGE = "This is a test datagram message";
     private static final long TEST_EXPIRE_TIMER_SATELLITE_ALIGN = TimeUnit.SECONDS.toMillis(1);
     private static final long TEST_DATAGRAM_WAIT_FOR_CONNECTED_STATE_TIMEOUT_MILLIS =
@@ -144,6 +147,7 @@
         when(mMockDatagramController.isPollingInIdleState()).thenReturn(true);
         when(mMockDatagramController.needsWaitingForSatelliteConnected(
                 eq(SatelliteManager.DATAGRAM_TYPE_UNKNOWN))).thenReturn(false);
+        when(mMockSatelliteController.getSatelliteCarrierId()).thenReturn(UNKNOWN_CARRIER_ID);
         processAllMessages();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/PointingAppControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/PointingAppControllerTest.java
index 36d32fe..a228617 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/PointingAppControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/PointingAppControllerTest.java
@@ -39,6 +39,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.os.UserHandle;
 import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
 import android.telephony.satellite.PointingInfo;
 import android.telephony.satellite.SatelliteManager;
@@ -196,6 +197,11 @@
             }
         }
 
+        @Override
+        public void onSendDatagramRequested(int datagramType) {
+            logd("onSendDatagramRequested: datagramType=" + datagramType);
+        }
+
         public int getDatagramType() {
             return mDatagramType;
         }
@@ -316,7 +322,7 @@
     public void testStartPointingUI() throws Exception {
         ArgumentCaptor<Intent> startedIntentCaptor = ArgumentCaptor.forClass(Intent.class);
         mPointingAppController.startPointingUI(true, true, true);
-        verify(mContext).startActivity(startedIntentCaptor.capture());
+        verify(mContext).startActivityAsUser(startedIntentCaptor.capture(), eq(UserHandle.CURRENT));
         Intent intent = startedIntentCaptor.getValue();
         assertEquals(KEY_POINTING_UI_PACKAGE_NAME, intent.getComponent().getPackageName());
         assertEquals(KEY_POINTING_UI_CLASS_NAME, intent.getComponent().getClassName());
@@ -332,10 +338,12 @@
     @Test
     public void testRestartPointingUi() throws Exception {
         mPointingAppController.startPointingUI(true, false, true);
-        mInOrderForPointingUi.verify(mContext).startActivity(any(Intent.class));
+        mInOrderForPointingUi.verify(mContext).startActivityAsUser(any(Intent.class),
+                eq(UserHandle.CURRENT));
         testRestartPointingUi(true, false, true);
         mPointingAppController.startPointingUI(false, true, false);
-        mInOrderForPointingUi.verify(mContext).startActivity(any(Intent.class));
+        mInOrderForPointingUi.verify(mContext).startActivityAsUser(any(Intent.class),
+                eq(UserHandle.CURRENT));
         testRestartPointingUi(false, true, false);
     }
 
@@ -346,7 +354,8 @@
             .getPackagesForUid(anyInt());
         mPointingAppController.mUidImportanceListener.onUidImportance(1, IMPORTANCE_GONE);
         ArgumentCaptor<Intent> restartedIntentCaptor = ArgumentCaptor.forClass(Intent.class);
-        mInOrderForPointingUi.verify(mContext).startActivity(restartedIntentCaptor.capture());
+        mInOrderForPointingUi.verify(mContext).startActivityAsUser(restartedIntentCaptor.capture(),
+                eq(UserHandle.CURRENT));
         Intent restartIntent = restartedIntentCaptor.getValue();
         assertEquals(KEY_POINTING_UI_PACKAGE_NAME, restartIntent.getComponent().getPackageName());
         assertEquals(KEY_POINTING_UI_CLASS_NAME, restartIntent.getComponent().getClassName());
@@ -413,7 +422,6 @@
         TestSatelliteTransmissionUpdateCallback callback2 = new
                 TestSatelliteTransmissionUpdateCallback();
         int subId1 = 3;
-        int subId2 = 4;
         mPointingAppController.registerForSatelliteTransmissionUpdates(subId1, callback1);
         mInOrder.verify(mMockSatelliteModemInterface).registerForSatellitePositionInfoChanged(any(),
                 eq(1), eq(null));
@@ -424,16 +432,6 @@
                 .registerForSatellitePositionInfoChanged(any(), eq(1), eq(null));
         mInOrder.verify(mMockSatelliteModemInterface, never())
                 .registerForDatagramTransferStateChanged(any(), eq(4), eq(null));
-        mPointingAppController.registerForSatelliteTransmissionUpdates(subId2, callback1);
-        mInOrder.verify(mMockSatelliteModemInterface).registerForSatellitePositionInfoChanged(any(),
-                eq(1), eq(null));
-        mInOrder.verify(mMockSatelliteModemInterface).registerForDatagramTransferStateChanged(any(),
-                eq(4), eq(null));
-        mPointingAppController.registerForSatelliteTransmissionUpdates(subId2, callback2);
-        mInOrder.verify(mMockSatelliteModemInterface, never())
-                .registerForSatellitePositionInfoChanged(any(), eq(1), eq(null));
-        mInOrder.verify(mMockSatelliteModemInterface, never())
-                .registerForDatagramTransferStateChanged(any(), eq(4), eq(null));
         mPointingAppController.unregisterForSatelliteTransmissionUpdates(subId1,
                 mResultListener::offer, callback1);
         processAllMessages();
@@ -446,22 +444,6 @@
                 any(Handler.class));
         mInOrder.verify(mMockSatelliteModemInterface).unregisterForDatagramTransferStateChanged(
                 any(Handler.class));
-        mPointingAppController.unregisterForSatelliteTransmissionUpdates(subId2,
-                mResultListener::offer, callback1);
-        processAllMessages();
-        assertThat(mResultListener.peek()).isEqualTo(SatelliteManager.SATELLITE_RESULT_SUCCESS);
-        mResultListener.remove();
-        mInOrder.verify(mMockSatelliteModemInterface, never())
-                .unregisterForSatellitePositionInfoChanged(any(Handler.class));
-        mInOrder.verify(mMockSatelliteModemInterface, never())
-                .unregisterForDatagramTransferStateChanged(any(Handler.class));
-        mPointingAppController.unregisterForSatelliteTransmissionUpdates(subId2,
-                mResultListener::offer, callback2);
-        processAllMessages();
-        mInOrder.verify(mMockSatelliteModemInterface).unregisterForSatellitePositionInfoChanged(
-                any(Handler.class));
-        mInOrder.verify(mMockSatelliteModemInterface).unregisterForDatagramTransferStateChanged(
-                any(Handler.class));
         mInOrder = null;
     }
 
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 a671e13..9c395ad 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
@@ -16,6 +16,11 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN;
+import static android.hardware.devicestate.feature.flags.Flags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION;
 import static android.telephony.CarrierConfigManager.KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT;
 import static android.telephony.CarrierConfigManager.KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT;
 import static android.telephony.CarrierConfigManager.KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT;
@@ -23,6 +28,7 @@
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ESOS_SUPPORTED_BOOL;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_NIDD_APN_NAME_STRING;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL;
 import static android.telephony.NetworkRegistrationInfo.SERVICE_TYPE_DATA;
 import static android.telephony.SubscriptionManager.SATELLITE_ENTITLEMENT_STATUS;
@@ -31,6 +37,7 @@
 import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE;
 import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_POOR;
 import static android.telephony.satellite.SatelliteManager.KEY_DEMO_MODE_ENABLED;
+import static android.telephony.satellite.SatelliteManager.KEY_DEPROVISION_SATELLITE_TOKENS;
 import static android.telephony.satellite.SatelliteManager.KEY_EMERGENCY_MODE_ENABLED;
 import static android.telephony.satellite.SatelliteManager.KEY_NTN_SIGNAL_STRENGTH;
 import static android.telephony.satellite.SatelliteManager.KEY_PROVISION_SATELLITE_TOKENS;
@@ -108,6 +115,7 @@
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.res.Resources;
+import android.hardware.devicestate.DeviceState;
 import android.os.AsyncResult;
 import android.os.Bundle;
 import android.os.CancellationSignal;
@@ -119,6 +127,10 @@
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CellSignalStrength;
@@ -166,6 +178,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -513,6 +526,10 @@
         }
     };
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
@@ -561,6 +578,9 @@
         mContextFixture.putIntResource(
                 R.integer.config_satellite_wait_for_cellular_modem_off_timeout_millis,
                 TEST_WAIT_FOR_CELLULAR_MODEM_OFF_TIMEOUT_MILLIS);
+        mContextFixture.putIntArrayResource(
+                R.array.config_foldedDeviceStates,
+                new int[0]);
         doReturn(ACTIVE_SUB_IDS).when(mMockSubscriptionManagerService).getActiveSubIdList(true);
 
         mCarrierConfigBundle = mContextFixture.getCarrierConfigBundle();
@@ -606,12 +626,16 @@
                 .when(mMockSessionMetricsStats).setSessionDurationSec(anyInt());
         doReturn(mMockSessionMetricsStats)
                 .when(mMockSessionMetricsStats).setIsDemoMode(anyBoolean());
+        doReturn(mMockSessionMetricsStats)
+                .when(mMockSessionMetricsStats).setCarrierId(anyInt());
         doNothing().when(mMockSessionMetricsStats).reportSessionMetrics();
 
         doReturn(mMockProvisionMetricsStats).when(mMockProvisionMetricsStats)
                 .setResultCode(anyInt());
         doReturn(mMockProvisionMetricsStats).when(mMockProvisionMetricsStats)
                 .setIsProvisionRequest(anyBoolean());
+        doReturn(mMockProvisionMetricsStats).when(mMockProvisionMetricsStats)
+                .setCarrierId(anyInt());
         doNothing().when(mMockProvisionMetricsStats).reportProvisionMetrics();
         doNothing().when(mMockControllerMetricsStats).reportDeprovisionCount(anyInt());
         when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
@@ -917,6 +941,7 @@
 
     @Test
     public void testRequestSatelliteEnabled() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
         mIsSatelliteEnabledSemaphore.drainPermits();
 
         // Fail to enable satellite when SatelliteController is not fully loaded yet.
@@ -1466,49 +1491,6 @@
 
     @Test
     public void testStopSatelliteTransmissionUpdates() {
-        mIIntegerConsumerSemaphore.drainPermits();
-        mIIntegerConsumerResults.clear();
-        mSatelliteControllerUT.stopSatelliteTransmissionUpdates(mIIntegerConsumer,
-                mStopTransmissionUpdateCallback);
-        processAllMessages();
-        assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
-                (long) mIIntegerConsumerResults.get(0));
-
-        mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.stopSatelliteTransmissionUpdates(mIIntegerConsumer,
-                mStopTransmissionUpdateCallback);
-        processAllMessages();
-        assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, (long) mIIntegerConsumerResults.get(0));
-
-        resetSatelliteControllerUT();
-        mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.stopSatelliteTransmissionUpdates(mIIntegerConsumer,
-                mStopTransmissionUpdateCallback);
-        processAllMessages();
-        assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_RESULT_SERVICE_NOT_PROVISIONED,
-                (long) mIIntegerConsumerResults.get(0));
-
-        resetSatelliteControllerUT();
-        mIIntegerConsumerResults.clear();
-        setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForRequestIsSatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteProvisioned(false, SATELLITE_RESULT_SUCCESS);
-        setUpResponseForStopSatelliteTransmissionUpdates(SATELLITE_RESULT_SUCCESS);
-        mSatelliteControllerUT.stopSatelliteTransmissionUpdates(mIIntegerConsumer,
-                mStopTransmissionUpdateCallback);
-        processAllMessages();
-        assertTrue(waitForIIntegerConsumerResult(1));
-        assertEquals(SATELLITE_RESULT_SERVICE_NOT_PROVISIONED,
-                (long) mIIntegerConsumerResults.get(0));
-
         resetSatelliteControllerUT();
         mIIntegerConsumerResults.clear();
         provisionSatelliteService();
@@ -1528,7 +1510,11 @@
         setUpResponseForStopSatelliteTransmissionUpdates(SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
         mSatelliteControllerUT.stopSatelliteTransmissionUpdates(mIIntegerConsumer,
                 mStopTransmissionUpdateCallback);
+        verify(mMockPointingAppController, times(2)).unregisterForSatelliteTransmissionUpdates(
+                anyInt(), any(), eq(mStopTransmissionUpdateCallback));
         processAllMessages();
+        verify(mMockPointingAppController, times(2)).stopSatelliteTransmissionUpdates(
+                any(Message.class));
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE,
                 (long) mIIntegerConsumerResults.get(0));
@@ -1582,7 +1568,7 @@
     public void testIsSatelliteEnabled() {
         logd("testIsSatelliteEnabled: starting");
         setUpResponseForRequestIsSatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
-        assertFalse(mSatelliteControllerUT.isSatelliteEnabled());
+        assertFalse(mSatelliteControllerUT.isSatelliteEnabledOrBeingEnabled());
         mIsSatelliteEnabledSemaphore.drainPermits();
         mSatelliteControllerUT.requestIsSatelliteEnabled(mIsSatelliteEnabledReceiver);
         processAllMessages();
@@ -1598,7 +1584,8 @@
         mSatelliteControllerUT.requestIsSatelliteEnabled(mIsSatelliteEnabledReceiver);
         processAllMessages();
         assertEquals(SATELLITE_RESULT_SUCCESS, mQueriedIsSatelliteEnabledResultCode);
-        assertEquals(mSatelliteControllerUT.isSatelliteEnabled(), mQueriedIsSatelliteEnabled);
+        assertEquals(mSatelliteControllerUT.isSatelliteEnabledOrBeingEnabled(),
+                mQueriedIsSatelliteEnabled);
     }
 
     @Test
@@ -1631,6 +1618,11 @@
             public void onEmergencyModeChanged(boolean isEmergency) {
                 logd("onEmergencyModeChanged: emergency=" + isEmergency);
             }
+
+            @Override
+            public void onRegistrationFailure(int causeCode) {
+                logd("onRegistrationFailure: causeCode=" + causeCode);
+            }
         };
         int errorCode = mSatelliteControllerUT.registerForSatelliteModemStateChanged(callback);
         assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE, errorCode);
@@ -1657,6 +1649,11 @@
             public void onEmergencyModeChanged(boolean isEmergency) {
                 logd("onEmergencyModeChanged: emergency=" + isEmergency);
             }
+
+            @Override
+            public void onRegistrationFailure(int causeCode) {
+                logd("onRegistrationFailure: causeCode=" + causeCode);
+            }
         };
         mSatelliteControllerUT.unregisterForModemStateChanged(callback);
         verify(mMockSatelliteSessionController, never())
@@ -3617,12 +3614,14 @@
         processAllMessages();
         verify(mMockNotificationManager, times(1)).notifyAsUser(anyString(), anyInt(), any(),
                 any());
-        assertTrue(mSharedPreferences.getBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY, false));
+        // Just by showing notification we do not update the pref file , only once user interact
+        // only we will update the pref value to true.
+        assertFalse(mSharedPreferences.getBoolean(SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY, false));
 
         // Check don't display again after displayed already a system notification.
         sendServiceStateChangedEvent();
         processAllMessages();
-        verify(mMockNotificationManager, times(1)).notifyAsUser(anyString(), anyInt(), any(),
+        verify(mMockNotificationManager, times(2)).notifyAsUser(anyString(), anyInt(), any(),
                 any());
     }
 
@@ -4039,6 +4038,7 @@
         when(mServiceState2.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
         mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, true);
         mCarrierConfigBundle.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, 1);
+        mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL, true);
         int[] supportedServices2 = {2};
         int[] supportedServices3 = {1, 3};
         PersistableBundle carrierSupportedSatelliteServicesPerProvider = new PersistableBundle();
@@ -4059,6 +4059,33 @@
         processAllMessages();
 
         assertTrue(mSatelliteControllerUT.isCarrierRoamingNtnEligible(mPhone));
+
+        when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+        when(mServiceState2.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+        processAllMessages();
+        assertFalse(mSatelliteControllerUT.isCarrierRoamingNtnEligible(mPhone));
+
+        when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+        when(mServiceState2.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+        mSatelliteControllerUT.overrideCarrierRoamingNtnEligibilityChanged(true, false);
+        processAllMessages();
+        assertTrue(mSatelliteControllerUT.isCarrierRoamingNtnEligible(mPhone));
+    }
+
+    @Test
+    public void testOverrideCarrierRoamingNtNEligibilityChange() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        mSatelliteControllerUT.overrideCarrierRoamingNtnEligibilityChanged(true, false);
+        verify(mPhone, times(1)).notifyCarrierRoamingNtnEligibleStateChanged(eq(true));
+        clearInvocations(mPhone);
+
+        mSatelliteControllerUT.overrideCarrierRoamingNtnEligibilityChanged(false, false);
+        verify(mPhone, times(1)).notifyCarrierRoamingNtnEligibleStateChanged(eq(false));
+        clearInvocations(mPhone);
+
+        mSatelliteControllerUT.overrideCarrierRoamingNtnEligibilityChanged(false, true);
+        verify(mPhone, times(0)).notifyCarrierRoamingNtnEligibleStateChanged(eq(true));
+        clearInvocations(mPhone);
     }
 
     @Test
@@ -4071,6 +4098,7 @@
         mCarrierConfigBundle.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, 1);
         mCarrierConfigBundle.putInt(
                 KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT, 1 * 60);
+        mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL, true);
         int[] supportedServices2 = {2};
         int[] supportedServices3 = {1, 3};
         PersistableBundle carrierSupportedSatelliteServicesPerProvider = new PersistableBundle();
@@ -4140,7 +4168,7 @@
         assertTrue(mSatelliteControllerUT.isCarrierRoamingNtnEligible(mPhone));
         verify(mPhone, times(0)).notifyCarrierRoamingNtnEligibleStateChanged(eq(true));
         verify(mPhone2, times(0)).notifyCarrierRoamingNtnEligibleStateChanged(anyBoolean());
-        verify(mMockNotificationManager, times(1)).cancelAsUser(anyString(), anyInt(),
+        verify(mMockNotificationManager, times(2)).cancelAsUser(anyString(), anyInt(),
                 any());
     }
 
@@ -4176,7 +4204,7 @@
         assertTrue(mSatelliteControllerUT.getWwanIsInService(mServiceState));
 
         nri = new NetworkRegistrationInfo.Builder().setRegistrationState(
-                NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING)
+                        NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING)
                 .build();
         when(mServiceState.getNetworkRegistrationInfoListForTransportType(
                 eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
@@ -4184,6 +4212,81 @@
         assertFalse(mSatelliteControllerUT.getWwanIsInService(mServiceState));
     }
 
+    @Test
+    public void testRegistrationFailureCallback() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        Semaphore semaphore = new Semaphore(0);
+        final int[] resultErrorCode = new int[1];
+        ISatelliteModemStateCallback callback = new ISatelliteModemStateCallback.Stub() {
+            @Override
+            public void onSatelliteModemStateChanged(int state) {
+                logd("onSatelliteModemStateChanged: state=" + state);
+            }
+
+            @Override
+            public void onEmergencyModeChanged(boolean isEmergency) {
+                logd("onEmergencyModeChanged: emergency=" + isEmergency);
+            }
+
+            @Override
+            public void onRegistrationFailure(int causeCode) {
+                logd("onRegistrationFailure: causeCode=" + causeCode);
+                resultErrorCode[0] = causeCode;
+                semaphore.release();
+            }
+        };
+        resetSatelliteControllerUTToSupportedAndProvisionedState();
+        mSatelliteControllerUT.setSatelliteSessionController(mMockSatelliteSessionController);
+
+        int RegisterErrorCode = mSatelliteControllerUT.registerForSatelliteModemStateChanged(
+                callback);
+        assertEquals(SATELLITE_RESULT_SUCCESS, RegisterErrorCode);
+        verify(mMockSatelliteSessionController).registerForSatelliteModemStateChanged(callback);
+
+        int expectedErrorCode = 100;
+        mIIntegerConsumerResults.clear();
+        sendSatelliteRegistrationFailureEvent(100, null);
+        processAllMessages();
+        assertTrue(waitForForEvents(
+                semaphore, 1, "testRegistrationFailureCallback"));
+        assertEquals(expectedErrorCode, resultErrorCode[0]);
+    }
+
+    @RequiresFlagsDisabled(FLAG_DEVICE_STATE_PROPERTY_MIGRATION)
+    @Test
+    public void testDetermineIsFoldable_overlayConfigurationValues() {
+        // isFoldable should return false with the base configuration.
+        assertFalse(mSatelliteControllerUT.isFoldable(mContext,
+                mSatelliteControllerUT.getSupportedDeviceStates()));
+
+        mContextFixture.putIntArrayResource(R.array.config_foldedDeviceStates, new int[2]);
+        assertTrue(mSatelliteControllerUT.isFoldable(mContext,
+                mSatelliteControllerUT.getSupportedDeviceStates()));
+    }
+
+    @RequiresFlagsEnabled(FLAG_DEVICE_STATE_PROPERTY_MIGRATION)
+    @Test
+    public void testDetermineIsFoldable_deviceStateManager() {
+        // isFoldable should return false with the base configuration.
+        assertFalse(mSatelliteControllerUT.isFoldable(mContext,
+                mSatelliteControllerUT.getSupportedDeviceStates()));
+
+        DeviceState foldedDeviceState = new DeviceState(new DeviceState.Configuration.Builder(
+                0 /* identifier */, "FOLDED")
+                .setSystemProperties(Set.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY))
+                .setPhysicalProperties(
+                        Set.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED))
+                .build());
+        DeviceState unfoldedDeviceState = new DeviceState(new DeviceState.Configuration.Builder(
+                1 /* identifier */, "UNFOLDED")
+                .setSystemProperties(Set.of(PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY))
+                .setPhysicalProperties(
+                        Set.of(PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN))
+                .build());
+        List<DeviceState> foldableDeviceStateList = List.of(foldedDeviceState, unfoldedDeviceState);
+        assertTrue(mSatelliteControllerUT.isFoldable(mContext, foldableDeviceStateList));
+    }
+
     private boolean mProvisionState = false;
     private int mProvisionSateResultCode = -1;
     private Semaphore mProvisionSateSemaphore = new Semaphore(0);
@@ -4300,7 +4403,6 @@
         assertTrue(mProvisionState);
     }
 
-
     @Test
     public void testRegisterForSatelliteSubscriptionProvisionStateChanged() throws Exception {
         when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
@@ -4359,8 +4461,6 @@
         inputList.add(list.get(1));
         verifyProvisionSatellite(inputList);
 
-        verify(mMockSatelliteModemInterface, times(2)).updateSatelliteSubscription(anyString(),
-                any());
         assertTrue(waitForForEvents(
                 semaphore, 1, "testRegisterForSatelliteSubscriptionProvisionStateChanged"));
         assertTrue(resultArray[1].getProvisionStatus());
@@ -4370,10 +4470,92 @@
         // requested, and verify that onSatelliteSubscriptionProvisionStateChanged is not called.
         verifyProvisionSatellite(inputList);
 
-        verify(mMockSatelliteModemInterface, times(2)).updateSatelliteSubscription(anyString(),
-                any());
         assertFalse(waitForForEvents(
                 semaphore, 1, "testRegisterForSatelliteSubscriptionProvisionStateChanged"));
+
+        // Request deprovision for subscriberID 2, verify that subscriberID 2 is set to
+        // deprovision and that subscriberID 1 is set to provision.
+        verifyDeprovisionSatellite(inputList);
+        assertTrue(waitForForEvents(
+                semaphore, 1, "testRegisterForSatelliteSubscriptionProvisionStateChanged"));
+        assertFalse(resultArray[1].getProvisionStatus());
+        assertEquals(mSubscriberId2, resultArray[1].getSatelliteSubscriberInfo().getSubscriberId());
+        assertTrue(resultArray[0].getProvisionStatus());
+        assertEquals(mSubscriberId, resultArray[0].getSatelliteSubscriberInfo().getSubscriberId());
+
+        // Request deprovision for subscriberID 1, verify that subscriberID 1 is set to deprovision.
+        inputList = new ArrayList<>();
+        inputList.add(list.get(0));
+        verifyDeprovisionSatellite(inputList);
+        assertTrue(waitForForEvents(
+                semaphore, 1, "testRegisterForSatelliteSubscriptionProvisionStateChanged"));
+        assertFalse(resultArray[1].getProvisionStatus());
+        assertEquals(mSubscriberId2, resultArray[1].getSatelliteSubscriberInfo().getSubscriberId());
+        assertFalse(resultArray[0].getProvisionStatus());
+        assertEquals(mSubscriberId, resultArray[0].getSatelliteSubscriberInfo().getSubscriberId());
+
+        // Request provision for subscriberID 2, verify that subscriberID 2 is set to provision.
+        inputList = new ArrayList<>();
+        inputList.add(list.get(1));
+        verifyProvisionSatellite(inputList);
+
+        assertTrue(waitForForEvents(
+                semaphore, 1, "testRegisterForSatelliteSubscriptionProvisionStateChanged"));
+        assertTrue(resultArray[1].getProvisionStatus());
+        assertEquals(mSubscriberId2, resultArray[1].getSatelliteSubscriberInfo().getSubscriberId());
+        assertFalse(resultArray[0].getProvisionStatus());
+        assertEquals(mSubscriberId, resultArray[0].getSatelliteSubscriberInfo().getSubscriberId());
+    }
+
+    private boolean mDeprovisionDone = false;
+    private int mDeprovisionSateResultCode = -1;
+    private Semaphore mDeprovisionSateSemaphore = new Semaphore(0);
+    private ResultReceiver mDeprovisionSatelliteReceiver = new ResultReceiver(null) {
+        @Override
+        protected void onReceiveResult(int resultCode, Bundle resultData) {
+            mDeprovisionSateResultCode = resultCode;
+            logd("DeprovisionSatelliteReceiver: resultCode=" + resultCode);
+            if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                if (resultData.containsKey(KEY_DEPROVISION_SATELLITE_TOKENS)) {
+                    mDeprovisionDone = resultData.getBoolean(KEY_DEPROVISION_SATELLITE_TOKENS);
+                    logd("DeprovisionSatelliteReceiver: deprovision=" + mDeprovisionDone);
+                } else {
+                    loge("KEY_DEPROVISION_SATELLITE_TOKENS does not exist.");
+                    mDeprovisionDone = false;
+                }
+            } else {
+                mDeprovisionDone = false;
+            }
+            try {
+                mDeprovisionSateSemaphore.release();
+            } catch (Exception ex) {
+                loge("DeprovisionSatelliteReceiver: Got exception in releasing semaphore " + ex);
+            }
+        }
+    };
+
+    @Test
+    public void testDeprovisionSatellite() throws Exception {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
+        verifyRequestSatelliteSubscriberProvisionStatus();
+        List<SatelliteSubscriberInfo> inputList = getExpectedSatelliteSubscriberInfoList();
+        verifyProvisionSatellite(inputList);
+        verifyDeprovisionSatellite(inputList);
+    }
+
+    private void verifyDeprovisionSatellite(List<SatelliteSubscriberInfo> inputList) {
+        doAnswer(invocation -> {
+            Message message = (Message) invocation.getArguments()[1];
+            AsyncResult.forMessage(message, null, new SatelliteException(SATELLITE_RESULT_SUCCESS));
+            message.sendToTarget();
+            return null;
+        }).when(mMockSatelliteModemInterface).updateSatelliteSubscription(anyString(), any());
+
+        mSatelliteControllerUT.deprovisionSatellite(inputList, mDeprovisionSatelliteReceiver);
+        processAllMessages();
+        assertEquals(SATELLITE_RESULT_SUCCESS, mDeprovisionSateResultCode);
+        assertTrue(mDeprovisionDone);
     }
 
     private void setSatelliteSubscriberTesting() throws Exception {
@@ -4536,6 +4718,55 @@
         verify(mContext, times(1)).sendBroadcast(any(Intent.class));
     }
 
+    @Test
+    public void testProvisionStatusPerSubscriberIdGetFromDb() throws Exception {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+
+        setSatelliteSubscriberTesting();
+        // Check if the cache is not updated when the value read from the database is false.
+        verifyProvisionStatusPerSubscriberIdGetFromDb(false);
+
+        // Check if the cache is updated when the value read from the database is true.
+        verifyProvisionStatusPerSubscriberIdGetFromDb(true);
+    }
+
+    @Test
+    public void testProvisionStatusPerSubscriberIdStoreToDb() throws Exception {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+
+        setSatelliteSubscriberTesting();
+        // Check if the cache is not updated when the value read from the database is false.
+        verifyProvisionStatusPerSubscriberIdGetFromDb(false);
+
+        List<SatelliteSubscriberInfo> inputList = getExpectedSatelliteSubscriberInfoList();
+        verifyProvisionSatellite(inputList);
+        verify(mMockSubscriptionManagerService).setIsSatelliteProvisionedForNonIpDatagram(
+                eq(SUB_ID), eq(true));
+    }
+
+    private void verifyProvisionStatusPerSubscriberIdGetFromDb(boolean provision) {
+        doReturn(provision).when(
+                mMockSubscriptionManagerService).isSatelliteProvisionedForNonIpDatagram(anyInt());
+        mCarrierConfigBundle.putString(KEY_SATELLITE_NIDD_APN_NAME_STRING, mNiddApn);
+        mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ESOS_SUPPORTED_BOOL, true);
+        for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair
+                : mCarrierConfigChangedListenerList) {
+            pair.first.execute(() -> pair.second.onCarrierConfigChanged(
+                    /*slotIndex*/ 0, /*subId*/ SUB_ID, /*carrierId*/ 0, /*specificCarrierId*/ 0)
+            );
+        }
+        moveTimeForward(TimeUnit.MINUTES.toMillis(1));
+        processAllMessages();
+        mSatelliteControllerUT.requestSatelliteSubscriberProvisionStatus(
+                mRequestSatelliteSubscriberProvisionStatusReceiver);
+        moveTimeForward(TimeUnit.MINUTES.toMillis(1));
+        processAllMessages();
+        assertEquals(SATELLITE_RESULT_SUCCESS,
+                mRequestSatelliteSubscriberProvisionStatusResultCode);
+        assertEquals(provision,
+                mRequestSatelliteSubscriberProvisionStatusResultList.get(0).getProvisionStatus());
+    }
+
     private void setComponentName() {
         when(mSatelliteControllerUT.getStringFromOverlayConfigTest(
                 R.string.config_satellite_gateway_service_package))
@@ -5055,6 +5286,13 @@
         msg.sendToTarget();
     }
 
+    private void sendSatelliteRegistrationFailureEvent(int errorCode, Throwable exception) {
+        Message msg = mSatelliteControllerUT.obtainMessage(
+                54 /* EVENT_SATELLITE_REGISTRATION_FAILURE */);
+        msg.obj = new AsyncResult(null, errorCode, exception);
+        msg.sendToTarget();
+    }
+
     private void setRadioPower(boolean on) {
         mSimulatedCommands.setRadioPower(on, false, false, null);
     }
@@ -5328,6 +5566,12 @@
             return false;
         }
 
+        @Override
+        protected List<DeviceState> getSupportedDeviceStates() {
+            return List.of(new DeviceState(new DeviceState.Configuration.Builder(0 /* identifier */,
+                    "DEFAULT" /* name */).build()));
+        }
+
         void setSatelliteProvisioned(@Nullable Boolean isProvisioned) {
             synchronized (mSatelliteViaOemProvisionLock) {
                 mIsSatelliteViaOemProvisioned = isProvisioned;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
index 4cb8a03..f15ffbd 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
@@ -29,6 +29,9 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -39,6 +42,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
+import android.hardware.devicestate.DeviceState;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Looper;
@@ -67,6 +71,7 @@
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.metrics.SatelliteStats;
 
 import org.junit.After;
 import org.junit.Before;
@@ -118,6 +123,8 @@
     private Uri mTestConnectionAddress = Uri.parse("tel:1234");
     private TestSOSMessageRecommender mTestSOSMessageRecommender;
     private ServiceState mServiceState2;
+    @Mock
+    private SatelliteStats mMockSatelliteStats;
 
     @Before
     public void setUp() throws Exception {
@@ -153,6 +160,10 @@
         when(mServiceState2.getState()).thenReturn(STATE_OUT_OF_SERVICE);
         when(mPhone.isImsRegistered()).thenReturn(false);
         when(mPhone2.isImsRegistered()).thenReturn(false);
+        replaceInstance(SatelliteStats.class, "sInstance", null,
+                mMockSatelliteStats);
+        doNothing().when(mMockSatelliteStats).onSatelliteSosMessageRecommender(
+                any(SatelliteStats.SatelliteSosMessageRecommenderParams.class));
     }
 
     @After
@@ -165,6 +176,8 @@
         testTimeoutBeforeEmergencyCallEnd(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911,
                 DEFAULT_SATELLITE_MESSAGING_PACKAGE, DEFAULT_SATELLITE_MESSAGING_CLASS,
                 DEFAULT_T911_HANDOVER_INTENT_ACTION);
+        verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+        assertTrue(mTestSOSMessageRecommender.isDialerNotified());
     }
 
     @Test
@@ -177,6 +190,8 @@
         testTimeoutBeforeEmergencyCallEnd(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS,
                 "android.com.vendor.message", "android.com.vendor.message.SmsApp",
                 DEFAULT_HANDOVER_INTENT_ACTION);
+        verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+        assertTrue(mTestSOSMessageRecommender.isDialerNotified());
     }
 
     @Test
@@ -188,6 +203,8 @@
         mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false);
         testTimeoutBeforeEmergencyCallEnd(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS, "", "",
                 DEFAULT_HANDOVER_INTENT_ACTION);
+        verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+        assertTrue(mTestSOSMessageRecommender.isDialerNotified());
     }
 
     @Test
@@ -197,6 +214,8 @@
         mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false);
         testTimeoutBeforeEmergencyCallEnd(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS, "", "",
                 DEFAULT_HANDOVER_INTENT_ACTION);
+        verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+        assertTrue(mTestSOSMessageRecommender.isDialerNotified());
     }
 
     @Test
@@ -204,16 +223,17 @@
         when(mServiceState.getState()).thenReturn(STATE_OUT_OF_SERVICE);
         when(mServiceState2.getState()).thenReturn(STATE_OUT_OF_SERVICE);
         mTestSOSMessageRecommender.isSatelliteAllowedCallback = null;
-        mTestSOSMessageRecommender.onEmergencyCallStarted(null);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(null, false);
         processAllMessages();
         assertFalse(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
+        verify(mMockSatelliteStats, never()).onSatelliteSosMessageRecommender(any());
     }
 
     private void testTimeoutBeforeEmergencyCallEnd(int expectedHandoverType,
             String expectedPackageName, String expectedClassName, String expectedAction) {
         mTestSOSMessageRecommender.isSatelliteAllowedCallback = null;
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
         processAllMessages();
         assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
         assertRegisterForStateChangedEventsTriggered(mPhone, 1,  1);
@@ -239,6 +259,8 @@
         }
         assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1);
         assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 1);
+        verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+        assertTrue(mTestSOSMessageRecommender.isDialerNotified());
     }
 
     @Test
@@ -246,7 +268,7 @@
         mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false);
         mTestSatelliteController.setIsSatelliteViaOemProvisioned(false);
         mTestSOSMessageRecommender.isSatelliteAllowedCallback = null;
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
         processAllMessages();
         assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
         assertRegisterForStateChangedEventsTriggered(mPhone, 1, 1);
@@ -266,6 +288,8 @@
         assertFalse(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE));
         assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1);
         assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 1);
+        verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+        assertFalse(mTestSOSMessageRecommender.isDialerNotified());
     }
 
     @Test
@@ -273,7 +297,7 @@
         mTestSOSMessageRecommender.isSatelliteAllowedCallback = null;
         mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false);
         mTestSatelliteController.isOemEnabledSatelliteSupported = false;
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
         processAllMessages();
         assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
         assertRegisterForStateChangedEventsTriggered(mPhone, 1, 1);
@@ -302,17 +326,21 @@
     @Test
     public void testStopTrackingCallBeforeTimeout_ConnectionActive() {
         testStopTrackingCallBeforeTimeout(Connection.STATE_ACTIVE);
+        verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+        assertFalse(mTestSOSMessageRecommender.isDialerNotified());
     }
 
     @Test
     public void testStopTrackingCallBeforeTimeout_ConnectionDisconnected() {
         testStopTrackingCallBeforeTimeout(Connection.STATE_DISCONNECTED);
+        verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+        assertFalse(mTestSOSMessageRecommender.isDialerNotified());
     }
 
     @Test
     public void testNetworkStateChangedBeforeTimeout() {
         mTestSOSMessageRecommender.isSatelliteAllowedCallback = null;
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
         processAllMessages();
         assertTrue(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
@@ -337,11 +365,13 @@
         assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1);
         assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 1);
         assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
+        verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+        assertTrue(mTestSOSMessageRecommender.isDialerNotified());
     }
 
     @Test
     public void testSatelliteProvisionStateChangedBeforeTimeout() {
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
         processAllMessages();
 
         assertTrue(mTestSOSMessageRecommender.isTimerStarted());
@@ -357,8 +387,11 @@
         assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
         assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1);
         assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 1);
+        verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+        assertFalse(mTestSOSMessageRecommender.isDialerNotified());
+        reset(mMockSatelliteStats);
 
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
         processAllMessages();
         assertTrue(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
@@ -387,12 +420,14 @@
         assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
         assertUnregisterForStateChangedEventsTriggered(mPhone, 2, 2);
         assertUnregisterForStateChangedEventsTriggered(mPhone2, 2, 2);
+        verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+        assertTrue(mTestSOSMessageRecommender.isDialerNotified());
     }
 
     @Test
     public void testEmergencyCallRedialBeforeTimeout() {
         mTestSOSMessageRecommender.isSatelliteAllowedCallback = null;
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
         processAllMessages();
         assertTrue(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
@@ -400,7 +435,7 @@
         assertRegisterForStateChangedEventsTriggered(mPhone2, 1, 1);
         assertNull(mTestSOSMessageRecommender.isSatelliteAllowedCallback);
 
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
         processAllMessages();
         assertNull(mTestSOSMessageRecommender.isSatelliteAllowedCallback);
         assertTrue(mTestSOSMessageRecommender.isTimerStarted());
@@ -426,35 +461,45 @@
         assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 1);
         assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
         assertFalse(mTestSOSMessageRecommender.isTimerStarted());
+        verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+        assertTrue(mTestSOSMessageRecommender.isDialerNotified());
     }
 
     @Test
     public void testCellularServiceStateChangedBeforeTimeout_InServiceToOutOfService() {
         testCellularServiceStateChangedBeforeTimeout(
                 ServiceState.STATE_IN_SERVICE, STATE_OUT_OF_SERVICE);
+        verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+        assertTrue(mTestSOSMessageRecommender.isDialerNotified());
     }
 
     @Test
     public void testCellularServiceStateChangedBeforeTimeout_InServiceToPowerOff() {
         testCellularServiceStateChangedBeforeTimeout(
                 ServiceState.STATE_IN_SERVICE, ServiceState.STATE_POWER_OFF);
+        verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+        assertTrue(mTestSOSMessageRecommender.isDialerNotified());
     }
 
     @Test
     public void testCellularServiceStateChangedBeforeTimeout_EmergencyOnlyToOutOfService() {
         testCellularServiceStateChangedBeforeTimeout(
                 ServiceState.STATE_EMERGENCY_ONLY, STATE_OUT_OF_SERVICE);
+        verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+        assertTrue(mTestSOSMessageRecommender.isDialerNotified());
     }
 
     @Test
     public void testCellularServiceStateChangedBeforeTimeout_EmergencyOnlyToPowerOff() {
         testCellularServiceStateChangedBeforeTimeout(
                 ServiceState.STATE_EMERGENCY_ONLY, ServiceState.STATE_POWER_OFF);
+        verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+        assertTrue(mTestSOSMessageRecommender.isDialerNotified());
     }
 
     @Test
     public void testOnEmergencyCallConnectionStateChangedWithWrongCallId() {
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
         processAllMessages();
 
         assertTrue(mTestSOSMessageRecommender.isTimerStarted());
@@ -471,12 +516,15 @@
         assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
         assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1);
         assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 1);
+        verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+        assertFalse(mTestSOSMessageRecommender.isDialerNotified());
     }
 
     @Test
     public void testSatelliteNotAllowedInCurrentLocation() {
         mTestSOSMessageRecommender.isSatelliteAllowedCallback = null;
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
         processAllMessages();
         assertNull(mTestSOSMessageRecommender.isSatelliteAllowedCallback);
         assertTrue(mTestSOSMessageRecommender.isTimerStarted());
@@ -498,21 +546,25 @@
         assertRegisterForStateChangedEventsTriggered(mPhone2, 1, 1);
         assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1);
         assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 1);
+        verify(mMockSatelliteStats, times(1)).onSatelliteSosMessageRecommender(any());
+        assertFalse(mTestSOSMessageRecommender.isDialerNotified());
     }
 
     @Test
     public void testOnEmergencyCallStarted() {
-        SatelliteController satelliteController = new SatelliteController(
-                mContext, Looper.myLooper(), mFeatureFlags);
+        SatelliteController satelliteController = new MinimalSatelliteControllerWrapper(mContext,
+                Looper.myLooper(), mFeatureFlags);
         TestSOSMessageRecommender testSOSMessageRecommender = new TestSOSMessageRecommender(
                 mContext,
                 Looper.myLooper(),
                 satelliteController, mTestImsManager);
-        testSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(true);
+        testSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
         processAllMessages();
 
         assertFalse(testSOSMessageRecommender.isTimerStarted());
         assertEquals(0, testSOSMessageRecommender.getCountOfTimerStarted());
+        verify(mMockSatelliteStats, never()).onSatelliteSosMessageRecommender(any());
     }
 
     @Test
@@ -538,25 +590,41 @@
         // Both OEM and carrier don't support satellite
         mTestSatelliteController.isSatelliteEmergencyMessagingSupportedViaCarrier = false;
         mTestSatelliteController.isOemEnabledSatelliteSupported = false;
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
         processAllMessages();
         assertEquals(0, mTestSOSMessageRecommender.getTimeOutMillis());
 
         // Only OEM support satellite
         mTestSatelliteController.isOemEnabledSatelliteSupported = true;
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
         processAllMessages();
         assertEquals(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS,
                 mTestSOSMessageRecommender.getTimeOutMillis());
+        verify(mMockSatelliteStats, never()).onSatelliteSosMessageRecommender(any());
 
-        // Both OEM and carrier support satellite. Thus, carrier's timeout duration will be used
+        // Both OEM and carrier support satellite, but device is not connected to carrier satellite
+        // within hysteresis time. Thus, OEM timer will be used.
         long carrierTimeoutMillis = 1000;
         mTestSatelliteController.isSatelliteEmergencyMessagingSupportedViaCarrier = true;
+        mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false);
         mTestSatelliteController.carrierEmergencyCallWaitForConnectionTimeoutMillis =
                 carrierTimeoutMillis;
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
+        processAllMessages();
+        assertEquals(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS,
+                mTestSOSMessageRecommender.getTimeOutMillis());
+        verify(mMockSatelliteStats, never()).onSatelliteSosMessageRecommender(any());
+
+        // Both OEM and carrier support satellite, and device is connected to carrier satellite
+        // within hysteresis time. Thus, carrier timer will be used.
+        mTestSatelliteController.isSatelliteEmergencyMessagingSupportedViaCarrier = true;
+        mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(true);
+        mTestSatelliteController.carrierEmergencyCallWaitForConnectionTimeoutMillis =
+                carrierTimeoutMillis;
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
         processAllMessages();
         assertEquals(carrierTimeoutMillis, mTestSOSMessageRecommender.getTimeOutMillis());
+        verify(mMockSatelliteStats, never()).onSatelliteSosMessageRecommender(any());
     }
 
     @Test
@@ -565,9 +633,10 @@
 
         mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(true);
         mTestSatelliteController.mIsSatelliteViaOemProvisioned = true;
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
         assertEquals(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911,
                 mTestSOSMessageRecommender.getEmergencyCallToSatelliteHandoverType());
+        verify(mMockSatelliteStats, never()).onSatelliteSosMessageRecommender(any());
 
         mSetFlagsRule.disableFlags(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN);
     }
@@ -578,9 +647,10 @@
 
         mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(true);
         mTestSatelliteController.mIsSatelliteViaOemProvisioned = false;
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
         assertEquals(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911,
                 mTestSOSMessageRecommender.getEmergencyCallToSatelliteHandoverType());
+        verify(mMockSatelliteStats, never()).onSatelliteSosMessageRecommender(any());
 
         mSetFlagsRule.disableFlags(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN);
     }
@@ -591,22 +661,23 @@
 
         mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false);
         mTestSatelliteController.mIsSatelliteViaOemProvisioned = true;
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
         assertEquals(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS,
                 mTestSOSMessageRecommender.getEmergencyCallToSatelliteHandoverType());
 
         mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false);
         mTestSatelliteController.mIsSatelliteViaOemProvisioned = false;
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
         assertEquals(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS,
                 mTestSOSMessageRecommender.getEmergencyCallToSatelliteHandoverType());
+        verify(mMockSatelliteStats, never()).onSatelliteSosMessageRecommender(any());
 
         mSetFlagsRule.disableFlags(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN);
     }
 
     private void testStopTrackingCallBeforeTimeout(
             @Connection.ConnectionState int connectionState) {
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
         processAllMessages();
 
         assertTrue(mTestSOSMessageRecommender.isTimerStarted());
@@ -628,7 +699,7 @@
             @ServiceState.RegState int availableServiceState,
             @ServiceState.RegState int unavailableServiceState) {
         mTestSOSMessageRecommender.isSatelliteAllowedCallback = null;
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, false);
         processAllMessages();
         assertNull(mTestSOSMessageRecommender.isSatelliteAllowedCallback);
         assertTrue(mTestSOSMessageRecommender.isTimerStarted());
@@ -777,6 +848,12 @@
             return carrierEmergencyCallWaitForConnectionTimeoutMillis;
         }
 
+        @Override
+        protected List<DeviceState> getSupportedDeviceStates() {
+            return List.of(new DeviceState(new DeviceState.Configuration.Builder(0 /* identifier */,
+                    "DEFAULT" /* name */).build()));
+        }
+
         public void setSatelliteConnectedViaCarrierWithinHysteresisTime(
                 boolean connectedViaCarrier) {
             mIsSatelliteConnectedViaCarrierWithinHysteresisTime = connectedViaCarrier;
@@ -810,6 +887,26 @@
         }
     }
 
+    /**
+     * Now that {@link SatelliteController} uses
+     * {@link android.hardware.devicestate.DeviceStateManager} to determine if a device is a
+     * foldable or not, we have to provide a minimal wrapper for {@link SatelliteController} for
+     * tests that want to use a non-fake {@link SatelliteController}.
+     */
+    private static class MinimalSatelliteControllerWrapper extends SatelliteController {
+
+        protected MinimalSatelliteControllerWrapper(
+                Context context, Looper looper, FeatureFlags featureFlags) {
+            super(context, looper, featureFlags);
+        }
+
+        @Override
+        protected List<DeviceState> getSupportedDeviceStates() {
+            return List.of(new DeviceState(new DeviceState.Configuration.Builder(0 /* identifier */,
+                    "DEFAULT" /* name */).build()));
+        }
+    }
+
     private static class TestImsManager extends ImsManager {
 
         private final List<RegistrationManager.RegistrationCallback> mCallbacks;
@@ -883,6 +980,7 @@
                 isSatelliteAllowedCallback = null;
         private ComponentName mSmsAppComponent = new ComponentName(
                 DEFAULT_SATELLITE_MESSAGING_PACKAGE, DEFAULT_SATELLITE_MESSAGING_CLASS);
+        private boolean mIsDialerNotified;
 
         /**
          * Create an instance of SatelliteSOSMessageRecommender.
@@ -911,6 +1009,12 @@
             isSatelliteAllowedCallback = callback;
         }
 
+        @Override
+        protected void reportESosRecommenderDecision(boolean isDialerNotified) {
+            super.reportESosRecommenderDecision(isDialerNotified);
+            mIsDialerNotified = isDialerNotified;
+        }
+
         public boolean isTimerStarted() {
             return hasMessages(EVENT_TIME_OUT);
         }
@@ -926,6 +1030,10 @@
         public long getTimeOutMillis() {
             return mTimeoutMillis;
         }
+
+        public boolean isDialerNotified() {
+            return mIsDialerNotified;
+        }
     }
 
     private static class TestConnection extends Connection {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
index 95924dd..5ffb6ba 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
@@ -19,24 +19,31 @@
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT;
 import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT;
+import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_SMS;
+import static android.telephony.satellite.SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_SUCCESS;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT;
+import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -49,6 +56,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.PersistableBundle;
+import android.telephony.ServiceState;
 import android.telephony.satellite.ISatelliteModemStateCallback;
 import android.telephony.satellite.SatelliteManager;
 import android.testing.AndroidTestingRunner;
@@ -57,6 +65,7 @@
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.flags.FeatureFlags;
+import com.android.internal.telephony.satellite.metrics.SessionMetricsStats;
 
 import org.junit.After;
 import org.junit.Before;
@@ -104,6 +113,8 @@
     @Mock private DatagramReceiver mMockDatagramReceiver;
     @Mock private DatagramDispatcher mMockDatagramDispatcher;
     @Mock private DatagramController mMockDatagramController;
+    @Mock private ServiceState mMockServiceState;
+    @Mock private SessionMetricsStats mMockSessionMetricsStats;
 
     @Captor ArgumentCaptor<Handler> mHandlerCaptor;
     @Captor ArgumentCaptor<Integer> mMsgCaptor;
@@ -121,6 +132,8 @@
                 mMockSatelliteController);
         replaceInstance(DatagramController.class, "sInstance", null,
                 mMockDatagramController);
+        replaceInstance(SessionMetricsStats.class, "sInstance", null,
+                mMockSessionMetricsStats);
 
         Resources resources = mContext.getResources();
         when(resources.getInteger(anyInt())).thenReturn(TEST_SATELLITE_TIMEOUT_MILLIS);
@@ -131,6 +144,10 @@
                 anyInt())).thenReturn(false);
         when(mMockSatelliteController.isSatelliteEsosSupported(anyInt())).thenReturn(false);
         when(mMockSatelliteController.getSatellitePhone()).thenReturn(mPhone);
+        when(mMockSessionMetricsStats.addCountOfAutoExitDueToScreenOff()).thenReturn(
+                mMockSessionMetricsStats);
+        when(mMockSessionMetricsStats.addCountOfAutoExitDueToTnNetwork()).thenReturn(
+                mMockSessionMetricsStats);
         mSatelliteModemInterface = new TestSatelliteModemInterface(
                 mContext, mMockSatelliteController, Looper.myLooper(), mFeatureFlags);
         mTestSatelliteSessionController = new TestSatelliteSessionController(mContext,
@@ -202,6 +219,7 @@
         bundle.putInt(KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT,
                 SCREEN_OFF_INACTIVITY_TIMEOUT_SEC);
         when(mMockSatelliteController.getPersistableBundle(anyInt())).thenReturn(bundle);
+        when(mMockSatelliteController.isInCarrierRoamingNbIotNtn()).thenReturn(true);
 
         // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
         assertNotNull(mTestSatelliteSessionController);
@@ -216,6 +234,7 @@
         // Notify Screen off
         sendScreenStateChanged(mHandlerCaptor.getValue(), mMsgCaptor.getValue(), false);
         processAllMessages();
+        clearInvocations(mMockSatelliteController);
 
         // Verify that the screen off inactivity timer is started.
         assertTrue(mTestSatelliteSessionController.isScreenOffInActivityTimerStarted());
@@ -240,6 +259,7 @@
         bundle.putInt(KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT,
                 SCREEN_OFF_INACTIVITY_TIMEOUT_SEC);
         when(mMockSatelliteController.getPersistableBundle(anyInt())).thenReturn(bundle);
+        when(mMockSatelliteController.isInCarrierRoamingNbIotNtn()).thenReturn(true);
 
         // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
         assertNotNull(mTestSatelliteSessionController);
@@ -280,9 +300,9 @@
     @Test
     public void testP2pSmsInactivityTimer() {
         when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
-        doNothing().when(mDeviceStateMonitor).registerForScreenStateChanged(
-                eq(mTestSatelliteSessionController.getHandler()), anyInt(), any());
         when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true);
+        when(mMockDatagramController.getDatagramType()).thenReturn(
+                SatelliteManager.DATAGRAM_TYPE_SMS);
 
         when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(false);
         when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported(
@@ -295,23 +315,24 @@
 
         // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
         assertNotNull(mTestSatelliteSessionController);
+        mTestSatelliteSessionController.setSatelliteEnabledForNtnOnlySubscription(false);
         assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
         setupDatagramTransferringState(true);
 
         moveToNotConnectedState();
 
         // Verify that the P2P SMS inactivity timer is started.
-        assertTrue(mTestSatelliteSessionController.isCarrierRoamingNbIotInActivityTimerStarted());
+        assertTrue(mTestSatelliteSessionController.isP2pSmsInActivityTimerStarted());
 
         mTestSatelliteSessionController.setDeviceAlignedWithSatellite(true);
 
         // Verify that the P2P SMS inactivity timer is stopped.
-        assertFalse(mTestSatelliteSessionController.isCarrierRoamingNbIotInActivityTimerStarted());
+        assertFalse(mTestSatelliteSessionController.isP2pSmsInActivityTimerStarted());
 
         mTestSatelliteSessionController.setDeviceAlignedWithSatellite(false);
 
         // Verify that the P2P SMS inactivity timer is started.
-        assertTrue(mTestSatelliteSessionController.isCarrierRoamingNbIotInActivityTimerStarted());
+        assertTrue(mTestSatelliteSessionController.isP2pSmsInActivityTimerStarted());
 
         // Time shift to cause timeout
         moveTimeForward(P2P_SMS_INACTIVITY_TIMEOUT_SEC * 1000);
@@ -325,10 +346,9 @@
     @Test
     public void testEsosInactivityTimer() {
         when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
-        doNothing().when(mDeviceStateMonitor).registerForScreenStateChanged(
-                eq(mTestSatelliteSessionController.getHandler()), anyInt(), any());
         when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true);
-
+        when(mMockDatagramController.getDatagramType()).thenReturn(
+                SatelliteManager.DATAGRAM_TYPE_SOS_MESSAGE);
         when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(true);
         when(mMockSatelliteController.isSatelliteEsosSupported(anyInt())).thenReturn(true);
         PersistableBundle bundle = new PersistableBundle();
@@ -338,23 +358,24 @@
 
         // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
         assertNotNull(mTestSatelliteSessionController);
+        mTestSatelliteSessionController.setSatelliteEnabledForNtnOnlySubscription(false);
         assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
         setupDatagramTransferringState(true);
 
         moveToNotConnectedState();
 
         // Verify that the ESOS inactivity timer is started.
-        assertTrue(mTestSatelliteSessionController.isCarrierRoamingNbIotInActivityTimerStarted());
+        assertTrue(mTestSatelliteSessionController.isEsosInActivityTimerStarted());
 
         mTestSatelliteSessionController.setDeviceAlignedWithSatellite(true);
 
         // Verify that the ESOS inactivity timer is stopped.
-        assertFalse(mTestSatelliteSessionController.isCarrierRoamingNbIotInActivityTimerStarted());
+        assertFalse(mTestSatelliteSessionController.isEsosInActivityTimerStarted());
 
         mTestSatelliteSessionController.setDeviceAlignedWithSatellite(false);
 
         // Verify that the ESOS inactivity timer is started.
-        assertTrue(mTestSatelliteSessionController.isCarrierRoamingNbIotInActivityTimerStarted());
+        assertTrue(mTestSatelliteSessionController.isEsosInActivityTimerStarted());
 
         // Time shift to cause timeout
         moveTimeForward(ESOS_INACTIVITY_TIMEOUT_SEC * 1000);
@@ -366,6 +387,449 @@
     }
 
     @Test
+    public void testEsosP2pSmsInactivityTimerCase1() {
+        // Send eSOS and SMS
+        // After 10 minutes SatelliteSessionController moves to idle
+        // TN network reports IN_SERVICE
+        // Report the callback only and don't auto exit
+
+        long passedTime = 0;
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true);
+        // Support ESOS
+        when(mMockSatelliteController.isSatelliteEsosSupported(anyInt())).thenReturn(true);
+        when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(true);
+        // Support P2P_SMS
+        when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported(
+                anyInt())).thenReturn(true);
+
+        // Setup carrier config for timer values
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putInt(KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT,
+                ESOS_INACTIVITY_TIMEOUT_SEC);
+        bundle.putInt(KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT,
+                P2P_SMS_INACTIVITY_TIMEOUT_SEC);
+        when(mMockSatelliteController.getPersistableBundle(anyInt())).thenReturn(bundle);
+
+        // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
+        assertNotNull(mTestSatelliteSessionController);
+        mTestSatelliteSessionController.setSatelliteEnabledForNtnOnlySubscription(false);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+        // Set up Datagram SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE
+        setupDatagramTransferringState(true);
+
+        moveToNotConnectedState();
+
+        // Notify datagram controller is in WAITING_TO_CONNECT.
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT,
+                SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT);
+        processAllMessages();
+
+        // Verify that ESOS, P2P_SMS timer are not started.
+        verifyEsosP2pSmsInactivityTimer(false, false);
+
+        // Sent ESOS
+        sendMessage(DATAGRAM_TYPE_SOS_MESSAGE);
+
+        // Verify that ESOS, P2P_SMS timer are started.
+        verifyEsosP2pSmsInactivityTimer(true, true);
+
+        // Sent SMS
+        sendMessage(DATAGRAM_TYPE_SMS);
+
+        // Verify that ESOS, P2P_SMS timer are started.
+        verifyEsosP2pSmsInactivityTimer(true, true);
+
+        // Time shift to cause P2P_SMS timeout
+        passedTime = P2P_SMS_INACTIVITY_TIMEOUT_SEC * 1000;
+        moveTimeForward(P2P_SMS_INACTIVITY_TIMEOUT_SEC * 1000);
+        processAllMessages();
+
+        // Verify that keep ESOS timer, expired P2P_SMS timer.
+        // NOT_CONNECTED state, satellite disabling not called.
+        verifyEsosP2pSmsInactivityTimer(true, false);
+        assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+        verify(mMockSatelliteController, never()).requestSatelliteEnabled(
+                eq(false), eq(false), eq(true), any(IIntegerConsumer.Stub.class));
+
+        // Time shift to cause ESOS timeout
+        moveTimeForward(ESOS_INACTIVITY_TIMEOUT_SEC * 1000 - passedTime);
+        processAllMessages();
+
+        // Verify that expired ESOS and P2P_SMS timer
+        // reported IDLE state, not called satellite disabling.
+        verifyEsosP2pSmsInactivityTimer(false, false);
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteModemStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+        verify(mMockSatelliteController, never()).requestSatelliteEnabled(
+                eq(false), eq(false), eq(true), any(IIntegerConsumer.Stub.class));
+    }
+
+    @Test
+    public void testEsosP2pSmsInactivityTimerCase2() {
+        // Send eSOS and SMS
+        // Send SMS after 3 mins
+        // Send SMS after 1 mins
+        // After 10 minutes SatelliteSessionController moves to idle
+        // TN network reports IN_SERVICE
+        // Report the callback only and don't auto exit
+
+        long passedTime = 0;
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true);
+        // Support ESOS
+        when(mMockSatelliteController.isSatelliteEsosSupported(anyInt())).thenReturn(true);
+        when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(true);
+        // Support P2P_SMS
+        when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported(
+                anyInt())).thenReturn(true);
+
+        // Setup carrier config for timer values
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putInt(KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT,
+                ESOS_INACTIVITY_TIMEOUT_SEC);
+        bundle.putInt(KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT,
+                P2P_SMS_INACTIVITY_TIMEOUT_SEC);
+        when(mMockSatelliteController.getPersistableBundle(anyInt())).thenReturn(bundle);
+
+        // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
+        assertNotNull(mTestSatelliteSessionController);
+        mTestSatelliteSessionController.setSatelliteEnabledForNtnOnlySubscription(false);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+        // Set up Datagram SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE
+        setupDatagramTransferringState(true);
+
+        moveToNotConnectedState();
+
+        // Notify datagram controller is in WAITING_TO_CONNECT.
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT,
+                SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT);
+        processAllMessages();
+
+        // Verify that ESOS, P2P_SMS timer are not started.
+        verifyEsosP2pSmsInactivityTimer(false, false);
+
+        // Sent ESOS, SMS
+        sendMessage(DATAGRAM_TYPE_SOS_MESSAGE);
+        sendMessage(DATAGRAM_TYPE_SMS);
+        // Verify that ESOS, P2P_SMS timer are started.
+        verifyEsosP2pSmsInactivityTimer(true, true);
+
+        // Sent SMS again after 3 mins
+        passedTime = 3 * 60 * 1000;
+        moveTimeForward(3 * 60 * 1000);
+        processAllMessages();
+        sendMessage(DATAGRAM_TYPE_SMS);
+
+        // Sent SMS again after 1 mins
+        passedTime += 1 * 60 * 1000;
+        moveTimeForward(1 * 60 * 1000);
+        processAllMessages();
+        sendMessage(DATAGRAM_TYPE_SMS);
+
+        // Time shift to cause ESOS timeout
+        moveTimeForward(ESOS_INACTIVITY_TIMEOUT_SEC * 1000 - passedTime);
+        processAllMessages();
+
+        // Verify that expired ESOS and P2P_SMS timer
+        // reported IDLE state, not called satellite disabling.
+        verifyEsosP2pSmsInactivityTimer(false, false);
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteModemStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+        verify(mMockSatelliteController, never()).requestSatelliteEnabled(
+                eq(false), eq(false), eq(true), any(IIntegerConsumer.Stub.class));
+    }
+
+    @Test
+    public void testEsosP2pSmsInactivityTimerCase3() {
+        // Send eSOS and SMS
+        // Send eSOS after 5 mins
+        // After 15 minutes SatelliteSessionController moves to idle
+        // TN network reports IN_SERVICE
+        // Report the callback only and don't auto exit
+
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true);
+        // Support ESOS
+        when(mMockSatelliteController.isSatelliteEsosSupported(anyInt())).thenReturn(true);
+        when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(true);
+        // Support P2P_SMS
+        when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported(
+                anyInt())).thenReturn(true);
+
+        // Setup carrier config for timer values
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putInt(KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT,
+                ESOS_INACTIVITY_TIMEOUT_SEC);
+        bundle.putInt(KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT,
+                P2P_SMS_INACTIVITY_TIMEOUT_SEC);
+        when(mMockSatelliteController.getPersistableBundle(anyInt())).thenReturn(bundle);
+
+        // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
+        assertNotNull(mTestSatelliteSessionController);
+        mTestSatelliteSessionController.setSatelliteEnabledForNtnOnlySubscription(false);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+        // Set up Datagram SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE
+        setupDatagramTransferringState(true);
+
+        moveToNotConnectedState();
+
+        // Notify datagram controller is in WAITING_TO_CONNECT.
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT,
+                SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT);
+        processAllMessages();
+
+        // Verify that ESOS, P2P_SMS timer are not started.
+        verifyEsosP2pSmsInactivityTimer(false, false);
+
+        // Sent ESOS
+        sendMessage(DATAGRAM_TYPE_SOS_MESSAGE);
+        // Verify that ESOS, P2P_SMS timer are started.
+        verifyEsosP2pSmsInactivityTimer(true, true);
+
+        // Sent ESOS after 5 mins
+        moveTimeForward(3 * 60 * 1000);
+        processAllMessages();
+        sendMessage(DATAGRAM_TYPE_SOS_MESSAGE);
+
+        // Time shift to cause ESOS timeout
+        moveTimeForward(ESOS_INACTIVITY_TIMEOUT_SEC * 1000);
+        processAllMessages();
+
+        // Verify that expired ESOS and P2P_SMS timer
+        // reported IDLE state, not called satellite disabling.
+        verifyEsosP2pSmsInactivityTimer(false, false);
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteModemStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+        verify(mMockSatelliteController, never()).requestSatelliteEnabled(
+                eq(false), eq(false), eq(true), any(IIntegerConsumer.Stub.class));
+    }
+
+    @Test
+    public void testEsosP2pSmsInactivityTimerCase4() {
+        // Send SMS
+        // Send SMS after 2 mins
+        // After 3 minutes SatelliteSessionController moves to idle
+        // TN network reports IN_SERVICE
+        // Report the callback only and auto exit
+
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true);
+        // Support ESOS, Satellite is not in emergency mode
+        when(mMockSatelliteController.isSatelliteEsosSupported(anyInt())).thenReturn(true);
+        when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(false);
+        // Support P2P_SMS
+        when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported(
+                anyInt())).thenReturn(true);
+
+        // Setup carrier config for timer values
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putInt(KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT,
+                ESOS_INACTIVITY_TIMEOUT_SEC);
+        bundle.putInt(KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT,
+                P2P_SMS_INACTIVITY_TIMEOUT_SEC);
+        when(mMockSatelliteController.getPersistableBundle(anyInt())).thenReturn(bundle);
+
+        // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
+        assertNotNull(mTestSatelliteSessionController);
+        mTestSatelliteSessionController.setSatelliteEnabledForNtnOnlySubscription(false);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+        // Set up Datagram SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE
+        setupDatagramTransferringState(true);
+
+        moveToNotConnectedState();
+
+        // Notify datagram controller is in WAITING_TO_CONNECT.
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT,
+                SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT);
+        processAllMessages();
+
+        // Verify that ESOS, P2P_SMS timer are not started.
+        verifyEsosP2pSmsInactivityTimer(false, false);
+
+        // Sent SMS
+        sendMessage(DATAGRAM_TYPE_SMS);
+
+        // Verify that ESOS is not started, P2P_SMS timer is started.
+        verifyEsosP2pSmsInactivityTimer(false, true);
+
+        // Sent SMS again after 2 mins
+        moveTimeForward(2 * 60 * 1000);
+        processAllMessages();
+        sendMessage(DATAGRAM_TYPE_SMS);
+
+        // Verify that ESOS is not started, P2P_SMS timer is started.
+        verifyEsosP2pSmsInactivityTimer(false, true);
+
+        // Time shift to cause P2P_SMS timeout
+        moveTimeForward(P2P_SMS_INACTIVITY_TIMEOUT_SEC * 1000);
+        processAllMessages();
+
+        // Verify that expired P2P_SMS timer
+        // reported IDLE state, called satellite disabling.
+        verifyEsosP2pSmsInactivityTimer(false, false);
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteModemStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+        verify(mMockSatelliteController, times(1)).requestSatelliteEnabled(
+                eq(false), eq(false), eq(false), any(IIntegerConsumer.Stub.class));
+    }
+
+    @Test
+    public void testEsosP2pSmsInactivityTimerCase5() {
+        // Send SMS
+        // Send ESOS after 2 mins
+        // After 12 minutes SatelliteSessionController moves to idle
+        // TN network reports IN_SERVICE
+        // Report the callback only and don'tauto exit
+
+        long passedTime = 0;
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true);
+        // Support ESOS
+        when(mMockSatelliteController.isSatelliteEsosSupported(anyInt())).thenReturn(true);
+        when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(true);
+        // Support P2P_SMS
+        when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported(
+                anyInt())).thenReturn(true);
+
+        // Setup carrier config for timer values
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putInt(KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT,
+                ESOS_INACTIVITY_TIMEOUT_SEC);
+        bundle.putInt(KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT,
+                P2P_SMS_INACTIVITY_TIMEOUT_SEC);
+        when(mMockSatelliteController.getPersistableBundle(anyInt())).thenReturn(bundle);
+
+        // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
+        assertNotNull(mTestSatelliteSessionController);
+        mTestSatelliteSessionController.setSatelliteEnabledForNtnOnlySubscription(false);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+        // Set up Datagram SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE
+        setupDatagramTransferringState(true);
+
+        moveToNotConnectedState();
+
+        // Notify datagram controller is in WAITING_TO_CONNECT.
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT,
+                SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT);
+        processAllMessages();
+
+        // Verify that ESOS, P2P_SMS timer are not started.
+        verifyEsosP2pSmsInactivityTimer(false, false);
+
+        // Sent SMS
+        sendMessage(DATAGRAM_TYPE_SMS);
+
+        // Verify that ESOS is not started, P2P_SMS timer is started.
+        verifyEsosP2pSmsInactivityTimer(true, true);
+
+        // Sent ESOS again after 2 mins
+        passedTime = 2 * 60 * 1000;
+        moveTimeForward(2 * 60 * 1000);
+        processAllMessages();
+        sendMessage(DATAGRAM_TYPE_SOS_MESSAGE);
+
+        // Verify that ESOS is not started, P2P_SMS timer is started.
+        verifyEsosP2pSmsInactivityTimer(true, true);
+
+        // Time shift
+        moveTimeForward(ESOS_INACTIVITY_TIMEOUT_SEC * 1000 - passedTime);
+        processAllMessages();
+        verifyEsosP2pSmsInactivityTimer(true, false);
+
+        // Time shift
+        moveTimeForward(passedTime);
+        processAllMessages();
+
+        // Verify that expired P2P_SMS timer
+        // reported IDLE state, called satellite disabling.
+        verifyEsosP2pSmsInactivityTimer(false, false);
+        assertSuccessfulModemStateChangedCallback(
+                mTestSatelliteModemStateCallback, SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+        verify(mMockSatelliteController, never()).requestSatelliteEnabled(
+                eq(false), eq(false), eq(true), any(IIntegerConsumer.Stub.class));
+    }
+
+    @Test
+    public void testDisableSatelliteWhenCellularModemEnabledInIdleMode() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        doNothing().when(mDeviceStateMonitor).registerForScreenStateChanged(
+                eq(mTestSatelliteSessionController.getHandler()), anyInt(), any());
+        when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(false);
+        when(mPhone.getServiceState()).thenReturn(mMockServiceState);
+        setUpResponseForRequestSatelliteEnabled(SATELLITE_RESULT_SUCCESS);
+
+        // Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
+        assertNotNull(mTestSatelliteSessionController);
+        mTestSatelliteSessionController.setSatelliteEnabledForNtnOnlySubscription(false);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+
+        // Conditions for operation
+        boolean isEmergency = true;
+        // Cellular network is not IN_SERVICE and emergency only.
+        // Satellite request is emergency and emergency communication was established.
+        // Disabling satellite was not allowed
+        when(mMockServiceState.getVoiceRegState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+        when(mMockServiceState.getDataRegState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+        when(mMockServiceState.isEmergencyOnly()).thenReturn(false);
+        when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(isEmergency);
+        when(mMockDatagramController.isEmergencyCommunicationEstablished()).thenReturn(true);
+        when(mMockSatelliteController.turnOffSatelliteSessionForEmergencyCall(
+                anyInt())).thenReturn(false);
+
+        moveToIdleState();
+
+        // Cellular network is not in STATE_IN_SERVICE or emergency only.
+        // Should not disable satellite
+        verify(mMockSatelliteController, never()).requestSatelliteEnabled(
+                eq(false), eq(false), eq(isEmergency), any(IIntegerConsumer.Stub.class));
+        verify(mMockSessionMetricsStats, never()).addCountOfAutoExitDueToTnNetwork();
+
+        // Notify cellular service is in STATE_IN_SERVICE.
+        ServiceState serviceState = new ServiceState();
+        serviceState.setVoiceRegState(ServiceState.STATE_IN_SERVICE);
+        serviceState.setDataRegState(ServiceState.STATE_OUT_OF_SERVICE);
+        serviceState.setEmergencyOnly(false);
+        mTestSatelliteSessionController.onCellularServiceStateChanged(serviceState);
+        processAllMessages();
+
+        // Satellite is in emergency mode and emergency communication was established.
+        // Should not disable satellite
+        verify(mMockSatelliteController, never()).requestSatelliteEnabled(
+                eq(false), eq(false), eq(isEmergency), any(IIntegerConsumer.Stub.class));
+        verify(mMockSessionMetricsStats, never()).addCountOfAutoExitDueToTnNetwork();
+
+        // Satellite is in emergency mode but emergency communication was not established.
+        // Disabling satellite was not allowed
+        when(mMockDatagramController.isEmergencyCommunicationEstablished()).thenReturn(false);
+        when(mMockSatelliteController.turnOffSatelliteSessionForEmergencyCall(
+                anyInt())).thenReturn(false);
+        mTestSatelliteSessionController.onCellularServiceStateChanged(serviceState);
+        processAllMessages();
+
+        // Should not disable satellite
+        verify(mMockSatelliteController, never()).requestSatelliteEnabled(
+                eq(false), eq(false), eq(isEmergency), any(IIntegerConsumer.Stub.class));
+        verify(mMockSessionMetricsStats, never()).addCountOfAutoExitDueToTnNetwork();
+        // Satellite is in emergency mode but emergency communication was not established.
+        // Disabling satellite was allowed
+        when(mMockSatelliteController.turnOffSatelliteSessionForEmergencyCall(
+                anyInt())).thenReturn(true);
+        mTestSatelliteSessionController.onCellularServiceStateChanged(serviceState);
+        processAllMessages();
+
+        // Should disable satellite
+        verify(mMockSatelliteController).requestSatelliteEnabled(
+                eq(false), eq(false), eq(isEmergency), any(IIntegerConsumer.Stub.class));
+        verify(mMockSessionMetricsStats, times(1)).addCountOfAutoExitDueToTnNetwork();
+    }
+
+    @Test
     public void testStateTransition() {
         /**
          * Since satellite is supported, SatelliteSessionController should move to POWER_OFF state.
@@ -1230,6 +1694,29 @@
         assertEmergencyModeChangedCallbackNotCalled(mTestSatelliteModemStateCallback);
     }
 
+
+    private void verifyEsosP2pSmsInactivityTimer(boolean esosTimer, boolean p2pSmsTimer) {
+        assertEquals(mTestSatelliteSessionController.isEsosInActivityTimerStarted(), esosTimer);
+        assertEquals(mTestSatelliteSessionController.isP2pSmsInActivityTimerStarted(),
+                p2pSmsTimer);
+    }
+
+    private void sendMessage(@SatelliteManager.DatagramType int datagramType) {
+        when(mMockDatagramController.getDatagramType()).thenReturn(datagramType);
+
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
+                SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE);
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS,
+                SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE);
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+                SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE);
+
+        processAllMessages();
+    }
+
     private void setupDatagramTransferringState(boolean isTransferring) {
         when(mMockDatagramController.isSendingInIdleState()).thenReturn(isTransferring);
         when(mMockDatagramController.isPollingInIdleState()).thenReturn(isTransferring);
@@ -1419,6 +1906,8 @@
     }
 
     private static class TestSatelliteSessionController extends SatelliteSessionController {
+        boolean mSatelliteEnabledForNtnOnlySubscription = true;
+
         TestSatelliteSessionController(Context context, Looper looper, FeatureFlags featureFlags,
                 boolean isSatelliteSupported,
                 SatelliteModemInterface satelliteModemInterface) {
@@ -1445,12 +1934,12 @@
             return hasMessages(EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT);
         }
 
-        boolean isCarrierRoamingNbIotInActivityTimerStarted() {
-            return hasMessages(EVENT_CARRIER_ROAMING_NB_IOT_INACTIVITY_TIMER_TIMED_OUT);
+        protected boolean isSatelliteEnabledForNtnOnlySubscription() {
+            return mSatelliteEnabledForNtnOnlySubscription;
         }
 
-        protected boolean isSatelliteEnabledForNtnOnlySubscription() {
-            return true;
+        void setSatelliteEnabledForNtnOnlySubscription(boolean enabled) {
+            mSatelliteEnabledForNtnOnlySubscription = false;
         }
     }
 
@@ -1488,6 +1977,11 @@
             }
         }
 
+        @Override
+        public void onRegistrationFailure(int causeCode) {
+            logd("onRegistrationFailure: causeCode=" + causeCode);
+        }
+
         public boolean waitUntilResultForModemStateChanged() {
             try {
                 if (!mSemaphoreForModemStateChanged.tryAcquire(EVENT_PROCESSING_TIME_MILLIS,
@@ -1582,4 +2076,14 @@
         msg.obj = new AsyncResult(null, screenOn, null);
         h.sendMessage(msg);
     }
+
+    private void setUpResponseForRequestSatelliteEnabled(
+            @SatelliteManager.SatelliteResult int expectedResult) {
+        doAnswer(invocation -> {
+            IIntegerConsumer integerConsumer = invocation.getArgument(3);
+            integerConsumer.accept(expectedResult);
+            return null;
+        }).when(mMockSatelliteController).requestSatelliteEnabled(anyBoolean(), anyBoolean(),
+                anyBoolean(), any(IIntegerConsumer.class));
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/IsimUiccRecordsTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/IsimUiccRecordsTest.java
index 6f4666c..f33b002 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/IsimUiccRecordsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/IsimUiccRecordsTest.java
@@ -66,7 +66,7 @@
     private class IsimUiccRecordsUT extends IsimUiccRecords {
         IsimUiccRecordsUT(UiccCardApplication app, Context c,
                 CommandsInterface ci, IccFileHandler mFhMock) {
-            super(app, c, ci);
+            super(app, c, ci, mFeatureFlags);
             mFh = mFhMock;
         }
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardApplicationTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardApplicationTest.java
index d2490ef..10c51c3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardApplicationTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardApplicationTest.java
@@ -74,7 +74,7 @@
         mUiccCardAppStatus.pin2 = IccCardStatus.PinState.PINSTATE_ENABLED_VERIFIED;
 
         mUiccCardApplication = new UiccCardApplication(mUiccProfile, mUiccCardAppStatus,
-            mContext, mSimulatedCommands);
+            mContext, mSimulatedCommands, mFeatureFlags);
         mHandler = new Handler() {
             @Override
             public void handleMessage(Message msg) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
index 9265a62..58a8153 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
@@ -123,7 +123,7 @@
         // (before 1.2) of hal
         mIccCardStatus.mSlotPortMapping = new IccSlotPortMapping();
         mIccCardStatus.mSlotPortMapping.mPhysicalSlotIndex = 0;
-        mUiccControllerUT = UiccController.make(mContext);
+        mUiccControllerUT = UiccController.make(mContext, mFeatureFlags);
         // reset sLastSlotStatus so that onGetSlotStatusDone always sees a change in the slot status
         mUiccControllerUT.sLastSlotStatus = null;
         processAllMessages();
@@ -145,7 +145,7 @@
                 com.android.internal.R.array.non_removable_euicc_slots,
                 nonRemovableEuiccSlots);
         replaceInstance(UiccController.class, "mInstance", null, null);
-        mUiccControllerUT = UiccController.make(mContext);
+        mUiccControllerUT = UiccController.make(mContext, mFeatureFlags);
         processAllMessages();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
index 8209dfa..f88bc1e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
@@ -32,6 +32,7 @@
 import android.content.res.Resources;
 import android.os.Looper;
 import android.os.Message;
+import android.os.UserHandle;
 import android.telephony.TelephonyManager;
 
 import androidx.test.filters.SmallTest;
@@ -89,7 +90,7 @@
         }
 
         UiccStateChangedLauncher uiccLauncher =
-                new UiccStateChangedLauncher(mContext, UiccController.getInstance());
+                new UiccStateChangedLauncher(mContext, UiccController.getInstance(), mFeatureFlags);
         ArgumentCaptor<Integer> integerArgumentCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(UiccController.getInstance(), times(1)).registerForIccChanged(eq(uiccLauncher),
                 integerArgumentCaptor.capture(),
@@ -108,7 +109,8 @@
 
         // Amount of sent broadcasts to the device provisioning package.
         int broadcast_count = 1;
-        verify(mContext, times(broadcast_count)).sendBroadcast(intentArgumentCaptor.capture());
+        verify(mContext, times(broadcast_count)).sendBroadcastAsUser(intentArgumentCaptor.capture(),
+                eq(UserHandle.ALL));
         assertEquals(PROVISIONING_PACKAGE_NAME, intentArgumentCaptor.getValue().getPackage());
         assertEquals(TelephonyIntents.ACTION_SIM_STATE_CHANGED,
                 intentArgumentCaptor.getValue().getAction());
@@ -119,14 +121,16 @@
         uiccLauncher.handleMessage(msg);
 
         broadcast_count++;
-        verify(mContext, times(broadcast_count)).sendBroadcast(intentArgumentCaptor.capture());
+        verify(mContext, times(broadcast_count)).sendBroadcastAsUser(intentArgumentCaptor.capture(),
+                eq(UserHandle.ALL));
         assertEquals(PROVISIONING_PACKAGE_NAME, intentArgumentCaptor.getValue().getPackage());
         assertEquals(TelephonyIntents.ACTION_SIM_STATE_CHANGED,
                 intentArgumentCaptor.getValue().getAction());
 
         // Nothing's changed. Broadcast should not be sent.
         uiccLauncher.handleMessage(msg);
-        verify(mContext, times(broadcast_count)).sendBroadcast(any(Intent.class));
+        verify(mContext, times(broadcast_count)).sendBroadcastAsUser(any(Intent.class),
+                eq(UserHandle.ALL));
 
         // Card state's changed from restricted. Broadcast should be sent.
         card.update(mContext, mSimulatedCommands,
@@ -134,7 +138,8 @@
         uiccLauncher.handleMessage(msg);
 
         broadcast_count++;
-        verify(mContext, times(broadcast_count)).sendBroadcast(intentArgumentCaptor.capture());
+        verify(mContext, times(broadcast_count)).sendBroadcastAsUser(any(Intent.class),
+                eq(UserHandle.ALL));
         assertEquals(PROVISIONING_PACKAGE_NAME, intentArgumentCaptor.getValue().getPackage());
         assertEquals(TelephonyIntents.ACTION_SIM_STATE_CHANGED,
                 intentArgumentCaptor.getValue().getAction());
@@ -151,7 +156,7 @@
         }
 
         UiccStateChangedLauncher uiccLauncher =
-                new UiccStateChangedLauncher(mContext, UiccController.getInstance());
+                new UiccStateChangedLauncher(mContext, UiccController.getInstance(), mFeatureFlags);
         verify(UiccController.getInstance(), never()).registerForIccChanged(eq(uiccLauncher),
                 anyInt(), anyObject());
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java
index 5a07714..1252ff8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony.uicc.euicc.apdu;
 
+import static com.android.internal.telephony.CommandException.Error.RADIO_NOT_AVAILABLE;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThrows;
@@ -24,13 +25,19 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.os.Handler;
 import android.os.Looper;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.preference.PreferenceManager;
+import android.telephony.IccOpenLogicalChannelResponse;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
@@ -38,17 +45,23 @@
 
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.euicc.EuiccSession;
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.uicc.IccIoResult;
 import com.android.internal.telephony.uicc.IccUtils;
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mockito;
 
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class ApduSenderTest {
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     private static class ResponseCaptor extends ApduSenderResultCallback {
         public byte[] response;
@@ -78,6 +91,13 @@
         }
     }
 
+    private static final int PHONE_ID = 0;
+    private static final String SESSION_ID = "TEST";
+    // keep in sync with ApduSender.mChannelKey
+    private static final String SHARED_PREFS_KEY_CHANNEL_ID = "esim-channel_0";
+    // keep in sync with ApduSender.mChannelResponseKey
+    private static final String SHARED_PREFS_KEY_CHANNEL_RESPONSE = "esim-res-id_0";
+
     // Mocked classes
     private CommandsInterface mMockCi;
 
@@ -89,15 +109,16 @@
 
     @Before
     public void setUp() {
-        mMockCi = mock(CommandsInterface.class);
-        mHandler = new Handler(Looper.myLooper());
+        mSetFlagsRule.enableFlags(Flags.FLAG_OPTIMIZATION_APDU_SENDER);
 
+        mMockCi = mock(CommandsInterface.class);
+        mLooper = TestableLooper.get(this);
+        mHandler = new Handler(mLooper.getLooper());
         mResponseCaptor = new ResponseCaptor();
         mSelectResponse = null;
 
-        mSender = new ApduSender(InstrumentationRegistry.getContext(), 0 /* phoneId= */,
+        mSender = new ApduSender(InstrumentationRegistry.getContext(), PHONE_ID,
                             mMockCi, ApduSender.ISD_R_AID, false /* supportExtendedApdu */);
-        mLooper = TestableLooper.get(this);
     }
 
     @After
@@ -108,6 +129,9 @@
         mResponseCaptor = null;
         mSelectResponse = null;
         mSender = null;
+
+        EuiccSession.get().endSession(SESSION_ID);
+        clearSharedPreferences();
     }
 
     @Test
@@ -162,8 +186,11 @@
         mLooper.processAllMessages();
 
         assertEquals("A1A1A1", IccUtils.bytesToHexString(mResponseCaptor.response));
-        verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
-                eq(3), eq(0), eq("a"), anyBoolean(), any());
+        InOrder inOrder = inOrder(mMockCi);
+        inOrder.verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
+        inOrder.verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10),
+                eq(1), eq(2), eq(3), eq(0), eq("a"), anyBoolean(), any());
+        inOrder.verify(mMockCi).iccCloseLogicalChannel(eq(channel), eq(true /*isEs10*/), any());
     }
 
     @Test
@@ -182,14 +209,17 @@
         mLooper.processAllMessages();
 
         assertEquals("A4", IccUtils.bytesToHexString(mResponseCaptor.response));
-        verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
-                eq(3), eq(0), eq("a"), anyBoolean(), any());
-        verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
-                eq(3), eq(1), eq("ab"), anyBoolean(), any());
-        verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10), eq(1), eq(2),
-                eq(3), eq(0), eq(""), anyBoolean(), any());
-        verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x91),
-                eq(0), eq(2), eq("abcd"), anyBoolean(), any());
+        InOrder inOrder = inOrder(mMockCi);
+        inOrder.verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
+        inOrder.verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10),
+                eq(1), eq(2), eq(3), eq(0), eq("a"), anyBoolean(), any());
+        inOrder.verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10),
+                eq(1), eq(2), eq(3), eq(1), eq("ab"), anyBoolean(), any());
+        inOrder.verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10),
+                eq(1), eq(2),  eq(3), eq(0), eq(""), anyBoolean(), any());
+        inOrder.verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81),
+                eq(0xE2), eq(0x91), eq(0), eq(2), eq("abcd"), anyBoolean(), any());
+        inOrder.verify(mMockCi).iccCloseLogicalChannel(eq(channel), eq(true /*isEs10*/), any());
     }
 
     @Test
@@ -354,4 +384,156 @@
         assertTrue(mResponseCaptor.exception instanceof ApduException);
         verify(mMockCi, times(1)).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
     }
+
+    @Test
+    public void testConstructor_doNotCloseOpenChannelInSharedPreference()
+                  throws InterruptedException {
+        // Open a channel and not close it, by making CI.iccTransmitApduLogicalChannel throw.
+        int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
+        doThrow(new RuntimeException()).when(mMockCi).iccTransmitApduLogicalChannel(
+                eq(channel), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), any(),
+                anyBoolean(), any());
+        mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
+                10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
+        mLooper.processAllMessages();
+        // Stub close channel
+        reset(mMockCi);
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
+
+        // Call constructor
+        mSender = new ApduSender(InstrumentationRegistry.getContext(), PHONE_ID,
+                            mMockCi, ApduSender.ISD_R_AID, false /* supportExtendedApdu */);
+        mLooper.processAllMessages();
+
+        // The constructor should have closed channel
+        verify(mMockCi, times(0)).iccCloseLogicalChannel(eq(channel), eq(true /*isEs10*/), any());
+        assertEquals(1, getChannelIdFromSharedPreferences());
+    }
+
+    @Test
+    public void testSend_OpenChannelFailedNoSuchElement_useChannelInSharedPreference() {
+        // Open a channel but not close, by making CI.iccTransmitApduLogicalChannel throw.
+        int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
+        doThrow(new RuntimeException()).when(mMockCi).iccTransmitApduLogicalChannel(
+                eq(channel), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), any(),
+                anyBoolean(), any());
+        mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
+                10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
+        mLooper.processAllMessages();
+        reset(mMockCi);
+        // Constructor fails to close channel
+        LogicalChannelMocker.mockCloseLogicalChannel(
+                mMockCi, channel, new CommandException(RADIO_NOT_AVAILABLE));
+        mSender = new ApduSender(InstrumentationRegistry.getContext(), PHONE_ID,
+                            mMockCi, ApduSender.ISD_R_AID, false /* supportExtendedApdu */);
+        mLooper.processAllMessages();
+        reset(mMockCi);
+        // Stub open channel failure NO_SUCH_ELEMENT
+        LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi,
+                new CommandException(CommandException.Error.NO_SUCH_ELEMENT));
+        LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "A1A1A19000");
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
+
+        mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
+                10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
+        mLooper.processAllMessages();
+
+        // open channel would fail, and send/close would succeed because of
+        // previous open response saved in sharedPref
+        InOrder inOrder = inOrder(mMockCi);
+        inOrder.verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
+        inOrder.verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel),
+                 eq(channel | 10), eq(1), eq(2), eq(3), eq(0), eq("a"), anyBoolean(), any());
+        inOrder.verify(mMockCi).iccCloseLogicalChannel(eq(channel), eq(true /*isEs10*/), any());
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testSend_euiccSession_shouldNotCloseChannel()
+            throws InterruptedException {
+        int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
+        LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "A1A1A19000");
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
+        EuiccSession.get().startSession(SESSION_ID);
+
+        mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
+                10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
+        mLooper.processAllMessages();
+
+        assertEquals("A1A1A1", IccUtils.bytesToHexString(mResponseCaptor.response));
+        InOrder inOrder = inOrder(mMockCi);
+        inOrder.verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
+        inOrder.verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(channel | 10),
+                eq(1), eq(2), eq(3), eq(0), eq("a"), anyBoolean(), any());
+        // No iccCloseLogicalChannel
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testSendTwice_euiccSession_shouldOpenChannelOnceNotCloseChannel()
+            throws InterruptedException {
+        int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
+        LogicalChannelMocker.mockSendToLogicalChannel(
+                    mMockCi, channel, "A1A1A19000", "A1A1A19000");
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
+        EuiccSession.get().startSession(SESSION_ID);
+
+        mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
+                10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
+        mLooper.processAllMessages();
+        mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
+                10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
+        mLooper.processAllMessages();
+
+        assertEquals("A1A1A1", IccUtils.bytesToHexString(mResponseCaptor.response));
+        InOrder inOrder = inOrder(mMockCi);
+        // iccOpenLogicalChannel once
+        inOrder.verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
+        // iccTransmitApduLogicalChannel twice
+        inOrder.verify(mMockCi, times(2)).iccTransmitApduLogicalChannel(eq(channel),
+                 eq(channel | 10), eq(1), eq(2), eq(3), eq(0), eq("a"), anyBoolean(), any());
+        // No iccCloseLogicalChannel
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void testSendTwice_thenEndSession() throws InterruptedException {
+        int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
+        LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel,
+                "A1A1A19000", "A1A1A19000");
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel, /* error= */ null);
+        EuiccSession.get().startSession(SESSION_ID);
+
+        mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
+                10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
+        mLooper.processAllMessages();
+        mSender.send((selectResponse, requestBuilder) -> requestBuilder.addApdu(
+                10, 1, 2, 3, 0, "a"), mResponseCaptor, mHandler);
+        mLooper.processAllMessages();
+        EuiccSession.get().endSession(SESSION_ID);
+        mLooper.processAllMessages();
+
+        assertEquals("A1A1A1", IccUtils.bytesToHexString(mResponseCaptor.response));
+        InOrder inOrder = inOrder(mMockCi);
+        // iccOpenLogicalChannel once
+        inOrder.verify(mMockCi).iccOpenLogicalChannel(eq(ApduSender.ISD_R_AID), anyInt(), any());
+        // iccTransmitApduLogicalChannel twice
+        inOrder.verify(mMockCi, times(2)).iccTransmitApduLogicalChannel(eq(channel),
+                 eq(channel | 10), eq(1), eq(2), eq(3), eq(0), eq("a"), anyBoolean(), any());
+        // iccCloseLogicalChannel once
+        inOrder.verify(mMockCi).iccCloseLogicalChannel(eq(channel), eq(true /*isEs10*/), any());
+    }
+
+    private int getChannelIdFromSharedPreferences() {
+        return PreferenceManager.getDefaultSharedPreferences(InstrumentationRegistry.getContext())
+                .getInt(SHARED_PREFS_KEY_CHANNEL_ID, -1);
+    }
+
+    private void clearSharedPreferences() {
+        PreferenceManager.getDefaultSharedPreferences(InstrumentationRegistry.getContext())
+                .edit()
+                .remove(SHARED_PREFS_KEY_CHANNEL_ID)
+                .remove(SHARED_PREFS_KEY_CHANNEL_RESPONSE)
+                .apply();
+    }
 }