Merge "changed the name to onSatelliteEntitlementStatus." into main
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 04e3706..b2548f1 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -169,9 +169,8 @@
     <!-- Needed to register for UWB state changes for satellite communication -->
     <uses-permission android:name="android.permission.UWB_PRIVILEGED"/>
 
-    <permission android:name="com.android.phone.permission.ACCESS_LAST_KNOWN_CELL_ID"
-                android:label="Access last known cell identity."
-                android:protectionLevel="signature"/>
+    <!-- Needed to bind the domain selection service. -->
+    <uses-permission android:name="android.permission.BIND_DOMAIN_SELECTION_SERVICE" />
 
     <application android:name="PhoneApp"
             android:persistent="true"
@@ -628,5 +627,13 @@
             android:multiprocess="false"
             android:singleUser="true"
             android:writePermission="android.permission.MODIFY_PHONE_STATE"/>
+
+        <service android:name="com.android.services.telephony.domainselection.TelephonyDomainSelectionService"
+            android:exported="true"
+            android:permission="android.permission.BIND_DOMAIN_SELECTION_SERVICE">
+            <intent-filter>
+                <action android:name="android.telephony.DomainSelectionService"/>
+            </intent-filter>
+        </service>
     </application>
 </manifest>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index c52cb86..76ae668 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -661,7 +661,7 @@
     <string name="description_dial_button" msgid="8614631902795087259">"شماره گیری"</string>
     <string name="description_dialpad_button" msgid="7395114120463883623">"نمایش صفحه شماره گیری"</string>
     <string name="pane_title_emergency_dialpad" msgid="3627372514638694401">"صفحه شماره‌گیری اضطراری"</string>
-    <string name="voicemail_visual_voicemail_switch_title" msgid="6610414098912832120">"پست صوتی دیداری"</string>
+    <string name="voicemail_visual_voicemail_switch_title" msgid="6610414098912832120">"پست صوتی تصویری"</string>
     <string name="voicemail_set_pin_dialog_title" msgid="7005128605986960003">"تنظیم پین"</string>
     <string name="voicemail_change_pin_dialog_title" msgid="4633077715231764435">"تغییر پین"</string>
     <string name="preference_category_ringtone" msgid="8787281191375434976">"آهنگ‌ زنگ و لرزش"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index fa6e2b4..011b30b 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -466,7 +466,7 @@
     <string name="get_pin2" msgid="4221654606863196332">"PIN2 ಅನ್ನು ಟೈಪ್‌ ಮಾಡಿ"</string>
     <string name="name" msgid="1347432469852527784">"ಹೆಸರು"</string>
     <string name="number" msgid="1564053487748491000">"ಸಂಖ್ಯೆ"</string>
-    <string name="save" msgid="983805790346099749">"ಉಳಿಸು"</string>
+    <string name="save" msgid="983805790346099749">"ಸೇವ್ ಮಾಡಿ"</string>
     <string name="add_fdn_contact" msgid="1169713422306640887">"ಸ್ಥಿರ ಡಯಲಿಂಗ್ ಸಂಖ್ಯೆಯನ್ನು ಸೇರಿಸಿ"</string>
     <string name="adding_fdn_contact" msgid="3112531600824361259">"ಸ್ಥಿರ ಡಯಲಿಂಗ್‌‌ ಸಂಖ್ಯೆಯನ್ನು ಸೇರಿಸಲಾಗುತ್ತಿದೆ…"</string>
     <string name="fdn_contact_added" msgid="2840016151693394596">"ಸ್ಥಿರ ಡಯಲಿಂಗ್ ಸಂಖ್ಯೆಯನ್ನು ಸೇರಿಸಲಾಗಿದೆ."</string>
diff --git a/res/values/config.xml b/res/values/config.xml
index dcfa364..7cd4e18 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -337,7 +337,8 @@
         <item>de</item>
     </string-array>
 
-    <!-- Flag specifying whether the AOSP domain selection is enabled or
-         the device should fallback to the modem based domain selection architecture. -->
-    <bool name="config_enable_aosp_domain_selection">false</bool>
+    <!-- The component name(a flattened ComponentName string) for the telephony domain selection
+         service. The device should fallback to the modem based domain selection architecture
+         if this is not configured. -->
+    <string name="config_domain_selection_service_component_name" translatable="false"></string>
 </resources>
diff --git a/src/com/android/phone/DiagnosticDataCollector.java b/src/com/android/phone/DiagnosticDataCollector.java
index e997270..bdd9ce9 100644
--- a/src/com/android/phone/DiagnosticDataCollector.java
+++ b/src/com/android/phone/DiagnosticDataCollector.java
@@ -16,7 +16,6 @@
 
 package com.android.phone;
 
-import android.annotation.AnyThread;
 import android.annotation.NonNull;
 import android.annotation.WorkerThread;
 import android.os.DropBoxManager;
@@ -25,7 +24,6 @@
 import android.telephony.AnomalyReporter;
 import android.telephony.TelephonyManager;
 import android.util.Log;
-import java.util.UUID;
 
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -34,6 +32,7 @@
 import java.util.Arrays;
 import java.util.Date;
 import java.util.Locale;
+import java.util.UUID;
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
 
@@ -84,7 +83,7 @@
             persistTelecomState(dc, tag);
         }
         if (edp.isLogcatCollectionEnabled()) {
-            persistLogcat(dc, tag, edp.getLogcatStartTime());
+            persistLogcat(dc, tag, edp.getLogcatCollectionStartTimeMillis());
         }
     }
 
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 76cf979..7fba651 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -83,7 +83,6 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.phone.settings.SettingsConstants;
 import com.android.phone.vvm.CarrierVvmPackageInstalledReceiver;
-import com.android.services.telephony.domainselection.TelephonyDomainSelectionService;
 import com.android.services.telephony.rcs.TelephonyRcsService;
 
 import java.io.FileDescriptor;
@@ -166,7 +165,6 @@
     public ImsStateCallbackController mImsStateCallbackController;
     public ImsProvisioningController mImsProvisioningController;
     CarrierConfigLoader configLoader;
-    TelephonyDomainSelectionService mDomainSelectionService;
 
     private Phone phoneInEcm;
 
@@ -496,8 +494,9 @@
             // Create DomainSelectionResolver always, but it MUST be initialized only when
             // the device supports AOSP domain selection architecture and
             // has new IRadio that supports its related HAL APIs.
-            DomainSelectionResolver.make(this,
-                    getResources().getBoolean(R.bool.config_enable_aosp_domain_selection));
+            String dssComponentName = getResources().getString(
+                    R.string.config_domain_selection_service_component_name);
+            DomainSelectionResolver.make(this, dssComponentName);
 
             // Initialize the telephony framework
             mFeatureFlags = new FeatureFlagsImpl();
@@ -506,8 +505,7 @@
             // Initialize the DomainSelectionResolver after creating the Phone instance
             // to check the Radio HAL version.
             if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
-                mDomainSelectionService = new TelephonyDomainSelectionService(this);
-                DomainSelectionResolver.getInstance().initialize(mDomainSelectionService);
+                DomainSelectionResolver.getInstance().initialize();
                 // Initialize EmergencyStateTracker if domain selection is supported
                 boolean isSuplDdsSwitchRequiredForEmergencyCall = getResources()
                         .getBoolean(R.bool.config_gnss_supl_requires_default_data_for_emergency);
@@ -1390,9 +1388,6 @@
             e.printStackTrace();
         }
         pw.decreaseIndent();
