Merge "Notifying ECBM stop, if an emergency call starts during ECBM" into main
diff --git a/flags/data.aconfig b/flags/data.aconfig
index 321ac6b..ccd5db4 100644
--- a/flags/data.aconfig
+++ b/flags/data.aconfig
@@ -102,15 +102,12 @@
   bug:"318519337"
 }
 
-# OWNER=jackyu TARGET=25Q1
+# OWNER=jackyu TARGET=24Q4
 flag {
   name: "dds_callback"
   namespace: "telephony"
   description: "Adding new callback when DDS changed"
   bug:"353723350"
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
 }
 
 # OWNER=jackyu TARGET=25Q1
diff --git a/flags/ims.aconfig b/flags/ims.aconfig
index 4426933..4ce7508 100644
--- a/flags/ims.aconfig
+++ b/flags/ims.aconfig
@@ -138,6 +138,17 @@
     }
 }
 
+# OWNER=breadley TARGET=25Q1
+flag {
+    name: "ims_resolver_user_aware"
+    namespace: "telephony"
+    description: "When enabled, it makes ImsResolver mult-user aware for configurations like HSUM."
+    bug:"371272669"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
 # OWNER=meghapatil TARGET=25Q2
 flag {
     name: "support_sms_over_ims_apis"
@@ -145,3 +156,11 @@
     description: "Used to expose SMS related hidden APIs for SMS over IMS to public API."
     bug:"359721349"
 }
+
+# OWNER=jhyoon TARGET=25Q2
+flag {
+    name: "support_ims_mmtel_interface"
+    namespace: "telephony"
+    description: "This flag controls the type of API regarding MmTelFeature, either hidden or system type."
+    bug:"359721349"
+}
diff --git a/flags/misc.aconfig b/flags/misc.aconfig
index 2d6992a..303c0ff 100644
--- a/flags/misc.aconfig
+++ b/flags/misc.aconfig
@@ -78,6 +78,15 @@
   bug: "309896524"
 }
 
+# OWNER=rambowang TARGET=24Q3
+flag {
+    name: "reset_mobile_network_settings"
+    is_exported: true
+    namespace: "telephony"
+    description: "Allows applications to launch Reset Mobile Network Settings page in Settings app."
+    bug:"271921464"
+}
+
 # OWNER=sangyun TARGET=24Q3
 flag {
     name: "roaming_notification_for_single_data_network"
diff --git a/flags/satellite.aconfig b/flags/satellite.aconfig
index 825ea78..8662572 100644
--- a/flags/satellite.aconfig
+++ b/flags/satellite.aconfig
@@ -63,10 +63,19 @@
   }
 }
 
+# OWNER=amallampati TARGET=25Q2
+flag {
+    name: "satellite_system_apis"
+    is_exported: true
+    namespace: "telephony"
+    description: "Convert hidden SatelliteManager APIs to system APIs."
+    bug:"373436320"
+}
+
 # OWNER=rambowang TARGET=25Q2
 flag {
   name: "satellite_state_change_listener"
   namespace: "telephony"
   description: "Introduce SatelliteManager APIs for carrier apps to monitor satellite state change"
   bug: "357638490"
-}
\ No newline at end of file
+}
diff --git a/flags/subscription.aconfig b/flags/subscription.aconfig
index 543aa20..aea9bd0 100644
--- a/flags/subscription.aconfig
+++ b/flags/subscription.aconfig
@@ -19,6 +19,15 @@
   bug: "296076674"
 }
 
+# OWNER=rambowang TARGET=24Q3
+flag {
+  name: "data_only_cellular_service"
+  is_exported: true
+  namespace: "telephony"
+  description: "Supports customized cellular service capabilities per subscription."
+  bug: "296097429"
+}
+
 # OWNER=hhshin TARGET=24Q3
 flag {
   name: "support_psim_to_esim_conversion"
@@ -72,3 +81,11 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+# OWNER=jmattis TARGET=25Q2
+flag {
+  name: "subscription_plan_allow_status_and_end_date"
+  namespace: "telephony"
+  description: "Provide APIs to retrieve the status and recurrence rule info on a subscription plan"
+  bug: "357272015"
+}
diff --git a/flags/uicc.aconfig b/flags/uicc.aconfig
index ad0c59f..abe4296 100644
--- a/flags/uicc.aconfig
+++ b/flags/uicc.aconfig
@@ -79,4 +79,12 @@
     metadata {
         purpose: PURPOSE_BUGFIX
     }
-}
\ No newline at end of file
+}
+
+# OWNER=jhyoon TARGET=25Q2
+flag {
+    name: "support_isim_record"
+    namespace: "telephony"
+    description: "This flag controls the type of API that retrieves ISIM records, either hidden or system type."
+    bug:"359721349"
+}
diff --git a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index 1aaa1d3..732582f 100644
--- a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -41,6 +41,7 @@
 import android.telephony.ims.ImsCallSession;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.MediaQualityStatus;
+import android.telephony.satellite.NtnSignalStrength;
 
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.telephony.Rlog;
@@ -348,6 +349,13 @@
                 sender.getSubId(), availableServices);
     }
 
+    @Override
+    public void notifyCarrierRoamingNtnSignalStrengthChanged(Phone sender,
+            @NonNull NtnSignalStrength ntnSignalStrength) {
+        mTelephonyRegistryMgr.notifyCarrierRoamingNtnSignalStrengthChanged(
+                sender.getSubId(), ntnSignalStrength);
+    }
+
     /**
      * Convert the {@link Call.State} enum into the PreciseCallState.PRECISE_CALL_STATE_* constants
      * for the public API.
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 2e1a89f..a91000e 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -42,7 +42,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
 import android.database.SQLException;
 import android.hardware.radio.modem.ImeiInfo;
 import android.net.Uri;
@@ -470,12 +469,6 @@
         }
     };
 
-    private boolean hasCalling() {
-        if (!TelephonyCapabilities.minimalTelephonyCdmCheck(mFeatureFlags)) return true;
-        return mContext.getPackageManager().hasSystemFeature(
-            PackageManager.FEATURE_TELEPHONY_CALLING);
-    }
-
     private void initOnce(CommandsInterface ci) {
         if (ci instanceof SimulatedRadioControl) {
             mSimulatedRadioControl = (SimulatedRadioControl) ci;
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index 3141406..9eebc60 100644
--- a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -48,6 +48,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
+import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
 import com.android.internal.telephony.uicc.IccConstants;
@@ -1511,8 +1512,7 @@
     @VisibleForTesting
     public void notifyIfOutgoingEmergencySms(String destAddr) {
         Phone[] allPhones = mPhoneFactoryProxy.getPhones();
-        EmergencyNumber emergencyNumber = mPhone.getEmergencyNumberTracker().getEmergencyNumber(
-                destAddr);
+        EmergencyNumber emergencyNumber = getEmergencyNumber(mPhone, destAddr);
         if (emergencyNumber != null) {
             mPhone.notifyOutgoingEmergencySms(emergencyNumber);
         } else if (allPhones.length > 1) {
@@ -1522,8 +1522,7 @@
                 if (phone.getPhoneId() == mPhone.getPhoneId()) {
                     continue;
                 }
-                emergencyNumber = phone.getEmergencyNumberTracker()
-                        .getEmergencyNumber(destAddr);
+                emergencyNumber = getEmergencyNumber(phone, destAddr);
                 if (emergencyNumber != null) {
                     mPhone.notifyOutgoingEmergencySms(emergencyNumber);
                     break;
@@ -1532,6 +1531,13 @@
         }
     }
 
+    private EmergencyNumber getEmergencyNumber(Phone phone, String number) {
+        if (!phone.hasCalling()) return null;
+        EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker();
+        if (tracker == null) return null;
+        return tracker.getEmergencyNumber(number);
+    }
+
     private void returnUnspecifiedFailure(PendingIntent pi) {
         if (pi != null) {
             try {
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index 011e67b..f404e04 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -692,11 +692,10 @@
         if (mFeatureFlags.carrierRoamingNbIotNtn()) {
             if (result == Intents.RESULT_SMS_HANDLED) {
                 SatelliteController satelliteController = SatelliteController.getInstance();
-                if (satelliteController == null) {
-                    log("SatelliteController is not initialized");
-                    return;
+                if (satelliteController != null
+                        && satelliteController.shouldSendSmsToDatagramDispatcher(mPhone)) {
+                    satelliteController.onSmsReceived(mPhone.getSubId());
                 }
-                satelliteController.onSmsReceived(mPhone.getSubId());
             }
         }
 
@@ -772,8 +771,7 @@
         if (result != Intents.RESULT_SMS_HANDLED && result != Activity.RESULT_OK) {
             mMetrics.writeIncomingSmsError(mPhone.getPhoneId(), is3gpp2(), smsSource, result);
             mPhone.getSmsStats().onIncomingSmsError(is3gpp2(), smsSource, result,
-                    TelephonyManager.from(mContext)
-                            .isEmergencyNumber(smsb.getOriginatingAddress()));
+                    isEmergencyNumber(smsb.getOriginatingAddress()));
             if (mPhone != null) {
                 TelephonyAnalytics telephonyAnalytics = mPhone.getTelephonyAnalytics();
                 if (telephonyAnalytics != null) {
@@ -1052,7 +1050,7 @@
             logeWithLocalLog(errorMsg, tracker.getMessageId());
             mPhone.getSmsStats().onIncomingSmsError(
                     is3gpp2(), tracker.getSource(), RESULT_SMS_NULL_PDU,
-                    TelephonyManager.from(mContext).isEmergencyNumber(tracker.getAddress()));
+                    isEmergencyNumber(tracker.getAddress()));
             if (mPhone != null) {
                 TelephonyAnalytics telephonyAnalytics = mPhone.getTelephonyAnalytics();
                 if (telephonyAnalytics != null) {
@@ -1082,8 +1080,7 @@
                                 tracker.getMessageId());
                         mPhone.getSmsStats().onIncomingSmsWapPush(tracker.getSource(),
                                 messageCount, RESULT_SMS_NULL_MESSAGE, tracker.getMessageId(),
-                                TelephonyManager.from(mContext)
-                                        .isEmergencyNumber(tracker.getAddress()));
+                                isEmergencyNumber(tracker.getAddress()));
                         return false;
                     }
                 }
@@ -1118,8 +1115,7 @@
             mMetrics.writeIncomingWapPush(mPhone.getPhoneId(), tracker.getSource(),
                     format, timestamps, wapPushResult, tracker.getMessageId());
             mPhone.getSmsStats().onIncomingSmsWapPush(tracker.getSource(), messageCount,
-                    result, tracker.getMessageId(), TelephonyManager.from(mContext)
-                            .isEmergencyNumber(tracker.getAddress()));
+                    result, tracker.getMessageId(), isEmergencyNumber(tracker.getAddress()));
             // result is Activity.RESULT_OK if an ordered broadcast was sent
             if (result == Activity.RESULT_OK) {
                 return true;
@@ -1140,7 +1136,7 @@
                 format, timestamps, block, tracker.getMessageId());
         mPhone.getSmsStats().onIncomingSmsSuccess(is3gpp2(), tracker.getSource(),
                 messageCount, block, tracker.getMessageId(),
-                TelephonyManager.from(mContext).isEmergencyNumber(tracker.getAddress()));
+                isEmergencyNumber(tracker.getAddress()));
         CarrierRoamingSatelliteSessionStats sessionStats =
                 CarrierRoamingSatelliteSessionStats.getInstance(mPhone.getSubId());
         sessionStats.onIncomingSms(mPhone.getSubId());
@@ -1178,6 +1174,13 @@
         return true;
     }
 
+    private boolean isEmergencyNumber(String number) {
+        if (!mPhone.hasCalling()) return false;
+        TelephonyManager manager = TelephonyManager.from(mContext);
+        if (manager == null) return false;
+        return manager.isEmergencyNumber(number);
+    }
+
     /**
      * Processes the message part while the credential-encrypted storage is still locked.
      *
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index ca82d31..ab9be76 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -74,6 +74,7 @@
 import android.telephony.ims.RegistrationManager;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.satellite.NtnSignalStrength;
 import android.text.TextUtils;
 import android.util.LocalLog;
 import android.util.Log;
@@ -660,7 +661,7 @@
         mSmsStorageMonitor = mTelephonyComponentFactory.inject(SmsStorageMonitor.class.getName())
                 .makeSmsStorageMonitor(this, mFeatureFlags);
         mSmsUsageMonitor = mTelephonyComponentFactory.inject(SmsUsageMonitor.class.getName())
-                .makeSmsUsageMonitor(context);
+                .makeSmsUsageMonitor(context, mFeatureFlags);
         mUiccController = UiccController.getInstance();
         mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
         mSimActivationTracker = mTelephonyComponentFactory
@@ -1966,6 +1967,13 @@
     }
 
     /**
+     * @return true if this device supports calling, false otherwise.
+     */
+    public boolean hasCalling() {
+        return TelephonyCapabilities.supportsTelephonyCalling(mFeatureFlags, mContext);
+    }
+
+    /**
      * Retrieves the EmergencyNumberTracker of the phone instance.
      */
     public EmergencyNumberTracker getEmergencyNumberTracker() {
@@ -5380,6 +5388,18 @@
         mNotifier.notifyCarrierRoamingNtnAvailableServicesChanged(this, availableServices);
     }
 
+    /**
+     * Notify external listeners that carrier roaming non-terrestrial network
+     * signal strength changed.
+     * @param ntnSignalStrength non-terrestrial network signal strength.
+     */
+    public void notifyCarrierRoamingNtnSignalStrengthChanged(
+            @NonNull NtnSignalStrength ntnSignalStrength) {
+        logd("notifyCarrierRoamingNtnSignalStrengthChanged: ntnSignalStrength="
+                + ntnSignalStrength.getLevel());
+        mNotifier.notifyCarrierRoamingNtnSignalStrengthChanged(this, ntnSignalStrength);
+    }
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("Phone: subId=" + getSubId());
         pw.println(" mPhoneId=" + mPhoneId);
diff --git a/src/java/com/android/internal/telephony/PhoneNotifier.java b/src/java/com/android/internal/telephony/PhoneNotifier.java
index f652370..faf4fd1 100644
--- a/src/java/com/android/internal/telephony/PhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/PhoneNotifier.java
@@ -38,6 +38,7 @@
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.MediaQualityStatus;
+import android.telephony.satellite.NtnSignalStrength;
 
 import java.util.List;
 import java.util.Set;
@@ -169,4 +170,8 @@
     /** Notify carrier roaming non-terrestrial available services changed. */
     void notifyCarrierRoamingNtnAvailableServicesChanged(
             Phone sender, @NetworkRegistrationInfo.ServiceType int[] availableServices);
+
+    /** Notify carrier roaming non-terrestrial network signal strength changed. */
+    void notifyCarrierRoamingNtnSignalStrengthChanged(Phone sender,
+            @NonNull NtnSignalStrength ntnSignalStrength);
 }
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoController.java b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
index 7ee3de2..c40193e 100644
--- a/src/java/com/android/internal/telephony/PhoneSubInfoController.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
@@ -53,7 +53,9 @@
 import com.android.telephony.Rlog;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.stream.Collectors;
 
 public class PhoneSubInfoController extends IPhoneSubInfo.Stub {
     private static final String TAG = "PhoneSubInfoController";
@@ -477,39 +479,37 @@
      *
      * @param subId subscriptionId
      * @param callingPackage package name of the caller
-     * @param callingFeatureId feature Id of the caller
      * @return List of public user identities of type android.net.Uri or empty list  if
      * EF_IMPU is not available.
      * @throws IllegalArgumentException if the subscriptionId is not valid
      * @throws IllegalStateException in case the ISIM hasn’t been loaded.
      * @throws SecurityException if the caller does not have the required permission
      */
-    public List<Uri> getImsPublicUserIdentities(int subId, String callingPackage,
-            String callingFeatureId) {
-        if (TelephonyPermissions.
-                checkCallingOrSelfReadPrivilegedPhoneStatePermissionOrReadPhoneNumber(
-                mContext, subId, callingPackage, callingFeatureId, "getImsPublicUserIdentities")) {
-
-            enforceTelephonyFeatureWithException(callingPackage,
-                    PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getImsPublicUserIdentities");
-
-            Phone phone = getPhone(subId);
-            assert phone != null;
-            IsimRecords isimRecords = phone.getIsimRecords();
-            if (isimRecords != null) {
-                String[] impus = isimRecords.getIsimImpu();
-                List<Uri> impuList = new ArrayList<>();
-                for (String impu : impus) {
-                    if (impu != null && impu.trim().length() > 0) {
-                        impuList.add(Uri.parse(impu));
-                    }
-                }
-                return impuList;
-            }
-            throw new IllegalStateException("ISIM is not loaded");
-        } else {
-            throw new IllegalArgumentException("Invalid SubscriptionID  = " + subId);
+    public List<Uri> getImsPublicUserIdentities(int subId, String callingPackage) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid subscription: " + subId);
         }
+
+        TelephonyPermissions
+                .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                        mContext, subId, "getImsPublicUserIdentities");
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getImsPublicUserIdentities");
+
+        Phone phone = getPhone(subId);
+        assert phone != null;
+        IsimRecords isimRecords = phone.getIsimRecords();
+        if (isimRecords != null) {
+            String[] impus = isimRecords.getIsimImpu();
+            List<Uri> impuList = new ArrayList<>();
+            for (String impu : impus) {
+                if (impu != null && impu.trim().length() > 0) {
+                    impuList.add(Uri.parse(impu));
+                }
+            }
+            return impuList;
+        }
+        throw new IllegalStateException("ISIM is not loaded");
     }
 
     /**
@@ -546,6 +546,45 @@
     }
 
     /**
+     * Fetches the IMS Proxy Call Session Control Function(P-CSCF) based on the subscription.
+     *
+     * @param subId subscriptionId
+     * @param callingPackage package name of the caller
+     * @return List of IMS Proxy Call Session Control Function strings.
+     * @throws IllegalArgumentException if the subscriptionId is not valid
+     * @throws IllegalStateException in case the ISIM hasn’t been loaded.
+     * @throws SecurityException if the caller does not have the required permission
+     */
+    public List<String> getImsPcscfAddresses(int subId, String callingPackage) {
+        if (!mFeatureFlags.supportIsimRecord()) {
+            return new ArrayList<>();
+        }
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid subscription: " + subId);
+        }
+
+        TelephonyPermissions
+                .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                        mContext, subId, "getImsPcscfAddresses");
+        enforceTelephonyFeatureWithException(callingPackage,
+                PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getImsPcscfAddresses");
+
+        Phone phone = getPhone(subId);
+        assert phone != null;
+        IsimRecords isimRecords = phone.getIsimRecords();
+        if (isimRecords != null) {
+            String[] pcscfs = isimRecords.getIsimPcscf();
+            List<String> pcscfList = Arrays.stream(pcscfs)
+                    .filter(u -> u != null)
+                    .map(u -> u.trim())
+                    .filter(u -> u.length() > 0)
+                    .collect(Collectors.toList());
+            return pcscfList;
+        }
+        throw new IllegalStateException("ISIM is not loaded");
+    }
+
+    /**
      * Returns the USIM service table that fetched from EFUST elementary field that are loaded
      * based on the appType.
      */
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 88ead6f..34ac832 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -178,6 +178,9 @@
     /** @hide */
     public static final HalVersion RADIO_HAL_VERSION_2_2 = new HalVersion(2, 2);
 
+    /** @hide */
+    public static final HalVersion RADIO_HAL_VERSION_2_3 = new HalVersion(2, 3);
+
     // Hal version
     private final Map<Integer, HalVersion> mHalVersion = new HashMap<>();
 
@@ -6212,6 +6215,7 @@
             case 1: return RADIO_HAL_VERSION_2_0;
             case 2: return RADIO_HAL_VERSION_2_1;
             case 3: return RADIO_HAL_VERSION_2_2;
+            case 4: return RADIO_HAL_VERSION_2_3;
             default: return RADIO_HAL_VERSION_UNKNOWN;
         }
     }
