Merge "Swap parameters in QosCallback#registerQosCallback"
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index ef83ead..ae6f072 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -30,6 +30,9 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
 import android.os.PersistableBundle;
 import android.os.UserManager;
 import android.preference.Preference;
@@ -40,8 +43,8 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
-import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 import android.telephony.ims.ProvisioningManager;
 import android.telephony.ims.feature.ImsFeature;
@@ -103,6 +106,7 @@
     private ImsManager mImsMgr;
     private SubscriptionInfoHelper mSubscriptionInfoHelper;
     private TelecomManager mTelecomManager;
+    private TelephonyCallback mTelephonyCallback;
 
     private SwitchPreference mButtonAutoRetry;
     private PreferenceScreen mVoicemailSettingsScreen;
@@ -263,6 +267,7 @@
         mSubscriptionInfoHelper.setActionBarTitle(
                 getActionBar(), getResourcesForSubId(), R.string.call_settings_with_label);
         mTelecomManager = getSystemService(TelecomManager.class);
+        mTelephonyCallback = new CallFeaturesTelephonyCallback();
     }
 
     private void updateImsManager(Phone phone) {
@@ -279,11 +284,16 @@
     private void listenPhoneState(boolean listen) {
         TelephonyManager telephonyManager = getSystemService(TelephonyManager.class)
                 .createForSubscriptionId(mPhone.getSubId());
-        telephonyManager.listen(mPhoneStateListener, listen
-                ? PhoneStateListener.LISTEN_CALL_STATE : PhoneStateListener.LISTEN_NONE);
+        if (listen) {
+            telephonyManager.registerTelephonyCallback(
+                    new HandlerExecutor(new Handler(Looper.getMainLooper())), mTelephonyCallback);
+        } else {
+            telephonyManager.unregisterTelephonyCallback(mTelephonyCallback);
+        }
     }
 
-    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+    private final class CallFeaturesTelephonyCallback extends TelephonyCallback implements
+            TelephonyCallback.CallStateListener {
         @Override
         public void onCallStateChanged(int state, String incomingNumber) {
             if (DBG) log("PhoneStateListener onCallStateChanged: state is " + state);
diff --git a/src/com/android/phone/CallNotifier.java b/src/com/android/phone/CallNotifier.java
index 6c18623..1a867b6 100644
--- a/src/com/android/phone/CallNotifier.java
+++ b/src/com/android/phone/CallNotifier.java
@@ -25,13 +25,14 @@
 import android.media.ToneGenerator;
 import android.os.AsyncResult;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.Message;
 import android.os.SystemProperties;
 import android.telecom.TelecomManager;
-import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -68,8 +69,8 @@
     /** The singleton instance. */
     private static CallNotifier sInstance;
 
-    private Map<Integer, CallNotifierPhoneStateListener> mPhoneStateListeners =
-            new ArrayMap<Integer, CallNotifierPhoneStateListener>();
+    private Map<Integer, CallNotifierTelephonyCallback> mTelephonyCallback =
+            new ArrayMap<Integer, CallNotifierTelephonyCallback>();
     private Map<Integer, Boolean> mCFIStatus = new ArrayMap<Integer, Boolean>();
     private Map<Integer, Boolean> mMWIStatus = new ArrayMap<Integer, Boolean>();
     private PhoneGlobals mApplication;
@@ -566,7 +567,7 @@
         // slot 0 first then slot 1. This is needed to ensure that when CFI or MWI is enabled for
         // both slots, user always sees icon related to slot 0 on left side followed by that of
         // slot 1.
-        List<Integer> subIdList = new ArrayList<Integer>(mPhoneStateListeners.keySet());
+        List<Integer> subIdList = new ArrayList<Integer>(mTelephonyCallback.keySet());
         Collections.sort(subIdList, new Comparator<Integer>() {
             public int compare(Integer sub1, Integer sub2) {
                 int slotId1 = SubscriptionController.getInstance().getSlotIndex(sub1);
@@ -583,10 +584,9 @@
                 mApplication.notificationMgr.updateMwi(subId, false);
                 mApplication.notificationMgr.updateCfi(subId, false);
 
-                // Listening to LISTEN_NONE removes the listener.
-                mTelephonyManager.listen(
-                        mPhoneStateListeners.get(subId), PhoneStateListener.LISTEN_NONE);
-                mPhoneStateListeners.remove(subId);
+                // Unregister the listener.
+                mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback.get(subId));
+                mTelephonyCallback.remove(subId);
             } else {
                 Log.d(LOG_TAG, "updatePhoneStateListeners: update CF notifications.");
 
@@ -616,12 +616,11 @@
         // Register new phone listeners for active subscriptions.
         for (int i = 0; i < subInfos.size(); i++) {
             int subId = subInfos.get(i).getSubscriptionId();
-            if (!mPhoneStateListeners.containsKey(subId)) {
-                CallNotifierPhoneStateListener listener = new CallNotifierPhoneStateListener(subId);
-                mTelephonyManager.createForSubscriptionId(subId).listen(listener,
-                        PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
-                        | PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR);
-                mPhoneStateListeners.put(subId, listener);
+            if (!mTelephonyCallback.containsKey(subId)) {
+                CallNotifierTelephonyCallback listener = new CallNotifierTelephonyCallback(subId);
+                mTelephonyManager.createForSubscriptionId(subId).registerTelephonyCallback(
+                        new HandlerExecutor(this), listener);
+                mTelephonyCallback.put(subId, listener);
             }
         }
     }
@@ -768,10 +767,13 @@
                 }
             };
 
-    private class CallNotifierPhoneStateListener extends PhoneStateListener {
+    private class CallNotifierTelephonyCallback extends TelephonyCallback implements
+            TelephonyCallback.MessageWaitingIndicatorListener,
+            TelephonyCallback.CallForwardingIndicatorListener {
+
         private final int mSubId;
 
-        CallNotifierPhoneStateListener(int subId) {
+        CallNotifierTelephonyCallback(int subId) {
             super();
             this.mSubId = subId;
         }
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 831b537..de41309 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -247,6 +247,12 @@
                 }
 
                 case EVENT_DO_FETCH_DEFAULT: {
+                    // Clear in-memory cache for carrier app config, so when carrier app gets
+                    // uninstalled, no stale config is left.
+                    if (mConfigFromCarrierApp[phoneId] != null
+                            && getCarrierPackageForPhoneId(phoneId) == null) {
+                        mConfigFromCarrierApp[phoneId] = null;
+                    }
                     // Restore persistent override values.
                     PersistableBundle config = restoreConfigFromXml(
                             mPlatformCarrierConfigPackage, OVERRIDE_PACKAGE_ADDITION, phoneId);
@@ -1142,12 +1148,6 @@
      * have a saved config file to use instead.
      */
     private void updateConfigForPhoneId(int phoneId) {
-        // Clear in-memory cache for carrier app config, so when carrier app gets uninstalled, no
-        // stale config is left.
-        if (mConfigFromCarrierApp[phoneId] != null &&
-                getCarrierPackageForPhoneId(phoneId) == null) {
-            mConfigFromCarrierApp[phoneId] = null;
-        }
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_DO_FETCH_DEFAULT, phoneId, -1));
     }
 
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index 5e616b7..ad33302 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -28,6 +28,7 @@
 import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.ims.DelegateRequest;
 import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsContactUceCapability;
 import android.telephony.ims.RcsUceAdapter.PublishState;
 import android.telephony.ims.RegistrationManager;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
@@ -56,6 +57,7 @@
 import com.android.services.telephony.rcs.UceControllerManager;
 
 import java.util.List;
+import java.util.Set;
 
 /**
  * Implementation of the IImsRcsController interface.
@@ -336,6 +338,82 @@
         }
     }
 
+    /**
+     * Add new feature tags to the Set used to calculate the capabilities in PUBLISH.
+     */
+    // Used for SHELL command only right now.
+    public RcsContactUceCapability addUceRegistrationOverrideShell(int subId,
+            Set<String> featureTags) throws ImsException {
+        // Permission check happening in PhoneInterfaceManager.
+        UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                UceControllerManager.class);
+        if (uceCtrlManager == null) {
+            return null;
+        }
+        return uceCtrlManager.addUceRegistrationOverride(featureTags);
+    }
+
+    /**
+     * Remove existing feature tags to the Set used to calculate the capabilities in PUBLISH.
+     */
+    // Used for SHELL command only right now.
+    public RcsContactUceCapability removeUceRegistrationOverrideShell(int subId,
+            Set<String> featureTags) throws ImsException {
+        // Permission check happening in PhoneInterfaceManager.
+        UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                UceControllerManager.class);
+        if (uceCtrlManager == null) {
+            return null;
+        }
+        return uceCtrlManager.removeUceRegistrationOverride(featureTags);
+    }
+
+    /**
+     * Clear all overrides in the Set used to calculate the capabilities in PUBLISH.
+     */
+    // Used for SHELL command only right now.
+    public RcsContactUceCapability clearUceRegistrationOverrideShell(int subId)
+            throws ImsException {
+        // Permission check happening in PhoneInterfaceManager.
+        UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                UceControllerManager.class);
+        if (uceCtrlManager == null) {
+            return null;
+        }
+        return uceCtrlManager.clearUceRegistrationOverride();
+    }
+
+    /**
+     * @return current RcsContactUceCapability instance that will be used for PUBLISH.
+     */
+    // Used for SHELL command only right now.
+    public RcsContactUceCapability getLatestRcsContactUceCapabilityShell(int subId)
+            throws ImsException {
+        // Permission check happening in PhoneInterfaceManager.
+        UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                UceControllerManager.class);
+        if (uceCtrlManager == null) {
+            return null;
+        }
+        return uceCtrlManager.getLatestRcsContactUceCapability();
+    }
+
+    /**
+     * @return the PIDf XML used in the last PUBLISH procedure or "none" if the device is not
+     * published. Returns {@code null} if the operation failed due to an error.
+     */
+    // Used for SHELL command only right now.
+    public String getLastUcePidfXmlShell(int subId) throws ImsException {
+        // Permission check happening in PhoneInterfaceManager.
+        UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                UceControllerManager.class);
+        if (uceCtrlManager == null) {
+            return null;
+        }
+        String pidfXml = uceCtrlManager.getLastPidfXml();
+        return pidfXml == null ? "none" : pidfXml;
+    }
+
     @Override
     public void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
         enforceReadPrivilegedPermission("registerUcePublishStateCallback");
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 38d3299..d2cfa29 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -67,7 +67,6 @@
 import android.telephony.Annotation.ApnType;
 import android.telephony.Annotation.ThermalMitigationResult;
 import android.telephony.CallForwardingInfo;
-import android.telephony.CarrierBandwidth;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CarrierRestrictionRules;
 import android.telephony.CellIdentity;
@@ -111,6 +110,7 @@
 import android.telephony.ims.ImsException;
 import android.telephony.ims.ProvisioningManager;
 import android.telephony.ims.RcsClientConfiguration;
+import android.telephony.ims.RcsContactUceCapability;
 import android.telephony.ims.RegistrationManager;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsConfig;
@@ -159,6 +159,8 @@
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.ProxyController;
 import com.android.internal.telephony.RIL;
+import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.RadioInterfaceCapabilityController;
 import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.SmsController;
 import com.android.internal.telephony.SmsPermissions;
@@ -341,6 +343,7 @@
     private SubscriptionController mSubscriptionController;
     private SharedPreferences mTelephonySharedPreferences;
     private PhoneConfigurationManager mPhoneConfigurationManager;
+    private final RadioInterfaceCapabilityController mRadioInterfaceCapabilities;
 
     /** User Activity */
     private AtomicBoolean mNotifyUserActivity;
@@ -861,6 +864,10 @@
                                 request.result =
                                         TelephonyManager
                                                 .ENABLE_NR_DUAL_CONNECTIVITY_RADIO_NOT_AVAILABLE;
+                            } else if (error == CommandException.Error.REQUEST_NOT_SUPPORTED) {
+                                request.result =
+                                        TelephonyManager
+                                                .ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED;
                             }
                             loge("enableNrDualConnectivity" + ": CommandException: "
                                     + ar.exception);
@@ -2091,6 +2098,7 @@
                 PreferenceManager.getDefaultSharedPreferences(mApp);
         mNetworkScanRequestTracker = new NetworkScanRequestTracker();
         mPhoneConfigurationManager = PhoneConfigurationManager.getInstance();
+        mRadioInterfaceCapabilities = RadioInterfaceCapabilityController.getInstance();
         mNotifyUserActivity = new AtomicBoolean(false);
 
         publish();
@@ -2798,9 +2806,7 @@
             if (sst == null) return "";
             LocaleTracker lt = sst.getLocaleTracker();
             if (lt == null) return "";
-            if (!TextUtils.isEmpty(lt.getCurrentCountry())) return lt.getCurrentCountry();
-            EmergencyNumberTracker ent = phone.getEmergencyNumberTracker();
-            return (ent == null) ? "" : ent.getEmergencyCountryIso();
+            return lt.getCurrentCountry();
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -6047,9 +6053,8 @@
     @Override
     public long getAllowedNetworkTypesForReason(int subId,
             @TelephonyManager.AllowedNetworkTypesReason int reason) {
-        TelephonyPermissions
-                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
-                        mApp, subId, "getAllowedNetworkTypesForReason");
+        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "getAllowedNetworkTypesForReason");
         final long identity = Binder.clearCallingIdentity();
         try {
             return getPhoneFromSubId(subId).getAllowedNetworkTypes(reason);
@@ -6076,6 +6081,11 @@
             @TelephonyManager.NrDualConnectivityState int nrDualConnectivityState) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "enableNRDualConnectivity");