-        if (mDomainSelectionService != null) {
-            mDomainSelectionService.dump(fd, pw, args);
-        }
         pw.decreaseIndent();
         if (mFeatureFlags.reorganizeRoamingNotification()) {
             pw.println("mShownNotificationReasons=" + mShownNotificationReasons);
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 5fb2d57..a4e9355 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -12573,8 +12573,9 @@
      * @return {@CellIdentity} last known cell identity {@CellIdentity}.
      *
      * Require {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and
-     * com.android.phone.permission.ACCESS_LAST_KNOWN_CELL_ID, otherwise throws
+     * {@link android.Manifest.permission#ACCESS_LAST_KNOWN_CELL_ID}, otherwise throws
      * SecurityException.
+     *
      * If there is current registered network this value will be same as the registered cell
      * identity. If the device goes out of service the previous cell identity is cached and
      * will be returned. If the cache age of the Cell identity is more than 24 hours
@@ -12875,11 +12876,15 @@
             long logcatStartTimestampMillis, boolean enableTelecomDump,
             boolean enableTelephonyDump) {
         DropBoxManager db = mApp.getSystemService(DropBoxManager.class);
-        TelephonyManager.EmergencyCallDiagnosticParams edp =
-                new TelephonyManager.EmergencyCallDiagnosticParams();
-        edp.setLogcatCollection(enableLogcat, logcatStartTimestampMillis);
-        edp.setTelephonyDumpSysCollection(enableTelephonyDump);
-        edp.setTelecomDumpSysCollection(enableTelecomDump);
+        TelephonyManager.EmergencyCallDiagnosticParams.Builder edpBuilder =
+                new TelephonyManager.EmergencyCallDiagnosticParams.Builder();
+        edpBuilder
+                .setTelecomDumpSysCollectionEnabled(enableTelecomDump)
+                .setTelephonyDumpSysCollectionEnabled(enableTelephonyDump);
+        if (enableLogcat) {
+            edpBuilder.setLogcatCollectionStartTimeMillis(logcatStartTimestampMillis);
+        }
+        TelephonyManager.EmergencyCallDiagnosticParams edp = edpBuilder.build();
         Log.d(LOG_TAG, "persisting with Params " + edp.toString());
         DiagnosticDataCollector ddc = new DiagnosticDataCollector(Runtime.getRuntime(),
                 Executors.newCachedThreadPool(), db,
@@ -13003,34 +13008,41 @@
     public void requestSatelliteEnabled(int subId, boolean enableSatellite, boolean enableDemoMode,
             @NonNull IIntegerConsumer callback) {
         enforceSatelliteCommunicationPermission("requestSatelliteEnabled");
-        ResultReceiver resultReceiver = new ResultReceiver(mMainThreadHandler) {
-            @Override
-            protected void onReceiveResult(int resultCode, Bundle resultData) {
-                Log.d(LOG_TAG, "Satellite access restriction resultCode=" + resultCode
-                        + ", resultData=" + resultData);
-                boolean isAllowed = false;
-                Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
-                if (resultCode == SATELLITE_RESULT_SUCCESS) {
-                    if (resultData != null
-                            && resultData.containsKey(KEY_SATELLITE_COMMUNICATION_ALLOWED)) {
-                        isAllowed = resultData.getBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED);
+        if (enableSatellite) {
+            ResultReceiver resultReceiver = new ResultReceiver(mMainThreadHandler) {
+                @Override
+                protected void onReceiveResult(int resultCode, Bundle resultData) {
+                    Log.d(LOG_TAG, "Satellite access restriction resultCode=" + resultCode
+                            + ", resultData=" + resultData);
+                    boolean isAllowed = false;
+                    Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(
+                            callback::accept);
+                    if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                        if (resultData != null
+                                && resultData.containsKey(KEY_SATELLITE_COMMUNICATION_ALLOWED)) {
+                            isAllowed = resultData.getBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED);
+                        } else {
+                            loge("KEY_SATELLITE_COMMUNICATION_ALLOWED does not exist.");
+                        }
                     } else {
-                        loge("KEY_SATELLITE_COMMUNICATION_ALLOWED does not exist.");
+                        result.accept(resultCode);
+                        return;
                     }
-                } else {
-                    result.accept(resultCode);
-                    return;
+                    if (isAllowed) {
+                        mSatelliteController.requestSatelliteEnabled(
+                                subId, enableSatellite, enableDemoMode, callback);
+                    } else {
+                        result.accept(SATELLITE_RESULT_ACCESS_BARRED);
+                    }
                 }
-                if (isAllowed) {
-                    mSatelliteController.requestSatelliteEnabled(
-                            subId, enableSatellite, enableDemoMode, callback);
-                } else {
-                    result.accept(SATELLITE_RESULT_ACCESS_BARRED);
-                }
-            }
-        };
-        mSatelliteAccessController.requestIsSatelliteCommunicationAllowedForCurrentLocation(
-                subId, resultReceiver);
+            };
+            mSatelliteAccessController.requestIsSatelliteCommunicationAllowedForCurrentLocation(
+                    subId, resultReceiver);
+        } else {
+            // No need to check if satellite is allowed at current location when disabling satellite
+            mSatelliteController.requestSatelliteEnabled(
+                    subId, enableSatellite, enableDemoMode, callback);
+        }
     }
 
     /**
@@ -13684,6 +13696,25 @@
     }
 
     /**
+     * This API can be used in only testing to override oem-enabled satellite provision status.
+     *
+     * @param reset {@code true} mean the overriding status should not be used, {@code false}
+     *              otherwise.
+     * @param isProvisioned The overriding provision status.
+     * @return {@code true} if the provision status is set successfully, {@code false} otherwise.
+     */
+    public boolean setOemEnabledSatelliteProvisionStatus(boolean reset, boolean isProvisioned) {
+        Log.d(LOG_TAG, "setOemEnabledSatelliteProvisionStatus - reset=" + reset
+                + ", isProvisioned=" + isProvisioned);
+        TelephonyPermissions.enforceShellOnly(
+                Binder.getCallingUid(), "setOemEnabledSatelliteProvisionStatus");
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
+                SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+                "setOemEnabledSatelliteProvisionStatus");
+        return mSatelliteController.setOemEnabledSatelliteProvisionStatus(reset, isProvisioned);
+    }
+
+    /**
      * This API should be used by only CTS tests to forcefully set telephony country codes.
      *
      * @return {@code true} if the country code is set successfully, {@code false} otherwise.
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index 80b7cf6..f7a3640d 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -197,6 +197,8 @@
     private static final String SET_COUNTRY_CODES = "set-country-codes";
     private static final String SET_SATELLITE_ACCESS_CONTROL_OVERLAY_CONFIGS =
             "set-satellite-access-control-overlay-configs";
+    private static final String SET_OEM_ENABLED_SATELLITE_PROVISION_STATUS =
+            "set-oem-enabled-satellite-provision-status";
     private static final String SET_SHOULD_SEND_DATAGRAM_TO_MODEM_IN_DEMO_MODE =
             "set-should-send-datagram-to-modem-in-demo-mode";
 
@@ -398,6 +400,8 @@
                 return handleSetSatelliteAccessControlOverlayConfigs();
             case SET_COUNTRY_CODES:
                 return handleSetCountryCodes();
+            case SET_OEM_ENABLED_SATELLITE_PROVISION_STATUS:
+                return handleSetOemEnabledSatelliteProvisionStatus();
             default: {
                 return handleDefaultCommands(cmd);
             }
@@ -824,6 +828,10 @@
         pw.println("      -c: the cached network country code ISOs.");
         pw.println("      -l: the location country code ISO.");
         pw.println("      -t: the update timestamp nanos of the location country code.");
+        pw.println("  set-oem-enabled-satellite-provision-status [-p true/false]");
+        pw.println("    Sets the OEM-enabled satellite provision status. Options are:");
+        pw.println("      -p: the overriding satellite provision status. If no option is ");
+        pw.println("          specified, reset the overridden provision status.");
     }
 
     private void onHelpImei() {
@@ -3533,6 +3541,43 @@
         return 0;
     }
 
+    private int handleSetOemEnabledSatelliteProvisionStatus() {
+        PrintWriter errPw = getErrPrintWriter();
+        boolean isProvisioned = false;
+        boolean reset = true;
+
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "-p": {
+                    try {
+                        isProvisioned = Boolean.parseBoolean(getNextArgRequired());
+                        reset = false;
+                    } catch (Exception e) {
+                        errPw.println("setOemEnabledSatelliteProvisionStatus requires a boolean "
+                                + "after -p indicating provision status");
+                        return -1;
+                    }
+                }
+            }
+        }
+        Log.d(LOG_TAG, "setOemEnabledSatelliteProvisionStatus: reset=" + reset
+                + ", isProvisioned=" + isProvisioned);
+
+        try {
+            boolean result = mInterface.setOemEnabledSatelliteProvisionStatus(reset, isProvisioned);
+            if (VDBG) {
+                Log.v(LOG_TAG, "setOemEnabledSatelliteProvisionStatus result = " + result);
+            }
+            getOutPrintWriter().println(result);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "setOemEnabledSatelliteProvisionStatus: error = " + e.getMessage());
+            errPw.println("Exception: " + e.getMessage());
+            return -1;
+        }
+        return 0;
+    }
+
     /**
      * Sample inputStr = "US,UK,CA;2,1,3"
      * Sample output: {[US,2], [UK,1], [CA,3]}
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index ea29b77..efa5278 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -64,6 +64,7 @@
 import com.android.internal.telephony.ExponentialBackoff;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.subscription.SubscriptionManagerService;
 import com.android.phone.PhoneGlobals;
 import com.android.phone.PhoneUtils;
@@ -465,6 +466,15 @@
             mIsUsingSimCallManager = isCarrierUsingSimCallManager();
             mIsShowPreciseFailedCause = isCarrierShowPreciseFailedCause();
 
+            // Set CAPABILITY_EMERGENCY_CALLS_ONLY flag if either
+            // - Carrier config overrides subscription is not voice capable, or
+            // - Resource config overrides it be emergency_calls_only
+            // TODO(b/316183370:): merge the two cases when clearing up flag
+            if (Flags.dataOnlyServiceAllowEmergencyCallOnly()) {
+                if (!isSubscriptionVoiceCapableByCarrierConfig()) {
+                    capabilities |= PhoneAccount.CAPABILITY_EMERGENCY_CALLS_ONLY;
+                }
+            }
             if (isEmergency && mContext.getResources().getBoolean(
                     R.bool.config_emergency_account_emergency_calls_only)) {
                 capabilities |= PhoneAccount.CAPABILITY_EMERGENCY_CALLS_ONLY;
@@ -804,6 +814,21 @@
         }
 
         /**
+         * @return true if the subscription is voice capable by the carrier config.
+         */
+        private boolean isSubscriptionVoiceCapableByCarrierConfig() {
+            PersistableBundle b =
+                    PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+            if (b == null) {
+                return true; // For any abnormal case, we assume subscription is voice capable
+            }
+            final int[] serviceCapabilities = b.getIntArray(
+                    CarrierConfigManager.KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY);
+            return Arrays.stream(serviceCapabilities).anyMatch(
+                    i -> i == SubscriptionManager.SERVICE_CAPABILITY_VOICE);
+        }
+
+        /**
          * Receives callback from {@link PstnPhoneCapabilitiesNotifier} when the video capabilities
          * have changed.
          *
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 62c573d..2070629 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -642,6 +642,13 @@
                 }
             };
 
+    private void clearNormalCallDomainSelectionConnection() {
+        if (mDomainSelectionConnection != null) {
+            mDomainSelectionConnection.finishSelection();
+            mDomainSelectionConnection = null;
+        }
+    }
+
     /**
      * A listener for calls.
      */
@@ -654,17 +661,15 @@
                     if (c != null) {
                         switch(c.getState()) {
                             case Connection.STATE_ACTIVE: {
-                                Log.d(LOG_TAG, "Call State->ACTIVE."
-                                        + "Clearing DomainSelectionConnection");
-                                if (mDomainSelectionConnection != null) {
-                                    mDomainSelectionConnection.finishSelection();
-                                    mDomainSelectionConnection = null;
-                                }
+                                clearNormalCallDomainSelectionConnection();
                                 mNormalCallConnection = null;
                             }
                             break;
 
                             case Connection.STATE_DISCONNECTED: {
+                                // Clear connection if the call state changes from
+                                // DIALING -> DISCONNECTED without ACTIVE State.
+                                clearNormalCallDomainSelectionConnection();
                                 c.removeTelephonyConnectionListener(mNormalCallConnectionListener);
                             }
                             break;
@@ -2308,6 +2313,7 @@
                     mNormalCallConnection.setTelephonyConnectionDisconnected(mDisconnectCauseFactory
                             .toTelecomDisconnectCause(telephonyDisconnectCause,
                                     "Connection is null", phone.getPhoneId()));
+                    clearNormalCallDomainSelectionConnection();
                     mNormalCallConnection.close();
                     return;
                 }
@@ -2335,10 +2341,7 @@
                             e.getMessage(), phone.getPhoneId()));
             mNormalCallConnection.close();
         }
-        if (mDomainSelectionConnection != null) {
-            mDomainSelectionConnection.finishSelection();
-            mDomainSelectionConnection = null;
-        }
+        clearNormalCallDomainSelectionConnection();
         mNormalCallConnection = null;
     }
 
@@ -2579,10 +2582,7 @@
                             && extraCode == ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY)) {
                 // clear normal call domain selector
                 c.removeTelephonyConnectionListener(mNormalCallConnectionListener);
-                if (mDomainSelectionConnection != null) {
-                    mDomainSelectionConnection.finishSelection();
-                    mDomainSelectionConnection = null;
-                }
+                clearNormalCallDomainSelectionConnection();
                 mNormalCallConnection = null;
 
                 onEmergencyRedial(c, c.getPhone().getDefaultPhone());
@@ -2778,10 +2778,7 @@
         }
 
         c.removeTelephonyConnectionListener(mTelephonyConnectionListener);
-        if (mDomainSelectionConnection != null) {
-            mDomainSelectionConnection.finishSelection();
-            mDomainSelectionConnection = null;
-        }
+        clearNormalCallDomainSelectionConnection();
         mNormalCallConnection = null;
         Log.d(LOG_TAG, "Reselect call domain not triggered.");
         return false;