diff --git a/src/java/com/android/internal/telephony/RILUtils.java b/src/java/com/android/internal/telephony/RILUtils.java
index a81dbc8..cbf2330 100644
--- a/src/java/com/android/internal/telephony/RILUtils.java
+++ b/src/java/com/android/internal/telephony/RILUtils.java
@@ -4132,6 +4132,34 @@
     }
 
     /**
+     * This API is for fallback to support getAllowedCarriers too.
+     *
+     * Convert an array of CarrierInfo defined in
+     * radio/aidl/android/hardware/radio/sim/CarrierInfo.aidl to a list of CarrierIdentifiers.
+     *
+     * @param carrierInfos array of CarrierInfo defined in
+     *                     radio/aidl/android/hardware/radio/sim/CarrierInfo.aidl
+     * @return The converted list of CarrierIdentifiers
+     */
+    public static List<CarrierIdentifier> convertAidlCarrierInfoListToHalCarrierList(
+            android.hardware.radio.sim.CarrierInfo[] carrierInfos) {
+        List<CarrierIdentifier> ret = new ArrayList<>();
+        if (carrierInfos == null) {
+            return ret;
+        }
+        for (android.hardware.radio.sim.CarrierInfo carrierInfo : carrierInfos) {
+            String mcc = carrierInfo.mcc;
+            String mnc = carrierInfo.mnc;
+            String spn = carrierInfo.spn;
+            String imsi = carrierInfo.imsiPrefix;
+            String gid1 = carrierInfo.gid1;
+            String gid2 = carrierInfo.gid2;
+            ret.add(new CarrierIdentifier(mcc, mnc, spn, imsi, gid1, gid2));
+        }
+        return ret;
+    }
+
+    /**
      * Convert the sim policy defined in
      * radio/aidl/android/hardware/radio/sim/SimLockMultiSimPolicy.aidl to the equivalent sim
      * policy defined in android.telephony/CarrierRestrictionRules.MultiSimPolicy
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index 8a2d5bf..871cabc 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -2120,7 +2120,7 @@
             }
         }
 
-        if (mTelephonyManager.isEmergencyNumber(trackers[0].mDestAddress)) {
+        if (mPhone.hasCalling() && mTelephonyManager.isEmergencyNumber(trackers[0].mDestAddress)) {
             new AsyncEmergencyContactNotifier(mContext).execute();
         }
     }
diff --git a/src/java/com/android/internal/telephony/SimResponse.java b/src/java/com/android/internal/telephony/SimResponse.java
index 97692a0..12c9a3c 100644
--- a/src/java/com/android/internal/telephony/SimResponse.java
+++ b/src/java/com/android/internal/telephony/SimResponse.java
@@ -123,19 +123,34 @@
         if (!carrierRestrictions.allowedCarriersPrioritized) {
             carrierRestrictionDefault = CarrierRestrictionRules.CARRIER_RESTRICTION_DEFAULT_ALLOWED;
         }
-
-        CarrierRestrictionRules ret = CarrierRestrictionRules.newBuilder().setAllowedCarriers(
-                RILUtils.convertHalCarrierList(
-                        carrierRestrictions.allowedCarriers)).setExcludedCarriers(
-                RILUtils.convertHalCarrierList(
-                        carrierRestrictions.excludedCarriers)).setDefaultCarrierRestriction(
-                carrierRestrictionDefault).setMultiSimPolicy(policy).setCarrierRestrictionStatus(
-                carrierRestrictions.status).setAllowedCarrierInfo(
-                RILUtils.convertAidlCarrierInfoList(
-                        carrierRestrictions.allowedCarrierInfoList)).setExcludedCarrierInfo(
-                RILUtils.convertAidlCarrierInfoList(
-                        carrierRestrictions.excludedCarrierInfoList)).setCarrierLockInfoFeature(
-                carrierLockInfoSupported).build();
+        CarrierRestrictionRules ret = null;
+        if (carrierLockInfoSupported) {
+            // In order to support the old API { @link TelephonyManager#getAllowedCarriers() } we
+            // are parsing the allowedCarrierInfoList to CarrierIdentifier List also along with
+            // CarrierInfo List
+            ret = CarrierRestrictionRules.newBuilder().setAllowedCarriers(
+                    RILUtils.convertAidlCarrierInfoListToHalCarrierList(
+                            carrierRestrictions.allowedCarrierInfoList)).setExcludedCarriers(
+                    RILUtils.convertAidlCarrierInfoListToHalCarrierList(
+                            carrierRestrictions.excludedCarrierInfoList)).
+                    setDefaultCarrierRestriction(
+                    carrierRestrictionDefault).setMultiSimPolicy(
+                    policy).setCarrierRestrictionStatus(
+                    carrierRestrictions.status).setAllowedCarrierInfo(
+                    RILUtils.convertAidlCarrierInfoList(
+                            carrierRestrictions.allowedCarrierInfoList)).setExcludedCarrierInfo(
+                    RILUtils.convertAidlCarrierInfoList(
+                            carrierRestrictions.excludedCarrierInfoList)).setCarrierLockInfoFeature(
+                            true).build();
+        } else {
+            ret = CarrierRestrictionRules.newBuilder().setAllowedCarriers(
+                    RILUtils.convertHalCarrierList(
+                            carrierRestrictions.allowedCarriers)).setExcludedCarriers(
+                    RILUtils.convertHalCarrierList(
+                            carrierRestrictions.excludedCarriers)).setDefaultCarrierRestriction(
+                    carrierRestrictionDefault).setMultiSimPolicy(
+                    policy).build();
+        }
         if (responseInfo.error == RadioError.NONE) {
             RadioResponse.sendMessageResponse(rr.mResult, ret);
         }
diff --git a/src/java/com/android/internal/telephony/SmsController.java b/src/java/com/android/internal/telephony/SmsController.java
index e3c409d..051fbbd 100644
--- a/src/java/com/android/internal/telephony/SmsController.java
+++ b/src/java/com/android/internal/telephony/SmsController.java
@@ -276,6 +276,7 @@
         }
         UserHandle callingUser = Binder.getCallingUserHandle();
 
+
         Rlog.d(LOG_TAG, "sendTextForSubscriber caller=" + callingPackage);
 
         if (skipFdnCheck || skipShortCodeCheck) {
@@ -1194,8 +1195,9 @@
         }
 
         // Skip FDN check for emergency numbers
+        if (!TelephonyCapabilities.supportsTelephonyCalling(mFlags, mContext)) return false;
         TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
-        if (tm.isEmergencyNumber(destAddr)) {
+        if (tm != null && tm.isEmergencyNumber(destAddr)) {
             return false;
         }
 
diff --git a/src/java/com/android/internal/telephony/SmsDispatchersController.java b/src/java/com/android/internal/telephony/SmsDispatchersController.java
index 023680a..77fd1f6 100644
--- a/src/java/com/android/internal/telephony/SmsDispatchersController.java
+++ b/src/java/com/android/internal/telephony/SmsDispatchersController.java
@@ -29,7 +29,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.res.Resources;
 import android.net.Uri;
 import android.os.AsyncResult;
 import android.os.Binder;
@@ -46,13 +45,14 @@
 import android.telephony.ServiceState;
 import android.telephony.SmsManager;
 import android.telephony.SmsMessage;
-import android.telephony.TelephonyManager;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.satellite.SatelliteManager;
 import android.text.TextUtils;
 
 import com.android.ims.ImsManager;
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.telephony.cdma.CdmaInboundSmsHandler;
@@ -67,18 +67,17 @@
 import com.android.internal.telephony.gsm.GsmSMSDispatcher;
 import com.android.internal.telephony.satellite.DatagramDispatcher;
 import com.android.internal.telephony.satellite.SatelliteController;
-import com.android.internal.R;
 import com.android.telephony.Rlog;
 
 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;
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicLong;
 
 /**
  *
@@ -830,8 +829,7 @@
 
         if (!tracker.mUsesImsServiceForIms) {
             if (isSmsDomainSelectionEnabled()) {
-                TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
-                boolean isEmergency = tm.isEmergencyNumber(tracker.mDestAddress);
+                boolean isEmergency = isEmergencyNumber(tracker.mDestAddress);
                 // This may be invoked by another thread, so this operation is posted and
                 // handled through the execution flow of SmsDispatchersController.
                 SomeArgs args = SomeArgs.obtain();
@@ -1220,8 +1218,7 @@
     private void handleSmsSentCompletedUsingDomainSelection(@NonNull String destAddr,
             long messageId, boolean success, boolean isOverIms, boolean isLastSmsPart) {
         if (mEmergencyStateTracker != null) {
-            TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
-            if (tm.isEmergencyNumber(destAddr)) {
+            if (isEmergencyNumber(destAddr)) {
                 mEmergencyStateTracker.endSms(String.valueOf(messageId), success,
                         isOverIms ? NetworkRegistrationInfo.DOMAIN_PS
                                   : NetworkRegistrationInfo.DOMAIN_CS,
@@ -1258,7 +1255,7 @@
     }
 
     private void notifySmsSentToDatagramDispatcher(long messageId, boolean success) {
-        if (SatelliteController.getInstance().isInCarrierRoamingNbIotNtn(mPhone)) {
+        if (SatelliteController.getInstance().shouldSendSmsToDatagramDispatcher(mPhone)) {
             DatagramDispatcher.getInstance().onSendSmsDone(mPhone.getSubId(), messageId, success);
         }
     }
@@ -1270,8 +1267,7 @@
      */
     private void handleSmsReceivedViaIms(@Nullable String origAddr) {
         if (mEmergencyStateTracker != null) {
-            TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
-            if (origAddr != null && tm.isEmergencyNumber(origAddr)) {
+            if (origAddr != null && isEmergencyNumber(origAddr)) {
                 mEmergencyStateTracker.onEmergencySmsReceived();
             }
         }
@@ -1289,7 +1285,9 @@
 
     private boolean isTestEmergencyNumber(String number) {
         try {
+            if (!mPhone.hasCalling()) return false;
             TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+            if (tm == null) return false;
             Map<Integer, List<EmergencyNumber>> eMap = tm.getEmergencyNumberList();
             return eMap.values().stream().flatMap(Collection::stream).anyMatch(eNumber ->
                     eNumber.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST)
@@ -1866,7 +1864,7 @@
                 messageUri, persistMessage, priority, expectMore, validityPeriod, messageId,
                 skipShortCodeCheck, false);
 
-        if (SatelliteController.getInstance().isInCarrierRoamingNbIotNtn(mPhone)) {
+        if (SatelliteController.getInstance().shouldSendSmsToDatagramDispatcher(mPhone)) {
             // Send P2P SMS using carrier roaming NB IOT NTN
             DatagramDispatcher.getInstance().sendSms(pendingRequest);
             return;
@@ -1879,8 +1877,7 @@
         logd("sendTextInternal: messageId=" + request.messageId
                  + ", uniqueMessageId=" + request.uniqueMessageId);
         if (isSmsDomainSelectionEnabled()) {
-            TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
-            boolean isEmergency = tm.isEmergencyNumber(request.destAddr);
+            boolean isEmergency = isEmergencyNumber(request.destAddr);
             sendSmsUsingDomainSelection(getDomainSelectionConnectionHolder(isEmergency),
                     request, "sendText");
             return;
@@ -2031,7 +2028,7 @@
                 null, 0, parts, messageUri, persistMessage, priority, expectMore,
                 validityPeriod, messageId, false, false);
 
-        if (SatelliteController.getInstance().isInCarrierRoamingNbIotNtn(mPhone)) {
+        if (SatelliteController.getInstance().shouldSendSmsToDatagramDispatcher(mPhone)) {
             // Send multipart P2P SMS using carrier roaming NB IOT NTN
             DatagramDispatcher.getInstance().sendSms(pendingRequest);
             return;
@@ -2040,11 +2037,17 @@
         sendMultipartTextInternal(pendingRequest);
     }
 
+    private boolean isEmergencyNumber(String number) {
+        if (!mPhone.hasCalling()) return false;
+        TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+        if (tm == null) return false;
+        return tm.isEmergencyNumber(number);
+    }
+
     private void sendMultipartTextInternal(PendingRequest request) {
         logd("sendMultipartTextInternal: messageId=" + request.messageId);
         if (isSmsDomainSelectionEnabled()) {
-            TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
-            boolean isEmergency = tm.isEmergencyNumber(request.destAddr);
+            boolean isEmergency = isEmergencyNumber(request.destAddr);
             sendSmsUsingDomainSelection(getDomainSelectionConnectionHolder(isEmergency),
                     request, "sendMultipartText");
             return;
@@ -2241,7 +2244,7 @@
      * to trigger SMSC to send all pending SMS to the particular subscription.
      */
     public void sendMtSmsPollingMessage() {
-        if (!SatelliteController.getInstance().isInCarrierRoamingNbIotNtn(mPhone)) {
+        if (!SatelliteController.getInstance().shouldSendSmsToDatagramDispatcher(mPhone)) {
             logd("sendMtSmsPollingMessage: not in roaming nb iot ntn");
             return;
         }
@@ -2268,7 +2271,9 @@
                 asArrayList(null), false, null, 0, asArrayList(mtSmsPollingText), null, false, 0,
                 false, 5, 0L, true, true);
 
-        DatagramDispatcher.getInstance().sendSms(pendingRequest);
+        if (SatelliteController.getInstance().shouldSendSmsToDatagramDispatcher(mPhone)) {
+            DatagramDispatcher.getInstance().sendSms(pendingRequest);
+        }
     }
 
     public interface SmsInjectionCallback {
diff --git a/src/java/com/android/internal/telephony/SmsUsageMonitor.java b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
index 8e4ac60..7fb5811 100644
--- a/src/java/com/android/internal/telephony/SmsUsageMonitor.java
+++ b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
@@ -35,6 +35,7 @@
 import android.util.AtomicFile;
 import android.util.Xml;
 
+import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.util.XmlUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.telephony.Rlog;
@@ -113,6 +114,8 @@
     /** Context for retrieving regexes from XML resource. */
     private final Context mContext;
 
+    private final FeatureFlags mFeatureFlags;
+
     /** Country code for the cached short code pattern matcher. */
     private String mCurrentCountry;
 
@@ -255,8 +258,9 @@
      * @param context the context to use to load resources and get TelephonyManager service
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public SmsUsageMonitor(Context context) {
+    public SmsUsageMonitor(Context context, FeatureFlags flags) {
         mContext = context;
+        mFeatureFlags = flags;
         ContentResolver resolver = context.getContentResolver();
         mRoleManager = (RoleManager) mContext.getSystemService(Context.ROLE_SERVICE);
 
@@ -404,7 +408,8 @@
         synchronized (mSettingsObserverHandler) {
             TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
             // always allow emergency numbers
-            if (tm.isEmergencyNumber(destAddress)) {
+            if (TelephonyCapabilities.supportsTelephonyCalling(mFeatureFlags, mContext)
+                    && tm != null && tm.isEmergencyNumber(destAddress)) {
                 if (DBG) Rlog.d(TAG, "isEmergencyNumber");
                 return SmsManager.SMS_CATEGORY_NOT_SHORT_CODE;
             }
diff --git a/src/java/com/android/internal/telephony/TelephonyCapabilities.java b/src/java/com/android/internal/telephony/TelephonyCapabilities.java
index 71d3b14..b650b43 100644
--- a/src/java/com/android/internal/telephony/TelephonyCapabilities.java
+++ b/src/java/com/android/internal/telephony/TelephonyCapabilities.java
@@ -18,6 +18,8 @@
 
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.Build;
 import android.os.SystemProperties;
 
@@ -209,4 +211,14 @@
 
         return featureFlags.minimalTelephonyCdmCheck();
     }
+
+    /**
+     * @return true if this device supports telephony calling, false if it does not.
+     */
+    public static boolean supportsTelephonyCalling(@NonNull FeatureFlags featureFlags,
+            Context context) {
+        if (!TelephonyCapabilities.minimalTelephonyCdmCheck(featureFlags)) return true;
+        return context.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_TELEPHONY_CALLING);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index 10e97b6..b4a3ee6 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -301,8 +301,8 @@
         return new SmsStorageMonitor(phone, flags);
     }
 
-    public SmsUsageMonitor makeSmsUsageMonitor(Context context) {
-        return new SmsUsageMonitor(context);
+    public SmsUsageMonitor makeSmsUsageMonitor(Context context, FeatureFlags flags) {
+        return new SmsUsageMonitor(context, flags);
     }
 
     public ServiceStateTracker makeServiceStateTracker(GsmCdmaPhone phone, CommandsInterface ci,
diff --git a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
index 62d33d9..079e705 100644
--- a/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/data/PhoneSwitcher.java
@@ -1834,16 +1834,18 @@
         if (phone == null) {
             return false;
         }
-
+        Call bgCall = phone.getBackgroundCall();
+        Call fgCall = phone.getForegroundCall();
+        if (bgCall == null || fgCall == null) {
+            return false;
+        }
         // A phone in voice call might trigger data being switched to it.
         // Exclude dialing to give modem time to process an EMC first before dealing with DDS switch
         // Include alerting because modem RLF leads to delay in switch, so carrier required to
         // switch in alerting phase.
         // TODO: check ringing call for vDADA
-        return (!phone.getBackgroundCall().isIdle()
-                && phone.getBackgroundCall().getState() != Call.State.DIALING)
-                || (!phone.getForegroundCall().isIdle()
-                && phone.getForegroundCall().getState() != Call.State.DIALING);
+        return (!bgCall.isIdle() && bgCall.getState() != Call.State.DIALING)
+                || (!fgCall.isIdle() && fgCall.getState() != Call.State.DIALING);
     }
 
     private void updateHalCommandToUse() {
diff --git a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
index 977dd6e..48cc7cb 100644
--- a/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
+++ b/src/java/com/android/internal/telephony/emergency/EmergencyStateTracker.java
@@ -1695,6 +1695,17 @@
             mRadioOnHelper.triggerRadioOnAndListen(new RadioOnStateListener.Callback() {
                 @Override
                 public void onComplete(RadioOnStateListener listener, boolean isRadioReady) {
+                    // Make sure the Call has not already been canceled by the user.
+                    if (expectedConnection.getState() == STATE_DISCONNECTED) {
+                        Rlog.i(TAG, "Call disconnected before the outgoing call was placed."
+                                + "Skipping call placement.");
+                        // If call is already canceled by the user, notify modem to exit emergency
+                        // call mode by sending radio on with forEmergencyCall=false.
+                        for (Phone phone : mPhoneFactoryProxy.getPhones()) {
+                            phone.setRadioPower(true, false, false, true);
+                        }
+                        return;
+                    }
                     if (!isRadioReady) {
                         if (satelliteController.isSatelliteEnabledOrBeingEnabled()) {
                             // Could not turn satellite off
diff --git a/src/java/com/android/internal/telephony/ims/ImsResolver.java b/src/java/com/android/internal/telephony/ims/ImsResolver.java
index eb389b7..b95911f 100644
--- a/src/java/com/android/internal/telephony/ims/ImsResolver.java
+++ b/src/java/com/android/internal/telephony/ims/ImsResolver.java
@@ -19,6 +19,7 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -51,6 +52,7 @@
 import android.util.ArraySet;
 import android.util.LocalLog;
 import android.util.Log;
+import android.util.Pair;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 
@@ -129,6 +131,10 @@
     private static final int HANDLER_MSIM_CONFIGURATION_CHANGE = 7;
     // clear any carrier ImsService test overrides.
     private static final int HANDLER_CLEAR_CARRIER_IMS_SERVICE_CONFIG = 8;
+    // the user has switched
+    private static final int HANDLER_USER_SWITCHED = 9;
+    // A dynamic query has failed permanently, remove the package from being tracked
+    private static final int HANDLER_REMOVE_PACKAGE_PERM_ERROR = 10;
 
     // Delay between dynamic ImsService queries.
     private static final int DELAY_DYNAMIC_QUERY_MS = 5000;
@@ -158,14 +164,19 @@
     }
 
     private static class OverrideConfig {
+        public final String packageName;
         public final int slotId;
+        public final int userId;
         public final boolean isCarrierService;
-        public final Map<Integer, String> featureTypeToPackageMap;
+        public final int[] featureTypes;
 
-        OverrideConfig(int slotIndex, boolean isCarrier, Map<Integer, String> feature) {
+        OverrideConfig(String pkgName, int slotIndex, int userIndex, boolean isCarrier,
+                int[] features) {
+            packageName = pkgName;
             slotId = slotIndex;
+            userId = userIndex;
             isCarrierService = isCarrier;
-            featureTypeToPackageMap = feature;
+            featureTypes = features;
         }
     }
 
@@ -175,7 +186,8 @@
      */
     @VisibleForTesting
     public static class ImsServiceInfo {
-        public ComponentName name;
+        public final ComponentName name;
+        public final Set<UserHandle> users = new HashSet<>();
         // Determines if features were created from metadata in the manifest or through dynamic
         // query.
         public boolean featureFromMetadata = true;
@@ -184,7 +196,8 @@
         // Map slotId->Feature
         private final HashSet<ImsFeatureConfiguration.FeatureSlotPair> mSupportedFeatures;
 
-        public ImsServiceInfo() {
+        public ImsServiceInfo(ComponentName componentName) {
+            name = componentName;
             mSupportedFeatures = new HashSet<>();
         }
 
@@ -208,37 +221,41 @@
         public boolean equals(Object o) {
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
-
             ImsServiceInfo that = (ImsServiceInfo) o;
-
-            if (name != null ? !name.equals(that.name) : that.name != null) return false;
-            if (!mSupportedFeatures.equals(that.mSupportedFeatures)) {
-                return false;
-            }
-            return controllerFactory != null ? controllerFactory.equals(that.controllerFactory)
-                    : that.controllerFactory == null;
+            return Objects.equals(name, that.name) && Objects.equals(users, that.users);
         }
 
         @Override
         public int hashCode() {
-            // We do not include mSupportedFeatures in hashcode because the internal structure
-            // changes after adding.
-            int result = name != null ? name.hashCode() : 0;
-            result = 31 * result + (controllerFactory != null ? controllerFactory.hashCode() : 0);
-            return result;
+            return Objects.hash(name, users);
         }
 
         @Override
         public String toString() {
             return "[ImsServiceInfo] name="
                     + name
-                    + ", featureFromMetadata="
+                    + ", user="
+                    + users
+                    + ",featureFromMetadata="
                     + featureFromMetadata
                     + ","
                     + printFeatures(mSupportedFeatures);
         }
     }
 
+    private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            final UserHandle handle = intent.getParcelableExtra(Intent.EXTRA_USER,
+                    UserHandle.class);
+            switch (action) {
+                case Intent.ACTION_USER_SWITCHED ->
+                        mHandler.obtainMessage(HANDLER_USER_SWITCHED, handle).sendToTarget();
+            }
+        }
+    };
+
     // Receives broadcasts from the system involving changes to the installed applications. If
     // an ImsService that we are configured to use is installed, we must bind to it.
     private final BroadcastReceiver mAppChangedReceiver = new BroadcastReceiver() {
@@ -311,6 +328,17 @@
     };
 
     /**
+     * Testing interface used to mock ActivityManager in testing
+     */
+    @VisibleForTesting
+    public interface ActivityManagerProxy {
+        /**
+         * @return The current user
+         */
+        UserHandle getCurrentUser();
+    }
+
+    /**
      * Testing interface used to mock SubscriptionManager in testing
      */
     @VisibleForTesting
@@ -360,6 +388,13 @@
         }
     };
 
+    private ActivityManagerProxy mActivityManagerProxy = new ActivityManagerProxy() {
+        @Override
+        public UserHandle getCurrentUser() {
+            return UserHandle.of(ActivityManager.getCurrentUser());
+        }
+    };
+
     /**
      * Testing interface for injecting mock ImsServiceControllers.
      */
@@ -470,6 +505,11 @@
                     maybeRemovedImsService(packageName);
                     break;
                 }
+                case HANDLER_REMOVE_PACKAGE_PERM_ERROR: {
+                    Pair<String, UserHandle> packageInfo = (Pair<String, UserHandle>) msg.obj;
+                    maybeRemovedImsServiceForUser(packageInfo.first, packageInfo.second);
+                    break;
+                }
                 case HANDLER_BOOT_COMPLETE: {
                     if (!mBootCompletedHandlerRan) {
                         mBootCompletedHandlerRan = true;
@@ -517,11 +557,15 @@
                 }
                 case HANDLER_OVERRIDE_IMS_SERVICE_CONFIG: {
                     OverrideConfig config = (OverrideConfig) msg.obj;
+                    setPackageNameUserOverride(config.packageName, config.userId);
+                    Map<Integer, String> featureConfig = new HashMap<>();
+                    for (int featureType : config.featureTypes) {
+                        featureConfig.put(featureType, config.packageName);
+                    }
                     if (config.isCarrierService) {
-                        overrideCarrierService(config.slotId,
-                                config.featureTypeToPackageMap);
+                        overrideCarrierService(config.slotId, featureConfig);
                     } else {
-                        overrideDeviceService(config.featureTypeToPackageMap);
+                        overrideDeviceService(featureConfig);
                     }
                     break;
                 }
@@ -534,6 +578,11 @@
                     clearCarrierServiceOverrides(msg.arg1);
                     break;
                 }
+                case HANDLER_USER_SWITCHED: {
+                    UserHandle handle = (UserHandle) msg.obj;
+                    Log.i(TAG, "onUserSwitched=" + handle);
+                    maybeAddedImsService(null);
+                }
                 default:
                     break;
             }
@@ -561,11 +610,16 @@
                 }
 
                 @Override
-                public void onPermanentError(ComponentName name) {
+                public void onPermanentError(ComponentName name, UserHandle user) {
                     Log.w(TAG, "onPermanentError: component=" + name);
                     mEventLog.log("onPermanentError - error for " + name);
-                    mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE,
-                            name.getPackageName()).sendToTarget();
+                    if (!mFeatureFlags.imsResolverUserAware()) {
+                        mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE,
+                                name.getPackageName()).sendToTarget();
+                    } else {
+                        mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE_PERM_ERROR,
+                                new Pair<>(name.getPackageName(), user)).sendToTarget();
+                    }
                 }
             };
 
@@ -573,6 +627,8 @@
     // Array index corresponds to slot, per slot there is a feature->package name mapping.
     // should only be accessed from handler
     private final SparseArray<SparseArray<String>> mOverrideServices;
+    //Used during testing, restricts the ImsService to be bound on a specific user.
+    private final Map<String, UserHandle> mImsServiceTestUserRestrictions = new HashMap<>();
     // Outer array index corresponds to Slot Id, Maps ImsFeature.FEATURE->bound ImsServiceController
     // Locked on mBoundServicesLock
     private final SparseArray<SparseArray<ImsServiceController>> mBoundImsServicesByFeature;
@@ -628,6 +684,11 @@
     }
 
     @VisibleForTesting
+    public void setActivityManagerProxy(ActivityManagerProxy proxy) {
+        mActivityManagerProxy = proxy;
+    }
+
+    @VisibleForTesting
     public Handler getHandler() {
         return mHandler;
     }
@@ -660,6 +721,10 @@
         appChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
         appChangedFilter.addDataScheme("package");
         mReceiverContext.registerReceiver(mAppChangedReceiver, appChangedFilter);
+        if (mFeatureFlags.imsResolverUserAware()) {
+            mReceiverContext.registerReceiver(mUserReceiver, new IntentFilter(
+                    Intent.ACTION_USER_SWITCHED));
+        }
         mReceiverContext.registerReceiver(mConfigChangedReceiver, new IntentFilter(
                 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
 
@@ -706,19 +771,24 @@
     // carrier config changed.
     private void bindCarrierServicesIfAvailable() {
         boolean hasConfigChanged = false;
+        boolean pendingDynamicQuery = false;
         for (int slotId = 0; slotId < mNumSlots; slotId++) {
             int subId = mSubscriptionManagerProxy.getSubId(slotId);
             Map<Integer, String> featureMap = getImsPackageOverrideConfig(subId);
             for (int f = ImsFeature.FEATURE_EMERGENCY_MMTEL; f < ImsFeature.FEATURE_MAX; f++) {
                 String newPackageName = featureMap.getOrDefault(f, "");
                 if (!TextUtils.isEmpty(newPackageName)) {
+                    Log.d(TAG, "bindCarrierServicesIfAvailable - carrier package found: "
+                            + newPackageName + ", feature "
+                            + ImsFeature.FEATURE_LOG_MAP.getOrDefault(f, "invalid")
+                            + " on slot " + slotId);
                     mEventLog.log("bindCarrierServicesIfAvailable - carrier package found: "
                             + newPackageName + " on slot " + slotId);
                     // Carrier configs are already available, so mark received.
                     mCarrierConfigReceived = true;
                     setSubId(slotId, subId);
                     setCarrierConfiguredPackageName(newPackageName, slotId, f);
-                    ImsServiceInfo info = getImsServiceInfoFromCache(newPackageName);
+                    ImsServiceInfo info = getVisibleImsServiceInfoFromCache(newPackageName);
                     // We do not want to trigger feature configuration changes unless there is
                     // already a valid carrier config change.
                     if (info != null && info.featureFromMetadata) {
@@ -726,11 +796,18 @@
                     } else {
                         // Config will change when this query completes
                         scheduleQueryForFeatures(info);
+                        if (info != null) pendingDynamicQuery = true;
                     }
                 }
             }
         }
-        if (hasConfigChanged) calculateFeatureConfigurationChange();
+        // we want to make sure that we are either pending to bind to a carrier configured service
+        // or bind to the device config if we potentially missed the carrier config changed
+        // indication.
+        if (hasConfigChanged || (mFeatureFlags.imsResolverUserAware()
+                && mCarrierConfigReceived && !pendingDynamicQuery)) {
+            calculateFeatureConfigurationChange();
+        }
     }
 
     /**
@@ -833,14 +910,15 @@
     }
 
     // Used for testing only.
-    public boolean overrideImsServiceConfiguration(int slotId, boolean isCarrierService,
-            Map<Integer, String> featureConfig) {
+    public boolean overrideImsServiceConfiguration(String packageName, int slotId, int userId,
+            boolean isCarrierService, int[] overrideFeatureTypes) {
         if (slotId < 0 || slotId >= mNumSlots) {
             Log.w(TAG, "overrideImsServiceConfiguration: invalid slotId!");
             return false;
         }
 
-        OverrideConfig overrideConfig = new OverrideConfig(slotId, isCarrierService, featureConfig);
+        OverrideConfig overrideConfig = new OverrideConfig(packageName, slotId, userId,
+                isCarrierService, overrideFeatureTypes);
         Message.obtain(mHandler, HANDLER_OVERRIDE_IMS_SERVICE_CONFIG, overrideConfig)
                 .sendToTarget();
         return true;
@@ -881,10 +959,14 @@
     }
 
     // not synchronized, access in handler ONLY.
-    private void removeOverridePackageName(int slotId) {
+    private Set<String> removeOverridePackageName(int slotId) {
+        Set<String> removedOverrides = new HashSet<>();
         for (int f = ImsFeature.FEATURE_EMERGENCY_MMTEL; f < ImsFeature.FEATURE_MAX; f++) {
-            getOverridePackageName(slotId).remove(f);
+            SparseArray<String> overrides = getOverridePackageName(slotId);
+            String packageName = overrides.removeReturnOld(f);
+            if (packageName != null) removedOverrides.add(packageName);
         }
+        return removedOverrides;
     }
 
     // not synchronized, access in handler ONLY.
@@ -894,6 +976,22 @@
     }
 
     // not synchronized, access in handler ONLY.
+    private void setPackageNameUserOverride(String packageName, int userId) {
+        if (packageName == null || packageName.isEmpty() || userId == UserHandle.USER_NULL) return;
+        Log.i(TAG, "setPackageNameUserOverride: set for " + packageName + ", user= " + userId);
+        mImsServiceTestUserRestrictions.put(packageName, UserHandle.of(userId));
+    }
+
+    // not synchronized, access in handler ONLY.
+    private void clearPackageNameUserOverride(String packageName) {
+        UserHandle handle = mImsServiceTestUserRestrictions.remove(packageName);
+        if (handle != null) {
+            Log.i(TAG, "clearPackageNameUserOverride: cleared for " + packageName
+                    + "on user " + handle);
+        }
+    }
+
+    // not synchronized, access in handler ONLY.
     private @Nullable String getOverridePackageName(int slotId,
             @ImsFeature.FeatureType int featureType) {
         return getOverridePackageName(slotId).get(featureType);
@@ -927,13 +1025,14 @@
 
     /**
      * Check the cached ImsServices that exist on this device to determine if there is a ImsService
-     * with the same package name that matches the provided configuration.
+     * with the same package name that matches the provided configuration and is configured to run
+     * in one of the active users.
      */
     // not synchronized, access in handler ONLY.
     private boolean doesCachedImsServiceExist(String packageName, int slotId,
             @ImsFeature.FeatureType int featureType) {
         // Config exists, but the carrier ImsService also needs to support this feature
-        ImsServiceInfo info = getImsServiceInfoFromCache(packageName);
+        ImsServiceInfo info = getVisibleImsServiceInfoFromCache(packageName);
         return info != null && info.getSupportedFeatures().stream().anyMatch(
                 feature -> feature.slotId == slotId && feature.featureType == featureType);
     }
@@ -1073,7 +1172,8 @@
         return null;
     }
 
-    private void putImsController(int slotId, int feature, ImsServiceController controller) {
+    private void putImsController(int slotId, int subId, int feature,
+            ImsServiceController controller) {
         if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.FEATURE_INVALID
                 || feature >= ImsFeature.FEATURE_MAX) {
             Log.w(TAG, "putImsController received invalid parameters - slot: " + slotId
@@ -1088,9 +1188,9 @@
             }
             mEventLog.log("putImsController - [" + slotId + ", "
                     + ImsFeature.FEATURE_LOG_MAP.get(feature) + "] -> " + controller);
-            Log.i(TAG, "ImsServiceController added on slot: " + slotId + " with feature: "
-                    + ImsFeature.FEATURE_LOG_MAP.get(feature) + " using package: "
-                    + controller.getComponentName());
+            Log.i(TAG, "ImsServiceController added on slot: " + slotId + ", subId: " + subId
+                    + " with feature: " + ImsFeature.FEATURE_LOG_MAP.get(feature)
+                    + " using package: " + controller.getComponentName());
             services.put(feature, controller);
         }
     }
@@ -1134,6 +1234,10 @@
             // features. Will only be one (if it exists), since it is a set.
             ImsServiceInfo match = getInfoByComponentName(mInstalledServicesCache, info.name);
             if (match != null) {
+                if (mFeatureFlags.imsResolverUserAware()) {
+                    match.users.clear();
+                    match.users.addAll(info.users);
+                }
                 // for dynamic query the new "info" will have no supported features yet. Don't wipe
                 // out the cache for the existing features or update yet. Instead start a query
                 // for features dynamically.
@@ -1141,9 +1245,8 @@
                     mEventLog.log("maybeAddedImsService - updating features for " + info.name
                             + ": " + printFeatures(match.getSupportedFeatures()) + " -> "
                             + printFeatures(info.getSupportedFeatures()));
-                    Log.i(TAG, "Updating features in cached ImsService: " + info.name);
-                    Log.d(TAG, "Updating features - Old features: " + match + " new features: "
-                            + info);
+                    Log.d(TAG, "Updating features in cached ImsService: " + info.name
+                            + ", old features: " + match + " new features: " + info);
                     // update features in the cache
                     match.replaceFeatures(info.getSupportedFeatures());
                     requiresCalculation = true;
@@ -1168,10 +1271,9 @@
         if (requiresCalculation) calculateFeatureConfigurationChange();
     }
 
-    // Remove the ImsService from the cache. This may have been due to the ImsService being removed
-    // from the device or was returning permanent errors when bound.
+    // Remove the ImsService from the cache due to the ImsService package being removed.
     // Called from the handler ONLY
-    private boolean maybeRemovedImsService(String packageName) {
+    private boolean maybeRemovedImsServiceOld(String packageName) {
         ImsServiceInfo match = getInfoByPackageName(mInstalledServicesCache, packageName);
         if (match != null) {
             mInstalledServicesCache.remove(match.name);
@@ -1184,6 +1286,70 @@
         return false;
     }
 
+    // Remove the ImsService from the cache due to the ImsService package being removed.
+    // Called from the handler ONLY
+    private boolean maybeRemovedImsService(String packageName) {
+        if (!mFeatureFlags.imsResolverUserAware()) {
+            return maybeRemovedImsServiceOld(packageName);
+        }
+        ImsServiceInfo match = getInfoByPackageName(mInstalledServicesCache, packageName);
+        if (match != null) {
+            List<ImsServiceInfo> imsServices = searchForImsServices(packageName,
+                    match.controllerFactory);
+            ImsServiceInfo newMatch = imsServices.isEmpty() ? null : imsServices.getFirst();
+            if (newMatch == null) {
+                clearPackageNameUserOverride(match.name.getPackageName());
+                // The package doesn't exist anymore on any user, so remove
+                mInstalledServicesCache.remove(match.name);
+                mEventLog.log("maybeRemovedImsService - removing ImsService: " + match);
+                Log.i(TAG, "maybeRemovedImsService Removing ImsService for all users: "
+                        + match.name);
+                unbindImsService(match);
+            } else {
+                // The Package exists on some users still, so modify the users
+                match.users.clear();
+                match.users.addAll(newMatch.users);
+                mEventLog.log("maybeRemovedImsService - modifying ImsService users: " + match);
+                Log.i(TAG, "maybeRemovedImsService - Modifying ImsService users " + match);
+                // If this package still remains on some users, then it is possible we are unbinding
+                // an active ImsService, but the assumption here is that the package is being
+                // removed on an active user. Be safe and unbind now - we will rebind below if
+                // needed.
+                unbindImsService(match);
+            }
+            calculateFeatureConfigurationChange();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Remove the cached ImsService for a specific user. If there are no more users available after
+     * removing the specified user, remove the ImsService cache entry entirely.
+     */
+    // Called from the handler ONLY
+    private boolean maybeRemovedImsServiceForUser(String packageName, UserHandle user) {
+        ImsServiceInfo match = getInfoByPackageName(mInstalledServicesCache, packageName);
+        if (match != null) {
+            mEventLog.log("maybeRemovedImsServiceForUser - removing ImsService " + match
+                    + "for user " + user);
+            Log.i(TAG, "maybeRemovedImsServiceForUser: Removing ImsService "
+                    + match + "for user " + user);
+            unbindImsService(match);
+            match.users.remove(user);
+            if (match.users.isEmpty()) {
+                mEventLog.log("maybeRemovedImsServiceForUser - no more users, removing "
+                        + "ImsService " + match);
+                Log.i(TAG, "maybeRemovedImsServiceForUser - no more users, removing "
+                        + "ImsService " + match);
+                mInstalledServicesCache.remove(match.name);
+            }
+            calculateFeatureConfigurationChange();
+            return true;
+        }
+        return false;
+    }
+
     private boolean isDeviceService(ImsServiceInfo info) {
         if (info == null) return false;
         synchronized (mDeviceServices) {
@@ -1193,6 +1359,14 @@
 
     private List<Integer> getSlotsForActiveCarrierService(ImsServiceInfo info) {
         if (info == null) return Collections.emptyList();
+        if (mFeatureFlags.imsResolverUserAware()) {
+            UserHandle activeUser = getUserForBind(info);
+            if (activeUser == null) {
+                Log.d(TAG, "getSlotsForActiveCarrierService: ImsService " + info.name + "is not "
+                        + "configured to run for any users, skipping...");
+                return Collections.emptyList();
+            }
+        }
         List<Integer> slots = new ArrayList<>(mNumSlots);
         for (int i = 0; i < mNumSlots; i++) {
             if (!TextUtils.isEmpty(getCarrierConfiguredPackageNames(i).values().stream()
@@ -1222,7 +1396,7 @@
         return searchMap.get(matchValue);
     }
 
-    private void bindImsServiceWithFeatures(ImsServiceInfo info,
+    private void bindImsServiceWithFeatures(ImsServiceInfo info, UserHandle user,
             Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
         // Only bind if there are features that will be created by the service.
         if (shouldFeaturesCauseBind(features)) {
@@ -1230,10 +1404,21 @@
             ImsServiceController controller = getControllerByServiceInfo(mActiveControllers, info);
             SparseIntArray slotIdToSubIdMap = mSlotIdToSubIdMap.clone();
             if (controller != null) {
-                Log.i(TAG, "ImsService connection exists for " + info.name + ", updating features "
-                        + features);
                 try {
-                    controller.changeImsServiceFeatures(features, slotIdToSubIdMap);
+                    if (!mFeatureFlags.imsResolverUserAware()
+                            || Objects.equals(user, controller.getBoundUser())) {
+                        Log.i(TAG, "ImsService connection exists for " + info.name
+                                + ", updating features " + features);
+                        controller.changeImsServiceFeatures(features, slotIdToSubIdMap);
+                    } else {
+                        // Changing a user is a pretty rare event, we need to unbind and rebind
+                        // on the correct new user.
+                        Log.i(TAG, "ImsService user changed for " + info.name
+                                + ", rebinding on user " + user + ", features " + features);
+                        controller.unbind();
+                        controller.bind(user, features, slotIdToSubIdMap);
+                    }
+
                     // Features have been set, there was an error adding/removing. When the
                     // controller recovers, it will add/remove again.
                 } catch (RemoteException e) {
@@ -1243,8 +1428,9 @@
                 controller = info.controllerFactory.create(mContext, info.name, this, mRepo,
                         mFeatureFlags);
                 Log.i(TAG, "Binding ImsService: " + controller.getComponentName()
-                        + " with features: " + features);
-                controller.bind(features, slotIdToSubIdMap);
+                        + "on user " + user + " with features: " + features + ", subIdMap: "
+                        + slotIdToSubIdMap);
+                controller.bind(user, features, slotIdToSubIdMap);
                 mEventLog.log("bindImsServiceWithFeatures - create new controller: "
                         + controller);
             }
@@ -1285,7 +1471,7 @@
             imsFeaturesBySlot.addAll(info.getSupportedFeatures().stream()
                     .filter(feature -> info.name.getPackageName().equals(
                             getCarrierConfiguredPackageName(feature.slotId, feature.featureType)))
-                    .collect(Collectors.toList()));
+                    .toList());
             return imsFeaturesBySlot;
         }
         if (isDeviceService(info)) {
@@ -1298,7 +1484,7 @@
                     // by the carrier ImsService.
                     .filter(feature -> !doesCarrierConfigurationExist(feature.slotId,
                             feature.featureType))
-                    .collect(Collectors.toList()));
+                    .toList());
         }
         return imsFeaturesBySlot;
     }
@@ -1309,8 +1495,9 @@
      * adds the ImsServiceController from the mBoundImsServicesByFeature structure.
      */
     @Override
-    public void imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller) {
-        putImsController(slotId, feature, controller);
+    public void imsServiceFeatureCreated(int slotId, int subId, int feature,
+            ImsServiceController controller) {
+        putImsController(slotId, subId, feature, controller);
     }
 
     /**
@@ -1341,13 +1528,19 @@
     }
 
     @Override
-    public void imsServiceBindPermanentError(ComponentName name) {
+    public void imsServiceBindPermanentError(ComponentName name, UserHandle user) {
         if (name == null) {
             return;
         }
-        Log.w(TAG, "imsServiceBindPermanentError: component=" + name);
-        mEventLog.log("imsServiceBindPermanentError - for " + name);
-        mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE, name.getPackageName()).sendToTarget();
+        Log.w(TAG, "imsServiceBindPermanentError: component=" + name + ", user=" + user);
+        mEventLog.log("imsServiceBindPermanentError - for " + name + ", user " + user);
+        if (!mFeatureFlags.imsResolverUserAware()) {
+            mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE,
+                    name.getPackageName()).sendToTarget();
+        } else {
+            mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE_PERM_ERROR,
+                    new Pair<>(name.getPackageName(), user)).sendToTarget();
+        }
     }
 
     /**
@@ -1381,7 +1574,10 @@
     private void clearCarrierServiceOverrides(int slotId) {
         Log.i(TAG, "clearing carrier ImsService overrides");
         mEventLog.log("clearing carrier ImsService overrides");
-        removeOverridePackageName(slotId);
+        Set<String> removedPackages = removeOverridePackageName(slotId);
+        for (String pkg : removedPackages) {
+            clearPackageNameUserOverride(pkg);
+        }
         carrierConfigChanged(slotId, getSubId(slotId));
     }
 
@@ -1399,8 +1595,9 @@
                         + oldPackageName + " -> " + overridePackageName);
                 mEventLog.log("overrideDeviceService - device package changed (override): "
                         + oldPackageName + " -> " + overridePackageName);
+                clearPackageNameUserOverride(oldPackageName);
                 setDeviceConfiguration(overridePackageName, featureType);
-                ImsServiceInfo info = getImsServiceInfoFromCache(overridePackageName);
+                ImsServiceInfo info = getVisibleImsServiceInfoFromCache(overridePackageName);
                 if (info == null || info.featureFromMetadata) {
                     requiresRecalc = true;
                 } else {
@@ -1430,7 +1627,7 @@
         ArrayMap<String, ImsServiceInfo> featureDynamicImsPackages = new ArrayMap<>();
         for (int f = ImsFeature.FEATURE_EMERGENCY_MMTEL; f < ImsFeature.FEATURE_MAX; f++) {
             String packageName = getDeviceConfiguration(f);
-            ImsServiceInfo serviceInfo = getImsServiceInfoFromCache(packageName);
+            ImsServiceInfo serviceInfo = getVisibleImsServiceInfoFromCache(packageName);
             if (serviceInfo != null && !serviceInfo.featureFromMetadata
                     && !featureDynamicImsPackages.containsKey(packageName)) {
                 featureDynamicImsPackages.put(packageName, serviceInfo);
@@ -1465,13 +1662,7 @@
             setCarrierConfiguredPackageName(newPackageName, slotId, f);
             // Carrier config may have not changed, but we still want to kick off a recalculation
             // in case there has been a change to the supported device features.
-            ImsServiceInfo info = getImsServiceInfoFromCache(newPackageName);
-            Log.i(TAG, "updateBoundServices - carrier package changed: "
-                    + oldPackageName + " -> " + newPackageName + " on slot " + slotId
-                    + ", hasConfigChanged=" + hasConfigChanged);
-            mEventLog.log("updateBoundServices - carrier package changed: "
-                    + oldPackageName + " -> " + newPackageName + " on slot " + slotId
-                    + ", hasConfigChanged=" + hasConfigChanged);
+            ImsServiceInfo info = getVisibleImsServiceInfoFromCache(newPackageName);
             if (info == null || info.featureFromMetadata) {
                 hasConfigChanged = true;
             } else {
@@ -1479,6 +1670,12 @@
                 scheduleQueryForFeatures(info);
                 didQuerySchedule = true;
             }
+            Log.i(TAG, "updateBoundServices - carrier package changed: "
+                    + oldPackageName + " -> " + newPackageName + " on slot " + slotId
+                    + ", hasConfigChanged=" + hasConfigChanged);
+            mEventLog.log("updateBoundServices - carrier package changed: "
+                    + oldPackageName + " -> " + newPackageName + " on slot " + slotId
+                    + ", hasConfigChanged=" + hasConfigChanged);
         }
         if (hasConfigChanged) calculateFeatureConfigurationChange();
 
@@ -1530,7 +1727,7 @@
     }
 
     private void scheduleQueryForFeatures(ComponentName name, int delayMs) {
-        ImsServiceInfo service = getImsServiceInfoFromCache(name.getPackageName());
+        ImsServiceInfo service = getVisibleImsServiceInfoFromCache(name.getPackageName());
         if (service == null) {
             Log.w(TAG, "scheduleQueryForFeatures: Couldn't find cached info for name: " + name);
             return;
@@ -1614,6 +1811,12 @@
 
     // Starts a dynamic query. Called from handler ONLY.
     private void startDynamicQuery(ImsServiceInfo service) {
+        UserHandle user = getUserForBind(service);
+        if (user == null) {
+            Log.i(TAG, "scheduleQueryForFeatures: skipping query for ImsService that is not"
+                    + " running: " + service);
+            return;
+        }
         // if not current device/carrier service, don't perform query. If this changes, this method
         // will be called again.
         if (!isDeviceService(service) && getSlotsForActiveCarrierService(service).isEmpty()) {
@@ -1622,7 +1825,7 @@
             return;
         }
         mEventLog.log("startDynamicQuery - starting query for " + service);
-        boolean queryStarted = mFeatureQueryManager.startQuery(service.name,
+        boolean queryStarted = mFeatureQueryManager.startQuery(service.name, user,
                 service.controllerFactory.getServiceInterface());
         if (!queryStarted) {
             Log.w(TAG, "startDynamicQuery: service could not connect. Retrying after delay.");
@@ -1637,7 +1840,7 @@
     // process complete dynamic query. Called from handler ONLY.
     private void dynamicQueryComplete(ComponentName name,
             Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
-        ImsServiceInfo service = getImsServiceInfoFromCache(name.getPackageName());
+        ImsServiceInfo service = getVisibleImsServiceInfoFromCache(name.getPackageName());
         if (service == null) {
             Log.w(TAG, "dynamicQueryComplete: Couldn't find cached info for name: "
                     + name);
@@ -1683,17 +1886,92 @@
 
     // Calculate the new configuration for the bound ImsServices.
     // Should ONLY be called from the handler.
-    private void calculateFeatureConfigurationChange() {
+    private void calculateFeatureConfigurationChangeOld() {
         for (ImsServiceInfo info : mInstalledServicesCache.values()) {
             Set<ImsFeatureConfiguration.FeatureSlotPair> features = calculateFeaturesToCreate(info);
             if (shouldFeaturesCauseBind(features)) {
-                bindImsServiceWithFeatures(info, features);
+                bindImsServiceWithFeatures(info, mContext.getUser(), features);
             } else {
                 unbindImsService(info);
             }
         }
     }
 
+    // Should ONLY be called from the handler.
+    private void calculateFeatureConfigurationChange() {
+        if (!mFeatureFlags.imsResolverUserAware()) {
+            calculateFeatureConfigurationChangeOld();
+            return;
+        }
+        // There is an implicit assumption here that the ImsServiceController will remove itself
+        // from caches BEFORE adding a new one. If this assumption is broken, we will remove a valid
+        // ImsServiceController from the cache accidentally. To keep this assumption valid, we will
+        // iterate through the cache twice - first to unbind, then to bind and change features of
+        // existing ImsServiceControllers. This is a little inefficient, but there should be on the
+        // order of 10 installed ImsServices at most, so running through this list twice is
+        // reasonable vs the memory cost of caching binding vs unbinding services.
+
+        // Unbind first if needed
+        for (ImsServiceInfo info : mInstalledServicesCache.values()) {
+            Set<ImsFeatureConfiguration.FeatureSlotPair> features = calculateFeaturesToCreate(info);
+            UserHandle user = getUserForBind(info);
+            if (shouldFeaturesCauseBind(features) && user != null) continue;
+            unbindImsService(info);
+        }
+        // Bind/alter features second
+        for (ImsServiceInfo info : mInstalledServicesCache.values()) {
+            Set<ImsFeatureConfiguration.FeatureSlotPair> features = calculateFeaturesToCreate(info);
+            UserHandle user = getUserForBind(info);
+            if (shouldFeaturesCauseBind(features) && user != null) {
+                bindImsServiceWithFeatures(info, user, features);
+            }
+        }
+    }
+
+    /**
+     * Returns the UserHandle that should be used to bind the ImsService.
+     *
+     * @return The UserHandle of the user that telephony is running in if the
+     * ImsService is configured to run in that user, or the current active user
+     * if not. Returns null if the ImsService is not configured to run in any
+     * active user.
+     */
+    private UserHandle getUserForBind(ImsServiceInfo info) {
+        if (!mFeatureFlags.imsResolverUserAware()) {
+            return mContext.getUser();
+        }
+        UserHandle currentUser = mActivityManagerProxy.getCurrentUser();
+        List<UserHandle> activeUsers = getActiveUsers().stream()
+                .filter(info.users::contains).toList();
+        if (activeUsers.isEmpty()) return null;
+        // If there is a test restriction in place for this package, prioritize that restriction
+        UserHandle testRestriction = mImsServiceTestUserRestrictions.getOrDefault(
+                info.name.getPackageName(), null);
+        if (testRestriction != null && activeUsers.stream()
+                .anyMatch(u -> Objects.equals(u, testRestriction))) {
+            return testRestriction;
+        }
+        // Prioritize the User that Telephony is in, since it is always running
+        if (activeUsers.stream()
+                .anyMatch(u -> Objects.equals(u, mContext.getUser()))) {
+            return mContext.getUser();
+        }
+        if (activeUsers.stream().anyMatch(u -> Objects.equals(u, currentUser))) {
+            return currentUser;
+        }
+        return null;
+    }
+
+  /**
+   * Returns the set of full users that are currently active.
+   */
+    private Set<UserHandle> getActiveUsers() {
+        Set<UserHandle> profiles = new HashSet<>();
+        profiles.add(mContext.getUser());
+        profiles.add(mActivityManagerProxy.getCurrentUser());
+        return profiles;
+    }
+
     private static String printFeatures(Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
         StringBuilder featureString = new StringBuilder();
         featureString.append(" features: [");
@@ -1711,8 +1989,24 @@
     }
 
     /**
-     * Returns the ImsServiceInfo that matches the provided packageName. Visible for testing
-     * the ImsService caching functionality.
+     * Returns the ImsServiceInfo that matches the provided packageName if it belongs to a
+     * package that is visible as part of the set of active users.
+     */
+    public ImsServiceInfo getVisibleImsServiceInfoFromCache(String packageName) {
+        ImsServiceInfo match = getImsServiceInfoFromCache(packageName);
+        if (!mFeatureFlags.imsResolverUserAware()) {
+            return match;
+        }
+        if (match == null) return null;
+        UserHandle targetUser = getUserForBind(match);
+        Log.d(TAG, "getVisibleImsServiceInfoFromCache: " + packageName + ", match=" + match
+                + ", targetUser=" + targetUser);
+        if (targetUser != null) return match; else return null;
+    }
+
+    /**
+     * Returns the ImsServiceInfo that matches the provided packageName. This includes
+     * ImsServiceInfos that are not currently visible for the active users.
      */
     @VisibleForTesting
     public ImsServiceInfo getImsServiceInfoFromCache(String packageName) {
@@ -1738,6 +2032,12 @@
         return infos;
     }
 
+    private ImsServiceInfo getInfoFromCache(List<ImsServiceInfo> infos,
+            ComponentName componentName) {
+        return infos.stream().filter(info -> Objects.equals(info.name, componentName)).findFirst()
+                .orElse(null);
+    }
+
     private List<ImsServiceInfo> searchForImsServices(String packageName,
             ImsServiceControllerFactory controllerFactory) {
         List<ImsServiceInfo> infos = new ArrayList<>();
@@ -1745,62 +2045,84 @@
         Intent serviceIntent = new Intent(controllerFactory.getServiceInterface());
         serviceIntent.setPackage(packageName);
 
+        Set<UserHandle> profiles;
+        if (mFeatureFlags.imsResolverUserAware()) {
+            profiles = getActiveUsers();
+        } else {
+            profiles = Collections.singleton(mContext.getUser());
+        }
+        Log.v(TAG, "searchForImsServices: package=" + packageName + ", users=" + profiles);
+
         PackageManager packageManager = mContext.getPackageManager();
-        for (ResolveInfo entry : packageManager.queryIntentServicesAsUser(
-                serviceIntent,
-                PackageManager.GET_META_DATA,
-                UserHandle.of(UserHandle.myUserId()))) {
-            ServiceInfo serviceInfo = entry.serviceInfo;
+        for (UserHandle handle : profiles) {
+            for (ResolveInfo entry : packageManager.queryIntentServicesAsUser(serviceIntent,
+                    PackageManager.GET_META_DATA, handle)) {
+                ServiceInfo serviceInfo = entry.serviceInfo;
 
-            if (serviceInfo != null) {
-                ImsServiceInfo info = new ImsServiceInfo();
-                info.name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
-                info.controllerFactory = controllerFactory;
+                if (serviceInfo != null) {
+                    ComponentName name = new ComponentName(serviceInfo.packageName,
+                            serviceInfo.name);
+                    ImsServiceInfo info = getInfoFromCache(infos, name);
+                    if (info != null) {
+                        info.users.add(handle);
+                        Log.d(TAG, "service modify users:" + info);
+                        continue;
+                    } else {
+                        info = new ImsServiceInfo(name);
+                        info.users.add(handle);
+                    }
+                    info.controllerFactory = controllerFactory;
 
-                // we will allow the manifest method of declaring manifest features in two cases:
-                // 1) it is the device overlay "default" ImsService, where the features do not
-                // change (the new method can still be used if the default does not define manifest
-                // entries).
-                // 2) using the "compat" ImsService, which only supports manifest query.
-                if (isDeviceService(info)
-                        || mImsServiceControllerFactoryCompat == controllerFactory) {
-                    if (serviceInfo.metaData != null) {
-                        if (serviceInfo.metaData.getBoolean(METADATA_MMTEL_FEATURE, false)) {
-                            info.addFeatureForAllSlots(mNumSlots, ImsFeature.FEATURE_MMTEL);
-                            // only allow FEATURE_EMERGENCY_MMTEL if FEATURE_MMTEL is defined.
-                            if (serviceInfo.metaData.getBoolean(METADATA_EMERGENCY_MMTEL_FEATURE,
-                                    false)) {
-                                info.addFeatureForAllSlots(mNumSlots,
-                                        ImsFeature.FEATURE_EMERGENCY_MMTEL);
+                    // we will allow the manifest method of declaring manifest features in two
+                    // cases:
+
+                    // 1) it is the device overlay "default" ImsService, where the features do not
+                    // change (the new method can still be used if the default does not define
+                    // manifest entries).
+                    // 2) using the "compat" ImsService, which only supports manifest query.
+                    if (isDeviceService(info)
+                            || mImsServiceControllerFactoryCompat == controllerFactory) {
+                        if (serviceInfo.metaData != null) {
+                            if (serviceInfo.metaData.getBoolean(METADATA_MMTEL_FEATURE, false)) {
+                                info.addFeatureForAllSlots(mNumSlots, ImsFeature.FEATURE_MMTEL);
+                                // only allow FEATURE_EMERGENCY_MMTEL if FEATURE_MMTEL is defined.
+                                if (serviceInfo.metaData.getBoolean(
+                                        METADATA_EMERGENCY_MMTEL_FEATURE,
+                                        false)) {
+                                    info.addFeatureForAllSlots(mNumSlots,
+                                            ImsFeature.FEATURE_EMERGENCY_MMTEL);
+                                }
+                            }
+                            if (serviceInfo.metaData.getBoolean(METADATA_RCS_FEATURE, false)) {
+                                info.addFeatureForAllSlots(mNumSlots, ImsFeature.FEATURE_RCS);
                             }
                         }
-                        if (serviceInfo.metaData.getBoolean(METADATA_RCS_FEATURE, false)) {
-                            info.addFeatureForAllSlots(mNumSlots, ImsFeature.FEATURE_RCS);
+                        // Only dynamic query if we are not a compat version of ImsService and the
+                        // default service.
+                        if (mImsServiceControllerFactoryCompat != controllerFactory
+                                && info.getSupportedFeatures().isEmpty()) {
+                            // metadata empty, try dynamic query instead
+                            info.featureFromMetadata = false;
                         }
-                    }
-                    // Only dynamic query if we are not a compat version of ImsService and the
-                    // default service.
-                    if (mImsServiceControllerFactoryCompat != controllerFactory
-                            && info.getSupportedFeatures().isEmpty()) {
-                        // metadata empty, try dynamic query instead
+                    } else {
+                        // We are a carrier service and not using the compat version of ImsService.
                         info.featureFromMetadata = false;
                     }
-                } else {
-                    // We are a carrier service and not using the compat version of ImsService.
-                    info.featureFromMetadata = false;
-                }
-                Log.i(TAG, "service name: " + info.name + ", manifest query: "
-                        + info.featureFromMetadata);
-                // Check manifest permission to be sure that the service declares the correct
-                // permissions. Overridden if the METADATA_OVERRIDE_PERM_CHECK metadata is set to
-                // true.
-                // NOTE: METADATA_OVERRIDE_PERM_CHECK should only be set for testing.
-                if (TextUtils.equals(serviceInfo.permission, Manifest.permission.BIND_IMS_SERVICE)
-                        || serviceInfo.metaData.getBoolean(METADATA_OVERRIDE_PERM_CHECK, false)) {
-                    infos.add(info);
-                } else {
-                    Log.w(TAG, "ImsService is not protected with BIND_IMS_SERVICE permission: "
-                            + info.name);
+                    Log.d(TAG, "service name: " + info.name + ", manifest query: "
+                            + info.featureFromMetadata + ", users: " + info.users);
+                    // Check manifest permission to be sure that the service declares the correct
+                    // permissions. Overridden if the METADATA_OVERRIDE_PERM_CHECK metadata is set
+                    // to true.
+                    // NOTE: METADATA_OVERRIDE_PERM_CHECK should only be set for testing.
+                    if (TextUtils.equals(serviceInfo.permission,
+                            Manifest.permission.BIND_IMS_SERVICE)
+                            || serviceInfo.metaData.getBoolean(METADATA_OVERRIDE_PERM_CHECK,
+                            false)) {
+                        infos.add(info);
+                    } else {
+                        Log.w(TAG, "ImsService is not protected with BIND_IMS_SERVICE permission: "
+                                + info.name);
+                    }
                 }
             }
         }
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceController.java b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
index ea8399f..37c10eb 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceController.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
@@ -196,7 +196,7 @@
             }
             if (mCallbacks != null) {
                 // Will trigger an unbind.
-                mCallbacks.imsServiceBindPermanentError(getComponentName());
+                mCallbacks.imsServiceBindPermanentError(getComponentName(), mBoundUser);
             }
         }
 
@@ -217,7 +217,8 @@
         /**
          * Called by ImsServiceController when a new MMTEL or RCS feature has been created.
          */
-        void imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller);
+        void imsServiceFeatureCreated(int slotId, int subId, int feature,
+                ImsServiceController controller);
         /**
          * Called by ImsServiceController when a new MMTEL or RCS feature has been removed.
          */
@@ -234,7 +235,7 @@
          * Called by the ImsServiceController when there has been an error binding that is
          * not recoverable, such as the ImsService returning a null binder.
          */
-        void imsServiceBindPermanentError(ComponentName name);
+        void imsServiceBindPermanentError(ComponentName name, UserHandle user);
     }
 
     /**
@@ -273,6 +274,7 @@
 
     private boolean mIsBound = false;
     private boolean mIsBinding = false;
+    private UserHandle mBoundUser = null;
     // Set of a pair of slotId->feature
     private Set<ImsFeatureConfiguration.FeatureSlotPair> mImsFeatures;
     private SparseIntArray mSlotIdToSubIdMap;
@@ -337,7 +339,7 @@
                 if (mIsBound) {
                     return;
                 }
-                bind(mImsFeatures, mSlotIdToSubIdMap);
+                bind(mBoundUser, mImsFeatures, mSlotIdToSubIdMap);
             }
         }
     };
@@ -413,17 +415,18 @@
      * @return {@link true} if the service is in the process of being bound, {@link false} if it
      * has failed.
      */
-    public boolean bind(Set<ImsFeatureConfiguration.FeatureSlotPair> imsFeatureSet,
-            SparseIntArray  slotIdToSubIdMap) {
+    public boolean bind(UserHandle user, Set<ImsFeatureConfiguration.FeatureSlotPair> imsFeatureSet,
+            SparseIntArray slotIdToSubIdMap) {
         synchronized (mLock) {
             if (!mIsBound && !mIsBinding) {
                 mIsBinding = true;
+                mBoundUser = user;
                 sanitizeFeatureConfig(imsFeatureSet);
                 mImsFeatures = imsFeatureSet;
                 mSlotIdToSubIdMap = slotIdToSubIdMap;
                 // Set the number of slots that support the feature
                 mImsEnablementTracker.setNumOfSlots(mSlotIdToSubIdMap.size());
-                grantPermissionsToService();
+                grantPermissionsToService(user);
                 Intent imsServiceIntent = new Intent(getServiceInterface()).setComponent(
                         mComponentName);
                 mImsServiceConnection = new ImsServiceConnection();
@@ -432,8 +435,8 @@
                 mLocalLog.log("binding " + imsFeatureSet);
                 Log.i(LOG_TAG, "Binding ImsService:" + mComponentName);
                 try {
-                    boolean bindSucceeded = mContext.bindService(imsServiceIntent,
-                            mImsServiceConnection, serviceFlags);
+                    boolean bindSucceeded = mContext.bindServiceAsUser(imsServiceIntent,
+                            mImsServiceConnection, serviceFlags, user);
                     if (!bindSucceeded) {
                         mLocalLog.log("    binding failed, retrying in "
                                 + mBackoff.getCurrentDelay() + " mS");
@@ -482,6 +485,7 @@
             changeImsServiceFeatures(new HashSet<>(), mSlotIdToSubIdMap);
             mIsBound = false;
             mIsBinding = false;
+            mBoundUser = null;
             setServiceController(null);
             unbindService();
         }
@@ -608,6 +612,13 @@
     }
 
     /**
+     * @return The UserHandle that this controller is bound to or null if bound to no service.
+     */
+    public UserHandle getBoundUser() {
+        return mBoundUser;
+    }
+
+    /**
      * Notify ImsService to enable IMS for the framework. This will trigger IMS registration and
      * trigger ImsFeature status updates.
      */
@@ -766,7 +777,7 @@
 
     // Grant runtime permissions to ImsService. PermissionManager ensures that the ImsService is
     // system/signed before granting permissions.
-    private void grantPermissionsToService() {
+    private void grantPermissionsToService(UserHandle user) {
         mLocalLog.log("grant permissions to " + getComponentName());
         Log.i(LOG_TAG, "Granting Runtime permissions to:" + getComponentName());
         String[] pkgToGrant = {mComponentName.getPackageName()};
@@ -774,8 +785,7 @@
             if (mPermissionManager != null) {
                 CountDownLatch latch = new CountDownLatch(1);
                 mPermissionManager.grantDefaultPermissionsToEnabledImsServices(
-                        pkgToGrant, UserHandle.of(UserHandle.myUserId()), Runnable::run,
-                        isSuccess -> {
+                        pkgToGrant, user, Runnable::run, isSuccess -> {
                             if (isSuccess) {
                                 latch.countDown();
                             } else {
@@ -807,7 +817,8 @@
             Log.i(LOG_TAG, "supports emergency calling on slot " + featurePair.slotId);
         }
         // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
-        mCallbacks.imsServiceFeatureCreated(featurePair.slotId, featurePair.featureType, this);
+        mCallbacks.imsServiceFeatureCreated(featurePair.slotId, subId, featurePair.featureType,
+                this);
     }
 
     // This method should only be called when synchronized on mLock
@@ -978,10 +989,11 @@
     @Override
     public String toString() {
         synchronized (mLock) {
-            return "[ImsServiceController: componentName=" + getComponentName() + ", features="
-                    + mImsFeatures + ", isBinding=" + mIsBinding + ", isBound=" + mIsBound
-                    + ", serviceController=" + getImsServiceController() + ", rebindDelay="
-                    + getRebindDelay() + "]";
+            return "[ImsServiceController: componentName=" + getComponentName() + ", boundUser="
+                    + mBoundUser + ", features=" + mImsFeatures + ", isBinding=" + mIsBinding
+                    + ", isBound=" + mIsBound + ", serviceController=" + getImsServiceController()
+                    + ", rebindDelay=" + getRebindDelay() + ", slotToSubIdMap=" + mSlotIdToSubIdMap
+                    + "]";
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java b/src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java
index 564cdcc..a4b4f46 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceFeatureQueryManager.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.os.IBinder;
+import android.os.UserHandle;
 import android.telephony.ims.aidl.IImsServiceController;
 import android.telephony.ims.stub.ImsFeatureConfiguration;
 import android.util.Log;
@@ -42,14 +43,16 @@
         private static final String LOG_TAG = "ImsServiceFeatureQuery";
 
         private final ComponentName mName;
+        private final UserHandle mUser;
         private final String mIntentFilter;
         // Track the status of whether or not the Service has died in case we need to permanently
         // unbind (see onNullBinding below).
         private boolean mIsServiceConnectionDead = false;
 
 
-        ImsServiceFeatureQuery(ComponentName name, String intentFilter) {
+        ImsServiceFeatureQuery(ComponentName name, UserHandle user, String intentFilter) {
             mName = name;
+            mUser = user;
             mIntentFilter = intentFilter;
         }
 
@@ -62,7 +65,8 @@
             Intent imsServiceIntent = new Intent(mIntentFilter).setComponent(mName);
             int serviceFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
                     | Context.BIND_IMPORTANT;
-            boolean bindStarted = mContext.bindService(imsServiceIntent, this, serviceFlags);
+            boolean bindStarted = mContext.bindServiceAsUser(imsServiceIntent, this,
+                    serviceFlags, mUser);
             if (!bindStarted) {
                 // Docs say to unbind if this fails.
                 cleanup();
@@ -78,7 +82,7 @@
             } else {
                 Log.w(LOG_TAG, "onServiceConnected: " + name + " binder null.");
                 cleanup();
-                mListener.onPermanentError(name);
+                mListener.onPermanentError(name, mUser);
             }
         }
 
@@ -103,7 +107,7 @@
             // permanently unbind and instead let the automatic rebind occur.
             if (mIsServiceConnectionDead) return;
             cleanup();
-            mListener.onPermanentError(name);
+            mListener.onPermanentError(name, mUser);
         }
 
         private void queryImsFeatures(IImsServiceController controller) {
@@ -154,7 +158,7 @@
         /**
          * Called when a query has failed due to a permanent error and should not be retried.
          */
-        void onPermanentError(ComponentName name);
+        void onPermanentError(ComponentName name, UserHandle user);
     }
 
     // Maps an active ImsService query (by Package Name String) its query.
@@ -171,16 +175,17 @@
     /**
      * Starts an ImsService feature query for the ComponentName and Intent specified.
      * @param name The ComponentName of the ImsService being queried.
+     * @param user The User associated with the request.
      * @param intentFilter The Intent filter that the ImsService specified.
      * @return true if the query started, false if it was unable to start.
      */
-    public boolean startQuery(ComponentName name, String intentFilter) {
+    public boolean startQuery(ComponentName name, UserHandle user, String intentFilter) {
         synchronized (mLock) {
             if (mActiveQueries.containsKey(name)) {
                 // We already have an active query, wait for it to return.
                 return true;
             }
-            ImsServiceFeatureQuery query = new ImsServiceFeatureQuery(name, intentFilter);
+            ImsServiceFeatureQuery query = new ImsServiceFeatureQuery(name, user, intentFilter);
             mActiveQueries.put(name, query);
             return query.start();
         }
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
index 1517064..07530d2 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramDispatcher.java
@@ -625,6 +625,13 @@
                     pendingDatagram.iterator().next().getValue();
             if (mDatagramController.needsWaitingForSatelliteConnected(datagramArg.datagramType)) {
                 plogd("sendPendingDatagrams: wait for satellite connected");
+                mDatagramController.updateSendStatus(datagramArg.subId,
+                        datagramArg.datagramType,
+                        SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT,
+                        getPendingMessagesCount(),
+                        SatelliteManager.SATELLITE_RESULT_SUCCESS);
+                startDatagramWaitForConnectedStateTimer(
+                        datagramArg.datagramType);
                 return;
             }
 
@@ -1144,16 +1151,23 @@
         }
 
         if (pendingSms != null && pendingSms.iterator().hasNext()) {
+            PendingRequest pendingRequest = pendingSms.iterator().next().getValue();
+            int datagramType = pendingRequest.isMtSmsPolling
+                    ? DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS : DATAGRAM_TYPE_SMS;
             if (mDatagramController.needsWaitingForSatelliteConnected(DATAGRAM_TYPE_SMS)) {
                 plogd("sendPendingSms: wait for satellite connected");
+                mDatagramController.updateSendStatus(subId,
+                        datagramType,
+                        SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT,
+                        getPendingMessagesCount(),
+                        SatelliteManager.SATELLITE_RESULT_SUCCESS);
+                startDatagramWaitForConnectedStateTimer(datagramType);
                 return;
             }
 
             mSendingInProgress = true;
-            PendingRequest pendingRequest = pendingSms.iterator().next().getValue();
             mDatagramController.updateSendStatus(subId,
-                    pendingRequest.isMtSmsPolling ?
-                            DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS : DATAGRAM_TYPE_SMS,
+                    datagramType,
                     SatelliteManager.SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
                     getPendingMessagesCount(), SATELLITE_RESULT_SUCCESS);
             sendMessage(obtainMessage(CMD_SEND_SMS, pendingRequest));
@@ -1257,8 +1271,10 @@
     }
 
     private boolean shouldPollMtSms() {
+        SatelliteController satelliteController = SatelliteController.getInstance();
+        Phone satellitePhone = satelliteController.getSatellitePhone();
         return isEnabledMtSmsPolling()
-                && SatelliteController.getInstance().isInCarrierRoamingNbIotNtn();
+                && satelliteController.shouldSendSmsToDatagramDispatcher(satellitePhone);
     }
 
     @GuardedBy("mLock")
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteController.java b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
index f39e9c3..a23e505 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -22,6 +22,7 @@
 import static android.telephony.CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC;
 import static android.telephony.CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_MANUAL;
 import static android.telephony.CarrierConfigManager.CARRIER_ROAMING_NTN_CONNECT_TYPE;
+import static android.telephony.CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL;
 import static android.telephony.CarrierConfigManager.KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT;
 import static android.telephony.CarrierConfigManager.KEY_CARRIER_ROAMING_NTN_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_INT;
 import static android.telephony.CarrierConfigManager.KEY_CARRIER_ROAMING_SATELLITE_DEFAULT_SERVICES_INT_ARRAY;
@@ -33,13 +34,14 @@
 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_ESOS_SUPPORTED_BOOL;
-import static android.telephony.CarrierConfigManager.KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_SUPPORTED_MSG_APPS_STRING_ARRAY;
 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_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.CarrierConfigManager.KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE;
 import static android.telephony.SubscriptionManager.SATELLITE_ATTACH_ENABLED_FOR_CARRIER;
 import static android.telephony.SubscriptionManager.SATELLITE_ENTITLEMENT_STATUS;
 import static android.telephony.SubscriptionManager.isValidSubscriptionId;
@@ -62,6 +64,7 @@
 import android.annotation.ArrayRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.AlertDialog;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
@@ -77,6 +80,7 @@
 import android.content.SharedPreferences;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.hardware.devicestate.DeviceState;
@@ -110,6 +114,7 @@
 import android.provider.Telephony;
 import android.telecom.TelecomManager;
 import android.telephony.AccessNetworkConstants;
+import android.telephony.AnomalyReporter;
 import android.telephony.CarrierConfigManager;
 import android.telephony.DropBoxManagerLoggerBackend;
 import android.telephony.NetworkRegistrationInfo;
@@ -119,6 +124,7 @@
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.TelephonyRegistryManager;
 import android.telephony.satellite.INtnSignalStrengthCallback;
 import android.telephony.satellite.ISatelliteCapabilitiesCallback;
 import android.telephony.satellite.ISatelliteDatagramCallback;
@@ -140,6 +146,7 @@
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.uwb.UwbManager;
+import android.view.WindowManager;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
@@ -161,6 +168,7 @@
 import com.android.internal.telephony.satellite.metrics.SessionMetricsStats;
 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
+import com.android.internal.telephony.util.ArrayUtils;
 import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.internal.util.FunctionalUtils;
 
@@ -174,6 +182,7 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.TreeMap;
+import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
@@ -213,6 +222,11 @@
     /** 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";
+    /** Key used to read/write default messages application NTN SMS support
+     * in shared preferences. */
+    @VisibleForTesting(visibility =  VisibleForTesting.Visibility.PRIVATE)
+    public static final String NTN_SMS_SUPPORTED_BY_MESSAGES_APP_KEY =
+            "ntn_sms_supported_by_messages_app_key";
 
     public static final long DEFAULT_CARRIER_EMERGENCY_CALL_WAIT_FOR_CONNECTION_TIMEOUT_MILLIS =
             TimeUnit.SECONDS.toMillis(30);
@@ -221,6 +235,14 @@
     private static final long WAIT_FOR_REPORT_ENTITLED_MERTICS_TIMEOUT_MILLIS =
             TimeUnit.HOURS.toMillis(23);
 
+    /**
+     * Delay SatelliteEnable request when network selection auto. current RIL not verified to
+     * response right after network selection auto changed. Some RIL has delay for waiting in-svc
+     * with Automatic selection request.
+     */
+    private static final long DELAY_WAITING_SET_NETWORK_SELECTION_AUTO_MILLIS =
+            TimeUnit.SECONDS.toMillis(1);
+
     /** 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;
@@ -274,6 +296,8 @@
     private static final int EVENT_WAIT_FOR_REPORT_ENTITLED_TO_MERTICS_HYSTERESIS_TIMED_OUT = 53;
     protected static final int EVENT_SATELLITE_REGISTRATION_FAILURE = 54;
     private static final int EVENT_TERRESTRIAL_NETWORK_AVAILABLE_CHANGED = 55;
+    private static final int EVENT_SET_NETWORK_SELECTION_AUTO_DONE = 56;
+    private static final int EVENT_SIGNAL_STRENGTH_CHANGED = 57;
 
     @NonNull private static SatelliteController sInstance;
     @NonNull private final Context mContext;
@@ -499,6 +523,10 @@
     @NonNull private final Map<Integer, List<Integer>>
             mSatModeCapabilitiesForCarrierRoaming = new HashMap<>();
 
+    @GuardedBy("mSatelliteConnectedLock")
+    private SparseArray<NtnSignalStrength> mLastNotifiedCarrierRoamingNtnSignalStrength =
+            new SparseArray<>();
+
     /**
      * This is used for testing only. When mEnforcedEmergencyCallToSatelliteHandoverType is valid,
      * Telephony will ignore the IMS registration status and cellular availability, and always send
@@ -540,6 +568,11 @@
     @GuardedBy("mSatelliteTokenProvisionedLock")
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     protected TreeMap<Integer, List<SubscriptionInfo>> mSubsInfoListPerPriority = new TreeMap<>();
+    // List of subscriber information and status at the time of last evaluation
+    @GuardedBy("mSatelliteTokenProvisionedLock")
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    private List<SatelliteSubscriberProvisionStatus> mLastEvaluatedSubscriberProvisionStatus =
+            new ArrayList<>();
     // The ID of the satellite subscription that has highest priority and is provisioned.
     @GuardedBy("mSatelliteTokenProvisionedLock")
     private int mSelectedSatelliteSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -557,6 +590,7 @@
     private long mLastEmergencyCallTime;
     private long mSatelliteEmergencyModeDurationMillis;
     private static final int DEFAULT_SATELLITE_EMERGENCY_MODE_DURATION_SECONDS = 300;
+    private AlertDialog mNetworkSelectionModeAutoDialog = null;
 
     /** Key used to read/write satellite system notification done in shared preferences. */
     private static final String SATELLITE_SYSTEM_NOTIFICATION_DONE_KEY =
@@ -598,6 +632,10 @@
     private AtomicBoolean mOverrideNtnEligibility;
     private String mDefaultSmsPackageName = "";
     private String mSatelliteGatewayServicePackageName = "";
+
+    private final Object mNtnSmsSupportedByMessagesAppLock = new Object();
+    @GuardedBy("mNtnSmsSupportedByMessagesAppLock")
+    private Boolean mNtnSmsSupportedByMessagesApp = null;
     private BroadcastReceiver
             mDefaultSmsSubscriptionChangedBroadcastReceiver = new BroadcastReceiver() {
                 @Override
@@ -640,6 +678,79 @@
     // device.
     private List<DeviceState> mDeviceStates = new ArrayList();
 
+    public static final int RESULT_RECEIVER_COUNT_ANOMALY_THRESHOLD = 100;
+    protected final Object mResultReceiverTotalCountLock = new Object();
+    @GuardedBy("mResultReceiverTotalCountLock")
+    protected int mResultReceiverTotalCount;
+    @GuardedBy("mResultReceiverTotalCountLock")
+    protected HashMap<String, Integer> mResultReceiverCountPerMethodMap = new HashMap<>();
+
+    // Satellite anomaly uuid -- ResultReceiver count threshold exceeded
+    private final UUID mAnomalyUnexpectedResultReceiverCountUUID =
+            UUID.fromString("e268f22d-9bba-4d27-b76a-1c7f5b42e241");
+
+    private UUID generateAnomalyUnexpectedResultReceiverCountUUID(int error, int errorCode) {
+        long lerror = error;
+        long lerrorCode = errorCode;
+        return new UUID(mAnomalyUnexpectedResultReceiverCountUUID.getMostSignificantBits(),
+                mAnomalyUnexpectedResultReceiverCountUUID.getLeastSignificantBits()
+                        + ((lerrorCode << 32) + lerror));
+    }
+
+    /**
+     * Increments the ResultReceiver count and logs the caller information.
+     * If the count exceeds the threshold, it reports an anomaly via AnomalyReporter.
+     *
+     * @param caller The caller information that created the ResultReceiver
+     *               (e.g., class name and method name)
+     */
+    public void incrementResultReceiverCount(String caller) {
+        if (mFeatureFlags.geofenceEnhancementForBetterUx()) {
+            synchronized (mResultReceiverTotalCountLock) {
+                mResultReceiverTotalCount++;
+                logd("[incrementResultReceiverCount] : " + caller
+                        + " | ResultReceiver total count= " + mResultReceiverTotalCount);
+                mResultReceiverCountPerMethodMap.compute(caller, (k, v) -> v == null ? 1 : v + 1);
+
+                if (mResultReceiverTotalCount > RESULT_RECEIVER_COUNT_ANOMALY_THRESHOLD) {
+                    loge("[mResultReceiverTotalCount] is exceeds limits : "
+                            + mResultReceiverTotalCount);
+                    loge("[incrementResultReceiverCount] mResultReceiverCountPerMethodMap is "
+                            + mResultReceiverCountPerMethodMap);
+                    AnomalyReporter.reportAnomaly(
+                            generateAnomalyUnexpectedResultReceiverCountUUID(0, 0),
+                            "Satellite ResultReceiver total count= "
+                                    + mResultReceiverTotalCount + " exceeds limit.");
+                }
+            }
+        } else {
+            logd("[incrementResultReceiverCount]: geofenceEnhancementForBetterUx is not enabled");
+        }
+    }
+
+    /**
+     * Decrements the ResultReceiver count and logs the caller information.
+     * Prevents the count from going below zero.
+     *
+     * @param caller The caller information that released the ResultReceiver
+     *               (e.g., class name and method name)
+     */
+    public void decrementResultReceiverCount(String caller) {
+        if (mFeatureFlags.geofenceEnhancementForBetterUx()) {
+            synchronized (mResultReceiverTotalCountLock) {
+                if (mResultReceiverTotalCount > 0) {
+                    mResultReceiverTotalCount--;
+                }
+                logd("[decrementResultReceiverCount] : " + caller
+                        + " | ResultReceiver total count=" + mResultReceiverTotalCount);
+                mResultReceiverCountPerMethodMap.computeIfPresent(caller,
+                        (k, v) -> v > 0 ? v - 1 : v);
+            }
+        } else {
+            logd("[decrementResultReceiverCount]: geofenceEnhancementForBetterUx is not enabled");
+        }
+    }
+
     /**
      * @return The singleton instance of SatelliteController.
      */
@@ -724,6 +835,7 @@
         registerForPendingDatagramCount();
         registerForSatelliteModemStateChanged();
         registerForServiceStateChanged();
+        registerForSignalStrengthChanged();
         mContentResolver = mContext.getContentResolver();
         mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
 
@@ -768,7 +880,15 @@
 
         mDSM.registerForSignalStrengthReportDecision(this, CMD_UPDATE_NTN_SIGNAL_STRENGTH_REPORTING,
                 null);
+
         loadSatelliteSharedPreferences();
+        if (mSharedPreferences != null) {
+            synchronized (mNtnSmsSupportedByMessagesAppLock) {
+                mNtnSmsSupportedByMessagesApp = mSharedPreferences.getBoolean(
+                        NTN_SMS_SUPPORTED_BY_MESSAGES_APP_KEY, false);
+            }
+        }
+
         mWaitTimeForSatelliteEnablingResponse = getWaitForSatelliteEnablingResponseTimeoutMillis();
         mDemoPointingAlignedDurationMillis = getDemoPointingAlignedDurationMillisFromResources();
         mDemoPointingNotAlignedDurationMillis =
@@ -1280,8 +1400,8 @@
                     // If Satellite enable/disable request returned Error, no need to wait for radio
                     argument.callback.accept(error);
                 }
-
                 if (argument.enableSatellite) {
+                    mSessionMetricsStats.resetSessionStatsShadowCounters();
                     mSessionMetricsStats.setInitializationResult(error)
                             .setSatelliteTechnology(getSupportedNtnRadioTechnology())
                             .setInitializationProcessingTime(
@@ -1438,6 +1558,7 @@
                     updateSatelliteSupportedState(false);
                 }
                 ((ResultReceiver) request.argument).send(error, bundle);
+                decrementResultReceiverCount("SC:requestIsSatelliteEnabled");
                 break;
             }
 
@@ -1532,6 +1653,7 @@
                     }
                 }
                 ((ResultReceiver) request.argument).send(error, bundle);
+                decrementResultReceiverCount("SC:requestTimeForNextSatelliteVisibility");
                 break;
             }
 
@@ -1557,16 +1679,19 @@
                     if (mSatelliteModemInterface.isSatelliteServiceConnected()) {
                         synchronized (mIsSatelliteSupportedLock) {
                             if (mIsSatelliteSupported == null || !mIsSatelliteSupported) {
+                                final String caller = "SC:CMD_IS_SATELLITE_SUPPORTED";
                                 ResultReceiver receiver = new ResultReceiver(this) {
                                     @Override
                                     protected void onReceiveResult(
                                             int resultCode, Bundle resultData) {
+                                        decrementResultReceiverCount(caller);
                                         plogd("onRadioStateChanged.requestIsSatelliteSupported: "
                                                 + "resultCode=" + resultCode
                                                 + ", resultData=" + resultData);
                                     }
                                 };
                                 sendRequestAsync(CMD_IS_SATELLITE_SUPPORTED, receiver, null);
+                                incrementResultReceiverCount(caller);
                             }
                         }
                     }
@@ -1605,6 +1730,7 @@
                     ploge("EVENT_SATELLITE_MODEM_STATE_CHANGED: result is null");
                 } else {
                     handleEventSatelliteModemStateChanged((int) ar.result);
+                    updateLastNotifiedCarrierRoamingNtnSignalStrengthAndNotify(getSatellitePhone());
                 }
                 break;
 
@@ -1684,6 +1810,7 @@
                     }
                     result.send(errorCode, null);
                 }
+                decrementResultReceiverCount("SC:requestNtnSignalStrength");
                 break;
             }
 
@@ -1693,6 +1820,7 @@
                     ploge("EVENT_NTN_SIGNAL_STRENGTH_CHANGED: result is null");
                 } else {
                     handleEventNtnSignalStrengthChanged((NtnSignalStrength) ar.result);
+                    updateLastNotifiedCarrierRoamingNtnSignalStrengthAndNotify(getSatellitePhone());
                 }
                 break;
             }
@@ -1794,7 +1922,7 @@
                 onCompleted = obtainMessage(EVENT_UPDATE_PROVISION_SATELLITE_TOKEN_DONE, request);
                 boolean provisionChanged = updateSatelliteSubscriptionProvisionState(
                         argument.mSatelliteSubscriberInfoList, argument.mProvisioned);
-                selectBindingSatelliteSubscription();
+                selectBindingSatelliteSubscription(false);
                 int subId = getSelectedSatelliteSubId();
                 SubscriptionInfo subscriptionInfo =
                     mSubscriptionManagerService.getSubscriptionInfo(subId);
@@ -1823,6 +1951,7 @@
                         argument.mProvisioned ? SatelliteManager.KEY_PROVISION_SATELLITE_TOKENS
                                 : SatelliteManager.KEY_DEPROVISION_SATELLITE_TOKENS, true);
                 argument.mResult.send(SATELLITE_RESULT_SUCCESS, bundle);
+                decrementResultReceiverCount("SC:provisionSatellite");
                 break;
             }
 
@@ -1849,7 +1978,7 @@
                     plogd("EVENT_WIFI_CONNECTIVITY_STATE_CHANGED: mIsWifiConnected="
                             + mIsWifiConnected);
                 }
-                handleStateChangedForCarrierRoamingNtnEligibility();
+                evaluateCarrierRoamingNtnEligibilityChange();
                 break;
             }
             case EVENT_SATELLITE_ACCESS_RESTRICTION_CHECKING_RESULT: {
@@ -1897,6 +2026,21 @@
                 }
                 break;
 
+            case EVENT_SET_NETWORK_SELECTION_AUTO_DONE: {
+                logd("EVENT_SET_NETWORK_SELECTION_AUTO_DONE");
+                RequestSatelliteEnabledArgument argument =
+                        (RequestSatelliteEnabledArgument) msg.obj;
+                sendRequestAsync(CMD_SET_SATELLITE_ENABLED, argument, null);
+                break;
+            }
+
+            case EVENT_SIGNAL_STRENGTH_CHANGED: {
+                ar = (AsyncResult) msg.obj;
+                int phoneId = (int) ar.userObj;
+                updateLastNotifiedCarrierRoamingNtnSignalStrengthAndNotify(
+                        PhoneFactory.getPhone(phoneId));
+            }
+
             default:
                 Log.w(TAG, "SatelliteControllerHandler: unexpected message code: " +
                         msg.what);
@@ -2012,6 +2156,21 @@
          *      4. ongoing request = enable, current request = disable: send request to modem
          */
         synchronized (mSatelliteEnabledRequestLock) {
+            if (mFeatureFlags.carrierRoamingNbIotNtn()) {
+                if (mSatelliteEnabledRequest != null && mNetworkSelectionModeAutoDialog != null
+                        && mNetworkSelectionModeAutoDialog.isShowing()
+                        && request.isEmergency && request.enableSatellite) {
+                    synchronized (mSatellitePhoneLock) {
+                        sendErrorAndReportSessionMetrics(
+                                SatelliteManager.SATELLITE_RESULT_ILLEGAL_STATE,
+                                FunctionalUtils.ignoreRemoteException(
+                                        mSatelliteEnabledRequest.callback::accept));
+                    }
+                    mSatelliteEnabledRequest = null;
+                    mNetworkSelectionModeAutoDialog.dismiss();
+                    mNetworkSelectionModeAutoDialog = null;
+                }
+            }
             if (!isSatelliteEnabledRequestInProgress()) {
                 synchronized (mIsSatelliteEnabledLock) {
                     if (mIsSatelliteEnabled != null && mIsSatelliteEnabled == enableSatellite) {
@@ -2074,7 +2233,87 @@
                 }
             }
         }
-        sendRequestAsync(CMD_SET_SATELLITE_ENABLED, request, null);
+
+        if (mFeatureFlags.carrierRoamingNbIotNtn()) {
+            Phone satellitePhone = getSatellitePhone();
+            if (enableSatellite && satellitePhone != null
+                    && satellitePhone.getServiceStateTracker() != null
+                    && satellitePhone.getServiceStateTracker().getServiceState()
+                    .getIsManualSelection()) {
+                checkNetworkSelectionModeAuto(request);
+            } else {
+                sendRequestAsync(CMD_SET_SATELLITE_ENABLED, request, null);
+            }
+        } else {
+            sendRequestAsync(CMD_SET_SATELLITE_ENABLED, request, null);
+        }
+    }
+
+    private void checkNetworkSelectionModeAuto(RequestSatelliteEnabledArgument argument) {
+        plogd("checkNetworkSelectionModeAuto");
+        if (argument.isEmergency) {
+            // ESOS
+            getSatellitePhone().setNetworkSelectionModeAutomatic(null);
+            sendMessageDelayed(obtainMessage(EVENT_SET_NETWORK_SELECTION_AUTO_DONE, argument),
+                    DELAY_WAITING_SET_NETWORK_SELECTION_AUTO_MILLIS);
+        } else {
+            // P2P
+            if (mNetworkSelectionModeAutoDialog != null
+                    && mNetworkSelectionModeAutoDialog.isShowing()) {
+                logd("requestSatelliteEnabled: already auto network selection mode popup showing");
+                sendErrorAndReportSessionMetrics(
+                        SatelliteManager.SATELLITE_RESULT_REQUEST_IN_PROGRESS,
+                        FunctionalUtils.ignoreRemoteException(argument.callback::accept));
+                return;
+            }
+            logd("requestSatelliteEnabled: auto network selection mode popup");
+            Configuration configuration = Resources.getSystem().getConfiguration();
+            boolean nightMode = (configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK)
+                    == Configuration.UI_MODE_NIGHT_YES;
+
+            AlertDialog.Builder builder = new AlertDialog.Builder(mContext, nightMode
+                    ? AlertDialog.THEME_DEVICE_DEFAULT_DARK
+                    : AlertDialog.THEME_DEVICE_DEFAULT_LIGHT);
+
+            String title = mContext.getResources().getString(
+                    R.string.satellite_manual_selection_state_popup_title);
+            String message = mContext.getResources().getString(
+                    R.string.satellite_manual_selection_state_popup_message);
+            String ok = mContext.getResources().getString(
+                    R.string.satellite_manual_selection_state_popup_ok);
+            String cancel = mContext.getResources().getString(
+                    R.string.satellite_manual_selection_state_popup_cancel);
+
+            builder.setTitle(title).setMessage(message)
+                    .setPositiveButton(ok, (dialog, which) -> {
+                        logd("checkNetworkSelectionModeAuto: setPositiveButton");
+                        getSatellitePhone().setNetworkSelectionModeAutomatic(null);
+                        sendMessageDelayed(obtainMessage(EVENT_SET_NETWORK_SELECTION_AUTO_DONE,
+                                argument), DELAY_WAITING_SET_NETWORK_SELECTION_AUTO_MILLIS);
+                    })
+                    .setNegativeButton(cancel, (dialog, which) -> {
+                        logd("checkNetworkSelectionModeAuto: setNegativeButton");
+                        synchronized (mSatelliteEnabledRequestLock) {
+                            mSatelliteEnabledRequest = null;
+                        }
+                        sendErrorAndReportSessionMetrics(
+                                SatelliteManager.SATELLITE_RESULT_ILLEGAL_STATE,
+                                FunctionalUtils.ignoreRemoteException(argument.callback::accept));
+                    })
+                    .setOnCancelListener(dialog -> {
+                        logd("checkNetworkSelectionModeAuto: setOnCancelListener");
+                        synchronized (mSatelliteEnabledRequestLock) {
+                            mSatelliteEnabledRequest = null;
+                        }
+                        sendErrorAndReportSessionMetrics(
+                                SatelliteManager.SATELLITE_RESULT_ILLEGAL_STATE,
+                                FunctionalUtils.ignoreRemoteException(argument.callback::accept));
+                    });
+            mNetworkSelectionModeAutoDialog = builder.create();
+            mNetworkSelectionModeAutoDialog.getWindow()
+                    .setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+            mNetworkSelectionModeAutoDialog.show();
+        }
     }
 
     /**
@@ -2170,6 +2409,7 @@
         }
 
         sendRequestAsync(CMD_IS_SATELLITE_ENABLED, result, null);
+        incrementResultReceiverCount("SC:requestIsSatelliteEnabled");
     }
 
     /**
@@ -2331,6 +2571,7 @@
         synchronized (mSatelliteCapabilitiesLock) {
             if (mSatelliteCapabilities != null) {
                 Bundle bundle = new Bundle();
+                overrideSatelliteCapabilitiesIfApplicable();
                 bundle.putParcelable(SatelliteManager.KEY_SATELLITE_CAPABILITIES,
                         mSatelliteCapabilities);
                 result.send(SATELLITE_RESULT_SUCCESS, bundle);
@@ -2513,11 +2754,6 @@
      */
     @SatelliteManager.SatelliteResult public int registerForSatelliteProvisionStateChanged(
             @NonNull ISatelliteProvisionStateCallback callback) {
-        int error = evaluateOemSatelliteRequestAllowed(false);
-        if (error != SATELLITE_RESULT_SUCCESS) {
-            return error;
-        }
-
         mSatelliteProvisionStateChangedListeners.put(callback.asBinder(), callback);
 
         boolean isProvisioned = Boolean.TRUE.equals(isDeviceProvisioned());
@@ -2576,6 +2812,7 @@
         }
 
         sendRequestAsync(CMD_IS_SATELLITE_PROVISIONED, result, null);
+        incrementResultReceiverCount("SC:requestIsSatelliteProvisioned");
     }
 
     /**
@@ -2751,6 +2988,7 @@
         }
 
         sendRequestAsync(CMD_GET_TIME_SATELLITE_NEXT_VISIBLE, result, null);
+        incrementResultReceiverCount("SC:requestTimeForNextSatelliteVisibility");
     }
 
     /**
@@ -2903,6 +3141,7 @@
 
         Phone phone = SatelliteServiceUtils.getPhone();
         sendRequestAsync(CMD_REQUEST_NTN_SIGNAL_STRENGTH, result, phone);
+        incrementResultReceiverCount("SC:requestNtnSignalStrength");
     }
 
     /**
@@ -3315,15 +3554,18 @@
             plogd("onSatelliteServiceConnected");
             // Vendor service might have just come back from a crash
             moveSatelliteToOffStateAndCleanUpResources(SATELLITE_RESULT_MODEM_ERROR);
+            final String caller = "SC:onSatelliteServiceConnected";
             ResultReceiver receiver = new ResultReceiver(this) {
                 @Override
                 protected void onReceiveResult(
                         int resultCode, Bundle resultData) {
+                    decrementResultReceiverCount(caller);
                     plogd("onSatelliteServiceConnected.requestIsSatelliteSupported:"
                             + " resultCode=" + resultCode);
                 }
             };
             requestIsSatelliteSupported(receiver);
+            incrementResultReceiverCount(caller);
         } else {
             plogd("onSatelliteServiceConnected: Satellite vendor service is not supported."
                     + " Ignored the event");
@@ -3687,31 +3929,14 @@
      * else {@return false}
      */
     public boolean isInCarrierRoamingNbIotNtn() {
-        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;
-        }
-
-        Phone satellitePhone = getSatellitePhone();
-        if (!isCarrierRoamingNtnEligible(satellitePhone)) {
-            plogd("isInCarrierRoamingNbIotNtn: not carrier roaming ntn eligible.");
-            return false;
-        }
-        plogd("isInCarrierRoamingNbIotNtn: carrier roaming ntn eligible.");
-        return true;
+        return isInCarrierRoamingNbIotNtn(getSatellitePhone());
     }
 
     /**
      * @return {@code true} if phone is in carrier roaming nb iot ntn mode,
      * else {@return false}
      */
-    public boolean isInCarrierRoamingNbIotNtn(@NonNull Phone phone) {
+    private boolean isInCarrierRoamingNbIotNtn(@Nullable Phone phone) {
         if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
             plogd("isInCarrierRoamingNbIotNtn: carrier roaming nb iot ntn "
                     + "feature flag is disabled");
@@ -3723,12 +3948,31 @@
             return false;
         }
 
-        if (!isCarrierRoamingNtnEligible(phone)) {
-            plogd("isInCarrierRoamingNbIotNtn: phone associated with subId "
-                      + phone.getSubId()
-                      + " is not carrier roaming ntn eligible.");
+        if (phone == null) {
+            plogd("isInCarrierRoamingNbIotNtn: phone is null");
             return false;
         }
+
+        int subId = phone.getSubId();
+        if (!isSatelliteSupportedViaCarrier(subId)) {
+            plogd("isInCarrierRoamingNbIotNtn[phoneId=" + phone.getPhoneId()
+                    + "]: satellite is not supported via carrier");
+            return false;
+        }
+
+        int carrierRoamingNtnConnectType = getCarrierRoamingNtnConnectType(subId);
+        if (carrierRoamingNtnConnectType != CARRIER_ROAMING_NTN_CONNECT_MANUAL) {
+            plogd("isInCarrierRoamingNbIotNtn[phoneId=" + phone.getPhoneId() + "]: not manual "
+                    + "connect. carrierRoamingNtnConnectType = " + carrierRoamingNtnConnectType);
+            return false;
+        }
+
+        if (subId != getSelectedSatelliteSubId()) {
+            plogd("isInCarrierRoamingNbIotNtn: subId=" + subId
+                    + " does not match satellite subId=" + getSelectedSatelliteSubId());
+            return false;
+        }
+
         plogd("isInCarrierRoamingNbIotNtn: carrier roaming ntn eligible for phone"
                   + " associated with subId " + phone.getSubId());
         return true;
@@ -3944,10 +4188,13 @@
                 new ResultReceiver(this) {
                     @Override
                     protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        decrementResultReceiverCount(
+                                "SC:isSatelliteSupportedViaOemInternal");
                         plogd("isSatelliteSupportedViaOemInternal.requestIsSatelliteSupported:"
                                 + " resultCode=" + resultCode);
                     }
                 });
+        incrementResultReceiverCount("SC:isSatelliteSupportedViaOemInternal");
         return null;
     }
 
@@ -4074,7 +4321,7 @@
         RequestSatelliteEnabledArgument argument =
                 (RequestSatelliteEnabledArgument) request.argument;
         handlePersistentLoggingOnSessionStart(argument);
-        selectBindingSatelliteSubscription();
+        selectBindingSatelliteSubscription(argument.enableSatellite);
         SatelliteModemEnableRequestAttributes enableRequestAttributes =
                     createModemEnableRequest(argument);
         if (enableRequestAttributes == null) {
@@ -4173,6 +4420,7 @@
                         protected void onReceiveResult(int resultCode, Bundle resultData) {
                             plogd("updateSatelliteSupportedState.requestIsSatelliteProvisioned: "
                                     + "resultCode=" + resultCode + ", resultData=" + resultData);
+                            decrementResultReceiverCount("SC:requestIsSatelliteProvisioned");
                             requestSatelliteEnabled(false, false, false,
                                     new IIntegerConsumer.Stub() {
                                         @Override
@@ -4183,17 +4431,22 @@
                                     });
                         }
                     });
+            incrementResultReceiverCount("SC:requestIsSatelliteProvisioned");
+
             requestSatelliteCapabilities(
                     new ResultReceiver(this) {
                         @Override
                         protected void onReceiveResult(int resultCode, Bundle resultData) {
                             plogd("updateSatelliteSupportedState.requestSatelliteCapabilities: "
                                     + "resultCode=" + resultCode + ", resultData=" + resultData);
+                            decrementResultReceiverCount("SC:requestSatelliteCapabilities");
                         }
                     });
+            incrementResultReceiverCount("SC:requestSatelliteCapabilities");
         }
         registerForSatelliteSupportedStateChanged();
-        selectBindingSatelliteSubscription();
+        selectBindingSatelliteSubscription(false);
+        notifySatelliteSupportedStateChanged(supported);
     }
 
     private void updateSatelliteEnabledState(boolean enabled, String caller) {
@@ -4209,6 +4462,9 @@
         if (!enabled) {
             mIsModemEnabledReportingNtnSignalStrength.set(false);
         }
+        if (mFeatureFlags.satelliteStateChangeListener()) {
+            notifyEnabledStateChanged(enabled);
+        }
     }
 
     private void registerForPendingDatagramCount() {
@@ -4357,8 +4613,8 @@
                     && mProvisionedSubscriberId.containsValue(Boolean.TRUE);
             mControllerMetricsStats.setIsProvisioned(isProvisioned);
         }
-        selectBindingSatelliteSubscription();
-        handleStateChangedForCarrierRoamingNtnEligibility();
+        selectBindingSatelliteSubscription(false);
+        evaluateCarrierRoamingNtnEligibilityChange();
     }
 
     private void updateDeviceProvisionStatus() {
@@ -4467,13 +4723,11 @@
 
         synchronized (mSatelliteCapabilitiesLock) {
             mSatelliteCapabilities = capabilities;
+            overrideSatelliteCapabilitiesIfApplicable();
         }
 
         List<ISatelliteCapabilitiesCallback> deadCallersList = new ArrayList<>();
         mSatelliteCapabilitiesChangedListeners.values().forEach(listener -> {
-            synchronized (this.mSatelliteCapabilitiesLock) {
-                overrideSatelliteCapabilitiesIfApplicable();
-            }
             try {
                 listener.onSatelliteCapabilitiesChanged(this.mSatelliteCapabilities);
             } catch (RemoteException e) {
@@ -4520,7 +4774,9 @@
             }
             mIsSatelliteSupported = supported;
         }
+    }
 
+    private void notifySatelliteSupportedStateChanged(boolean supported) {
         List<ISatelliteSupportedStateCallback> deadCallersList = new ArrayList<>();
         mSatelliteSupportedStateChangedListeners.values().forEach(listener -> {
             try {
@@ -4747,7 +5003,7 @@
             updateSatelliteEnabledState(
                     false, "moveSatelliteToOffStateAndCleanUpResources");
         }
-        selectBindingSatelliteSubscription();
+        selectBindingSatelliteSubscription(false);
         synchronized (mSatellitePhoneLock) {
             updateLastNotifiedNtnModeAndNotify(mSatellitePhone);
         }
@@ -4947,7 +5203,9 @@
                         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
+                        KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT,
+                        KEY_SATELLITE_SOS_MAX_DATAGRAM_SIZE,
+                        KEY_SATELLITE_SUPPORTED_MSG_APPS_STRING_ARRAY
                 );
             } catch (Exception e) {
                 logw("getConfigForSubId: " + e);
@@ -4975,7 +5233,7 @@
         updateSupportedSatelliteServicesForActiveSubscriptions();
         processNewCarrierConfigData(subId);
         resetCarrierRoamingSatelliteModeParams(subId);
-        handleStateChangedForCarrierRoamingNtnEligibility();
+        evaluateCarrierRoamingNtnEligibilityChange();
         sendMessageDelayed(obtainMessage(CMD_EVALUATE_ESOS_PROFILES_PRIORITIZATION),
                 mEvaluateEsosProfilesPrioritizationDurationMillis);
     }
@@ -5433,6 +5691,17 @@
         }
     }
 
+    /**
+     * Returns a list of messaging apps that support satellite.
+     */
+    @NonNull public List<String> getSatelliteSupportedMsgApps(int subId) {
+        String[] satelliteSupportedMsgApps = getConfigForSubId(subId)
+                .getStringArray(KEY_SATELLITE_SUPPORTED_MSG_APPS_STRING_ARRAY);
+
+        return satelliteSupportedMsgApps != null
+                ? List.of(satelliteSupportedMsgApps) : Collections.emptyList();
+    }
+
     private void sendErrorAndReportSessionMetrics(@SatelliteManager.SatelliteResult int error,
             Consumer<Integer> result) {
         result.accept(error);
@@ -5454,8 +5723,15 @@
         }
     }
 
+    private void registerForSignalStrengthChanged() {
+        for (Phone phone : PhoneFactory.getPhones()) {
+            phone.getSignalStrengthController().registerForSignalStrengthChanged(this,
+                    EVENT_SIGNAL_STRENGTH_CHANGED, phone.getPhoneId());
+        }
+    }
+
     private void handleEventServiceStateChanged() {
-        handleStateChangedForCarrierRoamingNtnEligibility();
+        evaluateCarrierRoamingNtnEligibilityChange();
         handleServiceStateForSatelliteConnectionViaCarrier();
     }
 
@@ -5519,6 +5795,7 @@
                     mWasSatelliteConnectedViaCarrier.put(subId, false);
                 }
                 updateLastNotifiedNtnModeAndNotify(phone);
+                updateLastNotifiedCarrierRoamingNtnSignalStrengthAndNotify(phone);
             }
         }
         determineAutoConnectSystemNotification();
@@ -5539,6 +5816,7 @@
                 if (!initialized) mInitialized.put(subId, true);
                 mLastNotifiedNtnMode.put(subId, currNtnMode);
                 phone.notifyCarrierRoamingNtnModeChanged(currNtnMode);
+                updateLastNotifiedCarrierRoamingNtnSignalStrengthAndNotify(phone);
                 logCarrierRoamingSatelliteSessionStats(phone, lastNotifiedNtnMode, currNtnMode);
                 if(mIsNotificationShowing && !currNtnMode) {
                     dismissSatelliteNotification();
@@ -5567,15 +5845,15 @@
         }
     }
 
-    private void handleStateChangedForCarrierRoamingNtnEligibility() {
+    private void evaluateCarrierRoamingNtnEligibilityChange() {
         if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
-            plogd("handleStateChangedForCarrierRoamingNtnEligibility: "
+            plogd("evaluateCarrierRoamingNtnEligibilityChange: "
                     + "carrierRoamingNbIotNtn flag is disabled");
             return;
         }
 
         boolean eligible = isCarrierRoamingNtnEligible(mSatellitePhone);
-        plogd("handleStateChangedForCarrierRoamingNtnEligibility: "
+        plogd("evaluateCarrierRoamingNtnEligibilityChange: "
                 + "isCarrierRoamingNtnEligible=" + eligible);
 
         synchronized (mSatellitePhoneLock) {
@@ -5767,6 +6045,7 @@
         bundle.putBoolean(SatelliteManager.KEY_SATELLITE_PROVISIONED,
                 Boolean.TRUE.equals(isDeviceProvisioned()));
         ((ResultReceiver) request.argument).send(SATELLITE_RESULT_SUCCESS, bundle);
+        decrementResultReceiverCount("SC:requestIsSatelliteProvisioned");
     }
 
     private long getWaitForSatelliteEnablingResponseTimeoutMillis() {
@@ -6513,6 +6792,11 @@
                 if (!isActive && !isNtnOnly) {
                     continue;
                 }
+                if (!isNtnOnly && !isCarrierConfigLoaded(subId)) {
+                    // Skip to add priority list if the carrier config is not loaded properly
+                    // for the given carrier subscription.
+                    continue;
+                }
 
                 int keyPriority = (isESOSSupported && isActive && isDefaultSmsSubId) ? 0
                         : (isESOSSupported && isActive) ? 1
@@ -6553,16 +6837,30 @@
 
         // If priority has changed, send broadcast for provisioned ESOS subs IDs
         synchronized (mSatelliteTokenProvisionedLock) {
+            List<SatelliteSubscriberProvisionStatus> newEvaluatedSubscriberProvisionStatus =
+                    getPrioritizedSatelliteSubscriberProvisionStatusList(
+                            newSubsInfoListPerPriority);
             if (isPriorityChanged(mSubsInfoListPerPriority, newSubsInfoListPerPriority)
+                    || isSubscriberContentChanged(mLastEvaluatedSubscriberProvisionStatus,
+                            newEvaluatedSubscriberProvisionStatus)
                     || isChanged) {
                 mSubsInfoListPerPriority = newSubsInfoListPerPriority;
+                mLastEvaluatedSubscriberProvisionStatus = newEvaluatedSubscriberProvisionStatus;
                 sendBroadCastForProvisionedESOSSubs();
                 mHasSentBroadcast = true;
-                selectBindingSatelliteSubscription();
+                selectBindingSatelliteSubscription(false);
             }
         }
     }
 
+    // to check if the contents of carrier config is loaded properly
+    private Boolean isCarrierConfigLoaded(int subId) {
+        PersistableBundle carrierConfig = mCarrierConfigManager
+                .getConfigForSubId(subId, KEY_CARRIER_CONFIG_APPLIED_BOOL);
+        return carrierConfig != null ? carrierConfig.getBoolean(
+                CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL) : false;
+    }
+
     // The subscriberId for ntnOnly SIMs is the Iccid, whereas for ESOS supported SIMs, the
     // subscriberId is the Imsi prefix 6 digit + phone number.
     private Pair<String, Integer> getSubscriberIdAndType(@Nullable SubscriptionInfo info) {
@@ -6625,6 +6923,24 @@
         return false;
     }
 
+    // Checks if there are any changes between subscriberInfos. return false if the same.
+    // Note that, Use lists with the same priority so we can compare contents properly.
+    private boolean isSubscriberContentChanged(List<SatelliteSubscriberProvisionStatus> currentList,
+            List<SatelliteSubscriberProvisionStatus> newList) {
+        if (currentList.size() != newList.size()) {
+            return true;
+        }
+        for (int i = 0; i < currentList.size(); i++) {
+            SatelliteSubscriberProvisionStatus curSub = currentList.get(i);
+            SatelliteSubscriberProvisionStatus newSub = newList.get(i);
+            if (!curSub.getSatelliteSubscriberInfo().equals(newSub.getSatelliteSubscriberInfo())) {
+                logd("isSubscriberContentChanged: cur=" + curSub + " , new=" + newSub);
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void sendBroadCastForProvisionedESOSSubs() {
         String packageName = getConfigSatelliteGatewayServicePackage();
         String className = getConfigSatelliteCarrierRoamingEsosProvisionedClass();
@@ -6678,10 +6994,18 @@
 
     private List<SatelliteSubscriberProvisionStatus>
             getPrioritizedSatelliteSubscriberProvisionStatusList() {
+        synchronized (mSatelliteTokenProvisionedLock) {
+            return getPrioritizedSatelliteSubscriberProvisionStatusList(mSubsInfoListPerPriority);
+        }
+    }
+
+    private List<SatelliteSubscriberProvisionStatus>
+            getPrioritizedSatelliteSubscriberProvisionStatusList(
+                    Map<Integer, List<SubscriptionInfo>> subsInfoListPerPriority) {
         List<SatelliteSubscriberProvisionStatus> list = new ArrayList<>();
         synchronized (mSatelliteTokenProvisionedLock) {
-            for (int priority : mSubsInfoListPerPriority.keySet()) {
-                List<SubscriptionInfo> infoList = mSubsInfoListPerPriority.get(priority);
+            for (int priority : subsInfoListPerPriority.keySet()) {
+                List<SubscriptionInfo> infoList = subsInfoListPerPriority.get(priority);
                 if (infoList == null) {
                     logd("getPrioritySatelliteSubscriberProvisionStatusList: no exist this "
                             + "priority " + priority);
@@ -6727,8 +7051,8 @@
         }
     }
 
-    private void selectBindingSatelliteSubscription() {
-        if (isSatelliteEnabled() || isSatelliteBeingEnabled()) {
+    private void selectBindingSatelliteSubscription(boolean shouldIgnoreEnabledState) {
+        if ((isSatelliteEnabled() || isSatelliteBeingEnabled()) && !shouldIgnoreEnabledState) {
             plogd("selectBindingSatelliteSubscription: satellite subscription will be selected "
                     + "once the satellite session ends");
             return;
@@ -6818,6 +7142,7 @@
         RequestProvisionSatelliteArgument request = new RequestProvisionSatelliteArgument(list,
                 result, true);
         sendRequestAsync(CMD_UPDATE_PROVISION_SATELLITE_TOKEN, request, null);
+        incrementResultReceiverCount("SC:provisionSatellite");
     }
 
     /**
@@ -6843,6 +7168,62 @@
         RequestProvisionSatelliteArgument request = new RequestProvisionSatelliteArgument(list,
                 result, false);
         sendRequestAsync(CMD_UPDATE_PROVISION_SATELLITE_TOKEN, request, null);
+        incrementResultReceiverCount("SC:provisionSatellite");
+    }
+
+    /**
+     * Inform whether application supports NTN SMS in satellite mode.
+     *
+     * This method is used by default messaging application to inform framework whether it supports
+     * NTN SMS or not.
+     *
+     * @param ntnSmsSupported {@code true} If application supports NTN SMS, else {@code false}.
+     */
+    public void setNtnSmsSupportedByMessagesApp(boolean ntnSmsSupported) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) {
+            return;
+        }
+        persistNtnSmsSupportedByMessagesApp(ntnSmsSupported);
+        handleCarrierRoamingNtnAvailableServicesChanged(getSelectedSatelliteSubId());
+    }
+
+    private void persistNtnSmsSupportedByMessagesApp(boolean ntnSmsSupported) {
+        plogd("persistNtnSmsSupportedByMessagesApp: ntnSmsSupported=" + ntnSmsSupported);
+        if (!loadSatelliteSharedPreferences()) return;
+
+        if (mSharedPreferences == null) {
+            ploge("persistNtnSmsSupportedByMessagesApp: mSharedPreferences is null");
+        } else {
+            mSharedPreferences.edit().putBoolean(
+                    NTN_SMS_SUPPORTED_BY_MESSAGES_APP_KEY, ntnSmsSupported).apply();
+            synchronized (mNtnSmsSupportedByMessagesAppLock) {
+                mNtnSmsSupportedByMessagesApp = ntnSmsSupported;
+            }
+        }
+    }
+
+    private boolean isNtnSmsSupportedByMessagesApp() {
+        synchronized (mNtnSmsSupportedByMessagesAppLock) {
+            if (mNtnSmsSupportedByMessagesApp != null) {
+                plogd("isNtnSmsSupportedByMessagesApp:" + mNtnSmsSupportedByMessagesApp);
+                return mNtnSmsSupportedByMessagesApp;
+            }
+        }
+
+        if (!loadSatelliteSharedPreferences()) return false;
+
+        if (mSharedPreferences == null) {
+            ploge("isNtnSmsSupportedByMessagesApp: mSharedPreferences is null");
+            return false;
+        } else {
+            boolean ntnSmsSupported = mSharedPreferences.getBoolean(
+                    NTN_SMS_SUPPORTED_BY_MESSAGES_APP_KEY, false);
+            synchronized (mNtnSmsSupportedByMessagesAppLock) {
+                mNtnSmsSupportedByMessagesApp = ntnSmsSupported;
+                plogd("isNtnSmsSupportedByMessagesApp:" + mNtnSmsSupportedByMessagesApp);
+            }
+            return ntnSmsSupported;
+        }
     }
 
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
@@ -6898,6 +7279,11 @@
             return false;
         }
 
+        if (!mIsRadioOn) {
+            plogd("isCarrierRoamingNtnEligible: radio is off");
+            return false;
+        }
+
         if (phone == null) {
             plogd("isCarrierRoamingNtnEligible: phone is null");
             return false;
@@ -6921,8 +7307,9 @@
             return false;
         }
 
-        if (!isSatelliteServiceSupportedByCarrier(subId,
-                NetworkRegistrationInfo.SERVICE_TYPE_SMS)) {
+
+        int[] services = getSupportedServicesOnCarrierRoamingNtn(subId);
+        if (!ArrayUtils.contains(services, NetworkRegistrationInfo.SERVICE_TYPE_SMS)) {
             plogd("isCarrierRoamingNtnEligible[phoneId=" + phone.getPhoneId()
                     + "]: SMS is not supported by carrier");
             return false;
@@ -6957,7 +7344,15 @@
         return true;
     }
 
-    private boolean isSatelliteServiceSupportedByCarrier(int subId,
+
+    /**
+     * Checks if the satellite service is supported by the carrier for the specified
+     * subscription ID and servicetype.
+     *
+     * @param subId The subscription id.
+     * @param serviceType The type of service to check
+     */
+    public boolean isSatelliteServiceSupportedByCarrier(int subId,
             @NetworkRegistrationInfo.ServiceType int serviceType) {
         List<String> satellitePlmnList = getSatellitePlmnsForCarrier(subId);
         for (String satellitePlmn : satellitePlmnList) {
@@ -7321,6 +7716,7 @@
             return;
         }
         updateLastNotifiedNtnAvailableServicesAndNotify(subId);
+        evaluateCarrierRoamingNtnEligibilityChange();
     }
 
     private void updateLastNotifiedNtnAvailableServicesAndNotify(int subId) {
@@ -7334,18 +7730,22 @@
             return;
         }
         plogd("updateLastNotifiedNtnAvailableServicesAndNotify: phoneId= " + phone.getPhoneId());
+        int[] services = getSupportedServicesOnCarrierRoamingNtn(subId);
+        phone.notifyCarrierRoamingNtnAvailableServicesChanged(services);
+    }
 
+    /** Return services that are supported on carrier roaming non-terrestrial network. */
+    public int[] getSupportedServicesOnCarrierRoamingNtn(int subId) {
         if (isSatelliteSupportedViaCarrier(subId)) {
-            // TODO: Invoke SatelliteManager#getSatelliteDisallowedReasons() NOT EMPTY.
+            // TODO: b/377367448 Cleanup get supported satellite services to align with starlink.
             int[] services = getSupportedSatelliteServicesForCarrier(subId);
             if (isP2PSmsDisallowedOnCarrierRoamingNtn(subId)) {
                 services = Arrays.stream(services).filter(
                         value -> value != NetworkRegistrationInfo.SERVICE_TYPE_SMS).toArray();
             }
-            phone.notifyCarrierRoamingNtnAvailableServicesChanged(services);
-        } else {
-            phone.notifyCarrierRoamingNtnAvailableServicesChanged(new int[0]);
+            return services;
         }
+        return new int[0];
     }
 
     /**
@@ -7361,7 +7761,7 @@
         if (carrierRoamingNtnConnectType == CARRIER_ROAMING_NTN_CONNECT_MANUAL) {
             // Manual Connected
             plogd("isP2PSmsDisallowedOnCarrierRoamingNtn: manual connect");
-            if (!isApplicationSupportsP2P(mDefaultSmsPackageName)
+            if (!isNtnSmsSupportedByMessagesApp()
                     || !isApplicationSupportsP2P(mSatelliteGatewayServicePackageName)) {
                 plogd("isP2PSmsDisallowedOnCarrierRoamingNtn: APKs do not supports P2P");
                 return true;
@@ -7426,4 +7826,75 @@
         mContext.registerReceiver(mPackageStateChangedReceiver, packageFilter,
                 mContext.RECEIVER_EXPORTED);
     }
+
+
+    private void notifyEnabledStateChanged(boolean isEnabled) {
+        TelephonyRegistryManager trm = mContext.getSystemService(TelephonyRegistryManager.class);
+        if (trm == null) {
+            loge("Telephony registry service is down!");
+            return;
+        }
+
+        trm.notifySatelliteStateChanged(isEnabled);
+        logd("notifyEnabledStateChanged to " + isEnabled);
+    }
+
+    private NtnSignalStrength getCarrierRoamingNtnSignalStrength(@NonNull Phone phone) {
+        NtnSignalStrength carrierRoamingNtnSignalStrength = new NtnSignalStrength(
+                NTN_SIGNAL_STRENGTH_NONE);
+
+        if (isInCarrierRoamingNbIotNtn(phone)) {
+            if (mSatelliteSessionController.isInConnectedState()) {
+                synchronized (mNtnSignalsStrengthLock) {
+                    carrierRoamingNtnSignalStrength = mNtnSignalStrength;
+                }
+                plogd("getCarrierRoamingNtnSignalStrength[phoneId=" + phone.getPhoneId()
+                        + "]: in carrier roaming nb iot ntn mode.");
+            }
+        } else if (isInSatelliteModeForCarrierRoaming(phone)) {
+            ServiceState serviceState = phone.getServiceState();
+            if (serviceState.getState() != ServiceState.STATE_OUT_OF_SERVICE) {
+                carrierRoamingNtnSignalStrength = new NtnSignalStrength(
+                        phone.getSignalStrength().getLevel());
+                plogd("getCarrierRoamingNtnSignalStrength[phoneId=" + phone.getPhoneId()
+                        + "]: is in satellite mode for carrier roaming.");
+            }
+        }
+
+        return carrierRoamingNtnSignalStrength;
+    }
+
+    protected void updateLastNotifiedCarrierRoamingNtnSignalStrengthAndNotify(
+            @Nullable Phone phone) {
+        if (!mFeatureFlags.carrierRoamingNbIotNtn()) return;
+        if (phone == null) {
+            return;
+        }
+
+        NtnSignalStrength currSignalStrength = getCarrierRoamingNtnSignalStrength(phone);
+        int subId = phone.getSubId();
+        synchronized (mSatelliteConnectedLock) {
+            NtnSignalStrength lastNotifiedSignalStrength =
+                    mLastNotifiedCarrierRoamingNtnSignalStrength.get(subId);
+            if (lastNotifiedSignalStrength == null
+                    || lastNotifiedSignalStrength.getLevel() != currSignalStrength.getLevel()) {
+                mLastNotifiedCarrierRoamingNtnSignalStrength.put(subId, currSignalStrength);
+                phone.notifyCarrierRoamingNtnSignalStrengthChanged(currSignalStrength);
+            }
+        }
+    }
+
+    /** Returns whether to send SMS to DatagramDispatcher or not. */
+    public boolean shouldSendSmsToDatagramDispatcher(@Nullable Phone phone) {
+        if (!isInCarrierRoamingNbIotNtn(phone)) {
+            return false;
+        }
+
+        if (isDemoModeEnabled()) {
+            return false;
+        }
+
+        int[] services = getSupportedServicesOnCarrierRoamingNtn(phone.getSubId());
+        return ArrayUtils.contains(services, NetworkRegistrationInfo.SERVICE_TYPE_SMS);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
index 1b6f9f1..d1d03a0 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSessionController.java
@@ -57,6 +57,7 @@
 import android.os.SystemProperties;
 import android.os.WorkSource;
 import android.telephony.DropBoxManagerLoggerBackend;
+import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PersistentLogger;
 import android.telephony.ServiceState;
 import android.telephony.satellite.ISatelliteModemStateCallback;
@@ -76,6 +77,7 @@
 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.telephony.util.ArrayUtils;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 import com.android.telephony.Rlog;
@@ -135,6 +137,7 @@
     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;
@@ -180,8 +183,6 @@
     boolean mIsScreenOn = true;
     private boolean mIsDeviceAlignedWithSatellite = false;
 
-    @GuardedBy("mLock")
-    @NonNull private boolean mIsDisableCellularModemInProgress = false;
     @NonNull private final SatelliteController mSatelliteController;
     @NonNull private final DatagramController mDatagramController;
     @Nullable private PersistentLogger mPersistentLogger = null;
@@ -585,6 +586,17 @@
     }
 
     /**
+     * Get whether state machine is in connected state.
+     *
+     * @return {@code true} if state machine is in connected state and {@code false} otherwise.
+     */
+    public boolean isInConnectedState() {
+        if (DBG) plogd("isInConnectedState: getCurrentState=" + getCurrentState());
+        return getCurrentState() == mConnectedState;
+    }
+
+
+    /**
      * Release all resource.
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
@@ -676,9 +688,6 @@
             mPreviousState = mCurrentState;
             mCurrentState = SatelliteManager.SATELLITE_MODEM_STATE_OFF;
             mIsSendingTriggeredDuringTransferringState.set(false);
-            synchronized (mLock) {
-                mIsDisableCellularModemInProgress = false;
-            }
             unbindService();
             stopNbIotInactivityTimer();
             DemoSimulator.getInstance().onSatelliteModeOff();
@@ -880,7 +889,11 @@
             Message onCompleted =
                     obtainMessage(EVENT_ENABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE);
             mSatelliteModemInterface.enableCellularModemWhileSatelliteModeIsOn(true, onCompleted);
-            notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+            if (isConcurrentTnScanningSupported()) {
+                plogd("IDLE state is hidden from clients");
+            } else {
+                notifyStateChangedEvent(SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+            }
         }
 
         @Override
@@ -895,7 +908,7 @@
                     break;
                 case EVENT_DISABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE:
                     handleEventDisableCellularModemWhileSatelliteModeIsOnDone(
-                            (AsyncResult) msg.obj);
+                        (AsyncResult) msg.obj);
                     break;
                 case EVENT_SATELLITE_ENABLEMENT_STARTED:
                     handleSatelliteEnablementStarted((boolean) msg.obj);
@@ -935,17 +948,13 @@
             if ((datagramTransferState.sendState == SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING)
                     || (datagramTransferState.receiveState
                     == SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVING)) {
-                if (mSatelliteController.isSatelliteAttachRequired()) {
-                    ploge("Unexpected transferring state received for NB-IOT NTN");
-                } else {
-                    transitionTo(mTransferringState);
-                }
+                transitionTo(mTransferringState);
             } else if ((datagramTransferState.sendState
                     == SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT)
                     || (datagramTransferState.receiveState
                     == SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT)) {
                 if (mSatelliteController.isSatelliteAttachRequired()) {
-                    disableCellularModemWhileSatelliteModeIsOn();
+                    transitionTo(mNotConnectedState);
                 } else {
                     ploge("Unexpected transferring state received for non-NB-IOT NTN");
                 }
@@ -993,49 +1002,32 @@
 
         private void handleEventDisableCellularModemWhileSatelliteModeIsOnDone(
                 @NonNull AsyncResult result) {
-            synchronized (mLock) {
-                if (mIsDisableCellularModemInProgress) {
-                    int error = SatelliteServiceUtils.getSatelliteError(
-                            result, "DisableCellularModemWhileSatelliteModeIsOnDone");
-                    if (error == SatelliteManager.SATELLITE_RESULT_SUCCESS) {
-                        transitionTo(mNotConnectedState);
-                    }
-                    mIsDisableCellularModemInProgress = false;
-                } else {
-                    ploge("DisableCellularModemWhileSatelliteModeIsOn is not in progress");
-                }
-            }
+            int error = SatelliteServiceUtils.getSatelliteError(
+                        result, "DisableCellularModemWhileSatelliteModeIsOnDone");
+            plogd("Disable TN scanning done with result: " + error);
         }
 
         private void handleSatelliteModemStateChanged(@NonNull Message msg) {
             int state = msg.arg1;
             if (state == SatelliteManager.SATELLITE_MODEM_STATE_OFF) {
                 transitionTo(mPowerOffState);
-            }
-        }
-
-        private void disableCellularModemWhileSatelliteModeIsOn() {
-            synchronized (mLock) {
-                if (mIsDisableCellularModemInProgress) {
-                    plogd("Cellular scanning is already being disabled");
-                    return;
+            } else if (state == SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED
+                           || state == SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED) {
+                if (isConcurrentTnScanningSupported()) {
+                    plogd("Notifying the new state " + state + " to clients but still"
+                            + " stay at IDLE state internally");
+                    notifyStateChangedEvent(state);
+                } else {
+                    plogd("Ignoring the modem state " + state);
                 }
-
-                mIsDisableCellularModemInProgress = true;
-                Message onCompleted =
-                        obtainMessage(EVENT_DISABLE_CELLULAR_MODEM_WHILE_SATELLITE_MODE_IS_ON_DONE);
-                mSatelliteModemInterface.enableCellularModemWhileSatelliteModeIsOn(false,
-                        onCompleted);
             }
         }
 
         @Override
         public void exit() {
             if (DBG) plogd("Exiting IdleState");
-            if (!mSatelliteController.isSatelliteAttachRequired()) {
-                // Disable cellular modem scanning
-                mSatelliteModemInterface.enableCellularModemWhileSatelliteModeIsOn(false, null);
-            }
+            // Disable cellular modem scanning
+            mSatelliteModemInterface.enableCellularModemWhileSatelliteModeIsOn(false, null);
         }
     }
 
@@ -1309,6 +1301,8 @@
             startNbIotInactivityTimer();
             evaluateStartingEsosInactivityTimer();
             evaluateStartingP2pSmsInactivityTimer();
+            mSatelliteController.updateLastNotifiedCarrierRoamingNtnSignalStrengthAndNotify(
+                    mSatelliteController.getSatellitePhone());
         }
 
         @Override
@@ -1659,6 +1653,19 @@
         }
         mIsScreenOn = screenOn;
 
+        if (mSatelliteController.getRequestIsEmergency()) {
+            if (DBG) logd("handleEventScreenStateChanged: Emergency mode");
+            // This is for coexistence
+            // emergency mode can be set after registerForScreenStateChanged() called for P2P-sms
+            return;
+        }
+
+        int subId = getSubId();
+        if (!isP2pSmsSupportedOnCarrierRoamingNtn(subId)) {
+            if (DBG) plogd("handleEventScreenStateChanged: P2P_SMS is not supported");
+            return;
+        }
+
         if (!screenOn) {
             // Screen off, start timer
             int timeoutMillis = getScreenOffInactivityTimeoutDurationSec() * 1000;
@@ -1770,19 +1777,19 @@
         }
 
         if (isP2pSmsInActivityTimerStarted()) {
-            plogd("isEsosInActivityTimerStarted: "
+            plogd("isP2pSmsInActivityTimerStarted: "
                     + "P2P_SMS inactivity timer already started");
             return;
         }
 
         int subId = getSubId();
-        if (!mSatelliteController.isSatelliteRoamingP2pSmSSupported(subId)) {
-            plogd("evaluateStartingEsosInactivityTimer: P2P_SMS is not supported");
+        if (!isP2pSmsSupportedOnCarrierRoamingNtn(subId)) {
+            if (DBG) plogd("evaluateStartingP2pSmsInactivityTimer: P2P_SMS is not supported");
             return;
         }
 
         if (mIsDeviceAlignedWithSatellite) {
-            plogd("evaluateStartingEsosInactivityTimer: "
+            plogd("evaluateStartingP2pSmsInactivityTimer: "
                     + "can't start P2P_SMS inactivity timer due to device aligned satellite");
             return;
         }
@@ -1792,10 +1799,10 @@
         if (datagramController.isSendingInIdleState()
                 && datagramController.isPollingInIdleState()) {
             sendMessageDelayed(EVENT_P2P_SMS_INACTIVITY_TIMER_TIMED_OUT, timeOutMillis);
-            plogd("evaluateStartingEsosInactivityTimer: start P2P_SMS inactivity timer "
+            plogd("evaluateStartingP2pSmsInactivityTimer: start P2P_SMS inactivity timer "
                     + timeOutMillis);
         } else {
-            plogd("evaluateStartingEsosInactivityTimer: "
+            plogd("evaluateStartingP2pSmsInactivityTimer: "
                     + "can't start P2P_SMS inactivity timer");
         }
     }
@@ -1813,6 +1820,15 @@
     }
 
     private void handleEventScreenOffInactivityTimerTimedOut() {
+        if (mSatelliteController.getRequestIsEmergency()) {
+            loge("handleEventScreenOffInactivityTimerTimedOut: Emergency mode");
+            /* This is for coexistence
+             * mIsEmergency can be set after
+             * EVENT_SCREEN_OFF_INACTIVITY_TIMER_TIMED_OUT timer started
+             */
+            return;
+        }
+
         plogd("handleEventScreenOffInactivityTimerTimedOut: request disable satellite");
 
         mSatelliteController.requestSatelliteEnabled(
@@ -1908,6 +1924,25 @@
         return true;
     }
 
+    private boolean isP2pSmsSupportedOnCarrierRoamingNtn(int subId) {
+        if (!mSatelliteController.isSatelliteRoamingP2pSmSSupported(subId)) {
+            if (DBG) plogd("isP2pSmsSupportedOnCarrierRoamingNtn: P2P_SMS is not supported");
+            return false;
+        }
+
+        int[] services = mSatelliteController.getSupportedServicesOnCarrierRoamingNtn(subId);
+        if (!ArrayUtils.contains(services, NetworkRegistrationInfo.SERVICE_TYPE_SMS)) {
+            if (DBG) {
+                plogd("isP2pSmsSupportedOnCarrierRoamingNtn: P2P_SMS service is not supported "
+                        + "on carrier roaming ntn.");
+            }
+            return false;
+        }
+
+        if (DBG) plogd("isP2pSmsSupportedOnCarrierRoamingNtn: P2_SMS is supported");
+        return true;
+    }
+
     private boolean isSatellitePersistentLoggingEnabled(
             @NonNull Context context, @NonNull FeatureFlags featureFlags) {
         if (featureFlags.satellitePersistentLogging()) {
@@ -1921,6 +1956,16 @@
         }
     }
 
+    private boolean isConcurrentTnScanningSupported() {
+        try {
+            return mContext.getResources().getBoolean(
+                R.bool.config_satellite_modem_support_concurrent_tn_scanning);
+        } catch (RuntimeException e) {
+            plogd("isConcurrentTnScanningSupported: ex=" + e);
+            return false;
+        }
+    }
+
     private void plogd(@NonNull String log) {
         logd(log);
         if (mPersistentLogger != null) {
diff --git a/src/java/com/android/internal/telephony/satellite/metrics/CarrierRoamingSatelliteSessionStats.java b/src/java/com/android/internal/telephony/satellite/metrics/CarrierRoamingSatelliteSessionStats.java
index 771432e..3138b16 100644
--- a/src/java/com/android/internal/telephony/satellite/metrics/CarrierRoamingSatelliteSessionStats.java
+++ b/src/java/com/android/internal/telephony/satellite/metrics/CarrierRoamingSatelliteSessionStats.java
@@ -325,13 +325,18 @@
             return;
         }
         String simCountry = MccTable.countryCodeForMcc(subscriptionInfoInternal.getMcc());
-        String satelliteRegisteredCountry = MccTable.countryCodeForMcc(
-                satelliteRegisteredPlmn.substring(0, 3));
-        if (simCountry.equalsIgnoreCase(satelliteRegisteredCountry)) {
-            mIsNtnRoamingInHomeCountry = false;
-        } else {
-            // If device is connected to roaming non-terrestrial network, update to true.
-            mIsNtnRoamingInHomeCountry = true;
+        mIsNtnRoamingInHomeCountry = true;
+        if (satelliteRegisteredPlmn != null
+                && satelliteRegisteredPlmn.length() >= 3) {
+            String satelliteRegisteredCountry = MccTable.countryCodeForMcc(
+                    satelliteRegisteredPlmn.substring(0, 3));
+            if (simCountry.equalsIgnoreCase(satelliteRegisteredCountry)) {
+                mIsNtnRoamingInHomeCountry = true;
+            } else {
+                // If device is connected to roaming non-terrestrial network, then marking as
+                // roaming in external country
+                mIsNtnRoamingInHomeCountry = false;
+            }
         }
         logd("updateNtnRoamingInHomeCountry: mIsNtnRoamingInHomeCountry="
                 + mIsNtnRoamingInHomeCountry);
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 a234378..2ae8f9d 100644
--- a/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
+++ b/src/java/com/android/internal/telephony/satellite/metrics/SessionMetricsStats.java
@@ -46,9 +46,13 @@
     private long mTerminationProcessingTimeMillis;
     private int mSessionDurationSec;
     private int mCountOfSuccessfulOutgoingDatagram;
+    private int mShadowCountOfSuccessfulOutgoingDatagram;
     private int mCountOfFailedOutgoingDatagram;
+    private int mShadowCountOfFailedOutgoingDatagram;
     private int mCountOfTimedOutUserMessagesWaitingForConnection;
+    private int mShadowCountOfTimedOutUserMessagesWaitingForConnection;
     private int mCountOfTimedOutUserMessagesWaitingForAck;
+    private int mShadowCountOfTimedOutUserMessagesWaitingForAck;
     private int mCountOfSuccessfulIncomingDatagram;
     private int mCountOfIncomingDatagramFailed;
     private boolean mIsDemoMode;
@@ -131,6 +135,7 @@
         }
 
         mCountOfSuccessfulOutgoingDatagram++;
+        mShadowCountOfSuccessfulOutgoingDatagram++;
         logd("addCountOfSuccessfulOutgoingDatagram: current count="
                 + mCountOfSuccessfulOutgoingDatagram);
         return this;
@@ -146,6 +151,7 @@
         }
 
         mCountOfFailedOutgoingDatagram++;
+        mShadowCountOfFailedOutgoingDatagram++;
         logd("addCountOfFailedOutgoingDatagram: current count=" + mCountOfFailedOutgoingDatagram);
 
         if (resultCode == SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE) {
@@ -166,6 +172,7 @@
         }
 
         mCountOfTimedOutUserMessagesWaitingForConnection++;
+        mShadowCountOfTimedOutUserMessagesWaitingForConnection++;
         logd("addCountOfTimedOutUserMessagesWaitingForConnection: current count="
                 + mCountOfTimedOutUserMessagesWaitingForConnection);
         return this;
@@ -180,6 +187,7 @@
         }
 
         mCountOfTimedOutUserMessagesWaitingForAck++;
+        mShadowCountOfTimedOutUserMessagesWaitingForAck++;
         logd("addCountOfTimedOutUserMessagesWaitingForAck: current count="
                 + mCountOfTimedOutUserMessagesWaitingForAck);
         return this;
@@ -278,12 +286,12 @@
     public void requestSatelliteSessionStats(int subId, @NonNull ResultReceiver result) {
         Bundle bundle = new Bundle();
         SatelliteSessionStats sessionStats = new SatelliteSessionStats.Builder()
-                .setCountOfSuccessfulUserMessages(mCountOfSuccessfulOutgoingDatagram)
-                .setCountOfUnsuccessfulUserMessages(mCountOfFailedOutgoingDatagram)
+                .setCountOfSuccessfulUserMessages(mShadowCountOfSuccessfulOutgoingDatagram)
+                .setCountOfUnsuccessfulUserMessages(mShadowCountOfFailedOutgoingDatagram)
                 .setCountOfTimedOutUserMessagesWaitingForConnection(
-                        mCountOfTimedOutUserMessagesWaitingForConnection)
+                        mShadowCountOfTimedOutUserMessagesWaitingForConnection)
                 .setCountOfTimedOutUserMessagesWaitingForAck(
-                        mCountOfTimedOutUserMessagesWaitingForAck)
+                        mShadowCountOfTimedOutUserMessagesWaitingForAck)
                 .setCountOfUserMessagesInQueueToBeSent(
                         DatagramDispatcher.getInstance().getPendingUserMessagesCount())
                 .build();
@@ -322,6 +330,14 @@
         mCountOfAutoExitDueToTnNetwork = 0;
     }
 
+    public void resetSessionStatsShadowCounters() {
+        logd("resetTheStatsCounters");
+        mShadowCountOfSuccessfulOutgoingDatagram = 0;
+        mShadowCountOfFailedOutgoingDatagram = 0;
+        mShadowCountOfTimedOutUserMessagesWaitingForConnection = 0;
+        mShadowCountOfTimedOutUserMessagesWaitingForAck = 0;
+    }
+
     private static void logd(@NonNull String log) {
         if (DBG) {
             Log.d(TAG, log);
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index dd71c44..9db25b6 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -80,6 +80,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 import java.util.UUID;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
@@ -162,8 +163,7 @@
     // this needs to be here, because on bootup we dont know which index maps to which UiccSlot
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private CommandsInterface[] mCis;
-    @VisibleForTesting
-    public UiccSlot[] mUiccSlots;
+    private UiccSlot[] mUiccSlots;
     private int[] mPhoneIdToSlotId;
     private boolean mIsSlotStatusSupported = true;
 
@@ -491,6 +491,27 @@
     }
 
     /**
+     * Set UiccSlot object for a specific physical slot index on the device.
+     *
+     * This is only supposed to be used internally and by unit tests.
+     *
+     * @param slotId Slot index
+     * @param slot Slot object
+     */
+    @VisibleForTesting
+    public void setUiccSlot(int slotId, @NonNull UiccSlot slot) {
+        synchronized (mLock) {
+            if (!isValidSlotIndex(slotId)) {
+                throw new ArrayIndexOutOfBoundsException("Invalid slot index: " + slotId);
+            }
+            if (mUiccSlots[slotId] != null) {
+                mUiccSlots[slotId].dispose();
+            }
+            mUiccSlots[slotId] = Objects.requireNonNull(slot);
+        }
+    }
+
+    /**
      * API to get UiccSlot object for a given phone id
      * @return UiccSlot object for the given phone id
      */
@@ -1076,7 +1097,7 @@
                 log("Creating mUiccSlots[" + slotId + "]; mUiccSlots.length = "
                         + mUiccSlots.length);
             }
-            mUiccSlots[slotId] = new UiccSlot(mContext, true);
+            setUiccSlot(slotId, new UiccSlot(mContext, true));
         }
 
         mUiccSlots[slotId].update(mCis[index], status, index, slotId);
@@ -1353,7 +1374,7 @@
                 if (VDBG) {
                     log("Creating mUiccSlot[" + i + "]; mUiccSlots.length = " + mUiccSlots.length);
                 }
-                mUiccSlots[i] = new UiccSlot(mContext, isActive);
+                setUiccSlot(i, new UiccSlot(mContext, isActive));
             }
 
             if (isActive) { // check isActive flag so that we don't have to iterate through all
@@ -1803,6 +1824,17 @@
         return mCardStrings;
     }
 
+    /**
+     * Release resources. Must be called each time this class is used.
+     */
+    @VisibleForTesting
+    public void dispose() {
+        for (var slot : mUiccSlots) {
+            slot.dispose();
+        }
+        mUiccSlots = null;
+    }
+
     public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
         pw.println("mIsCdmaSupported=" + isCdmaSupported(mContext));
diff --git a/src/java/com/android/internal/telephony/uicc/UiccPort.java b/src/java/com/android/internal/telephony/uicc/UiccPort.java
index 905db70..8118a12 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccPort.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccPort.java
@@ -33,6 +33,8 @@
 import com.android.internal.telephony.flags.FeatureFlagsImpl;
 import com.android.telephony.Rlog;
 
+import dalvik.system.CloseGuard;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -46,6 +48,7 @@
     // The lock object is created by UiccSlot that owns this UiccCard - this is to share the lock
     // between UiccSlot, UiccCard, EuiccCard, UiccPort, EuiccPort and UiccProfile for now.
     protected final Object mLock;
+    private final CloseGuard mCloseGuard = CloseGuard.get();
 
     private String mIccid;
     protected String mCardId;
@@ -68,6 +71,7 @@
         if (DBG) log("Creating");
         mPhoneId = phoneId;
         mLock = lock;
+        mCloseGuard.open("cleanup");
         update(c, ci, ics, uiccCard);
     }
 
@@ -97,6 +101,7 @@
     public void dispose() {
         synchronized (mLock) {
             if (DBG) log("Disposing Port");
+            mCloseGuard.close();
             if (mUiccProfile != null) {
                 mUiccProfile.dispose();
             }
@@ -106,9 +111,14 @@
     }
 
     @Override
-    protected void finalize() {
+    protected void finalize() throws Throwable {
         if (DBG) log("UiccPort finalized");
-        cleanupOpenLogicalChannelRecordsIfNeeded();
+        try {
+            if (mCloseGuard != null) mCloseGuard.warnIfOpen();
+            cleanupOpenLogicalChannelRecordsIfNeeded();
+        } finally {
+            super.finalize();
+        }
     }
 
     /**
@@ -440,7 +450,13 @@
      * removal or modem reset. The obsoleted records may trigger a redundant release of logical
      * channel that may have been assigned to other client.
      */
+    @SuppressWarnings("GuardedBy")
     private void cleanupOpenLogicalChannelRecordsIfNeeded() {
+        // This check may raise GuardedBy warning, but we need it as long as this method is called
+        // from finalize(). We can remove it from there once UiccPort is fully protected against
+        // resource leak (e.g. with CloseGuard) and all (direct and indirect) users are fixed.
+        if (mOpenChannelRecords == null) return;
+
         synchronized (mOpenChannelRecords) {
             for (OpenLogicalChannelRecord record : mOpenChannelRecords) {
                 if (DBG) log("Clean up " + record);
diff --git a/src/java/com/android/internal/telephony/uicc/UiccSlot.java b/src/java/com/android/internal/telephony/uicc/UiccSlot.java
index db10271..d986c93 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccSlot.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccSlot.java
@@ -391,6 +391,13 @@
         }
     }
 
+    /**
+     * Release resources. Must be called each time this class is used.
+     */
+    public void dispose() {
+        nullifyUiccCard(false);
+    }
+
     public boolean isStateUnknown() {
         // CardState is not specific to any port index, use default port.
         CardState cardState = mCardState.get(TelephonyManager.DEFAULT_PORT_INDEX);
diff --git a/tests/telephonytests/src/android/telephony/ims/RcsConfigTest.java b/tests/telephonytests/src/android/telephony/ims/RcsConfigTest.java
index 4889187..c690ab4 100644
--- a/tests/telephonytests/src/android/telephony/ims/RcsConfigTest.java
+++ b/tests/telephonytests/src/android/telephony/ims/RcsConfigTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.telephony.ims;
+package android.telephony.ims;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
@@ -36,6 +36,7 @@
 
 import com.android.internal.telephony.FakeTelephonyProvider;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -176,6 +177,11 @@
         createFakeSimInfo();
     }
 
+    @After
+    public void tearDown() {
+        mFakeTelephonyProvider.close();
+    }
+
     @Test
     @SmallTest
     public void testLoadAndUpdateConfigForSub() {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
index c923f69..101c668 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
@@ -196,4 +196,11 @@
                 selectionArgs);
         return count;
     }
+
+    /**
+     * Release resources. Must be called each time this class is used.
+     */
+    public void close() {
+        mDbHelper.close();
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
index d8005e8..28d0318 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneSubInfoControllerTest.java
@@ -1270,8 +1270,7 @@
         doReturn(mIsimUiccRecords).when(mPhone).getIsimRecords();
         doReturn(refImpuArray).when(mIsimUiccRecords).getIsimImpu();
 
-        List<Uri> impuList = mPhoneSubInfoControllerUT.getImsPublicUserIdentities(0, TAG,
-                FEATURE_ID);
+        List<Uri> impuList = mPhoneSubInfoControllerUT.getImsPublicUserIdentities(0, TAG);
 
         assertNotNull(impuList);
         assertEquals(refImpuArray.length, impuList.size());
@@ -1288,8 +1287,7 @@
         refImpuArray[2] = "tel:+91987754324";
         doReturn(mIsimUiccRecords).when(mPhone).getIsimRecords();
         doReturn(refImpuArray).when(mIsimUiccRecords).getIsimImpu();
-        List<Uri> impuList = mPhoneSubInfoControllerUT.getImsPublicUserIdentities(0, TAG,
-                FEATURE_ID);
+        List<Uri> impuList = mPhoneSubInfoControllerUT.getImsPublicUserIdentities(0, TAG);
         assertNotNull(impuList);
         // Null or Empty string cannot be converted to URI
         assertEquals(refImpuArray.length - 2, impuList.size());
@@ -1300,7 +1298,7 @@
         doReturn(null).when(mPhone).getIsimRecords();
 
         try {
-            mPhoneSubInfoControllerUT.getImsPublicUserIdentities(0, TAG, FEATURE_ID);
+            mPhoneSubInfoControllerUT.getImsPublicUserIdentities(0, TAG);
             fail();
         } catch (Exception ex) {
             assertTrue(ex instanceof IllegalStateException);
@@ -1311,32 +1309,121 @@
     @Test
     public void getImsPublicUserIdentities_InValidSubIdCheck() {
         try {
-            mPhoneSubInfoControllerUT.getImsPublicUserIdentities(-1, TAG, FEATURE_ID);
+            mPhoneSubInfoControllerUT.getImsPublicUserIdentities(-1, TAG);
             fail();
         } catch (Exception ex) {
             assertTrue(ex instanceof IllegalArgumentException);
-            assertTrue(ex.getMessage().contains("Invalid SubscriptionID"));
+            assertTrue(ex.getMessage().contains("Invalid subscription"));
         }
     }
 
     @Test
     public void getImsPublicUserIdentities_NoReadPrivilegedPermission() {
         mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
-        String[] refImpuArray = new String[3];
-        refImpuArray[0] = "012345678";
-        refImpuArray[1] = "sip:test@verify.com";
-        refImpuArray[2] = "tel:+91987754324";
-        doReturn(mIsimUiccRecords).when(mPhone).getIsimRecords();
-        doReturn(refImpuArray).when(mIsimUiccRecords).getIsimImpu();
 
-        List<Uri> impuList = mPhoneSubInfoControllerUT.getImsPublicUserIdentities(0, TAG,
-                FEATURE_ID);
+        try {
+            mPhoneSubInfoControllerUT.getImsPublicUserIdentities(0, TAG);
+            fail();
+        } catch (Exception ex) {
+            assertTrue(ex instanceof SecurityException);
+            assertTrue(ex.getMessage().contains("getImsPublicUserIdentities"));
+        }
 
-        assertNotNull(impuList);
-        assertEquals(refImpuArray.length, impuList.size());
-        assertEquals(impuList.get(0).toString(), refImpuArray[0]);
-        assertEquals(impuList.get(1).toString(), refImpuArray[1]);
-        assertEquals(impuList.get(2).toString(), refImpuArray[2]);
         mContextFixture.addCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE);
     }
+
+    @Test
+    public void getImsPcscfAddresses() {
+        String[] preDefinedPcscfs = new String[3];
+        preDefinedPcscfs[0] = "127.0.0.1";
+        preDefinedPcscfs[1] = "192.168.0.1";
+        preDefinedPcscfs[2] = "::1";
+        doReturn(true).when(mFeatureFlags).supportIsimRecord();
+        doReturn(mIsimUiccRecords).when(mPhone).getIsimRecords();
+        doReturn(preDefinedPcscfs).when(mIsimUiccRecords).getIsimPcscf();
+
+        List<String> pcscfAddresses = mPhoneSubInfoControllerUT.getImsPcscfAddresses(0, TAG);
+
+        assertNotNull(pcscfAddresses);
+        assertEquals(preDefinedPcscfs.length, pcscfAddresses.size());
+        assertEquals(preDefinedPcscfs[0], pcscfAddresses.get(0).toString());
+        assertEquals(preDefinedPcscfs[1], pcscfAddresses.get(1).toString());
+        assertEquals(preDefinedPcscfs[2], pcscfAddresses.get(2).toString());
+    }
+
+    @Test
+    public void getImsPcscfAddresses_InvalidPcscf() {
+        String[] preDefinedPcscfs = new String[3];
+        preDefinedPcscfs[0] = null;
+        preDefinedPcscfs[2] = "";
+        preDefinedPcscfs[2] = "::1";
+        doReturn(true).when(mFeatureFlags).supportIsimRecord();
+        doReturn(mIsimUiccRecords).when(mPhone).getIsimRecords();
+        doReturn(preDefinedPcscfs).when(mIsimUiccRecords).getIsimPcscf();
+
+        List<String> pcscfAddresses = mPhoneSubInfoControllerUT.getImsPcscfAddresses(0, TAG);
+
+        assertNotNull(pcscfAddresses);
+        // Null or Empty string is not added to pcscf list
+        assertEquals(preDefinedPcscfs.length - 2, pcscfAddresses.size());
+    }
+
+    @Test
+    public void getImsPcscfAddresses_IsimNotLoadedError() {
+        doReturn(true).when(mFeatureFlags).supportIsimRecord();
+        doReturn(null).when(mPhone).getIsimRecords();
+
+        try {
+            mPhoneSubInfoControllerUT.getImsPcscfAddresses(0, TAG);
+            fail();
+        } catch (Exception ex) {
+            assertTrue(ex instanceof IllegalStateException);
+            assertTrue(ex.getMessage().contains("ISIM is not loaded"));
+        }
+    }
+
+    @Test
+    public void getImsPcscfAddresses_InValidSubIdCheck() {
+        doReturn(true).when(mFeatureFlags).supportIsimRecord();
+
+        try {
+            mPhoneSubInfoControllerUT.getImsPcscfAddresses(-1, TAG);
+            fail();
+        } catch (Exception ex) {
+            assertTrue(ex instanceof IllegalArgumentException);
+            assertTrue(ex.getMessage().contains("Invalid subscription"));
+        }
+    }
+
+    @Test
+    public void getImsPcscfAddresses_NoReadPrivilegedPermission() {
+        mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
+        doReturn(true).when(mFeatureFlags).supportIsimRecord();
+
+        try {
+            mPhoneSubInfoControllerUT.getImsPcscfAddresses(0, TAG);
+            fail();
+        } catch (Exception ex) {
+            assertTrue(ex instanceof SecurityException);
+            assertTrue(ex.getMessage().contains("getImsPcscfAddresses"));
+        }
+
+        mContextFixture.addCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE);
+    }
+
+    @Test
+    public void getImsPcscfAddresses_FlagDisabled() {
+        String[] preDefinedPcscfs = new String[3];
+        preDefinedPcscfs[0] = "127.0.0.1";
+        preDefinedPcscfs[1] = "192.168.0.1";
+        preDefinedPcscfs[2] = "::1";
+        doReturn(false).when(mFeatureFlags).supportIsimRecord();
+        doReturn(mIsimUiccRecords).when(mPhone).getIsimRecords();
+        doReturn(preDefinedPcscfs).when(mIsimUiccRecords).getIsimPcscf();
+
+        List<String> pcscfAddresses = mPhoneSubInfoControllerUT.getImsPcscfAddresses(0, TAG);
+
+        assertNotNull(pcscfAddresses);
+        assertEquals(0, pcscfAddresses.size());
+    }
 }
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
index 53ecac3..b8316cb 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
@@ -1086,7 +1086,8 @@
 
     @Test
     public void testSendSmsToDatagramDispatcher() {
-        when(mSatelliteController.isInCarrierRoamingNbIotNtn(any(Phone.class))).thenReturn(true);
+        when(mSatelliteController.shouldSendSmsToDatagramDispatcher(any(Phone.class)))
+                .thenReturn(true);
         mSmsDispatchersController.sendText("1111", "2222", "text", mSentIntent, null, null,
                 "test-app", mCallingUserId, false, 0, false, 10, false, 1L, false);
         processAllMessages();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java
index c983d4c..1846bae 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsMessageBodyTest.java
@@ -23,7 +23,9 @@
 
 import com.android.telephony.Rlog;
 
+import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mockito;
 
 /**
  * Test cases to verify selection of the optimal 7 bit encoding tables
@@ -252,6 +254,11 @@
      */
     private static final int UDH_SEPTET_COST_CONCATENATED_MESSAGE = 6;
 
+    @Before
+    public void setUp() {
+        TelephonyManager.setupISmsForTest(Mockito.mock(ISms.class));
+    }
+
     @Test
     public void testCalcLengthAscii() throws Exception {
         StringBuilder sb = new StringBuilder(320);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsUsageMonitorShortCodeTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsUsageMonitorShortCodeTest.java
index 3b637c9..1e1e43f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsUsageMonitorShortCodeTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsUsageMonitorShortCodeTest.java
@@ -26,6 +26,8 @@
 
 import android.os.Looper;
 
+import com.android.internal.telephony.flags.FeatureFlagsImpl;
+
 import org.junit.Ignore;
 
 /**
@@ -465,7 +467,8 @@
         if (Looper.myLooper() == null) {
             Looper.prepare();
         }
-        SmsUsageMonitor monitor = new SmsUsageMonitor(TestApplication.getAppContext());
+        SmsUsageMonitor monitor = new SmsUsageMonitor(TestApplication.getAppContext(),
+                new FeatureFlagsImpl());
         for (ShortCodeTest test : sShortCodeTests) {
             assertEquals("country: " + test.countryIso + " number: " + test.address,
                     test.category, monitor.checkDestination(test.address, test.countryIso));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
index 1f5a26b..2ff38b2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyRegistryTest.java
@@ -42,6 +42,7 @@
 import android.content.pm.UserInfo;
 import android.net.LinkProperties;
 import android.os.Build;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
@@ -68,6 +69,7 @@
 import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
+import android.telephony.satellite.NtnSignalStrength;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.text.TextUtils;
@@ -104,6 +106,7 @@
     // Mocked classes
     private SubscriptionInfo mMockSubInfo;
     private TelephonyRegistry.ConfigurationProvider mMockConfigurationProvider;
+    private IBinder mMockIBinder;
 
     private TelephonyCallbackWrapper mTelephonyCallback;
     private List<LinkCapacityEstimate> mLinkCapacityEstimateList;
@@ -129,6 +132,8 @@
     private boolean mCarrierRoamingNtnMode;
     private boolean mCarrierRoamingNtnEligible;
     private List<Integer> mCarrierRoamingNtnAvailableServices;
+    private NtnSignalStrength mCarrierRoamingNtnSignalStrength;
+    private boolean mIsSatelliteEnabled;
 
     // All events contribute to TelephonyRegistry#isPhoneStatePermissionRequired
     private static final Set<Integer> READ_PHONE_STATE_EVENTS;
@@ -340,6 +345,24 @@
             invocationCount.incrementAndGet();
             mCarrierRoamingNtnAvailableServices = services;
         }
+
+        @Override
+        public void onCarrierRoamingNtnSignalStrengthChanged(NtnSignalStrength ntnSignalStrength) {
+            invocationCount.incrementAndGet();
+            mCarrierRoamingNtnSignalStrength = ntnSignalStrength;
+        }
+    }
+
+    public class MySatelliteStateChangeListener implements ISatelliteStateChangeListener {
+        @Override
+        public void onSatelliteEnabledStateChanged(boolean isEnabled) throws RemoteException {
+            mIsSatelliteEnabled = isEnabled;
+        }
+
+        @Override
+        public IBinder asBinder() {
+            return mMockIBinder;
+        }
     }
 
     private void addTelephonyRegistryService() {
@@ -354,6 +377,7 @@
         super.setUp(getClass().getSimpleName());
         mMockSubInfo = mock(SubscriptionInfo.class);
         mMockConfigurationProvider = mock(TelephonyRegistry.ConfigurationProvider.class);
+        mMockIBinder = mock(IBinder.class);
         when(mMockConfigurationProvider.getRegistrationLimit()).thenReturn(-1);
         when(mMockConfigurationProvider.isRegistrationLimitEnabledInPlatformCompat(anyInt()))
                 .thenReturn(false);
@@ -381,6 +405,7 @@
         processAllMessages();
         assertEquals(mTelephonyRegistry.asBinder(),
                 ServiceManager.getService("telephony.registry"));
+        doReturn(new int[]{1}).when(mSubscriptionManager).getActiveSubscriptionIdList();
     }
 
     @After
@@ -1724,4 +1749,93 @@
                 .mapToInt(Integer::intValue).toArray();
         assertTrue(Arrays.equals(carrierServices, services));
     }
+
+    @Test
+    @EnableFlags(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public void testNotifyCarrierRoamingNtnSignalStrengthChanged() {
+        int subId = INVALID_SUBSCRIPTION_ID;
+        doReturn(mMockSubInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
+        doReturn(0/*slotIndex*/).when(mMockSubInfo).getSimSlotIndex();
+        int[] events = {TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_SIGNAL_STRENGTH_CHANGED};
+
+        mTelephonyRegistry.listenWithEventList(false, false, subId, mContext.getOpPackageName(),
+                mContext.getAttributionTag(), mTelephonyCallback.callback, events, true);
+
+        mTelephonyRegistry.notifyCarrierRoamingNtnSignalStrengthChanged(subId,
+                new NtnSignalStrength(NtnSignalStrength.NTN_SIGNAL_STRENGTH_GOOD));
+        processAllMessages();
+        assertEquals(mCarrierRoamingNtnSignalStrength.getLevel(),
+                NtnSignalStrength.NTN_SIGNAL_STRENGTH_GOOD);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_SATELLITE_STATE_CHANGE_LISTENER)
+    public void testNotifySatelliteStateChanged_onRegistration_getNotified() {
+        MySatelliteStateChangeListener listener = new MySatelliteStateChangeListener();
+        // Set initial satellite enabled state to true
+        mTelephonyRegistry.notifySatelliteStateChanged(true);
+
+        try {
+            // Start monitoring
+            mTelephonyRegistry.addSatelliteStateChangeListener(listener,
+                    mContext.getOpPackageName(), mContext.getAttributionTag());
+            processAllMessages();
+
+            // verify latest state is immediately available on registration
+            assertTrue(mIsSatelliteEnabled);
+        } finally {
+            // Clean up
+            mTelephonyRegistry.removeSatelliteStateChangeListener(listener,
+                    mContext.getOpPackageName());
+        }
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_SATELLITE_STATE_CHANGE_LISTENER)
+    public void testNotifySatelliteStateChanged_duringRegistration_getNotified() {
+        MySatelliteStateChangeListener listener = new MySatelliteStateChangeListener();
+        // Set initial satellite enabled state to true
+        mTelephonyRegistry.notifySatelliteStateChanged(true);
+
+        try {
+            // Start monitoring
+            mTelephonyRegistry.addSatelliteStateChangeListener(listener,
+                    mContext.getOpPackageName(), mContext.getAttributionTag());
+
+            // Satellite enabled state changed
+            mTelephonyRegistry.notifySatelliteStateChanged(false);
+            processAllMessages();
+            // We can receive the new state
+            assertFalse(mIsSatelliteEnabled);
+        } finally {
+            // Clean up
+            mTelephonyRegistry.removeSatelliteStateChangeListener(listener,
+                    mContext.getOpPackageName());
+        }
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_SATELLITE_STATE_CHANGE_LISTENER)
+    public void testNotifySatelliteStateChanged_removeRegistration_notNotified() {
+        MySatelliteStateChangeListener listener = new MySatelliteStateChangeListener();
+        // Set initial satellite enabled state to true
+        mTelephonyRegistry.notifySatelliteStateChanged(true);
+
+        try {
+            // Start monitoring
+            mTelephonyRegistry.addSatelliteStateChangeListener(listener,
+                    mContext.getOpPackageName(), mContext.getAttributionTag());
+            mTelephonyRegistry.notifySatelliteStateChanged(false);
+        } finally {
+            // Stop monitoring from now on
+            mTelephonyRegistry.removeSatelliteStateChangeListener(listener,
+                    mContext.getOpPackageName());
+        }
+
+        // Satellite enabled state changed again
+        mTelephonyRegistry.notifySatelliteStateChanged(true);
+        processAllMessages();
+        // We should not receive the new state change after monitoring end
+        assertFalse(mIsSatelliteEnabled);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index d80c9a2..88eea32 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -747,6 +747,8 @@
         doReturn(mDataRetryManager).when(mDataNetworkController).getDataRetryManager();
         doReturn(mCarrierPrivilegesTracker).when(mPhone).getCarrierPrivilegesTracker();
         doReturn(0).when(mPhone).getPhoneId();
+        doReturn(true).when(mPhone).hasCalling();
+        doReturn(true).when(mPhone2).hasCalling();
 
         //mUiccController
         doReturn(mUiccCardApplication3gpp).when(mUiccController).getUiccCardApplication(anyInt(),
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 3f919ae..df14080 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/emergency/EmergencyStateTrackerTest.java
@@ -270,6 +270,7 @@
     @Test
     @SmallTest
     public void startEmergencyCall_radioOff_turnOnRadioHangupCallTurnOffRadio() {
+        android.telecom.Connection testConnection = new android.telecom.Connection() {};
         EmergencyStateTracker emergencyStateTracker = setupEmergencyStateTracker(
                 true /* isSuplDdsSwitchRequiredForEmergencyCall */);
         // Create test Phones and set radio off
@@ -285,7 +286,7 @@
                 .build();
         doReturn(nri).when(ss).getNetworkRegistrationInfo(anyInt(), anyInt());
         CompletableFuture<Integer> future = emergencyStateTracker.startEmergencyCall(testPhone,
-                mTestConnection1, false);
+                testConnection, false);
 
         // startEmergencyCall should trigger radio on
         ArgumentCaptor<RadioOnStateListener.Callback> callback = ArgumentCaptor
@@ -294,7 +295,8 @@
                 eq(false), eq(DEFAULT_WAIT_FOR_IN_SERVICE_TIMEOUT_MS));
 
         // Hangup the call
-        emergencyStateTracker.endCall(mTestConnection1);
+        testConnection.setDisconnected(null);
+        emergencyStateTracker.endCall(testConnection);
 
         // onTimeout and isOkToCall should return true even in case radion is off
         assertTrue(callback.getValue()
@@ -305,6 +307,7 @@
         callback.getValue().onComplete(null, true);
 
         assertFalse(future.isDone());
+        verify(testPhone).setRadioPower(true, false, false, true);
     }
 
     /**
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
index 4abf33f..130fba8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -46,6 +47,7 @@
 import android.os.Looper;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyManager;
@@ -69,8 +71,10 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -95,13 +99,14 @@
             "TestCarrier2Pkg", "Carrier2ImsService");
 
     private static final int NUM_MAX_SLOTS = 2;
-    private static final String TAG = ImsResolverTest.class.getSimpleName();
+    private static final UserHandle TEST_USER_HANDLE = UserHandle.of(Integer.MAX_VALUE);
 
     // Mocked classes
     Context mMockContext;
     PackageManager mMockPM;
     ImsResolver.SubscriptionManagerProxy mTestSubscriptionManagerProxy;
     ImsResolver.TelephonyManagerProxy mTestTelephonyManagerProxy;
+    ImsResolver.ActivityManagerProxy mTestActivityManagerProxy;
     CarrierConfigManager mMockCarrierConfigManager;
     UserManager mMockUserManager;
     ImsResolver.ImsDynamicQueryManagerFactory mMockQueryManagerFactory;
@@ -112,6 +117,7 @@
     private BroadcastReceiver mTestPackageBroadcastReceiver;
     private BroadcastReceiver mTestCarrierConfigReceiver;
     private BroadcastReceiver mTestBootCompleteReceiver;
+    private BroadcastReceiver mTestUserChangedReceiver;
     private ImsServiceFeatureQueryManager.Listener mDynamicQueryListener;
     private PersistableBundle[] mCarrierConfigs;
     private FeatureFlags mFeatureFlags;
@@ -124,12 +130,14 @@
         mMockPM = mock(PackageManager.class);
         mTestSubscriptionManagerProxy = mock(ImsResolver.SubscriptionManagerProxy.class);
         mTestTelephonyManagerProxy = mock(ImsResolver.TelephonyManagerProxy.class);
+        mTestActivityManagerProxy = mock(ImsResolver.ActivityManagerProxy.class);
         mMockCarrierConfigManager = mock(CarrierConfigManager.class);
         mMockUserManager = mock(UserManager.class);
         mMockQueryManagerFactory = mock(ImsResolver.ImsDynamicQueryManagerFactory.class);
         mMockQueryManager = mock(ImsServiceFeatureQueryManager.class);
         mMockRepo = mock(ImsFeatureBinderRepository.class);
         mFeatureFlags = mock(FeatureFlags.class);
+        when(mFeatureFlags.imsResolverUserAware()).thenReturn(true);
     }
 
     @After
@@ -411,7 +419,7 @@
 
         ArgumentCaptor<SparseIntArray> arrayCaptor =
                         ArgumentCaptor.forClass(SparseIntArray.class);
-        verify(controller).bind(eq(features), arrayCaptor.capture());
+        verify(controller).bind(eq(mContext.getUser()), eq(features), arrayCaptor.capture());
         SparseIntArray slotIdToSubIdMap = arrayCaptor.getValue();
         SparseIntArray compareMap = new SparseIntArray();
         compareMap.put(0, 0);
@@ -469,11 +477,14 @@
         when(mMockQueryManager.isQueryInProgress()).thenReturn(false);
         setupDynamicQueryFeatures(TEST_CARRIER_2_DEFAULT_NAME, featuresAll, 1);
 
-        verify(deviceController).bind(eq(featuresDevice), any(SparseIntArray.class));
+        verify(deviceController).bind(eq(mContext.getUser()), eq(featuresDevice),
+                any(SparseIntArray.class));
         verify(deviceController, never()).unbind();
-        verify(carrierController1).bind(eq(featuresMmTel), any(SparseIntArray.class));
+        verify(carrierController1).bind(eq(mContext.getUser()), eq(featuresMmTel),
+                any(SparseIntArray.class));
         verify(carrierController1, never()).unbind();
-        verify(carrierController2).bind(eq(featuresRcs), any(SparseIntArray.class));
+        verify(carrierController2).bind(eq(mContext.getUser()), eq(featuresRcs),
+                any(SparseIntArray.class));
         verify(carrierController2, never()).unbind();
         assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController1.getComponentName());
         assertEquals(TEST_CARRIER_2_DEFAULT_NAME, carrierController2.getComponentName());
@@ -521,11 +532,14 @@
         when(mMockQueryManager.isQueryInProgress()).thenReturn(false);
         setupDynamicQueryFeatures(TEST_CARRIER_2_DEFAULT_NAME, allFeatures, 1);
 
-        verify(deviceController, never()).bind(any(), any(SparseIntArray.class));
+        verify(deviceController, never()).bind(eq(mContext.getUser()), any(),
+                any(SparseIntArray.class));
         verify(deviceController, never()).unbind();
-        verify(carrierController1).bind(eq(featuresMmTel), any(SparseIntArray.class));
+        verify(carrierController1).bind(eq(mContext.getUser()), eq(featuresMmTel),
+                any(SparseIntArray.class));
         verify(carrierController1, never()).unbind();
-        verify(carrierController2).bind(eq(featuresRcs), any(SparseIntArray.class));
+        verify(carrierController2).bind(eq(mContext.getUser()), eq(featuresRcs),
+                any(SparseIntArray.class));
         verify(carrierController2, never()).unbind();
         assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController1.getComponentName());
         assertEquals(TEST_CARRIER_2_DEFAULT_NAME, carrierController2.getComponentName());
@@ -553,7 +567,7 @@
         startBindCarrierConfigAlreadySet();
         setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, features, 1);
 
-        verify(controller).bind(eq(features), any(SparseIntArray.class));
+        verify(controller).bind(eq(mContext.getUser()), eq(features), any(SparseIntArray.class));
         verify(controller, never()).unbind();
         assertEquals(TEST_CARRIER_DEFAULT_NAME, controller.getComponentName());
     }
@@ -582,7 +596,7 @@
         // We will not bind with FEATURE_EMERGENCY_MMTEL
         features.remove(new ImsFeatureConfiguration.FeatureSlotPair(0,
                 ImsFeature.FEATURE_EMERGENCY_MMTEL));
-        verify(controller).bind(eq(features), any(SparseIntArray.class));
+        verify(controller).bind(eq(mContext.getUser()), eq(features), any(SparseIntArray.class));
         verify(controller, never()).unbind();
         assertEquals(TEST_CARRIER_DEFAULT_NAME, controller.getComponentName());
     }
@@ -606,20 +620,23 @@
         setupPackageQuery(info);
         ImsServiceController deviceController1 = mock(ImsServiceController.class);
         ImsServiceController deviceController2 = mock(ImsServiceController.class);
-        setImsServiceControllerFactory(deviceController1, deviceController2, null, null);
+        setImsServiceControllerDDCFactory(deviceController1, deviceController2, null);
         // Bind using default features
         startBindNoCarrierConfig(1);
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet1 =
                 convertToHashSet(featuresController1, 0);
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet2 =
                 convertToHashSet(featuresController2, 0);
-        verify(deviceController1).bind(eq(featureSet1), any(SparseIntArray.class));
-        verify(deviceController2).bind(eq(featureSet2), any(SparseIntArray.class));
+        verify(deviceController1).bind(eq(mContext.getUser()), eq(featureSet1),
+                any(SparseIntArray.class));
+        verify(deviceController2).bind(eq(mContext.getUser()), eq(featureSet2),
+                any(SparseIntArray.class));
         // simulate ImsServiceController binding and setup
-        mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.FEATURE_EMERGENCY_MMTEL,
+        mTestImsResolver.imsServiceFeatureCreated(0, 0, ImsFeature.FEATURE_EMERGENCY_MMTEL,
                 deviceController1);
-        mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.FEATURE_MMTEL, deviceController1);
-        mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.FEATURE_RCS, deviceController2);
+        mTestImsResolver.imsServiceFeatureCreated(0, 0, ImsFeature.FEATURE_MMTEL,
+                deviceController1);
+        mTestImsResolver.imsServiceFeatureCreated(0, 0, ImsFeature.FEATURE_RCS, deviceController2);
 
         mTestImsResolver.enableIms(0 /*slotId*/);
         // Verify enableIms is only called once per controller.
@@ -651,7 +668,7 @@
         // Bind without emergency calling
         startBindCarrierConfigAlreadySet();
         setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, features, 1);
-        verify(controller).bind(eq(features), any(SparseIntArray.class));
+        verify(controller).bind(eq(mContext.getUser()), eq(features), any(SparseIntArray.class));
         verify(controller, never()).unbind();
         assertEquals(TEST_CARRIER_DEFAULT_NAME, controller.getComponentName());
 
@@ -686,8 +703,8 @@
         startBindCarrierConfigAlreadySet();
 
         processAllMessages();
-        verify(mMockQueryManager, never()).startQuery(any(), any());
-        verify(controller, never()).bind(any(), any(SparseIntArray.class));
+        verify(mMockQueryManager, never()).startQuery(any(), any(), any());
+        verify(controller, never()).bind(any(), any(), any(SparseIntArray.class));
         verify(controller, never()).unbind();
     }
 
@@ -719,7 +736,7 @@
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet = convertToHashSet(features, 0);
         ArgumentCaptor<SparseIntArray> arrayCaptor =
                         ArgumentCaptor.forClass(SparseIntArray.class);
-        verify(controller).bind(eq(featureSet), arrayCaptor.capture());
+        verify(controller).bind(eq(mContext.getUser()), eq(featureSet), arrayCaptor.capture());
         SparseIntArray slotIdToSubIdMap = arrayCaptor.getValue();
         SparseIntArray compareMap = new SparseIntArray();
         compareMap.put(0, 0);
@@ -729,7 +746,7 @@
         }
 
         verify(controller, never()).unbind();
-        verify(mMockQueryManager, never()).startQuery(any(), any());
+        verify(mMockQueryManager, never()).startQuery(any(), any(), any());
         assertEquals(TEST_DEVICE_DEFAULT_NAME, controller.getComponentName());
     }
 
@@ -763,7 +780,7 @@
         featureSet.addAll(convertToHashSet(features, 1));
         ArgumentCaptor<SparseIntArray> arrayCaptor =
                         ArgumentCaptor.forClass(SparseIntArray.class);
-        verify(controller).bind(eq(featureSet), arrayCaptor.capture());
+        verify(controller).bind(eq(mContext.getUser()), eq(featureSet), arrayCaptor.capture());
         SparseIntArray slotIdToSubIdMap = arrayCaptor.getValue();
         assertEquals(slotIdToSubIdMap.size(), 2);
         SparseIntArray compareMap = new SparseIntArray();
@@ -774,7 +791,7 @@
             assertEquals(slotIdToSubIdMap.get(i), compareMap.get(i));
         }
         verify(controller, never()).unbind();
-        verify(mMockQueryManager, never()).startQuery(any(), any());
+        verify(mMockQueryManager, never()).startQuery(any(), any(), any());
         assertEquals(TEST_DEVICE_DEFAULT_NAME, controller.getComponentName());
 
         // Change number of SIMs and verify the features in the ImsServiceController are changed
@@ -823,7 +840,7 @@
 
         ArgumentCaptor<SparseIntArray> arrayCaptor =
                         ArgumentCaptor.forClass(SparseIntArray.class);
-        verify(controller).bind(eq(featureSet), arrayCaptor.capture());
+        verify(controller).bind(eq(mContext.getUser()), eq(featureSet), arrayCaptor.capture());
         SparseIntArray slotIdToSubIdMap = arrayCaptor.getValue();
         assertEquals(slotIdToSubIdMap.size(), 1);
         SparseIntArray compareMap = new SparseIntArray();
@@ -833,7 +850,7 @@
             assertEquals(slotIdToSubIdMap.get(i), compareMap.get(i));
         }
         verify(controller, never()).unbind();
-        verify(mMockQueryManager, never()).startQuery(any(), any());
+        verify(mMockQueryManager, never()).startQuery(any(), any(), any());
         assertEquals(TEST_DEVICE_DEFAULT_NAME, controller.getComponentName());
 
         // Change number of SIMs and verify the features in the ImsServiceController are changed
@@ -895,17 +912,19 @@
         processAllMessages();
         // ensure that startQuery was called
         verify(mMockQueryManager, times(1)).startQuery(eq(TEST_DEVICE_DEFAULT_NAME),
-                any(String.class));
+                any(UserHandle.class), any(String.class));
 
         verify(mMockQueryManager, times(1)).startQuery(eq(TEST_DEVICE2_DEFAULT_NAME),
-                any(String.class));
+                any(UserHandle.class), any(String.class));
 
         mDynamicQueryListener.onComplete(TEST_DEVICE_DEFAULT_NAME, deviceFeatures1);
         mDynamicQueryListener.onComplete(TEST_DEVICE2_DEFAULT_NAME, deviceFeatures2);
         processAllMessages();
 
-        verify(deviceController, times(2)).bind(eq(deviceFeatures1), any(SparseIntArray.class));
-        verify(deviceController2, times(1)).bind(eq(deviceFeatures2), any(SparseIntArray.class));
+        verify(deviceController, times(2)).bind(eq(mContext.getUser()), eq(deviceFeatures1),
+                any(SparseIntArray.class));
+        verify(deviceController2, times(1)).bind(eq(mContext.getUser()), eq(deviceFeatures2),
+                any(SparseIntArray.class));
     }
 
     /**
@@ -942,7 +961,8 @@
         setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
 
         // Verify that all features that have been defined for the carrier override are bound
-        verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class));
+        verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures),
+                any(SparseIntArray.class));
         verify(carrierController, never()).unbind();
         assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName());
         // Verify that all features that are not defined in the carrier override are bound in the
@@ -950,7 +970,8 @@
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
                 convertToHashSet(deviceFeatures, 0);
         deviceFeatureSet.removeAll(carrierFeatures);
-        verify(deviceController).bind(eq(deviceFeatureSet), any(SparseIntArray.class));
+        verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet),
+                any(SparseIntArray.class));
         verify(deviceController, never()).unbind();
         assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
 
@@ -1006,7 +1027,8 @@
         setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
 
         // Verify that all features that have been defined for the carrier override are bound
-        verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class));
+        verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures),
+                any(SparseIntArray.class));
         verify(carrierController, never()).unbind();
         assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName());
         // Verify that all features that are not defined in the carrier override are bound in the
@@ -1015,7 +1037,8 @@
                 convertToHashSet(deviceFeatures, 0);
         deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 1));
         deviceFeatureSet.removeAll(carrierFeatures);
-        verify(deviceController).bind(eq(deviceFeatureSet), any(SparseIntArray.class));
+        verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet),
+                any(SparseIntArray.class));
         verify(deviceController, never()).unbind();
         assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
 
@@ -1062,9 +1085,9 @@
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet = convertToHashSet(features, 0);
         // There is no carrier override set, so make sure that the ImsServiceController binds
         // to all SIMs.
-        verify(controller).bind(eq(featureSet), any(SparseIntArray.class));
+        verify(controller).bind(eq(mContext.getUser()), eq(featureSet), any(SparseIntArray.class));
         verify(controller, never()).unbind();
-        verify(mMockQueryManager, never()).startQuery(any(), any());
+        verify(mMockQueryManager, never()).startQuery(any(), any(), any());
         assertEquals(TEST_DEVICE_DEFAULT_NAME, controller.getComponentName());
     }
 
@@ -1100,7 +1123,8 @@
         setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
 
         // Verify that all features that have been defined for the carrier override are bound
-        verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class));
+        verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures),
+                any(SparseIntArray.class));
         verify(carrierController, never()).unbind();
         assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName());
         // Verify that all features that are not defined in the carrier override are bound in the
@@ -1108,7 +1132,8 @@
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
                 convertToHashSet(deviceFeatures, 0);
         deviceFeatureSet.removeAll(carrierFeatures);
-        verify(deviceController).bind(eq(deviceFeatureSet), any(SparseIntArray.class));
+        verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet),
+                any(SparseIntArray.class));
         verify(deviceController, never()).unbind();
         assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
     }
@@ -1127,11 +1152,11 @@
 
         // Callback from mock ImsServiceControllers
         // All features on slot 1 should be the device default
-        mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.FEATURE_MMTEL, deviceController);
-        mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.FEATURE_RCS, deviceController);
-        mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.FEATURE_MMTEL, deviceController);
+        mTestImsResolver.imsServiceFeatureCreated(1, 1, ImsFeature.FEATURE_MMTEL, deviceController);
+        mTestImsResolver.imsServiceFeatureCreated(1, 1, ImsFeature.FEATURE_RCS, deviceController);
+        mTestImsResolver.imsServiceFeatureCreated(0, 0, ImsFeature.FEATURE_MMTEL, deviceController);
         // The carrier override contains this feature
-        mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.FEATURE_RCS, carrierController);
+        mTestImsResolver.imsServiceFeatureCreated(0, 0, ImsFeature.FEATURE_RCS, carrierController);
     }
 
     /**
@@ -1155,7 +1180,7 @@
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet =
                 convertToHashSet(features, 0);
         featureSet.addAll(convertToHashSet(features, 1));
-        verify(controller).bind(eq(featureSet), any(SparseIntArray.class));
+        verify(controller).bind(eq(mContext.getUser()), eq(featureSet), any(SparseIntArray.class));
 
         // add RCS to features list
         Set<String> newFeatures = new HashSet<>(features);
@@ -1191,7 +1216,7 @@
         setupPackageQuery(info);
         ImsServiceController deviceController1 = mock(ImsServiceController.class);
         ImsServiceController deviceController2 = mock(ImsServiceController.class);
-        setImsServiceControllerFactory(deviceController1, deviceController2, null, null);
+        setImsServiceControllerDDCFactory(deviceController1, deviceController2, null);
         // Bind using default features
         startBindNoCarrierConfig(2);
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet1 =
@@ -1200,8 +1225,10 @@
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet2 =
                 convertToHashSet(featuresController2, 0);
         featureSet2.addAll(convertToHashSet(featuresController2, 1));
-        verify(deviceController1).bind(eq(featureSet1), any(SparseIntArray.class));
-        verify(deviceController2).bind(eq(featureSet2), any(SparseIntArray.class));
+        verify(deviceController1).bind(eq(mContext.getUser()), eq(featureSet1),
+                any(SparseIntArray.class));
+        verify(deviceController2).bind(eq(mContext.getUser()), eq(featureSet2),
+                any(SparseIntArray.class));
 
         // add RCS to features list for device 1
         Set<String> newFeatures1 = new HashSet<>(featuresController1);
@@ -1239,7 +1266,7 @@
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet =
                 convertToHashSet(features, 0);
         featureSet.addAll(convertToHashSet(features, 1));
-        verify(controller).bind(eq(featureSet), any(SparseIntArray.class));
+        verify(controller).bind(eq(mContext.getUser()), eq(featureSet), any(SparseIntArray.class));
 
         // add RCS to features list
         Set<String> newFeatures = new HashSet<>(features);
@@ -1288,7 +1315,8 @@
         setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
 
         // Verify that all features that have been defined for the carrier override are bound
-        verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class));
+        verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures),
+                any(SparseIntArray.class));
         verify(carrierController, never()).unbind();
         assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName());
         // Verify that all features that are not defined in the carrier override are bound in the
@@ -1297,7 +1325,8 @@
                 convertToHashSet(deviceFeatures, 1);
         deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
         deviceFeatureSet.removeAll(carrierFeatures);
-        verify(deviceController).bind(eq(deviceFeatureSet), any(SparseIntArray.class));
+        verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet),
+                any(SparseIntArray.class));
         verify(deviceController, never()).unbind();
         assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
 
@@ -1356,13 +1385,13 @@
         ImsServiceController deviceController1 = mock(ImsServiceController.class);
         ImsServiceController deviceController2 = mock(ImsServiceController.class);
         ImsServiceController carrierController = mock(ImsServiceController.class);
-        setImsServiceControllerFactory(deviceController1, deviceController2, carrierController,
-                null);
+        setImsServiceControllerDDCFactory(deviceController1, deviceController2, carrierController);
 
         startBindCarrierConfigAlreadySet();
         setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
         // Verify that all features that have been defined for the carrier override are bound
-        verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class));
+        verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures),
+                any(SparseIntArray.class));
         verify(carrierController, never()).unbind();
         assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName());
         // Verify that all features that are not defined in the carrier override are bound in the
@@ -1370,13 +1399,16 @@
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet1 =
                 convertToHashSet(deviceFeatures1, 1);
         deviceFeatureSet1.removeAll(carrierFeatures);
-        verify(deviceController1).bind(eq(deviceFeatureSet1), any(SparseIntArray.class));
+        verify(deviceController1).bind(eq(mContext.getUser()), eq(deviceFeatureSet1),
+                any(SparseIntArray.class));
         verify(deviceController1, never()).unbind();
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet2 =
                 convertToHashSet(deviceFeatures2, 0);
         deviceFeatureSet2.addAll(convertToHashSet(deviceFeatures2, 1));
+        deviceFeatureSet2.addAll(convertToHashSet(deviceFeatures2, 1));
         deviceFeatureSet2.removeAll(carrierFeatures);
-        verify(deviceController2).bind(eq(deviceFeatureSet2), any(SparseIntArray.class));
+        verify(deviceController2).bind(eq(mContext.getUser()), eq(deviceFeatureSet2),
+                any(SparseIntArray.class));
         verify(deviceController2, never()).unbind();
         assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController1.getComponentName());
         assertEquals(TEST_DEVICE2_DEFAULT_NAME, deviceController2.getComponentName());
@@ -1392,7 +1424,7 @@
         verify(carrierController).changeImsServiceFeatures(eq(carrierFeatures),
                 any(SparseIntArray.class));
         deviceFeatureSet1.removeAll(carrierFeatures);
-        verify(deviceController1, times(2)).changeImsServiceFeatures(eq(deviceFeatureSet1),
+        verify(deviceController1).changeImsServiceFeatures(eq(deviceFeatureSet1),
                 any(SparseIntArray.class));
         deviceFeatureSet2.removeAll(carrierFeatures);
         verify(deviceController2).changeImsServiceFeatures(eq(deviceFeatureSet2),
@@ -1429,7 +1461,8 @@
         startBindCarrierConfigAlreadySet();
         setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
         // Verify that all features that have been defined for the carrier override are bound
-        verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class));
+        verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures),
+                any(SparseIntArray.class));
         verify(carrierController, never()).unbind();
         assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName());
         // Verify that all features that are not defined in the carrier override are bound in the
@@ -1438,7 +1471,8 @@
                 convertToHashSet(deviceFeatures, 1);
         deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
         deviceFeatureSet.removeAll(carrierFeatures);
-        verify(deviceController).bind(eq(deviceFeatureSet), any(SparseIntArray.class));
+        verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet),
+                any(SparseIntArray.class));
         verify(deviceController, never()).unbind();
         assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
 
@@ -1497,14 +1531,20 @@
         setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
 
         // Verify that all features that have been defined for the carrier override are bound
-        verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class));
+        verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures),
+                any(SparseIntArray.class));
         // device features change
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
                 convertToHashSet(deviceFeatures, 1);
         deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
         deviceFeatureSet.removeAll(carrierFeatures);
-        verify(deviceController).changeImsServiceFeatures(eq(deviceFeatureSet),
-                any(SparseIntArray.class));
+        if (mFeatureFlags.imsResolverUserAware()) {
+            verify(deviceController).changeImsServiceFeatures(eq(deviceFeatureSet),
+                    any(SparseIntArray.class));
+        } else {
+            verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet),
+                    any(SparseIntArray.class));
+        }
     }
 
     /**
@@ -1644,7 +1684,8 @@
         assertNotNull(mTestImsResolver.getImsServiceInfoFromCache(
                 TEST_CARRIER_DEFAULT_NAME.getPackageName()));
         // Verify that carrier 2 is bound
-        verify(carrierController2).bind(eq(carrierFeatures2), any(SparseIntArray.class));
+        verify(carrierController2).bind(eq(mContext.getUser()), eq(carrierFeatures2),
+                any(SparseIntArray.class));
         assertNotNull(mTestImsResolver.getImsServiceInfoFromCache(
                 TEST_CARRIER_2_DEFAULT_NAME.getPackageName()));
         // device features change to accommodate for the features carrier 2 lacks
@@ -1692,7 +1733,8 @@
         setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
 
         // Verify that all features that have been defined for the carrier override are bound
-        verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class));
+        verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures),
+                any(SparseIntArray.class));
         // device features change
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
                 convertToHashSet(deviceFeatures, 1);
@@ -1733,13 +1775,15 @@
         setupDynamicQueryFeaturesFailure(TEST_CARRIER_DEFAULT_NAME, 1);
 
         // Verify that a bind never occurs for the carrier controller.
-        verify(carrierController, never()).bind(any(), any(SparseIntArray.class));
+        verify(carrierController, never()).bind(eq(mContext.getUser()), any(),
+                any(SparseIntArray.class));
         verify(carrierController, never()).unbind();
         // Verify that all features are used to bind to the device ImsService since the carrier
         // ImsService failed to bind properly.
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
                 convertToHashSet(deviceFeatures, 0);
-        verify(deviceController).bind(eq(deviceFeatureSet), any(SparseIntArray.class));
+        verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet),
+                any(SparseIntArray.class));
         verify(deviceController, never()).unbind();
         assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
     }
@@ -1774,18 +1818,21 @@
         setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
 
         // Verify that a bind never occurs for the carrier controller.
-        verify(carrierController).bind(eq(carrierFeatures), any(SparseIntArray.class));
+        verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures),
+                any(SparseIntArray.class));
         verify(carrierController, never()).unbind();
         // Verify that all features that are not defined in the carrier override are bound in the
         // device controller (including emergency voice for slot 0)
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
                 convertToHashSet(deviceFeatures, 0);
         deviceFeatureSet.removeAll(carrierFeatures);
-        verify(deviceController).bind(eq(deviceFeatureSet), any(SparseIntArray.class));
+        verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet),
+                any(SparseIntArray.class));
         verify(deviceController, never()).unbind();
         assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
 
-        mTestImsResolver.imsServiceBindPermanentError(TEST_CARRIER_DEFAULT_NAME);
+        mTestImsResolver.imsServiceBindPermanentError(TEST_CARRIER_DEFAULT_NAME,
+                mContext.getUser());
         processAllMessages();
         verify(carrierController).unbind();
         // Verify that the device ImsService features are changed to include the ones previously
@@ -1815,7 +1862,7 @@
         setupPackageQuery(info);
         ImsServiceController deviceController1 = mock(ImsServiceController.class);
         ImsServiceController deviceController2 = mock(ImsServiceController.class);
-        setImsServiceControllerFactory(deviceController1, deviceController2, null, null);
+        setImsServiceControllerDDCFactory(deviceController1, deviceController2, null);
 
         startBindNoCarrierConfig(1);
         processAllMessages();
@@ -1825,11 +1872,12 @@
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureResultSet =
                 convertToHashSet(featureResult, 0);
         featureResultSet.addAll(convertToHashSet(featureResult, 1));
-        verify(deviceController1).bind(eq(featureResultSet), any(SparseIntArray.class));
+        verify(deviceController1).bind(eq(mContext.getUser()), eq(featureResultSet),
+                any(SparseIntArray.class));
         verify(deviceController1, never()).unbind();
-        verify(deviceController2, never()).bind(any(), any(SparseIntArray.class));
+        verify(deviceController2, never()).bind(any(), any(), any(SparseIntArray.class));
         verify(deviceController2, never()).unbind();
-        verify(mMockQueryManager, never()).startQuery(any(), any());
+        verify(mMockQueryManager, never()).startQuery(any(), any(), any());
         assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController1.getComponentName());
     }
 
@@ -1852,7 +1900,7 @@
         setupPackageQuery(info);
         ImsServiceController deviceController1 = mock(ImsServiceController.class);
         ImsServiceController deviceController2 = mock(ImsServiceController.class);
-        setImsServiceControllerFactory(deviceController1, deviceController2, null, null);
+        setImsServiceControllerDDCFactory(deviceController1, deviceController2, null);
 
         startBindNoCarrierConfig(1);
         processAllMessages();
@@ -1862,11 +1910,12 @@
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureResultSet =
                 convertToHashSet(featureResult, 0);
         featureResultSet.addAll(convertToHashSet(featureResult, 1));
-        verify(deviceController1).bind(eq(featureResultSet), any(SparseIntArray.class));
+        verify(deviceController1).bind(eq(mContext.getUser()), eq(featureResultSet),
+                any(SparseIntArray.class));
         verify(deviceController1, never()).unbind();
-        verify(deviceController2, never()).bind(any(), any(SparseIntArray.class));
+        verify(deviceController2, never()).bind(any(), any(), any(SparseIntArray.class));
         verify(deviceController2, never()).unbind();
-        verify(mMockQueryManager, never()).startQuery(any(), any());
+        verify(mMockQueryManager, never()).startQuery(any(), any(), any());
         assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController1.getComponentName());
     }
 
@@ -1893,7 +1942,7 @@
         setupPackageQuery(info);
         ImsServiceController deviceController1 = mock(ImsServiceController.class);
         ImsServiceController deviceController2 = mock(ImsServiceController.class);
-        setImsServiceControllerFactory(deviceController1, deviceController2, null, null);
+        setImsServiceControllerDDCFactory(deviceController1, deviceController2, null);
 
         startBindNoCarrierConfig(1);
         processAllMessages();
@@ -1904,11 +1953,13 @@
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet2 =
                 convertToHashSet(features2, 0);
         featureSet2.addAll(convertToHashSet(features2, 1));
-        verify(deviceController1).bind(eq(featureSet1), any(SparseIntArray.class));
+        verify(deviceController1).bind(eq(mContext.getUser()), eq(featureSet1),
+                any(SparseIntArray.class));
         verify(deviceController1, never()).unbind();
-        verify(deviceController2).bind(eq(featureSet2), any(SparseIntArray.class));
+        verify(deviceController2).bind(eq(mContext.getUser()), eq(featureSet2),
+                any(SparseIntArray.class));
         verify(deviceController2, never()).unbind();
-        verify(mMockQueryManager, never()).startQuery(any(), any());
+        verify(mMockQueryManager, never()).startQuery(any(), any(), any());
         assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController1.getComponentName());
         assertEquals(TEST_DEVICE2_DEFAULT_NAME, deviceController2.getComponentName());
     }
@@ -1934,16 +1985,134 @@
         setupPackageQuery(info);
         ImsServiceController deviceController1 = mock(ImsServiceController.class);
         ImsServiceController deviceController2 = mock(ImsServiceController.class);
-        setImsServiceControllerFactory(deviceController1, deviceController2, null, null);
+        setImsServiceControllerDDCFactory(deviceController1, deviceController2, null);
 
         startBindNoCarrierConfig(1);
         processAllMessages();
 
-        verify(deviceController1, never()).bind(any(), any(SparseIntArray.class));
+        verify(deviceController1, never()).bind(any(), any(), any(SparseIntArray.class));
         verify(deviceController1, never()).unbind();
-        verify(deviceController2, never()).bind(any(), any(SparseIntArray.class));
+        verify(deviceController2, never()).bind(any(), any(), any(SparseIntArray.class));
         verify(deviceController2, never()).unbind();
-        verify(mMockQueryManager, never()).startQuery(any(), any());
+        verify(mMockQueryManager, never()).startQuery(any(), any(), any());
+    }
+
+    /**
+     * Change the current active user while having ImsServices in system user. The ImsService config
+     * should not change.
+     */
+    @Test
+    @SmallTest
+    public void testChangeCurrentUserServicesInSystem() throws RemoteException {
+        if (!mFeatureFlags.imsResolverUserAware()) {
+            return;
+        }
+        setupResolver(1 /*numSlots*/, TEST_DEVICE_DEFAULT_NAME.getPackageName(),
+                TEST_DEVICE_DEFAULT_NAME.getPackageName());
+        List<ResolveInfo> info = new ArrayList<>();
+        Set<String> deviceFeatures = new HashSet<>();
+        deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+        // Set the carrier override package for slot 0
+        setConfigCarrierStringMmTelRcs(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+        HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
+        // Carrier service doesn't support the voice feature.
+        carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
+        info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
+        info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
+        // Use device default package, which will load the ImsService that the device provides
+        setupPackageQuery(info);
+
+        ImsServiceController deviceController = mock(ImsServiceController.class);
+        ImsServiceController carrierController = mock(ImsServiceController.class);
+        setImsServiceControllerFactory(deviceController, carrierController);
+
+        startBindCarrierConfigAlreadySet();
+        setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
+
+        // Perform a user switch
+        userChanged(TEST_USER_HANDLE);
+        setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 2);
+
+
+        // Verify that all features that have been defined for the device/carrier override are bound
+        // and are not changed when the user changes.
+        verify(carrierController).bind(eq(mContext.getUser()), eq(carrierFeatures),
+                any(SparseIntArray.class));
+        verify(carrierController, atLeastOnce()).changeImsServiceFeatures(eq(carrierFeatures),
+                any(SparseIntArray.class));
+        verify(carrierController, never()).unbind();
+        HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
+                convertToHashSet(deviceFeatures, 0);
+        deviceFeatureSet.removeAll(carrierFeatures);
+        verify(deviceController).bind(eq(mContext.getUser()), eq(deviceFeatureSet),
+                any(SparseIntArray.class));
+        verify(deviceController, atLeastOnce()).changeImsServiceFeatures(eq(deviceFeatureSet),
+                any(SparseIntArray.class));
+        verify(deviceController, never()).unbind();
+    }
+
+    /**
+     * Change the current active user while having a carrier ImsService installed for second user.
+     * The features should change when the current user changes to the second user and back.
+     */
+    @Test
+    @SmallTest
+    public void testChangeCurrentUserCarrierInSecondUser() throws RemoteException {
+        if (!mFeatureFlags.imsResolverUserAware()) {
+            return;
+        }
+        setupResolver(1 /*numSlots*/, TEST_DEVICE_DEFAULT_NAME.getPackageName(),
+                TEST_DEVICE_DEFAULT_NAME.getPackageName());
+        Set<String> deviceFeatures = new HashSet<>();
+        deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+        // Set the carrier override package for slot 0
+        setConfigCarrierStringMmTelRcs(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+        HashSet<ImsFeatureConfiguration.FeatureSlotPair> carrierFeatures = new HashSet<>();
+        // Carrier service doesn't support the voice feature.
+        carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
+        // Use device default package, which will load the ImsService that the device provides
+        setupPackageQuery(Collections.singletonList(
+                getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true)));
+        setupPackageQueryForUser(Collections.singletonList(
+                        getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true)),
+                TEST_USER_HANDLE);
+
+        ImsServiceController deviceController = mock(ImsServiceController.class);
+        ImsServiceController carrierController = mock(ImsServiceController.class);
+        setImsServiceControllerFactory(deviceController, carrierController);
+
+        startBindCarrierConfigAlreadySet();
+
+        verify(carrierController, never()).bind(eq(mContext.getUser()), any(),
+                any(SparseIntArray.class));
+        verify(deviceController).bind(eq(mContext.getUser()),
+                eq(convertToHashSet(deviceFeatures, 0)), any(SparseIntArray.class));
+
+        // Perform a user switch
+        setBoundImsServiceControllerUser(carrierController, TEST_USER_HANDLE);
+        userChanged(TEST_USER_HANDLE);
+        setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 1);
+
+
+        // Verify the carrier controller was bound only when the user changed
+        verify(carrierController).bind(eq(TEST_USER_HANDLE), eq(carrierFeatures),
+                any(SparseIntArray.class));
+        verify(carrierController, never()).changeImsServiceFeatures(eq(carrierFeatures),
+                any(SparseIntArray.class));
+        verify(carrierController, never()).unbind();
+
+        HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatureSet =
+                convertToHashSet(deviceFeatures, 0);
+        deviceFeatureSet.removeAll(carrierFeatures);
+        verify(deviceController).changeImsServiceFeatures(eq(deviceFeatureSet),
+                any(SparseIntArray.class));
+        verify(deviceController, never()).unbind();
+    }
+
+    private void setCurrentUser(UserHandle handle) {
+        when(mTestActivityManagerProxy.getCurrentUser()).thenReturn(handle);
     }
 
     private void setupResolver(int numSlots, String deviceMmTelPkgName,
@@ -1970,12 +2139,15 @@
             when(mTestTelephonyManagerProxy.getSimState(any(Context.class), eq(i))).thenReturn(
                     TelephonyManager.SIM_STATE_READY);
         }
