Merge "Do not set brandOverride if opNames are invalid"
diff --git a/Android.bp b/Android.bp
index c567b99..5c79cf9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -62,6 +62,7 @@
         "android.hardware.radio-V1.2-java",
         "android.hardware.radio-V1.3-java",
         "android.hardware.radio-V1.4-java",
+        "android.hardware.radio-V1.5-java",
         "voip-common",
         "ims-common",
         "services",
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..e75dcb0
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "TeleServiceTests",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    }
+  ]
+}
diff --git a/proto/src/carrierId.proto b/proto/src/carrierId.proto
deleted file mode 100644
index 0f5eba8..0000000
--- a/proto/src/carrierId.proto
+++ /dev/null
@@ -1,92 +0,0 @@
-//
-// Copyright (C) 2017 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.
-//
-
-syntax = "proto2";
-
-package carrierIdentification;
-
-option java_package = "com.android.internal.telephony";
-option java_outer_classname = "CarrierIdProto";
-
-// A complete list of carriers
-message CarrierList {
-  // A collection of carriers. one entry for one carrier.
-  repeated CarrierId carrier_id = 1;
-  // Version number of current carrier list
-  optional int32 version = 2;
-};
-
-// CarrierId is the unique representation of a carrier in CID table.
-message CarrierId {
-  // [Optional] A unique canonical number designated to a carrier.
-  optional int32 canonical_id = 1;
-
-  // [Optional] A user-friendly carrier name (not localized).
-  optional string carrier_name = 2;
-
-  // [Optional] Carrier attributes to match a carrier. At least one value is required.
-  repeated CarrierAttribute carrier_attribute = 3;
-
-  // [Optional] A unique canonical number to represent its parent carrier. The parent-child
-  // relationship can be used to differentiate a single carrier by different networks,
-  // by prepaid v.s. postpaid  or even by 4G v.s. 3G plan.
-  optional int32 parent_canonical_id = 4;
-};
-
-// Attributes used to match a carrier.
-// For each field within this message:
-//   - if not set, the attribute is ignored;
-//   - if set, the device must have one of the specified values to match.
-// Match is based on AND between any field that is set and OR for values within a repeated field.
-message CarrierAttribute {
-  // [Optional] The MCC and MNC that map to this carrier. At least one value is required.
-  repeated string mccmnc_tuple = 1;
-
-  // [Optional] Prefix of IMSI (International Mobile Subscriber Identity) in
-  // decimal format. Some digits can be replaced with "x" symbols matching any digit.
-  // Sample values: 20404794, 21670xx2xxx.
-  repeated string imsi_prefix_xpattern = 2;
-
-  // [Optional] The Service Provider Name. Read from subscription EF_SPN.
-  // Sample values: C Spire, LeclercMobile
-  repeated string spn = 3;
-
-  // [Optional] PLMN network name. Read from subscription EF_PNN.
-  // Sample values:
-  repeated string plmn = 4;
-
-  // [Optional] Group Identifier Level1 for a GSM phone. Read from subscription EF_GID1.
-  // Sample values: 6D, BAE0000000000000
-  repeated string gid1 = 5;
-
-  // [Optional] Group Identifier Level2 for a GSM phone. Read from subscription EF_GID2.
-  // Sample values: 6D, BAE0000000000000
-  repeated string gid2 = 6;
-
-  // [Optional] The Access Point Name, corresponding to "apn" field returned by
-  // "content://telephony/carriers/preferapn" on device.
-  // Sample values: fast.t-mobile.com, internet
-  repeated string preferred_apn = 7;
-
-  // [Optional] Prefix of Integrated Circuit Card Identifier. Read from subscription EF_ICCID.
-  // Sample values: 894430, 894410
-  repeated string iccid_prefix = 8;
-
-  // [Optional] Carrier Privilege Access Rule in hex string.
-  // Sample values: 61ed377e85d386a8dfee6b864bd85b0bfaa5af88
-  repeated string privilege_access_rule = 9;
-};
-
diff --git a/src/java/com/android/internal/telephony/AsyncEmergencyContactNotifier.java b/src/java/com/android/internal/telephony/AsyncEmergencyContactNotifier.java
index 820a052..fd6ccd4 100644
--- a/src/java/com/android/internal/telephony/AsyncEmergencyContactNotifier.java
+++ b/src/java/com/android/internal/telephony/AsyncEmergencyContactNotifier.java
@@ -23,8 +23,7 @@
 
 /**
  * An {@link AsyncTask} that notifies the Blocked number provider that emergency services were
- * contacted. See {@link BlockedNumberContract.SystemContract#notifyEmergencyContact(Context)}
- * for details.
+ * contacted.
  * {@hide}
  */
 public class AsyncEmergencyContactNotifier extends AsyncTask<Void, Void, Void> {
@@ -39,10 +38,27 @@
     @Override
     protected Void doInBackground(Void... params) {
         try {
-            BlockedNumberContract.SystemContract.notifyEmergencyContact(mContext);
+            notifyEmergencyContact(mContext);
         } catch (Exception e) {
             Rlog.e(TAG, "Exception notifying emergency contact: " + e);
         }
         return null;
     }
+
+    /**
+     * Notifies the provider that emergency services were contacted by the user.
+     */
+    private void notifyEmergencyContact(Context context) {
+        try {
+            Rlog.i("notifyEmergencyContact; caller=%s", context.getOpPackageName());
+            context.getContentResolver().call(
+                    BlockedNumberContract.AUTHORITY_URI,
+                    BlockedNumberContract.METHOD_NOTIFY_EMERGENCY_CONTACT,
+                    null, null);
+        } catch (NullPointerException | IllegalArgumentException ex) {
+            // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+            // either of these happen.
+            Rlog.w(null, "notifyEmergencyContact: provider not ready.");
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/BlockChecker.java b/src/java/com/android/internal/telephony/BlockChecker.java
index 1a63db6..19981eb 100644
--- a/src/java/com/android/internal/telephony/BlockChecker.java
+++ b/src/java/com/android/internal/telephony/BlockChecker.java
@@ -67,19 +67,76 @@
         long startTimeNano = System.nanoTime();
 
         try {
-            blockStatus = BlockedNumberContract.SystemContract.shouldSystemBlockNumber(
-                    context, phoneNumber, extras);
+            blockStatus = shouldSystemBlockNumber(context, phoneNumber, extras);
             if (blockStatus != BlockedNumberContract.STATUS_NOT_BLOCKED) {
                 Rlog.d(TAG, phoneNumber + " is blocked.");
             }
         } catch (Exception e) {
             Rlog.e(TAG, "Exception checking for blocked number: " + e);
         }
-
         int durationMillis = (int) ((System.nanoTime() - startTimeNano) / 1000000);
         if (durationMillis > 500 || VDBG) {
             Rlog.d(TAG, "Blocked number lookup took: " + durationMillis + " ms.");
         }
         return blockStatus;
     }
+
+    /**
+     * Returns {@code true} if {@code phoneNumber} is blocked taking
+     * {@link #notifyEmergencyContact(Context)} into consideration. If emergency services
+     * have not been contacted recently and enhanced call blocking not been enabled, this
+     * method is equivalent to {@link #isBlocked(Context, String)}.
+     *
+     * @param context the context of the caller.
+     * @param phoneNumber the number to check.
+     * @param extras the extra attribute of the number.
+     * @return result code indicating if the number should be blocked, and if so why.
+     *         Valid values are: {@link #STATUS_NOT_BLOCKED}, {@link #STATUS_BLOCKED_IN_LIST},
+     *         {@link #STATUS_BLOCKED_NOT_IN_CONTACTS}, {@link #STATUS_BLOCKED_PAYPHONE},
+     *         {@link #STATUS_BLOCKED_RESTRICTED}, {@link #STATUS_BLOCKED_UNKNOWN_NUMBER}.
+     */
+    private static int shouldSystemBlockNumber(Context context, String phoneNumber,
+                                              Bundle extras) {
+        try {
+            String caller = context.getOpPackageName();
+            final Bundle res = context.getContentResolver().call(
+                    BlockedNumberContract.AUTHORITY_URI,
+                    BlockedNumberContract.METHOD_SHOULD_SYSTEM_BLOCK_NUMBER,
+                    phoneNumber, extras);
+            int blockResult = res != null ? res.getInt(BlockedNumberContract.RES_BLOCK_STATUS,
+                    BlockedNumberContract.STATUS_NOT_BLOCKED) :
+                    BlockedNumberContract.STATUS_NOT_BLOCKED;
+            Rlog.d(TAG, "shouldSystemBlockNumber: number=" + Rlog.pii(TAG, phoneNumber)
+                    + "caller=" + caller + "result=" + blockStatusToString(blockResult));
+            return blockResult;
+        } catch (NullPointerException | IllegalArgumentException ex) {
+            // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+            // either of these happen.
+            Rlog.w(null, "shouldSystemBlockNumber: provider not ready.");
+            return BlockedNumberContract.STATUS_NOT_BLOCKED;
+        }
+    }
+
+    /**
+     * Converts a block status constant to a string equivalent for logging.
+     * @hide
+     */
+    private static String blockStatusToString(int blockStatus) {
+        switch (blockStatus) {
+            case BlockedNumberContract.STATUS_NOT_BLOCKED:
+                return "not blocked";
+            case BlockedNumberContract.STATUS_BLOCKED_IN_LIST:
+                return "blocked - in list";
+            case BlockedNumberContract.STATUS_BLOCKED_RESTRICTED:
+                return "blocked - restricted";
+            case BlockedNumberContract.STATUS_BLOCKED_UNKNOWN_NUMBER:
+                return "blocked - unknown";
+            case BlockedNumberContract.STATUS_BLOCKED_PAYPHONE:
+                return "blocked - payphone";
+            case BlockedNumberContract.STATUS_BLOCKED_NOT_IN_CONTACTS:
+                return "blocked - not in contacts";
+        }
+        return "unknown";
+    }
+
 }
diff --git a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
index 5eb0456..4cdfd26 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
@@ -34,6 +34,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.service.carrier.CarrierService;
+import android.telephony.PackageChangeReceiver;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -41,9 +42,8 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.content.PackageMonitor;
-
 import com.android.internal.telephony.util.TelephonyUtils;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.List;
@@ -67,7 +67,7 @@
     public SparseArray<AppBinding> mBindings = new SparseArray();
     @VisibleForTesting
     public SparseArray<String> mLastSimState = new SparseArray<>();
-    private final PackageMonitor mPackageMonitor = new CarrierServicePackageMonitor();
+    private final PackageChangeReceiver mPackageMonitor = new CarrierServicePackageMonitor();
 
     private BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
         @Override
@@ -124,11 +124,11 @@
 
         updateBindingsAndSimStates();
 
-        PhoneConfigurationManager.getInstance().registerForMultiSimConfigChange(
+        PhoneConfigurationManager.registerForMultiSimConfigChange(
                 mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
 
         mPackageMonitor.register(
-                context, mHandler.getLooper(), UserHandle.ALL, false /* externalStorage */);
+                context, mHandler.getLooper(), UserHandle.ALL);
         try {
             Context contextAsUser = mContext.createPackageContextAsUser(mContext.getPackageName(),
                 0, UserHandle.SYSTEM);
@@ -373,19 +373,19 @@
         }
     }
 
-    private class CarrierServicePackageMonitor extends PackageMonitor {
+    private class CarrierServicePackageMonitor extends PackageChangeReceiver {
         @Override
-        public void onPackageAdded(String packageName, int reason) {
+        public void onPackageAdded(String packageName) {
             evaluateBinding(packageName, true /* forceUnbind */);
         }
 
         @Override
-        public void onPackageRemoved(String packageName, int reason) {
+        public void onPackageRemoved(String packageName) {
             evaluateBinding(packageName, true /* forceUnbind */);
         }
 
         @Override
-        public void onPackageUpdateFinished(String packageName, int uid) {
+        public void onPackageUpdateFinished(String packageName) {
             evaluateBinding(packageName, true /* forceUnbind */);
         }
 
@@ -395,13 +395,12 @@
         }
 
         @Override
-        public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
+        public void onHandleForceStop(String[] packages, boolean doit) {
             if (doit) {
                 for (String packageName : packages) {
                     evaluateBinding(packageName, true /* forceUnbind */);
                 }
             }
-            return super.onHandleForceStop(intent, packages, uid, doit);
         }
 
         private void evaluateBinding(String carrierPackageName, boolean forceUnbind) {
diff --git a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
index e112068..a61cc37 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
@@ -170,13 +170,6 @@
                 || mSST.mSS.getDataRegState() == ServiceState.STATE_IN_SERVICE);
     }
 
-    private boolean isPhoneVoiceRegistered() {
-        if (mSST.mSS == null) {
-            return true; //something has gone wrong, return true and not show the notification.
-        }
-        return (mSST.mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE);
-    }
-
     private boolean isPhoneRegisteredForWifiCalling() {
         Rlog.d(LOG_TAG, "isPhoneRegisteredForWifiCalling: " + mPhone.isWifiCallingEnabled());
         return mPhone.isWifiCallingEnabled();
@@ -456,8 +449,8 @@
     }
 
     /**
-     * Class that defines the emergency notification, which is shown when the user is out of cell
-     * connectivity, but has wifi enabled.
+     * Class that defines the emergency notification, which is shown when Wi-Fi Calling is
+     * available.
      */
     public class EmergencyNetworkNotification implements NotificationType {
 
@@ -502,10 +495,9 @@
          */
         public boolean sendMessage() {
             Rlog.i(LOG_TAG, "EmergencyNetworkNotification: sendMessage() w/values: "
-                    + "," + isPhoneVoiceRegistered() + "," + mDelay + ","
-                    + isPhoneRegisteredForWifiCalling() + "," + mSST.isRadioOn());
-            if (mDelay == UNINITIALIZED_DELAY_VALUE || isPhoneVoiceRegistered()
-                    || !isPhoneRegisteredForWifiCalling()) {
+                    + "," + mDelay + "," + isPhoneRegisteredForWifiCalling() + ","
+                    + mSST.isRadioOn());
+            if (mDelay == UNINITIALIZED_DELAY_VALUE || !isPhoneRegisteredForWifiCalling()) {
                 return false;
             }
             return true;
@@ -525,6 +517,7 @@
                     .setContentTitle(title)
                     .setStyle(new Notification.BigTextStyle().bigText(details))
                     .setContentText(details)
+                    .setFlag(Notification.FLAG_NO_CLEAR, true)
                     .setChannelId(NotificationChannelController.CHANNEL_ID_WFC);
         }
     }
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index 0c60bc6..273de72 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -26,6 +26,7 @@
 import android.telephony.ClientRequestStats;
 import android.telephony.ImsiEncryptionInfo;
 import android.telephony.NetworkScanRequest;
+import android.telephony.SignalThresholdInfo;
 import android.telephony.data.DataProfile;
 import android.telephony.emergency.EmergencyNumber;
 
@@ -2230,17 +2231,30 @@
     void setUnsolResponseFilter(int filter, Message result);
 
     /**
-     * Send the signal strength reporting criteria to the modem.
+     * Sets the signal strength reporting criteria.
      *
-     * @param hysteresisMs A hysteresis time in milliseconds. A value of 0 disables hysteresis.
-     * @param hysteresisDb An interval in dB defining the required magnitude change between reports.
-     *     A value of 0 disables hysteresis.
-     * @param thresholdsDbm An array of trigger thresholds in dBm. A size of 0 disables thresholds.
-     * @param ran RadioAccessNetwork for which to apply criteria.
+     * The resulting reporting rules are the AND of all the supplied criteria. For each RAN
+     * The hysteresisDb and thresholds apply to only the following measured quantities:
+     * -GERAN    - RSSI
+     * -CDMA2000 - RSSI
+     * -UTRAN    - RSCP
+     * -EUTRAN   - RSRP/RSRQ/RSSNR
+     * -NGRAN    - SSRSRP/SSRSRQ/SSSINR
+     *
+     * Note: Reporting criteria must be individually set for each RAN. For any unset reporting
+     * criteria, the value is implementation-defined.
+     *
+     * Response callback is
+     * IRadioResponse.setSignalStrengthReportingCriteriaResponse_1_5()
+     *
+     * @param signalThresholdInfo Signal threshold info including the threshold values,
+     *                            hysteresisDb, and hysteresisMs. See @1.5::SignalThresholdInfo
+     *                            for details.
+     * @param ran The type of network for which to apply these thresholds.
      * @param result callback message contains the information of SUCCESS/FAILURE
      */
-    void setSignalStrengthReportingCriteria(int hysteresisMs, int hysteresisDb, int[] thresholdsDbm,
-            int ran, Message result);
+    void setSignalStrengthReportingCriteria(SignalThresholdInfo signalThresholdInfo, int ran,
+            Message result);
 
     /**
      * Send the link capacity reporting criteria to the modem
diff --git a/src/java/com/android/internal/telephony/Connection.java b/src/java/com/android/internal/telephony/Connection.java
index 6cc9e63..36de488 100755
--- a/src/java/com/android/internal/telephony/Connection.java
+++ b/src/java/com/android/internal/telephony/Connection.java
@@ -217,6 +217,9 @@
     protected int mCause = DisconnectCause.NOT_DISCONNECTED;
     protected PostDialState mPostDialState = PostDialState.NOT_STARTED;
 
+    // Store the current audio code
+    protected int mAudioCodec;
+
     @UnsupportedAppUsage
     private static String LOG_TAG = "Connection";
 
@@ -1355,4 +1358,12 @@
                 .append(" post dial state: " + getPostDialState());
         return str.toString();
     }
+
+    /**
+     * Get current audio codec.
+     * @return current audio codec.
+     */
+    public int getAudioCodec() {
+        return mAudioCodec;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/DeviceStateMonitor.java b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
index f4d782c..bf033d8 100644
--- a/src/java/com/android/internal/telephony/DeviceStateMonitor.java
+++ b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
@@ -37,6 +37,7 @@
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
 import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
+import android.telephony.SignalThresholdInfo;
 import android.telephony.TelephonyManager;
 import android.util.LocalLog;
 import android.util.SparseIntArray;
@@ -587,14 +588,23 @@
     }
 
     private void setSignalStrengthReportingCriteria() {
-        mPhone.setSignalStrengthReportingCriteria(
-                AccessNetworkThresholds.GERAN, AccessNetworkType.GERAN);
-        mPhone.setSignalStrengthReportingCriteria(
-                AccessNetworkThresholds.UTRAN, AccessNetworkType.UTRAN);
-        mPhone.setSignalStrengthReportingCriteria(
-                AccessNetworkThresholds.EUTRAN, AccessNetworkType.EUTRAN);
-        mPhone.setSignalStrengthReportingCriteria(
-                AccessNetworkThresholds.CDMA2000, AccessNetworkType.CDMA2000);
+        mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSSI,
+                AccessNetworkThresholds.GERAN, AccessNetworkType.GERAN, true);
+        mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSCP,
+                AccessNetworkThresholds.UTRAN, AccessNetworkType.UTRAN, true);
+        mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSRP,
+                AccessNetworkThresholds.EUTRAN_RSRP, AccessNetworkType.EUTRAN, true);
+        mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSSI,
+                AccessNetworkThresholds.CDMA2000, AccessNetworkType.CDMA2000, true);
+        if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
+            // Defaultly we only need SSRSRP for NGRAN signal criterial reporting
+            mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_SSRSRP,
+                    AccessNetworkThresholds.NGRAN_RSRSRP, AccessNetworkType.NGRAN, true);
+            mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_SSRSRQ,
+                    AccessNetworkThresholds.NGRAN_RSRSRQ, AccessNetworkType.NGRAN, false);
+            mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_SSSINR,
+                    AccessNetworkThresholds.NGRAN_SSSINR, AccessNetworkType.NGRAN, false);
+        }
     }
 
     private void setLinkCapacityReportingCriteria() {
@@ -730,17 +740,19 @@
         };
 
         /**
-         * List of default dBm thresholds for EUTRAN {@link AccessNetworkType}.
+         * List of default dBm RSRP thresholds for EUTRAN {@link AccessNetworkType}.
          *
          * These thresholds are taken from the LTE RSRP defaults in {@link CarrierConfigManager}.
          */