@@ -3620,6 +3617,11 @@
     @VisibleForTesting
     public boolean isAvailableForEmergencyCalls(Phone phone,
             @EmergencyNumber.EmergencyCallRouting int routing) {
+        if (isCallDisallowedDueToSatellite(phone)) {
+            // Phone is connected to satellite due to which it is not preferred for emergency call.
+            return false;
+        }
+
         if (phone.getImsRegistrationTech() == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
             // When a Phone is registered to Cross-SIM calling, there must always be a Phone on the
             // other sub which is registered to cellular, so that must be selected.
@@ -4294,6 +4296,10 @@
         }
 
         ServiceState serviceState = phone.getServiceState();
+        if (serviceState == null) {
+            return false;
+        }
+
         if (!serviceState.isUsingNonTerrestrialNetwork()) {
             // Device is not connected to satellite
             return false;
diff --git a/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java b/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
index 570f942..30b9972 100644
--- a/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
+++ b/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
@@ -54,6 +54,7 @@
 import static android.telephony.PreciseDisconnectCause.EMERGENCY_PERM_FAILURE;
 import static android.telephony.PreciseDisconnectCause.EMERGENCY_TEMP_FAILURE;
 import static android.telephony.PreciseDisconnectCause.SERVICE_OPTION_NOT_AVAILABLE;
+import static android.telephony.TelephonyManager.DATA_CONNECTED;
 
 import android.annotation.NonNull;
 import android.content.Context;
@@ -211,13 +212,15 @@
     private final PowerManager.WakeLock mPartialWakeLock;
     private final CrossSimRedialingController mCrossSimRedialingController;
     private final CarrierConfigHelper mCarrierConfigHelper;
+    private final EmergencyCallbackModeHelper mEcbmHelper;
 
     /** Constructor. */
     public EmergencyCallDomainSelector(Context context, int slotId, int subId,
             @NonNull Looper looper, @NonNull ImsStateTracker imsStateTracker,
             @NonNull DestroyListener destroyListener,
             @NonNull CrossSimRedialingController csrController,
-            @NonNull CarrierConfigHelper carrierConfigHelper) {
+            @NonNull CarrierConfigHelper carrierConfigHelper,
+            @NonNull EmergencyCallbackModeHelper ecbmHelper) {
         super(context, slotId, subId, looper, imsStateTracker, destroyListener, TAG);
 
         mImsStateTracker.addBarringInfoListener(this);
@@ -228,6 +231,7 @@
 
         mCrossSimRedialingController = csrController;
         mCarrierConfigHelper = carrierConfigHelper;
+        mEcbmHelper = ecbmHelper;
         acquireWakeLock();
     }
 
@@ -630,7 +634,8 @@
             return;
         }
 
-        if (isWifiPreferred()) {
+        if (isWifiPreferred()
+                || isInEmergencyCallbackModeOnWlan()) {
             onWlanSelected();
             return;
         }
@@ -1545,6 +1550,12 @@
         }
     }
 
+    private boolean isInEmergencyCallbackModeOnWlan() {
+        return mEcbmHelper.isInEmergencyCallbackMode(getSlotId())
+                && mEcbmHelper.getTransportType(getSlotId()) == TRANSPORT_TYPE_WLAN
+                && mEcbmHelper.getDataConnectionState(getSlotId()) == DATA_CONNECTED;
+    }
+
     private void selectDomainForTestEmergencyNumber() {
         logi("selectDomainForTestEmergencyNumber");
         if (isImsRegisteredWithVoiceCapability()) {
diff --git a/src/com/android/services/telephony/domainselection/EmergencyCallbackModeHelper.java b/src/com/android/services/telephony/domainselection/EmergencyCallbackModeHelper.java
new file mode 100644
index 0000000..e42dfe7
--- /dev/null
+++ b/src/com/android/services/telephony/domainselection/EmergencyCallbackModeHelper.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.services.telephony.domainselection;
+
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL;
+import static android.telephony.SubscriptionManager.EXTRA_SLOT_INDEX;
+import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+import static android.telephony.TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED;
+import static android.telephony.TelephonyManager.EXTRA_PHONE_IN_ECM_STATE;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.os.SystemProperties;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.CarrierConfigManager;
+import android.telephony.PreciseDataConnectionState;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
+import android.util.ArrayMap;
+import android.util.Log;
+
+/** Helper class to cache emergency data connection state. */
+public class EmergencyCallbackModeHelper extends Handler {
+    private static final String TAG = "EmergencyCallbackModeHelper";
+    private static final boolean DBG = (SystemProperties.getInt("ro.debuggable", 0) == 1);
+
+    /**
+     * TelephonyCallback used to monitor ePDN state.
+     */
+    private static final class DataConnectionStateListener extends TelephonyCallback
+            implements TelephonyCallback.PreciseDataConnectionStateListener {
+
+        private final Handler mHandler;
+        private final TelephonyManager mTelephonyManager;
+        private final int mSubId;
+        private int mTransportType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+        private int mState = TelephonyManager.DATA_UNKNOWN;
+
+        DataConnectionStateListener(Handler handler, TelephonyManager tm, int subId) {
+            mHandler = handler;
+            mTelephonyManager = tm;
+            mSubId = subId;
+        }
+
+        @Override
+        public void onPreciseDataConnectionStateChanged(
+                @NonNull PreciseDataConnectionState dataConnectionState) {
+            ApnSetting apnSetting = dataConnectionState.getApnSetting();
+            if ((apnSetting == null)
+                    || ((apnSetting.getApnTypeBitmask() | ApnSetting.TYPE_EMERGENCY) == 0)) {
+                return;
+            }
+            mTransportType = dataConnectionState.getTransportType();
+            mState = dataConnectionState.getState();
+            Log.i(TAG, "onPreciseDataConnectionStateChanged ePDN state=" + mState
+                    + ", transport=" + mTransportType);
+        }
+
+        public void registerTelephonyCallback() {
+            TelephonyManager tm = mTelephonyManager.createForSubscriptionId(mSubId);
+            tm.registerTelephonyCallback(mHandler::post, this);
+        }
+
+        public void unregisterTelephonyCallback() {
+            mTelephonyManager.unregisterTelephonyCallback(this);
+        }
+
+        public int getSubId() {
+            return mSubId;
+        }
+
+        public int getTransportType() {
+            return mTransportType;
+        }
+
+        public int getState() {
+            return mState;
+        }
+    }
+
+    private final Context mContext;
+    private final TelephonyManager mTelephonyManager;
+    private final CarrierConfigManager mConfigManager;
+
+    private final ArrayMap<Integer, DataConnectionStateListener>
+            mDataConnectionStateListeners = new ArrayMap<>();
+
+    private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener =
+            (slotIndex, subId, carrierId, specificCarrierId) -> onCarrierConfigChanged(
+                    slotIndex, subId, carrierId);
+
+    /**
+     * Creates an instance.
+     *
+     * @param context The Context this is associated with.
+     * @param looper The Looper to run the EmergencyCallbackModeHelper.
+     */
+    public EmergencyCallbackModeHelper(@NonNull Context context, @NonNull Looper looper) {
+        super(looper);
+
+        mContext = context;
+        mTelephonyManager = context.getSystemService(TelephonyManager.class);
+        mConfigManager = context.getSystemService(CarrierConfigManager.class);
+        mConfigManager.registerCarrierConfigChangeListener(this::post,
+                mCarrierConfigChangeListener);
+    }
+
+    /**
+     * Returns whether it is in emergency callback mode.
+     *
+     * @param slotIndex The logical SIM slot index.
+     * @return true if it is in emergency callback mode.
+     */
+    public boolean isInEmergencyCallbackMode(int slotIndex) {
+        DataConnectionStateListener listener =
+                mDataConnectionStateListeners.get(Integer.valueOf(slotIndex));
+        if (listener == null) return false;
+
+        Intent intent = mContext.registerReceiver(null,
+                new IntentFilter(ACTION_EMERGENCY_CALLBACK_MODE_CHANGED));
+        if (intent != null
+                && ACTION_EMERGENCY_CALLBACK_MODE_CHANGED.equals(intent.getAction())) {
+            boolean inEcm = intent.getBooleanExtra(EXTRA_PHONE_IN_ECM_STATE, false);
+            int index = intent.getIntExtra(EXTRA_SLOT_INDEX, INVALID_SIM_SLOT_INDEX);
+            Log.i(TAG, "isInEmergencyCallbackMode inEcm=" + inEcm + ", slotIndex=" + index);
+            return inEcm && (slotIndex == index);
+        }
+        return false;
+    }
+
+    /**
+     * Returns the transport type of emergency data connection.
+     *
+     * @param slotIndex The logical SIM slot index.
+     * @return the transport type of emergency data connection.
+     */
+    public int getTransportType(int slotIndex) {
+        DataConnectionStateListener listener =
+                mDataConnectionStateListeners.get(Integer.valueOf(slotIndex));
+        if (listener == null) return AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+        Log.i(TAG, "getTransportType " + listener.getTransportType());
+        return listener.getTransportType();
+    }
+
+    /**
+     * Returns the data connection state.
+     *
+     * @param slotIndex The logical SIM slot index.
+     * @return the data connection state.
+     */
+    public int getDataConnectionState(int slotIndex) {
+        DataConnectionStateListener listener =
+                mDataConnectionStateListeners.get(Integer.valueOf(slotIndex));
+        if (listener == null) return TelephonyManager.DATA_UNKNOWN;
+        Log.i(TAG, "getDataConnectionState " + listener.getState());
+        return listener.getState();
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        switch(msg.what) {
+            default:
+                super.handleMessage(msg);
+                break;
+        }
+    }
+
+    private void onCarrierConfigChanged(int slotIndex, int subId, int carrierId) {
+        Log.i(TAG, "onCarrierConfigChanged slotIndex=" + slotIndex
+                + ", subId=" + subId + ", carrierId=" + carrierId);
+
+        if (slotIndex < 0) {
+            return;
+        }
+
+        PersistableBundle b = mConfigManager.getConfigForSubId(subId,
+                KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL);
+
+        if (b.getBoolean(KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL)) {
+            // ECBM supported
+            DataConnectionStateListener listener =
+                    mDataConnectionStateListeners.get(Integer.valueOf(slotIndex));
+
+            // Remove stale listener.
+            if (listener != null && listener.getSubId() != subId) {
+                listener.unregisterTelephonyCallback();
+                listener = null;
+            }
+
+            if (listener == null) {
+                listener = new DataConnectionStateListener(this, mTelephonyManager, subId);
+                listener.registerTelephonyCallback();
+                mDataConnectionStateListeners.put(Integer.valueOf(slotIndex), listener);
+                Log.i(TAG, "onCarrierConfigChanged register callback");
+            }
+        } else {
+            // ECBM not supported
+            DataConnectionStateListener listener =
+                    mDataConnectionStateListeners.get(Integer.valueOf(slotIndex));
+            if (listener != null) {
+                listener.unregisterTelephonyCallback();
+                mDataConnectionStateListeners.remove(Integer.valueOf(slotIndex));
+                Log.i(TAG, "onCarrierConfigChanged unregister callback");
+            }
+        }
+    }
+
+    /** Destroys the instance. */
+    public void destroy() {
+        if (DBG) Log.d(TAG, "destroy");
+        mConfigManager.unregisterCarrierConfigChangeListener(mCarrierConfigChangeListener);
+        mDataConnectionStateListeners.forEach((k, v) -> v.unregisterTelephonyCallback());
+    }
+}
diff --git a/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java b/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java
index f85dabe..cd70793 100644
--- a/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java
+++ b/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java
@@ -43,6 +43,7 @@
     private static final String LOG_TAG = "NCDS";
 
     private boolean mStopDomainSelection = true;
+    private boolean mDestroyed = false;
     private ServiceState mServiceState;
     private boolean mImsRegStateReceived;
     private boolean mMmTelCapabilitiesReceived;
@@ -116,12 +117,16 @@
         mImsStateTracker.removeImsStateListener(this);
         mSelectionAttributes = null;
         mTransportSelectorCallback = null;
+        destroy();
     }
 
     @Override
     public void destroy() {
-        finishSelection();
-        super.destroy();
+        logd("destroy");
+        if (!mDestroyed) {
+            mDestroyed = true;
+            super.destroy();
+        }
     }
 
     /**
diff --git a/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java b/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java
index 66894f7..fca5966 100644
--- a/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java
+++ b/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java
@@ -74,7 +74,8 @@
                 @NonNull ImsStateTracker imsStateTracker,
                 @NonNull DomainSelectorBase.DestroyListener listener,
                 @NonNull CrossSimRedialingController crossSimRedialingController,
-                @NonNull CarrierConfigHelper carrierConfigHelper);
+                @NonNull CarrierConfigHelper carrierConfigHelper,
+                @NonNull EmergencyCallbackModeHelper emergencyCallbackModeHelper);
     }
 
     private static final class DefaultDomainSelectorFactory implements DomainSelectorFactory {
@@ -84,7 +85,8 @@
                 @NonNull ImsStateTracker imsStateTracker,
                 @NonNull DomainSelectorBase.DestroyListener listener,
                 @NonNull CrossSimRedialingController crossSimRedialingController,
-                @NonNull CarrierConfigHelper carrierConfigHelper) {
+                @NonNull CarrierConfigHelper carrierConfigHelper,
+                @NonNull EmergencyCallbackModeHelper emergencyCallbackModeHelper) {
             DomainSelectorBase selector = null;
 
             logi("create-DomainSelector: slotId=" + slotId + ", subId=" + subId
@@ -96,7 +98,7 @@
                     if (isEmergency) {
                         selector = new EmergencyCallDomainSelector(context, slotId, subId, looper,
                                 imsStateTracker, listener, crossSimRedialingController,
-                                carrierConfigHelper);
+                                carrierConfigHelper, emergencyCallbackModeHelper);
                     } else {
                         selector = new NormalCallDomainSelector(context, slotId, subId, looper,
                                 imsStateTracker, listener);
@@ -192,7 +194,7 @@
 
     // Persistent Logging
     private static final LocalLog sEventLog = new LocalLog(20);
-    private final Context mContext;
+    private Context mContext;
     // Map of slotId -> ImsStateTracker
     private final SparseArray<ImsStateTracker> mImsStateTrackers = new SparseArray<>(2);
     private final List<DomainSelectorContainer> mDomainSelectorContainers = new ArrayList<>();
@@ -201,19 +203,28 @@
     private Handler mServiceHandler;
     private CrossSimRedialingController mCrossSimRedialingController;
     private CarrierConfigHelper mCarrierConfigHelper;
+    private EmergencyCallbackModeHelper mEmergencyCallbackModeHelper;
 
-    public TelephonyDomainSelectionService(Context context) {
-        this(context, ImsStateTracker::new, new DefaultDomainSelectorFactory(), null);
+    /** Default constructor. */
+    public TelephonyDomainSelectionService() {
+        this(ImsStateTracker::new, new DefaultDomainSelectorFactory(), null, null);
     }
 
     @VisibleForTesting
-    public TelephonyDomainSelectionService(Context context,
+    protected TelephonyDomainSelectionService(
             @NonNull ImsStateTrackerFactory imsStateTrackerFactory,
             @NonNull DomainSelectorFactory domainSelectorFactory,
-            @Nullable CarrierConfigHelper carrierConfigHelper) {
-        mContext = context;
+            @Nullable CarrierConfigHelper carrierConfigHelper,
+            @Nullable EmergencyCallbackModeHelper ecbmHelper) {
         mImsStateTrackerFactory = imsStateTrackerFactory;
         mDomainSelectorFactory = domainSelectorFactory;
+        mCarrierConfigHelper = carrierConfigHelper;
+    }
+
+    @Override
+    public void onCreate() {
+        logd("onCreate");
+        mContext = getApplicationContext();
 
         // Create a worker thread for this domain selection service.
         getExecutor();
@@ -231,9 +242,13 @@
             loge("Adding OnSubscriptionChangedListener failed");
         }
 
-        mCrossSimRedialingController = new CrossSimRedialingController(context, getLooper());
-        mCarrierConfigHelper = (carrierConfigHelper != null)
-                ? carrierConfigHelper : new CarrierConfigHelper(context, getLooper());
+        mCrossSimRedialingController = new CrossSimRedialingController(mContext, getLooper());
+        if (mCarrierConfigHelper == null) {
+            mCarrierConfigHelper = new CarrierConfigHelper(mContext, getLooper());
+        }
+        if (mEmergencyCallbackModeHelper == null) {
+            mEmergencyCallbackModeHelper = new EmergencyCallbackModeHelper(mContext, getLooper());
+        }
 
         logi("TelephonyDomainSelectionService created");
     }
@@ -282,6 +297,11 @@
             mCarrierConfigHelper = null;
         }
 