+        when(mMockContext.getUser()).thenReturn(mContext.getUser());
+        when(mTestActivityManagerProxy.getCurrentUser()).thenReturn(mContext.getUser());
 
         mTestImsResolver = new ImsResolver(mMockContext, deviceMmTelPkgName, deviceRcsPkgName,
                 numSlots, mMockRepo, Looper.myLooper(), mFeatureFlags);
 
         mTestImsResolver.setSubscriptionManagerProxy(mTestSubscriptionManagerProxy);
         mTestImsResolver.setTelephonyManagerProxy(mTestTelephonyManagerProxy);
+        mTestImsResolver.setActivityManagerProxy(mTestActivityManagerProxy);
         when(mMockQueryManagerFactory.create(any(Context.class),
                 any(ImsServiceFeatureQueryManager.Listener.class))).thenReturn(mMockQueryManager);
         mTestImsResolver.setImsDynamicQueryManagerFactory(mMockQueryManagerFactory);
@@ -1983,24 +2155,55 @@
     }
 
     private void setupPackageQuery(List<ResolveInfo> infos) {
-        // Only return info if not using the compat argument
-        when(mMockPM.queryIntentServicesAsUser(
+        doAnswer((Answer<List<ResolveInfo>>) invocation -> {
+            Intent intent = (Intent) invocation.getArguments()[0];
+            String pkg = intent.getPackage();
+            if (pkg == null) {
+                return infos;
+            } else {
+                for (ResolveInfo info : infos) {
+                    if (pkg.equals(info.serviceInfo.packageName)) {
+                        return Collections.singletonList(info);
+                    }
+                }
+            }
+            return Collections.emptyList();
+        }).when(mMockPM).queryIntentServicesAsUser(
+                // Only return info if not using the compat argument
                 argThat(argument -> ImsService.SERVICE_INTERFACE.equals(argument.getAction())),
-                anyInt(), any())).thenReturn(infos);
+                anyInt(), any());
+    }
+
+    private void setupPackageQueryForUser(List<ResolveInfo> infos, UserHandle user) {
+        doAnswer((Answer<List<ResolveInfo>>) invocation -> {
+            Intent intent = (Intent) invocation.getArguments()[0];
+            String pkg = intent.getPackage();
+            if (pkg == null) {
+                return infos;
+            } else {
+                for (ResolveInfo info : infos) {
+                    if (pkg.equals(info.serviceInfo.packageName)) {
+                        return Collections.singletonList(info);
+                    }
+                }
+            }
+            return Collections.emptyList();
+        }).when(mMockPM).queryIntentServicesAsUser(
+                // Only return info if not using the compat argument
+                argThat(argument -> ImsService.SERVICE_INTERFACE.equals(argument.getAction())),
+                anyInt(), eq(user));
     }
 
     private void setupPackageQuery(ComponentName name, Set<String> features,
             boolean isPermissionGranted) {
         List<ResolveInfo> info = new ArrayList<>();
         info.add(getResolveInfo(name, features, isPermissionGranted));
-        // Only return info if not using the compat argument
-        when(mMockPM.queryIntentServicesAsUser(
-                argThat(argument -> ImsService.SERVICE_INTERFACE.equals(argument.getAction())),
-                anyInt(), any())).thenReturn(info);
+        setupPackageQuery(info);
     }
 
     private ImsServiceController setupController() {
         ImsServiceController controller = mock(ImsServiceController.class);
+        when(controller.getBoundUser()).thenReturn(mContext.getUser());
         mTestImsResolver.setImsServiceControllerFactory(
                 new ImsResolver.ImsServiceControllerFactory() {
                     @Override
@@ -2028,15 +2231,25 @@
         processAllMessages();
         ArgumentCaptor<BroadcastReceiver> receiversCaptor =
                 ArgumentCaptor.forClass(BroadcastReceiver.class);
-        verify(mMockContext, times(3)).registerReceiver(receiversCaptor.capture(), any());
-        mTestPackageBroadcastReceiver = receiversCaptor.getAllValues().get(0);
-        mTestCarrierConfigReceiver = receiversCaptor.getAllValues().get(1);
-        mTestBootCompleteReceiver = receiversCaptor.getAllValues().get(2);
+        if (!mFeatureFlags.imsResolverUserAware()) {
+            verify(mMockContext, times(3)).registerReceiver(receiversCaptor.capture(), any());
+            mTestPackageBroadcastReceiver = receiversCaptor.getAllValues().get(0);
+            mTestCarrierConfigReceiver = receiversCaptor.getAllValues().get(1);
+            mTestBootCompleteReceiver = receiversCaptor.getAllValues().get(2);
+        } else {
+            verify(mMockContext, times(4)).registerReceiver(receiversCaptor.capture(), any());
+            mTestPackageBroadcastReceiver = receiversCaptor.getAllValues().get(0);
+            mTestUserChangedReceiver = receiversCaptor.getAllValues().get(1);
+            mTestCarrierConfigReceiver = receiversCaptor.getAllValues().get(2);
+            mTestBootCompleteReceiver = receiversCaptor.getAllValues().get(3);
+
+        }
         ArgumentCaptor<ImsServiceFeatureQueryManager.Listener> queryManagerCaptor =
                 ArgumentCaptor.forClass(ImsServiceFeatureQueryManager.Listener.class);
         verify(mMockQueryManagerFactory).create(any(Context.class), queryManagerCaptor.capture());
         mDynamicQueryListener = queryManagerCaptor.getValue();
-        when(mMockQueryManager.startQuery(any(ComponentName.class), any(String.class)))
+        when(mMockQueryManager.startQuery(any(ComponentName.class), any(UserHandle.class),
+                any(String.class)))
                 .thenReturn(true);
         processAllMessages();
     }
@@ -2050,10 +2263,18 @@
         processAllMessages();
         ArgumentCaptor<BroadcastReceiver> receiversCaptor =
                 ArgumentCaptor.forClass(BroadcastReceiver.class);
-        verify(mMockContext, times(3)).registerReceiver(receiversCaptor.capture(), any());
-        mTestPackageBroadcastReceiver = receiversCaptor.getAllValues().get(0);
-        mTestCarrierConfigReceiver = receiversCaptor.getAllValues().get(1);
-        mTestBootCompleteReceiver = receiversCaptor.getAllValues().get(2);
+        if (!mFeatureFlags.imsResolverUserAware()) {
+            verify(mMockContext, times(3)).registerReceiver(receiversCaptor.capture(), any());
+            mTestPackageBroadcastReceiver = receiversCaptor.getAllValues().get(0);
+            mTestCarrierConfigReceiver = receiversCaptor.getAllValues().get(1);
+            mTestBootCompleteReceiver = receiversCaptor.getAllValues().get(2);
+        } else {
+            verify(mMockContext, times(4)).registerReceiver(receiversCaptor.capture(), any());
+            mTestPackageBroadcastReceiver = receiversCaptor.getAllValues().get(0);
+            mTestUserChangedReceiver = receiversCaptor.getAllValues().get(1);
+            mTestCarrierConfigReceiver = receiversCaptor.getAllValues().get(2);
+            mTestBootCompleteReceiver = receiversCaptor.getAllValues().get(3);
+        }
         ArgumentCaptor<ImsServiceFeatureQueryManager.Listener> queryManagerCaptor =
                 ArgumentCaptor.forClass(ImsServiceFeatureQueryManager.Listener.class);
         verify(mMockQueryManagerFactory).create(any(Context.class), queryManagerCaptor.capture());
@@ -2069,7 +2290,8 @@
             HashSet<ImsFeatureConfiguration.FeatureSlotPair> features, int times) {
         processAllMessages();
         // ensure that startQuery was called
-        verify(mMockQueryManager, times(times)).startQuery(eq(name), any(String.class));
+        verify(mMockQueryManager, times(times)).startQuery(eq(name), any(UserHandle.class),
+                any(String.class));
         mDynamicQueryListener.onComplete(name, features);
         processAllMessages();
     }
@@ -2078,7 +2300,8 @@
             HashSet<ImsFeatureConfiguration.FeatureSlotPair> features, int times) {
         processAllFutureMessages();
         // ensure that startQuery was called
-        verify(mMockQueryManager, times(times)).startQuery(eq(name), any(String.class));
+        verify(mMockQueryManager, times(times)).startQuery(eq(name), any(UserHandle.class),
+                any(String.class));
         mDynamicQueryListener.onComplete(name, features);
         processAllMessages();
     }
