Merge "Change method to show mobile radio power to getRadioPower" into main
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index eef01fa..7e56e8b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -619,17 +619,6 @@
             </intent-filter>
         </activity>
 
-        <activity android:name=".settings.BandMode"
-                  android:label="@string/band_mode_title"
-                  android:exported="true"
-                  android:theme="@style/Theme.AppCompat.DayNight">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.VOICE_LAUNCH" />
-            </intent-filter>
-        </activity>
-
         <provider
             android:name="ServiceStateProvider"
             android:authorities="service-state"
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 6675ae7..992f1b2 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -596,7 +596,7 @@
     <string name="failedToImportSingleContactMsg" msgid="228095510489830266">"Uvoz kontakta nije uspio"</string>
     <string name="hac_mode_title" msgid="4127986689621125468">"Slušni aparat"</string>
     <string name="hac_mode_summary" msgid="7774989500136009881">"Uključite kompatibilnost za slušni aparat"</string>
-    <string name="rtt_mode_title" msgid="3075948111362818043">"Pozivanje sa slanjem SMS-ova u stvarnom vremenu (RTT)"</string>
+    <string name="rtt_mode_title" msgid="3075948111362818043">"Poziv sa SMS-ovima u stvarnom vremenu (RTT)"</string>
     <string name="rtt_mode_summary" msgid="8631541375609989562">"Dozvolite razmjenu poruka tokom glasovnog poziva"</string>
     <string name="rtt_mode_more_information" msgid="587500128658756318">"RTT pomaže pozivaocima koji su gluhi, imaju probleme sa sluhom ili govorom te onima kojima treba više od samog glasa.&lt;br&gt; &lt;a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>&gt;Saznajte više&lt;/a&gt;\n       &lt;br&gt;&lt;br&gt; - RTT pozivi se pohranjuju kao transkripti poruka\n       &lt;br&gt; - RTT nije dostupan za video pozive"</string>
     <string name="no_rtt_when_roaming" msgid="5268008247378355389">"Napomena: RTT nije dostupan u romingu"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index a9dd327..58f2670 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -596,9 +596,9 @@
     <string name="failedToImportSingleContactMsg" msgid="228095510489830266">"Kontaktpersonen kunne ikke importeres"</string>
     <string name="hac_mode_title" msgid="4127986689621125468">"Høreapparater"</string>
     <string name="hac_mode_summary" msgid="7774989500136009881">"Slå høreapparatskompatibilitet til"</string>
-    <string name="rtt_mode_title" msgid="3075948111362818043">"Opkald via sms i realtid"</string>
+    <string name="rtt_mode_title" msgid="3075948111362818043">"Opkald via beskeder i realtid"</string>
     <string name="rtt_mode_summary" msgid="8631541375609989562">"Tillad afsendelse af sms-beskeder i et taleopkald"</string>
-    <string name="rtt_mode_more_information" msgid="587500128658756318">"Sms i realtid hjælper personer, som er døve, hørehæmmede, talehandicappede, eller som har brug for mere end bare tale, med at foretage opkald.&lt;br&gt; &lt;a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>&gt;Få flere oplysninger&lt;/a&gt;\n       &lt;br&gt;&lt;br&gt; – Opkald via sms i realtid gemmes som en beskedtransskription\n       &lt;br&gt; – Sms i realtid er ikke tilgængeligt til videoopkald"</string>
+    <string name="rtt_mode_more_information" msgid="587500128658756318">"Beskeder i realtid hjælper personer, som er døve, hørehæmmede, talehandicappede, eller som har brug for mere end bare tale, med at foretage opkald.&lt;br&gt; &lt;a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>&gt;Få flere oplysninger&lt;/a&gt;\n       &lt;br&gt;&lt;br&gt; – Opkald via beskeder i realtid gemmes som en beskedtransskription\n       &lt;br&gt; – Beskeder i realtid er ikke tilgængeligt til videoopkald"</string>
     <string name="no_rtt_when_roaming" msgid="5268008247378355389">"Bemærk! RTT er ikke tilgængeligt under roaming"</string>
   <string-array name="tty_mode_entries">
     <item msgid="3238070884803849303">"TTY fra"</item>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 5d03fe8..4f8e559 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -314,7 +314,7 @@
     <string name="video_calling_settings_title" msgid="342829454913266078">"वाहक वीडियो कॉलिंग"</string>
     <string name="gsm_umts_options" msgid="4968446771519376808">"GSM/UMTS विकल्प"</string>
     <string name="cdma_options" msgid="3669592472226145665">"CDMA विकल्प"</string>
-    <string name="throttle_data_usage" msgid="1944145350660420711">"डेटा उपयोग"</string>
+    <string name="throttle_data_usage" msgid="1944145350660420711">"डेटा खर्च"</string>
     <string name="throttle_current_usage" msgid="7483859109708658613">"वर्तमान अवधि में उपयोग किया गया डेटा"</string>
     <string name="throttle_time_frame" msgid="1813452485948918791">"डेटा उपयोग अवधि"</string>
     <string name="throttle_rate" msgid="7641913901133634905">"डेटा दर नीति"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 7bd3d32..3fca1ed 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -385,9 +385,9 @@
     <string name="enable_disable_lafs" msgid="7448060358300805661">"Жергиликтүү аэропорттун учуу тартиби"</string>
     <string name="lafs_enable" msgid="3125783406052655690">"Жергиликтүү аэропорттун учуу тартиби иштетилген"</string>
     <string name="lafs_disable" msgid="7326815066813851447">"Жергиликтүү аэропорттун учуу тартиби өчүрүлгөн"</string>
-    <string name="enable_disable_restaurants" msgid="3873247081569423019">"Ресторандар"</string>
-    <string name="restaurants_enable" msgid="5810452674239139572">"Ресторандар иштетилген"</string>
-    <string name="restaurants_disable" msgid="2733507854548413505">"Ресторандар өчүрүлгөн"</string>
+    <string name="enable_disable_restaurants" msgid="3873247081569423019">"Тамактануучу жайлар"</string>
+    <string name="restaurants_enable" msgid="5810452674239139572">"Тамактануучу жайлар иштетилген"</string>
+    <string name="restaurants_disable" msgid="2733507854548413505">"Тамактануучу жайлар өчүрүлгөн"</string>
     <string name="enable_disable_lodgings" msgid="7849168585821435109">"Турак жайлар"</string>
     <string name="lodgings_enable" msgid="2020598411398609514">"Турак жайлар иштетилген"</string>
     <string name="lodgings_disable" msgid="5145649659459722661">"Турак жайлар өчүрүлгөн"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index f8bef4a..90165b8 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -908,7 +908,7 @@
     <string name="radio_info_smsc_update_label" msgid="5141996256097115753">"Atjaunināt"</string>
     <string name="radio_info_smsc_refresh_label" msgid="8409923721451604560">"Atsvaidzināt"</string>
     <string name="radio_info_toggle_dns_check_label" msgid="1394078554927787350">"Pārslēgt DNS pārbaudi"</string>