+        if (mEmergencyCallbackModeHelper != null) {
+            mEmergencyCallbackModeHelper.destroy();
+            mEmergencyCallbackModeHelper = null;
+        }
+
         if (mServiceHandler != null) {
             mServiceHandler.getLooper().quit();
             mServiceHandler = null;
@@ -304,7 +324,7 @@
         ImsStateTracker ist = getImsStateTracker(slotId);
         DomainSelectorBase selector = mDomainSelectorFactory.create(mContext, slotId, subId,
                 selectorType, isEmergency, getLooper(), ist, mDestroyListener,
-                mCrossSimRedialingController, mCarrierConfigHelper);
+                mCrossSimRedialingController, mCarrierConfigHelper, mEmergencyCallbackModeHelper);
 
         if (selector != null) {
             // Ensures that ImsStateTracker is started before selecting the domain if not started
@@ -313,15 +333,21 @@
             addDomainSelector(slotId, selectorType, isEmergency, selector);
         } else {
             loge("No proper domain selector: " + selectorTypeToString(selectorType));
-            callback.onSelectionTerminated(DisconnectCause.ERROR_UNSPECIFIED);
+            // Executed through the service handler to ensure that the callbacks are not called
+            // directly in this execution flow.
+            mServiceHandler.post(() ->
+                    callback.onSelectionTerminated(DisconnectCause.ERROR_UNSPECIFIED));
             return;
         }
 
