Merge "Revert "Update path for BasicShellCommandHandler""
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2b2df11..186ab50 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -218,9 +218,11 @@
          from the background.  -->
     <uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
     <uses-permission android:name="android.permission.NETWORK_STATS_PROVIDER" />
-    <uses-permission android:name="android.permission.HANDLE_CAR_MODE_CHANGES"/>
     <uses-permission android:name="android.permission.MANAGE_SUBSCRIPTION_PLANS"/>
 
+    <!-- Needed to listen to changes in projection state. -->
+    <uses-permission android:name="android.permission.READ_PROJECTION_STATE"/>
+
     <application android:name="PhoneApp"
             android:persistent="true"
             android:label="@string/phoneAppLabel"
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index e191d41..c767c67 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -181,8 +181,8 @@
     <string name="not_allowed" msgid="8541221928746104798">"તમારું SIM કાર્ડ આ નેટવર્કથી કનેક્શનને મંજૂરી આપતું નથી."</string>
     <string name="connect_later" msgid="1950138106010005425">"હમણાં આ નેટવર્કથી કનેક્ટ કરી શકાતું નથી. પછીથી ફરી પ્રયાસ કરો."</string>
     <string name="registration_done" msgid="5337407023566953292">"નેટવર્ક પર નોંધણી કરી."</string>
-    <string name="already_auto" msgid="8607068290733079336">"પહેલેથી જ આપમેળે પસંદગીમાં."</string>
-    <string name="select_automatically" msgid="779750291257872651">"નેટવર્ક આપમેળે પસંદ કરો"</string>
+    <string name="already_auto" msgid="8607068290733079336">"પહેલેથી જ ઑટોમૅટિક પસંદગીમાં."</string>
+    <string name="select_automatically" msgid="779750291257872651">"નેટવર્ક ઑટોમૅટિક રીતે પસંદ કરો"</string>
     <string name="manual_mode_disallowed_summary" msgid="3970048592179890197">"%1$s સાથે કનેક્ટ કરેલું હોય ત્યારે અનુપલબ્ધ"</string>
     <string name="network_select_title" msgid="4117305053881611988">"નેટવર્ક"</string>
     <string name="register_automatically" msgid="3907580547590554834">"સ્વયંચાલિત નોંધણી…"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 25b6e87..cdc7f70 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -97,7 +97,7 @@
     <string name="sum_hide_caller_id" msgid="131100328602371933">"Шығыс қоңырауларда нөмірді жасыру"</string>
     <string name="sum_show_caller_id" msgid="3571854755324664591">"Шығыс қоңыраулар нөмірді көрсету"</string>
     <string name="sum_default_caller_id" msgid="1767070797135682959">"Шығыс қоңырауларында менің нөмірім көрсетілуі үшін бастапқы оператор параметрлерін қолдану"</string>
-    <string name="labelCW" msgid="8449327023861428622">"Күтудегі қоңырау"</string>
+    <string name="labelCW" msgid="8449327023861428622">"Қоңырауды ұстап тұру"</string>
     <string name="sum_cw_enabled" msgid="3977308526187139996">"Қоңырау кезінде маған келген қоңыраулар жайлы хабарлау"</string>
     <string name="sum_cw_disabled" msgid="3658094589461768637">"Қоңырау кезінде маған келген қоңыраулар жайлы хабарлау"</string>
     <string name="call_forwarding_settings" msgid="8937130467468257671">"Қоңырауды басқа нөмірге бағыттау параметрлері"</string>
@@ -125,12 +125,12 @@
     <string name="sum_cfnrc_disabled" msgid="739289696796917683">"Өшірулі"</string>
     <string name="disable_cfnrc_forbidden" msgid="775348748084726890">"Телефоныңыз қол жетімсіз болғанда жабдықтаушы қоңырауды басқа нөмірге бағыттауды өшіруді қолдамайды."</string>
     <string name="registration_cf_forbidden" msgid="4386482610771190420">"Операторыңыз қоңырауды басқа нөмірге бағыттауды қолдамайды."</string>
-    <string name="cdma_call_waiting" msgid="4565070960879673216">"Басқа желідегі қоңырау мүмкіндігін қосу керек пе?"</string>
+    <string name="cdma_call_waiting" msgid="4565070960879673216">"Қоңырауды ұстап тұру мүмкіндігін қосу керек пе?"</string>
     <string name="enable_cdma_call_waiting_setting" msgid="5906811747921744307">"Сөйлесіп жатқанда сізге кіріс қоңыраулар туралы хабарландыру көрсетіледі."</string>
     <string name="enable_cdma_cw" msgid="811047045863422232">"Қосу"</string>
     <string name="disable_cdma_cw" msgid="7119290446496301734">"Бас тарту"</string>
-    <string name="cdma_call_waiting_in_ims_on" msgid="6390979414188659218">"IMS бойынша басқа CDMA желісіндегі қоңырау мүмкіндігі қосулы."</string>
-    <string name="cdma_call_waiting_in_ims_off" msgid="1099246114368636334">"IMS бойынша басқа CDMA желісіндегі қоңырау мүмкіндігі өшірулі."</string>
+    <string name="cdma_call_waiting_in_ims_on" msgid="6390979414188659218">"IMS бойынша басқа CDMA желісіндегі қоңырауды ұстап тұру мүмкіндігі қосулы."</string>
+    <string name="cdma_call_waiting_in_ims_off" msgid="1099246114368636334">"IMS бойынша басқа CDMA желісіндегі қоңырауды ұстап тұру мүмкіндігі өшірулі."</string>
     <string name="updating_title" msgid="6130548922615719689">"Қоңырау параметрлері"</string>
     <string name="call_settings_admin_user_only" msgid="7238947387649986286">"Қоңырау параметрлерін тек әкімші пайдаланушы өзгерте алады."</string>
     <string name="call_settings_with_label" msgid="8460230435361579511">"Параметрлер (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index f487305..0fbb963 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -31,7 +31,7 @@
     <string name="ussdRunning" msgid="1163586813106772717">"USSD код ажиллаж байна…"</string>
     <string name="mmiCancelled" msgid="5339191899200678272">"MMI код цуцлагдсан"</string>
     <string name="cancel" msgid="8984206397635155197">"Цуцлах"</string>
-    <string name="enter_input" msgid="6193628663039958990">"USSD зурвасын үсгийн тоо <xliff:g id="MIN_LEN">%1$d</xliff:g> болон <xliff:g id="MAX_LEN">%2$d</xliff:g> хооронд байх шаардлагатай. Дахин оролдоно уу."</string>
+    <string name="enter_input" msgid="6193628663039958990">"USSD мессежийн үсгийн тоо <xliff:g id="MIN_LEN">%1$d</xliff:g> болон <xliff:g id="MAX_LEN">%2$d</xliff:g> хооронд байх шаардлагатай. Дахин оролдоно уу."</string>
     <string name="manageConferenceLabel" msgid="8415044818156353233">"Хурлын дуудлага удирдах"</string>
     <string name="ok" msgid="7818974223666140165">"OK"</string>
     <string name="audio_mode_speaker" msgid="243689733219312360">"Чанга яригч"</string>
@@ -171,7 +171,7 @@
     <string name="vm_change_pin_error_mismatch" msgid="5364847280026257331">"Хуучин PIN таарахгүй байна."</string>
     <string name="vm_change_pin_error_invalid" msgid="5230002671175580674">"Шинэ PIN-д буруу тэмдэгт агуулагдаж байна."</string>
     <string name="vm_change_pin_error_system_error" msgid="9116483527909681791">"PIN-г өөрчлөх боломжгүй"</string>
-    <string name="vvm_unsupported_message_format" msgid="4206402558577739713">"Дэмжигдээгүй зурвасын төрөл, сонсохын тулд <xliff:g id="NUMBER">%s</xliff:g> руу залгана уу."</string>
+    <string name="vvm_unsupported_message_format" msgid="4206402558577739713">"Дэмжигдээгүй мессежийн төрөл, сонсохын тулд <xliff:g id="NUMBER">%s</xliff:g> руу залгана уу."</string>
     <string name="network_settings_title" msgid="7560807107123171541">"Мобайл сүлжээ"</string>
     <string name="label_available" msgid="1316084116670821258">"Боломжтой сүлжээнүүд"</string>
     <string name="load_networks_progress" msgid="4051433047717401683">"Хайж байна..."</string>
@@ -539,7 +539,7 @@
     <string name="incall_error_out_of_service_wfc" msgid="4497663185857190885">"Мобайл сүлжээнд холбогдох боломжгүй байна. Дуудлага хийхийн тулд утасгүй интернетэд холбогдоно уу."</string>
     <string name="incall_error_no_phone_number_supplied" msgid="8680831089508851894">"Дуудлага хийхийн тулд хүчин төгөлдөр дугаар оруулна уу."</string>
     <string name="incall_error_call_failed" msgid="393508653582682539">"Дуудлага амжилтгүй болсон."</string>
-    <string name="incall_error_cannot_add_call" msgid="5425764862628655443">"Дуудлагыг энэ удаад нэмэх боломжгүй. Та зурвас илгээн холбоо тогтоохыг оролдох боломжтой."</string>
+    <string name="incall_error_cannot_add_call" msgid="5425764862628655443">"Дуудлагыг энэ удаад нэмэх боломжгүй. Та мессеж илгээн холбоо тогтоохыг оролдох боломжтой."</string>
     <string name="incall_error_supp_service_unknown" msgid="8751177117194592623">"Үйлчилгээг дэмждэггүй байна"</string>
     <string name="incall_error_supp_service_switch" msgid="5272822448189448479">"Дуудлагыг солих боломжгүй байна."</string>
     <string name="incall_error_supp_service_resume" msgid="1276861499306817035">"Дуудлагыг үргэлжлүүлэх боломжгүй."</string>
@@ -590,8 +590,8 @@
     <string name="hac_mode_title" msgid="4127986689621125468">"Сонсголын төхөөрөмж"</string>
     <string name="hac_mode_summary" msgid="7774989500136009881">"Сонсголын төхөөрөмж тааруулагчийг асаана уу"</string>
     <string name="rtt_mode_title" msgid="3075948111362818043">"Шууд мессежлэх (RTT) дуудлага"</string>
-    <string name="rtt_mode_summary" msgid="8631541375609989562">"Дуудлагын дотор зурвас бичихийг зөвшөөрөх"</string>
-    <string name="rtt_mode_more_information" msgid="587500128658756318">"RTT нь дүлий, хатуу чихтэй, хэл ярианы хөгжлийн бэрхшээлтэй, эсвэл хэн нэгний тусламжтай ярьдаг дуудлага хийгчдэд тусладаг.&lt;br&gt; &lt;a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>&gt;Дэлгэрэнгүй үзэх&lt;/a&gt;\n       &lt;br&gt;&lt;br&gt; - RTT дуудлага нь зурвасын сийрүүлэг хэлбэрээр хадгалагдана\n       &lt;br&gt; - RTT нь видео дуудлага хийхэд боломжгүй"</string>
+    <string name="rtt_mode_summary" msgid="8631541375609989562">"Дуудлагын дотор мессеж бичихийг зөвшөөрөх"</string>
+    <string name="rtt_mode_more_information" msgid="587500128658756318">"RTT нь дүлий, хатуу чихтэй, хэл ярианы хөгжлийн бэрхшээлтэй, эсвэл хэн нэгний тусламжтай ярьдаг дуудлага хийгчдэд тусладаг.&lt;br&gt; &lt;a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>&gt;Дэлгэрэнгүй үзэх&lt;/a&gt;\n       &lt;br&gt;&lt;br&gt; - RTT дуудлага нь мессежийн сийрүүлэг хэлбэрээр хадгалагдана\n       &lt;br&gt; - RTT нь видео дуудлага хийхэд боломжгүй"</string>
     <string name="no_rtt_when_roaming" msgid="5268008247378355389">"Санамж: RTT (Тухайн агшины текст) роуминг үйлчилгээний үед боломжгүй байна"</string>
   <string-array name="tty_mode_entries">
     <item msgid="3238070884803849303">"TTY Унтраасан"</item>
@@ -605,8 +605,8 @@
     <item msgid="2271798469250155310">"Энгийн"</item>
     <item msgid="6044210222666533564">"Урт"</item>
   </string-array>
-    <string name="network_info_message" msgid="7599413947016532355">"Сүлжээний зурвас"</string>
-    <string name="network_error_message" msgid="4271579424089326618">"Алдаатай зурвас"</string>
+    <string name="network_info_message" msgid="7599413947016532355">"Сүлжээний мессеж"</string>
+    <string name="network_error_message" msgid="4271579424089326618">"Алдаатай мессеж"</string>
     <string name="ota_title_activate" msgid="4049645324841263423">"Утсаа идэвхжүүлнэ үү"</string>
     <string name="ota_touch_activate" msgid="838764494319694754">"Таны утасны үйлчилгээ идэвхжүүлэхийн тулд тусгай дуудлага хийх шаардлагатай. \n\n“Идэвхжүүлэх” гэснийг дарсны дараа өгч байгаа зааврыг сонсон утсаа идэвхжүүлнэ үү."</string>
     <string name="ota_hfa_activation_title" msgid="3300556778212729671">"Идэвхжүүлж байна..."</string>
@@ -679,7 +679,7 @@
     <string name="status_hint_label_incoming_wifi_call" msgid="2606052595898044071">"Wi-Fi дуудлага"</string>
     <string name="status_hint_label_wifi_call" msgid="942993035689809853">"Wi-Fi дуудлага"</string>
     <string name="emergency_action_launch_hint" msgid="2762016865340891314">"Нээхийн тулд дахин дарна уу"</string>
-    <string name="message_decode_error" msgid="1061856591500290887">"Зурвасын кодыг тайлах явцад алдаа гарсан."</string>
+    <string name="message_decode_error" msgid="1061856591500290887">"Мессежийн кодыг тайлах явцад алдаа гарсан."</string>
     <string name="callFailed_cdma_activation" msgid="5392057031552253550">"SIM карт таны үйлчилгээг идэвхжүүлж, утасны роаминг багтаамжийг шинэчиллээ."</string>
     <string name="callFailed_cdma_call_limit" msgid="1074219746093031412">"Хэт олон идэвхтэй дуудлага байна. Шинэ дуудлага хийхийн өмнө одоогийн дуудлагуудыг таслах буюу нэгтгэнэ үү."</string>
     <string name="callFailed_imei_not_accepted" msgid="7257903653685147251">"Холбох боломжгүй, хүчинтэй SIM карт оруулна уу."</string>
@@ -874,7 +874,7 @@
     <string name="radio_info_subid" msgid="6839966868621703203">"Одоогийн subId:"</string>
     <string name="radio_info_dds" msgid="1122593144425697126">"Өгөгдмөл дата SIM-н SubId:"</string>
     <string name="radio_info_dl_kbps" msgid="2382922659525318726">"DLзурвасын өргөн (kbps):"</string>
-    <string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL Зурвасын өргөн (kbps):"</string>
+    <string name="radio_info_ul_kbps" msgid="2102225400904799036">"UL Мессежийн өргөн (kbps):"</string>
     <string name="radio_info_phy_chan_config" msgid="1277949603275436081">"LTE Сувгийн бодит тохиргоо:"</string>
     <string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Үүрэн мэдээлэл сэргээх тариф:"</string>
     <string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Бүх үүрэн хэмжилтийн мэдээлэл:"</string>
