Merge "Internal Telephony Support For Cellular Identifier Disclosure HALs" into main
diff --git a/Android.bp b/Android.bp
index 301343c..121236a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -106,6 +106,7 @@
         "net-utils-framework-common",
         "telephony-protos",
         "modules-utils-build_system",
+        "modules-utils-fastxmlserializer",
         "modules-utils-statemachine",
     ],
 
diff --git a/flags/messaging.aconfig b/flags/messaging.aconfig
index 3c20d69..1ba89ba 100644
--- a/flags/messaging.aconfig
+++ b/flags/messaging.aconfig
@@ -13,3 +13,10 @@
   description: "This flag controls AP domain selection support for normal/emergency SMS."
   bug: "262804071"
 }
+
+flag {
+  name: "mms_disabled_error"
+  namespace: "telephony"
+  description: "This flag controls the support of the new MMS error code MMS_ERROR_MMS_DISABLED."
+  bug: "305062594"
+}
\ No newline at end of file
diff --git a/flags/misc.aconfig b/flags/misc.aconfig
index 87acccc..e9e3169 100644
--- a/flags/misc.aconfig
+++ b/flags/misc.aconfig
@@ -20,4 +20,11 @@
   namespace: "telephony"
   description: "Enable system api isWpsCallNumber. Its an utility api to check if the dialed number is for Wireless Priority Service call."
   bug: "304272356"
+}
+
+flag {
+  name: "ensure_access_to_call_settings_is_restricted"
+  namespace: "telephony"
+  description: "Check if access to mobile network configs restricted before displaying call options"
+  bug: "309655251"
 }
\ No newline at end of file
diff --git a/flags/uicc.aconfig b/flags/uicc.aconfig
index 39b344b..3ef2301 100644
--- a/flags/uicc.aconfig
+++ b/flags/uicc.aconfig
@@ -1,4 +1,11 @@
 package: "com.android.internal.telephony.flags"
+
+flag {
+    name: "esim_bootstrap_provisioning_flag"
+    namespace: "telephony"
+    description: "This flag controls eSIM Bootstrap provisioning feature support."
+    bug:"298567545"
+}
 flag {
     name: "imsi_key_retry_download_on_phone_unlock"
     namespace: "telephony"
diff --git a/proto/src/persist_atoms.proto b/proto/src/persist_atoms.proto
index 993aa4d..537f824 100644
--- a/proto/src/persist_atoms.proto
+++ b/proto/src/persist_atoms.proto
@@ -677,4 +677,7 @@
     optional bool is_ims_registered = 3;
     optional int32 cellular_service_state = 4;
     optional int32 count = 5;
+    optional bool is_multi_sim = 6;
+    optional int32 recommending_handover_type = 7;
+    optional bool is_satellite_allowed_in_current_location = 8;
 }
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index a796368..625a937 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -62,6 +62,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.reflect.Method;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -521,6 +522,27 @@
         return sMetricsCollector;
     }
 
+    /**
+     * Print all feature flag configurations that Telephony is using for debugging purposes.
+     */
+    private static void reflectAndPrintFlagConfigs(IndentingPrintWriter pw) {
+
+        try {
+            // Look away, a forbidden technique (reflection) is being used to allow us to get
+            // all flag configs without having to add them manually to this method.
+            Method[] methods = FeatureFlags.class.getMethods();
+            if (methods.length == 0) {
+                pw.println("NONE");
+                return;
+            }
+            for (Method m : methods) {
+                pw.println(m.getName() + "-> " + m.invoke(sFeatureFlags));
+            }
+        } catch (Exception e) {
+            pw.println("[ERROR]");
+        }
+    }
+
     public static void dump(FileDescriptor fd, PrintWriter printwriter, String[] args) {
         IndentingPrintWriter pw = new IndentingPrintWriter(printwriter, "  ");
         pw.println("PhoneFactory:");
@@ -613,8 +635,15 @@
         } catch (Exception e) {
             e.printStackTrace();
         }
-
         pw.flush();
         pw.decreaseIndent();
+
+        pw.println("++++++++++++++++++++++++++++++++");
+        pw.println("Flag Configurations:");
+        pw.increaseIndent();
+        reflectAndPrintFlagConfigs(pw);
+        pw.flush();
+        pw.decreaseIndent();
+        pw.println("++++++++++++++++++++++++++++++++");
     }
 }
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index 14f71bd..a94875d 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -1946,16 +1946,15 @@
                 err = ((CommandException)(ar.exception)).getCommandError();
             }
 
-            if (mCi.getRadioState() != TelephonyManager.RADIO_POWER_ON) {
-                log("handlePollStateResult: Invalid response due to radio off or unavailable. "
-                        + "Set ServiceState to out of service.");
-                pollStateInternal(false);
-                return;
-            }
-
             if (err == CommandException.Error.RADIO_NOT_AVAILABLE) {
-                loge("handlePollStateResult: RIL returned RADIO_NOT_AVAILABLE when radio is on.");
-                cancelPollState();
+                loge("handlePollStateResult: RIL returned RADIO_NOT_AVAILABLE.");
+                if (mCi.getRadioState() == TelephonyManager.RADIO_POWER_ON) {
+                    cancelPollState();
+                } else {
+                    handlePollStateInternalForRadioOffOrUnavailable(
+                            mCi.getRadioState() == TelephonyManager.RADIO_POWER_OFF);
+                    pollStateDone();
+                }
                 return;
             }
 
