Merge "Re-add unit tests for RTP Transport wiring." into sc-dev
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index dc432d0..e9ec299 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -105,23 +105,23 @@
     <string name="labelCF" msgid="3578719437928476078">"कॉल फॉरवर्डिंग"</string>
     <string name="labelCFU" msgid="8870170873036279706">"नेहमी फॉरवर्ड करा"</string>
     <string name="messageCFU" msgid="1361806450979589744">"नेहमी हा नंबर वापरा"</string>
-    <string name="sum_cfu_enabled_indicator" msgid="9030139213402432776">"सर्व कॉल अग्रेषित करत आहे"</string>
+    <string name="sum_cfu_enabled_indicator" msgid="9030139213402432776">"सर्व कॉल फॉरवर्ड करत आहे"</string>
     <string name="sum_cfu_enabled" msgid="5806923046528144526">"सर्व कॉल <xliff:g id="PHONENUMBER">{0}</xliff:g> वर फॉरवर्ड करत आहे"</string>
     <string name="sum_cfu_enabled_no_number" msgid="7287752761743377930">"नंबर अनुपलब्‍ध आहे"</string>
     <string name="sum_cfu_disabled" msgid="5010617134210809853">"बंद"</string>
     <string name="labelCFB" msgid="615265213360512768">"व्यस्त असताना"</string>
     <string name="messageCFB" msgid="1958017270393563388">"नंबर व्‍यस्‍त असताना"</string>
-    <string name="sum_cfb_enabled" msgid="332037613072049492">"<xliff:g id="PHONENUMBER">{0}</xliff:g> वर अग्रेषित करत आहे"</string>
+    <string name="sum_cfb_enabled" msgid="332037613072049492">"<xliff:g id="PHONENUMBER">{0}</xliff:g> वर फॉरवर्ड करत आहे"</string>
     <string name="sum_cfb_disabled" msgid="3589913334164866035">"बंद"</string>
     <string name="disable_cfb_forbidden" msgid="4831494744351633961">"तुमचा फोन व्‍यस्‍त असताना तुमचा ऑपरेटर कॉल अग्रेषण करणे अक्षम करण्‍यास समर्थन करीत नाही."</string>
     <string name="labelCFNRy" msgid="3403533792248457946">"उत्तर न दिल्यास"</string>
     <string name="messageCFNRy" msgid="7644434155765359009">"नंबर अनुत्तरित असताना"</string>
-    <string name="sum_cfnry_enabled" msgid="3000500837493854799">"<xliff:g id="PHONENUMBER">{0}</xliff:g> वर अग्रेषित करत आहे"</string>
+    <string name="sum_cfnry_enabled" msgid="3000500837493854799">"<xliff:g id="PHONENUMBER">{0}</xliff:g> वर फॉरवर्ड करत आहे"</string>
     <string name="sum_cfnry_disabled" msgid="1990563512406017880">"बंद"</string>
     <string name="disable_cfnry_forbidden" msgid="3174731413216550689">"तुमचा फोन उत्तर देत नसताना तुमचा ऑपरेटर कॉल अग्रेषण करणे अक्षम करण्‍यास समर्थन करीत नाही."</string>
     <string name="labelCFNRc" msgid="4163399350778066013">"आउट ऑफ रीच असताना"</string>
     <string name="messageCFNRc" msgid="6980340731313007250">"नंबर पोहचण्‍यायोग्‍य नसताना"</string>
-    <string name="sum_cfnrc_enabled" msgid="1799069234006073477">"<xliff:g id="PHONENUMBER">{0}</xliff:g> वर अग्रेषित करत आहे"</string>
+    <string name="sum_cfnrc_enabled" msgid="1799069234006073477">"<xliff:g id="PHONENUMBER">{0}</xliff:g> वर फॉरवर्ड करत आहे"</string>
     <string name="sum_cfnrc_disabled" msgid="739289696796917683">"बंद"</string>
     <string name="disable_cfnrc_forbidden" msgid="775348748084726890">"तुमचा फोन पोहचण्‍यायोग्‍य नसताना तुमचा वाहक कॉल अग्रेषण करणे अक्षम करण्‍यास समर्थन करीत नाही."</string>
     <string name="registration_cf_forbidden" msgid="4386482610771190420">"तुमचा वाहक कॉल फॉरवर्डिंग करण्यास सपोर्ट करत नाही."</string>
