Merge "Store the reference to Phone to handle asynchronous response with the Phone"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 22d007e..461b18e 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -424,6 +424,10 @@
                             "android.telephony.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS" />
                 <action android:name=
                             "android.telephony.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION" />
+                <action android:name=
+                    "android.telephony.euicc.action.TRANSFER_EMBEDDED_SUBSCRIPTIONS" />
+                <action android:name=
+                    "android.telephony.euicc.action.CONVERT_TO_EMBEDDED_SUBSCRIPTION" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 78a734a..200dc75 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -527,6 +527,8 @@
 
             imsRcsController = ImsRcsController.init(this);
 
+            configLoader = CarrierConfigLoader.init(this);
+
             if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS)) {
                 mImsStateCallbackController =
                         ImsStateCallbackController.make(this, PhoneFactory.getPhones().length);
@@ -538,8 +540,6 @@
                         ImsProvisioningController.make(this, PhoneFactory.getPhones().length);
             }
 
-            configLoader = CarrierConfigLoader.init(this);
-
             // Create the CallNotifier singleton, which handles
             // asynchronous events from the telephony layer (like
             // launching the incoming-call UI when an incoming call comes
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index b7dfb77..5fe6717 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -143,6 +143,9 @@
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.stub.ImsConfigImplBase;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.satellite.ISatellitePositionUpdateCallback;
+import android.telephony.satellite.PointingInfo;
+import android.telephony.satellite.SatelliteManager;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.EventLog;
@@ -185,6 +188,7 @@
 import com.android.internal.telephony.ProxyController;
 import com.android.internal.telephony.RIL;
 import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.RILUtils;
 import com.android.internal.telephony.RadioInterfaceCapabilityController;
 import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.SmsApplication;
@@ -251,6 +255,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Consumer;
@@ -376,6 +381,10 @@
     private static final int EVENT_IS_VONR_ENABLED_DONE = 116;
     private static final int CMD_PURCHASE_PREMIUM_CAPABILITY = 117;
     private static final int EVENT_PURCHASE_PREMIUM_CAPABILITY_DONE = 118;
+    private static final int CMD_START_SATELLITE_POSITION_UPDATES = 119;
+    private static final int EVENT_START_SATELLITE_POSITION_UPDATES_DONE = 120;
+    private static final int CMD_STOP_SATELLITE_POSITION_UPDATES = 121;
+    private static final int EVENT_STOP_SATELLITE_POSITION_UPDATES_DONE = 122;
     // Parameters of select command.
     private static final int SELECT_COMMAND = 0xA4;
     private static final int SELECT_P1 = 0x04;
@@ -406,6 +415,8 @@
     private static final int USER_ACTIVITY_NOTIFICATION_DELAY = 200;
 
     private Set<Integer> mCarrierPrivilegeTestOverrideSubIds = new ArraySet<>();
+    private Map<Integer, SatellitePositionUpdateHandler> mSatellitePositionUpdateHandlers =
+            new ConcurrentHashMap<>();
 
     private static final String PREF_CARRIERS_ALPHATAG_PREFIX = "carrier_alphtag_";
     private static final String PREF_CARRIERS_NUMBER_PREFIX = "carrier_number_";
@@ -501,6 +512,46 @@
         }
     }
 