-    <string name="oem_radio_info_label" msgid="2914167475119997456">"OEM raksturīga informācija/iestatījumi"</string>
+    <string name="oem_radio_info_label" msgid="2914167475119997456">"OAR raksturīga informācija/iestatījumi"</string>
     <string name="radio_info_endc_available" msgid="2983767110681230019">"EN-DC pieejamība (NSA):"</string>
     <string name="radio_info_dcnr_restricted" msgid="7147511536420148173">"DCNR ierobežojums (NSA):"</string>
     <string name="radio_info_nr_available" msgid="3383388088451237182">"NR pieejamība (NSA):"</string>
diff --git a/res/values/config.xml b/res/values/config.xml
index 939faa7..dcfa364 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -318,6 +318,13 @@
     <string-array name="thermal_mitigation_allowlisted_packages" translatable="false">
     </string-array>
 
+    <!-- Array of carriers that don't care about NGRAN's preference in the preferred emergency
+         network scan list after SIM is removed. -->
+    <integer-array name="config_carriers_ignore_ngran_preference_when_sim_removed">
+        <!-- 001-01 Test SIM -->
+        <item>1911</item>
+    </integer-array>
+
     <!-- Array of countries that active SIM is needed for emergency calls. Values should be
          ISO3166 country codes in lowercase. -->
     <string-array name="config_countries_require_sim_for_emergency" translatable="false">
diff --git a/src/com/android/phone/CallNotifier.java b/src/com/android/phone/CallNotifier.java
index 73b61b6..daf3aa2 100644
--- a/src/com/android/phone/CallNotifier.java
+++ b/src/com/android/phone/CallNotifier.java
@@ -489,7 +489,7 @@
     public void updatePhoneStateListeners(boolean isRefresh, int updateType, int subIdToUpdate) {
         List<SubscriptionInfo> subInfos = SubscriptionManagerService.getInstance()
                 .getActiveSubscriptionInfoList(mApplication.getOpPackageName(),
-                        mApplication.getAttributionTag());
+                        mApplication.getAttributionTag(), true/*isForAllProfile*/);
 
         // Sort sub id list based on slot id, so that CFI/MWI notifications will be updated for
         // slot 0 first then slot 1. This is needed to ensure that when CFI or MWI is enabled for
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 483a706..0cb95c5 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -50,6 +50,7 @@
 import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyLocalConnection;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.LocalLog;
 import android.util.Log;
@@ -1061,16 +1062,14 @@
                     && reason == ROAMING_NOTIFICATION_REASON_CARRIER_CONFIG_CHANGED) {
                 mShownNotificationReasons.add(callingReason);
             }
-            boolean isShowRoamingNotificationEnabled = getCarrierConfigForSubId(mDefaultDataSubId)
-                    .getBoolean(CarrierConfigManager
-                            .KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL);
+            boolean shouldShowRoamingNotification = shouldShowRoamingNotification(roamingNumeric);
             // No need to show it again if we never cancelled it explicitly.
             if (getCurrentRoamingNotification() == ROAMING_NOTIFICATION_CONNECTED) {
                 return;
             }
 
             // Inform users that roaming charges may apply.