+        if (!isRadioInterfaceCapabilitySupported(
+                TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE)) {
+            return TelephonyManager.ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED;
+        }
+
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -6098,6 +6108,10 @@
         TelephonyPermissions
                 .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "isNRDualConnectivityEnabled");
+        if (!isRadioInterfaceCapabilitySupported(
+                TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE)) {
+            return false;
+        }
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -6111,28 +6125,6 @@
     }
 
     /**
-     * get carrier bandwidth per primary and secondary carrier
-     * @param subId subscription id of the sim card
-     * @return CarrierBandwidth with bandwidth of both primary and secondary carrier..
-     */
-    @Override
-    public CarrierBandwidth getCarrierBandwidth(int subId) {
-        TelephonyPermissions
-                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
-                        mApp, subId, "isNRDualConnectivityEnabled");
-        WorkSource workSource = getWorkSource(Binder.getCallingUid());
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            CarrierBandwidth carrierBandwidth =
-                    getPhoneFromSubId(subId).getCarrierBandwidth();
-            if (DBG) log("getCarrierBandwidth: " + carrierBandwidth);
-            return carrierBandwidth;
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    /**
      * Set the allowed network types of the device and
      * provide the reason triggering the allowed network change.
      *
@@ -6152,10 +6144,15 @@
             return false;
         }
 
-        if (DBG) {
-            log("setAllowedNetworkTypesForReason: " + reason
-                    + " value: " + allowedNetworkTypes);
+        log("setAllowedNetworkTypesForReason: " + reason + " value: "
+                + TelephonyManager.convertNetworkTypeBitmaskToString(allowedNetworkTypes));
+
+
+        if (allowedNetworkTypes == getPhoneFromSubId(subId).getAllowedNetworkTypes(reason)) {
+            log("setAllowedNetworkTypesForReason: " + reason + "does not change value");
+            return true;
         }
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Boolean success = (Boolean) sendRequest(
@@ -7231,15 +7228,14 @@
                 setDataEnabledForReason(subId, TelephonyManager.DATA_ENABLED_REASON_USER,
                         getDefaultDataEnabled());
                 setNetworkSelectionModeAutomatic(subId);
-                setAllowedNetworkTypesForReason(subId,
-                        TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER,
-                        RadioAccessFamily.getRafFromNetworkType(getDefaultNetworkType(subId)));
-                setAllowedNetworkTypesForReason(subId,
-                        TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER,
-                        RadioAccessFamily.getRafFromNetworkType(getDefaultNetworkType(subId)));
-                setAllowedNetworkTypesForReason(subId,
-                        TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER,
-                        RadioAccessFamily.getRafFromNetworkType(getDefaultNetworkType(subId)));
+                Phone phone = getPhone(subId);
+                if (phone != null) {
+                    SubscriptionManager.setSubscriptionProperty(subId,
+                            SubscriptionManager.ALLOWED_NETWORK_TYPES,
+                            "user=" + RadioAccessFamily.getRafFromNetworkType(
+                                    RILConstants.PREFERRED_NETWORK_MODE));
+                    phone.loadAllowedNetworksFromSubscriptionDatabase();
+                }
                 setDataRoamingEnabled(subId, getDefaultDataRoamingEnabled(subId));
                 CarrierInfoManager.deleteAllCarrierKeysForImsiEncryption(mApp);
             }
@@ -9306,7 +9302,7 @@
     public boolean isRadioInterfaceCapabilitySupported(
             @NonNull @TelephonyManager.RadioInterfaceCapability String capability) {
         Set<String> radioInterfaceCapabilities =
-                mPhoneConfigurationManager.getRadioInterfaceCapabilities();
+                mRadioInterfaceCapabilities.getCapabilities();
         if (radioInterfaceCapabilities == null) {
             throw new RuntimeException("radio interface capabilities are not available");
         } else {
@@ -9622,10 +9618,10 @@
      * Register RCS provisioning callback.
      */
     @Override