-        public static final int[] EUTRAN = new int[] {
+        public static final int[] EUTRAN_RSRP = new int[] {
             -128, /* SIGNAL_STRENGTH_POOR */
             -118, /* SIGNAL_STRENGTH_MODERATE */
             -108, /* SIGNAL_STRENGTH_GOOD */
             -98,  /* SIGNAL_STRENGTH_GREAT */
         };
 
+        // TODO Add EUTRAN_RSRQ and EUTRAN_RSSNI
+
         /**
          * List of dBm thresholds for CDMA2000 {@link AccessNetworkType}.
          *
@@ -752,6 +764,36 @@
             -75,
             -65
         };
+
+        /**
+         * List of dB thresholds for NGRAN {@link AccessNetworkType} RSRSRP
+         */
+        public static final int[] NGRAN_RSRSRP = new int[] {
+            -125, /* SIGNAL_STRENGTH_POOR */
+            -115, /* SIGNAL_STRENGTH_MODERATE */
+            -105, /* SIGNAL_STRENGTH_GOOD */
+            -95,  /* SIGNAL_STRENGTH_GREAT */
+        };
+
+        /**
+         * List of dB thresholds for NGRAN {@link AccessNetworkType} RSRSRP
+         */
+        public static final int[] NGRAN_RSRSRQ = new int[] {
+            -14, /* SIGNAL_STRENGTH_POOR */
+            -12, /* SIGNAL_STRENGTH_MODERATE */
+            -10, /* SIGNAL_STRENGTH_GOOD */
+            -8  /* SIGNAL_STRENGTH_GREAT */
+        };
+
+        /**
+         * List of dB thresholds for NGRAN {@link AccessNetworkType} SSSINR
+         */
+        public static final int[] NGRAN_SSSINR = new int[] {
+            -8, /* SIGNAL_STRENGTH_POOR */
+            0, /* SIGNAL_STRENGTH_MODERATE */
+            8, /* SIGNAL_STRENGTH_GOOD */
+            16  /* SIGNAL_STRENGTH_GREAT */
+        };
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/GsmCdmaConnection.java b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
index e74c176..4f695e6 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaConnection.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
@@ -76,9 +76,6 @@
     // The cached delay to be used between DTMF tones fetched from carrier config.
     private int mDtmfToneDelay = 0;
 
-    // Store the current audio codec
-    private int mAudioCodec = DriverCall.AUDIO_QUALITY_UNSPECIFIED;
-
     private TelephonyMetrics mMetrics = TelephonyMetrics.getInstance();
 
     //***** Event Constants
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index e8c988d..41e0053 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -66,6 +66,7 @@
 import android.telephony.PhoneNumberUtils;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
+import android.telephony.SignalThresholdInfo;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -3658,9 +3659,11 @@
     }
 
     @Override
-    public void setSignalStrengthReportingCriteria(int[] thresholds, int ran) {
-        mCi.setSignalStrengthReportingCriteria(REPORTING_HYSTERESIS_MILLIS, REPORTING_HYSTERESIS_DB,
-                thresholds, ran, null);
+    public void setSignalStrengthReportingCriteria(
+            int signalStrengthMeasure, int[] thresholds, int ran, boolean isEnabled) {
+        mCi.setSignalStrengthReportingCriteria(new SignalThresholdInfo(signalStrengthMeasure,
+                REPORTING_HYSTERESIS_MILLIS, REPORTING_HYSTERESIS_DB, thresholds, isEnabled),
+                ran, null);
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/NitzStateMachineImpl.java b/src/java/com/android/internal/telephony/NitzStateMachineImpl.java
index 7760394..cc96cba 100644
--- a/src/java/com/android/internal/telephony/NitzStateMachineImpl.java
+++ b/src/java/com/android/internal/telephony/NitzStateMachineImpl.java
@@ -89,8 +89,8 @@
     private boolean mNitzTimeZoneDetectionSuccessful = false;
 
     // Miscellaneous dependencies and helpers not related to detection state.
-    private final LocalLog mTimeLog = new LocalLog(30);
-    private final LocalLog mTimeZoneLog = new LocalLog(30);
+    private final LocalLog mTimeLog = new LocalLog(30, false /* useLocalTimestamps */);
+    private final LocalLog mTimeZoneLog = new LocalLog(30, false /* useLocalTimestamps */);
     private final Phone mPhone;
     private final DeviceState mDeviceState;
     private final TimeServiceHelper mTimeServiceHelper;
@@ -253,7 +253,7 @@
             // Set state as needed.
             if (zoneId != null) {
                 if (mTimeServiceHelper.isTimeZoneDetectionEnabled()) {
-                    setAndBroadcastNetworkSetTimeZone(zoneId, logMsg);
+                    setTimeZone(zoneId, logMsg);
                 } else {
                     if (DBG) {
                         logMsg += " [Not setting device time zone]";
@@ -402,7 +402,8 @@
     }
 
     private void sendEmptyTimeSuggestion(String reason) {
-        PhoneTimeSuggestion timeSuggestion = new PhoneTimeSuggestion(mPhone.getPhoneId());
+        PhoneTimeSuggestion timeSuggestion =
+                new PhoneTimeSuggestion.Builder(mPhone.getPhoneId()).build();
         timeSuggestion.addDebugInfo("Empty suggestion, reason=" + reason);
         mTimeServiceHelper.suggestDeviceTime(timeSuggestion);
     }
@@ -484,9 +485,11 @@
                 Rlog.d(LOG_TAG, logMsg);
             }
             mTimeLog.log(logMsg);
-            PhoneTimeSuggestion phoneTimeSuggestion = new PhoneTimeSuggestion(mPhone.getPhoneId());
-            phoneTimeSuggestion.setUtcTime(newNitzTime);
-            phoneTimeSuggestion.addDebugInfo(logMsg);
+            PhoneTimeSuggestion phoneTimeSuggestion =
+                    new PhoneTimeSuggestion.Builder(mPhone.getPhoneId())
+                            .setUtcTime(newNitzTime)
+                            .addDebugInfo(logMsg)
+                            .build();
             mTimeServiceHelper.suggestDeviceTime(phoneTimeSuggestion);
 
             TelephonyMetrics.getInstance().writeNITZEvent(
@@ -501,7 +504,7 @@
         }
     }
 
-    private void setAndBroadcastNetworkSetTimeZone(String zoneId, String logMessage) {
+    private void setTimeZone(String zoneId, String logMessage) {
         logMessage += " [Setting device time zone to zoneId=" + zoneId + "]";
         if (DBG) {
             Rlog.d(LOG_TAG, logMessage);
@@ -528,7 +531,7 @@
         String logMsg = "handleAutoTimeZoneEnabled: "
                 + " mSavedTimeZoneId=" + mSavedTimeZoneId;
         if (mSavedTimeZoneId != null) {
-            setAndBroadcastNetworkSetTimeZone(mSavedTimeZoneId, logMsg);
+            setTimeZone(mSavedTimeZoneId, logMsg);
         } else {
             if (DBG) {
                 logMsg += " [Not setting device time zone]";
@@ -587,7 +590,7 @@
 
             String zoneId = lookupResult.zoneId;
             if (mTimeServiceHelper.isTimeZoneDetectionEnabled()) {
-                setAndBroadcastNetworkSetTimeZone(zoneId, logMsg);
+                setTimeZone(zoneId, logMsg);
             } else {
                 if (DBG) {
                     logMsg += " [Not setting device time zone]";
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index efa3954..db76e84 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -19,10 +19,8 @@
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.app.BroadcastOptions;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.net.LinkProperties;
 import android.net.NetworkCapabilities;
@@ -116,33 +114,6 @@
 
     protected final int USSD_MAX_QUEUE = 10;
 
-    private BroadcastReceiver mImsIntentReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            Rlog.d(LOG_TAG, "mImsIntentReceiver: action " + intent.getAction());
-            if (intent.hasExtra(ImsManager.EXTRA_PHONE_ID)) {
-                int extraPhoneId = intent.getIntExtra(ImsManager.EXTRA_PHONE_ID,
-                        SubscriptionManager.INVALID_PHONE_INDEX);
-                Rlog.d(LOG_TAG, "mImsIntentReceiver: extraPhoneId = " + extraPhoneId);
-                if (extraPhoneId == SubscriptionManager.INVALID_PHONE_INDEX ||
-                        extraPhoneId != getPhoneId()) {
-                    return;
-                }
-            }
-
-            synchronized (Phone.lockForRadioTechnologyChange) {
-                if (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_UP)) {
-                    mImsServiceReady = true;
-                    updateImsPhone();
-                    ImsManager.getInstance(mContext, mPhoneId).updateImsServiceConfig(false);
-                } else if (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_DOWN)) {
-                    mImsServiceReady = false;
-                    updateImsPhone();
-                }
-            }
-        }
-    };
-
     // Key used to read and write the saved network selection numeric value
     public static final String NETWORK_SELECTION_KEY = "network_selection_key";
     // Key used to read and write the saved network selection operator name
@@ -323,7 +294,6 @@
     @UnsupportedAppUsage
     protected int mPhoneId;
 
-    private boolean mImsServiceReady = false;
     @UnsupportedAppUsage
     protected Phone mImsPhone = null;
 
@@ -586,34 +556,20 @@
     }
 
     /**
-     * Start listening for IMS service UP/DOWN events. If using the new ImsResolver APIs, we should
-     * always be setting up ImsPhones.
+     * Start setup of ImsPhone, which will start trying to connect to the ImsResolver. Will not be
+     * called if this device does not support FEATURE_IMS_TELEPHONY.
      */
-    public void startMonitoringImsService() {
+    public void createImsPhone() {
         if (getPhoneType() == PhoneConstants.PHONE_TYPE_SIP) {
             return;
         }
 
         synchronized(Phone.lockForRadioTechnologyChange) {
-            IntentFilter filter = new IntentFilter();
-            ImsManager imsManager = ImsManager.getInstance(mContext, getPhoneId());
-            // Don't listen to deprecated intents using the new dynamic binding.
-            if (imsManager != null && !imsManager.isDynamicBinding()) {
-                filter.addAction(ImsManager.ACTION_IMS_SERVICE_UP);
-                filter.addAction(ImsManager.ACTION_IMS_SERVICE_DOWN);
-                mContext.registerReceiver(mImsIntentReceiver, filter);
-            }
-
-            // Monitor IMS service - but first poll to see if already up (could miss
-            // intent). Also, when using new ImsResolver APIs, the service will be available soon,
-            // so start trying to bind.
-            if (imsManager != null) {
-                // If it is dynamic binding, kick off ImsPhone creation now instead of waiting for
-                // the service to be available.
-                if (imsManager.isDynamicBinding() || imsManager.isServiceAvailable()) {
-                    mImsServiceReady = true;
-                    updateImsPhone();
-                }
+            if (mImsPhone == null) {
+                mImsPhone = PhoneFactory.makeImsPhone(mNotifier, this);
+                CallManager.getInstance().registerPhone(mImsPhone);
+                mImsPhone.registerForSilentRedial(
+                        this, EVENT_INITIATE_SILENT_REDIAL, null);
             }
         }
     }
@@ -3461,28 +3417,6 @@
     public void dispose() {
     }
 
-    private void updateImsPhone() {
-        Rlog.d(LOG_TAG, "updateImsPhone"
-                + " mImsServiceReady=" + mImsServiceReady);
-
-        if (mImsServiceReady && (mImsPhone == null)) {
-            mImsPhone = PhoneFactory.makeImsPhone(mNotifier, this);
-            CallManager.getInstance().registerPhone(mImsPhone);
-            mImsPhone.registerForSilentRedial(
-                    this, EVENT_INITIATE_SILENT_REDIAL, null);
-        } else if (!mImsServiceReady && (mImsPhone != null)) {
-            CallManager.getInstance().unregisterPhone(mImsPhone);
-            mImsPhone.unregisterForSilentRedial(this);
-
-            mImsPhone.dispose();
-            // Potential GC issue if someone keeps a reference to ImsPhone.
-            // However: this change will make sure that such a reference does
-            // not access functions through NULL pointer.
-            //mImsPhone.removeReferences();
-            mImsPhone = null;
-        }
-    }
-
     /**
      * Dials a number.
      *
@@ -3913,7 +3847,8 @@
     }
 
     /** Sets the SignalStrength reporting criteria. */
-    public void setSignalStrengthReportingCriteria(int[] thresholds, int ran) {
+    public void setSignalStrengthReportingCriteria(
+            int signalStrengthMeasure, int[] thresholds, int ran, boolean isEnabled) {
         // no-op default implementation
     }
 
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index a5b6437..3ed44d6 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -28,6 +28,7 @@
 import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.net.LocalServerSocket;
+import android.os.HandlerThread;
 import android.os.Looper;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
@@ -38,7 +39,6 @@
 import android.telephony.TelephonyManager;
 import android.util.LocalLog;
 
-import com.android.internal.os.BackgroundThread;
 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
 import com.android.internal.telephony.dataconnection.TelephonyNetworkFactory;
 import com.android.internal.telephony.euicc.EuiccCardController;
@@ -205,30 +205,27 @@
                 sMadeDefaults = true;
 
                 Rlog.i(LOG_TAG, "Creating SubInfoRecordUpdater ");
+                HandlerThread pfhandlerThread = new HandlerThread("PhoneFactoryHandlerThread");
+                pfhandlerThread.start();
                 sSubInfoRecordUpdater = new SubscriptionInfoUpdater(
-                        BackgroundThread.get().getLooper(), context, sCommandsInterfaces);
+                        pfhandlerThread.getLooper(), context, sCommandsInterfaces);
 
                 // Only bring up IMS if the device supports having an IMS stack.
                 if (context.getPackageManager().hasSystemFeature(
                         PackageManager.FEATURE_TELEPHONY_IMS)) {
-                    // Return whether or not the device should use dynamic binding or the static
-                    // implementation (deprecated)
-                    boolean isDynamicBinding = sContext.getResources().getBoolean(
-                            com.android.internal.R.bool.config_dynamic_bind_ims);
                     // Get the package name of the default IMS implementation.
                     String defaultImsPackage = sContext.getResources().getString(
                             com.android.internal.R.string.config_ims_package);
                     // Start ImsResolver and bind to ImsServices.
                     Rlog.i(LOG_TAG, "ImsResolver: defaultImsPackage: " + defaultImsPackage);
-                    sImsResolver = new ImsResolver(sContext, defaultImsPackage, numPhones,
-                            isDynamicBinding);
+                    sImsResolver = new ImsResolver(sContext, defaultImsPackage, numPhones);
                     sImsResolver.initialize();
                     // Start monitoring after defaults have been made.
                     // Default phone must be ready before ImsPhone is created because ImsService
                     // might need it when it is being opened. This should initialize multiple
                     // ImsPhones for ImsResolver implementations of ImsService.
                     for (int i = 0; i < numPhones; i++) {
-                        sPhones[i].startMonitoringImsService();
+                        sPhones[i].createImsPhone();
                     }
                 } else {
                     Rlog.i(LOG_TAG, "IMS is not supported on this device, skipping ImsResolver.");
@@ -284,7 +281,7 @@
                 sPhones[i] = createPhone(context, i);
                 if (context.getPackageManager().hasSystemFeature(
                         PackageManager.FEATURE_TELEPHONY_IMS)) {
-                    sPhones[i].startMonitoringImsService();
+                    sPhones[i].createImsPhone();
                 }
                 sTelephonyNetworkFactories[i] = new TelephonyNetworkFactory(
                         Looper.myLooper(), sPhones[i]);
diff --git a/src/java/com/android/internal/telephony/PhoneSwitcher.java b/src/java/com/android/internal/telephony/PhoneSwitcher.java
index ae514d9..3981eb6 100644
--- a/src/java/com/android/internal/telephony/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/PhoneSwitcher.java
@@ -1110,6 +1110,8 @@
 
     @VisibleForTesting
     protected boolean isPhoneActive(int phoneId) {
+        if (phoneId >= mActiveModemCount)
+            return false;
         return mPhoneStates[phoneId].active;
     }
 
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 195da41..a0be155 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -48,9 +48,9 @@
 import android.hardware.radio.V1_0.SimApdu;
 import android.hardware.radio.V1_0.SmsWriteArgs;
 import android.hardware.radio.V1_0.UusInfo;
-import android.hardware.radio.V1_2.AccessNetwork;
 import android.hardware.radio.V1_4.CarrierRestrictionsWithPriority;
 import android.hardware.radio.V1_4.SimLockMultiSimPolicy;
+import android.hardware.radio.V1_5.AccessNetwork;
 import android.hardware.radio.deprecated.V1_0.IOemHook;
 import android.net.ConnectivityManager;
 import android.net.KeepalivePacketData;
@@ -90,6 +90,7 @@
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
+import android.telephony.SignalThresholdInfo;
 import android.telephony.SmsManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyHistogram;
@@ -180,6 +181,9 @@
     /** @hide */
     public static final HalVersion RADIO_HAL_VERSION_1_4 = new HalVersion(1, 4);
 
+    /** @hide */
+    public static final HalVersion RADIO_HAL_VERSION_1_5 = new HalVersion(1, 5);
+
     // IRadio version
     private HalVersion mRadioVersion = RADIO_HAL_VERSION_UNKNOWN;
 
@@ -435,14 +439,23 @@
                         + " is disabled");
             } else {
                 try {
-                    mRadioProxy = android.hardware.radio.V1_4.IRadio.getService(
+                    mRadioProxy = android.hardware.radio.V1_5.IRadio.getService(
                             HIDL_SERVICE_NAME[mPhoneId], true);
-                    mRadioVersion = RADIO_HAL_VERSION_1_4;
+                    mRadioVersion = RADIO_HAL_VERSION_1_5;
                 } catch (NoSuchElementException e) {
                 }
 
                 if (mRadioProxy == null) {
                     try {
+                        mRadioProxy = android.hardware.radio.V1_4.IRadio.getService(
+                                HIDL_SERVICE_NAME[mPhoneId], true);
+                        mRadioVersion = RADIO_HAL_VERSION_1_4;
+                    } catch (NoSuchElementException e) {
+                    }
+                }
+
+                if (mRadioProxy == null) {
+                    try {
                         mRadioProxy = android.hardware.radio.V1_3.IRadio.getService(
                                 HIDL_SERVICE_NAME[mPhoneId], true);
                         mRadioVersion = RADIO_HAL_VERSION_1_3;
@@ -2080,12 +2093,83 @@
         return rasInHalFormat;
     }
 
+    private android.hardware.radio.V1_5.RadioAccessSpecifier
+            convertRadioAccessSpecifierToRadioHAL_1_5(RadioAccessSpecifier ras) {
+        android.hardware.radio.V1_5.RadioAccessSpecifier rasInHalFormat =
+                new android.hardware.radio.V1_5.RadioAccessSpecifier();
+        rasInHalFormat.radioAccessNetwork = ras.getRadioAccessNetwork();
+        List<Integer> bands = null;
+        switch (ras.getRadioAccessNetwork()) {
+            case AccessNetworkType.GERAN:
+                bands = rasInHalFormat.bands.geranBands();
+                break;
+            case AccessNetworkType.UTRAN:
+                bands = rasInHalFormat.bands.utranBands();
+                break;
+            case AccessNetworkType.EUTRAN:
+                bands = rasInHalFormat.bands.eutranBands();
+                break;
+            case AccessNetworkType.NGRAN:
+                bands = rasInHalFormat.bands.ngranBands();
+                break;
+            default:
+                Log.wtf(RILJ_LOG_TAG, "radioAccessNetwork " + ras.getRadioAccessNetwork()
+                        + " not supported!");
+                return null;
+        }
+
+        if (ras.getBands() != null) {
+            for (int band : ras.getBands()) {
+                bands.add(band);
+            }
+        }
+        if (ras.getChannels() != null) {
+            for (int channel : ras.getChannels()) {
+                rasInHalFormat.channels.add(channel);
+            }
+        }
+
+        return rasInHalFormat;
+    }
+
     @Override
     public void startNetworkScan(NetworkScanRequest nsr, Message result) {
         IRadio radioProxy = getRadioProxy(result);
         if (radioProxy != null) {
-            if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
+            if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
+                android.hardware.radio.V1_5.NetworkScanRequest request =
+                        new android.hardware.radio.V1_5.NetworkScanRequest();
+                request.type = nsr.getScanType();
+                request.interval = nsr.getSearchPeriodicity();
+                request.maxSearchTime = nsr.getMaxSearchTime();
+                request.incrementalResultsPeriodicity = nsr.getIncrementalResultsPeriodicity();
+                request.incrementalResults = nsr.getIncrementalResults();
 
+                for (RadioAccessSpecifier ras : nsr.getSpecifiers()) {
+                    android.hardware.radio.V1_5.RadioAccessSpecifier rasInHalFormat =
+                            convertRadioAccessSpecifierToRadioHAL_1_5(ras);
+                    if (rasInHalFormat == null) {
+                        return;
+                    }
+                    request.specifiers.add(rasInHalFormat);
+                }
+
+                request.mccMncs.addAll(nsr.getPlmns());
+                RILRequest rr = obtainRequest(RIL_REQUEST_START_NETWORK_SCAN, result,
+                        mRILDefaultWorkSource);
+
+                if (RILJ_LOGD) {
+                    riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+                }
+
+                try {
+                    android.hardware.radio.V1_5.IRadio radioProxy15 =
+                            (android.hardware.radio.V1_5.IRadio) radioProxy;
+                    radioProxy15.startNetworkScan_1_5(rr.mSerial, request);
+                } catch (RemoteException | RuntimeException e) {
+                    handleRadioProxyExceptionForRR(rr, "startNetworkScan", e);
+                }
+            } else if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)) {
                 android.hardware.radio.V1_2.NetworkScanRequest request =
                         new android.hardware.radio.V1_2.NetworkScanRequest();
                 request.type = nsr.getScanType();
@@ -4358,8 +4442,8 @@
     }
 
     @Override
-    public void setSignalStrengthReportingCriteria(int hysteresisMs, int hysteresisDb,
-            int[] thresholdsDbm, int ran, Message result) {
+    public void setSignalStrengthReportingCriteria(SignalThresholdInfo signalThresholdInfo,
+            int ran, Message result) {
         IRadio radioProxy = getRadioProxy(result);
         if (radioProxy != null) {
             if (mRadioVersion.less(RADIO_HAL_VERSION_1_2)) {
@@ -4367,26 +4451,57 @@
                         + "than 1.2");
                 return;
             }
-
-            RILRequest rr = obtainRequest(RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA,
-                    result, mRILDefaultWorkSource);
-
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-            }
-
-            try {
-                android.hardware.radio.V1_2.IRadio radioProxy12 =
+            if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_2)
+                    && mRadioVersion.less(RADIO_HAL_VERSION_1_5)) {
+                RILRequest rr = obtainRequest(RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA,
+                        result, mRILDefaultWorkSource);
+                if (RILJ_LOGD) {
+                    riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+                }
+                try {
+                    android.hardware.radio.V1_2.IRadio radioProxy12 =
                         (android.hardware.radio.V1_2.IRadio) radioProxy;
-                radioProxy12.setSignalStrengthReportingCriteria(rr.mSerial, hysteresisMs,
-                        hysteresisDb, primitiveArrayToArrayList(thresholdsDbm),
-                        convertRanToHalRan(ran));
-            } catch (RemoteException | RuntimeException e) {
-                handleRadioProxyExceptionForRR(rr, "setSignalStrengthReportingCriteria", e);
+                    radioProxy12.setSignalStrengthReportingCriteria(rr.mSerial,
+                            signalThresholdInfo.getHysteresisMs(),
+                            signalThresholdInfo.getHysteresisDb(),
+                            primitiveArrayToArrayList(signalThresholdInfo.getThresholds()),
+                            convertRanToHalRan(ran));
+                } catch (RemoteException | RuntimeException e) {
+                    handleRadioProxyExceptionForRR(rr, "setSignalStrengthReportingCriteria", e);
+                }
+            }
+            if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
+                RILRequest rr = obtainRequest(RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA,
+                        result, mRILDefaultWorkSource);
+                if (RILJ_LOGD) {
+                    riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+                }
+                try {
+                    android.hardware.radio.V1_5.IRadio radioProxy15 =
+                            (android.hardware.radio.V1_5.IRadio) radioProxy;
+                    radioProxy15.setSignalStrengthReportingCriteria_1_5(rr.mSerial,
+                            convertToHalSignalThresholdInfo(signalThresholdInfo),
+                            convertRanToHalRan(ran));
+                } catch (RemoteException | RuntimeException e) {
+                    handleRadioProxyExceptionForRR(
+                            rr, "setSignalStrengthReportingCriteria_1_5", e);
+                }
             }
         }
     }
 
+    private static android.hardware.radio.V1_5.SignalThresholdInfo convertToHalSignalThresholdInfo(
+            SignalThresholdInfo signalThresholdInfo) {
+        android.hardware.radio.V1_5.SignalThresholdInfo signalThresholdInfoHal =
+                new android.hardware.radio.V1_5.SignalThresholdInfo();
+        signalThresholdInfoHal.signalMeasurement = signalThresholdInfo.getSignalMeasurement();
+        signalThresholdInfoHal.hysteresisMs = signalThresholdInfo.getHysteresisMs();
+        signalThresholdInfoHal.hysteresisDb = signalThresholdInfo.getHysteresisDb();
+        signalThresholdInfoHal.thresholds = primitiveArrayToArrayList(
+                signalThresholdInfo.getThresholds());
+        return signalThresholdInfoHal;
+    }
+
     @Override
     public void setLinkCapacityReportingCriteria(int hysteresisMs, int hysteresisDlKbps,
             int hysteresisUlKbps, int[] thresholdsDlKbps, int[] thresholdsUlKbps, int ran,
@@ -4431,6 +4546,8 @@
                 return AccessNetwork.CDMA2000;
             case AccessNetworkType.IWLAN:
                 return AccessNetwork.IWLAN;
+            case AccessNetworkType.NGRAN:
+                return AccessNetwork.NGRAN;
             case AccessNetworkType.UNKNOWN:
             default:
                 return 0;
diff --git a/src/java/com/android/internal/telephony/RadioResponse.java b/src/java/com/android/internal/telephony/RadioResponse.java
index a36b7a6..da68b69 100644
--- a/src/java/com/android/internal/telephony/RadioResponse.java
+++ b/src/java/com/android/internal/telephony/RadioResponse.java
@@ -1452,6 +1452,13 @@
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
+    public void setSignalStrengthReportingCriteriaResponse_1_5(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
     public void setLinkCapacityReportingCriteriaResponse(RadioResponseInfo responseInfo) {
         responseVoid(responseInfo);
     }
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index d790a3e..b3149c2 100755
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -67,12 +67,14 @@
 import android.telephony.CellIdentityWcdma;
 import android.telephony.CellInfo;
 import android.telephony.CellLocation;
+import android.telephony.CellSignalStrengthNr;
 import android.telephony.DataSpecificRegistrationInfo;
 import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PhysicalChannelConfig;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
+import android.telephony.SignalThresholdInfo;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
@@ -4715,15 +4717,32 @@
     }
 
     private void updateReportingCriteria(PersistableBundle config) {
-        mPhone.setSignalStrengthReportingCriteria(
+        mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSRP,
                 config.getIntArray(CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY),
-                AccessNetworkType.EUTRAN);
-        mPhone.setSignalStrengthReportingCriteria(
+                AccessNetworkType.EUTRAN, true);
+        mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSCP,
                 config.getIntArray(CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY),
-                AccessNetworkType.UTRAN);
-        mPhone.setSignalStrengthReportingCriteria(
+                AccessNetworkType.UTRAN, true);
+        mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSSI,
                 config.getIntArray(CarrierConfigManager.KEY_GSM_RSSI_THRESHOLDS_INT_ARRAY),
-                AccessNetworkType.GERAN);
+                AccessNetworkType.GERAN, true);
+
+        if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
+            int measurementEnabled = config.getInt(CarrierConfigManager
+                    .KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT, CellSignalStrengthNr.USE_SSRSRP);
+            mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_SSRSRP,
+                    config.getIntArray(CarrierConfigManager.KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY),
+                    AccessNetworkType.NGRAN,
+                    (measurementEnabled & CellSignalStrengthNr.USE_SSRSRP) != 0);
+            mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_SSRSRQ,
+                    config.getIntArray(CarrierConfigManager.KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY),
+                    AccessNetworkType.NGRAN,
+                    (measurementEnabled & CellSignalStrengthNr.USE_SSRSRQ) != 0);
+            mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_SSSINR,
+                    config.getIntArray(CarrierConfigManager.KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY),
+                    AccessNetworkType.NGRAN,
+                    (measurementEnabled & CellSignalStrengthNr.USE_SSSINR) != 0);
+        }
     }
 
     private void updateServiceStateLteEarfcnBoost(ServiceState serviceState, int lteEarfcn) {
diff --git a/src/java/com/android/internal/telephony/TimeServiceHelperImpl.java b/src/java/com/android/internal/telephony/TimeServiceHelperImpl.java
index 5064d50..e5ffdad 100644
--- a/src/java/com/android/internal/telephony/TimeServiceHelperImpl.java
+++ b/src/java/com/android/internal/telephony/TimeServiceHelperImpl.java
@@ -21,11 +21,9 @@
 import android.app.timedetector.TimeDetector;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.Intent;
 import android.database.ContentObserver;
 import android.os.Handler;
 import android.os.SystemProperties;
-import android.os.UserHandle;
 import android.provider.Settings;
 
 /**
@@ -95,10 +93,6 @@
     public void setDeviceTimeZone(String zoneId) {
         AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         alarmManager.setTimeZone(zoneId);
-        Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
-        intent.putExtra("time-zone", zoneId);
-        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/cat/CatService.java b/src/java/com/android/internal/telephony/cat/CatService.java
index f149c6f..50ceaac 100644
--- a/src/java/com/android/internal/telephony/cat/CatService.java
+++ b/src/java/com/android/internal/telephony/cat/CatService.java
@@ -21,6 +21,7 @@
 import static com.android.internal.telephony.cat.CatCmdMessage.SetupEventListConstants.USER_ACTIVITY_EVENT;
 
 import android.annotation.UnsupportedAppUsage;
+import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.IActivityManager;
 import android.app.backup.BackupManager;
@@ -1182,8 +1183,7 @@
         for (int i = 0; i < defaultLocaleList.size(); i++) {
             locales[i+1] = defaultLocaleList.get(i);
         }
-        config.setLocales(new LocaleList(locales));
-        config.userSetLocale = true;
+        mContext.getSystemService(ActivityManager.class).setDeviceLocales(new LocaleList(locales));
         am.updatePersistentConfiguration(config);
         BackupManager.dataChanged("com.android.providers.settings");
     }
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index 822d249..cb4853e 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -82,7 +82,6 @@
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.nano.TelephonyProto.RilDataCall;
 import com.android.internal.util.AsyncChannel;
-import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Protocol;
 import com.android.internal.util.State;
@@ -709,8 +708,7 @@
             }
 
             linkProperties = dc.getLinkProperties();
-            if (linkProperties == null || CollectionUtils.isEmpty(
-                    linkProperties.getLinkAddresses())) {
+            if (linkProperties == null || linkProperties.getLinkAddresses().isEmpty()) {
                 loge("connect: Can't find link properties of handover data connection. dc="
                         + dc);
                 return DataFailCause.HANDOVER_FAILED;
@@ -1612,9 +1610,7 @@
                     AsyncResult ar = (AsyncResult)msg.obj;
                     Pair<Integer, Integer> drsRatPair = (Pair<Integer, Integer>)ar.result;
                     mDataRegState = drsRatPair.first;
-                    if (mRilRat != drsRatPair.second) {
-                        updateTcpBufferSizes(drsRatPair.second);
-                    }
+                    updateTcpBufferSizes(drsRatPair.second);
                     mRilRat = drsRatPair.second;
                     if (DBG) {
                         log("DcDefaultState: EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED"
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
index 874bd51..8b1bd33 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccConnector.java
@@ -56,6 +56,7 @@
 import android.service.euicc.IRetainSubscriptionsForFactoryResetCallback;
 import android.service.euicc.ISwitchToSubscriptionCallback;
 import android.service.euicc.IUpdateSubscriptionNicknameCallback;
+import android.telephony.PackageChangeReceiver;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.UiccCardInfo;
@@ -68,7 +69,6 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.content.PackageMonitor;
 import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.internal.util.IState;
 import com.android.internal.util.State;
@@ -327,7 +327,7 @@
     private TelephonyManager mTm;
     private SubscriptionManager mSm;
 
-    private final PackageMonitor mPackageMonitor = new EuiccPackageMonitor();
+    private final PackageChangeReceiver mPackageMonitor = new EuiccPackageMonitor();
     private final BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -393,7 +393,7 @@
         mSelectedComponent = findBestComponent();
         setInitialState(mSelectedComponent != null ? mAvailableState : mUnavailableState);
 
-        mPackageMonitor.register(mContext, null /* thread */, false /* externalStorage */);
+        mPackageMonitor.register(mContext, null /* thread */, null /* user */);
         mContext.registerReceiver(
                 mUserUnlockedReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
 
@@ -1124,19 +1124,19 @@
         sendMessage(CMD_SERVICE_DISCONNECTED);
     }
 