+    private static final class SatellitePositionUpdateHandler extends Handler {
+        public static final int EVENT_POSITION_UPDATE = 1;
+        public static final int EVENT_MESSAGE_TRANSFER_STATE_UPDATE = 2;
+
+        private final ISatellitePositionUpdateCallback mCallback;
+
+        SatellitePositionUpdateHandler(ISatellitePositionUpdateCallback callback, Looper looper) {
+            super(looper);
+            mCallback = callback;
+        }
+
+        @Override
+        public void handleMessage(@NonNull Message msg) {
+            switch (msg.what) {
+                case EVENT_POSITION_UPDATE: {
+                    AsyncResult ar = (AsyncResult) msg.obj;
+                    PointingInfo pointingInfo = (PointingInfo) ar.result;
+                    try {
+                        mCallback.onSatellitePositionUpdate(pointingInfo);
+                    } catch (RemoteException e) {
+                        loge("EVENT_POSITION_UPDATE RemoteException: " + e);
+                    }
+                    break;
+                }
+                case EVENT_MESSAGE_TRANSFER_STATE_UPDATE: {
+                    AsyncResult ar = (AsyncResult) msg.obj;
+                    int state = (int) ar.result;
+                    try {
+                        mCallback.onMessageTransferStateUpdate(state);
+                    } catch (RemoteException e) {
+                        loge("EVENT_MESSAGE_TRANSFER_STATE_UPDATE RemoteException: " + e);
+                    }
+                    break;
+                }
+                default:
+                    loge("SatellitePositionUpdateHandler unknown event: " + msg.what);
+            }
+        }
+    }
+
     /**
      * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
      * request after sending. The main thread will notify the request when it is complete.
@@ -2247,6 +2298,76 @@
                     notifyRequester(request);
                     break;
 
+                case CMD_START_SATELLITE_POSITION_UPDATES: {
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted =
+                            obtainMessage(EVENT_START_SATELLITE_POSITION_UPDATES_DONE, request);
+                    Phone phone = getPhoneFromRequest(request);
+                    if (phone != null) {
+                        phone.startSatellitePositionUpdates(onCompleted);
+                    } else {
+                        loge("startSatellitePositionUpdates: No phone object");
+                        request.result = SatelliteManager.SATELLITE_SERVICE_REQUEST_FAILED;
+                        notifyRequester(request);
+                    }
+                    break;
+                }
+
+                case EVENT_START_SATELLITE_POSITION_UPDATES_DONE: {
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    if (ar.exception == null) {
+                        request.result = SatelliteManager.SATELLITE_SERVICE_SUCCESS;
+                    } else {
+                        request.result = SatelliteManager.SATELLITE_SERVICE_ERROR;
+                        if (ar.exception instanceof CommandException) {
+                            CommandException.Error error =
+                                    ((CommandException) (ar.exception)).getCommandError();
+                            request.result = RILUtils.convertToSatelliteError(error);
+                            loge("startSatellitePositionUpdates CommandException: " + ar.exception);
+                        } else {
+                            loge("startSatellitePositionUpdates unknown exception:" + ar.exception);
+                        }
+                    }
+                    notifyRequester(request);
+                    break;
+                }
+
+                case CMD_STOP_SATELLITE_POSITION_UPDATES: {
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted =
+                            obtainMessage(EVENT_STOP_SATELLITE_POSITION_UPDATES_DONE, request);
+                    Phone phone = getPhoneFromRequest(request);
+                    if (phone != null) {
+                        phone.stopSatellitePositionUpdates(onCompleted);
+                    } else {
+                        loge("stopSatellitePositionUpdates: No phone object");
+                        request.result = SatelliteManager.SATELLITE_SERVICE_REQUEST_FAILED;
+                        notifyRequester(request);
+                    }
+                    break;
+                }
+
+                case EVENT_STOP_SATELLITE_POSITION_UPDATES_DONE: {
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    if (ar.exception == null) {
+                        request.result = SatelliteManager.SATELLITE_SERVICE_SUCCESS;
+                    } else {
+                        request.result = SatelliteManager.SATELLITE_SERVICE_ERROR;
+                        if (ar.exception instanceof CommandException) {
+                            CommandException.Error error =
+                                    ((CommandException) (ar.exception)).getCommandError();
+                            request.result = RILUtils.convertToSatelliteError(error);
+                            loge("stopSatellitePositionUpdates CommandException: " + ar.exception);
+                        } else {
+                            loge("stopSatellitePositionUpdates unknown exception:" + ar.exception);
+                        }
+                    }
+                    notifyRequester(request);
+                    break;
+                }
+
                 default:
                     Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
                     break;
@@ -2953,6 +3074,10 @@
     public boolean setRadioPower(boolean turnOn) {
         enforceModifyPermission();
 
+        if (!turnOn) {
+            log("setRadioPower off: callingPackage=" + getCurrentPackageName());
+        }
+
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone defaultPhone = PhoneFactory.getDefaultPhone();
@@ -2971,6 +3096,10 @@
     public boolean setRadioPowerForSubscriber(int subId, boolean turnOn) {
         enforceModifyPermission();
 
+        if (!turnOn) {
+            log("setRadioPowerForSubscriber off: subId=" + subId
+                    + ",callingPackage=" + getCurrentPackageName());
+        }
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -3005,6 +3134,8 @@
             @TelephonyManager.RadioPowerReason int reason) {
         enforceModifyPermission();
 
+        log("requestRadioPowerOffForReason: subId=" + subId
+                + ",reason=" + reason + ",callingPackage=" + getCurrentPackageName());
         final long identity = Binder.clearCallingIdentity();
         try {
             final Phone phone = getPhone(subId);
@@ -3776,7 +3907,7 @@
     }
 
     private static void log(String msg) {
-        Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
+        Log.d(LOG_TAG, msg);
     }
 
     private static void logv(String msg) {
@@ -6810,12 +6941,10 @@
         // any network types which were set for security reasons.
         if (mApp.checkCallingOrSelfPermission(Manifest.permission.MODIFY_PHONE_STATE)
                 != PERMISSION_GRANTED
-                && (reason == TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G
-                || reason == TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER_RESTRICTIONS)) {
+                && reason == TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G) {
             throw new SecurityException(
                     "setAllowedNetworkTypesForReason cannot be called with carrier privileges for"
-                            + " reason "
-                            + reason);
+                            + " reason " + reason);
         }
         if (!TelephonyManager.isValidAllowedNetworkTypesReason(reason)) {
             loge("setAllowedNetworkTypesForReason: Invalid allowed network type reason: " + reason);
@@ -12038,6 +12167,94 @@
     }
 
     /**
+     * Start receiving satellite position updates.
+     * This can be called by the pointing UI when the user starts pointing to the satellite.
+     * Modem should continue to report the pointing input as the device or satellite moves.
+     *
+     * @param subId The subId to start satellite position updates for.
+     * @param callbackId The callback ID associating the public SatellitePositionUpdateCallback to
+     *                   the internal ISatellitePositionUpdateCallback below.
+     * @param callback The callback to notify of changes in satellite position.
+     * @return The result of the operation.
+     */
+    @Override
+    @SatelliteManager.SatelliteServiceResult public int startSatellitePositionUpdates(int subId,
+            int callbackId, @NonNull ISatellitePositionUpdateCallback callback) {
+        // TODO: check for SATELLITE_COMMUNICATION permission
+        Phone phone = getPhone(subId);
+        if (phone == null) {
+            loge("startSatellitePositionUpdates called with invalid subId: " + subId
+                    + ". Retrying with default phone.");
+            phone = getDefaultPhone();
+            if (phone == null) {
+                loge("startSatellitePositionUpdates failed with no phone object.");
+                return SatelliteManager.SATELLITE_SERVICE_REQUEST_FAILED;
+            }
+        }
+
+        if (mSatellitePositionUpdateHandlers.containsKey(callbackId)) {
+            log("startSatellitePositionUpdates: callback already registered: " + callbackId);
+            return SatelliteManager.SATELLITE_SERVICE_SUCCESS;
+        }
+
+        SatellitePositionUpdateHandler handler =
+                new SatellitePositionUpdateHandler(callback, Looper.getMainLooper());
+        phone.registerForSatellitePointingInfoChanged(handler,
+                SatellitePositionUpdateHandler.EVENT_POSITION_UPDATE, null);
+        phone.registerForSatelliteMessagesTransferComplete(handler,
+                SatellitePositionUpdateHandler.EVENT_MESSAGE_TRANSFER_STATE_UPDATE, null);
+        mSatellitePositionUpdateHandlers.put(callbackId, handler);
+
+        int result = (int) sendRequest(CMD_START_SATELLITE_POSITION_UPDATES, null, subId);
+        if (DBG) log("startSatellitePositionUpdates result: " + result);
+        return result;
+    }
+
+    /**
+     * Stop receiving satellite position updates.
+     * This can be called by the pointing UI when the user stops pointing to the satellite.
+     *
+     * @param subId The subId to stop satellite position updates for.
+     * @param callbackId The ID of the callback that was passed in {@link
+     *                   #startSatellitePositionUpdates(int, int, ISatellitePositionUpdateCallback)}
+     * @return The result of the operation.
+     */
+    @Override
+    @SatelliteManager.SatelliteServiceResult public int stopSatellitePositionUpdates(int subId,
+            int callbackId) {
+        // TODO: check for SATELLITE_COMMUNICATION permission
+        Phone phone = getPhone(subId);
+        if (phone == null) {
+            loge("stopSatellitePositionUpdates called with invalid subId: " + subId
+                    + ". Retrying with default phone.");
+            phone = getDefaultPhone();
+            if (phone == null) {
+                loge("stopSatellitePositionUpdates failed with no phone object.");
+                return SatelliteManager.SATELLITE_SERVICE_REQUEST_FAILED;
+            }
+        }
+
+        SatellitePositionUpdateHandler handler =
+                mSatellitePositionUpdateHandlers.remove(callbackId);
+        if (handler == null) {
+            loge("stopSatellitePositionUpdates: No SatellitePositionArgument");
+            return SatelliteManager.SATELLITE_SERVICE_REQUEST_FAILED;
+        } else {
+            phone.unregisterForSatellitePointingInfoChanged(handler);
+            phone.unregisterForSatelliteMessagesTransferComplete(handler);
+        }
+
+        if (!mSatellitePositionUpdateHandlers.isEmpty()) {
+            log("stopSatellitePositionUpdates: other listeners still exist.");
+            return SatelliteManager.SATELLITE_SERVICE_SUCCESS;
+        }
+
+        int result = (int) sendRequest(CMD_STOP_SATELLITE_POSITION_UPDATES, null, subId);
+        if (DBG) log("stopSatellitePositionUpdates result: " + result);
+        return result;
+    }
+
+    /**
      * Check whether the caller (or self, if not processing an IPC) can read device identifiers.
      *
      * <p>This method behaves in one of the following ways:
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index f002e25..1858fe2 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -57,6 +57,7 @@
 import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.modules.utils.BasicShellCommandHandler;
 import com.android.phone.callcomposer.CallComposerPictureManager;
+import com.android.phone.euicc.EuiccUiDispatcherActivity;
 import com.android.phone.utils.CarrierAllowListInfo;
 
 import java.io.IOException;
@@ -121,6 +122,9 @@
     private static final String CC_SET_VALUES_FROM_XML = "set-values-from-xml";
     private static final String CC_CLEAR_VALUES = "clear-values";
 
+    private static final String EUICC_SUBCOMMAND = "euicc";
+    private static final String EUICC_SET_UI_COMPONENT = "set-euicc-uicomponent";
+
     private static final String GBA_SUBCOMMAND = "gba";
     private static final String GBA_SET_SERVICE = "set-service";
     private static final String GBA_GET_SERVICE = "get-service";
@@ -313,6 +317,8 @@
                 return handleDataTestModeCommand();
             case END_BLOCK_SUPPRESSION:
                 return handleEndBlockSuppressionCommand();
+            case EUICC_SUBCOMMAND:
+                return handleEuiccCommand();
             case GBA_SUBCOMMAND:
                 return handleGbaCommand();
             case D2D_SUBCOMMAND:
@@ -608,6 +614,15 @@
         pw.println("          is specified, it will choose the default voice SIM slot.");
     }
 
+    private void onHelpEuicc() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("Euicc Commands:");
+        pw.println("  euicc set-euicc-uicomponent COMPONENT_NAME PACKAGE_NAME");
+        pw.println("  Sets the Euicc Ui-Component which handles EuiccService Actions.");
+        pw.println("  COMPONENT_NAME: The component name which handles UI Actions.");
+        pw.println("  PACKAGE_NAME: THe package name in which ui component belongs.");
+    }
+
     private void onHelpGba() {
         PrintWriter pw = getOutPrintWriter();
         pw.println("Gba Commands:");
@@ -2058,6 +2073,35 @@
         return 0;
     }
 
+    private int handleEuiccCommand() {
+        String arg = getNextArg();
+        if (arg == null) {
+            onHelpEuicc();
+            return 0;
+        }
+
+        switch (arg) {
+            case EUICC_SET_UI_COMPONENT: {
+                return handleEuiccServiceCommand();
+            }
+        }
+        return -1;
+    }
+
+    private int handleEuiccServiceCommand() {
+        String uiComponent = getNextArg();
+        String packageName = getNextArg();
+        if (packageName == null || uiComponent == null) {
+            return -1;
+        }
+        EuiccUiDispatcherActivity.setTestEuiccUiComponent(packageName, uiComponent);
+        if (VDBG) {
+            Log.v(LOG_TAG, "euicc set-euicc-uicomponent " + uiComponent +" "
+                    + packageName);
+        }
+        return 0;
+    }
+
     private int handleRestartModemCommand() {
         // Verify that the user is allowed to run the command. Only allowed in rooted device in a
         // non user build.
diff --git a/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java b/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
index 804611f..5da52d6 100644
--- a/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
+++ b/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
@@ -29,6 +29,7 @@
 import android.permission.LegacyPermissionManager;
 import android.service.euicc.EuiccService;
 import android.telephony.euicc.EuiccManager;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -58,6 +59,8 @@
     private LegacyPermissionManager mPermissionManager;
     private boolean mGrantPermissionDone = false;
     private ThreadPoolExecutor mExecutor;
+    // Used for CTS EuiccManager action verification
+    private static ComponentName mTestEuiccUiComponentName;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -94,6 +97,18 @@
         }
     }
 
+    /**
+    * This API used to set the Test EuiccUiComponent for CTS
+    * @param packageName package which handles the intent
+    * @param componentName ui component to be launched for testing
+    */
+    public static void setTestEuiccUiComponent(String packageName, String componentName) {
+        mTestEuiccUiComponentName = null;
+        if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(componentName)) {
+            mTestEuiccUiComponentName = new ComponentName(packageName, componentName);
+        }
+    }
+
     @VisibleForTesting
     @Nullable
     Intent resolveEuiccUiIntent() {
@@ -109,6 +124,13 @@
             return null;
         }
 