-        // Notify the caller that the domain selector is created.
-        callback.onCreated(selector);
-
-        // Performs the domain selection.
-        selector.selectDomain(attr, callback);
+        // Executed through the service handler to ensure that the callbacks are not called
+        // directly in this execution flow.
+        mServiceHandler.post(() ->  {
+            // Notify the caller that the domain selector is created.
+            callback.onCreated(selector);
+            // Performs the domain selection.
+            selector.selectDomain(attr, callback);
+        });
     }
 
     /**
diff --git a/src/com/android/services/telephony/rcs/TransportSipMessageValidator.java b/src/com/android/services/telephony/rcs/TransportSipMessageValidator.java
index b15992e..3a8bdea 100644
--- a/src/com/android/services/telephony/rcs/TransportSipMessageValidator.java
+++ b/src/com/android/services/telephony/rcs/TransportSipMessageValidator.java
@@ -565,8 +565,8 @@
                         direction);
             } else {
                 //Message sending fail and there is no response.
-                mRcsStats.invalidatedMessageResult(mSubId, startLineSegments[0], direction,
-                        result.restrictedReason);
+                mRcsStats.invalidatedMessageResult(m.getCallIdParameter(), mSubId,
+                        startLineSegments[0], direction, result.restrictedReason);
             }
         } else if (SipMessageParsingUtils.isSipResponse(m.getStartLine())) {
             int statusCode = Integer.parseInt(startLineSegments[1]);
diff --git a/testapps/TestRcsApp/TestApp/AndroidManifest.xml b/testapps/TestRcsApp/TestApp/AndroidManifest.xml
index 35a0822..3ec9b69 100644
--- a/testapps/TestRcsApp/TestApp/AndroidManifest.xml
+++ b/testapps/TestRcsApp/TestApp/AndroidManifest.xml
@@ -56,6 +56,7 @@
         <activity android:name=".ContactListActivity" />
         <activity android:name=".ProvisioningActivity" />
         <activity android:name=".FileUploadActivity" />
+        <activity android:name=".carrierLock.CarrieLockModeListActivity" />
 
         <provider
             android:name=".util.ChatProvider"
@@ -117,6 +118,10 @@
             </intent-filter>
         </service>
 
+        <provider
+            android:name=".carrierLock.CarrierLockProvider"
+            android:authorities="com.sample.lockProvider"
+            android:exported="true" />
     </application>
 
 </manifest>
diff --git a/testapps/TestRcsApp/TestApp/res/layout/CarrierLockListLayout.xml b/testapps/TestRcsApp/TestApp/res/layout/CarrierLockListLayout.xml
new file mode 100644
index 0000000..f07c65c
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/layout/CarrierLockListLayout.xml
@@ -0,0 +1,77 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".MainActivity">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <Button
+            android:id="@+id/noLockMode"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/no_LockMode"
+            android:textAlignment="center"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/lockToVZW"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/no_LockTo_VZW"
+            android:textAlignment="center"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/lockToATT"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/no_LockTo_ATT"
+            android:textAlignment="center"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/lockToTMO"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/no_LockTo_TMO"
+            android:textAlignment="center"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/lockToKOODOS"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/no_LockTo_KOODOS"
+            android:textAlignment="center"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/lockToTELUS"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/no_LockTo_TELUS"
+            android:textAlignment="center"
+            android:textAllCaps="false" />
+    </LinearLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/testapps/TestRcsApp/TestApp/res/layout/activity_main.xml b/testapps/TestRcsApp/TestApp/res/layout/activity_main.xml
index 939feb0..ebf5508 100644
--- a/testapps/TestRcsApp/TestApp/res/layout/activity_main.xml
+++ b/testapps/TestRcsApp/TestApp/res/layout/activity_main.xml
@@ -74,6 +74,14 @@
             android:textAlignment="center"
             android:textAllCaps="false" />
 
+        <Button
+            android:id="@+id/setCarrierLockMode"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/setCarrierLockMode"
+            android:textAlignment="center"
+            android:textAllCaps="false"/>
+
         <TextView
             android:id="@+id/version_info"
             android:layout_width="match_parent"
diff --git a/testapps/TestRcsApp/TestApp/res/values/donottranslate_strings.xml b/testapps/TestRcsApp/TestApp/res/values/donottranslate_strings.xml
index f52b70d..b017139 100644
--- a/testapps/TestRcsApp/TestApp/res/values/donottranslate_strings.xml
+++ b/testapps/TestRcsApp/TestApp/res/values/donottranslate_strings.xml
@@ -72,6 +72,8 @@
     <string name="browse">Browse</string>
     <string name="upload">Upload</string>
     <string name="upload_file_gba">Upload File with GBA</string>
+    <string name="setCarrierLockMode">CarrierLock</string>
+
     <string name="invalid_parameters">Invalid Parameters</string>
     <string name="server">Server:</string>
     <string name="file_name">File Name:</string>
@@ -79,6 +81,13 @@
     <string name="file_empty">File is empty</string>
     <string name="version_info">Version: %s</string>
 
+    <string name="no_LockMode">NoLock/ UnLocked</string>
+    <string name="no_LockTo_VZW">Lock to Verizon</string>
+    <string name="no_LockTo_ATT">Lock to ATT</string>
+    <string name="no_LockTo_TMO">Lock to TMO</string>
+    <string name="no_LockTo_KOODOS">Lock to KOODO</string>
+    <string name="no_LockTo_TELUS">Lock to TELUS</string>
+
     <string-array name="rcs_profile">
         <item>UP_1.0</item>
         <item>UP_2.3</item>
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/MainActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/MainActivity.java
index 89c5268..5d2db73 100644
--- a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/MainActivity.java
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/MainActivity.java
@@ -29,6 +29,8 @@
 
 import androidx.appcompat.app.AppCompatActivity;
 
+import com.google.android.sample.rcsclient.carrierLock.CarrieLockModeListActivity;
+
 /** An activity to show function list. */
 public class MainActivity extends AppCompatActivity {
     private static final String TAG = "TestRcsApp.MainActivity";
@@ -39,6 +41,7 @@
     private Button mMessageClientButton;
     private Button mFileUploadButton;
     private TextView mVersionInfo;
+    private Button mCarrierLockModeListBtn;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -56,6 +59,7 @@
         mGbaButton = (Button) this.findViewById(R.id.gba);
         mFileUploadButton = findViewById(R.id.uploadFile);
         mVersionInfo = this.findViewById(R.id.version_info);
+        mCarrierLockModeListBtn = findViewById(R.id.setCarrierLockMode);
         mProvisionButton.setOnClickListener(view -> {
             Intent intent = new Intent(this, ProvisioningActivity.class);
             MainActivity.this.startActivity(intent);
@@ -90,6 +94,11 @@
                     appVersionName);
             mVersionInfo.setText(version);
         }
+
+        mCarrierLockModeListBtn.setOnClickListener(view -> {
+            Intent intent = new Intent(this, CarrieLockModeListActivity.class);
+            MainActivity.this.startActivity(intent);
+        });
     }
 
     @Override
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/carrierLock/CarrieLockModeListActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/carrierLock/CarrieLockModeListActivity.java
new file mode 100644
index 0000000..6547aeb
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/carrierLock/CarrieLockModeListActivity.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.sample.rcsclient.carrierLock;
+
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.google.android.sample.rcsclient.R;
+
+public class CarrieLockModeListActivity extends AppCompatActivity {
+
+    private final CarrierLockProvider mCarrierLockProvider = new CarrierLockProvider();
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.CarrierLockListLayout);
+
+        Button noLockModeBtn = this.findViewById(R.id.noLockMode);
+        assert noLockModeBtn != null;
+        noLockModeBtn.setOnClickListener(view -> {
+            mCarrierLockProvider.setLockMode(CarrierRestriction.UNLOCKED);
+            Toast.makeText(this, "Lock mode set to UNLOCKED", Toast.LENGTH_LONG).show();
+        });
+
+        Button vzwLockModeBtn = this.findViewById(R.id.lockToVZW);
+        assert vzwLockModeBtn != null;
+        vzwLockModeBtn.setOnClickListener(view -> {
+            mCarrierLockProvider.setLockMode(CarrierRestriction.LOCK_TO_VZW);
+            Toast.makeText(this, "Lock mode set to VZW", Toast.LENGTH_LONG).show();
+        });
+
+        Button attLockModeBtn = this.findViewById(R.id.lockToATT);
+        assert attLockModeBtn != null;
+        attLockModeBtn.setOnClickListener(view -> {
+            mCarrierLockProvider.setLockMode(CarrierRestriction.LOCK_TO_ATT);
+            Toast.makeText(this, "Lock mode set to ATT", Toast.LENGTH_LONG).show();
+        });
+
+        Button tmoLockModeBtn = this.findViewById(R.id.lockToTMO);
+        assert tmoLockModeBtn != null;
+        tmoLockModeBtn.setOnClickListener(view -> {
+            mCarrierLockProvider.setLockMode(CarrierRestriction.LOCK_TO_TMO);
+            Toast.makeText(this, "Lock mode set to TMO", Toast.LENGTH_LONG).show();
+        });
+
+        Button koodoLockModeBtn = this.findViewById(R.id.lockToKOODOS);
+        assert koodoLockModeBtn != null;
+        koodoLockModeBtn.setOnClickListener(view -> {
+            mCarrierLockProvider.setLockMode(CarrierRestriction.LOCK_TO_KOODO);
+            Toast.makeText(this, "Lock mode set to KOODO", Toast.LENGTH_LONG).show();
+        });
+
+        Button telusLockModeBtn = this.findViewById(R.id.lockToTELUS);
+        assert telusLockModeBtn != null;
+        telusLockModeBtn.setOnClickListener(view -> {
+            mCarrierLockProvider.setLockMode(CarrierRestriction.LOCK_TO_TELUS);
+            Toast.makeText(this, "Lock mode set to TELUS", Toast.LENGTH_LONG).show();
+        });
+    }
+}
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/carrierLock/CarrierLockProvider.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/carrierLock/CarrierLockProvider.java
new file mode 100644
index 0000000..8fa3cd6
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/carrierLock/CarrierLockProvider.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.sample.rcsclient.carrierLock;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringJoiner;
+
+public class CarrierLockProvider extends ContentProvider {
+
+    public static final String AUTHORITY = "com.sample.lockProvider";
+    public static final String TAG = "TestCarrierLockProvider";
+
+    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/carrierLock");
+    // content://com.sample.lockProvider/carrierLock
+
+    private static CarrierRestriction mLockMode = CarrierRestriction.UNLOCKED;
+    private static final ArrayList<Integer> mCarrierIds = new ArrayList<>();
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public Bundle call(String method, String args, Bundle extras) {
+        Bundle result = new Bundle();
+        Log.d(TAG, "call query STARTED on method = " + method);
+        switch (method) {
+            case "getCarrierRestrictionStatus":
+                try {
+                    if (mLockMode == CarrierRestriction.UNLOCKED) {
+                        result.putInt("restriction_status", 0); // Unlocked
+                    } else {
+                        result.putInt("restriction_status", 2); // Locked/Restricted
+                    }
+                    mCarrierIds.clear();
+                    Log.d(TAG, "Query come : Lock mode set to " + mLockMode);
+                    switch (mLockMode) {
+                        case UNLOCKED:
+                            // Do Nothing
+                            break;
+                        case LOCK_TO_VZW:
+                            mCarrierIds.add(1839);
+                            break;
+                        case LOCK_TO_ATT:
+                            mCarrierIds.add(1187);
+                            mCarrierIds.add(10021);
+                            mCarrierIds.add(2119);
+                            mCarrierIds.add(2120);
+                            mCarrierIds.add(1779);
+                            mCarrierIds.add(10028);
+                            break;
+                        case LOCK_TO_TMO:
+                            mCarrierIds.add(1);
+                            break;
+                        case LOCK_TO_KOODO:
+                            mCarrierIds.add(2020);
+                            break;
+                        case LOCK_TO_TELUS:
+                            mCarrierIds.add(1404);
+                            break;
+                        default:
+                            // Nothing
+                    }
+                    StringJoiner joiner = new StringJoiner(", ");
+                    if (!mCarrierIds.isEmpty()) {
+                        result.putIntegerArrayList("allowed_carrier_ids", mCarrierIds);
+                        for (Integer num : mCarrierIds) {
+                            joiner.add(num.toString());
+                        }
+                        result.putString("PrintableCarrierIds", joiner.toString());
+                        Log.d(TAG, "Locked to carrierIds = " + joiner.toString());
+                    } else {
+                        result.putString("allowed_carrier_ids", "");
+                        result.putString("PrintableCarrierIds", "");
+                    }
+
+                } catch (Exception e) {
+                    Log.e(TAG, " call :: query :: exception = " + e.getMessage());
+                }
+                return result;
+
+            case "getList:":
+                String list = String.valueOf(
+                        mCarrierIds.size());
+                result.putString("carrierList", list);
+                return result;
+            default:
+                return null;
+        }
+    }
+
+    private void updateLockValue(int lockValue) {
+        Log.d(TAG, "updateLockValue through ADB to = " + lockValue);
+        switch (lockValue) {
+            case 1:
+                mLockMode = CarrierRestriction.LOCK_TO_VZW;
+                break;
+            case 2:
+                mLockMode = CarrierRestriction.LOCK_TO_ATT;
+                break;
+            case 3:
+                mLockMode = CarrierRestriction.LOCK_TO_TMO;
+                break;
+            case 4:
+                mLockMode = CarrierRestriction.LOCK_TO_KOODO;
+                break;
+            case 5:
+                mLockMode = CarrierRestriction.LOCK_TO_TELUS;
+                break;
+            default:
+                mLockMode = CarrierRestriction.UNLOCKED;
+                break;
+        }
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        Log.d(TAG, "CarrierLockProvider Query");
+        return null;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        return "vnd.android.cursor.dir/vnd." + AUTHORITY + ".books";
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        Log.d(TAG, "CarrierLockProvider insert START");
+        assert values != null;
+        int newValue = values.getAsInteger("newValue");
+        updateLockValue(newValue);
+        return CONTENT_URI;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        return 0;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        return 0;
+    }
+
+    public void setLockMode(CarrierRestriction lockMode) {
+        mLockMode = lockMode;
+        Log.d(TAG, "Setting lockMode to " + mLockMode);
+    }
+}
\ No newline at end of file
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/carrierLock/CarrierRestriction.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/carrierLock/CarrierRestriction.java
new file mode 100644
index 0000000..34f9e7b
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/carrierLock/CarrierRestriction.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.sample.rcsclient.carrierLock;
+
+public enum CarrierRestriction {
+    UNLOCKED,
+    LOCK_TO_VZW,
+    LOCK_TO_ATT,
+    LOCK_TO_TMO,
+    LOCK_TO_KOODO,
+    LOCK_TO_TELUS
+}
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/TestSatelliteWrapper.java b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/TestSatelliteWrapper.java
index 792c984..cbbd621 100644
--- a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/TestSatelliteWrapper.java
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/TestSatelliteWrapper.java
@@ -23,7 +23,6 @@
 import android.os.OutcomeReceiver;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