-    private class EuiccPackageMonitor extends PackageMonitor {
+    private class EuiccPackageMonitor extends PackageChangeReceiver {
         @Override
-        public void onPackageAdded(String packageName, int reason) {
+        public void onPackageAdded(String packageName) {
             sendPackageChange(packageName, true /* forceUnbindForThisPackage */);
         }
 
         @Override
-        public void onPackageRemoved(String packageName, int reason) {
+        public void onPackageRemoved(String packageName) {
             sendPackageChange(packageName, true /* forceUnbindForThisPackage */);
         }
 
         @Override
-        public void onPackageUpdateFinished(String packageName, int uid) {
+        public void onPackageUpdateFinished(String packageName) {
             sendPackageChange(packageName, true /* forceUnbindForThisPackage */);
         }
 
@@ -1146,13 +1146,12 @@
         }
 
         @Override
-        public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
+        public void onHandleForceStop(String[] packages, boolean doit) {
             if (doit) {
                 for (String packageName : packages) {
                     sendPackageChange(packageName, true /* forceUnbindForThisPackage */);
                 }
             }
-            return super.onHandleForceStop(intent, packages, uid, doit);
         }
 
         private void sendPackageChange(String packageName, boolean forceUnbindForThisPackage) {
diff --git a/src/java/com/android/internal/telephony/ims/ImsResolver.java b/src/java/com/android/internal/telephony/ims/ImsResolver.java
index cee0c8a..9131a0e 100644
--- a/src/java/com/android/internal/telephony/ims/ImsResolver.java
+++ b/src/java/com/android/internal/telephony/ims/ImsResolver.java
@@ -17,6 +17,7 @@
 package com.android.internal.telephony.ims;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -44,6 +45,7 @@
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsFeatureConfiguration;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.LocalLog;
 import android.util.Log;
 import android.util.SparseArray;
@@ -56,20 +58,23 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.LinkedBlockingQueue;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 /**
  * Creates a list of ImsServices that are available to bind to based on the Device configuration
- * overlay value "config_ims_package" and Carrier Configuration value
- * "config_ims_package_override_string".
- * These ImsServices are then bound to in the following order:
+ * overlay values "config_ims_rcs_package" and "config_ims_mmtel_package" as well as Carrier
+ * Configuration value "config_ims_rcs_package_override_string" and
+ * "config_ims_mmtel_package_override_string".
+ * These ImsServices are then bound to in the following order for each mmtel and rcs feature:
  *
  * 1. Carrier Config defined override value per SIM.
  * 2. Device overlay default value (including no SIM case).
@@ -82,9 +87,12 @@
 
     private static final String TAG = "ImsResolver";
 
+    @VisibleForTesting
     public static final String METADATA_EMERGENCY_MMTEL_FEATURE =
             "android.telephony.ims.EMERGENCY_MMTEL_FEATURE";
+    @VisibleForTesting
     public static final String METADATA_MMTEL_FEATURE = "android.telephony.ims.MMTEL_FEATURE";
+    @VisibleForTesting
     public static final String METADATA_RCS_FEATURE = "android.telephony.ims.RCS_FEATURE";
     // Overrides the sanity permission check of android.permission.BIND_IMS_SERVICE for any
     // ImsService that is connecting to the platform.
@@ -99,7 +107,7 @@
     private static final int HANDLER_CONFIG_CHANGED = 2;
     // A query has been started for an ImsService to relay the features they support.
     private static final int HANDLER_START_DYNAMIC_FEATURE_QUERY = 3;
-    // A query to request ImsService features has completed or the ImsService has updated features.
+    // A dynamic query to request ImsService features has completed.
     private static final int HANDLER_DYNAMIC_FEATURE_CHANGE = 4;
     // Testing: Overrides the current configuration for ImsService binding
     private static final int HANDLER_OVERRIDE_IMS_SERVICE_CONFIG = 5;
@@ -144,7 +152,7 @@
         }
 
         @VisibleForTesting
-        public HashSet<ImsFeatureConfiguration.FeatureSlotPair> getSupportedFeatures() {
+        public Set<ImsFeatureConfiguration.FeatureSlotPair> getSupportedFeatures() {
             return mSupportedFeatures;
         }
 
@@ -359,21 +367,6 @@
                 }
             };
 
-    private ImsServiceControllerFactory mImsServiceControllerFactoryStaticBindingCompat =
-            new ImsServiceControllerFactory() {
-                @Override
-                public String getServiceInterface() {
-                    // The static method of binding does not use service interfaces.
-                    return null;
-                }
-
-                @Override
-                public ImsServiceController create(Context context, ComponentName componentName,
-                        ImsServiceController.ImsServiceControllerCallbacks callbacks) {
-                    return new ImsServiceControllerStaticCompat(context, componentName, callbacks);
-                }
-            };
-
     private ImsDynamicQueryManagerFactory mDynamicQueryManagerFactory =
             ImsServiceFeatureQueryManager::new;
 
@@ -388,13 +381,13 @@
     // ImsServiceController callbacks.
     private final Object mBoundServicesLock = new Object();
     private final int mNumSlots;
-    private final boolean mIsDynamicBinding;
-    // Package name of the default device service.
-    private String mDeviceService;
+    private SparseArray<Map<Integer, String>> mCarrierServices;
+    // Package name of the default device services, Maps ImsFeature -> packageName.
+    private Map<Integer, String> mDeviceServices;
     // Persistent Logging
     private final LocalLog mEventLog = new LocalLog(50);
 
-    // Synchronize all messages on a handler to ensure that the cache includes the most recent
+    // Synchronize all events on a handler to ensure that the cache includes the most recent
     // version of the installed ImsServices.
     private Handler mHandler = new Handler(Looper.getMainLooper(), (msg) -> {
         switch (msg.what) {
@@ -411,7 +404,7 @@
             case HANDLER_BOOT_COMPLETE: {
                 mEventLog.log("handling BOOT_COMPLETE");
                 // Re-evaluate bound services for all slots after requerying packagemanager
-                maybeAddedImsService(null);
+                maybeAddedImsService(null /*packageName*/);
                 break;
             }
             case HANDLER_CONFIG_CHANGED: {
@@ -438,33 +431,7 @@
                 // arg2 will be equal to 1 if it is a carrier service.
                 boolean isCarrierImsService = (msg.arg2 == 1);
                 String packageName = (String) msg.obj;
-                if (isCarrierImsService) {
-                    Log.i(TAG, "overriding carrier ImsService - slot=" + slotId + " packageName="
-                            + packageName);
-                    overrideService(slotId, packageName);
-                } else {
-                    Log.i(TAG, "overriding device ImsService -  packageName=" + packageName);
-                    mEventLog.log("overriding device ImsService with " + packageName);
-                    if (TextUtils.equals(mDeviceService, packageName)) {
-                        // No change in device service.
-                        break;
-                    }
-                    // Unbind from the previous ImsService before binding to the new one.
-                    unbindImsService(getImsServiceInfoFromCache(mDeviceService));
-                    mDeviceService = packageName;
-                    ImsServiceInfo deviceInfo = getImsServiceInfoFromCache(mDeviceService);
-                    if (deviceInfo == null) {
-                        // The package name is either "" or does not exist on the device.
-                        break;
-                    }
-                    if (deviceInfo.featureFromMetadata) {
-                        bindImsService(deviceInfo);
-                    } else {
-                        // newly added ImsServiceInfo that has not had features queried yet. Start
-                        // async bind and query features.
-                        scheduleQueryForFeatures(deviceInfo);
-                    }
-                }
+                overrideService(isCarrierImsService, slotId, packageName);
                 break;
             }
             default:
@@ -503,51 +470,44 @@
     // Used during testing, overrides the carrier services while non-empty.
     // Array index corresponds to slot Id associated with the service package name.
     private String[] mOverrideServices;
-    // Array index corresponds to slot Id associated with the service package name.
-    private String[] mCarrierServices;
     // List index corresponds to Slot Id, Maps ImsFeature.FEATURE->bound ImsServiceController
     // Locked on mBoundServicesLock
     private List<SparseArray<ImsServiceController>> mBoundImsServicesByFeature;
     // not locked, only accessed on a handler thread.
+    // Tracks list of all installed ImsServices
     private Map<ComponentName, ImsServiceInfo> mInstalledServicesCache = new HashMap<>();
     // not locked, only accessed on a handler thread.
+    // Active ImsServiceControllers, which are bound to ImsServices.
     private Map<ComponentName, ImsServiceController> mActiveControllers = new HashMap<>();
-    // Only used as the Component name for legacy ImsServices that did not use dynamic binding.
-    private final ComponentName mStaticComponent;
     private ImsServiceFeatureQueryManager mFeatureQueryManager;
 
-    public ImsResolver(Context context, String defaultImsPackageName, int numSlots,
-            boolean isDynamicBinding) {
+    public ImsResolver(Context context, String defaultImsPackageName, int numSlots) {
         mContext = context;
         mReceiverContext = context.createContextAsUser(UserHandle.ALL, 0 /*flags*/);
-        mDeviceService = defaultImsPackageName;
+
+        mCarrierServices = new SparseArray<>(numSlots);
+        mDeviceServices = new ArrayMap<>();
+        // TODO: create separate device configurations for MMTEL and RCS
+        setDeviceConfiguration(defaultImsPackageName, ImsFeature.FEATURE_EMERGENCY_MMTEL);
+        setDeviceConfiguration(defaultImsPackageName, ImsFeature.FEATURE_MMTEL);
+        setDeviceConfiguration(defaultImsPackageName, ImsFeature.FEATURE_RCS);
         mNumSlots = numSlots;
-        mIsDynamicBinding = isDynamicBinding;
-        mStaticComponent = new ComponentName(mContext, ImsResolver.class);
-        if (!mIsDynamicBinding) {
-            Log.i(TAG, "ImsResolver initialized with static binding.");
-            mDeviceService = mStaticComponent.getPackageName();
-        }
         mCarrierConfigManager = (CarrierConfigManager) mContext.getSystemService(
                 Context.CARRIER_CONFIG_SERVICE);
-        mCarrierServices = new String[numSlots];
         mOverrideServices = new String[numSlots];
         mBoundImsServicesByFeature = Stream.generate(SparseArray<ImsServiceController>::new)
                 .limit(mNumSlots).collect(Collectors.toList());
 
-        // Only register for Package/CarrierConfig updates if dynamic binding.
-        if(mIsDynamicBinding) {
-            IntentFilter appChangedFilter = new IntentFilter();
-            appChangedFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-            appChangedFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-            appChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
-            appChangedFilter.addDataScheme("package");
-            mReceiverContext.registerReceiver(mAppChangedReceiver, appChangedFilter);
-            mReceiverContext.registerReceiver(mConfigChangedReceiver, new IntentFilter(
-                    CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
-            mReceiverContext.registerReceiver(mBootCompleted, new IntentFilter(
-                    Intent.ACTION_BOOT_COMPLETED));
-        }
+        IntentFilter appChangedFilter = new IntentFilter();
+        appChangedFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        appChangedFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        appChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        appChangedFilter.addDataScheme("package");
+        mReceiverContext.registerReceiver(mAppChangedReceiver, appChangedFilter);
+        mReceiverContext.registerReceiver(mConfigChangedReceiver, new IntentFilter(
+                CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+        mReceiverContext.registerReceiver(mBootCompleted, new IntentFilter(
+                Intent.ACTION_BOOT_COMPLETED));
     }
 
     @VisibleForTesting
@@ -593,20 +553,34 @@
         // Update the package names of the carrier ImsServices if they do not exist already and
         // possibly bind if carrier configs exist. Otherwise wait for CarrierConfigChanged
         // indication.
-        for (int i = 0; i < mNumSlots; i++) {
-            int subId = mSubscriptionManagerProxy.getSubId(i);
-            PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId);
-            if (config != null && mCarrierServices[i] == null) {
-                String newPackageName = config.getString(
-                        CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null);
+        bindCarrierServicesIfAvailable();
+    }
+
+    // Only start the bind if there is an existing Carrier Configuration. Otherwise, wait for
+    // carrier config changed.
+    private void bindCarrierServicesIfAvailable() {
+        boolean hasConfigChanged = false;
+        for (int slotId = 0; slotId < mNumSlots; slotId++) {
+            Map<Integer, String> featureMap = getImsPackageOverrideConfig(slotId);
+            for (int f = ImsFeature.FEATURE_EMERGENCY_MMTEL; f < ImsFeature.FEATURE_MAX; f++) {
+                String newPackageName = featureMap.getOrDefault(f, "");
                 if (!TextUtils.isEmpty(newPackageName)) {
-                    mEventLog.log("Initializing: found carrier package.");
-                    updateBoundCarrierServices(i, newPackageName);
-                    Log.i(TAG, "Initializing, found package " + newPackageName + " on slot "
-                            + i);
+                    mEventLog.log("bindCarrierServicesIfAvailable - carrier package found: "
+                            + newPackageName + " on slot " + slotId);
+                    setCarrierConfiguration(newPackageName, slotId, f);
+                    ImsServiceInfo info = getImsServiceInfoFromCache(newPackageName);
+                    // We do not want to trigger feature configuration changes unless there is
+                    // already a valid carrier config change.
+                    if (info != null && info.featureFromMetadata) {
+                        hasConfigChanged = true;
+                    } else {
+                        // Config will change when this query completes
+                        scheduleQueryForFeatures(info);
+                    }
                 }
             }
         }
+        if (hasConfigChanged) calculateFeatureConfigurationChange();
     }
 
     /**
@@ -754,14 +728,73 @@
         return true;
     }
 
-    // used for testing only.
-    public String getImsServiceConfiguration(int slotId, boolean isCarrierService) {
+    // not synchronized, access through handler ONLY.
+    private String getDeviceConfiguration(@ImsFeature.FeatureType int featureType) {
+        return mDeviceServices.getOrDefault(featureType, "");
+    }
+
+    // not synchronized, access in handler ONLY.
+    private void setDeviceConfiguration(String name, @ImsFeature.FeatureType int featureType) {
+        mDeviceServices.put(featureType, name);
+    }
+
+    // not synchronized, access in handler ONLY.
+    private void setCarrierConfiguration(@NonNull String packageName, int slotId,
+            @ImsFeature.FeatureType int featureType) {
+        getCarrierConfigurations(slotId).put(featureType, packageName);
+    }
+
+    // not synchronized, access in handler ONLY.
+    private @NonNull String getCarrierConfiguration(int slotId,
+            @ImsFeature.FeatureType int featureType) {
+        return getCarrierConfigurations(slotId).getOrDefault(featureType, "");
+    }
+
+    // not synchronized, access in handler ONLY.
+    private @NonNull Map<Integer, String> getCarrierConfigurations(int slotId) {
+        Map<Integer, String> carrierConfig = mCarrierServices.get(slotId);
+        if (carrierConfig == null) {
+            carrierConfig = new ArrayMap<>();
+            mCarrierServices.put(slotId, carrierConfig);
+        }
+        return carrierConfig;
+    }
+
+    /**
+     * @return true if there is a carrier configuration that exists for the slot & featureType pair
+     * and the cached carrier ImsService associated with the configuration also supports the
+     * requested ImsFeature type.
+     */
+    // not synchronized, access in handler ONLY.
+    private boolean doesCarrierConfigurationExist(int slotId,
+            @ImsFeature.FeatureType int featureType) {
+        String carrierPackage = getCarrierConfiguration(slotId, featureType);
+        if (TextUtils.isEmpty(carrierPackage)) {
+            return false;
+        }
+        // Config exists, but the carrier ImsService also needs to support this feature
+        ImsServiceInfo info = getImsServiceInfoFromCache(carrierPackage);
+        return info != null && info.getSupportedFeatures().stream().anyMatch(
+                feature -> feature.slotId == slotId && feature.featureType == featureType);
+    }
+
+    /**
+     * @return the package name of the ImsService with the requested configuration.
+     */
+    // used in shell commands queries during testing only.
+    public String getImsServiceConfiguration(int slotId, boolean isCarrierService,
+            @ImsFeature.FeatureType int featureType) {
         if (slotId < 0 || slotId >= mNumSlots) {
             Log.w(TAG, "getImsServiceConfiguration: invalid slotId!");
             return "";
         }
 
-        return isCarrierService ? mCarrierServices[slotId] : mDeviceService;
+        LinkedBlockingQueue<String> result = new LinkedBlockingQueue<>(1);
+        // access the configuration on the handler.
+        mHandler.post(() -> result.offer(isCarrierService
+                ? getCarrierConfiguration(slotId, featureType) :
+                getDeviceConfiguration(featureType)));
+        return result.poll();
     }
 
     private void putImsController(int slotId, int feature, ImsServiceController controller) {
@@ -817,7 +850,9 @@
     private void maybeAddedImsService(String packageName) {
         Log.d(TAG, "maybeAddedImsService, packageName: " + packageName);
         List<ImsServiceInfo> infos = getImsServiceInfo(packageName);
-        List<ImsServiceInfo> newlyAddedInfos = new ArrayList<>();
+        // Wait until all ImsServiceInfo is cached before calling
+        // calculateFeatureConfigurationChange to reduce churn.
+        boolean requiresCalculation = false;
         for (ImsServiceInfo info : infos) {
             // Checking to see if the ComponentName is the same, so we can update the supported
             // features. Will only be one (if it exists), since it is a set.
@@ -835,7 +870,7 @@
                             + info);
                     // update features in the cache
                     match.replaceFeatures(info.getSupportedFeatures());
-                    updateImsServiceFeatures(info);
+                    requiresCalculation = true;
                 } else {
                     mEventLog.log("maybeAddedImsService - scheduling query for " + info);
                     // start a query to get ImsService features
@@ -846,7 +881,7 @@
                 mEventLog.log("maybeAddedImsService - adding new ImsService: " + info);
                 mInstalledServicesCache.put(info.name, info);
                 if (info.featureFromMetadata) {
-                    newlyAddedInfos.add(info);
+                    requiresCalculation = true;
                 } else {
                     // newly added ImsServiceInfo that has not had features queried yet. Start async
                     // bind and query features.
@@ -854,20 +889,7 @@
                 }
             }
         }
-        // Loop through the newly created ServiceInfos in a separate loop to make sure the cache
-        // is fully updated.
-        for (ImsServiceInfo info : newlyAddedInfos) {
-            if (isActiveCarrierService(info)) {
-                // New ImsService is registered to active carrier services and must be newly
-                // bound.
-                bindImsService(info);
-                // Update existing device service features
-                updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
-            } else if (isDeviceService(info)) {
-                // New ImsService is registered as device default and must be newly bound.
-                bindImsService(info);
-            }
-        }
+        if (requiresCalculation) calculateFeatureConfigurationChange();
     }
 
     // Remove the ImsService from the cache. This may have been due to the ImsService being removed
@@ -880,35 +902,27 @@
             mEventLog.log("maybeRemovedImsService - removing ImsService: " + match);
             Log.i(TAG, "Removing ImsService: " + match.name);
             unbindImsService(match);
-            updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
+            calculateFeatureConfigurationChange();
             return true;
         }
         return false;
     }
 
-    // Returns true if the CarrierConfig that has been loaded includes this ImsServiceInfo
-    // package name.
-    // Called from Handler ONLY
-    private boolean isActiveCarrierService(ImsServiceInfo info) {
-        for (int i = 0; i < mNumSlots; i++) {
-            if (TextUtils.equals(mCarrierServices[i], info.name.getPackageName())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     private boolean isDeviceService(ImsServiceInfo info) {
-        return TextUtils.equals(mDeviceService, info.name.getPackageName());
+        if (info == null) return false;
+        return mDeviceServices.containsValue(info.name.getPackageName());
     }
 
-    private int getSlotForActiveCarrierService(ImsServiceInfo info) {
+    private List<Integer> getSlotsForActiveCarrierService(ImsServiceInfo info) {
+        if (info == null) return Collections.emptyList();
+        List<Integer> slots = new ArrayList<>(mNumSlots);
         for (int i = 0; i < mNumSlots; i++) {
-            if (TextUtils.equals(mCarrierServices[i], info.name.getPackageName())) {
-                return i;
+            if (!TextUtils.isEmpty(getCarrierConfigurations(i).values().stream()
+                    .filter(e -> e.equals(info.name.getPackageName())).findAny().orElse(""))) {
+                slots.add(i);
             }
         }
-        return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+        return slots;
     }
 
     private ImsServiceController getControllerByServiceInfo(
@@ -930,58 +944,8 @@
         return searchMap.get(matchValue);
     }
 
-    // Creates new features in active ImsServices and removes obsolete cached features. If
-    // cachedInfo == null, then newInfo is assumed to be a new ImsService and will have all features
-    // created.
-    private void updateImsServiceFeatures(ImsServiceInfo newInfo) {
-        if (newInfo == null) {
-            return;
-        }
-        ImsServiceController controller = getControllerByServiceInfo(mActiveControllers, newInfo);
-        // Will return zero if these features are overridden or it should not currently have any
-        // features because it is not carrier/device.
-        HashSet<ImsFeatureConfiguration.FeatureSlotPair> features =
-                calculateFeaturesToCreate(newInfo);
-        if (shouldFeaturesCauseBind(features)) {
-            try {
-                if (controller != null) {
-                    Log.i(TAG, "Updating features for ImsService: "
-                            + controller.getComponentName());
-                    Log.d(TAG, "Updating Features - New Features: " + features);
-                    controller.changeImsServiceFeatures(features);
-                } else {
-                    Log.i(TAG, "updateImsServiceFeatures: unbound with active features, binding");
-                    bindImsServiceWithFeatures(newInfo, features);
-                }
-                // If the carrier service features have changed, the device features will also
-                // need to be recalculated.
-                if (isActiveCarrierService(newInfo)
-                        // Prevent infinite recursion from bad behavior
-                        && !TextUtils.equals(newInfo.name.getPackageName(), mDeviceService)) {
-                    Log.i(TAG, "Updating device default");
-                    updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
-                }
-            } catch (RemoteException e) {
-                Log.e(TAG, "updateImsServiceFeatures: Remote Exception: " + e.getMessage());
-            }
-        // Don't stay bound if the ImsService is providing no features.
-        } else if (controller != null) {
-            Log.i(TAG, "Unbinding: features = 0 for ImsService: " + controller.getComponentName());
-            unbindImsService(newInfo);
-        }
-    }
-
-    // Bind to an ImsService and wait for the service to be connected to create ImsFeatures.
-    private void bindImsService(ImsServiceInfo info) {
-        if (info == null) {
-            return;
-        }
-        HashSet<ImsFeatureConfiguration.FeatureSlotPair> features = calculateFeaturesToCreate(info);
-        bindImsServiceWithFeatures(info, features);
-    }
-
     private void bindImsServiceWithFeatures(ImsServiceInfo info,
-            HashSet<ImsFeatureConfiguration.FeatureSlotPair> features) {
+            Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
         // Only bind if there are features that will be created by the service.
         if (shouldFeaturesCauseBind(features)) {
             // Check to see if an active controller already exists
@@ -1033,36 +997,27 @@
     private HashSet<ImsFeatureConfiguration.FeatureSlotPair> calculateFeaturesToCreate(
             ImsServiceInfo info) {
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> imsFeaturesBySlot = new HashSet<>();
-        // Check if the info is a carrier service
-        int slotId = getSlotForActiveCarrierService(info);
-        if (slotId != SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+        List<Integer> slots = getSlotsForActiveCarrierService(info);
+        if (!slots.isEmpty()) {
+            // There is an active carrier config associated with this. Return with the ImsService's
+            // supported features that are also within the carrier configuration
             imsFeaturesBySlot.addAll(info.getSupportedFeatures().stream()
-                    // Match slotId with feature slotId.
-                    .filter(feature -> slotId == feature.slotId)
+                    .filter(feature -> info.name.getPackageName().equals(
+                            getCarrierConfiguration(feature.slotId, feature.featureType)))
                     .collect(Collectors.toList()));
-        } else if (isDeviceService(info)) {
-            // For all slots that are not currently using a carrier ImsService, enable all features
-            // for the device default.
-            for (int i = 0; i < mNumSlots; i++) {
-                final int currSlotId = i;
-                ImsServiceInfo carrierImsInfo = getImsServiceInfoFromCache(mCarrierServices[i]);
-                if (carrierImsInfo == null) {
-                    // No Carrier override, add all features for this slot
-                    imsFeaturesBySlot.addAll(info.getSupportedFeatures().stream()
-                            .filter(feature -> currSlotId == feature.slotId)
-                            .collect(Collectors.toList()));
-                } else {
-                    // Add all features to the device service that are not currently covered by
-                    // the carrier ImsService.
-                    HashSet<ImsFeatureConfiguration.FeatureSlotPair> deviceFeatures =
-                            new HashSet<>(info.getSupportedFeatures());
-                    deviceFeatures.removeAll(carrierImsInfo.getSupportedFeatures());
-                    // only add features for current slot
-                    imsFeaturesBySlot.addAll(deviceFeatures.stream()
-                            .filter(feature -> currSlotId == feature.slotId).collect(
-                            Collectors.toList()));
-                }
-            }
+            return imsFeaturesBySlot;
+        }
+        if (isDeviceService(info)) {
+            imsFeaturesBySlot.addAll(info.getSupportedFeatures().stream()
+                    // only allow supported features that are also set for this package as the
+                    // device configuration.
+                    .filter(feature -> info.name.getPackageName().equals(
+                            getDeviceConfiguration(feature.featureType)))
+                    // filter out any separate carrier configuration, since that feature is handled
+                    // by the carrier ImsService.
+                    .filter(feature -> !doesCarrierConfigurationExist(feature.slotId,
+                            feature.featureType))
+                    .collect(Collectors.toList()));
         }
         return imsFeaturesBySlot;
     }
@@ -1118,8 +1073,7 @@
      * @return true if MMTEL or RCS features are present, false if they are not or only
      * EMERGENCY_MMTEL is specified.
      */
-    private boolean shouldFeaturesCauseBind(
-            HashSet<ImsFeatureConfiguration.FeatureSlotPair> features) {
+    private boolean shouldFeaturesCauseBind(Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
         long bindableFeatures = features.stream()
                 // remove all emergency features
                 .filter(f -> f.featureType != ImsFeature.FEATURE_EMERGENCY_MMTEL).count();
@@ -1128,20 +1082,26 @@
 
     // Possibly rebind to another ImsService for testing.
     // Called from the handler ONLY
-    private void overrideService(int slotId, String newPackageName) {
+    private void overrideService(boolean isCarrierService, int slotId, String newPackageName) {
         mEventLog.log("overriding carrier ImsService to " + newPackageName
                 + " on slot " + slotId);
-        if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
-            // not specified, replace package on all slots.
-            for (int i = 0; i < mNumSlots; i++) {
-                mOverrideServices[i] = newPackageName;
-                updateBoundCarrierServices(i, newPackageName);
+        Map<Integer, String> overrideMap = new ArrayMap<>();
+        overrideMap.put(ImsFeature.FEATURE_MMTEL, newPackageName);
+        overrideMap.put(ImsFeature.FEATURE_RCS, newPackageName);
+        if (isCarrierService) {
+            if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+                // not specified, replace package on all slots.
+                for (int i = 0; i < mNumSlots; i++) {
+                    mOverrideServices[i] = newPackageName;
+                    updateBoundServices(i, overrideMap);
+                }
+            } else {
+                mOverrideServices[slotId] = newPackageName;
+                updateBoundServices(slotId, overrideMap);
             }
         } else {
-            mOverrideServices[slotId] = newPackageName;
-            updateBoundCarrierServices(slotId, newPackageName);
+            overrideBoundDeviceServices(overrideMap);
         }
-
     }
 
     // Called from handler ONLY.
@@ -1149,62 +1109,79 @@
         if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
             // not specified, update carrier override cache and possibly rebind on all slots.
             for (int i = 0; i < mNumSlots; i++) {
-                updateBoundCarrierServices(i, getImsPackageOverrideConfig(i));
+                updateBoundServices(i, getImsPackageOverrideConfig(i));
             }
         }
-        updateBoundCarrierServices(slotId, getImsPackageOverrideConfig(slotId));
+        updateBoundServices(slotId, getImsPackageOverrideConfig(slotId));
     }
 
-    private String getImsPackageOverrideConfig(int slotId) {
-        int subId = mSubscriptionManagerProxy.getSubId(slotId);
-        PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId);
-        if (config == null) return null;
-        return config.getString(CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null);
-    }
-
-    /**
-     * Use the slotId specified to update the carrier override cache with the new package name.
-     * If it has changed, trigger an unbind from the old service kick off the process to recalculate
-     * features supported on the new service.
-     */
-    private void updateBoundCarrierServices(int slotId, String newPackageName) {
-        if (slotId > SubscriptionManager.INVALID_SIM_SLOT_INDEX && slotId < mNumSlots) {
-            String overridePackageName = mOverrideServices[slotId];
-            String oldPackageName = mCarrierServices[slotId];
+    private void updateBoundServices(int slotId, Map<Integer, String> featureMap) {
+        if (slotId <= SubscriptionManager.INVALID_SIM_SLOT_INDEX || slotId >= mNumSlots) {
+            return;
+        }
+        boolean hasConfigChanged = false;
+        String overridePackageName = mOverrideServices[slotId];
+        for (int f = ImsFeature.FEATURE_EMERGENCY_MMTEL; f < ImsFeature.FEATURE_MAX; f++) {
+            String oldPackageName = getCarrierConfiguration(slotId, f);
+            String newPackageName = featureMap.getOrDefault(f, "");
             if (!TextUtils.isEmpty(overridePackageName)) {
-                // Do not allow carrier config changes to change the override package while it is
-                // in effect.
-                Log.i(TAG, "CarrierConfig change ignored for " + newPackageName + " while override"
-                        + " is in effect for " + overridePackageName);
+                // Do not allow carrier config changes to change the override package while it
+                // is in effect.
+                Log.i(TAG, "CarrierConfig change ignored for " + newPackageName + " while "
+                        + "override is in effect for " + overridePackageName);
                 newPackageName = overridePackageName;
             }
-            mCarrierServices[slotId] = newPackageName;
-            if (!TextUtils.equals(newPackageName, oldPackageName)) {
-                Log.i(TAG, "Carrier Config updated, binding new ImsService");
-                mEventLog.log("updateBoundCarrierServices - carrier package changed: "
-                        + oldPackageName + " -> " + newPackageName + " on slot " + slotId);
-                // Unbind old ImsService, not needed anymore
-                // ImsService is retrieved from the cache. If the cache hasn't been populated yet,
-                // the calls to unbind/bind will fail (intended during initial start up).
-                unbindImsService(getImsServiceInfoFromCache(oldPackageName));
-            }
-            ImsServiceInfo newInfo = getImsServiceInfoFromCache(newPackageName);
-            // if there is no carrier ImsService, newInfo is null. This we still want to update
-            // bindings for device ImsService to pick up the missing features.
-            if (newInfo == null || newInfo.featureFromMetadata) {
-                mEventLog.log("updateBoundCarrierServices - recalculating bindings "
-                        + (newInfo != null ? newInfo : "for device"));
-                bindImsService(newInfo);
-                // Recalculate the device ImsService features to reflect changes.
-                updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
+            mEventLog.log("updateBoundServices - carrier package changed: "
+                    + oldPackageName + " -> " + newPackageName + " on slot " + slotId);
+            setCarrierConfiguration(newPackageName, slotId, f);
+            // Carrier config may have not changed, but we still want to kick off a recalculation
+            // in case there has been a change to the supported device features.
+            ImsServiceInfo info = getImsServiceInfoFromCache(newPackageName);
+            if (info == null || info.featureFromMetadata) {
+                hasConfigChanged = true;
             } else {
-                // ImsServiceInfo that has not had features queried yet. Start async
-                // bind and query features.
-                mEventLog.log("updateBoundCarrierServices - scheduling feature query for "
-                        + newInfo);
-                scheduleQueryForFeatures(newInfo);
+                // Config will change when this query completes
+                scheduleQueryForFeatures(info);
             }
         }
+        if (hasConfigChanged) calculateFeatureConfigurationChange();
+    }
+
+    private @NonNull Map<Integer, String> getImsPackageOverrideConfig(int slotId) {
+        int subId = mSubscriptionManagerProxy.getSubId(slotId);
+        PersistableBundle config = mCarrierConfigManager.getConfigForSubId(subId);
+        if (config == null) return Collections.emptyMap();
+        // TODO: Create carrier configs for all feature types
+        String packageName = config.getString(
+                CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null);
+        Map<Integer, String> result = new ArrayMap<>();
+        if (!TextUtils.isEmpty(packageName)) {
+            result.put(ImsFeature.FEATURE_EMERGENCY_MMTEL, packageName);
+            result.put(ImsFeature.FEATURE_MMTEL, packageName);
+            result.put(ImsFeature.FEATURE_RCS, packageName);
+        }
+        return result;
+    }
+
+    private void overrideBoundDeviceServices(Map<Integer, String> overrideMap) {
+        boolean hasConfigChanged = false;
+        for (int f = ImsFeature.FEATURE_EMERGENCY_MMTEL; f < ImsFeature.FEATURE_MAX; f++) {
+            String oldPackageName = getDeviceConfiguration(f);
+            String overridePackageName = overrideMap.getOrDefault(f, "");
+            if (!TextUtils.equals(oldPackageName, overridePackageName)) {
+                mEventLog.log("overrideBoundDeviceServices - device package changed: "
+                        + oldPackageName + " -> " + overridePackageName);
+                setDeviceConfiguration(overridePackageName, f);
+                ImsServiceInfo info = getImsServiceInfoFromCache(overridePackageName);
+                if (info == null || info.featureFromMetadata) {
+                    hasConfigChanged = true;
+                } else {
+                    // Config will change when this query completes
+                    scheduleQueryForFeatures(info);
+                }
+            }
+        }
+        if (hasConfigChanged) calculateFeatureConfigurationChange();
     }
 
     /**
@@ -1213,8 +1190,7 @@
     private void scheduleQueryForFeatures(ImsServiceInfo service, int delayMs) {
         // if not current device/carrier service, don't perform query. If this changes, this method
         // will be called again.
-        if (!isDeviceService(service) && getSlotForActiveCarrierService(service)
-                == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+        if (!isDeviceService(service) && getSlotsForActiveCarrierService(service).isEmpty()) {
             Log.i(TAG, "scheduleQueryForFeatures: skipping query for ImsService that is not"
                     + " set as carrier/device ImsService.");
             return;
@@ -1274,23 +1250,43 @@
             Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
         ImsServiceInfo service = getImsServiceInfoFromCache(name.getPackageName());
         if (service == null) {
-            Log.w(TAG, "handleFeaturesChanged: Couldn't find cached info for name: "
+            Log.w(TAG, "dynamicQueryComplete: Couldn't find cached info for name: "
                     + name);
             return;
         }
-        mEventLog.log("dynamicQueryComplete for package " + name + ", features: "
+        mEventLog.log("dynamicQueryComplete: for package " + name + ", features: "
                 + printFeatures(service.getSupportedFeatures()) + " -> " + printFeatures(features));
+        sanitizeFeatureConfig(features);
         // Add features to service
         service.replaceFeatures(features);
-        if (isActiveCarrierService(service)) {
-            // New ImsService is registered to active carrier services and must be newly
-            // bound.
-            bindImsService(service);
-            // Update existing device service features
-            updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
-        } else if (isDeviceService(service)) {
-            // New ImsService is registered as device default and must be newly bound.
-            bindImsService(service);
+        calculateFeatureConfigurationChange();
+    }
+
+    /**
+     * Ensure the feature includes MMTEL when it supports EMERGENCY_MMTEL, if not, remove.
+     */
+    private void sanitizeFeatureConfig(Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
+        Set<ImsFeatureConfiguration.FeatureSlotPair> emergencyMmtelFeatures = features.stream()
+                .filter(feature -> feature.featureType == ImsFeature.FEATURE_EMERGENCY_MMTEL)
+                .collect(Collectors.toSet());
+        for (ImsFeatureConfiguration.FeatureSlotPair feature : emergencyMmtelFeatures) {
+            if (!features.contains(new ImsFeatureConfiguration.FeatureSlotPair(feature.slotId,
+                    ImsFeature.FEATURE_MMTEL))) {
+                features.remove(feature);
+            }
+        }
+    }
+
+    // Calculate the new configuration for the bound ImsServices.
+    // Should ONLY be called from the handler.
+    private void calculateFeatureConfigurationChange() {
+        for (ImsServiceInfo info : mInstalledServicesCache.values()) {
+            Set<ImsFeatureConfiguration.FeatureSlotPair> features = calculateFeaturesToCreate(info);
+            if (shouldFeaturesCauseBind(features)) {
+                bindImsServiceWithFeatures(info, features);
+            } else {
+                unbindImsService(info);
+            }
         }
     }
 
@@ -1331,27 +1327,10 @@
     // get all packages that support ImsServices.
     private List<ImsServiceInfo> getImsServiceInfo(String packageName) {
         List<ImsServiceInfo> infos = new ArrayList<>();
-        if (!mIsDynamicBinding) {
-            // always return the same ImsService info.
-            infos.addAll(getStaticImsService());
-        } else {
-            // Search for Current ImsService implementations
-            infos.addAll(searchForImsServices(packageName, mImsServiceControllerFactory));
-            // Search for compat ImsService Implementations
-            infos.addAll(searchForImsServices(packageName, mImsServiceControllerFactoryCompat));
-        }
-        return infos;
-    }
-
-    private List<ImsServiceInfo> getStaticImsService() {
-        List<ImsServiceInfo> infos = new ArrayList<>();
-
-        ImsServiceInfo info = new ImsServiceInfo(mNumSlots);
-        info.name = mStaticComponent;
-        info.controllerFactory = mImsServiceControllerFactoryStaticBindingCompat;
-        info.addFeatureForAllSlots(ImsFeature.FEATURE_EMERGENCY_MMTEL);
-        info.addFeatureForAllSlots(ImsFeature.FEATURE_MMTEL);
-        infos.add(info);
+        // Search for Current ImsService implementations
+        infos.addAll(searchForImsServices(packageName, mImsServiceControllerFactory));
+        // Search for compat ImsService Implementations
+        infos.addAll(searchForImsServices(packageName, mImsServiceControllerFactoryCompat));
         return infos;
     }
 
@@ -1382,12 +1361,13 @@
                 if (isDeviceService(info)
                         || mImsServiceControllerFactoryCompat == controllerFactory) {
                     if (serviceInfo.metaData != null) {
-                        if (serviceInfo.metaData.getBoolean(METADATA_EMERGENCY_MMTEL_FEATURE,
-                                false)) {
-                            info.addFeatureForAllSlots(ImsFeature.FEATURE_EMERGENCY_MMTEL);
-                        }
                         if (serviceInfo.metaData.getBoolean(METADATA_MMTEL_FEATURE, false)) {
                             info.addFeatureForAllSlots(ImsFeature.FEATURE_MMTEL);
+                            // only allow FEATURE_EMERGENCY_MMTEL if FEATURE_MMTEL is defined.
+                            if (serviceInfo.metaData.getBoolean(METADATA_EMERGENCY_MMTEL_FEATURE,
+                                    false)) {
+                                info.addFeatureForAllSlots(ImsFeature.FEATURE_EMERGENCY_MMTEL);
+                            }
                         }
                         if (serviceInfo.metaData.getBoolean(METADATA_RCS_FEATURE, false)) {
                             info.addFeatureForAllSlots(ImsFeature.FEATURE_RCS);
@@ -1428,14 +1408,31 @@
         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
         pw.println("ImsResolver:");
         pw.increaseIndent();
-        pw.println("mIsDynamicBinding = " + mIsDynamicBinding);
-        pw.println("mDeviceService = " + mDeviceService);
-        pw.println("mCarrierServices: ");
+        pw.println("Configurations:");
         pw.increaseIndent();
-        for (String s : mCarrierServices) {
-            pw.println(s);
+        pw.println("Device:");
+        pw.increaseIndent();
+        for (Integer i : mDeviceServices.keySet()) {
+            pw.println(ImsFeature.FEATURE_LOG_MAP.get(i) + " -> " + mDeviceServices.get(i));
         }
         pw.decreaseIndent();
+        pw.println("Carrier: ");
+        pw.increaseIndent();
+        for (int i = 0; i < mNumSlots; i++) {
+            for (int j = 0; j < MmTelFeature.FEATURE_MAX; j++) {
+                pw.print("slot=");
+                pw.print(i);
+                pw.print(", feature=");
+                pw.print(ImsFeature.FEATURE_LOG_MAP.getOrDefault(j, "?"));
+                pw.println(": ");
+                pw.increaseIndent();
+                String name = getCarrierConfiguration(i, j);
+                pw.println(TextUtils.isEmpty(name) ? "none" : name);
+                pw.decreaseIndent();
+            }
+        }
+        pw.decreaseIndent();
+        pw.decreaseIndent();
         pw.println("Bound Features:");
         pw.increaseIndent();
         for (int i = 0; i < mNumSlots; i++) {
@@ -1443,7 +1440,7 @@
                 pw.print("slot=");
                 pw.print(i);
                 pw.print(", feature=");
-                pw.print(MmTelFeature.FEATURE_LOG_MAP.getOrDefault(j, "?"));
+                pw.print(ImsFeature.FEATURE_LOG_MAP.getOrDefault(j, "?"));
                 pw.println(": ");
                 pw.increaseIndent();
                 ImsServiceController c = getImsServiceController(i, j);
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceController.java b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
index c4b7399..967152d 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceController.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
@@ -49,6 +49,7 @@
 import java.util.Iterator;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
 
 /**
  * Manages the Binding lifecycle of one ImsService as well as the relevant ImsFeatures that the
@@ -205,7 +206,7 @@
     private boolean mIsBound = false;
     private boolean mIsBinding = false;
     // Set of a pair of slotId->feature
-    private HashSet<ImsFeatureConfiguration.FeatureSlotPair> mImsFeatures;
+    private Set<ImsFeatureConfiguration.FeatureSlotPair> mImsFeatures;
     // Binder interfaces to the features set in mImsFeatures;
     private HashSet<ImsFeatureContainer> mImsFeatureBinders = new HashSet<>();
     private IImsServiceController mIImsServiceController;
@@ -362,10 +363,11 @@
      * @return {@link true} if the service is in the process of being bound, {@link false} if it
      * has failed.
      */
-    public boolean bind(HashSet<ImsFeatureConfiguration.FeatureSlotPair> imsFeatureSet) {
+    public boolean bind(Set<ImsFeatureConfiguration.FeatureSlotPair> imsFeatureSet) {
         synchronized (mLock) {
             if (!mIsBound && !mIsBinding) {
                 mIsBinding = true;
+                sanitizeFeatureConfig(imsFeatureSet);
                 mImsFeatures = imsFeatureSet;
                 grantPermissionsToService();
                 Intent imsServiceIntent = new Intent(getServiceInterface()).setComponent(
@@ -376,7 +378,7 @@
                 mLocalLog.log("binding " + imsFeatureSet);
                 Log.i(LOG_TAG, "Binding ImsService:" + mComponentName);
                 try {
-                    boolean bindSucceeded = startBindToService(imsServiceIntent,
+                    boolean bindSucceeded = mContext.bindService(imsServiceIntent,
                             mImsServiceConnection, serviceFlags);
                     if (!bindSucceeded) {
                         mLocalLog.log("    binding failed, retrying in "
@@ -401,12 +403,18 @@
     }
 
     /**
-     * Starts the bind to the ImsService. Overridden by subclasses that need to access the service
-     * in a different fashion.
+     * Ensure the feature includes MMTEL when it supports EMERGENCY_MMTEL, if not, remove.
      */
-    protected boolean startBindToService(Intent intent, ImsServiceConnection connection,
-            int flags) {
-        return mContext.bindService(intent, connection, flags);
+    private void sanitizeFeatureConfig(Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
+        Set<ImsFeatureConfiguration.FeatureSlotPair> emergencyMmtelFeatures = features.stream()
+                .filter(feature -> feature.featureType == ImsFeature.FEATURE_EMERGENCY_MMTEL)
+                .collect(Collectors.toSet());
+        for (ImsFeatureConfiguration.FeatureSlotPair feature : emergencyMmtelFeatures) {
+            if (!features.contains(new ImsFeatureConfiguration.FeatureSlotPair(feature.slotId,
+                    ImsFeature.FEATURE_MMTEL))) {
+                features.remove(feature);
+            }
+        }
     }
 
     /**
@@ -436,8 +444,9 @@
      * ImsFeature that is removed, {@link IImsServiceController#removeImsFeature} is called.
      */
     public void changeImsServiceFeatures(
-            HashSet<ImsFeatureConfiguration.FeatureSlotPair> newImsFeatures)
+            Set<ImsFeatureConfiguration.FeatureSlotPair> newImsFeatures)
             throws RemoteException {
+        sanitizeFeatureConfig(newImsFeatures);
         synchronized (mLock) {
             if (mImsFeatures.equals(newImsFeatures)) {
                 return;
@@ -706,12 +715,12 @@
             IInterface f = createImsFeature(featurePair.slotId, featurePair.featureType,
                     c.getCallback());
             addImsFeatureBinder(featurePair.slotId, featurePair.featureType, f);
-            // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
-            mCallbacks.imsServiceFeatureCreated(featurePair.slotId, featurePair.featureType, this);
         } else {
             // Don't update ImsService for emergency MMTEL feature.
             Log.i(LOG_TAG, "supports emergency calling on slot " + featurePair.slotId);
         }
+        // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
+        mCallbacks.imsServiceFeatureCreated(featurePair.slotId, featurePair.featureType, this);
         // Send callback to ImsServiceProxy to change supported ImsFeatures including emergency
         // MMTEL state.
         sendImsFeatureCreatedCallback(featurePair.slotId, featurePair.featureType);
@@ -723,6 +732,8 @@
             Log.w(LOG_TAG, "removeImsServiceFeature called with null values.");
             return;
         }
+        // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
+        mCallbacks.imsServiceFeatureRemoved(featurePair.slotId, featurePair.featureType, this);
         if (featurePair.featureType != ImsFeature.FEATURE_EMERGENCY_MMTEL) {
             ImsFeatureStatusCallback callbackToRemove = mFeatureStatusCallbacks.stream().filter(c ->
                     c.mSlotId == featurePair.slotId && c.mFeatureType == featurePair.featureType)
@@ -732,8 +743,6 @@
                 mFeatureStatusCallbacks.remove(callbackToRemove);
             }
             removeImsFeatureBinder(featurePair.slotId, featurePair.featureType);
-            // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
-            mCallbacks.imsServiceFeatureRemoved(featurePair.slotId, featurePair.featureType, this);
             try {
                 removeImsFeature(featurePair.slotId, featurePair.featureType,
                         (callbackToRemove != null ? callbackToRemove.getCallback() : null));
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java b/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java
index 11cae27..835d780 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java
@@ -171,7 +171,7 @@
         return mServiceController != null;
     }
 
-    protected MmTelInterfaceAdapter getInterface(int slotId, IImsFeatureStatusCallback c)
+    private MmTelInterfaceAdapter getInterface(int slotId, IImsFeatureStatusCallback c)
             throws RemoteException {
         IImsMMTelFeature feature = mServiceController.createMMTelFeature(slotId, c);
         if (feature == null) {
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceControllerStaticCompat.java b/src/java/com/android/internal/telephony/ims/ImsServiceControllerStaticCompat.java
deleted file mode 100644
index bd033db..0000000
--- a/src/java/com/android/internal/telephony/ims/ImsServiceControllerStaticCompat.java
+++ /dev/null
@@ -1,119 +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.internal.telephony.ims;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.util.Log;
-
-import com.android.ims.internal.IImsFeatureStatusCallback;
-import com.android.ims.internal.IImsService;
-
-/**
- * A compat layer for communicating with older devices that still used the ServiceManager to get
- * the ImsService.
- */
-
-public class ImsServiceControllerStaticCompat extends ImsServiceControllerCompat {
-
-    private static final String TAG = "ImsSCStaticCompat";
-
-    private static final String IMS_SERVICE_NAME = "ims";
-
-    private class ImsDeathRecipient implements IBinder.DeathRecipient {
-
-        private ComponentName mComponentName;
-        private ServiceConnection mServiceConnection;
-
-        ImsDeathRecipient(ComponentName name, ServiceConnection conn) {
-            mComponentName = name;
-            mServiceConnection = conn;
-        }
-
-        @Override
-        public void binderDied() {
-            Log.e(TAG, "ImsService(" + mComponentName + ") died. Restarting...");
-            // This is hacky... ImsServiceController uses the traditional service binding procedure,
-            // so we have to emulate it when using a persistent service.
-            mServiceConnection.onBindingDied(mComponentName);
-        }
-    }
-
-    private IImsService mImsServiceCompat = null;
-    private ImsDeathRecipient mImsDeathRecipient = null;
-
-    public ImsServiceControllerStaticCompat(Context context, ComponentName componentName,
-            ImsServiceController.ImsServiceControllerCallbacks callbacks) {
-        super(context, componentName, callbacks);
-    }
-
-    @Override
-    public boolean startBindToService(Intent intent, ImsServiceConnection connection, int flags) {
-        IBinder binder = ServiceManager.checkService(IMS_SERVICE_NAME);
-
-        if (binder == null) {
-            return false;
-        }
-        // This is a little hacky, but we are going to call the onServiceConnected to "pretend" like
-        // bindService has completed here, which will pass the binder to setServiceController and
-        // set up all supporting structures.
-        ComponentName name = new ComponentName(mContext, ImsServiceControllerStaticCompat.class);
-        connection.onServiceConnected(name, binder);
-        try {
-            mImsDeathRecipient = new ImsDeathRecipient(name, connection);
-            binder.linkToDeath(mImsDeathRecipient, 0);
-        } catch (RemoteException e) {
-            // The binder connection is already dead.. signal to the ImsServiceController to retry.
-            mImsDeathRecipient.binderDied();
-            mImsDeathRecipient = null;
-        }
-        return true;
-    }
-
-    @Override
-    protected void setServiceController(IBinder serviceController) {
-        if (serviceController == null) {
-            // The service controller has been set to null, meaning it has been unbound or died.
-            // Unlink if needed.
-            if (mImsServiceCompat != null) {
-                mImsServiceCompat.asBinder().unlinkToDeath(mImsDeathRecipient, 0);
-            }
-            mImsDeathRecipient = null;
-        }
-        mImsServiceCompat = IImsService.Stub.asInterface(serviceController);
-    }
-
-    @Override
-    // used for add/remove features and cleanup in ImsServiceController.
-    protected boolean isServiceControllerAvailable() {
-        return mImsServiceCompat != null;
-    }
-
-    @Override
-    protected MmTelInterfaceAdapter getInterface(int slotId, IImsFeatureStatusCallback c) {
-        if (mImsServiceCompat == null) {
-            Log.w(TAG, "getInterface: IImsService returned null.");
-            return null;
-        }
-        return new ImsServiceInterfaceAdapter(slotId, mImsServiceCompat.asBinder());
-    }
-}
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceInterfaceAdapter.java b/src/java/com/android/internal/telephony/ims/ImsServiceInterfaceAdapter.java
deleted file mode 100644
index f554e6f..0000000
--- a/src/java/com/android/internal/telephony/ims/ImsServiceInterfaceAdapter.java
+++ /dev/null
@@ -1,128 +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.internal.telephony.ims;
-
-import android.app.PendingIntent;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
-import android.telephony.ims.ImsCallProfile;
-import android.telephony.ims.compat.feature.ImsFeature;
-
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsConfig;
-import com.android.ims.internal.IImsEcbm;
-import com.android.ims.internal.IImsMultiEndpoint;
-import com.android.ims.internal.IImsRegistrationListener;
-import com.android.ims.internal.IImsService;
-import com.android.ims.internal.IImsUt;
-
-/**
- * Compatibility layer for IImsService implementations of IMS. Converts "generic" MMTel commands
- * to implementation.
- */
-
-public class ImsServiceInterfaceAdapter extends MmTelInterfaceAdapter {
-
-    private static final int SERVICE_ID = ImsFeature.MMTEL;
-
-    public ImsServiceInterfaceAdapter(int slotId, IBinder binder) {
-        super(slotId, binder);
-    }
-
-    public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
-            throws RemoteException {
-        return getInterface().open(mSlotId, ImsFeature.MMTEL, incomingCallIntent, listener);
-    }
-
-    public void endSession(int sessionId) throws RemoteException {
-        getInterface().close(sessionId);
-    }
-
-    public boolean isConnected(int callSessionType, int callType) throws RemoteException {
-        return getInterface().isConnected(SERVICE_ID, callSessionType, callType);
-    }
-
-    public boolean isOpened() throws RemoteException {
-        return getInterface().isOpened(SERVICE_ID);
-    }
-
-    public int getFeatureState() throws RemoteException {
-        return ImsFeature.STATE_READY;
-    }
-
-    public void addRegistrationListener(IImsRegistrationListener listener) throws RemoteException {
-        getInterface().addRegistrationListener(mSlotId, ImsFeature.MMTEL, listener);
-    }
-
-    public void removeRegistrationListener(IImsRegistrationListener listener)
-            throws RemoteException {
-        // Not Implemented in the old ImsService. If the registration listener becomes invalid, the
-        // ImsService will remove it.
-    }
-
-    public ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType)
-            throws RemoteException {
-        return getInterface().createCallProfile(sessionId, callSessionType, callType);
-    }
-
-    public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile)
-            throws RemoteException {
-        return getInterface().createCallSession(sessionId, profile, null);
-    }
-
-    public IImsCallSession getPendingCallSession(int sessionId, String callId)
-            throws RemoteException {
-        return getInterface().getPendingCallSession(sessionId, callId);
-    }
-
-    public IImsUt getUtInterface() throws RemoteException {
-        return getInterface().getUtInterface(SERVICE_ID);
-    }
-
-    public IImsConfig getConfigInterface() throws RemoteException {
-        return getInterface().getConfigInterface(mSlotId);
-    }
-
-    public void turnOnIms() throws RemoteException {
-        getInterface().turnOnIms(mSlotId);
-    }
-
-    public void turnOffIms() throws RemoteException {
-        getInterface().turnOffIms(mSlotId);
-    }
-
-    public IImsEcbm getEcbmInterface() throws RemoteException {
-        return getInterface().getEcbmInterface(SERVICE_ID);
-    }
-
-    public void setUiTTYMode(int uiTtyMode, Message onComplete) throws RemoteException {
-        getInterface().setUiTTYMode(SERVICE_ID, uiTtyMode, onComplete);
-    }
-
-    public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
-        return getInterface().getMultiEndpointInterface(SERVICE_ID);
-    }
-
-    private IImsService getInterface() throws RemoteException {
-        IImsService feature = IImsService.Stub.asInterface(mBinder);
-        if (feature == null) {
-            throw new RemoteException("Binder not Available");
-        }
-        return feature;
-    }
-}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index 7f2e70d..d1adee5 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -681,8 +681,18 @@
                 if (getRingingCall().getState() != ImsPhoneCall.State.IDLE) {
                     if (DBG) logd("MmiCode 2: accept ringing call");
                     mCT.acceptCall(ImsCallProfile.CALL_TYPE_VOICE);
-                } else {
-                    if (DBG) logd("MmiCode 2: holdActiveCall");
+                } else if (getBackgroundCall().getState() == ImsPhoneCall.State.HOLDING) {
+                    // If there's an active ongoing call as well, hold it and the background one
+                    // should automatically unhold. Otherwise just unhold the background call.
+                    if (getForegroundCall().getState() != ImsPhoneCall.State.IDLE) {
+                        if (DBG) logd("MmiCode 2: switch holding and active");
+                        mCT.holdActiveCall();
+                    } else {
+                        if (DBG) logd("MmiCode 2: unhold held call");
+                        mCT.unholdHeldCall();
+                    }
+                } else if (getForegroundCall().getState() != ImsPhoneCall.State.IDLE) {
+                    if (DBG) logd("MmiCode 2: hold active call");
                     mCT.holdActiveCall();
                 }
             } catch (CallStateException e) {
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index 0fbc3d4..34b4f3b 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -1345,7 +1345,7 @@
     /**
      * Unhold the currently held call.
      */
-    void unholdHeldCall() throws CallStateException {
+    public void unholdHeldCall() throws CallStateException {
         ImsCall imsCall = mBackgroundCall.getImsCall();
         if (mHoldSwitchingState == HoldSwapState.PENDING_SINGLE_CALL_UNHOLD
                 || mHoldSwitchingState == HoldSwapState.SWAPPING_ACTIVE_AND_HELD) {
@@ -3252,6 +3252,14 @@
         @Override
         public void onProvisioningIntChanged(int item, int value) {
             sendConfigChangedIntent(item, Integer.toString(value));
+            if ((mImsManager != null)
+                    && (item == ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED
+                    || item == ImsConfig.ConfigConstants.VLT_SETTING_ENABLED
+                    || item == ImsConfig.ConfigConstants.LVC_SETTING_ENABLED)) {
+                // Update Ims Service state to make sure updated provisioning values take effect
+                // immediately.
+                mImsManager.updateImsServiceConfig(true);
+            }
         }
 
         @Override
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
index 0eba486..28143f4 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
@@ -23,6 +23,7 @@
 import android.os.Message;
 import android.telephony.ImsiEncryptionInfo;
 import android.telephony.NetworkScanRequest;
+import android.telephony.SignalThresholdInfo;
 import android.telephony.data.DataProfile;
 import android.telephony.emergency.EmergencyNumber;
 
@@ -635,8 +636,8 @@
     }
 
     @Override
-    public void setSignalStrengthReportingCriteria(int hysteresisMs, int hysteresisDb,
-            int[] thresholdsDbm, int ran, Message result) {
+    public void setSignalStrengthReportingCriteria(
+            SignalThresholdInfo signalThresholdInfo, int ran, Message result) {
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index c7dbf4e..e1216c2 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -132,9 +132,6 @@
      */
     private boolean mIsLocalVideoCapable = true;
 
-    // Store the current audio codec
-    private int mAudioCodec = ImsStreamMediaProfile.AUDIO_QUALITY_NONE;
-
     //***** Event Constants
     private static final int EVENT_DTMF_DONE = 1;
     private static final int EVENT_PAUSE_DONE = 2;
diff --git a/src/java/com/android/internal/telephony/nitz/NewNitzStateMachineImpl.java b/src/java/com/android/internal/telephony/nitz/NewNitzStateMachineImpl.java
index 27d23e3..ce6d1ce 100644
--- a/src/java/com/android/internal/telephony/nitz/NewNitzStateMachineImpl.java
+++ b/src/java/com/android/internal/telephony/nitz/NewNitzStateMachineImpl.java
@@ -312,20 +312,20 @@
         try {
             Objects.requireNonNull(reason);
 
-            PhoneTimeSuggestion timeSuggestion = new PhoneTimeSuggestion(mPhoneId);
+            PhoneTimeSuggestion.Builder builder = new PhoneTimeSuggestion.Builder(mPhoneId);
             if (nitzSignal == null) {
-                timeSuggestion.addDebugInfo("Clearing time zone suggestion"
+                builder.addDebugInfo("Clearing time zone suggestion"
                         + " reason=" + reason);
             } else {
                 TimestampedValue<Long> newNitzTime = new TimestampedValue<>(
                         nitzSignal.getReferenceTimeMillis(),
                         nitzSignal.getValue().getCurrentTimeInMillis());
-                timeSuggestion.setUtcTime(newNitzTime);
-                timeSuggestion.addDebugInfo("Sending new time zone suggestion"
+                builder.setUtcTime(newNitzTime);
+                builder.addDebugInfo("Sending new time zone suggestion"
                         + " nitzSignal=" + nitzSignal
                         + ", reason=" + reason);
             }
-            mNewTimeServiceHelper.suggestDeviceTime(timeSuggestion);
+            mNewTimeServiceHelper.suggestDeviceTime(builder.build());
         } catch (RuntimeException ex) {
             Rlog.e(LOG_TAG, "doTimeDetection: Exception thrown"
                     + " mPhoneId=" + mPhoneId
diff --git a/src/java/com/android/internal/telephony/nitz/NewTimeServiceHelperImpl.java b/src/java/com/android/internal/telephony/nitz/NewTimeServiceHelperImpl.java
index 36d38f6..bb0c9cf 100644
--- a/src/java/com/android/internal/telephony/nitz/NewTimeServiceHelperImpl.java
+++ b/src/java/com/android/internal/telephony/nitz/NewTimeServiceHelperImpl.java
@@ -42,8 +42,8 @@
     private final TimeDetector mTimeDetector;
     private final TimeZoneDetector mTimeZoneDetector;
 
-    private final LocalLog mTimeZoneLog = new LocalLog(30);
-    private final LocalLog mTimeLog = new LocalLog(30);
+    private final LocalLog mTimeZoneLog = new LocalLog(30, false /* mUseLocalTimestamps */);
+    private final LocalLog mTimeLog = new LocalLog(30, false /* mUseLocalTimestamps */);
 
     /**
      * Records the last time zone suggestion made. Used to avoid sending duplicate suggestions to
diff --git a/src/java/com/android/internal/telephony/sip/SipCommandInterface.java b/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
index d904caf..4762979 100644
--- a/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
+++ b/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
@@ -23,6 +23,7 @@
 import android.os.Message;
 import android.telephony.ImsiEncryptionInfo;
 import android.telephony.NetworkScanRequest;
+import android.telephony.SignalThresholdInfo;
 import android.telephony.data.DataProfile;
 import android.telephony.emergency.EmergencyNumber;
 
@@ -635,8 +636,8 @@
     }
 
     @Override
-    public void setSignalStrengthReportingCriteria(int hysteresisMs, int hysteresisDb,
-            int[] thresholdsDbm, int ran, Message result) {
+    public void setSignalStrengthReportingCriteria(
+            SignalThresholdInfo signalThresholdInfo, int ran, Message result) {
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
index 2585e26..114447f 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
@@ -114,6 +114,8 @@
     // Max number of retries for open logical channel, interval is 10s.
     private static final int MAX_RETRY = 1;
     private static final int RETRY_INTERVAL_MS = 10000;
+    private static final int STATUS_CODE_CONDITION_NOT_SATISFIED = 0x6985;
+    private static final int STATUS_CODE_APPLET_SELECT_FAILED = 0x6999;
 
     // Used for parsing the data from the UICC.
     public static class TLV {
@@ -426,6 +428,30 @@
         return null;
     }
 
+    /**
+     * The following three situations could be due to logical channels temporarily unavailable, so
+     * we retry up to MAX_RETRY times, with an interval of RETRY_INTERVAL_MS: 1. MISSING_RESOURCE,
+     * 2. NO_SUCH_ELEMENT and the status code is 6985, 3. INTERNAL_ERR and the status code is 6999.
+     */
+    public static boolean shouldRetry(AsyncResult ar, int retryCount) {
+        if (ar.exception instanceof CommandException && retryCount < MAX_RETRY) {
+            CommandException.Error error = ((CommandException) (ar.exception)).getCommandError();
+            int[] results = (int[]) ar.result;
+            int statusCode = 0;
+            if (results.length == 3) {
+                byte[] bytes = new byte[]{(byte) results[1], (byte) results[2]};
+                statusCode = Integer.parseInt(IccUtils.bytesToHexString(bytes), 16);
+                log("status code: " + String.valueOf(statusCode));
+            }
+            return (error == CommandException.Error.MISSING_RESOURCE)
+                            || (error == CommandException.Error.NO_SUCH_ELEMENT
+                    && statusCode == STATUS_CODE_CONDITION_NOT_SATISFIED)
+                            || (error == CommandException.Error.INTERNAL_ERR
+                    && statusCode == STATUS_CODE_APPLET_SELECT_FAILED);
+        }
+        return false;
+    }
+
     @Override
     public void handleMessage(Message msg) {
         AsyncResult ar;
@@ -442,11 +468,8 @@
                             DATA, obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE, mChannelId,
                                     mAIDInUse));
                 } else {
-                    // MISSING_RESOURCE could be due to logical channels temporarily unavailable,
-                    // so we retry up to MAX_RETRY times, with an interval of RETRY_INTERVAL_MS.
-                    if (ar.exception instanceof CommandException && mRetryCount < MAX_RETRY
-                            && ((CommandException) (ar.exception)).getCommandError()
-                            == CommandException.Error.MISSING_RESOURCE) {
+                    if (shouldRetry(ar, mRetryCount)) {
+                        log("should retry");
                         mRetryCount++;
                         removeCallbacks(mRetryRunnable);
                         postDelayed(mRetryRunnable, RETRY_INTERVAL_MS);
@@ -745,4 +768,4 @@
                 return "UNKNOWN";
         }
     }
-}
+}
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
index bf36e73..9736f47 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
@@ -187,7 +187,6 @@
         notificationTypeMap.put(CarrierServiceStateTracker.NOTIFICATION_EMERGENCY_NETWORK,
                 spyEmergencyNetworkNotification);
         Notification.Builder mNotificationBuilder = new Notification.Builder(mContext);
-        doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mSST.mSS).getVoiceRegState();
         doReturn(mNotificationBuilder).when(spyEmergencyNetworkNotification)
                 .getNotificationBuilder();
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthNrTest.java b/tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthNrTest.java
index d967bf6..f0a22ff 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthNrTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CellSignalStrengthNrTest.java
@@ -109,11 +109,9 @@
                     CSIRSRP, CSIRSRQ, CSISINR, ssRsrp, SSRSRQ, SSSINR);
 
             // THEN the signal level is valid
-            assertThat(css.getLevel()).isAnyOf(
-                    CellSignalStrength.SIGNAL_STRENGTH_GREAT,
-                    CellSignalStrength.SIGNAL_STRENGTH_GOOD,
-                    CellSignalStrength.SIGNAL_STRENGTH_MODERATE,
-                    CellSignalStrength.SIGNAL_STRENGTH_POOR);
+            assertThat(css.getLevel()).isIn(Range.range(
+                    CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, BoundType.CLOSED,
+                    CellSignalStrength.SIGNAL_STRENGTH_GREAT, BoundType.CLOSED));
         }
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTestSupport.java b/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTestSupport.java
index b9215ed..e4db62d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTestSupport.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NitzStateMachineTestSupport.java
@@ -278,17 +278,17 @@
     }
 
     public static PhoneTimeSuggestion createEmptyTimeSuggestion(int phoneId) {
-        PhoneTimeSuggestion timeSuggestion = new PhoneTimeSuggestion(phoneId);
-        timeSuggestion.addDebugInfo("Test");
-        return timeSuggestion;
+        return new PhoneTimeSuggestion.Builder(phoneId)
+                .addDebugInfo("Test")
+                .build();
     }
 
     public static PhoneTimeSuggestion createTimeSuggestionFromNitzSignal(
             int phoneId, TimestampedValue<NitzData> nitzSignal) {
-        PhoneTimeSuggestion timeSuggestion = new PhoneTimeSuggestion(phoneId);
-        timeSuggestion.setUtcTime(createTimeSignalFromNitzSignal(nitzSignal));
-        timeSuggestion.addDebugInfo("Test");
-        return timeSuggestion;
+        return new PhoneTimeSuggestion.Builder(phoneId)
+                .setUtcTime(createTimeSignalFromNitzSignal(nitzSignal))
+                .addDebugInfo("Test")
+                .build();
     }
 
     public static TimestampedValue<Long> createTimeSignalFromNitzSignal(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index cb6a5e6..36850a7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -302,6 +302,27 @@
                 com.android.internal.R.array.config_display_no_service_when_sim_unready,
                 new String[0]);
 
+        mBundle.putIntArray(CarrierConfigManager.KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY,
+                new int[] {
+                    -125, /* SIGNAL_STRENGTH_POOR */
+                    -115, /* SIGNAL_STRENGTH_MODERATE */
+                    -105, /* SIGNAL_STRENGTH_GOOD */
+                    -95,  /* SIGNAL_STRENGTH_GREAT */
+                });
+        mBundle.putIntArray(CarrierConfigManager.KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY,
+                new int[] {
+                    -14, /* SIGNAL_STRENGTH_POOR */
+                    -12, /* SIGNAL_STRENGTH_MODERATE */
+                    -10, /* SIGNAL_STRENGTH_GOOD */
+                    -8  /* SIGNAL_STRENGTH_GREAT */
+                });
+        mBundle.putIntArray(CarrierConfigManager.KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY,
+                new int[] {
+                    -8, /* SIGNAL_STRENGTH_POOR */
+                    0, /* SIGNAL_STRENGTH_MODERATE */
+                    8, /* SIGNAL_STRENGTH_GOOD */
+                    16  /* SIGNAL_STRENGTH_GREAT */
+                });
         logd("ServiceStateTrackerTest -Setup!");
     }
 
@@ -682,6 +703,105 @@
     }
 
     @Test
+    public void test5gNrSignalStrengthReportingCriteria_UseSsRsrp() {
+        SignalStrength ss = new SignalStrength(
+                new CellSignalStrengthCdma(),
+                new CellSignalStrengthGsm(),
+                new CellSignalStrengthWcdma(),
+                new CellSignalStrengthTdscdma(),
+                new CellSignalStrengthLte(),
+                new CellSignalStrengthNr(
+                    -139, /** csiRsrp NONE */
+                    -20, /** csiRsrq NONE */
+                    -23, /** CsiSinr NONE */
+                    -44, /** SsRsrp SIGNAL_STRENGTH_GREAT */
+                    -20, /** SsRsrq NONE */
+                    -23) /** SsSinr NONE */
+         );
+
+        // SSRSRP = 1 << 0
+        mBundle.putInt(CarrierConfigManager.KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT,
+                CellSignalStrengthNr.USE_SSRSRP);
+        sendCarrierConfigUpdate();
+        mSimulatedCommands.setSignalStrength(ss);
+        mSimulatedCommands.notifySignalStrength();
+        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+        assertEquals(CellSignalStrength.SIGNAL_STRENGTH_GREAT, sst.getSignalStrength().getLevel());
+    }
+
+    @Test
+    public void test5gNrSignalStrengthReportingCriteria_UseSsRsrpAndSsRsrq() {
+        SignalStrength ss = new SignalStrength(
+                new CellSignalStrengthCdma(),
+                new CellSignalStrengthGsm(),
+                new CellSignalStrengthWcdma(),
+                new CellSignalStrengthTdscdma(),
+                new CellSignalStrengthLte(),
+                new CellSignalStrengthNr(
+                    -139, /** csiRsrp NONE */
+                    -20, /** csiRsrq NONE */
+                    -23, /** CsiSinr NONE */
+                    -44, /** SsRsrp SIGNAL_STRENGTH_GREAT */
+                    -20, /** SsRsrq NONE */
+                    -23) /** SsSinr NONE */
+        );
+
+        // SSRSRP = 1 << 0 | SSSINR = 1 << 2
+        mBundle.putInt(CarrierConfigManager.KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT,
+                CellSignalStrengthNr.USE_SSRSRP | CellSignalStrengthNr.USE_SSRSRQ);
+        sendCarrierConfigUpdate();
+        mSimulatedCommands.setSignalStrength(ss);
+        mSimulatedCommands.notifySignalStrength();
+        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+        assertEquals(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
+                sst.getSignalStrength().getLevel());
+    }
+
+    @Test
+    public void test5gNrSignalStrengthReportingCriteria_ConfiguredThresholds() {
+        SignalStrength ss = new SignalStrength(
+                new CellSignalStrengthCdma(),
+                new CellSignalStrengthGsm(),
+                new CellSignalStrengthWcdma(),
+                new CellSignalStrengthTdscdma(),
+                new CellSignalStrengthLte(),
+                new CellSignalStrengthNr(
+                    -139, /** csiRsrp NONE */
+                    -20, /** csiRsrq NONE */
+                    -23, /** CsiSinr NONE */
+                    -44, /** SsRsrp SIGNAL_STRENGTH_GREAT */
+                    -20, /** SsRsrq NONE */
+                    -23) /** SsSinr NONE */
+        );
+
+        // SSRSRP = 1 << 0
+        mBundle.putInt(CarrierConfigManager.KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT,
+                CellSignalStrengthNr.USE_SSRSRP);
+        sendCarrierConfigUpdate();
+        mSimulatedCommands.setSignalStrength(ss);
+        mSimulatedCommands.notifySignalStrength();
+        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+        assertEquals(CellSignalStrength.SIGNAL_STRENGTH_GREAT, sst.getSignalStrength().getLevel());
+
+        int[] nrSsRsrpThresholds = {
+                -45, // SIGNAL_STRENGTH_POOR
+                -40, // SIGNAL_STRENGTH_MODERATE
+                -37, // SIGNAL_STRENGTH_GOOD
+                -34,  // SIGNAL_STRENGTH_GREAT
+        };
+        mBundle.putIntArray(CarrierConfigManager.KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY,
+                nrSsRsrpThresholds);
+        mBundle.putInt(CarrierConfigManager.KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT,
+                CellSignalStrengthNr.USE_SSRSRP);
+        sendCarrierConfigUpdate();
+        mSimulatedCommands.setSignalStrength(ss);
+        mSimulatedCommands.notifySignalStrength();
+        waitForLastHandlerAction(mSSTTestHandler.getThreadHandler());
+        assertEquals(CellSignalStrength.SIGNAL_STRENGTH_POOR,
+                sst.getSignalStrength().getLevel());
+    }
+
+    @Test
     public void testWcdmaSignalStrengthReportingCriteria() {
         SignalStrength ss = new SignalStrength(
                 new CellSignalStrengthCdma(),
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SignalThresholdInfoTest.java b/tests/telephonytests/src/com/android/internal/telephony/SignalThresholdInfoTest.java
new file mode 100644
index 0000000..1574a06
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/SignalThresholdInfoTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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.internal.telephony;
+
+import android.telephony.SignalThresholdInfo;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+
+public class SignalThresholdInfoTest extends TestCase {
+    private static final int HYSTERESIS_DB = 2;
+    private static final int HYSTERESIS_MS = 30;
+    private static final int[] SSRSRP_THRESHOLDS = new int[] {-30, 10, 45, 130};
+
+    public void testSignalThresholdInfo() throws Exception {
+        SignalThresholdInfo signalThresholdInfo = new SignalThresholdInfo(
+                SignalThresholdInfo.SIGNAL_SSRSRP,
+                HYSTERESIS_MS,
+                HYSTERESIS_DB,
+                SSRSRP_THRESHOLDS,
+                false);
+
+        assertEquals(SignalThresholdInfo.SIGNAL_SSRSRP,
+                signalThresholdInfo.getSignalMeasurement());
+        assertEquals(HYSTERESIS_MS, signalThresholdInfo.getHysteresisMs());
+        assertEquals(HYSTERESIS_DB, signalThresholdInfo.getHysteresisDb());
+        assertEquals(Arrays.toString(SSRSRP_THRESHOLDS), Arrays.toString(
+                signalThresholdInfo.getThresholds()));
+        assertFalse(signalThresholdInfo.isEnabled());
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
index 261b60e..009f71c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
@@ -45,6 +45,7 @@
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
+import android.telephony.SignalThresholdInfo;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
 import android.telephony.data.DataCallResponse;
@@ -2248,8 +2249,8 @@
     }
 
     @Override
-    public void setSignalStrengthReportingCriteria(int hysteresisMs, int hysteresisDb,
-            int[] thresholdsDbm, int ran, Message result) {
+    public void setSignalStrengthReportingCriteria(SignalThresholdInfo signalThresholdInfo,
+            int ran, Message result) {
     }
 
     @Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
index f030a40..64b0c38 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
@@ -22,6 +22,7 @@
 import android.os.Message;
 import android.telephony.ImsiEncryptionInfo;
 import android.telephony.NetworkScanRequest;
+import android.telephony.SignalThresholdInfo;
 import android.telephony.TelephonyManager;
 import android.telephony.data.DataProfile;
 import android.telephony.emergency.EmergencyNumber;
@@ -1393,8 +1394,8 @@
     }
 
     @Override
-    public void setSignalStrengthReportingCriteria(int hysteresisMs, int hysteresisDb,
-            int[] thresholdsDbm, int ran, Message result) {
+    public void setSignalStrengthReportingCriteria(SignalThresholdInfo signalThresholdInfo,
+            int ran, Message result) {
     }
 
     @Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 6c2faf0..743fcaf 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -661,14 +661,14 @@
         @Override
         public Bundle call(String method, String arg, Bundle extras) {
             switch (method) {
-                case BlockedNumberContract.SystemContract.METHOD_SHOULD_SYSTEM_BLOCK_NUMBER:
+                case BlockedNumberContract.METHOD_SHOULD_SYSTEM_BLOCK_NUMBER:
                     Bundle bundle = new Bundle();
                     int blockStatus = mBlockedNumbers.contains(arg)
                             ? BlockedNumberContract.STATUS_BLOCKED_IN_LIST
                             : BlockedNumberContract.STATUS_NOT_BLOCKED;
                     bundle.putInt(BlockedNumberContract.RES_BLOCK_STATUS, blockStatus);
                     return bundle;
-                case BlockedNumberContract.SystemContract.METHOD_NOTIFY_EMERGENCY_CONTACT:
+                case BlockedNumberContract.METHOD_NOTIFY_EMERGENCY_CONTACT:
                     mNumEmergencyContactNotifications++;
                     return new Bundle();
                 default:
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataFailCauseTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataFailCauseTest.java
index e881cc9..24d9bd6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataFailCauseTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataFailCauseTest.java
@@ -138,9 +138,7 @@
         mFailCauseDataList.add(new DcFailCauseData(0x10000, false, false));
         mFailCauseDataList.add(new DcFailCauseData(0x10001, true, false));
         mFailCauseDataList.add(new DcFailCauseData(0x10002, true, true));
-        mFailCauseDataList.add(new DcFailCauseData(0x10003, false, false));
         mFailCauseDataList.add(new DcFailCauseData(0x10004, false, false));
-        mFailCauseDataList.add(new DcFailCauseData(0x10005, false, false));
 
         CarrierConfigManager configManager = (CarrierConfigManager)
                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
index 84e1dce..e1f984f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
@@ -212,6 +212,8 @@
         // Set CarrierConfig default package name and make it available to the package manager
         setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> features = new HashSet<>();
+        features.add(new ImsFeatureConfiguration.FeatureSlotPair(0,
+                ImsFeature.FEATURE_EMERGENCY_MMTEL));
         features.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_MMTEL));
         features.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
         setupPackageQuery(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true);
@@ -226,6 +228,34 @@
     }
 
     /**
+     * Creates a carrier ImsService that defines FEATURE_EMERGENCY_MMTEL but not FEATURE_MMTEL and
+     * ensure that the controller doesn't set FEATURE_EMERGENCY_MMTEL.
+     */
+    @Test
+    @SmallTest
+    public void testCarrierPackageBindWithEmergencyButNotMmtel() throws RemoteException {
+        setupResolver(1/*numSlots*/);
+        // Set CarrierConfig default package name and make it available to the package manager
+        setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+        HashSet<ImsFeatureConfiguration.FeatureSlotPair> features = new HashSet<>();
+        features.add(new ImsFeatureConfiguration.FeatureSlotPair(0,
+                ImsFeature.FEATURE_EMERGENCY_MMTEL));
+        features.add(new ImsFeatureConfiguration.FeatureSlotPair(0, ImsFeature.FEATURE_RCS));
+        setupPackageQuery(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true);
+        ImsServiceController controller = setupController();
+
+        startBindCarrierConfigAlreadySet();
+        setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, features, 1);
+
+        // We will not bind with FEATURE_EMERGENCY_MMTEL
+        features.remove(new ImsFeatureConfiguration.FeatureSlotPair(0,
+                ImsFeature.FEATURE_EMERGENCY_MMTEL));
+        verify(controller).bind(features);
+        verify(controller, never()).unbind();
+        assertEquals(TEST_CARRIER_DEFAULT_NAME, controller.getComponentName());
+    }
+
+    /**
      * Creates a carrier ImsService that does not report FEATURE_EMERGENCY_MMTEL and then update the
      * ImsService to define it. Ensure that the controller sets this capability once enabled.
      */
@@ -313,6 +343,37 @@
     }
 
     /**
+     * Test that the ImsService corresponding to the default device ImsService package name is
+     * bound to only RCS if METADATA_EMERGENCY_MMTEL_FEATURE but not METADATA_MMTEL_FEATURE.
+     */
+    @Test
+    @SmallTest
+    public void testDevicePackageInvalidMmTelBind() throws RemoteException {
+        setupResolver(1/*numSlots*/);
+        List<ResolveInfo> info = new ArrayList<>();
+        Set<String> features = new HashSet<>();
+        features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+        features.add(ImsResolver.METADATA_RCS_FEATURE);
+        // Use device default package, which will load the ImsService that the device provides
+        info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, features, true));
+        info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, new HashSet<>(), true));
+        setupPackageQuery(info);
+        ImsServiceController controller = setupController();
+
+
+        startBindNoCarrierConfig(1);
+        mLooper.processAllMessages();
+
+        // There is no carrier override set, so make sure that the ImsServiceController binds
+        // to all SIMs.
+        HashSet<ImsFeatureConfiguration.FeatureSlotPair> featureSet = convertToHashSet(features, 0);
+        verify(controller).bind(featureSet);
+        verify(controller, never()).unbind();
+        verify(mMockQueryManager, never()).startQuery(any(), any());
+        assertEquals(TEST_DEVICE_DEFAULT_NAME, controller.getComponentName());
+    }
+
+    /**
      * Test that when a device and carrier override package are set, both ImsServices are bound.
      * Verify that the carrier ImsService features are created and the device default features
      * are created for all features that are not covered by the carrier ImsService.
@@ -484,7 +545,9 @@
         // remove carrier overrides for slot 0
         newDeviceFeatureSet.removeAll(carrierFeatures);
         verify(deviceController).changeImsServiceFeatures(newDeviceFeatureSet);
-        verify(carrierController, never()).changeImsServiceFeatures(any());
+        // features should be the same as before, ImsServiceController will disregard change if it
+        // is the same feature set anyway.
+        verify(carrierController).changeImsServiceFeatures(carrierFeatures);
     }
 
     /**
@@ -941,7 +1004,7 @@
         }
 
         mTestImsResolver = new ImsResolver(mMockContext, TEST_DEVICE_DEFAULT_NAME.getPackageName(),
-                numSlots, true);
+                numSlots);
         try {
             mLooper = new TestableLooper(mTestImsResolver.getHandler().getLooper());
         } catch (Exception e) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
index 85a83ce..ff17216 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
@@ -38,6 +38,7 @@
 import android.os.RemoteException;
 import android.telephony.ims.ImsService;
 import android.telephony.ims.aidl.IImsServiceController;
+import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.stub.ImsFeatureConfiguration;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -60,6 +61,9 @@
 @RunWith(AndroidJUnit4.class)
 public class ImsServiceControllerTest extends ImsTestBase {
 
+    private static final int SLOT_0 = 0;
+    private static final int SLOT_1 = 1;
+
     private static final ImsServiceController.RebindRetry REBIND_RETRY =
             new ImsServiceController.RebindRetry() {
         @Override
@@ -110,10 +114,10 @@
     @Test
     public void testBindService() {
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
-        // Slot 1, MMTel
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
-        // Slot 1, RCS
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_MMTEL));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_RCS));
         ArgumentCaptor<Intent> intentCaptor =
                 ArgumentCaptor.forClass(Intent.class);
 
@@ -134,8 +138,8 @@
     @Test
     public void testBindFailureWhenBound() {
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
-        // Slot 1, MMTel
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_MMTEL));
         bindAndConnectService(testFeatures);
 
         // already bound, should return false
@@ -152,21 +156,21 @@
     @Test
     public void testBindServiceAndConnected() throws RemoteException {
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
-        // Slot 1, MMTel
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
-        // Slot 1, RCS
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_MMTEL));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_RCS));
 
         bindAndConnectService(testFeatures);
 
-        verify(mMockServiceControllerBinder).createMmTelFeature(eq(1), any());
-        verify(mMockServiceControllerBinder).createRcsFeature(eq(1), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
+        verify(mMockServiceControllerBinder).createMmTelFeature(eq(SLOT_0), any());
+        verify(mMockServiceControllerBinder).createRcsFeature(eq(SLOT_0), any());
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
                 eq(mTestImsServiceController));
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(2),
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
                 eq(mTestImsServiceController));
-        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
-        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(2));
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL));
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS));
     }
 
     /**
@@ -177,24 +181,56 @@
     @Test
     public void testBindEmergencyMmTel() throws RemoteException {
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
-        // Slot 1, Emergency MMTel
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 0));
-        // Slot 1, MmTel
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_EMERGENCY_MMTEL));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_MMTEL));
 
         bindAndConnectService(testFeatures);
 
-        // We do not want this callback to happen for emergency MMTEL
-        verify(mMockServiceControllerBinder, never()).createMmTelFeature(eq(0), any());
-        verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(1), eq(0),
+        verify(mMockServiceControllerBinder).createMmTelFeature(eq(SLOT_0), any());
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
                 eq(mTestImsServiceController));
-        verify(mMockServiceControllerBinder).createMmTelFeature(eq(1), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
-                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0),
+                eq(ImsFeature.FEATURE_EMERGENCY_MMTEL), eq(mTestImsServiceController));
         // Make sure this callback happens, which will notify the framework of emergency calling
         // availability.
-        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(0));
-        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0),
+                eq(ImsFeature.FEATURE_EMERGENCY_MMTEL));
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL));
+    }
+
+    /**
+     * Tests to make sure that if EMERGENCY_MMTEL is specified, but not MMTEL, we do not bind to
+     * MMTEL.
+     */
+    @SmallTest
+    @Test
+    public void testBindEmergencyMmTelButNotMmTel() throws RemoteException {
+        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_EMERGENCY_MMTEL));
+        // did not add FEATURE_MMTEL
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_RCS));
+
+        bindAndConnectService(testFeatures);
+
+        // Verify no MMTEL or EMERGENCY_MMTEL features are created
+        verify(mMockServiceControllerBinder, never()).createMmTelFeature(eq(SLOT_0), any());
+        verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
+        verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0),
+                eq(ImsFeature.FEATURE_EMERGENCY_MMTEL), eq(mTestImsServiceController));
+        verify(mMockProxyCallbacks, never()).imsFeatureCreated(eq(SLOT_0),
+                eq(ImsFeature.FEATURE_EMERGENCY_MMTEL));
+        verify(mMockProxyCallbacks, never()).imsFeatureCreated(eq(SLOT_0),
+                eq(ImsFeature.FEATURE_MMTEL));
+        // verify RCS feature is created
+        verify(mMockServiceControllerBinder).createRcsFeature(eq(SLOT_0), any());
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
+                eq(mTestImsServiceController));
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS));
     }
 
     /**
@@ -205,10 +241,10 @@
     @Test
     public void testCallbacksHappenWhenAddedAfterBind() throws RemoteException {
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
-        // Slot 1, Emergency MMTel
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 0));
-        // Slot 1, MmTel
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_EMERGENCY_MMTEL));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_MMTEL));
         mTestImsServiceController.removeImsServiceFeatureCallbacks();
 
         bindAndConnectService(testFeatures);
@@ -216,8 +252,9 @@
         mTestImsServiceController.addImsServiceFeatureCallback(mMockProxyCallbacks);
 
         // Make sure this callback happens for Emergency MMTEL and MMTEL
-        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(0));
-        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0),
+                eq(ImsFeature.FEATURE_EMERGENCY_MMTEL));
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL));
     }
 
     /**
@@ -228,20 +265,20 @@
     @Test
     public void testBindServiceAndConnectedDisconnected() throws RemoteException {
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
-        // Slot 1, MMTel
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
-        // Slot 1, RCS
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_MMTEL));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_RCS));
         ServiceConnection conn = bindAndConnectService(testFeatures);
 
         conn.onServiceDisconnected(mTestComponentName);
 
-        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(1), eq(1),
+        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
                 eq(mTestImsServiceController));
-        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(1), eq(2),
+        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
                 eq(mTestImsServiceController));
-        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(1), eq(1));
-        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(1), eq(2));
+        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL));
+        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS));
     }
 
     /**
@@ -252,23 +289,25 @@
     @Test
     public void testBindServiceBindUnbind() throws RemoteException {
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
-        // Slot 1, MMTel
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
-        // Slot 1, RCS
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_MMTEL));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_RCS));
         ServiceConnection conn = bindAndConnectService(testFeatures);
 
         mTestImsServiceController.unbind();
 
         verify(mMockContext).unbindService(eq(conn));
-        verify(mMockServiceControllerBinder).removeImsFeature(eq(1), eq(1), any());
-        verify(mMockServiceControllerBinder).removeImsFeature(eq(1), eq(2), any());
-        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(1), eq(1),
+        verify(mMockServiceControllerBinder).removeImsFeature(eq(SLOT_0),
+                eq(ImsFeature.FEATURE_MMTEL), any());
+        verify(mMockServiceControllerBinder).removeImsFeature(eq(SLOT_0),
+                eq(ImsFeature.FEATURE_RCS), any());
+        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
                 eq(mTestImsServiceController));
-        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(1), eq(2),
+        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
                 eq(mTestImsServiceController));
-        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(1), eq(1));
-        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(1), eq(2));
+        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL));
+        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS));
     }
 
     /**
@@ -278,20 +317,20 @@
     @Test
     public void testBindServiceAndBinderDied() throws RemoteException {
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
-        // Slot 1, MMTel
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
-        // Slot 1, RCS
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_MMTEL));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_RCS));
         ServiceConnection conn = bindAndConnectService(testFeatures);
 
         conn.onBindingDied(null /*null*/);
 
-        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(1), eq(1),
+        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
                 eq(mTestImsServiceController));
-        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(1), eq(2),
+        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
                 eq(mTestImsServiceController));
-        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(1), eq(1));
-        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(1), eq(2));
+        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL));
+        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS));
     }
 
     /**
@@ -301,10 +340,10 @@
     @Test
     public void testBindServiceAndReturnedNull() throws RemoteException {
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
-        // Slot 1, MMTel
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
-        // Slot 1, RCS
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_MMTEL));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_RCS));
 
         bindAndNullServiceError(testFeatures);
 
@@ -321,24 +360,85 @@
     @Test
     public void testBindServiceAndAddFeature() throws RemoteException {
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
-        // Slot 1, MMTel
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_MMTEL));
         bindAndConnectService(testFeatures);
-        verify(mMockServiceControllerBinder).createMmTelFeature(eq(1), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
+        verify(mMockServiceControllerBinder).createMmTelFeature(eq(SLOT_0), any());
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
                 eq(mTestImsServiceController));
-        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL));
         // Create a new list with an additional item
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithAddition = new HashSet<>(
                 testFeatures);
-        testFeaturesWithAddition.add(new ImsFeatureConfiguration.FeatureSlotPair(2, 1));
+        testFeaturesWithAddition.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_1,
+                ImsFeature.FEATURE_MMTEL));
 
         mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithAddition);
 
-        verify(mMockServiceControllerBinder).createMmTelFeature(eq(2), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(2), eq(1),
+        verify(mMockServiceControllerBinder).createMmTelFeature(eq(SLOT_1), any());
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
                 eq(mTestImsServiceController));
-        verify(mMockProxyCallbacks).imsFeatureCreated(eq(2), eq(1));
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL));
+    }
+
+    /**
+     * Ensures that the when EMERGENCY_MMTEL_FEATURE is defined but not MMTEL_FEATURE when the
+     * features are changed, we do not bind to MMTEL.
+     */
+    @SmallTest
+    @Test
+    public void testBindServiceAndAddEmergencyButNotMmtel() throws RemoteException {
+        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_RCS));
+        bindAndConnectService(testFeatures);
+        verify(mMockServiceControllerBinder).createRcsFeature(eq(SLOT_0), any());
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS),
+                eq(mTestImsServiceController));
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_RCS));
+        // Add FEATURE_EMERGENCY_MMTEL and ensure it doesn't cause MMTEL bind
+        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithAddition = new HashSet<>(
+                testFeatures);
+        testFeaturesWithAddition.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_1,
+                ImsFeature.FEATURE_EMERGENCY_MMTEL));
+
+        mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithAddition);
+
+        verify(mMockServiceControllerBinder, never()).createMmTelFeature(eq(SLOT_1), any());
+        verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_1),
+                eq(ImsFeature.FEATURE_MMTEL),
+                eq(mTestImsServiceController));
+        verify(mMockProxyCallbacks, never()).imsFeatureCreated(eq(SLOT_1),
+                eq(ImsFeature.FEATURE_MMTEL));
+    }
+
+    /**
+     * Ensures ImsServiceController disregards changes to features that result in the same feature
+     * set.
+     */
+    @SmallTest
+    @Test
+    public void testBindServiceCallChangeWithNoNewFeatures() throws RemoteException {
+        HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_MMTEL));
+        bindAndConnectService(testFeatures);
+        verify(mMockServiceControllerBinder).createMmTelFeature(eq(SLOT_0), any());
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
+                eq(mTestImsServiceController));
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL));
+
+        // Call change with the same features and make sure it is disregarded
+        mTestImsServiceController.changeImsServiceFeatures(testFeatures);
+
+        verify(mMockServiceControllerBinder, times(1)).createMmTelFeature(eq(SLOT_0), any());
+        verify(mMockServiceControllerBinder, never()).removeImsFeature(anyInt(), anyInt(), any());
+        verify(mMockCallbacks, times(1)).imsServiceFeatureCreated(eq(SLOT_0),
+                eq(ImsFeature.FEATURE_MMTEL), eq(mTestImsServiceController));
+        verify(mMockCallbacks, never()).imsServiceFeatureRemoved(anyInt(), anyInt(), any());
+        verify(mMockProxyCallbacks, times(1)).imsFeatureCreated(eq(SLOT_0),
+                eq(ImsFeature.FEATURE_MMTEL));
+        verify(mMockProxyCallbacks, never()).imsFeatureRemoved(anyInt(), anyInt());
     }
 
     /**
@@ -348,30 +448,32 @@
     @Test
     public void testBindServiceAndRemoveFeature() throws RemoteException {
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
-        // Slot 1, MMTel
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
-        // Slot 2, MMTel
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(2, 1));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_MMTEL));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_1,
+                ImsFeature.FEATURE_MMTEL));
         bindAndConnectService(testFeatures);
-        verify(mMockServiceControllerBinder).createMmTelFeature(eq(1), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
+        verify(mMockServiceControllerBinder).createMmTelFeature(eq(SLOT_0), any());
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
                 eq(mTestImsServiceController));
-        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
-        verify(mMockServiceControllerBinder).createMmTelFeature(eq(2), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(2), eq(1),
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL));
+        verify(mMockServiceControllerBinder).createMmTelFeature(eq(SLOT_1), any());
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
                 eq(mTestImsServiceController));
-        verify(mMockProxyCallbacks).imsFeatureCreated(eq(2), eq(1));
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL));
         // Create a new list with one less item
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithSubtraction =
                 new HashSet<>(testFeatures);
-        testFeaturesWithSubtraction.remove(new ImsFeatureConfiguration.FeatureSlotPair(2, 1));
+        testFeaturesWithSubtraction.remove(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_1,
+                ImsFeature.FEATURE_MMTEL));
 
         mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithSubtraction);
 
-        verify(mMockServiceControllerBinder).removeImsFeature(eq(2), eq(1), any());
-        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(2), eq(1),
+        verify(mMockServiceControllerBinder).removeImsFeature(eq(SLOT_1),
+                eq(ImsFeature.FEATURE_MMTEL), any());
+        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
                 eq(mTestImsServiceController));
-        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(2), eq(1));
+        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL));
     }
 
     /**
@@ -381,31 +483,33 @@
     @Test
     public void testBindServiceAndRemoveAllFeatures() throws RemoteException {
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
-        // slot 1, MMTel
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
-        // slot 2, MMTel
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(2, 1));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_MMTEL));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_1,
+                ImsFeature.FEATURE_MMTEL));
         bindAndConnectService(testFeatures);
-        verify(mMockServiceControllerBinder).createMmTelFeature(eq(1), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
+        verify(mMockServiceControllerBinder).createMmTelFeature(eq(SLOT_0), any());
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
                 eq(mTestImsServiceController));
-        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
-        verify(mMockServiceControllerBinder).createMmTelFeature(eq(2), any());
-        verify(mMockCallbacks).imsServiceFeatureCreated(eq(2), eq(1),
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL));
+        verify(mMockServiceControllerBinder).createMmTelFeature(eq(SLOT_1), any());
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
                 eq(mTestImsServiceController));
-        verify(mMockProxyCallbacks).imsFeatureCreated(eq(2), eq(1));
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL));
 
         // Create a new empty list
         mTestImsServiceController.changeImsServiceFeatures(new HashSet<>());
 
-        verify(mMockServiceControllerBinder).removeImsFeature(eq(1), eq(1), any());
-        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(1), eq(1),
+        verify(mMockServiceControllerBinder).removeImsFeature(eq(SLOT_0),
+                eq(ImsFeature.FEATURE_MMTEL), any());
+        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
                 eq(mTestImsServiceController));
-        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(1), eq(1));
-        verify(mMockServiceControllerBinder).removeImsFeature(eq(2), eq(1), any());
-        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(2), eq(1),
+        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL));
+        verify(mMockServiceControllerBinder).removeImsFeature(eq(SLOT_1),
+                eq(ImsFeature.FEATURE_MMTEL), any());
+        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL),
                 eq(mTestImsServiceController));
-        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(2), eq(1));
+        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(SLOT_1), eq(ImsFeature.FEATURE_MMTEL));
     }
 
     /**
@@ -415,22 +519,24 @@
     @Test
     public void testBindUnbindServiceAndAddFeature() throws RemoteException {
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
-        // Slot 1, MMTel
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_MMTEL));
         bindAndConnectService(testFeatures);
         mTestImsServiceController.unbind();
         // Create a new list with an additional item
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeaturesWithAddition = new HashSet<>(
                 testFeatures);
         // Try to create an RCS feature
-        testFeaturesWithAddition.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
+        testFeaturesWithAddition.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_RCS));
 
         mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithAddition);
 
-        verify(mMockServiceControllerBinder, never()).createRcsFeature(eq(1), any());
-        verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(1), eq(2),
-                eq(mTestImsServiceController));
-        verify(mMockProxyCallbacks, never()).imsFeatureCreated(eq(1), eq(2));
+        verify(mMockServiceControllerBinder, never()).createRcsFeature(eq(SLOT_0), any());
+        verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(SLOT_0),
+                eq(ImsFeature.FEATURE_RCS), eq(mTestImsServiceController));
+        verify(mMockProxyCallbacks, never()).imsFeatureCreated(eq(SLOT_0),
+                eq(ImsFeature.FEATURE_RCS));
     }
 
     /**
@@ -441,10 +547,10 @@
     @Test
     public void testAutoBindAfterBinderDied() throws RemoteException {
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
-        // Slot 1, MMTel
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
-        // Slot 1, RCS
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_MMTEL));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_RCS));
         ServiceConnection conn = bindAndConnectService(testFeatures);
 
         conn.onBindingDied(null /*null*/);