@@ -2086,8 +2309,19 @@
     private void setupDynamicQueryFeaturesFailure(ComponentName name, int times) {
         processAllMessages();
         // ensure that startQuery was called
-        verify(mMockQueryManager, times(times)).startQuery(eq(name), any(String.class));
-        mDynamicQueryListener.onPermanentError(name);
+        verify(mMockQueryManager, times(times)).startQuery(eq(name), any(UserHandle.class),
+                any(String.class));
+        mDynamicQueryListener.onPermanentError(name, mContext.getUser());
+        processAllMessages();
+    }
+
+    public void userChanged(UserHandle newUser) {
+        setCurrentUser(newUser);
+        // Tell the package manager that a new device feature is installed
+        Intent userSwitchedIntent = new Intent();
+        userSwitchedIntent.setAction(Intent.ACTION_USER_SWITCHED);
+        userSwitchedIntent.putExtra(Intent.EXTRA_USER, newUser);
+        mTestUserChangedReceiver.onReceive(null, userSwitchedIntent);
         processAllMessages();
     }
 
@@ -2110,7 +2344,14 @@
         processAllMessages();
     }
 
+    private void setBoundImsServiceControllerUser(ImsServiceController controller,
+            UserHandle handle) {
+        when(controller.getBoundUser()).thenReturn(handle);
+    }
+
     private void setImsServiceControllerFactory(Map<String, ImsServiceController> controllerMap) {
+        controllerMap.values()
+                .forEach(c -> setBoundImsServiceControllerUser(c, mContext.getUser()));
         mTestImsResolver.setImsServiceControllerFactory(
                 new ImsResolver.ImsServiceControllerFactory() {
                     @Override
@@ -2129,6 +2370,8 @@
 
     private void setImsServiceControllerFactory(ImsServiceController deviceController,
             ImsServiceController carrierController) {
+        setBoundImsServiceControllerUser(deviceController, mContext.getUser());
+        setBoundImsServiceControllerUser(carrierController, mContext.getUser());
         mTestImsResolver.setImsServiceControllerFactory(
                 new ImsResolver.ImsServiceControllerFactory() {
                     @Override
@@ -2156,6 +2399,9 @@
 
     private void setImsServiceControllerFactory(ImsServiceController deviceController,
             ImsServiceController carrierController1, ImsServiceController carrierController2) {
+        setBoundImsServiceControllerUser(deviceController, mContext.getUser());
+        setBoundImsServiceControllerUser(carrierController1, mContext.getUser());
+        setBoundImsServiceControllerUser(carrierController2, mContext.getUser());
         mTestImsResolver.setImsServiceControllerFactory(
                 new ImsResolver.ImsServiceControllerFactory() {
                     @Override
@@ -2185,9 +2431,13 @@
                 });
     }
 
-    private void setImsServiceControllerFactory(ImsServiceController deviceController1,
-            ImsServiceController deviceController2, ImsServiceController carrierController1,
-            ImsServiceController carrierController2) {
+    private void setImsServiceControllerDDCFactory(ImsServiceController deviceController1,
+            ImsServiceController deviceController2, ImsServiceController carrierController1) {
+        setBoundImsServiceControllerUser(deviceController1, mContext.getUser());
+        setBoundImsServiceControllerUser(deviceController2, mContext.getUser());
+        if (carrierController1 != null) {
+            setBoundImsServiceControllerUser(carrierController1, mContext.getUser());
+        }
         mTestImsResolver.setImsServiceControllerFactory(
                 new ImsResolver.ImsServiceControllerFactory() {
                     @Override
@@ -2211,10 +2461,6 @@
                                 componentName.getPackageName())) {
                             when(carrierController1.getComponentName()).thenReturn(componentName);
                             return carrierController1;
-                        } else if (TEST_CARRIER_2_DEFAULT_NAME.getPackageName().equals(
-                                componentName.getPackageName())) {
-                            when(carrierController2.getComponentName()).thenReturn(componentName);
-                            return carrierController2;
                         }
                         return null;
                     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerCompatTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerCompatTest.java
index 2544fc1..aa6bd7f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerCompatTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerCompatTest.java
@@ -108,9 +108,9 @@
         mMmTelCompatAdapterSpy = spy(new MmTelFeatureCompatAdapter(mMockContext, SLOT_0,
                 mMockMmTelInterfaceAdapter));
         mTestImsServiceController = new ImsServiceControllerCompat(mMockContext, mTestComponentName,
-                mMockCallbacks, mHandler, REBIND_RETRY, mRepo,
+                 mMockCallbacks, mHandler, REBIND_RETRY, mRepo,
                 (a, b, c) -> mMmTelCompatAdapterSpy);
-        when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(true);
+        when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true);
         when(mMockServiceControllerBinder.createMMTelFeature(anyInt()))
                 .thenReturn(mMockRemoteMMTelFeature);
         when(mMockRemoteMMTelFeature.getConfigInterface()).thenReturn(mMockImsConfig);
@@ -146,8 +146,8 @@
         verify(mMockServiceControllerBinder).createMMTelFeature(SLOT_0);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_1),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         validateMmTelFeatureContainerExists(SLOT_0);
         // Remove the feature
         conn.onBindingDied(mTestComponentName);
@@ -191,8 +191,9 @@
             SparseIntArray slotIdToSubIdMap) {
         ArgumentCaptor<ServiceConnection> serviceCaptor =
                 ArgumentCaptor.forClass(ServiceConnection.class);
-        assertTrue(mTestImsServiceController.bind(testFeatures, slotIdToSubIdMap));
-        verify(mMockContext).bindService(any(), serviceCaptor.capture(), anyInt());
+        assertTrue(mTestImsServiceController.bind(mContext.getUser(), testFeatures,
+                slotIdToSubIdMap));
+        verify(mMockContext).bindServiceAsUser(any(), serviceCaptor.capture(), anyInt(), any());
         return serviceCaptor.getValue();
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
index 65b73fb..5f16d9b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
@@ -39,6 +39,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.telephony.SubscriptionManager;
 import android.telephony.ims.ImsService;
 import android.telephony.ims.aidl.IImsConfig;
@@ -135,6 +136,7 @@
     private final Handler mHandler = new Handler(Looper.getMainLooper());
     private ImsServiceController mTestImsServiceController;
     private ImsFeatureBinderRepository mRepo;
+    private UserHandle mUser;
 
     @Before
     @Override
@@ -150,11 +152,12 @@
         mMockCallbacks = mock(ImsServiceController.ImsServiceControllerCallbacks.class);
         mFeatureFlags = mock(FeatureFlags.class);
         mMockContext = mock(Context.class);
+        mUser = UserHandle.of(UserHandle.myUserId());
 
         mRepo = new ImsFeatureBinderRepository();
         mTestImsServiceController = new ImsServiceController(mMockContext, mTestComponentName,
                 mMockCallbacks, mHandler, REBIND_RETRY, mRepo, mFeatureFlags);
-        when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(true);
+        when(mMockContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true);
         when(mMockServiceControllerBinder.createMmTelFeature(anyInt(), anyInt()))
                 .thenReturn(mMockMmTelFeature);
         when(mMockServiceControllerBinder.createRcsFeature(anyInt(), anyInt()))
@@ -198,11 +201,12 @@
 
         SparseIntArray slotIdToSubIdMap = new SparseIntArray();
         slotIdToSubIdMap.put(SLOT_0, SUB_2);
-        assertTrue(mTestImsServiceController.bind(testFeatures, slotIdToSubIdMap.clone()));
+        assertTrue(mTestImsServiceController.bind(mUser, testFeatures, slotIdToSubIdMap.clone()));
 
         int expectedFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
                 | Context.BIND_IMPORTANT;
-        verify(mMockContext).bindService(intentCaptor.capture(), any(), eq(expectedFlags));
+        verify(mMockContext).bindServiceAsUser(intentCaptor.capture(), any(), eq(expectedFlags),
+                any());
         Intent testIntent = intentCaptor.getValue();
         assertEquals(ImsService.SERVICE_INTERFACE, testIntent.getAction());
         assertEquals(mTestComponentName, testIntent.getComponent());
@@ -222,9 +226,9 @@
         bindAndConnectService(testFeatures, slotIdToSubIdMap.clone());
 
         // already bound, should return false
-        assertFalse(mTestImsServiceController.bind(testFeatures, slotIdToSubIdMap.clone()));
+        assertFalse(mTestImsServiceController.bind(mUser, testFeatures, slotIdToSubIdMap.clone()));
 
-        verify(mMockContext, times(1)).bindService(any(), any(), anyInt());
+        verify(mMockContext, times(1)).bindServiceAsUser(any(), any(), anyInt(), any());
     }
 
     /**
@@ -250,10 +254,10 @@
         verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_2);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_RCS), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0),  eq(SUB_2),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0),  eq(SUB_2),
+                eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
         validateMmTelFeatureContainerExists(SLOT_0);
         validateRcsFeatureContainerExists(SLOT_0);
     }
@@ -282,10 +286,10 @@
         verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_2);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_RCS), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+                eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
         validateMmTelFeatureContainerExists(SLOT_0);
         validateRcsFeatureContainerExists(SLOT_0);
 
@@ -313,9 +317,9 @@
         verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_3);
         verify(mMockServiceControllerBinder, times(2)).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_RCS), any());
-        verify(mMockCallbacks, times(2)).imsServiceFeatureCreated(eq(SLOT_0),
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_3),
                 eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
-        verify(mMockCallbacks, times(2)).imsServiceFeatureCreated(eq(SLOT_0),
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_3),
                 eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
         validateMmTelFeatureContainerExists(SLOT_0);
         validateRcsFeatureContainerExists(SLOT_0);
@@ -341,13 +345,13 @@
         verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         validateMmTelFeatureContainerExists(SLOT_0);
         validateMmTelFeatureContainerExists(SLOT_1);
 
@@ -373,12 +377,12 @@
         verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_4);
         verify(mMockServiceControllerBinder, times(2)).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks, times(2)).imsServiceFeatureCreated(eq(SLOT_0),
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_4),
                 eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_5);
         verify(mMockServiceControllerBinder, times(2)).addFeatureStatusCallback(eq(SLOT_1),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks, times(2)).imsServiceFeatureCreated(eq(SLOT_1),
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_5),
                 eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         validateMmTelFeatureContainerExists(SLOT_0);
         validateMmTelFeatureContainerExists(SLOT_1);
@@ -411,13 +415,14 @@
         verify(mMockServiceControllerBinder).createEmergencyOnlyMmTelFeature(SLOT_0);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0),
+                eq(SubscriptionManager.INVALID_SUBSCRIPTION_ID), eq(ImsFeature.FEATURE_MMTEL),
                 eq(mTestImsServiceController));
         verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         validateMmTelFeatureContainerExistsWithEmergency(SLOT_0);
         validateMmTelFeatureContainerExistsWithEmergency(SLOT_1);
 
@@ -437,8 +442,9 @@
         verify(mMockServiceControllerBinder).createEmergencyOnlyMmTelFeature(SLOT_1);
         verify(mMockServiceControllerBinder, times(2)).addFeatureStatusCallback(eq(SLOT_1),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks, times(2)).imsServiceFeatureCreated(eq(SLOT_1),
-                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1),
+                eq(SubscriptionManager.INVALID_SUBSCRIPTION_ID), eq(ImsFeature.FEATURE_MMTEL),
+                eq(mTestImsServiceController));
         validateMmTelFeatureContainerExistsWithEmergency(SLOT_0);
         validateMmTelFeatureContainerExistsWithEmergency(SLOT_1);
 
@@ -470,10 +476,10 @@
         verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_2);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_RCS), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0),  eq(SUB_2),
+                eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
         validateFeatureContainerExistsWithSipDelegate(ImsFeature.FEATURE_MMTEL, SLOT_0);
         validateFeatureContainerExistsWithSipDelegate(ImsFeature.FEATURE_RCS, SLOT_0);
     }
@@ -499,7 +505,8 @@
         verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+                eq(ImsFeature.FEATURE_MMTEL),
                 eq(mTestImsServiceController));
         // verify CAPABILITY_SIP_DELEGATE_CREATION is not set because MMTEL and RCS are not set.
         validateFeatureContainerDoesNotHaveSipDelegate(ImsFeature.FEATURE_MMTEL, SLOT_0);
@@ -525,9 +532,9 @@
         verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0),
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
                 eq(ImsFeature.FEATURE_EMERGENCY_MMTEL), eq(mTestImsServiceController));
         // Make sure this callback happens, which will notify the framework of emergency calling
         // availability.
@@ -553,9 +560,11 @@
         verify(mMockServiceControllerBinder).createEmergencyOnlyMmTelFeature(SLOT_0);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0),
+                eq(SubscriptionManager.INVALID_SUBSCRIPTION_ID), eq(ImsFeature.FEATURE_MMTEL),
                 eq(mTestImsServiceController));
         verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0),
+                eq(SubscriptionManager.INVALID_SUBSCRIPTION_ID),
                 eq(ImsFeature.FEATURE_EMERGENCY_MMTEL), eq(mTestImsServiceController));
         // Make sure this callback happens, which will notify the framework of emergency calling
         // availability.
@@ -584,17 +593,17 @@
         verify(mMockServiceControllerBinder, never()).createMmTelFeature(SLOT_0, SUB_2);
         verify(mMockServiceControllerBinder, never()).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0),
+        verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
                 eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
-        verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0),
+        verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
                 eq(ImsFeature.FEATURE_EMERGENCY_MMTEL), eq(mTestImsServiceController));
         validateMmTelFeatureContainerDoesntExist(SLOT_0);
         // verify RCS feature is created
         verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_2);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_RCS), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0),  eq(SUB_2),
+                eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
         validateRcsFeatureContainerExists(SLOT_0);
     }
 
@@ -798,9 +807,9 @@
 
         long delay = mTestImsServiceController.getRebindDelay();
         waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
-        verify(mMockCallbacks, never()).imsServiceFeatureCreated(anyInt(), anyInt(),
+        verify(mMockCallbacks, never()).imsServiceFeatureCreated(anyInt(), anyInt(), anyInt(),
                 eq(mTestImsServiceController));
-        verify(mMockCallbacks).imsServiceBindPermanentError(eq(mTestComponentName));
+        verify(mMockCallbacks).imsServiceBindPermanentError(eq(mTestComponentName), eq(mUser));
         validateMmTelFeatureContainerDoesntExist(SLOT_0);
         validateRcsFeatureContainerDoesntExist(SLOT_0);
     }
@@ -822,8 +831,8 @@
         bindAndConnectService(testFeatures, slotIdToSubIdMap.clone());
 
         verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_2);
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0),  eq(SUB_2),
+                eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
         validateRcsFeatureContainerDoesntExist(SLOT_0);
     }
 
@@ -843,8 +852,8 @@
         verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0),  eq(SUB_2),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         // Create a new list with an additional item
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithAddition = new HashSet<>(
                 testFeatures);
@@ -859,8 +868,8 @@
         verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1),  eq(SUB_3),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         validateMmTelFeatureContainerExists(SLOT_0);
         validateMmTelFeatureContainerExists(SLOT_1);
     }
@@ -880,8 +889,8 @@
         verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0),  eq(SUB_2),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         // Create a new list with an additional item
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithAddition = new HashSet<>(
                 testFeatures);
@@ -895,8 +904,8 @@
         verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1),  eq(SUB_3),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         validateMmTelFeatureContainerExists(SLOT_0);
         validateMmTelFeatureContainerExists(SLOT_1);
     }
@@ -918,8 +927,8 @@
         verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         validateMmTelFeatureContainerExists(SLOT_0);
         assertEquals(mMockMmTelBinder, cb.container.imsFeature);
         assertTrue((ImsService.CAPABILITY_EMERGENCY_OVER_MMTEL
@@ -934,9 +943,8 @@
         mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithAddition,
                 slotIdToSubIdMap.clone());
 
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0),
-                eq(ImsFeature.FEATURE_EMERGENCY_MMTEL),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+                eq(ImsFeature.FEATURE_EMERGENCY_MMTEL), eq(mTestImsServiceController));
         validateMmTelFeatureContainerExistsWithEmergency(SLOT_0);
         assertEquals(mMockMmTelBinder, cb.container.imsFeature);
 
@@ -972,8 +980,8 @@
         verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_2);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_RCS), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+                eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
         validateRcsFeatureContainerExists(SLOT_0);
         // Add FEATURE_EMERGENCY_MMTEL and ensure it doesn't cause MMTEL bind
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithAddition = new HashSet<>(
@@ -988,9 +996,8 @@
         verify(mMockServiceControllerBinder, never()).createMmTelFeature(SLOT_1, SUB_3);
         verify(mMockServiceControllerBinder, never()).addFeatureStatusCallback(eq(SLOT_1),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_1),
-                eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         validateMmTelFeatureContainerDoesntExist(SLOT_1);
     }
 
@@ -1010,8 +1017,8 @@
         verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         validateMmTelFeatureContainerExists(SLOT_0);
 
         // Call change with the same features and make sure it is disregarded
@@ -1027,7 +1034,7 @@
         verify(mMockServiceControllerBinder, never()).removeFeatureStatusCallback(anyInt(),
                 anyInt(), any());
         verify(mMockCallbacks, times(1)).imsServiceFeatureCreated(eq(SLOT_0),
-                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
+                eq(SUB_2), eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         verify(mMockCallbacks, never()).imsServiceFeatureRemoved(anyInt(), anyInt(), any());
         validateMmTelFeatureContainerExists(SLOT_0);
     }
@@ -1050,13 +1057,13 @@
         verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         validateMmTelFeatureContainerExists(SLOT_0);
         validateMmTelFeatureContainerExists(SLOT_1);
         // Create a new list with one less item
@@ -1098,13 +1105,13 @@
         verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         validateMmTelFeatureContainerExists(SLOT_0);
         validateMmTelFeatureContainerExists(SLOT_1);
         // Create a new list with one less item
@@ -1149,23 +1156,23 @@
         verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_2);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_RCS), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+                eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
         verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         verify(mMockServiceControllerBinder).createRcsFeature(SLOT_1, SUB_3);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1),
                 eq(ImsFeature.FEATURE_RCS), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_RCS),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3),
+                eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
         validateMmTelFeatureContainerExists(SLOT_0);
         validateMmTelFeatureContainerExists(SLOT_1);
         validateRcsFeatureContainerExists(SLOT_0);
@@ -1205,12 +1212,12 @@
         verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_4);
         verify(mMockServiceControllerBinder, times(2)).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks, times(2)).imsServiceFeatureCreated(eq(SLOT_0),
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_4),
                 eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         verify(mMockServiceControllerBinder).createRcsFeature(SLOT_0, SUB_4);
         verify(mMockServiceControllerBinder, times(2)).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_RCS), any());
-        verify(mMockCallbacks, times(2)).imsServiceFeatureCreated(eq(SLOT_0),
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_4),
                 eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
         validateMmTelFeatureContainerExists(SLOT_0);
         validateRcsFeatureContainerExists(SLOT_0);
@@ -1235,13 +1242,13 @@
         verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_0, SUB_2);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         verify(mMockServiceControllerBinder).createMmTelFeature(SLOT_1, SUB_3);
         verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_1),
                 eq(ImsFeature.FEATURE_MMTEL), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(SUB_3),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
         validateMmTelFeatureContainerExists(SLOT_0);
         validateMmTelFeatureContainerExists(SLOT_1);
 
@@ -1291,7 +1298,7 @@
         verify(mMockServiceControllerBinder, never()).createRcsFeature(SLOT_0, SUB_2);
         verify(mMockServiceControllerBinder, never()).removeFeatureStatusCallback(eq(SLOT_0),
                 eq(ImsFeature.FEATURE_RCS), any());
-        verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0),
+        verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0), eq(SUB_2),
                 eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
         validateMmTelFeatureContainerDoesntExist(SLOT_0);
         validateRcsFeatureContainerDoesntExist(SLOT_0);
@@ -1318,7 +1325,7 @@
         long delay = REBIND_RETRY.getStartDelay();
         waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
         // The service should autobind after rebind event occurs
-        verify(mMockContext, times(2)).bindService(any(), any(), anyInt());
+        verify(mMockContext, times(2)).bindServiceAsUser(any(), any(), anyInt(), any());
     }
 
     /**
@@ -1344,7 +1351,7 @@
         long delay = REBIND_RETRY.getStartDelay();
         waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
         // The service should autobind after rebind event occurs
-        verify(mMockContext, times(2)).bindService(any(), any(), anyInt());
+        verify(mMockContext, times(2)).bindServiceAsUser(any(), any(), anyInt(), any());
     }
 
     /**
@@ -1365,7 +1372,7 @@
         conn.onBindingDied(null /*null*/);
 
         // Be sure that there are no binds before the RETRY_TIMEOUT expires
-        verify(mMockContext, times(1)).bindService(any(), any(), anyInt());
+        verify(mMockContext, times(1)).bindServiceAsUser(any(), any(), anyInt(), any());
     }
 
     /**
@@ -1390,7 +1397,7 @@
         waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
 
         // Unbind should stop the autobind from occurring.
-        verify(mMockContext, times(1)).bindService(any(), any(), anyInt());
+        verify(mMockContext, times(1)).bindServiceAsUser(any(), any(), anyInt(), any());
     }
 
     /**
@@ -1412,10 +1419,10 @@
 
         long delay = REBIND_RETRY.getStartDelay();
         waitForHandlerActionDelayed(mHandler, delay, 2 * delay);
-        mTestImsServiceController.bind(testFeatures, slotIdToSubIdMap.clone());
+        mTestImsServiceController.bind(mUser, testFeatures, slotIdToSubIdMap.clone());
 
         // Should only see two binds, not three from the auto rebind that occurs.
-        verify(mMockContext, times(2)).bindService(any(), any(), anyInt());
+        verify(mMockContext, times(2)).bindServiceAsUser(any(), any(), anyInt(), any());
     }
 
     private void validateMmTelFeatureContainerExists(int slotId) {
@@ -1504,8 +1511,8 @@
             SparseIntArray slotIdToSubIdMap) {
         ArgumentCaptor<ServiceConnection> serviceCaptor =
                 ArgumentCaptor.forClass(ServiceConnection.class);
-        assertTrue(mTestImsServiceController.bind(testFeatures, slotIdToSubIdMap));
-        verify(mMockContext).bindService(any(), serviceCaptor.capture(), anyInt());
+        assertTrue(mTestImsServiceController.bind(mUser, testFeatures, slotIdToSubIdMap));
+        verify(mMockContext).bindServiceAsUser(any(), serviceCaptor.capture(), anyInt(), any());
         return serviceCaptor.getValue();
     }
 }
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 46252a2..876410c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
@@ -22,6 +22,7 @@
 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.CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC;
+import static android.telephony.CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL;
 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;
@@ -99,6 +100,7 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.anyVararg;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
@@ -145,6 +147,7 @@
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.satellite.INtnSignalStrengthCallback;
@@ -228,6 +231,7 @@
     private static final int TEST_WAIT_FOR_CELLULAR_MODEM_OFF_TIMEOUT_MILLIS =
             (int) TimeUnit.SECONDS.toMillis(60);
 
+
     private static final String SATELLITE_PLMN = "00103";
     private List<Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener>>
             mCarrierConfigChangedListenerList = new ArrayList<>();
@@ -575,9 +579,11 @@
         when(mPhone.getServiceState()).thenReturn(mServiceState);
         when(mPhone.getSubId()).thenReturn(SUB_ID);
         when(mPhone.getPhoneId()).thenReturn(0);
+        when(mPhone.getSignalStrengthController()).thenReturn(mSignalStrengthController);
         when(mPhone2.getServiceState()).thenReturn(mServiceState2);
         when(mPhone2.getSubId()).thenReturn(SUB_ID1);
         when(mPhone2.getPhoneId()).thenReturn(1);
+        when(mPhone2.getSignalStrengthController()).thenReturn(mSignalStrengthController);
 
         mContextFixture.putStringArrayResource(
                 R.array.config_satellite_providers,
@@ -952,6 +958,7 @@
     @Test
     public void testRequestSatelliteEnabled() {
         when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        when(mFeatureFlags.satelliteStateChangeListener()).thenReturn(true);
         mIsSatelliteEnabledSemaphore.drainPermits();
 
         // Fail to enable satellite when SatelliteController is not fully loaded yet.
@@ -1008,6 +1015,7 @@
         doReturn(false).when(mTelecomManager).isInEmergencyCall();
 
         // Successfully enable satellite
+        reset(mTelephonyRegistryManager);
         mIIntegerConsumerResults.clear();
         mIIntegerConsumerSemaphore.drainPermits();
         mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
@@ -1029,8 +1037,10 @@
         verify(mMockDatagramController, times(2)).setDemoMode(eq(false));
         verify(mMockControllerMetricsStats, times(1)).onSatelliteEnabled();
         verify(mMockControllerMetricsStats, times(1)).reportServiceEnablementSuccessCount();
+        verify(mTelephonyRegistryManager).notifySatelliteStateChanged(eq(true));
 
         // Successfully disable satellite when radio is turned off.
+        reset(mTelephonyRegistryManager);
         clearInvocations(mMockSatelliteSessionController);
         clearInvocations(mMockDatagramController);
         mSatelliteControllerUT.setSatelliteSessionController(mMockSatelliteSessionController);
@@ -1054,6 +1064,7 @@
         verify(mMockDatagramController, times(2)).setDemoMode(eq(false));
         verify(mMockControllerMetricsStats, times(1)).onSatelliteDisabled();
         mSatelliteControllerUT.isSatelliteBeingDisabled = false;
+        verify(mTelephonyRegistryManager, atLeastOnce()).notifySatelliteStateChanged(eq(false));
 
         // Fail to enable satellite when radio is off.
         mIIntegerConsumerResults.clear();
@@ -1089,6 +1100,7 @@
         verify(mMockControllerMetricsStats, times(1)).reportServiceEnablementFailCount();
 
         // Successfully enable satellite when radio is on.
+        reset(mTelephonyRegistryManager);
         mIIntegerConsumerResults.clear();
         mIIntegerConsumerSemaphore.drainPermits();
         mSatelliteControllerUT.setSettingsKeyForSatelliteModeCalled = false;
@@ -1107,6 +1119,7 @@
         verify(mMockDatagramController, times(3)).setDemoMode(eq(false));
         verify(mMockControllerMetricsStats, times(2)).onSatelliteEnabled();
         verify(mMockControllerMetricsStats, times(2)).reportServiceEnablementSuccessCount();
+        verify(mTelephonyRegistryManager).notifySatelliteStateChanged(eq(true));
 
         // Successfully enable satellite when it is already enabled.
         mIIntegerConsumerResults.clear();
@@ -1127,6 +1140,7 @@
         verifySatelliteEnabled(true, SATELLITE_RESULT_SUCCESS);
 
         // Successfully disable satellite.
+        reset(mTelephonyRegistryManager);
         mIIntegerConsumerResults.clear();
         mIIntegerConsumerSemaphore.drainPermits();
         setUpResponseForRequestSatelliteEnabled(false, false, false, SATELLITE_RESULT_SUCCESS);
@@ -1135,6 +1149,7 @@
         assertTrue(waitForIIntegerConsumerResult(1));
         assertEquals(SATELLITE_RESULT_SUCCESS, (long) mIIntegerConsumerResults.get(0));
         verifySatelliteEnabled(false, SATELLITE_RESULT_SUCCESS);
+        verify(mTelephonyRegistryManager, atLeastOnce()).notifySatelliteStateChanged(eq(false));
 
         // Disable satellite when satellite is already disabled.
         mIIntegerConsumerResults.clear();
@@ -1708,12 +1723,7 @@
                     }
                 };
         int errorCode = mSatelliteControllerUT.registerForSatelliteProvisionStateChanged(callback);
-        assertEquals(SATELLITE_RESULT_INVALID_TELEPHONY_STATE, errorCode);
-
-        setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
-        verifySatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
-        errorCode = mSatelliteControllerUT.registerForSatelliteProvisionStateChanged(callback);
-        assertEquals(SATELLITE_RESULT_NOT_SUPPORTED, errorCode);
+        assertEquals(SATELLITE_RESULT_SUCCESS, errorCode);
 
         resetSatelliteControllerUT();
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
@@ -1745,6 +1755,7 @@
                 semaphore, 1, "testRegisterForSatelliteProvisionStateChanged"));
 
         mSatelliteControllerUT.unregisterForSatelliteProvisionStateChanged(callback);
+        semaphore.drainPermits();
         cancelRemote = mSatelliteControllerUT.provisionSatelliteService(
                 TEST_SATELLITE_TOKEN,
                 testProvisionData, mIIntegerConsumer);
@@ -2543,6 +2554,7 @@
         assertTrue(
                 mQueriedSatelliteCapabilities.getSupportedRadioTechnologies().contains(
                         satelliteController.getSupportedNtnRadioTechnology()));
+        assertEquals(mQueriedSatelliteCapabilities.getMaxBytesPerOutgoingDatagram(), 255);
         assertTrue(satelliteController.isSatelliteAttachRequired());
 
         when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(false);
@@ -4078,6 +4090,7 @@
         when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
         when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
         when(mServiceState2.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+        mSatelliteControllerUT.mIsApplicationSupportsP2P = true;
         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);
@@ -4136,6 +4149,7 @@
         when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
         when(mServiceState2.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
         when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+        mSatelliteControllerUT.mIsApplicationSupportsP2P = true;
         mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, true);
         mCarrierConfigBundle.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, 1);
         mCarrierConfigBundle.putInt(
@@ -4215,6 +4229,43 @@
     }
 
     @Test
+    public void testNotifyCarrierRoamingNtnSignalStrengthChanged() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+
+        sendSignalStrengthChangedEvent(mPhone.getPhoneId());
+        processAllMessages();
+        ArgumentCaptor<NtnSignalStrength> captor = ArgumentCaptor.forClass(NtnSignalStrength.class);
+        verify(mPhone, times(1)).notifyCarrierRoamingNtnSignalStrengthChanged(
+                captor.capture());
+        NtnSignalStrength actualSignalStrength = captor.getValue();
+        assertEquals(NTN_SIGNAL_STRENGTH_NONE, actualSignalStrength.getLevel());
+        clearInvocations(mPhone);
+
+        when(mSignalStrength.getLevel()).thenReturn(SignalStrength.SIGNAL_STRENGTH_GOOD);
+        when(mPhone.getSignalStrength()).thenReturn(mSignalStrength);
+        mCarrierConfigBundle.putInt(KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT, 1 * 60);
+        mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ATTACH_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)
+            );
+        }
+        processAllMessages();
+        when(mServiceState.isUsingNonTerrestrialNetwork()).thenReturn(true);
+        when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+        sendServiceStateChangedEvent();
+        processAllMessages();
+        captor = ArgumentCaptor.forClass(NtnSignalStrength.class);
+        verify(mPhone, times(1)).notifyCarrierRoamingNtnSignalStrengthChanged(
+                captor.capture());
+        actualSignalStrength = captor.getValue();
+        assertEquals(NTN_SIGNAL_STRENGTH_GOOD, actualSignalStrength.getLevel());
+        clearInvocations(mPhone);
+    }
+
+    @Test
     public void testGetWwanIsInService() {
         when(mServiceState.getNetworkRegistrationInfoListForTransportType(
                 eq(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)))
@@ -4441,6 +4492,7 @@
     private void verifyRequestSatelliteSubscriberProvisionStatus() throws Exception {
         setSatelliteSubscriberTesting();
         List<SatelliteSubscriberInfo> list = getExpectedSatelliteSubscriberInfoList();
+        mCarrierConfigBundle.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
         mCarrierConfigBundle.putString(KEY_SATELLITE_NIDD_APN_NAME_STRING, mNiddApn);
         mCarrierConfigBundle.putBoolean(KEY_SATELLITE_ESOS_SUPPORTED_BOOL, true);
         for (Pair<Executor, CarrierConfigManager.CarrierConfigChangeListener> pair
@@ -4756,6 +4808,7 @@
     @Test
     public void testCheckForSubscriberIdChange_changed() {
         when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        mCarrierConfigBundle.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
         List<SubscriptionInfo> allSubInfos = new ArrayList<>();
 
         String imsi = "012345";
@@ -5463,6 +5516,13 @@
         msg.sendToTarget();
     }
 
+    private void sendSignalStrengthChangedEvent(int phoneId) {
+        Message msg = mSatelliteControllerUT.obtainMessage(
+                57 /* EVENT_SIGNAL_STRENGTH_CHANGED */);
+        msg.obj = new AsyncResult(phoneId, null, null);
+        msg.sendToTarget();
+    }
+
     private void sendCmdStartSendingNtnSignalStrengthChangedEvent(boolean shouldReport) {
         Message msg = mSatelliteControllerUT.obtainMessage(
                 35 /* CMD_UPDATE_NTN_SIGNAL_STRENGTH_REPORTING */);
@@ -5726,6 +5786,7 @@
         public static boolean isApplicationUpdated;
         public String packageName = "com.example.app";
         public boolean isSatelliteBeingDisabled = false;
+        public boolean mIsApplicationSupportsP2P = false;
 
         TestSatelliteController(
                 Context context, Looper looper, @NonNull FeatureFlags featureFlags) {
@@ -5821,6 +5882,17 @@
             isApplicationUpdated = true;
         }
 
+        @Override
+        public boolean isApplicationSupportsP2P(String packageName) {
+            return mIsApplicationSupportsP2P;
+        }
+
+        @Override
+        public int[] getSupportedServicesOnCarrierRoamingNtn(int subId) {
+            return new int[]{3, 5};
+        }
+
+
         void setSatelliteProvisioned(@Nullable Boolean isProvisioned) {
             synchronized (mDeviceProvisionLock) {
                 mIsDeviceProvisioned = isProvisioned;
@@ -5866,5 +5938,88 @@
         public boolean isAnyWaitForSatelliteEnablingResponseTimerStarted() {
             return hasMessages(EVENT_WAIT_FOR_SATELLITE_ENABLING_RESPONSE_TIMED_OUT);
         }
+
+        public int getResultReceiverTotalCount() {
+            synchronized (mResultReceiverTotalCountLock) {
+                return mResultReceiverTotalCount;
+            }
+        }
+
+        public HashMap<String, Integer> getResultReceiverCountPerMethodMap() {
+            synchronized (mResultReceiverTotalCountLock) {
+                return mResultReceiverCountPerMethodMap;
+            }
+        }
+    }
+
+    @Test
+    public void testLoggingCodeForResultReceiverCount() throws Exception {
+        final String callerSC =  "SC:ResultReceiver";
+        final String callerSAC =  "SAC:ResultReceiver";
+
+        doReturn(false).when(mFeatureFlags).geofenceEnhancementForBetterUx();
+
+        mSatelliteControllerUT.incrementResultReceiverCount(callerSC);
+        assertEquals(0, mSatelliteControllerUT.getResultReceiverTotalCount());
+        mSatelliteControllerUT.decrementResultReceiverCount(callerSC);
+        assertEquals(0, mSatelliteControllerUT.getResultReceiverTotalCount());
+
+        doReturn(true).when(mFeatureFlags).geofenceEnhancementForBetterUx();
+
+        mSatelliteControllerUT.incrementResultReceiverCount(callerSC);
+        assertEquals(1, mSatelliteControllerUT.getResultReceiverTotalCount());
+        assertEquals(1, mSatelliteControllerUT.getResultReceiverCountPerMethodMap().size());
+        assertEquals(1, (int) Optional.ofNullable(mSatelliteControllerUT
+                .getResultReceiverCountPerMethodMap().get(callerSC)).orElse(0));
+        assertEquals(0, (int) Optional.ofNullable(mSatelliteControllerUT
+                .getResultReceiverCountPerMethodMap().get(callerSAC)).orElse(0));
+
+        mSatelliteControllerUT.incrementResultReceiverCount(callerSC);
+        assertEquals(2, mSatelliteControllerUT.getResultReceiverTotalCount());
+        assertEquals(1, mSatelliteControllerUT.getResultReceiverCountPerMethodMap().size());
+        assertEquals(2, (int) Optional.ofNullable(mSatelliteControllerUT
+                .getResultReceiverCountPerMethodMap().get(callerSC)).orElse(0));
+        assertEquals(0, (int) Optional.ofNullable(mSatelliteControllerUT
+                .getResultReceiverCountPerMethodMap().get(callerSAC)).orElse(0));
+
+        mSatelliteControllerUT.incrementResultReceiverCount(callerSAC);
+        assertEquals(3, mSatelliteControllerUT.getResultReceiverTotalCount());
+        assertEquals(2, mSatelliteControllerUT.getResultReceiverCountPerMethodMap().size());
+        assertEquals(2, (int) Optional.ofNullable(mSatelliteControllerUT
+                .getResultReceiverCountPerMethodMap().get(callerSC)).orElse(0));
+        assertEquals(1, (int) Optional.ofNullable(mSatelliteControllerUT
+                .getResultReceiverCountPerMethodMap().get(callerSAC)).orElse(0));
+
+        mSatelliteControllerUT.decrementResultReceiverCount(callerSC);
+        assertEquals(2, mSatelliteControllerUT.getResultReceiverTotalCount());
+        assertEquals(2, mSatelliteControllerUT.getResultReceiverCountPerMethodMap().size());
+        assertEquals(1, (int) Optional.ofNullable(mSatelliteControllerUT
+                .getResultReceiverCountPerMethodMap().get(callerSC)).orElse(0));
+        assertEquals(1, (int) Optional.ofNullable(mSatelliteControllerUT
+                .getResultReceiverCountPerMethodMap().get(callerSAC)).orElse(0));
+
+        mSatelliteControllerUT.decrementResultReceiverCount(callerSC);
+        assertEquals(1, mSatelliteControllerUT.getResultReceiverTotalCount());
+        assertEquals(2, mSatelliteControllerUT.getResultReceiverCountPerMethodMap().size());
+        assertEquals(0, (int) Optional.ofNullable(mSatelliteControllerUT
+                .getResultReceiverCountPerMethodMap().get(callerSC)).orElse(0));
+        assertEquals(1, (int) Optional.ofNullable(mSatelliteControllerUT
+                .getResultReceiverCountPerMethodMap().get(callerSAC)).orElse(0));
+
+        mSatelliteControllerUT.decrementResultReceiverCount(callerSAC);
+        assertEquals(0, mSatelliteControllerUT.getResultReceiverTotalCount());
+        assertEquals(2, mSatelliteControllerUT.getResultReceiverCountPerMethodMap().size());
+        assertEquals(0, (int) Optional.ofNullable(mSatelliteControllerUT
+                .getResultReceiverCountPerMethodMap().get(callerSC)).orElse(0));
+        assertEquals(0, (int) Optional.ofNullable(mSatelliteControllerUT
+                .getResultReceiverCountPerMethodMap().get(callerSAC)).orElse(0));
+    }
+
+    @Test
+    public void testSetNtnSmsSupportedByMessagesApp() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        mSatelliteControllerUT.setNtnSmsSupportedByMessagesApp(true);
+        assertTrue(mSharedPreferences.getBoolean(
+                SatelliteController.NTN_SMS_SUPPORTED_BY_MESSAGES_APP_KEY, false));
     }
 }
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 2609ffc..56d5731 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
@@ -152,8 +152,10 @@
         mServiceState2 = Mockito.mock(ServiceState.class);
         when(mPhone.getServiceState()).thenReturn(mServiceState);
         when(mPhone.getPhoneId()).thenReturn(PHONE_ID);
+        when(mPhone.getSignalStrengthController()).thenReturn(mSignalStrengthController);
         when(mPhone2.getServiceState()).thenReturn(mServiceState2);
         when(mPhone2.getPhoneId()).thenReturn(PHONE_ID2);
+        when(mPhone2.getSignalStrengthController()).thenReturn(mSignalStrengthController);
         mTestSOSMessageRecommender = new TestSOSMessageRecommender(mContext, Looper.myLooper(),
                 mTestSatelliteController, mTestImsManager);
         when(mServiceState.getState()).thenReturn(STATE_OUT_OF_SERVICE);
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 96c50b6..8c1ae50 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSessionControllerTest.java
@@ -60,12 +60,14 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.PersistableBundle;
+import android.telephony.NetworkRegistrationInfo;
 import android.telephony.ServiceState;
 import android.telephony.satellite.ISatelliteModemStateCallback;
 import android.telephony.satellite.SatelliteManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import com.android.internal.R;
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.flags.FeatureFlags;
@@ -145,6 +147,9 @@
 
         Resources resources = mContext.getResources();
         when(resources.getInteger(anyInt())).thenReturn(TEST_SATELLITE_TIMEOUT_MILLIS);
+        when(resources.getBoolean(
+                 R.bool.config_satellite_modem_support_concurrent_tn_scanning))
+            .thenReturn(false);
 
         when(mFeatureFlags.satellitePersistentLogging()).thenReturn(true);
         when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(false);
@@ -221,6 +226,13 @@
     @Test
     public void testScreenOffInactivityTimer() {
         when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        // Support P2P_SMS
+        when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported(
+                anyInt())).thenReturn(true);
+        when(mMockSatelliteController.getSupportedServicesOnCarrierRoamingNtn(anyInt()))
+                .thenReturn(new int[]{
+                        NetworkRegistrationInfo.SERVICE_TYPE_SMS,
+                        NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY});
         doNothing().when(mDeviceStateMonitor).registerForScreenStateChanged(
                 eq(mTestSatelliteSessionController.getHandler()), anyInt(), any());
         when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(false);
@@ -267,6 +279,13 @@
     @Test
     public void testScreenOffInactivityTimerStop() {
         when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        // Support P2P_SMS
+        when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported(
+                anyInt())).thenReturn(true);
+        when(mMockSatelliteController.getSupportedServicesOnCarrierRoamingNtn(anyInt()))
+                .thenReturn(new int[]{
+                        NetworkRegistrationInfo.SERVICE_TYPE_SMS,
+                        NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY});
         doNothing().when(mDeviceStateMonitor).registerForScreenStateChanged(
                 eq(mTestSatelliteSessionController.getHandler()), anyInt(), any());
         // Satellite enabling request is for an emergency.
@@ -329,6 +348,10 @@
         when(mMockSatelliteController.getRequestIsEmergency()).thenReturn(false);
         when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported(
                 anyInt())).thenReturn(true);