@@ -3312,42 +3311,17 @@
 
     private void pollStateInternal(boolean modemTriggered) {
         mPollingContext = new int[1];
-        NetworkRegistrationInfo nri;
 
         log("pollState: modemTriggered=" + modemTriggered + ", radioState=" + mCi.getRadioState());
 
         switch (mCi.getRadioState()) {
             case TelephonyManager.RADIO_POWER_UNAVAILABLE:
-                // Preserve the IWLAN registration state, because that should not be affected by
-                // radio availability.
-                nri = mNewSS.getNetworkRegistrationInfo(
-                        NetworkRegistrationInfo.DOMAIN_PS,
-                        AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
-                mNewSS.setOutOfService(false);
-                // Add the IWLAN registration info back to service state.
-                if (nri != null) {
-                    mNewSS.addNetworkRegistrationInfo(nri);
-                }
-                mPhone.getSignalStrengthController().setSignalStrengthDefaultValues();
-                mLastNitzData = null;
-                mNitzState.handleNetworkUnavailable();
+                handlePollStateInternalForRadioOffOrUnavailable(false);
                 pollStateDone();
                 break;
 
             case TelephonyManager.RADIO_POWER_OFF:
-                // Preserve the IWLAN registration state, because that should not be affected by
-                // radio availability.
-                nri = mNewSS.getNetworkRegistrationInfo(
-                        NetworkRegistrationInfo.DOMAIN_PS,
-                        AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
-                mNewSS.setOutOfService(true);
-                // Add the IWLAN registration info back to service state.
-                if (nri != null) {
-                    mNewSS.addNetworkRegistrationInfo(nri);
-                }
-                mPhone.getSignalStrengthController().setSignalStrengthDefaultValues();
-                mLastNitzData = null;
-                mNitzState.handleNetworkUnavailable();
+                handlePollStateInternalForRadioOffOrUnavailable(true);
                 // Don't poll when device is shutting down or the poll was not modemTriggered
                 // (they sent us new radio data) and the current network is not IWLAN
                 if (mDeviceShuttingDown ||
@@ -3391,6 +3365,21 @@
         }
     }
 
+    private void handlePollStateInternalForRadioOffOrUnavailable(boolean radioOff) {
+        // Preserve the IWLAN registration state, which should not be affected by radio availability
+        NetworkRegistrationInfo nri = mNewSS.getNetworkRegistrationInfo(
+                NetworkRegistrationInfo.DOMAIN_PS,
+                AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+        mNewSS.setOutOfService(radioOff);
+        // Add the IWLAN registration info back to service state.
+        if (nri != null) {
+            mNewSS.addNetworkRegistrationInfo(nri);
+        }
+        mPhone.getSignalStrengthController().setSignalStrengthDefaultValues();
+        mLastNitzData = null;
+        mNitzState.handleNetworkUnavailable();
+    }
+
     /**
      * Get the highest-priority CellIdentity for a provided ServiceState.
      *
diff --git a/src/java/com/android/internal/telephony/data/DataNetwork.java b/src/java/com/android/internal/telephony/data/DataNetwork.java
index 0c5c9cb..4cf5ef9 100644
--- a/src/java/com/android/internal/telephony/data/DataNetwork.java
+++ b/src/java/com/android/internal/telephony/data/DataNetwork.java
@@ -2290,6 +2290,10 @@
             }
         }
 
+        if (mDataNetworkController.isEsimBootStrapProvisioningActivated()) {
+            builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+        }
+
         // If one of the capabilities are for special use, for example, IMS, CBS, then this
         // network should be restricted, regardless data is enabled or not.
         if (NetworkCapabilitiesUtils.inferRestrictedCapability(builder.build())
@@ -3318,7 +3322,8 @@
             TelephonyNetworkRequest networkRequest = mAttachedNetworkRequestList.get(0);
             DataProfile dataProfile = mDataNetworkController.getDataProfileManager()
                     .getDataProfileForNetworkRequest(networkRequest, targetNetworkType,
-                            mPhone.getServiceState().isUsingNonTerrestrialNetwork(), false);
+                            mPhone.getServiceState().isUsingNonTerrestrialNetwork(),
+                            mDataNetworkController.isEsimBootStrapProvisioningActivated(), false);
             // Some carriers have different profiles between cellular and IWLAN. We need to
             // dynamically switch profile, but only when those profiles have same APN name.
             if (dataProfile != null && dataProfile.getApnSetting() != null
diff --git a/src/java/com/android/internal/telephony/data/DataNetworkController.java b/src/java/com/android/internal/telephony/data/DataNetworkController.java
index 2d91967..c29afa6 100644
--- a/src/java/com/android/internal/telephony/data/DataNetworkController.java
+++ b/src/java/com/android/internal/telephony/data/DataNetworkController.java
@@ -101,6 +101,8 @@
 import com.android.internal.telephony.data.LinkBandwidthEstimator.LinkBandwidthEstimatorCallback;
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.ims.ImsResolver;
+import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.telephony.Rlog;
 
@@ -1330,6 +1332,7 @@
                         .getDataProfileForNetworkRequest(requestList.getFirst(),
                                 TelephonyManager.NETWORK_TYPE_IWLAN,
                                 mServiceState.isUsingNonTerrestrialNetwork(),
+                                isEsimBootStrapProvisioningActivated(),
                                 false/*ignorePermanentFailure*/);
                 if (candidate != null && !dataNetwork.getDataProfile().equals(candidate)) {
                     logv("But skipped because found better data profile " + candidate
@@ -1494,7 +1497,8 @@
             evaluation.addDataAllowedReason(DataAllowedReason.EMERGENCY_REQUEST);
             evaluation.setCandidateDataProfile(mDataProfileManager.getDataProfileForNetworkRequest(
                     networkRequest, getDataNetworkType(transport),
-                    mServiceState.isUsingNonTerrestrialNetwork(), true));
+                    mServiceState.isUsingNonTerrestrialNetwork(),
+                    isEsimBootStrapProvisioningActivated(), true));
             networkRequest.setEvaluation(evaluation);
             log(evaluation.toString());
             return evaluation;
@@ -1653,6 +1657,7 @@
         DataProfile dataProfile = mDataProfileManager
                 .getDataProfileForNetworkRequest(networkRequest, networkType,
                         mServiceState.isUsingNonTerrestrialNetwork(),
+                        isEsimBootStrapProvisioningActivated(),
                         // If the evaluation is due to environmental changes, then we should ignore
                         // the permanent failure reached earlier.
                         reason.isConditionBased());
@@ -2272,6 +2277,22 @@
     }
 
     /**
+     * Check if the device is in eSIM bootstrap provisioning state.
+     *
+     * @return {@code true} if the device is under eSIM bootstrap provisioning.
+     */
+    public boolean isEsimBootStrapProvisioningActivated() {
+        if (!mFeatureFlags.esimBootstrapProvisioningFlag()) {
+            return false;
+        }
+
+        SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance()
+                .getSubscriptionInfoInternal(mPhone.getSubId());
+        return subInfo != null
+                && subInfo.getProfileClass() == SubscriptionManager.PROFILE_CLASS_PROVISIONING;
+    }
+
+    /**
      * Register for IMS feature registration state.
      *
      * @param subId The subscription index.
diff --git a/src/java/com/android/internal/telephony/data/DataProfileManager.java b/src/java/com/android/internal/telephony/data/DataProfileManager.java
index 273dc8b..df1fc92 100644
--- a/src/java/com/android/internal/telephony/data/DataProfileManager.java
+++ b/src/java/com/android/internal/telephony/data/DataProfileManager.java
@@ -255,6 +255,7 @@
         cursor.close();
         return dataProfile;
     }
+
     /**
      * Update all data profiles, including preferred data profile, and initial attach data profile.
      * Also send those profiles down to the modem if needed.
@@ -640,12 +641,12 @@
      */
     public @Nullable DataProfile getDataProfileForNetworkRequest(
             @NonNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType,
-            boolean isNtn, boolean ignorePermanentFailure) {
+            boolean isNtn, boolean isEsimBootstrapProvisioning, boolean ignorePermanentFailure) {
         ApnSetting apnSetting = null;
         if (networkRequest.hasAttribute(TelephonyNetworkRequest
                 .CAPABILITY_ATTRIBUTE_APN_SETTING)) {
             apnSetting = getApnSettingForNetworkRequest(networkRequest, networkType, isNtn,
-                    ignorePermanentFailure);
+                    isEsimBootstrapProvisioning, ignorePermanentFailure);
         }
 
         TrafficDescriptor.Builder trafficDescriptorBuilder = new TrafficDescriptor.Builder();
@@ -711,48 +712,52 @@
      */
     private @Nullable ApnSetting getApnSettingForNetworkRequest(
             @NonNull TelephonyNetworkRequest networkRequest, @NetworkType int networkType,
-            boolean isNtn, boolean ignorePermanentFailure) {
+            boolean isNtn, boolean isEsimBootStrapProvisioning, boolean ignorePermanentFailure) {
         if (!networkRequest.hasAttribute(
                 TelephonyNetworkRequest.CAPABILITY_ATTRIBUTE_APN_SETTING)) {
             loge("Network request does not have APN setting attribute.");
             return null;
         }
 
-        if (mFeatureFlags.carrierEnabledSatelliteFlag()) {
-            // If the preferred data profile can be used, always use it if it can satisfy the
-            // network request with current network type (even though it's been marked as permanent
-            // failed.)
-            if (mPreferredDataProfile != null
-                    && networkRequest.canBeSatisfiedBy(mPreferredDataProfile)
-                    && mPreferredDataProfile.getApnSetting() != null
-                    && mPreferredDataProfile.getApnSetting().canSupportNetworkType(networkType)
-                    && ((isNtn && mPreferredDataProfile.getApnSetting().isForInfrastructure(
-                            ApnSetting.INFRASTRUCTURE_SATELLITE))
-                    || (!isNtn && mPreferredDataProfile.getApnSetting().isForInfrastructure(
-                            ApnSetting.INFRASTRUCTURE_CELLULAR)))) {
-                if (ignorePermanentFailure || !mPreferredDataProfile.getApnSetting()
-                        .getPermanentFailed()) {
-                    return mPreferredDataProfile.getApnSetting();
+        // if esim bootstrap provisioning in progress, do not apply preferred data profile
+        if (!isEsimBootStrapProvisioning) {
+            if (mFeatureFlags.carrierEnabledSatelliteFlag()) {
+                // If the preferred data profile can be used, always use it if it can satisfy the
+                // network request with current network type (even though it's been marked as
+                // permanent failed.)
+                if (mPreferredDataProfile != null
+                        && networkRequest.canBeSatisfiedBy(mPreferredDataProfile)
+                        && mPreferredDataProfile.getApnSetting() != null
+                        && mPreferredDataProfile.getApnSetting().canSupportNetworkType(networkType)
+                        && ((isNtn && mPreferredDataProfile.getApnSetting().isForInfrastructure(
+                        ApnSetting.INFRASTRUCTURE_SATELLITE))
+                        || (!isNtn && mPreferredDataProfile.getApnSetting().isForInfrastructure(
+                        ApnSetting.INFRASTRUCTURE_CELLULAR)))) {
+                    if (ignorePermanentFailure || !mPreferredDataProfile.getApnSetting()
+                            .getPermanentFailed()) {
+                        return mPreferredDataProfile.getApnSetting();
+                    }
+                    log("The preferred data profile is permanently failed. Only condition based "
+                            + "retry can happen.");
+                    return null;
                 }
-                log("The preferred data profile is permanently failed. Only condition based "
-                        + "retry can happen.");
-                return null;
-            }
-        } else {
-            // If the preferred data profile can be used, always use it if it can satisfy the
-            // network request with current network type (even though it's been marked as permanent
-            // failed.)
-            if (mPreferredDataProfile != null
-                    && networkRequest.canBeSatisfiedBy(mPreferredDataProfile)
-                    && mPreferredDataProfile.getApnSetting() != null
-                    && mPreferredDataProfile.getApnSetting().canSupportNetworkType(networkType)) {
-                if (ignorePermanentFailure || !mPreferredDataProfile.getApnSetting()
-                        .getPermanentFailed()) {
-                    return mPreferredDataProfile.getApnSetting();
+            } else {
+                // If the preferred data profile can be used, always use it if it can satisfy the
+                // network request with current network type (even though it's been marked as
+                // permanent failed.)
+                if (mPreferredDataProfile != null
+                        && networkRequest.canBeSatisfiedBy(mPreferredDataProfile)
+                        && mPreferredDataProfile.getApnSetting() != null
+                        && mPreferredDataProfile.getApnSetting()
+                        .canSupportNetworkType(networkType)) {
+                    if (ignorePermanentFailure || !mPreferredDataProfile.getApnSetting()
+                            .getPermanentFailed()) {
+                        return mPreferredDataProfile.getApnSetting();
+                    }
+                    log("The preferred data profile is permanently failed. Only condition based "
+                            + "retry can happen.");
+                    return null;
                 }
-                log("The preferred data profile is permanently failed. Only condition based "
-                        + "retry can happen.");
-                return null;
             }
         }
 
@@ -778,6 +783,8 @@
                 .filter((dp) -> {
                     if (dp.getApnSetting() == null) return false;
                     if (!dp.getApnSetting().canSupportNetworkType(networkType)) return false;
+                    if (isEsimBootStrapProvisioning
+                            != dp.getApnSetting().isEsimBootstrapProvisioning()) return false;
                     if (mFeatureFlags.carrierEnabledSatelliteFlag()) {
                         if (isNtn && !dp.getApnSetting().isForInfrastructure(
                                 ApnSetting.INFRASTRUCTURE_SATELLITE)) {
@@ -820,6 +827,10 @@
             return null;
         }
 
+        if (isEsimBootStrapProvisioning) {
+            log("Found esim bootstrap provisioning data profile for network request: "
+                    + dataProfiles.get(0).getApnSetting());
+        }
         return dataProfiles.get(0).getApnSetting();
     }
 
@@ -865,7 +876,9 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
                         .build(), mPhone);
         return getDataProfileForNetworkRequest(networkRequest, networkType,
-                mPhone.getServiceState().isUsingNonTerrestrialNetwork(), true) != null;
+                mPhone.getServiceState().isUsingNonTerrestrialNetwork(),
+                mDataNetworkController.isEsimBootStrapProvisioningActivated(),
+                true) != null;
     }
 
     /**
@@ -1040,6 +1053,8 @@
         apnBuilder.setCarrierId(apn1.getCarrierId());
         apnBuilder.setSkip464Xlat(apn1.getSkip464Xlat());
         apnBuilder.setAlwaysOn(apn1.isAlwaysOn());
+        apnBuilder.setInfrastructureBitmask(apn1.getInfrastructureBitmask());
+        apnBuilder.setEsimBootstrapProvisioning(apn1.isEsimBootstrapProvisioning());
 
         return new DataProfile.Builder()
                 .setApnSetting(apnBuilder.build())
diff --git a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
index bd7ce2a..8c1aae3 100644
--- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
@@ -1336,7 +1336,10 @@
                 stats.countOfTimerStarted,
                 stats.isImsRegistered,
                 stats.cellularServiceState,
-                stats.count);
+                stats.count,
+                stats.isMultiSim,
+                stats.recommendingHandoverType,
+                stats.isSatelliteAllowedInCurrentLocation);
     }
 
     /** Returns all phones in {@link PhoneFactory}, or an empty array if phones not made yet. */
diff --git a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
index 1da0c46..d5d041a 100644
--- a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
+++ b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
@@ -2070,7 +2070,9 @@
             if (stats.isDisplaySosMessageSent == key.isDisplaySosMessageSent
                     && stats.countOfTimerStarted == key.countOfTimerStarted
                     && stats.isImsRegistered == key.isImsRegistered
-                    && stats.cellularServiceState == key.cellularServiceState) {
+                    && stats.cellularServiceState == key.cellularServiceState
+                    && stats.isMultiSim == key.isMultiSim
+                    && stats.recommendingHandoverType == key.recommendingHandoverType) {
                 return stats;
             }
         }
diff --git a/src/java/com/android/internal/telephony/metrics/SatelliteStats.java b/src/java/com/android/internal/telephony/metrics/SatelliteStats.java
index 7ff370c..55eee1a 100644
--- a/src/java/com/android/internal/telephony/metrics/SatelliteStats.java
+++ b/src/java/com/android/internal/telephony/metrics/SatelliteStats.java
@@ -732,12 +732,19 @@
         private final int mCountOfTimerStarted;
         private final boolean mIsImsRegistered;
         private final int mCellularServiceState;
+        private final boolean mIsMultiSim;
+        private final int mRecommendingHandoverType;
+        private final boolean mIsSatelliteAllowedInCurrentLocation;
 
         private SatelliteSosMessageRecommenderParams(Builder builder) {
             this.mIsDisplaySosMessageSent = builder.mIsDisplaySosMessageSent;
             this.mCountOfTimerStarted = builder.mCountOfTimerStarted;
             this.mIsImsRegistered = builder.mIsImsRegistered;
             this.mCellularServiceState = builder.mCellularServiceState;
+            this.mIsMultiSim = builder.mIsMultiSim;
+            this.mRecommendingHandoverType = builder.mRecommendingHandoverType;
+            this.mIsSatelliteAllowedInCurrentLocation =
+                    builder.mIsSatelliteAllowedInCurrentLocation;
         }
 
         public boolean isDisplaySosMessageSent() {
@@ -756,6 +763,18 @@
             return mCellularServiceState;
         }
 
+        public boolean isMultiSim() {
+            return mIsMultiSim;
+        }
+
+        public int getRecommendingHandoverType() {
+            return mRecommendingHandoverType;
+        }
+
+        public boolean isSatelliteAllowedInCurrentLocation() {
+            return mIsSatelliteAllowedInCurrentLocation;
+        }
+
         /**
          * A builder class to create {@link SatelliteProvisionParams} data structure class
          */
@@ -764,6 +783,10 @@
             private int mCountOfTimerStarted = -1;
             private boolean mIsImsRegistered = false;
             private int mCellularServiceState = -1;
+            private boolean mIsMultiSim = false;
+            private int mRecommendingHandoverType = -1;
+            private boolean mIsSatelliteAllowedInCurrentLocation = false;
+
 
             /**
              * Sets resultCode value of {@link SatelliteSosMessageRecommender} atom
@@ -803,6 +826,34 @@
             }
 
             /**
+             * Sets isMultiSim value of {@link SatelliteSosMessageRecommender} atom
+             * then returns Builder class
+             */
+            public Builder setIsMultiSim(boolean isMultiSim) {
+                this.mIsMultiSim = isMultiSim;
+                return this;
+            }
+
+            /**
+             * Sets recommendingHandoverType value of {@link SatelliteSosMessageRecommender} atom
+             * then returns Builder class
+             */
+            public Builder setRecommendingHandoverType(int recommendingHandoverType) {
+                this.mRecommendingHandoverType = recommendingHandoverType;
+                return this;
+            }
+
+            /**
+             * Sets isSatelliteAllowedInCurrentLocation value of
+             * {@link SatelliteSosMessageRecommender} atom then returns Builder class.
+             */
+            public Builder setIsSatelliteAllowedInCurrentLocation(
+                    boolean satelliteAllowedInCurrentLocation) {
+                mIsSatelliteAllowedInCurrentLocation = satelliteAllowedInCurrentLocation;
+                return this;
+            }
+
+            /**
              * Returns SosMessageRecommenderParams, which contains whole component of
              * {@link SatelliteSosMessageRecommenderParams} atom
              */
@@ -818,7 +869,11 @@
                     + "isDisplaySosMessageSent=" + mIsDisplaySosMessageSent
                     + ", countOfTimerStarted=" + mCountOfTimerStarted
                     + ", isImsRegistered=" + mIsImsRegistered
-                    + ", cellularServiceState=" + mCellularServiceState + ")";
+                    + ", cellularServiceState=" + mCellularServiceState
+                    + ", isMultiSim=" + mIsMultiSim
+                    + ", recommendingHandoverType=" + mRecommendingHandoverType
+                    + ", isSatelliteAllowedInCurrentLocation="
+                    + mIsSatelliteAllowedInCurrentLocation + ")";
         }
     }
 
@@ -899,6 +954,9 @@
         proto.countOfTimerStarted = param.getCountOfTimerStarted();
         proto.isImsRegistered = param.isImsRegistered();
         proto.cellularServiceState = param.getCellularServiceState();
+        proto.isMultiSim = param.isMultiSim();
+        proto.recommendingHandoverType = param.getRecommendingHandoverType();
+        proto.isSatelliteAllowedInCurrentLocation = param.isSatelliteAllowedInCurrentLocation();
         proto.count = 1;
         mAtomsStorage.addSatelliteSosMessageRecommenderStats(proto);
     }
diff --git a/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
index fd04779..695a563 100644
--- a/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
+++ b/src/java/com/android/internal/telephony/satellite/DatagramReceiver.java
@@ -496,7 +496,7 @@
      */
     @SatelliteManager.SatelliteResult public int registerForSatelliteDatagram(int subId,
             @NonNull ISatelliteDatagramCallback callback) {
-        if (!SatelliteController.getInstance().isSatelliteSupported()) {
+        if (!SatelliteController.getInstance().isSatelliteSupportedViaOem()) {
             return SatelliteManager.SATELLITE_RESULT_NOT_SUPPORTED;
         }
 
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteController.java b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
index 5148ecb..1f31d5a 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteController.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteController.java
@@ -16,9 +16,14 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.telephony.CarrierConfigManager.KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT;
 import static android.telephony.SubscriptionManager.SATELLITE_ATTACH_ENABLED_FOR_CARRIER;
 import static android.telephony.SubscriptionManager.isValidSubscriptionId;
 import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE;
+import static android.telephony.satellite.SatelliteManager.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS;
+import static android.telephony.satellite.SatelliteManager.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911;
 import static android.telephony.satellite.SatelliteManager.KEY_NTN_SIGNAL_STRENGTH;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
@@ -51,10 +56,12 @@
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
+import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.satellite.INtnSignalStrengthCallback;
@@ -68,6 +75,7 @@
 import android.telephony.satellite.SatelliteManager;
 import android.util.Log;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.uwb.UwbManager;
 
 import com.android.internal.R;
@@ -77,6 +85,7 @@
 import com.android.internal.telephony.DeviceStateMonitor;
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
 import com.android.internal.telephony.satellite.metrics.ProvisionMetricsStats;
@@ -111,6 +120,7 @@
     /** Value to pass for the setting key SATELLITE_MODE_ENABLED, enabled = 1, disabled = 0 */
     public static final int SATELLITE_MODE_ENABLED_TRUE = 1;
     public static final int SATELLITE_MODE_ENABLED_FALSE = 0;
+    public static final int INVALID_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE = -1;
 
     /** Message codes used in handleMessage() */
     //TODO: Move the Commands and events related to position updates to PointingAppController
@@ -146,8 +156,9 @@
     private static final int CMD_REQUEST_NTN_SIGNAL_STRENGTH = 32;
     private static final int EVENT_REQUEST_NTN_SIGNAL_STRENGTH_DONE = 33;
     private static final int EVENT_NTN_SIGNAL_STRENGTH_CHANGED = 34;
-    private static final int CMD_START_SENDING_NTN_SIGNAL_STRENGTH = 35;
-    private static final int EVENT_UPDATE_SIGNAL_STRENGTH_REPORTING = 36;
+    private static final int CMD_UPDATE_NTN_SIGNAL_STRENGTH_REPORTING = 35;
+    private static final int EVENT_UPDATE_NTN_SIGNAL_STRENGTH_REPORTING_DONE = 36;
+    private static final int EVENT_SERVICE_STATE_CHANGED = 37;
 
     @NonNull private static SatelliteController sInstance;
     @NonNull private final Context mContext;
@@ -159,12 +170,12 @@
     @NonNull private final ProvisionMetricsStats mProvisionMetricsStats;
     @NonNull private final SubscriptionManagerService mSubscriptionManagerService;
     private final CommandsInterface mCi;
-    private ContentResolver mContentResolver = null;
+    private ContentResolver mContentResolver;
     private final DeviceStateMonitor mDSM;
 
     private final Object mRadioStateLock = new Object();
 
-    /** Flags to indicate whether the resepective radio is enabled */
+    /** Flags to indicate whether the respective radio is enabled */
     @GuardedBy("mRadioStateLock")
     private boolean mBTStateEnabled = false;
     @GuardedBy("mRadioStateLock")
@@ -261,6 +272,32 @@
     @NonNull private final Map<Integer, Boolean> mIsSatelliteAttachEnabledForCarrierArrayPerSub =
             new HashMap<>();
     @NonNull private final FeatureFlags mFeatureFlags;
+    @NonNull private final Object mSatelliteConnectedLock = new Object();
+    /** Key: Subscription ID; Value: Last satellite connected time */
+    @GuardedBy("mSatelliteConnectedLock")
+    @NonNull private final SparseArray<Long> mLastSatelliteDisconnectedTimesMillis =
+            new SparseArray<>();
+    /**
+     * Key: Subscription ID; Value: {@code true} if satellite was just connected,
+     * {@code false} otherwise.
+     */
+    @GuardedBy("mSatelliteConnectedLock")
+    @NonNull private final SparseBooleanArray
+            mWasSatelliteConnectedViaCarrier = new SparseBooleanArray();
+
+    @GuardedBy("mSatelliteConnectedLock")
+    @NonNull private final SparseBooleanArray
+            mIsSatelliteConnectedViaCarrierHysteresisTimeExpired = new SparseBooleanArray();
+
+    /**
+     * This is used for testing only. When mEnforcedEmergencyCallToSatelliteHandoverType is valid,
+     * Telephony will ignore the IMS registration status and cellular availability, and always send
+     * the connection event EVENT_DISPLAY_EMERGENCY_MESSAGE to Dialer.
+     */
+    private int mEnforcedEmergencyCallToSatelliteHandoverType =
+            INVALID_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE;
+    private int mDelayInSendingEventDisplayEmergencyMessage = 0;
+
     /**
      * @return The singleton instance of SatelliteController.
      */
@@ -332,6 +369,7 @@
         registerForSatelliteProvisionStateChanged();
         registerForPendingDatagramCount();
         registerForSatelliteModemStateChanged();
+        registerForServiceStateChanged();
         mContentResolver = mContext.getContentResolver();
         mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
 
@@ -356,7 +394,7 @@
                         handleCarrierConfigChanged(slotIndex, subId, carrierId, specificCarrierId);
         mCarrierConfigManager.registerCarrierConfigChangeListener(
                         new HandlerExecutor(new Handler(looper)), mCarrierConfigChangeListener);
-        mDSM.registerForSignalStrengthReportDecision(this, CMD_START_SENDING_NTN_SIGNAL_STRENGTH,
+        mDSM.registerForSignalStrengthReportDecision(this, CMD_UPDATE_NTN_SIGNAL_STRENGTH_REPORTING,
                 null);
     }
 
@@ -1133,16 +1171,16 @@
                 break;
             }
 
-            case CMD_START_SENDING_NTN_SIGNAL_STRENGTH: {
+            case CMD_UPDATE_NTN_SIGNAL_STRENGTH_REPORTING: {
                 ar = (AsyncResult) msg.obj;
                 boolean shouldReport = (boolean) ar.result;
-                logd("CMD_START_SENDING_NTN_SIGNAL_STRENGTH: shouldReport=" + shouldReport);
+                logd("CMD_UPDATE_NTN_SIGNAL_STRENGTH_REPORTING: shouldReport=" + shouldReport);
                 request = new SatelliteControllerHandlerRequest(shouldReport,
                         SatelliteServiceUtils.getPhone());
                 if (SATELLITE_RESULT_SUCCESS != evaluateOemSatelliteRequestAllowed(true)) {
                     return;
                 }
-                onCompleted = obtainMessage(EVENT_UPDATE_SIGNAL_STRENGTH_REPORTING,
+                onCompleted = obtainMessage(EVENT_UPDATE_NTN_SIGNAL_STRENGTH_REPORTING_DONE,
                         request);
                 if (shouldReport) {
                     mSatelliteModemInterface.startSendingNtnSignalStrength(onCompleted);
@@ -1152,11 +1190,11 @@
                 break;
             }
 
-            case EVENT_UPDATE_SIGNAL_STRENGTH_REPORTING: {
+            case EVENT_UPDATE_NTN_SIGNAL_STRENGTH_REPORTING_DONE: {
                 ar = (AsyncResult) msg.obj;
                 request = (SatelliteControllerHandlerRequest) ar.userObj;
                 int errorCode =  SatelliteServiceUtils.getSatelliteError(ar,
-                        "EVENT_UPDATE_SIGNAL_STRENGTH_REPORTING");
+                        "EVENT_UPDATE_NTN_SIGNAL_STRENGTH_REPORTING_DONE");
                 if (errorCode != SATELLITE_RESULT_SUCCESS) {
                     loge(((boolean) request.argument ? "startSendingNtnSignalStrength"
                             : "stopSendingNtnSignalStrength") + "returns " + errorCode);
@@ -1164,6 +1202,11 @@
                 break;
             }
 
+            case EVENT_SERVICE_STATE_CHANGED: {
+                handleEventServiceStateChanged();
+                break;
+            }
+
             default:
                 Log.w(TAG, "SatelliteControllerHandler: unexpected message code: " +
                         msg.what);
@@ -1475,7 +1518,7 @@
             return null;
         }
 
-        Boolean satelliteProvisioned = isSatelliteProvisioned();
+        Boolean satelliteProvisioned = isSatelliteViaOemProvisioned();
         if (satelliteProvisioned != null && satelliteProvisioned) {
             result.accept(SATELLITE_RESULT_SUCCESS);
             return null;
@@ -1515,7 +1558,7 @@
             return;
         }
 
-        Boolean satelliteProvisioned = isSatelliteProvisioned();
+        Boolean satelliteProvisioned = isSatelliteViaOemProvisioned();
         if (satelliteProvisioned == null) {
             result.accept(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
             return;
@@ -2072,6 +2115,53 @@
     }
 
     /**
+     * This API can be used in only testing to override connectivity status in monitoring emergency
+     * calls and sending EVENT_DISPLAY_EMERGENCY_MESSAGE to Dialer.
+     *
+     * @param handoverType The type of handover from emergency call to satellite messaging. Use one
+     *                     of the following values to enable the override:
+     *                     0 - EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS
+     *                     1 - EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911
+     *                     To disable the override, use -1 for handoverType.
+     * @param delaySeconds The event EVENT_DISPLAY_EMERGENCY_MESSAGE will be sent to Dialer
+     *                     delaySeconds after the emergency call starts.
+     * @return {@code true} if the handover type is set successfully, {@code false} otherwise.
+     */
+    public boolean setEmergencyCallToSatelliteHandoverType(int handoverType, int delaySeconds) {
+        if (!isMockModemAllowed()) {
+            loge("setEmergencyCallToSatelliteHandoverType: mock modem not allowed");
+            return false;
+        }
+        if (isHandoverTypeValid(handoverType)) {
+            mEnforcedEmergencyCallToSatelliteHandoverType = handoverType;
+            mDelayInSendingEventDisplayEmergencyMessage = delaySeconds > 0 ? delaySeconds : 0;
+        } else {
+            mEnforcedEmergencyCallToSatelliteHandoverType =
+                    INVALID_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE;
+            mDelayInSendingEventDisplayEmergencyMessage = 0;
+        }
+        return true;
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    protected int getEnforcedEmergencyCallToSatelliteHandoverType() {
+        return mEnforcedEmergencyCallToSatelliteHandoverType;
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    protected int getDelayInSendingEventDisplayEmergencyMessage() {
+        return mDelayInSendingEventDisplayEmergencyMessage;
+    }
+
+    private boolean isHandoverTypeValid(int handoverType) {
+        if (handoverType == EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS
+                || handoverType == EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * This function is used by {@link SatelliteModemInterface} to notify
      * {@link SatelliteController} that the satellite vendor service was just connected.
      * <p>
@@ -2132,14 +2222,15 @@
     }
 
     /**
-     * @return {@code true} is satellite is supported on the device, {@code  false} otherwise.
+     * @return {@code true} if satellite is supported via OEM on the device,
+     * {@code  false} otherwise.
      */
-    public boolean isSatelliteSupported() {
+    public boolean isSatelliteSupportedViaOem() {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
             logd("isSatelliteSupported: oemEnabledSatelliteFlag is disabled");
             return false;
         }
-        Boolean supported = isSatelliteSupportedInternal();
+        Boolean supported = isSatelliteSupportedViaOemInternal();
         return (supported != null ? supported : false);
     }
 
@@ -2218,10 +2309,92 @@
     }
 
     /**
+     * @return {@code true} if satellite is supported via carrier by any subscription on the device,
+     * {@code false} otherwise.
+     */
+    public boolean isSatelliteSupportedViaCarrier() {
+        if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
+            logd("isSatelliteSupportedViaCarrier: carrierEnabledSatelliteFlag is disabled");
+            return false;
+        }
+        for (Phone phone : PhoneFactory.getPhones()) {
+            if (isSatelliteSupportedViaCarrier(phone.getSubId())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @return {@code true} if any subscription on the device is connected to satellite,
+     * {@code false} otherwise.
+     */
+    private boolean isUsingNonTerrestrialNetworkViaCarrier() {
+        if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
+            logd("isUsingNonTerrestrialNetwork: carrierEnabledSatelliteFlag is disabled");
+            return false;
+        }
+        for (Phone phone : PhoneFactory.getPhones()) {
+            ServiceState serviceState = phone.getServiceState();
+            if (serviceState != null && serviceState.isUsingNonTerrestrialNetwork()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @return {@code true} if the device is connected to satellite via any carrier within the
+     * {@link CarrierConfigManager#KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT}
+     * duration, {@code false} otherwise.
+     */
+    public boolean isSatelliteConnectedViaCarrierWithinHysteresisTime() {
+        if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
+            logd("isSatelliteConnectedViaCarrierWithinHysteresisTime: carrierEnabledSatelliteFlag"
+                    + " is disabled");
+            return false;
+        }
+        if (isUsingNonTerrestrialNetworkViaCarrier()) {
+            return true;
+        }
+        for (Phone phone : PhoneFactory.getPhones()) {
+            if (isSatelliteSupportedViaCarrier(phone.getSubId())) {
+                synchronized (mSatelliteConnectedLock) {
+                    Boolean isHysteresisTimeExpired =
+                            mIsSatelliteConnectedViaCarrierHysteresisTimeExpired.get(
+                                    phone.getSubId());
+                    if (isHysteresisTimeExpired != null && isHysteresisTimeExpired) {
+                        continue;
+                    }
+
+                    Long lastDisconnectedTime =
+                            mLastSatelliteDisconnectedTimesMillis.get(phone.getSubId());
+                    long satelliteConnectionHysteresisTime =
+                            getSatelliteConnectionHysteresisTimeMillis(phone.getSubId());
+                    if (lastDisconnectedTime != null
+                            && (getElapsedRealtime() - lastDisconnectedTime)
+                            <= satelliteConnectionHysteresisTime) {
+                        return true;
+                    } else {
+                        mIsSatelliteConnectedViaCarrierHysteresisTimeExpired.put(
+                                phone.getSubId(), true);
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected long getElapsedRealtime() {
+        return SystemClock.elapsedRealtime();
+    }
+
+    /**
      * If we have not successfully queried the satellite modem for its satellite service support,
      * we will retry the query one more time. Otherwise, we will return the cached result.
      */
-    private Boolean isSatelliteSupportedInternal() {
+    private Boolean isSatelliteSupportedViaOemInternal() {
         synchronized (mIsSatelliteSupportedLock) {
             if (mIsSatelliteSupported != null) {
                 /* We have already successfully queried the satellite modem. */
@@ -2352,7 +2525,7 @@
      * @return true if satellite is provisioned on the given subscription else return false.
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    protected Boolean isSatelliteProvisioned() {
+    protected Boolean isSatelliteViaOemProvisioned() {
         synchronized (mIsSatelliteProvisionedLock) {
             if (mIsSatelliteProvisioned != null) {
                 return mIsSatelliteProvisioned;
@@ -2724,16 +2897,16 @@
                 mCarrierConfigArray.put(subId, config);
             }
             return SatelliteServiceUtils.parseSupportedSatelliteServices(
-                    config.getPersistableBundle(CarrierConfigManager
-                            .KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE));
+                    config.getPersistableBundle(
+                            KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE));
         }
     }
 
     @NonNull private PersistableBundle getConfigForSubId(int subId) {
         PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId,
-                CarrierConfigManager
-                        .KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
-                CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL);
+                KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE,
+                KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
+                KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT);
         if (config == null || config.isEmpty()) {
             config = CarrierConfigManager.getDefaultConfig();
         }
@@ -2799,10 +2972,9 @@
         return strArray;
     }
 
-    private boolean isSatelliteSupportedForCarrier(int subId) {
+    private boolean isSatelliteSupportedViaCarrier(int subId) {
         return getConfigForSubId(subId)
-                .getBoolean(CarrierConfigManager
-                        .KEY_SATELLITE_ATTACH_SUPPORTED_BOOL);
+                .getBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL);
     }
 
     /**
@@ -2951,7 +3123,7 @@
                     + "SetSatelliteAttachEnableForCarrier error code =" + errorCode);
         }
 
-        if (!isSatelliteSupportedForCarrier(subId)) {
+        if (!isSatelliteSupportedViaCarrier(subId)) {
             logd("Satellite for carrier is not supported. Only user setting is stored");
             callback.accept(SATELLITE_RESULT_SUCCESS);
             return;
@@ -2991,7 +3163,7 @@
             return SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
         }
 
-        Boolean satelliteSupported = isSatelliteSupportedInternal();
+        Boolean satelliteSupported = isSatelliteSupportedViaOemInternal();
         if (satelliteSupported == null) {
             return SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
         }
@@ -3000,7 +3172,7 @@
         }
 
         if (isProvisionRequired) {
-            Boolean satelliteProvisioned = isSatelliteProvisioned();
+            Boolean satelliteProvisioned = isSatelliteViaOemProvisioned();
             if (satelliteProvisioned == null) {
                 return SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
             }
@@ -3036,6 +3208,56 @@
                 .reportSessionMetrics();
     }
 
+    private void registerForServiceStateChanged() {
+        if (!mFeatureFlags.carrierEnabledSatelliteFlag()) {
+            return;
+        }
+        for (Phone phone : PhoneFactory.getPhones()) {
+            phone.registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null);
+        }
+    }
+
+    private void handleEventServiceStateChanged() {
+        handleServiceStateForSatelliteConnectionViaCarrier();
+    }
+
+    private void handleServiceStateForSatelliteConnectionViaCarrier() {
+        for (Phone phone : PhoneFactory.getPhones()) {
+            ServiceState serviceState = phone.getServiceState();
+            if (serviceState != null) {
+                synchronized (mSatelliteConnectedLock) {
+                    if (serviceState.isUsingNonTerrestrialNetwork()) {
+                        mWasSatelliteConnectedViaCarrier.put(phone.getSubId(), true);
+                        mIsSatelliteConnectedViaCarrierHysteresisTimeExpired.put(
+                                phone.getSubId(), false);
+                    } else {
+                        Boolean connected = mWasSatelliteConnectedViaCarrier.get(phone.getSubId());
+                        if (connected != null && connected) {
+                            // The device just got disconnected from a satellite network.
+                            mLastSatelliteDisconnectedTimesMillis.put(
+                                    phone.getSubId(), getElapsedRealtime());
+                            mIsSatelliteConnectedViaCarrierHysteresisTimeExpired.put(
+                                    phone.getSubId(), false);
+                        }
+                        mWasSatelliteConnectedViaCarrier.put(phone.getSubId(), false);
+                    }
+                }
+            }
+        }
+    }
+
+    private long getSatelliteConnectionHysteresisTimeMillis(int subId) {
+        synchronized (mCarrierConfigArrayLock) {
+            PersistableBundle config = mCarrierConfigArray.get(subId);
+            if (config == null) {
+                config = getConfigForSubId(subId);
+                mCarrierConfigArray.put(subId, config);
+            }
+            return (config.getInt(
+                    KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT) * 1000L);
+        }
+    }
+
     private static void logd(@NonNull String log) {
         Rlog.d(TAG, log);
     }
diff --git a/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java b/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
index 9bf82d1..f40880b 100644
--- a/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
+++ b/src/java/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommender.java
@@ -16,16 +16,31 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.telephony.ServiceState.STATE_EMERGENCY_ONLY;
+import static android.telephony.ServiceState.STATE_IN_SERVICE;
+import static android.telephony.ServiceState.STATE_OUT_OF_SERVICE;
+import static android.telephony.TelephonyManager.EXTRA_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE;
+import static android.telephony.TelephonyManager.EXTRA_EMERGENCY_CALL_TO_SATELLITE_LAUNCH_INTENT;
+import static android.telephony.satellite.SatelliteManager.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS;
+import static android.telephony.satellite.SatelliteManager.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911;
 import static android.telephony.satellite.SatelliteManager.KEY_SATELLITE_COMMUNICATION_ALLOWED;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
+import static com.android.internal.telephony.satellite.SatelliteController.INVALID_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE;
+
 import android.annotation.NonNull;
-import android.os.AsyncResult;
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.ResultReceiver;
+import android.os.SystemProperties;
 import android.provider.DeviceConfig;
 import android.telecom.Connection;
 import android.telephony.Rlog;
@@ -36,16 +51,20 @@
 import android.telephony.ims.ImsRegistrationAttributes;
 import android.telephony.ims.RegistrationManager;
 import android.telephony.satellite.ISatelliteProvisionStateCallback;
+import android.text.TextUtils;
 import android.util.Pair;
+import android.util.SparseArray;
 
 import com.android.ims.ImsException;
 import com.android.ims.ImsManager;
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.SmsApplication;
 import com.android.internal.telephony.metrics.SatelliteStats;
 
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
 
 
 /**
@@ -56,74 +75,48 @@
  */
 public class SatelliteSOSMessageRecommender extends Handler {
     private static final String TAG = "SatelliteSOSMessageRecommender";
-
-    /**
-     * Device config for the timeout duration in milliseconds to determine whether to recommend
-     * Dialer to show the SOS button to users.
-     * <p>
-     * The timer is started when there is an ongoing emergency call, and the IMS is not registered,
-     * and cellular service is not available. When the timer expires, SatelliteSOSMessageRecommender
-     * will send the event EVENT_DISPLAY_SOS_MESSAGE to Dialer, which will then prompt user to
-     * switch to using satellite SOS messaging.
-     */
-    public static final String EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS =
-            "emergency_call_to_sos_msg_hysteresis_timeout_millis";
-    /**
-     * The default value of {@link #EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS} when it is
-     * not provided in the device config.
-     */
-    public static final long DEFAULT_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS = 20000;
-
+    private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
+    private static final String BOOT_ALLOW_MOCK_MODEM_PROPERTY = "ro.boot.radio.allow_mock_modem";
     private static final int EVENT_EMERGENCY_CALL_STARTED = 1;
-    protected static final int EVENT_CELLULAR_SERVICE_STATE_CHANGED = 2;
-    private static final int EVENT_IMS_REGISTRATION_STATE_CHANGED = 3;
-    protected static final int EVENT_TIME_OUT = 4;
-    private static final int EVENT_SATELLITE_PROVISIONED_STATE_CHANGED = 5;
-    private static final int EVENT_EMERGENCY_CALL_CONNECTION_STATE_CHANGED = 6;
+    protected static final int EVENT_SERVICE_STATE_CHANGED = 2;
+    protected static final int EVENT_TIME_OUT = 3;
+    private static final int EVENT_SATELLITE_PROVISIONED_STATE_CHANGED = 4;
+    private static final int EVENT_EMERGENCY_CALL_CONNECTION_STATE_CHANGED = 5;
+    private static final int CMD_SEND_EVENT_DISPLAY_EMERGENCY_MESSAGE_FORCEFULLY = 6;
 
+    @NonNull private final Context mContext;
     @NonNull
     private final SatelliteController mSatelliteController;
     private ImsManager mImsManager;
 
     private Connection mEmergencyConnection = null;
-    /* The phone used for emergency call */
-    private Phone mPhone = null;
     private final ISatelliteProvisionStateCallback mISatelliteProvisionStateCallback;
-    @ServiceState.RegState
-    private AtomicInteger mCellularServiceState = new AtomicInteger();
-    private AtomicBoolean mIsImsRegistered = new AtomicBoolean();
+    /** Key: Phone ID; Value: IMS RegistrationCallback */
+    private SparseArray<RegistrationManager.RegistrationCallback>
+            mImsRegistrationCallbacks = new SparseArray<>();
     private AtomicBoolean mIsSatelliteAllowedInCurrentLocation = new AtomicBoolean();
     private final ResultReceiver mReceiverForRequestIsSatelliteAllowedForCurrentLocation;
     private final long mTimeoutMillis;
+    private final AtomicBoolean mIsSatelliteConnectedViaCarrierWithinHysteresisTime =
+            new AtomicBoolean(false);
     protected int mCountOfTimerStarted = 0;
 
-    private RegistrationManager.RegistrationCallback mImsRegistrationCallback =
-            new RegistrationManager.RegistrationCallback() {
-                @Override
-                public void onRegistered(ImsRegistrationAttributes attributes) {
-                    sendMessage(obtainMessage(EVENT_IMS_REGISTRATION_STATE_CHANGED, true));
-                }
-
-                @Override
-                public void onUnregistered(ImsReasonInfo info) {
-                    sendMessage(obtainMessage(EVENT_IMS_REGISTRATION_STATE_CHANGED, false));
-                }
-            };
-
     /**
      * Create an instance of SatelliteSOSMessageRecommender.
      *
+     * @param context The Context for the SatelliteSOSMessageRecommender.
      * @param looper The looper used with the handler of this class.
      */
-    public SatelliteSOSMessageRecommender(@NonNull Looper looper) {
-        this(looper, SatelliteController.getInstance(), null,
-                getEmergencyCallToSosMsgHysteresisTimeoutMillis());
+    public SatelliteSOSMessageRecommender(@NonNull Context context, @NonNull Looper looper) {
+        this(context, looper, SatelliteController.getInstance(), null,
+                getEmergencyCallWaitForConnectionTimeoutMillis(context));
     }
 
     /**
      * Create an instance of SatelliteSOSMessageRecommender. This constructor should be used in
      * only unit tests.
      *
+     * @param context The Context for the SatelliteSOSMessageRecommender.
      * @param looper The looper used with the handler of this class.
      * @param satelliteController The SatelliteController singleton instance.
      * @param imsManager The ImsManager instance associated with the phone, which is used for making
@@ -131,10 +124,11 @@
      * @param timeoutMillis The timeout duration of the timer.
      */
     @VisibleForTesting
-    protected SatelliteSOSMessageRecommender(@NonNull Looper looper,
+    protected SatelliteSOSMessageRecommender(@NonNull Context context, @NonNull Looper looper,
             @NonNull SatelliteController satelliteController, ImsManager imsManager,
             long timeoutMillis) {
         super(looper);
+        mContext = context;
         mSatelliteController = satelliteController;
         mImsManager = imsManager;
         mTimeoutMillis = timeoutMillis;
@@ -176,7 +170,7 @@
     public void handleMessage(@NonNull Message msg) {
         switch (msg.what) {
             case EVENT_EMERGENCY_CALL_STARTED:
-                handleEmergencyCallStartedEvent((Pair<Connection, Phone>) msg.obj);
+                handleEmergencyCallStartedEvent((Connection) msg.obj);
                 break;
             case EVENT_TIME_OUT:
                 handleTimeoutEvent();
@@ -187,12 +181,11 @@
             case EVENT_EMERGENCY_CALL_CONNECTION_STATE_CHANGED:
                 handleEmergencyCallConnectionStateChangedEvent((Pair<String, Integer>) msg.obj);
                 break;
-            case EVENT_IMS_REGISTRATION_STATE_CHANGED:
-                handleImsRegistrationStateChangedEvent((boolean) msg.obj);
+            case EVENT_SERVICE_STATE_CHANGED:
+                handleStateChangedEventForHysteresisTimer();
                 break;
-            case EVENT_CELLULAR_SERVICE_STATE_CHANGED:
-                AsyncResult ar = (AsyncResult) msg.obj;
-                handleCellularServiceStateChangedEvent((ServiceState) ar.result);
+            case CMD_SEND_EVENT_DISPLAY_EMERGENCY_MESSAGE_FORCEFULLY:
+                handleCmdSendEventDisplayEmergencyMessageForcefully((Connection) msg.obj);
                 break;
             default:
                 logd("handleMessage: unexpected message code: " + msg.what);
@@ -205,15 +198,23 @@
      *
      * @param connection The connection created by TelephonyConnectionService for the emergency
      *                   call.
-     * @param phone The phone used for the emergency call.
      */
-    public void onEmergencyCallStarted(@NonNull Connection connection, @NonNull Phone phone) {
-        if (!mSatelliteController.isSatelliteSupported()) {
+    public void onEmergencyCallStarted(@NonNull Connection connection) {
+        if (!mSatelliteController.isSatelliteSupportedViaOem()
+                && !mSatelliteController.isSatelliteSupportedViaCarrier()) {
             logd("onEmergencyCallStarted: satellite is not supported");
             return;
         }
-        Pair<Connection, Phone> argument = new Pair<>(connection, phone);
-        sendMessage(obtainMessage(EVENT_EMERGENCY_CALL_STARTED, argument));
+
+        /**
+         * Right now, assume that the device is connected to satellite via carrier within hysteresis
+         * time. However, this might not be correct when the monitoring timer expires. Thus, we
+         * should do this check now so that we have higher chance of sending the event
+         * EVENT_DISPLAY_EMERGENCY_MESSAGE to Dialer.
+         */
+        mIsSatelliteConnectedViaCarrierWithinHysteresisTime.set(
+                mSatelliteController.isSatelliteConnectedViaCarrierWithinHysteresisTime());
+        sendMessage(obtainMessage(EVENT_EMERGENCY_CALL_STARTED, connection));
     }
 
     /**
@@ -225,7 +226,8 @@
      */
     public void onEmergencyCallConnectionStateChanged(
             String callId, @Connection.ConnectionState int state) {
-        if (!mSatelliteController.isSatelliteSupported()) {
+        if (!mSatelliteController.isSatelliteSupportedViaOem()
+                && !mSatelliteController.isSatelliteSupportedViaCarrier()) {
             logd("onEmergencyCallConnectionStateChanged: satellite is not supported");
             return;
         }
@@ -233,21 +235,23 @@
         sendMessage(obtainMessage(EVENT_EMERGENCY_CALL_CONNECTION_STATE_CHANGED, argument));
     }
 
-    private void handleEmergencyCallStartedEvent(@NonNull Pair<Connection, Phone> arg) {
-        mSatelliteController.requestIsSatelliteCommunicationAllowedForCurrentLocation(
-                SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
-                mReceiverForRequestIsSatelliteAllowedForCurrentLocation);
-        if (mPhone != null) {
-            logd("handleEmergencyCallStartedEvent: new emergency call started while there is "
-                    + " an ongoing call");
-            unregisterForInterestedStateChangedEvents(mPhone);
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected ComponentName getDefaultSmsApp() {
+        return SmsApplication.getDefaultSmsApplication(mContext, false);
+    }
+
+    private void handleEmergencyCallStartedEvent(@NonNull Connection connection) {
+        if (sendEventDisplayEmergencyMessageForcefully(connection)) {
+            return;
         }
-        mPhone = arg.second;
-        mEmergencyConnection = arg.first;
-        mCellularServiceState.set(mPhone.getServiceState().getState());
-        mIsImsRegistered.set(mPhone.isImsRegistered());
-        handleStateChangedEventForHysteresisTimer();
-        registerForInterestedStateChangedEvents(mPhone);
+        if (mEmergencyConnection == null) {
+            mSatelliteController.requestIsSatelliteCommunicationAllowedForCurrentLocation(
+                    SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+                    mReceiverForRequestIsSatelliteAllowedForCurrentLocation);
+            handleStateChangedEventForHysteresisTimer();
+            registerForInterestedStateChangedEvents();
+        }
+        mEmergencyConnection = connection;
     }
 
     private void handleSatelliteProvisionStateChangedEvent(boolean provisioned) {
@@ -257,20 +261,49 @@
     }
 
     private void handleTimeoutEvent() {
+        /**
+         * The device might be connected to satellite after the emergency call started. Thus, we
+         * need to do this check again so that we will have higher chance of sending the event
+         * EVENT_DISPLAY_EMERGENCY_MESSAGE to Dialer.
+         */
+        updateSatelliteViaCarrierAvailability();
+
         boolean isDialerNotified = false;
-        if (!mIsImsRegistered.get() && !isCellularAvailable()
+        if (!isImsRegistered() && !isCellularAvailable()
                 && mIsSatelliteAllowedInCurrentLocation.get()
-                && mSatelliteController.isSatelliteProvisioned()
+                && (isSatelliteViaOemAvailable() || isSatelliteViaCarrierAvailable())
                 && shouldTrackCall(mEmergencyConnection.getState())) {
-            logd("handleTimeoutEvent: Sending EVENT_DISPLAY_SOS_MESSAGE to Dialer...");
-            mEmergencyConnection.sendConnectionEvent(TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE,
-                    null);
+            logd("handleTimeoutEvent: Sent EVENT_DISPLAY_EMERGENCY_MESSAGE to Dialer");
+            Bundle extras = createExtraBundleForEventDisplayEmergencyMessage();
+            mEmergencyConnection.sendConnectionEvent(
+                    TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE, extras);
             isDialerNotified = true;
+
         }
+        logd("handleTimeoutEvent: isImsRegistered=" + isImsRegistered()
+                + ", isCellularAvailable=" + isCellularAvailable()
+                + ", mIsSatelliteAllowedInCurrentLocation="
+                + mIsSatelliteAllowedInCurrentLocation.get()
+                + ", shouldTrackCall=" + shouldTrackCall(mEmergencyConnection.getState()));
         reportEsosRecommenderDecision(isDialerNotified);
         cleanUpResources();
     }
 
+    private void updateSatelliteViaCarrierAvailability() {
+        if (!mIsSatelliteConnectedViaCarrierWithinHysteresisTime.get()) {
+            mIsSatelliteConnectedViaCarrierWithinHysteresisTime.set(
+                    mSatelliteController.isSatelliteConnectedViaCarrierWithinHysteresisTime());
+        }
+    }
+
+    private boolean isSatelliteViaOemAvailable() {
+        return mSatelliteController.isSatelliteViaOemProvisioned();
+    }
+
+    private boolean isSatelliteViaCarrierAvailable() {
+        return mIsSatelliteConnectedViaCarrierWithinHysteresisTime.get();
+    }
+
     private void handleEmergencyCallConnectionStateChangedEvent(
             @NonNull Pair<String, Integer> arg) {
         if (mEmergencyConnection == null) {
@@ -299,78 +332,117 @@
         }
     }
 
-    private void handleImsRegistrationStateChangedEvent(boolean registered) {
-        if (registered != mIsImsRegistered.get()) {
-            mIsImsRegistered.set(registered);
-            handleStateChangedEventForHysteresisTimer();
-        }
-    }
-
-    private void handleCellularServiceStateChangedEvent(@NonNull ServiceState serviceState) {
-        int state = serviceState.getState();
-        if (mCellularServiceState.get() != state) {
-            mCellularServiceState.set(state);
-            handleStateChangedEventForHysteresisTimer();
-        }
-    }
-
     private void reportEsosRecommenderDecision(boolean isDialerNotified) {
         SatelliteStats.getInstance().onSatelliteSosMessageRecommender(
                 new SatelliteStats.SatelliteSosMessageRecommenderParams.Builder()
                         .setDisplaySosMessageSent(isDialerNotified)
                         .setCountOfTimerStarted(mCountOfTimerStarted)
-                        .setImsRegistered(mIsImsRegistered.get())
-                        .setCellularServiceState(mCellularServiceState.get())
+                        .setImsRegistered(isImsRegistered())
+                        .setCellularServiceState(getBestCellularServiceState())
+                        .setIsMultiSim(isMultiSim())
+                        .setRecommendingHandoverType(getEmergencyCallToSatelliteHandoverType())
+                        .setIsSatelliteAllowedInCurrentLocation(
+                                mIsSatelliteAllowedInCurrentLocation.get())
                         .build());
     }
 
     private void cleanUpResources() {
         stopTimer();
-        if (mPhone != null) {
-            unregisterForInterestedStateChangedEvents(mPhone);
-            mPhone = null;
+        if (mEmergencyConnection != null) {
+            unregisterForInterestedStateChangedEvents();
         }
         mEmergencyConnection = null;
         mCountOfTimerStarted = 0;
     }
 
-    private void registerForInterestedStateChangedEvents(@NonNull Phone phone) {
+    private void registerForInterestedStateChangedEvents() {
         mSatelliteController.registerForSatelliteProvisionStateChanged(
                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, mISatelliteProvisionStateCallback);
-        phone.registerForServiceStateChanged(this, EVENT_CELLULAR_SERVICE_STATE_CHANGED, null);
-        registerForImsRegistrationStateChanged(phone);
+        for (Phone phone : PhoneFactory.getPhones()) {
+            phone.registerForServiceStateChanged(
+                    this, EVENT_SERVICE_STATE_CHANGED, null);
+            registerForImsRegistrationStateChanged(phone);
+        }
     }
 
     private void registerForImsRegistrationStateChanged(@NonNull Phone phone) {
         ImsManager imsManager = (mImsManager != null) ? mImsManager : ImsManager.getInstance(
                 phone.getContext(), phone.getPhoneId());
         try {
-            imsManager.addRegistrationCallback(mImsRegistrationCallback, this::post);
+            imsManager.addRegistrationCallback(
+                    getOrCreateImsRegistrationCallback(phone.getPhoneId()), this::post);
         } catch (ImsException ex) {
             loge("registerForImsRegistrationStateChanged: ex=" + ex);
         }
     }
 
-    private void unregisterForInterestedStateChangedEvents(@NonNull Phone phone) {
+    private void unregisterForInterestedStateChangedEvents() {
         mSatelliteController.unregisterForSatelliteProvisionStateChanged(
                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, mISatelliteProvisionStateCallback);
-        phone.unregisterForServiceStateChanged(this);
-        unregisterForImsRegistrationStateChanged(phone);
+        for (Phone phone : PhoneFactory.getPhones()) {
+            phone.unregisterForServiceStateChanged(this);
+            unregisterForImsRegistrationStateChanged(phone);
+        }
     }
 
     private void unregisterForImsRegistrationStateChanged(@NonNull Phone phone) {
-        ImsManager imsManager = (mImsManager != null) ? mImsManager : ImsManager.getInstance(
-                phone.getContext(), phone.getPhoneId());
-        imsManager.removeRegistrationListener(mImsRegistrationCallback);
+        if (mImsRegistrationCallbacks.contains(phone.getPhoneId())) {
+            ImsManager imsManager =
+                    (mImsManager != null) ? mImsManager : ImsManager.getInstance(
+                            phone.getContext(), phone.getPhoneId());
+            imsManager.removeRegistrationListener(
+                    mImsRegistrationCallbacks.get(phone.getPhoneId()));
+        } else {
+            loge("Phone ID=" + phone.getPhoneId() + " was not registered with ImsManager");
+        }
     }
 
     private boolean isCellularAvailable() {
-        return (mCellularServiceState.get() == ServiceState.STATE_IN_SERVICE
-                || mCellularServiceState.get() == ServiceState.STATE_EMERGENCY_ONLY);
+        for (Phone phone : PhoneFactory.getPhones()) {
+            ServiceState serviceState = phone.getServiceState();
+            if (serviceState != null) {
+                int state = serviceState.getState();
+                if ((state == STATE_IN_SERVICE || state == STATE_EMERGENCY_ONLY)
+                        && !serviceState.isUsingNonTerrestrialNetwork()) {
+                    return true;
+                }
+            }
+        }
+        return false;
     }
 
-    private void handleStateChangedEventForHysteresisTimer() {
-        if (!mIsImsRegistered.get() && !isCellularAvailable()) {
+    /**
+     * @return {@link ServiceState#STATE_IN_SERVICE} if any subscription is in this state; else
+     * {@link ServiceState#STATE_EMERGENCY_ONLY} if any subscription is in this state; else
+     * {@link ServiceState#STATE_OUT_OF_SERVICE}.
+     */
+    private int getBestCellularServiceState() {
+        boolean isStateOutOfService = true;
+        for (Phone phone : PhoneFactory.getPhones()) {
+            ServiceState serviceState = phone.getServiceState();
+            if (serviceState != null) {
+                int state = serviceState.getState();
+                if (!serviceState.isUsingNonTerrestrialNetwork()) {
+                    if ((state == STATE_IN_SERVICE)) {
+                        return STATE_IN_SERVICE;
+                    } else if (state == STATE_EMERGENCY_ONLY) {
+                        isStateOutOfService = false;
+                    }
+                }
+            }
+        }
+        return isStateOutOfService ? STATE_OUT_OF_SERVICE : STATE_EMERGENCY_ONLY;
+    }
+
+    private boolean isImsRegistered() {
+        for (Phone phone : PhoneFactory.getPhones()) {
+            if (phone.isImsRegistered()) return true;
+        }
+        return false;
+    }
+
+    private synchronized void handleStateChangedEventForHysteresisTimer() {
+        if (!isImsRegistered() && !isCellularAvailable()) {
             startTimer();
         } else {
             stopTimer();
@@ -389,10 +461,61 @@
         removeMessages(EVENT_TIME_OUT);
     }
 
-    private static long getEmergencyCallToSosMsgHysteresisTimeoutMillis() {
-        return DeviceConfig.getLong(DeviceConfig.NAMESPACE_TELEPHONY,
-                EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS,
-                DEFAULT_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
+    private static long getEmergencyCallWaitForConnectionTimeoutMillis(@NonNull Context context) {
+        return context.getResources().getInteger(
+                R.integer.config_emergency_call_wait_for_connection_timeout_millis);
+    }
+
+    /**
+     * @return The Pair(PackageName, ClassName) of the oem-enabled satellite handover app.
+     */
+    @NonNull
+    private static Pair<String, String> getOemEnabledSatelliteHandoverAppFromOverlayConfig(
+            @NonNull Context context) {
+        String app = null;
+        try {
+            app = context.getResources().getString(
+                    R.string.config_oem_enabled_satellite_sos_handover_app);
+        } catch (Resources.NotFoundException ex) {
+            loge("getOemEnabledSatelliteHandoverAppFromOverlayConfig: ex=" + ex);
+        }
+        if (TextUtils.isEmpty(app) && isMockModemAllowed()) {
+            logd("getOemEnabledSatelliteHandoverAppFromOverlayConfig: Read "
+                    + "config_oem_enabled_satellite_sos_handover_app from device config");
+            app = DeviceConfig.getString(DeviceConfig.NAMESPACE_TELEPHONY,
+                    "config_oem_enabled_satellite_sos_handover_app", "");
+        }
+        if (TextUtils.isEmpty(app)) return new Pair<>("", "");
+
+        String[] appComponent = app.split(";");
+        if (appComponent.length == 2) {
+            return new Pair<>(appComponent[0], appComponent[1]);
+        } else {
+            loge("getOemEnabledSatelliteHandoverAppFromOverlayConfig: invalid configured app="
+                    + app);
+        }
+        return new Pair<>("", "");
+    }
+
+
+    @Nullable
+    private static String getSatelliteEmergencyHandoverIntentActionFromOverlayConfig(
+            @NonNull Context context) {
+        String action;
+        try {
+            action = context.getResources().getString(
+                    R.string.config_satellite_emergency_handover_intent_action);
+        } catch (Resources.NotFoundException ex) {
+            loge("getSatelliteEmergencyHandoverIntentFilterActionFromOverlayConfig: ex=" + ex);
+            action = null;
+        }
+        if (TextUtils.isEmpty(action) && isMockModemAllowed()) {
+            logd("getSatelliteEmergencyHandoverIntentActionFromOverlayConfig: Read "
+                    + "config_satellite_emergency_handover_intent_action from device config");
+            action = DeviceConfig.getString(DeviceConfig.NAMESPACE_TELEPHONY,
+                    "config_satellite_emergency_handover_intent_action", null);
+        }
+        return action;
     }
 
     private boolean shouldTrackCall(int connectionState) {
@@ -405,6 +528,110 @@
                 && connectionState != Connection.STATE_DISCONNECTED);
     }
 
+    @NonNull
+    private RegistrationManager.RegistrationCallback getOrCreateImsRegistrationCallback(
+            int phoneId) {
+        RegistrationManager.RegistrationCallback callback =
+                mImsRegistrationCallbacks.get(phoneId);
+        if (callback == null) {
+            callback = new RegistrationManager.RegistrationCallback() {
+                @Override
+                public void onRegistered(ImsRegistrationAttributes attributes) {
+                    sendMessage(obtainMessage(EVENT_SERVICE_STATE_CHANGED));
+                }
+
+                @Override
+                public void onUnregistered(ImsReasonInfo info) {
+                    sendMessage(obtainMessage(EVENT_SERVICE_STATE_CHANGED));
+                }
+            };
+            mImsRegistrationCallbacks.put(phoneId, callback);
+        }
+        return callback;
+    }
+
+    @NonNull private Bundle createExtraBundleForEventDisplayEmergencyMessage() {
+        int handoverType = EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS;
+        Pair<String, String> oemSatelliteMessagingApp =
+                getOemEnabledSatelliteHandoverAppFromOverlayConfig(mContext);
+        String packageName = oemSatelliteMessagingApp.first;
+        String className = oemSatelliteMessagingApp.second;
+        String action = getSatelliteEmergencyHandoverIntentActionFromOverlayConfig(mContext);
+
+        if (isSatelliteViaCarrierAvailable()
+                || isEmergencyCallToSatelliteHandoverTypeT911Enforced()) {
+            ComponentName defaultSmsAppComponent = getDefaultSmsApp();
+            handoverType = EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911;
+            packageName = defaultSmsAppComponent.getPackageName();
+            className = defaultSmsAppComponent.getClassName();
+        }
+        logd("EVENT_DISPLAY_EMERGENCY_MESSAGE: handoverType=" + handoverType + ", packageName="
+                + packageName + ", className=" + className + ", action=" + action);
+
+        Bundle result = new Bundle();
+        result.putInt(EXTRA_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE, handoverType);
+        if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(className)) {
+            result.putParcelable(EXTRA_EMERGENCY_CALL_TO_SATELLITE_LAUNCH_INTENT,
+                    createHandoverAppLaunchPendingIntent(packageName, className, action));
+        }
+        return result;
+    }
+
+    @NonNull private PendingIntent createHandoverAppLaunchPendingIntent(
+            @NonNull String packageName, @NonNull String className, @Nullable String action) {
+        Intent intent = new Intent(action);
+        intent.setComponent(new ComponentName(packageName, className));
+        return PendingIntent.getActivity(mContext, 0, intent,
+                PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
+    }
+
+    private boolean isEmergencyCallToSatelliteHandoverTypeT911Enforced() {
+        return (mSatelliteController.getEnforcedEmergencyCallToSatelliteHandoverType()
+                == EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911);
+    }
+
+    private boolean sendEventDisplayEmergencyMessageForcefully(@NonNull Connection connection) {
+        if (mSatelliteController.getEnforcedEmergencyCallToSatelliteHandoverType()
+                == INVALID_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE) {
+            return false;
+        }
+
+        long delaySeconds = mSatelliteController.getDelayInSendingEventDisplayEmergencyMessage();
+        sendMessageDelayed(
+                obtainMessage(CMD_SEND_EVENT_DISPLAY_EMERGENCY_MESSAGE_FORCEFULLY, connection),
+                delaySeconds * 1000);
+        return true;
+    }
+
+    private void handleCmdSendEventDisplayEmergencyMessageForcefully(
+            @NonNull Connection connection) {
+        logd("Sent EVENT_DISPLAY_EMERGENCY_MESSAGE to Dialer forcefully.");
+        Bundle extras = createExtraBundleForEventDisplayEmergencyMessage();
+        connection.sendConnectionEvent(TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE, extras);
+    }
+
+    private boolean isMultiSim() {
+        TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
+        if (telephonyManager == null) {
+            loge("isMultiSim: telephonyManager is null");
+            return false;
+        }
+        return telephonyManager.isMultiSimEnabled();
+    }
+
+    private int getEmergencyCallToSatelliteHandoverType() {
+        if (isSatelliteViaCarrierAvailable()) {
+            return EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911;
+        } else {
+            return EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS;
+        }
+    }
+
+    private static boolean isMockModemAllowed() {
+        return (SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false)
+                || SystemProperties.getBoolean(BOOT_ALLOW_MOCK_MODEM_PROPERTY, false));
+    }
+
     private static void logd(@NonNull String log) {
         Rlog.d(TAG, log);
     }
diff --git a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
index 8146983..1bf866b 100644
--- a/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
+++ b/src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java
@@ -1161,7 +1161,16 @@
                         builder.setDisplayName(nickName);
                         builder.setDisplayNameSource(SubscriptionManager.NAME_SOURCE_CARRIER);
                     }
-                    builder.setProfileClass(embeddedProfile.getProfileClass());
+
+                    if (android.os.Build.isDebuggable() &&
+                            SystemProperties.getInt("telephony.test.bootstrap_cid", -2)
+                                == carrierId) {
+                        // Force set as provisioning profile for test purpose
+                        log("Hardcording as bootstrap subscription for cid=" + carrierId);
+                        builder.setProfileClass(SimInfo.PROFILE_CLASS_PROVISIONING);
+                    } else {
+                        builder.setProfileClass(embeddedProfile.getProfileClass());
+                    }
                     builder.setPortIndex(getPortIndex(embeddedProfile.getIccid()));
 
                     CarrierIdentifier cid = embeddedProfile.getCarrierIdentifier();
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java b/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java
index 80167d6..2df688d 100644
--- a/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java
+++ b/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java
@@ -48,7 +48,7 @@
 @RunWith(AndroidJUnit4.class)
 public class ImsFeatureTest {
     // Public for Mockito testing
-    public class CapabilityCallback extends IImsCapabilityCallback.Stub {
+    public static class CapabilityCallback extends IImsCapabilityCallback.Stub {
 
         @Override
         public void onQueryCapabilityConfiguration(int capability, int radioTech, boolean enabled)
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsMmTelManagerTests.java b/tests/telephonytests/src/android/telephony/ims/ImsMmTelManagerTests.java
index 337e296..df53374 100644
--- a/tests/telephonytests/src/android/telephony/ims/ImsMmTelManagerTests.java
+++ b/tests/telephonytests/src/android/telephony/ims/ImsMmTelManagerTests.java
@@ -41,7 +41,7 @@
     ITelephony mMockTelephonyInterface;
     BinderCacheManager<ITelephony> mBinderCache;
 
-    public class LocalCallback extends ImsMmTelManager.RegistrationCallback {
+    public static class LocalCallback extends ImsMmTelManager.RegistrationCallback {
         int mRegResult = -1;
 
         @Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index 3c8db99..2dac867 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -108,7 +108,7 @@
  * Controls a test {@link Context} as would be provided by the Android framework to an
  * {@code Activity}, {@code Service} or other system-instantiated component.
  *
- * Contains Fake<Component> classes like FakeContext for components that require complex and
+ * Contains {@code Fake<Component>} classes like FakeContext for components that require complex and
  * reusable stubbing. Others can be mocked using Mockito functions in tests or constructor/public
  * methods of this class.
  */
@@ -117,7 +117,6 @@
     public static final String PERMISSION_ENABLE_ALL = "android.permission.STUB_PERMISSION";
 
     public static class FakeContentProvider extends MockContentProvider {
-        private String[] mColumns = {"name", "value"};
         private HashMap<String, String> mKeyValuePairs = new HashMap<String, String>();
         private int mNumKeyValuePairs = 0;
         private HashMap<String, String> mFlags = new HashMap<>();
@@ -747,18 +746,14 @@
         doAnswer(new Answer<List<ResolveInfo>>() {
             @Override
             public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable {
-                return doQueryIntentServices(
-                        (Intent) invocation.getArguments()[0],
-                        (Integer) invocation.getArguments()[1]);
+                return doQueryIntentServices((Intent) invocation.getArguments()[0]);
             }
         }).when(mPackageManager).queryIntentServices((Intent) any(), anyInt());
 
         doAnswer(new Answer<List<ResolveInfo>>() {
             @Override
             public List<ResolveInfo> answer(InvocationOnMock invocation) throws Throwable {
-                return doQueryIntentServices(
-                        (Intent) invocation.getArguments()[0],
-                        (Integer) invocation.getArguments()[1]);
+                return doQueryIntentServices((Intent) invocation.getArguments()[0]);
             }
         }).when(mPackageManager).queryIntentServicesAsUser((Intent) any(), anyInt(), any());
 
@@ -766,6 +761,7 @@
             doReturn(mPackageInfo).when(mPackageManager).getPackageInfo(nullable(String.class),
                     anyInt());
         } catch (NameNotFoundException e) {
+            Log.d(TAG, "NameNotFoundException: e=" + e);
         }
 
         doAnswer((Answer<Boolean>)
@@ -775,7 +771,7 @@
         try {
             doReturn(mResources).when(mPackageManager).getResourcesForApplication(anyString());
         } catch (NameNotFoundException ex) {
-            Log.d(TAG, "NameNotFoundException: " + ex);
+            Log.d(TAG, "NameNotFoundException: ex=" + ex);
         }
 
         doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt());
@@ -851,7 +847,7 @@
         mMockBindingFailureForPackage.add(packageName);
     }
 
-    private List<ResolveInfo> doQueryIntentServices(Intent intent, int flags) {
+    private List<ResolveInfo> doQueryIntentServices(Intent intent) {
         List<ResolveInfo> result = new ArrayList<ResolveInfo>();
         for (ComponentName componentName : mComponentNamesByAction.get(intent.getAction())) {
             ResolveInfo resolveInfo = new ResolveInfo();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 0b25c55..378907b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -2065,6 +2065,79 @@
     }
 
     @Test
+    public void testPollStateExceptionRadioPowerOn() {
+        assertEquals(TelephonyManager.RADIO_POWER_ON, mSimulatedCommands.getRadioState());
+        assertEquals(ServiceState.STATE_IN_SERVICE, sst.getServiceState().getState());
+        assertEquals(ServiceState.STATE_IN_SERVICE,
+                sst.getServiceState().getDataRegistrationState());
+
+        sst.mPollingContext[0] = 1;
+        sst.sendMessage(sst.obtainMessage(
+                ServiceStateTracker.EVENT_POLL_STATE_OPERATOR,
+                new AsyncResult(sst.mPollingContext, null,
+                        new CommandException(CommandException.Error.RADIO_NOT_AVAILABLE))));
+        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+
+        assertEquals(ServiceState.STATE_IN_SERVICE, sst.getServiceState().getState());
+        assertEquals(ServiceState.STATE_IN_SERVICE,
+                sst.getServiceState().getDataRegistrationState());
+        assertEquals(0, sst.mPollingContext[0]);
+    }
+
+    @Test
+    public void testPollStateExceptionRadioPowerOff() {
+        // Turn off radio first.
+        sst.setRadioPower(false);
+        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+        assertEquals(TelephonyManager.RADIO_POWER_OFF, mSimulatedCommands.getRadioState());
+        assertEquals(ServiceState.STATE_POWER_OFF, sst.getServiceState().getState());
+        assertEquals(ServiceState.STATE_POWER_OFF,
+                sst.getServiceState().getDataRegistrationState());
+        // Override service state
+        sst.getServiceState().setVoiceRegState(ServiceState.STATE_IN_SERVICE);
+        sst.getServiceState().setDataRegState(ServiceState.STATE_IN_SERVICE);
+
+        sst.mPollingContext[0] = 1;
+        sst.sendMessage(sst.obtainMessage(
+                ServiceStateTracker.EVENT_POLL_STATE_OPERATOR,
+                new AsyncResult(sst.mPollingContext, null,
+                        new CommandException(CommandException.Error.RADIO_NOT_AVAILABLE))));
+        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+
+        assertEquals(ServiceState.STATE_POWER_OFF, sst.getServiceState().getVoiceRegState());
+        assertEquals(ServiceState.STATE_POWER_OFF, sst.getServiceState().getDataRegState());
+        assertEquals(1, sst.mPollingContext[0]);
+    }
+
+    @Test
+    public void testPollStateExceptionRadioPowerOffOnIwlan() {
+        // Turn off radio first.
+        sst.setRadioPower(false);
+        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+        assertEquals(TelephonyManager.RADIO_POWER_OFF, mSimulatedCommands.getRadioState());
+        assertEquals(ServiceState.STATE_POWER_OFF, sst.getServiceState().getState());
+        assertEquals(ServiceState.STATE_POWER_OFF,
+                sst.getServiceState().getDataRegistrationState());
+        // Override service state
+        sst.getServiceState().setVoiceRegState(ServiceState.STATE_IN_SERVICE);
+        sst.getServiceState().setDataRegState(ServiceState.STATE_IN_SERVICE);
+        // Override to IWLAN
+        sst.mSS.setRilDataRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN);
+
+        sst.mPollingContext[0] = 1;
+        sst.sendMessage(sst.obtainMessage(
+                ServiceStateTracker.EVENT_POLL_STATE_OPERATOR,
+                new AsyncResult(sst.mPollingContext, null,
+                        new CommandException(CommandException.Error.RADIO_NOT_AVAILABLE))));
+        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+
+        assertNull(null, sst.getServiceState().getOperatorAlpha());
+        assertEquals(ServiceState.STATE_POWER_OFF, sst.getServiceState().getVoiceRegState());
+        assertEquals(ServiceState.STATE_POWER_OFF, sst.getServiceState().getDataRegState());
+        assertEquals(1, sst.mPollingContext[0]);
+    }
+
+    @Test
     public void testCSEmergencyRegistrationState() throws Exception {
         CellIdentityGsm cellIdentity =
                 new CellIdentityGsm(0, 1, 900, 5, "001", "01", "test", "tst",
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
index 95a6154..39c0cac 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
@@ -1014,7 +1014,6 @@
 
     @VisibleForTesting
     public int getGetOperatorCallCount() {
-        final int count = mGetOperatorCallCount.get();
         return mGetOperatorCallCount.get();
     }
 
@@ -1153,7 +1152,7 @@
                         "fd00:976a:c305:1d::8 fd00:976a:c202:1d::7 fd00:976a:c305:1d::5"));
                 mSetupDataCallResult.mtu = 1440;
             } catch (Exception e) {
-
+                Rlog.e(LOG_TAG, "setupDataCall: e=" + e);
             }
         }
 
@@ -1993,7 +1992,7 @@
         if (!mShouldReturnCellInfo) return;
 
         if (mCellInfoList == null) {
-            ArrayList<CellInfo> mCellInfoList = new ArrayList();
+            mCellInfoList = new ArrayList();
             mCellInfoList.add(getCellInfoGsm());
         }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index c613155..1274aa3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -64,6 +64,7 @@
 import android.os.RegistrantList;
 import android.os.ServiceManager;
 import android.os.StrictMode;
+import android.os.SystemClock;
 import android.os.UserManager;
 import android.permission.LegacyPermissionManager;
 import android.provider.BlockedNumberContract;
@@ -148,8 +149,6 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
@@ -300,7 +299,7 @@
     protected Context mContext;
     protected FakeBlockedNumberContentProvider mFakeBlockedNumberContentProvider;
     private final ContentProvider mContentProvider = spy(new ContextFixture.FakeContentProvider());
-    private Object mLock = new Object();
+    private final Object mLock = new Object();
     private boolean mReady;
     protected HashMap<String, IBinder> mServiceManagerMockedServices = new HashMap<>();
     protected Phone[] mPhones;
@@ -314,9 +313,9 @@
 
     private final HashMap<InstanceKey, Object> mOldInstances = new HashMap<>();
 
-    private final LinkedList<InstanceKey> mInstanceKeys = new LinkedList<>();
+    private final List<InstanceKey> mInstanceKeys = new ArrayList<>();
 
-    private class InstanceKey {
+    private static class InstanceKey {
         public final Class mClass;
         public final String mInstName;
         public final Object mObj;
@@ -333,7 +332,7 @@
 
         @Override
         public boolean equals(Object obj) {
-            if (obj == null || obj.getClass() != getClass()) {
+            if (obj == null || !(obj instanceof InstanceKey)) {
                 return false;
             }
 
@@ -345,15 +344,18 @@
 
     protected void waitUntilReady() {
         synchronized (mLock) {
-            if (!mReady) {
+            long now = SystemClock.elapsedRealtime();
+            long deadline = now + MAX_INIT_WAIT_MS;
+            while (!mReady && now < deadline) {
                 try {
                     mLock.wait(MAX_INIT_WAIT_MS);
-                } catch (InterruptedException ie) {
+                } catch (Exception e) {
+                    fail("Telephony tests failed to initialize: e=" + e);
                 }
-
-                if (!mReady) {
-                    fail("Telephony tests failed to initialize");
-                }
+                now = SystemClock.elapsedRealtime();
+            }
+            if (!mReady) {
+                fail("Telephony tests failed to initialize");
             }
         }
     }
@@ -392,10 +394,8 @@
     }
 
     protected synchronized void restoreInstances() throws Exception {
-        Iterator<InstanceKey> it = mInstanceKeys.descendingIterator();
-
-        while (it.hasNext()) {
-            InstanceKey key = it.next();
+        for (int i = mInstanceKeys.size() - 1; i >= 0; i--) {
+            InstanceKey key = mInstanceKeys.get(i);
             Field field = key.mClass.getDeclaredField(key.mInstName);
             field.setAccessible(true);
             field.set(key.mObj, mOldInstances.get(key));
@@ -710,7 +710,7 @@
         doReturn(mSimRecords).when(mUiccProfile).getIccRecords();
         doAnswer(new Answer<IccRecords>() {
             public IccRecords answer(InvocationOnMock invocation) {
-                return (mPhone.isPhoneTypeGsm()) ? mSimRecords : mRuimRecords;
+                return mSimRecords;
             }
         }).when(mUiccProfile).getIccRecords();
 
@@ -920,7 +920,9 @@
         }
         if (mContext != null) {
             SharedPreferences sharedPreferences = mContext.getSharedPreferences((String) null, 0);
-            sharedPreferences.edit().clear().commit();
+            if (sharedPreferences != null) {
+                sharedPreferences.edit().clear().commit();
+            }
         }
         restoreInstances();
         TelephonyManager.enableServiceHandleCaching();
@@ -944,7 +946,6 @@
         mContextFixture = null;
         mContext = null;
         mFakeBlockedNumberContentProvider = null;
-        mLock = null;
         mServiceManagerMockedServices.clear();
         mServiceManagerMockedServices = null;
         mPhone = null;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
index a09994b..01a82ba 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
@@ -415,6 +415,47 @@
             .setPreferred(false)
             .build();
 
+    private final DataProfile mEsimBootstrapDataProfile = new DataProfile.Builder()
+            .setApnSetting(new ApnSetting.Builder()
+                    .setEntryName("ESIM BOOTSTRAP")
+                    .setApnName("ESIM BOOTSTRAP")
+                    .setApnTypeBitmask(ApnSetting.TYPE_DEFAULT)
+                    .setNetworkTypeBitmask((int) TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+                            | (int) TelephonyManager.NETWORK_TYPE_BITMASK_NR)
+                    .setCarrierEnabled(true)
+                    .setEsimBootstrapProvisioning(true)
+                    .build())
+            .setPreferred(false)
+            .build();
+
+    private final DataProfile mEsimBootstrapImsProfile = new DataProfile.Builder()
+            .setApnSetting(new ApnSetting.Builder()
+                    .setEntryName("IMS BOOTSTRAP")
+                    .setApnName("IMS BOOTSTRAP")
+                    .setApnTypeBitmask(ApnSetting.TYPE_IMS)
+                    .setNetworkTypeBitmask((int) TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+                            | (int) TelephonyManager.NETWORK_TYPE_BITMASK_NR)
+                    .setCarrierEnabled(true)
+                    .setEsimBootstrapProvisioning(true)
+                    .build())
+            .setPreferred(false)
+            .build();
+
+    private final DataProfile mEsimBootstrapRcsInfraStructureProfile =
+            new DataProfile.Builder()
+            .setApnSetting(new ApnSetting.Builder()
+                    .setEntryName("INFRASTRUCTURE BOOTSTRAP")
+                    .setApnName("INFRASTRUCTURE BOOTSTRAP")
+                    .setApnTypeBitmask(ApnSetting.TYPE_RCS)
+                    .setNetworkTypeBitmask((int) TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+                            | (int) TelephonyManager.NETWORK_TYPE_BITMASK_NR)
+                    .setCarrierEnabled(true)
+                    .setInfrastructureBitmask(2)
+                    .setEsimBootstrapProvisioning(true)
+                    .build())
+            .setPreferred(false)
+            .build();
+
     /** Data call response map. The first key is the transport type, the second key is the cid. */
     private final Map<Integer, Map<Integer, DataCallResponse>> mDataCallResponses = new HashMap<>();
 
@@ -919,7 +960,8 @@
                 mGeneralPurposeDataProfileAlternative, mImsCellularDataProfile,
                 mImsIwlanDataProfile, mEmergencyDataProfile, mFotaDataProfile,
                 mTetheringDataProfile, mMmsOnWlanDataProfile, mLowLatencyDataProfile,
-                mNtnDataProfile);
+                mNtnDataProfile, mEsimBootstrapDataProfile,
+                mEsimBootstrapImsProfile, mEsimBootstrapRcsInfraStructureProfile);
 
         doAnswer(invocation -> {
             DataProfile dp = (DataProfile) invocation.getArguments()[0];
@@ -951,7 +993,8 @@
                     (TelephonyNetworkRequest) invocation.getArguments()[0];
             int networkType = (int) invocation.getArguments()[1];
             boolean isNtn = (boolean) invocation.getArguments()[2];
-            boolean ignorePermanentFailure = (boolean) invocation.getArguments()[3];
+            boolean isEsimBootstrapProvisioning = (boolean) invocation.getArguments()[3];
+            boolean ignorePermanentFailure = (boolean) invocation.getArguments()[4];
 
             for (DataProfile dataProfile : profiles) {
                 ApnSetting apnSetting = dataProfile.getApnSetting();
@@ -960,20 +1003,24 @@
                         && (apnSetting.getNetworkTypeBitmask() == 0
                         || (apnSetting.getNetworkTypeBitmask()
                         & ServiceState.getBitmaskForTech(networkType)) != 0)
+                        && (isEsimBootstrapProvisioning
+                        == apnSetting.isEsimBootstrapProvisioning())
                         && ((isNtn && apnSetting.isForInfrastructure(
-                                ApnSetting.INFRASTRUCTURE_SATELLITE))
-                        || ((!isNtn && apnSetting.isForInfrastructure(
-                                ApnSetting.INFRASTRUCTURE_CELLULAR))))
+                        ApnSetting.INFRASTRUCTURE_SATELLITE))
+                        || (!isNtn && apnSetting.isForInfrastructure(
+                        ApnSetting.INFRASTRUCTURE_CELLULAR)))
                         && (ignorePermanentFailure || !apnSetting.getPermanentFailed())) {
                     return dataProfile;
                 }
             }
             logd("Cannot find data profile to satisfy " + networkRequest + ", network type="
                     + TelephonyManager.getNetworkTypeName(networkType) + ", ignorePermanentFailure="
-                    + ignorePermanentFailure + ", isNtn=" + isNtn);
+                    + ignorePermanentFailure + ", isNtn=" + isNtn + ","
+                    + "isEsimBootstrapProvisioning=" + isEsimBootstrapProvisioning);
             return null;
         }).when(mDataProfileManager).getDataProfileForNetworkRequest(
-                any(TelephonyNetworkRequest.class), anyInt(), anyBoolean(), anyBoolean());
+                any(TelephonyNetworkRequest.class), anyInt(), anyBoolean(), anyBoolean(),
+                anyBoolean());
 
         doReturn(AccessNetworkConstants.TRANSPORT_TYPE_WWAN).when(mAccessNetworksManager)
                 .getPreferredTransportByNetworkCapability(anyInt());
@@ -1181,6 +1228,18 @@
                 + dataNetworkList);
     }
 
+    private void verifyConnectedNetworkHasNoDataProfile(@NonNull DataProfile dataProfile)
+            throws Exception {
+        List<DataNetwork> dataNetworkList = getDataNetworks();
+        for (DataNetwork dataNetwork : getDataNetworks()) {
+            if (dataNetwork.isConnected() && dataNetwork.getDataProfile().equals(dataProfile)) {
+                fail("network with " + dataProfile + " is connected. dataNetworkList="
+                        + dataNetworkList);
+            }
+        }
+        return;
+    }
+
     private void verifyAllDataDisconnected() throws Exception {
         List<DataNetwork> dataNetworkList = getDataNetworks();
         assertWithMessage("All data should be disconnected but it's not. " + dataNetworkList)
@@ -1255,7 +1314,8 @@
                     + TelephonyManager.getNetworkTypeName(networkType));
             return null;
         }).when(mDataProfileManager).getDataProfileForNetworkRequest(
-                any(TelephonyNetworkRequest.class), anyInt(), anyBoolean(), anyBoolean());
+                any(TelephonyNetworkRequest.class), anyInt(), anyBoolean(), anyBoolean(),
+                anyBoolean());
 
         // verify the network still connects
         verify(mMockedDataNetworkControllerCallback).onConnectedInternetDataNetworksChanged(any());
@@ -1300,7 +1360,7 @@
                 createDataCallResponse(1, DataCallResponse.LINK_STATUS_ACTIVE, tdList));
         doReturn(mEnterpriseDataProfile).when(mDataProfileManager)
                 .getDataProfileForNetworkRequest(any(TelephonyNetworkRequest.class), anyInt(),
-                        anyBoolean(), anyBoolean());
+                        anyBoolean(), anyBoolean(), anyBoolean());
 
         NetworkCapabilities netCaps = new NetworkCapabilities();
         netCaps.addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE);
@@ -1515,7 +1575,7 @@
         // Now RAT changes from UMTS to GSM
         doReturn(null).when(mDataProfileManager).getDataProfileForNetworkRequest(
                 any(TelephonyNetworkRequest.class), eq(TelephonyManager.NETWORK_TYPE_GSM),
-                anyBoolean(), anyBoolean());
+                anyBoolean(), anyBoolean(), anyBoolean());
         serviceStateChanged(TelephonyManager.NETWORK_TYPE_GSM,
                 NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
         verifyAllDataDisconnected();
@@ -1529,14 +1589,14 @@
         // Now RAT changes from GSM to UMTS
         doReturn(null).when(mDataProfileManager).getDataProfileForNetworkRequest(
                 any(TelephonyNetworkRequest.class), eq(TelephonyManager.NETWORK_TYPE_UMTS),
-                anyBoolean(), anyBoolean());
+                anyBoolean(), anyBoolean(), anyBoolean());
         serviceStateChanged(TelephonyManager.NETWORK_TYPE_UMTS,
                 NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
         verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
 
         doReturn(mGeneralPurposeDataProfile).when(mDataProfileManager)
                 .getDataProfileForNetworkRequest(any(TelephonyNetworkRequest.class), anyInt(),
-                        anyBoolean(), anyBoolean());
+                        anyBoolean(), anyBoolean(), anyBoolean());
         // Now RAT changes from UMTS to LTE
         serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
                 NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
@@ -3531,7 +3591,7 @@
                 createDataCallResponse(1, DataCallResponse.LINK_STATUS_ACTIVE, tdList));
         doReturn(mEnterpriseDataProfile).when(mDataProfileManager)
                 .getDataProfileForNetworkRequest(any(TelephonyNetworkRequest.class), anyInt(),
-                        anyBoolean(), anyBoolean());
+                        anyBoolean(), anyBoolean(), anyBoolean());
 
         NetworkCapabilities netCaps = new NetworkCapabilities();
         netCaps.addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE);
@@ -4593,7 +4653,7 @@
                 createDataCallResponse(1, DataCallResponse.LINK_STATUS_ACTIVE, tdList));
         doReturn(mEnterpriseDataProfile).when(mDataProfileManager)
                 .getDataProfileForNetworkRequest(any(TelephonyNetworkRequest.class), anyInt(),
-                        anyBoolean(), anyBoolean());
+                        anyBoolean(), anyBoolean(), anyBoolean());
         mDataNetworkControllerUT.addNetworkRequest(new TelephonyNetworkRequest(
                 new NetworkRequest.Builder()
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)
@@ -4631,7 +4691,7 @@
                 createDataCallResponse(2, DataCallResponse.LINK_STATUS_ACTIVE, tdList));
         doReturn(mLowLatencyDataProfile).when(mDataProfileManager)
                 .getDataProfileForNetworkRequest(any(TelephonyNetworkRequest.class), anyInt(),
-                        anyBoolean(), anyBoolean());
+                        anyBoolean(), anyBoolean(), anyBoolean());
         processAllFutureMessages();
 
         dataNetworkList = getDataNetworks();
@@ -4664,7 +4724,7 @@
         // Mock the designated MMS profile when WLAN is preferred
         doReturn(mMmsOnWlanDataProfile).when(mDataProfileManager).getDataProfileForNetworkRequest(
                 any(TelephonyNetworkRequest.class), eq(TelephonyManager.NETWORK_TYPE_IWLAN),
-                anyBoolean(), anyBoolean());
+                anyBoolean(), anyBoolean(), anyBoolean());
         setSuccessfulSetupDataResponse(mMockedWlanDataServiceManager,
                 createDataCallResponse(2, DataCallResponse.LINK_STATUS_ACTIVE));
 
@@ -4686,4 +4746,132 @@
         processAllMessages();
         verifyConnectedNetworkHasDataProfile(mNtnDataProfile);
     }
+
+    @Test
+    public void testIsEsimBootStrapProvisioningActivatedWithFlagEnabledAndProvisioningClass() {
+        when(mFeatureFlags.esimBootstrapProvisioningFlag()).thenReturn(true);
+        doReturn(new SubscriptionInfoInternal.Builder().setId(1)
+                .setProfileClass(SubscriptionManager.PROFILE_CLASS_PROVISIONING).build())
+                .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
+
+        assertThat(mDataNetworkControllerUT.isEsimBootStrapProvisioningActivated()).isTrue();
+    }
+
+    @Test
+    public void testIsEsimBootStrapProvisioningActivatedWithFlagEnabledAndNoProvisioningClass() {
+        when(mFeatureFlags.esimBootstrapProvisioningFlag()).thenReturn(true);
+        doReturn(new SubscriptionInfoInternal.Builder().setId(1)
+                .setProfileClass(SubscriptionManager.PROFILE_CLASS_UNSET).build())
+                .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
+
+        assertThat(mDataNetworkControllerUT.isEsimBootStrapProvisioningActivated()).isFalse();
+    }
+
+    @Test
+    public void testIsEsimBootStrapProvisioningActivatedWithFlagDisabledAndNoProvisioningClass() {
+        when(mFeatureFlags.esimBootstrapProvisioningFlag()).thenReturn(false);
+        doReturn(new SubscriptionInfoInternal.Builder().setId(1)
+                .setProfileClass(SubscriptionManager.PROFILE_CLASS_UNSET).build())
+                .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
+
+        assertThat(mDataNetworkControllerUT.isEsimBootStrapProvisioningActivated()).isFalse();
+    }
+
+    @Test
+    public void testIsEsimBootStrapProvisioningActivatedWithFlagDisabledAndProvisioningClass() {
+        when(mFeatureFlags.esimBootstrapProvisioningFlag()).thenReturn(false);
+        doReturn(new SubscriptionInfoInternal.Builder().setId(1)
+                .setProfileClass(SubscriptionManager.PROFILE_CLASS_PROVISIONING).build())
+                .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
+
+        assertThat(mDataNetworkControllerUT.isEsimBootStrapProvisioningActivated()).isFalse();
+    }
+
+    @Test
+    public void testNetworkOnProvisioningProfileClass_WithFlagEnabled() throws Exception {
+        when(mFeatureFlags.esimBootstrapProvisioningFlag()).thenReturn(true);
+        doReturn(new SubscriptionInfoInternal.Builder().setId(1)
+                .setProfileClass(SubscriptionManager.PROFILE_CLASS_PROVISIONING).build())
+                .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
+        serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
+                NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+        processAllMessages();
+        verifyConnectedNetworkHasDataProfile(mEsimBootstrapDataProfile);
+
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_IMS,
+                        NetworkCapabilities.NET_CAPABILITY_MMTEL));
+        setSuccessfulSetupDataResponse(mMockedDataServiceManagers
+                .get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN), 2);
+        processAllMessages();
+        verifyConnectedNetworkHasDataProfile(mEsimBootstrapImsProfile);
+
+        serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
+                NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_RCS));
+        processAllMessages();
+        verifyNoConnectedNetworkHasCapability(NetworkCapabilities.NET_CAPABILITY_RCS);
+    }
+
+    @Test
+    public void testNetworkOnNonProvisioningProfileClass_WithFlagEnabled() throws Exception {
+        when(mFeatureFlags.esimBootstrapProvisioningFlag()).thenReturn(true);
+        doReturn(new SubscriptionInfoInternal.Builder().setId(1)
+                .setProfileClass(SubscriptionManager.PROFILE_CLASS_UNSET).build())
+                .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
+        serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
+                NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_INTERNET));
+        processAllMessages();
+        verifyConnectedNetworkHasNoDataProfile(mEsimBootstrapDataProfile);
+
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_IMS,
+                        NetworkCapabilities.NET_CAPABILITY_MMTEL));
+        setSuccessfulSetupDataResponse(mMockedDataServiceManagers
+                .get(AccessNetworkConstants.TRANSPORT_TYPE_WWAN), 2);
+        processAllMessages();
+        verifyConnectedNetworkHasNoDataProfile(mEsimBootstrapImsProfile);
+    }
+
+    @Test
+    public void testNtnNetworkOnProvisioningProfileClass_WithFlagEnabled() throws Exception {
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+        when(mFeatureFlags.esimBootstrapProvisioningFlag()).thenReturn(true);
+        doReturn(new SubscriptionInfoInternal.Builder().setId(1)
+                .setProfileClass(SubscriptionManager.PROFILE_CLASS_PROVISIONING).build())
+                .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
+        mIsNonTerrestrialNetwork = true;
+        serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
+                NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_RCS));
+        processAllMessages();
+
+        assertThat(mDataNetworkControllerUT.isEsimBootStrapProvisioningActivated()).isTrue();
+        verifyConnectedNetworkHasNoDataProfile(mNtnDataProfile);
+        verifyConnectedNetworkHasDataProfile(mEsimBootstrapRcsInfraStructureProfile);
+    }
+
+    @Test
+    public void testNonNtnNetworkOnProvisioningProfileClass_WithFlagEnabled() throws Exception {
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+        when(mFeatureFlags.esimBootstrapProvisioningFlag()).thenReturn(true);
+        doReturn(new SubscriptionInfoInternal.Builder().setId(1)
+                .setProfileClass(SubscriptionManager.PROFILE_CLASS_PROVISIONING).build())
+                .when(mSubscriptionManagerService).getSubscriptionInfoInternal(anyInt());
+        serviceStateChanged(TelephonyManager.NETWORK_TYPE_LTE,
+                NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
+        mDataNetworkControllerUT.addNetworkRequest(
+                createNetworkRequest(NetworkCapabilities.NET_CAPABILITY_RCS));
+        processAllMessages();
+
+        assertThat(mDataNetworkControllerUT.isEsimBootStrapProvisioningActivated()).isTrue();
+        verifyConnectedNetworkHasNoDataProfile(mNtnDataProfile);
+        verifyConnectedNetworkHasNoDataProfile(mEsimBootstrapRcsInfraStructureProfile);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
index f9a11be..f8d22cd 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataProfileManagerTest.java
@@ -84,8 +84,12 @@
     private static final String TETHERING_APN = "DUN_APN";
     private static final String APN_SET_ID_1_APN = "APN_SET_ID_1_APN";
     private static final String RCS_APN = "RCS_APN";
+    private static final String RCS_APN1 = "RCS_APN1";
     private static final String APN_SET_ID_1_TETHERING_APN = "APN_SET_ID_1_TETHERING_APN";
     private static final String MATCH_ALL_APN_SET_ID_IMS_APN = "MATCH_ALL_APN_SET_ID_IMS_APN";
+    private static final String ESIM_BOOTSTRAP_PROVISIONING_APN = "ESIM_BOOTSTRAP_PROVISIONING_APN";
+    private static final String TEST_BOOTSTRAP_APN =
+            "TEST_BOOTSTRAP_APN";
     private static final String PLMN = "330123";
     private static final int DEFAULT_APN_SET_ID = Telephony.Carriers.NO_APN_SET_ID;
     private static final int APN_SET_ID_1 = 1;
@@ -137,7 +141,8 @@
                 Telephony.Carriers.CARRIER_ID,
                 Telephony.Carriers.SKIP_464XLAT,
                 Telephony.Carriers.ALWAYS_ON,
-                Telephony.Carriers.INFRASTRUCTURE_BITMASK
+                Telephony.Carriers.INFRASTRUCTURE_BITMASK,
+                Telephony.Carriers.ESIM_BOOTSTRAP_PROVISIONING
         };
 
         private int mPreferredApnSet = 0;
@@ -177,7 +182,8 @@
                         -1,                     // carrier_id
                         -1,                     // skip_464xlat
                         0,                      // always_on
-                        1                       // INFRASTRUCTURE_CELLULAR
+                        1,                      // INFRASTRUCTURE_CELLULAR
+                        0                       // esim_bootstrap_provisioning
                 },
                 // default internet data profile for RAT CDMA, to test update preferred data profile
                 new Object[]{
@@ -213,7 +219,8 @@
                         -1,                     // carrier_id
                         -1,                     // skip_464xlat
                         0,                      // always_on
-                        1                       // INFRASTRUCTURE_CELLULAR
+                        1,                       // INFRASTRUCTURE_CELLULAR
+                        0                       // esim_bootstrap_provisioning
                 },
                 new Object[]{
                         2,                      // id
@@ -248,7 +255,8 @@
                         -1,                     // carrier_id
                         -1,                     // skip_464xlat
                         0,                      // always_on
-                        1                       // INFRASTRUCTURE_CELLULAR
+                        1,                       // INFRASTRUCTURE_CELLULAR
+                        0                       // esim_bootstrap_provisioning
                 },
                 new Object[]{
                         3,                      // id
@@ -283,7 +291,8 @@
                         -1,                     // carrier_id
                         -1,                     // skip_464xlat
                         0,                      // always_on
-                        1                       // INFRASTRUCTURE_CELLULAR
+                        1,                       // INFRASTRUCTURE_CELLULAR
+                        0                       // esim_bootstrap_provisioning
                 },
                 new Object[]{
                         4,                      // id
@@ -319,7 +328,8 @@
                         -1,                     // carrier_id
                         -1,                     // skip_464xlat
                         0,                      // always_on
-                        1                       // INFRASTRUCTURE_CELLULAR
+                        1,                       // INFRASTRUCTURE_CELLULAR
+                        0                       // esim_bootstrap_provisioning
                 },
                 // This APN entry is created to test de-duping.
                 new Object[]{
@@ -356,7 +366,8 @@
                         -1,                     // carrier_id
                         -1,                     // skip_464xlat
                         0,                      // always_on
-                        1                       // INFRASTRUCTURE_CELLULAR
+                        1,                       // INFRASTRUCTURE_CELLULAR
+                        0                       // esim_bootstrap_provisioning
                 },
                 new Object[]{
                         6,                      // id
@@ -392,7 +403,8 @@
                         -1,                     // carrier_id
                         -1,                     // skip_464xlat
                         0,                      // always_on
-                        1                       // INFRASTRUCTURE_CELLULAR
+                        1,                       // INFRASTRUCTURE_CELLULAR
+                        0                       // esim_bootstrap_provisioning
                 },
                 new Object[]{
                         7,                      // id
@@ -428,7 +440,8 @@
                         -1,                     // carrier_id
                         -1,                     // skip_464xlat
                         0,                      // always_on
-                        1                       // INFRASTRUCTURE_CELLULAR
+                        1,                       // INFRASTRUCTURE_CELLULAR
+                        0                       // esim_bootstrap_provisioning
                 },
                 new Object[]{
                         8,                      // id
@@ -464,7 +477,8 @@
                         -1,                     // carrier_id
                         -1,                     // skip_464xlat
                         0,                      // always_on
-                        1                       // INFRASTRUCTURE_CELLULAR
+                        1,                       // INFRASTRUCTURE_CELLULAR
+                        0                       // esim_bootstrap_provisioning
                 },
                 new Object[]{
                         9,                      // id
@@ -500,7 +514,156 @@
                         -1,                     // carrier_id
                         -1,                     // skip_464xlat
                         0,                      // always_on
-                        2                       // INFRASTRUCTURE_SATELLITE
+                        2,                      // INFRASTRUCTURE_SATELLITE
+                        0                      // esim_bootstrap_provisioning
+                },
+                new Object[]{
+                        10,                      // id
+                        PLMN,                   // numeric
+                        ESIM_BOOTSTRAP_PROVISIONING_APN, // name
+                        ESIM_BOOTSTRAP_PROVISIONING_APN, // apn
+                        "",                     // proxy
+                        "",                     // port
+                        "",                     // mmsc
+                        "",                     // mmsproxy
+                        "",                     // mmsport
+                        "",                     // user
+                        "",                     // password
+                        -1,                     // authtype
+                        "default,supl",          // types
+                        "IPV4V6",               // protocol
+                        "IPV4V6",               // roaming_protocol
+                        1,                      // carrier_enabled
+                        0,                      // profile_id
+                        1,                      // modem_cognitive
+                        0,                      // max_conns
+                        0,                      // wait_time
+                        0,                      // max_conns_time
+                        0,                      // mtu
+                        1280,                   // mtu_v4
+                        1280,                   // mtu_v6
+                        "",                     // mvno_type
+                        "",                     // mnvo_match_data
+                        TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+                                | TelephonyManager.NETWORK_TYPE_BITMASK_NR, // network_type_bitmask
+                        0,                      // lingering_network_type_bitmask
+                        MATCH_ALL_APN_SET_ID,   // apn_set_id
+                        -1,                     // carrier_id
+                        -1,                     // skip_464xlat
+                        0,                      // always_on
+                        1,                       // INFRASTRUCTURE_CELLULAR
+                        1                       // esim_bootstrap_provisioning
+                },
+                new Object[]{
+                        11,                      // id
+                        PLMN,                   // numeric
+                        IMS_APN,                // name
+                        IMS_APN,                // apn
+                        "",                     // proxy
+                        "",                     // port
+                        "",                     // mmsc
+                        "",                     // mmsproxy
+                        "",                     // mmsport
+                        "",                     // user
+                        "",                     // password
+                        -1,                     // authtype
+                        "ims",                  // types
+                        "IPV4V6",               // protocol
+                        "IPV4V6",               // roaming_protocol
+                        1,                      // carrier_enabled
+                        0,                      // profile_id
+                        1,                      // modem_cognitive
+                        0,                      // max_conns
+                        0,                      // wait_time
+                        0,                      // max_conns_time
+                        0,                      // mtu
+                        1280,                   // mtu_v4
+                        1280,                   // mtu_v6
+                        "",                     // mvno_type
+                        "",                     // mnvo_match_data
+                        TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+                                | TelephonyManager.NETWORK_TYPE_BITMASK_NR, // network_type_bitmask
+                        0,                      // lingering_network_type_bitmask
+                        MATCH_ALL_APN_SET_ID,   // apn_set_id
+                        -1,                     // carrier_id
+                        -1,                     // skip_464xlat
+                        0,                      // always_on
+                        1,                       // INFRASTRUCTURE_SATELLITE
+                        1                       // esim_bootstrap_provisioning
+                },
+                new Object[]{
+                        12,                     // id
+                        PLMN,                   // numeric
+                        TEST_BOOTSTRAP_APN,     // name
+                        TEST_BOOTSTRAP_APN,      // apn
+                        "",                     // proxy
+                        "",                     // port
+                        "",                     // mmsc
+                        "",                     // mmsproxy
+                        "",                     // mmsport
+                        "",                     // user
+                        "",                     // password
+                        -1,                     // authtype
+                        "default",              // types
+                        "IPV4V6",               // protocol
+                        "IPV4V6",               // roaming_protocol
+                        1,                      // carrier_enabled
+                        0,                      // profile_id
+                        1,                      // modem_cognitive
+                        0,                      // max_conns
+                        0,                      // wait_time
+                        0,                      // max_conns_time
+                        0,                      // mtu
+                        1280,                   // mtu_v4
+                        1280,                   // mtu_v6
+                        "",                     // mvno_type
+                        "",                     // mnvo_match_data
+                        TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+                                | TelephonyManager.NETWORK_TYPE_BITMASK_NR, // network_type_bitmask
+                        0,                      // lingering_network_type_bitmask
+                        MATCH_ALL_APN_SET_ID,   // apn_set_id
+                        -1,                     // carrier_id
+                        -1,                     // skip_464xlat
+                        0,                      // always_on
+                        2,                       // INFRASTRUCTURE_SATELLITE
+                        1                       // esim_bootstrap_provisioning
+                },
+                new Object[]{
+                        13,                      // id
+                        PLMN,                   // numeric
+                        RCS_APN1,                // name
+                        RCS_APN1,                // apn
+                        "",                     // proxy
+                        "",                     // port
+                        "",                     // mmsc
+                        "",                     // mmsproxy
+                        "",                     // mmsport
+                        "",                     // user
+                        "",                     // password
+                        -1,                     // authtype
+                        "rcs",                  // types
+                        "IPV4V6",               // protocol
+                        "IPV4V6",               // roaming_protocol
+                        1,                      // carrier_enabled
+                        0,                      // profile_id
+                        1,                      // modem_cognitive
+                        0,                      // max_conns
+                        0,                      // wait_time
+                        0,                      // max_conns_time
+                        0,                      // mtu
+                        1280,                   // mtu_v4
+                        1280,                   // mtu_v6
+                        "",                     // mvno_type
+                        "",                     // mnvo_match_data
+                        TelephonyManager.NETWORK_TYPE_BITMASK_LTE
+                                | TelephonyManager.NETWORK_TYPE_BITMASK_NR, // network_type_bitmask
+                        0,                      // lingering_network_type_bitmask
+                        DEFAULT_APN_SET_ID,     // apn_set_id
+                        -1,                     // carrier_id
+                        -1,                     // skip_464xlat
+                        0,                      // always_on
+                        2,                       // INFRASTRUCTURE_SATELLITE
+                        1                       // esim_bootstrap_provisioning
                 }
         );
 
@@ -716,7 +879,7 @@
                 .build();
         TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(request, mPhone);
         DataProfile dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
 
         assertThat(dp.canSatisfy(tnr.getCapabilities())).isTrue();
         assertThat(dp.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN);
@@ -727,7 +890,7 @@
                 .build();
         tnr = new TelephonyNetworkRequest(request, mPhone);
         dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
 
         assertThat(dp.canSatisfy(tnr.getCapabilities())).isTrue();
         assertThat(dp.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN);
@@ -737,7 +900,7 @@
                 .build();
         tnr = new TelephonyNetworkRequest(request, mPhone);
         dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
         assertThat(dp.canSatisfy(tnr.getCapabilities())).isTrue();
         assertThat(dp.getApnSetting().getApnName()).isEqualTo(IMS_APN);
 
@@ -746,7 +909,7 @@
                 .build();
         tnr = new TelephonyNetworkRequest(request, mPhone);
         dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
         assertThat(dp).isNull();
 
         doReturn(new NetworkRegistrationInfo.Builder()
@@ -759,7 +922,7 @@
                 .build();
         tnr = new TelephonyNetworkRequest(request, mPhone);
         dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_NR, false, false);
+                TelephonyManager.NETWORK_TYPE_NR, false, false , false);
         assertThat(dp.canSatisfy(tnr.getCapabilities())).isTrue();
         assertThat(dp.getApnSetting().getApnName()).isEqualTo(TETHERING_APN);
     }
@@ -771,7 +934,7 @@
                 .build();
         TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(request, mPhone);
         DataProfile dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_GSM, false, false);
+                TelephonyManager.NETWORK_TYPE_GSM, false, false, false);
         // Should not find data profile due to RAT incompatible.
         assertThat(dp).isNull();
     }
@@ -783,14 +946,14 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                         .build(), mPhone);
         DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
         assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN);
         logd("Set setLastSetupTimestamp on " + dataProfile);
         dataProfile.setLastSetupTimestamp(SystemClock.elapsedRealtime());
 
         // See if another one can be returned.
         dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
         assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN1);
     }
 