-import android.telephony.satellite.SatelliteManager;
 import android.telephony.satellite.wrapper.NtnSignalStrengthCallbackWrapper;
 import android.telephony.satellite.wrapper.NtnSignalStrengthWrapper;
 import android.telephony.satellite.wrapper.SatelliteCapabilitiesCallbackWrapper;
@@ -41,7 +40,6 @@
 import java.util.concurrent.Executors;
 import java.util.stream.Collectors;
 
-
 /**
  * Activity related to SatelliteControl APIs for satellite.
  */
@@ -148,7 +146,7 @@
 
         try {
             mSatelliteManagerWrapper.requestNtnSignalStrength(mExecutor, receiver);
-        } catch (SecurityException | IllegalStateException ex) {
+        } catch (SecurityException ex) {
             String errorMessage = "requestNtnSignalStrength: " + ex.getMessage();
             Log.d(TAG, errorMessage);
             addLogMessage(errorMessage);
@@ -167,18 +165,10 @@
             mSatelliteManagerWrapper.registerForNtnSignalStrengthChanged(mExecutor,
                     mNtnSignalStrengthCallback);
         } catch (Exception ex) {
-            String errorMessage;
-            if (ex instanceof SatelliteManager.SatelliteException) {
-                errorMessage =
-                        "registerForNtnSignalStrengthChanged: " + translateResultCodeToString(
-                                ((SatelliteManager.SatelliteException) ex).getErrorCode());
-            } else {
-                errorMessage = "registerForNtnSignalStrengthChanged: " + ex.getMessage();
-            }
+            String errorMessage = "registerForNtnSignalStrengthChanged: " + ex.getMessage();
             Log.d(TAG, errorMessage);
             addLogMessage(errorMessage);
             mNtnSignalStrengthCallback = null;
-
         }
     }
 
@@ -317,6 +307,8 @@
                 return "SATELLITE_RESULT_REQUEST_IN_PROGRESS";
             case SatelliteManagerWrapper.SATELLITE_RESULT_MODEM_BUSY:
                 return "SATELLITE_RESULT_MODEM_BUSY";
+            case SatelliteManagerWrapper.SATELLITE_RESULT_ILLEGAL_STATE:
+                return "SATELLITE_RESULT_ILLEGAL_STATE";
             default:
                 return "INVALID CODE: " + result;
         }
diff --git a/tests/src/com/android/phone/DiagnosticDataCollectorTest.java b/tests/src/com/android/phone/DiagnosticDataCollectorTest.java
index e0d89bc..2ca04b9 100644
--- a/tests/src/com/android/phone/DiagnosticDataCollectorTest.java
+++ b/tests/src/com/android/phone/DiagnosticDataCollectorTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.Mockito.verify;
 
 import android.os.DropBoxManager;
+import android.os.SystemClock;
 import android.telephony.TelephonyManager;
 
 import org.junit.After;
@@ -94,9 +95,10 @@
 
     @Test
     public void testPersistForTelecomDumpsys() throws IOException, InterruptedException {
+        TelephonyManager.EmergencyCallDiagnosticParams.Builder callDiagnosticBuilder =
+                new TelephonyManager.EmergencyCallDiagnosticParams.Builder();
         TelephonyManager.EmergencyCallDiagnosticParams dp =
-                new TelephonyManager.EmergencyCallDiagnosticParams();
-        dp.setTelecomDumpSysCollection(true);
+                callDiagnosticBuilder.setTelecomDumpSysCollectionEnabled(true).build();
         mDiagnosticDataCollector.persistEmergencyDianosticData(mConfig, dp, "test_tag_telecom");
 
         verifyCmdAndDropboxTag(TELECOM_DUMPSYS_COMMAND, "test_tag_telecom", false);
@@ -104,9 +106,10 @@
 
     @Test
     public void testPersistForTelephonyDumpsys() throws IOException, InterruptedException {
+        TelephonyManager.EmergencyCallDiagnosticParams.Builder callDiagnosticBuilder =
+                new TelephonyManager.EmergencyCallDiagnosticParams.Builder();
         TelephonyManager.EmergencyCallDiagnosticParams dp =
-                new TelephonyManager.EmergencyCallDiagnosticParams();
-        dp.setTelephonyDumpSysCollection(true);
+                callDiagnosticBuilder.setTelephonyDumpSysCollectionEnabled(true).build();
         mDiagnosticDataCollector.persistEmergencyDianosticData(mConfig, dp, "test_tag_telephony");
 
         verifyCmdAndDropboxTag(TELEPHONY_DUMPSYS_COMMAND, "test_tag_telephony", false);
@@ -114,9 +117,11 @@
 
     @Test
     public void testPersistForLogcat() throws IOException, InterruptedException {
+        TelephonyManager.EmergencyCallDiagnosticParams.Builder callDiagnosticBuilder =
+                new TelephonyManager.EmergencyCallDiagnosticParams.Builder();
         TelephonyManager.EmergencyCallDiagnosticParams dp =
-                new TelephonyManager.EmergencyCallDiagnosticParams();
-        dp.setLogcatCollection(true, System.currentTimeMillis());
+                callDiagnosticBuilder.setLogcatCollectionStartTimeMillis(
+                        SystemClock.elapsedRealtime()).build();
         mDiagnosticDataCollector.persistEmergencyDianosticData(mConfig, dp, "test_tag_logcat");
 
         verifyCmdAndDropboxTag(LOGCAT_BINARY, "test_tag_logcat", true);
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 250d27c..39d66f1 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -3366,6 +3366,52 @@
     }
 
     @Test
+    public void testIsAvailableForEmergencyCallsUsingNonTerrestrialNetwork_enableFlag() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG);
+
+        Phone mockPhone = Mockito.mock(Phone.class);
+        NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+                .setIsNonTerrestrialNetwork(true)
+                .setAvailableServices(List.of(NetworkRegistrationInfo.SERVICE_TYPE_DATA))
+                .build();
+        ServiceState ss = new ServiceState();
+        ss.addNetworkRegistrationInfo(nri);
+        ss.setEmergencyOnly(true);
+        ss.setState(ServiceState.STATE_EMERGENCY_ONLY);
+        when(mockPhone.getServiceState()).thenReturn(ss);
+
+        assertFalse(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY));
+        assertFalse(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL));
+        assertFalse(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN));
+    }
+
+    @Test
+    public void testIsAvailableForEmergencyCallsUsingNonTerrestrialNetwork_disableFlag() {
+        mSetFlagsRule.disableFlags(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG);
+
+        Phone mockPhone = Mockito.mock(Phone.class);
+        NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+                .setIsNonTerrestrialNetwork(true)
+                .setAvailableServices(List.of(NetworkRegistrationInfo.SERVICE_TYPE_VOICE))
+                .build();
+        ServiceState ss = new ServiceState();
+        ss.addNetworkRegistrationInfo(nri);
+        ss.setEmergencyOnly(true);
+        ss.setState(ServiceState.STATE_EMERGENCY_ONLY);
+        when(mockPhone.getServiceState()).thenReturn(ss);
+
+        assertTrue(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY));
+        assertFalse(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL));
+        assertTrue(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN));
+    }
+
+    @Test
     public void testIsAvailableForEmergencyCallsForEmergencyRoutingInEmergencyOnly() {
         ServiceState mockService = Mockito.mock(ServiceState.class);
         when(mockService.isEmergencyOnly()).thenReturn(true);
diff --git a/tests/src/com/android/services/telephony/domainselection/CarrierConfigHelperTest.java b/tests/src/com/android/services/telephony/domainselection/CarrierConfigHelperTest.java
index 5d4fe17..8f51dab 100644
--- a/tests/src/com/android/services/telephony/domainselection/CarrierConfigHelperTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/CarrierConfigHelperTest.java
@@ -32,7 +32,6 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.content.SharedPreferences;
@@ -68,11 +67,11 @@
     private static final int SUB_1 = 1;
     private static final int TEST_SIM_CARRIER_ID = 1911;
 
-    @Mock private Context mContext;
     @Mock private SharedPreferences mSharedPreferences;
     @Mock private SharedPreferences.Editor mEditor;
     @Mock private Resources mResources;
 
+    private Context mContext;
     private HandlerThread mHandlerThread;
     private TestableLooper mLooper;
     private CarrierConfigHelper mCarrierConfigHelper;
diff --git a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
index 8cad6a4..119c980 100644
--- a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
@@ -16,6 +16,8 @@
 
 package com.android.services.telephony.domainselection;
 
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WLAN;
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
 import static android.telephony.AccessNetworkConstants.AccessNetworkType.EUTRAN;
 import static android.telephony.AccessNetworkConstants.AccessNetworkType.GERAN;
 import static android.telephony.AccessNetworkConstants.AccessNetworkType.NGRAN;
@@ -53,6 +55,7 @@
 import static android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_HOME;
 import static android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN;
 import static android.telephony.PreciseDisconnectCause.SERVICE_OPTION_NOT_AVAILABLE;
+import static android.telephony.TelephonyManager.DATA_CONNECTED;
 
 import static com.android.services.telephony.domainselection.EmergencyCallDomainSelector.MSG_MAX_CELLULAR_TIMEOUT;
 import static com.android.services.telephony.domainselection.EmergencyCallDomainSelector.MSG_NETWORK_SCAN_TIMEOUT;
@@ -69,6 +72,7 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -147,6 +151,7 @@
     @Mock private ProvisioningManager mProvisioningManager;
     @Mock private CrossSimRedialingController mCsrdCtrl;
     @Mock private CarrierConfigHelper mCarrierConfigHelper;
+    @Mock private EmergencyCallbackModeHelper mEcbmHelper;
     @Mock private Resources mResources;
 
     private Context mContext;
@@ -297,6 +302,26 @@
     }
 
     @Test
+    public void testDestroyed() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+
+        EmergencyRegResult regResult = getEmergencyRegResult(UTRAN, REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_CS,
+                true, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        mDomainSelector.destroy();
+        mDomainSelector.handleMessage(mDomainSelector.obtainMessage(Integer.MAX_VALUE));
+        unsolBarringInfoChanged(false);
+
+        verify(mTransportSelectorCallback, never()).onWwanSelected(any());
+    }
+
+    @Test
     public void testNoRedundantDomainSelectionFromInitialState() throws Exception {
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
@@ -484,6 +509,39 @@
     }
 
     @Test