+        when(mMockSatelliteController.getSupportedServicesOnCarrierRoamingNtn(anyInt()))
+                .thenReturn(new int[]{
+                        NetworkRegistrationInfo.SERVICE_TYPE_SMS,
+                        NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY});
         when(mMockSatelliteController.isInCarrierRoamingNbIotNtn()).thenReturn(true);
         PersistableBundle bundle = new PersistableBundle();
         bundle.putInt(KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT,
@@ -423,6 +446,10 @@
         // Support P2P_SMS
         when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported(
                 anyInt())).thenReturn(true);
+        when(mMockSatelliteController.getSupportedServicesOnCarrierRoamingNtn(anyInt()))
+                .thenReturn(new int[]{
+                        NetworkRegistrationInfo.SERVICE_TYPE_SMS,
+                        NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY});
 
         // Setup carrier config for timer values
         PersistableBundle bundle = new PersistableBundle();
@@ -506,6 +533,10 @@
         // Support P2P_SMS
         when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported(
                 anyInt())).thenReturn(true);
+        when(mMockSatelliteController.getSupportedServicesOnCarrierRoamingNtn(anyInt()))
+                .thenReturn(new int[]{
+                        NetworkRegistrationInfo.SERVICE_TYPE_SMS,
+                        NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY});
 
         // Setup carrier config for timer values
         PersistableBundle bundle = new PersistableBundle();