+        if (mTestEuiccUiComponentName != null) {
+            Log.i(TAG, "Test mode");
+            euiccUiIntent.setComponent(mTestEuiccUiComponentName);
+            mTestEuiccUiComponentName = null;
+            return euiccUiIntent;
+        }
+
         revokePermissionFromLuiApps(euiccUiIntent);
 
         ActivityInfo activityInfo = findBestActivity(euiccUiIntent);
@@ -148,6 +170,12 @@
             case EuiccManager.ACTION_PROVISION_EMBEDDED_SUBSCRIPTION:
                 intent.setAction(EuiccService.ACTION_PROVISION_EMBEDDED_SUBSCRIPTION);
                 break;
+            case EuiccManager.ACTION_TRANSFER_EMBEDDED_SUBSCRIPTIONS:
+                intent.setAction(EuiccService.ACTION_TRANSFER_EMBEDDED_SUBSCRIPTIONS);
+                break;
+            case EuiccManager.ACTION_CONVERT_TO_EMBEDDED_SUBSCRIPTION:
+                intent.setAction(EuiccService.ACTION_CONVERT_TO_EMBEDDED_SUBSCRIPTION);
+                break;
             default:
                 Log.w(TAG, "Unsupported action: " + action);
                 return null;
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 9c9ddc9..8253f71 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -595,6 +595,30 @@
         public void onSelectionTerminated(@DisconnectCauses int cause) {
             if (mEmergencyCallDomainSelectionConnection != null) {
                 Log.i(this, "onSelectionTerminated cause=" + cause);
+
+                // Cross stack redial
+                if (cause == android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE
+                        || cause == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE) {
+                    if (mEmergencyConnection != null) {
+                        final boolean isPermanentFailure =
+                                cause == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE;
+                        Log.i(this, "onSelectionTerminated trigger cross stack redial"
+                                + " permanent=" + isPermanentFailure);
+                        mDomainSelectionMainExecutor.execute(() -> {
+                            Log.i(this, "onSelectionTerminated execute cross stack redial"
+                                    + " permanent=" + isPermanentFailure);
+                            TelephonyConnection c = mEmergencyConnection;
+                            Phone phone = mEmergencyCallDomainSelectionConnection.getPhone();
+                            mEmergencyConnection.removeTelephonyConnectionListener(
+                                    mEmergencyConnectionListener);
+                            mEmergencyStateTracker.endCall(
+                                    mEmergencyConnection.getTelecomCallId());
+                            releaseEmergencyCallDomainSelection(true);
+                            retryOutgoingOriginalConnection(c, phone, isPermanentFailure);
+                        });
+                        return;
+                    }
+                }
                 mEmergencyCallDomainSelectionConnection = null;
                 if (mEmergencyConnection != null) {
                     mEmergencyConnection.hangup(android.telephony.DisconnectCause.OUT_OF_NETWORK);
@@ -658,7 +682,7 @@
 
         @Override
         public void onOriginalConnectionRetry(TelephonyConnection c, boolean isPermanentFailure) {
-            retryOutgoingOriginalConnection(c, isPermanentFailure);
+            retryOutgoingOriginalConnection(c, c.getPhone(), isPermanentFailure);
         }
     };
 
@@ -1809,7 +1833,7 @@
     // Update the mEmergencyRetryCache by removing the Phone used to call the last failed emergency
     // number and then moving it to the back of the queue if it is not a permanent failure cause
     // from the modem.
-    private void updateCachedConnectionPhonePair(TelephonyConnection c,
+    private void updateCachedConnectionPhonePair(TelephonyConnection c, Phone phone,
             boolean isPermanentFailure) {
         // No cache exists, create a new one.
         if (mEmergencyRetryCache == null) {
@@ -1824,7 +1848,7 @@
         Queue<Phone> cachedPhones = mEmergencyRetryCache.second;
         // Need to refer default phone considering ImsPhone because
         // cachedPhones is a list that contains default phones.
-        Phone phoneUsed = c.getPhone().getDefaultPhone();
+        Phone phoneUsed = phone.getDefaultPhone();
         if (phoneUsed == null) {
             return;
         }
@@ -1853,9 +1877,10 @@
      * This will continue until there are no more slots to dial on.
      */
     @VisibleForTesting
-    public void retryOutgoingOriginalConnection(TelephonyConnection c, boolean isPermanentFailure) {
-        int phoneId = (c.getPhone() == null) ? -1 : c.getPhone().getPhoneId();
-        updateCachedConnectionPhonePair(c, isPermanentFailure);
+    public void retryOutgoingOriginalConnection(TelephonyConnection c,
+            Phone phone, boolean isPermanentFailure) {
+        int phoneId = (phone == null) ? -1 : phone.getPhoneId();
+        updateCachedConnectionPhonePair(c, phone, isPermanentFailure);
         // Pull next phone to use from the cache or null if it is empty
         Phone newPhoneToUse = (mEmergencyRetryCache.second != null)
                 ? mEmergencyRetryCache.second.peek() : null;
diff --git a/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java b/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
index fa11a5d..d1eda18 100644
--- a/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
+++ b/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
@@ -68,6 +68,7 @@
 import android.telephony.AccessNetworkConstants.TransportType;
 import android.telephony.BarringInfo;
 import android.telephony.CarrierConfigManager;
+import android.telephony.DisconnectCause;
 import android.telephony.DomainSelectionService;
 import android.telephony.DomainSelectionService.SelectionAttributes;
 import android.telephony.EmergencyRegResult;
@@ -106,6 +107,18 @@
 
     private static final LocalLog sLocalLog = new LocalLog(LOG_SIZE);
 
+    private static final ArrayList<String> sAllowOnlyWithSimReady = new ArrayList<>();
+
+    static {
+        // b/177967010, JP
+        sAllowOnlyWithSimReady.add("jp"); // Japan
+        // b/198393826, DE
+        sAllowOnlyWithSimReady.add("de"); // Germany
+        // b/230443699, IN and SG
+        sAllowOnlyWithSimReady.add("in"); // India
+        sAllowOnlyWithSimReady.add("sg"); // Singapore
+    }
+
     /**
      * Network callback used to determine whether Wi-Fi is connected or not.
      */
@@ -167,6 +180,7 @@
     private boolean mRequiresVoLteEnabled;
     private boolean mLtePreferredAfterNrFailure;
     private boolean mTryCsWhenPsFails;
+    private int mModemCount;
 
     /** Indicates whether this instance is deactivated. */
     private boolean mDestroyed = false;
@@ -228,6 +242,12 @@
     private void handleScanResult(EmergencyRegResult result) {
         logi("handleScanResult result=" + result);
 
+        // Detected the country and found that emergency calls are not allowed with this slot.
+        if (!allowEmergencyCalls(result)) {
+            terminateSelectionPermanentlyForSlot();
+            return;
+        }
+
         if (result.getAccessNetwork() == UNKNOWN) {
             if ((mPreferredNetworkScanType == SCAN_TYPE_FULL_SERVICE_FOLLOWED_BY_LIMITED_SERVICE)
                       || (mScanType == DomainSelectionService.SCAN_TYPE_FULL_SERVICE)) {
@@ -247,7 +267,7 @@
 
         removeMessages(MSG_NETWORK_SCAN_TIMEOUT);
         onWwanNetworkTypeSelected(result.getAccessNetwork());
-        mIsScanRequested = false;
+        mCancelSignal = null;
     }
 
     @Override
@@ -318,11 +338,14 @@
         mTransportSelectorCallback = cb;
         mSelectionAttributes = attr;
 
+        TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+        mModemCount = tm.getActiveModemCount();
+
         sendEmptyMessage(MSG_START_DOMAIN_SELECTION);
     }
 
     private void startDomainSelection() {
-        logi("startDomainSelection");
+        logi("startDomainSelection modemCount=" + mModemCount);
         updateCarrierConfiguration();
         mDomainSelectionRequested = true;
         if (SubscriptionManager.isValidSubscriptionId(getSubId())) {
@@ -458,6 +481,12 @@
             return;
         }
 
+        if (!allowEmergencyCalls(mSelectionAttributes.getEmergencyRegResult())) {
+            // Detected the country and found that emergency calls are not allowed with this slot.
+            terminateSelectionPermanentlyForSlot();
+            return;
+        }
+
         if (isWifiPreferred()) {
             onWlanSelected();
             return;
@@ -793,7 +822,7 @@
         List<Integer> ratList = getImsNetworkTypeConfiguration();
         if (ratList.contains(accessNetwork)) {
             if (mIsEmergencyBarred) {
-                logi("sgetSelectablePsNetworkType barred");
+                logi("getSelectablePsNetworkType barred");
                 return UNKNOWN;
             }
             if (accessNetwork == NGRAN) {
@@ -1100,6 +1129,36 @@
         }
     }
 
+    private boolean allowEmergencyCalls(EmergencyRegResult regResult) {
+        if (mModemCount < 2) return true;
+        if (regResult == null) {
+            loge("allowEmergencyCalls null regResult");
+            return true;
+        }
+
+        String iso = regResult.getIso();
+        if (sAllowOnlyWithSimReady.contains(iso)) {
+            TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+            int simState = tm.getSimState(getSlotId());
+            if (simState != TelephonyManager.SIM_STATE_READY) {
+                logi("allowEmergencyCalls not ready, simState=" + simState + ", iso=" + iso);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private void terminateSelectionPermanentlyForSlot() {
+        logi("terminateSelectionPermanentlyForSlot");
+        mTransportSelectorCallback.onSelectionTerminated(DisconnectCause.EMERGENCY_PERM_FAILURE);
+
+        if (mIsScanRequested && mCancelSignal != null) {
+            mCancelSignal.cancel();
+            mCancelSignal = null;
+        }
+    }
+
     private static String arrayToString(int[] intArray, IntFunction<String> func) {
         int length = intArray.length;
         StringBuilder sb = new StringBuilder("{");
diff --git a/src/com/android/services/telephony/rcs/TelephonyRcsService.java b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
index 13b3a7d..3a8ad62 100644
--- a/src/com/android/services/telephony/rcs/TelephonyRcsService.java
+++ b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
@@ -25,6 +25,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.util.Log;
@@ -351,23 +352,17 @@
 
     private boolean doesSubscriptionSupportPresence(int subId) {
         if (!SubscriptionManager.isValidSubscriptionId(subId)) return false;
-        CarrierConfigManager carrierConfigManager =
-                mContext.getSystemService(CarrierConfigManager.class);
-        if (carrierConfigManager == null) return false;
-        boolean supportsUce = carrierConfigManager.getConfigForSubId(subId).getBoolean(
-                CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL);
-        supportsUce |= carrierConfigManager.getConfigForSubId(subId).getBoolean(
-                CarrierConfigManager.KEY_USE_RCS_SIP_OPTIONS_BOOL);
+        boolean supportsUce = getConfig(subId,
+                CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL, false /*default*/);
+        supportsUce |= getConfig(subId,
+                CarrierConfigManager.KEY_USE_RCS_SIP_OPTIONS_BOOL, false /*default*/);
         return supportsUce;
     }
 
     private boolean doesSubscriptionSupportSingleRegistration(int subId) {
         if (!SubscriptionManager.isValidSubscriptionId(subId)) return false;
-        CarrierConfigManager carrierConfigManager =
-                mContext.getSystemService(CarrierConfigManager.class);
-        if (carrierConfigManager == null) return false;
-        return carrierConfigManager.getConfigForSubId(subId).getBoolean(
-                CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL);
+        return getConfig(subId, CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL,
+                false /*defaultValue*/);
     }
 
     private int getSubscriptionFromSlot(int slotId) {
@@ -384,6 +379,16 @@
     }
 
     /**
+     * @return the boolean result corresponding to a boolean {@link CarrierConfigManager} key.
+     */
+    private boolean getConfig(int subId, String key, boolean defaultValue) {
+        CarrierConfigManager c = mContext.getSystemService(CarrierConfigManager.class);
+        if (c == null) return defaultValue;
+        PersistableBundle b = c.getConfigForSubId(subId, key);
+        return b != null ? b.getBoolean(key, defaultValue) : defaultValue;
+    }
+
+    /**
      * Dump this instance into a readable format for dumpsys usage.
      */
     public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
diff --git a/tests/src/com/android/TestContext.java b/tests/src/com/android/TestContext.java
index 720d235..111df53 100644
--- a/tests/src/com/android/TestContext.java
+++ b/tests/src/com/android/TestContext.java
@@ -17,6 +17,7 @@
 package com.android;
 
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doAnswer;
 
 import android.content.AttributionSource;
@@ -73,13 +74,12 @@
         MockitoAnnotations.initMocks(this);
         doAnswer((Answer<PersistableBundle>) invocation -> {
             int subId = (int) invocation.getArguments()[0];
-            if (subId < 0) {
-                return new PersistableBundle();
-            }
-            PersistableBundle b = mCarrierConfigs.get(subId);
-
-            return (b != null ? b : new PersistableBundle());
+            return getTestConfigs(subId);
         }).when(mMockCarrierConfigManager).getConfigForSubId(anyInt());
+        doAnswer((Answer<PersistableBundle>) invocation -> {
+            int subId = (int) invocation.getArguments()[0];
+            return getTestConfigs(subId);
+        }).when(mMockCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
     }
 
     @Override
@@ -252,18 +252,16 @@
 
     public void grantPermission(String permission) {
         synchronized (mPermissionTable) {
-            if (mPermissionTable != null && permission != null) {
-                mPermissionTable.remove(STUB_PERMISSION_ENABLE_ALL);
-                mPermissionTable.add(permission);
-            }
+            if (permission == null) return;
+            mPermissionTable.remove(STUB_PERMISSION_ENABLE_ALL);
+            mPermissionTable.add(permission);
         }
     }
 
     public void revokePermission(String permission) {
         synchronized (mPermissionTable) {
-            if (mPermissionTable != null && permission != null) {
-                mPermissionTable.remove(permission);
-            }
+            if (permission == null) return;
+            mPermissionTable.remove(permission);
         }
     }
 
@@ -281,6 +279,14 @@
         return mReceiver;
     }
 
+    private PersistableBundle getTestConfigs(int subId) {
+        if (subId < 0) {
+            return new PersistableBundle();
+        }
+        PersistableBundle b = getCarrierConfig(subId);
+        return (b != null ? b : new PersistableBundle());
+    }
+
     private static void logd(String s) {
         Log.d(TAG, s);
     }
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 3d6a0aa..ba064e5 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -16,6 +16,8 @@
 
 package com.android.services.telephony;
 
+import static android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE;
+import static android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE;
 import static android.telephony.DisconnectCause.NOT_DISCONNECTED;
 import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING;
 import static android.telephony.NetworkRegistrationInfo.DOMAIN_CS;
@@ -55,6 +57,7 @@
 import android.telecom.DisconnectCause;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
+import android.telephony.DomainSelectionService;
 import android.telephony.RadioAccessFamily;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
@@ -76,6 +79,7 @@
 import com.android.internal.telephony.PhoneInternalInterface.DialArgs;
 import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.data.PhoneSwitcher;
+import com.android.internal.telephony.domainselection.DomainSelectionConnection;
 import com.android.internal.telephony.domainselection.DomainSelectionResolver;
 import com.android.internal.telephony.domainselection.EmergencyCallDomainSelectionConnection;
 import com.android.internal.telephony.domainselection.NormalCallDomainSelectionConnection;
@@ -864,7 +868,8 @@
         setPhones(phones);
         c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
 
-        mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
+        mTestConnectionService.retryOutgoingOriginalConnection(c,
+                c.getPhone(), false /*isPermanentFailure*/);
 
         // We never need to be notified in telecom that the PhoneAccount has changed, because it
         // was redialed on the same slot
@@ -895,7 +900,8 @@
         setPhones(phones);
         c.setAddress(TEST_ADDRESS, TelecomManager.PRESENTATION_ALLOWED);
 
-        mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
+        mTestConnectionService.retryOutgoingOriginalConnection(c,
+                c.getPhone(), true /*isPermanentFailure*/);
 
         // We never need to be notified in telecom that the PhoneAccount has changed, because it
         // was never redialed
@@ -936,7 +942,8 @@
         doReturn(PHONE_ACCOUNT_HANDLE_2).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
                 slot1Phone);
 
-        mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
+        mTestConnectionService.retryOutgoingOriginalConnection(c,
+                c.getPhone(), false /*isPermanentFailure*/);
 
         // The cache should still contain all of the Phones, since it was a temporary failure.
         assertEquals(2, mTestConnectionService.mEmergencyRetryCache.second.size());
@@ -977,7 +984,8 @@
         doReturn(PHONE_ACCOUNT_HANDLE_2).when(mPhoneUtilsProxy).makePstnPhoneAccountHandle(
                 slot1Phone);
 
-        mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
+        mTestConnectionService.retryOutgoingOriginalConnection(c,
+                c.getPhone(), true /*isPermanentFailure*/);
 
         // The cache should only contain the slot1Phone.
         assertEquals(1, mTestConnectionService.mEmergencyRetryCache.second.size());
@@ -1019,7 +1027,8 @@
                 slot1Phone);
 
         // First Temporary failure
-        mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
+        mTestConnectionService.retryOutgoingOriginalConnection(c,
+                c.getPhone(), false /*isPermanentFailure*/);
         // Set the Phone to the new phone that was just used to dial.
         c.setMockPhone(slot1Phone);
         // The cache should still contain all of the Phones, since it was a temporary failure.
@@ -1027,7 +1036,8 @@
         // Make sure slot 1 is next in the queue.
         assertEquals(slot1Phone, mTestConnectionService.mEmergencyRetryCache.second.peek());
         // Second Temporary failure
-        mTestConnectionService.retryOutgoingOriginalConnection(c, false /*isPermanentFailure*/);
+        mTestConnectionService.retryOutgoingOriginalConnection(c,
+                c.getPhone(), false /*isPermanentFailure*/);
         // Set the Phone to the new phone that was just used to dial.
         c.setMockPhone(slot0Phone);
         // The cache should still contain all of the Phones, since it was a temporary failure.
@@ -1074,7 +1084,8 @@
                 slot1Phone);
 
         // First Permanent failure
-        mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
+        mTestConnectionService.retryOutgoingOriginalConnection(c,
+                c.getPhone(), true /*isPermanentFailure*/);
         // Set the Phone to the new phone that was just used to dial.
         c.setMockPhone(slot1Phone);
         // The cache should only contain one phone
@@ -1082,7 +1093,8 @@
         // Make sure slot 1 is next in the queue.
         assertEquals(slot1Phone, mTestConnectionService.mEmergencyRetryCache.second.peek());
         // Second Permanent failure
-        mTestConnectionService.retryOutgoingOriginalConnection(c, true /*isPermanentFailure*/);
+        mTestConnectionService.retryOutgoingOriginalConnection(c,
+                c.getPhone(), true /*isPermanentFailure*/);
         // The cache should be empty
         assertEquals(true, mTestConnectionService.mEmergencyRetryCache.second.isEmpty());
 
@@ -1631,6 +1643,98 @@
     }
 
     @Test
+    public void testOnSelectionTerminatedPerm() throws Exception {
+        setupForCallTest();
+
+        doReturn(mEmergencyCallDomainSelectionConnection).when(mDomainSelectionResolver)
+                .getDomainSelectionConnection(any(), anyInt(), eq(true));
+        doReturn(mPhone0).when(mEmergencyCallDomainSelectionConnection).getPhone();
+        doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+
+        doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
+        doReturn(mImsPhone).when(mPhone0).getImsPhone();
+
+        mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+                createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+                        TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+        ArgumentCaptor<DomainSelectionConnection.DomainSelectionConnectionCallback> callbackCaptor =
+                ArgumentCaptor.forClass(
+                        DomainSelectionConnection.DomainSelectionConnectionCallback.class);
+
+        verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(
+                any(), callbackCaptor.capture());
+
+        DomainSelectionConnection.DomainSelectionConnectionCallback callback =
+                callbackCaptor.getValue();
+
+        assertNotNull(callback);
+
+        EmergencyCallDomainSelectionConnection ecdsc =
+                Mockito.mock(EmergencyCallDomainSelectionConnection.class);
+        doReturn(ecdsc).when(mDomainSelectionResolver)
+                .getDomainSelectionConnection(any(), anyInt(), eq(true));
+
+        callback.onSelectionTerminated(EMERGENCY_PERM_FAILURE);
+
+        ArgumentCaptor<DomainSelectionService.SelectionAttributes> attrCaptor =
+                ArgumentCaptor.forClass(
+                        DomainSelectionService.SelectionAttributes.class);
+
+        verify(ecdsc).createEmergencyConnection(attrCaptor.capture(), any());
+
+        DomainSelectionService.SelectionAttributes attr = attrCaptor.getValue();
+
+        assertEquals(mPhone1.getPhoneId(), attr.getSlotId());
+    }
+
+    @Test
+    public void testOnSelectionTerminatedTemp() throws Exception {
+        setupForCallTest();
+
+        doReturn(mEmergencyCallDomainSelectionConnection).when(mDomainSelectionResolver)
+                .getDomainSelectionConnection(any(), anyInt(), eq(true));
+        doReturn(mPhone0).when(mEmergencyCallDomainSelectionConnection).getPhone();
+        doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+
+        doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
+        doReturn(mImsPhone).when(mPhone0).getImsPhone();
+
+        mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+                createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+                        TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+        ArgumentCaptor<DomainSelectionConnection.DomainSelectionConnectionCallback> callbackCaptor =
+                ArgumentCaptor.forClass(
+                        DomainSelectionConnection.DomainSelectionConnectionCallback.class);
+
+        verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(
+                any(), callbackCaptor.capture());
+
+        DomainSelectionConnection.DomainSelectionConnectionCallback callback =
+                callbackCaptor.getValue();
+
+        assertNotNull(callback);
+
+        EmergencyCallDomainSelectionConnection ecdsc =
+                Mockito.mock(EmergencyCallDomainSelectionConnection.class);
+        doReturn(ecdsc).when(mDomainSelectionResolver)
+                .getDomainSelectionConnection(any(), anyInt(), eq(true));
+
+        callback.onSelectionTerminated(EMERGENCY_TEMP_FAILURE);
+
+        ArgumentCaptor<DomainSelectionService.SelectionAttributes> attrCaptor =
+                ArgumentCaptor.forClass(
+                        DomainSelectionService.SelectionAttributes.class);
+
+        verify(ecdsc).createEmergencyConnection(attrCaptor.capture(), any());
+
+        DomainSelectionService.SelectionAttributes attr = attrCaptor.getValue();
+
+        assertEquals(mPhone1.getPhoneId(), attr.getSlotId());
+    }
+
+    @Test
     public void testDomainSelectionWithMmiCode() {
         //UT domain selection should not be handled by new domain selector.
         doNothing().when(mContext).startActivity(any());
diff --git a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
index c50c6b5..27b3f0a 100644
--- a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
@@ -80,6 +80,7 @@
 import android.telephony.BarringInfo;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CellIdentityLte;
+import android.telephony.DisconnectCause;
 import android.telephony.DomainSelectionService;
 import android.telephony.DomainSelectionService.SelectionAttributes;
 import android.telephony.EmergencyRegResult;
@@ -1106,6 +1107,27 @@
         verify(mTransportSelectorCallback, times(1)).onWlanSelected();
     }
 
+    @Test
+    public void testDualSimInvalidSubscription() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+        doReturn(2).when(mTelephonyManager).getActiveModemCount();
+        doReturn(TelephonyManager.SIM_STATE_PIN_REQUIRED)
+                .when(mTelephonyManager).getSimState(anyInt());
+
+        EmergencyRegResult regResult = getEmergencyRegResult(EUTRAN, REGISTRATION_STATE_UNKNOWN,
+                0, false, false, 0, 0, "", "", "jp");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+        processAllMessages();
+
+        verify(mTransportSelectorCallback, times(1))
+                .onSelectionTerminated(eq(DisconnectCause.EMERGENCY_PERM_FAILURE));
+    }
+
     private void createSelector(int subId) throws Exception {
         mDomainSelector = new EmergencyCallDomainSelector(
                 mContext, SLOT_0, subId, mHandlerThread.getLooper(),
@@ -1174,9 +1196,19 @@
             @NetworkRegistrationInfo.Domain int domain,
             boolean isVopsSupported, boolean isEmcBearerSupported, int emc, int emf,
             @NonNull String mcc, @NonNull String mnc) {
+        return getEmergencyRegResult(accessNetwork, regState, domain, isVopsSupported,
+                isEmcBearerSupported, emc, emf, mcc, mnc, "");
+    }
+
+    private static EmergencyRegResult getEmergencyRegResult(
+            @AccessNetworkConstants.RadioAccessNetworkType int accessNetwork,
+            @NetworkRegistrationInfo.RegistrationState int regState,
+            @NetworkRegistrationInfo.Domain int domain,
+            boolean isVopsSupported, boolean isEmcBearerSupported, int emc, int emf,
+            @NonNull String mcc, @NonNull String mnc, @NonNull String iso) {
         return new EmergencyRegResult(accessNetwork, regState,
                 domain, isVopsSupported, isEmcBearerSupported,
-                emc, emf, mcc, mnc, "");
+                emc, emf, mcc, mnc, iso);
     }
 
     private static PersistableBundle getDefaultPersistableBundle() {