-            if (!shownInThisNumeric && !shownForThisReason && isShowRoamingNotificationEnabled) {
+            if (!shownInThisNumeric && !shownForThisReason && shouldShowRoamingNotification) {
                 updateDataRoamingNotification(ROAMING_NOTIFICATION_CONNECTED);
             } else {
                 // Don't show roaming notification if we've already shown for this MccMnc or
@@ -1078,7 +1077,7 @@
                 Log.d(LOG_TAG, "Skip roaming connected notification since already"
                         + " shownInThisNumeric:" + shownInThisNumeric
                         + " shownForThisReason:" + shownForThisReason
-                        + " isShowRoamingNotificationEnabled:" + isShowRoamingNotificationEnabled);
+                        + " shouldShowRoamingNotification:" + shouldShowRoamingNotification);
                 // Dismiss notification if the other notification is shown.
                 if (getCurrentRoamingNotification() != ROAMING_NOTIFICATION_NO_NOTIFICATION) {
                     updateDataRoamingNotification(ROAMING_NOTIFICATION_NO_NOTIFICATION);
@@ -1173,10 +1172,10 @@
             msg.arg1 = mDefaultDataSubId;
             msg.sendToTarget();
         } else if (dataAllowed && dataIsNowRoaming(mDefaultDataSubId)) {
-            boolean isShowRoamingNotificationEnabled = getCarrierConfigForSubId(mDefaultDataSubId)
-                    .getBoolean(CarrierConfigManager
-                            .KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL);
-            if (!isShowRoamingNotificationEnabled) return;
+            if (!shouldShowRoamingNotification(roamingOperatorNumeric)) {
+                Log.d(LOG_TAG, "Skip showing roaming connected notification.");
+                return;
+            }
             // Don't show roaming notification if we've already shown for this MccMnc
             if (roamingOperatorNumeric != null
                     && !mPrevRoamingOperatorNumerics.add(roamingOperatorNumeric)) {
@@ -1213,6 +1212,46 @@
         return getPhone(subId).getServiceState().getDataRoaming();
     }
 
+    private boolean shouldShowRoamingNotification(String roamingNumeric) {
+        PersistableBundle config = getCarrierConfigForSubId(mDefaultDataSubId);
+        boolean showRoamingNotification = config.getBoolean(
+                CarrierConfigManager.KEY_SHOW_DATA_CONNECTED_ROAMING_NOTIFICATION_BOOL);
+
+        if (TextUtils.isEmpty(roamingNumeric) || !mFeatureFlags.hideRoamingIcon()) {
+            Log.d(LOG_TAG, "shouldShowRoamingNotification: roamingNumeric=" + roamingNumeric
+                    + ", hideRoaming=" + mFeatureFlags.hideRoamingIcon());
+            return showRoamingNotification;
+        }
+
+        String[] includedMccMncs = config.getStringArray(CarrierConfigManager
+                .KEY_DATA_CONNECTED_ROAMING_NOTIFICATION_INCLUDED_MCC_MNCS_STRING_ARRAY);
+        if (includedMccMncs != null) {
+            for (String mccMnc : includedMccMncs) {
+                if (roamingNumeric.equals(mccMnc)) {
+                    Log.d(LOG_TAG, "shouldShowRoamingNotification: show for MCC/MNC " + mccMnc);
+                    return showRoamingNotification;
+                }
+            }
+        }
+
+        String[] excludedMccs = config.getStringArray(CarrierConfigManager
+                .KEY_DATA_CONNECTED_ROAMING_NOTIFICATION_EXCLUDED_MCCS_STRING_ARRAY);
+        String roamingMcc = roamingNumeric.length() < 3 ? "" : roamingNumeric.substring(0, 3);
+        if (excludedMccs != null && !TextUtils.isEmpty(roamingMcc)) {
+            for (String mcc : excludedMccs) {
+                if (roamingMcc.equals(mcc)) {
+                    Log.d(LOG_TAG, "shouldShowRoamingNotification: ignore for MCC " + mcc);
+                    return false;
+                }
+            }
+        }
+
+        if (showRoamingNotification) {
+            Log.d(LOG_TAG, "shouldShowRoamingNotification: show for numeric " + roamingNumeric);
+        }
+        return showRoamingNotification;
+    }
+
     private void updateLimitedSimFunctionForDualSim() {
         if (DBG) Log.d(LOG_TAG, "updateLimitedSimFunctionForDualSim");
         // check conditions to display limited SIM function notification under dual SIM
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 6a560f6..1937658 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -8051,7 +8051,7 @@
      */
     private List<SubscriptionInfo> getActiveSubscriptionInfoListPrivileged() {
         return getSubscriptionManagerService().getActiveSubscriptionInfoList(
-                mApp.getOpPackageName(), mApp.getAttributionTag());
+                mApp.getOpPackageName(), mApp.getAttributionTag(), true/*isForAllProfile*/);
     }
 
     private ActivityStatsTechSpecificInfo[] mLastModemActivitySpecificInfo = null;
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 7ee3a1e..48169a2 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -1389,7 +1389,7 @@
             // one and causing UI Jank.
             boolean noActiveSimCard = SubscriptionManagerService.getInstance()
                     .getActiveSubInfoCount(phone.getContext().getOpPackageName(),
-                            phone.getContext().getAttributionTag()) == 0;
+                            phone.getContext().getAttributionTag(), true/*isForAllProfile*/) == 0;
             // If there's no active sim card and the device is in emergency mode, use E account.
             addExistingConnection(mPhoneUtilsProxy.makePstnPhoneAccountHandleWithPrefix(
                     phone, "", isEmergencyNumber && noActiveSimCard), repConnection);
diff --git a/src/com/android/services/telephony/domainselection/CarrierConfigHelper.java b/src/com/android/services/telephony/domainselection/CarrierConfigHelper.java
new file mode 100644
index 0000000..d39a6b7
--- /dev/null
+++ b/src/com/android/services/telephony/domainselection/CarrierConfigHelper.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.services.telephony.domainselection;
+
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.NGRAN;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.os.SystemProperties;
+import android.preference.PreferenceManager;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.phone.R;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/** Helper class to cache carrier configurations. */
+public class CarrierConfigHelper extends Handler {
+    private static final String TAG = "CarrierConfigHelper";
+    private static final boolean DBG = (SystemProperties.getInt("ro.debuggable", 0) == 1);
+
+    @VisibleForTesting
+    public static final String KEY_VONR_EMERGENCY_SUPPORT = "vonr_emergency_support";
+
+    private final Context mContext;
+    private final CarrierConfigManager mConfigManager;
+    private final TelephonyManager mTelephonyManager;
+    private final ArrayMap<Integer, Boolean> mVoNrSupported = new ArrayMap<>();
+
+    private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener =
+            (slotIndex, subId, carrierId, specificCarrierId) -> onCarrierConfigurationChanged(
+                    slotIndex, subId, carrierId);
+
+    // For test purpose only
+    private final SharedPreferences mSharedPreferences;
+
+    private List<Integer> mIgnoreNrWhenSimRemoved = null;
+
+    /**
+     * Creates an instance.
+     *
+     * @param context The Context this is associated with.
+     * @param looper The Looper to run the CarrierConfigHelper.
+     */
+    public CarrierConfigHelper(@NonNull Context context, @NonNull Looper looper) {
+        this(context, looper, null);
+    }
+
+    /**
+     * Creates an instance.
+     *
+     * @param context The Context this is associated with.
+     * @param looper The Looper to run the CarrierConfigHelper.
+     * @param sharedPreferences The SharedPreferences instance.
+     */
+    @VisibleForTesting
+    public CarrierConfigHelper(@NonNull Context context, @NonNull Looper looper,
+            @Nullable SharedPreferences sharedPreferences) {
+        super(looper);
+
+        mContext = context;
+        mTelephonyManager = context.getSystemService(TelephonyManager.class);
+        mConfigManager = context.getSystemService(CarrierConfigManager.class);
+        mConfigManager.registerCarrierConfigChangeListener(this::post,
+                mCarrierConfigChangeListener);
+        mSharedPreferences = sharedPreferences;
+
+        readFromSharedPreference();
+        readResourceConfiguration();
+    }
+
+    /**
+     * Returns whether VoNR emergency was supported with the last valid subscription.
+     *
+     * @param slotIndex The SIM slot index.
+     * @return true if VoNR emergency was supported with the last valid subscription.
+     *         Otherwise, false.
+     */
+    public boolean isVoNrEmergencySupported(int slotIndex) {
+        return mVoNrSupported.get(Integer.valueOf(slotIndex));
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        switch(msg.what) {
+            default:
+                super.handleMessage(msg);
+                break;
+        }
+    }
+
+    private void readFromSharedPreference() {
+        mVoNrSupported.clear();
+        int modemCount = mTelephonyManager.getActiveModemCount();
+        SharedPreferences sp = (mSharedPreferences != null) ? mSharedPreferences
+                : PreferenceManager.getDefaultSharedPreferences(mContext);
+        for (int i = 0; i < modemCount; i++) {
+            Boolean savedConfig = Boolean.valueOf(
+                    sp.getBoolean(KEY_VONR_EMERGENCY_SUPPORT + i, false));
+            mVoNrSupported.put(Integer.valueOf(i), savedConfig);
+            Log.i(TAG, "readFromSharedPreference slot=" + i + ", " + savedConfig);
+        }
+    }
+
+    private void onCarrierConfigurationChanged(int slotIndex, int subId, int carrierId) {
+        Log.i(TAG, "onCarrierConfigurationChanged slotIndex=" + slotIndex
+                + ", subId=" + subId + ", carrierId=" + carrierId);
+
+        if (slotIndex < 0
+                || !SubscriptionManager.isValidSubscriptionId(subId)
+                || mTelephonyManager.getSimState(slotIndex) != TelephonyManager.SIM_STATE_READY) {
+            return;
+        }
+
+        PersistableBundle b = mConfigManager.getConfigForSubId(subId,
+                KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY);
+        if (b.isEmpty()) {
+            Log.e(TAG, "onCarrierConfigurationChanged empty result");
+            return;
+        }
+
+        if (!CarrierConfigManager.isConfigForIdentifiedCarrier(b)) {
+            Log.i(TAG, "onCarrierConfigurationChanged not carrier specific configuration");
+            return;
+        }
+
+        int[] imsRatsConfig = b.getIntArray(
+                KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY);
+        if (imsRatsConfig == null) imsRatsConfig = new int[0];
+        boolean carrierConfig = false;
+        for (int i = 0; i < imsRatsConfig.length; i++) {
+            if (imsRatsConfig[i] == NGRAN) {
+                carrierConfig = true;
+                break;
+            }
+        }
+        if (mIgnoreNrWhenSimRemoved.contains(carrierId)) carrierConfig = false;
+
+        Boolean savedConfig = mVoNrSupported.get(Integer.valueOf(slotIndex));
+        if (carrierConfig == savedConfig) {
+            return;
+        }
+
+        mVoNrSupported.put(Integer.valueOf(slotIndex), Boolean.valueOf(carrierConfig));
+
+        SharedPreferences sp = (mSharedPreferences != null) ? mSharedPreferences
+                : PreferenceManager.getDefaultSharedPreferences(mContext);
+        SharedPreferences.Editor editor = sp.edit();
+        editor.putBoolean(KEY_VONR_EMERGENCY_SUPPORT + slotIndex, carrierConfig);
+        editor.apply();
+
+        Log.i(TAG, "onCarrierConfigurationChanged preference updated slotIndex=" + slotIndex
+                + ", supported=" + carrierConfig);
+    }
+
+    private void readResourceConfiguration() {
+        try {
+            mIgnoreNrWhenSimRemoved = Arrays.stream(mContext.getResources().getIntArray(
+                    R.array.config_carriers_ignore_ngran_preference_when_sim_removed))
+                    .boxed().collect(Collectors.toList());
+        } catch (Resources.NotFoundException nfe) {
+            Log.e(TAG, "readResourceConfiguration exception=" + nfe);
+        } catch (NullPointerException npe) {
+            Log.e(TAG, "readResourceConfiguration exception=" + npe);
+        }
+        if (mIgnoreNrWhenSimRemoved == null) {
+            mIgnoreNrWhenSimRemoved = new ArrayList<Integer>();
+        }
+        Log.i(TAG, "readResourceConfiguration ignoreNrWhenSimRemoved=" + mIgnoreNrWhenSimRemoved);
+    }
+
+    /** Destroys the instance. */
+    public void destroy() {
+        if (DBG) Log.d(TAG, "destroy");
+        mConfigManager.unregisterCarrierConfigChangeListener(mCarrierConfigChangeListener);
+    }
+}
diff --git a/src/com/android/services/telephony/domainselection/CrossSimRedialingController.java b/src/com/android/services/telephony/domainselection/CrossSimRedialingController.java
index f1bb78c..44904f4 100644
--- a/src/com/android/services/telephony/domainselection/CrossSimRedialingController.java
+++ b/src/com/android/services/telephony/domainselection/CrossSimRedialingController.java
@@ -31,16 +31,19 @@
 import android.os.SystemProperties;
 import android.telephony.Annotation.PreciseDisconnectCauses;
 import android.telephony.CarrierConfigManager;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
 import android.text.TextUtils;
 import android.util.LocalLog;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneFactory;
 
 import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
 
 /** Controls the cross stack redialing. */
 public class CrossSimRedialingController extends Handler {
@@ -53,11 +56,11 @@
         /**
          * Returns whether the number is an emergency number in the given modem slot.
          *
-         * @param slotId The slot id to be checked.
+         * @param subId The sub id to be checked.
          * @param number The number.
          * @return {@code true} if the number is an emergency number in the given slot.
          */
-        boolean isEmergencyNumber(int slotId, String number);
+        boolean isEmergencyNumber(int subId, String number);
     }
 
     @VisibleForTesting
@@ -73,17 +76,23 @@
 
     private EmergencyNumberHelper mEmergencyNumberHelper = new EmergencyNumberHelper() {
         @Override
-        public boolean isEmergencyNumber(int slotId, String number) {
-            // TODO(b/258112541) Add System api to check emergency number per subscription.
+        public boolean isEmergencyNumber(int subId, String number) {
+            number = PhoneNumberUtils.stripSeparators(number);
+            if (TextUtils.isEmpty(number)) return false;
+            Map<Integer, List<EmergencyNumber>> lists = null;
             try {
-                Phone phone = PhoneFactory.getPhone(slotId);
-                if (phone != null
-                        && phone.getEmergencyNumberTracker() != null
-                        && phone.getEmergencyNumberTracker().isEmergencyNumber(number)) {
-                    return true;
-                }
-            } catch (IllegalStateException e) {
-                loge("isEmergencyNumber e=" + e);
+                lists = mTelephonyManager.getEmergencyNumberList();
+            } catch (IllegalStateException ise) {
+                loge("isEmergencyNumber ise=" + ise);
+            } catch (RuntimeException rte) {
+                loge("isEmergencyNumber rte=" + rte);
+            }
+            if (lists == null) return false;
+
+            List<EmergencyNumber> list = lists.get(subId);
+            if (list == null || list.isEmpty()) return false;
+            for (EmergencyNumber eNumber : list) {
+                if (number.equals(eNumber.getNumber())) return true;
             }
             return false;
         }
@@ -242,11 +251,12 @@
                 continue;
             }
 
-            if (mEmergencyNumberHelper.isEmergencyNumber(i, mNumber)) {
-                logi("isThereOtherSlot index=" + i + ", found");
+            int subId = SubscriptionManager.getSubscriptionId(i);
+            if (mEmergencyNumberHelper.isEmergencyNumber(subId, mNumber)) {
+                logi("isThereOtherSlot index=" + i + "(" + subId + "), found");
                 return true;
             } else {
-                logi("isThereOtherSlot index=" + i + ", not emergency number");
+                logi("isThereOtherSlot index=" + i + "(" + subId + "), not emergency number");
             }
         }
 
@@ -278,6 +288,12 @@
                 + ", startQuickTimerInService=" + mStartQuickCrossStackTimerWhenInService);
     }
 