+    public void testDefaultCombinedImsRegisteredSelectPsThenNotExtendedServiceRequestFails()
+            throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+                true, true, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsService();
+
+        verifyPsDialed();
+
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verifyCsDialed();
+
+        SelectionAttributes.Builder builder =
+                new SelectionAttributes.Builder(SLOT_0, SLOT_0_SUB_ID, SELECTOR_TYPE_CALLING)
+                .setEmergency(true)
+                .setEmergencyRegResult(regResult);
+        attr = builder.build();
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verifyScanPsPreferred();
+    }
+
+    @Test
     public void testDefaultCombinedImsNotRegisteredSelectCs() throws Exception {
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
@@ -1122,6 +1180,7 @@
         bundle.putBoolean(KEY_EMERGENCY_CALL_OVER_EMERGENCY_PDN_BOOL, true);
         when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
 
+        mResultConsumer = null;
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
@@ -1146,6 +1205,14 @@
         mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_NETWORK_SCAN_TIMEOUT));
 
         verify(mTransportSelectorCallback, times(1)).onWlanSelected(eq(true));
+
+        assertNotNull(mResultConsumer);
+
+        mResultConsumer.accept(regResult);
+        processAllMessages();
+
+        // Ignore the stale result
+        verify(mWwanSelectorCallback, never()).onDomainSelected(anyInt(), anyBoolean());
     }
 
     @Test
@@ -1340,6 +1407,36 @@
     }
 
     @Test
+    public void testDualSimInvalidSubscriptionAfterScan() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+        doReturn(2).when(mTelephonyManager).getActiveModemCount();
+        doReturn(TelephonyManager.SIM_STATE_PIN_REQUIRED)
+                .when(mTelephonyManager).getSimState(anyInt());
+        doReturn(true).when(mCsrdCtrl).isThereOtherSlot();
+        doReturn(new String[] {"jp"}).when(mResources).getStringArray(anyInt());
+
+        EmergencyRegResult regResult = getEmergencyRegResult(UNKNOWN, REGISTRATION_STATE_UNKNOWN,
+                0, false, false, 0, 0, "", "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+        processAllMessages();
+
+        assertNotNull(mResultConsumer);
+
+        regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_UNKNOWN,
+                0, false, false, 0, 0, "", "", "jp");
+        mResultConsumer.accept(regResult);
+        processAllMessages();
+
+        verify(mTransportSelectorCallback, times(1))
+                .onSelectionTerminated(eq(DisconnectCause.EMERGENCY_PERM_FAILURE));
+    }
+
+    @Test
     public void testDualSimInvalidSubscriptionButNoOtherSlot() throws Exception {
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
@@ -1376,6 +1473,30 @@
     }
 
     @Test
+    public void testEutranWithPsDomainOnly() throws Exception {
+        setupForHandleScanResult();
+
+        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_HOME,
+                DOMAIN_PS, false, false, 0, 0, "", "");
+        mResultConsumer.accept(regResult);
+        processAllMessages();
+
+        verifyPsDialed();
+    }
+
+    @Test
+    public void testUtran() throws Exception {
+        setupForHandleScanResult();
+
+        EmergencyRegResult regResult = getEmergencyRegResult(UTRAN, REGISTRATION_STATE_HOME,
+                DOMAIN_CS, false, false, 0, 0, "", "");
+        mResultConsumer.accept(regResult);
+        processAllMessages();
+
+        verifyCsDialed();
+    }
+
+    @Test
     public void testFullService() throws Exception {
         PersistableBundle bundle = getDefaultPersistableBundle();
         bundle.putInt(KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT, SCAN_TYPE_FULL_SERVICE);
@@ -1934,6 +2055,9 @@
         mDomainSelector.handleMessage(mDomainSelector.obtainMessage(MSG_MAX_CELLULAR_TIMEOUT));
         processAllMessages();
 
+        assertFalse(mDomainSelector.hasMessages(MSG_MAX_CELLULAR_TIMEOUT));
+        verify(mTransportSelectorCallback, times(1)).onWlanSelected(anyBoolean());
+
         mDomainSelector.reselectDomain(attr);
         processAllMessages();
 
@@ -2246,6 +2370,93 @@
         verifyCsDialed();
     }
 
+    @Test
+    public void testWhileInEcbmOnWwan() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        doReturn(true).when(mEcbmHelper).isInEmergencyCallbackMode(anyInt());
+        doReturn(TRANSPORT_TYPE_WWAN).when(mEcbmHelper).getTransportType(anyInt());
+        doReturn(DATA_CONNECTED).when(mEcbmHelper).getDataConnectionState(anyInt());
+
+        EmergencyRegResult regResult = getEmergencyRegResult(UNKNOWN, REGISTRATION_STATE_UNKNOWN,
+                0, false, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+        processAllMessages();
+
+        verify(mTransportSelectorCallback, never()).onWlanSelected(anyBoolean());
+        verify(mTransportSelectorCallback).onWwanSelected(any());
+    }
+
+    @Test
+    public void testWhileInEcbmOnWlanConnected() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        doReturn(true).when(mEcbmHelper).isInEmergencyCallbackMode(anyInt());
+        doReturn(TRANSPORT_TYPE_WLAN).when(mEcbmHelper).getTransportType(anyInt());
+        doReturn(DATA_CONNECTED).when(mEcbmHelper).getDataConnectionState(anyInt());
+
+        EmergencyRegResult regResult = getEmergencyRegResult(UNKNOWN, REGISTRATION_STATE_UNKNOWN,
+                0, false, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+        processAllMessages();
+
+        verify(mTransportSelectorCallback).onWlanSelected(anyBoolean());
+        verify(mTransportSelectorCallback, never()).onWwanSelected(any());
+    }
+
+    @Test
+    public void testWhileInEcbmOnWlanNotConnected() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        doReturn(true).when(mEcbmHelper).isInEmergencyCallbackMode(anyInt());
+        doReturn(TRANSPORT_TYPE_WLAN).when(mEcbmHelper).getTransportType(anyInt());
+
+        EmergencyRegResult regResult = getEmergencyRegResult(UNKNOWN, REGISTRATION_STATE_UNKNOWN,
+                0, false, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+        processAllMessages();
+
+        verify(mTransportSelectorCallback, never()).onWlanSelected(anyBoolean());
+        verify(mTransportSelectorCallback).onWwanSelected(any());
+    }
+
+    @Test
+    public void testNotInEcbmOnWlanConnected() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        doReturn(false).when(mEcbmHelper).isInEmergencyCallbackMode(anyInt());
+        doReturn(TRANSPORT_TYPE_WLAN).when(mEcbmHelper).getTransportType(anyInt());
+        doReturn(DATA_CONNECTED).when(mEcbmHelper).getDataConnectionState(anyInt());
+
+        EmergencyRegResult regResult = getEmergencyRegResult(UNKNOWN, REGISTRATION_STATE_UNKNOWN,
+                0, false, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+        processAllMessages();
+
+        verify(mTransportSelectorCallback, never()).onWlanSelected(anyBoolean());
+        verify(mTransportSelectorCallback).onWwanSelected(any());
+    }
+
     private void setupForScanListTest(PersistableBundle bundle) throws Exception {
         setupForScanListTest(bundle, false);
     }
@@ -2319,7 +2530,7 @@
     private void createSelector(int subId) throws Exception {
         mDomainSelector = new EmergencyCallDomainSelector(
                 mContext, SLOT_0, subId, mHandlerThread.getLooper(),
-                mImsStateTracker, mDestroyListener, mCsrdCtrl, mCarrierConfigHelper);
+                mImsStateTracker, mDestroyListener, mCsrdCtrl, mCarrierConfigHelper, mEcbmHelper);
         mDomainSelector.clearResourceConfiguration();
         replaceInstance(DomainSelectorBase.class,
                 "mWwanSelectorCallback", mDomainSelector, mWwanSelectorCallback);