@@ -889,9 +889,9 @@
     <string name="radio_info_signal_strength_label" msgid="5545444702102543260">"Дохионы хүч:"</string>
     <string name="radio_info_call_status_label" msgid="7693575431923095487">"Дуут дуудлагын төлөв:"</string>
     <string name="radio_info_ppp_sent_label" msgid="6542208429356199695">"Дата илгээсэн:"</string>
-    <string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Зурвасыг хүлээж байна:"</string>
+    <string name="radio_info_message_waiting_label" msgid="1886549432566952078">"Мессежийг хүлээж байна:"</string>
     <string name="radio_info_phone_number_label" msgid="2533852539562512203">"Утасны дугаар:"</string>
-    <string name="radio_info_band_mode_label" msgid="23480556225515290">"Радио зурвасыг сонгох"</string>
+    <string name="radio_info_band_mode_label" msgid="23480556225515290">"Радио мессежийг сонгох"</string>
     <string name="radio_info_voice_network_type_label" msgid="2395347336419593265">"Дуут сүлжээний төрөл:"</string>
     <string name="radio_info_data_network_type_label" msgid="8886597029237501929">"Дата сүлжээний төрөл:"</string>
     <string name="phone_index_label" msgid="6222406512768964268">"Утасны индекс сонгох"</string>
@@ -910,8 +910,8 @@
     <string name="radio_info_nr_available" msgid="1321318331361249997">"NR боломжтой:"</string>
     <string name="radio_info_nr_state" msgid="1337571996788535356">"NR төлөв:"</string>
     <string name="radio_info_nr_frequency" msgid="1201156032796584128">"NR давтамж:"</string>
-    <string name="band_mode_title" msgid="7988822920724576842">"Радио зурвасын горимыг тохируулах"</string>
-    <string name="band_mode_loading" msgid="795923726636735967">"Зурвасын жагсаалтыг ачаалж байна…"</string>
+    <string name="band_mode_title" msgid="7988822920724576842">"Радио мессежийн горимыг тохируулах"</string>
+    <string name="band_mode_loading" msgid="795923726636735967">"Мессежийн жагсаалтыг ачаалж байна…"</string>
     <string name="band_mode_set" msgid="6657819412803771421">"Тохируулах"</string>
     <string name="band_mode_failed" msgid="1707488541847192924">"Амжилтгүй"</string>
     <string name="band_mode_succeeded" msgid="2230018000534761063">"Амжилттай"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 9c773d9..b6996f7 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -296,7 +296,7 @@
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"వేరొక SIMను ఉపయోగిస్తున్నప్పుడు <xliff:g id="CARRIER_NAME">%1$s</xliff:g> కాల్‌లు మరియు డేటా సేవలు బ్లాక్ చేయబడవచ్చు."</string>
     <string name="data_usage_title" msgid="8438592133893837464">"యాప్ డేటా వినియోగం"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_2">%2$s</xliff:g> మధ్య కాలంలో <xliff:g id="ID_1">%1$s</xliff:g> మొబైల్ డేటా ఉపయోగించబడింది"</string>
-    <string name="advanced_options_title" msgid="9208195294513520934">"అధునాతనం"</string>
+    <string name="advanced_options_title" msgid="9208195294513520934">"అధునాతన సెట్టింగ్‌లు"</string>
     <string name="carrier_settings_euicc" msgid="1190237227261337749">"క్యారియర్"</string>
     <string name="keywords_carrier_settings_euicc" msgid="8540160967922063745">"క్యారియర్, ఇసిమ్, సిమ్, ఇయుక్, క్యారియర్‌లను మార్చు, క్యారియర్‌ను జోడించు"</string>
     <string name="carrier_settings_euicc_summary" msgid="2027941166597330117">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> — <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>"</string>
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index fd61936..e415122 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -23,6 +23,7 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsUceAdapter.PublishState;
 import android.telephony.ims.RegistrationManager;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsRcsController;
@@ -44,7 +45,7 @@
 import com.android.services.telephony.rcs.RcsFeatureController;
 import com.android.services.telephony.rcs.SipTransportController;
 import com.android.services.telephony.rcs.TelephonyRcsService;
-import com.android.services.telephony.rcs.UserCapabilityExchangeImpl;
+import com.android.services.telephony.rcs.UceControllerManager;
 
 import java.util.List;
 
@@ -203,40 +204,6 @@
         }
     }
 
-    @Override
-    public void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
-        enforceReadPrivilegedPermission("registerUcePublishStateCallback");
-        final long token = Binder.clearCallingIdentity();
-        try {
-            UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
-                    UserCapabilityExchangeImpl.class);
-            if (uce == null) {
-                throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
-                    "This subscription does not support UCE.");
-            }
-            uce.registerPublishStateCallback(c);
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    @Override
-    public void unregisterUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
-        enforceReadPrivilegedPermission("unregisterUcePublishStateCallback");
-        final long token = Binder.clearCallingIdentity();
-        try {
-            UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
-                    UserCapabilityExchangeImpl.class);
-            if (uce == null) {
-                throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
-                    "This subscription does not support UCE.");
-            }
-            uce.unregisterUcePublishStateCallback(c);
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
     /**
      * Query for the capability of an IMS RCS service
      *
@@ -296,30 +263,94 @@
         }
         final long token = Binder.clearCallingIdentity();
         try {
-            UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
-                    UserCapabilityExchangeImpl.class);
-            if (uce == null) {
+            UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                    UceControllerManager.class);
+            if (uceCtrlManager == null) {
                 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
                         "This subscription does not support UCE.");
             }
-            uce.requestCapabilities(contactNumbers, c);
+            uceCtrlManager.requestCapabilities(contactNumbers, c);
+        } catch (ImsException e) {
+            throw new ServiceSpecificException(e.getCode(), e.getMessage());
         } finally {
             Binder.restoreCallingIdentity(token);
         }
     }
 
     @Override
-    public int getUcePublishState(int subId) {
-        enforceReadPrivilegedPermission("getUcePublishState");
+    public void requestNetworkAvailability(int subId, String callingPackage,
+            String callingFeatureId, Uri contactNumber, IRcsUceControllerCallback c) {
+        enforceReadPrivilegedPermission("requestNetworkAvailability");
+        if (!isUceSettingEnabled(subId, callingPackage, callingFeatureId)) {
+            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                    "The user has not enabled UCE for this subscription.");
+        }
         final long token = Binder.clearCallingIdentity();
         try {
-            UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
-                    UserCapabilityExchangeImpl.class);
-            if (uce == null) {
+            UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                    UceControllerManager.class);
+            if (uceCtrlManager == null) {
                 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
                         "This subscription does not support UCE.");
             }
-            return uce.getUcePublishState();
+            uceCtrlManager.requestNetworkAvailability(contactNumber, c);
+        } catch (ImsException e) {
+            throw new ServiceSpecificException(e.getCode(), e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public @PublishState int getUcePublishState(int subId) {
+        enforceReadPrivilegedPermission("getUcePublishState");
+        final long token = Binder.clearCallingIdentity();
+        try {
+            UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                    UceControllerManager.class);
+            if (uceCtrlManager == null) {
+                throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                        "This subscription does not support UCE.");
+            }
+            return uceCtrlManager.getUcePublishState();
+        } catch (ImsException e) {
+            throw new ServiceSpecificException(e.getCode(), e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
+        enforceReadPrivilegedPermission("registerUcePublishStateCallback");
+        final long token = Binder.clearCallingIdentity();
+        try {
+            UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                    UceControllerManager.class);
+            if (uceCtrlManager == null) {
+                throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                        "This subscription does not support UCE.");
+            }
+            uceCtrlManager.registerPublishStateCallback(c);
+        } catch (ImsException e) {
+            throw new ServiceSpecificException(e.getCode(), e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void unregisterUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
+        enforceReadPrivilegedPermission("unregisterUcePublishStateCallback");
+        final long token = Binder.clearCallingIdentity();
+        try {
+            UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                    UceControllerManager.class);
+            if (uceCtrlManager == null) {
+                throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                        "This subscription does not support UCE.");
+            }
+            uceCtrlManager.unregisterPublishStateCallback(c);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
diff --git a/src/com/android/phone/ImsUtil.java b/src/com/android/phone/ImsUtil.java
index 38936ec..0825cfb 100644
--- a/src/com/android/phone/ImsUtil.java
+++ b/src/com/android/phone/ImsUtil.java
@@ -25,6 +25,8 @@
 
 import com.android.ims.ImsConfig;
 import com.android.ims.ImsManager;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.imsphone.ImsPhone;
 
 public class ImsUtil {
     private static final String LOG_TAG = ImsUtil.class.getSimpleName();
@@ -128,6 +130,13 @@
             return false;
         }
 
+        // Do not promote WFC if in roaming and WFC roaming not allowed.
+        // WFC roaming setting is not modifiable, so its value is decided by the default value
+        // chosen by the carrier, hence it really means if the carrier supports WFC roaming.
+        if (getLastKnownRoamingState(phoneId) && !imsManager.isWfcRoamingEnabledByUser()) {
+            return false;
+        }
+
         ConnectivityManager cm =
                 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
         if (cm != null) {
@@ -152,4 +161,13 @@
         }
         return subId;
     }
+
+    private static boolean getLastKnownRoamingState(int phoneId) {
+        try {
+            ImsPhone imsPhone = (ImsPhone) (PhoneFactory.getPhone(phoneId).getImsPhone());
+            return imsPhone.getRoamingState();
+        } catch (NullPointerException | ClassCastException e) {
+            return false;
+        }
+    }
 }
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index c2dece5..fe4a0ba 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -536,7 +536,11 @@
                 int slotId = SubscriptionManager.getSlotIndex(subId);
                 resId = (slotId == 0) ? R.drawable.stat_sys_phone_call_forward_sub1
                         : R.drawable.stat_sys_phone_call_forward_sub2;
-                notificationTitle = subInfo.getDisplayName().toString();
+                if (subInfo.getDisplayName() != null) {
+                    notificationTitle = subInfo.getDisplayName().toString();
+                } else {
+                    notificationTitle = mContext.getString(R.string.labelCF);
+                }
             } else {
                 notificationTitle = mContext.getString(R.string.labelCF);
             }
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index ed4372e..724ffbe 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -65,6 +65,7 @@
 import android.telecom.TelecomManager;
 import android.telephony.Annotation.ApnType;
 import android.telephony.CallForwardingInfo;
+import android.telephony.CarrierBandwidth;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CarrierRestrictionRules;
 import android.telephony.CellIdentity;
@@ -293,6 +294,8 @@
     private static final int EVENT_ENABLE_NR_DUAL_CONNECTIVITY_DONE = 92;
     private static final int CMD_IS_NR_DUAL_CONNECTIVITY_ENABLED = 93;
     private static final int EVENT_IS_NR_DUAL_CONNECTIVITY_ENABLED_DONE = 94;
+    private static final int CMD_GET_CDMA_SUBSCRIPTION_MODE = 95;
+    private static final int EVENT_GET_CDMA_SUBSCRIPTION_MODE_DONE = 96;
 
     // Parameters of select command.
     private static final int SELECT_COMMAND = 0xA4;
@@ -1395,11 +1398,27 @@
                     request.result = ar.exception == null;
                     notifyRequester(request);
                     break;
+                case CMD_GET_CDMA_SUBSCRIPTION_MODE:
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_MODE_DONE, request);
+                    getPhoneFromRequest(request).queryCdmaSubscriptionMode(onCompleted);
+                    break;
+                case EVENT_GET_CDMA_SUBSCRIPTION_MODE_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    if (ar.exception != null) {
+                        request.result = TelephonyManager.CDMA_SUBSCRIPTION_RUIM_SIM;
+                    } else {
+                        request.result = ((int[]) ar.result)[0];
+                    }
+                    notifyRequester(request);
+                    break;
                 case CMD_SET_CDMA_SUBSCRIPTION_MODE:
                     request = (MainThreadRequest) msg.obj;
                     onCompleted = obtainMessage(EVENT_SET_CDMA_SUBSCRIPTION_MODE_DONE, request);
                     int subscriptionMode = (int) request.argument;
-                    getPhoneFromRequest(request).setCdmaSubscription(subscriptionMode, onCompleted);
+                    getPhoneFromRequest(request).setCdmaSubscriptionMode(
+                            subscriptionMode, onCompleted);
                     break;
                 case EVENT_SET_CDMA_SUBSCRIPTION_MODE_DONE:
                     ar = (AsyncResult) msg.obj;
@@ -5892,6 +5911,28 @@
     }
 
     /**
+     * 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);
+        }
+    }
+
+    /**
      * Get the effective allowed network types on the device.
      * This API will return an intersection of allowed network types for all reasons,
      * including the configuration done through setAllowedNetworkTypes
@@ -8149,6 +8190,20 @@
     }
 
     @Override
+    public int getCdmaSubscriptionMode(int subId) {
+        TelephonyPermissions
+                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                        mApp, subId, "getCdmaSubscriptionMode");
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return (int) sendRequest(CMD_GET_CDMA_SUBSCRIPTION_MODE, null /* argument */, subId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
     public boolean setCdmaSubscriptionMode(int subId, int mode) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setCdmaSubscriptionMode");
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index 4826ecb..c9f762b 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -16,6 +16,7 @@
 
 package com.android.services.telephony;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
@@ -28,6 +29,7 @@
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
 import android.util.Pair;
 
 import com.android.ims.internal.ConferenceParticipant;
@@ -1269,58 +1271,79 @@
     }
 
     /**
+     * Extracts a phone number from a {@link Uri}.
+     * <p>
+     * Phone numbers can be represented either as a TEL URI or a SIP URI.
+     * For conference event packages, RFC3261 specifies how participants can be identified using a
+     * SIP URI.
+     * A valid SIP uri has the format: sip:user:password@host:port;uri-parameters?headers
+     * Per RFC3261, the "user" can be a telephone number.
+     * For example: sip:1650555121;phone-context=blah.com@host.com
+     * In this case, the phone number is in the user field of the URI, and the parameters can be
+     * ignored.
+     *
+     * A SIP URI can also specify a phone number in a format similar to:
+     * sip:+1-212-555-1212@something.com;user=phone
+     * In this case, the phone number is again in user field and the parameters can be ignored.
+     * We can get the user field in these instances by splitting the string on the @, ;, or :
+     * and looking at the first found item.
+     * @param handle The URI containing a SIP or TEL formatted phone number.
+     * @return extracted phone number.
+     */
+    private static @NonNull String extractPhoneNumber(@NonNull Uri handle) {
+        // Number is always in the scheme specific part, regardless of whether this is a TEL or SIP
+        // URI.
+        String number = handle.getSchemeSpecificPart();
+        // Get anything before the @ for the SIP case.
+        String[] numberParts = number.split("[@;:]");
+
+        if (numberParts.length == 0) {
+            Log.v(LOG_TAG, "extractPhoneNumber(N) : no number in handle");
+            return "";
+        }
+        return numberParts[0];
+    }
+
+    /**
      * Determines if the passed in participant handle is the same as the conference host's handle.
      * Starts with a simple equality check.  However, the handles from a conference event package
      * will be a SIP uri, so we need to pull that apart to look for the participant's phone number.
      *
-     * @param hostHandles The handle(s) of the connection hosting the conference.
+     * @param hostHandles The handle(s) of the connection hosting the conference, typically obtained
+     *                    from P-Associated-Uri entries.
      * @param handle The handle of the conference participant.
      * @return {@code true} if the host's handle matches the participant's handle, {@code false}
      *      otherwise.
      */