-    public void registerRcsProvisioningChangedCallback(int subId,
+    public void registerRcsProvisioningCallback(int subId,
             IRcsConfigCallback callback) {
         TelephonyPermissions.enforceAnyPermissionGrantedOrCarrierPrivileges(mApp, subId,
-                Binder.getCallingUid(), "registerRcsProvisioningChangedCallback",
+                Binder.getCallingUid(), "registerRcsProvisioningCallback",
                 Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION,
                 permission.READ_PRIVILEGED_PHONE_STATE);
 
@@ -9640,7 +9636,7 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             if (!RcsProvisioningMonitor.getInstance()
-                    .registerRcsProvisioningChangedCallback(subId, callback)) {
+                    .registerRcsProvisioningCallback(subId, callback)) {
                 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
                         "Service not available for the subscription.");
             }
@@ -9653,10 +9649,10 @@
      * Unregister RCS provisioning callback.
      */
     @Override
-    public void unregisterRcsProvisioningChangedCallback(int subId,
+    public void unregisterRcsProvisioningCallback(int subId,
             IRcsConfigCallback callback) {
         TelephonyPermissions.enforceAnyPermissionGrantedOrCarrierPrivileges(mApp, subId,
-                Binder.getCallingUid(), "unregisterRcsProvisioningChangedCallback",
+                Binder.getCallingUid(), "unregisterRcsProvisioningCallback",
                 Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION,
                 permission.READ_PRIVILEGED_PHONE_STATE);
 
@@ -9671,7 +9667,7 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             RcsProvisioningMonitor.getInstance()
-                    .unregisterRcsProvisioningChangedCallback(subId, callback);
+                    .unregisterRcsProvisioningCallback(subId, callback);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -9896,6 +9892,104 @@
         }
     }
 
+    /**
+     * Add new feature tags to the Set used to calculate the capabilities in PUBLISH.
+     * @return current RcsContactUceCapability instance that will be used for PUBLISH.
+     */
+    // Used for SHELL command only right now.
+    @Override
+    public RcsContactUceCapability addUceRegistrationOverrideShell(int subId,
+            List<String> featureTags) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+                "addUceRegistrationOverrideShell");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mApp.imsRcsController.addUceRegistrationOverrideShell(subId,
+                    new ArraySet<>(featureTags));
+        } catch (ImsException e) {
+            throw new ServiceSpecificException(e.getCode(), e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Remove existing feature tags to the Set used to calculate the capabilities in PUBLISH.
+     * @return current RcsContactUceCapability instance that will be used for PUBLISH.
+     */
+    // Used for SHELL command only right now.
+    @Override
+    public RcsContactUceCapability removeUceRegistrationOverrideShell(int subId,
+            List<String> featureTags) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+                "removeUceRegistrationOverrideShell");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mApp.imsRcsController.removeUceRegistrationOverrideShell(subId,
+                    new ArraySet<>(featureTags));
+        } catch (ImsException e) {
+            throw new ServiceSpecificException(e.getCode(), e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Clear all overrides in the Set used to calculate the capabilities in PUBLISH.
+     * @return current RcsContactUceCapability instance that will be used for PUBLISH.
+     */
+    // Used for SHELL command only right now.
+    @Override
+    public RcsContactUceCapability clearUceRegistrationOverrideShell(int subId) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+                "clearUceRegistrationOverrideShell");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mApp.imsRcsController.clearUceRegistrationOverrideShell(subId);
+        } catch (ImsException e) {
+            throw new ServiceSpecificException(e.getCode(), e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * @return current RcsContactUceCapability instance that will be used for PUBLISH.
+     */
+    // Used for SHELL command only right now.
+    @Override
+    public RcsContactUceCapability getLatestRcsContactUceCapabilityShell(int subId) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+                "getLatestRcsContactUceCapabilityShell");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mApp.imsRcsController.getLatestRcsContactUceCapabilityShell(subId);
+        } catch (ImsException e) {
+            throw new ServiceSpecificException(e.getCode(), e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Returns the last PIDF XML sent to the network during the last PUBLISH or "none" if the
+     * device does not have an active PUBLISH.
+     */
+    // Used for SHELL command only right now.
+    @Override
+    public String getLastUcePidfXmlShell(int subId) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "uceGetLastPidfXml");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mApp.imsRcsController.getLastUcePidfXmlShell(subId);
+        } catch (ImsException e) {
+            throw new ServiceSpecificException(e.getCode(), e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+
     @Override
     public void setSignalStrengthUpdateRequest(int subId, SignalStrengthUpdateRequest request,
             String callingPackage) {
diff --git a/src/com/android/phone/RcsProvisioningMonitor.java b/src/com/android/phone/RcsProvisioningMonitor.java
index 2191e28..bcf1491 100644
--- a/src/com/android/phone/RcsProvisioningMonitor.java
+++ b/src/com/android/phone/RcsProvisioningMonitor.java
@@ -558,13 +558,13 @@
     }
 
     /**
-     * Called when the application registers rcs provisioning changed callback
+     * Called when the application registers rcs provisioning callback
      */
-    public boolean registerRcsProvisioningChangedCallback(int subId, IRcsConfigCallback cb) {
+    public boolean registerRcsProvisioningCallback(int subId, IRcsConfigCallback cb) {
         RcsProvisioningInfo info = mRcsProvisioningInfos.get(subId);
         // should not happen in normal case
         if (info == null) {
-            logd("fail to register rcs provisioning changed due to subscription unavailable");
+            logd("fail to register rcs provisioning callback due to subscription unavailable");
             return false;
         }
 
@@ -572,9 +572,9 @@
     }
 
     /**
-     * Called when the application unregisters rcs provisioning changed callback
+     * Called when the application unregisters rcs provisioning callback
      */
-    public boolean unregisterRcsProvisioningChangedCallback(int subId, IRcsConfigCallback cb) {
+    public boolean unregisterRcsProvisioningCallback(int subId, IRcsConfigCallback cb) {
         RcsProvisioningInfo info = mRcsProvisioningInfos.get(subId);
         // should not happen in normal case
         if (info == null) {
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index 74d6b57..87dc868 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -26,15 +26,21 @@
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.ServiceSpecificException;
 import android.provider.BlockedNumberContract;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsContactUceCapability;
 import android.telephony.ims.feature.ImsFeature;
 import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
 
+import com.android.ims.rcs.uce.util.FeatureTags;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
@@ -45,9 +51,12 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.TreeSet;
 
 /**
@@ -113,6 +122,11 @@
     private static final String UCE_REMOVE_EAB_CONTACT = "remove-eab-contact";
     private static final String UCE_GET_DEVICE_ENABLED = "get-device-enabled";
     private static final String UCE_SET_DEVICE_ENABLED = "set-device-enabled";
+    private static final String UCE_OVERRIDE_PUBLISH_CAPS = "override-published-caps";
+    private static final String UCE_GET_LAST_PIDF_XML = "get-last-publish-pidf";
+
+    // Check if a package has carrier privileges on any SIM, regardless of subId/phoneId.
+    private static final String HAS_CARRIER_PRIVILEGES_COMMAND = "has-carrier-privileges";
 
     // Take advantage of existing methods that already contain permissions checks when possible.
     private final ITelephony mInterface;
@@ -164,6 +178,48 @@
         }
     };
 
+    /**
+     * Map from a shorthand string to the feature tags required in registration required in order
+     * for the RCS feature to be considered "capable".
+     */
+    private static final Map<String, Set<String>> TEST_FEATURE_TAG_MAP;
+    static {
+        ArrayMap<String, Set<String>> map = new ArrayMap<>(18);
+        map.put("chat_v1", Collections.singleton(FeatureTags.FEATURE_TAG_CHAT_IM));
+        map.put("chat_v2", Collections.singleton(FeatureTags.FEATURE_TAG_CHAT_SESSION));
+        map.put("ft", Collections.singleton(FeatureTags.FEATURE_TAG_FILE_TRANSFER));
+        map.put("ft_sms", Collections.singleton(FeatureTags.FEATURE_TAG_FILE_TRANSFER_VIA_SMS));
+        map.put("mmtel", Collections.singleton(FeatureTags.FEATURE_TAG_MMTEL));
+        map.put("mmtel_vt", new ArraySet<>(Arrays.asList(FeatureTags.FEATURE_TAG_MMTEL,
+                FeatureTags.FEATURE_TAG_VIDEO)));
+        map.put("geo_push", Collections.singleton(FeatureTags.FEATURE_TAG_GEO_PUSH));
+        map.put("geo_push_sms", Collections.singleton(FeatureTags.FEATURE_TAG_GEO_PUSH_VIA_SMS));
+        map.put("call_comp",
+                Collections.singleton(FeatureTags.FEATURE_TAG_CALL_COMPOSER_ENRICHED_CALLING));
+        map.put("call_comp_mmtel",
+                Collections.singleton(FeatureTags.FEATURE_TAG_CALL_COMPOSER_VIA_TELEPHONY));
+        map.put("call_post", Collections.singleton(FeatureTags.FEATURE_TAG_POST_CALL));
+        map.put("map", Collections.singleton(FeatureTags.FEATURE_TAG_SHARED_MAP));
+        map.put("sketch", Collections.singleton(FeatureTags.FEATURE_TAG_SHARED_SKETCH));
+        // Feature tags defined twice for chatbot session because we want v1 and v2 based on bot
+        // version
+        map.put("chatbot", new ArraySet<>(Arrays.asList(
+                FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_SESSION,
+                FeatureTags.FEATURE_TAG_CHATBOT_VERSION_SUPPORTED)));
+        map.put("chatbot_v2", new ArraySet<>(Arrays.asList(
+                FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_SESSION,
+                FeatureTags.FEATURE_TAG_CHATBOT_VERSION_SUPPORTED)));
+        map.put("chatbot_sa", new ArraySet<>(Arrays.asList(
+                FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_STANDALONE_MSG,
+                FeatureTags.FEATURE_TAG_CHATBOT_VERSION_SUPPORTED)));
+        map.put("chatbot_sa_v2", new ArraySet<>(Arrays.asList(
+                FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_STANDALONE_MSG,
+                FeatureTags.FEATURE_TAG_CHATBOT_VERSION_SUPPORTED)));
+        map.put("chatbot_role", Collections.singleton(FeatureTags.FEATURE_TAG_CHATBOT_ROLE));
+        TEST_FEATURE_TAG_MAP = Collections.unmodifiableMap(map);
+    }
+
+
     public TelephonyShellCommand(ITelephony binder, Context context) {
         mInterface = binder;
         mCarrierConfigManager =
@@ -206,6 +262,8 @@
                 return handleRestartModemCommand();
             case UNATTENDED_REBOOT:
                 return handleUnattendedReboot();
+            case HAS_CARRIER_PRIVILEGES_COMMAND:
+                return handleHasCarrierPrivilegesCommand();
             default: {
                 return handleDefaultCommands(cmd);
             }
@@ -238,6 +296,8 @@
         pw.println("    Restart modem command.");
         pw.println("  unattended-reboot");
         pw.println("    Prepare for unattended reboot.");
+        pw.println("  has-carrier-privileges [package]");
+        pw.println("    Query carrier privilege status for a package. Prints true or false.");
         onHelpIms();
         onHelpUce();
         onHelpEmergencyNumber();
@@ -321,6 +381,22 @@
         pw.println("  uce set-device-enabled true|false");
         pw.println("    Set the device config for RCS User Capability Exchange to the value.");
         pw.println("    The value could be true, false.");
+        pw.println("  uce override-published-caps [-s SLOT_ID] add|remove|clear [CAPABILITIES]");
+        pw.println("    Override the existing SIP PUBLISH with different capabilities.");
+        pw.println("    Options are:");
+        pw.println("      -s: The SIM slot ID to read carrier config value for. If no option");
+        pw.println("          is specified, it will choose the default voice SIM slot.");
+        pw.println("      add [CAPABILITY]: add a new capability");
+        pw.println("      remove [CAPABILITY]: remove a capability");
+        pw.println("      clear: clear all capability overrides");
+        pw.println("      CAPABILITY: \":\" separated list of capabilities.");
+        pw.println("          Valid options are: [mmtel(_vt), chat_v1, chat_v2, ft, ft_sms,");
+        pw.println("          geo_push, geo_push_sms, call_comp, call_post, map, sketch, chatbot,");
+        pw.println("          chatbot_sa, chatbot_role] as well as full length");
+        pw.println("          featureTag=\"featureValue\" feature tags that are not defined here.");
+        pw.println("  uce get-last-publish-pidf [-s SLOT_ID]");
+        pw.println("    Get the PIDF XML included in the last SIP PUBLISH, or \"none\" if no ");
+        pw.println("    PUBLISH is active");
     }
 
     private void onHelpNumberVerification() {
@@ -666,7 +742,7 @@
             errPw.println("message value must be a valid integer");
             return -1;
         }
-        
+
         try {
             mInterface.sendDeviceToDeviceMessage(messageType, messageValue);
         } catch (RemoteException e) {
@@ -1651,8 +1727,8 @@
     private int handleRcsUceCommand() {
         String arg = getNextArg();
         if (arg == null) {
-            Log.w(LOG_TAG, "cannot get uce parameter");
-            return -1;
+            onHelpUce();
+            return 0;
         }
 
         switch (arg) {
@@ -1664,6 +1740,10 @@
                 return handleUceGetDeviceEnabledCommand();
             case UCE_SET_DEVICE_ENABLED:
                 return handleUceSetDeviceEnabledCommand();
+            case UCE_OVERRIDE_PUBLISH_CAPS:
+                return handleUceOverridePublishCaps();
+            case UCE_GET_LAST_PIDF_XML:
+                return handleUceGetPidfXml();
         }
         return -1;
     }
@@ -1784,6 +1864,101 @@
         return 0;
     }
 
+    private int handleUceOverridePublishCaps() {
+        int subId = getSubId("uce override-published-caps");
+        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            return -1;
+        }
+        //uce override-published-caps [-s SLOT_ID] add|remove|clear|list [CAPABILITIES]
+        String operation = getNextArgRequired();
+        String caps = getNextArg();
+        if (!"add".equals(operation) && !"remove".equals(operation) && !"clear".equals(operation)
+                && !"list".equals(operation)) {
+            getErrPrintWriter().println("Invalid operation: " + operation);
+            return -1;
+        }
+
+        // add/remove requires capabilities to be specified.
+        if ((!"clear".equals(operation) && !"list".equals(operation)) && TextUtils.isEmpty(caps)) {
+            getErrPrintWriter().println("\"" + operation + "\" requires capabilities to be "
+                    + "specified");
+            return -1;
+        }
+
+        ArraySet<String> capSet = new ArraySet<>();
+        if (!TextUtils.isEmpty(caps)) {
+            String[] capArray = caps.split(":");
+            for (String cap : capArray) {
+                // Allow unknown tags to be passed in as well.
+                capSet.addAll(TEST_FEATURE_TAG_MAP.getOrDefault(cap, Collections.singleton(cap)));
+            }
+        }
+
+        RcsContactUceCapability result = null;
+        try {
+            switch (operation) {
+                case "add":
+                    result = mInterface.addUceRegistrationOverrideShell(subId,
+                            new ArrayList<>(capSet));
+                    break;
+                case "remove":
+                    result = mInterface.removeUceRegistrationOverrideShell(subId,
+                            new ArrayList<>(capSet));
+                    break;
+                case "clear":
+                    result = mInterface.clearUceRegistrationOverrideShell(subId);
+                    break;
+                case "list":
+                    result = mInterface.getLatestRcsContactUceCapabilityShell(subId);
+                    break;
+            }
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "uce override-published-caps, error " + e.getMessage());
+            getErrPrintWriter().println("Exception: " + e.getMessage());
+            return -1;
+        } catch (ServiceSpecificException sse) {
+            // Reconstruct ImsException
+            ImsException imsException = new ImsException(sse.getMessage(), sse.errorCode);
+            Log.w(LOG_TAG, "uce override-published-caps, error " + imsException);
+            getErrPrintWriter().println("Exception: " + imsException);
+            return -1;
+        }
+        if (result == null) {
+            getErrPrintWriter().println("Service not available");
+            return -1;
+        }
+        getOutPrintWriter().println(result);
+        return 0;
+    }
+
+    private int handleUceGetPidfXml() {
+        int subId = getSubId("uce get-last-publish-pidf");
+        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            return -1;
+        }
+
+        String result;
+        try {
+            result = mInterface.getLastUcePidfXmlShell(subId);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "uce get-last-publish-pidf, error " + e.getMessage());
+            getErrPrintWriter().println("Exception: " + e.getMessage());
+            return -1;
+        } catch (ServiceSpecificException sse) {
+            // Reconstruct ImsException
+            ImsException imsException = new ImsException(sse.getMessage(), sse.errorCode);
+            Log.w(LOG_TAG, "uce get-last-publish-pidf error " + imsException);
+            getErrPrintWriter().println("Exception: " + imsException);
+            return -1;
+        }
+        if (result == null) {
+            getErrPrintWriter().println("Service not available");
+            return -1;
+        }
+        getOutPrintWriter().println(result);
+        return 0;
+    }
+
     private int handleSrcSetDeviceEnabledCommand() {
         String enabledStr = getNextArg();
         if (enabledStr == null) {
@@ -1866,4 +2041,22 @@
         getOutPrintWriter().println(result);
         return 0;
     }
+
+    private int handleHasCarrierPrivilegesCommand() {
+        String packageName = getNextArgRequired();
+
+        boolean hasCarrierPrivileges;
+        try {
+            hasCarrierPrivileges =
+                    mInterface.checkCarrierPrivilegesForPackageAnyPhone(packageName)
+                            == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, HAS_CARRIER_PRIVILEGES_COMMAND + " exception", e);
+            getErrPrintWriter().println("Exception: " + e.getMessage());
+            return -1;
+        }
+
+        getOutPrintWriter().println(hasCarrierPrivileges);
+        return 0;
+    }
 }
diff --git a/src/com/android/phone/settings/AccessibilitySettingsFragment.java b/src/com/android/phone/settings/AccessibilitySettingsFragment.java
index ad3f133..8355fa6 100644
--- a/src/com/android/phone/settings/AccessibilitySettingsFragment.java
+++ b/src/com/android/phone/settings/AccessibilitySettingsFragment.java
@@ -19,6 +19,9 @@
 import android.content.Context;
 import android.media.AudioManager;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
 import android.os.PersistableBundle;
 import android.preference.Preference;
 import android.preference.PreferenceFragment;
@@ -27,8 +30,8 @@
 import android.provider.Settings;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.CarrierConfigManager;
-import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -56,13 +59,10 @@
 
     private static final int WFC_QUERY_TIMEOUT_MILLIS = 20;
 
-    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
-        /**
-         * Disable the TTY setting when in/out of a call (and if carrier doesn't
-         * support VoLTE with TTY).
-         * @see android.telephony.PhoneStateListener#onCallStateChanged(int,
-         * java.lang.String)
-         */
+    private final TelephonyCallback mTelephonyCallback = new AccessibilityTelephonyCallback();
+
+    private final class AccessibilityTelephonyCallback extends TelephonyCallback implements
+            TelephonyCallback.CallStateListener {
         @Override
         public void onCallStateChanged(int state, String incomingNumber) {
             if (DBG) Log.d(LOG_TAG, "PhoneStateListener.onCallStateChanged: state=" + state);
@@ -148,7 +148,8 @@
         super.onResume();
         TelephonyManager tm =
                 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
-        tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+        tm.registerTelephonyCallback(new HandlerExecutor(new Handler(Looper.getMainLooper())),
+                mTelephonyCallback);
     }
 
     @Override
@@ -156,7 +157,7 @@
         super.onPause();
         TelephonyManager tm =
                 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
-        tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+        tm.unregisterTelephonyCallback(mTelephonyCallback);
     }
 
     @Override
diff --git a/src/com/android/phone/settings/RadioInfo.java b/src/com/android/phone/settings/RadioInfo.java
index b7a413c..3f4ae58 100644
--- a/src/com/android/phone/settings/RadioInfo.java
+++ b/src/com/android/phone/settings/RadioInfo.java
@@ -36,6 +36,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.SystemProperties;
@@ -56,13 +57,13 @@
 import android.telephony.CellSignalStrengthWcdma;
 import android.telephony.DataSpecificRegistrationInfo;
 import android.telephony.NetworkRegistrationInfo;
-import android.telephony.PhoneStateListener;
 import android.telephony.PhysicalChannelConfig;
 import android.telephony.PreciseCallState;
 import android.telephony.RadioAccessFamily;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -295,10 +296,20 @@
     };
 
     // not final because we need to recreate this object to register on a new subId (b/117555407)
-    private PhoneStateListener mPhoneStateListener = new RadioInfoPhoneStateListener();
-    private class RadioInfoPhoneStateListener extends PhoneStateListener {
+    private TelephonyCallback mTelephonyCallback = new RadioInfoTelephonyCallback();
+    private class RadioInfoTelephonyCallback extends TelephonyCallback implements
+            TelephonyCallback.DataConnectionStateListener,
+            TelephonyCallback.DataActivityListener,
+            TelephonyCallback.CallStateListener,
+            TelephonyCallback.MessageWaitingIndicatorListener,
+            TelephonyCallback.CallForwardingIndicatorListener,
+            TelephonyCallback.CellInfoListener,
+            TelephonyCallback.SignalStrengthsListener,
+            TelephonyCallback.ServiceStateListener, 
+            TelephonyCallback.PreciseCallStateListener {
+
         @Override
-        public void onDataConnectionStateChanged(int state) {
+        public void onDataConnectionStateChanged(int state, int networkType) {
             updateDataState();
             updateNetworkType();
         }
@@ -673,7 +684,7 @@
 
         log("onPause: unregister phone & data intents");
 
-        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+        mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback);
         mTelephonyManager.setCellInfoListRate(sCellInfoListRateDisabled);
         mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
 
@@ -774,7 +785,7 @@
     }
 
     private void unregisterPhoneStateListener() {
-        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+        mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback);
         mPhone.unregisterForPhysicalChannelConfig(mHandler);
 
         // clear all fields so they are blank until the next listener event occurs
@@ -796,21 +807,11 @@
         mPhyChanConfig.setText("");
     }
 