+    /** Test purpose only. */
+    @VisibleForTesting
+    public EmergencyNumberHelper getEmergencyNumberHelper() {
+        return mEmergencyNumberHelper;
+    }
+
     /** Destroys the instance. */
     public void destroy() {
         if (DBG) logd("destroy");
diff --git a/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java b/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
index b8956d5..3d6a4d1 100644
--- a/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
+++ b/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
@@ -210,12 +210,14 @@
 
     private final PowerManager.WakeLock mPartialWakeLock;
     private final CrossSimRedialingController mCrossSimRedialingController;
+    private final CarrierConfigHelper mCarrierConfigHelper;
 
     /** Constructor. */
     public EmergencyCallDomainSelector(Context context, int slotId, int subId,
             @NonNull Looper looper, @NonNull ImsStateTracker imsStateTracker,
             @NonNull DestroyListener destroyListener,
-            @NonNull CrossSimRedialingController csrController) {
+            @NonNull CrossSimRedialingController csrController,
+            @NonNull CarrierConfigHelper carrierConfigHelper) {
         super(context, slotId, subId, looper, imsStateTracker, destroyListener, TAG);
 
         mImsStateTracker.addBarringInfoListener(this);
@@ -225,6 +227,7 @@
         mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
 
         mCrossSimRedialingController = csrController;
+        mCarrierConfigHelper = carrierConfigHelper;
         acquireWakeLock();
     }
 
@@ -496,12 +499,7 @@
                 b.getIntArray(KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY);
         mImsRoamRatsConfig = b.getIntArray(
                 KEY_EMERGENCY_OVER_IMS_ROAMING_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY);
-        if (!isSimReady()) {
-            // Default configuration includes only EUTRAN.
-            // In case of no SIM or SIM locked state, add NGRAN.
-            mImsRatsConfig = new int[] { EUTRAN, NGRAN };
-            mImsRoamRatsConfig = new int[] { EUTRAN, NGRAN };
-        }
+        maybeModifyImsRats();
 
         mCsRatsConfig =
                 b.getIntArray(KEY_EMERGENCY_OVER_CS_SUPPORTED_ACCESS_NETWORK_TYPES_INT_ARRAY);
@@ -571,6 +569,16 @@
         }
     }
 