-    private boolean isParticipantHost(Uri[] hostHandles, Uri handle) {
+    @VisibleForTesting
+    public static boolean isParticipantHost(Uri[] hostHandles, Uri handle) {
         // If there is no host handle or no participant handle, bail early.
         if (hostHandles == null || hostHandles.length == 0 || handle == null) {
-            Log.v(this, "isParticipantHost(N) : host or participant uri null");
+            Log.v(LOG_TAG, "isParticipantHost(N) : host or participant uri null");
             return false;
         }
 
-        // Conference event package participants are identified using SIP URIs (see RFC3261).
-        // A valid SIP uri has the format: sip:user:password@host:port;uri-parameters?headers
-        // Per RFC3261, the "user" can be a telephone number.
-        // For example: sip:1650555121;phone-context=blah.com@host.com
-        // In this case, the phone number is in the user field of the URI, and the parameters can be
-        // ignored.
-        //
-        // A SIP URI can also specify a phone number in a format similar to:
-        // sip:+1-212-555-1212@something.com;user=phone
-        // In this case, the phone number is again in user field and the parameters can be ignored.
-        // We can get the user field in these instances by splitting the string on the @, ;, or :
-        // and looking at the first found item.
-
-        String number = handle.getSchemeSpecificPart();
-        String numberParts[] = number.split("[@;:]");
-
-        if (numberParts.length == 0) {
-            Log.v(this, "isParticipantHost(N) : no number in participant handle");
+        String number = extractPhoneNumber(handle);
+        // If we couldn't extract the participant's number, then we can't determine if it is the
+        // host or not.
+        if (TextUtils.isEmpty(number)) {
             return false;
         }
-        number = numberParts[0];
 
         for (Uri hostHandle : hostHandles) {
             if (hostHandle == null) {
                 continue;
             }
-            // The host number will be a tel: uri.  Per RFC3966, the part after tel: is the phone
-            // number.
-            String hostNumber = hostHandle.getSchemeSpecificPart();
+            // Similar to the CEP participant data, the host identity in the P-Associated-Uri could
+            // be a SIP URI or a TEL URI.
+            String hostNumber = extractPhoneNumber(hostHandle);
 
             // Use a loose comparison of the phone numbers.  This ensures that numbers that differ
             // by special characters are counted as equal.
             // E.g. +16505551212 would be the same as 16505551212
             boolean isHost = PhoneNumberUtils.compare(hostNumber, number);
 
-            Log.v(this, "isParticipantHost(%s) : host: %s, participant %s", (isHost ? "Y" : "N"),
+            Log.v(LOG_TAG, "isParticipantHost(%s) : host: %s, participant %s", (isHost ? "Y" : "N"),
                     Rlog.pii(LOG_TAG, hostNumber), Rlog.pii(LOG_TAG, number));
 
             if (isHost) {
diff --git a/src/com/android/services/telephony/PstnIncomingCallNotifier.java b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
index 377b471..b6d5d3e 100644
--- a/src/com/android/services/telephony/PstnIncomingCallNotifier.java
+++ b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
@@ -289,7 +289,7 @@
             }
             if (((ImsPhoneConnection) connection).isIncomingCallAutoRejected()) {
                 extras.putString(TelecomManager.EXTRA_CALL_DISCONNECT_MESSAGE,
-                        "Call Dropped by lower layers");
+                        TelecomManager.CALL_AUTO_DISCONNECT_MESSAGE_STRING);
             }
         }
 
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 666ce4b..7af1678 100755
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -96,6 +96,9 @@
     private static final int MSG_CONFERENCE_MERGE_FAILED = 6;
     private static final int MSG_SUPP_SERVICE_NOTIFY = 7;
 
+    // the threshold used to compare mAudioCodecBitrateKbps and mAudioCodecBandwidth.
+    private static final float THRESHOLD = 0.01f;
+
     /**
      * Mappings from {@link com.android.internal.telephony.Connection} extras keys to their
      * equivalents defined in {@link android.telecom.Connection}.
@@ -1496,7 +1499,8 @@
         }
     }
 
-    private void refreshCodecType() {
+    private void refreshCodec() {
+        boolean changed = false;
         Bundle newExtras = getExtras();
         if (newExtras == null) {
             newExtras = new Bundle();
@@ -1512,6 +1516,31 @@
                 Connection.AUDIO_CODEC_NONE);
         if (newCodecType != oldCodecType) {
             newExtras.putInt(Connection.EXTRA_AUDIO_CODEC, newCodecType);
+            changed = true;
+        }
+        if (isImsConnection()) {
+            float newBitrate = getOriginalConnection().getAudioCodecBitrateKbps();
+            float oldBitrate = newExtras.getFloat(Connection.EXTRA_AUDIO_CODEC_BITRATE_KBPS, 0.0f);
+            if (Math.abs(newBitrate - oldBitrate) > THRESHOLD) {
+                newExtras.putFloat(Connection.EXTRA_AUDIO_CODEC_BITRATE_KBPS, newBitrate);
+                changed = true;
+            }
+
+            float newBandwidth = getOriginalConnection().getAudioCodecBandwidthKhz();
+            float oldBandwidth = newExtras.getFloat(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ,
+                    0.0f);
+            if (Math.abs(newBandwidth - oldBandwidth) > THRESHOLD) {
+                newExtras.putFloat(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ, newBandwidth);
+                changed = true;
+            }
+        } else {
+            ArrayList<String> toRemove = new ArrayList<>();
+            toRemove.add(Connection.EXTRA_AUDIO_CODEC_BITRATE_KBPS);
+            toRemove.add(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ);
+            removeTelephonyExtras(toRemove);
+        }
+
+        if (changed) {
             putTelephonyExtras(newExtras);
         }
     }
@@ -2195,7 +2224,7 @@
         updateAddress();
         updateMultiparty();
         refreshDisableAddCall();
-        refreshCodecType();
+        refreshCodec();
     }
 
     /**
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 322993a..f98198b 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -1190,15 +1190,23 @@
                             "Phone is null"));
         }
 
+        Bundle extras = request.getExtras();
+        String disconnectMessage = null;
+        if (extras.containsKey(TelecomManager.EXTRA_CALL_DISCONNECT_MESSAGE)) {
+            disconnectMessage = extras.getString(TelecomManager.EXTRA_CALL_DISCONNECT_MESSAGE);
+            Log.i(this, "onCreateIncomingConnection Disconnect message " + disconnectMessage);
+        }
+
         Call call = phone.getRingingCall();
-        if (!call.getState().isRinging()) {
+        if (!call.getState().isRinging()
+                || (disconnectMessage != null
+                && disconnectMessage.equals(TelecomManager.CALL_AUTO_DISCONNECT_MESSAGE_STRING))) {
             Log.i(this, "onCreateIncomingConnection, no ringing call");
             Connection connection = Connection.createFailedConnection(
                     mDisconnectCauseFactory.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.INCOMING_MISSED,
                             "Found no ringing call",
                             phone.getPhoneId()));
-            Bundle extras = request.getExtras();
 
             long time = extras.getLong(TelecomManager.EXTRA_CALL_CREATED_EPOCH_TIME_MILLIS);
             if (time != 0) {
diff --git a/src/com/android/services/telephony/rcs/TelephonyRcsService.java b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
index 79170af..941a6a8 100644
--- a/src/com/android/services/telephony/rcs/TelephonyRcsService.java
+++ b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
@@ -47,21 +47,19 @@
     private static final String LOG_TAG = "TelephonyRcsService";
 
     /**
-     * Used to inject RcsFeatureController and UserCapabilityExchangeImpl instances for testing.
+     * Used to inject RcsFeatureController and UceController instances for testing.
      */
     @VisibleForTesting
     public interface FeatureFactory {
         /**
-         * @return an {@link RcsFeatureController} assoicated with the slot specified.
+         * @return an {@link RcsFeatureController} associated with the slot specified.
          */
         RcsFeatureController createController(Context context, int slotId);
 
         /**
-         * @return an instance of {@link UserCapabilityExchangeImpl} associated with the slot
-         * specified.
+         * @return an instance of {@link UceControllerManager} associated with the slot specified.
          */
-        UserCapabilityExchangeImpl createUserCapabilityExchange(Context context, int slotId,
-                int subId);
+        UceControllerManager createUceControllerManager(Context context, int slotId, int subId);
 
         /**
          * @return an instance of {@link SipTransportController} for the slot and subscription
@@ -77,9 +75,9 @@
         }
 
         @Override
-        public UserCapabilityExchangeImpl createUserCapabilityExchange(Context context, int slotId,
+        public UceControllerManager createUceControllerManager(Context context, int slotId,
                 int subId) {
-            return new UserCapabilityExchangeImpl(context, slotId, subId);
+            return new UceControllerManager(context, slotId, subId);
         }
 
         @Override
@@ -237,13 +235,13 @@
 
     private void updateSupportedFeatures(RcsFeatureController c, int slotId, int subId) {
         if (doesSubscriptionSupportPresence(subId)) {
-            if (c.getFeature(UserCapabilityExchangeImpl.class) == null) {
-                c.addFeature(mFeatureFactory.createUserCapabilityExchange(mContext, slotId, subId),
-                        UserCapabilityExchangeImpl.class);
+            if (c.getFeature(UceControllerManager.class) == null) {
+                c.addFeature(mFeatureFactory.createUceControllerManager(mContext, slotId, subId),
+                        UceControllerManager.class);
             }
         } else {
-            if (c.getFeature(UserCapabilityExchangeImpl.class) != null) {
-                c.removeFeature(UserCapabilityExchangeImpl.class);
+            if (c.getFeature(UceControllerManager.class) != null) {
+                c.removeFeature(UceControllerManager.class);
             }
         }
 
diff --git a/src/com/android/services/telephony/rcs/UceControllerManager.java b/src/com/android/services/telephony/rcs/UceControllerManager.java
new file mode 100644
index 0000000..db8c933
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/UceControllerManager.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.services.telephony.rcs;
+
+import android.content.Context;
+import android.net.Uri;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.RcsUceAdapter.PublishState;
+import android.telephony.ims.aidl.IRcsUceControllerCallback;
+import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
+import android.util.Log;
+
+import com.android.ims.RcsFeatureManager;
+import com.android.ims.rcs.uce.UceController;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+/**
+ * Responsible for managing the creation and destruction of UceController. It also received the
+ * requests from {@link com.android.phone.ImsRcsController} and pass these requests to
+ * {@link UceController}
+ */
+public class UceControllerManager implements RcsFeatureController.Feature {
+
+    private static final String LOG_TAG = "UceControllerManager";
+
+    private final int mSlotId;
+    private final Context mContext;
+    private final ExecutorService mExecutorService;
+
+    private volatile UceController mUceController;
+
+    public UceControllerManager(Context context, int slotId, int subId) {
+        Log.d(LOG_TAG, "create: slotId=" + slotId + ", subId=" + subId);
+
+        mSlotId = slotId;
+        mContext = context;
+        mExecutorService = Executors.newSingleThreadExecutor();
+        mUceController = new UceController(mContext, subId);
+    }
+
+    /**
+     * Constructor to inject dependencies for testing.
+     */
+    @VisibleForTesting
+    public UceControllerManager(Context context, int slotId, int subId, ExecutorService executor) {
+        mSlotId = slotId;
+        mContext = context;
+        mExecutorService = executor;
+        mUceController = new UceController(mContext, subId);
+    }
+
+    @Override
+    public void onRcsConnected(RcsFeatureManager manager) {
+        mExecutorService.submit(() -> mUceController.onRcsConnected(manager));
+    }
+
+    @Override
+    public void onRcsDisconnected() {
+        mExecutorService.submit(() -> mUceController.onRcsDisconnected());
+    }
+
+    @Override
+    public void onDestroy() {
+        Log.d(LOG_TAG, "onDestroy");
+        mExecutorService.submit(() -> mUceController.onDestroy());
+        // When the shutdown is called, it will refuse any new tasks and let existing tasks finish.
+        mExecutorService.shutdown();
+    }
+
+    /**
+     * 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.
+     */
+    @Override
+    public void onAssociatedSubscriptionUpdated(int subId) {
+        mExecutorService.submit(() -> {
+            Log.i(LOG_TAG, "onAssociatedSubscriptionUpdated: slotId=" + mSlotId
+                    + ", subId=" + subId);
+
+            // Destroy existing UceController and create a new one.
+            mUceController.onDestroy();
+            mUceController = new UceController(mContext, subId);
+        });
+    }
+
+    @VisibleForTesting
+    public void setUceController(UceController uceController) {
+        mUceController = uceController;
+    }
+
+    /**
+     * Request the capabilities for contacts.
+     *
+     * @param contactNumbers A list of numbers that the capabilities are being requested for.
+     * @param c A callback for when the request for capabilities completes.
+     * @throws ImsException if the ImsService connected to this controller is currently down.
+     */
+    public void requestCapabilities(List<Uri> contactNumbers, IRcsUceControllerCallback c)
+            throws ImsException {
+        Future future = mExecutorService.submit(() -> {
+            checkUceControllerState();
+            mUceController.requestCapabilities(contactNumbers, c);
+            return true;
+        });
+
+        try {
+            future.get();
+        } catch (ExecutionException | InterruptedException e) {
+            Log.w(LOG_TAG, "requestCapabilities: " + e);
+            Throwable cause = e.getCause();
+            if (cause instanceof ImsException) {
+                throw (ImsException) cause;
+            }
+        }
+    }
+
+    /**
+     * Request the capabilities for the given contact.
+     * @param contactNumber The contact of the capabilities are being requested for.
+     * @param c A callback for when the request for capabilities completes.
+     * @throws ImsException if the ImsService connected to this controller is currently down.
+     */
+    public void requestNetworkAvailability(Uri contactNumber, IRcsUceControllerCallback c)
+            throws ImsException {
+        Future future = mExecutorService.submit(() -> {
+            checkUceControllerState();
+            mUceController.requestAvailability(contactNumber, c);
+            return true;
+        });
+
+        try {
+            future.get();
+        } catch (ExecutionException | InterruptedException e) {
+            Log.w(LOG_TAG, "requestNetworkAvailability exception: " + e);
+            Throwable cause = e.getCause();
+            if (cause instanceof ImsException) {
+                throw (ImsException) cause;
+            }
+        }
+    }
+
+    /**
+     * Get the UCE publish state.
+     *
+     * @throws ImsException if the ImsService connected to this controller is currently down.
+     */
+    public @PublishState int getUcePublishState() throws ImsException {
+        Future future = mExecutorService.submit(() -> {
+            checkUceControllerState();
+            return mUceController.getUcePublishState();
+        });
+
+        try {
+            return (Integer) future.get();
+        } catch (ExecutionException | InterruptedException e) {
+            Log.w(LOG_TAG, "requestNetworkAvailability exception: " + e);
+            Throwable cause = e.getCause();
+            if (cause instanceof ImsException) {
+                throw (ImsException) cause;
+            }
+            return RcsUceAdapter.PUBLISH_STATE_OTHER_ERROR;
+        }
+    }
+
+    /**
+     * Register the Publish state changed callback.
+     *
+     * @throws ImsException if the ImsService connected to this controller is currently down.
+     */
+    public void registerPublishStateCallback(IRcsUcePublishStateCallback c) throws ImsException {
+        Future future = mExecutorService.submit(() -> {
+            checkUceControllerState();
+            mUceController.registerPublishStateCallback(c);
+            return true;
+        });
+
+        try {
+            future.get();
+        } catch (ExecutionException | InterruptedException e) {
+            Log.w(LOG_TAG, "registerPublishStateCallback exception: " + e);
+            Throwable cause = e.getCause();
+            if (cause instanceof ImsException) {
+                throw (ImsException) cause;
+            }
+        }
+    }
+
+    /**
+     * Unregister the existing publish state changed callback.
+     */
+    public void unregisterPublishStateCallback(IRcsUcePublishStateCallback c) {
+        Future future = mExecutorService.submit(() -> {
+            if (checkUceControllerState()) {
+                mUceController.unregisterPublishStateCallback(c);
+            }
+            return true;
+        });
+
+        try {
+            future.get();
+        } catch (ExecutionException | InterruptedException e) {
+            Log.w(LOG_TAG, "unregisterPublishStateCallback exception: " + e);
+        }
+    }
+
+    private boolean checkUceControllerState() throws ImsException {
+        if (mUceController == null || mUceController.isUnavailable()) {
+            throw new ImsException("UCE controller is unavailable",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+        return true;
+    }
+}
diff --git a/src/com/android/services/telephony/rcs/UserCapabilityExchangeImpl.java b/src/com/android/services/telephony/rcs/UserCapabilityExchangeImpl.java
deleted file mode 100644
index ee0c5be..0000000
--- a/src/com/android/services/telephony/rcs/UserCapabilityExchangeImpl.java
+++ /dev/null
@@ -1,1065 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.services.telephony.rcs;
-
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PersistableBundle;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.provider.Settings;
-import android.provider.Telephony;
-import android.telecom.TelecomManager;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionManager;
-import android.telephony.ims.ImsException;
-import android.telephony.ims.ImsManager;
-import android.telephony.ims.ImsMmTelManager;
-import android.telephony.ims.ImsReasonInfo;
-import android.telephony.ims.ProvisioningManager;
-import android.telephony.ims.RcsContactUceCapability;
-import android.telephony.ims.RcsUceAdapter;
-import android.telephony.ims.RegistrationManager;
-import android.telephony.ims.aidl.IRcsUceControllerCallback;
-import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
-import android.telephony.ims.feature.MmTelFeature;
-import android.telephony.ims.stub.RcsCapabilityExchange;
-import android.telephony.ims.stub.RcsPresenceExchangeImplBase;
-import android.util.Log;
-
-import com.android.ims.RcsFeatureManager;
-import com.android.ims.RcsFeatureManager.RcsFeatureCallbacks;
-import com.android.ims.ResultCode;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.phone.R;
-import com.android.service.ims.presence.ContactCapabilityResponse;
-import com.android.service.ims.presence.PresenceBase;
-import com.android.service.ims.presence.PresencePublication;
-import com.android.service.ims.presence.PresencePublisher;
-import com.android.service.ims.presence.PresenceSubscriber;
-import com.android.service.ims.presence.SubscribePublisher;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Collectors;
-
-/**
- * Implements User Capability Exchange using Presence.
- */
-public class UserCapabilityExchangeImpl implements RcsFeatureController.Feature, SubscribePublisher,
-        PresencePublisher {
-
-    private static final String LOG_TAG = "RcsUceImpl";
-
-    private final int mSlotId;
-    private volatile int mSubId;
-    private volatile boolean mImsContentChangedCallbackRegistered = false;
-    // The result of requesting publish
-    private volatile int mPublishState = PresenceBase.PUBLISH_STATE_NOT_PUBLISHED;
-    // The network type which IMS registers on
-    private volatile int mNetworkRegistrationType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
-    // The MMTel capabilities of this subscription Id
-    private MmTelFeature.MmTelCapabilities mMmTelCapabilities;
-    private final Object mCapabilitiesLock = new Object();
-
-    private final Context mContext;
-    private final UceImplHandler mUceImplHandler;
-    private RcsFeatureManager mRcsFeatureManager;
-    private final PresencePublication mPresencePublication;
-    private final PresenceSubscriber mPresenceSubscriber;
-
-    // The task Ids of updating capabilities
-    private final Set<Integer> mRequestingPublishTaskIds = new HashSet<>();
-
-    // The callbacks to notify publish state changed.
-    private final RemoteCallbackList<IRcsUcePublishStateCallback> mPublishStateCallbacks;
-
-    // The task Ids of pending availability request.
-    private final Set<Integer> mPendingAvailabilityRequests = new HashSet<>();
-
-    private final ConcurrentHashMap<Integer, IRcsUceControllerCallback> mPendingCapabilityRequests =
-            new ConcurrentHashMap<>();
-
-    UserCapabilityExchangeImpl(Context context, int slotId, int subId) {
-        mSlotId = slotId;
-        mSubId = subId;
-        logi("created");
-
-        mContext = context;
-        mPublishStateCallbacks = new RemoteCallbackList<>();
-
-        HandlerThread handlerThread = new HandlerThread("UceImplHandlerThread");
-        handlerThread.start();
-        mUceImplHandler = new UceImplHandler(this, handlerThread.getLooper());
-
-        String[] volteError = context.getResources().getStringArray(
-                R.array.config_volte_provision_error_on_publish_response);
-        String[] rcsError = context.getResources().getStringArray(
-                R.array.config_rcs_provision_error_on_publish_response);
-
-        // Initialize PresencePublication
-        mPresencePublication = new PresencePublication(null /*PresencePublisher*/, context,
-                volteError, rcsError);
-        // Initialize PresenceSubscriber
-        mPresenceSubscriber = new PresenceSubscriber(null /*SubscribePublisher*/, context,
-                volteError, rcsError);
-
-        onAssociatedSubscriptionUpdated(mSubId);
-        registerReceivers();
-    }
-
-    @VisibleForTesting
-    UserCapabilityExchangeImpl(Context context, int slotId, int subId, Looper looper,
-            PresencePublication presencePublication, PresenceSubscriber presenceSubscriber,
-            RemoteCallbackList<IRcsUcePublishStateCallback> publishStateCallbacks) {
-        mSlotId = slotId;
-        mSubId = subId;
-        mContext = context;
-        mPublishStateCallbacks = publishStateCallbacks;
-        mUceImplHandler = new UceImplHandler(this, looper);
-        mPresencePublication = presencePublication;
-        mPresenceSubscriber = presenceSubscriber;
-        onAssociatedSubscriptionUpdated(mSubId);
-        registerReceivers();
-    }
-
-    // Runs on main thread.
-    @Override
-    public void onRcsConnected(RcsFeatureManager rcsFeatureManager) {
-        logi("onRcsConnected");
-        mRcsFeatureManager = rcsFeatureManager;
-        mRcsFeatureManager.addFeatureListenerCallback(mRcsFeatureCallback);
-
-        mPresencePublication.updatePresencePublisher(this);
-        mPresenceSubscriber.updatePresenceSubscriber(this);
-    }
-
-    // Runs on main thread.
-    @Override
-    public void onRcsDisconnected() {
-        logi("onRcsDisconnected");
-        mPresencePublication.removePresencePublisher();
-        mPresenceSubscriber.removePresenceSubscriber();
-
-        if (mRcsFeatureManager != null) {
-            mRcsFeatureManager.releaseConnection();
-            mRcsFeatureManager = null;
-        }
-    }
-
-    // Runs on main thread.
-    @Override
-    public void onAssociatedSubscriptionUpdated(int subId) {
-        logi("onAssociatedSubscriptionUpdated: new subId=" + subId);
-
-        // Listen to the IMS content changed with new subId.
-        mUceImplHandler.registerImsContentChangedReceiver(subId);
-
-        mSubId = subId;
-        mPresencePublication.handleAssociatedSubscriptionChanged(subId);
-        mPresenceSubscriber.handleAssociatedSubscriptionChanged(subId);
-    }
-
-    /**
-     * Should be called before destroying this instance.
-     * This instance is not usable after this method is called.
-     */
-    // Called on main thread.
-    public void onDestroy() {
-        logi("onDestroy");
-        mUceImplHandler.getLooper().quit();
-        unregisterReceivers();
-        unregisterImsProvisionCallback(mSubId);
-        onRcsDisconnected();
-    }
-
-    /**
-     * @return the UCE Publish state.
-     */
-    // May happen on a Binder thread, PresencePublication locks to get result.
-    public int getUcePublishState() {
-        int publishState = mPresencePublication.getPublishState();
-        return toUcePublishState(publishState);
-    }
-
-    @VisibleForTesting
-    public UceImplHandler getHandler() {
-        return mUceImplHandler;
-    }
-
-    /**
-     * Register receiver to receive UCE publish state changed.
-     */
-    public void registerPublishStateCallback(IRcsUcePublishStateCallback c) {
-        synchronized (mPublishStateCallbacks) {
-            mPublishStateCallbacks.register(c);
-        }
-    }
-
-    /**
-     * Unregister UCE publish state callback.
-     */
-    public void unregisterUcePublishStateCallback(IRcsUcePublishStateCallback c) {
-        synchronized (mPublishStateCallbacks) {
-            mPublishStateCallbacks.unregister(c);
-        }
-    }
-
-    private void clearPublishStateCallbacks() {
-        synchronized (mPublishStateCallbacks) {
-            logi("clearPublishStateCallbacks");
-            final int lastIndex = mPublishStateCallbacks.getRegisteredCallbackCount() - 1;
-            for (int index = lastIndex; index >= 0; index--) {
-                IRcsUcePublishStateCallback callback =
-                        mPublishStateCallbacks.getRegisteredCallbackItem(index);
-                mPublishStateCallbacks.unregister(callback);
-            }
-        }
-    }
-
-    private void notifyPublishStateChanged(@PresenceBase.PresencePublishState int state) {
-        int result = toUcePublishState(state);
-        synchronized (mPublishStateCallbacks) {
-            mPublishStateCallbacks.broadcast(c -> {
-                try {
-                    c.onPublishStateChanged(result);
-                } catch (RemoteException e) {
-                    logw("notifyPublishStateChanged error: " + e);
-                }
-            });
-        }
-    }
-
-    /**
-     * Perform a capabilities request and call {@link IRcsUceControllerCallback} with the result.
-     */
-    // May happen on a Binder thread, PresenceSubscriber locks when requesting Capabilities.
-    public void requestCapabilities(List<Uri> contactNumbers, IRcsUceControllerCallback c) {
-        List<String> numbers = contactNumbers.stream()
-                .map(UserCapabilityExchangeImpl::getNumberFromUri).collect(Collectors.toList());
-        int taskId = mPresenceSubscriber.requestCapability(numbers,
-                new ContactCapabilityResponse() {
-                    @Override
-                    public void onSuccess(int reqId) {
-                        logi("onSuccess called for reqId:" + reqId);
-                    }
-
-                    @Override
-                    public void onError(int reqId, int resultCode) {
-                        IRcsUceControllerCallback c = mPendingCapabilityRequests.remove(reqId);
-                        try {
-                            if (c != null) {
-                                c.onError(toUceError(resultCode));
-                            } else {
-                                logw("onError called for unknown reqId:" + reqId);
-                            }
-                        } catch (RemoteException e) {
-                            logi("Calling back to dead service");
-                        }
-                    }
-
-                    @Override
-                    public void onFinish(int reqId) {
-                        logi("onFinish called for reqId:" + reqId);
-                    }
-
-                    @Override
-                    public void onTimeout(int reqId) {
-                        IRcsUceControllerCallback c = mPendingCapabilityRequests.remove(reqId);
-                        try {
-                            if (c != null) {
-                                c.onError(RcsUceAdapter.ERROR_REQUEST_TIMEOUT);
-                            } else {
-                                logw("onTimeout called for unknown reqId:" + reqId);
-                            }
-                        } catch (RemoteException e) {
-                            logi("Calling back to dead service");
-                        }
-                    }
-
-                    @Override
-                    public void onCapabilitiesUpdated(int reqId,
-                            List<RcsContactUceCapability> contactCapabilities,
-                            boolean updateLastTimestamp) {
-                        IRcsUceControllerCallback c = mPendingCapabilityRequests.remove(reqId);
-                        try {
-                            if (c != null) {
-                                c.onCapabilitiesReceived(contactCapabilities);
-                            } else {
-                                logw("onCapabilitiesUpdated, unknown reqId:" + reqId);
-                            }
-                        } catch (RemoteException e) {
-                            logw("onCapabilitiesUpdated on dead service");
-                        }
-                    }
-                });
-        if (taskId < 0) {
-            try {
-                c.onError(toUceError(taskId));
-            } catch (RemoteException e) {
-                logi("Calling back to dead service");
-            }
-            return;
-        }
-        mPendingCapabilityRequests.put(taskId, c);
-    }
-
-    @Override
-    public int requestCapability(String[] formattedContacts, int taskId) {
-        if (formattedContacts == null || formattedContacts.length == 0) {
-            logw("requestCapability error: contacts is null.");
-            return ResultCode.SUBSCRIBE_INVALID_PARAM;
-        }
-        if (mRcsFeatureManager == null) {
-            logw("requestCapability error: RcsFeatureManager is null.");
-            return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
-        }
-
-        logi("requestCapability: taskId=" + taskId);
-
-        try {
-            List<Uri> contactList = Arrays.stream(formattedContacts)
-                    .map(Uri::parse).collect(Collectors.toList());
-            mRcsFeatureManager.requestCapabilities(contactList, taskId);
-        } catch (Exception e) {
-            logw("requestCapability error: " + e.getMessage());
-            return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
-        }
-        return ResultCode.SUCCESS;
-    }
-
-    @Override
-    public int requestAvailability(String formattedContact, int taskId) {
-        if (formattedContact == null || formattedContact.isEmpty()) {
-            logw("requestAvailability error: contact is null.");
-            return ResultCode.SUBSCRIBE_INVALID_PARAM;
-        }
-        if (mRcsFeatureManager == null) {
-            logw("requestAvailability error: RcsFeatureManager is null.");
-            return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
-        }
-
-        logi("requestAvailability: taskId=" + taskId);
-        addRequestingAvailabilityTaskId(taskId);
-
-        try {
-            Uri contactUri = Uri.parse(formattedContact);
-            List<Uri> contactUris = new ArrayList<>(Arrays.asList(contactUri));
-            mRcsFeatureManager.requestCapabilities(contactUris, taskId);
-        } catch (Exception e) {
-            logw("requestAvailability error: " + e.getMessage());
-            removeRequestingAvailabilityTaskId(taskId);
-            return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
-        }
-        return ResultCode.SUCCESS;
-    }
-
-    @Override
-    public int getStackStatusForCapabilityRequest() {
-        if (mRcsFeatureManager == null) {
-            logw("Check Stack status: Error! RcsFeatureManager is null.");
-            return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
-        }
-
-        if (!isCapabilityDiscoveryEnabled(mSubId)) {
-            logw("Check Stack status: Error! capability discovery not enabled");
-            return ResultCode.ERROR_SERVICE_NOT_ENABLED;
-        }
-
-        if (!isEabProvisioned(mContext, mSubId)) {
-            logw("Check Stack status: Error! EAB provisioning disabled.");
-            return ResultCode.ERROR_SERVICE_NOT_ENABLED;
-        }
-
-        if (getPublisherState() != PresenceBase.PUBLISH_STATE_200_OK) {
-            logw("Check Stack status: Error! publish state " + getPublisherState());
-            return ResultCode.ERROR_SERVICE_NOT_PUBLISHED;
-        }
-        return ResultCode.SUCCESS;
-    }
-
-    /**
-     * The feature callback is to receive the request and update from RcsPresExchangeImplBase
-     */
-    @VisibleForTesting
-    public RcsFeatureCallbacks mRcsFeatureCallback = new RcsFeatureCallbacks() {
-        public void onCommandUpdate(int commandCode, int operationToken) {
-            logi("onCommandUpdate: code=" + commandCode + ", token=" + operationToken);
-            if (isPublishRequestExisted(operationToken)) {
-                onCommandUpdateForPublishRequest(commandCode, operationToken);
-            } else if (isCapabilityRequestExisted(operationToken)) {
-                onCommandUpdateForCapabilityRequest(commandCode, operationToken);
-            } else if (isAvailabilityRequestExisted(operationToken)) {
-                onCommandUpdateForAvailabilityRequest(commandCode, operationToken);
-            } else {
-                logw("onCommandUpdate: invalid token " + operationToken);
-            }
-        }
-
-        /** See {@link RcsPresenceExchangeImplBase#onNetworkResponse(int, String, int)} */
-        public void onNetworkResponse(int responseCode, String reason, int operationToken) {
-            logi("onNetworkResponse: code=" + responseCode + ", reason=" + reason
-                    + ", operationToken=" + operationToken);
-            if (isPublishRequestExisted(operationToken)) {
-                onNetworkResponseForPublishRequest(responseCode, reason, operationToken);
-            } else if (isCapabilityRequestExisted(operationToken)) {
-                onNetworkResponseForCapabilityRequest(responseCode, reason, operationToken);
-            } else if (isAvailabilityRequestExisted(operationToken)) {
-                onNetworkResponseForAvailabilityRequest(responseCode, reason, operationToken);
-            } else {
-                logw("onNetworkResponse: invalid token " + operationToken);
-            }
-        }
-
-        /** See {@link RcsPresenceExchangeImplBase#onCapabilityRequestResponse(List, int)} */
-        public void onCapabilityRequestResponsePresence(List<RcsContactUceCapability> infos,
-                int operationToken) {
-            if (isAvailabilityRequestExisted(operationToken)) {
-                handleAvailabilityReqResponse(infos, operationToken);
-            } else if (isCapabilityRequestExisted(operationToken)) {
-                handleCapabilityReqResponse(infos, operationToken);
-            } else {
-                logw("capability request response: invalid token " + operationToken);
-            }
-        }
-
-        /** See {@link RcsPresenceExchangeImplBase#onNotifyUpdateCapabilites(int)} */
-        public void onNotifyUpdateCapabilities(int publishTriggerType) {
-            logi("onNotifyUpdateCapabilities: type=" + publishTriggerType);
-            mUceImplHandler.notifyUpdateCapabilities(publishTriggerType);
-        }
-
-        /** See {@link RcsPresenceExchangeImplBase#onUnpublish()} */
-        public void onUnpublish() {
-            logi("onUnpublish");
-            mUceImplHandler.unpublish();
-        }
-    };
-
-    private static class UceImplHandler extends Handler {
-        private static final int EVENT_REGISTER_IMS_CHANGED_RECEIVER = 1;
-        private static final int EVENT_NOTIFY_UPDATE_CAPABILITIES = 2;
-        private static final int EVENT_UNPUBLISH = 3;
-
-        private static final int REGISTER_IMS_CHANGED_DELAY = 10000;  //10 seconds
-
-        private final WeakReference<UserCapabilityExchangeImpl> mUceImplRef;
-
-        UceImplHandler(UserCapabilityExchangeImpl uceImpl, Looper looper) {
-            super(looper);
-            mUceImplRef = new WeakReference(uceImpl);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            UserCapabilityExchangeImpl uceImpl = mUceImplRef.get();
-            if (uceImpl == null) {
-                return;
-            }
-            switch (msg.what) {
-                case EVENT_REGISTER_IMS_CHANGED_RECEIVER:
-                    int subId = msg.arg1;
-                    uceImpl.registerImsContentChangedReceiverInternal(subId);
-                    break;
-                case EVENT_NOTIFY_UPDATE_CAPABILITIES:
-                    int publishTriggerType = msg.arg1;
-                    uceImpl.onNotifyUpdateCapabilities(publishTriggerType);
-                    break;
-                case EVENT_UNPUBLISH:
-                    uceImpl.onUnPublish();
-                    break;
-                default:
-                    Log.w(LOG_TAG, "handleMessage: error=" + msg.what);
-                    break;
-            }
-        }
-
-        private void retryRegisteringImsContentChangedReceiver(int subId) {
-            sendRegisteringImsContentChangedMessage(subId, REGISTER_IMS_CHANGED_DELAY);
-        }
-
-        private void registerImsContentChangedReceiver(int subId) {
-            sendRegisteringImsContentChangedMessage(subId, 0);
-        }
-
-        private void sendRegisteringImsContentChangedMessage(int subId, int delay) {
-            if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-                return;
-            }
-            removeRegisteringImsContentChangedReceiver();
-            Message message = obtainMessage(EVENT_REGISTER_IMS_CHANGED_RECEIVER);
-            message.arg1 = subId;
-            sendMessageDelayed(message, delay);
-        }
-
-        private void removeRegisteringImsContentChangedReceiver() {
-            removeMessages(EVENT_REGISTER_IMS_CHANGED_RECEIVER);
-        }
-
-        private void notifyUpdateCapabilities(int publishTriggerType) {
-            Message message = obtainMessage(EVENT_NOTIFY_UPDATE_CAPABILITIES);
-            message.arg1 = publishTriggerType;
-            sendMessage(message);
-        }
-
-        private void unpublish() {
-            sendEmptyMessage(EVENT_UNPUBLISH);
-        }
-    }
-
-    private void onNotifyUpdateCapabilities(int publishTriggerType) {
-        mPresencePublication.onStackPublishRequested(publishTriggerType);
-    }
-
-    private void onUnPublish() {
-        mPresencePublication.setPublishState(PresenceBase.PUBLISH_STATE_NOT_PUBLISHED);
-    }
-
-    @Override
-    public @PresenceBase.PresencePublishState int getPublisherState() {
-        return mPublishState;
-    }
-
-    @Override
-    public int requestPublication(RcsContactUceCapability capabilities, String contactUri,
-            int taskId) {
-        if (mRcsFeatureManager == null) {
-            logw("requestPublication error: RcsFeatureManager is null.");
-            return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
-        }
-
-        logi("requestPublication: taskId=" + taskId);
-        addPublishRequestTaskId(taskId);
-
-        try {
-            mRcsFeatureManager.requestPublication(capabilities, taskId);
-        } catch (Exception ex) {
-            logw("requestPublication error: " + ex.getMessage());
-            removePublishRequestTaskId(taskId);
-            return ResultCode.PUBLISH_GENERIC_FAILURE;
-        }
-        return ResultCode.SUCCESS;
-    }
-
-    /*
-     * Handle the callback method RcsFeatureCallbacks#onCommandUpdate(int, int)
-     */
-    private void onCommandUpdateForPublishRequest(int commandCode, int operationToken) {
-        if (!isPublishRequestExisted(operationToken)) {
-            return;
-        }
-        int resultCode = ResultCode.SUCCESS;
-        if (commandCode != RcsCapabilityExchange.COMMAND_CODE_SUCCESS) {
-            logw("onCommandUpdateForPublishRequest failed! taskId=" + operationToken
-                    + ", code=" + commandCode);
-            removePublishRequestTaskId(operationToken);
-            resultCode = ResultCode.PUBLISH_GENERIC_FAILURE;
-        }
-        mPresencePublication.onCommandStatusUpdated(operationToken, operationToken, resultCode);
-    }
-
-    private void onCommandUpdateForCapabilityRequest(int commandCode, int operationToken) {
-        if (!isCapabilityRequestExisted(operationToken)) {
-            return;
-        }
-        int resultCode = ResultCode.SUCCESS;
-        if (commandCode != RcsCapabilityExchange.COMMAND_CODE_SUCCESS) {
-            logw("onCommandUpdateForCapabilityRequest failed! taskId=" + operationToken
-                    + ", code=" + commandCode);
-            mPendingCapabilityRequests.remove(operationToken);
-            resultCode = ResultCode.PUBLISH_GENERIC_FAILURE;
-        }
-        mPresenceSubscriber.onCommandStatusUpdated(operationToken, operationToken, resultCode);
-    }
-
-    private void onCommandUpdateForAvailabilityRequest(int commandCode, int operationToken) {
-        if (!isAvailabilityRequestExisted(operationToken)) {
-            return;
-        }
-        int resultCode = ResultCode.SUCCESS;
-        if (commandCode != RcsCapabilityExchange.COMMAND_CODE_SUCCESS) {
-            logw("onCommandUpdateForAvailabilityRequest failed! taskId=" + operationToken
-                    + ", code=" + commandCode);
-            removeRequestingAvailabilityTaskId(operationToken);
-            resultCode = ResultCode.PUBLISH_GENERIC_FAILURE;
-        }
-        mPresenceSubscriber.onCommandStatusUpdated(operationToken, operationToken, resultCode);
-    }
-
-    /*
-     * Handle the callback method RcsFeatureCallbacks#onNetworkResponse(int, String, int)
-     */
-    private void onNetworkResponseForPublishRequest(int responseCode, String reason,
-            int operationToken) {
-        if (!isPublishRequestExisted(operationToken)) {
-            return;
-        }
-        removePublishRequestTaskId(operationToken);
-        mPresencePublication.onSipResponse(operationToken, responseCode, reason);
-    }
-
-    private void onNetworkResponseForCapabilityRequest(int responseCode, String reason,
-            int operationToken) {
-        if (!isCapabilityRequestExisted(operationToken)) {
-            return;
-        }
-        mPresenceSubscriber.onSipResponse(operationToken, responseCode, reason);
-    }
-
-    private void onNetworkResponseForAvailabilityRequest(int responseCode, String reason,
-            int operationToken) {
-        if (!isAvailabilityRequestExisted(operationToken)) {
-            return;
-        }
-        removeRequestingAvailabilityTaskId(operationToken);
-        mPresenceSubscriber.onSipResponse(operationToken, responseCode, reason);
-    }
-
-    private void handleAvailabilityReqResponse(List<RcsContactUceCapability> infos, int token) {
-        try {
-            if (infos == null || infos.isEmpty()) {
-                logw("handle availability request response: infos is null " + token);
-                return;
-            }
-            logi("handleAvailabilityReqResponse: token=" + token);
-            mPresenceSubscriber.updatePresence(infos.get(0));
-        } finally {
-            removeRequestingAvailabilityTaskId(token);
-        }
-    }
-
-    private void handleCapabilityReqResponse(List<RcsContactUceCapability> infos, int token) {
-        if (infos == null) {
-            logw("handleCapabilityReqResponse: infos is null " + token);
-            mPendingCapabilityRequests.remove(token);
-            return;
-        }
-        logi("handleCapabilityReqResponse: token=" + token);
-        mPresenceSubscriber.updatePresences(token, infos, true, null);
-    }
-
-    @Override
-    public void updatePublisherState(@PresenceBase.PresencePublishState int publishState) {
-        logi("updatePublisherState: from " + mPublishState + " to " + publishState);
-        mPublishState = publishState;
-        notifyPublishStateChanged(publishState);
-    }
-
-    private void addPublishRequestTaskId(int taskId) {
-        synchronized (mRequestingPublishTaskIds) {
-            mRequestingPublishTaskIds.add(taskId);
-        }
-    }
-
-    private void removePublishRequestTaskId(int taskId) {
-        synchronized (mRequestingPublishTaskIds) {
-            mRequestingPublishTaskIds.remove(taskId);
-        }
-    }
-
-    private boolean isPublishRequestExisted(Integer taskId) {
-        synchronized (mRequestingPublishTaskIds) {
-            return mRequestingPublishTaskIds.contains(taskId);
-        }
-    }
-
-    private void addRequestingAvailabilityTaskId(int taskId) {
-        synchronized (mPendingAvailabilityRequests) {
-            mPendingAvailabilityRequests.contains(taskId);
-        }
-    }
-
-    private void removeRequestingAvailabilityTaskId(int taskId) {
-        synchronized (mPendingAvailabilityRequests) {
-            mPendingAvailabilityRequests.remove(taskId);
-        }
-    }
-
-    private boolean isAvailabilityRequestExisted(Integer taskId) {
-        synchronized (mPendingAvailabilityRequests) {
-            return mPendingAvailabilityRequests.contains(taskId);
-        }
-    }
-
-    private boolean isCapabilityRequestExisted(Integer taskId) {
-        return mPendingCapabilityRequests.containsKey(taskId);
-    }
-
-    private static String getNumberFromUri(Uri uri) {
-        String number = uri.getSchemeSpecificPart();
-        String[] numberParts = number.split("[@;:]");
-
-        if (numberParts.length == 0) {
-            return null;
-        }
-        return numberParts[0];
-    }
-
-    private static int toUcePublishState(int publishState) {
-        switch (publishState) {
-            case PresenceBase.PUBLISH_STATE_200_OK:
-                return RcsUceAdapter.PUBLISH_STATE_OK;
-            case PresenceBase.PUBLISH_STATE_NOT_PUBLISHED:
-                return RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED;
-            case PresenceBase.PUBLISH_STATE_VOLTE_PROVISION_ERROR:
-                return RcsUceAdapter.PUBLISH_STATE_VOLTE_PROVISION_ERROR;
-            case PresenceBase.PUBLISH_STATE_RCS_PROVISION_ERROR:
-                return RcsUceAdapter.PUBLISH_STATE_RCS_PROVISION_ERROR;
-            case PresenceBase.PUBLISH_STATE_REQUEST_TIMEOUT:
-                return RcsUceAdapter.PUBLISH_STATE_REQUEST_TIMEOUT;
-            case PresenceBase.PUBLISH_STATE_OTHER_ERROR:
-                return RcsUceAdapter.PUBLISH_STATE_OTHER_ERROR;
-            default:
-                return RcsUceAdapter.PUBLISH_STATE_OTHER_ERROR;
-        }
-    }
-
-    private static int toUceError(int resultCode) {
-        switch (resultCode) {
-            case ResultCode.SUBSCRIBE_NOT_REGISTERED:
-                return RcsUceAdapter.ERROR_NOT_REGISTERED;
-            case ResultCode.SUBSCRIBE_REQUEST_TIMEOUT:
-                return RcsUceAdapter.ERROR_REQUEST_TIMEOUT;
-            case ResultCode.SUBSCRIBE_FORBIDDEN:
-                return RcsUceAdapter.ERROR_FORBIDDEN;
-            case ResultCode.SUBSCRIBE_NOT_FOUND:
-                return RcsUceAdapter.ERROR_NOT_FOUND;
-            case ResultCode.SUBSCRIBE_TOO_LARGE:
-                return RcsUceAdapter.ERROR_REQUEST_TOO_LARGE;
-            case ResultCode.SUBSCRIBE_INSUFFICIENT_MEMORY:
-                return RcsUceAdapter.ERROR_INSUFFICIENT_MEMORY;
-            case ResultCode.SUBSCRIBE_LOST_NETWORK:
-                return RcsUceAdapter.ERROR_LOST_NETWORK;
-            case ResultCode.SUBSCRIBE_ALREADY_IN_QUEUE:
-                return RcsUceAdapter.ERROR_ALREADY_IN_QUEUE;
-            default:
-                return RcsUceAdapter.ERROR_GENERIC_FAILURE;
-        }
-    }
-
-    /*
-     * Register receivers for updating capabilities
-     */
-    private void registerReceivers() {
-        IntentFilter filter = new IntentFilter(TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED);
-        filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        mContext.registerReceiver(mReceiver, filter);
-
-        ContentResolver resolver = mContext.getContentResolver();
-        if (resolver != null) {
-            // Register mobile data content changed.
-            resolver.registerContentObserver(
-                    Settings.Global.getUriFor(Settings.Global.MOBILE_DATA), false,
-                    mMobileDataObserver);
-
-            // Register SIM info content changed.
-            resolver.registerContentObserver(Telephony.SimInfo.CONTENT_URI, false,
-                    mSimInfoContentObserver);
-        }
-    }
-
-    private void unregisterReceivers() {
-        mContext.unregisterReceiver(mReceiver);
-        ContentResolver resolver = mContext.getContentResolver();
-        if (resolver != null) {
-            resolver.unregisterContentObserver(mMobileDataObserver);
-            resolver.unregisterContentObserver(mSimInfoContentObserver);
-        }
-    }
-
-    /**
-     * Register IMS and provision content changed.
-     *
-     * Call the UceImplHandler#registerImsContentChangedReceiver instead of
-     * calling this method directly.
-     */
-    private void registerImsContentChangedReceiverInternal(int subId) {
-        mUceImplHandler.removeRegisteringImsContentChangedReceiver();
-        try {
-            final int originalSubId = mSubId;
-            if ((originalSubId == subId) && (mImsContentChangedCallbackRegistered)) {
-                logi("registerImsContentChangedReceiverInternal: already registered. skip");
-                return;
-            }
-            // Unregister original IMS and Provision callback
-            unregisterImsProvisionCallback(originalSubId);
-            // Register new IMS and Provision callback
-            registerImsProvisionCallback(subId);
-        } catch (ImsException e) {
-            logw("registerImsContentChangedReceiverInternal error: " + e);
-            mUceImplHandler.retryRegisteringImsContentChangedReceiver(subId);
-        }
-    }
-
-    private void unregisterImsProvisionCallback(int subId) {
-        if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            return;
-        }
-        // Unregister IMS callback
-        ImsMmTelManager imsMmtelManager = getImsMmTelManager(subId);
-        if (imsMmtelManager != null) {
-            try {
-                imsMmtelManager.unregisterImsRegistrationCallback(mImsRegistrationCallback);
-                imsMmtelManager.unregisterMmTelCapabilityCallback(mCapabilityCallback);
-            } catch (RuntimeException e) {
-                logw("unregister IMS callback error: " + e.getMessage());
-            }
-        }
-
-        // Unregister provision changed callback
-        ProvisioningManager provisioningManager =
-                ProvisioningManager.createForSubscriptionId(subId);
-        try {
-            provisioningManager.unregisterProvisioningChangedCallback(mProvisioningChangedCallback);
-        } catch (RuntimeException e) {
-            logw("unregister provisioning callback error: " + e.getMessage());
-        }
-
-        // Remove all publish state callbacks
-        clearPublishStateCallbacks();
-
-        mImsContentChangedCallbackRegistered = false;
-    }
-
-    private void registerImsProvisionCallback(int subId) throws ImsException {
-        if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            return;
-        }
-        // Register IMS callback
-        ImsMmTelManager imsMmtelManager = getImsMmTelManager(subId);
-        if (imsMmtelManager != null) {
-            imsMmtelManager.registerImsRegistrationCallback(mContext.getMainExecutor(),
-                    mImsRegistrationCallback);
-            imsMmtelManager.registerMmTelCapabilityCallback(mContext.getMainExecutor(),
-                    mCapabilityCallback);
-        }
-        // Register provision changed callback
-        ProvisioningManager provisioningManager =
-                ProvisioningManager.createForSubscriptionId(subId);
-        provisioningManager.registerProvisioningChangedCallback(mContext.getMainExecutor(),
-                mProvisioningChangedCallback);
-
-        mImsContentChangedCallbackRegistered = true;
-        logi("registerImsProvisionCallback");
-    }
-
-    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (intent == null) return;
-            switch (intent.getAction()) {
-                case TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED:
-                    int preferredMode = intent.getIntExtra(
-                            TelecomManager.EXTRA_TTY_PREFERRED_MODE, TelecomManager.TTY_MODE_OFF);
-                    logi("TTY preferred mode changed: " + preferredMode);
-                    mPresencePublication.onTtyPreferredModeChanged(preferredMode);
-                    break;
-
-                case Intent.ACTION_AIRPLANE_MODE_CHANGED:
-                    boolean airplaneMode = intent.getBooleanExtra("state", false);
-                    logi("Airplane mode changed: " + airplaneMode);
-                    mPresencePublication.onAirplaneModeChanged(airplaneMode);
-                    break;
-            }
-        }
-    };
-
-    private ContentObserver mMobileDataObserver = new ContentObserver(
-            new Handler(Looper.getMainLooper())) {
-        @Override
-        public void onChange(boolean selfChange) {
-            boolean isEnabled = Settings.Global.getInt(mContext.getContentResolver(),
-                    Settings.Global.MOBILE_DATA, 1) == 1;
-            logi("Mobile data changed: enabled=" + isEnabled);
-            mPresencePublication.onMobileDataChanged(isEnabled);
-        }
-    };
-
-    private ContentObserver mSimInfoContentObserver = new ContentObserver(
-            new Handler(Looper.getMainLooper())) {
-        @Override
-        public void onChange(boolean selfChange) {
-            if (mSubId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-                return;
-            }
-
-            ImsMmTelManager ims = getImsMmTelManager(mSubId);
-            if (ims == null) return;
-
-            try {
-                boolean isEnabled = ims.isVtSettingEnabled();
-                logi("SimInfo changed: VT setting=" + isEnabled);
-                mPresencePublication.onVtEnabled(isEnabled);
-            } catch (RuntimeException e) {
-                logw("SimInfo changed error: " + e);
-            }
-        }
-    };
-
-    private RegistrationManager.RegistrationCallback mImsRegistrationCallback =
-            new RegistrationManager.RegistrationCallback() {
-        @Override
-        public void onRegistered(int imsTransportType) {
-            logi("onRegistered: type=" + imsTransportType);
-            mNetworkRegistrationType = imsTransportType;
-            mPresencePublication.onImsConnected();
-
-            // Also trigger PresencePublication#onFeatureCapabilityChanged method
-            MmTelFeature.MmTelCapabilities capabilities = null;
-            synchronized (mCapabilitiesLock) {
-                capabilities = mMmTelCapabilities;
-            }
-
-            if (capabilities != null) {
-                mPresencePublication.onFeatureCapabilityChanged(mNetworkRegistrationType,
-                        capabilities);
-            }
-        }
-
-        @Override
-        public void onUnregistered(ImsReasonInfo info) {
-            logi("onUnregistered");
-            mNetworkRegistrationType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
-
-            // Also trigger PresencePublication#onFeatureCapabilityChanged method
-            MmTelFeature.MmTelCapabilities capabilities = null;
-            synchronized (mCapabilitiesLock) {
-                capabilities = mMmTelCapabilities;
-            }
-
-            if (capabilities != null) {
-                mPresencePublication.onFeatureCapabilityChanged(mNetworkRegistrationType,
-                        capabilities);
-            }
-            mPresencePublication.onImsDisconnected();
-        }
-    };
-
-    private ImsMmTelManager.CapabilityCallback mCapabilityCallback =
-            new ImsMmTelManager.CapabilityCallback() {
-        @Override
-        public void onCapabilitiesStatusChanged(MmTelFeature.MmTelCapabilities capabilities) {
-            if (capabilities == null) {
-                logw("onCapabilitiesStatusChanged: parameter is null");
-                return;
-            }
-            synchronized (mCapabilitiesLock) {
-                mMmTelCapabilities = capabilities;
-            }
-            mPresencePublication.onFeatureCapabilityChanged(mNetworkRegistrationType, capabilities);
-        }
-    };
-
-    private ProvisioningManager.Callback mProvisioningChangedCallback =
-            new ProvisioningManager.Callback() {
-        @Override
-        public void onProvisioningIntChanged(int item, int value) {
-            logi("onProvisioningIntChanged: item=" + item);
-            switch (item) {
-                case ProvisioningManager.KEY_EAB_PROVISIONING_STATUS:
-                case ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS:
-                case ProvisioningManager.KEY_VT_PROVISIONING_STATUS:
-                    mPresencePublication.handleProvisioningChanged();
-                    break;
-                default:
-                    break;
-            }
-        }
-    };
-
-    private boolean isCapabilityDiscoveryEnabled(int subId) {
-        try {
-            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(subId);
-            int discoveryEnabled = manager.getProvisioningIntValue(
-                    ProvisioningManager.KEY_RCS_CAPABILITY_DISCOVERY_ENABLED);
-            return (discoveryEnabled == ProvisioningManager.PROVISIONING_VALUE_ENABLED);
-        } catch (Exception e) {
-            logw("isCapabilityDiscoveryEnabled error: " + e.getMessage());
-        }
-        return false;
-    }
-
-    private boolean isEabProvisioned(Context context, int subId) {
-        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
-            logw("isEabProvisioned error: invalid subscriptionId " + subId);
-            return false;
-        }
-
-        CarrierConfigManager configManager = (CarrierConfigManager)
-                context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        if (configManager != null) {
-            PersistableBundle config = configManager.getConfigForSubId(subId);
-            if (config != null && !config.getBoolean(
-                    CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONED_BOOL)) {
-                return true;
-            }
-        }
-
-        try {
-            ProvisioningManager manager = ProvisioningManager.createForSubscriptionId(subId);
-            int provisioningStatus = manager.getProvisioningIntValue(
-                    ProvisioningManager.KEY_EAB_PROVISIONING_STATUS);
-            return (provisioningStatus == ProvisioningManager.PROVISIONING_VALUE_ENABLED);
-        } catch (Exception e) {
-            logw("isEabProvisioned error: " + e.getMessage());
-        }
-        return false;
-    }
-
-    private ImsMmTelManager getImsMmTelManager(int subId) {
-        try {
-            ImsManager imsManager = (ImsManager) mContext.getSystemService(
-                    Context.TELEPHONY_IMS_SERVICE);
-            return (imsManager == null) ? null : imsManager.getImsMmTelManager(subId);
-        } catch (IllegalArgumentException e) {
-            logw("getImsMmTelManager error: " + e.getMessage());
-            return null;
-        }
-    }
-
-    private void logi(String log) {
-        Log.i(LOG_TAG, getLogPrefix().append(log).toString());
-    }
-
-    private void logw(String log) {
-        Log.w(LOG_TAG, getLogPrefix().append(log).toString());
-    }
-
-    private StringBuilder getLogPrefix() {
-        StringBuilder builder = new StringBuilder("[");
-        builder.append(mSlotId);
-        builder.append("->");
-        builder.append(mSubId);
-        builder.append("] ");
-        return builder;
-    }
-}
diff --git a/tests/src/com/android/services/telephony/ImsConferenceTest.java b/tests/src/com/android/services/telephony/ImsConferenceTest.java
index f13d709..7e6488d 100644
--- a/tests/src/com/android/services/telephony/ImsConferenceTest.java
+++ b/tests/src/com/android/services/telephony/ImsConferenceTest.java
@@ -16,6 +16,8 @@
 
 package com.android.services.telephony;
 