@@ -581,6 +612,10 @@
         // Support P2P_SMS
         when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported(
                 anyInt())).thenReturn(true);
+        when(mMockSatelliteController.getSupportedServicesOnCarrierRoamingNtn(anyInt()))
+                .thenReturn(new int[]{
+                        NetworkRegistrationInfo.SERVICE_TYPE_SMS,
+                        NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY});
 
         // Setup carrier config for timer values
         PersistableBundle bundle = new PersistableBundle();
@@ -648,6 +683,10 @@
         // Support P2P_SMS
         when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported(
                 anyInt())).thenReturn(true);
+        when(mMockSatelliteController.getSupportedServicesOnCarrierRoamingNtn(anyInt()))
+                .thenReturn(new int[]{
+                        NetworkRegistrationInfo.SERVICE_TYPE_SMS,
+                        NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY});
 
         // Setup carrier config for timer values
         PersistableBundle bundle = new PersistableBundle();
@@ -712,6 +751,10 @@
         // Support P2P_SMS
         when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported(
                 anyInt())).thenReturn(true);
+        when(mMockSatelliteController.getSupportedServicesOnCarrierRoamingNtn(anyInt()))
+                .thenReturn(new int[]{
+                        NetworkRegistrationInfo.SERVICE_TYPE_SMS,
+                        NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY});
 
         // Setup carrier config for timer values
         PersistableBundle bundle = new PersistableBundle();