+    /** Adds NGRAN if SIM is absent or locked and the last valid subscription supported NGRAN. */
+    private void maybeModifyImsRats() {
+        if (mCarrierConfigHelper.isVoNrEmergencySupported(getSlotId())
+                && !isSimReady() && mImsRatsConfig.length < 2) {
+            // Default configuration includes only EUTRAN.
+            mImsRatsConfig = new int[] { EUTRAN, NGRAN };
+            mImsRoamRatsConfig = new int[] { EUTRAN, NGRAN };
+        }
+    }
+
     /**
      * Caches the resource configuration.
      */
@@ -849,6 +857,11 @@
             }
         }
 
+        // Adds NGRAN at the end of the list if SIM is absent or locked and NGRAN is not included.
+        if (!isSimReady() && !preferredNetworks.contains(NGRAN)) {
+            preferredNetworks.add(NGRAN);
+        }
+
         return preferredNetworks;
     }
 
diff --git a/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java b/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java
index 3a8fc86..de2d752 100644
--- a/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java
+++ b/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionService.java
@@ -17,6 +17,7 @@
 package com.android.services.telephony.domainselection;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.os.Handler;
@@ -71,7 +72,8 @@
                 @SelectorType int selectorType, boolean isEmergency, @NonNull Looper looper,
                 @NonNull ImsStateTracker imsStateTracker,
                 @NonNull DomainSelectorBase.DestroyListener listener,
-                @NonNull CrossSimRedialingController crossSimRedialingController);
+                @NonNull CrossSimRedialingController crossSimRedialingController,
+                @NonNull CarrierConfigHelper carrierConfigHelper);
     }
 
     private static final class DefaultDomainSelectorFactory implements DomainSelectorFactory {
@@ -80,7 +82,8 @@
                 @SelectorType int selectorType, boolean isEmergency, @NonNull Looper looper,
                 @NonNull ImsStateTracker imsStateTracker,
                 @NonNull DomainSelectorBase.DestroyListener listener,
-                @NonNull CrossSimRedialingController crossSimRedialingController) {
+                @NonNull CrossSimRedialingController crossSimRedialingController,
+                @NonNull CarrierConfigHelper carrierConfigHelper) {
             DomainSelectorBase selector = null;
 
             logi("create-DomainSelector: slotId=" + slotId + ", subId=" + subId
@@ -91,7 +94,8 @@
                 case SELECTOR_TYPE_CALLING:
                     if (isEmergency) {
                         selector = new EmergencyCallDomainSelector(context, slotId, subId, looper,
-                                imsStateTracker, listener, crossSimRedialingController);
+                                imsStateTracker, listener, crossSimRedialingController,
+                                carrierConfigHelper);
                     } else {
                         selector = new NormalCallDomainSelector(context, slotId, subId, looper,
                                 imsStateTracker, listener);
@@ -195,15 +199,17 @@
     private final DomainSelectorFactory mDomainSelectorFactory;
     private Handler mServiceHandler;
     private CrossSimRedialingController mCrossSimRedialingController;
+    private CarrierConfigHelper mCarrierConfigHelper;
 
     public TelephonyDomainSelectionService(Context context) {
-        this(context, ImsStateTracker::new, new DefaultDomainSelectorFactory());
+        this(context, ImsStateTracker::new, new DefaultDomainSelectorFactory(), null);
     }
 
     @VisibleForTesting
     public TelephonyDomainSelectionService(Context context,
             @NonNull ImsStateTrackerFactory imsStateTrackerFactory,
-            @NonNull DomainSelectorFactory domainSelectorFactory) {
+            @NonNull DomainSelectorFactory domainSelectorFactory,
+            @Nullable CarrierConfigHelper carrierConfigHelper) {
         mContext = context;
         mImsStateTrackerFactory = imsStateTrackerFactory;
         mDomainSelectorFactory = domainSelectorFactory;
@@ -225,6 +231,8 @@
         }
 
         mCrossSimRedialingController = new CrossSimRedialingController(context, getLooper());
+        mCarrierConfigHelper = (carrierConfigHelper != null)
+                ? carrierConfigHelper : new CarrierConfigHelper(context, getLooper());
 
         logi("TelephonyDomainSelectionService created");
     }
@@ -268,6 +276,11 @@
             mCrossSimRedialingController = null;
         }
 