@@ -801,7 +964,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_ENTERPRISE)
                         .build(), mPhone);
         DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
         assertThat(dataProfile.getApnSetting()).isNull();
         OsAppId osAppId = new OsAppId(dataProfile.getTrafficDescriptor().getOsAppId());
 
@@ -814,7 +977,7 @@
                 .addEnterpriseId(2), ConnectivityManager.TYPE_NONE,
                 0, NetworkRequest.Type.REQUEST), mPhone);
         dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
         assertThat(dataProfile.getApnSetting()).isNull();
         osAppId = new OsAppId(dataProfile.getTrafficDescriptor().getOsAppId());
 
@@ -830,7 +993,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY)
                         .build(), mPhone);
         DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
         assertThat(dataProfile.getApnSetting()).isNull();
         OsAppId osAppId = new OsAppId(dataProfile.getTrafficDescriptor().getOsAppId());
 
@@ -846,7 +1009,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH)
                         .build(), mPhone);
         DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
         assertThat(dataProfile.getApnSetting()).isNull();
         OsAppId osAppId = new OsAppId(dataProfile.getTrafficDescriptor().getOsAppId());
 
@@ -864,7 +1027,7 @@
                 .build();
         TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(request, mPhone);
         DataProfile dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_LTE, true, false);
+                TelephonyManager.NETWORK_TYPE_LTE, true, false, false);
 
         assertThat(dp.canSatisfy(tnr.getCapabilities())).isTrue();
         assertThat(dp.getApnSetting().getApnName()).isEqualTo(RCS_APN);