+import static junit.framework.Assert.assertTrue;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -573,4 +575,40 @@
         assertEquals(0, imsConference.getNumberOfParticipants());
         verify(mConferenceHost.mMockCall).hangup();
     }
+
+    /**
+     * Verifies that an ImsConference can handle SIP and TEL URIs for both the P-Associated-Uri and
+     * conference event package identities.
+     */
+    @Test
+    public void testIsParticipantHost() {
+        // Simplest case, assume P-Associated-Uri is a tel URI and that the CEP participant is also
+        // a tel URI.
+        assertTrue(ImsConference.isParticipantHost(new Uri[] {
+                        Uri.parse("tel:+8616505551234")},
+                Uri.parse("tel:+8616505551234")));
+
+        // Assume P-Associated-Uri is a tel URI and the CEP participant is a sip URI.
+        assertTrue(ImsConference.isParticipantHost(new Uri[] {
+                        Uri.parse("tel:+8616505551234")},
+                Uri.parse("sip:+8616505551234@bj.ims.mnc011.mcc460.3gppnetwork.org")));
+
+        // Assume P-Associated-Uri is a sip URI and the CEP participant is a tel URI.
+        assertTrue(ImsConference.isParticipantHost(new Uri[] {
+                        Uri.parse("sip:+8616505551234@bj.ims.mnc011.mcc460.3gppnetwork.org")},
+                Uri.parse("tel:+8616505551234")));
+
+        // Assume both P-Associated-Uri and the CEP participant are SIP URIs.
+        assertTrue(ImsConference.isParticipantHost(new Uri[] {
+                        Uri.parse("sip:+8616505551234@bj.ims.mnc011.mcc460.3gppnetwork.org")},
+                Uri.parse("sip:+8616505551234@bj.ims.mnc011.mcc460.3gppnetwork.org")));
+
+        // Corner cases
+        assertFalse(ImsConference.isParticipantHost(new Uri[] {
+                        Uri.parse("tel:+8616505551234")}, Uri.fromParts("", "", "")));
+        assertFalse(ImsConference.isParticipantHost(new Uri[] {
+                        Uri.parse("tel:+8616505551234")}, null));
+        assertFalse(ImsConference.isParticipantHost(null, null));
+        assertFalse(ImsConference.isParticipantHost(new Uri[0], null));
+    }
 }
