Merge "Block outgoing call if satellite is connected withing hysteresis time" into 24D1-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 5c8504a..1190d38 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -596,7 +596,8 @@
 
         <receiver
             android:name=".security.SafetySourceReceiver"
-            android:exported="false">
+            android:exported="false"
+            androidprv:systemUserOnly="true">
             <intent-filter>
                 <action android:name="android.safetycenter.action.REFRESH_SAFETY_SOURCES"/>
             </intent-filter>
diff --git a/res/values/config.xml b/res/values/config.xml
index 2ad2af3..5dae649 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -339,6 +339,15 @@
         <item>sg</item>
     </string-array>
 
+    <!-- Array of countries that a CS preferred scan is preferred after CSFB failure
+         due to the failure of extended service request. Values should be ISO3166
+         country codes in lowercase. -->
+    <string-array name="config_countries_prefer_cs_preferred_scan_after_csfb_failure"
+            translatable="false">
+        <!-- b/300022457 -->
+        <item>us</item>
+    </string-array>
+
     <!-- 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. -->
diff --git a/src/com/android/phone/satellite/entitlement/SatelliteEntitlementApi.java b/src/com/android/phone/satellite/entitlement/SatelliteEntitlementApi.java
index c856eb5..45482ec 100644
--- a/src/com/android/phone/satellite/entitlement/SatelliteEntitlementApi.java
+++ b/src/com/android/phone/satellite/entitlement/SatelliteEntitlementApi.java
@@ -32,15 +32,18 @@
  * @hide
  */
 public class SatelliteEntitlementApi {
+    private static final String DEFAULT_APP_NAME = "androidSatmode";
     @NonNull
     private final ServiceEntitlement mServiceEntitlement;
     private final Context mContext;
+    private final PersistableBundle mCarrierConfig;
 
     public SatelliteEntitlementApi(@NonNull Context context,
             @NonNull PersistableBundle carrierConfig, @NonNull int subId) {
         mContext = context;
         mServiceEntitlement = new ServiceEntitlement(mContext,
                 getCarrierConfigFromEntitlementServerUrl(carrierConfig), subId);
+        mCarrierConfig = carrierConfig;
     }
 
     /**
@@ -50,6 +53,7 @@
     public SatelliteEntitlementResult checkEntitlementStatus() throws ServiceEntitlementException {
         ServiceEntitlementRequest.Builder requestBuilder = ServiceEntitlementRequest.builder();
         requestBuilder.setAcceptContentType(ServiceEntitlementRequest.ACCEPT_CONTENT_TYPE_JSON);
+        requestBuilder.setAppName(getSatelliteEntitlementAppName(mCarrierConfig));
         ServiceEntitlementRequest request = requestBuilder.build();
 
         String response = mServiceEntitlement.queryEntitlementStatus(
@@ -68,4 +72,10 @@
                 "");
         return CarrierConfig.builder().setServerUrl(entitlementServiceUrl).build();
     }
+
+    @NonNull
+    private String getSatelliteEntitlementAppName(@NonNull PersistableBundle carrierConfig) {
+        return carrierConfig.getString(
+                CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_APP_NAME_STRING, DEFAULT_APP_NAME);
+    }
 }
diff --git a/src/com/android/phone/satellite/entitlement/SatelliteEntitlementController.java b/src/com/android/phone/satellite/entitlement/SatelliteEntitlementController.java
index 94362a0..accae32 100644
--- a/src/com/android/phone/satellite/entitlement/SatelliteEntitlementController.java
+++ b/src/com/android/phone/satellite/entitlement/SatelliteEntitlementController.java
@@ -509,7 +509,8 @@
         PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId,
                 CarrierConfigManager.ImsServiceEntitlement.KEY_ENTITLEMENT_SERVER_URL_STRING,
                 CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT,
-                CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL);
+                CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL,
+                CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_APP_NAME_STRING);
         if (config == null || config.isEmpty()) {
             config = CarrierConfigManager.getDefaultConfig();
         }
diff --git a/src/com/android/phone/satellite/entitlement/SatelliteEntitlementResponse.java b/src/com/android/phone/satellite/entitlement/SatelliteEntitlementResponse.java
index 1fe0ecf..b877bee 100644
--- a/src/com/android/phone/satellite/entitlement/SatelliteEntitlementResponse.java
+++ b/src/com/android/phone/satellite/entitlement/SatelliteEntitlementResponse.java
@@ -18,6 +18,7 @@
 
 import static com.android.phone.satellite.entitlement.SatelliteEntitlementResult.SATELLITE_ENTITLEMENT_STATUS_DISABLED;
 
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -70,7 +71,9 @@
         mEntitlementStatus = SATELLITE_ENTITLEMENT_STATUS_DISABLED;
         mPlmnAllowedList = new ArrayList<>();
         mPlmnBarredList = new ArrayList<>();
-        parsingResponse(response);
+        if (!TextUtils.isEmpty(response)) {
+            parsingResponse(response);
+        }
     }
 
     /**
@@ -123,8 +126,11 @@
                 for (int i = 0; i < jsonArray.length(); i++) {
                     String dataPlanType = jsonArray.getJSONObject(i).has(DATA_PLAN_TYPE_KEY)
                             ? jsonArray.getJSONObject(i).getString(DATA_PLAN_TYPE_KEY) : "";
-                    mPlmnAllowedList.add(new SatelliteNetworkInfo(
-                            jsonArray.getJSONObject(i).getString(PLMN_KEY), dataPlanType));
+                    String plmn = jsonArray.getJSONObject(i).getString(PLMN_KEY);
+                    logd("parsingResponse: plmn=" + plmn + " dataplan=" + dataPlanType);
+                    if (!TextUtils.isEmpty(plmn)) {
+                        mPlmnAllowedList.add(new SatelliteNetworkInfo(plmn, dataPlanType));
+                    }
                 }
             }
             if (jsonToken.has(PLMN_BARRED_KEY)) {
@@ -141,6 +147,10 @@
         }
     }
 
+    private static void logd(String log) {
+        Log.d(TAG, log);
+    }
+
     private static void loge(String log) {
         Log.e(TAG, log);
     }
diff --git a/src/com/android/services/telephony/domainselection/EmergencyCallbackModeHelper.java b/src/com/android/services/telephony/domainselection/DataConnectionStateHelper.java
similarity index 77%
rename from src/com/android/services/telephony/domainselection/EmergencyCallbackModeHelper.java
rename to src/com/android/services/telephony/domainselection/DataConnectionStateHelper.java
index cdf2225..8fbf7e3 100644
--- a/src/com/android/services/telephony/domainselection/EmergencyCallbackModeHelper.java
+++ b/src/com/android/services/telephony/domainselection/DataConnectionStateHelper.java
@@ -16,7 +16,6 @@
 
 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;
@@ -29,11 +28,11 @@
 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.SubscriptionManager;
 import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
@@ -41,8 +40,8 @@
 import android.util.Log;
 
 /** Helper class to cache emergency data connection state. */
-public class EmergencyCallbackModeHelper extends Handler {
-    private static final String TAG = "EmergencyCallbackModeHelper";
+public class DataConnectionStateHelper extends Handler {
+    private static final String TAG = "DataConnectionStateHelper";
     private static final boolean DBG = (SystemProperties.getInt("ro.debuggable", 0) == 1);
 
     /**
@@ -53,14 +52,19 @@
 
         private final Handler mHandler;
         private final TelephonyManager mTelephonyManager;
+        private final DataConnectionStateHelper mOwner;
         private final int mSubId;
+        private final int mSlotIndex;
         private int mTransportType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
         private int mState = TelephonyManager.DATA_UNKNOWN;
 
-        DataConnectionStateListener(Handler handler, TelephonyManager tm, int subId) {
+        DataConnectionStateListener(Handler handler, TelephonyManager tm,
+                DataConnectionStateHelper owner, int subId, int slotIndex) {
             mHandler = handler;
             mTelephonyManager = tm;
+            mOwner = owner;
             mSubId = subId;
+            mSlotIndex = slotIndex;
         }
 
         @Override
@@ -73,16 +77,19 @@
             }
             mTransportType = dataConnectionState.getTransportType();
             mState = dataConnectionState.getState();
+            mOwner.notifyDataConnectionStateChange(mSlotIndex, mState);
             Log.i(TAG, "onPreciseDataConnectionStateChanged ePDN state=" + mState
-                    + ", transport=" + mTransportType);
+                    + ", transport=" + mTransportType + ", subId=" + mSubId);
         }
 
         public void registerTelephonyCallback() {
+            Log.i(TAG, "registerTelephonyCallback subId=" + mSubId);
             TelephonyManager tm = mTelephonyManager.createForSubscriptionId(mSubId);
             tm.registerTelephonyCallback(mHandler::post, this);
         }
 
         public void unregisterTelephonyCallback() {
+            Log.i(TAG, "unregisterTelephonyCallback subId=" + mSubId);
             mTelephonyManager.unregisterTelephonyCallback(this);
         }
 
@@ -110,13 +117,15 @@
             (slotIndex, subId, carrierId, specificCarrierId) -> onCarrierConfigChanged(
                     slotIndex, subId, carrierId);
 
+    private EmergencyCallDomainSelector mSelector;
+
     /**
      * Creates an instance.
      *
      * @param context The Context this is associated with.
-     * @param looper The Looper to run the EmergencyCallbackModeHelper.
+     * @param looper The Looper to run the DataConnectionStateHelper.
      */
-    public EmergencyCallbackModeHelper(@NonNull Context context, @NonNull Looper looper) {
+    public DataConnectionStateHelper(@NonNull Context context, @NonNull Looper looper) {
         super(looper);
 
         mContext = context;
@@ -133,10 +142,6 @@
      * @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
@@ -177,6 +182,24 @@
         return listener.getState();
     }
 
+    /**
+     * Sets the EmergencyCallDomainSelector instance.
+     *
+     * @param selector The instance of {@link EmergencyCallDomainSelector}.
+     */
+    public void setEmergencyCallDomainSelector(EmergencyCallDomainSelector selector) {
+        mSelector = selector;
+    }
+
+    private void notifyDataConnectionStateChange(int slotIndex, int state) {
+        EmergencyCallDomainSelector selector = mSelector;
+        if (selector != null) {
+            Log.i(TAG, "notifyDataConnectionStateChange slot=" + slotIndex + ", state=" + state);
+            selector.notifyDataConnectionStateChange(slotIndex, state);
+        }
+    }
+
+
     @Override
     public void handleMessage(Message msg) {
         switch(msg.what) {
@@ -194,35 +217,22 @@
             return;
         }
 
-        PersistableBundle b = mConfigManager.getConfigForSubId(subId,
-                KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL);
+        DataConnectionStateListener listener =
+                mDataConnectionStateListeners.get(Integer.valueOf(slotIndex));
 
-        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();
+            mDataConnectionStateListeners.remove(Integer.valueOf(slotIndex));
+            listener = null;
+        }
 
-            // 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");
-            }
+        if (listener == null
+                && SubscriptionManager.isValidSubscriptionId(subId)) {
+            listener = new DataConnectionStateListener(this, mTelephonyManager,
+                    this, subId, slotIndex);
+            listener.registerTelephonyCallback();
+            mDataConnectionStateListeners.put(Integer.valueOf(slotIndex), listener);
         }
     }
 
diff --git a/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java b/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
index 5b9ee02..a073397 100644
--- a/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
+++ b/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
@@ -42,6 +42,7 @@
 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_REQUIRES_VOLTE_ENABLED_BOOL;
 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_SCAN_TIMER_SEC_INT;
 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_VOWIFI_REQUIRES_CONDITION_INT;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_IMS_REASONINFO_CODE_TO_RETRY_EMERGENCY_INT_ARRAY;
 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT;
 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_MAXIMUM_NUMBER_OF_EMERGENCY_TRIES_OVER_VOWIFI_INT;
 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_PREFER_IMS_EMERGENCY_WHEN_VOICE_CALLS_ON_CS_BOOL;
@@ -57,6 +58,9 @@
 import static android.telephony.PreciseDisconnectCause.NO_VALID_SIM;
 import static android.telephony.PreciseDisconnectCause.SERVICE_OPTION_NOT_AVAILABLE;
 import static android.telephony.TelephonyManager.DATA_CONNECTED;
+import static android.telephony.TelephonyManager.DATA_DISCONNECTED;
+import static android.telephony.TelephonyManager.DATA_DISCONNECTING;
+import static android.telephony.TelephonyManager.DATA_UNKNOWN;
 
 import android.annotation.NonNull;
 import android.content.Context;
@@ -98,6 +102,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.function.IntFunction;
+import java.util.stream.Collectors;
 
 /**
  * Selects the domain for emergency calling.
@@ -108,19 +113,34 @@
     private static final boolean DBG = (SystemProperties.getInt("ro.debuggable", 0) == 1);
     private static final int LOG_SIZE = 50;
 
+    /**
+     * Timeout before we requests network scan without waiting for the disconnection
+     * of ePDN.
+     */
+    private static final int DEFAULT_DATA_DISCONNECTION_TIMEOUT_MS = 2 * 1000; // 2 seconds
+
     private static final int MSG_START_DOMAIN_SELECTION = 11;
     @VisibleForTesting
     public static final int MSG_NETWORK_SCAN_TIMEOUT = 12;
     private static final int MSG_NETWORK_SCAN_RESULT = 13;
     @VisibleForTesting
     public static final int MSG_MAX_CELLULAR_TIMEOUT = 14;
+    @VisibleForTesting
+    public static final int MSG_WAIT_DISCONNECTION_TIMEOUT = 15;
 
     private static final int NOT_SUPPORTED = -1;
 
+    private static List<Integer> sDefaultRetryReasonCodes = List.of(
+            ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED,
+            ImsReasonInfo.CODE_LOCAL_INTERNAL_ERROR,
+            ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED,
+            ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL);
+
     private static final LocalLog sLocalLog = new LocalLog(LOG_SIZE);
 
     private static List<String> sSimReadyAllowList;
     private static List<String> sPreferSlotWithNormalServiceList;
+    private static List<String> sPreferCsAfterCsfbFailure;
 
     /**
      * Network callback used to determine whether Wi-Fi is connected or not.
@@ -184,13 +204,15 @@
     private boolean mRequiresVoLteEnabled;
     private boolean mLtePreferredAfterNrFailure;
     private boolean mScanLimitedOnlyAfterVolteFailure;
+    private List<Integer> mRetryReasonCodes;
 
     // Members for states
     private boolean mIsMonitoringConnectivity;
     private boolean mWiFiAvailable;
     private boolean mWasCsfbAfterPsFailure;
     private boolean mTryCsWhenPsFails;
-    private boolean mTryEpsFallback;
+    private boolean mTryEsFallback;
+    private boolean mIsWaitingForDataDisconnection;
     private int mModemCount;
 
     /** Indicates whether this instance is deactivated. */
@@ -212,14 +234,14 @@
 
     private final PowerManager.WakeLock mPartialWakeLock;
     private final CrossSimRedialingController mCrossSimRedialingController;
-    private final EmergencyCallbackModeHelper mEcbmHelper;
+    private final DataConnectionStateHelper mEpdnHelper;
 
     /** Constructor. */
     public EmergencyCallDomainSelector(Context context, int slotId, int subId,
             @NonNull Looper looper, @NonNull ImsStateTracker imsStateTracker,
             @NonNull DestroyListener destroyListener,
             @NonNull CrossSimRedialingController csrController,
-            @NonNull EmergencyCallbackModeHelper ecbmHelper) {
+            @NonNull DataConnectionStateHelper epdnHelper) {
         super(context, slotId, subId, looper, imsStateTracker, destroyListener, TAG);
 
         mImsStateTracker.addBarringInfoListener(this);
@@ -229,7 +251,8 @@
         mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
 
         mCrossSimRedialingController = csrController;
-        mEcbmHelper = ecbmHelper;
+        mEpdnHelper = epdnHelper;
+        epdnHelper.setEmergencyCallDomainSelector(this);
         acquireWakeLock();
     }
 
@@ -254,6 +277,10 @@
                 handleMaxCellularTimeout();
                 break;
 
+            case MSG_WAIT_DISCONNECTION_TIMEOUT:
+                requestScanDelayed();
+                break;
+
             default:
                 super.handleMessage(msg);
                 break;
@@ -346,17 +373,17 @@
             return;
         }
 
-        if (maybeTerminateSelection(cause)) {
-            logi("reselectDomain terminate selection");
-            return;
-        }
-
         if (mCrossStackTimerExpired) {
             logi("reselectDomain cross stack timer expired");
             terminateSelectionForCrossSimRedialing(false);
             return;
         }
 
+        if (maybeTerminateSelection(cause)) {
+            logi("reselectDomain terminate selection");
+            return;
+        }
+
         if (mTryCsWhenPsFails) {
             mTryCsWhenPsFails = false;
             // Initial state was CSFB available and dial PS failed.
@@ -371,7 +398,7 @@
 
         if (mWasCsfbAfterPsFailure) {
             mWasCsfbAfterPsFailure = false;
-            if (cause == SERVICE_OPTION_NOT_AVAILABLE) {
+            if (preferCsAfterCsfbFailure(cause)) {
                 // b/299875872, combined attach but EXTENDED_SERVICE_REQUEST failed.
                 // Try CS preferred scan instead of PS preferred scan.
                 mLastNetworkType = EUTRAN;
@@ -390,6 +417,25 @@
             }
         }
 
+        if (mLastTransportType == TRANSPORT_TYPE_WWAN) {
+            if (mLastNetworkType == NGRAN && (!mTryEsFallback) && mLtePreferredAfterNrFailure) {
+                int state = mEpdnHelper.getDataConnectionState(getSlotId());
+                if (state != DATA_DISCONNECTED && state != DATA_UNKNOWN) {
+                    mIsWaitingForDataDisconnection = true;
+                    // If deactivation of ePDN has been started, then wait for the disconnection
+                    // with the timeout of 2 seconds and then request network scan.
+                    // If deactivation of ePDN hasn't been started yet, then wait for the start
+                    // of the deactivation with the timeout of 2 seconds.
+                    // The timer shall be restarted in notifyDataConnectionStateChange()
+                    // when starting the deactivation.
+                    sendEmptyMessageDelayed(MSG_WAIT_DISCONNECTION_TIMEOUT,
+                            DEFAULT_DATA_DISCONNECTION_TIMEOUT_MS);
+                    mDomainSelected = false;
+                    return;
+                }
+            }
+        }
+
         if (mLastTransportType == TRANSPORT_TYPE_WLAN) {
             // Dialing over Wi-Fi failed. Try scanning cellular networks.
             onWwanSelected(this::reselectDomainInternal);
@@ -412,6 +458,17 @@
         mDomainSelected = false;
     }
 
+    private boolean preferCsAfterCsfbFailure(int cause) {
+        if (cause != SERVICE_OPTION_NOT_AVAILABLE) return false;
+        if (sPreferCsAfterCsfbFailure == null || mLastRegResult == null
+                || TextUtils.isEmpty(mLastRegResult.getCountryIso())) {
+            // Enabled by default if country is not identified.
+            return true;
+        }
+
+        return sPreferCsAfterCsfbFailure.contains(mLastRegResult.getCountryIso());
+    }
+
     private int getDisconnectCause() {
         int cause = mSelectionAttributes.getCsDisconnectCause();
 
@@ -439,6 +496,15 @@
         });
     }
 
+    private void requestScanDelayed() {
+        logi("requestScanDelayed waiting=" + mIsWaitingForDataDisconnection);
+        if (!mDestroyed && mIsWaitingForDataDisconnection) {
+            requestScan(true);
+            removeMessages(MSG_WAIT_DISCONNECTION_TIMEOUT);
+        }
+        mIsWaitingForDataDisconnection = false;
+    }
+
     @Override
     public void finishSelection() {
         logi("finishSelection");
@@ -538,6 +604,7 @@
                 KEY_EMERGENCY_REQUIRES_VOLTE_ENABLED_BOOL,
                 KEY_EMERGENCY_LTE_PREFERRED_AFTER_NR_FAILED_BOOL,
                 KEY_SCAN_LIMITED_SERVICE_AFTER_VOLTE_FAILURE_BOOL,
+                KEY_IMS_REASONINFO_CODE_TO_RETRY_EMERGENCY_INT_ARRAY,
                 KEY_EMERGENCY_CDMA_PREFERRED_NUMBERS_STRING_ARRAY);
         if (b == null) {
             b = CarrierConfigManager.getDefaultConfig();
@@ -571,6 +638,8 @@
         mScanLimitedOnlyAfterVolteFailure = b.getBoolean(
                 KEY_SCAN_LIMITED_SERVICE_AFTER_VOLTE_FAILURE_BOOL);
         String[] numbers = b.getStringArray(KEY_EMERGENCY_CDMA_PREFERRED_NUMBERS_STRING_ARRAY);
+        int[] imsReasonCodes =
+                b.getIntArray(KEY_IMS_REASONINFO_CODE_TO_RETRY_EMERGENCY_INT_ARRAY);
 
         if (mImsRatsConfig == null) mImsRatsConfig = new int[0];
         if (mCsRatsConfig == null) mCsRatsConfig = new int[0];
@@ -579,6 +648,10 @@
         if (mDomainPreference == null) mDomainPreference = new int[0];
         if (mDomainPreferenceRoam == null) mDomainPreferenceRoam = new int[0];
         if (numbers == null) numbers = new String[0];
+        if (imsReasonCodes == null) imsReasonCodes = new int[0];
+
+        mRetryReasonCodes = Arrays.stream(imsReasonCodes).boxed().collect(Collectors.toList());
+        mRetryReasonCodes.addAll(sDefaultRetryReasonCodes);
 
         logi("updateCarrierConfiguration "
                 + "imsRats=" + arrayToString(mImsRatsConfig,
@@ -606,6 +679,7 @@
                 + ", requiresVoLteEnabled=" + mRequiresVoLteEnabled
                 + ", ltePreferredAfterNr=" + mLtePreferredAfterNrFailure
                 + ", scanLimitedOnly=" + mScanLimitedOnlyAfterVolteFailure
+                + ", retryReasonCodes=" + mRetryReasonCodes
                 + ", cdmaPreferredNumbers=" + arrayToString(numbers));
 
         mCdmaPreferredNumbers = Arrays.asList(numbers);
@@ -635,6 +709,13 @@
         }
         logi("readResourceConfiguration preferNormalServiceCountries="
                 + sPreferSlotWithNormalServiceList);
+
+        if (sPreferCsAfterCsfbFailure == null) {
+            sPreferCsAfterCsfbFailure = readResourceConfiguration(
+                    R.array.config_countries_prefer_cs_preferred_scan_after_csfb_failure);
+        }
+        logi("readResourceConfiguration preferCsAfterCsfbFailure="
+                + sPreferCsAfterCsfbFailure);
     }
 
     private List<String> readResourceConfiguration(int id) {
@@ -660,6 +741,7 @@
     public void clearResourceConfiguration() {
         sSimReadyAllowList = null;
         sPreferSlotWithNormalServiceList = null;
+        sPreferCsAfterCsfbFailure = null;
     }
 
     private void selectDomain() {
@@ -763,7 +845,7 @@
                 onWwanNetworkTypeSelected(mCsNetworkType);
             }
         } else if (psAvailable) {
-            mTryEpsFallback = (mPsNetworkType == NGRAN) && isEpsFallbackAvailable();
+            mTryEsFallback = (mPsNetworkType == NGRAN) && isEsFallbackAvailable();
             if (mSelectionAttributes.isExitedFromAirplaneMode()
                     || !mRequiresImsRegistration || isImsRegisteredWithVoiceCapability()) {
                 onWwanNetworkTypeSelected(mPsNetworkType);
@@ -773,7 +855,7 @@
             } else {
                 // Carrier configuration requires IMS registration for emergency services over PS,
                 // but not registered. Try CS emergency call.
-                mTryEpsFallback = false;
+                mTryEsFallback = false;
                 requestScan(true, true);
             }
         } else if (csAvailable) {
@@ -786,7 +868,7 @@
                 // but not registered. Try CS emergency call.
                 requestScan(true, true);
             } else {
-                mTryEpsFallback = isEpsFallbackAvailable();
+                mTryEsFallback = isEsFallbackAvailable();
                 requestScan(true);
             }
         }