+        if (mCarrierConfigHelper != null) {
+            mCarrierConfigHelper.destroy();
+            mCarrierConfigHelper = null;
+        }
+
         if (mServiceHandler != null) {
             mServiceHandler.getLooper().quit();
             mServiceHandler = null;
@@ -290,7 +303,7 @@
         ImsStateTracker ist = getImsStateTracker(slotId);
         DomainSelectorBase selector = mDomainSelectorFactory.create(mContext, slotId, subId,
                 selectorType, isEmergency, getLooper(), ist, mDestroyListener,
-                mCrossSimRedialingController);
+                mCrossSimRedialingController, mCarrierConfigHelper);
 
         if (selector != null) {
             // Ensures that ImsStateTracker is started before selecting the domain if not started
diff --git a/tests/src/com/android/services/telephony/domainselection/CarrierConfigHelperTest.java b/tests/src/com/android/services/telephony/domainselection/CarrierConfigHelperTest.java
new file mode 100644
index 0000000..5d4fe17
--- /dev/null
+++ b/tests/src/com/android/services/telephony/domainselection/CarrierConfigHelperTest.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.services.telephony.domainselection;
+
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.EUTRAN;
+import static android.telephony.AccessNetworkConstants.AccessNetworkType.NGRAN;
+import static android.telephony.CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL;
+import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY;
+
+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.anyBoolean;
+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.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+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.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Unit tests for CarrierConfigHelper
+ */
+public class CarrierConfigHelperTest {
+    private static final String TAG = "CarrierConfigHelperTest";
+
+    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 TEST_SIM_CARRIER_ID = 1911;
+
+    @Mock private Context mContext;
+    @Mock private SharedPreferences mSharedPreferences;
+    @Mock private SharedPreferences.Editor mEditor;
+    @Mock private Resources mResources;
+
+    private HandlerThread mHandlerThread;
+    private TestableLooper mLooper;
+    private CarrierConfigHelper mCarrierConfigHelper;
+    private CarrierConfigManager mCarrierConfigManager;
+    private TelephonyManager mTelephonyManager;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mContext = new TestContext() {
+            @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 Resources getResources() {
+                return mResources;
+            }
+        };
+
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+
+        mHandlerThread = new HandlerThread("CarrierConfigHelperTest");
+        mHandlerThread.start();
+
+        try {
+            mLooper = new TestableLooper(mHandlerThread.getLooper());
+        } catch (Exception e) {
+            logd("Unable to create looper from handler.");
+        }
+
+        doReturn(mEditor).when(mSharedPreferences).edit();
+
+        mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
+        mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+        doReturn(2).when(mTelephonyManager).getActiveModemCount();
+        doReturn(TelephonyManager.SIM_STATE_READY)
+                .when(mTelephonyManager).getSimState(anyInt());
+
+        doReturn(new int[] { TEST_SIM_CARRIER_ID }).when(mResources).getIntArray(anyInt());
+
+        mCarrierConfigHelper = new CarrierConfigHelper(mContext, mHandlerThread.getLooper(),
+                mSharedPreferences);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mCarrierConfigHelper != null) {
+            mCarrierConfigHelper.destroy();
+            mCarrierConfigHelper = 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());
+        assertFalse(mCarrierConfigHelper.isVoNrEmergencySupported(SLOT_0));
+    }
+
+    @Test
+    public void testCarrierConfigNotApplied() throws Exception {
+        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> callbackCaptor =
+                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+
+        verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+                callbackCaptor.capture());
+
+        CarrierConfigManager.CarrierConfigChangeListener callback = callbackCaptor.getValue();
+
+        assertNotNull(callback);
+
+        // NR is included but carrier config is not applied.
+        PersistableBundle b = getPersistableBundle(new int[] { EUTRAN, NGRAN }, false);
+        doReturn(b).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
+        callback.onCarrierConfigChanged(SLOT_0, SUB_1, 0, 0);
+
+        assertFalse(mCarrierConfigHelper.isVoNrEmergencySupported(SLOT_0));
+    }
+
+    @Test
+    public void testCarrierConfigApplied() throws Exception {
+        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> callbackCaptor =
+                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+
+        verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+                callbackCaptor.capture());
+
+        CarrierConfigManager.CarrierConfigChangeListener callback = callbackCaptor.getValue();
+
+        assertNotNull(callback);
+
+        // NR is included and carrier config is applied.
+        PersistableBundle b = getPersistableBundle(new int[] { EUTRAN, NGRAN }, true);
+        doReturn(b).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
+        callback.onCarrierConfigChanged(SLOT_0, SUB_1, 0, 0);
+
+        assertTrue(mCarrierConfigHelper.isVoNrEmergencySupported(SLOT_0));
+        assertFalse(mCarrierConfigHelper.isVoNrEmergencySupported(SLOT_1));
+
+        verify(mEditor).putBoolean(eq(CarrierConfigHelper.KEY_VONR_EMERGENCY_SUPPORT + SLOT_0),
+                eq(true));
+
+        // NR is not included and carrier config is applied.
+        b = getPersistableBundle(new int[] { EUTRAN }, true);
+        doReturn(b).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
+        callback.onCarrierConfigChanged(SLOT_0, SUB_1, 0, 0);
+
+        assertFalse(mCarrierConfigHelper.isVoNrEmergencySupported(SLOT_0));
+
+        verify(mEditor).putBoolean(eq(CarrierConfigHelper.KEY_VONR_EMERGENCY_SUPPORT + SLOT_0),
+                eq(false));
+    }
+
+    @Test
+    public void testCarrierConfigInvalidSubId() throws Exception {
+        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> callbackCaptor =
+                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+
+        verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+                callbackCaptor.capture());
+
+        CarrierConfigManager.CarrierConfigChangeListener callback = callbackCaptor.getValue();
+
+        assertNotNull(callback);
+
+        // NR is included and carrier config is applied.
+        PersistableBundle b = getPersistableBundle(new int[] { EUTRAN, NGRAN }, true);
+        doReturn(b).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
+
+        // Invalid subscription
+        callback.onCarrierConfigChanged(SLOT_0, SubscriptionManager.INVALID_SUBSCRIPTION_ID, 0, 0);
+
+        assertFalse(mCarrierConfigHelper.isVoNrEmergencySupported(SLOT_0));
+    }
+
+    @Test
+    public void testRestoreFromSharedPreferences() throws Exception {
+        doReturn(true).when(mSharedPreferences).getBoolean(anyString(), anyBoolean());
+        mCarrierConfigHelper = new CarrierConfigHelper(mContext, mHandlerThread.getLooper(),
+                mSharedPreferences);
+
+        assertTrue(mCarrierConfigHelper.isVoNrEmergencySupported(SLOT_0));
+    }
+
+    @Test
+    public void testCarrierIgnoreNrWhenSimRemoved() throws Exception {
+        ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> callbackCaptor =
+                ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
+
+        verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+                callbackCaptor.capture());
+
+        CarrierConfigManager.CarrierConfigChangeListener callback = callbackCaptor.getValue();
+
+        assertNotNull(callback);
+
+        // NR is included and carrier config for TEST SIM is applied.
+        PersistableBundle b = getPersistableBundle(new int[] { EUTRAN, NGRAN }, true);
+        doReturn(b).when(mCarrierConfigManager).getConfigForSubId(anyInt(), anyString());
+        callback.onCarrierConfigChanged(SLOT_0, SUB_1, TEST_SIM_CARRIER_ID, 0);
+
+        // NR is ignored.
+        assertFalse(mCarrierConfigHelper.isVoNrEmergencySupported(SLOT_0));
+        assertFalse(mCarrierConfigHelper.isVoNrEmergencySupported(SLOT_1));
+    }
+
+    private static PersistableBundle getPersistableBundle(int[] imsRats, boolean applied) {
+        PersistableBundle bundle  = new PersistableBundle();
+        bundle.putIntArray(KEY_EMERGENCY_OVER_IMS_SUPPORTED_3GPP_NETWORK_TYPES_INT_ARRAY, imsRats);
+        bundle.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, applied);
+        return bundle;
+    }
+
+    private static void logd(String str) {
+        Log.d(TAG, str);
+    }
+}
diff --git a/tests/src/com/android/services/telephony/domainselection/CrossSimRedialingControllerTest.java b/tests/src/com/android/services/telephony/domainselection/CrossSimRedialingControllerTest.java
index a32329d..2ed91b8 100644
--- a/tests/src/com/android/services/telephony/domainselection/CrossSimRedialingControllerTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/CrossSimRedialingControllerTest.java
@@ -27,6 +27,7 @@
 import static com.android.services.telephony.domainselection.CrossSimRedialingController.MSG_QUICK_CROSS_STACK_TIMEOUT;
 
 import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -42,6 +43,7 @@
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
 import android.testing.TestableLooper;
 import android.util.Log;
 