diff --git a/tests/src/com/android/services/telephony/TelephonyManagerTest.java b/tests/src/com/android/services/telephony/TelephonyManagerTest.java
new file mode 100644
index 0000000..e9cdc98
--- /dev/null
+++ b/tests/src/com/android/services/telephony/TelephonyManagerTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.services.telephony;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
+import android.test.mock.MockContext;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.telephony.ITelephony;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Unit tests for {@link TelephonyManager}. */
+@RunWith(AndroidJUnit4.class)
+public class TelephonyManagerTest {
+    private static final String PKG_NAME = "Unittest.TelephonyManagerTest";
+    private static final String TAG = "TelephonyManagerTest";
+
+    private ITelephony mMockITelephony;
+    private SubscriptionManager mMockSubscriptionManager;
+    private Context mMockContext;
+
+    private TelephonyManager mTelephonyManager;
+
+    private final MockContext mContext =
+            new MockContext() {
+                @Override
+                public String getOpPackageName() {
+                    return PKG_NAME;
+                }
+                @Override
+                public String getAttributionTag() {
+                    return TAG;
+                }
+                @Override
+                public Context getApplicationContext() {
+                    return null;
+                }
+                @Override
+                public Object getSystemService(String name) {
+                    switch (name) {
+                        case (Context.TELEPHONY_SUBSCRIPTION_SERVICE) : {
+                            return mMockSubscriptionManager;
+                        }
+                    }
+                    return null;
+                }
+            };
+
+    @Before
+    public void setUp() throws Exception {
+        mMockITelephony = mock(ITelephony.class);
+        mMockSubscriptionManager = mock(SubscriptionManager.class);
+        mMockContext = mock(Context.class);
+        when(mMockContext.getSystemService(eq(Context.TELEPHONY_SUBSCRIPTION_SERVICE)))
+                .thenReturn(mMockSubscriptionManager);
+
+        mTelephonyManager = new TelephonyManager(mContext);
+        TelephonyManager.setupITelephonyForTest(mMockITelephony);
+        TelephonyManager.enableServiceHandleCaching();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        TelephonyManager.setupITelephonyForTest(null);
+        TelephonyManager.disableServiceHandleCaching();
+    }
+
+    @Test
+    public void testGetEmergencyNumberListForCategories() throws Exception {
+        Map<Integer, List<EmergencyNumber>> emergencyNumberLists = new HashMap<>();
+        List<EmergencyNumber> emergencyNumberList = new ArrayList<>();
+        EmergencyNumber number_police = new EmergencyNumber(
+                "911",
+                "us",
+                "30",
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE,
+                new ArrayList<String>(),
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+        EmergencyNumber number_fire = new EmergencyNumber(
+                "912",
+                "us",
+                "30",
+                EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE,
+                new ArrayList<String>(),
+                EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+        emergencyNumberList.add(number_police);
+        emergencyNumberList.add(number_fire);
+        final int test_sub_id = 1;
+        emergencyNumberLists.put(test_sub_id, emergencyNumberList);
+        when(mMockITelephony.getEmergencyNumberList(eq(PKG_NAME), eq(TAG))).thenReturn(
+                emergencyNumberLists);
+
+        // Call TelephonyManager.getEmergencyNumberList(Category)
+        Map<Integer, List<EmergencyNumber>> returnedEmergencyNumberLists =
+                mTelephonyManager.getEmergencyNumberList(
+                        EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE);
+
+        // Verify the ITelephony service is called
+        verify(mMockITelephony, times(1)).getEmergencyNumberList(eq(PKG_NAME), eq(TAG));
+
+        // Verify the returned number list contains only the police number(s)
+        List<EmergencyNumber> returnedEmergencyNumberList = returnedEmergencyNumberLists.get(
+                test_sub_id);
+        for (EmergencyNumber num : returnedEmergencyNumberList) {
+            assertTrue(num.isInEmergencyServiceCategories(
+                    EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE));
+        }
+    }
+}
diff --git a/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java b/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
index ffbb71d..7a9f9e3 100644
--- a/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
+++ b/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
@@ -50,8 +50,8 @@
 
     @Captor ArgumentCaptor<BroadcastReceiver> mReceiverCaptor;
     @Mock TelephonyRcsService.FeatureFactory mFeatureFactory;
-    @Mock UserCapabilityExchangeImpl mMockUceSlot0;
-    @Mock UserCapabilityExchangeImpl mMockUceSlot1;
+    @Mock UceControllerManager mMockUceSlot0;
+    @Mock UceControllerManager mMockUceSlot1;
     @Mock SipTransportController mMockSipTransportSlot0;
     @Mock SipTransportController mMockSipTransportSlot1;
     @Mock RcsFeatureController.RegistrationHelperFactory mRegistrationFactory;
@@ -70,9 +70,9 @@
         mFeatureControllerSlot1 = createFeatureController(1 /*slotId*/);
         doReturn(mFeatureControllerSlot0).when(mFeatureFactory).createController(any(), eq(0));
         doReturn(mFeatureControllerSlot1).when(mFeatureFactory).createController(any(), eq(1));
-        doReturn(mMockUceSlot0).when(mFeatureFactory).createUserCapabilityExchange(any(), eq(0),
+        doReturn(mMockUceSlot0).when(mFeatureFactory).createUceControllerManager(any(), eq(0),
                 anyInt());
-        doReturn(mMockUceSlot1).when(mFeatureFactory).createUserCapabilityExchange(any(), eq(1),
+        doReturn(mMockUceSlot1).when(mFeatureFactory).createUceControllerManager(any(), eq(1),
                 anyInt());
         doReturn(mMockSipTransportSlot0).when(mFeatureFactory).createSipTransportController(any(),
                 eq(0), anyInt());
@@ -89,20 +89,20 @@
     }
 
     @Test
-    public void testUserCapabilityExchangePresenceConnected() {
+    public void testUceControllerPresenceConnected() {
         setCarrierConfig(1 /*subId*/, CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL,
                 true /*isEnabled*/);
         createRcsService(1 /*numSlots*/);
-        verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UceControllerManager.class);
         verify(mFeatureControllerSlot0).connect();
     }
 
     @Test
-    public void testUserCapabilityExchangeOptionsConnected() {
+    public void testUceControllerOptionsConnected() {
         setCarrierConfig(1 /*subId*/, CarrierConfigManager.KEY_USE_RCS_SIP_OPTIONS_BOOL,
                 true /*isEnabled*/);
         createRcsService(1 /*numSlots*/);
-        verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UceControllerManager.class);
         verify(mFeatureControllerSlot0).connect();
     }
 
@@ -111,7 +111,7 @@
         createRcsService(1 /*numSlots*/);
         // No carrier config set for UCE.
         verify(mFeatureControllerSlot0, never()).addFeature(mMockUceSlot0,
-                UserCapabilityExchangeImpl.class);
+                UceControllerManager.class);
         verify(mFeatureControllerSlot0, never()).connect();
     }
 
@@ -171,7 +171,7 @@
 
         sendCarrierConfigChanged(0, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
         verify(mFeatureControllerSlot0, never()).addFeature(mMockUceSlot0,
-                UserCapabilityExchangeImpl.class);
+                UceControllerManager.class);
         verify(mFeatureControllerSlot0, never()).connect();
         verify(mFeatureControllerSlot0, never()).updateAssociatedSubscription(anyInt());
     }