@@ -789,6 +832,10 @@
         // Support P2P_SMS
         when(mMockSatelliteController.isSatelliteRoamingP2pSmSSupported(
                 anyInt())).thenReturn(true);
+        when(mMockSatelliteController.getSupportedServicesOnCarrierRoamingNtn(anyInt()))
+                .thenReturn(new int[]{
+                        NetworkRegistrationInfo.SERVICE_TYPE_SMS,
+                        NetworkRegistrationInfo.SERVICE_TYPE_EMERGENCY});
 
         // Setup carrier config for timer values
         PersistableBundle bundle = new PersistableBundle();
@@ -1483,23 +1530,6 @@
                 SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
         assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName());
 
-        // Set up error response for the request to disable cellular scanning
-        mSatelliteModemInterface.setErrorCode(SatelliteManager.SATELLITE_RESULT_MODEM_ERROR);
-
-        // Start sending datagrams
-        mTestSatelliteSessionController.onDatagramTransferStateChanged(
-                SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT,
-                SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
-                DATAGRAM_TYPE_UNKNOWN);
-        processAllMessages();
-
-        // SatelliteSessionController should stay at IDLE state because it failed to disable
-        // cellular scanning.
-        assertModemStateChangedCallbackNotCalled(mTestSatelliteModemStateCallback);
-        assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName());
-
-        mSatelliteModemInterface.setErrorCode(SatelliteManager.SATELLITE_RESULT_SUCCESS);
-
         // Power off the modem.
         mTestSatelliteSessionController.onSatelliteEnabledStateChanged(false);
         processAllMessages();