@@ -462,10 +568,10 @@
     @Test
     public void testNoAutoBindBeforeTimeout() throws RemoteException {
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
-        // Slot 1, MMTel
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
-        // Slot 1, RCS
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_MMTEL));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_RCS));
         ServiceConnection conn = bindAndConnectService(testFeatures);
 
         conn.onBindingDied(null /*null*/);
@@ -481,10 +587,10 @@
     @Test
     public void testUnbindCauseAutoBindCancelAfterBinderDied() throws RemoteException {
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
-        // Slot 1, MMTel
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
-        // Slot 1, RCS
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_MMTEL));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_RCS));
         ServiceConnection conn = bindAndConnectService(testFeatures);
 
         conn.onBindingDied(null /*null*/);
@@ -505,10 +611,10 @@
     @Test
     public void testBindCauseAutoBindCancelAfterBinderDied() throws RemoteException {
         HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
-        // Slot 1, MMTel
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 1));
-        // Slot 1, RCS
-        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, 2));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_MMTEL));
+        testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+                ImsFeature.FEATURE_RCS));
         ServiceConnection conn = bindAndConnectService(testFeatures);
         conn.onBindingDied(null /*null*/);
         mTestImsServiceController.bind(testFeatures);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
index 0db3204..2e7f8a5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
@@ -223,11 +223,25 @@
         assertEquals(Phone.SuppService.SEPARATE,
                 ((AsyncResult) messageArgumentCaptor.getValue().obj).result);
 