@@ -184,25 +184,25 @@
         setCarrierConfig(2 /*subId*/, CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL,
                 true /*isEnabled*/);
         TelephonyRcsService service = createRcsService(1 /*numSlots*/);
-        verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UceControllerManager.class);
         verify(mFeatureControllerSlot0).connect();
 
         // there should be no changes if the new num slots = old num
         service.updateFeatureControllerSize(1 /*newNumSlots*/);
         verify(mFeatureControllerSlot0, times(1)).addFeature(mMockUceSlot0,
-                UserCapabilityExchangeImpl.class);
+                UceControllerManager.class);
         verify(mFeatureControllerSlot0, times(1)).connect();
 
         // Add a new slot.
         verify(mFeatureControllerSlot1, never()).addFeature(mMockUceSlot1,
-                UserCapabilityExchangeImpl.class);
+                UceControllerManager.class);
         verify(mFeatureControllerSlot1, never()).connect();
         service.updateFeatureControllerSize(2 /*newNumSlots*/);
         // This shouldn't have changed for slot 0.
         verify(mFeatureControllerSlot0, times(1)).addFeature(mMockUceSlot0,
-                UserCapabilityExchangeImpl.class);
+                UceControllerManager.class);
         verify(mFeatureControllerSlot0, times(1)).connect();