@@ -826,9 +908,9 @@
         mCancelSignal = new CancellationSignal();
         // In case dialing over Wi-Fi has failed, do not the change the domain preference.
         if (!wifiFailed || mLastPreferredNetworks == null) {
-            mLastPreferredNetworks = getNextPreferredNetworks(csPreferred, mTryEpsFallback);
+            mLastPreferredNetworks = getNextPreferredNetworks(csPreferred, mTryEsFallback);
         }
-        mTryEpsFallback = false;
+        mTryEsFallback = false;
 
         if (isInRoaming()
                 && (mPreferredNetworkScanType
@@ -864,12 +946,12 @@
      * Gets the list of preferred network type for the new scan request.
      *
      * @param csPreferred Indicates whether CS preferred scan is requested.
-     * @param tryEpsFallback Indicates whether scan requested for EPS fallback.
+     * @param tryEsFallback Indicates whether scan requested for ES fallback.
      * @return The list of preferred network types.
      */
     @VisibleForTesting
     public @RadioAccessNetworkType List<Integer> getNextPreferredNetworks(boolean csPreferred,
-            boolean tryEpsFallback) {
+            boolean tryEsFallback) {
         if (mRequiresVoLteEnabled && !isAdvancedCallingSettingEnabled()) {
             // Emergency call over IMS is not supported.
             logi("getNextPreferredNetworks VoLte setting is not enabled.");
@@ -882,10 +964,10 @@
         int psPriority = domains.indexOf(DOMAIN_PS_3GPP);
         int csPriority = domains.indexOf(DOMAIN_CS);
         logi("getNextPreferredNetworks psPriority=" + psPriority + ", csPriority=" + csPriority
-                + ", csPreferred=" + csPreferred + ", epsFallback=" + tryEpsFallback
+                + ", csPreferred=" + csPreferred + ", esFallback=" + tryEsFallback
                 + ", lastNetworkType=" + accessNetworkTypeToString(mLastNetworkType));
 
-        if (!csPreferred && (mLastNetworkType == UNKNOWN || tryEpsFallback)) {
+        if (!csPreferred && (mLastNetworkType == UNKNOWN || tryEsFallback)) {
             // Generate the list per the domain preference.
 
             if (psPriority == NOT_SUPPORTED && csPriority == NOT_SUPPORTED) {
@@ -909,7 +991,7 @@
             }
 
             // Make NGRAN have the lowest priority
-            if (tryEpsFallback && preferredNetworks.contains(NGRAN)) {
+            if (tryEsFallback && preferredNetworks.contains(NGRAN)) {
                 preferredNetworks.remove(Integer.valueOf(NGRAN));
                 preferredNetworks.add(NGRAN);
             }
@@ -1135,7 +1217,7 @@
         return UNKNOWN;
     }
 
-    private boolean isEpsFallbackAvailable() {
+    private boolean isEsFallbackAvailable() {
         EmergencyRegistrationResult regResult =
                 mSelectionAttributes.getEmergencyRegistrationResult();
         if (regResult == null) return false;
@@ -1308,7 +1390,6 @@
         EmergencyRegistrationResult regResult = mLastRegResult;
         if (regResult != null) {
             if (regResult.getRegState() == REGISTRATION_STATE_HOME) return false;
-            if (regResult.getRegState() == REGISTRATION_STATE_ROAMING) return true;
 
             String iso = regResult.getCountryIso();
             if (!TextUtils.isEmpty(iso)) netIso = iso;
@@ -1498,30 +1579,38 @@
 
     private void terminateSelectionPermanentlyForSlot() {
         logi("terminateSelectionPermanentlyForSlot");
-        terminateSelection(true);
+        terminateSelection(DisconnectCause.EMERGENCY_PERM_FAILURE);
     }
 
     private void terminateSelectionForCrossSimRedialing(boolean permanent) {
         logi("terminateSelectionForCrossSimRedialing perm=" + permanent);
-        terminateSelection(permanent);
+        terminateSelection(permanent ? DisconnectCause.EMERGENCY_PERM_FAILURE
+                : DisconnectCause.EMERGENCY_TEMP_FAILURE);
     }
 
-    private void terminateSelection(boolean permanent) {
-        mTransportSelectorCallback.onSelectionTerminated(permanent
-                ? DisconnectCause.EMERGENCY_PERM_FAILURE
-                : DisconnectCause.EMERGENCY_TEMP_FAILURE);
+    private void terminateSelection(int cause) {
+        mTransportSelectorCallback.onSelectionTerminated(cause);
     }
 
     private boolean maybeTerminateSelection(int cause) {
         switch (cause) {
             case NO_VALID_SIM:
                 // The disconnect cause saved in DomainSelectionConnection shall be used.
-                mTransportSelectorCallback.onSelectionTerminated(DisconnectCause.NOT_VALID);
+                terminateSelection(DisconnectCause.NOT_VALID);
                 return true;
             default:
                 break;
         }
 
+        // If CS call fails, retry always. Otherwise, check the reason code.
+        ImsReasonInfo reasonInfo = mSelectionAttributes.getPsDisconnectCause();
+        if (mRetryReasonCodes != null && reasonInfo != null) {
+            if (!mRetryReasonCodes.contains(reasonInfo.getCode())) {
+                // The disconnect cause saved in DomainSelectionConnection shall be used.
+                terminateSelection(DisconnectCause.NOT_VALID);
+                return true;
+            }
+        }
         return false;
     }
 
@@ -1559,9 +1648,25 @@
             // When reselecting domain, terminateSelection will be called.
             return;
         }
+        mIsWaitingForDataDisconnection = false;
+        removeMessages(MSG_WAIT_DISCONNECTION_TIMEOUT);
         terminateSelectionForCrossSimRedialing(false);
     }
 
+    /** Notifies the ePDN connection state changes. */
+    public void notifyDataConnectionStateChange(int slotId, int state) {
+        if (slotId == getSlotId() && mIsWaitingForDataDisconnection) {
+            if (state == DATA_DISCONNECTED || state == DATA_UNKNOWN) {
+                requestScanDelayed();
+            } else if (state == DATA_DISCONNECTING) {
+                logi("notifyDataConnectionStateChange deactivation starting, restart timer");
+                removeMessages(MSG_WAIT_DISCONNECTION_TIMEOUT);
+                sendEmptyMessageDelayed(MSG_WAIT_DISCONNECTION_TIMEOUT,
+                        DEFAULT_DATA_DISCONNECTION_TIMEOUT_MS);
+            }
+        }
+    }
+
     private void maybeModifyScanType(int selectedNetworkType) {
         if ((mPreferredNetworkScanType
                 != CarrierConfigManager.ImsEmergency.SCAN_TYPE_FULL_SERVICE)
@@ -1641,6 +1746,7 @@
     public void destroy() {
         if (DBG) logd("destroy");
 
+        mEpdnHelper.setEmergencyCallDomainSelector(null);
         mCrossSimRedialingController.stopTimer();
         releaseWakeLock();
 
@@ -1673,15 +1779,15 @@
     }
 
     private boolean isInEmergencyCallbackModeOnWlan() {
-        return mEcbmHelper.isInEmergencyCallbackMode(getSlotId())
-                && mEcbmHelper.getTransportType(getSlotId()) == TRANSPORT_TYPE_WLAN
-                && mEcbmHelper.getDataConnectionState(getSlotId()) == DATA_CONNECTED;
+        return mEpdnHelper.isInEmergencyCallbackMode(getSlotId())
+                && mEpdnHelper.getTransportType(getSlotId()) == TRANSPORT_TYPE_WLAN
+                && mEpdnHelper.getDataConnectionState(getSlotId()) == DATA_CONNECTED;
     }
 
     private boolean isInEmergencyCallbackModeOnPsWwan() {
-        return mEcbmHelper.isInEmergencyCallbackMode(getSlotId())
-                && mEcbmHelper.getTransportType(getSlotId()) == TRANSPORT_TYPE_WWAN
-                && mEcbmHelper.getDataConnectionState(getSlotId()) == DATA_CONNECTED;
+        return mEpdnHelper.isInEmergencyCallbackMode(getSlotId())
+                && mEpdnHelper.getTransportType(getSlotId()) == TRANSPORT_TYPE_WWAN
+                && mEpdnHelper.getDataConnectionState(getSlotId()) == DATA_CONNECTED;
     }
 
     @Override
diff --git a/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java b/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java
index 8f1a319..7e1a2e6 100644
--- a/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java
+++ b/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java
@@ -73,7 +73,7 @@
                 @NonNull ImsStateTracker imsStateTracker,
                 @NonNull DomainSelectorBase.DestroyListener listener,
                 @NonNull CrossSimRedialingController crossSimRedialingController,
-                @NonNull EmergencyCallbackModeHelper emergencyCallbackModeHelper);
+                @NonNull DataConnectionStateHelper dataConnectionStateHelper);
     }
 
     private static final class DefaultDomainSelectorFactory implements DomainSelectorFactory {
@@ -83,7 +83,7 @@
                 @NonNull ImsStateTracker imsStateTracker,
                 @NonNull DomainSelectorBase.DestroyListener listener,
                 @NonNull CrossSimRedialingController crossSimRedialingController,
-                @NonNull EmergencyCallbackModeHelper emergencyCallbackModeHelper) {
+                @NonNull DataConnectionStateHelper dataConnectionStateHelper) {
             DomainSelectorBase selector = null;
 
             logi("create-DomainSelector: slotId=" + slotId + ", subId=" + subId
@@ -95,7 +95,7 @@
                     if (isEmergency) {
                         selector = new EmergencyCallDomainSelector(context, slotId, subId, looper,
                                 imsStateTracker, listener, crossSimRedialingController,
-                                emergencyCallbackModeHelper);
+                                dataConnectionStateHelper);
                     } else {
                         selector = new NormalCallDomainSelector(context, slotId, subId, looper,
                                 imsStateTracker, listener);
@@ -199,7 +199,7 @@
     private final DomainSelectorFactory mDomainSelectorFactory;
     private Handler mServiceHandler;
     private CrossSimRedialingController mCrossSimRedialingController;
-    private EmergencyCallbackModeHelper mEmergencyCallbackModeHelper;
+    private DataConnectionStateHelper mDataConnectionStateHelper;
 
     /** Default constructor. */
     public TelephonyDomainSelectionService() {
@@ -210,9 +210,10 @@
     protected TelephonyDomainSelectionService(
             @NonNull ImsStateTrackerFactory imsStateTrackerFactory,
             @NonNull DomainSelectorFactory domainSelectorFactory,
-            @Nullable EmergencyCallbackModeHelper ecbmHelper) {
+            @Nullable DataConnectionStateHelper dataConnectionStateHelper) {
         mImsStateTrackerFactory = imsStateTrackerFactory;
         mDomainSelectorFactory = domainSelectorFactory;
+        mDataConnectionStateHelper = dataConnectionStateHelper;
     }
 
     @Override
@@ -237,8 +238,8 @@
         }
 
         mCrossSimRedialingController = new CrossSimRedialingController(mContext, getLooper());
-        if (mEmergencyCallbackModeHelper == null) {
-            mEmergencyCallbackModeHelper = new EmergencyCallbackModeHelper(mContext, getLooper());
+        if (mDataConnectionStateHelper == null) {
+            mDataConnectionStateHelper = new DataConnectionStateHelper(mContext, getLooper());
         }
 
         logi("TelephonyDomainSelectionService created");
@@ -283,9 +284,9 @@
             mCrossSimRedialingController = null;
         }
 
-        if (mEmergencyCallbackModeHelper != null) {
-            mEmergencyCallbackModeHelper.destroy();
-            mEmergencyCallbackModeHelper = null;
+        if (mDataConnectionStateHelper != null) {
+            mDataConnectionStateHelper.destroy();
+            mDataConnectionStateHelper = null;
         }
 
         if (mServiceHandler != null) {
@@ -310,7 +311,7 @@
         ImsStateTracker ist = getImsStateTracker(slotId);
         DomainSelectorBase selector = mDomainSelectorFactory.create(mContext, slotId, subId,
                 selectorType, isEmergency, getLooper(), ist, mDestroyListener,
-                mCrossSimRedialingController, mEmergencyCallbackModeHelper);
+                mCrossSimRedialingController, mDataConnectionStateHelper);
 
         if (selector != null) {
             // Ensures that ImsStateTracker is started before selecting the domain if not started
diff --git a/tests/src/com/android/phone/satellite/entitlement/SatelliteEntitlementApiTest.java b/tests/src/com/android/phone/satellite/entitlement/SatelliteEntitlementApiTest.java
index f096e0d..f7cbc55 100644
--- a/tests/src/com/android/phone/satellite/entitlement/SatelliteEntitlementApiTest.java
+++ b/tests/src/com/android/phone/satellite/entitlement/SatelliteEntitlementApiTest.java
@@ -29,6 +29,7 @@
 import static org.mockito.ArgumentMatchers.anyVararg;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
 
 import android.content.Context;
 import android.os.PersistableBundle;
@@ -39,6 +40,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.libraries.entitlement.ServiceEntitlement;
+import com.android.libraries.entitlement.ServiceEntitlementRequest;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -54,6 +56,7 @@
 public class SatelliteEntitlementApiTest {
     private static final String TEST_URL = "https://test.url";
     private static final List<String> TEST_PLMN_ALLOWED = Arrays.asList("31026", "302820");
+    private static final String TEST_APP_NAME = "androidSatmode";
     @Mock
     Context mContext;
     @Mock
@@ -83,18 +86,22 @@
 
         mSatelliteEntitlementAPI = new SatelliteEntitlementApi(mContext, mCarrierConfigBundle,
                 SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
-    }
 
-    @Test
-    public void testCheckEntitlementStatus() throws Exception {
         mCarrierConfigBundle.putString(
                 CarrierConfigManager.ImsServiceEntitlement.KEY_ENTITLEMENT_SERVER_URL_STRING,
                 TEST_URL);
+        mCarrierConfigBundle.putString(
+                CarrierConfigManager.KEY_SATELLITE_ENTITLEMENT_APP_NAME_STRING,
+                TEST_APP_NAME);
+
         Field fieldServiceEntitlement = SatelliteEntitlementApi.class.getDeclaredField(
                 "mServiceEntitlement");
         fieldServiceEntitlement.setAccessible(true);
         fieldServiceEntitlement.set(mSatelliteEntitlementAPI, mServiceEntitlement);
+    }
 
+    @Test
+    public void testCheckEntitlementStatus() throws Exception {
         // Get the EntitlementStatus to DISABLED
         int expectedEntitlementStatus = SATELLITE_ENTITLEMENT_STATUS_DISABLED;
         doReturn(getResponse(SATELLITE_ENTITLEMENT_STATUS_DISABLED))
@@ -137,6 +144,22 @@
         assertTrue(result.getAllowedPLMNList().size() == 0);
     }
 
+    @Test
+    public void testServiceEntitlementRequest() throws Exception {
+        String expectedAppId = ServiceEntitlement.APP_SATELLITE_ENTITLEMENT;
+        doReturn(getResponse(SATELLITE_ENTITLEMENT_STATUS_DISABLED))
+                .when(mServiceEntitlement)
+                .queryEntitlementStatus(eq(expectedAppId), any());
+        ServiceEntitlementRequest.Builder requestBuilder = ServiceEntitlementRequest.builder();
+        requestBuilder.setAcceptContentType(ServiceEntitlementRequest.ACCEPT_CONTENT_TYPE_JSON);
+        requestBuilder.setAppName(TEST_APP_NAME);
+        ServiceEntitlementRequest expectedRequest = requestBuilder.build();
+
+        mSatelliteEntitlementAPI.checkEntitlementStatus();
+
+        verify(mServiceEntitlement).queryEntitlementStatus(eq(expectedAppId), eq(expectedRequest));
+    }
+
     private String getResponse(int entitlementStatus) {
         return "{\"VERS\":{\"version\":\"1\",\"validity\":\"172800\"},"
                 + "\"TOKEN\":{\"token\":\"ASH127AHHA88SF\"},\""
diff --git a/tests/src/com/android/phone/satellite/entitlement/SatelliteEntitlementResponseTest.java b/tests/src/com/android/phone/satellite/entitlement/SatelliteEntitlementResponseTest.java
index 45e2a71..13d43c8 100644
--- a/tests/src/com/android/phone/satellite/entitlement/SatelliteEntitlementResponseTest.java
+++ b/tests/src/com/android/phone/satellite/entitlement/SatelliteEntitlementResponseTest.java
@@ -52,12 +52,27 @@
                     + "\"TOKEN\":{\"token\":\"ASH127AHHA88SF\"},\""
                     + ServiceEntitlement.APP_SATELLITE_ENTITLEMENT + "\":{}}";
 
+    private static final String RESPONSE_WITHOUT_PLMN =
+            "{\"VERS\":{\"version\":\"1\",\"validity\":\"172800\"},"
+                    + "\"TOKEN\":{\"token\":\"ASH127AHHA88SF\"},\""
+                    + ServiceEntitlement.APP_SATELLITE_ENTITLEMENT + "\":{"
+                    + "\"EntitlementStatus\":\"" + SATELLITE_ENTITLEMENT_STATUS_ENABLED + "\"}}";
+
+    private static final String RESPONSE_WITHOUT_PLMN_ALLOWED =
+            "{\"VERS\":{\"version\":\"1\",\"validity\":\"172800\"},"
+                    + "\"TOKEN\":{\"token\":\"ASH127AHHA88SF\"},\""
+                    + ServiceEntitlement.APP_SATELLITE_ENTITLEMENT + "\":{"
+                    + "\"EntitlementStatus\":\"" + SATELLITE_ENTITLEMENT_STATUS_ENABLED + "\"" + ","
+                    + "\"PLMNBarred\":[{\"PLMN\":\"31017\"},"
+                    + "{\"PLMN\":\"302020\"}]}}";
+
     @Test
     public void testGetSatelliteEntitlementResponse() throws Exception {
         // Received the body with satellite service enabled.
         SatelliteEntitlementResponse response = new SatelliteEntitlementResponse(
                 getResponse(SATELLITE_ENTITLEMENT_STATUS_ENABLED));
         assertEquals(SATELLITE_ENTITLEMENT_STATUS_ENABLED, response.getEntitlementStatus());
+        assertTrue(response.getPlmnAllowed().size() == 2);
         assertEquals(TEST_PLMN_DATA_PLAN_TYPE_LIST.get(0).mPlmn,
                 response.getPlmnAllowed().get(0).mPlmn);
         assertEquals(TEST_PLMN_DATA_PLAN_TYPE_LIST.get(0).mDataPlanType,
@@ -66,6 +81,7 @@
                 response.getPlmnAllowed().get(1).mPlmn);
         assertEquals(TEST_PLMN_DATA_PLAN_TYPE_LIST.get(1).mDataPlanType,
                 response.getPlmnAllowed().get(1).mDataPlanType);
+        assertTrue(response.getPlmnBarredList().size() == 2);
         assertEquals(TEST_PLMN_BARRED_LIST, response.getPlmnBarredList());
 
         // Received the empty body.
@@ -106,6 +122,50 @@
         assertEquals(SATELLITE_ENTITLEMENT_STATUS_PROVISIONING, response.getEntitlementStatus());
         assertTrue(response.getPlmnAllowed().size() == 0);
         assertTrue(response.getPlmnBarredList().size() == 0);
+
+        // Received the body without plmn.
+        response = new SatelliteEntitlementResponse(RESPONSE_WITHOUT_PLMN);
+        assertEquals(SATELLITE_ENTITLEMENT_STATUS_ENABLED, response.getEntitlementStatus());
+        assertTrue(response.getPlmnAllowed().size() == 0);
+        assertTrue(response.getPlmnBarredList().size() == 0);
+
+        // Received the body without plmn allowed key.
+        response = new SatelliteEntitlementResponse(RESPONSE_WITHOUT_PLMN_ALLOWED);
+        assertEquals(SATELLITE_ENTITLEMENT_STATUS_ENABLED, response.getEntitlementStatus());
+        assertTrue(response.getPlmnAllowed().size() == 0);
+        assertTrue(response.getPlmnBarredList().size() == 2);
+        assertEquals(TEST_PLMN_BARRED_LIST, response.getPlmnBarredList());
+
+        String plmn = "123456";
+        // Received 123456 and empty string list
+        response = new SatelliteEntitlementResponse(
+                getChangedPLMNListResponse(plmn, ""));
+        assertEquals(SATELLITE_ENTITLEMENT_STATUS_ENABLED, response.getEntitlementStatus());
+        assertTrue(response.getPlmnAllowed().size() == 1);
+        assertEquals(plmn, response.getPlmnAllowed().get(0).mPlmn);
+
+        // Received empty string and 123456 list
+        response = new SatelliteEntitlementResponse(
+                getChangedPLMNListResponse("", plmn));
+        assertEquals(SATELLITE_ENTITLEMENT_STATUS_ENABLED, response.getEntitlementStatus());
+        assertTrue(response.getPlmnAllowed().size() == 1);
+        assertEquals(plmn, response.getPlmnAllowed().get(0).mPlmn);
+
+        // Received all empty strings list
+        response = new SatelliteEntitlementResponse(
+                getChangedPLMNListResponse("", ""));
+        assertEquals(SATELLITE_ENTITLEMENT_STATUS_ENABLED, response.getEntitlementStatus());
+        assertTrue(response.getPlmnAllowed().size() == 0);
+
+        // Received null
+        response = new SatelliteEntitlementResponse(null);
+        assertEquals(SATELLITE_ENTITLEMENT_STATUS_DISABLED, response.getEntitlementStatus());
+        assertTrue(response.getPlmnAllowed().size() == 0);
+
+        // Received empty string
+        response = new SatelliteEntitlementResponse("");
+        assertEquals(SATELLITE_ENTITLEMENT_STATUS_DISABLED, response.getEntitlementStatus());
+        assertTrue(response.getPlmnAllowed().size() == 0);
     }
 
     private String getResponse(int entitlementStatus) {
@@ -124,4 +184,18 @@
                 + "\"PLMNBarred\":[{\"PLMN\":\"31017\"},"
                 + "{\"PLMN\":\"302020\"}]" : "";
     }
+
+    private String getAllowedPlmns(String firstPlmn, String secondPlmn) {
+        return ",\"PLMNAllowed\":[{\"PLMN\":\"" + firstPlmn + "\",\"DataPlanType\":\"unmetered\"},"
+                + "{\"PLMN\":\"" + secondPlmn + "\",\"DataPlanType\":\"metered\"}]";
+    }
+
+    private String getChangedPLMNListResponse(String firstPlmn, String secondPlmn) {
+        return "{\"VERS\":{\"version\":\"1\",\"validity\":\"172800\"},"
+                + "\"TOKEN\":{\"token\":\"ASH127AHHA88SF\"},\""
+                + ServiceEntitlement.APP_SATELLITE_ENTITLEMENT + "\":{"
+                + "\"EntitlementStatus\":\"" + SATELLITE_ENTITLEMENT_STATUS_ENABLED + "\""
+                + getAllowedPlmns(firstPlmn, secondPlmn)
+                + "}}";
+    }
 }
diff --git a/tests/src/com/android/services/telephony/domainselection/DataConnectionStateHelperTest.java b/tests/src/com/android/services/telephony/domainselection/DataConnectionStateHelperTest.java
new file mode 100644
index 0000000..8d950c2
--- /dev/null
+++ b/tests/src/com/android/services/telephony/domainselection/DataConnectionStateHelperTest.java
@@ -0,0 +1,376 @@
+/*
+ * 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.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
+
+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.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.LinkProperties;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.telephony.CarrierConfigManager;
+import android.telephony.PreciseDataConnectionState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
+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.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Unit tests for DataConnectionStateHelper
+ */
+public class DataConnectionStateHelperTest {
+    private static final String TAG = "DataConnectionStateHelperTest";
+
+    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;
+
+    @Mock private TelephonyManager mTm1;
+    @Mock private TelephonyManager mTm2;
+    @Mock private EmergencyCallDomainSelector mDomainSelector;
+
+    private Context mContext;
+    private HandlerThread mHandlerThread;
+    private TestableLooper mLooper;
+    private DataConnectionStateHelper mEpdnHelper;
+    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("DataConnectionStateHelperTest");
+        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(mTm1).when(mTelephonyManager).createForSubscriptionId(eq(SUB_1));
+        doReturn(mTm2).when(mTelephonyManager).createForSubscriptionId(eq(SUB_2));
+
+        mEpdnHelper = new DataConnectionStateHelper(mContext, mHandlerThread.getLooper());
+        mEpdnHelper.setEmergencyCallDomainSelector(mDomainSelector);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mEpdnHelper != null) {
+            mEpdnHelper.destroy();
+            mEpdnHelper = 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 testCarrierConfigChanged() throws Exception {
+        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> callbackCaptor =
+                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+
+        verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+                callbackCaptor.capture());
+
+        CarrierConfigManager.CarrierConfigChangeListener callback = callbackCaptor.getValue();
+
+        assertNotNull(callback);
+
+        callback.onCarrierConfigChanged(SLOT_0, SUB_1, 0, 0);
+
+        verify(mTelephonyManager).createForSubscriptionId(eq(SUB_1));
+
+        ArgumentCaptor<TelephonyCallback> telephonyCallbackCaptor1 =
+                ArgumentCaptor.forClass(TelephonyCallback.class);
+
+        // TelephonyCallback for SUB_1 registered
+        verify(mTm1).registerTelephonyCallback(any(), telephonyCallbackCaptor1.capture());
+
+        assertNotNull(telephonyCallbackCaptor1.getValue());
+
+        callback.onCarrierConfigChanged(SLOT_1, SUB_2, 0, 0);
+
+        verify(mTelephonyManager).createForSubscriptionId(eq(SUB_2));
+
+        ArgumentCaptor<TelephonyCallback> telephonyCallbackCaptor2 =
+                ArgumentCaptor.forClass(TelephonyCallback.class);
+
+        // TelephonyCallback for SUB_2 registered
+        verify(mTm2).registerTelephonyCallback(any(), telephonyCallbackCaptor2.capture());
+
+        assertNotNull(telephonyCallbackCaptor2.getValue());
+
+        verify(mTm1, never()).unregisterTelephonyCallback(any());
+        verify(mTm2, never()).unregisterTelephonyCallback(any());
+    }
+
+    @Test
+    public void testSubscriptionChangedOnTheSameSlot() throws Exception {
+        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> callbackCaptor =
+                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+
+        verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+                callbackCaptor.capture());
+
+        CarrierConfigManager.CarrierConfigChangeListener callback = callbackCaptor.getValue();
+
+        assertNotNull(callback);
+
+        callback.onCarrierConfigChanged(SLOT_0, SUB_1, 0, 0);
+
+        verify(mTelephonyManager).createForSubscriptionId(eq(SUB_1));
+
+        ArgumentCaptor<TelephonyCallback> telephonyCallbackCaptor1 =
+                ArgumentCaptor.forClass(TelephonyCallback.class);
+
+        // TelephonyCallback for SUB_1 registered
+        verify(mTm1).registerTelephonyCallback(any(), telephonyCallbackCaptor1.capture());
+
+        TelephonyCallback telephonyCallback1 = telephonyCallbackCaptor1.getValue();
+
+        assertNotNull(telephonyCallback1);
+
+        // Subscription changed
+        callback.onCarrierConfigChanged(SLOT_0, SUB_2, 0, 0);
+
+        // TelephonyCallback for SUB_1 unregistered
+        verify(mTelephonyManager).unregisterTelephonyCallback(eq(telephonyCallback1));
+
+        verify(mTelephonyManager).createForSubscriptionId(eq(SUB_2));
+
+        ArgumentCaptor<TelephonyCallback> telephonyCallbackCaptor2 =
+                ArgumentCaptor.forClass(TelephonyCallback.class);
+
+        // TelephonyCallback for SUB_2 registered
+        verify(mTm2).registerTelephonyCallback(any(), telephonyCallbackCaptor2.capture());
+
+        TelephonyCallback telephonyCallback2 = telephonyCallbackCaptor2.getValue();
+
+        assertNotNull(telephonyCallback2);
+    }
+
+    @Test
+    public void testDataConnectionStateChanged() throws Exception {
+        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> callbackCaptor =
+                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+
+        verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+                callbackCaptor.capture());
+
+        CarrierConfigManager.CarrierConfigChangeListener callback = callbackCaptor.getValue();
+
+        assertNotNull(callback);
+
+        callback.onCarrierConfigChanged(SLOT_0, SUB_1, 0, 0);
+
+        verify(mTelephonyManager).createForSubscriptionId(eq(SUB_1));
+
+        ArgumentCaptor<TelephonyCallback> telephonyCallbackCaptor1 =
+                ArgumentCaptor.forClass(TelephonyCallback.class);
+
+        // TelephonyCallback for SUB_1 registered
+        verify(mTm1).registerTelephonyCallback(any(), telephonyCallbackCaptor1.capture());
+
+        TelephonyCallback cb1 = telephonyCallbackCaptor1.getValue();
+
+        assertNotNull(cb1);
+        assertTrue(cb1 instanceof TelephonyCallback.PreciseDataConnectionStateListener);
+
+        callback.onCarrierConfigChanged(SLOT_1, SUB_2, 0, 0);
+
+        verify(mTelephonyManager).createForSubscriptionId(eq(SUB_2));
+
+        ArgumentCaptor<TelephonyCallback> telephonyCallbackCaptor2 =
+                ArgumentCaptor.forClass(TelephonyCallback.class);
+
+        // TelephonyCallback for SUB_2 registered
+        verify(mTm2).registerTelephonyCallback(any(), telephonyCallbackCaptor2.capture());
+
+        TelephonyCallback cb2 = telephonyCallbackCaptor2.getValue();
+
+        assertNotNull(cb2);
+        assertTrue(cb2 instanceof TelephonyCallback.PreciseDataConnectionStateListener);
+
+        TelephonyCallback.PreciseDataConnectionStateListener listener1 =
+                (TelephonyCallback.PreciseDataConnectionStateListener) cb1;
+        TelephonyCallback.PreciseDataConnectionStateListener listener2 =
+                (TelephonyCallback.PreciseDataConnectionStateListener) cb2;
+
+        PreciseDataConnectionState state = getPreciseDataConnectionState(
+                ApnSetting.TYPE_DEFAULT, TelephonyManager.DATA_CONNECTED);
+        listener1.onPreciseDataConnectionStateChanged(state);
+        listener2.onPreciseDataConnectionStateChanged(state);
+
+        verify(mDomainSelector, never()).notifyDataConnectionStateChange(anyInt(), anyInt());
+        verify(mDomainSelector, never()).notifyDataConnectionStateChange(anyInt(), anyInt());
+
+        state = getPreciseDataConnectionState(
+                ApnSetting.TYPE_EMERGENCY, TelephonyManager.DATA_CONNECTED);
+        listener1.onPreciseDataConnectionStateChanged(state);
+        listener2.onPreciseDataConnectionStateChanged(state);
+
+        verify(mDomainSelector, times(1)).notifyDataConnectionStateChange(
+                eq(SLOT_0), eq(TelephonyManager.DATA_CONNECTED));
+        verify(mDomainSelector, times(1)).notifyDataConnectionStateChange(
+                eq(SLOT_1), eq(TelephonyManager.DATA_CONNECTED));
+
+        state = getPreciseDataConnectionState(
+                ApnSetting.TYPE_EMERGENCY, TelephonyManager.DATA_DISCONNECTING);
+        listener1.onPreciseDataConnectionStateChanged(state);
+        listener2.onPreciseDataConnectionStateChanged(state);
+
+        verify(mDomainSelector, times(1)).notifyDataConnectionStateChange(
+                eq(SLOT_0), eq(TelephonyManager.DATA_DISCONNECTING));
+        verify(mDomainSelector, times(1)).notifyDataConnectionStateChange(
+                eq(SLOT_1), eq(TelephonyManager.DATA_DISCONNECTING));
+
+        state = getPreciseDataConnectionState(
+                ApnSetting.TYPE_EMERGENCY, TelephonyManager.DATA_DISCONNECTED);
+        listener1.onPreciseDataConnectionStateChanged(state);
+        listener2.onPreciseDataConnectionStateChanged(state);
+
+        verify(mDomainSelector, times(1)).notifyDataConnectionStateChange(
+                eq(SLOT_0), eq(TelephonyManager.DATA_DISCONNECTED));
+        verify(mDomainSelector, times(1)).notifyDataConnectionStateChange(
+                eq(SLOT_1), eq(TelephonyManager.DATA_DISCONNECTED));
+    }
+
+    @Test
+    public void testEmergencyCallbackModeEnter() throws Exception {
+        // Enter ECBM on slot 1
+        mContext.sendStickyBroadcast(getIntent(true, SLOT_1));
+
+        assertFalse(mEpdnHelper.isInEmergencyCallbackMode(SLOT_0));
+        assertTrue(mEpdnHelper.isInEmergencyCallbackMode(SLOT_1));
+    }
+
+    @Test
+    public void testEmergencyCallbackModeExit() throws Exception {
+        // Exit ECBM
+        mContext.sendStickyBroadcast(getIntent(false, SLOT_0));
+
+        assertFalse(mEpdnHelper.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 PreciseDataConnectionState getPreciseDataConnectionState(
+            int apnType, int state) {
+        return new PreciseDataConnectionState.Builder()
+                .setTransportType(TRANSPORT_TYPE_WWAN)
+                .setId(1)
+                .setState(state)
+                .setNetworkType(TelephonyManager.NETWORK_TYPE_LTE)
+                .setApnSetting(new ApnSetting.Builder()
+                        .setApnTypeBitmask(apnType)
+                        .setApnName("default")
+                        .setEntryName("default")
+                        .build())
+                .setLinkProperties(new LinkProperties())
+                .setFailCause(0)
+                .build();
+    }
+
+    private static void logd(String str) {
+        Log.d(TAG, str);
+    }
+}
diff --git a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
index 7cdc4a8..a1d4162 100644
--- a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
@@ -39,6 +39,7 @@
 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_REQUIRES_VOLTE_ENABLED_BOOL;
 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_SCAN_TIMER_SEC_INT;
 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_VOWIFI_REQUIRES_CONDITION_INT;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_IMS_REASONINFO_CODE_TO_RETRY_EMERGENCY_INT_ARRAY;
 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_MAXIMUM_CELLULAR_SEARCH_TIMER_SEC_INT;
 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_MAXIMUM_NUMBER_OF_EMERGENCY_TRIES_OVER_VOWIFI_INT;
 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_PREFER_IMS_EMERGENCY_WHEN_VOICE_CALLS_ON_CS_BOOL;
@@ -58,10 +59,13 @@
 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 android.telephony.TelephonyManager.DATA_DISCONNECTED;
+import static android.telephony.TelephonyManager.DATA_DISCONNECTING;
 import static android.telephony.TelephonyManager.SIM_ACTIVATION_STATE_DEACTIVATED;
 
 import static com.android.services.telephony.domainselection.EmergencyCallDomainSelector.MSG_MAX_CELLULAR_TIMEOUT;
 import static com.android.services.telephony.domainselection.EmergencyCallDomainSelector.MSG_NETWORK_SCAN_TIMEOUT;
+import static com.android.services.telephony.domainselection.EmergencyCallDomainSelector.MSG_WAIT_DISCONNECTION_TIMEOUT;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
@@ -156,7 +160,7 @@
     @Mock private DomainSelectorBase.DestroyListener mDestroyListener;
     @Mock private ProvisioningManager mProvisioningManager;
     @Mock private CrossSimRedialingController mCsrdCtrl;
-    @Mock private EmergencyCallbackModeHelper mEcbmHelper;
+    @Mock private DataConnectionStateHelper mEpdnHelper;
     @Mock private Resources mResources;
 
     private Context mContext;
@@ -306,6 +310,7 @@
         verify(mWwanSelectorCallback, times(0)).onRequestEmergencyNetworkScan(
                 any(), anyInt(), anyBoolean(), any(), any());
         verify(mWwanSelectorCallback, times(0)).onDomainSelected(anyInt(), eq(true));
+        verify(mEpdnHelper).setEmergencyCallDomainSelector(eq(mDomainSelector));
     }
 
     @Test
@@ -327,6 +332,7 @@
         unsolBarringInfoChanged(false);
 
         verify(mTransportSelectorCallback, never()).onWwanSelected(any());
+        verify(mEpdnHelper).setEmergencyCallDomainSelector(eq(null));
     }
 
     @Test
@@ -563,6 +569,84 @@
     }
 
     @Test
+    public void testDefaultCombinedImsRegisteredSelectPsThenExtendedServiceRequestFailIsoMatch()
+            throws Exception {
+        doReturn(new String[] {"us"}).when(mResources).getStringArray(anyInt());
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+                true, true, 0, 0, "", "", "us");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsService();
+
+        verifyPsDialed();
+
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verifyCsDialed();
+
+        //Extended service request failed
+        SelectionAttributes.Builder builder =
+                new SelectionAttributes.Builder(SLOT_0, SLOT_0_SUB_ID, SELECTOR_TYPE_CALLING)
+                .setAddress(TEST_URI)
+                .setCsDisconnectCause(SERVICE_OPTION_NOT_AVAILABLE)
+                .setEmergency(true)
+                .setEmergencyRegistrationResult(regResult);
+        attr = builder.build();
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verifyScanCsPreferred();
+    }
+
+    @Test
+    public void testDefaultCombinedImsRegisteredSelectPsThenExtendedServiceRequestFailIsoNotMatch()
+            throws Exception {
+        doReturn(new String[] {"us"}).when(mResources).getStringArray(anyInt());
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_CS | NetworkRegistrationInfo.DOMAIN_PS,
+                true, true, 0, 0, "", "", "zz");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsService();
+
+        verifyPsDialed();
+
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verifyCsDialed();
+
+        //Extended service request failed
+        SelectionAttributes.Builder builder =
+                new SelectionAttributes.Builder(SLOT_0, SLOT_0_SUB_ID, SELECTOR_TYPE_CALLING)
+                .setAddress(TEST_URI)
+                .setCsDisconnectCause(SERVICE_OPTION_NOT_AVAILABLE)
+                .setEmergency(true)
+                .setEmergencyRegistrationResult(regResult);
+        attr = builder.build();
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verifyScanPsPreferred();
+    }
+
+    @Test
     public void testDefaultCombinedImsRegisteredSelectPsThenNotExtendedServiceRequestFails()
             throws Exception {
         createSelector(SLOT_0_SUB_ID);
@@ -1854,17 +1938,43 @@
     }
 
     @Test
-    public void testFullServiceInRoaming() throws Exception {
+    public void testFullServiceInDomesticRoaming() throws Exception {
         PersistableBundle bundle = getDefaultPersistableBundle();
         bundle.putInt(KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT, SCAN_TYPE_FULL_SERVICE);
         when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+        doReturn("us").when(mTelephonyManager).getSimCountryIso();
 
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(true);
 
         EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
                 REGISTRATION_STATE_ROAMING,
-                0, true, false, 0, 0, "", "");
+                0, true, false, 0, 0, "", "", "us");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+        processAllMessages();
+
+        verify(mWwanSelectorCallback).onRequestEmergencyNetworkScan(
+                any(), eq(DomainSelectionService.SCAN_TYPE_FULL_SERVICE),
+                anyBoolean(), any(), any());
+    }
+
+    @Test
+    public void testFullServiceInInterNationalRoaming() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putInt(KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT, SCAN_TYPE_FULL_SERVICE);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+        doReturn("us").when(mTelephonyManager).getSimCountryIso();
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(true);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(EUTRAN,
+                REGISTRATION_STATE_ROAMING,
+                0, true, false, 0, 0, "", "", "zz");
         SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
         mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
         processAllMessages();
@@ -2817,12 +2927,51 @@
     }
 
     @Test
-    public void testScanLtePreferredAfterNgranFailure() throws Exception {
+    public void testScanLtePreferredAfterNgranFailureSupportEmf() throws Exception {
         PersistableBundle bundle = getDefaultPersistableBundle();
         bundle.putIntArray(KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY,
                 new int[] { NGRAN, EUTRAN });
         bundle.putBoolean(KEY_EMERGENCY_LTE_PREFERRED_AFTER_NR_FAILED_BOOL, true);
         when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+        doReturn(DATA_CONNECTED).when(mEpdnHelper).getDataConnectionState(anyInt());
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(NGRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_PS, true, false, 1, 1, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyPsDialed();
+
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        // Verify not waiting for the disconnection in case EMF is supported.
+        assertFalse(mDomainSelector.hasMessages(MSG_WAIT_DISCONNECTION_TIMEOUT));
+
+        verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
+                any(), anyInt(), anyBoolean(), any(), any());
+        assertEquals(4, mAccessNetwork.size());
+        assertEquals(EUTRAN, (int) mAccessNetwork.get(0));
+        assertEquals(UTRAN, (int) mAccessNetwork.get(1));
+        assertEquals(GERAN, (int) mAccessNetwork.get(2));
+        assertEquals(NGRAN, (int) mAccessNetwork.get(3));
+    }
+
+    @Test
+    public void testScanLtePreferredAfterNgranFailureNotSupportEmf() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putIntArray(KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY,
+                new int[] { NGRAN, EUTRAN });
+        bundle.putBoolean(KEY_EMERGENCY_LTE_PREFERRED_AFTER_NR_FAILED_BOOL, true);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+        doReturn(DATA_CONNECTED).when(mEpdnHelper).getDataConnectionState(anyInt());
 
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
@@ -2841,6 +2990,176 @@
         mDomainSelector.reselectDomain(attr);
         processAllMessages();
 
+        // Verify waiting for the disconnection in case EMF is supported.
+        assertTrue(mDomainSelector.hasMessages(MSG_WAIT_DISCONNECTION_TIMEOUT));
+
+        mDomainSelector.removeMessages(MSG_WAIT_DISCONNECTION_TIMEOUT);
+        processAllMessages();
+
+        verify(mWwanSelectorCallback, never()).onRequestEmergencyNetworkScan(
+                any(), anyInt(), anyBoolean(), any(), any());
+
+        mDomainSelector.sendEmptyMessage(MSG_WAIT_DISCONNECTION_TIMEOUT);
+        processAllMessages();
+
+        verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
+                any(), anyInt(), anyBoolean(), any(), any());
+        assertEquals(4, mAccessNetwork.size());
+        assertEquals(EUTRAN, (int) mAccessNetwork.get(0));
+        assertEquals(UTRAN, (int) mAccessNetwork.get(1));
+        assertEquals(GERAN, (int) mAccessNetwork.get(2));
+        assertEquals(NGRAN, (int) mAccessNetwork.get(3));
+    }
+
+    @Test
+    public void testScanLtePreferredAfterNgranFailureRestartWaitingTimer() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putIntArray(KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY,
+                new int[] { NGRAN, EUTRAN });
+        bundle.putBoolean(KEY_EMERGENCY_LTE_PREFERRED_AFTER_NR_FAILED_BOOL, true);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+        doReturn(DATA_CONNECTED).when(mEpdnHelper).getDataConnectionState(anyInt());
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(NGRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_PS, true, false, 1, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyPsDialed();
+
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        assertTrue(mDomainSelector.hasMessages(MSG_WAIT_DISCONNECTION_TIMEOUT));
+
+        mDomainSelector.removeMessages(MSG_WAIT_DISCONNECTION_TIMEOUT);
+
+        assertFalse(mDomainSelector.hasMessages(MSG_WAIT_DISCONNECTION_TIMEOUT));
+
+        mDomainSelector.notifyDataConnectionStateChange(SLOT_0, DATA_DISCONNECTING);
+
+        assertTrue(mDomainSelector.hasMessages(MSG_WAIT_DISCONNECTION_TIMEOUT));
+    }
+
+    @Test
+    public void testScanLtePreferredAfterNgranFailureDataDisconnected() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putIntArray(KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY,
+                new int[] { NGRAN, EUTRAN });
+        bundle.putBoolean(KEY_EMERGENCY_LTE_PREFERRED_AFTER_NR_FAILED_BOOL, true);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+        doReturn(DATA_CONNECTED).when(mEpdnHelper).getDataConnectionState(anyInt());
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(NGRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_PS, true, false, 1, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyPsDialed();
+
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        assertTrue(mDomainSelector.hasMessages(MSG_WAIT_DISCONNECTION_TIMEOUT));
+
+        mDomainSelector.removeMessages(MSG_WAIT_DISCONNECTION_TIMEOUT);
+        processAllMessages();
+
+        verify(mWwanSelectorCallback, never()).onRequestEmergencyNetworkScan(
+                any(), anyInt(), anyBoolean(), any(), any());
+
+        mDomainSelector.notifyDataConnectionStateChange(SLOT_0, DATA_DISCONNECTED);
+
+        verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
+                any(), anyInt(), anyBoolean(), any(), any());
+        assertEquals(4, mAccessNetwork.size());
+        assertEquals(EUTRAN, (int) mAccessNetwork.get(0));
+        assertEquals(UTRAN, (int) mAccessNetwork.get(1));
+        assertEquals(GERAN, (int) mAccessNetwork.get(2));
+        assertEquals(NGRAN, (int) mAccessNetwork.get(3));
+    }
+
+    @Test
+    public void testDefaultAfterNgranFailureNotSupportEmf() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putIntArray(KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY,
+                new int[] { NGRAN, EUTRAN });
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+        doReturn(DATA_CONNECTED).when(mEpdnHelper).getDataConnectionState(anyInt());
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(NGRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_PS, true, false, 1, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyPsDialed();
+
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        // Verify not waiting for the disconnection
+        // in case KEY_EMERGENCY_LTE_PREFERRED_AFTER_NR_FAILED_BOOL is false.
+        assertFalse(mDomainSelector.hasMessages(MSG_WAIT_DISCONNECTION_TIMEOUT));
+
+        verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
+                any(), anyInt(), anyBoolean(), any(), any());
+        assertEquals(4, mAccessNetwork.size());
+        assertEquals(UTRAN, (int) mAccessNetwork.get(0));
+        assertEquals(GERAN, (int) mAccessNetwork.get(1));
+        assertEquals(NGRAN, (int) mAccessNetwork.get(2));
+        assertEquals(EUTRAN, (int) mAccessNetwork.get(3));
+    }
+
+    @Test
+    public void testDefaultAfterNgranFailureSupportEmf() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putIntArray(KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY,
+                new int[] { NGRAN, EUTRAN });
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+        doReturn(DATA_CONNECTED).when(mEpdnHelper).getDataConnectionState(anyInt());
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult regResult = getEmergencyRegResult(NGRAN,
+                REGISTRATION_STATE_HOME,
+                NetworkRegistrationInfo.DOMAIN_PS, true, false, 1, 1, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+
+        verifyPsDialed();
+
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        // Verify not waiting for the disconnection
+        // in case KEY_EMERGENCY_LTE_PREFERRED_AFTER_NR_FAILED_BOOL is false.
+        assertFalse(mDomainSelector.hasMessages(MSG_WAIT_DISCONNECTION_TIMEOUT));
+
         verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
                 any(), anyInt(), anyBoolean(), any(), any());
         assertEquals(4, mAccessNetwork.size());
@@ -2944,9 +3263,9 @@
         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());
