Merge "Remap call diagnostic service constants to internal D2D constants" into sc-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 60d5986..ce56d92 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -97,6 +97,9 @@
     <protected-broadcast android:name= "android.telephony.action.MULTI_SIM_CONFIG_CHANGED" />
     <protected-broadcast android:name= "android.telephony.action.CARRIER_SIGNAL_RESET" />
 
+    <protected-broadcast android:name= "com.android.phone.settings.CARRIER_PROVISIONING" />
+    <protected-broadcast android:name= "com.android.phone.settings.TRIGGER_CARRIER_PROVISIONING" />
+
     <!-- For Vendor Debugging in Telephony -->
     <protected-broadcast android:name="android.telephony.action.ANOMALY_REPORTED" />
 
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index e1eacbd..63390c8 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -780,7 +780,7 @@
     <string name="clh_callFailed_protocol_Error_unspecified_txt" msgid="9203320572562697755">"تعذر إكمال المكالمة. رمز الخطأ 111."</string>
     <string name="clh_callFailed_interworking_unspecified_txt" msgid="7969686413930847182">"تعذر إكمال المكالمة. رمز الخطأ 127."</string>
     <string name="labelCallBarring" msgid="4180377113052853173">"منع الاتصال"</string>
-    <string name="sum_call_barring_enabled" msgid="5184331188926370824">"تفعيل"</string>
+    <string name="sum_call_barring_enabled" msgid="5184331188926370824">"مفعّلة"</string>
     <string name="sum_call_barring_disabled" msgid="5699448000600153096">"متوقف"</string>
     <string name="call_barring_baoc" msgid="7400892586336429326">"كل المكالمات الصادرة"</string>
     <string name="call_barring_baoc_enabled" msgid="3131509193386668182">"هل تريد إيقاف حظر كل المكالمات الصادرة؟"</string>
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 2c282f7..702dd7f 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -70,7 +70,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;
@@ -114,6 +113,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;
@@ -6153,7 +6153,7 @@
             @TelephonyManager.NrDualConnectivityState int nrDualConnectivityState) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "enableNRDualConnectivity");