@@ -877,7 +1040,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                         .build(), mPhone);
         DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
         assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN);
         dataProfile.setLastSetupTimestamp(SystemClock.elapsedRealtime());
         dataProfile.setPreferred(true);
@@ -891,14 +1054,14 @@
 
         // Test See if the same one can be returned.
         dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
         assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN);
         assertThat(mDataProfileManagerUT.isDataProfilePreferred(dataProfile)).isTrue();
 
         // Test Another default internet network connected due to RAT changed. Verify the preferred
         // data profile is updated.
         DataProfile legacyRatDataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_CDMA, false, false);
+                tnr, TelephonyManager.NETWORK_TYPE_CDMA, false, false, false);
         DataNetwork legacyRatInternetNetwork = Mockito.mock(DataNetwork.class);
         doReturn(legacyRatDataProfile).when(legacyRatInternetNetwork).getDataProfile();
         doReturn(new DataNetworkController.NetworkRequestList(List.of(tnr)))
@@ -917,7 +1080,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
                         .build(), mPhone);
         DataProfile dunDataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                dunTnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                dunTnr, TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
         DataNetwork dunInternetNetwork = Mockito.mock(DataNetwork.class);
         doReturn(dunDataProfile).when(dunInternetNetwork).getDataProfile();
         doReturn(new DataNetworkController.NetworkRequestList(List.of(dunTnr)))