-        verify(mFeatureControllerSlot1).addFeature(mMockUceSlot1, UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot1).addFeature(mMockUceSlot1, UceControllerManager.class);
         verify(mFeatureControllerSlot1, times(1)).connect();
 
         // Remove a slot.
@@ -211,10 +211,10 @@
         service.updateFeatureControllerSize(1 /*newNumSlots*/);
         // addFeature/connect shouldn't have been called again
         verify(mFeatureControllerSlot0, times(1)).addFeature(mMockUceSlot0,
-                UserCapabilityExchangeImpl.class);
+                UceControllerManager.class);
         verify(mFeatureControllerSlot0, times(1)).connect();
         verify(mFeatureControllerSlot1, times(1)).addFeature(mMockUceSlot1,
-                UserCapabilityExchangeImpl.class);
+                UceControllerManager.class);
         verify(mFeatureControllerSlot1, times(1)).connect();
         // Verify destroy is only called for slot 1.
         verify(mFeatureControllerSlot0, never()).destroy();
@@ -228,8 +228,8 @@
         setCarrierConfig(2 /*subId*/, CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL,
                 true /*isEnabled*/);
         createRcsService(2 /*numSlots*/);
-        verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
-        verify(mFeatureControllerSlot1).addFeature(mMockUceSlot1, UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UceControllerManager.class);
+        verify(mFeatureControllerSlot1).addFeature(mMockUceSlot1, UceControllerManager.class);
         verify(mFeatureControllerSlot0).connect();
         verify(mFeatureControllerSlot1).connect();
 
@@ -248,7 +248,7 @@
         setCarrierConfig(1 /*subId*/, CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL,
                 true /*isEnabled*/);
         createRcsService(1 /*numSlots*/);
-        verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UceControllerManager.class);
         verify(mFeatureControllerSlot0).connect();
 
 
@@ -256,7 +256,7 @@
         setCarrierConfig(1 /*subId*/, CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL,
                 false /*isEnabled*/);
         sendCarrierConfigChanged(0 /*slotId*/, 1 /*subId*/);
-        verify(mFeatureControllerSlot0).removeFeature(UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot0).removeFeature(UceControllerManager.class);
         verify(mFeatureControllerSlot0).updateAssociatedSubscription(1);
     }
 
@@ -284,7 +284,7 @@
     public void testCarrierConfigUpdateNoUceToUce() {
         createRcsService(1 /*numSlots*/);
         verify(mFeatureControllerSlot0, never()).addFeature(mMockUceSlot0,
-                UserCapabilityExchangeImpl.class);
+                UceControllerManager.class);
         verify(mFeatureControllerSlot0, never()).connect();
 
 
@@ -292,7 +292,7 @@
         setCarrierConfig(1 /*subId*/, CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL,
                 true /*isEnabled*/);
         sendCarrierConfigChanged(0 /*slotId*/, 1 /*subId*/);
-        verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UceControllerManager.class);
         verify(mFeatureControllerSlot0).connect();
         verify(mFeatureControllerSlot0).updateAssociatedSubscription(1);
     }