diff --git a/tests/src/com/android/services/telephony/domainselection/EmergencyCallbackModeHelperTest.java b/tests/src/com/android/services/telephony/domainselection/EmergencyCallbackModeHelperTest.java
new file mode 100644
index 0000000..9a4e0d8
--- /dev/null
+++ b/tests/src/com/android/services/telephony/domainselection/EmergencyCallbackModeHelperTest.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.services.telephony.domainselection;
+
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.assertNotNull;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyManager;
+import android.testing.TestableLooper;
+import android.util.Log;
+
+import com.android.TestContext;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Unit tests for EmergencyCallbackModeHelper
+ */
+public class EmergencyCallbackModeHelperTest {
+    private static final String TAG = "EmergencyCallbackModeHelperTest";
+
+    private static final int SLOT_0 = 0;
+    private static final int SLOT_1 = 1;
+    private static final int SUB_1 = 1;
+    private static final int SUB_2 = 2;
+
+    private Context mContext;
+    private HandlerThread mHandlerThread;
+    private TestableLooper mLooper;
+    private EmergencyCallbackModeHelper mEcbmHelper;
+    private CarrierConfigManager mCarrierConfigManager;
+    private TelephonyManager mTelephonyManager;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mContext = new TestContext() {
+            private Intent mIntent;
+
+            @Override
+            public String getSystemServiceName(Class<?> serviceClass) {
+                if (serviceClass == TelephonyManager.class) {
+                    return Context.TELEPHONY_SERVICE;
+                } else if (serviceClass == CarrierConfigManager.class) {
+                    return Context.CARRIER_CONFIG_SERVICE;
+                }
+                return super.getSystemServiceName(serviceClass);
+            }
+
+            @Override
+            public String getOpPackageName() {
+                return "";
+            }
+
+            @Override
+            public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+                return mIntent;
+            }
+
+            @Override
+            public void sendStickyBroadcast(Intent intent) {
+                mIntent = intent;
+            }
+        };
+
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+
+        mHandlerThread = new HandlerThread("EmergencyCallbackModeHelperTest");
+        mHandlerThread.start();
+
+        try {
+            mLooper = new TestableLooper(mHandlerThread.getLooper());
+        } catch (Exception e) {
+            logd("Unable to create looper from handler.");
+        }
+
+        mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
+        mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+        doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
+
+        mEcbmHelper = new EmergencyCallbackModeHelper(mContext, mHandlerThread.getLooper());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mEcbmHelper != null) {
+            mEcbmHelper.destroy();
+            mEcbmHelper = null;
+        }
+
+        if (mLooper != null) {
+            mLooper.destroy();
+            mLooper = null;
+        }
+    }
+
+    @Test
+    public void testInit() throws Exception {
+        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> callbackCaptor =
+                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+        ArgumentCaptor<Executor> executorCaptor = ArgumentCaptor.forClass(Executor.class);
+
+        verify(mCarrierConfigManager).registerCarrierConfigChangeListener(executorCaptor.capture(),
+                callbackCaptor.capture());
+        assertNotNull(executorCaptor.getValue());
+        assertNotNull(callbackCaptor.getValue());
+    }
+
+    @Test
+    public void testEmergencyCallbackModeNotSupported() throws Exception {
+        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> callbackCaptor =
+                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+
+        verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+                callbackCaptor.capture());
+
+        CarrierConfigManager.CarrierConfigChangeListener callback = callbackCaptor.getValue();
+
+        assertNotNull(callback);
+
+        // ECBM not supported
+        PersistableBundle b = getPersistableBundle(false);
+        doReturn(b).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
+        callback.onCarrierConfigChanged(SLOT_0, SUB_1, 0, 0);
+
+        // No TelephonyCallback registered
+        verify(mTelephonyManager, never()).registerTelephonyCallback(any(), any());
+    }
+
+    @Test
+    public void testEmergencyCallbackModeSupported() throws Exception {
+        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> callbackCaptor =
+                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+
+        verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+                callbackCaptor.capture());
+
+        CarrierConfigManager.CarrierConfigChangeListener callback = callbackCaptor.getValue();
+
+        assertNotNull(callback);
+
+        // ECBM supported
+        PersistableBundle b = getPersistableBundle(true);
+        doReturn(b).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
+        callback.onCarrierConfigChanged(SLOT_0, SUB_1, 0, 0);
+
+        verify(mTelephonyManager).createForSubscriptionId(eq(SUB_1));
+
+        ArgumentCaptor<TelephonyCallback> telephonyCallbackCaptor =
+                ArgumentCaptor.forClass(TelephonyCallback.class);
+
+        // TelephonyCallback registered
+        verify(mTelephonyManager).registerTelephonyCallback(any(),
+                telephonyCallbackCaptor.capture());
+
+        assertNotNull(telephonyCallbackCaptor.getValue());
+    }
+
+    @Test
+    public void testEmergencyCallbackModeChanged() throws Exception {
+        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> callbackCaptor =
+                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+
+        verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+                callbackCaptor.capture());
+
+        CarrierConfigManager.CarrierConfigChangeListener callback = callbackCaptor.getValue();
+
+        assertNotNull(callback);
+
+        // ECBM supported
+        PersistableBundle b = getPersistableBundle(true);
+        doReturn(b).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
+        callback.onCarrierConfigChanged(SLOT_0, SUB_1, 0, 0);
+
+        verify(mTelephonyManager).createForSubscriptionId(eq(SUB_1));
+
+        ArgumentCaptor<TelephonyCallback> telephonyCallbackCaptor =
+                ArgumentCaptor.forClass(TelephonyCallback.class);
+
+        // TelephonyCallback registered
+        verify(mTelephonyManager).registerTelephonyCallback(any(),
+                telephonyCallbackCaptor.capture());
+
+        TelephonyCallback telephonyCallback = telephonyCallbackCaptor.getValue();
+
+        assertNotNull(telephonyCallback);
+
+        // Carrier config changes, ECBM not supported
+        b = getPersistableBundle(false);
+        doReturn(b).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
+        callback.onCarrierConfigChanged(SLOT_0, SUB_1, 0, 0);
+
+        // TelephonyCallback unregistered
+        verify(mTelephonyManager).unregisterTelephonyCallback(eq(telephonyCallback));
+    }
+
+    @Test
+    public void testEmergencyCallbackModeEnter() throws Exception {
+        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> callbackCaptor =
+                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+
+        verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+                callbackCaptor.capture());
+
+        CarrierConfigManager.CarrierConfigChangeListener callback = callbackCaptor.getValue();
+
+        assertNotNull(callback);
+
+        // ECBM supported
+        PersistableBundle b = getPersistableBundle(true);
+        doReturn(b).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
+        callback.onCarrierConfigChanged(SLOT_0, SUB_1, 0, 0);
+        callback.onCarrierConfigChanged(SLOT_1, SUB_2, 0, 0);
+
+        // Enter ECBM on slot 1
+        mContext.sendStickyBroadcast(getIntent(true, SLOT_1));
+
+        assertFalse(mEcbmHelper.isInEmergencyCallbackMode(SLOT_0));
+        assertTrue(mEcbmHelper.isInEmergencyCallbackMode(SLOT_1));
+    }
+
+    @Test
+    public void testEmergencyCallbackModeExit() throws Exception {
+        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> callbackCaptor =
+                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+
+        verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+                callbackCaptor.capture());
+
+        CarrierConfigManager.CarrierConfigChangeListener callback = callbackCaptor.getValue();
+
+        assertNotNull(callback);
+
+        // ECBM supported
+        PersistableBundle b = getPersistableBundle(true);
+        doReturn(b).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
+        callback.onCarrierConfigChanged(SLOT_0, SUB_1, 0, 0);
+
+        // Exit ECBM
+        mContext.sendStickyBroadcast(getIntent(false, SLOT_0));
+
+        assertFalse(mEcbmHelper.isInEmergencyCallbackMode(SLOT_0));
+    }
+
+    private static Intent getIntent(boolean inEcm, int slotIndex) {
+        Intent intent = new Intent(TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
+        intent.putExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, inEcm);
+        intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, slotIndex);
+        return intent;
+    }
+
+    private static PersistableBundle getPersistableBundle(boolean supported) {
+        PersistableBundle bundle  = new PersistableBundle();
+        bundle.putBoolean(KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL, supported);
+        return bundle;
+    }
+
+    private static void logd(String str) {
+        Log.d(TAG, str);
+    }
+}
diff --git a/tests/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionServiceTest.java b/tests/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionServiceTest.java
index f4d2732..d9c737e 100644
--- a/tests/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionServiceTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
@@ -81,7 +82,8 @@
                         @NonNull Looper looper, @NonNull ImsStateTracker imsStateTracker,
                         @NonNull DomainSelectorBase.DestroyListener listener,
                         @NonNull CrossSimRedialingController crossSimRedialingController,
-                        @NonNull CarrierConfigHelper carrierConfigHelper) {
+                        @NonNull CarrierConfigHelper carrierConfigHelper,
+                        @NonNull EmergencyCallbackModeHelper ecbmHelper) {
                     switch (selectorType) {
                         case DomainSelectionService.SELECTOR_TYPE_CALLING: // fallthrough
                         case DomainSelectionService.SELECTOR_TYPE_SMS: // fallthrough
@@ -97,6 +99,26 @@
                     }
                 }
             };
+    private static class TestTelephonyDomainSelectionService
+            extends TelephonyDomainSelectionService {
+        private final Context mContext;
+
+        TestTelephonyDomainSelectionService(Context context,
+                @NonNull ImsStateTrackerFactory imsStateTrackerFactory,
+                @NonNull DomainSelectorFactory domainSelectorFactory,
+                @Nullable CarrierConfigHelper carrierConfigHelper,
+                @Nullable EmergencyCallbackModeHelper ecbmHelper) {
+            super(imsStateTrackerFactory, domainSelectorFactory, carrierConfigHelper, ecbmHelper);
+            mContext = context;
+        }
+
+        @Override
+        public void onCreate() {
+            // attach test context.
+            attachBaseContext(mContext);
+            super.onCreate();
+        }
+    }
     private static final int SLOT_0 = 0;
     private static final int SUB_1 = 1;
     private static final int SUB_2 = 2;
@@ -111,6 +133,7 @@
     @Mock private TransportSelectorCallback mSelectorCallback2;
     @Mock private ImsStateTracker mImsStateTracker;
     @Mock private CarrierConfigHelper mCarrierConfigHelper;
+    @Mock private EmergencyCallbackModeHelper mEcbmHelper;
 
     private final ServiceState mServiceState = new ServiceState();
     private final BarringInfo mBarringInfo = new BarringInfo();
@@ -131,8 +154,9 @@
         }
 
         mContext = new TestContext();
-        mDomainSelectionService = new TelephonyDomainSelectionService(mContext,
-                mImsStateTrackerFactory, mDomainSelectorFactory, mCarrierConfigHelper);
+        mDomainSelectionService = new TestTelephonyDomainSelectionService(mContext,
+                mImsStateTrackerFactory, mDomainSelectorFactory, mCarrierConfigHelper, mEcbmHelper);
+        mDomainSelectionService.onCreate();
         mServiceHandler = new Handler(mDomainSelectionService.getLooper());
         mTestableLooper = new TestableLooper(mDomainSelectionService.getLooper());
 
@@ -184,9 +208,7 @@
                 .setCallId(CALL_ID)
                 .setEmergency(true)
                 .build();
-        mServiceHandler.post(() -> {
-            mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
-        });
+        mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
         processAllMessages();
 
         verify(mImsStateTracker).start(eq(SUB_1));
@@ -203,9 +225,7 @@
                 .setCallId(CALL_ID)
                 .setEmergency(true)
                 .build();
-        mServiceHandler.post(() -> {
-            mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
-        });
+        mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
         processAllMessages();
 
         verify(mImsStateTracker, never()).start(anyInt());
@@ -222,9 +242,7 @@
                 .setCallId(CALL_ID)
                 .setEmergency(true)
                 .build();
-        mServiceHandler.post(() -> {
-            mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
-        });
+        mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
         processAllMessages();
 
         verify(mImsStateTracker).start(eq(SUB_1));
@@ -240,9 +258,7 @@
                 .setCallId(CALL_ID)
                 .setEmergency(true)
                 .build();
-        mServiceHandler.post(() -> {
-            mDomainSelectionService.onDomainSelection(attr2, mSelectorCallback2);
-        });
+        mDomainSelectionService.onDomainSelection(attr2, mSelectorCallback2);
         processAllMessages();
 
         verify(mImsStateTracker).start(eq(SUB_2));
@@ -259,9 +275,7 @@
                 .setCallId(CALL_ID)
                 .setEmergency(true)
                 .build();
-        mServiceHandler.post(() -> {
-            mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
-        });
+        mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
         processAllMessages();
 
         verify(mImsStateTracker).start(eq(SUB_1));
@@ -274,9 +288,7 @@
                 .setCallId(CALL_ID)
                 .setEmergency(true)
                 .build();
-        mServiceHandler.post(() -> {
-            mDomainSelectionService.onDomainSelection(attr2, mSelectorCallback2);
-        });
+        mDomainSelectionService.onDomainSelection(attr2, mSelectorCallback2);
         processAllMessages();
 
         verify(mImsStateTracker).start(eq(SUB_2));
@@ -309,9 +321,7 @@
                 .setCallId(CALL_ID)
                 .setEmergency(true)
                 .build();
-        mServiceHandler.post(() -> {
-            mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
-        });
+        mDomainSelectionService.onDomainSelection(attr1, mSelectorCallback1);
         processAllMessages();
 
         mDomainSelectionService.onDestroy();