@@ -1016,7 +1179,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                         .build(), mPhone);
         DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
         assertThat(dataProfile).isNull();
 
         // expect default EIMS when SIM absent
@@ -1025,7 +1188,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)
                         .build(), mPhone);
         dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
         assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo("sos");
 
         // expect no default IMS when SIM absent
@@ -1034,7 +1197,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
                         .build(), mPhone);
         dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
         assertThat(dataProfile).isEqualTo(null);
 
         // Verify null as initial attached data profile is sent to modem
@@ -1064,7 +1227,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                         .build(), mPhone);
         DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
         assertThat(dataProfile).isNull();
 
         // expect default EIMS when SIM absent
@@ -1073,7 +1236,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)
                         .build(), mPhone);
         dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
         assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo("sos");
 
         // expect no default IMS when SIM absent
@@ -1082,7 +1245,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
                         .build(), mPhone);
         dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
         assertThat(dataProfile).isEqualTo(null);
 
         // Verify in legacy mode, null IA should NOT be sent to modem
@@ -1117,7 +1280,7 @@
                 new TelephonyNetworkRequest(new NetworkRequest.Builder()
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
                         .build(), mPhone),
-                TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
         assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(IMS_APN);
     }
 
@@ -1129,7 +1292,7 @@
         TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(request, mPhone);
         // This should get the merged data profile after deduping.
         DataProfile dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
         assertThat(dp.canSatisfy(NetworkCapabilities.NET_CAPABILITY_INTERNET)).isTrue();
     }
 