@@ -1814,6 +1844,283 @@
         assertEmergencyModeChangedCallbackNotCalled(mTestSatelliteModemStateCallback);
     }
 
+    @Test
+    public void testNotConnectedToIdleToNotConnectedStateTransition() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true);
+        when(mContext.getResources().getBoolean(
+                 R.bool.config_satellite_modem_support_concurrent_tn_scanning)).thenReturn(true);
+
+        assertNotNull(mTestSatelliteSessionController);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+        setupDatagramTransferringState(true);
+
+        powerOnSatelliteModem();
+
+        // SatelliteSessionController should move to NOT_CONNECTED state after the satellite modem
+        // is powered on.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+        // The inactivity timer should be started.
+        assertTrue(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+        verify(mMockDatagramController).onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        clearInvocations(mMockDatagramController);
+
+        // Wait for timeout
+        moveTimeForward(TEST_SATELLITE_TIMEOUT_MILLIS);
+        processAllMessages();
+
+        // SatelliteSessionController should move to IDLE state, but the state transition will
+        // be hidden because device does not support satellite modem IDLE state.
+        assertModemStateChangedCallbackNotCalled(mTestSatelliteModemStateCallback);
+        assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName());
+        // The inactivity timer should be stopped.
+        assertFalse(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+        // The transition is hidden and thus DatagramController is not notified.
+        verify(mMockDatagramController, never()).onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+        clearInvocations(mMockDatagramController);
+
+        // Start sending datagrams
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT,
+                SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+                DATAGRAM_TYPE_UNKNOWN);
+        processAllMessages();
+
+        // SatelliteSessionController should move to NOT_CONNECTED state
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+        verify(mMockDatagramController).onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        clearInvocations(mMockDatagramController);
+    }
+
+    @Test
+    public void testNotConnectedToIdleToTransferringStateTransition() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true);
+        when(mContext.getResources().getBoolean(
+                 R.bool.config_satellite_modem_support_concurrent_tn_scanning)).thenReturn(true);
+
+        assertNotNull(mTestSatelliteSessionController);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+        setupDatagramTransferringState(true);
+
+        powerOnSatelliteModem();
+
+        // SatelliteSessionController should move to NOT_CONNECTED state after the satellite modem
+        // is powered on.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+        // The inactivity timer should be started.
+        assertTrue(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+        verify(mMockDatagramController).onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        clearInvocations(mMockDatagramController);
+
+        // Wait for timeout
+        moveTimeForward(TEST_SATELLITE_TIMEOUT_MILLIS);
+        processAllMessages();
+
+        // SatelliteSessionController should move to IDLE state, but the state transition will
+        // be hidden because device does not support satellite modem IDLE state.
+        assertModemStateChangedCallbackNotCalled(mTestSatelliteModemStateCallback);
+        assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName());
+        // The inactivity timer should be stopped.
+        assertFalse(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+        // The transition is hidden and thus DatagramController is not notified.
+        verify(mMockDatagramController, never()).onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+        clearInvocations(mMockDatagramController);
+
+        // Modem report CONNECTED state
+        mTestSatelliteSessionController.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        processAllMessages();
+
+        // SatelliteSessionController should stay in IDLE state, but clients should be
+        // notified that modem has moved to CONNECTED state.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName());
+        verify(mMockDatagramController).onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        clearInvocations(mMockDatagramController);
+
+        // Start sending datagrams
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
+                SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+                DATAGRAM_TYPE_UNKNOWN);
+        processAllMessages();
+
+        // SatelliteSessionController should move to TRANSFERRING state.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING);
+        assertEquals(STATE_TRANSFERRING, mTestSatelliteSessionController.getCurrentStateName());
+        verify(mMockDatagramController).onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING);
+        clearInvocations(mMockDatagramController);
+    }
+
+    @Test
+    public void testConnectedToIdleToTransferringStateTransition() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true);
+        when(mContext.getResources().getBoolean(
+                 R.bool.config_satellite_modem_support_concurrent_tn_scanning)).thenReturn(true);
+
+        assertNotNull(mTestSatelliteSessionController);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+        setupDatagramTransferringState(false);
+
+        powerOnSatelliteModem();
+
+        // SatelliteSessionController should move to NOT_CONNECTED state after the satellite modem
+        // is powered on.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+        assertFalse(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+        verify(mMockDatagramController).onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        clearInvocations(mMockDatagramController);
+
+        // Modem report CONNECTED state
+        setupDatagramTransferringState(true);
+        mTestSatelliteSessionController.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        processAllMessages();
+
+        // SatelliteSessionController should move to CONNECTED state.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        assertEquals(STATE_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+        // The inactivity timer should be started.
+        assertTrue(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+        verify(mMockDatagramController).onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        clearInvocations(mMockDatagramController);
+
+        // Wait for timeout
+        moveTimeForward(TEST_SATELLITE_TIMEOUT_MILLIS);
+        processAllMessages();
+
+        // SatelliteSessionController should move to IDLE state, but the state transition will
+        // be hidden because device does not support satellite modem IDLE state.
+        assertModemStateChangedCallbackNotCalled(mTestSatelliteModemStateCallback);
+        assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName());
+        // The inactivity timer should be stopped.
+        assertFalse(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+        // The transition is hidden and thus DatagramController is not notified.
+        verify(mMockDatagramController, never()).onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+        clearInvocations(mMockDatagramController);
+
+        // Start sending datagrams
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_SENDING,
+                SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+                DATAGRAM_TYPE_UNKNOWN);
+        processAllMessages();
+
+        // SatelliteSessionController should move to TRANSFERRING state.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING);
+        assertEquals(STATE_TRANSFERRING, mTestSatelliteSessionController.getCurrentStateName());
+        verify(mMockDatagramController).onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING);
+        clearInvocations(mMockDatagramController);
+    }
+
+    @Test
+    public void testConnectedToIdleToNotConnectedStateTransition() {
+        when(mFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
+        when(mMockSatelliteController.isSatelliteAttachRequired()).thenReturn(true);
+        when(mContext.getResources().getBoolean(
+                 R.bool.config_satellite_modem_support_concurrent_tn_scanning)).thenReturn(true);
+
+        assertNotNull(mTestSatelliteSessionController);
+        assertEquals(STATE_POWER_OFF, mTestSatelliteSessionController.getCurrentStateName());
+        setupDatagramTransferringState(false);
+
+        powerOnSatelliteModem();
+
+        // SatelliteSessionController should move to NOT_CONNECTED state after the satellite modem
+        // is powered on.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+        assertFalse(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+        verify(mMockDatagramController).onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        clearInvocations(mMockDatagramController);
+
+        // Modem report CONNECTED state
+        setupDatagramTransferringState(true);
+        mTestSatelliteSessionController.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        processAllMessages();
+
+        // SatelliteSessionController should move to CONNECTED state.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        assertEquals(STATE_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+        // The inactivity timer should be started.
+        assertTrue(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+        verify(mMockDatagramController).onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED);
+        clearInvocations(mMockDatagramController);
+
+        // Wait for timeout
+        moveTimeForward(TEST_SATELLITE_TIMEOUT_MILLIS);
+        processAllMessages();
+
+        // SatelliteSessionController should move to IDLE state, but the state transition will
+        // be hidden because device does not support satellite modem IDLE state.
+        assertModemStateChangedCallbackNotCalled(mTestSatelliteModemStateCallback);
+        assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName());
+        // The inactivity timer should be stopped.
+        assertFalse(mTestSatelliteSessionController.isNbIotInactivityTimerStarted());
+        // The transition is hidden and thus DatagramController is not notified.
+        verify(mMockDatagramController, never()).onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_IDLE);
+        clearInvocations(mMockDatagramController);
+
+        // Modem report NOT_CONNECTED state
+        mTestSatelliteSessionController.onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        processAllMessages();
+
+        // SatelliteSessionController should stay in IDLE state, but the clients
+        // should be notified that modem has moved to NOT_CONNECTED state.
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        assertEquals(STATE_IDLE, mTestSatelliteSessionController.getCurrentStateName());
+        verify(mMockDatagramController).onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        clearInvocations(mMockDatagramController);
+
+        // Start sending datagrams
+        mTestSatelliteSessionController.onDatagramTransferStateChanged(
+                SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT,
+                SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE,
+                DATAGRAM_TYPE_UNKNOWN);
+        processAllMessages();
+
+        // SatelliteSessionController should move to NOT_CONNECTED state
+        assertSuccessfulModemStateChangedCallback(mTestSatelliteModemStateCallback,
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        assertEquals(STATE_NOT_CONNECTED, mTestSatelliteSessionController.getCurrentStateName());
+        verify(mMockDatagramController).onSatelliteModemStateChanged(
+                SatelliteManager.SATELLITE_MODEM_STATE_NOT_CONNECTED);
+        clearInvocations(mMockDatagramController);
+    }
 
     private void verifyEsosP2pSmsInactivityTimer(boolean esosTimer, boolean p2pSmsTimer) {
         assertEquals(mTestSatelliteSessionController.isEsosInActivityTimerStarted(), esosTimer);
@@ -1835,9 +2142,9 @@
         processAllMessages();
     }
 
-    private void setupDatagramTransferringState(boolean isTransferring) {
-        when(mMockDatagramController.isSendingInIdleState()).thenReturn(isTransferring);
-        when(mMockDatagramController.isPollingInIdleState()).thenReturn(isTransferring);
+    private void setupDatagramTransferringState(boolean isIdle) {
+        when(mMockDatagramController.isSendingInIdleState()).thenReturn(isIdle);
+        when(mMockDatagramController.isPollingInIdleState()).thenReturn(isIdle);
     }
 
     private void powerOnSatelliteModem() {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionPlanTest.java b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionPlanTest.java
new file mode 100644
index 0000000..2c13d3b
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionPlanTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.subscription;
+
+import static android.telephony.SubscriptionPlan.SUBSCRIPTION_STATUS_ACTIVE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.telephony.SubscriptionPlan;
+import android.testing.AndroidTestingRunner;
+
+import com.android.internal.telephony.flags.Flags;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Period;
+import java.time.ZonedDateTime;
+
+@RunWith(AndroidTestingRunner.class)
+public class SubscriptionPlanTest {
+    private static final ZonedDateTime ZONED_DATE_TIME_START =
+            ZonedDateTime.parse("2007-03-14T00:00:00.000Z");
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+    public void testBuilderExpirationDateSetsCorrectly() {
+        ZonedDateTime endDate = ZonedDateTime.parse("2024-11-07T00:00:00.000Z");
+
+        SubscriptionPlan planNonRecurring = SubscriptionPlan.Builder
+                .createNonrecurring(ZONED_DATE_TIME_START, endDate)
+                .setTitle("unit test")
+                .build();
+        SubscriptionPlan planRecurring = SubscriptionPlan.Builder
+                .createRecurring(ZONED_DATE_TIME_START, Period.ofMonths(1))
+                .setTitle("unit test")
+                .build();
+
+        assertThat(planNonRecurring.getPlanEndDate()).isEqualTo(endDate);
+        assertNull(planRecurring.getPlanEndDate());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+    public void testBuilderValidSubscriptionStatusSetsCorrectly() {
+        @SubscriptionPlan.SubscriptionStatus int status = SUBSCRIPTION_STATUS_ACTIVE;
+
+        SubscriptionPlan plan = SubscriptionPlan.Builder
+                .createRecurring(ZONED_DATE_TIME_START, Period.ofMonths(1))
+                .setSubscriptionStatus(status)
+                .setTitle("unit test")
+                .build();
+
+        assertThat(plan.getSubscriptionStatus()).isEqualTo(status);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_SUBSCRIPTION_PLAN_ALLOW_STATUS_AND_END_DATE)
+    public void testBuilderInvalidSubscriptionStatusThrowsError() {
+        int minInvalid = -1;
+        int maxInvalid = 5;
+
+        assertThrows(IllegalArgumentException.class, () -> {
+            SubscriptionPlan.Builder
+                    .createRecurring(ZONED_DATE_TIME_START, Period.ofMonths(1))
+                    .setSubscriptionStatus(minInvalid)
+                    .setTitle("unit test")
+                    .build();
+        });
+
+        assertThrows(IllegalArgumentException.class, () -> {
+            SubscriptionPlan.Builder
+                    .createRecurring(ZONED_DATE_TIME_START, Period.ofMonths(1))
+                    .setSubscriptionStatus(maxInvalid)
+                    .setTitle("unit test")
+                    .build();
+        });
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
index 33b195c..bfdca0f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
@@ -69,6 +69,7 @@
 
     @After
     public void tearDown() throws Exception {
+        mUiccCard.dispose();
         mUiccCard = null;
         mIccIoResult = null;
         super.tearDown();
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 58a8153..3343570 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
@@ -15,8 +15,6 @@
  */
 package com.android.internal.telephony.uicc;
 
-import static junit.framework.Assert.fail;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -131,6 +129,7 @@
 
     @After
     public void tearDown() throws Exception {
+        if (mUiccControllerUT != null) mUiccControllerUT.dispose();
         mUiccControllerUT = null;
         super.tearDown();
     }
@@ -145,6 +144,7 @@
                 com.android.internal.R.array.non_removable_euicc_slots,
                 nonRemovableEuiccSlots);
         replaceInstance(UiccController.class, "mInstance", null, null);
+        mUiccControllerUT.dispose();
         mUiccControllerUT = UiccController.make(mContext, mFeatureFlags);
         processAllMessages();
     }
@@ -250,7 +250,7 @@
         mUiccControllerUT.mContext = InstrumentationRegistry.getContext();
 
         // Mock out UiccSlots
-        mUiccControllerUT.mUiccSlots[0] = mMockSlot;
+        mUiccControllerUT.setUiccSlot(0, mMockSlot);
         doReturn(mMockCard).when(mMockSlot).getUiccCard();
         doReturn(mMockPort).when(mMockCard).getUiccPort(0);
         doReturn("A1B2C3D4").when(mMockPort).getIccId();
@@ -296,7 +296,7 @@
         mUiccControllerUT.mContext = InstrumentationRegistry.getContext();
 
         // Mock out UiccSlots
-        mUiccControllerUT.mUiccSlots[0] = mMockSlot;
+        mUiccControllerUT.setUiccSlot(0, mMockSlot);
         doReturn(true).when(mMockSlot).isEuicc();
 
         // simulate slot status loaded so that the UiccController sets the card ID
@@ -323,7 +323,7 @@
         mUiccControllerUT.mContext = InstrumentationRegistry.getContext();
 
         // Mock out UiccSlots
-        mUiccControllerUT.mUiccSlots[0] = mMockSlot;
+        mUiccControllerUT.setUiccSlot(0, mMockSlot);
         doReturn(true).when(mMockSlot).isEuicc();
 
         // simulate slot status loaded so that the UiccController sets the card ID
@@ -351,7 +351,7 @@
         mUiccControllerUT.mContext = InstrumentationRegistry.getContext();
 
         // Mock out UiccSlots
-        mUiccControllerUT.mUiccSlots[0] = mMockSlot;
+        mUiccControllerUT.setUiccSlot(0, mMockSlot);
         doReturn(false).when(mMockSlot).isEuicc();
         doReturn(mMockCard).when(mMockSlot).getUiccCard();
         doReturn("ASDF1234").when(mMockCard).getCardId();
@@ -402,7 +402,7 @@
         mUiccControllerUT.mContext = InstrumentationRegistry.getContext();
 
         // Mock out UiccSlots
-        mUiccControllerUT.mUiccSlots[0] = mMockSlot;
+        mUiccControllerUT.setUiccSlot(0, mMockSlot);
         doReturn(false).when(mMockSlot).isEuicc();
         doReturn(mMockCard).when(mMockSlot).getUiccCard();
         doReturn("ASDF1234").when(mMockCard).getCardId();
@@ -453,7 +453,7 @@
         mUiccControllerUT.mContext = InstrumentationRegistry.getContext();
 
         // Mock out UiccSlots
-        mUiccControllerUT.mUiccSlots[0] = mMockSlot;
+        mUiccControllerUT.setUiccSlot(0, mMockSlot);
         doReturn(true).when(mMockSlot).isEuicc();
         doReturn(null).when(mMockSlot).getUiccCard();
         doReturn(false).when(mMockSlot).isRemovable();
@@ -499,21 +499,17 @@
      * The default eUICC should not be the removable slot if there is a built-in eUICC.
      */
     @Test
-    public void testDefaultEuiccIsNotRemovable() {
-        try {
-            reconfigureSlots(2, new int[]{ 1 } /* non-removable slot */);
-        } catch (Exception e) {
-            fail("Unable to reconfigure slots.");
-        }
+    public void testDefaultEuiccIsNotRemovable() throws Exception {
+        reconfigureSlots(2, new int[]{ 1 } /* non-removable slot */);
 
         // Give UiccController a real context so it can use shared preferences
         mUiccControllerUT.mContext = InstrumentationRegistry.getContext();
 
         // Mock out UiccSlots so that [0] is a removable eUICC and [1] is built-in
-        mUiccControllerUT.mUiccSlots[0] = mMockRemovableEuiccSlot;
+        mUiccControllerUT.setUiccSlot(0, mMockRemovableEuiccSlot);
         doReturn(true).when(mMockRemovableEuiccSlot).isEuicc();
         doReturn(true).when(mMockRemovableEuiccSlot).isRemovable();
-        mUiccControllerUT.mUiccSlots[1] = mMockSlot;
+        mUiccControllerUT.setUiccSlot(1, mMockSlot);
         doReturn(true).when(mMockSlot).isEuicc();
         doReturn(false).when(mMockSlot).isRemovable();
 
@@ -550,21 +546,17 @@
      * not depend on the order of the slots.
      */
     @Test
-    public void testDefaultEuiccIsNotRemovable_swapSlotOrder() {
-        try {
-            reconfigureSlots(2, new int[]{ 0 } /* non-removable slot */);
-        } catch (Exception e) {
-            fail("Unable to reconfigure slots.");
-        }
+    public void testDefaultEuiccIsNotRemovable_swapSlotOrder() throws Exception {
+        reconfigureSlots(2, new int[]{ 0 } /* non-removable slot */);
 
         // Give UiccController a real context so it can use shared preferences
         mUiccControllerUT.mContext = InstrumentationRegistry.getContext();
 
         // Mock out UiccSlots so that [0] is a built-in eUICC and [1] is removable
-        mUiccControllerUT.mUiccSlots[0] = mMockSlot;
+        mUiccControllerUT.setUiccSlot(0, mMockSlot);
         doReturn(true).when(mMockSlot).isEuicc();
         doReturn(false).when(mMockSlot).isRemovable();
-        mUiccControllerUT.mUiccSlots[1] = mMockRemovableEuiccSlot;
+        mUiccControllerUT.setUiccSlot(1, mMockRemovableEuiccSlot);
         doReturn(true).when(mMockRemovableEuiccSlot).isEuicc();
         doReturn(true).when(mMockRemovableEuiccSlot).isRemovable();
 
@@ -603,21 +595,17 @@
      * the removable eUICC.
      */
     @Test
-    public void testDefaultEuiccIsNotRemovable_EuiccIsInactive() {
-        try {
-            reconfigureSlots(2, new int[]{ 1 } /* non-removable slot */);
-        } catch (Exception e) {
-            fail();
-        }
+    public void testDefaultEuiccIsNotRemovable_EuiccIsInactive() throws Exception {
+        reconfigureSlots(2, new int[]{ 1 } /* non-removable slot */);
 
         // Give UiccController a real context so it can use shared preferences
         mUiccControllerUT.mContext = InstrumentationRegistry.getContext();
 
         // Mock out UiccSlots. Slot 0 is inactive here.
-        mUiccControllerUT.mUiccSlots[0] = mMockSlot;
+        mUiccControllerUT.setUiccSlot(0, mMockSlot);
         doReturn(true).when(mMockSlot).isEuicc();
         doReturn(false).when(mMockSlot).isRemovable();
-        mUiccControllerUT.mUiccSlots[1] = mMockRemovableEuiccSlot;
+        mUiccControllerUT.setUiccSlot(1, mMockRemovableEuiccSlot);
         doReturn(true).when(mMockRemovableEuiccSlot).isEuicc();
         doReturn(true).when(mMockRemovableEuiccSlot).isRemovable();
 
@@ -669,7 +657,7 @@
         mUiccControllerUT.mContext = InstrumentationRegistry.getContext();
 
         // Mock out UiccSlots
-        mUiccControllerUT.mUiccSlots[0] = mMockSlot;
+        mUiccControllerUT.setUiccSlot(0, mMockSlot);
         doReturn(true).when(mMockSlot).isEuicc();
         doReturn(null).when(mMockSlot).getUiccCard();
         //doReturn("123451234567890").when(mMockSlot).getIccId();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccPortTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccPortTest.java
index a2b42af..47b7c53 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccPortTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccPortTest.java
@@ -82,6 +82,7 @@
 
     @After
     public void tearDown() throws Exception {
+        mUiccPort.dispose();
         mUiccPort = null;
         mIccIoResult = null;
         super.tearDown();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
index 671f273..8449ecc 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
@@ -103,6 +103,7 @@
         mTestHandlerThread = null;
         mTestHandler.removeCallbacksAndMessages(null);
         mTestHandler = null;
+        mUiccSlot.dispose();
         mUiccSlot = null;
         super.tearDown();
     }
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 f88bc1e..c9b159c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
@@ -51,6 +51,8 @@
     private static final int CARD_COUNT = 1;
     private static final String PROVISIONING_PACKAGE_NAME = "test.provisioning.package";
 
+    private UiccCard mUiccCardToDispose;
+
     // Mocked classes
     private Resources mResources;
 
@@ -77,6 +79,9 @@
     @After
     public void tearDown() throws Exception {
         super.tearDown();
+
+        if (mUiccCardToDispose != null) mUiccCardToDispose.dispose();
+        mUiccCardToDispose = null;
     }
 
     @Test @SmallTest
@@ -99,7 +104,7 @@
         msg.what = integerArgumentCaptor.getValue();
 
         // The first broadcast should be sent after initialization.
-        UiccCard card = new UiccCard(mContext, mSimulatedCommands,
+        UiccCard card = mUiccCardToDispose = new UiccCard(mContext, mSimulatedCommands,
                 makeCardStatus(CardState.CARDSTATE_PRESENT), 0 /* phoneId */, new Object(),
                 IccSlotStatus.MultipleEnabledProfilesMode.NONE);
         when(UiccController.getInstance().getUiccCardForPhone(0)).thenReturn(card);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
index bcb5c4c..560c9aa 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
@@ -109,7 +109,10 @@
             mHandler.removeCallbacksAndMessages(null);
             mHandler = null;
         }
-        mEuiccCard = null;
+        if (mEuiccCard != null) {
+            mEuiccCard.dispose();
+            mEuiccCard = null;
+        }
         super.tearDown();
     }
 
@@ -132,6 +135,7 @@
     @Test
     public void testPassEidInConstructor() {
         mMockIccCardStatus.eid = "1A2B3C4D";
+        mEuiccCard.dispose();
         mEuiccCard = new EuiccCard(mContextFixture.getTestDouble(), mMockCi,
                 mMockIccCardStatus, 0 /* phoneId */, new Object(),
                 IccSlotStatus.MultipleEnabledProfilesMode.NONE);
@@ -154,6 +158,7 @@
     public void testLoadEidAndNotifyRegistrants() {
         int channel = mockLogicalChannelResponses("BF3E065A041A2B3C4D9000");
         mHandler.post(() -> {
+            mEuiccCard.dispose();
             mEuiccCard = new EuiccCard(mContextFixture.getTestDouble(), mMockCi,
                     mMockIccCardStatus, 0 /* phoneId */, new Object(),
                     IccSlotStatus.MultipleEnabledProfilesMode.NONE);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccPortTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccPortTest.java
index 2fef021..f0f1af3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccPortTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccPortTest.java
@@ -141,6 +141,7 @@
     public void tearDown() throws Exception {
         mHandler.removeCallbacksAndMessages(null);
         mHandler = null;
+        mEuiccPort.dispose();
         mEuiccPort = null;
         super.tearDown();
     }