-        // ringing call is idle
+        // ringing call is idle, only an active call present
+        doReturn(Call.State.ACTIVE).when(mForegroundCall).getState();
         assertEquals(true, mImsPhoneUT.handleInCallMmiCommands("2"));
         verify(mImsCT).holdActiveCall();
 
+        // background call is holding
+        doReturn(Call.State.HOLDING).when(mBackgroundCall).getState();
+        doReturn(Call.State.IDLE).when(mForegroundCall).getState();
+        assertEquals(true, mImsPhoneUT.handleInCallMmiCommands("2"));
+        verify(mImsCT).unholdHeldCall();
+
+        // background call is holding and there's an active foreground call
+        doReturn(Call.State.ACTIVE).when(mForegroundCall).getState();
+        assertEquals(true, mImsPhoneUT.handleInCallMmiCommands("2"));
+        verify(mImsCT, times(2)).holdActiveCall();
+
         // ringing call is not idle
+        doReturn(Call.State.IDLE).when(mForegroundCall).getState();
+        doReturn(Call.State.IDLE).when(mBackgroundCall).getState();
         doReturn(Call.State.INCOMING).when(mRingingCall).getState();
         assertEquals(true, mImsPhoneUT.handleInCallMmiCommands("2"));
         verify(mImsCT).acceptCall(ImsCallProfile.CALL_TYPE_VOICE);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