@@ -1266,7 +1429,7 @@
                 .addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)
                 .build(), mPhone);
         DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
 
         assertThat(dataProfile.getApn()).isEqualTo("sos");
         assertThat(dataProfile.getTrafficDescriptor().getDataNetworkName()).isEqualTo("sos");
@@ -1283,7 +1446,7 @@
                         .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                         .build(), mPhone);
         DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
         assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN);
         dataProfile.setLastSetupTimestamp(SystemClock.elapsedRealtime());
         DataNetwork internetNetwork = Mockito.mock(DataNetwork.class);
@@ -1354,7 +1517,7 @@
 
         // The carrier configured data profile should be the preferred APN after APN reset
         DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
 
         assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN1);
         assertThat(mDataProfileManagerUT.isDataProfilePreferred(dataProfile)).isTrue();
@@ -1367,7 +1530,7 @@
 
         // The carrier configured data profile should be the preferred APN after APN reset
         dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
-                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
         assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(GENERAL_PURPOSE_APN1);
         assertThat(mDataProfileManagerUT.isDataProfilePreferred(dataProfile)).isTrue();
     }
@@ -1648,7 +1811,7 @@
                 .build();
         TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(request, mPhone);
         DataProfile dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
 
         // Mark the APN as permanent failed.
         dp.getApnSetting().setPermanentFailed(true);
@@ -1656,7 +1819,8 @@
         // Data profile manager should return a different data profile for setup as the previous
         // data profile has been marked as permanent failed.
         assertThat(mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_LTE, false, false)).isNotEqualTo(dp);
+                TelephonyManager.NETWORK_TYPE_LTE, false, false, false))
+                .isNotEqualTo(dp);
     }
 
     @Test
@@ -1671,7 +1835,7 @@
                 .build();
         TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(request, mPhone);
         DataProfile dp = mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_LTE, false, false);
+                TelephonyManager.NETWORK_TYPE_LTE, false, false, false);
 
         // Mark the APN as permanent failed.
         dp.getApnSetting().setPermanentFailed(true);
@@ -1679,7 +1843,8 @@
         // Since preferred APN is already set, and that data profile was marked as permanent failed,
         // so this should result in getting nothing.
         assertThat(mDataProfileManagerUT.getDataProfileForNetworkRequest(tnr,
-                TelephonyManager.NETWORK_TYPE_LTE, false, false)).isNull();
+                TelephonyManager.NETWORK_TYPE_LTE, false, false, false))
+                .isNull();
     }
 
     private void changeSimStateTo(@TelephonyManager.SimState int simState) {
@@ -1700,6 +1865,98 @@
         // Verify the we can get the previously permanent failed data profile again.
         assertThat(mDataProfileManagerUT.getDataProfileForNetworkRequest(
                 new TelephonyNetworkRequest(request, mPhone),
-                TelephonyManager.NETWORK_TYPE_LTE, false, false)).isNotNull();
+                TelephonyManager.NETWORK_TYPE_LTE, false, false, false))
+                .isNotNull();
     }
+
+    @Test
+    public void testDifferentNetworkRequestProfilesOnEsimBootStrapProvisioning() {
+        Mockito.clearInvocations(mDataProfileManagerCallback);
+        Mockito.clearInvocations(mMockedWwanDataServiceManager);
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+
+        // SIM inserted
+        mDataProfileManagerUT.obtainMessage(3 /* EVENT_SIM_REFRESH */).sendToTarget();
+        processAllMessages();
+
+        // expect default profile for internet network request, when esim bootstrap provisioning
+        // flag is enabled at data profile, during esim bootstrap provisioning
+        TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(new NetworkRequest.Builder()
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                .build(), mPhone);
+        DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, true, false);
+        assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(
+                "ESIM_BOOTSTRAP_PROVISIONING_APN");
+
+        // expect IMS profile for ims network request, when esim bootstrap provisioning flag
+        // is enabled at data profile, during esim bootstrap provisioning
+        tnr = new TelephonyNetworkRequest(new NetworkRequest.Builder()
+                 .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS)
+                 .build(), mPhone);
+        dataProfile  = mDataProfileManagerUT.getDataProfileForNetworkRequest(
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, true, false);
+        assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo("IMS_APN");
+
+        // expect no mms profile for mms network request, when esim bootstrap provisioning flag
+        // is disabled at data profile, during esim bootstrap provisioning
+        tnr = new TelephonyNetworkRequest(new NetworkRequest.Builder()
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_MMS)
+                .build(), mPhone);
+        dataProfile  = mDataProfileManagerUT.getDataProfileForNetworkRequest(
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, true, false);
+        assertThat(dataProfile).isEqualTo(null);
+
+        // expect no rcs profile for rcs network request, when esim bootstrap provisioning flag
+        // is disabled at data profile, during esim bootstrap provisioning
+        tnr = new TelephonyNetworkRequest(new NetworkRequest.Builder()
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_RCS)
+                .build(), mPhone);
+        dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, true, false);
+        assertThat(dataProfile).isEqualTo(null);
+    }
+
+    @Test
+    public void testEsimBootstrapProvisioningEnabled_MultipleProfile() {
+        Mockito.clearInvocations(mDataProfileManagerCallback);
+        Mockito.clearInvocations(mMockedWwanDataServiceManager);
+
+        // SIM inserted
+        mDataProfileManagerUT.obtainMessage(3 /* EVENT_SIM_REFRESH */).sendToTarget();
+        processAllMessages();
+
+        // expect initial default profile entry selected for internet network request, when
+        // multiple esim bootstrap provisioning flag is enabled at data profile for same apn
+        // type
+        TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(new NetworkRequest.Builder()
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                .build(), mPhone);
+        DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, false, true, false);
+        assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(
+                ESIM_BOOTSTRAP_PROVISIONING_APN);
+    }
+
+    @Test
+    public void testInfrastructureProfileOnEsimBootStrapProvisioning() {
+        Mockito.clearInvocations(mDataProfileManagerCallback);
+        Mockito.clearInvocations(mMockedWwanDataServiceManager);
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+
+        // SIM inserted
+        mDataProfileManagerUT.obtainMessage(3 /* EVENT_SIM_REFRESH */).sendToTarget();
+        processAllMessages();
+
+        // expect initial default profile entry selected for internet network request, when
+        // multiple esim bootstrap provisioning flag is enabled at data profile for same apn
+        // type
+        TelephonyNetworkRequest tnr = new TelephonyNetworkRequest(new NetworkRequest.Builder()
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_RCS)
+                .build(), mPhone);
+        DataProfile dataProfile = mDataProfileManagerUT.getDataProfileForNetworkRequest(
+                tnr, TelephonyManager.NETWORK_TYPE_LTE, true, true, false);
+        assertThat(dataProfile.getApnSetting().getApnName()).isEqualTo(RCS_APN1);
+    }
+
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
index aa24c46..0cbb0f6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
@@ -1034,6 +1034,9 @@
         mSatelliteSosMessageRecommender1.isImsRegistered = false;
         mSatelliteSosMessageRecommender1.cellularServiceState =
                 TelephonyProtoEnums.SERVICE_STATE_OUT_OF_SERVICE;