@@ -54,6 +56,11 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 /**
  * Unit tests for CrossSimRedialingController
  */
@@ -66,8 +73,6 @@
     private static final String TELECOM_CALL_ID1 = "TC1";
     private static final String TEST_EMERGENCY_NUMBER = "911";
 
-    @Mock private CarrierConfigManager mCarrierConfigManager;
-    @Mock private TelephonyManager mTelephonyManager;
     @Mock private EmergencyCallDomainSelector mEcds;
     @Mock private CrossSimRedialingController.EmergencyNumberHelper mEmergencyNumberHelper;
 
@@ -76,6 +81,8 @@
     private HandlerThread mHandlerThread;
     private TestableLooper mLooper;
     private CrossSimRedialingController mCsrController;
+    private CarrierConfigManager mCarrierConfigManager;
+    private TelephonyManager mTelephonyManager;
 
     @Before
     public void setUp() throws Exception {
@@ -462,6 +469,42 @@
         verify(mEcds, times(0)).notifyCrossStackTimerExpired();
     }
 
+    @Test
+    public void testEmergencyNumberHelper() throws Exception {
+        mCsrController = new CrossSimRedialingController(mContext,
+                mHandlerThread.getLooper());
+
+        CrossSimRedialingController.EmergencyNumberHelper helper =
+                mCsrController.getEmergencyNumberHelper();
+
+        assertNotNull(helper);
+
+        EmergencyNumber num1 = new EmergencyNumber(TEST_EMERGENCY_NUMBER, "us", "",
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE, new ArrayList<String>(),
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+
+        EmergencyNumber num2 = new EmergencyNumber("119", "jp", "",
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE, new ArrayList<String>(),
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN);
+
+        Map<Integer, List<EmergencyNumber>> lists = new HashMap<>();
+        List<EmergencyNumber> list = new ArrayList<>();
+        list.add(num1);
+        lists.put(1, list);
+
+        list = new ArrayList<>();
+        list.add(num2);
+        lists.put(2, list);
+
+        doReturn(lists).when(mTelephonyManager).getEmergencyNumberList();
+
+        assertTrue(helper.isEmergencyNumber(1, TEST_EMERGENCY_NUMBER));
+        assertFalse(helper.isEmergencyNumber(2, TEST_EMERGENCY_NUMBER));
+        assertFalse(helper.isEmergencyNumber(3, TEST_EMERGENCY_NUMBER));
+    }
+
     private void createController() throws Exception {
         mCsrController = new CrossSimRedialingController(mContext,
                 mHandlerThread.getLooper(), mEmergencyNumberHelper);
diff --git a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
index c8ac7e9..eab40f9 100644
--- a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
@@ -96,6 +96,7 @@
 import android.telephony.EmergencyRegResult;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PreciseDisconnectCause;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.TransportSelectorCallback;
 import android.telephony.WwanSelectorCallback;
@@ -140,6 +141,7 @@
     @Mock private DomainSelectorBase.DestroyListener mDestroyListener;
     @Mock private ProvisioningManager mProvisioningManager;
     @Mock private CrossSimRedialingController mCsrdCtrl;
+    @Mock private CarrierConfigHelper mCarrierConfigHelper;
     @Mock private Resources mResources;
 
     private Context mContext;
@@ -2002,7 +2004,35 @@
     }
 
     @Test
-    public void testSimLockScanPsPreferredIncludingNr() throws Exception {
+    public void testSimLockScanPsPreferredWithNr() throws Exception {
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(false);
+
+        // The last valid subscription supported NR.
+        doReturn(true).when(mCarrierConfigHelper).isVoNrEmergencySupported(eq(SLOT_0));
+        when(mTelephonyManager.getSimState(anyInt())).thenReturn(
+                TelephonyManager.SIM_STATE_PIN_REQUIRED);
+
+        EmergencyRegResult regResult = getEmergencyRegResult(
+                UNKNOWN, REGISTRATION_STATE_UNKNOWN, 0, false, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+        processAllMessages();
+
+        verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
+                any(), anyInt(), any(), any());
+        assertEquals(4, mAccessNetwork.size());
+        assertEquals(EUTRAN, (int) mAccessNetwork.get(0));
+        assertEquals(NGRAN, (int) mAccessNetwork.get(1));
+        assertEquals(UTRAN, (int) mAccessNetwork.get(2));
+        assertEquals(GERAN, (int) mAccessNetwork.get(3));
+    }
+
+    @Test
+    public void testSimLockScanPsPreferredWithNrAtTheEnd() throws Exception {
         createSelector(SLOT_0_SUB_ID);
         unsolBarringInfoChanged(false);
 
@@ -2020,8 +2050,62 @@
 
         verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
                 any(), anyInt(), 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 testInvalidSubscriptionScanPsPreferredWithNrAtTheEnd() throws Exception {
+        createSelector(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        unsolBarringInfoChanged(false);
+
+        EmergencyRegResult regResult = getEmergencyRegResult(
+                UNKNOWN, REGISTRATION_STATE_UNKNOWN, 0, false, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0,
+                SubscriptionManager.INVALID_SUBSCRIPTION_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+        processAllMessages();
+
+        verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
+                any(), anyInt(), 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 testInvalidSubscriptionScanPsPreferredWithNr() throws Exception {
+        createSelector(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        unsolBarringInfoChanged(false);
+
+        // The last valid subscription supported NR.
+        doReturn(true).when(mCarrierConfigHelper).isVoNrEmergencySupported(eq(SLOT_0));
+
+        EmergencyRegResult regResult = getEmergencyRegResult(
+                UNKNOWN, REGISTRATION_STATE_UNKNOWN, 0, false, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0,
+                SubscriptionManager.INVALID_SUBSCRIPTION_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+        processAllMessages();
+
+        verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
+                any(), anyInt(), any(), any());
+        assertEquals(4, mAccessNetwork.size());
         assertEquals(EUTRAN, (int) mAccessNetwork.get(0));
         assertEquals(NGRAN, (int) mAccessNetwork.get(1));
+        assertEquals(UTRAN, (int) mAccessNetwork.get(2));
+        assertEquals(GERAN, (int) mAccessNetwork.get(3));
     }
 
     private void setupForScanListTest(PersistableBundle bundle) throws Exception {
@@ -2097,7 +2181,7 @@
     private void createSelector(int subId) throws Exception {
         mDomainSelector = new EmergencyCallDomainSelector(
                 mContext, SLOT_0, subId, mHandlerThread.getLooper(),
-                mImsStateTracker, mDestroyListener, mCsrdCtrl);
+                mImsStateTracker, mDestroyListener, mCsrdCtrl, mCarrierConfigHelper);
         mDomainSelector.clearResourceConfiguration();
         replaceInstance(DomainSelectorBase.class,
                 "mWwanSelectorCallback", mDomainSelector, mWwanSelectorCallback);
diff --git a/tests/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionServiceTest.java b/tests/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionServiceTest.java
index f340e94..647ef41 100644
--- a/tests/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/TelephonyDomainSelectionServiceTest.java
@@ -78,7 +78,8 @@
                         @SelectorType int selectorType, boolean isEmergency,
                         @NonNull Looper looper, @NonNull ImsStateTracker imsStateTracker,
                         @NonNull DomainSelectorBase.DestroyListener listener,
-                        @NonNull CrossSimRedialingController crossSimRedialingController) {
+                        @NonNull CrossSimRedialingController crossSimRedialingController,
+                        @NonNull CarrierConfigHelper carrierConfigHelper) {
                     switch (selectorType) {
                         case DomainSelectionService.SELECTOR_TYPE_CALLING: // fallthrough
                         case DomainSelectionService.SELECTOR_TYPE_SMS: // fallthrough
@@ -107,6 +108,7 @@
     @Mock private TransportSelectorCallback mSelectorCallback1;
     @Mock private TransportSelectorCallback mSelectorCallback2;
     @Mock private ImsStateTracker mImsStateTracker;
+    @Mock private CarrierConfigHelper mCarrierConfigHelper;
 
     private final ServiceState mServiceState = new ServiceState();
     private final BarringInfo mBarringInfo = new BarringInfo();
@@ -128,7 +130,7 @@
 
         mContext = new TestContext();
         mDomainSelectionService = new TelephonyDomainSelectionService(mContext,
-                mImsStateTrackerFactory, mDomainSelectorFactory);
+                mImsStateTrackerFactory, mDomainSelectorFactory, mCarrierConfigHelper);
         mServiceHandler = new Handler(mDomainSelectionService.getLooper());
         mTestableLooper = new TestableLooper(mDomainSelectionService.getLooper());