diff --git a/tests/src/com/android/services/telephony/rcs/UceControllerManagerTest.java b/tests/src/com/android/services/telephony/rcs/UceControllerManagerTest.java
new file mode 100644
index 0000000..4148d13
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/UceControllerManagerTest.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.services.telephony.rcs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.net.Uri;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.aidl.IRcsUceControllerCallback;
+import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+import com.android.TestExecutorService;
+import com.android.ims.RcsFeatureManager;
+import com.android.ims.rcs.uce.UceController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+
+@RunWith(AndroidJUnit4.class)
+public class UceControllerManagerTest extends TelephonyTestBase {
+
+    @Mock private UceController mUceController;
+    @Mock private RcsFeatureManager mRcsFeatureManager;
+
+    private final ExecutorService mExecutorService = new TestExecutorService();
+
+    private int mSlotId = 1;
+    private int mSubId = 1;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        doReturn(mSubId).when(mUceController).getSubId();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Test
+    public void testRcsConnected() throws Exception {
+        UceControllerManager controllerManager = getUceControllerManager();
+
+        controllerManager.onRcsConnected(mRcsFeatureManager);
+
+        verify(mUceController).onRcsConnected(mRcsFeatureManager);
+    }
+
+    @Test
+    public void testRcsDisconnected() throws Exception {
+        UceControllerManager uceCtrlManager = getUceControllerManager();
+
+        uceCtrlManager.onRcsDisconnected();
+
+        verify(mUceController).onRcsDisconnected();
+    }
+
+    @Test
+    public void testDestroy() throws Exception {
+        UceControllerManager uceCtrlManager = getUceControllerManager();
+
+        uceCtrlManager.onDestroy();
+
+        verify(mUceController).onDestroy();
+    }
+
+    @Test
+    public void testSubscriptionUpdated() throws Exception {
+        UceControllerManager uceCtrlManager = getUceControllerManager();
+
+        uceCtrlManager.onAssociatedSubscriptionUpdated(mSubId);
+
+        verify(mUceController).onDestroy();
+    }
+
+    @Test
+    public void testRequestCapabilitiesWithRcsUnavailable() throws Exception {
+        UceControllerManager uceCtrlManager = getUceControllerManager();
+        doReturn(true).when(mUceController).isUnavailable();
+        uceCtrlManager.onRcsDisconnected();
+
+        try {
+            List<Uri> contacts = Arrays.asList(Uri.fromParts("sip", "00000", null));
+            IRcsUceControllerCallback callback = Mockito.mock(IRcsUceControllerCallback.class);
+
+            uceCtrlManager.requestCapabilities(contacts, callback);
+
+            fail();
+        } catch (ImsException e) {
+            assertEquals(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE, e.getCode());
+            return;
+        }
+        fail();
+    }
+
+    @Test
+    public void testRequestCapabilitiesWithRcsConnected() throws Exception {
+        UceControllerManager uceCtrlManager = getUceControllerManager();
+        doReturn(false).when(mUceController).isUnavailable();
+        uceCtrlManager.onRcsConnected(mRcsFeatureManager);
+
+        try {
+            List<Uri> contacts = Arrays.asList(Uri.fromParts("sip", "00000", null));
+            IRcsUceControllerCallback callback = Mockito.mock(IRcsUceControllerCallback.class);
+
+            uceCtrlManager.requestCapabilities(contacts, callback);
+
+            verify(mUceController).requestCapabilities(contacts, callback);
+        } catch (ImsException e) {
+            fail();
+        }
+    }
+
+    @Test
+    public void testRequestNetworkAvailability() throws Exception {
+        UceControllerManager uceCtrlManager = getUceControllerManager();
+        doReturn(false).when(mUceController).isUnavailable();
+        uceCtrlManager.onRcsConnected(mRcsFeatureManager);
+
+        Uri contact = Uri.fromParts("sip", "00000", null);
+        IRcsUceControllerCallback callback = Mockito.mock(IRcsUceControllerCallback.class);
+
+        uceCtrlManager.requestNetworkAvailability(contact, callback);
+
+        verify(mUceController).requestAvailability(contact, callback);
+    }
+
+    @Test
+    public void testRequestNetworkAvailabilityWithRcsUnavailable() throws Exception {
+        UceControllerManager uceCtrlManager = getUceControllerManager();
+        doReturn(true).when(mUceController).isUnavailable();
+        uceCtrlManager.onRcsDisconnected();
+
+        try {
+            Uri contact = Uri.fromParts("sip", "00000", null);
+            IRcsUceControllerCallback callback = Mockito.mock(IRcsUceControllerCallback.class);
+            uceCtrlManager.requestNetworkAvailability(contact, callback);
+            fail();
+        } catch (ImsException e) {
+            assertEquals(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE, e.getCode());
+            return;
+        }
+        fail();
+    }
+
+    @Test
+    public void testGetPublishState() throws Exception {
+        UceControllerManager uceCtrlManager = getUceControllerManager();
+        doReturn(false).when(mUceController).isUnavailable();
+        uceCtrlManager.onRcsConnected(mRcsFeatureManager);
+
+        uceCtrlManager.getUcePublishState();
+
+        verify(mUceController).getUcePublishState();
+    }
+
+    @Test
+    public void testGetPublishStateWithRcsUnavailable() throws Exception {
+        UceControllerManager uceCtrlManager = getUceControllerManager();
+        doReturn(true).when(mUceController).isUnavailable();
+        uceCtrlManager.onRcsDisconnected();
+
+        try {
+            uceCtrlManager.getUcePublishState();
+            fail();
+        } catch (ImsException e) {
+            assertEquals(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE, e.getCode());
+            return;
+        }
+        fail();
+    }
+
+    @Test
+    public void testRegisterPublishStateCallback() throws Exception {
+        UceControllerManager uceCtrlManager = getUceControllerManager();
+        IRcsUcePublishStateCallback callback = Mockito.mock(IRcsUcePublishStateCallback.class);
+
+        uceCtrlManager.registerPublishStateCallback(callback);
+
+        verify(mUceController).registerPublishStateCallback(callback);
+    }
+
+    @Test
+    public void testRegisterPublishStateCallbackWithRcsUnavailable() throws Exception {
+        UceControllerManager uceCtrlManager = getUceControllerManager();
+        doReturn(true).when(mUceController).isUnavailable();
+        uceCtrlManager.onRcsDisconnected();
+
+        try {
+            IRcsUcePublishStateCallback callback = Mockito.mock(IRcsUcePublishStateCallback.class);
+            uceCtrlManager.registerPublishStateCallback(callback);
+            fail();
+        } catch (ImsException e) {
+            assertEquals(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE, e.getCode());
+            return;
+        }
+        fail();
+    }
+
+    @Test
+    public void testUnregisterPublishStateCallback() throws Exception {
+        UceControllerManager uceCtrlManager = getUceControllerManager();
+        IRcsUcePublishStateCallback callback = Mockito.mock(IRcsUcePublishStateCallback.class);
+
+        uceCtrlManager.unregisterPublishStateCallback(callback);
+
+        verify(mUceController).unregisterPublishStateCallback(callback);
+    }
+
+    private UceControllerManager getUceControllerManager() {
+        UceControllerManager manager = new UceControllerManager(mContext, mSlotId, mSubId,
+                mExecutorService);
+        manager.setUceController(mUceController);
+        return manager;
+    }
+}
diff --git a/tests/src/com/android/services/telephony/rcs/UserCapabilityExchangeImplTest.java b/tests/src/com/android/services/telephony/rcs/UserCapabilityExchangeImplTest.java
deleted file mode 100644
index 3cbe8bf..0000000
--- a/tests/src/com/android/services/telephony/rcs/UserCapabilityExchangeImplTest.java
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.services.telephony.rcs;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyList;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.net.Uri;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.RemoteCallbackList;
-import android.telephony.ims.ImsManager;
-import android.telephony.ims.ImsMmTelManager;
-import android.telephony.ims.RcsContactPresenceTuple;
-import android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities;
-import android.telephony.ims.RcsContactUceCapability;
-import android.telephony.ims.RcsContactUceCapability.PresenceBuilder;
-import android.telephony.ims.RegistrationManager;
-import android.telephony.ims.aidl.IRcsUceControllerCallback;
-import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
-import android.telephony.ims.stub.RcsCapabilityExchange;
-import android.telephony.ims.stub.RcsPresenceExchangeImplBase;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.TelephonyTestBase;
-import com.android.ims.RcsFeatureManager;
-import com.android.ims.RcsFeatureManager.RcsFeatureCallbacks;
-import com.android.ims.ResultCode;
-import com.android.service.ims.presence.PresenceBase;
-import com.android.service.ims.presence.PresencePublication;
-import com.android.service.ims.presence.PresencePublisher;
-import com.android.service.ims.presence.PresenceSubscriber;
-import com.android.service.ims.presence.SubscribePublisher;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.Executor;
-
-@RunWith(AndroidJUnit4.class)
-public class UserCapabilityExchangeImplTest extends TelephonyTestBase {
-
-    private int  mSlotId = 0;
-    private int mSubId = 1;
-    private int mUpdatedSubId = 2;
-
-    @Captor ArgumentCaptor<IRcsUcePublishStateCallback> mPublishStateCallbacksCaptor;
-
-    @Mock PresencePublication mPresencePublication;
-    @Mock PresenceSubscriber mPresenceSubscriber;
-    @Mock RcsFeatureManager mRcsFeatureManager;
-    @Mock ImsMmTelManager mImsMmTelManager;
-    @Mock RemoteCallbackList<IRcsUcePublishStateCallback> mPublishStateCallbacks;
-
-    private Looper mLooper;
-
-    @Before
-    public void setUp() throws Exception {
-        super.setUp();
-
-        ImsManager imsManager =
-                (ImsManager) mContext.getSystemService(Context.TELEPHONY_IMS_SERVICE);
-        when(imsManager.getImsMmTelManager(mSubId)).thenReturn(mImsMmTelManager);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        super.tearDown();
-
-        if (mLooper != null) {
-            mLooper.quit();
-            mLooper = null;
-        }
-    }
-
-    @Test
-    public void testServiceConnected() throws Exception {
-        UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
-        uceImpl.onRcsConnected(mRcsFeatureManager);
-
-        verify(mRcsFeatureManager).addFeatureListenerCallback(any(RcsFeatureCallbacks.class));
-        verify(mPresencePublication).updatePresencePublisher(any(PresencePublisher.class));
-        verify(mPresenceSubscriber).updatePresenceSubscriber(any(SubscribePublisher.class));
-    }
-
-    @Test
-    public void testServiceDisconnected() throws Exception {
-        UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
-        uceImpl.onRcsDisconnected();
-
-        verify(mPresencePublication).removePresencePublisher();
-        verify(mPresenceSubscriber).removePresenceSubscriber();
-    }
-
-    @Test
-    public void testSubscriptionUpdated() throws Exception {
-        UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
-        uceImpl.onAssociatedSubscriptionUpdated(mUpdatedSubId);
-
-        verify(mImsMmTelManager).registerImsRegistrationCallback(any(Executor.class),
-                any(RegistrationManager.RegistrationCallback.class));
-        verify(mImsMmTelManager).registerMmTelCapabilityCallback(any(Executor.class),
-                any(ImsMmTelManager.CapabilityCallback.class));
-        verify(mPresencePublication).handleAssociatedSubscriptionChanged(mUpdatedSubId);
-        verify(mPresenceSubscriber).handleAssociatedSubscriptionChanged(mUpdatedSubId);
-    }
-
-    @Test
-    public void testUcePublishStateRetrieval() throws Exception {
-        UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
-        uceImpl.getUcePublishState();
-
-        verify(mPresencePublication).getPublishState();
-    }
-
-    @Test
-    public void testRegisterPublishStateCallbacks() throws Exception {
-        UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
-        uceImpl.registerPublishStateCallback(any(IRcsUcePublishStateCallback.class));
-        verify(mPublishStateCallbacks).register(mPublishStateCallbacksCaptor.capture());
-    }
-
-    @Test
-    public void testOnNotifyUpdateCapabilities() throws Exception {
-        UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
-        uceImpl.onRcsConnected(mRcsFeatureManager);
-
-        int triggerType = RcsPresenceExchangeImplBase.CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN;
-        uceImpl.mRcsFeatureCallback.onNotifyUpdateCapabilities(triggerType);
-        waitForMs(1000);
-
-        verify(mPresencePublication).onStackPublishRequested(triggerType);
-    }
-
-    @Test
-    public void testRequestPublicationWithSuccessfulResponse() throws Exception {
-        int taskId = 1;
-        int sipResponse = 200;
-        Uri contact = Uri.fromParts("sip", "test", null);
-        RcsContactUceCapability capability = getRcsContactUceCapability(contact);
-
-        UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
-        uceImpl.onRcsConnected(mRcsFeatureManager);
-
-        doAnswer(invocation -> {
-            uceImpl.mRcsFeatureCallback.onCommandUpdate(RcsCapabilityExchange.COMMAND_CODE_SUCCESS,
-                    taskId);
-            uceImpl.mRcsFeatureCallback.onNetworkResponse(sipResponse, null, taskId);
-            return null;
-        }).when(mRcsFeatureManager).requestPublication(capability, taskId);
-
-        // Request publication
-        int result = uceImpl.requestPublication(capability, contact.toString(), taskId);
-
-        assertEquals(ResultCode.SUCCESS, result);
-        verify(mPresencePublication).onCommandStatusUpdated(taskId, taskId, ResultCode.SUCCESS);
-        verify(mPresencePublication).onSipResponse(taskId, sipResponse, null);
-    }
-
-    @Test
-    public void testRequestPublicationWithFailedResponse() throws Exception {
-        int taskId = 1;
-        Uri contact = Uri.fromParts("sip", "test", null);
-        RcsContactUceCapability capability = getRcsContactUceCapability(contact);
-
-        UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
-        uceImpl.onRcsConnected(mRcsFeatureManager);
-
-        doAnswer(invocation -> {
-            uceImpl.mRcsFeatureCallback.onCommandUpdate(
-                    RcsCapabilityExchange.COMMAND_CODE_GENERIC_FAILURE, taskId);
-            return null;
-        }).when(mRcsFeatureManager).requestPublication(capability, taskId);
-
-        // Request publication
-        int result = uceImpl.requestPublication(capability, contact.toString(), taskId);
-
-        assertEquals(ResultCode.SUCCESS, result);
-        verify(mPresencePublication).onCommandStatusUpdated(taskId, taskId,
-                ResultCode.PUBLISH_GENERIC_FAILURE);
-    }
-
-    private RcsContactUceCapability getRcsContactUceCapability(Uri contact) {
-        ServiceCapabilities.Builder servCapsBuilder = new ServiceCapabilities.Builder(true, true);
-        servCapsBuilder.addSupportedDuplexMode(ServiceCapabilities.DUPLEX_MODE_FULL);
-
-        RcsContactPresenceTuple.Builder tupleBuilder = new RcsContactPresenceTuple.Builder(
-                RcsContactPresenceTuple.TUPLE_BASIC_STATUS_OPEN,
-                RcsContactPresenceTuple.SERVICE_ID_MMTEL, "1.0");
-        tupleBuilder.addContactUri(contact).addServiceCapabilities(servCapsBuilder.build());
-
-        PresenceBuilder presenceBuilder = new PresenceBuilder(contact,
-                RcsContactUceCapability.SOURCE_TYPE_CACHED,
-                RcsContactUceCapability.REQUEST_RESULT_FOUND);
-        presenceBuilder.addCapabilityTuple(tupleBuilder.build());
-        return presenceBuilder.build();
-    }
-
-    @Test
-    public void testRequestCapability() throws Exception {
-        int taskId = 1;
-        int sipResponse = 200;
-        List<RcsContactUceCapability> infos = new ArrayList<>();
-        List<Uri> contacts = Arrays.asList(Uri.fromParts("sip", "00000", null));
-        IRcsUceControllerCallback callback = Mockito.mock(IRcsUceControllerCallback.class);
-
-        UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
-        uceImpl.onRcsConnected(mRcsFeatureManager);
-
-        when(mPresenceSubscriber.requestCapability(anyList(), any())).thenReturn(taskId);
-
-        doAnswer(invocation -> {
-            uceImpl.mRcsFeatureCallback.onCommandUpdate(RcsCapabilityExchange.COMMAND_CODE_SUCCESS,
-                    taskId);
-            uceImpl.mRcsFeatureCallback.onNetworkResponse(sipResponse, null, taskId);
-            uceImpl.mRcsFeatureCallback.onCapabilityRequestResponsePresence(infos, taskId);
-            return null;
-        }).when(mRcsFeatureManager).requestCapabilities(anyList(), anyInt());
-
-        uceImpl.requestCapabilities(contacts, callback);
-        uceImpl.requestCapability(new String[] {"00000"}, taskId);
-
-        verify(mPresenceSubscriber).onCommandStatusUpdated(taskId, taskId, ResultCode.SUCCESS);
-        verify(mPresenceSubscriber).onSipResponse(taskId, sipResponse, null);
-        verify(mPresenceSubscriber).updatePresences(taskId, infos, true, null);
-    }
-
-    @Test
-    public void testUpdatePublisherState() throws Exception {
-        IRcsUcePublishStateCallback callback = Mockito.mock(IRcsUcePublishStateCallback.class);
-        doAnswer(invocation -> {
-            callback.onPublishStateChanged(anyInt());
-            return null;
-        }).when(mPublishStateCallbacks).broadcast(any());
-
-        UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
-        uceImpl.onRcsConnected(mRcsFeatureManager);
-        uceImpl.registerPublishStateCallback(callback);
-        uceImpl.updatePublisherState(PresenceBase.PUBLISH_STATE_200_OK);
-
-        assertEquals(PresenceBase.PUBLISH_STATE_200_OK, uceImpl.getPublisherState());
-        verify(callback).onPublishStateChanged(anyInt());
-    }
-
-    @Test
-    public void testUnpublish() throws Exception {
-        IRcsUcePublishStateCallback callback = Mockito.mock(IRcsUcePublishStateCallback.class);
-        doAnswer(invocation -> {
-            callback.onPublishStateChanged(anyInt());
-            return null;
-        }).when(mPublishStateCallbacks).broadcast(any());
-
-        UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
-        uceImpl.onRcsConnected(mRcsFeatureManager);
-        uceImpl.mRcsFeatureCallback.onUnpublish();
-        waitForMs(1000);
-
-        verify(mPresencePublication).setPublishState(PresenceBase.PUBLISH_STATE_NOT_PUBLISHED);
-    }
-
-    private UserCapabilityExchangeImpl createUserCapabilityExchangeImpl() throws Exception {
-        HandlerThread handlerThread = new HandlerThread("UceImplHandlerThread");
-        handlerThread.start();
-        mLooper = handlerThread.getLooper();
-        UserCapabilityExchangeImpl uceImpl = new UserCapabilityExchangeImpl(mContext, mSlotId,
-                mSubId, mLooper, mPresencePublication, mPresenceSubscriber,
-                mPublishStateCallbacks);
-        verify(mPresencePublication).handleAssociatedSubscriptionChanged(1);
-        verify(mPresenceSubscriber).handleAssociatedSubscriptionChanged(1);
-        waitForHandlerAction(uceImpl.getHandler(), 1000);
-        verify(mImsMmTelManager, atLeast(1)).registerImsRegistrationCallback(
-                any(Executor.class), any(RegistrationManager.RegistrationCallback.class));
-        verify(mContext).registerReceiver(any(), any());
-        return uceImpl;
-    }
-}