+        mSatelliteSosMessageRecommender1.isMultiSim = true;
+        mSatelliteSosMessageRecommender1.recommendingHandoverType = 1;
+        mSatelliteSosMessageRecommender1.isSatelliteAllowedInCurrentLocation = true;
         mSatelliteSosMessageRecommender1.count = 1;
 
         mSatelliteSosMessageRecommender2 = new SatelliteSosMessageRecommender();
@@ -1042,6 +1045,9 @@
         mSatelliteSosMessageRecommender2.isImsRegistered = true;
         mSatelliteSosMessageRecommender2.cellularServiceState =
                 TelephonyProtoEnums.SERVICE_STATE_POWER_OFF;
+        mSatelliteSosMessageRecommender2.isMultiSim = false;
+        mSatelliteSosMessageRecommender2.recommendingHandoverType = 0;
+        mSatelliteSosMessageRecommender2.isSatelliteAllowedInCurrentLocation = true;
         mSatelliteSosMessageRecommender2.count = 1;
 
         mSatelliteSosMessageRecommenders =
@@ -4689,7 +4695,11 @@
                     == expectedStats.isDisplaySosMessageSent
                     && stats.countOfTimerStarted == expectedStats.countOfTimerStarted
                     && stats.isImsRegistered == expectedStats.isImsRegistered
-                    && stats.cellularServiceState == expectedStats.cellularServiceState) {
+                    && stats.cellularServiceState == expectedStats.cellularServiceState
+                    && stats.isMultiSim == expectedStats.isMultiSim
+                    && stats.recommendingHandoverType == expectedStats.recommendingHandoverType
+                    && stats.isSatelliteAllowedInCurrentLocation
+                    == expectedStats.isSatelliteAllowedInCurrentLocation) {
                 actualCount = stats.count;
             }
         }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java
index 4393f1c..959b643 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/SatelliteStatsTest.java
@@ -225,6 +225,9 @@
                         .setCountOfTimerStarted(5)
                         .setImsRegistered(false)
                         .setCellularServiceState(TelephonyProtoEnums.SERVICE_STATE_OUT_OF_SERVICE)
+                        .setIsMultiSim(false)
+                        .setRecommendingHandoverType(0)
+                        .setIsSatelliteAllowedInCurrentLocation(true)
                         .build();
 
         mSatelliteStats.onSatelliteSosMessageRecommender(param);
@@ -238,6 +241,10 @@
         assertEquals(param.getCountOfTimerStarted(), stats.countOfTimerStarted);
         assertEquals(param.isImsRegistered(), stats.isImsRegistered);
         assertEquals(param.getCellularServiceState(), stats.cellularServiceState);
+        assertEquals(param.isMultiSim(), stats.isMultiSim);
+        assertEquals(param.getRecommendingHandoverType(), stats.recommendingHandoverType);
+        assertEquals(param.isSatelliteAllowedInCurrentLocation(),
+                stats.isSatelliteAllowedInCurrentLocation);
         verifyNoMoreInteractions(mPersistAtomsStorage);
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
index 3578707..94f56b4 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/DatagramReceiverTest.java
@@ -429,7 +429,7 @@
 
     @Test
     public void testRegisterForSatelliteDatagram_satelliteNotSupported() {
-        when(mMockSatelliteController.isSatelliteSupported()).thenReturn(false);
+        when(mMockSatelliteController.isSatelliteSupportedViaOem()).thenReturn(false);
 
         ISatelliteDatagramCallback callback = new ISatelliteDatagramCallback() {
             @Override
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 221aa0e..0556ef5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_ATTACH_SUPPORTED_BOOL;
+import static android.telephony.CarrierConfigManager.KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT;
 import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_GOOD;
 import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_GREAT;
 import static android.telephony.satellite.NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE;
@@ -92,6 +94,7 @@
 import android.os.ResultReceiver;
 import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
+import android.telephony.ServiceState;
 import android.telephony.satellite.INtnSignalStrengthCallback;
 import android.telephony.satellite.ISatelliteDatagramCallback;
 import android.telephony.satellite.ISatelliteProvisionStateCallback;
@@ -109,6 +112,8 @@
 import com.android.internal.R;
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.IVoidConsumer;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.flags.FeatureFlags;
 import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
@@ -121,6 +126,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
@@ -154,6 +160,7 @@
     private TestSatelliteController mSatelliteControllerUT;
     private TestSharedPreferences mSharedPreferences;
     private PersistableBundle mCarrierConfigBundle;
+    private ServiceState mServiceState2;
 
     @Mock private DatagramController mMockDatagramController;
     @Mock private SatelliteModemInterface mMockSatelliteModemInterface;
@@ -433,6 +440,13 @@
                 mMockSessionMetricsStats);
         replaceInstance(SubscriptionManagerService.class, "sInstance", null,
                 mMockSubscriptionManagerService);
+        replaceInstance(PhoneFactory.class, "sPhones", null, new Phone[]{mPhone, mPhone2});
+
+        mServiceState2 = Mockito.mock(ServiceState.class);
+        when(mPhone.getServiceState()).thenReturn(mServiceState);
+        when(mPhone.getSubId()).thenReturn(SUB_ID);
+        when(mPhone2.getServiceState()).thenReturn(mServiceState2);
+        when(mPhone2.getSubId()).thenReturn(SUB_ID1);
 
         mContextFixture.putStringArrayResource(
                 R.array.config_satellite_providers,
@@ -2318,6 +2332,70 @@
                 .stopSendingNtnSignalStrength(any(Message.class));
     }
 
+    @Test
+    public void testIsSatelliteSupportedViaCarrier() {
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(false);
+        assertFalse(mSatelliteControllerUT.isSatelliteSupportedViaCarrier());
+
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+        assertFalse(mSatelliteControllerUT.isSatelliteSupportedViaCarrier());
+
+        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();
+        assertTrue(mSatelliteControllerUT.isSatelliteSupportedViaCarrier());
+    }
+
+    @Test
+    public void testCarrierEnabledSatelliteConnectionHysteresisTime() {
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(false);
+        assertFalse(mSatelliteControllerUT.isSatelliteConnectedViaCarrierWithinHysteresisTime());
+
+        when(mFeatureFlags.carrierEnabledSatelliteFlag()).thenReturn(true);
+        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();
+        mSatelliteControllerUT.elapsedRealtime = 0;
+        assertFalse(mSatelliteControllerUT.isSatelliteConnectedViaCarrierWithinHysteresisTime());
+
+        when(mServiceState.isUsingNonTerrestrialNetwork()).thenReturn(false);
+        when(mServiceState2.isUsingNonTerrestrialNetwork()).thenReturn(false);
+        sendServiceStateChangedEvent();
+        processAllMessages();
+        assertFalse(mSatelliteControllerUT.isSatelliteConnectedViaCarrierWithinHysteresisTime());
+
+        // Last satellite connected time of Phone2 should be 0
+        when(mServiceState2.isUsingNonTerrestrialNetwork()).thenReturn(true);
+        sendServiceStateChangedEvent();
+        processAllMessages();
+        // 2 minutes later and hysteresis timeout is 1 minute
+        mSatelliteControllerUT.elapsedRealtime = 2 * 60 * 1000;
+        // But Phone2 is connected to NTN right now
+        assertTrue(mSatelliteControllerUT.isSatelliteConnectedViaCarrierWithinHysteresisTime());
+
+        // Last satellite disconnected time of Phone2 should be 2 * 60 * 1000
+        when(mServiceState2.isUsingNonTerrestrialNetwork()).thenReturn(false);
+        sendServiceStateChangedEvent();
+        processAllMessages();
+        // Current time (2) - last disconnected time (2) < hysteresis timeout (1)
+        assertTrue(mSatelliteControllerUT.isSatelliteConnectedViaCarrierWithinHysteresisTime());
+
+        // Current time (4) - last disconnected time (2) > hysteresis timeout (1)
+        mSatelliteControllerUT.elapsedRealtime = 4 * 60 * 1000;
+        assertFalse(mSatelliteControllerUT.isSatelliteConnectedViaCarrierWithinHysteresisTime());
+    }
+
     private void resetSatelliteControllerUTEnabledState() {
         logd("resetSatelliteControllerUTEnabledState");
         setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
@@ -2841,7 +2919,7 @@
 
     private void sendCmdStartSendingNtnSignalStrengthChangedEvent(boolean shouldReport) {
         Message msg = mSatelliteControllerUT.obtainMessage(
-                35 /* CMD_START_SENDING_NTN_SIGNAL_STRENGTH */);
+                35 /* CMD_UPDATE_NTN_SIGNAL_STRENGTH_REPORTING */);
         msg.obj = new AsyncResult(null, shouldReport, null);
         msg.sendToTarget();
     }
@@ -2850,12 +2928,17 @@
             @NtnSignalStrength.NtnSignalStrengthLevel int ntnSignalStrengthLevel,
             Throwable exception) {
         Message msg = mSatelliteControllerUT.obtainMessage(
-                36 /* EVENT_START_SENDING_NTN_SIGNAL_STRENGTH_DONE */);
+                36 /* EVENT_UPDATE_NTN_SIGNAL_STRENGTH_REPORTING_DONE */);
         msg.obj = new AsyncResult(null, new NtnSignalStrength(ntnSignalStrengthLevel),
                 exception);
         msg.sendToTarget();
     }
 
+    private void sendServiceStateChangedEvent() {
+        mSatelliteControllerUT.obtainMessage(37 /* EVENT_SERVICE_STATE_CHANGED */).sendToTarget();
+
+    }
+
     private void setRadioPower(boolean on) {
         mSimulatedCommands.setRadioPower(on, false, false, null);
     }
@@ -3008,6 +3091,7 @@
     private static class TestSatelliteController extends SatelliteController {
         public boolean setSettingsKeyForSatelliteModeCalled = false;
         public boolean allRadiosDisabled = true;
+        public long elapsedRealtime = 0;
         public int satelliteModeSettingValue = SATELLITE_MODE_ENABLED_FALSE;
 
         TestSatelliteController(
@@ -3039,5 +3123,10 @@
             logd("getCurrentNtnRadioTechnology: val=" + ntRadioTechnology);
             return ntRadioTechnology;
         }
+
+        @Override
+        protected long getElapsedRealtime() {
+            return elapsedRealtime;
+        }
     }
 }
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 3a108aa..589f32b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/satellite/SatelliteSOSMessageRecommenderTest.java
@@ -16,6 +16,11 @@
 
 package com.android.internal.telephony.satellite;
 
+import static android.telephony.TelephonyManager.EXTRA_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE;
+import static android.telephony.TelephonyManager.EXTRA_EMERGENCY_CALL_TO_SATELLITE_LAUNCH_INTENT;
+import static android.telephony.satellite.SatelliteManager.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS;
+import static android.telephony.satellite.SatelliteManager.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -26,17 +31,17 @@
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
-import android.app.ActivityManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.Resources;
-import android.os.AsyncResult;
 import android.os.Bundle;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.telecom.Connection;
 import android.telephony.BinderCacheManager;
-import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -45,11 +50,14 @@
 import android.telephony.satellite.SatelliteManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.ims.ImsException;
 import com.android.ims.ImsManager;
+import com.android.internal.R;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.flags.FeatureFlags;
 
@@ -61,8 +69,10 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Executor;
@@ -76,13 +86,16 @@
     private static final String TAG = "SatelliteSOSMessageRecommenderTest";
     private static final long TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS = 500;
     private static final int PHONE_ID = 0;
+    private static final int PHONE_ID2 = 1;
     private static final String CALL_ID = "CALL_ID";
     private static final String WRONG_CALL_ID = "WRONG_CALL_ID";
+    private static final String DEFAULT_SATELLITE_MESSAGING_PACKAGE = "android.com.google.default";
+    private static final String DEFAULT_SATELLITE_MESSAGING_CLASS =
+            "android.com.google.default.SmsMmsApp";
+    private static final String DEFAULT_HANDOVER_INTENT_ACTION =
+            "android.com.vendor.action.EMERGENCY_MESSAGING";
     private TestSatelliteController mTestSatelliteController;
     private TestImsManager mTestImsManager;
-
-    @Mock
-    private Context mMockContext;
     @Mock
     private Resources mResources;
     @Mock
@@ -91,40 +104,38 @@
     private FeatureFlags mFeatureFlags;
     private TestConnection mTestConnection;
     private TestSOSMessageRecommender mTestSOSMessageRecommender;
+    private ServiceState mServiceState2;
 
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
         MockitoAnnotations.initMocks(this);
 
-        when(mMockContext.getMainLooper()).thenReturn(Looper.myLooper());
-        when(mMockContext.getResources()).thenReturn(mResources);
-        when(mResources.getString(com.android.internal.R.string.config_satellite_service_package))
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mResources.getString(R.string.config_satellite_service_package))
                 .thenReturn("");
-        when(mMockContext.getSystemServiceName(CarrierConfigManager.class))
-                .thenReturn("CarrierConfigManager");
-        when(mMockContext.getSystemService(CarrierConfigManager.class))
-                .thenReturn(mCarrierConfigManager);
-        when(mMockContext.getSystemServiceName(SubscriptionManager.class))
-                .thenReturn("SubscriptionManager");
-        when(mMockContext.getSystemService(SubscriptionManager.class))
-                .thenReturn(mSubscriptionManager);
-        when(mMockContext.getSystemServiceName(ActivityManager.class))
-                .thenReturn("ActivityManager");
-        when(mMockContext.getSystemService(ActivityManager.class))
-                .thenReturn(mActivityManager);
+        when(mResources.getString(R.string.config_satellite_emergency_handover_intent_action))
+                .thenReturn(DEFAULT_HANDOVER_INTENT_ACTION);
         when(mFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
-        mTestSatelliteController = new TestSatelliteController(mMockContext,
+        mTestSatelliteController = new TestSatelliteController(mContext,
                 Looper.myLooper(), mFeatureFlags);
         mTestImsManager = new TestImsManager(
-                mMockContext, PHONE_ID, mMmTelFeatureConnectionFactory, null, null, null);
+                mContext, PHONE_ID, mMmTelFeatureConnectionFactory, null, null, null);
         mTestConnection = new TestConnection(CALL_ID);
+        mPhones = new Phone[] {mPhone, mPhone2};
+        replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+        mServiceState2 = Mockito.mock(ServiceState.class);
         when(mPhone.getServiceState()).thenReturn(mServiceState);
-        mTestSOSMessageRecommender = new TestSOSMessageRecommender(Looper.myLooper(),
+        when(mPhone.getPhoneId()).thenReturn(PHONE_ID);
+        when(mPhone2.getServiceState()).thenReturn(mServiceState2);
+        when(mPhone2.getPhoneId()).thenReturn(PHONE_ID2);
+        mTestSOSMessageRecommender = new TestSOSMessageRecommender(mContext, Looper.myLooper(),
                 mTestSatelliteController, mTestImsManager,
                 TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
         when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+        when(mServiceState2.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
         when(mPhone.isImsRegistered()).thenReturn(false);
+        when(mPhone2.isImsRegistered()).thenReturn(false);
     }
 
     @After
@@ -133,18 +144,103 @@
     }
 
     @Test
-    public void testTimeoutBeforeEmergencyCallEnd() {
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, mPhone);
+    public void testTimeoutBeforeEmergencyCallEnd_T911() {
+        testTimeoutBeforeEmergencyCallEnd(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911,
+                DEFAULT_SATELLITE_MESSAGING_PACKAGE, DEFAULT_SATELLITE_MESSAGING_CLASS,
+                DEFAULT_HANDOVER_INTENT_ACTION);
+    }
+
+    @Test
+    public void testTimeoutBeforeEmergencyCallEnd_SOS_WithValidHandoverAppConfigured() {
+        String satelliteHandoverApp =
+                "android.com.vendor.message;android.com.vendor.message.SmsApp";
+        when(mResources.getString(R.string.config_oem_enabled_satellite_sos_handover_app))
+                .thenReturn(satelliteHandoverApp);
+        mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false);
+        testTimeoutBeforeEmergencyCallEnd(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS,
+                "android.com.vendor.message", "android.com.vendor.message.SmsApp",
+                DEFAULT_HANDOVER_INTENT_ACTION);
+    }
+
+    @Test
+    public void testTimeoutBeforeEmergencyCallEnd_SOS_WithInValidHandoverAppConfigured() {
+        String satelliteHandoverApp =
+                "android.com.vendor.message;android.com.vendor.message.SmsApp;abc";
+        when(mResources.getString(R.string.config_oem_enabled_satellite_sos_handover_app))
+                .thenReturn(satelliteHandoverApp);
+        mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false);
+        testTimeoutBeforeEmergencyCallEnd(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS, "", "",
+                DEFAULT_HANDOVER_INTENT_ACTION);
+    }
+
+    @Test
+    public void testTimeoutBeforeEmergencyCallEnd_SOS_WithoutHandoverAppConfigured() {
+        when(mResources.getString(R.string.config_oem_enabled_satellite_sos_handover_app))
+                .thenReturn("");
+        mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false);
+        testTimeoutBeforeEmergencyCallEnd(EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS, "", "",
+                DEFAULT_HANDOVER_INTENT_ACTION);
+    }
+
+    private void testTimeoutBeforeEmergencyCallEnd(int expectedHandoverType,
+            String expectedPackageName, String expectedClassName, String expectedAction) {
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
         processAllMessages();
         assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
+        assertRegisterForStateChangedEventsTriggered(mPhone, 1, 2, 1);
+        assertRegisterForStateChangedEventsTriggered(mPhone2, 1, 2, 1);
 
         // Wait for the timeout to expires
         moveTimeForward(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
         processAllMessages();
+        if (TextUtils.isEmpty(expectedPackageName) || TextUtils.isEmpty(expectedClassName)) {
+            assertTrue(mTestConnection.isEventWithoutLaunchIntentSent(
+                    TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE, expectedHandoverType));
+        } else {
+            assertTrue(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE,
+                    expectedHandoverType, expectedPackageName, expectedClassName, expectedAction));
+        }
+        assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 2, 1);
+        assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 2, 1);
+    }
 
-        assertRegisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
-        assertTrue(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE));
-        assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+    @Test
+    public void testTimeoutBeforeEmergencyCallEnd_EventDisplayEmergencyMessageNotSent() {
+        mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false);
+        mTestSatelliteController.setIsSatelliteViaOemProvisioned(false);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        processAllMessages();
+        assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
+        assertRegisterForStateChangedEventsTriggered(mPhone, 1, 2, 1);
+        assertRegisterForStateChangedEventsTriggered(mPhone2, 1, 2, 1);
+
+        // Wait for the timeout to expires
+        moveTimeForward(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
+        processAllMessages();
+        assertFalse(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE));
+        assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 2, 1);
+        assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 2, 1);
+    }
+
+    @Test
+    public void testTimeoutBeforeEmergencyCallEnd_T911_FromNotConnectedToConnected() {
+        mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(false);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
+        processAllMessages();
+        assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
+        assertRegisterForStateChangedEventsTriggered(mPhone, 1, 2, 1);
+        assertRegisterForStateChangedEventsTriggered(mPhone2, 1, 2, 1);
+
+        mTestSatelliteController.setSatelliteConnectedViaCarrierWithinHysteresisTime(true);
+        // Wait for the timeout to expires
+        moveTimeForward(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
+        processAllMessages();
+        assertTrue(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE,
+                EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911,
+                DEFAULT_SATELLITE_MESSAGING_PACKAGE, DEFAULT_SATELLITE_MESSAGING_CLASS,
+                DEFAULT_HANDOVER_INTENT_ACTION));
+        assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 2, 1);
+        assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 2, 1);
     }
 
     @Test