index 80353fe..b383bfa 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
@@ -30,6 +30,7 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.TelephonyTest;
 
 import org.junit.After;
@@ -256,6 +257,38 @@
         assertEquals(0, mUiccCarrierPrivilegeRules.getPackageNames().size());
     }
 
+    @Test
+    @SmallTest
+    public void testRetryARAM_shouldRetry() {
+        AsyncResult ar1 = new AsyncResult(
+                null,
+                new int[]{0, 105, -123},
+                new CommandException(CommandException.Error.NO_SUCH_ELEMENT));
+        assertTrue(mUiccCarrierPrivilegeRules.shouldRetry(ar1, 0));
+
+        AsyncResult ar2 = new AsyncResult(
+                null,
+                new int[]{0},
+                new CommandException(CommandException.Error.MISSING_RESOURCE));
+        assertTrue(mUiccCarrierPrivilegeRules.shouldRetry(ar2, 0));
+
+        AsyncResult ar3 = new AsyncResult(
+                null,
+                new int[]{0, 105, 153},
+                new CommandException(CommandException.Error.INTERNAL_ERR));
+        assertTrue(mUiccCarrierPrivilegeRules.shouldRetry(ar3, 0));
+    }
+
+    @Test
+    @SmallTest
+    public void testRetryARAM_shouldNotRetry() {
+        AsyncResult ar = new AsyncResult(
+                null,
+                new int[]{0, 106, -126},
+                new CommandException(CommandException.Error.NO_SUCH_ELEMENT));
+        assertTrue(!mUiccCarrierPrivilegeRules.shouldRetry(ar, 0));
+    }
+
     private static final String ARAM = "A00000015141434C00";
     private static final String ARAD = "A00000015144414300";
     private static final String PKCS15_AID = "A000000063504B43532D3135";