+        doReturn(true).when(mEpdnHelper).isInEmergencyCallbackMode(anyInt());
+        doReturn(TRANSPORT_TYPE_WWAN).when(mEpdnHelper).getTransportType(anyInt());
+        doReturn(DATA_CONNECTED).when(mEpdnHelper).getDataConnectionState(anyInt());
 
         doAnswer(new Answer<Void>() {
             @Override
@@ -2978,9 +3297,9 @@
         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());
+        doReturn(true).when(mEpdnHelper).isInEmergencyCallbackMode(anyInt());
+        doReturn(TRANSPORT_TYPE_WLAN).when(mEpdnHelper).getTransportType(anyInt());
+        doReturn(DATA_CONNECTED).when(mEpdnHelper).getDataConnectionState(anyInt());
 
         EmergencyRegistrationResult regResult = getEmergencyRegResult(UNKNOWN,
                 REGISTRATION_STATE_UNKNOWN,
@@ -3001,8 +3320,8 @@
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
-        doReturn(true).when(mEcbmHelper).isInEmergencyCallbackMode(anyInt());
-        doReturn(TRANSPORT_TYPE_WLAN).when(mEcbmHelper).getTransportType(anyInt());
+        doReturn(true).when(mEpdnHelper).isInEmergencyCallbackMode(anyInt());
+        doReturn(TRANSPORT_TYPE_WLAN).when(mEpdnHelper).getTransportType(anyInt());
 
         EmergencyRegistrationResult regResult = getEmergencyRegResult(UNKNOWN,
                 REGISTRATION_STATE_UNKNOWN,
@@ -3023,9 +3342,9 @@
         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());
+        doReturn(false).when(mEpdnHelper).isInEmergencyCallbackMode(anyInt());
+        doReturn(TRANSPORT_TYPE_WLAN).when(mEpdnHelper).getTransportType(anyInt());
+        doReturn(DATA_CONNECTED).when(mEpdnHelper).getDataConnectionState(anyInt());
 
         EmergencyRegistrationResult regResult = getEmergencyRegResult(UNKNOWN,
                 REGISTRATION_STATE_UNKNOWN,
@@ -3046,9 +3365,9 @@
         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());
+        doReturn(false).when(mEpdnHelper).isInEmergencyCallbackMode(anyInt());
+        doReturn(TRANSPORT_TYPE_WLAN).when(mEpdnHelper).getTransportType(anyInt());
+        doReturn(DATA_CONNECTED).when(mEpdnHelper).getDataConnectionState(anyInt());
 
         doAnswer(new Answer<Void>() {
             @Override
@@ -3162,6 +3481,162 @@
                 .onSelectionTerminated(eq(DisconnectCause.NOT_VALID));
     }
 
+    @Test
+    public void testReceiveSipErrorThenTerminateSelection() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult 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();
+
+        attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, false, regResult,
+                new ImsReasonInfo(ImsReasonInfo.CODE_SIP_FORBIDDEN, 0, null));
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verify(mTransportSelectorCallback)
+                .onSelectionTerminated(eq(DisconnectCause.NOT_VALID));
+    }
+
+    @Test
+    public void testReceiveSipErrorThenNotTerminateSelection() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putIntArray(KEY_IMS_REASONINFO_CODE_TO_RETRY_EMERGENCY_INT_ARRAY,
+                new int[] { ImsReasonInfo.CODE_SIP_FORBIDDEN });
+        when(mCarrierConfigManager.getConfigForSubId(anyInt(), anyVararg())).thenReturn(bundle);
+
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult 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();
+
+        attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, false, regResult,
+                new ImsReasonInfo(ImsReasonInfo.CODE_SIP_FORBIDDEN, 0, null));
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verifyCsDialed();
+    }
+
+    @Test
+    public void testRetryWithCodeLocalCallCsRetryRequired() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult 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();
+
+        attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, false, regResult,
+                new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED, 0, null));
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verifyCsDialed();
+    }
+
+    @Test
+    public void testRetryWithCodeLocalNotRegistered() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult 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();
+
+        attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, false, regResult,
+                new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED, 0, null));
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verifyCsDialed();
+    }
+
+    @Test
+    public void testRetryWithCodeSipAlternateEemergencyCall() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult 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();
+
+        attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, false, regResult,
+                new ImsReasonInfo(ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL, 0, null));
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verifyCsDialed();
+    }
+
+    @Test
+    public void testRetryWithCodeLocalInternalError() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegistrationResult 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();
+
+        attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, false, regResult,
+                new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_INTERNAL_ERROR, 0, null));
+        mDomainSelector.reselectDomain(attr);
+        processAllMessages();
+
+        verifyCsDialed();
+    }
+
     private void setupForScanListTest(PersistableBundle bundle) throws Exception {
         setupForScanListTest(bundle, false);
     }