@@ -157,7 +157,7 @@
   </string-array>
     <string name="vm_changed" msgid="4739599044379692505">"व्हॉइसमेल नंबर बदलला."</string>
     <string name="vm_change_failed" msgid="7877733929455763566">"व्हॉइसमेल नंबर बदलू शकले नाही.\nही समस्‍या  कायम राहिल्‍यास आपल्‍या वाहकाशी संपर्क साधा."</string>
-    <string name="fw_change_failed" msgid="9179241823460192148">"अग्रेषित करण्‍याचा नंबर बदलू शकलो नाही.\n ही समस्‍या कायम राहिल्‍यास आपल्‍या वाहकाशी संपर्क साधा."</string>
+    <string name="fw_change_failed" msgid="9179241823460192148">"फॉरवर्ड करण्‍याचा नंबर बदलू शकलो नाही.\n ही समस्‍या कायम राहिल्‍यास आपल्‍या वाहकाशी संपर्क साधा."</string>
     <string name="fw_get_in_vm_failed" msgid="2432678237218183844">"वर्तमान अग्रेषण नंबर सेटिंग्‍ज पुनर्प्राप्त करू शकलो नाही आणि सेव्ह करू शकलो नाही.\nतरीही नवीन प्रदात्‍यावर स्‍विच करायचे?"</string>
     <string name="no_change" msgid="3737264882821031892">"कोणतेही बदल केले नाहीत."</string>
     <string name="sum_voicemail_choose_provider" msgid="6750824719081403773">"व्हॉइसमेल सेवा निवडा"</string>
diff --git a/res/values/config.xml b/res/values/config.xml
index 7dd26bb..0fd9b4b 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -307,6 +307,9 @@
     <!-- Whether or not to support RCS VoLTE single registration  -->
     <bool name="config_rcsVolteSingleRegistrationEnabled">true</bool>
 
+    <!-- Whether or not to support RCS User Capability Exchange -->
+    <bool name="config_rcs_user_capability_exchange_enabled">true</bool>
+
     <!-- Whether or not to support device to device communication using RTP and DTMF communication
          transports. -->
     <bool name="config_use_device_to_device_communication">false</bool>
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index ef83ead..2fcc2ee 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -30,6 +30,9 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
 import android.os.PersistableBundle;
 import android.os.UserManager;
 import android.preference.Preference;
@@ -103,6 +106,7 @@
     private ImsManager mImsMgr;
     private SubscriptionInfoHelper mSubscriptionInfoHelper;
     private TelecomManager mTelecomManager;
+    private PhoneStateListener mPhoneStateListener;
 
     private SwitchPreference mButtonAutoRetry;
     private PreferenceScreen mVoicemailSettingsScreen;
@@ -263,6 +267,7 @@
         mSubscriptionInfoHelper.setActionBarTitle(
                 getActionBar(), getResourcesForSubId(), R.string.call_settings_with_label);
         mTelecomManager = getSystemService(TelecomManager.class);
