Merge "Fix conference call cannot be hung up"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index ce774d8..a7c2ae3 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -92,6 +92,7 @@
     <protected-broadcast android:name= "android.telephony.action.SIM_SLOT_STATUS_CHANGED" />
     <protected-broadcast android:name= "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED" />
     <protected-broadcast android:name= "android.telephony.action.SUBSCRIPTION_PRECISE_CARRIER_IDENTITY_CHANGED" />
+    <protected-broadcast android:name= "android.telephony.action.NETWORK_COUNTRY_CHANGED" />
 
     <uses-permission android:name="android.permission.BROADCAST_STICKY" />
     <uses-permission android:name="android.permission.CALL_PHONE" />
diff --git a/ecc/README.md b/ecc/README.md
index b5dc538..304cdfb 100644
--- a/ecc/README.md
+++ b/ecc/README.md
@@ -30,4 +30,4 @@
 4. Make TeleService
 5. Push TeleService.apk to system/priv-app/TeleService
 6. Reboot device
-7. run 'atest TeleServiceTests:IsoToEccProtobufRepositoryTest#testEccDataContent'
+7. run 'atest TeleServiceTests:EccDataTest#testEccDataContent'
diff --git a/ecc/gen_eccdata.sh b/ecc/gen_eccdata.sh
index 4f3a097..e4dd745 100755
--- a/ecc/gen_eccdata.sh
+++ b/ecc/gen_eccdata.sh
@@ -57,4 +57,4 @@
 echo "  1. make TeleService"
 echo "  2. push TeleService.apk to system/priv-app/TeleService"
 echo "  3. reboot device"
-echo "  4. run 'atest TeleServiceTests:IsoToEccProtobufRepositoryTest#testEccDataContent'"
+echo "  4. run 'atest TeleServiceTests:EccDataTest#testEccDataContent'"
diff --git a/src/com/android/phone/EccShortcutAdapter.java b/src/com/android/phone/EccShortcutAdapter.java
index e14b90a..19b1fec 100644
--- a/src/com/android/phone/EccShortcutAdapter.java
+++ b/src/com/android/phone/EccShortcutAdapter.java
@@ -17,6 +17,7 @@
 package com.android.phone;
 
 import android.content.Context;
+import android.telephony.emergency.EmergencyNumber;
 import android.text.TextUtils;
 import android.view.View;
 import android.view.ViewGroup;
@@ -25,9 +26,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.phone.ecc.CountryEccInfo;
-import com.android.phone.ecc.EccInfo;
-
 import com.google.common.collect.LinkedListMultimap;
 
 import java.util.ArrayList;