@@ -3237,7 +3712,7 @@
     private void createSelector(int subId) throws Exception {
         mDomainSelector = new EmergencyCallDomainSelector(
                 mContext, SLOT_0, subId, mHandlerThread.getLooper(),
-                mImsStateTracker, mDestroyListener, mCsrdCtrl, mEcbmHelper);
+                mImsStateTracker, mDestroyListener, mCsrdCtrl, mEpdnHelper);
         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
deleted file mode 100644
index 9a4e0d8..0000000
--- a/tests/src/com/android/services/telephony/domainselection/EmergencyCallbackModeHelperTest.java
+++ /dev/null
@@ -1,305 +0,0 @@
-/*
- * 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 40a4616..7031bf3 100644
--- a/tests/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionServiceTest.java
@@ -82,7 +82,7 @@
                         @NonNull Looper looper, @NonNull ImsStateTracker imsStateTracker,
                         @NonNull DomainSelectorBase.DestroyListener listener,
                         @NonNull CrossSimRedialingController crossSimRedialingController,
-                        @NonNull EmergencyCallbackModeHelper ecbmHelper) {
+                        @NonNull DataConnectionStateHelper dataConnectionStateHelper) {
                     switch (selectorType) {
                         case DomainSelectionService.SELECTOR_TYPE_CALLING: // fallthrough
                         case DomainSelectionService.SELECTOR_TYPE_SMS:
@@ -104,8 +104,9 @@
         TestTelephonyDomainSelectionService(Context context,
                 @NonNull ImsStateTrackerFactory imsStateTrackerFactory,
                 @NonNull DomainSelectorFactory domainSelectorFactory,
-                @Nullable EmergencyCallbackModeHelper ecbmHelper) {
-            super(imsStateTrackerFactory, domainSelectorFactory, ecbmHelper);
+                @Nullable DataConnectionStateHelper dataConnectionStateHelper) {
+            super(imsStateTrackerFactory, domainSelectorFactory,
+                    dataConnectionStateHelper);
             mContext = context;
         }
 
@@ -129,7 +130,7 @@
     @Mock private TransportSelectorCallback mSelectorCallback1;
     @Mock private TransportSelectorCallback mSelectorCallback2;
     @Mock private ImsStateTracker mImsStateTracker;
-    @Mock private EmergencyCallbackModeHelper mEcbmHelper;
+    @Mock private DataConnectionStateHelper mDataConnectionStateHelper;
 
     private final ServiceState mServiceState = new ServiceState();
     private final BarringInfo mBarringInfo = new BarringInfo();
@@ -151,7 +152,8 @@
 
         mContext = new TestContext();
         mDomainSelectionService = new TestTelephonyDomainSelectionService(mContext,
-                mImsStateTrackerFactory, mDomainSelectorFactory, mEcbmHelper);
+                mImsStateTrackerFactory, mDomainSelectorFactory,
+                mDataConnectionStateHelper);
         mDomainSelectionService.onCreate();
         mServiceHandler = new Handler(mDomainSelectionService.getLooper());
         mTestableLooper = new TestableLooper(mDomainSelectionService.getLooper());