+        mPhoneStateListener = new CallFeaturesPhoneStateListener();
     }
 
     private void updateImsManager(Phone phone) {
@@ -279,11 +284,16 @@
     private void listenPhoneState(boolean listen) {
         TelephonyManager telephonyManager = getSystemService(TelephonyManager.class)
                 .createForSubscriptionId(mPhone.getSubId());
-        telephonyManager.listen(mPhoneStateListener, listen
-                ? PhoneStateListener.LISTEN_CALL_STATE : PhoneStateListener.LISTEN_NONE);
+        if (listen) {
+            telephonyManager.registerPhoneStateListener(
+                    new HandlerExecutor(new Handler(Looper.getMainLooper())), mPhoneStateListener);
+        } else {
+            telephonyManager.unregisterPhoneStateListener(mPhoneStateListener);
+        }
     }
 
-    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+    private final class CallFeaturesPhoneStateListener extends PhoneStateListener implements
+            PhoneStateListener.CallStateChangedListener {
         @Override
         public void onCallStateChanged(int state, String incomingNumber) {
             if (DBG) log("PhoneStateListener onCallStateChanged: state is " + state);
@@ -295,7 +305,7 @@
                 mButtonWifiCalling.setEnabled(isCallStateIdle);
             }
         }
-    };
+    }
 
     private final ProvisioningManager.Callback mProvisioningCallback =
             new ProvisioningManager.Callback() {
diff --git a/src/com/android/phone/CallNotifier.java b/src/com/android/phone/CallNotifier.java
index 6c18623..4c38d98 100644
--- a/src/com/android/phone/CallNotifier.java
+++ b/src/com/android/phone/CallNotifier.java
@@ -25,6 +25,7 @@
 import android.media.ToneGenerator;
 import android.os.AsyncResult;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.Message;
 import android.os.SystemProperties;
 import android.telecom.TelecomManager;
@@ -583,9 +584,8 @@
                 mApplication.notificationMgr.updateMwi(subId, false);
                 mApplication.notificationMgr.updateCfi(subId, false);
 
-                // Listening to LISTEN_NONE removes the listener.
-                mTelephonyManager.listen(
-                        mPhoneStateListeners.get(subId), PhoneStateListener.LISTEN_NONE);
+                // Unregister the listener.
+                mTelephonyManager.unregisterPhoneStateListener(mPhoneStateListeners.get(subId));
                 mPhoneStateListeners.remove(subId);
             } else {
                 Log.d(LOG_TAG, "updatePhoneStateListeners: update CF notifications.");
@@ -618,9 +618,8 @@
             int subId = subInfos.get(i).getSubscriptionId();
             if (!mPhoneStateListeners.containsKey(subId)) {
                 CallNotifierPhoneStateListener listener = new CallNotifierPhoneStateListener(subId);
-                mTelephonyManager.createForSubscriptionId(subId).listen(listener,
-                        PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
-                        | PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR);
+                mTelephonyManager.createForSubscriptionId(subId).registerPhoneStateListener(
+                        new HandlerExecutor(this), listener);
                 mPhoneStateListeners.put(subId, listener);
             }
         }
@@ -768,7 +767,10 @@
                 }
             };
 
-    private class CallNotifierPhoneStateListener extends PhoneStateListener {
+    private class CallNotifierPhoneStateListener extends PhoneStateListener implements
+            PhoneStateListener.MessageWaitingIndicatorChangedListener,
+            PhoneStateListener.CallForwardingIndicatorChangedListener {
+
         private final int mSubId;
 
         CallNotifierPhoneStateListener(int subId) {
@@ -790,7 +792,7 @@
             mCFIStatus.put(this.mSubId, visible);
             updatePhoneStateListeners(false, UPDATE_TYPE_CFI, this.mSubId);
         }
-    };
+    }
 
     private void log(String msg) {
         Log.d(LOG_TAG, msg);
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 23f6d7e..9692a50 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -944,6 +944,23 @@
     }
 
     /**
+     * @return whether the device supports RCS User Capability Exchange or not.
+     */
+    public boolean getDeviceUceEnabled() {
+        return (mTelephonyRcsService == null) ? false : mTelephonyRcsService.isDeviceUceEnabled();
+    }
+
+    /**
+     * Set the device supports RCS User Capability Exchange.
+     * @param isEnabled true if the device supports UCE.
+     */
+    public void setDeviceUceEnabled(boolean isEnabled) {
+        if (mTelephonyRcsService != null) {
+            mTelephonyRcsService.setDeviceUceEnabled(isEnabled);
+        }
+    }
+
+    /**
      * Dump the state of the object, add calls to other objects as desired.
      *
      * @param fd File descriptor
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index b74787a..84c20f6 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -9909,6 +9909,21 @@
     }
 
     /**
+     * Get the EAB contact from the EAB database.
+     */
+    @Override
+    public String getContactFromEab(String contact) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getContactFromEab");
+        enforceModifyPermission();
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return EabUtil.getContactFromEab(getDefaultPhone().getContext(), contact);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
      * Remove the EAB contacts from the EAB database.
      */
     @Override
@@ -9924,6 +9939,28 @@
     }
 
     @Override
+    public boolean getDeviceUceEnabled() {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getDeviceUceEnabled");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mApp.getDeviceUceEnabled();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void setDeviceUceEnabled(boolean isEnabled) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setDeviceUceEnabled");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mApp.setDeviceUceEnabled(isEnabled);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
     public void setSignalStrengthUpdateRequest(int subId, SignalStrengthUpdateRequest request,
             String callingPackage) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index f1ba3ee..9676e59 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -114,7 +114,10 @@
     private static final String D2D_SEND = "send";
 
     private static final String RCS_UCE_COMMAND = "uce";