@@ -159,22 +255,34 @@
 
     @Test
     public void testImsRegistrationStateChangedBeforeTimeout() {
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, mPhone);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
         processAllMessages();
 
         assertTrue(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
-        assertRegisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+        assertRegisterForStateChangedEventsTriggered(mPhone, 1, 2, 1);
+        assertRegisterForStateChangedEventsTriggered(mPhone2, 1, 2, 1);
 
-        mTestImsManager.sendImsRegistrationStateChangedEvent(true);
+        when(mPhone.isImsRegistered()).thenReturn(true);
+        mTestImsManager.sendImsRegistrationStateChangedEvent(0, true);
         processAllMessages();
 
-        assertFalse(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE));
+        assertFalse(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE));
         assertFalse(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
         assertUnregisterForStateChangedEventsTriggered(mPhone, 0, 0, 0);
 
-        mTestImsManager.sendImsRegistrationStateChangedEvent(false);
+        when(mPhone.isImsRegistered()).thenReturn(false);
+        when(mPhone2.isImsRegistered()).thenReturn(true);
+        mTestImsManager.sendImsRegistrationStateChangedEvent(1, true);
+        processAllMessages();
+        assertFalse(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE));
+        assertFalse(mTestSOSMessageRecommender.isTimerStarted());
+        assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
+        assertUnregisterForStateChangedEventsTriggered(mPhone, 0, 0, 0);
+
+        when(mPhone2.isImsRegistered()).thenReturn(false);
+        mTestImsManager.sendImsRegistrationStateChangedEvent(1, false);
         processAllMessages();
         assertEquals(2, mTestSOSMessageRecommender.getCountOfTimerStarted());
 
@@ -182,19 +290,23 @@
         moveTimeForward(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
         processAllMessages();
 
-        assertTrue(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE));
-        assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+        assertTrue(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE,
+                EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911, DEFAULT_SATELLITE_MESSAGING_PACKAGE,
+                DEFAULT_SATELLITE_MESSAGING_CLASS, DEFAULT_HANDOVER_INTENT_ACTION));
+        assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 2, 1);
+        assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 2, 1);
         assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
     }
 
     @Test
     public void testSatelliteProvisionStateChangedBeforeTimeout() {
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, mPhone);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
         processAllMessages();
 
         assertTrue(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
-        assertRegisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+        assertRegisterForStateChangedEventsTriggered(mPhone, 1, 2, 1);
+        assertRegisterForStateChangedEventsTriggered(mPhone2, 1, 2, 1);
 
         mTestSatelliteController.sendProvisionStateChangedEvent(
                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false);
@@ -202,13 +314,15 @@
 
         assertFalse(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
-        assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+        assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 2, 1);
+        assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 2, 1);
 
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, mPhone);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
         processAllMessages();
         assertTrue(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
-        assertRegisterForStateChangedEventsTriggered(mPhone, 2, 2, 2);
+        assertRegisterForStateChangedEventsTriggered(mPhone, 2, 4, 2);
+        assertRegisterForStateChangedEventsTriggered(mPhone2, 2, 4, 2);
 
         mTestSatelliteController.sendProvisionStateChangedEvent(
                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, true);
@@ -217,57 +331,44 @@
         moveTimeForward(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
         processAllMessages();
 
-        assertTrue(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE));
+        assertTrue(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE,
+                EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911, DEFAULT_SATELLITE_MESSAGING_PACKAGE,
+                DEFAULT_SATELLITE_MESSAGING_CLASS, DEFAULT_HANDOVER_INTENT_ACTION));
         assertFalse(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
-        assertUnregisterForStateChangedEventsTriggered(mPhone, 2, 2, 2);
+        assertUnregisterForStateChangedEventsTriggered(mPhone, 2, 4, 2);
+        assertUnregisterForStateChangedEventsTriggered(mPhone2, 2, 4, 2);
     }
 
     @Test
     public void testEmergencyCallRedialBeforeTimeout() {
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, mPhone);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
         processAllMessages();
 
         assertTrue(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
-        assertRegisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+        assertRegisterForStateChangedEventsTriggered(mPhone, 1, 2, 1);
+        assertRegisterForStateChangedEventsTriggered(mPhone2, 1, 2, 1);
 
-        Phone newPhone = Mockito.mock(Phone.class);
-        when(newPhone.getServiceState()).thenReturn(mServiceState);
-        when(newPhone.isImsRegistered()).thenReturn(false);
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, newPhone);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
         processAllMessages();
 
-        assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
         assertTrue(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
-        /**
-         * Since {@link SatelliteSOSMessageRecommender} always uses
-         * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} when registering for provision state
-         * changed events with {@link SatelliteController}, registerForProvisionCount does
-         * not depend on Phone.
-         * <p>
-         * Since we use a single mocked ImsManager instance, registerForImsCount does not depend on
-         * Phone.
-         */
-        assertRegisterForStateChangedEventsTriggered(newPhone, 2, 2, 1);
+        assertRegisterForStateChangedEventsTriggered(mPhone, 1, 2, 1);
+        assertRegisterForStateChangedEventsTriggered(mPhone2, 1, 2, 1);
 
         // Wait for the timeout to expires
         moveTimeForward(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
         processAllMessages();
 
-        assertTrue(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE));
-        /**
-         * Since {@link SatelliteSOSMessageRecommender} always uses
-         * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} when unregistering for provision
-         * state changed events with {@link SatelliteController}, unregisterForProvisionCount does
-         * not depend on Phone.
-         * <p>
-         * Since we use a single mocked ImsManager instance, unregisterForImsCount does not depend
-         * on Phone.
-         */
-        assertUnregisterForStateChangedEventsTriggered(newPhone, 2, 2, 1);
+        assertTrue(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE,
+                EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911, DEFAULT_SATELLITE_MESSAGING_PACKAGE,
+                DEFAULT_SATELLITE_MESSAGING_CLASS, DEFAULT_HANDOVER_INTENT_ACTION));
+        assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 2, 1);
+        assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 2, 1);
         assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
+        assertFalse(mTestSOSMessageRecommender.isTimerStarted());
     }
 
     @Test
@@ -296,50 +397,55 @@
 
     @Test
     public void testOnEmergencyCallConnectionStateChangedWithWrongCallId() {
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, mPhone);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
         processAllMessages();
 
         assertTrue(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
-        assertRegisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+        assertRegisterForStateChangedEventsTriggered(mPhone, 1, 2, 1);
+        assertRegisterForStateChangedEventsTriggered(mPhone2, 1, 2, 1);
 
         mTestSOSMessageRecommender.onEmergencyCallConnectionStateChanged(
                 WRONG_CALL_ID, Connection.STATE_ACTIVE);
         processAllMessages();
 
-        assertFalse(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE));
+        assertFalse(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE));
         assertFalse(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
-        assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+        assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 2, 1);
+        assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 2, 1);
     }
 
     @Test
     public void testSatelliteNotAllowedInCurrentLocation() {
         mTestSatelliteController.setIsSatelliteCommunicationAllowed(false);
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, mPhone);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
         processAllMessages();
 
         /**
-         * We should have registered for the state change events abd started the timer when
+         * We should have registered for the state change events and started the timer when
          * receiving the event onEmergencyCallStarted. After getting the callback for the result of
          * the request requestIsSatelliteCommunicationAllowedForCurrentLocation, the resources
          * should be cleaned up.
          */
         assertFalse(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
-        assertRegisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
-        assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+        assertRegisterForStateChangedEventsTriggered(mPhone, 1, 2, 1);
+        assertRegisterForStateChangedEventsTriggered(mPhone2, 1, 2, 1);
+        assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 2, 1);
+        assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 2, 1);
     }
 
     @Test
     public void testOnEmergencyCallStarted() {
         SatelliteController satelliteController = new SatelliteController(
-                mMockContext, Looper.myLooper(), mFeatureFlags);
+                mContext, Looper.myLooper(), mFeatureFlags);
         TestSOSMessageRecommender testSOSMessageRecommender = new TestSOSMessageRecommender(
+                mContext,
                 Looper.myLooper(),
                 satelliteController, mTestImsManager,
                 TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
-        testSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, mPhone);
+        testSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
         processAllMessages();
 
         assertFalse(testSOSMessageRecommender.isTimerStarted());
@@ -348,51 +454,68 @@
 
     private void testStopTrackingCallBeforeTimeout(
             @Connection.ConnectionState int connectionState) {
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, mPhone);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
         processAllMessages();
 
         assertTrue(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
-        assertRegisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+        assertRegisterForStateChangedEventsTriggered(mPhone, 1, 2, 1);
+        assertRegisterForStateChangedEventsTriggered(mPhone2, 1, 2, 1);
 
         mTestSOSMessageRecommender.onEmergencyCallConnectionStateChanged(CALL_ID, connectionState);
         processAllMessages();
 
-        assertFalse(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE));
+        assertFalse(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE));
         assertFalse(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
-        assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+        assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 2, 1);
+        assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 2, 1);
     }
 
     private void testCellularServiceStateChangedBeforeTimeout(
             @ServiceState.RegState int availableServiceState,
             @ServiceState.RegState int unavailableServiceState) {
-        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection, mPhone);
+        mTestSOSMessageRecommender.onEmergencyCallStarted(mTestConnection);
         processAllMessages();
 
         assertTrue(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
-        assertRegisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+        assertRegisterForStateChangedEventsTriggered(mPhone, 1, 2, 1);
+        assertRegisterForStateChangedEventsTriggered(mPhone2, 1, 2, 1);
 
-        mTestSOSMessageRecommender.sendServiceStateChangedEvent(availableServiceState);
+        when(mServiceState.getState()).thenReturn(availableServiceState);
+        mTestSOSMessageRecommender.sendServiceStateChangedEvent();
         processAllMessages();
-
-        assertFalse(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE));
+        assertFalse(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE));
         assertFalse(mTestSOSMessageRecommender.isTimerStarted());
         assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
         assertUnregisterForStateChangedEventsTriggered(mPhone, 0, 0, 0);
+        assertUnregisterForStateChangedEventsTriggered(mPhone2, 0, 0, 0);
 
-        mTestSOSMessageRecommender.sendServiceStateChangedEvent(unavailableServiceState);
+        when(mServiceState.getState()).thenReturn(unavailableServiceState);
+        when(mServiceState2.getState()).thenReturn(availableServiceState);
+        mTestSOSMessageRecommender.sendServiceStateChangedEvent();
+        processAllMessages();
+        assertFalse(mTestSOSMessageRecommender.isTimerStarted());
+        assertEquals(1, mTestSOSMessageRecommender.getCountOfTimerStarted());
+
+        when(mServiceState2.getState()).thenReturn(unavailableServiceState);
+        mTestSOSMessageRecommender.sendServiceStateChangedEvent();
         processAllMessages();
         assertEquals(2, mTestSOSMessageRecommender.getCountOfTimerStarted());
+        assertTrue(mTestSOSMessageRecommender.isTimerStarted());
 
         // Wait for the timeout to expires
         moveTimeForward(TEST_EMERGENCY_CALL_TO_SOS_MSG_HYSTERESIS_TIMEOUT_MILLIS);
         processAllMessages();
 
-        assertTrue(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_SOS_MESSAGE));
-        assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 1, 1);
+        assertTrue(mTestConnection.isEventSent(TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE,
+                EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911, DEFAULT_SATELLITE_MESSAGING_PACKAGE,
+                DEFAULT_SATELLITE_MESSAGING_CLASS, DEFAULT_HANDOVER_INTENT_ACTION));
+        assertUnregisterForStateChangedEventsTriggered(mPhone, 1, 2, 1);
+        assertUnregisterForStateChangedEventsTriggered(mPhone2, 1, 2, 1);
         assertEquals(0, mTestSOSMessageRecommender.getCountOfTimerStarted());
+        assertFalse(mTestSOSMessageRecommender.isTimerStarted());
     }
 
     private void assertRegisterForStateChangedEventsTriggered(
@@ -421,8 +544,9 @@
                 mProvisionStateChangedCallbacks;
         private int mRegisterForSatelliteProvisionStateChangedCalls = 0;
         private int mUnregisterForSatelliteProvisionStateChangedCalls = 0;
-        private boolean mIsSatelliteProvisioned = true;
+        private boolean mIsSatelliteViaOemProvisioned = true;
         private boolean mIsSatelliteCommunicationAllowed = true;
+        private boolean mIsSatelliteConnectedViaCarrierWithinHysteresisTime = true;
 
         /**
          * Create a SatelliteController to act as a backend service of
@@ -437,12 +561,12 @@
         }
 
         @Override
-        public Boolean isSatelliteProvisioned() {
-            return mIsSatelliteProvisioned;
+        public Boolean isSatelliteViaOemProvisioned() {
+            return mIsSatelliteViaOemProvisioned;
         }
 
         @Override
-        public boolean isSatelliteSupported() {
+        public boolean isSatelliteSupportedViaOem() {
             return true;
         }
 
@@ -477,10 +601,25 @@
             result.send(SatelliteManager.SATELLITE_RESULT_SUCCESS, bundle);
         }
 
+        @Override
+        public boolean isSatelliteConnectedViaCarrierWithinHysteresisTime() {
+            return mIsSatelliteConnectedViaCarrierWithinHysteresisTime;
+        }
+
+        @Override
+        protected int getEnforcedEmergencyCallToSatelliteHandoverType() {
+            return INVALID_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE;
+        }
+
         public void setIsSatelliteCommunicationAllowed(boolean allowed) {
             mIsSatelliteCommunicationAllowed = allowed;
         }
 
+        public void setSatelliteConnectedViaCarrierWithinHysteresisTime(
+                boolean connectedViaCarrier) {
+            mIsSatelliteConnectedViaCarrierWithinHysteresisTime = connectedViaCarrier;
+        }
+
         public int getRegisterForSatelliteProvisionStateChangedCalls() {
             return mRegisterForSatelliteProvisionStateChangedCalls;
         }
@@ -489,8 +628,12 @@
             return mUnregisterForSatelliteProvisionStateChangedCalls;
         }
 
+        public void setIsSatelliteViaOemProvisioned(boolean provisioned) {
+            mIsSatelliteViaOemProvisioned = provisioned;
+        }
+
         public void sendProvisionStateChangedEvent(int subId, boolean provisioned) {
-            mIsSatelliteProvisioned = provisioned;
+            mIsSatelliteViaOemProvisioned = provisioned;
             Set<ISatelliteProvisionStateCallback> perSubscriptionCallbacks =
                     mProvisionStateChangedCallbacks.get(subId);
             if (perSubscriptionCallbacks != null) {
@@ -507,7 +650,7 @@
 
     private static class TestImsManager extends ImsManager {
 
-        private final Set<RegistrationManager.RegistrationCallback> mCallbacks;
+        private final List<RegistrationManager.RegistrationCallback> mCallbacks;
         private int mAddRegistrationCallbackCalls = 0;
         private int mRemoveRegistrationListenerCalls = 0;
 
@@ -518,7 +661,7 @@
                 SubscriptionManagerProxy subManagerProxy, SettingsProxy settingsProxy,
                 BinderCacheManager binderCacheManager) {
             super(context, phoneId, factory, subManagerProxy, settingsProxy, binderCacheManager);
-            mCallbacks = new HashSet<>();
+            mCallbacks = new ArrayList<>();
         }
 
         @Override
@@ -535,7 +678,9 @@
             }
 
             callback.setExecutor(executor);
-            mCallbacks.add(callback);
+            if (!mCallbacks.contains(callback)) {
+                mCallbacks.add(callback);
+            }
         }
 
         @Override
@@ -556,20 +701,24 @@
             return mRemoveRegistrationListenerCalls;
         }
 
-        public void sendImsRegistrationStateChangedEvent(boolean registered) {
+        public void sendImsRegistrationStateChangedEvent(int callbackIndex, boolean registered) {
+            if (callbackIndex < 0 || callbackIndex >= mCallbacks.size()) {
+                throw new IndexOutOfBoundsException("sendImsRegistrationStateChangedEvent: invalid"
+                        + "callbackIndex=" + callbackIndex
+                        + ", mCallbacks.size=" + mCallbacks.size());
+            }
+            RegistrationManager.RegistrationCallback callback = mCallbacks.get(callbackIndex);
             if (registered) {
-                for (RegistrationManager.RegistrationCallback callback : mCallbacks) {
-                    callback.onRegistered(null);
-                }
+                callback.onRegistered(null);
             } else {
-                for (RegistrationManager.RegistrationCallback callback : mCallbacks) {
-                    callback.onUnregistered(null);
-                }
+                callback.onUnregistered(null);
             }
         }
     }
 
     private static class TestSOSMessageRecommender extends SatelliteSOSMessageRecommender {
+        private ComponentName mSmsAppComponent = new ComponentName(
+                DEFAULT_SATELLITE_MESSAGING_PACKAGE, DEFAULT_SATELLITE_MESSAGING_CLASS);
 
         /**
          * Create an instance of SatelliteSOSMessageRecommender.
@@ -581,9 +730,15 @@
          *                            null only in unit tests.
          * @param timeoutMillis       The timeout duration of the timer.
          */
-        TestSOSMessageRecommender(Looper looper, SatelliteController satelliteController,
-                ImsManager imsManager, long timeoutMillis) {
-            super(looper, satelliteController, imsManager, timeoutMillis);
+        TestSOSMessageRecommender(Context context, Looper looper,
+                SatelliteController satelliteController, ImsManager imsManager,
+                long timeoutMillis) {
+            super(context, looper, satelliteController, imsManager, timeoutMillis);
+        }
+
+        @Override
+        protected ComponentName getDefaultSmsApp() {
+            return mSmsAppComponent;
         }
 
         public boolean isTimerStarted() {
@@ -594,28 +749,66 @@
             return mCountOfTimerStarted;
         }
 
-        public void sendServiceStateChangedEvent(@ServiceState.RegState int state) {
-            ServiceState serviceState = new ServiceState();
-            serviceState.setState(state);
-            sendMessage(obtainMessage(EVENT_CELLULAR_SERVICE_STATE_CHANGED,
-                    new AsyncResult(null, serviceState, null)));
+        public void sendServiceStateChangedEvent() {
+            sendMessage(obtainMessage(EVENT_SERVICE_STATE_CHANGED));
         }
     }
 
     private static class TestConnection extends Connection {
-        private final Set<String> mSentEvents;
+        private String mSentEvent = null;
+        private Bundle mExtras = null;
         TestConnection(String callId) {
             setTelecomCallId(callId);
-            mSentEvents = new HashSet<>();
         }
 
         @Override
         public void sendConnectionEvent(String event, Bundle extras) {
-            mSentEvents.add(event);
+            mSentEvent = event;
+            mExtras = extras;
+        }
+
+        public boolean isEventSent(String event, int handoverType, String packageName,
+                String className, String action) {
+            if (mSentEvent == null || mExtras == null) {
+                return false;
+            }
+
+            PendingIntent pendingIntent = mExtras.getParcelable(
+                    EXTRA_EMERGENCY_CALL_TO_SATELLITE_LAUNCH_INTENT, PendingIntent.class);
+            Intent intent = pendingIntent.getIntent();
+            if (!TextUtils.equals(event, mSentEvent) || handoverType != mExtras.getInt(
+                    EXTRA_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE)
+                    || !TextUtils.equals(packageName, intent.getComponent().getPackageName())
+                    || !TextUtils.equals(className, intent.getComponent().getClassName())
+                    || !TextUtils.equals(action, intent.getAction())) {
+                return false;
+            }
+            return true;
+        }
+
+        public boolean isEventWithoutLaunchIntentSent(String event, int handoverType) {
+            if (mSentEvent == null || mExtras == null) {
+                return false;
+            }
+
+            PendingIntent pendingIntent = mExtras.getParcelable(
+                    EXTRA_EMERGENCY_CALL_TO_SATELLITE_LAUNCH_INTENT, PendingIntent.class);
+            if (!TextUtils.equals(event, mSentEvent) || handoverType != mExtras.getInt(
+                    EXTRA_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE) || pendingIntent != null) {
+                return false;
+            }
+
+            return true;
         }
 
         public boolean isEventSent(String event) {
-            return mSentEvents.contains(event);
+            if (mSentEvent == null) {
+                return false;
+            }
+            if (!TextUtils.equals(event, mSentEvent)) {
+                return false;
+            }
+            return true;
         }
     }
 }
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 230f147..1d320a3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
@@ -42,7 +42,6 @@
 import org.junit.Test;
 
 public class UiccSlotTest extends TelephonyTest {
-    private UiccSlot mUiccSlot;
     private UiccSlotTestHandlerThread mTestHandlerThread;
     private Handler mTestHandler;
 
@@ -263,7 +262,7 @@
 
         // assert on updated values
         assertTrue(mUiccSlot.isActive());
-        assertEquals(mUiccSlot.getMinimumVoltageClass(), UiccSlot.VOLTAGE_CLASS_A);
+        assertEquals(UiccSlot.VOLTAGE_CLASS_A, mUiccSlot.getMinimumVoltageClass());
     }
 
     @Test
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 7e51bad..d5b6ccb 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
@@ -50,7 +50,6 @@
     private static final String PROVISIONING_PACKAGE_NAME = "test.provisioning.package";
 
     // Mocked classes
-    private Context mContext;
     private Resources mResources;
 
     private IccCardStatus makeCardStatus(CardState state) {
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 b6dd7bd..c38be60 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
@@ -52,7 +52,6 @@
 
     private static class ResultCaptor<T> extends AsyncResultCallback<T> {
         public T result;
-        public Throwable exception;
 
         private CountDownLatch mLatch;
 
@@ -68,7 +67,6 @@
 
         @Override
         public void onException(Throwable e) {
-            exception = e;
             mLatch.countDown();
         }
     }