-        if (isRadioInterfaceCapabilitySupported(
+        if (!isRadioInterfaceCapabilitySupported(
                 TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE)) {
             return TelephonyManager.ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED;
         }
@@ -6180,7 +6180,7 @@
         TelephonyPermissions
                 .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "isNRDualConnectivityEnabled");
-        if (isRadioInterfaceCapabilitySupported(
+        if (!isRadioInterfaceCapabilitySupported(
                 TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE)) {
             return false;
         }
@@ -6197,28 +6197,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.
      *
@@ -10130,6 +10108,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/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index 47666ef..07136d7 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -28,15 +28,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;
@@ -48,9 +54,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;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
@@ -124,6 +133,8 @@
     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";
@@ -178,6 +189,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 =
@@ -346,6 +399,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() {
@@ -1709,8 +1778,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) {
@@ -1722,6 +1791,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;
     }
@@ -1842,6 +1915,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) {
diff --git a/src/com/android/phone/callcomposer/CallComposerPictureManager.java b/src/com/android/phone/callcomposer/CallComposerPictureManager.java
index 3c9e27e..818994a 100644
--- a/src/com/android/phone/callcomposer/CallComposerPictureManager.java
+++ b/src/com/android/phone/callcomposer/CallComposerPictureManager.java
@@ -20,6 +20,7 @@
 import android.location.Location;
 import android.net.Uri;
 import android.os.OutcomeReceiver;
+import android.os.PersistableBundle;
 import android.os.UserHandle;
 import android.provider.CallLog;
 import android.telephony.CarrierConfigManager;
@@ -33,14 +34,12 @@
 import androidx.annotation.NonNull;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.phone.callcomposer.CallComposerPictureTransfer.PictureCallback;
 import com.android.phone.R;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
 import java.util.HashMap;
-import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executor;
@@ -53,6 +52,7 @@
 public class CallComposerPictureManager {
     private static final String TAG = CallComposerPictureManager.class.getSimpleName();
     private static final SparseArray<CallComposerPictureManager> sInstances = new SparseArray<>();
+    private static final String THREE_GPP_BOOTSTRAPPING = "3GPP-bootstrapping";
 
     public static CallComposerPictureManager getInstance(Context context, int subscriptionId) {
         synchronized (sInstances) {
@@ -104,7 +104,7 @@
 
     private final HashMap<UUID, String> mCachedServerUrls = new HashMap<>();
     private final HashMap<UUID, ImageData> mCachedImages = new HashMap<>();
-    private final Map<String, GbaCredentials> mCachedCredentials = new HashMap<>();
+    private GbaCredentials mCachedCredentials = null;
     private final int mSubscriptionId;
     private final TelephonyManager mTelephonyManager;
     private final Context mContext;
@@ -127,7 +127,8 @@
             return;
         }
 
-        String uploadUrl = mTelephonyManager.getCarrierConfig().getString(
+        PersistableBundle carrierConfig = mTelephonyManager.getCarrierConfig();
+        String uploadUrl = carrierConfig.getString(
                 CarrierConfigManager.KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING);
         if (TextUtils.isEmpty(uploadUrl)) {
             Log.e(TAG, "Call composer upload URL not configured in carrier config");
@@ -141,7 +142,7 @@
                 mSubscriptionId, uploadUrl, sExecutorService);
 
         AtomicBoolean hasRetried = new AtomicBoolean(false);
-        transfer.setCallback(new PictureCallback() {
+        transfer.setCallback(new CallComposerPictureTransfer.PictureCallback() {
             @Override
             public void onError(int error) {
                 callback.accept(Pair.create(null, error));
@@ -157,7 +158,7 @@
                 }
                 GbaCredentialsSupplier supplier =
                         (realm, executor) ->
-                                getGbaCredentials(credentialRefresh, realm, executor);
+                                getGbaCredentials(credentialRefresh, carrierConfig, executor);
 
                 sExecutorService.schedule(() -> transfer.uploadPicture(imageData, supplier),
                         backoffMillis, TimeUnit.MILLISECONDS);
@@ -174,7 +175,7 @@
         });
 
         transfer.uploadPicture(imageData,
-                (realm, executor) -> getGbaCredentials(false, realm, executor));
+                (realm, executor) -> getGbaCredentials(false, carrierConfig, executor));
     }
 
     public void handleDownloadFromServer(CallComposerPictureTransfer.Factory transferFactory,
@@ -187,11 +188,12 @@
             return;
         }
 
+        PersistableBundle carrierConfig = mTelephonyManager.getCarrierConfig();
         CallComposerPictureTransfer transfer = transferFactory.create(mContext,
                 mSubscriptionId, remoteUrl, sExecutorService);
 
         AtomicBoolean hasRetried = new AtomicBoolean(false);
-        transfer.setCallback(new PictureCallback() {
+        transfer.setCallback(new CallComposerPictureTransfer.PictureCallback() {
             @Override
             public void onError(int error) {
                 callback.accept(Pair.create(null, error));
@@ -207,7 +209,7 @@
                 }
                 GbaCredentialsSupplier supplier =
                         (realm, executor) ->
-                                getGbaCredentials(credentialRefresh, realm, executor);
+                                getGbaCredentials(credentialRefresh, carrierConfig, executor);
 
                 sExecutorService.schedule(() -> transfer.downloadPicture(supplier),
                         backoffMillis, TimeUnit.MILLISECONDS);
@@ -237,7 +239,8 @@
             }
         });
 
-        transfer.downloadPicture(((realm, executor) -> getGbaCredentials(false, realm, executor)));
+        transfer.downloadPicture(((realm, executor) ->
+                getGbaCredentials(false, carrierConfig, executor)));
     }
 
     public void storeUploadedPictureToCallLog(UUID id, Consumer<Uri> callback) {
@@ -298,32 +301,36 @@
     }
 
     private CompletableFuture<GbaCredentials> getGbaCredentials(
-            boolean forceRefresh, String nafId, Executor executor) {
-        synchronized (mCachedCredentials) {
-            if (!forceRefresh && mCachedCredentials.containsKey(nafId)) {
-                return CompletableFuture.completedFuture(mCachedCredentials.get(nafId));
+            boolean forceRefresh, PersistableBundle config, Executor executor) {
+        synchronized (this) {
+            if (!forceRefresh && mCachedCredentials != null) {
+                return CompletableFuture.completedFuture(mCachedCredentials);
             }
+
             if (forceRefresh) {
-                mCachedCredentials.remove(nafId);
+                mCachedCredentials = null;
             }
         }
 
         UaSecurityProtocolIdentifier securityProtocolIdentifier =
                 new UaSecurityProtocolIdentifier.Builder()
-                        .setOrg(UaSecurityProtocolIdentifier.ORG_3GPP)
-                        .setProtocol(UaSecurityProtocolIdentifier
-                                .UA_SECURITY_PROTOCOL_3GPP_HTTP_DIGEST_AUTHENTICATION)
+                        .setOrg(config.getInt(
+                                CarrierConfigManager.KEY_GBA_UA_SECURITY_ORGANIZATION_INT))
+                        .setProtocol(config.getInt(
+                                CarrierConfigManager.KEY_GBA_UA_SECURITY_PROTOCOL_INT))
+                        .setTlsCipherSuite(config.getInt(
+                                CarrierConfigManager.KEY_GBA_UA_TLS_CIPHER_SUITE_INT))
                         .build();
         CompletableFuture<GbaCredentials> resultFuture = new CompletableFuture<>();
 
-        mTelephonyManager.bootstrapAuthenticationRequest(TelephonyManager.APPTYPE_UNKNOWN,
-                Uri.parse(nafId), securityProtocolIdentifier, forceRefresh, executor,
+        mTelephonyManager.bootstrapAuthenticationRequest(TelephonyManager.APPTYPE_ISIM,
+                getNafUri(config), securityProtocolIdentifier, forceRefresh, executor,
                 new TelephonyManager.BootstrapAuthenticationCallback() {
                     @Override
                     public void onKeysAvailable(byte[] gbaKey, String transactionId) {
                         GbaCredentials creds = new GbaCredentials(transactionId, gbaKey);
-                        synchronized (mCachedCredentials) {
-                            mCachedCredentials.put(nafId, creds);
+                        synchronized (CallComposerPictureManager.this) {
+                            mCachedCredentials = creds;
                         }
                         resultFuture.complete(creds);
                     }
@@ -338,6 +345,30 @@
         return resultFuture;
     }
 
+    private static Uri getNafUri(PersistableBundle carrierConfig) {
+        String uploadUriString = carrierConfig.getString(
+                CarrierConfigManager.KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING);
+        Uri uploadUri = Uri.parse(uploadUriString);
+        String nafPrefix;
+        switch (carrierConfig.getInt(CarrierConfigManager.KEY_GBA_MODE_INT)) {
+            case CarrierConfigManager.GBA_U:
+                nafPrefix = THREE_GPP_BOOTSTRAPPING + "-uicc";
+                break;
+            case CarrierConfigManager.GBA_DIGEST:
+                nafPrefix = THREE_GPP_BOOTSTRAPPING + "-digest";
+                break;
+            case CarrierConfigManager.GBA_ME:
+            default:
+                nafPrefix = THREE_GPP_BOOTSTRAPPING;
+        }
+        String newAuthority = nafPrefix + "@" + uploadUri.getAuthority();
+        Uri nafUri = new Uri.Builder().scheme(uploadUri.getScheme())
+                .encodedAuthority(newAuthority)
+                .build();
+        Log.i(TAG, "using NAF uri " + nafUri + " for GBA");
+        return nafUri;
+    }
+
     @VisibleForTesting
     static ScheduledExecutorService getExecutor() {
         return sExecutorService;
diff --git a/src/com/android/phone/callcomposer/CallComposerPictureTransfer.java b/src/com/android/phone/callcomposer/CallComposerPictureTransfer.java
index 1a176dd..e4458cd 100644
--- a/src/com/android/phone/callcomposer/CallComposerPictureTransfer.java
+++ b/src/com/android/phone/callcomposer/CallComposerPictureTransfer.java
@@ -21,6 +21,7 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
+import android.os.Build;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
@@ -49,6 +50,9 @@
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.nio.charset.Charset;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
 import java.util.Iterator;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
@@ -140,7 +144,10 @@
                         mExecutorService);
         networkUrlFuture.thenAcceptAsync((result) -> {
             if (result != null) mCallback.onUploadSuccessful(result);
-        }, mExecutorService);
+        }, mExecutorService).exceptionally((ex) -> {
+            logException("Exception uploading image" , ex);
+            return null;
+        });
     }
 
     public void downloadPicture(GbaCredentialsSupplier credentialsSupplier) {
@@ -203,6 +210,9 @@
             }
             if (fromAuth != null) mCallback.onDownloadSuccessful(fromAuth);
             mCallback.onDownloadSuccessful(fromImmediate);
+        }).exceptionally((ex) -> {
+            logException("Exception downloading image" , ex);
+            return null;
         });
     }
 
@@ -223,7 +233,7 @@
         return resultFuture;
     }
 
-    private static HttpURLConnection prepareInitialPost(Network network, String uploadUrl) {
+    private HttpURLConnection prepareInitialPost(Network network, String uploadUrl) {
         try {
             HttpURLConnection connection =
                     (HttpURLConnection) network.openConnection(new URL(uploadUrl));
@@ -231,7 +241,7 @@
             connection.setInstanceFollowRedirects(false);
             connection.setConnectTimeout(HTTP_TIMEOUT_MILLIS);
             connection.setReadTimeout(HTTP_TIMEOUT_MILLIS);
-            connection.setRequestProperty("User-Agent", THREE_GPP_GBA);
+            connection.setRequestProperty("User-Agent", getUserAgent());
             return connection;
         } catch (MalformedURLException e) {
             Log.e(TAG, "Malformed URL: " + uploadUrl);
@@ -242,14 +252,14 @@
         }
     }
 
-    private static HttpURLConnection prepareImageDownloadRequest(Network network, String imageUrl) {
+    private HttpURLConnection prepareImageDownloadRequest(Network network, String imageUrl) {
         try {
             HttpURLConnection connection =
                     (HttpURLConnection) network.openConnection(new URL(imageUrl));
             connection.setRequestMethod("GET");
             connection.setConnectTimeout(HTTP_TIMEOUT_MILLIS);
             connection.setReadTimeout(HTTP_TIMEOUT_MILLIS);
-            connection.setRequestProperty("User-Agent", THREE_GPP_GBA);
+            connection.setRequestProperty("User-Agent", getUserAgent());
             return connection;
         } catch (MalformedURLException e) {
             Log.e(TAG, "Malformed URL: " + imageUrl);
@@ -387,7 +397,7 @@
             public void sendDispositionHeader(OutputStream out) throws IOException {
                 super.sendDispositionHeader(out);
                 if (filename != null) {
-                    String fileNameSuffix = ";filename=\"" + filename + "\"";
+                    String fileNameSuffix = "; filename=\"" + filename + "\"";
                     out.write(fileNameSuffix.getBytes());
                 }
             }
@@ -416,6 +426,11 @@
         HttpURLConnection connection = prepareInitialPost(network, mUrl);
         connection.setDoOutput(true);
         connection.addRequestProperty("Authorization", authHeader);
+        connection.addRequestProperty("Content-Length",
+                String.valueOf(multipartEntity.getContentLength()));
+        connection.addRequestProperty("Content-Type", multipartEntity.getContentType().getValue());
+        connection.addRequestProperty("Accept-Encoding", "*");
+
         try (OutputStream requestBodyOut = connection.getOutputStream()) {
             multipartEntity.writeTo(requestBodyOut);
         } catch (IOException e) {
@@ -425,6 +440,8 @@
 
         try {
             int response = connection.getResponseCode();
+            Log.i(TAG, "Received response code: " + response
+                    + ", message=" + connection.getResponseMessage());
             if (response == 401 || response == 403) {
                 deliverFailure(TelephonyManager.CallComposerException.ERROR_AUTHENTICATION_FAILED);
                 return null;
@@ -493,6 +510,22 @@
         return sb.toString();
     }
 
+    private String getUserAgent() {
+        String carrierName = mContext.getSystemService(TelephonyManager.class)
+                .createForSubscriptionId(mSubscriptionId)
+                .getSimOperatorName();
+        String buildId = Build.ID;
+        String buildDate = DateTimeFormatter.ofPattern("yyyy-MM-dd")
+                .withZone(ZoneId.systemDefault())
+                .format(Instant.ofEpochMilli(Build.TIME));
+        String buildVersion = Build.VERSION.RELEASE_OR_CODENAME;
+        String deviceName = Build.DEVICE;
+        return String.format("%s %s %s %s %s %s %s",
+                carrierName, buildId, buildDate, "Android", buildVersion,
+                deviceName, THREE_GPP_GBA);
+
+    }
+
     private static void logException(String message, Throwable e) {
         StringWriter log = new StringWriter();
         log.append(message);
diff --git a/src/com/android/phone/callcomposer/DigestAuthUtils.java b/src/com/android/phone/callcomposer/DigestAuthUtils.java
index 52a278b..2f081f7 100644
--- a/src/com/android/phone/callcomposer/DigestAuthUtils.java
+++ b/src/com/android/phone/callcomposer/DigestAuthUtils.java
@@ -56,9 +56,13 @@
         if (!TextUtils.isEmpty(parsedHeader.getAlgorithm())
                 && !MD5_ALGORITHM.equals(parsedHeader.getAlgorithm().toLowerCase())) {
             Log.e(TAG, "This client only supports MD5 auth");
+            return "";
         }
-
-        Log.i(TAG, "nonce=" + parsedHeader.getNonce());
+        if (!TextUtils.isEmpty(parsedHeader.getQop())
+                && !AUTH_QOP.equals(parsedHeader.getQop().toLowerCase())) {
+            Log.e(TAG, "This client only supports the auth qop");
+            return "";
+        }
 
         String clientNonce = makeClientNonce();
 
@@ -71,7 +75,9 @@
             replyHeader.setScheme(parsedHeader.getScheme());
             replyHeader.setUsername(credentials.getTransactionId());
             replyHeader.setURI(new WorkaroundURI(uri));
+            replyHeader.setRealm(parsedHeader.getRealm());
             replyHeader.setQop(AUTH_QOP);
+            replyHeader.setNonce(parsedHeader.getNonce());
             replyHeader.setCNonce(clientNonce);
             replyHeader.setNonceCount(1);
             replyHeader.setResponse(response);
@@ -83,7 +89,7 @@
             return null;
         }
 
-        return replyHeader.encode();
+        return replyHeader.encodeBody();
     }
 
     public static String computeResponse(String serverNonce, String clientNonce, String qop,
diff --git a/src/com/android/phone/settings/RadioInfo.java b/src/com/android/phone/settings/RadioInfo.java
index dbb902d..88de151 100644
--- a/src/com/android/phone/settings/RadioInfo.java
+++ b/src/com/android/phone/settings/RadioInfo.java
@@ -210,6 +210,11 @@
     private static final int MENU_ITEM_GET_IMS_STATUS      = 4;
     private static final int MENU_ITEM_TOGGLE_DATA         = 5;
 
+    private static final String CARRIER_PROVISIONING_ACTION =
+            "com.android.phone.settings.CARRIER_PROVISIONING";
+    private static final String TRIGGER_CARRIER_PROVISIONING_ACTION =
+            "com.android.phone.settings.TRIGGER_CARRIER_PROVISIONING";
+
     private TextView mDeviceId; //DeviceId is the IMEI in GSM and the MEID in CDMA
     private TextView mLine1Number;
     private TextView mSubscriptionId;
@@ -571,11 +576,20 @@
         mDnsCheckToggleButton = (Button) findViewById(R.id.dns_check_toggle);
         mDnsCheckToggleButton.setOnClickListener(mDnsCheckButtonHandler);
         mCarrierProvisioningButton = (Button) findViewById(R.id.carrier_provisioning);
-        mCarrierProvisioningButton.setOnClickListener(mCarrierProvisioningButtonHandler);
+        if (!TextUtils.isEmpty(getCarrierProvisioningAppString())) {
+            mCarrierProvisioningButton.setOnClickListener(mCarrierProvisioningButtonHandler);
+        } else {
+            mCarrierProvisioningButton.setEnabled(false);
+        }
+
         mTriggerCarrierProvisioningButton = (Button) findViewById(
                 R.id.trigger_carrier_provisioning);
-        mTriggerCarrierProvisioningButton.setOnClickListener(
-                mTriggerCarrierProvisioningButtonHandler);
+        if (!TextUtils.isEmpty(getCarrierProvisioningAppString())) {
+            mTriggerCarrierProvisioningButton.setOnClickListener(
+                    mTriggerCarrierProvisioningButtonHandler);
+        } else {
+            mTriggerCarrierProvisioningButton.setEnabled(false);
+        }
 
         mOemInfoButton = (Button) findViewById(R.id.oem_info);
         mOemInfoButton.setOnClickListener(mOemInfoButtonHandler);
@@ -1610,21 +1624,23 @@
         }
     };
 
-    OnClickListener mCarrierProvisioningButtonHandler = new OnClickListener() {
-        public void onClick(View v) {
-            final Intent intent = new Intent("com.android.settings.CARRIER_PROVISIONING");
-            final ComponentName serviceComponent = ComponentName.unflattenFromString(
-                    "com.android.omadm.service/.DMIntentReceiver");
+    OnClickListener mCarrierProvisioningButtonHandler = v -> {
+        String carrierProvisioningApp = getCarrierProvisioningAppString();
+        if (!TextUtils.isEmpty(carrierProvisioningApp)) {
+            final Intent intent = new Intent(CARRIER_PROVISIONING_ACTION);
+            final ComponentName serviceComponent =
+                    ComponentName.unflattenFromString(carrierProvisioningApp);
             intent.setComponent(serviceComponent);
             sendBroadcast(intent);
         }
     };
 
-    OnClickListener mTriggerCarrierProvisioningButtonHandler = new OnClickListener() {
-        public void onClick(View v) {
-            final Intent intent = new Intent("com.android.settings.TRIGGER_CARRIER_PROVISIONING");
-            final ComponentName serviceComponent = ComponentName.unflattenFromString(
-                    "com.android.omadm.service/.DMIntentReceiver");
+    OnClickListener mTriggerCarrierProvisioningButtonHandler = v -> {
+        String carrierProvisioningApp = getCarrierProvisioningAppString();
+        if (!TextUtils.isEmpty(carrierProvisioningApp)) {
+            final Intent intent = new Intent(TRIGGER_CARRIER_PROVISIONING_ACTION);
+            final ComponentName serviceComponent =
+                    ComponentName.unflattenFromString(carrierProvisioningApp);
             intent.setComponent(serviceComponent);
             sendBroadcast(intent);
         }
@@ -1689,6 +1705,19 @@
         }
     };
 
+    private String getCarrierProvisioningAppString() {
+        if (mPhone != null) {
+            CarrierConfigManager configManager =
+                    mPhone.getContext().getSystemService(CarrierConfigManager.class);
+            PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId());
+            if (b != null) {
+                return b.getString(
+                        CarrierConfigManager.KEY_CARRIER_PROVISIONING_APP_STRING, "");
+            }
+        }
+        return "";
+    }
+
     boolean isCbrsSupported() {
         return getResources().getBoolean(
               com.android.internal.R.bool.config_cbrs_supported);
diff --git a/src/com/android/services/telephony/DisconnectCauseUtil.java b/src/com/android/services/telephony/DisconnectCauseUtil.java
index f243462..f9742b4 100644
--- a/src/com/android/services/telephony/DisconnectCauseUtil.java
+++ b/src/com/android/services/telephony/DisconnectCauseUtil.java
@@ -23,6 +23,7 @@
 import android.telecom.DisconnectCause;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
+import android.telephony.ims.ImsReasonInfo;
 
 import com.android.internal.telephony.CallFailCause;
 import com.android.internal.telephony.Phone;
@@ -64,13 +65,13 @@
     * message and tone.
     *
     * @param telephonyDisconnectCause The code for the reason for the disconnect.
-    * @param telephonyPerciseDisconnectCause The code for the percise reason for the disconnect.
+    * @param telephonyPreciseDisconnectCause The code for the precise reason for the disconnect.
     * @param reason Description of the reason for the disconnect, not intended for the user to see..
     */
     public static DisconnectCause toTelecomDisconnectCause(
-            int telephonyDisconnectCause, int telephonyPerciseDisconnectCause, String reason) {
-        return toTelecomDisconnectCause(telephonyDisconnectCause, telephonyPerciseDisconnectCause,
-                reason, SubscriptionManager.getDefaultVoicePhoneId());
+            int telephonyDisconnectCause, int telephonyPreciseDisconnectCause, String reason) {
+        return toTelecomDisconnectCause(telephonyDisconnectCause, telephonyPreciseDisconnectCause,
+                reason, SubscriptionManager.getDefaultVoicePhoneId(), null);
     }
 
     /**
@@ -84,30 +85,33 @@
     public static DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause,
             String reason, int phoneId) {
         return toTelecomDisconnectCause(telephonyDisconnectCause, CallFailCause.NOT_VALID,
-                reason, phoneId);
+                reason, phoneId, null);
     }
 
    /**
     * Converts from a disconnect code in {@link android.telephony.DisconnectCause} into a more
     * generic {@link android.telecom.DisconnectCause}.object, possibly populated with a localized
     * message and tone for Slot.
-    *
     * @param telephonyDisconnectCause The code for the reason for the disconnect.
-    * @param telephonyPerciseDisconnectCause The code for the percise reason for the disconnect.
-    * @param reason Description of the reason for the disconnect, not intended for the user to see..
+    * @param telephonyPreciseDisconnectCause The code for the precise reason for the disconnect.
+    * @param reason Description of the reason for the disconnect, not intended for the user to see.
     * @param phoneId To support localized message based on phoneId
+    * @param imsReasonInfo
     */
     public static DisconnectCause toTelecomDisconnectCause(
-            int telephonyDisconnectCause, int telephonyPerciseDisconnectCause, String reason,
-            int phoneId) {
+            int telephonyDisconnectCause, int telephonyPreciseDisconnectCause, String reason,
+            int phoneId, ImsReasonInfo imsReasonInfo) {
         Context context = PhoneGlobals.getInstance();
         return new DisconnectCause(
                 toTelecomDisconnectCauseCode(telephonyDisconnectCause),
                 toTelecomDisconnectCauseLabel(context, telephonyDisconnectCause,
-                        telephonyPerciseDisconnectCause),
+                        telephonyPreciseDisconnectCause),
                 toTelecomDisconnectCauseDescription(context, telephonyDisconnectCause, phoneId),
                 toTelecomDisconnectReason(context,telephonyDisconnectCause, reason, phoneId),
-                toTelecomDisconnectCauseTone(telephonyDisconnectCause, phoneId));
+                toTelecomDisconnectCauseTone(telephonyDisconnectCause, phoneId),
+                telephonyDisconnectCause,
+                telephonyPreciseDisconnectCause,
+                imsReasonInfo);
     }
 
     /**
@@ -233,10 +237,10 @@
      * Returns a label for to the disconnect cause to be shown to the user.
      */
     private static CharSequence toTelecomDisconnectCauseLabel(
-            Context context, int telephonyDisconnectCause, int telephonyPerciseDisconnectCause) {
+            Context context, int telephonyDisconnectCause, int telephonyPreciseDisconnectCause) {
         CharSequence label;
-        if (telephonyPerciseDisconnectCause != CallFailCause.NOT_VALID) {
-            label = getLabelFromPreciseDisconnectCause(context, telephonyPerciseDisconnectCause,
+        if (telephonyPreciseDisconnectCause != CallFailCause.NOT_VALID) {
+            label = getLabelFromPreciseDisconnectCause(context, telephonyPreciseDisconnectCause,
                     telephonyDisconnectCause);
         } else {
             label = getLabelFromDisconnectCause(context, telephonyDisconnectCause);
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 18b1426..e684c7d 100755
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -50,6 +50,7 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.ImsStreamMediaProfile;
 import android.telephony.ims.RtpHeaderExtension;
 import android.telephony.ims.RtpHeaderExtensionType;
@@ -2371,12 +2372,18 @@
                                     + " -> " + mHangupDisconnectCause);
                             disconnectCause = mHangupDisconnectCause;
                         }
+                        ImsReasonInfo imsReasonInfo = null;
+                        if (isImsConnection()) {
+                            ImsPhoneConnection imsPhoneConnection =
+                                    (ImsPhoneConnection) mOriginalConnection;
+                            imsReasonInfo = imsPhoneConnection.getImsReasonInfo();
+                        }
                         setTelephonyConnectionDisconnected(
                                 DisconnectCauseUtil.toTelecomDisconnectCause(
                                         disconnectCause,
                                         preciseDisconnectCause,
                                         mOriginalConnection.getVendorDisconnectCause(),
-                                        getPhone().getPhoneId()));
+                                        getPhone().getPhoneId(), imsReasonInfo));
                         close();
                     }
                     break;
@@ -3168,7 +3175,7 @@
     }
 
     /**
-     * Set this {@link TelephonyConnection} to a held state.
+     * Set this {@link TelephonyConnection} to a disconnected state.
      * <p>
      * Note: This should be used instead of
      * {@link #setDisconnected(android.telecom.DisconnectCause)} to ensure listeners are notified.
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 20ea17b..d62fb5e 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 05745c8..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="7"
-    android:versionName="1.0.6">
+    android:versionCode="9"
+    android:versionName="1.0.8">
 
     <uses-sdk
         android:minSdkVersion="30"
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/UceActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java
index 6835fea..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;
@@ -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 ec048b8..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,7 +66,7 @@
     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) {
@@ -90,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());
@@ -173,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());
@@ -222,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);
@@ -240,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 3f8b986..3a95a0a 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/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/callcomposer/PictureManagerTest.java b/tests/src/com/android/phone/callcomposer/PictureManagerTest.java
index b52b297..f1ce3b8 100644
--- a/tests/src/com/android/phone/callcomposer/PictureManagerTest.java
+++ b/tests/src/com/android/phone/callcomposer/PictureManagerTest.java
@@ -34,6 +34,7 @@
 import android.provider.CallLog;
 import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyManager;
+import android.telephony.gba.TlsParams;
 import android.telephony.gba.UaSecurityProtocolIdentifier;
 
 import org.junit.After;
@@ -78,6 +79,14 @@
         PersistableBundle b = new PersistableBundle();
         b.putString(CarrierConfigManager.KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING,
                 FAKE_URL_BASE);
+        b.putInt(CarrierConfigManager.KEY_GBA_MODE_INT,
+                CarrierConfigManager.GBA_ME);
+        b.putInt(CarrierConfigManager.KEY_GBA_UA_SECURITY_ORGANIZATION_INT,
+                UaSecurityProtocolIdentifier.ORG_3GPP);
+        b.putInt(CarrierConfigManager.KEY_GBA_UA_SECURITY_PROTOCOL_INT,
+                UaSecurityProtocolIdentifier.UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT);
+        b.putInt(CarrierConfigManager.KEY_GBA_UA_TLS_CIPHER_SUITE_INT,
+                TlsParams.TLS_RSA_WITH_AES_128_CBC_SHA);
         when(telephonyManager.getCarrierConfig()).thenReturn(b);
     }
 
@@ -263,7 +272,7 @@
 
     public void testGbaCredLookup(GbaCredentialsSupplier supplier, boolean forceExpected)
             throws Exception {
-        String fakeRealm = "3gpp-bootstraping@naf1.example.com";
+        String fakeNafId = "https://3GPP-bootstrapping@www.example.com";
         byte[] fakeKey = new byte[] {1, 2, 3, 4, 5};
         String fakeTxId = "89sdfjggf";
 
@@ -271,8 +280,9 @@
                 ArgumentCaptor.forClass(TelephonyManager.BootstrapAuthenticationCallback.class);
 
         CompletableFuture<GbaCredentials> credsFuture =
-                supplier.getCredentials(fakeRealm, CallComposerPictureManager.getExecutor());
-        verify(telephonyManager).bootstrapAuthenticationRequest(anyInt(), eq(Uri.parse(fakeRealm)),
+                supplier.getCredentials(fakeNafId, CallComposerPictureManager.getExecutor());
+        verify(telephonyManager).bootstrapAuthenticationRequest(anyInt(),
+                eq(Uri.parse(fakeNafId)),
                 nullable(UaSecurityProtocolIdentifier.class), eq(forceExpected),
                 nullable(Executor.class),
                 authCallbackCaptor.capture());
@@ -285,9 +295,9 @@
         // Do it again and see if we make another request, then make sure that matches up with what
         // we expected.
         CompletableFuture<GbaCredentials> credsFuture1 =
-                supplier.getCredentials(fakeRealm, CallComposerPictureManager.getExecutor());
+                supplier.getCredentials(fakeNafId, CallComposerPictureManager.getExecutor());
         verify(telephonyManager, times(forceExpected ? 2 : 1))
-                .bootstrapAuthenticationRequest(anyInt(), eq(Uri.parse(fakeRealm)),
+                .bootstrapAuthenticationRequest(anyInt(), eq(Uri.parse(fakeNafId)),
                         nullable(UaSecurityProtocolIdentifier.class),
                         eq(forceExpected),
                         nullable(Executor.class),
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();
     }