+    private static final String UCE_GET_EAB_CONTACT = "get-eab-contact";
     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";
 
     // Take advantage of existing methods that already contain permissions checks when possible.
     private final ITelephony mInterface;
@@ -304,12 +307,23 @@
     private void onHelpUce() {
         PrintWriter pw = getOutPrintWriter();
         pw.println("User Capability Exchange Commands:");
+        pw.println("  uce get-eab-contact [PHONE_NUMBER]");
+        pw.println("    Get the EAB contacts from the EAB database.");
+        pw.println("    Options are:");
+        pw.println("      PHONE_NUMBER: The phone numbers to be removed from the EAB databases");
+        pw.println("    Expected output format :");
+        pw.println("      [PHONE_NUMBER],[RAW_CONTACT_ID],[CONTACT_ID],[DATA_ID]");
         pw.println("  uce remove-eab-contact [-s SLOT_ID] [PHONE_NUMBER]");
         pw.println("    Remove the EAB contacts from the EAB database.");
         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("      PHONE_NUMBER: The phone numbers to be removed from the EAB databases");
+        pw.println("  uce get-device-enabled");
+        pw.println("    Get the config to check whether the device supports RCS UCE or not.");
+        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.");
     }
 
     private void onHelpNumberVerification() {
@@ -1622,6 +1636,12 @@
         switch (arg) {
             case UCE_REMOVE_EAB_CONTACT:
                 return handleRemovingEabContactCommand();
+            case UCE_GET_EAB_CONTACT:
+                return handleGettingEabContactCommand();
+            case UCE_GET_DEVICE_ENABLED:
+                return handleUceGetDeviceEnabledCommand();
+            case UCE_SET_DEVICE_ENABLED:
+                return handleUceSetDeviceEnabledCommand();
         }
         return -1;
     }
@@ -1651,6 +1671,62 @@
         return result;
     }
 
+    private int handleGettingEabContactCommand() {
+        String phoneNumber = getNextArgRequired();
+        if (TextUtils.isEmpty(phoneNumber)) {
+            return -1;
+        }
+        String result = "";
+        try {
+            result = mInterface.getContactFromEab(phoneNumber);
+
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "uce get-eab-contact, error " + e.getMessage());
+            getErrPrintWriter().println("Exception: " + e.getMessage());
+            return -1;
+        }
+
+        if (VDBG) {
+            Log.v(LOG_TAG, "uce get-eab-contact, result: " + result);
+        }
+        return 0;
+    }
+
+    private int handleUceGetDeviceEnabledCommand() {
+        boolean result = false;
+        try {
+            result = mInterface.getDeviceUceEnabled();
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "uce get-device-enabled, error " + e.getMessage());
+            return -1;
+        }
+        if (VDBG) {
+            Log.v(LOG_TAG, "uce get-device-enabled, returned: " + result);
+        }
+        getOutPrintWriter().println(result);
+        return 0;
+    }
+
+    private int handleUceSetDeviceEnabledCommand() {
+        String enabledStr = getNextArg();
+        if (TextUtils.isEmpty(enabledStr)) {
+            return -1;
+        }
+
+        try {
+            boolean isEnabled = Boolean.parseBoolean(enabledStr);
+            mInterface.setDeviceUceEnabled(isEnabled);
+            if (VDBG) {
+                Log.v(LOG_TAG, "uce set-device-enabled " + enabledStr + ", done");
+            }
+        } catch (NumberFormatException | RemoteException e) {
+            Log.w(LOG_TAG, "uce set-device-enabled " + enabledStr + ", error " + e.getMessage());
+            getErrPrintWriter().println("Exception: " + e.getMessage());
+            return -1;
+        }
+        return 0;
+    }
+
     private int handleSrcSetDeviceEnabledCommand() {
         String enabledStr = getNextArg();
         if (enabledStr == null) {
diff --git a/src/com/android/phone/settings/AccessibilitySettingsFragment.java b/src/com/android/phone/settings/AccessibilitySettingsFragment.java
index ad3f133..523c710 100644
--- a/src/com/android/phone/settings/AccessibilitySettingsFragment.java
+++ b/src/com/android/phone/settings/AccessibilitySettingsFragment.java
@@ -19,6 +19,9 @@
 import android.content.Context;
 import android.media.AudioManager;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
 import android.os.PersistableBundle;
 import android.preference.Preference;
 import android.preference.PreferenceFragment;
@@ -56,13 +59,10 @@
 
     private static final int WFC_QUERY_TIMEOUT_MILLIS = 20;
 
-    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
-        /**
-         * Disable the TTY setting when in/out of a call (and if carrier doesn't
-         * support VoLTE with TTY).
-         * @see android.telephony.PhoneStateListener#onCallStateChanged(int,
-         * java.lang.String)
-         */
+    private final PhoneStateListener mPhoneStateListener = new AccessibilityPhoneStateListener();
+
+    private final class AccessibilityPhoneStateListener extends PhoneStateListener implements
+            PhoneStateListener.CallStateChangedListener {
         @Override
         public void onCallStateChanged(int state, String incomingNumber) {
             if (DBG) Log.d(LOG_TAG, "PhoneStateListener.onCallStateChanged: state=" + state);
@@ -78,7 +78,7 @@
                         || (telephonyManager.getCallState() == TelephonyManager.CALL_STATE_IDLE));
             }
         }
-    };
+    }
 
     private Context mContext;
     private AudioManager mAudioManager;