@@ -35,15 +33,11 @@
 
 /**
  * An abstract adapter between ECC data and the view contains ECC shortcuts.
- * This adapter will convert given {@link CountryEccInfo} to number string, description string and
- * icon resource id for each {@link EccInfo}.
+ * This adapter prepares description and icon for every promoted emergency number.
  * The subclass should implements {@link #inflateView} to provide the view for an ECC data, when the
  * view container calls {@link #getView}.
  */
 public abstract class EccShortcutAdapter extends BaseAdapter {
-    // GSM default emergency number, used when country's fallback ECC(112 or 911) not available.
-    private static final String FALLBACK_EMERGENCY_NUMBER = "112";
-
     private List<EccDisplayMaterial> mEccDisplayMaterialList;
 
     private CharSequence mPoliceDescription;
@@ -90,16 +84,16 @@
      * Get a View that display the given ECC data: number, description and iconRes.
      *
      * @param convertView The old view to reuse, if possible. Note: You should check that this view
-     *                   is non-null and of an appropriate type before using. If it is not possible
-     *                   to convert this view to display the correct data, this method can create a
-     *                   new view. Heterogeneous lists can specify their number of view types, so
-     *                   that this View is always of the right type (see {@link
-     *                   BaseAdapter#getViewTypeCount()} and {@link
-     *                   BaseAdapter#getItemViewType(int)}).
-     * @param parent The parent that this view will eventually be attached to.
-     * @param number The number of the ECC shortcut to display in the view.
+     *                    is non-null and of an appropriate type before using. If it is not possible
+     *                    to convert this view to display the correct data, this method can create a
+     *                    new view. Heterogeneous lists can specify their number of view types, so
+     *                    that this View is always of the right type (see {@link
+     *                    BaseAdapter#getViewTypeCount()} and {@link
+     *                    BaseAdapter#getItemViewType(int)}).
+     * @param parent      The parent that this view will eventually be attached to.
+     * @param number      The number of the ECC shortcut to display in the view.
      * @param description The description of the ECC shortcut to display in the view.
-     * @param iconRes The icon resource ID represent for the ECC shortcut.
+     * @param iconRes     The icon resource ID represent for the ECC shortcut.
      * @return A View corresponding to the data at the specified position.
      */
     public abstract View inflateView(View convertView, ViewGroup parent, CharSequence number,
@@ -110,89 +104,68 @@
      * be display by the short container View.
      *
      * @param context The context used to access resources.
-     * @param countryEccInfo Updated country ECC info.
+     * @param phoneInfo Information of the phone to make an emergency call.
      */
-    public void updateCountryEccInfo(@NonNull Context context, CountryEccInfo countryEccInfo) {
+    public void updateCountryEccInfo(@NonNull Context context,
+            @Nullable ShortcutViewUtils.PhoneInfo phoneInfo) {
         List<EccDisplayMaterial> displayMaterials = new ArrayList<>();
 
-        final EccInfo.Type[] orderedMustHaveTypes =
-                { EccInfo.Type.POLICE, EccInfo.Type.AMBULANCE, EccInfo.Type.FIRE };
-
-        String fallback = null;
-        EccInfo[] eccInfoList = null;
-        if (countryEccInfo != null) {
-            fallback = countryEccInfo.getFallbackEcc();
-            eccInfoList = countryEccInfo.getEccInfoList();
-        }
-        if (TextUtils.isEmpty(fallback)) {
-            fallback = FALLBACK_EMERGENCY_NUMBER;
-        }
-
-        // Finding matched ECC for each must have types.
-        // Using LinkedListMultimap to prevent duplicated keys.
-        // LinkedListMultimap also preserve the insertion order of keys (ECC number) and values
-        // (matched types of the ECC number), which follows the order in orderedMustHaveTypes.
-        LinkedListMultimap<String, EccInfo.Type> eccList = LinkedListMultimap.create();
-        for (EccInfo.Type type : orderedMustHaveTypes) {
-            String number = null;
-            if (eccInfoList != null) {
-                number = pickEccNumberForType(type, eccInfoList);
+        try {
+            if (phoneInfo == null) {
+                return;
             }
-            if (number == null) {
-                number = fallback;
-            }
-            // append type for exist number, otherwise insert a new entry.
-            eccList.put(number, type);
-        }
 
-        // prepare display material for picked ECC
-        for (String number : eccList.keySet()) {
-            EccDisplayMaterial material = prepareDisplayMaterialForEccInfo(context,
-                    new EccInfo(number, eccList.asMap().get(number)));
-            if (material != null) {
-                displayMaterials.add(material);
-            }
-        }
-
-        mEccDisplayMaterialList = displayMaterials;
-        notifyDataSetChanged();
-    }
-
-    private @Nullable String pickEccNumberForType(@NonNull EccInfo.Type targetType,
-            @NonNull EccInfo[] eccInfoList) {
-        EccInfo pickedEccInfo = null;
-        for (EccInfo eccInfo : eccInfoList) {
-            if (eccInfo.containsType(targetType)) {
-                // An ECC is more suitable for a type if the ECC has fewer other types.
-                if (pickedEccInfo == null
-                        || eccInfo.getTypesCount() < pickedEccInfo.getTypesCount()) {
-                    pickedEccInfo = eccInfo;
+            LinkedListMultimap<String, Integer> emergencyNumbers = LinkedListMultimap.create();
+            for (int category : ShortcutViewUtils.PROMOTED_CATEGORIES) {
+                String number = pickEmergencyNumberForCategory(category,
+                        phoneInfo.getPromotedEmergencyNumbers());
+                if (number != null) {
+                    emergencyNumbers.put(number, category);
                 }
             }
+
+            // prepare display material for picked ECC
+            for (String number : emergencyNumbers.keySet()) {
+                EccDisplayMaterial material = prepareEccMaterial(context, number,
+                        emergencyNumbers.get(number));
+                if (material != null) {
+                    displayMaterials.add(material);
+                }
+            }
+        } finally {
+            mEccDisplayMaterialList = displayMaterials;
+            notifyDataSetChanged();
         }
-        if (pickedEccInfo != null) {
-            return pickedEccInfo.getNumber();
+    }
+
+    @Nullable
+    private String pickEmergencyNumberForCategory(int category,
+            @NonNull List<EmergencyNumber> emergencyNumbers) {
+        for (EmergencyNumber number : emergencyNumbers) {
+            if ((number.getEmergencyServiceCategoryBitmask() & category) != 0) {
+                return number.getNumber();
+            }
         }
         return null;
     }
 
-    private @Nullable EccDisplayMaterial prepareDisplayMaterialForEccInfo(@NonNull Context context,
-            @NonNull EccInfo eccInfo) {
+    @Nullable
+    private EccDisplayMaterial prepareEccMaterial(@NonNull Context context, @NonNull String number,
+            @NonNull List<Integer> categories) {
         EccDisplayMaterial material = new EccDisplayMaterial();
-        material.number = eccInfo.getNumber();
-        EccInfo.Type[] types = eccInfo.getTypes();
-        for (EccInfo.Type type : types) {
+        material.number = number;
+        for (int category : categories) {
             CharSequence description;
-            switch (type) {
-                case POLICE:
+            switch (category) {
+                case EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE:
                     description = mPoliceDescription;
                     material.iconRes = R.drawable.ic_local_police_gm2_24px;
                     break;
-                case AMBULANCE:
+                case EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE:
                     description = mAmbulanceDescription;
                     material.iconRes = R.drawable.ic_local_hospital_gm2_24px;
                     break;
-                case FIRE:
+                case EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE:
                     description = mFireDescription;
                     material.iconRes = R.drawable.ic_local_fire_department_gm2_24px;
                     break;
@@ -200,6 +173,7 @@
                     // ignore unknown types
                     continue;
             }
+
             if (TextUtils.isEmpty(material.description)) {
                 material.description = description;
             } else {
@@ -209,10 +183,10 @@
                         material.description, description);
             }
         }
+
         if (TextUtils.isEmpty(material.description) || material.iconRes == 0) {
             return null;
         }
         return material;
     }
-
 }
diff --git a/src/com/android/phone/EmergencyDialer.java b/src/com/android/phone/EmergencyDialer.java
index c89ddc6..80b4632 100644
--- a/src/com/android/phone/EmergencyDialer.java
+++ b/src/com/android/phone/EmergencyDialer.java
@@ -83,9 +83,6 @@
 import com.android.phone.common.dialpad.DialpadKeyButton;
 import com.android.phone.common.util.ViewUtil;
 import com.android.phone.common.widget.ResizingTextEditText;
-import com.android.phone.ecc.CountryEccInfo;
-import com.android.phone.ecc.EccInfoHelper;
-import com.android.phone.ecc.IsoToEccProtobufRepository;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -235,7 +232,7 @@
     private View mEmergencyShortcutView;
     private View mDialpadView;
 
-    private EccInfoHelper mEccInfoHelper;
+    private ShortcutViewUtils.PhoneInfo mPhoneInfo;
 
     private List<EmergencyShortcutButton> mEmergencyShortcutButtonList;
     private EccShortcutAdapter mShortcutAdapter;
@@ -363,14 +360,10 @@
                 configMgr.getConfigForSubId(SubscriptionManager.getDefaultVoiceSubscriptionId());
 
         mIsShortcutViewEnabled = false;
+        mPhoneInfo = null;
         if (canEnableShortcutView(carrierConfig)) {
-            TelephonyManager tm = getSystemService(TelephonyManager.class);
-            String countryIso = tm.getNetworkCountryIso();
-            if (TextUtils.isEmpty(countryIso)) {
-                Log.d(LOG_TAG, "Unable to determine the country of current network.");
-            } else if (!EccInfoHelper.isCountryEccInfoAvailable(this, countryIso)) {
-                Log.d(LOG_TAG, "ECC info is unavailable.");
-            } else {
+            mPhoneInfo = ShortcutViewUtils.pickPreferredPhone(this);
+            if (mPhoneInfo != null) {
                 mIsShortcutViewEnabled = true;
             }
         }
@@ -469,7 +462,6 @@
         mEmergencyInfoGroup = (EmergencyInfoGroup) findViewById(R.id.emergency_info_button);
 
         if (mIsShortcutViewEnabled) {
-            mEccInfoHelper = new EccInfoHelper(IsoToEccProtobufRepository.getInstance());
             setupEmergencyShortcutsView();
         }
     }
@@ -611,11 +603,8 @@
 
         if (!TextUtils.isEmpty(phoneNumber)) {
             if (DBG) Log.d(LOG_TAG, "dial emergency number: " + Rlog.pii(LOG_TAG, phoneNumber));
-            Bundle extras = new Bundle();
-            extras.putInt(TelecomManager.EXTRA_CALL_SOURCE,
-                    ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_SHORTCUT);
-            TelecomManager tm = (TelecomManager) getSystemService(TELECOM_SERVICE);
-            tm.placeCall(Uri.fromParts(PhoneAccount.SCHEME_TEL, phoneNumber, null), extras);
+            placeCall(phoneNumber, ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_SHORTCUT,
+                    mPhoneInfo);
         } else {
             Log.d(LOG_TAG, "emergency number is empty");
         }
@@ -769,30 +758,9 @@
             updateTheme(lockScreenColors.supportsDarkText());
         }
 
-        if (mIsShortcutViewEnabled && mEccInfoHelper != null) {
-            final Context context = this;
-            mEccInfoHelper.getCountryEccInfoAsync(context,
-                    new EccInfoHelper.CountryEccInfoResultCallback() {
-                        @Override
-                        public void onSuccess(String iso, CountryEccInfo countryEccInfo) {
-                            Log.d(LOG_TAG, "Retrieve ECC info success, country ISO: "
-                                    + Rlog.pii(LOG_TAG, iso));
-                            updateLocationAndEccInfo(iso, countryEccInfo);
-                        }
-
-                        @Override
-                        public void onDetectCountryFailed() {
-                            Log.w(LOG_TAG, "Cannot detect current country.");
-                            updateLocationAndEccInfo(null, null);
-                        }
-
-                        @Override
-                        public void onRetrieveCountryEccInfoFailed(String iso) {
-                            Log.w(LOG_TAG, "Retrieve ECC info failed, country ISO: "
-                                    + Rlog.pii(LOG_TAG, iso));
-                            updateLocationAndEccInfo(iso, null);
-                        }
-                    });
+        if (mIsShortcutViewEnabled) {
+            mPhoneInfo = ShortcutViewUtils.pickPreferredPhone(this);
+            updateLocationAndEccInfo();
         }
     }
 
@@ -897,7 +865,20 @@
         // nothing and just returns input number.
         mLastNumber = PhoneNumberUtils.convertToEmergencyNumber(this, mLastNumber);
 
-        if (PhoneNumberUtils.isLocalEmergencyNumber(this, mLastNumber)) {
+        boolean isEmergencyNumber = false;
+        ShortcutViewUtils.PhoneInfo phoneToMakeCall = null;
+        if (mPhoneInfo != null) {
+            isEmergencyNumber = mPhoneInfo.hasPromotedEmergencyNumber(mLastNumber);
+            if (isEmergencyNumber) {
+                phoneToMakeCall = mPhoneInfo;
+            }
+        }
+        if (!isEmergencyNumber) {
+            TelephonyManager tm = getSystemService(TelephonyManager.class);
+            isEmergencyNumber = tm.isCurrentEmergencyNumber(mLastNumber);
+        }
+
+        if (isEmergencyNumber) {
             if (DBG) Log.d(LOG_TAG, "placing call to " + mLastNumber);
 
             // place the call if it is a valid number
@@ -910,11 +891,8 @@
             mMetricsWriter.writeMetricsForMakingCall(MetricsWriter.CALL_SOURCE_DIALPAD,
                     MetricsWriter.PHONE_NUMBER_TYPE_EMERGENCY, isShortcutNumber(mLastNumber));
 
-            Bundle extras = new Bundle();
-            extras.putInt(TelecomManager.EXTRA_CALL_SOURCE,
-                    ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_DIALPAD);
-            TelecomManager tm = (TelecomManager) getSystemService(TELECOM_SERVICE);
-            tm.placeCall(Uri.fromParts(PhoneAccount.SCHEME_TEL, mLastNumber, null), extras);
+            placeCall(mLastNumber, ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_DIALPAD,
+                    phoneToMakeCall);
         } else {
             if (DBG) Log.d(LOG_TAG, "rejecting bad requested number " + mLastNumber);
 
@@ -928,6 +906,20 @@
         mDigits.getText().delete(0, mDigits.getText().length());
     }
 
+    private void placeCall(String number, int callSource, ShortcutViewUtils.PhoneInfo phone) {
+        Bundle extras = new Bundle();
+        extras.putInt(TelecomManager.EXTRA_CALL_SOURCE, callSource);
+
+        if (phone != null && phone.getPhoneAccountHandle() != null) {
+            // Requests to dial through the specified phone.
+            extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
+                    phone.getPhoneAccountHandle());
+        }
+
+        TelecomManager tm = this.getSystemService(TelecomManager.class);
+        tm.placeCall(Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null), extras);
+    }
+
     /**
      * Plays the specified tone for TONE_LENGTH_MS milliseconds.
      *
@@ -1071,7 +1063,7 @@
         AsyncTask<Void, Void, Boolean> showWfcWarningTask = new AsyncTask<Void, Void, Boolean>() {
             @Override
             protected Boolean doInBackground(Void... voids) {
-                TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
+                TelephonyManager tm = getSystemService(TelephonyManager.class);
                 boolean isWfcAvailable = tm.isWifiCallingAvailable();
                 ServiceState ss = tm.getServiceState();
                 boolean isCellAvailable =
@@ -1143,14 +1135,15 @@
         mEmergencyShortcutButtonList = new ArrayList<>();
         setupEmergencyCallShortcutButton();
 
-        updateLocationAndEccInfo(null, null);
+        updateLocationAndEccInfo();
 
         switchView(mEmergencyShortcutView, mDialpadView, false);
     }
 
-    private void setLocationInfo(String countryIso) {
+    private void setLocationInfo() {
         final View locationInfo = findViewById(R.id.location_info);
 
+        String countryIso = mPhoneInfo != null ? mPhoneInfo.getCountryIso() : null;
         String countryName = null;
         if (!TextUtils.isEmpty(countryIso)) {
             Locale locale = Locale.getDefault();
@@ -1227,11 +1220,11 @@
         mShortcutAdapter.registerDataSetObserver(mShortcutDataSetObserver);
     }
 
-    private void updateLocationAndEccInfo(String iso, CountryEccInfo countryEccInfo) {
+    private void updateLocationAndEccInfo() {
         if (!isFinishing() && !isDestroyed()) {
-            setLocationInfo(iso);
+            setLocationInfo();
             if (mShortcutAdapter != null) {
-                mShortcutAdapter.updateCountryEccInfo(this, countryEccInfo);
+                mShortcutAdapter.updateCountryEccInfo(this, mPhoneInfo);
             }
         }
     }
diff --git a/src/com/android/phone/MobileNetworkSettings.java b/src/com/android/phone/MobileNetworkSettings.java
index 86babd5..465b89a 100644
--- a/src/com/android/phone/MobileNetworkSettings.java
+++ b/src/com/android/phone/MobileNetworkSettings.java
@@ -485,7 +485,7 @@
 
         public void onIntentUpdate(Intent intent) {
             if (!mUnavailable) {
-                updateCurrentTab(intent);
+                updateCurrentTab(intent.getExtras());
             }
         }
 
@@ -583,8 +583,7 @@
             }
         };
 
-        private int getSlotIdFromIntent(Intent intent) {
-            Bundle data = intent.getExtras();
+        private int getSlotIdFromBundle(Bundle data) {
             int subId = -1;
             if (data != null) {
                 subId = data.getInt(Settings.EXTRA_SUB_ID, -1);
@@ -754,8 +753,8 @@
                     mEmptyTabContent);
         }
 
-        private void updateCurrentTab(Intent intent) {
-            int slotId = getSlotIdFromIntent(intent);
+        private void updateCurrentTab(Bundle data) {
+            int slotId = getSlotIdFromBundle(data);
             if (slotId >= 0 && mTabHost != null && mTabHost.getCurrentTab() != slotId) {
                 mTabHost.setCurrentTab(slotId);
             }
@@ -768,6 +767,9 @@
             // If advanced fields are already expanded, we save it and expand it
             // when it's re-created.
             outState.putBoolean(EXPAND_ADVANCED_FIELDS, mExpandAdvancedFields);
+
+            // Save subId of currently shown tab.
+            outState.putInt(Settings.EXTRA_SUB_ID, mSubId);
         }
 
         @Override
@@ -851,7 +853,12 @@
                 getActivity().setContentView(R.layout.telephony_disallowed_preference_screen);
             } else {
                 initializeSubscriptions();
-                updateCurrentTab(getActivity().getIntent());
+
+                if (savedInstanceState != null) {
+                    updateCurrentTab(savedInstanceState);
+                } else {
+                    updateCurrentTab(getActivity().getIntent().getExtras());
+                }
             }
         }
 
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index ff69bb7..3078220 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -86,6 +86,7 @@
 import android.telephony.data.ApnSetting;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.gsm.GsmCellLocation;
+import android.telephony.ims.ProvisioningManager;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsConfig;
 import android.telephony.ims.aidl.IImsConfigCallback;
@@ -93,6 +94,8 @@
 import android.telephony.ims.aidl.IImsRcsFeature;
 import android.telephony.ims.aidl.IImsRegistration;
 import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsConfigImplBase;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.text.TextUtils;
 import android.util.ArraySet;
@@ -130,6 +133,7 @@
 import com.android.internal.telephony.SmsApplication.SmsApplicationData;
 import com.android.internal.telephony.SubscriptionController;
 import com.android.internal.telephony.TelephonyPermissions;
+import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 import com.android.internal.telephony.euicc.EuiccConnector;
 import com.android.internal.telephony.uicc.IccIoResult;
 import com.android.internal.telephony.uicc.IccUtils;
@@ -153,9 +157,11 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Implementation of the ITelephony interface.
@@ -257,6 +263,10 @@
     private static final String PREF_CARRIERS_ALPHATAG_PREFIX = "carrier_alphtag_";
     private static final String PREF_CARRIERS_NUMBER_PREFIX = "carrier_number_";
     private static final String PREF_CARRIERS_SUBSCRIBER_PREFIX = "carrier_subscriber_";
+    private static final String PREF_PROVISION_IMS_MMTEL_PREFIX = "provision_ims_mmtel_";
+
+    // String to store multi SIM allowed
+    private static final String PREF_MULTI_SIM_RESTRICTED = "multisim_restricted";
 
     // The AID of ISD-R.
     private static final String ISDR_AID = "A0000005591010FFFFFFFF8900000100";
@@ -2940,8 +2950,10 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            boolean isRoaming = TelephonyManager.from(
+                    getPhone(subId).getContext()).isNetworkRoaming(subId);
             ImsManager.getInstance(mApp,
-                    getSlotIndexOrException(subId)).setWfcNonPersistent(isCapable, mode);
+                    getSlotIndexOrException(subId)).setWfcNonPersistent(isCapable, mode, isRoaming);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -3063,15 +3075,249 @@
     }
 
     @Override
+    public void setImsProvisioningStatusForCapability(int subId, int capability, int tech,
+            boolean isProvisioned) {
+        if (tech != ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN
+                && tech != ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
+            throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
+        }
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
+                "setProvisioningStatusForCapability");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            Phone phone = getPhone(subId);
+            if (phone == null) {
+                loge("setImsProvisioningStatusForCapability: phone instance null for subid "
+                        + subId);
+                return;
+            }
+            if (!doesImsCapabilityRequireProvisioning(phone.getContext(), subId, capability)) {
+                return;
+            }
+
+            // this capability requires provisioning, route to the correct API.
+            ImsManager ims = ImsManager.getInstance(mApp, getSlotIndex(subId));
+            switch (capability) {
+                case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE: {
+                    if (tech == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
+                        ims.setVolteProvisioned(isProvisioned);
+                    } else if (tech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) {
+                        ims.setWfcProvisioned(isProvisioned);
+                    }
+                    break;
+                }
+                case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO: {
+                    // There is currently no difference in VT provisioning type.
+                    ims.setVtProvisioned(isProvisioned);
+                    break;
+                }
+                case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT: {
+                    // There is no "deprecated" UT provisioning mechanism through ImsConfig, so
+                    // change the capability of the feature instead if needed.
+                    if (isMmTelCapabilityProvisionedInCache(subId, capability, tech)
+                            == isProvisioned) {
+                        // No change in provisioning.
+                        return;
+                    }
+                    cacheMmTelCapabilityProvisioning(subId, capability, tech, isProvisioned);
+                    try {
+                        ims.changeMmTelCapability(capability, tech, isProvisioned);
+                    } catch (ImsException e) {
+                        loge("setImsProvisioningStatusForCapability: couldn't change UT capability"
+                                + ", Exception" + e.getMessage());
+                    }
+                    break;
+                }
+                default: {
+                    throw new IllegalArgumentException("Tried to set provisioning for capability '"
+                            + capability + "', which does not require provisioning.");
+                }
+            }
+
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public boolean getImsProvisioningStatusForCapability(int subId, int capability, int tech) {
+        if (tech != ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN
+                && tech != ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
+            throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
+        }
+        enforceReadPrivilegedPermission("getProvisioningStatusForCapability");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            Phone phone = getPhone(subId);
+            if (phone == null) {
+                loge("getImsProvisioningStatusForCapability: phone instance null for subid "
+                        + subId);
+                // We will fail with "true" as the provisioning status because this is the default
+                // if we do not require provisioning.
+                return true;
+            }
+
+            if (!doesImsCapabilityRequireProvisioning(phone.getContext(), subId, capability)) {
+                return true;
+            }
+
+            ImsManager ims = ImsManager.getInstance(mApp, getSlotIndex(subId));
+            switch (capability) {
+                case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE: {
+                    if (tech == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
+                        return ims.isVolteProvisionedOnDevice();
+                    } else if (tech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) {
+                        return ims.isWfcProvisionedOnDevice();
+                    }
+                    // This should never happen, since we are checking tech above to make sure it
+                    // is either LTE or IWLAN.
+                    throw new IllegalArgumentException("Invalid radio technology for voice "
+                            + "capability.");
+                }
+                case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO: {
+                    // There is currently no difference in VT provisioning type.
+                    return ims.isVtProvisionedOnDevice();
+                }
+                case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT: {
+                    // There is no "deprecated" UT provisioning mechanism, so get from shared prefs.
+                    return isMmTelCapabilityProvisionedInCache(subId, capability, tech);
+                }
+                default: {
+                    throw new IllegalArgumentException("Tried to get provisioning for capability '"
+                            + capability + "', which does not require provisioning.");
+                }
+            }
+
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public boolean isMmTelCapabilityProvisionedInCache(int subId, int capability, int tech) {
+        if (tech != ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN
+                && tech != ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
+            throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
+        }
+        enforceReadPrivilegedPermission("isMmTelCapabilityProvisionedInCache");
+        int provisionedBits = getMmTelCapabilityProvisioningBitfield(subId, tech);
+        return (provisionedBits & capability) > 0;
+    }
+
+    @Override
+    public void cacheMmTelCapabilityProvisioning(int subId, int capability, int tech,
+            boolean isProvisioned) {
+        if (tech != ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN
+                && tech != ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
+            throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
+        }
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
+                "setProvisioningStatusForCapability");
+        int provisionedBits = getMmTelCapabilityProvisioningBitfield(subId, tech);
+        // If the current provisioning status for capability already matches isProvisioned,
+        // do nothing.
+        if (((provisionedBits & capability) > 0) == isProvisioned) {
+            return;
+        }
+        if (isProvisioned) {
+            setMmTelCapabilityProvisioningBitfield(subId, tech, (provisionedBits | capability));
+        } else {
+            setMmTelCapabilityProvisioningBitfield(subId, tech, (provisionedBits & ~capability));
+        }
+    }
+
+    /**
+     * @return the bitfield containing the MmTel provisioning for the provided subscription and
+     * technology. The bitfield should mirror the bitfield defined by
+     * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}.
+     */
+    private int getMmTelCapabilityProvisioningBitfield(int subId, int tech) {
+        String key = getMmTelProvisioningKey(subId, tech);
+        // Default is no capabilities are provisioned.
+        return mTelephonySharedPreferences.getInt(key, 0 /*default*/);
+    }
+
+    /**
+     * Sets the MmTel capability provisioning bitfield (defined by
+     *     {@link MmTelFeature.MmTelCapabilities.MmTelCapability}) for the subscription and
+     *     technology specified.
+     *
+     * Note: This is a synchronous command and should not be called on UI thread.
+     */
+    private void setMmTelCapabilityProvisioningBitfield(int subId, int tech, int newField) {
+        final SharedPreferences.Editor editor = mTelephonySharedPreferences.edit();
+        String key = getMmTelProvisioningKey(subId, tech);
+        editor.putInt(key, newField);
+        editor.commit();
+    }
+
+    private static String getMmTelProvisioningKey(int subId, int tech) {
+        // resulting key is provision_ims_mmtel_{subId}_{tech}
+        return PREF_PROVISION_IMS_MMTEL_PREFIX + subId + "_" + tech;
+    }
+
+    /**
+     * Query CarrierConfig to see if the specified capability requires provisioning for the
+     * carrier associated with the subscription id.
+     */
+    private boolean doesImsCapabilityRequireProvisioning(Context context, int subId,
+            int capability) {
+        CarrierConfigManager configManager = new CarrierConfigManager(context);
+        PersistableBundle c = configManager.getConfigForSubId(subId);
+        boolean requireUtProvisioning = c.getBoolean(
+                // By default, this config is true (even if there is no SIM). We also check to make
+                // sure the subscription needs provisioning here, so we do not need to check for
+                // the no-SIM case, where we would normally shortcut this to false.
+                CarrierConfigManager.KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL, true)
+                && c.getBoolean(CarrierConfigManager.KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL,
+                false);
+        boolean requireVoiceVtProvisioning = c.getBoolean(
+                CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
+
+        // First check to make sure that the capability requires provisioning.
+        switch (capability) {
+            case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE:
+                // intentional fallthrough
+            case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO: {
+                if (requireVoiceVtProvisioning) {
+                    // Voice and Video requires provisioning
+                    return true;
+                }
+                break;
+            }
+            case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT: {
+                if (requireUtProvisioning) {
+                    // UT requires provisioning
+                    return true;
+                }
+                break;
+            }
+        }
+        return false;
+    }
+
+    @Override
     public int getImsProvisioningInt(int subId, int key) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid Subscription id '" + subId + "'");
+        }
         enforceReadPrivilegedPermission("getImsProvisioningInt");
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            return ImsManager.getInstance(mApp,
-                    getSlotIndexOrException(subId)).getConfigInterface().getConfigInt(key);
+            int slotId = getSlotIndex(subId);
+            if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+                Log.w(LOG_TAG, "getImsProvisioningInt: called with an inactive subscription '"
+                        + subId + "' for key:" + key);
+                return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
+            }
+            return ImsManager.getInstance(mApp, slotId).getConfigInterface().getConfigInt(key);
         } catch (ImsException e) {
-            throw new IllegalArgumentException(e.getMessage());
+            Log.w(LOG_TAG, "getImsProvisioningInt: ImsService is not available for subscription '"
+                    + subId + "' for key:" + key);
+            return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -3079,14 +3325,24 @@
 
     @Override
     public String getImsProvisioningString(int subId, int key) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid Subscription id '" + subId + "'");
+        }
         enforceReadPrivilegedPermission("getImsProvisioningString");
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            return ImsManager.getInstance(mApp,
-                    getSlotIndexOrException(subId)).getConfigInterface().getConfigString(key);
+            int slotId = getSlotIndex(subId);
+            if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+                Log.w(LOG_TAG, "getImsProvisioningString: called for an inactive subscription id '"
+                        + subId + "' for key:" + key);
+                return ProvisioningManager.STRING_QUERY_RESULT_ERROR_GENERIC;
+            }
+            return ImsManager.getInstance(mApp, slotId).getConfigInterface().getConfigString(key);
         } catch (ImsException e) {
-            throw new IllegalArgumentException(e.getMessage());
+            Log.w(LOG_TAG, "getImsProvisioningString: ImsService is not available for sub '"
+                    + subId + "' for key:" + key);
+            return ProvisioningManager.STRING_QUERY_RESULT_ERROR_NOT_READY;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -3094,15 +3350,25 @@
 
     @Override
     public int setImsProvisioningInt(int subId, int key, int value) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid Subscription id '" + subId + "'");
+        }
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setImsProvisioningInt");
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            return ImsManager.getInstance(mApp,
-                    getSlotIndexOrException(subId)).getConfigInterface().setConfig(key, value);
+            int slotId = getSlotIndex(subId);
+            if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+                Log.w(LOG_TAG, "setImsProvisioningInt: called with an inactive subscription id '"
+                        + subId + "' for key:" + key);
+                return ImsConfigImplBase.CONFIG_RESULT_FAILED;
+            }
+            return ImsManager.getInstance(mApp, slotId).getConfigInterface().setConfig(key, value);
         } catch (ImsException e) {
-            throw new IllegalArgumentException(e.getMessage());
+            Log.w(LOG_TAG, "setImsProvisioningInt: ImsService unavailable for sub '" + subId
+                    + "' for key:" + key);
+            return ImsConfigImplBase.CONFIG_RESULT_FAILED;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -3110,15 +3376,25 @@
 
     @Override
     public int setImsProvisioningString(int subId, int key, String value) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            throw new IllegalArgumentException("Invalid Subscription id '" + subId + "'");
+        }
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
                 "setImsProvisioningString");
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            return ImsManager.getInstance(mApp,
-                    getSlotIndexOrException(subId)).getConfigInterface().setConfig(key, value);
+            int slotId = getSlotIndex(subId);
+            if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+                Log.w(LOG_TAG, "setImsProvisioningString: called with an inactive subscription id '"
+                        + subId + "' for key:" + key);
+                return ImsConfigImplBase.CONFIG_RESULT_FAILED;
+            }
+            return ImsManager.getInstance(mApp, slotId).getConfigInterface().setConfig(key, value);
         } catch (ImsException e) {
-            throw new IllegalArgumentException(e.getMessage());
+            Log.w(LOG_TAG, "setImsProvisioningString: ImsService unavailable for sub '" + subId
+                    + "' for key:" + key);
+            return ImsConfigImplBase.CONFIG_RESULT_FAILED;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -3132,6 +3408,14 @@
         return slotId;
     }
 
+    private int getSlotIndex(int subId) {
+        int slotId = SubscriptionManager.getSlotIndex(subId);
+        if (!SubscriptionManager.isValidSlotIndex(slotId)) {
+            return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+        }
+        return slotId;
+    }
+
     /**
      * Returns the network type for a subId
      */
@@ -4836,51 +5120,29 @@
     }
 
     @Override
-    public String getLocaleFromDefaultSim() {
+    public String getSimLocaleForSubscriber(int subId) {
+        enforceReadPrivilegedPermission("getSimLocaleForSubscriber, subId: " + subId);
+        final Phone phone = getPhone(subId);
+        if (phone == null) {
+            log("getSimLocaleForSubscriber, invalid subId");
+            return null;
+        }
         final long identity = Binder.clearCallingIdentity();
         try {
-            // We query all subscriptions instead of just the active ones, because
-            // this might be called early on in the provisioning flow when the
-            // subscriptions potentially aren't active yet.
-            final List<SubscriptionInfo> slist = getAllSubscriptionInfoList();
-            if (slist == null || slist.isEmpty()) {
-                return null;
-            }
-
-            // This function may be called very early, say, from the setup wizard, at
-            // which point we won't have a default subscription set. If that's the case
-            // we just choose the first, which will be valid in "most cases".
-            final int defaultSubId = getDefaultSubscription();
-            SubscriptionInfo info = null;
-            if (defaultSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-                info = slist.get(0);
-            } else {
-                for (SubscriptionInfo item : slist) {
-                    if (item.getSubscriptionId() == defaultSubId) {
-                        info = item;
-                        break;
-                    }
-                }
-
-                if (info == null) {
-                    return null;
-                }
-            }
-
+            final SubscriptionInfo info = mSubscriptionController.getActiveSubscriptionInfo(subId,
+                    phone.getContext().getOpPackageName());
             // Try and fetch the locale from the carrier properties or from the SIM language
             // preferences (EF-PL and EF-LI)...
             final int mcc = info.getMcc();
-            final Phone defaultPhone = getPhone(info.getSubscriptionId());
             String simLanguage = null;
-            if (defaultPhone != null) {
-                final Locale localeFromDefaultSim = defaultPhone.getLocaleFromSimAndCarrierPrefs();
-                if (localeFromDefaultSim != null) {
-                    if (!localeFromDefaultSim.getCountry().isEmpty()) {
-                        if (DBG) log("Using locale from default SIM:" + localeFromDefaultSim);
-                        return localeFromDefaultSim.toLanguageTag();
-                    } else {
-                        simLanguage = localeFromDefaultSim.getLanguage();
-                    }
+            final Locale localeFromDefaultSim = phone.getLocaleFromSimAndCarrierPrefs();
+            if (localeFromDefaultSim != null) {
+                if (!localeFromDefaultSim.getCountry().isEmpty()) {
+                    if (DBG) log("Using locale from subId: " + subId + " locale: "
+                            + localeFromDefaultSim);
+                    return localeFromDefaultSim.toLanguageTag();
+                } else {
+                    simLanguage = localeFromDefaultSim.getLanguage();
                 }
             }
 
@@ -4890,7 +5152,7 @@
             // determined from the SIM MCC to provide an exact locale.
             final Locale mccLocale = MccTable.getLocaleFromMcc(mApp, mcc, simLanguage);
             if (mccLocale != null) {
-                if (DBG) log("No locale from default SIM, using mcc locale:" + mccLocale);
+                if (DBG) log("No locale from SIM, using mcc locale:" + mccLocale);
                 return mccLocale.toLanguageTag();
             }
 
@@ -5696,13 +5958,37 @@
     }
 
     @Override
-    public UiccCardInfo[] getUiccCardsInfo() {
-        enforceReadPrivilegedPermission("getUiccCardsInfo");
+    public List<UiccCardInfo> getUiccCardsInfo(String callingPackage) {
+        if (checkCarrierPrivilegesForPackageAnyPhone(callingPackage)
+                != TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+            throw new SecurityException("Caller does not have carrier privileges on any UICC.");
+        }
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            ArrayList<UiccCardInfo> cards = UiccController.getInstance().getAllUiccCardInfos();
-            return cards.toArray(new UiccCardInfo[cards.size()]);
+            UiccController uiccController = UiccController.getInstance();
+            ArrayList<UiccCardInfo> cardInfos = uiccController.getAllUiccCardInfos();
+
+            ApplicationInfo ai = mApp.getPackageManager().getApplicationInfo(callingPackage, 0);
+            if ((ai.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+                // Remove private info if the caller doesn't have access
+                ArrayList<UiccCardInfo> filteredInfos = new ArrayList<>();
+                for (UiccCardInfo cardInfo : cardInfos) {
+                    UiccCard card = uiccController.getUiccCard(cardInfo.getSlotIndex());
+                    UiccProfile profile = card.getUiccProfile();
+                    if (profile.getCarrierPrivilegeStatus(mApp.getPackageManager(), callingPackage)
+                            != TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+                        filteredInfos.add(cardInfo.getUnprivileged());
+                    } else {
+                        filteredInfos.add(cardInfo);
+                    }
+                }
+                return filteredInfos;
+            }
+            return cardInfos;
+        } catch (PackageManager.NameNotFoundException e) {
+            // This should not happen since we pass the package info in from TelephonyManager
+            throw new SecurityException("Invalid calling package.");
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -5782,11 +6068,6 @@
 
     @Override
     public int getCardIdForDefaultEuicc(int subId, String callingPackage) {
-        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, subId, callingPackage, "getCardIdForDefaultEuicc")) {
-            return TelephonyManager.INVALID_CARD_ID;
-        }
-
         final long identity = Binder.clearCallingIdentity();
         try {
             return UiccController.getInstance().getCardIdForDefaultEuicc();
@@ -6085,6 +6366,52 @@
         }
     }
 
+    /**
+     * Update emergency number list for test mode.
+     */
+    @Override
+    public void updateEmergencyNumberListTestMode(int action, EmergencyNumber num) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+                "updateEmergencyNumberListTestMode");
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            for (Phone phone: PhoneFactory.getPhones()) {
+                EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker();
+                if (tracker != null) {
+                    tracker.executeEmergencyNumberTestModeCommand(action, num);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Get the full emergency number list for test mode.
+     */
+    @Override
+    public List<String> getEmergencyNumberListTestMode() {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+                "getEmergencyNumberListTestMode");
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            Set<String> emergencyNumbers = new HashSet<>();
+            for (Phone phone: PhoneFactory.getPhones()) {
+                EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker();
+                if (tracker != null) {
+                    for (EmergencyNumber num : tracker.getEmergencyNumberList()) {
+                        emergencyNumbers.add(num.getNumber());
+                    }
+                }
+            }
+            return new ArrayList<>(emergencyNumbers);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     @Override
     public List<String> getCertsFromCarrierPrivilegeAccessRules(int subId) {
         enforceReadPrivilegedPermission("getCertsFromCarrierPrivilegeAccessRules");
@@ -6124,4 +6451,83 @@
             Binder.restoreCallingIdentity(identity);
         }
     }
+
+    @Override
+    public void setMultisimCarrierRestriction(boolean isMultisimCarrierRestricted) {
+        enforceModifyPermission();
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mTelephonySharedPreferences.edit()
+                    .putBoolean(PREF_MULTI_SIM_RESTRICTED, isMultisimCarrierRestricted)
+                    .commit();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public boolean isMultisimCarrierRestricted() {
+        enforceReadPrivilegedPermission("isMultisimCarrierRestricted");
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // If the device has less than 2 SIM cards, indicate that multisim is restricted.
+            int numPhysicalSlots = UiccController.getInstance().getUiccSlots().length;
+            if (numPhysicalSlots < 2) {
+                loge("isMultisimCarrierRestricted: requires at least 2 cards");
+                return true;
+            }
+
+            // Default value is false. Multi SIM is allowed unless explicitly restricted.
+            return mTelephonySharedPreferences.getBoolean(PREF_MULTI_SIM_RESTRICTED, false);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Switch configs to enable multi-sim or switch back to single-sim
+     * @param numOfSims number of active sims we want to switch to
+     */
+    @Override
+    public void switchMultiSimConfig(int numOfSims) {
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+                mApp, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, "switchMultiSimConfig");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mPhoneConfigurationManager.switchMultiSimConfig(numOfSims);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Get how many sims have been activated on the phone
+     */
+    @Override
+    public int getNumOfActiveSims() {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mPhoneConfigurationManager.getNumOfActiveSims();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Get whether reboot is required or not after making changes to modem configurations.
+     * Return value defaults to false
+     */
+    @Override
+    public boolean isRebootRequiredForModemConfigChange() {
+        enforceReadPrivilegedPermission("isRebootRequiredForModemConfigChange");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mPhoneConfigurationManager.isRebootRequiredForModemConfigChange();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
 }
diff --git a/src/com/android/phone/ShortcutViewUtils.java b/src/com/android/phone/ShortcutViewUtils.java
new file mode 100644
index 0000000..28ee24f
--- /dev/null
+++ b/src/com/android/phone/ShortcutViewUtils.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2019 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.phone;
+
+import android.content.Context;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+class ShortcutViewUtils {
+    private static final String LOG_TAG = "ShortcutViewUtils";
+
+    // Emergency services which will be promoted on the shortcut view.
+    static final int[] PROMOTED_CATEGORIES = {
+            EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE,
+            EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE,
+            EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE,
+    };
+
+    static final int PROMOTED_CATEGORIES_BITMASK;
+
+    static {
+        int bitmask = 0;
+        for (int category : PROMOTED_CATEGORIES) {
+            bitmask |= category;
+        }
+        PROMOTED_CATEGORIES_BITMASK = bitmask;
+    }
+
+    // Info and emergency call capability of every phone.
+    static class PhoneInfo {
+        private final PhoneAccountHandle mHandle;
+        private final boolean mCanPlaceEmergencyCall;
+        private final int mSubId;
+        private final String mCountryIso;
+        private final List<EmergencyNumber> mPromotedEmergencyNumbers;
+
+        private PhoneInfo(int subId, String countryIso,
+                List<EmergencyNumber> promotedEmergencyNumbers) {
+            this(null, true, subId, countryIso, promotedEmergencyNumbers);
+        }
+
+        private PhoneInfo(PhoneAccountHandle handle, boolean canPlaceEmergencyCall, int subId,
+                String countryIso, List<EmergencyNumber> promotedEmergencyNumbers) {
+            mHandle = handle;
+            mCanPlaceEmergencyCall = canPlaceEmergencyCall;
+            mSubId = subId;
+            mCountryIso = countryIso;
+            mPromotedEmergencyNumbers = promotedEmergencyNumbers;
+        }
+
+        public PhoneAccountHandle getPhoneAccountHandle() {
+            return mHandle;
+        }
+
+        public boolean canPlaceEmergencyCall() {
+            return mCanPlaceEmergencyCall;
+        }
+
+        public int getSubId() {
+            return mSubId;
+        }
+
+        public String getCountryIso() {
+            return mCountryIso;
+        }
+
+        public List<EmergencyNumber> getPromotedEmergencyNumbers() {
+            return mPromotedEmergencyNumbers;
+        }
+
+        public boolean isSufficientForEmergencyCall() {
+            // Checking mCountryIso because the emergency number list is not reliable to be
+            // suggested to users if the device didn't camp to any network. In this case, users
+            // can still try to dial emergency numbers with dial pad.
+            return mCanPlaceEmergencyCall && mPromotedEmergencyNumbers != null
+                    && !TextUtils.isEmpty(mCountryIso);
+        }
+
+        public boolean hasPromotedEmergencyNumber(String number) {
+            for (EmergencyNumber emergencyNumber : mPromotedEmergencyNumbers) {
+                if (emergencyNumber.getNumber().equalsIgnoreCase(number)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("{");
+            if (mHandle != null) {
+                sb.append("handle=").append(mHandle.getId()).append(", ");
+            }
+            sb.append("subId=").append(mSubId)
+                    .append(", canPlaceEmergencyCall=").append(mCanPlaceEmergencyCall)
+                    .append(", networkCountryIso=").append(mCountryIso);
+            if (mPromotedEmergencyNumbers != null) {
+                sb.append(", emergencyNumbers=");
+                for (EmergencyNumber emergencyNumber : mPromotedEmergencyNumbers) {
+                    sb.append(emergencyNumber.getNumber()).append(":")
+                            .append(emergencyNumber).append(",");
+                }
+            }
+            sb.append("}");
+            return sb.toString();
+        }
+    }
+
+    /**
+     * Picks a preferred phone (SIM slot) which is sufficient for emergency call and can provide
+     * promoted emergency numbers.
+     *
+     * A promoted emergency number should be dialed out over the preferred phone. Other emergency
+     * numbers should be still dialable over the system default phone.
+     *
+     * @return A preferred phone and its promoted emergency number, or null if no phone/promoted
+     * emergency numbers available.
+     */
+    @Nullable
+    static PhoneInfo pickPreferredPhone(@NonNull Context context) {
+        TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
+        if (telephonyManager.getPhoneCount() <= 0) {
+            Log.w(LOG_TAG, "No phone available!");
+            return null;
+        }
+
+        Map<Integer, List<EmergencyNumber>> promotedLists =
+                getPromotedEmergencyNumberLists(telephonyManager);
+        if (promotedLists == null || promotedLists.isEmpty()) {
+            return null;
+        }
+
+        // For a multi-phone device, tries the default phone account.
+        TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
+        PhoneAccountHandle defaultHandle = telecomManager.getDefaultOutgoingPhoneAccount(
+                PhoneAccount.SCHEME_TEL);
+        if (defaultHandle != null) {
+            PhoneInfo phone = loadPhoneInfo(defaultHandle, telephonyManager, telecomManager,
+                    promotedLists);
+            if (phone.isSufficientForEmergencyCall()) {
+                return phone;
+            }
+            Log.w(LOG_TAG, "Default PhoneAccount is insufficient for emergency call: "
+                    + phone.toString());
+        } else {
+            Log.w(LOG_TAG, "Missing default PhoneAccount! Is this really a phone device?");
+        }
+
+        // Looks for any one phone which supports emergency call.
+        List<PhoneAccountHandle> allHandles = telecomManager.getCallCapablePhoneAccounts();
+        if (allHandles != null && !allHandles.isEmpty()) {
+            for (PhoneAccountHandle handle : allHandles) {
+                PhoneInfo phone = loadPhoneInfo(handle, telephonyManager, telecomManager,
+                        promotedLists);
+                if (phone.isSufficientForEmergencyCall()) {
+                    return phone;
+                } else {
+                    if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
+                        Log.d(LOG_TAG, "PhoneAccount " + phone.toString()
+                                + " is insufficient for emergency call.");
+                    }
+                }
+            }
+        }
+
+        Log.w(LOG_TAG, "No PhoneAccount available for emergency call!");
+        return null;
+    }
+
+    private static PhoneInfo loadPhoneInfo(@NonNull PhoneAccountHandle handle,
+            @NonNull TelephonyManager telephonyManager, @NonNull TelecomManager telecomManager,
+            Map<Integer, List<EmergencyNumber>> promotedLists) {
+        boolean canPlaceEmergencyCall = false;
+        int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        String countryIso = null;
+        List<EmergencyNumber> emergencyNumberList = null;
+
+        PhoneAccount phoneAccount = telecomManager.getPhoneAccount(handle);
+        if (phoneAccount != null) {
+            canPlaceEmergencyCall = phoneAccount.hasCapabilities(
+                    PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS);
+            subId = telephonyManager.getSubIdForPhoneAccount(phoneAccount);
+        }
+
+        TelephonyManager subTelephonyManager = telephonyManager.createForSubscriptionId(subId);
+        if (subTelephonyManager != null) {
+            countryIso = subTelephonyManager.getNetworkCountryIso();
+        }
+
+        if (promotedLists != null) {
+            emergencyNumberList = promotedLists.get(subId);
+        }
+
+        return new PhoneInfo(handle, canPlaceEmergencyCall, subId, countryIso, emergencyNumberList);
+    }
+
+    @NonNull
+    private static Map<Integer, List<EmergencyNumber>> getPromotedEmergencyNumberLists(
+            @NonNull TelephonyManager telephonyManager) {
+        Map<Integer, List<EmergencyNumber>> allLists =
+                telephonyManager.getCurrentEmergencyNumberList();
+        if (allLists == null || allLists.isEmpty()) {
+            Log.w(LOG_TAG, "Unable to retrieve emergency number lists!");
+            return new ArrayMap<>();
+        }
+
+        boolean isDebugLoggable = Log.isLoggable(LOG_TAG, Log.DEBUG);
+        Map<Integer, List<EmergencyNumber>> promotedEmergencyNumberLists = new ArrayMap<>();
+        for (Map.Entry<Integer, List<EmergencyNumber>> entry : allLists.entrySet()) {
+            if (entry.getKey() == null || entry.getValue() == null) {
+                continue;
+            }
+            List<EmergencyNumber> emergencyNumberList = entry.getValue();
+            if (isDebugLoggable) {
+                Log.d(LOG_TAG, "Emergency numbers of " + entry.getKey());
+            }
+
+            // The list of promoted emergency numbers which will be visible on shortcut view.
+            List<EmergencyNumber> promotedList = new ArrayList<>();
+            // A temporary list for non-prioritized emergency numbers.
+            List<EmergencyNumber> tempList = new ArrayList<>();
+
+            for (EmergencyNumber emergencyNumber : emergencyNumberList) {
+                boolean isPromotedCategory = (emergencyNumber.getEmergencyServiceCategoryBitmask()
+                        & PROMOTED_CATEGORIES_BITMASK) != 0;
+
+                // Emergency numbers in DATABASE are prioritized for shortcut view since they were
+                // well-categorized.
+                boolean isFromPrioritizedSource =
+                        (emergencyNumber.getEmergencyNumberSourceBitmask()
+                                & EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE) != 0;
+                if (isDebugLoggable) {
+                    Log.d(LOG_TAG, "  " + emergencyNumber
+                            + (isPromotedCategory ? "M" : "")
+                            + (isFromPrioritizedSource ? "P" : ""));
+                }
+
+                if (isPromotedCategory) {
+                    if (isFromPrioritizedSource) {
+                        promotedList.add(emergencyNumber);
+                    } else {
+                        tempList.add(emergencyNumber);
+                    }
+                }
+            }
+            // Puts numbers in temp list after prioritized numbers.
+            promotedList.addAll(tempList);
+
+            if (!promotedList.isEmpty()) {
+                promotedEmergencyNumberLists.put(entry.getKey(), promotedList);
+            }
+        }
+
+        if (promotedEmergencyNumberLists.isEmpty()) {
+            Log.w(LOG_TAG, "No promoted emergency number found!");
+        }
+        return promotedEmergencyNumberLists;
+    }
+}
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index 9250118..956e0e2 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -22,11 +22,14 @@
 import android.os.ShellCommand;
 import android.os.UserHandle;
 import android.telephony.SubscriptionManager;
+import android.telephony.emergency.EmergencyNumber;
 import android.util.Log;
 
 import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
 
 /**
  * Takes actions based on the adb commands given by "adb shell cmd phone ...". Be careful, no
@@ -44,6 +47,7 @@
     private static final String IMS_SUBCOMMAND = "ims";
     private static final String SMS_SUBCOMMAND = "sms";
     private static final String NUMBER_VERIFICATION_SUBCOMMAND = "numverify";
+    private static final String EMERGENCY_NUMBER_TEST_MODE = "emergency-number-test-mode";
 
     private static final String IMS_SET_CARRIER_SERVICE = "set-ims-service";
     private static final String IMS_GET_CARRIER_SERVICE = "get-ims-service";
@@ -79,6 +83,8 @@
             }
             case NUMBER_VERIFICATION_SUBCOMMAND:
                 return handleNumberVerificationCommand();
+            case EMERGENCY_NUMBER_TEST_MODE:
+                return handleEmergencyNumberTestModeCommand();
             default: {
                 return handleDefaultCommands(cmd);
             }
@@ -95,8 +101,11 @@
         pw.println("    IMS Commands.");
         pw.println("  sms");
         pw.println("    SMS Commands.");
+        pw.println("  emergency-number-test-mode");
+        pw.println("    Emergency Number Test Mode Commands.");
         onHelpIms();
         onHelpSms();
+        onHelpEmergencyNumber();
     }
 
     private void onHelpIms() {
@@ -147,6 +156,20 @@
         pw.println("    1 if the call would have been intercepted, 0 otherwise.");
     }
 
+    private void onHelpEmergencyNumber() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("Emergency Number Test Mode Commands:");
+        pw.println("  emergency-number-test-mode ");
+        pw.println("    Add(-a), Clear(-c), Print (-p) or Remove(-r) the emergency number list in"
+                + " the test mode");
+        pw.println("      -a <emergency number address>: add an emergency number address for the"
+                + " test mode, only allows '0'-'9', '*', or '#'.");
+        pw.println("      -c: clear the emergency number list in the test mode.");
+        pw.println("      -r <emergency number address>: remove an existing emergency number"
+                + " address added by the test mode, only allows '0'-'9', '*', or '#'.");
+        pw.println("      -p: get the full emergency number list in the test mode.");
+    }
+
     private int handleImsCommand() {
         String arg = getNextArg();
         if (arg == null) {
@@ -172,6 +195,91 @@
         return -1;
     }
 
+    private int handleEmergencyNumberTestModeCommand() {
+        PrintWriter errPw = getErrPrintWriter();
+        String opt = getNextOption();
+        if (opt == null) {
+            onHelpEmergencyNumber();
+            return 0;
+        }
+
+        switch (opt) {
+            case "-a": {
+                String emergencyNumberCmd = getNextArgRequired();
+                if (emergencyNumberCmd == null
+                        || !EmergencyNumber.validateEmergencyNumberAddress(emergencyNumberCmd)) {
+                    errPw.println("An emergency number (only allow '0'-'9', '*', or '#') needs"
+                            + " to be specified after -a in the command ");
+                    return -1;
+                }
+                try {
+                    mInterface.updateEmergencyNumberListTestMode(
+                            EmergencyNumberTracker.ADD_EMERGENCY_NUMBER_TEST_MODE,
+                            new EmergencyNumber(emergencyNumberCmd, "", "",
+                                    EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+                                    new ArrayList<String>(),
+                                    EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST,
+                                    EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN));
+                } catch (RemoteException ex) {
+                    Log.w(LOG_TAG, "emergency-number-test-mode -a " + emergencyNumberCmd
+                            + ", error " + ex.getMessage());
+                    errPw.println("Exception: " + ex.getMessage());
+                    return -1;
+                }
+                break;
+            }
+            case "-c": {
+                try {
+                    mInterface.updateEmergencyNumberListTestMode(
+                            EmergencyNumberTracker.RESET_EMERGENCY_NUMBER_TEST_MODE, null);
+                } catch (RemoteException ex) {
+                    Log.w(LOG_TAG, "emergency-number-test-mode -c " + "error " + ex.getMessage());
+                    errPw.println("Exception: " + ex.getMessage());
+                    return -1;
+                }
+                break;
+            }
+            case "-r": {
+                String emergencyNumberCmd = getNextArgRequired();
+                if (emergencyNumberCmd == null
+                        || !EmergencyNumber.validateEmergencyNumberAddress(emergencyNumberCmd)) {
+                    errPw.println("An emergency number (only allow '0'-'9', '*', or '#') needs"
+                            + " to be specified after -r in the command ");
+                    return -1;
+                }
+                try {
+                    mInterface.updateEmergencyNumberListTestMode(
+                            EmergencyNumberTracker.REMOVE_EMERGENCY_NUMBER_TEST_MODE,
+                            new EmergencyNumber(emergencyNumberCmd, "", "",
+                                    EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+                                    new ArrayList<String>(),
+                                    EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST,
+                                    EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN));
+                } catch (RemoteException ex) {
+                    Log.w(LOG_TAG, "emergency-number-test-mode -r " + emergencyNumberCmd
+                            + ", error " + ex.getMessage());
+                    errPw.println("Exception: " + ex.getMessage());
+                    return -1;
+                }
+                break;
+            }
+            case "-p": {
+                try {
+                    getOutPrintWriter().println(mInterface.getEmergencyNumberListTestMode());
+                } catch (RemoteException ex) {
+                    Log.w(LOG_TAG, "emergency-number-test-mode -p " + "error " + ex.getMessage());
+                    errPw.println("Exception: " + ex.getMessage());
+                    return -1;
+                }
+                break;
+            }
+            default:
+                onHelpEmergencyNumber();
+                break;
+        }
+        return 0;
+    }
+
     private int handleNumberVerificationCommand() {
         String arg = getNextArg();
         if (arg == null) {
diff --git a/src/com/android/phone/ecc/EccInfoHelper.java b/src/com/android/phone/ecc/EccInfoHelper.java
deleted file mode 100644
index c471c4b..0000000
--- a/src/com/android/phone/ecc/EccInfoHelper.java
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (C) 2018 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.phone.ecc;
-
-import android.content.Context;
-import android.os.AsyncTask;
-import android.provider.Settings;
-import android.telephony.CellIdentityGsm;
-import android.telephony.CellIdentityLte;
-import android.telephony.CellIdentityWcdma;
-import android.telephony.CellInfo;
-import android.telephony.CellInfoGsm;
-import android.telephony.CellInfoLte;
-import android.telephony.CellInfoWcdma;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Pair;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.telephony.MccTable;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Helper for retrieve ECC info for current country.
- */
-public class EccInfoHelper {
-    // Debug constants.
-    private static final boolean DBG = false;
-    private static final String LOG_TAG = "EccInfoHelper";
-
-    /**
-     * Check if current CountryEccInfo is available for current environment.
-     */
-    public static boolean isCountryEccInfoAvailable(Context context, String countryIso) {
-        CountryEccInfo countryEccInfo;
-        try {
-            countryEccInfo = IsoToEccProtobufRepository.getInstance()
-                    .getCountryEccInfo(context, countryIso);
-        } catch (IOException e) {
-            Log.e(LOG_TAG, "Failed to retrieve ECC: ", e);
-            return false;
-        }
-
-        if (countryEccInfo == null) {
-            return false;
-        }
-        for (EccInfo entry : countryEccInfo.getEccInfoList()) {
-            if (!PhoneNumberUtils.isEmergencyNumber(entry.getNumber())) {
-                // The CountryEccInfo is unavailable if any ecc number in the local table was
-                // declined.
-                return false;
-            }
-        }
-        return true;
-    }
-
-    // country ISO to ECC list data source
-    private IsoToEccRepository mEccRepo;
-
-    /**
-     * Callback for {@link #getCountryEccInfoAsync}.
-     */
-    public interface CountryEccInfoResultCallback {
-        /**
-         * Called if successfully get country ECC info.
-         *
-         * @param iso Detected current country ISO.
-         * @param countryEccInfo The EccInfo of current country.
-         */
-        void onSuccess(@NonNull String iso, @NonNull CountryEccInfo countryEccInfo);
-
-        /**
-         * Called if failed to get country ISO.
-         */
-        void onDetectCountryFailed();
-
-        /**
-         * Called if failed to get ECC info for given country ISO.
-         *
-         * @param iso Detected current country ISO.
-         */
-        void onRetrieveCountryEccInfoFailed(@NonNull String iso);
-    }
-
-    /**
-     * Constructor of EccInfoHelper
-     *
-     * @param eccRepository A repository for ECC info, indexed by country ISO.
-     */
-    public EccInfoHelper(@NonNull IsoToEccRepository eccRepository) {
-        mEccRepo = eccRepository;
-    }
-
-    /**
-     * Get ECC info for current location, base on detected country ISO.
-     * It's possible we cannot detect current country, ex. device is in airplane mode,
-     * or there's no available base station near by.
-     *
-     * @param context The context used to access resources.
-     * @param callback Callback for result.
-     */
-    public void getCountryEccInfoAsync(final @NonNull Context context,
-            final CountryEccInfoResultCallback callback) {
-        new AsyncTask<Void, Void, Pair<String, CountryEccInfo>>() {
-            @Override
-            protected Pair<String, CountryEccInfo> doInBackground(Void... voids) {
-                String iso = getCurrentCountryIso(context);
-                if (TextUtils.isEmpty(iso)) {
-                    return null;
-                }
-
-                CountryEccInfo dialableCountryEccInfo;
-                try {
-                    // access data source in background thread to avoid possible file IO caused ANR.
-                    CountryEccInfo rawEccInfo = mEccRepo.getCountryEccInfo(context, iso);
-                    dialableCountryEccInfo = getDialableCountryEccInfo(rawEccInfo);
-                } catch (IOException e) {
-                    Log.e(LOG_TAG, "Failed to retrieve ECC: " + e.getMessage());
-                    dialableCountryEccInfo = null;
-                }
-                return new Pair<>(iso, dialableCountryEccInfo);
-            }
-
-            @Override
-            protected void onPostExecute(Pair<String, CountryEccInfo> result) {
-                if (callback != null) {
-                    if (result == null) {
-                        callback.onDetectCountryFailed();
-                    } else {
-                        String iso = result.first;
-                        CountryEccInfo countryEccInfo = result.second;
-                        if (countryEccInfo == null) {
-                            callback.onRetrieveCountryEccInfoFailed(iso);
-                        } else {
-                            callback.onSuccess(iso, countryEccInfo);
-                        }
-                    }
-                }
-            }
-        }.execute();
-    }
-
-    @NonNull
-    private CountryEccInfo getDialableCountryEccInfo(CountryEccInfo countryEccInfo) {
-        ArrayList<EccInfo> dialableECCList = new ArrayList<>();
-        String dialableFallback = null;
-
-        // filter out non-dialable ECC
-        if (countryEccInfo != null) {
-            for (EccInfo entry : countryEccInfo.getEccInfoList()) {
-                if (PhoneNumberUtils.isEmergencyNumber(entry.getNumber())) {
-                    dialableECCList.add(entry);
-                }
-            }
-            String defaultFallback = countryEccInfo.getFallbackEcc();
-            if (PhoneNumberUtils.isEmergencyNumber(defaultFallback)) {
-                dialableFallback = defaultFallback;
-            }
-        }
-        return new CountryEccInfo(dialableFallback, dialableECCList);
-    }
-
-    @Nullable
-    private String getCurrentCountryIso(@NonNull Context context) {
-        // Do not detect country ISO if airplane mode is on
-        int airplaneMode = Settings.System.getInt(context.getContentResolver(),
-                Settings.Global.AIRPLANE_MODE_ON, 0);
-        if (airplaneMode != 0) {
-            Log.d(LOG_TAG, "Airplane mode is on, do not get country ISO.");
-            return null;
-        }
-
-        TelephonyManager tm = (TelephonyManager) context.getSystemService(
-                Context.TELEPHONY_SERVICE);
-        String iso = tm.getNetworkCountryIso();
-        if (DBG) Log.d(LOG_TAG, "Current country ISO is " + Rlog.pii(LOG_TAG, iso));
-
-        if (TextUtils.isEmpty(iso)) {
-            // XXX: according to ServiceStateTracker's implementation, retrieve cell info in a
-            // thread other than TelephonyManager's main thread.
-            String mcc = getCurrentMccFromCellInfo(context);
-            iso = MccTable.countryCodeForMcc(mcc);
-            if (DBG) {
-                Log.d(LOG_TAG, "Current mcc is " + Rlog.pii(LOG_TAG, mcc) + ", mapping to ISO: "
-                        + Rlog.pii(LOG_TAG, iso));
-            }
-        }
-        return iso;
-    }
-
-    // XXX: According to ServiceStateTracker implementation, to actually get current cell info,
-    // this method must be called in a separate thread from ServiceStateTracker, which is the
-    // main thread of Telephony service.
-    @Nullable
-    private String getCurrentMccFromCellInfo(@NonNull Context context) {
-        // retrieve mcc info from base station even no SIM present.
-        TelephonyManager tm = (TelephonyManager) context.getSystemService(
-                Context.TELEPHONY_SERVICE);
-        List<CellInfo> cellInfos = tm.getAllCellInfo();
-        String mcc = null;
-        if (cellInfos != null) {
-            for (CellInfo ci : cellInfos) {
-                if (ci instanceof CellInfoGsm) {
-                    CellInfoGsm cellInfoGsm = (CellInfoGsm) ci;
-                    CellIdentityGsm cellIdentityGsm = cellInfoGsm.getCellIdentity();
-                    mcc = cellIdentityGsm.getMccString();
-                    break;
-                } else if (ci instanceof CellInfoWcdma) {
-                    CellInfoWcdma cellInfoWcdma = (CellInfoWcdma) ci;
-                    CellIdentityWcdma cellIdentityWcdma = cellInfoWcdma.getCellIdentity();
-                    mcc = cellIdentityWcdma.getMccString();
-                    break;
-                } else if (ci instanceof CellInfoLte) {
-                    CellInfoLte cellInfoLte = (CellInfoLte) ci;
-                    CellIdentityLte cellIdentityLte = cellInfoLte.getCellIdentity();
-                    mcc = cellIdentityLte.getMccString();
-                    break;
-                }
-            }
-            if (DBG) Log.d(LOG_TAG, "Retrieve MCC from cell info list: " + Rlog.pii(LOG_TAG, mcc));
-        } else {
-            Log.w(LOG_TAG, "Cannot get cell info list.");
-        }
-        return mcc;
-    }
-}
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 37c5d7c..9cc7eb3 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -316,6 +316,8 @@
                     && isImsVoiceAvailable()) {
                 capabilities |= PhoneAccount.CAPABILITY_RTT;
                 mIsRttCapable = true;
+            } else {
+                mIsRttCapable = false;
             }
 
             extras.putBoolean(PhoneAccount.EXTRA_SUPPORTS_VIDEO_CALLING_FALLBACK,
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index ab9e211..e0db44e 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -71,6 +71,8 @@
 import java.util.Queue;
 import java.util.regex.Pattern;
 
+import javax.annotation.Nullable;
+
 /**
  * Service for making GSM and CDMA connections.
  */
@@ -321,8 +323,8 @@
         if (PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {
             // TODO: We don't check for SecurityException here (requires
             // CALL_PRIVILEGED permission).
-            final Phone phone = getPhoneForAccount(request.getAccountHandle(), false,
-                    handle.getSchemeSpecificPart());
+            final Phone phone = getPhoneForAccount(request.getAccountHandle(),
+                    false /* isEmergencyCall */, null /* not an emergency call */);
             if (phone == null) {
                 Log.d(this, "onCreateOutgoingConnection, phone is null");
                 return Connection.createFailedConnection(
@@ -360,8 +362,8 @@
                                 "Unable to parse number"));
             }
 
-            final Phone phone = getPhoneForAccount(request.getAccountHandle(), false,
-                    handle.getSchemeSpecificPart());
+            final Phone phone = getPhoneForAccount(request.getAccountHandle(),
+                    false /* isEmergencyCall*/, null /* not an emergency call */);
             if (phone != null && CDMA_ACTIVATION_CODE_REGEX_PATTERN.matcher(number).matches()) {
                 // Obtain the configuration for the outgoing phone's SIM. If the outgoing number
                 // matches the *228 regex pattern, fail the call. This number is used for OTASP, and
@@ -468,7 +470,8 @@
 
             // Get the right phone object from the account data passed in.
             final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber,
-                    handle.getSchemeSpecificPart());
+                    /* Note: when not an emergency, handle can be null for unknown callers */
+                    handle == null ? null : handle.getSchemeSpecificPart());
             Connection resultConnection = getTelephonyConnection(request, numberToDial,
                     isEmergencyNumber, handle, phone);
             // If there was a failure, the resulting connection will not be a TelephonyConnection,
@@ -512,8 +515,9 @@
         if (isRadioReady) {
             // Get the right phone object since the radio has been turned on
             // successfully.
-            final Phone phone = getPhoneForAccount(request.getAccountHandle(),
-                    isEmergencyNumber, handle.getSchemeSpecificPart());
+            final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber,
+                    /* Note: when not an emergency, handle can be null for unknown callers */
+                    handle == null ? null : handle.getSchemeSpecificPart());
             // If the PhoneType of the Phone being used is different than the Default Phone, then we
             // need create a new Connection using that PhoneType and replace it in Telecom.
             if (phone.getPhoneType() != originalPhoneType) {
@@ -732,7 +736,8 @@
             isEmergency = true;
         }
         Phone phone = getPhoneForAccount(accountHandle, isEmergency,
-                request.getAddress().getSchemeSpecificPart());
+                /* Note: when not an emergency, handle can be null for unknown callers */
+                request.getAddress() == null ? null : request.getAddress().getSchemeSpecificPart());
         if (phone == null) {
             return Connection.createFailedConnection(
                     DisconnectCauseUtil.toTelecomDisconnectCause(
@@ -844,7 +849,8 @@
             isEmergency = true;
         }
         Phone phone = getPhoneForAccount(accountHandle, isEmergency,
-                request.getAddress().getSchemeSpecificPart());
+                /* Note: when not an emergency, handle can be null for unknown callers */
+                request.getAddress() == null ? null : request.getAddress().getSchemeSpecificPart());
         if (phone == null) {
             return Connection.createFailedConnection(
                     DisconnectCauseUtil.toTelecomDisconnectCause(
@@ -1234,8 +1240,17 @@
         return false;
     }
 
+    /**
+     * Determines which {@link Phone} will be used to place the call.
+     * @param accountHandle The {@link PhoneAccountHandle} which was sent from Telecom to place the
+     *      call on.
+     * @param isEmergency {@code true} if this is an emergency call, {@code false} otherwise.
+     * @param emergencyNumberAddress When {@code isEmergency} is {@code true}, will be the phone
+     *      of the emergency call.  Otherwise, this can be {@code null}  .
+     * @return
+     */
     private Phone getPhoneForAccount(PhoneAccountHandle accountHandle, boolean isEmergency,
-                                     String emergencyNumberAddress) {
+                                     @Nullable String emergencyNumberAddress) {
         Phone chosenPhone = null;
         int subId = PhoneUtils.getSubIdForPhoneAccountHandle(accountHandle);
         if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
diff --git a/testapps/TelephonyRegistryTestApp/AndroidManifest.xml b/testapps/TelephonyRegistryTestApp/AndroidManifest.xml
index 708ea66..1cfd3ba 100644
--- a/testapps/TelephonyRegistryTestApp/AndroidManifest.xml
+++ b/testapps/TelephonyRegistryTestApp/AndroidManifest.xml
@@ -19,6 +19,7 @@
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
     <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
     <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+    <uses-permission android:name="android.permission.READ_PRECISE_PHONE_STATE"/>
     <application android:label="TelephonyRegistryTestApp">
         <activity
             android:name=".TelephonyRegistryTestApp"
diff --git a/tests/src/com/android/phone/ecc/EccDataTest.java b/tests/src/com/android/phone/ecc/EccDataTest.java
new file mode 100644
index 0000000..8f4abc5
--- /dev/null
+++ b/tests/src/com/android/phone/ecc/EccDataTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 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.phone.ecc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+import com.android.phone.ecc.nano.ProtobufEccData;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashSet;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * Unit tests for eccdata.
+ */
+@RunWith(AndroidJUnit4.class)
+public class EccDataTest extends TelephonyTestBase {
+    @Test
+    public void testEccDataContent() throws IOException {
+        InputStream eccData = new GZIPInputStream(new BufferedInputStream(
+                InstrumentationRegistry.getTargetContext().getAssets().open("eccdata")));
+        ProtobufEccData.AllInfo allEccMessages = ProtobufEccData.AllInfo.parseFrom(
+                readInputStreamToByteArray(eccData));
+        eccData.close();
+
+        HashSet loadedIsos = new HashSet(300);
+        HashSet loadedNumbers = new HashSet(5);
+
+        for (ProtobufEccData.CountryInfo countryInfo : allEccMessages.countries) {
+            assertThat(countryInfo.isoCode).isNotEmpty();
+            assertThat(countryInfo.isoCode).isEqualTo(countryInfo.isoCode.toUpperCase().trim());
+            assertThat(loadedIsos.contains(countryInfo.isoCode)).isFalse();
+            loadedIsos.add(countryInfo.isoCode);
+
+            loadedNumbers.clear();
+            for (ProtobufEccData.EccInfo eccInfo : countryInfo.eccs) {
+                assertThat(eccInfo.phoneNumber).isNotEmpty();
+                assertThat(eccInfo.phoneNumber).isEqualTo(eccInfo.phoneNumber.trim());
+                assertThat(loadedNumbers.contains(eccInfo.phoneNumber)).isFalse();
+                assertThat(eccInfo.types).isNotEmpty();
+                loadedNumbers.add(eccInfo.phoneNumber);
+            }
+        }
+    }
+
+    /**
+     * Util function to convert inputStream to byte array before parsing proto data.
+     */
+    private static byte[] readInputStreamToByteArray(InputStream inputStream) throws IOException {
+        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+        int nRead;
+        byte[] data = new byte[16 * 1024]; // Read 16k chunks
+        while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
+            buffer.write(data, 0, nRead);
+        }
+        buffer.flush();
+        return buffer.toByteArray();
+    }
+}
diff --git a/tests/src/com/android/phone/ecc/IsoToEccProtobufRepositoryTest.java b/tests/src/com/android/phone/ecc/IsoToEccProtobufRepositoryTest.java
deleted file mode 100644
index f6e5ba2..0000000
--- a/tests/src/com/android/phone/ecc/IsoToEccProtobufRepositoryTest.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2018 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.phone.ecc;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-import android.util.Log;
-
-import com.android.TelephonyTestBase;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.HashSet;
-import java.util.Map;
-
-/**
- * Unit tests for IsoToEccProtobufRepository.
- */
-
-@RunWith(AndroidJUnit4.class)
-public class IsoToEccProtobufRepositoryTest extends TelephonyTestBase {
-    private static final String LOG_TAG = "IsoToEccProtobufRepositoryTest";
-
-    @Test
-    public void testEccDataContent() {
-        IsoToEccProtobufRepository repository = IsoToEccProtobufRepository.getInstance();
-        repository.loadMappingTable(InstrumentationRegistry.getTargetContext());
-        Map<String, CountryEccInfo> eccTable = repository.getEccTable();
-        HashSet loadedIsos = new HashSet(300);
-        HashSet loadedNumbers = new HashSet(5);
-
-        assertThat(eccTable).isNotEmpty();
-        for (Map.Entry<String, CountryEccInfo> entry : eccTable.entrySet()) {
-            String countryIso = entry.getKey();
-            CountryEccInfo countryEccInfo = entry.getValue();
-            EccInfo[] eccInfoList = countryEccInfo.getEccInfoList();
-            if (eccInfoList.length > 0) {
-                Log.i(LOG_TAG, "Verifying country " + countryIso + " with "
-                        + eccInfoList.length + " ecc(s)");
-            } else {
-                Log.w(LOG_TAG, "Verifying country " + countryIso + " with no ecc");
-            }
-
-            assertThat(countryIso).isNotEmpty();
-            assertThat(countryIso).isEqualTo(countryIso.toUpperCase().trim());
-            assertThat(loadedIsos.contains(countryIso)).isFalse();
-            loadedIsos.add(countryIso);
-
-            assertThat(countryEccInfo.getFallbackEcc()).isNotEmpty();
-
-            if (eccInfoList.length != 0) {
-                loadedNumbers.clear();
-                for (EccInfo eccInfo : eccInfoList) {
-                    String eccNumber = eccInfo.getNumber();
-                    assertThat(eccNumber).isNotEmpty();
-                    assertThat(eccNumber).isEqualTo(eccNumber.trim());
-                    assertThat(eccInfo.getTypes()).isNotEmpty();
-                    assertThat(loadedNumbers.contains(eccNumber)).isFalse();
-                    loadedNumbers.add(eccNumber);
-                }
-            }
-        }
-    }
-}