-    // register mPhoneStateListener for relevant fields using the current TelephonyManager
+    // register mTelephonyCallback for relevant fields using the current TelephonyManager
     private void registerPhoneStateListener() {
-        mPhoneStateListener = new RadioInfoPhoneStateListener();
-        mTelephonyManager.listen(mPhoneStateListener,
-                  PhoneStateListener.LISTEN_CALL_STATE
-        //b/27803938 - RadioInfo currently cannot read PRECISE_CALL_STATE
-        //      | PhoneStateListener.LISTEN_PRECISE_CALL_STATE
-                | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
-                | PhoneStateListener.LISTEN_DATA_ACTIVITY
-                | PhoneStateListener.LISTEN_CELL_LOCATION
-                | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
-                | PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
-                | PhoneStateListener.LISTEN_CELL_INFO
-                | PhoneStateListener.LISTEN_SERVICE_STATE
-                | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
+        mTelephonyCallback = new RadioInfoTelephonyCallback();
+        mTelephonyManager.registerTelephonyCallback(new HandlerExecutor(mHandler),
+                mTelephonyCallback);
     }
 
     private void updateDnsCheckState() {
diff --git a/src/com/android/phone/vvm/VvmSimStateTracker.java b/src/com/android/phone/vvm/VvmSimStateTracker.java
index c648d9c..a77bd7b 100644
--- a/src/com/android/phone/vvm/VvmSimStateTracker.java
+++ b/src/com/android/phone/vvm/VvmSimStateTracker.java
@@ -20,13 +20,16 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
 import android.os.SystemProperties;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
-import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -68,7 +71,8 @@
      * Waits for the account to become {@link ServiceState#STATE_IN_SERVICE} and notify the
      * connected event. Will unregister itself once the event has been triggered.
      */
-    private class ServiceStateListener extends PhoneStateListener {
+    private class ServiceStateListener extends TelephonyCallback implements
+            TelephonyCallback.ServiceStateListener  {
 
         private final PhoneAccountHandle mPhoneAccountHandle;
         private final Context mContext;
@@ -84,7 +88,8 @@
                 VvmLog.e(TAG, "Cannot create TelephonyManager from " + mPhoneAccountHandle);
                 return;
             }
-            telephonyManager.listen(this, PhoneStateListener.LISTEN_SERVICE_STATE);
+            telephonyManager.registerTelephonyCallback(
+                    new HandlerExecutor(new Handler(Looper.getMainLooper())), this);
         }
 
         public void unlisten() {
@@ -92,7 +97,7 @@
             // PhoneStateListener, and mPhoneAccountHandle might be invalid at this point
             // (e.g. SIM removal)
             mContext.getSystemService(TelephonyManager.class)
-                    .listen(this, PhoneStateListener.LISTEN_NONE);
+                    .unregisterTelephonyCallback(this);
             sListeners.put(mPhoneAccountHandle, null);
         }
 
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 9d4edfd..9226cae 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -32,6 +32,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.PersistableBundle;
@@ -42,11 +43,11 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
-import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 import android.telephony.ims.ImsException;
 import android.telephony.ims.ImsMmTelManager;
@@ -113,6 +114,8 @@
     private static final String EXTRA_SUPPORTS_VIDEO_CALLING_FALLBACK =
             "android.telecom.extra.SUPPORTS_VIDEO_CALLING_FALLBACK";
 
+    private Handler mHandler;
+
     final class AccountEntry implements PstnPhoneCapabilitiesNotifier.Listener {
         private final Phone mPhone;
         private PhoneAccount mAccount;
@@ -1078,7 +1081,11 @@
         }
     };
 
-    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+    private final TelephonyCallback mTelephonyCallback = new TelecomAccountTelephonyCallback();
+
+    private class TelecomAccountTelephonyCallback extends TelephonyCallback implements
+            TelephonyCallback.ActiveDataSubscriptionIdListener,
+            TelephonyCallback.ServiceStateListener {
         @Override
         public void onServiceStateChanged(ServiceState serviceState) {
             int newState = serviceState.getState();
@@ -1144,6 +1151,7 @@
         mTelephonyManager = TelephonyManager.from(context);
         mSubscriptionManager = SubscriptionManager.from(context);
         mHandlerThread.start();
+        mHandler = new Handler(Looper.getMainLooper());
         mRegisterSubscriptionListenerBackoff = new ExponentialBackoff(
                 REGISTER_START_DELAY_MS,
                 REGISTER_MAXIMUM_DELAY_MS,
@@ -1371,8 +1379,8 @@
 
         // We also need to listen for changes to the service state (e.g. emergency -> in service)
         // because this could signal a removal or addition of a SIM in a single SIM phone.
-        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE
-                | PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
+        mTelephonyManager.registerTelephonyCallback(new HandlerExecutor(mHandler),
+                mTelephonyCallback);
 
         // Listen for user switches.  When the user switches, we need to ensure that if the current
         // use is not the primary user we disable video calling.
@@ -1392,8 +1400,7 @@
 
     private void registerContentObservers() {
         // Listen to the RTT system setting so that we update it when the user flips it.
-        ContentObserver rttUiSettingObserver = new ContentObserver(
-                new Handler(Looper.getMainLooper())) {
+        ContentObserver rttUiSettingObserver = new ContentObserver(mHandler) {
             @Override
             public void onChange(boolean selfChange) {
                 synchronized (mAccountsLock) {
@@ -1409,8 +1416,7 @@
                 rttSettingUri, false, rttUiSettingObserver);
 
         // Listen to the changes to the user's Contacts Discovery Setting.
-        ContentObserver contactDiscoveryObserver = new ContentObserver(
-                new Handler(Looper.getMainLooper())) {
+        ContentObserver contactDiscoveryObserver = new ContentObserver(mHandler) {
             @Override
             public void onChange(boolean selfChange) {
                 synchronized (mAccountsLock) {
diff --git a/src/com/android/services/telephony/rcs/RcsFeatureController.java b/src/com/android/services/telephony/rcs/RcsFeatureController.java
index 5a1acb5..3eefdb0 100644
--- a/src/com/android/services/telephony/rcs/RcsFeatureController.java
+++ b/src/com/android/services/telephony/rcs/RcsFeatureController.java
@@ -20,7 +20,9 @@
 import android.content.Context;
 import android.net.Uri;
 import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsRcsManager;
 import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.RegistrationManager;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsRegistrationCallback;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -63,12 +65,16 @@
         void onRcsDisconnected();
 
         /**
-         * The subscription associated with the slot this controller is bound to has changed or its
-         * carrier configuration has changed.
+         * The subscription associated with the slot this controller is bound to has changed.
          */
         void onAssociatedSubscriptionUpdated(int subId);
 
         /**
+         * The carrier configuration associated with the active subscription id has changed.
+         */
+        void onCarrierConfigChanged();
+
+        /**
          * Called when the feature should be destroyed.
          */
         void onDestroy();
@@ -118,6 +124,7 @@
     private final Object mLock = new Object();
     private FeatureConnector<RcsFeatureManager> mFeatureConnector;
     private RcsFeatureManager mFeatureManager;
+    private int mAssociatedSubId;
 
     private FeatureConnector.Listener<RcsFeatureManager> mFeatureConnectorListener =
             new FeatureConnector.Listener<RcsFeatureManager>() {
@@ -171,9 +178,10 @@
                 }
             };
 
-    public RcsFeatureController(Context context, int slotId) {
+    public RcsFeatureController(Context context, int slotId, int associatedSubId) {
         mContext = context;
         mSlotId = slotId;
+        mAssociatedSubId = associatedSubId;
         mImsRcsRegistrationHelper = mRegistrationHelperFactory.create(mRcsRegistrationUpdate,
                 mContext.getMainExecutor());
     }
@@ -182,9 +190,11 @@
      * Should only be used to inject registration helpers for testing.
      */
     @VisibleForTesting
-    public RcsFeatureController(Context context, int slotId, RegistrationHelperFactory f) {
+    public RcsFeatureController(Context context, int slotId, int associatedSubId,
+            RegistrationHelperFactory f) {
         mContext = context;
         mSlotId = slotId;
+        mAssociatedSubId = associatedSubId;
         mRegistrationHelperFactory = f;
         mImsRcsRegistrationHelper = mRegistrationHelperFactory.create(mRcsRegistrationUpdate,
                 mContext.getMainExecutor());
@@ -248,17 +258,12 @@
     }
 
     /**
-     * Update the subscription associated with this controller.
+     * Update the Features associated with this controller due to the associated subscription
+     * changing.
      */
     public void updateAssociatedSubscription(int newSubId) {
-        RcsFeatureManager manager = getFeatureManager();
-        if (manager != null) {
-            try {
-                manager.updateCapabilities();
-            } catch (ImsException e) {
-                Log.w(LOG_TAG, "associatedSubscriptionChanged failed:" + e);
-            }
-        }
+        mAssociatedSubId = newSubId;
+        updateCapabilities();
         synchronized (mLock) {
             for (Feature c : mFeatures.values()) {
                 c.onAssociatedSubscriptionUpdated(newSubId);
@@ -267,6 +272,19 @@
     }
 
     /**
+     * Update the features associated with this controller due to the carrier configuration
+     * changing.
+     */
+    public void onCarrierConfigChangedForSubscription() {
+        updateCapabilities();
+        synchronized (mLock) {
+            for (Feature c : mFeatures.values()) {
+                c.onCarrierConfigChanged();
+            }
+        }
+    }
+
+    /**
      * Call before this controller is destroyed to tear down associated features.
      */
     public void destroy() {
@@ -314,8 +332,8 @@
     }
 
     /**
-     * Register an {@link ImsRcsManager.AvailabilityCallback} with the associated RcsFeature,
-     * which will provide availability updates.
+     * Register an {@link ImsRcsManager.OnAvailabilityChangedListener} with the associated
+     * RcsFeature, which will provide availability updates.
      */
     public void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback)
             throws ImsException {
@@ -328,7 +346,7 @@
     }
 
     /**
-     * Remove a registered {@link ImsRcsManager.AvailabilityCallback} from the RcsFeature.
+     * Remove a registered {@link ImsRcsManager.OnAvailabilityChangedListener} from the RcsFeature.
      */
     public void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) {
         RcsFeatureManager manager = getFeatureManager();
@@ -381,10 +399,21 @@
         callback.accept(mImsRcsRegistrationHelper.getImsRegistrationState());
     }
 
+    private void updateCapabilities() {
+        RcsFeatureManager manager = getFeatureManager();
+        if (manager != null) {
+            try {
+                manager.updateCapabilities(mAssociatedSubId);
+            } catch (ImsException e) {
+                Log.w(LOG_TAG, "updateCapabilities failed:" + e);
+            }
+        }
+    }
+
     private void setupConnectionToService(RcsFeatureManager manager) throws ImsException {
         // Open persistent listener connection, sends RcsFeature#onFeatureReady.
         manager.openConnection();
-        manager.updateCapabilities();
+        manager.updateCapabilities(mAssociatedSubId);
         manager.registerImsRegistrationCallback(mImsRcsRegistrationHelper.getCallbackBinder());
     }
 
diff --git a/src/com/android/services/telephony/rcs/SipTransportController.java b/src/com/android/services/telephony/rcs/SipTransportController.java
index 028e49f..a948cdb 100644
--- a/src/com/android/services/telephony/rcs/SipTransportController.java
+++ b/src/com/android/services/telephony/rcs/SipTransportController.java
@@ -296,6 +296,11 @@
     }
 
     @Override
+    public void onCarrierConfigChanged() {
+        mExecutorService.submit(this::onCarrierConfigChangedInternal);
+    }
+
+    @Override
     public void onDestroy() {
         mExecutorService.submit(()-> {
             // Ensure new create/destroy requests are denied.
@@ -903,8 +908,7 @@
     }
 
     /**
-     * Called when either the sub ID associated with the slot has changed or the carrier
-     * configuration associated with the same subId has changed.
+     * Called when the sub ID associated with the slot has changed.
      */
     private void onSubIdChanged(int newSubId) {
         logi("subId changed, " + mSubId + "->" + newSubId);
@@ -913,10 +917,14 @@
             mSubId = newSubId;
             scheduleDestroyDelegates(
                     SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN);
-            return;
         }
-        // TODO: if subId hasn't changed this means that we should load in any new carrier configs
-        // that we care about and apply.
+    }
+
+    /**
+     * Called when the carrier configuration associated with the same subId has changed.
+     */
+    private void onCarrierConfigChangedInternal() {
+        logi("Carrier Config changed for subId: " + mSubId);
     }
 
     /**
diff --git a/src/com/android/services/telephony/rcs/TelephonyRcsService.java b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
index 66492ae..034382c 100644
--- a/src/com/android/services/telephony/rcs/TelephonyRcsService.java
+++ b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
@@ -55,7 +55,7 @@
         /**
          * @return an {@link RcsFeatureController} associated with the slot specified.
          */
-        RcsFeatureController createController(Context context, int slotId);
+        RcsFeatureController createController(Context context, int slotId, int subId);
 
         /**
          * @return an instance of {@link UceControllerManager} associated with the slot specified.
@@ -71,8 +71,8 @@
 
     private FeatureFactory mFeatureFactory = new FeatureFactory() {
         @Override
-        public RcsFeatureController createController(Context context, int slotId) {
-            return new RcsFeatureController(context, slotId);
+        public RcsFeatureController createController(Context context, int slotId, int subId) {
+            return new RcsFeatureController(context, slotId, subId);
         }
 
         @Override
@@ -113,6 +113,8 @@
 
     // Maps slot ID -> RcsFeatureController.
     private SparseArray<RcsFeatureController> mFeatureControllers;
+    // Maps slotId -> associatedSubIds
+    private SparseArray<Integer> mSlotToAssociatedSubIds;
 
     // Whether the device supports User Capability Exchange
     private boolean mRcsUceEnabled;
@@ -132,7 +134,7 @@
                         SubscriptionManager.INVALID_PHONE_INDEX);
                 int subId = bundle.getInt(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-                updateFeatureControllerSubscription(slotId, subId);
+                onCarrierConfigChangedForSlot(slotId, subId);
             }
         }
     };
@@ -159,6 +161,7 @@
         mContext = context;
         mNumSlots = numSlots;
         mFeatureControllers = new SparseArray<>(numSlots);
+        mSlotToAssociatedSubIds = new SparseArray<>(numSlots);
         mRcsUceEnabled = sResourceProxy.getDeviceUceEnabled(mContext);
     }
 
@@ -167,6 +170,7 @@
         mContext = context;
         mNumSlots = numSlots;
         mFeatureControllers = new SparseArray<>(numSlots);
+        mSlotToAssociatedSubIds = new SparseArray<>(numSlots);
         sResourceProxy = resourceProxy;
         mRcsUceEnabled = sResourceProxy.getDeviceUceEnabled(mContext);
     }
@@ -218,6 +222,8 @@
                     // Do not add feature controllers for inactive subscriptions
                     if (c.hasActiveFeatures()) {
                         mFeatureControllers.put(i, c);
+                        // Do not change mSlotToAssociatedSubIds, it will be updated upon carrier
+                        // config change.
                     }
                 }
             } else {
@@ -225,6 +231,7 @@
                     RcsFeatureController c = mFeatureControllers.get(i);
                     if (c != null) {
                         mFeatureControllers.remove(i);
+                        mSlotToAssociatedSubIds.remove(i);
                         c.destroy();
                     }
                 }
@@ -232,19 +239,29 @@
         }
     }
 
-    private void updateFeatureControllerSubscription(int slotId, int newSubId) {
+    /**
+     * ACTION_CARRIER_CONFIG_CHANGED was received by this service for a specific slot.
+     * @param slotId The slotId associated with the event.
+     * @param subId The subId associated with the event. May cause the subId associated with the
+     *              RcsFeatureController to change if the subscription itself has changed.
+     */
+    private void onCarrierConfigChangedForSlot(int slotId, int subId) {
         synchronized (mLock) {
             RcsFeatureController f = mFeatureControllers.get(slotId);
-            Log.i(LOG_TAG, "updateFeatureControllerSubscription: slotId=" + slotId + " newSubId="
-                    + newSubId + ", existing feature=" + (f != null));
-            if (SubscriptionManager.isValidSubscriptionId(newSubId)) {
+            final int oldSubId = mSlotToAssociatedSubIds.get(slotId,
+                    SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+            mSlotToAssociatedSubIds.put(slotId, subId);
+            Log.i(LOG_TAG, "updateFeatureControllerSubscription: slotId=" + slotId
+                    + ", oldSubId= " + oldSubId + ", subId=" + subId + ", existing feature="
+                    + (f != null));
+            if (SubscriptionManager.isValidSubscriptionId(subId)) {
                 if (f == null) {
                     // A controller doesn't exist for this slot yet.
-                    f = mFeatureFactory.createController(mContext, slotId);
-                    updateSupportedFeatures(f, slotId, newSubId);
+                    f = mFeatureFactory.createController(mContext, slotId, subId);
+                    updateSupportedFeatures(f, slotId, subId);
                     if (f.hasActiveFeatures()) mFeatureControllers.put(slotId, f);
                 } else {
-                    updateSupportedFeatures(f, slotId, newSubId);
+                    updateSupportedFeatures(f, slotId, subId);
                     // Do not keep an empty container around.
                     if (!f.hasActiveFeatures()) {
                         f.destroy();
@@ -252,13 +269,19 @@
                     }
                 }
             }
-            if (f != null) f.updateAssociatedSubscription(newSubId);
+            if (f != null) {
+                if (oldSubId == subId) {
+                    f.onCarrierConfigChangedForSubscription();
+                } else {
+                    f.updateAssociatedSubscription(subId);
+                }
+            }
         }
     }
 
     private RcsFeatureController constructFeatureController(int slotId) {
-        RcsFeatureController c = mFeatureFactory.createController(mContext, slotId);
         int subId = getSubscriptionFromSlot(slotId);
+        RcsFeatureController c = mFeatureFactory.createController(mContext, slotId, subId);
         updateSupportedFeatures(c, slotId, subId);
         return c;
     }
diff --git a/src/com/android/services/telephony/rcs/UceControllerManager.java b/src/com/android/services/telephony/rcs/UceControllerManager.java
index 7270d69..3051253 100644
--- a/src/com/android/services/telephony/rcs/UceControllerManager.java
+++ b/src/com/android/services/telephony/rcs/UceControllerManager.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.net.Uri;
 import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsContactUceCapability;
 import android.telephony.ims.RcsUceAdapter;
 import android.telephony.ims.RcsUceAdapter.PublishState;
 import android.telephony.ims.aidl.IRcsUceControllerCallback;
@@ -32,6 +33,7 @@
 
 import java.io.PrintWriter;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -50,6 +52,7 @@
     private final Context mContext;
     private final ExecutorService mExecutorService;
 
+    private volatile int mSubId;
     private volatile UceController mUceController;
     private volatile RcsFeatureManager mRcsFeatureManager;
 
@@ -57,6 +60,7 @@
         Log.d(LOG_TAG, "create: slotId=" + slotId + ", subId=" + subId);
 
         mSlotId = slotId;
+        mSubId = subId;
         mContext = context;
         mExecutorService = Executors.newSingleThreadExecutor();
         mUceController = new UceController(mContext, subId);
@@ -68,6 +72,7 @@
     @VisibleForTesting
     public UceControllerManager(Context context, int slotId, int subId, ExecutorService executor) {
         mSlotId = slotId;
+        mSubId = subId;
         mContext = context;
         mExecutorService = executor;
         mUceController = new UceController(mContext, subId);
@@ -98,15 +103,19 @@
     }
 
     /**
-     * This method will be called when either the subscription ID associated with the slot has
-     * changed or the carrier configuration associated with the same subId has changed.
+     * This method will be called when the subscription ID associated with the slot has
+     * changed.
      */
     @Override
     public void onAssociatedSubscriptionUpdated(int subId) {
         mExecutorService.submit(() -> {
             Log.i(LOG_TAG, "onAssociatedSubscriptionUpdated: slotId=" + mSlotId
-                    + ", subId=" + subId);
-
+                    + ", subId=" + mSubId + ", newSubId=" + subId);
+            if (mSubId == subId) {
+                Log.w(LOG_TAG, "onAssociatedSubscriptionUpdated called with the same subId");
+                return;
+            }
+            mSubId = subId;
             // Destroy existing UceController and create a new one.
             mUceController.onDestroy();
             mUceController = new UceController(mContext, subId);
@@ -119,6 +128,18 @@
         });
     }
 
+    /**
+     * This method will be called when the carrier config of the subscription associated with this
+     * manager has changed.
+     */
+    @Override
+    public void onCarrierConfigChanged() {
+        mExecutorService.submit(() -> {
+            Log.i(LOG_TAG, "onCarrierConfigChanged: subId=" + mSubId);
+            mUceController.onCarrierConfigChanged();
+        });
+    }
+
     @VisibleForTesting
     public void setUceController(UceController uceController) {
         mUceController = uceController;
@@ -181,15 +202,15 @@
      * @throws ImsException if the ImsService connected to this controller is currently down.
      */
     public @PublishState int getUcePublishState() throws ImsException {
-        Future future = mExecutorService.submit(() -> {
+        Future<Integer> future = mExecutorService.submit(() -> {
             checkUceControllerState();
             return mUceController.getUcePublishState();
         });
 
         try {
-            return (Integer) future.get();
+            return future.get();
         } catch (ExecutionException | InterruptedException e) {
-            Log.w(LOG_TAG, "requestNetworkAvailability exception: " + e);
+            Log.w(LOG_TAG, "getUcePublishState exception: " + e);
             Throwable cause = e.getCause();
             if (cause instanceof ImsException) {
                 throw (ImsException) cause;
@@ -199,6 +220,114 @@
     }
 
     /**
+     * Add new feature tags to the Set used to calculate the capabilities in PUBLISH.
+     */
+    public RcsContactUceCapability addUceRegistrationOverride(
+            Set<String> featureTags) throws ImsException {
+        Future<RcsContactUceCapability> future = mExecutorService.submit(() -> {
+            checkUceControllerState();
+            return mUceController.addRegistrationOverrideCapabilities(featureTags);
+        });
+
+        try {
+            return future.get();
+        } catch (ExecutionException | InterruptedException e) {
+            Log.w(LOG_TAG, "addUceRegistrationOverride exception: " + e);
+            Throwable cause = e.getCause();
+            if (cause instanceof ImsException) {
+                throw (ImsException) cause;
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Remove existing feature tags to the Set used to calculate the capabilities in PUBLISH.
+     */
+    public RcsContactUceCapability removeUceRegistrationOverride(
+            Set<String> featureTags) throws ImsException {
+        Future<RcsContactUceCapability> future = mExecutorService.submit(() -> {
+            checkUceControllerState();
+            return mUceController.removeRegistrationOverrideCapabilities(featureTags);
+        });
+
+        try {
+            return future.get();
+        } catch (ExecutionException | InterruptedException e) {
+            Log.w(LOG_TAG, "removeUceRegistrationOverride exception: " + e);
+            Throwable cause = e.getCause();
+            if (cause instanceof ImsException) {
+                throw (ImsException) cause;
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Clear all overrides in the Set used to calculate the capabilities in PUBLISH.
+     */
+    public RcsContactUceCapability clearUceRegistrationOverride() throws ImsException {
+        Future<RcsContactUceCapability> future = mExecutorService.submit(() -> {
+            checkUceControllerState();
+            return mUceController.clearRegistrationOverrideCapabilities();
+        });
+
+        try {
+            return future.get();
+        } catch (ExecutionException | InterruptedException e) {
+            Log.w(LOG_TAG, "clearUceRegistrationOverride exception: " + e);
+            Throwable cause = e.getCause();
+            if (cause instanceof ImsException) {
+                throw (ImsException) cause;
+            }
+            return null;
+        }
+    }
+
+    /**
+     * @return current RcsContactUceCapability instance that will be used for PUBLISH.
+     */
+    public RcsContactUceCapability getLatestRcsContactUceCapability() throws ImsException {
+        Future<RcsContactUceCapability> future = mExecutorService.submit(() -> {
+            checkUceControllerState();
+            return mUceController.getLatestRcsContactUceCapability();
+        });
+
+        try {
+            return future.get();
+        } catch (ExecutionException | InterruptedException e) {
+            Log.w(LOG_TAG, "getLatestRcsContactUceCapability exception: " + e);
+            Throwable cause = e.getCause();
+            if (cause instanceof ImsException) {
+                throw (ImsException) cause;
+            }
+            return null;
+        }
+    }
+
+    /**
+     *
+     * @return The last PIDF XML sent to the IMS stack to be published.
+     */
+    public String getLastPidfXml() throws ImsException {
+        Future<String> future = mExecutorService.submit(() -> {
+            checkUceControllerState();
+            return mUceController.getLastPidfXml();
+        });
+
+        try {
+            return future.get();
+        } catch (ExecutionException | InterruptedException e) {
+            Log.w(LOG_TAG, "getLastPidfXml exception: " + e);
+            Throwable cause = e.getCause();
+            if (cause instanceof ImsException) {
+                throw (ImsException) cause;
+            }
+            return null;
+        }
+    }
+
+    /**
      * Register the Publish state changed callback.
      *
      * @throws ImsException if the ImsService connected to this controller is currently down.
diff --git a/testapps/TestRcsApp/TestApp/AndroidManifest.xml b/testapps/TestRcsApp/TestApp/AndroidManifest.xml
index 2eea909..4e40120 100644
--- a/testapps/TestRcsApp/TestApp/AndroidManifest.xml
+++ b/testapps/TestRcsApp/TestApp/AndroidManifest.xml
@@ -19,8 +19,8 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.google.android.sample.rcsclient"
-    android:versionCode="6"
-    android:versionName="1.0.5">
+    android:versionCode="9"
+    android:versionName="1.0.8">
 
     <uses-sdk
         android:minSdkVersion="30"
diff --git a/testapps/TestRcsApp/TestApp/res/layout/provision_layout.xml b/testapps/TestRcsApp/TestApp/res/layout/provision_layout.xml
index d98dde2..a70cd4a 100644
--- a/testapps/TestRcsApp/TestApp/res/layout/provision_layout.xml
+++ b/testapps/TestRcsApp/TestApp/res/layout/provision_layout.xml
@@ -11,12 +11,30 @@
         android:layout_height="wrap_content"
         android:orientation="vertical">
 
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/rcs_profile"
+                android:textSize="15dp"
+                android:textStyle="bold" />
+
+            <Spinner
+                android:id="@+id/rcs_profile_list"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+        </LinearLayout>
+
         <Button
             android:id="@+id/provisioning_register_btn"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginTop="10dp"
-            android:text="register"
+            android:text="@string/register_provisioning_callback"
             android:textAllCaps="false" />
 
         <Button
@@ -24,7 +42,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginTop="10dp"
-            android:text="unregister"
+            android:text="@string/unregister_provisioning_callback"
             android:textAllCaps="false" />
 
         <Button
@@ -33,7 +51,7 @@
             android:layout_height="wrap_content"
             android:layout_marginTop="10dp"
             android:layout_marginBottom="10dp"
-            android:text="isRcsVolteSingleRegCapable"
+            android:text="@string/isRcsVolteSingleRegCapable"
             android:textAllCaps="false" />
 
         <TextView
diff --git a/testapps/TestRcsApp/TestApp/res/values/donottranslate_strings.xml b/testapps/TestRcsApp/TestApp/res/values/donottranslate_strings.xml
index a193b46..502874f 100644
--- a/testapps/TestRcsApp/TestApp/res/values/donottranslate_strings.xml
+++ b/testapps/TestRcsApp/TestApp/res/values/donottranslate_strings.xml
@@ -6,6 +6,10 @@
     <string name="gba_test">GBA Test</string>
     <string name="test_msg_client">TestMessageClient</string>
     <string name="db_client">DBClient</string>
+    <string name="rcs_profile">RcsProfile:</string>
+    <string name="register_provisioning_callback">registerProvisioningCallback</string>
+    <string name="unregister_provisioning_callback">unRegisterProvisioningCallback</string>
+    <string name="isRcsVolteSingleRegCapable">isRcsVolteSingleRegCapable</string>
     <string name="result">Result:</string>
     <string name="callback_result">Callback Result:</string>
     <string name="initialize_delegate">initializeSipDelegate</string>
@@ -49,6 +53,10 @@
     <string name="registration_failed">Registration failed</string>
     <string name="version_info">Version: %s</string>
 
+    <string-array name="rcs_profile">
+        <item>UP_1.0</item>
+        <item>UP_2.3</item>
+    </string-array>
     <string-array name="organization">
         <item>NONE</item>
         <item>3GPP</item>
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java
index bb1283a..8f7e6a8 100644
--- a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java
@@ -49,10 +49,10 @@
 public class ChatActivity extends AppCompatActivity {
 
     public static final String EXTRA_REMOTE_PHONE_NUMBER = "REMOTE_PHONE_NUMBER";
-    public static final String TELURI_PREFIX = "tel:";
     private static final String TAG = "TestRcsApp.ChatActivity";
     private static final int INIT_LIST = 1;
     private static final int SHOW_STATUS = 2;
+    private static final int EMPTY_MSG = 3;
     private static final float TEXT_SIZE = 20.0f;
     private static final int MARGIN_SIZE = 20;
     private static final long TIMEOUT_IN_MS = 10000L;
@@ -85,6 +85,9 @@
                     case SHOW_STATUS:
                         mTips.setText(msg.obj.toString());
                         break;
+                    case EMPTY_MSG:
+                        mNewMessage.setText("");
+                        break;
                     default:
                         Log.d(TAG, "unknown msg:" + msg.what);
                         break;
@@ -134,7 +137,7 @@
                     ChatActivity.this.getResources().getString(R.string.session_timeout)),
                     TIMEOUT_IN_MS);
             ChatManager.getInstance(getApplicationContext(), mSubId).initChatSession(
-                    TELURI_PREFIX + mDestNumber, new SessionStateCallback() {
+                    mDestNumber, new SessionStateCallback() {
                         @Override
                         public void onSuccess() {
                             Log.i(TAG, "session init succeeded");
@@ -176,7 +179,8 @@
                         ChatManager.getInstance(getApplicationContext(), mSubId).addNewMessage(
                                 mNewMessage.getText().toString(), ChatManager.SELF, mDestNumber);
                         ChatManager.getInstance(getApplicationContext(), mSubId).sendMessage(
-                                TELURI_PREFIX + mDestNumber, mNewMessage.getText().toString());
+                                mDestNumber, mNewMessage.getText().toString());
+                        mHandler.sendMessage(mHandler.obtainMessage(EMPTY_MSG));
                     }
                 });
             });
@@ -259,8 +263,7 @@
     protected void onDestroy() {
         super.onDestroy();
         Log.i(TAG, "onDestroy");
-        ChatManager.getInstance(getApplicationContext(), mSubId).terminateSession(
-                TELURI_PREFIX + mDestNumber);
+        ChatManager.getInstance(getApplicationContext(), mSubId).terminateSession(mDestNumber);
     }
 
     @Override
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java
index aa90487..0c2996c 100644
--- a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.SharedPreferences;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -32,7 +33,11 @@
 import android.text.method.ScrollingMovementMethod;
 import android.util.Log;
 import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
 import android.widget.Button;
+import android.widget.Spinner;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
@@ -47,15 +52,27 @@
 public class ProvisioningActivity extends AppCompatActivity {
 
     private static final String TAG = "TestRcsApp.ProvisioningActivity";
+    private static final String UP_10 = "UP_1.0";
+    private static final String UP_23 = "UP_2.3";
+    private static final String V_6 = "6.0";
+    private static final String V_9 = "9.0";
+    private static final String RCS_CONFIG = "CONFIG";
+    private static final String RCS_PROFILE = "RCS_PROFILE";
+    private static final String RCS_VERSION = "RCS_VERSION";
     private static final int MSG_RESULT = 1;
+
     private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
     private int mDefaultSmsSubId;
     private ProvisioningManager mProvisioningManager;
+    private Spinner mRcsProfileSpinner;
+    private String mRcsVersion;
+    private String mRcsProfile;
     private Button mRegisterButton;
     private Button mUnRegisterButton;
     private Button mIsCapableButton;
     private TextView mSingleRegResult;
     private TextView mCallbackResult;
+    private SharedPreferences mPref;
     private SingleRegCapabilityReceiver mSingleRegCapabilityReceiver;
     private boolean mIsRegistered = false;
     private Handler mHandler;
@@ -85,10 +102,10 @@
             };
 
     // Static configuration.
-    private static RcsClientConfiguration getDefaultClientConfiguration() {
+    private RcsClientConfiguration getDefaultClientConfiguration() {
         return new RcsClientConfiguration(
-                /*rcsVersion=*/ "6.0",
-                /*rcsProfile=*/ "UP_1.0",
+                /*rcsVersion=*/ mRcsVersion,
+                /*rcsProfile=*/ mRcsProfile,
                 /*clientVendor=*/ "Goog",
                 /*clientVersion=*/ "RCSAndrd-1.0");
     }
@@ -112,6 +129,8 @@
                 }
             }
         };
+        mPref = getSharedPreferences(RCS_CONFIG, MODE_PRIVATE);
+        initRcsProfile();
     }
 
     @Override
@@ -130,7 +149,7 @@
         super.onDestroy();
         this.unregisterReceiver(mSingleRegCapabilityReceiver);
         if (mIsRegistered) {
-            mProvisioningManager.unregisterRcsProvisioningChangedCallback(mCallback);
+            mProvisioningManager.unregisterRcsProvisioningCallback(mCallback);
         }
     }
 
@@ -157,9 +176,9 @@
                 Log.i(TAG, "Using configuration: " + getDefaultClientConfiguration());
                 try {
                     Log.i(TAG, "setRcsClientConfiguration()");
-                    Log.i(TAG, "registerRcsProvisioningChangedCallback()");
+                    Log.i(TAG, "registerRcsProvisioningCallback()");
                     mProvisioningManager.setRcsClientConfiguration(getDefaultClientConfiguration());
-                    mProvisioningManager.registerRcsProvisioningChangedCallback(mExecutorService,
+                    mProvisioningManager.registerRcsProvisioningCallback(mExecutorService,
                             mCallback);
                     mIsRegistered = true;
                 } catch (ImsException e) {
@@ -173,7 +192,7 @@
         });
         mUnRegisterButton.setOnClickListener(view -> {
             if (mProvisioningManager != null) {
-                mProvisioningManager.unregisterRcsProvisioningChangedCallback(mCallback);
+                mProvisioningManager.unregisterRcsProvisioningCallback(mCallback);
                 setClickable(mRegisterButton, false);
                 setClickable(mRegisterButton, true);
                 mIsRegistered = false;
@@ -206,6 +225,44 @@
         return SubscriptionManager.isValidSubscriptionId(mDefaultSmsSubId);
     }
 
+    private void initRcsProfile() {
+        mRcsProfileSpinner = findViewById(R.id.rcs_profile_list);
+        ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
+                R.array.rcs_profile, android.R.layout.simple_spinner_item);
+        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        mRcsProfileSpinner.setAdapter(adapter);
+        mRcsProfileSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+                Log.i(TAG, "rcs profile position:" + position);
+                switch (position) {
+                    case 0:
+                        mRcsProfile = UP_10;
+                        mRcsVersion = V_6;
+                        break;
+                    case 1:
+                        mRcsProfile = UP_23;
+                        mRcsVersion = V_9;
+                        break;
+                    default:
+                        Log.e(TAG, "invalid position:" + position);
+                        return;
+                }
+                mPref.edit().putString(RCS_PROFILE, mRcsProfile)
+                        .putString(RCS_VERSION, mRcsVersion)
+                        .commit();
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> parent) {
+                // TODO Auto-generated method stub
+            }
+        });
+        mRcsProfile = mPref.getString(RCS_PROFILE, UP_10);
+        mRcsVersion = mPref.getString(RCS_VERSION, V_6);
+        mRcsProfileSpinner.setSelection(mRcsProfile.equals(UP_10) ? 0 : 1);
+    }
+
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         if (item.getItemId() == android.R.id.home) {
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java
index bbecbc2..0fae8f6 100644
--- a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java
@@ -46,6 +46,7 @@
 public class UceActivity extends AppCompatActivity {
 
     private static final String TAG = "TestRcsApp.UceActivity";
+    private static final String TELURI_PREFIX = "tel:";
     private Button mCapabilityButton;
     private Button mAvailabilityButton;
     private TextView mCapabilityResult;
@@ -106,7 +107,7 @@
 
                             public void onComplete() {
                                 Log.i(TAG, "onComplete()");
-                                mCapabilityResult.append("complete");
+                                mCapabilityResult.append("onComplete");
 
                             }
 
@@ -146,7 +147,7 @@
 
                             public void onComplete() {
                                 Log.i(TAG, "onComplete()");
-                                mCapabilityResult.append("complete");
+                                mCapabilityResult.append("onComplete");
 
                             }
 
@@ -174,7 +175,7 @@
             for (String number : numbers) {
                 String formattedNumber = NumberUtils.formatNumber(this, number);
                 if (formattedNumber != null) {
-                    contactList.add(Uri.parse(ChatActivity.TELURI_PREFIX + formattedNumber));
+                    contactList.add(Uri.parse(TELURI_PREFIX + formattedNumber));
                 } else {
                     Log.w(TAG, "number formatted improperly, skipping: " + number);
                 }
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/ChatManager.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/ChatManager.java
index 4489349..399a860 100644
--- a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/ChatManager.java
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/ChatManager.java
@@ -55,6 +55,7 @@
 public class ChatManager {
     public static final String SELF = "self";
     private static final String TAG = "TestRcsApp.ChatManager";
+    private static final String TELURI_PREFIX = "tel:";
     private static AddressFactory sAddressFactory = new AddressFactoryImpl();
     private static HashMap<Integer, ChatManager> sChatManagerInstances = new HashMap<>();
     private final ExecutorService mFixedThreadPool = Executors.newFixedThreadPool(5);
@@ -65,13 +66,14 @@
     private SimpleRcsClient mSimpleRcsClient;
     private State mState;
     private int mSubId;
-    private HashMap<URI, SimpleChatSession> mContactSessionMap = new HashMap<>();
+    private HashMap<String, SimpleChatSession> mContactSessionMap = new HashMap<>();
     private RcsStateChangedCallback mRcsStateChangedCallback;
 
     private ChatManager(Context context, int subId) {
         mContext = context;
         mSubId = subId;
-        mProvisioningController = StaticConfigProvisioningController.createForSubscriptionId(subId);
+        mProvisioningController = StaticConfigProvisioningController.createForSubscriptionId(subId,
+                context);
         ImsManager imsManager = mContext.getSystemService(ImsManager.class);
         mRegistrationController = new RegistrationControllerImpl(subId, mFixedThreadPool,
                 imsManager);
@@ -89,15 +91,14 @@
             mRcsStateChangedCallback.notifyStateChange(oldState, newState);
         });
         mImsService.setListener((session) -> {
-            Log.i(TAG, "onIncomingSession()");
-            mContactSessionMap.put(session.getRemoteUri(), session);
+            Log.i(TAG, "onIncomingSession():" + session.getRemoteUri());
+            String phoneNumber = getNumberFromUri(session.getRemoteUri().toString());
+            mContactSessionMap.put(phoneNumber, session);
             session.setListener(
                     // implement onMessageReceived()
                     (message) -> {
                         mFixedThreadPool.execute(() -> {
                             String msg = message.content();
-                            String phoneNumber = getNumberFromUri(
-                                    session.getRemoteUri().toString());
                             if (TextUtils.isEmpty(phoneNumber)) {
                                 Log.i(TAG, "dest number is empty, uri:"
                                         + session.getRemoteUri());
@@ -172,33 +173,34 @@
 
     /**
      * Initiate 1 to 1 chat session.
-     * @param telUriContact destination tel Uri.
+     *
+     * @param contact  destination phone number.
      * @param callback callback for session state.
      */
-    public void initChatSession(String telUriContact, SessionStateCallback callback) {
+    public void initChatSession(String contact, SessionStateCallback callback) {
         if (mState != State.REGISTERED) {
             Log.i(TAG, "Could not init session due to State = " + mState);
             return;
         }
-        URI uri = createUri(telUriContact);
-        if (mContactSessionMap.containsKey(uri)) {
+        Log.i(TAG, "initChatSession contact: " + contact);
+        if (mContactSessionMap.containsKey(contact)) {
             callback.onSuccess();
-            Log.i(TAG, "uri exists");
+            Log.i(TAG, "contact exists");
             return;
         }
         Futures.addCallback(
-                mImsService.startOriginatingChatSession(telUriContact),
+                mImsService.startOriginatingChatSession(TELURI_PREFIX + contact),
                 new FutureCallback<SimpleChatSession>() {
                     @Override
                     public void onSuccess(SimpleChatSession chatSession) {
-                        mContactSessionMap.put(chatSession.getRemoteUri(), chatSession);
+                        String phoneNumber = getNumberFromUri(
+                                chatSession.getRemoteUri().toString());
+                        mContactSessionMap.put(phoneNumber, chatSession);
                         chatSession.setListener(
                                 // implement onMessageReceived()
                                 (message) -> {
                                     mFixedThreadPool.execute(() -> {
                                         String msg = message.content();
-                                        String phoneNumber = getNumberFromUri(
-                                                chatSession.getRemoteUri().toString());
                                         if (TextUtils.isEmpty(phoneNumber)) {
                                             Log.i(TAG, "dest number is empty, uri:"
                                                     + chatSession.getRemoteUri());
@@ -221,13 +223,14 @@
 
     /**
      * Send a chat message.
-     * @param telUriContact destination tel Uri.
+     *
+     * @param contact destination phone number.
      * @param message chat message.
      */
-    public void sendMessage(String telUriContact, String message) {
-        SimpleChatSession chatSession = mContactSessionMap.get(createUri(telUriContact));
+    public void sendMessage(String contact, String message) {
+        SimpleChatSession chatSession = mContactSessionMap.get(contact);
         if (chatSession == null) {
-            Log.i(TAG, "session is unavailable for telUriContact = " + telUriContact);
+            Log.i(TAG, "session is unavailable for contact = " + contact);
             return;
         }
         chatSession.sendMessage(message);
@@ -239,22 +242,23 @@
 
     /**
      * Terminate the chat session.
-     * @param telUriContact destination tel Uri
+     *
+     * @param contact destination phone number.
      */
-    public void terminateSession(String telUriContact) {
+    public void terminateSession(String contact) {
         Log.i(TAG, "terminateSession");
-        URI uri = createUri(telUriContact);
-        SimpleChatSession chatSession = mContactSessionMap.get(uri);
+        SimpleChatSession chatSession = mContactSessionMap.get(contact);
         if (chatSession == null) {
-            Log.i(TAG, "session is unavailable for telUriContact = " + telUriContact);
+            Log.i(TAG, "session is unavailable for contact = " + contact);
             return;
         }
         chatSession.terminate();
-        mContactSessionMap.remove(uri);
+        mContactSessionMap.remove(contact);
     }
 
     /**
      * Insert chat information into database.
+     *
      * @param message chat message.
      * @param src source phone number.
      * @param dest destination phone number.
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpManager.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpManager.java
index 81abe89..58a6eef 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpManager.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpManager.java
@@ -31,17 +31,19 @@
 /** Provides creating and managing {@link MsrpSession} */
 public class MsrpManager {
     private final ImsPdnNetworkFetcher imsPdnNetworkFetcher;
+    private Context context;
 
     public MsrpManager(Context context) {
+        this.context = context;
         imsPdnNetworkFetcher = new ImsPdnNetworkFetcher(context);
     }
 
-    private static MsrpSession createMsrpSession(ConnectivityManager manager,
+    private MsrpSession createMsrpSession(ConnectivityManager manager,
             Network network, String host, int port, String localIp, int localPort,
             MsrpSessionListener listener) throws IOException {
         Socket socket = network.getSocketFactory().createSocket(host, port,
                 InetAddress.getByName(localIp), localPort);
-        MsrpSession msrpSession = new MsrpSession(manager,
+        MsrpSession msrpSession = new MsrpSession(manager, context,
                 network, socket, listener);
         Thread thread = new Thread(msrpSession::run);
         thread.start();
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java
index 59a0c0e..1c461fe 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java
@@ -19,6 +19,7 @@
 import static com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpChunk.Method.SEND;
 import static com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpChunk.Method.UNKNOWN;
 
+import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.Network;
 import android.net.QosCallback;
@@ -27,6 +28,7 @@
 import android.net.QosSessionAttributes;
 import android.net.QosSocketInfo;
 import android.util.Log;
+import android.widget.Toast;
 
 import androidx.annotation.NonNull;
 import androidx.concurrent.futures.CallbackToFutureAdapter;
@@ -49,6 +51,8 @@
  * Provides MSRP sending and receiving messages ability.
  */
 public class MsrpSession {
+    private static final String DEDICATED_BEARER_SUCCESS = "Dedicated bearer succeeded";
+    private static final String DEDICATED_BEARER_ERROR = "Dedicated bearer error";
     private final Network network;
     private final Socket socket;
     private final InputStream input;
@@ -59,11 +63,13 @@
     private final MsrpSessionListener listener;
     private final ConnectivityManager connectivityManager;
     private final String LOG_TAG = MsrpSession.class.getSimpleName();
+    private final Context context;
 
     /** Creates a new MSRP session on the given listener and the provided streams. */
-    MsrpSession(ConnectivityManager connectivityManager, Network network, Socket socket,
-            MsrpSessionListener listener) throws IOException {
+    MsrpSession(ConnectivityManager connectivityManager, Context context, Network network,
+            Socket socket, MsrpSessionListener listener) throws IOException {
         this.connectivityManager = connectivityManager;
+        this.context = context;
         this.network = network;
         this.socket = socket;
         this.input = socket.getInputStream();
@@ -76,6 +82,7 @@
     private final QosCallback qosCallback = new QosCallback() {
         @Override
         public void onError(@NonNull QosCallbackException exception) {
+            Toast.makeText(context, DEDICATED_BEARER_ERROR, Toast.LENGTH_SHORT).show();
             Log.e(LOG_TAG, "onError: " + exception.toString());
             super.onError(exception);
         }
@@ -83,6 +90,7 @@
         @Override
         public void onQosSessionAvailable(@NonNull QosSession session,
                 @NonNull QosSessionAttributes sessionAttributes) {
+            Toast.makeText(context, DEDICATED_BEARER_SUCCESS, Toast.LENGTH_SHORT).show();
             Log.d(LOG_TAG, "onQosSessionAvailable: " + session.toString() + ", "
                     + sessionAttributes.toString());
             super.onQosSessionAvailable(session, sessionAttributes);
@@ -90,6 +98,7 @@
 
         @Override
         public void onQosSessionLost(@NonNull QosSession session) {
+            Toast.makeText(context, DEDICATED_BEARER_ERROR, Toast.LENGTH_SHORT).show();
             Log.e(LOG_TAG, "onQosSessionLost: " + session.toString());
             super.onQosSessionLost(session);
         }
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java
index 350f43c..b8b1f21 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java
@@ -16,6 +16,8 @@
 
 package com.android.libraries.rcs.simpleclient.provisioning;
 
+import android.content.Context;
+import android.content.SharedPreferences;
 import android.os.Build.VERSION_CODES;
 import android.telephony.SubscriptionManager;
 import android.telephony.ims.ImsException;
@@ -24,15 +26,15 @@
 import android.telephony.ims.RcsClientConfiguration;
 import android.util.Log;
 
-import java.util.Optional;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.RequiresPermission;
 import androidx.annotation.VisibleForTesting;
 
+import java.util.Optional;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
 /**
  * "Fake" provisioning implementation for supplying a static config when testing ProvisioningManager
  * is unnecessary. State changes are invoked manually.
@@ -45,29 +47,35 @@
     private Optional<RcsProvisioningCallback> storedCallback = Optional.empty();
     private Optional<ProvisioningStateChangeCallback> stateChangeCallback = Optional.empty();
     private Optional<byte[]> configXmlData = Optional.empty();
+    private Context context;
 
-    private StaticConfigProvisioningController(int subId) {
+    private StaticConfigProvisioningController(int subId, Context context) {
         this.provisioningManager = ProvisioningManager.createForSubscriptionId(subId);
+        this.context = context;
     }
 
     @RequiresApi(api = VERSION_CODES.R)
-    public static StaticConfigProvisioningController createWithDefaultSubscriptionId() {
+    public static StaticConfigProvisioningController createWithDefaultSubscriptionId(
+            Context context) {
         return new StaticConfigProvisioningController(
-                SubscriptionManager.getActiveDataSubscriptionId());
+                SubscriptionManager.getActiveDataSubscriptionId(), context);
     }
 
-    public static StaticConfigProvisioningController createForSubscriptionId(int subscriptionId) {
-        return new StaticConfigProvisioningController(subscriptionId);
+    /** Create ProvisioningController */
+    public static StaticConfigProvisioningController createForSubscriptionId(int subscriptionId,
+            Context context) {
+        return new StaticConfigProvisioningController(subscriptionId, context);
     }
 
     // Static configuration.
-    private static RcsClientConfiguration getDefaultClientConfiguration() {
+    private RcsClientConfiguration getDefaultClientConfiguration() {
+        SharedPreferences pref = context.getSharedPreferences("CONFIG", context.MODE_PRIVATE);
 
         return new RcsClientConfiguration(
-                /*rcsVersion=*/ "6.0",
-                /*rcsProfile=*/ "UP_2.3",
+                /*rcsVersion=*/ pref.getString("RCS_VERSION", "6.0"),
+                /*rcsProfile=*/ pref.getString("RCS_PROFILE", "UP_1.0"),
                 /*clientVendor=*/ "Goog",
-                /*clientVersion=*/ "RCSAndrd-1.0");//"RCS fake library 1.0");
+                /*clientVersion=*/ "RCSAndrd-1.0");
     }
 
     @Override
@@ -136,7 +144,7 @@
 
         Log.i(TAG, "Registering the callback.");
         synchronized (this) {
-            provisioningManager.registerRcsProvisioningChangedCallback(executorService, callback);
+            provisioningManager.registerRcsProvisioningCallback(executorService, callback);
             storedCallback = Optional.of(callback);
         }
     }
@@ -147,7 +155,7 @@
             RcsProvisioningCallback callback =
                     storedCallback.orElseThrow(
                             () -> new IllegalStateException("No callback present."));
-            provisioningManager.unregisterRcsProvisioningChangedCallback(callback);
+            provisioningManager.unregisterRcsProvisioningCallback(callback);
             storedCallback = Optional.empty();
         }
     }
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/ChatServiceException.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/ChatServiceException.java
index 94850fd..bc2c611 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/ChatServiceException.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/ChatServiceException.java
@@ -31,6 +31,7 @@
 public final class ChatServiceException extends Exception {
 
     public static final int CODE_ERROR_UNSPECIFIED = 0;
+    public static final int CODE_ERROR_SEND_MESSAGE_FAILED = 1;
     private int mCode = CODE_ERROR_UNSPECIFIED;
 
     /**
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/SimpleChatSession.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/SimpleChatSession.java
index 4cc474c..fbeb205 100644
--- a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/SimpleChatSession.java
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/SimpleChatSession.java
@@ -17,6 +17,7 @@
 package com.android.libraries.rcs.simpleclient.service.chat;
 
 import static com.android.libraries.rcs.simpleclient.protocol.cpim.CpimUtils.CPIM_CONTENT_TYPE;
+import static com.android.libraries.rcs.simpleclient.service.chat.ChatServiceException.CODE_ERROR_SEND_MESSAGE_FAILED;
 import static com.android.libraries.rcs.simpleclient.service.chat.ChatServiceException.CODE_ERROR_UNSPECIFIED;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
@@ -101,11 +102,12 @@
     }
 
     /** Send a text message via MSRP session associated with this session. */
-    public void sendMessage(String msg) {
+    public ListenableFuture<Void> sendMessage(String msg) {
         MsrpSession session = mMsrpSession;
         if (session == null || mRemoteSdp == null || mLocalSdp == null) {
             Log.e(TAG, "Session is not established");
-            return;
+            return Futures.immediateFailedFuture(
+                    new IllegalStateException("Session is not established"));
         }
 
         // Build a new CPIM message and send it out through the MSRP session.
@@ -133,27 +135,21 @@
                         .build();
 
         Log.i(TAG, "Send a MSRP chunk: " + msrpChunk);
-        Futures.addCallback(
-                session.send(msrpChunk),
-                new FutureCallback<MsrpChunk>() {
-                    @Override
-                    public void onSuccess(MsrpChunk result) {
-                        if (result.responseCode() != 200) {
-                            Log.d(
-                                    TAG,
-                                    "Received error response id="
-                                            + result.transactionId()
-                                            + " code="
-                                            + result.responseCode());
-                        }
-                    }
-
-                    @Override
-                    public void onFailure(Throwable t) {
-                        Log.d(TAG, "Failed to send msrp chunk", t);
-                    }
-                },
-                MoreExecutors.directExecutor());
+        return Futures.transformAsync(session.send(msrpChunk), result -> {
+            if (result == null) {
+                return Futures.immediateFailedFuture(
+                        new ChatServiceException("Failed to send a chunk",
+                                CODE_ERROR_SEND_MESSAGE_FAILED));
+            }
+            if (result.responseCode() != 200) {
+                Log.d(TAG, "Received error response id=" + result.transactionId()
+                        + " code=" + result.responseCode());
+                return Futures.immediateFailedFuture(
+                        new ChatServiceException("Msrp response code: " + result.responseCode(),
+                                CODE_ERROR_SEND_MESSAGE_FAILED));
+            }
+            return Futures.immediateFuture(null);
+        }, MoreExecutors.directExecutor());
     }
 
     /** Start outgoing chat session. */
diff --git a/tests/src/com/android/phone/RcsProvisioningMonitorTest.java b/tests/src/com/android/phone/RcsProvisioningMonitorTest.java
index f77dd55..28c4390 100644
--- a/tests/src/com/android/phone/RcsProvisioningMonitorTest.java
+++ b/tests/src/com/android/phone/RcsProvisioningMonitorTest.java
@@ -475,13 +475,13 @@
     public void testRegisterThenUnregisterCallback() throws Exception {
         createMonitor(1);
 
-        boolean result = mRcsProvisioningMonitor.registerRcsProvisioningChangedCallback(
+        boolean result = mRcsProvisioningMonitor.registerRcsProvisioningCallback(
                 FAKE_SUB_ID_BASE, mCallback);
 
         assertTrue(result);
         verify(mIImsConfig, times(1)).addRcsConfigCallback(eq(mCallback));
 
-        result = mRcsProvisioningMonitor.unregisterRcsProvisioningChangedCallback(
+        result = mRcsProvisioningMonitor.unregisterRcsProvisioningCallback(
                 FAKE_SUB_ID_BASE, mCallback);
 
         assertTrue(result);
@@ -494,7 +494,7 @@
     public void testCallbackRemovedWhenSubInfoChanged() throws Exception {
         createMonitor(1);
 
-        boolean result = mRcsProvisioningMonitor.registerRcsProvisioningChangedCallback(
+        boolean result = mRcsProvisioningMonitor.registerRcsProvisioningCallback(
                 FAKE_SUB_ID_BASE, mCallback);
         makeFakeActiveSubIds(0);
         mExecutor.execute(() -> mSubChangedListener.onSubscriptionsChanged());
@@ -510,7 +510,7 @@
     public void testCallbackRemovedWhenDmaChanged() throws Exception {
         createMonitor(1);
 
-        boolean result = mRcsProvisioningMonitor.registerRcsProvisioningChangedCallback(
+        boolean result = mRcsProvisioningMonitor.registerRcsProvisioningCallback(
                 FAKE_SUB_ID_BASE, mCallback);
         updateDefaultMessageApplication(DEFAULT_MESSAGING_APP2);
         processAllMessages();
@@ -524,7 +524,7 @@
     @SmallTest
     public void testRcsConnectedAndDisconnected() throws Exception {
         createMonitor(1);
-        mRcsProvisioningMonitor.registerRcsProvisioningChangedCallback(
+        mRcsProvisioningMonitor.registerRcsProvisioningCallback(
                 FAKE_SUB_ID_BASE, mCallback);
 
         verify(mIImsConfig, times(1))
diff --git a/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java b/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
index eecbd2e..da614fc 100644
--- a/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
+++ b/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
@@ -58,6 +58,8 @@
 @RunWith(AndroidJUnit4.class)
 public class RcsFeatureControllerTest extends TelephonyTestBase {
 
+    private static final int TEST_SUB_ID = 1;
+
     private static final ImsReasonInfo REASON_DISCONNECTED = new ImsReasonInfo(
             ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN, 0, "test");
 
@@ -96,7 +98,7 @@
         // Connect the RcsFeatureManager
         mConnectorListener.getValue().connectionReady(mFeatureManager);
 
-        verify(mFeatureManager).updateCapabilities();
+        verify(mFeatureManager).updateCapabilities(TEST_SUB_ID);
         verify(mFeatureManager).registerImsRegistrationCallback(any());
         verify(mMockFeature).onRcsConnected(mFeatureManager);
 
@@ -132,16 +134,16 @@
         mConnectorListener.getValue().connectionReady(mFeatureManager);
 
         try {
-            controller.registerImsRegistrationCallback(0 /*subId*/, regCb);
-            controller.registerRcsAvailabilityCallback(0 /*subId*/, capCb);
+            controller.registerImsRegistrationCallback(TEST_SUB_ID, regCb);
+            controller.registerRcsAvailabilityCallback(TEST_SUB_ID, capCb);
             controller.isCapable(RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE,
                     ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
             controller.isAvailable(RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE,
                     ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
             controller.getRegistrationTech(integer -> {
             });
-            verify(mFeatureManager).registerImsRegistrationCallback(0, regCb);
-            verify(mFeatureManager).registerRcsAvailabilityCallback(0, capCb);
+            verify(mFeatureManager).registerImsRegistrationCallback(TEST_SUB_ID, regCb);
+            verify(mFeatureManager).registerRcsAvailabilityCallback(TEST_SUB_ID, capCb);
             verify(mFeatureManager).isCapable(
                     RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE,
                     ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
@@ -153,10 +155,10 @@
             fail("ImsException not expected.");
         }
 
-        controller.unregisterImsRegistrationCallback(0, regCb);
-        controller.unregisterRcsAvailabilityCallback(0, capCb);
-        verify(mFeatureManager).unregisterImsRegistrationCallback(0, regCb);
-        verify(mFeatureManager).unregisterRcsAvailabilityCallback(0, capCb);
+        controller.unregisterImsRegistrationCallback(TEST_SUB_ID, regCb);
+        controller.unregisterRcsAvailabilityCallback(TEST_SUB_ID, capCb);
+        verify(mFeatureManager).unregisterImsRegistrationCallback(TEST_SUB_ID, regCb);
+        verify(mFeatureManager).unregisterRcsAvailabilityCallback(TEST_SUB_ID, capCb);
     }
 
     @Test
@@ -216,13 +218,13 @@
                 FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED);
 
         try {
-            controller.registerImsRegistrationCallback(0 /*subId*/, null /*callback*/);
+            controller.registerImsRegistrationCallback(TEST_SUB_ID, null /*callback*/);
             fail("ImsException expected for IMS registration.");
         } catch (ImsException e) {
             //expected
         }
         try {
-            controller.registerRcsAvailabilityCallback(0 /*subId*/, null /*callback*/);
+            controller.registerRcsAvailabilityCallback(TEST_SUB_ID, null /*callback*/);
             fail("ImsException expected for availability");
         } catch (ImsException e) {
             //expected
@@ -245,10 +247,25 @@
             assertNotNull(integer);
             assertEquals(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, integer.intValue());
         });
-        controller.unregisterImsRegistrationCallback(0, regCb);
-        controller.unregisterRcsAvailabilityCallback(0, capCb);
-        verify(mFeatureManager, never()).unregisterImsRegistrationCallback(0, regCb);
-        verify(mFeatureManager, never()).unregisterRcsAvailabilityCallback(0, capCb);
+        controller.unregisterImsRegistrationCallback(TEST_SUB_ID, regCb);
+        controller.unregisterRcsAvailabilityCallback(TEST_SUB_ID, capCb);
+        verify(mFeatureManager, never()).unregisterImsRegistrationCallback(TEST_SUB_ID, regCb);
+        verify(mFeatureManager, never()).unregisterRcsAvailabilityCallback(TEST_SUB_ID, capCb);
+    }
+
+    @Test
+    public void testCarrierConfigChanged() throws Exception {
+        RcsFeatureController controller = createFeatureController();
+        // Connect the RcsFeatureManager
+        mConnectorListener.getValue().connectionReady(mFeatureManager);
+        verify(mFeatureManager).updateCapabilities(TEST_SUB_ID);
+        controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
+
+        controller.onCarrierConfigChangedForSubscription();
+
+        verify(mFeatureManager, times(2)).updateCapabilities(TEST_SUB_ID);
+        verify(mMockFeature).onCarrierConfigChanged();
+        verify(mMockFeature, never()).onAssociatedSubscriptionUpdated(anyInt());
     }
 
     @Test
@@ -256,13 +273,13 @@
         RcsFeatureController controller = createFeatureController();
         // Connect the RcsFeatureManager
         mConnectorListener.getValue().connectionReady(mFeatureManager);
-        verify(mFeatureManager).updateCapabilities();
+        verify(mFeatureManager).updateCapabilities(TEST_SUB_ID);
         controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
 
-        controller.updateAssociatedSubscription(1 /*new sub id*/);
+        controller.updateAssociatedSubscription(2 /*new subId*/);
 
-        verify(mFeatureManager, times(2)).updateCapabilities();
-        verify(mMockFeature).onAssociatedSubscriptionUpdated(1 /*new sub id*/);
+        verify(mFeatureManager).updateCapabilities(2 /*new subId*/);
+        verify(mMockFeature).onAssociatedSubscriptionUpdated(2 /*new subId*/);
     }
 
     @Test
@@ -281,7 +298,7 @@
 
     private RcsFeatureController createFeatureController() {
         RcsFeatureController controller = new RcsFeatureController(mContext, 0 /*slotId*/,
-                mRegistrationFactory);
+                TEST_SUB_ID, mRegistrationFactory);
         controller.setFeatureConnectorFactory(mFeatureFactory);
         doReturn(mFeatureConnector).when(mFeatureFactory).create(any(), anyInt(),
                 mConnectorListener.capture(), any(), any());
diff --git a/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java b/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
index c367af3..39469b6 100644
--- a/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
+++ b/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
@@ -67,10 +67,12 @@
         super.setUp();
         doReturn(mFeatureConnector).when(mFeatureConnectorFactory).create(any(), anyInt(),
                 any(), any(), any());
-        mFeatureControllerSlot0 = createFeatureController(0 /*slotId*/);
-        mFeatureControllerSlot1 = createFeatureController(1 /*slotId*/);
-        doReturn(mFeatureControllerSlot0).when(mFeatureFactory).createController(any(), eq(0));
-        doReturn(mFeatureControllerSlot1).when(mFeatureFactory).createController(any(), eq(1));
+        mFeatureControllerSlot0 = createFeatureController(0 /*slotId*/, 1 /*subId*/);
+        mFeatureControllerSlot1 = createFeatureController(1 /*slotId*/, 2 /*subId*/);
+        doReturn(mFeatureControllerSlot0).when(mFeatureFactory).createController(any(), eq(0),
+                anyInt());
+        doReturn(mFeatureControllerSlot1).when(mFeatureFactory).createController(any(), eq(1),
+                anyInt());
         doReturn(mMockUceSlot0).when(mFeatureFactory).createUceControllerManager(any(), eq(0),
                 anyInt());
         doReturn(mMockUceSlot1).when(mFeatureFactory).createUceControllerManager(any(), eq(1),
@@ -227,7 +229,7 @@
     }
 
     @Test
-    public void testCarrierConfigUpdate() {
+    public void testCarrierConfigUpdateAssociatedSub() {
         setCarrierConfig(1 /*subId*/,
                 CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL,
                 true /*isEnabled*/);
@@ -251,6 +253,26 @@
     }
 
     @Test
+    public void testCarrierConfigNotifyFeatures() {
+        setCarrierConfig(1 /*subId*/,
+                CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL,
+                true /*isEnabled*/);
+        createRcsService(1 /*numSlots*/);
+        verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UceControllerManager.class);
+        verify(mFeatureControllerSlot0).connect();
+
+
+        // Send carrier config update twice with no update to subId
+        sendCarrierConfigChanged(0 /*slotId*/, 1 /*subId*/);
+        verify(mFeatureControllerSlot0).updateAssociatedSubscription(1);
+        verify(mFeatureControllerSlot0, never()).onCarrierConfigChangedForSubscription();
+        sendCarrierConfigChanged(0 /*slotId*/, 1 /*subId*/);
+        verify(mFeatureControllerSlot0, times(1)).updateAssociatedSubscription(1);
+        // carrier config changed should be sent here
+        verify(mFeatureControllerSlot0).onCarrierConfigChangedForSubscription();
+    }
+
+    @Test
     public void testCarrierConfigUpdateUceToNoUce() {
         setCarrierConfig(1 /*subId*/,
                 CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL,
@@ -334,10 +356,10 @@
         return service;
     }
 
-    private RcsFeatureController createFeatureController(int slotId) {
+    private RcsFeatureController createFeatureController(int slotId, int subId) {
         // Create a spy instead of a mock because TelephonyRcsService relies on state provided by
         // RcsFeatureController.
-        RcsFeatureController controller = spy(new RcsFeatureController(mContext, slotId,
+        RcsFeatureController controller = spy(new RcsFeatureController(mContext, slotId, subId,
                 mRegistrationFactory));
         controller.setFeatureConnectorFactory(mFeatureConnectorFactory);
         return controller;
diff --git a/tests/src/com/android/services/telephony/rcs/UceControllerManagerTest.java b/tests/src/com/android/services/telephony/rcs/UceControllerManagerTest.java
index 4148d13..82687f8 100644
--- a/tests/src/com/android/services/telephony/rcs/UceControllerManagerTest.java
+++ b/tests/src/com/android/services/telephony/rcs/UceControllerManagerTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
 import android.net.Uri;
@@ -94,11 +95,15 @@
     }
 
     @Test
-    public void testSubscriptionUpdated() throws Exception {
+    public void testSubIdAndCarrierConfigUpdate() throws Exception {
         UceControllerManager uceCtrlManager = getUceControllerManager();
 
-        uceCtrlManager.onAssociatedSubscriptionUpdated(mSubId);
+        // Updates with the same subId should not destroy the UceController
+        uceCtrlManager.onCarrierConfigChanged();
+        verify(mUceController, never()).onDestroy();
 
+        // Updates with different subIds should trigger the creation of a new controller.
+        uceCtrlManager.onAssociatedSubscriptionUpdated(mSubId + 1);
         verify(mUceController).onDestroy();
     }