@@ -148,7 +148,8 @@
         super.onResume();
         TelephonyManager tm =
                 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
-        tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+        tm.registerPhoneStateListener(new HandlerExecutor(new Handler(Looper.getMainLooper())),
+                mPhoneStateListener);
     }
 
     @Override
@@ -156,7 +157,7 @@
         super.onPause();
         TelephonyManager tm =
                 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
-        tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+        tm.unregisterPhoneStateListener(mPhoneStateListener);
     }
 
     @Override
diff --git a/src/com/android/phone/settings/RadioInfo.java b/src/com/android/phone/settings/RadioInfo.java
index b7a413c..fd8d936 100644
--- a/src/com/android/phone/settings/RadioInfo.java
+++ b/src/com/android/phone/settings/RadioInfo.java
@@ -36,6 +36,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.SystemProperties;
@@ -296,9 +297,19 @@
 
     // not final because we need to recreate this object to register on a new subId (b/117555407)
     private PhoneStateListener mPhoneStateListener = new RadioInfoPhoneStateListener();
-    private class RadioInfoPhoneStateListener extends PhoneStateListener {
+    private class RadioInfoPhoneStateListener extends PhoneStateListener implements
+            PhoneStateListener.DataConnectionStateChangedListener,
+            PhoneStateListener.DataActivityListener,
+            PhoneStateListener.CallStateChangedListener,
+            PhoneStateListener.MessageWaitingIndicatorChangedListener,
+            PhoneStateListener.CallForwardingIndicatorChangedListener,
+            PhoneStateListener.CellInfoChangedListener,
+            PhoneStateListener.CellLocationChangedListener,
+            PhoneStateListener.SignalStrengthsChangedListener,
+            PhoneStateListener.ServiceStateChangedListener {
+
         @Override
-        public void onDataConnectionStateChanged(int state) {
+        public void onDataConnectionStateChanged(int state, int networkType) {
             updateDataState();
             updateNetworkType();
         }
@@ -315,11 +326,6 @@
         }
 
         @Override
-        public void onPreciseCallStateChanged(PreciseCallState preciseState) {
-            updateNetworkType();
-        }
-
-        @Override
         public void onMessageWaitingIndicatorChanged(boolean mwi) {
             mMwiValue = mwi;
             updateMessageWaiting();
@@ -636,6 +642,8 @@
         mCellInfoRefreshRateSpinner.setOnItemSelectedListener(mCellInfoRefreshRateHandler);
         //set selection after registering listener to force update
         mCellInfoRefreshRateSpinner.setSelection(mCellInfoRefreshRateIndex);
+        // Request cell information update from RIL.
+        mTelephonyManager.setCellInfoListRate(CELL_INFO_REFRESH_RATES[mCellInfoRefreshRateIndex]);
 
         //set selection before registering to prevent update
         mPreferredNetworkType.setSelection(mPreferredNetworkTypeResult, true);
@@ -673,7 +681,7 @@
 
         log("onPause: unregister phone & data intents");
 
-        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+        mTelephonyManager.unregisterPhoneStateListener(mPhoneStateListener);
         mTelephonyManager.setCellInfoListRate(sCellInfoListRateDisabled);
         mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
 
@@ -774,7 +782,7 @@
     }
 
     private void unregisterPhoneStateListener() {
-        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+        mTelephonyManager.unregisterPhoneStateListener(mPhoneStateListener);
         mPhone.unregisterForPhysicalChannelConfig(mHandler);
 
         // clear all fields so they are blank until the next listener event occurs
@@ -799,18 +807,8 @@
     // register mPhoneStateListener for relevant fields using the current TelephonyManager
     private void registerPhoneStateListener() {
         mPhoneStateListener = new RadioInfoPhoneStateListener();
-        mTelephonyManager.listen(mPhoneStateListener,
-                  PhoneStateListener.LISTEN_CALL_STATE
-        //b/27803938 - RadioInfo currently cannot read PRECISE_CALL_STATE
-        //      | PhoneStateListener.LISTEN_PRECISE_CALL_STATE
-                | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
-                | PhoneStateListener.LISTEN_DATA_ACTIVITY
-                | PhoneStateListener.LISTEN_CELL_LOCATION
-                | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
-                | PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
-                | PhoneStateListener.LISTEN_CELL_INFO
-                | PhoneStateListener.LISTEN_SERVICE_STATE
-                | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
+        mTelephonyManager.registerPhoneStateListener(new HandlerExecutor(mHandler),
+                mPhoneStateListener);
     }
 
     private void updateDnsCheckState() {
diff --git a/src/com/android/phone/vvm/VvmSimStateTracker.java b/src/com/android/phone/vvm/VvmSimStateTracker.java
index c648d9c..d26ef6c 100644
--- a/src/com/android/phone/vvm/VvmSimStateTracker.java
+++ b/src/com/android/phone/vvm/VvmSimStateTracker.java
@@ -20,6 +20,9 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
 import android.os.SystemProperties;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
@@ -68,7 +71,8 @@
      * Waits for the account to become {@link ServiceState#STATE_IN_SERVICE} and notify the
      * connected event. Will unregister itself once the event has been triggered.
      */
-    private class ServiceStateListener extends PhoneStateListener {
+    private class ServiceStateListener extends PhoneStateListener
+            implements PhoneStateListener.ServiceStateChangedListener  {
 
         private final PhoneAccountHandle mPhoneAccountHandle;
         private final Context mContext;
@@ -84,7 +88,8 @@
                 VvmLog.e(TAG, "Cannot create TelephonyManager from " + mPhoneAccountHandle);
                 return;
             }
-            telephonyManager.listen(this, PhoneStateListener.LISTEN_SERVICE_STATE);
+            telephonyManager.registerPhoneStateListener(
+                    new HandlerExecutor(new Handler(Looper.getMainLooper())), this);
         }
 
         public void unlisten() {
@@ -92,7 +97,7 @@
             // PhoneStateListener, and mPhoneAccountHandle might be invalid at this point
             // (e.g. SIM removal)
             mContext.getSystemService(TelephonyManager.class)
-                    .listen(this, PhoneStateListener.LISTEN_NONE);
+                    .unregisterPhoneStateListener(this);
             sListeners.put(mPhoneAccountHandle, null);
         }
 
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 7fd9d39..4846d44 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -32,6 +32,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.PersistableBundle;
@@ -113,6 +114,8 @@
     private static final String EXTRA_SUPPORTS_VIDEO_CALLING_FALLBACK =
             "android.telecom.extra.SUPPORTS_VIDEO_CALLING_FALLBACK";
 
+    private Handler mHandler;
+
     final class AccountEntry implements PstnPhoneCapabilitiesNotifier.Listener {
         private final Phone mPhone;
         private PhoneAccount mAccount;
@@ -1081,7 +1084,11 @@
         }
     };
 
-    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+    private final PhoneStateListener mPhoneStateListener = new TelecomAccountPhoneStateListener();
+
+    private class TelecomAccountPhoneStateListener extends PhoneStateListener implements
+            PhoneStateListener.ActiveDataSubscriptionIdChangedListener,
+            PhoneStateListener.ServiceStateChangedListener {
         @Override
         public void onServiceStateChanged(ServiceState serviceState) {
             int newState = serviceState.getState();
@@ -1107,7 +1114,7 @@
                 }
             }
         }
-    };
+    }
 
     private static TelecomAccountRegistry sInstance;
     private final Context mContext;
@@ -1147,6 +1154,7 @@
         mTelephonyManager = TelephonyManager.from(context);
         mSubscriptionManager = SubscriptionManager.from(context);
         mHandlerThread.start();
+        mHandler = new Handler(Looper.getMainLooper());
         mRegisterSubscriptionListenerBackoff = new ExponentialBackoff(
                 REGISTER_START_DELAY_MS,
                 REGISTER_MAXIMUM_DELAY_MS,
@@ -1374,8 +1382,8 @@
 
         // We also need to listen for changes to the service state (e.g. emergency -> in service)
         // because this could signal a removal or addition of a SIM in a single SIM phone.
-        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE
-                | PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
+        mTelephonyManager.registerPhoneStateListener(new HandlerExecutor(mHandler),
+                mPhoneStateListener);
 
         // Listen for user switches.  When the user switches, we need to ensure that if the current
         // use is not the primary user we disable video calling.
@@ -1395,8 +1403,7 @@
 
     private void registerContentObservers() {
         // Listen to the RTT system setting so that we update it when the user flips it.
-        ContentObserver rttUiSettingObserver = new ContentObserver(
-                new Handler(Looper.getMainLooper())) {
+        ContentObserver rttUiSettingObserver = new ContentObserver(mHandler) {
             @Override
             public void onChange(boolean selfChange) {
                 synchronized (mAccountsLock) {
@@ -1412,8 +1419,7 @@
                 rttSettingUri, false, rttUiSettingObserver);
 
         // Listen to the changes to the user's Contacts Discovery Setting.
-        ContentObserver contactDiscoveryObserver = new ContentObserver(
-                new Handler(Looper.getMainLooper())) {
+        ContentObserver contactDiscoveryObserver = new ContentObserver(mHandler) {
             @Override
             public void onChange(boolean selfChange) {
                 synchronized (mAccountsLock) {
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 0a0ab96..c41bf1a 100755
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -1133,19 +1133,21 @@
     @Override
     public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts) {
         if (isImsConnection()) {
-            ImsPhone imsPhone = (ImsPhone) getPhone().getImsPhone();
+            ImsPhone imsPhone = (getPhone() instanceof ImsPhone) ? (ImsPhone) getPhone() : null;
             if (imsPhone != null
                     && imsPhone.getCallComposerStatus() == TelephonyManager.CALL_COMPOSER_STATUS_ON
                     && !isBlocked && isInContacts) {
                 ImsPhoneConnection originalConnection = (ImsPhoneConnection) mOriginalConnection;
                 ImsCallProfile profile = originalConnection.getImsCall().getCallProfile();
+                String serverUrl = CallComposerPictureManager.sTestMode
+                        ? CallComposerPictureManager.FAKE_SERVER_URL
+                        : profile.getCallExtra(ImsCallProfile.EXTRA_PICTURE_URL);
                 if (profile != null
-                        && !TextUtils.isEmpty(
-                        profile.getCallExtra(ImsCallProfile.EXTRA_PICTURE_URL))) {
+                        && !TextUtils.isEmpty(serverUrl)) {
                     CallComposerPictureManager manager = CallComposerPictureManager
                             .getInstance(getPhone().getContext(), getPhone().getSubId());
                     manager.handleDownloadFromServer(new CallComposerPictureTransfer.Factory() {},
-                            profile.getCallExtra(ImsCallProfile.EXTRA_PICTURE_URL),
+                            serverUrl,
                             (result) -> {
                                 if (result.first != null) {
                                     Bundle newExtras = new Bundle();
diff --git a/src/com/android/services/telephony/rcs/TelephonyRcsService.java b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
index a6789f5..66492ae 100644
--- a/src/com/android/services/telephony/rcs/TelephonyRcsService.java
+++ b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
@@ -33,6 +33,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.PhoneConfigurationManager;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.phone.R;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -87,6 +88,22 @@
         }
     };
 
+    /**
+     * Used to inject device resource for testing.
+     */
+    @VisibleForTesting
+    public interface ResourceProxy {
+        /**
+         * @return an whether the device supports User Capability Exchange.
+         */
+        boolean getDeviceUceEnabled(Context context);
+    }
+
+    private static ResourceProxy sResourceProxy = context -> {
+        return context.getResources().getBoolean(
+                R.bool.config_rcs_user_capability_exchange_enabled);
+    };
+
     // Notifies this service that there has been a change in available slots.
     private static final int HANDLER_MSIM_CONFIGURATION_CHANGE = 1;
 
@@ -97,6 +114,9 @@
     // Maps slot ID -> RcsFeatureController.
     private SparseArray<RcsFeatureController> mFeatureControllers;
 
+    // Whether the device supports User Capability Exchange
+    private boolean mRcsUceEnabled;
+
     private BroadcastReceiver mCarrierConfigChangedReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -139,6 +159,16 @@
         mContext = context;
         mNumSlots = numSlots;
         mFeatureControllers = new SparseArray<>(numSlots);
+        mRcsUceEnabled = sResourceProxy.getDeviceUceEnabled(mContext);
+    }
+
+    @VisibleForTesting
+    public TelephonyRcsService(Context context, int numSlots, ResourceProxy resourceProxy) {
+        mContext = context;
+        mNumSlots = numSlots;
+        mFeatureControllers = new SparseArray<>(numSlots);
+        sResourceProxy = resourceProxy;
+        mRcsUceEnabled = sResourceProxy.getDeviceUceEnabled(mContext);
     }
 
     /**
@@ -234,7 +264,7 @@
     }
 
     private void updateSupportedFeatures(RcsFeatureController c, int slotId, int subId) {
-        if (doesSubscriptionSupportPresence(subId)) {
+        if (isDeviceUceEnabled() && doesSubscriptionSupportPresence(subId)) {
             if (c.getFeature(UceControllerManager.class) == null) {
                 c.addFeature(mFeatureFactory.createUceControllerManager(mContext, slotId, subId),
                         UceControllerManager.class);
@@ -259,6 +289,20 @@
         if (c.hasActiveFeatures()) c.connect();
     }
 
+    /**
+     * Get whether the device supports RCS User Capability Exchange or not.
+     */
+    public boolean isDeviceUceEnabled() {
+        return mRcsUceEnabled;
+    }
+
+    /**
+     * Set the device supports RCS User Capability Exchange.
+     */
+    public void setDeviceUceEnabled(boolean isEnabled) {
+        mRcsUceEnabled = isEnabled;
+    }
+
     private boolean doesSubscriptionSupportPresence(int subId) {
         if (!SubscriptionManager.isValidSubscriptionId(subId)) return false;
         CarrierConfigManager carrierConfigManager =
diff --git a/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java b/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
index a1f97a0..c367af3 100644
--- a/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
+++ b/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
@@ -50,6 +50,7 @@
 
     @Captor ArgumentCaptor<BroadcastReceiver> mReceiverCaptor;
     @Mock TelephonyRcsService.FeatureFactory mFeatureFactory;
+    @Mock TelephonyRcsService.ResourceProxy mResourceProxy;
     @Mock UceControllerManager mMockUceSlot0;
     @Mock UceControllerManager mMockUceSlot1;
     @Mock SipTransportController mMockSipTransportSlot0;
@@ -78,6 +79,7 @@
                 eq(0), anyInt());
         doReturn(mMockSipTransportSlot1).when(mFeatureFactory).createSipTransportController(any(),
                 eq(1), anyInt());
+        doReturn(true).when(mResourceProxy).getDeviceUceEnabled(any());
         //set up default slot-> sub ID mappings.
         setSlotToSubIdMapping(0 /*slotId*/, 1/*subId*/);
         setSlotToSubIdMapping(1 /*slotId*/, 2/*subId*/);
@@ -325,7 +327,7 @@
     }
 
     private TelephonyRcsService createRcsService(int numSlots) {
-        TelephonyRcsService service = new TelephonyRcsService(mContext, numSlots);
+        TelephonyRcsService service = new TelephonyRcsService(mContext, numSlots, mResourceProxy);
         service.setFeatureFactory(mFeatureFactory);
         service.initialize();
         verify(mContext).registerReceiver(mReceiverCaptor.capture(), any());