Merge "Fix cellinfo stop to refresh" into sc-dev am: 6727a9dbf5 am: 340ad87b88

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/services/Telephony/+/13449015

Change-Id: I6a6e86f41dbbd519f7a2189fefa8b87e5ef3d44e
diff --git a/Android.bp b/Android.bp
index 6f97d86..5eb8765 100644
--- a/Android.bp
+++ b/Android.bp
@@ -15,6 +15,23 @@
 // Build the Phone app which includes the emergency dialer. See Contacts
 // for the 'other' dialer.
 
+package {
+    default_applicable_licenses: ["packages_services_Telephony_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+    name: "packages_services_Telephony_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+    ],
+    license_text: [
+        "NOTICE",
+    ],
+}
+
 android_app {
     name: "TeleService",
 
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 8f637c6..81ed490 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -38,6 +38,7 @@
     <protected-broadcast android:name="android.intent.action.ACTION_MDN_STATE_CHANGED" />
     <protected-broadcast android:name="android.telephony.action.SERVICE_PROVIDERS_UPDATED" />
     <protected-broadcast android:name="android.provider.Telephony.SIM_FULL" />
+    <protected-broadcast android:name="com.android.internal.telephony.carrier_key_download_alarm" />
     <protected-broadcast android:name="com.android.internal.telephony.data-restart-trysetup" />
     <protected-broadcast android:name="com.android.internal.telephony.data-stall" />
     <protected-broadcast android:name="com.android.internal.telephony.provisioning_apn_alarm" />
@@ -95,12 +96,21 @@
     <protected-broadcast android:name= "android.telephony.action.NETWORK_COUNTRY_CHANGED" />
     <protected-broadcast android:name= "android.telephony.action.PRIMARY_SUBSCRIPTION_LIST_CHANGED" />
     <protected-broadcast android:name= "android.telephony.action.MULTI_SIM_CONFIG_CHANGED" />
+    <protected-broadcast android:name= "android.telephony.action.CARRIER_SIGNAL_RESET" />
+    <protected-broadcast android:name= "android.telephony.action.CARRIER_SIGNAL_PCO_VALUE" />
+    <protected-broadcast android:name= "android.telephony.action.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE" />
+    <protected-broadcast android:name= "android.telephony.action.CARRIER_SIGNAL_REDIRECTED" />
+    <protected-broadcast android:name= "android.telephony.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED" />
+
+    <protected-broadcast android:name= "com.android.phone.settings.CARRIER_PROVISIONING" />
+    <protected-broadcast android:name= "com.android.phone.settings.TRIGGER_CARRIER_PROVISIONING" />
 
     <!-- For Vendor Debugging in Telephony -->
     <protected-broadcast android:name="android.telephony.action.ANOMALY_REPORTED" />
 
     <protected-broadcast android:name= "android.intent.action.SUBSCRIPTION_INFO_RECORD_ADDED" />
     <protected-broadcast android:name= "android.intent.action.ACTION_MANAGED_ROAMING_IND" />
+    <protected-broadcast android:name= "android.telephony.ims.action.RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE" />
 
     <!-- Allows granting runtime permissions to telephony related components. -->
     <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS" />
@@ -124,8 +134,8 @@
     <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
     <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" />
     <uses-permission android:name="android.permission.VIBRATE" />
-    <uses-permission android:name="android.permission.BLUETOOTH" />
-    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
+    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
     <uses-permission android:name="android.permission.REORDER_TASKS" />
     <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
@@ -246,6 +256,13 @@
                 android:readPermission="android.permission.READ_CONTACTS"
                 android:writePermission="android.permission.WRITE_CONTACTS" />
 
+        <provider android:name=".SimPhonebookProvider"
+            android:authorities="com.android.simphonebook"
+            android:multiprocess="true"
+            android:exported="true"
+            android:readPermission="android.permission.READ_CONTACTS"
+            android:writePermission="android.permission.WRITE_CONTACTS" />
+
         <provider android:name="com.android.ims.rcs.uce.eab.EabProvider"
                 android:authorities="eab"
                 android:exported="false"/>
@@ -419,7 +436,7 @@
                 android:configChanges="orientation|screenSize|keyboardHidden"
                 android:exported="true"
                 android:theme="@style/DialerSettingsLight">
-            <intent-filter>
+            <intent-filter android:priority="1">
                 <action android:name="android.intent.action.VIEW" />
                 <action android:name="android.intent.action.MAIN" />
                 <action android:name="android.telecom.action.SHOW_CALL_SETTINGS" />
@@ -570,16 +587,6 @@
             </intent-filter>
         </receiver>
 
-        <activity android:name="com.android.services.telephony.sip.SipPhoneAccountSettingsActivity"
-                android:theme="@android:style/Theme.NoDisplay"
-                android:exported="true"
-                android:excludeFromRecents="true">
-            <intent-filter>
-                <action android:name="android.telecom.action.CONFIGURE_PHONE_ACCOUNT" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-        </activity>
-
         <activity android:label="Sip Settings"
                   android:name="com.android.services.telephony.sip.SipSettings"
                   android:theme="@style/DialerSettingsLight"
diff --git a/OWNERS b/OWNERS
index 3059d4d..e095b89 100644
--- a/OWNERS
+++ b/OWNERS
@@ -13,3 +13,4 @@
 dbright@google.com
 xiaotonj@google.com
 
+per-file *SimPhonebookProvider* = file:platform/packages/apps/Contacts:/OWNERS
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 75b9d49..da61c80 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -10,6 +10,46 @@
     },
     {
       "name": "CarrierAppIntegrationTestCases"
+    },
+    {
+      "name": "CtsTelephonySdk28TestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "CtsSimRestrictedApisTestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "CtsTelephony2TestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "CtsTelephony3TestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "CtsTelephonyProviderTestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
     }
   ]
 }
diff --git a/apex/Android.bp b/apex/Android.bp
index a7137d9..a0e5713 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "packages_services_Telephony_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["packages_services_Telephony_license"],
+}
+
 apex_defaults {
     name: "com.android.telephony-defaults",
 
@@ -9,6 +18,8 @@
 
     key: "com.android.telephony.key",
     certificate: ":com.android.telephony.certificate",
+
+    updatable: false,
 }
 
 apex {
diff --git a/apex/testing/Android.bp b/apex/testing/Android.bp
index 10455a4..1138b5e 100644
--- a/apex/testing/Android.bp
+++ b/apex/testing/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "packages_services_Telephony_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["packages_services_Telephony_license"],
+}
+
 apex {
     name: "test_com.android.telephony",
     visibility: [
@@ -22,4 +31,4 @@
     file_contexts: ":com.android.telephony-file_contexts",
     // Test APEX, should never be installed
     installable: false,
-}
\ No newline at end of file
+}
diff --git a/ecc/conversion_toolset_v1/proto/Android.bp b/ecc/conversion_toolset_v1/proto/Android.bp
index e1e0643..632ab40 100644
--- a/ecc/conversion_toolset_v1/proto/Android.bp
+++ b/ecc/conversion_toolset_v1/proto/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "packages_services_Telephony_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["packages_services_Telephony_license"],
+}
+
 java_library_static {
     name: "ecc-protos-lite",
     proto: {
diff --git a/res/layout/radio_info.xml b/res/layout/radio_info.xml
index 72627a3..68c67a3 100644
--- a/res/layout/radio_info.xml
+++ b/res/layout/radio_info.xml
@@ -153,6 +153,12 @@
             <TextView android:id="@+id/nr_frequency" style="@style/info_value" />
         </LinearLayout>
 
+        <!-- NR Frequency -->
+        <LinearLayout style="@style/RadioInfo_entry_layout" android:orientation="horizontal">
+            <TextView android:id="@+id/network_slicing_config_label" android:text="@string/radio_info_network_slicing_config" style="@style/info_label" />
+            <TextView android:id="@+id/network_slicing_config" style="@style/info_value" />
+        </LinearLayout>
+
         <!-- Physical Channel Config -->
         <LinearLayout style="@style/RadioInfo_entry_layout">
             <TextView android:text="@string/radio_info_phy_chan_config" style="@style/info_label" />
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 3c4d561..8484549 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Beperkte SIM-funksionaliteit"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> oproepe- en datadienste kan geblokkeer word terwyl <xliff:g id="PHONE_NUMBER">%2$s</xliff:g> gebruik word."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> oproepe- en datadienste kan geblokkeer word terwyl \'n ander SIM gebruik word."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Opgeskorte SIP-rekeninge is gevind en verwyder"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP-bel word nie meer deur die Android-platform ondersteun nie.\nJou bestaande SIP-rekeninge <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> is verwyder.\nBevestig asseblief jou verstekbelinstelling."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Gaan na instellings"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Programdatagebruik"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> mobiele data gebruik <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Gevorderd"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Wagwoord is verkeerd. SIM is nou geblokkeer. Voer PUK2 in."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 is permanent geblokkeer."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Jy het <xliff:g id="NUMBER">%d</xliff:g> oorblywende pogings."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 is gesluit. Kontak diensverskaffer om te ontsluit."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 is nie meer geblokkeer nie"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Netwerk- of SIM-kaartfout"</string>
     <string name="doneButton" msgid="7371209609238460207">"Klaar"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Fooninligting"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Diensverskaffervoorsiening-inligting"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Trigger-diensverskaffervoorsiening"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Voorstel: Verbeter Bluetooth-konnektiwiteit"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Jou bluetooth-sein is swak. Probeer om na luidsprekerfoon oor te skakel."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Kennisgewing oor oproepgehalte"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Opgeskorte SIP-rekeninge"</string>
 </resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index e7324e6..571af43 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"የተገደበ የሲም ተግባር"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"የ <xliff:g id="CARRIER_NAME">%1$s</xliff:g> ጥሪዎች እና የውሂብ አገልግሎቶች <xliff:g id="PHONE_NUMBER">%2$s</xliff:g> በሚጠቀሙ ጊዜ ሊታገዱ ይችላሉ።"</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"ሌላ ሲም በሚጠቀሙ ጊዜ የ <xliff:g id="CARRIER_NAME">%1$s</xliff:g> ጥሪዎች እና የውሂብ አገልግሎቶች ሊታገዱ ይችላሉ።"</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"የተቋረጡ የSIP መለያዎች ተገኝተው ተወግደዋል"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"የSIP መደወያ ከአሁን በኋላ በAndroid መሰረተ ስርዓት ላይ አይደገፍም።\nነባር የSIP መለያዎችዎ <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> ተወግደዋል።\nየእርስዎ ነባሪ መደወያ የመለያ ቅንብሮችን እባክዎ ያረጋግጡ።"</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"ወደ ቅንብሮች ሂድ"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"የመተግበሪያ ውሂብ አጠቃቀም"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> ጥቅም ላይ የዋለ የተንቀሳቃሽ ስልክ ውሂብ <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"የላቀ"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"የይለፍ ቃል ልክ አይደለም። SIM አሁን ተቆልፏል። PUK2 ያስገቡ።"</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"ፒን2 በቋሚነት ታግዷል።"</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"እርስዎ <xliff:g id="NUMBER">%d</xliff:g> ቀሪ ሙከራዎች አልዎት።"</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 ተቆልፏል። ለማስከፈት የአገልግሎት አቅራቢን ያነጋግሩ።"</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 ከአሁን በኋላ አልታገደም"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"የአውታረ መረብ ወይም የሲም ካርድ ስህተት"</string>
     <string name="doneButton" msgid="7371209609238460207">"ተከናውኗል"</string>
@@ -568,7 +572,7 @@
     <string name="police_type_description" msgid="2819533883972081757">"ፖሊስ"</string>
     <string name="ambulance_type_description" msgid="6798237503553180461">"አምቡላንስ"</string>
     <string name="fire_type_description" msgid="6565200468934914930">"እሳት"</string>
-    <!-- format error in translation for description_concat_format (2014471565101724088) -->
+    <string name="description_concat_format" msgid="2014471565101724088">"%1$s, %2$s"</string>
     <string name="dialerKeyboardHintText" msgid="1115266533703764049">"ለመደወል የሰሌዳ ቁልፍ ተጠቀም"</string>
     <string name="onscreenHoldText" msgid="4025348842151665191">"ያዝ"</string>
     <string name="onscreenEndCallText" msgid="6138725377654842757">"መጨረሻ"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"የስልክ መረጃ"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"የአገልግሎት አቅራቢ አቅርቦት መረጃ"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"የቀስቅሴ አገልግሎት አቅራቢ አቅርቦት"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"የአስተያየት ጥቆማ፦ የብሉቱዝን ግንኙነት ያሻሽሉ"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"የእርስዎ የብሉቱዝ ሲግናል ደካማ ነው። ወደ የስልክ ድምፅ ማጉያ ለመቀየር ይሞክሩ።"</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"የጥሪ ጥራት ማሳወቂያ"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"የተቋረጡ የSIP መለያዎች"</string>
 </resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 3f120c8..271ead8 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"‏وظائف محدودة لشريحة SIM"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"ربما يتم حظر المكالمات وخدمات البيانات المُقدّمة من <xliff:g id="CARRIER_NAME">%1$s</xliff:g> أثناء استخدام <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"‏ربما يتم حظر المكالمات وخدمات البيانات المقدّمة من <xliff:g id="CARRIER_NAME">%1$s</xliff:g> أثناء استخدام شريحة SIM أخرى."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"‏تم العثور على حسابات SIP المتوقّفة وحذفها"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"‏لم تعد ميزة إجراء مكالمات عبر بروتوكول SIP متوافقة مع نظام Android الأساسي. \nتم حذف حسابات SIP الحالية، وهي <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>.\nيُرجى تأكيد إعدادات الاتصال التلقائية في حسابك."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"الانتقال إلى الإعدادات"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"استخدام بيانات التطبيق"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"تم استخدام <xliff:g id="ID_1">%1$s</xliff:g> من بيانات الجوّال خلال الفترة بين <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"إعدادات متقدمة"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"‏كلمة المرور غير صحيحة. تم قفل شريحة SIM الآن. أدخل رمز PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"‏تم حظر PUK2 بشكل دائم."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"يتبقى لديك <xliff:g id="NUMBER">%d</xliff:g> من المحاولات."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"‏تم قفل شريحة SIM برمز PUK2. تواصَل مع مقدّم الخدمة لفتح قفل الشريحة."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"‏لم يعد رمز PIN2 محظورًا"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"‏حدث خطأ في الشبكة أو ببطاقة SIM"</string>
     <string name="doneButton" msgid="7371209609238460207">"تم"</string>
@@ -780,7 +784,7 @@
     <string name="clh_callFailed_protocol_Error_unspecified_txt" msgid="9203320572562697755">"تعذر إكمال المكالمة. رمز الخطأ 111."</string>
     <string name="clh_callFailed_interworking_unspecified_txt" msgid="7969686413930847182">"تعذر إكمال المكالمة. رمز الخطأ 127."</string>
     <string name="labelCallBarring" msgid="4180377113052853173">"منع الاتصال"</string>
-    <string name="sum_call_barring_enabled" msgid="5184331188926370824">"تفعيل"</string>
+    <string name="sum_call_barring_enabled" msgid="5184331188926370824">"مفعّلة"</string>
     <string name="sum_call_barring_disabled" msgid="5699448000600153096">"متوقف"</string>
     <string name="call_barring_baoc" msgid="7400892586336429326">"كل المكالمات الصادرة"</string>
     <string name="call_barring_baoc_enabled" msgid="3131509193386668182">"هل تريد إيقاف حظر كل المكالمات الصادرة؟"</string>
@@ -931,6 +935,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"معلومات الهاتف"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"معلومات توفير خدمة مشغّل شبكة الجوّال"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"توفير خدمة مشغّل شبكة الجوّال"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"اقتراح: تحسين نطاق اتصال البلوتوث"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"إشارة البلوتوث ضعيفة. حاوِل التبديل إلى مكبّر الصوت."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"إشعار بشأن جودة المكالمة"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"‏حسابات SIP المتوقّفة"</string>
 </resources>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index ca997de..67a192f 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"ছিমৰ সীমিত কাৰ্যকাৰিতা"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g> ব্যৱহাৰ কৰি থাকোঁতে <xliff:g id="CARRIER_NAME">%1$s</xliff:g>ৰ কল আৰু ডেটা সেৱাবোৰ অৱৰোধ কৰা হ’ব পাৰে।"</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"অন্য এটা ছিম ব্যৱহাৰ কৰি থাকোঁতে <xliff:g id="CARRIER_NAME">%1$s</xliff:g>ৰ কল আৰু ডেটা সেৱাবোৰ অৱৰোধ কৰা হ’ব পাৰে।"</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"বন্ধ হৈ যোৱা SIP একাউণ্ট বিচাৰি পোৱা গৈছে আৰু আঁতৰাই পেলোৱা হৈছে"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Android প্লেটফৰ্মত আৰু SIP কলিং ব্যৱহাৰ কৰিব পৰা নাযাব।\nআপোনাৰ পূৰ্বৰে পৰা থকা SIP একাউণ্টসমূহ <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> আঁতৰাই পেলোৱা হ’ল।\nঅনুগ্ৰহ কৰি আপোনাৰ ডিফ’ল্ট কলিং একাউণ্টৰ ছেটিং নিশ্চিত কৰক।"</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"ছেটিঙলৈ যাওক"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"এপৰ ডেটা ব্যৱহাৰ"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> ব্যৱহৃত ম’বাইল ডেটা <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"উচ্চখাপৰ ছেটিংসমূহ"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"ভুল পাছৱৰ্ড। এতিয়া ছিম লক কৰা হৈছে। PUK2 লিখক।"</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 স্থায়ীভাৱে অৱৰোধ কৰা হৈছে।"</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"আপোনাৰ হাতত <xliff:g id="NUMBER">%d</xliff:g>টা প্ৰয়াস বাকী আছে।"</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 লক কৰা আছে। আনলক কৰিবলৈ সেৱা প্ৰদানকাৰীৰ সৈতে যোগাযোগ কৰক।"</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 আৰু অৱৰোধ হৈ থকা নাই"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"নেটৱৰ্ক বা ছিম কাৰ্ডৰ আসোঁৱাহ।"</string>
     <string name="doneButton" msgid="7371209609238460207">"হ’ল"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"ফ\'নৰ তথ্য"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"বাহকৰ প্ৰ’ভিজনিং তথ্য"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"বাহকৰ প্ৰ’ভিজনিং সূচনা কৰক"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"পৰামৰ্শ: ব্লুটুথ সংযোগ উন্নত কৰক"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"আপোনাৰ ব্লুটুথৰ ছিগনেল দুৰ্বল। স্পীকাৰফ’নলৈ সলনি কৰি চাওক।"</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"কলৰ গুণগত মানৰ জাননী"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"বন্ধ হৈ যোৱা SIP একাউণ্ট"</string>
 </resources>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 5376968..4149e71 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -61,7 +61,7 @@
     <string name="labelGsmMore_with_label" msgid="3206015314393246224">"GSM zəng ayarları (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
     <string name="labelCDMAMore" msgid="7937441382611224632">"CDMA zəng parametrləri"</string>
     <string name="labelCdmaMore_with_label" msgid="7759692829160238152">"CDMA zəng ayarları (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
-    <string name="apn_settings" msgid="1978652203074756623">"Çatma Nöqtəsi Adları"</string>
+    <string name="apn_settings" msgid="1978652203074756623">"Giriş nöqtəsi adları"</string>
     <string name="settings_label" msgid="9101778088412567956">"Şəbəkə ayarları"</string>
     <string name="phone_accounts" msgid="1216879437523774604">"Hesabların çağırılması"</string>
     <string name="phone_accounts_make_calls_with" msgid="16747814788918145">"Zənglər et"</string>
@@ -182,15 +182,15 @@
     <string name="connect_later" msgid="1950138106010005425">"Hazırda bu şəbəkəyə qoşulmaq olmur. Sonra təkrar sınayın."</string>
     <string name="registration_done" msgid="5337407023566953292">"Şəbəkədə qeydiyyatdan keçib."</string>
     <string name="already_auto" msgid="8607068290733079336">"Artıq avtomatik seçimdədir."</string>
-    <string name="select_automatically" msgid="779750291257872651">"Avtomatik olaraq şəbəkə seçin"</string>
+    <string name="select_automatically" msgid="779750291257872651">"Şəbəkə avtomatik seçilsin"</string>
     <string name="manual_mode_disallowed_summary" msgid="3970048592179890197">"%1$s ünvanına qoşulduqda əlçatan olmur"</string>
     <string name="network_select_title" msgid="4117305053881611988">"Şəbəkə"</string>
     <string name="register_automatically" msgid="3907580547590554834">"Avtomatik qeydiyyat ..."</string>
-    <string name="preferred_network_mode_title" msgid="5253395265169539830">"Tərcih edilən şəbəkə növü"</string>
+    <string name="preferred_network_mode_title" msgid="5253395265169539830">"Şəbəkə növü"</string>
     <string name="preferred_network_mode_summary" msgid="3787989000044330064">"Şəbəkə əməliyyat rejimini dəyişin"</string>
-    <string name="preferred_network_mode_dialogtitle" msgid="2781447433514459696">"Tərcih edilən şəbəkə növü"</string>
+    <string name="preferred_network_mode_dialogtitle" msgid="2781447433514459696">"Şəbəkə növü"</string>
     <string name="forbidden_network" msgid="5081729819561333023">"(qadağandır)"</string>
-    <string name="choose_network_title" msgid="5335832663422653082">"Şəbəkəni seçin"</string>
+    <string name="choose_network_title" msgid="5335832663422653082">"Şəbəkə seçin"</string>
     <string name="network_disconnected" msgid="8844141106841160825">"Bağlantı kəsildi"</string>
     <string name="network_connected" msgid="2760235679963580224">"Qoşuldu"</string>
     <string name="network_connecting" msgid="160901383582774987">"Qoşulur..."</string>
@@ -282,26 +282,29 @@
     <string name="data_enable_summary" msgid="696860063456536557">"Data istifadəsinə icazə verin"</string>
     <string name="dialog_alert_title" msgid="5260471806940268478">"Diqqət"</string>
     <string name="roaming" msgid="1576180772877858949">"Rominq"</string>
-    <string name="roaming_enable" msgid="6853685214521494819">"Rouminq zamanı data xidmətlərinə qoşulun"</string>
-    <string name="roaming_disable" msgid="8856224638624592681">"Rouminq zamanı data xidmətlərinə qoşulun"</string>
+    <string name="roaming_enable" msgid="6853685214521494819">"Rominq zamanı data xidmətinə qoşulun"</string>
+    <string name="roaming_disable" msgid="8856224638624592681">"Rominq zamanı data xidmətinə qoşulun"</string>
     <string name="roaming_reenable_message" msgid="1951802463885727915">"Data rominqi deaktivdir. Aktiv etmək üçün klikləyin."</string>
     <string name="roaming_enabled_message" msgid="9022249120750897">"Rominq xərcləri çıxıla bilər. Dəyişmək üçün toxunun."</string>
     <string name="roaming_notification_title" msgid="3590348480688047320">"Mobil data bağlantısı itdi"</string>
     <string name="roaming_on_notification_title" msgid="7451473196411559173">"Data rominqi aktivdir"</string>
     <string name="roaming_warning" msgid="7855681468067171971">"Sizə əhəmiyyətli xərclər tətbiq edilə bilər."</string>
     <string name="roaming_check_price_warning" msgid="8212484083990570215">"Qiymətləndirmə üçün şəbəkə provayderi ilə yoxlayın."</string>
-    <string name="roaming_alert_title" msgid="5689615818220960940">"Məlumat rominqinə icazə verilsin?"</string>
+    <string name="roaming_alert_title" msgid="5689615818220960940">"Data rominqə icazə verilsin?"</string>
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Məhdud SIM funksionallığı"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g> nömrəsindən istifadə edərkən <xliff:g id="CARRIER_NAME">%1$s</xliff:g> zəng və data xidmətləri bloklana bilər."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Başqa SIM-dən istifadə edərkən <xliff:g id="CARRIER_NAME">%1$s</xliff:g> zəng və data xidmətləri bloklana bilər."</string>
-    <string name="data_usage_title" msgid="8438592133893837464">"Tətbiq data istifadəsi"</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Ləğv edilmiş SIP hesabları tapıldı və silindi"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP zəngi artıq Android platforması tərəfindən dəstəklənmir.\nMövcud SIP hesablarınız <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> silinib.\nDefolt zəng hesabı ayarınızı təsdiq edin."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Ayarlara keçin"</string>
+    <string name="data_usage_title" msgid="8438592133893837464">"Tətbiq trafiki"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_2">%2$s</xliff:g> ərzində <xliff:g id="ID_1">%1$s</xliff:g> mobil data istifadə edildi"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Qabaqcıl"</string>
     <string name="carrier_settings_euicc" msgid="1190237227261337749">"Mobil Operator"</string>
     <string name="keywords_carrier_settings_euicc" msgid="8540160967922063745">"operator, esim, sim, euicc, operatoru dəyişin, operator əlavə edin"</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>
     <string name="mobile_data_settings_title" msgid="7228249980933944101">"Mobil data"</string>
-    <string name="mobile_data_settings_summary" msgid="5012570152029118471">"Mobil şəbəkədən istifadə edərək dataya daxil olun"</string>
+    <string name="mobile_data_settings_summary" msgid="5012570152029118471">"Mobil internetə giriş"</string>
     <string name="data_usage_disable_mobile" msgid="5669109209055988308">"Mobil data söndürülsün?"</string>
     <string name="sim_selection_required_pref" msgid="6985901872978341314">"Seçim tələb olunur"</string>
     <string name="sim_change_data_title" msgid="9142726786345906606">"Data SİM-i dəyişilsin?"</string>
@@ -462,7 +465,7 @@
     <string name="get_pin2" msgid="4221654606863196332">"PIN2 daxil edin"</string>
     <string name="name" msgid="1347432469852527784">"Ad"</string>
     <string name="number" msgid="1564053487748491000">"Nömrə"</string>
-    <string name="save" msgid="983805790346099749">"Yadda saxla"</string>
+    <string name="save" msgid="983805790346099749">"Yadda saxlayın"</string>
     <string name="add_fdn_contact" msgid="1169713422306640887">"Sabit yığım nömrəsi əlavə edin"</string>
     <string name="adding_fdn_contact" msgid="3112531600824361259">"Sabit yığım nömrəsi əlavə olunur..."</string>
     <string name="fdn_contact_added" msgid="2840016151693394596">"Sabit yığım nömrəsi əlavə edildi."</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Parol səhvdir. SIM indi kilidlənib. PUK2-ni daxil et."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 həmişəlik qapadıldı."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Sizin <xliff:g id="NUMBER">%d</xliff:g> cəhdiniz qalıb."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 kilidlidir. Açmaq üçün xidmət provayderi ilə əlaqə saxlayın."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 artıq bloklanmayıb"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Şəbəkə və ya SIM kart xətası"</string>
     <string name="doneButton" msgid="7371209609238460207">"Tamam"</string>
@@ -697,7 +701,7 @@
     <string name="change_pin_enter_new_pin_header" msgid="4739465616733486118">"Yeni PIN kodu təyin edin"</string>
     <string name="change_pin_enter_new_pin_hint" msgid="2326038476516364210">"PIN <xliff:g id="MIN">%1$d</xliff:g>-<xliff:g id="MAX">%2$d</xliff:g> rəqəm olmalıdır."</string>
     <string name="change_pin_confirm_pin_header" msgid="2606303906320705726">"PİN kodunuzu təsdiq edin"</string>
-    <string name="change_pin_confirm_pins_dont_match" msgid="305164501222587215">"PIN kodlar üst-üstə düşmür"</string>
+    <string name="change_pin_confirm_pins_dont_match" msgid="305164501222587215">"PIN-lər eyni deyil"</string>
     <string name="change_pin_succeeded" msgid="2504705600693014403">"Səsli poçtun PIN kodu yeniləndi"</string>
     <string name="change_pin_system_error" msgid="7772788809875146873">"PIN kodu ayarlamaq olmur"</string>
     <string name="mobile_data_status_roaming_turned_off_subtext" msgid="6840673347416227054">"Data rominq deaktivdir"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Telefon məlumatı"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Operator Təchizat Məlumatı"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Operator Təchizatlanmasını aktiv edin"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Təklif: Bluetooth bağlantısını təkmilləşdirin"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth siqnalı zəifdir. Telefon spikerinə keçin."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Zəng keyfiyyəti bildirişi"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Ləğv edilmiş SIP hesabları"</string>
 </resources>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 7ab9248..ffe6f86 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Ograničena funkcionalnost SIM-a"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> usluge za pozive i podatke se blokiraju dok koristite <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> usluge za pozive i podatke se blokiraju dok koristite drugi SIM."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Zastareli SIP nalozi su pronađeni i uklonjeni"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Android platforma više ne podržava SIP pozivanje.\nPostojeći SIP nalozi <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> su uklonjeni.\nPotvrdite podrazumevano podešavanje naloga pozivanja."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Idi u podešavanja"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Potrošnja podataka aplikacije"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"Potrošili ste <xliff:g id="ID_1">%1$s</xliff:g> mobilnih podataka u periodu <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Napredno"</string>
@@ -480,9 +483,9 @@
     <string name="simContacts_empty" msgid="1135632055473689521">"Nema kontakata na SIM kartici."</string>
     <string name="simContacts_title" msgid="2714029230160136647">"Izbor kontakata za uvoz"</string>
     <string name="simContacts_airplaneMode" msgid="4654884030631503808">"Isključite režim rada u avionu da biste uvezli kontakte sa SIM kartice."</string>
-    <string name="enable_pin" msgid="967674051730845376">"Omogućavanje/onemogućavanje SIM PIN-a"</string>
-    <string name="change_pin" msgid="3657869530942905790">"Promena SIM PIN-a"</string>
-    <string name="enter_pin_text" msgid="3182311451978663356">"SIM PIN:"</string>
+    <string name="enable_pin" msgid="967674051730845376">"Omogućavanje/onemogućavanje PIN-a za SIM"</string>
+    <string name="change_pin" msgid="3657869530942905790">"Promena PIN-a za SIM"</string>
+    <string name="enter_pin_text" msgid="3182311451978663356">"PIN za SIM:"</string>
     <string name="oldPinLabel" msgid="8618515202411987721">"Stari PIN"</string>
     <string name="newPinLabel" msgid="3585899083055354732">"Novi PIN"</string>
     <string name="confirmPinLabel" msgid="7783531218662473778">"Potvrdite novi PIN"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Lozinka nije tačna. SIM je sada blokiran. Unesite PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 je trajno blokiran."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Imate još <xliff:g id="NUMBER">%d</xliff:g> pokušaja."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 je zaključan. Za otključavanje se obratite dobavljaču usluge."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 više nije blokiran"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Greška na mreži ili SIM kartici"</string>
     <string name="doneButton" msgid="7371209609238460207">"Gotovo"</string>
@@ -922,6 +926,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Informacije o telefonu"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Informacije o dodeli mobilnog operatera"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Pokretanje dodele mobilnog operatera"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Predlog: poboljšajte Bluetooth vezu"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth signal je slab. Probajte da pređete na spikerfon."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Obaveštenje o kvalitetu poziva"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Zastareli SIP nalozi"</string>
 </resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index ee1c068..ff1e5e2 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Абмежаваныя функцыі SIM-карты"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Выклікі і перадача даных праз <xliff:g id="CARRIER_NAME">%1$s</xliff:g> могуць блакіравацца пры выкарыстанні нумара <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Выклікі і перадача даных праз <xliff:g id="CARRIER_NAME">%1$s</xliff:g> блакіруюцца пры выкарыстанні іншай SIM-карты."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Знойдзены і выдалены састарэлыя ўліковыя запісы SIP"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"На платформе Android больш не падтрымліваецца SIP-тэлефанія.\nВашы існуючыя ўліковыя запісы SIP (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) выдалены.\nПацвердзіце стандартны ўліковы запіс для выклікаў."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Перайсці ў налады"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Выкарыстанне трафіка"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> мабільнага трафіку выкарыстана ў перыяд <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Пашыраныя налады"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Пароль няправільны. Зараз SIM-карта заблакіравана. Увядзіце код PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"Код PUK2 заблакіраваны назаўжды."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"У вас засталося спроб: <xliff:g id="NUMBER">%d</xliff:g>."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"Код PUK2 заблакіраваны. Для разблакіроўкі звярніцеся да пастаўшчыка паслуг."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"Код PIN2 больш не заблакіраваны"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Памылка сеткі або SIM-карты"</string>
     <string name="doneButton" msgid="7371209609238460207">"Гатова"</string>
@@ -925,6 +929,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Звесткі аб тэлефоне"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Звесткі аб аператары"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Запусціць сінхранізацыю з аператарам"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Прапанова: палепшыце якасць падключэння праз Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Сігнал Bluetooth слабы. Паспрабуйце пераключыцца на гучную сувязь."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Апавяшчэнне пра якасць выкліку"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Састарэлыя ўліковыя запісы SIP"</string>
 </resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index d6eb65d..74c36e0 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Ограничена функционалност на SIM картата"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Обажданията и услугите за данни от <xliff:g id="CARRIER_NAME">%1$s</xliff:g> може да са блокирани, докато използвате <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Обажданията и услугите за данни от <xliff:g id="CARRIER_NAME">%1$s</xliff:g> може да са блокирани с друга SIM карта."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Открити са и са премахнати оттеглени профили за SIP"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Обажданията през SIP вече не се поддържат от платформата Android.\nСъществуващите ви профили за SIP <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> бяха премахнати.\nМоля, потвърдете настройката за стандартния си профил за обаждания."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Към настройките"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Пренос на данни от приложенията"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> мобилни данни са използвани за периода <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Разширени"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Паролата е неправилна. SIM картата вече е заключена. Въведете PUK2 кода."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 е блокиран завинаги."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Остават ви <xliff:g id="NUMBER">%d</xliff:g> опита."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"Заключена с PUK2. За отключване се обърнете към оператора."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"ПИН2 кодът вече не е блокиран"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Грешка в мрежата или SIM картата"</string>
     <string name="doneButton" msgid="7371209609238460207">"Готово"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Информация за телефона"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Информация за обезпечаването от оператора"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Задействане на обезпечаването от оператора"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Предложение: Подобрете връзката през Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Сигналът ви за Bluetooth е слаб. Опитайте да превключите на високоговорител."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Известия за качеството на обаждането"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Оттеглени профили за SIP"</string>
 </resources>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 3cdc467..3832903 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"সিমের কিছু কাজ করবে না"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g> ব্যবহার করার সময় <xliff:g id="CARRIER_NAME">%1$s</xliff:g> কল এবং ডেটা পরিষেবা হয়তো ব্লক করা থাকতে পারে।"</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"অন্য সিম ব্যবহার করার সময় <xliff:g id="CARRIER_NAME">%1$s</xliff:g> কল এবং ডেটা পরিষেবা হয়ত ব্লক হয়ে যেতে পারে।"</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"পুরনো SIP অ্যাকাউন্ট খুঁজে পাওয়া গেছে এবং সরানো হয়েছে"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Android প্ল্যাটফর্মে \'SIP কলিং\'-এর সুবিধা আর কাজ করবে না।\nআগে থেকে থাকা আপনার SIP অ্যাকাউন্ট <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> সরানো হয়েছে।\nকল করার জন্য ব্যবহৃত ডিফল্ট অ্যাকাউন্ট সেটিং কনফার্ম করুন।"</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"সেটিংসে যান"</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>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"পাসওয়ার্ডটি ভুল৷ সিম এখন অবরুদ্ধ৷ PUK2 লিখুন৷"</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 স্থায়ীভাবে অবরুদ্ধ করা হয়েছে৷"</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"আপনার কাছে <xliff:g id="NUMBER">%d</xliff:g>টি প্রচেষ্টা অবশিষ্ট রয়েছে৷"</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 লক করা আছে। আনলক করতে পরিষেবা প্রদানকারীর সাথে যোগাযোগ করুন।"</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 এখন আর অবরুদ্ধ নয়"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"নেটওয়ার্ক বা সিম কার্ড ত্রুটি৷"</string>
     <string name="doneButton" msgid="7371209609238460207">"সম্পন্ন হয়েছে"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"ফোনের তথ্য"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"পরিষেবা প্রদানকারীর ব্যবস্থামূলক তথ্য"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"ট্রিগার পরিষেবা প্রদানকারীর ব্যবস্থা"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"পরামর্শ: ব্লুটুথ কানেক্টিভিটি উন্নত করুন"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"আপনার ডিভাইসের ব্লুটুথ সিগনাল ভাল না। বদল করে স্পিকারফোন বেছে নিন।"</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"ফোন কলের কোয়ালিটি সংক্রান্ত বিজ্ঞপ্তি"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"পুরনো SIP অ্যাকাউন্ট"</string>
 </resources>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 6b223e6..0714fcb 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Ograničena funkcionalnost SIM-a"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Pozivi i usluge prijenosa podataka operatera <xliff:g id="CARRIER_NAME">%1$s</xliff:g> mogu biti blokirane kada koristite broj <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Pozivi i usluge prijenosa pod. op. <xliff:g id="CARRIER_NAME">%1$s</xliff:g> mogu biti blok. kada koristite drugi SIM."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Zastarjeli SIP računi su pronađeni i uklonjeni"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Android platforma više ne podržva SIP pozivanje.\nVaši postojeći SIP računi <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> su uklonjeni.\nPotvrdite zadanu postavku računa za pozivanje."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Idi u postavke"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Prijenos podataka u aplikaciji"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"Iskorišteno je <xliff:g id="ID_1">%1$s</xliff:g> prijenosa podataka u periodu <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Napredno"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Lozinka je netačna. SIM je sada zaključan. Unesite PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 je trajno blokiran."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Broj preostalih pokušaja: <xliff:g id="NUMBER">%d</xliff:g>."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"SIM je zaključan kodom PUK2. Kontaktirajte pružaoca usluge da ga otključate."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 više nije blokiran"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Greška na mreži ili SIM kartici"</string>
     <string name="doneButton" msgid="7371209609238460207">"Gotovo"</string>
@@ -530,13 +534,13 @@
     <string name="notification_voicemail_no_vm_number" msgid="3423686009815186750">"Nepoznat broj govorne pošte"</string>
     <string name="notification_network_selection_title" msgid="255595526707809121">"Nema mreže"</string>
     <string name="notification_network_selection_text" msgid="553288408722427659">"Odabrana mreža (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) je nedostupna"</string>
-    <string name="incall_error_power_off" product="watch" msgid="7191184639454113633">"Uključite mobilnu mrežu, isključite Način rada u avionu ili isključite Uštedu baterije da uputite poziv."</string>
-    <string name="incall_error_power_off" product="default" msgid="8131672264311208673">"Isključite način rada u avionu da uputite poziv."</string>
-    <string name="incall_error_power_off_wfc" msgid="9125661184694727052">"Isključite način rada u avionu ili se povežite na bežičnu mrežu da uputite poziv."</string>
+    <string name="incall_error_power_off" product="watch" msgid="7191184639454113633">"Uključite mobilnu mrežu, isključite Način rada u avionu ili isključite Uštedu baterije da pozovete."</string>
+    <string name="incall_error_power_off" product="default" msgid="8131672264311208673">"Isključite način rada u avionu da pozovete."</string>
+    <string name="incall_error_power_off_wfc" msgid="9125661184694727052">"Isključite način rada u avionu ili se povežite na bežičnu mrežu da pozovete."</string>
     <string name="incall_error_ecm_emergency_only" msgid="5622379058883722080">"Izađite iz načina rada za hitni povratni poziv da uputite poziv koji nije hitan."</string>
     <string name="incall_error_emergency_only" msgid="8786127461027964653">"Nije registrirano na mreži."</string>
     <string name="incall_error_out_of_service" msgid="1927265196942672791">"Mobilna mreža nije dostupna."</string>
-    <string name="incall_error_out_of_service_wfc" msgid="4497663185857190885">"Mobilna mreža nije dostupna. Povežite se na bežičnu mrežu da uputite poziv."</string>
+    <string name="incall_error_out_of_service_wfc" msgid="4497663185857190885">"Mobilna mreža nije dostupna. Povežite se na bežičnu mrežu da pozovete."</string>
     <string name="incall_error_no_phone_number_supplied" msgid="8680831089508851894">"Da uputite poziv, upišite važeći broj."</string>
     <string name="incall_error_call_failed" msgid="393508653582682539">"Poziv nije uspio."</string>
     <string name="incall_error_cannot_add_call" msgid="5425764862628655443">"Trenutno nije moguće dodati poziv. Možete pokušati poslati poruku."</string>
@@ -549,8 +553,8 @@
     <string name="incall_error_supp_service_reject" msgid="3044363092441655912">"Nije moguće odbiti poziv."</string>
     <string name="incall_error_supp_service_hangup" msgid="836524952243836735">"Nije moguće uputiti poziv(e)."</string>
     <string name="incall_error_supp_service_hold" msgid="8535056414643540997">"Nije moguće staviti pozive na čekanje."</string>
-    <string name="incall_error_wfc_only_no_wireless_network" msgid="5860742792811400109">"Povežite se na bežičnu mrežu da uputite poziv."</string>
-    <string name="incall_error_promote_wfc" msgid="9164896813931363415">"Omogućite pozivanje putem WiFi-ja da uputite poziv."</string>
+    <string name="incall_error_wfc_only_no_wireless_network" msgid="5860742792811400109">"Povežite se na bežičnu mrežu da pozovete."</string>
+    <string name="incall_error_promote_wfc" msgid="9164896813931363415">"Omogućite pozivanje putem WiFi-ja da pozovete."</string>
     <string name="emergency_information_hint" msgid="9208897544917793012">"Informacije za hitne slučajeve"</string>
     <string name="emergency_information_owner_hint" msgid="6256909888049185316">"Vlasnik"</string>
     <string name="emergency_information_confirm_hint" msgid="5109017615894918914">"Dodirnite ponovo da pogledate informacije"</string>
@@ -672,7 +676,7 @@
     <string name="preference_category_ringtone" msgid="8787281191375434976">"Melodija zvona i vibracija"</string>
     <string name="pstn_connection_service_label" msgid="9200102709997537069">"Ugrađene SIM kartice"</string>
     <string name="enable_video_calling_title" msgid="7246600931634161830">"Uključi video pozive"</string>
-    <string name="enable_video_calling_dialog_msg" msgid="7141478720386203540">"Da uključite video pozive, omogućite Poboljšani 4G LTE način rada u postavkama mreže."</string>
+    <string name="enable_video_calling_dialog_msg" msgid="7141478720386203540">"Da uključite video pozive, omogućite poboljšani 4G LTE način rada u postavkama mreže."</string>
     <string name="enable_video_calling_dialog_settings" msgid="8697890611305307110">"Postavke mreže"</string>
     <string name="enable_video_calling_dialog_close" msgid="4298929725917045270">"Zatvori"</string>
     <string name="sim_label_emergency_calls" msgid="9078241989421522310">"Hitni pozivi"</string>
@@ -922,6 +926,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Informacije o telefonu"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Operater koji pruža informacije"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Aktiviranje dodjele resursa operatera"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Prijedlog: poboljšajte Bluetooth povezivost"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Vaš Bluetooth signal je slab. Pokušajte prebaciti na zvučnik."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Obavještenje o kvalitetu poziva"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Zastarjeli SIP računi"</string>
 </resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 4beb631..8a906fb 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Funcionalitat de la SIM limitada"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Pot ser que les trucades i els serveis de dades de <xliff:g id="CARRIER_NAME">%1$s</xliff:g> es bloquegin mentre fas servir el número <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Les trucades i dades que t\'ofereix <xliff:g id="CARRIER_NAME">%1$s</xliff:g> es poden bloquejar quan tens una altra SIM."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"S\'han trobat comptes SIP obsolets i s\'han suprimit"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"La plataforma Android ja no admet les trucades SIP.\nEls teus comptes SIP existents <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> s\'han suprimit.\nConfirma la configuració predeterminada de trucades del teu compte."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Ves a la configuració"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Ús de dades de l\'aplicació"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"S\'han utilitzat <xliff:g id="ID_1">%1$s</xliff:g> en el període <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Configuració avançada"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"La contrasenya no és correcta i la SIM s\'ha bloquejat. Introdueix el PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"El PUK2 s\'ha bloquejat permanentment."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Et queden <xliff:g id="NUMBER">%d</xliff:g> intents."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"El PUK2 s\'ha bloquejat. Contacta amb el proveïdor de serveis per desbloquejar-lo."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"El PIN2 ja no està bloquejat."</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Error de la xarxa o de targeta SIM"</string>
     <string name="doneButton" msgid="7371209609238460207">"Fet"</string>
@@ -571,7 +575,7 @@
     <string name="description_concat_format" msgid="2014471565101724088">"%1$s, %2$s"</string>
     <string name="dialerKeyboardHintText" msgid="1115266533703764049">"Utilitzeu el teclat per marcar"</string>
     <string name="onscreenHoldText" msgid="4025348842151665191">"Posa en espera"</string>
-    <string name="onscreenEndCallText" msgid="6138725377654842757">"Final"</string>
+    <string name="onscreenEndCallText" msgid="6138725377654842757">"Finalitza"</string>
     <string name="onscreenShowDialpadText" msgid="658465753816164079">"Teclat"</string>
     <string name="onscreenMuteText" msgid="5470306116733843621">"Silencia"</string>
     <string name="onscreenAddCallText" msgid="9075675082903611677">"Afegeix una trucada"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Informació del telèfon"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Informació de serveis de l\'operador"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Activa els serveis de l\'operador"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Suggeriment: millora la connectivitat Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"El senyal del Bluetooth és feble. Fes servir l\'altaveu."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Notificació sobre la qualitat de la trucada"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Comptes SIP obsolets"</string>
 </resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 397c5ff..914c04d 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Omezená funkčnost SIM karty"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Hovory a datové služby operátora <xliff:g id="CARRIER_NAME">%1$s</xliff:g> mohou být zablokovány, když používáte číslo <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Hovory a datové služby operátora <xliff:g id="CARRIER_NAME">%1$s</xliff:g> mohou být s jinou SIM kartou blokovány."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Zastaralé účty SIP byly vyhledány a odstraněny"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Volání SIP už na platformě Android není podporováno.\nVaše stávající účty SIP (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) byly odstraněny.\nPotvrďte výchozí nastavení účtu pro volání."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Přejít do nastavení"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Využití dat aplikací"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"V období <xliff:g id="ID_2">%2$s</xliff:g> jste využili <xliff:g id="ID_1">%1$s</xliff:g> mobilních dat"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Rozšířená nastavení"</string>
@@ -301,7 +304,7 @@
     <string name="keywords_carrier_settings_euicc" msgid="8540160967922063745">"operátor, esim, sim, euicc, přepnout operátora, přidat operátora"</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>
     <string name="mobile_data_settings_title" msgid="7228249980933944101">"Mobilní data"</string>
-    <string name="mobile_data_settings_summary" msgid="5012570152029118471">"Přistupovat k datům přes mobilní síť"</string>
+    <string name="mobile_data_settings_summary" msgid="5012570152029118471">"Používat datové připojení přes mobilní síť"</string>
     <string name="data_usage_disable_mobile" msgid="5669109209055988308">"Vypnout mobilní data?"</string>
     <string name="sim_selection_required_pref" msgid="6985901872978341314">"Vyžadován výběr"</string>
     <string name="sim_change_data_title" msgid="9142726786345906606">"Změnit SIM kartu pro data?"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Nesprávné heslo. SIM karta je teď uzamčena. Zadejte kód PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 je trvale zablokován."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Počet zbývajících pokusů: <xliff:g id="NUMBER">%d</xliff:g>."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"Uzamčeno kódem PUK2. Se žádostí o odemknutí SIM karty se obraťte na poskytovatele služeb."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 již není blokován"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Došlo k chybě sítě nebo SIM karty."</string>
     <string name="doneButton" msgid="7371209609238460207">"Hotovo"</string>
@@ -925,6 +929,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Informace o telefonu"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Informace o zprovoznění služby u operátora"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Spustit zprovoznění služby u operátora"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Doporučení: Vylepšete připojení přes Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Signál sítě Bluetooth je slabý. Zkuste přepnout na hlasitý odposlech."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Oznámení o kvalitě hovoru"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Zastaralé účty SIP"</string>
 </resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 330998a..8b92d31 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"SIM-kortets funktionalitet er begrænset"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Opkalds- og datatjenester fra <xliff:g id="CARRIER_NAME">%1$s</xliff:g> blokeres muligvis, mens <xliff:g id="PHONE_NUMBER">%2$s</xliff:g> bruges."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Opkalds- og datatjenester fra <xliff:g id="CARRIER_NAME">%1$s</xliff:g> blokeres muligvis, mens der bruges et andet SIM-kort."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Udfasede SIP-konti blev fundet og fjernet"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP-opkald understøttes ikke længere af Android-platformen.\nDine eksisterende SIP-konti <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> er blevet fjernet.\nBekræft din standardkontoindstilling for opkald."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Gå til Indstillinger"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Dataforbrug i apps"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> mobildata er brugt i perioden <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Avanceret"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Forkert adgangskode. SIM-kortet er nu låst. Angiv PUK-kode 2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 er permanent blokeret."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Du har <xliff:g id="NUMBER">%d</xliff:g> forsøg tilbage."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2-låst. Kontakt din udbyder for at låse op."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"Pinkode 2 er ikke længere blokeret"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Fejl i netværk eller SIM-kort"</string>
     <string name="doneButton" msgid="7371209609238460207">"Afslut"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Telefonoplysninger"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Oplysninger om mobilselskab"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Aktivér mobilselskab"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Forslag: Optimer Bluetooth-forbindelsen"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Dit Bluetooth-signal er svagt. Prøv at skifte til medhør."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Notifikation om opkaldskvalitet"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Udfasede SIP-konti"</string>
 </resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index a9777db..758cc34 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Eingeschränkte SIM-Funktion"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Anrufe und Datendienste über <xliff:g id="CARRIER_NAME">%1$s</xliff:g> stehen möglicherweise nicht zur Verfügung, während du <xliff:g id="PHONE_NUMBER">%2$s</xliff:g> nutzt."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Anrufe/Datendienste über <xliff:g id="CARRIER_NAME">%1$s</xliff:g> sind bei Nutzung einer anderen SIM eventuell blockiert."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Eingestellte SIP-Konten gefunden und entfernt"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP-Anrufe werden von der Android-Plattform nicht mehr unterstützt.\nDie vorhandenen SIP-Konten (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) wurden entfernt.\nPrüfe die Einstellung für das Standardkonto für Anrufe."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Zu den Einstellungen"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Datenverbrauch durch Apps"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"Im Zeitraum <xliff:g id="ID_2">%2$s</xliff:g> genutzte mobile Daten: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Erweitert"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Passwort falsch. SIM-Karte ist jetzt gesperrt. Bitte PUK2 eingeben."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 wurde endgültig gesperrt."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Du hast noch <xliff:g id="NUMBER">%d</xliff:g> Versuche."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 gesperrt. Wende dich zum Entsperren an den Mobilfunkanbieter."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 nicht mehr gesperrt"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Netzwerk- oder SIM-Kartenfehler"</string>
     <string name="doneButton" msgid="7371209609238460207">"Fertig"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Über das Telefon"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Informationen zur Nutzerverwaltung durch den Anbieter"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Nutzerverwaltung durch den Anbieter auslösen"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Vorschlag: Bluetooth-Verbindung optimieren"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Das Bluetooth-Signal ist schwach. Verwende die Freisprechfunktion."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Benachrichtigung zu Anrufqualität"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Eingestellte SIP-Konten"</string>
 </resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index c7574d8..55975a1 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Περιορισμένη λειτουργικότητα SIM"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Οι κλήσεις και οι υπηρεσίες δεδομένων από την <xliff:g id="CARRIER_NAME">%1$s</xliff:g> ενδέχεται να αποκλειστούν κατά τη χρήση του αριθμού <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Κλήσεις και υπηρεσίες δεδομένων από <xliff:g id="CARRIER_NAME">%1$s</xliff:g> ίσως αποκλειστούν κατά τη χρήση άλλης SIM."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Βρέθηκαν και καταργήθηκαν λογαριασμοί SIP, οι οποίοι έχουν καταργηθεί"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Η δυνατότητα κλήσεων SIP δεν υποστηρίζεται πλέον στην πλατφόρμα Android.\nΟι υπάρχοντες λογαριασμοί σας SIP <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> έχουν καταργηθεί.\nΕπιβεβαιώστε την προεπιλεγμένη σας ρύθμιση λογαριασμού κλήσεων."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Μετάβαση στις ρυθμίσεις"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Χρήση δεδομένων εφαρμογής"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> δεδομένων κινητής τηλεφωνίας χρησιμοποιήθηκαν μεταξύ <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Σύνθετες ρυθμίσεις"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Ο κωδικός πρόσβασης είναι λανθασμένος. Η SIM κλειδώθηκε. Καταχωρίστε το PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"Ο κωδικός PUK2 έχει μπλοκαριστεί οριστικά."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Απομένουν <xliff:g id="NUMBER">%d</xliff:g> προσπάθειες."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"Το PUK2 κλειδώθηκε. Επικοινωνήστε με τον πάροχο υπηρεσιών για ξεκλείδωμα."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"Το PIN2 δεν είναι πλέον αποκλεισμένο"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Σφάλμα δικτύου ή κάρτας SIM"</string>
     <string name="doneButton" msgid="7371209609238460207">"Τέλος"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Πληροφορίες τηλεφώνου"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Πληροφορίες παροχής εταιρείας κινητής τηλεφωνίας"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Ενεργοποίηση παροχής εταιρείας κινητής τηλεφωνίας"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Πρόταση: Βελτιώστε τη συνδεσιμότητα Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Το σήμα bluetooth είναι ασθενές. Δοκιμάστε να αλλάξετε σε ανοιχτή ακρόαση."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Ειδοποίηση ποιότητας κλήσης"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Καταργημένοι λογαριασμοί SIP"</string>
 </resources>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index f246913..5e676c3 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Limited SIM functionality"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> calls and data services may be blocked while using <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> calls and data services may be blocked while using another SIM."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Deprecated SIP accounts found and removed"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP calling is no longer supported by the Android platform.\nYour existing SIP accounts <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> have been removed.\nPlease confirm your default calling account setting."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Go to settings"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"App data usage"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> mobile data used <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Advanced"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Password incorrect. SIM now locked. Enter PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 is permanently blocked."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"You have <xliff:g id="NUMBER">%d</xliff:g> remaining attempts."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 locked. Contact service provider to unlock."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 no longer blocked"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Network or SIM card error"</string>
     <string name="doneButton" msgid="7371209609238460207">"Done"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Phone info"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Operator provisioning info"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Trigger operator provisioning"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Suggestion: Improve Bluetooth connectivity"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Your Bluetooth signal is weak. Try switching to speakerphone."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Call quality notification"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Deprecated SIP accounts"</string>
 </resources>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index d325708..bc4169e 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Limited SIM functionality"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> calls and data services may be blocked while using <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> calls and data services may be blocked while using another SIM."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Deprecated SIP accounts found and removed"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP calling is no longer supported by the Android platform.\nYour existing SIP accounts <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> have been removed.\nPlease confirm your default calling account setting."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Go to settings"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"App data usage"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> mobile data used <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Advanced"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Password incorrect. SIM now locked. Enter PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 is permanently blocked."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"You have <xliff:g id="NUMBER">%d</xliff:g> remaining attempts."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 locked. Contact service provider to unlock."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 no longer blocked"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Network or SIM card error"</string>
     <string name="doneButton" msgid="7371209609238460207">"Done"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Phone info"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Operator provisioning info"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Trigger operator provisioning"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Suggestion: Improve Bluetooth connectivity"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Your Bluetooth signal is weak. Try switching to speakerphone."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Call quality notification"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Deprecated SIP accounts"</string>
 </resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index f246913..5e676c3 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Limited SIM functionality"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> calls and data services may be blocked while using <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> calls and data services may be blocked while using another SIM."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Deprecated SIP accounts found and removed"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP calling is no longer supported by the Android platform.\nYour existing SIP accounts <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> have been removed.\nPlease confirm your default calling account setting."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Go to settings"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"App data usage"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> mobile data used <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Advanced"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Password incorrect. SIM now locked. Enter PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 is permanently blocked."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"You have <xliff:g id="NUMBER">%d</xliff:g> remaining attempts."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 locked. Contact service provider to unlock."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 no longer blocked"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Network or SIM card error"</string>
     <string name="doneButton" msgid="7371209609238460207">"Done"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Phone info"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Operator provisioning info"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Trigger operator provisioning"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Suggestion: Improve Bluetooth connectivity"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Your Bluetooth signal is weak. Try switching to speakerphone."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Call quality notification"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Deprecated SIP accounts"</string>
 </resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index f246913..5e676c3 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Limited SIM functionality"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> calls and data services may be blocked while using <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> calls and data services may be blocked while using another SIM."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Deprecated SIP accounts found and removed"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP calling is no longer supported by the Android platform.\nYour existing SIP accounts <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> have been removed.\nPlease confirm your default calling account setting."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Go to settings"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"App data usage"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> mobile data used <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Advanced"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Password incorrect. SIM now locked. Enter PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 is permanently blocked."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"You have <xliff:g id="NUMBER">%d</xliff:g> remaining attempts."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 locked. Contact service provider to unlock."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 no longer blocked"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Network or SIM card error"</string>
     <string name="doneButton" msgid="7371209609238460207">"Done"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Phone info"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Operator provisioning info"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Trigger operator provisioning"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Suggestion: Improve Bluetooth connectivity"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Your Bluetooth signal is weak. Try switching to speakerphone."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Call quality notification"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Deprecated SIP accounts"</string>
 </resources>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index cca7e56..e69d221 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‎‎‎‏‎‎‎‎‎‎‎‏‏‎‎‏‏‎‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‎‎‎‏‎‎‏‎Limited SIM functionality‎‏‎‎‏‎"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‎‎‎‎‏‎‎‎‎‏‎‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="CARRIER_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ calls and data services may be blocked while using ‎‏‎‎‏‏‎<xliff:g id="PHONE_NUMBER">%2$s</xliff:g>‎‏‎‎‏‏‏‎.‎‏‎‎‏‎"</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‏‎‏‏‎‏‎‏‎‎‏‏‏‎‎‎‎‏‎‏‏‎‏‎‏‏‎‏‎‎‏‎‏‏‎‏‏‎‏‏‎‏‎‏‏‎‎‎‎‏‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="CARRIER_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ calls and data services may be blocked while using another SIM.‎‏‎‎‏‎"</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‏‏‏‎‏‏‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‏‎‎‎‏‎‎‎‏‎‏‎‎‎‏‎‎‏‎‎‎‏‎‎‎‏‏‏‏‏‎Deprecated SIP accounts found and removed‎‏‎‎‏‎"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‏‎‎‏‏‎‏‎‎‎‎‎‏‎‏‏‎‎‎‎‏‏‎‏‏‎‏‏‎‎‏‏‎‏‎‎‎‏‏‎‏‎‎‎‎‏‏‏‎‏‏‎‏‎‎‎‎SIP calling is no longer supported by Android platform.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Your existing SIP accounts ‎‏‎‎‏‏‎<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>‎‏‎‎‏‏‏‎ have been removed.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Please confirm your default calling account setting.‎‏‎‎‏‎"</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‎‏‏‏‎‎‏‏‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‏‎‎‏‏‎‎‏‎‎‎‎‎‎‎‎‏‏‏‎‏‎‏‎‏‎‏‎‎Go to settings‎‏‎‎‏‎"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‏‏‏‏‏‎‎‏‏‎‏‏‎‏‎‏‏‎‎‎‎‏‎‎‎‏‎‎‏‎‎‏‎‏‏‏‎‏‎‎‏‎‏‎‎‏‏‎‎‎‎App data usage‎‏‎‎‏‎"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‎‎‎‏‏‎‎‏‎‎‎‎‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‏‏‎‎‎‏‎‎‎‎‎‏‏‎‏‏‏‎‏‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="ID_1">%1$s</xliff:g>‎‏‎‎‏‏‏‎ mobile data used ‎‏‎‎‏‏‎<xliff:g id="ID_2">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‏‎‏‎‎‏‏‎‏‎‏‎‏‎‏‎‎‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‎‎‎‏‎‎‏‎‎‏‏‎‎Advanced‎‏‎‎‏‎"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‎‎‏‎‎‏‏‏‏‎‎‏‏‏‏‎‎‎‎‏‎‎‎‎‎‎‎‏‏‏‏‎‏‏‎‎‏‏‎‏‏‏‏‎‎‏‎‎‏‏‏‎‏‎‎Password incorrect. SIM now locked. Enter PUK2.‎‏‎‎‏‎"</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‏‎‏‎‎‎‎‎‎‏‎‏‎‎‏‎‏‏‏‎‎‎‎‏‎‎‎‏‏‏‎‎‏‎‎‎‏‎‎‏‎‎‎‎‎‎‏‏‎‎‏‏‏‎PUK2 is permanently blocked.‎‏‎‎‏‎"</string>
     <string name="pin2_attempts" msgid="5625178102026453023">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‎‏‎‏‎‎‏‎‏‏‎‏‎‏‏‏‎‎‎‏‎‏‎‎‏‏‎‎‏‎‏‏‏‏‏‎‎‎‎‎‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎You have ‎‏‎‎‏‏‎<xliff:g id="NUMBER">%d</xliff:g>‎‏‎‎‏‏‏‎ remaining attempts.‎‏‎‎‏‎"</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‎‎‏‎‏‎‏‏‏‏‎‎‎‎‏‎‏‏‎‏‎‎‏‏‎‎‏‎‏‏‏‎‎‎‏‏‎‎‎‎‏‎‏‏‎‎‎‎‎‏‎PUK2 locked. Contact service provider to unlock.‎‏‎‎‏‎"</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‏‎‎‎‎‎‎‎‏‎‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‏‎‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‏‏‎‏‏‏‏‎‏‏‏‎PIN2 no longer blocked‎‏‎‎‏‎"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‎‎‏‎‎‎‏‏‎‎‎‏‎‏‏‏‎‏‏‎‏‏‎‎‎‎‎‏‏‎‏‏‎‏‎‏‎‎‎‎‎‏‏‏‎‏‎‏‏‎‎‏‎Network or SIM card error‎‏‎‎‏‎"</string>
     <string name="doneButton" msgid="7371209609238460207">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‎‏‎‏‏‏‏‎‎‏‏‎‎‎‎‎‏‎‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‏‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‏‏‏‏‎Done‎‏‎‎‏‎"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‏‏‎‎‎‎‏‎‏‎‏‎‏‎‏‎‏‎‎‎‎‎‏‎‏‏‏‎‎‏‎‏‎‎‎‎‎‎‏‏‎‎‎‏‎‎‎‎‏‎‎‎‏‎‎‏‎Phone info‎‏‎‎‏‎"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‏‎‎‎‏‎‎‎‎‎‏‎‎‎‏‎‎‎‎Carrier Provisioning Info‎‏‎‎‏‎"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‏‎‎‎‎‏‎‎‎‏‎‎‎‎‎‏‏‏‎‎‎‏‏‏‎‏‎‎‎‎‏‎‏‏‏‏‎‏‎‎‏‏‎‏‏‏‏‎‎‎‎‎‎‏‏‎‏‎‎Trigger Carrier Provisioning‎‏‎‎‏‎"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‏‎‏‏‎‎‎‎‏‎‏‎‎‏‎‎‏‏‏‎‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‎‎‎‎‏‎‎‏‎‎‏‎‎‏‎‎‏‏‎‏‎‎‏‏‎Suggestion: Improve Bluetooth connectivity‎‏‎‎‏‎"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‏‏‎‏‎‏‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‎‎‏‎‎‎‎‏‏‎‎‏‎‏‏‏‏‎‎‏‏‏‏‎‎‎‏‎‏‏‏‎Your bluetooth signal is weak. Try switching to speakerphone.‎‏‎‎‏‎"</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‎‎‎‎‎‎‎‏‎‏‏‎‎‏‏‏‎‏‎‎‎‏‎‎‎‎‏‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‎‏‎‎‏‏‏‏‎‎Call Quality Notification‎‏‎‎‏‎"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‎‏‏‎‎‎‎‎‏‎‏‏‎‏‏‏‏‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‏‎‏‎‏‎‏‎‎‎‏‎‏‎‏‏‎‏‎‏‏‎‏‎‏‎Deprecated SIP accounts‎‏‎‎‏‎"</string>
 </resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index d346ea0..b3c8395 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Funcionalidad de SIM limitada"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Los servicios de llamadas y datos de <xliff:g id="CARRIER_NAME">%1$s</xliff:g> pueden bloquearse mientras uses el número <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Las llamadas y los servicios de datos de <xliff:g id="CARRIER_NAME">%1$s</xliff:g> podrían bloquearse al usar otra SIM."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Se encontraron y se quitaron cuentas SIP obsoletas"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"La plataforma de Android ya no admite las llamadas SIP.\nSe quitaron las cuentas SIP <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> existentes.\nConfirma la configuración predeterminada de tu cuenta para las llamadas SIP."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Ir a Configuración"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Uso de datos de la app"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"Datos móviles usados: <xliff:g id="ID_1">%1$s</xliff:g>, <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Avanzada"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Contraseña incorrecta. SIM bloqueada. Ingresa el PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"El código PUK2 se bloqueó de forma permanente."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Te quedan <xliff:g id="NUMBER">%d</xliff:g> intentos."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"SIM bloqueada con PUK2. Comunícate con el proveedor para desbloquearla."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"El PIN2 ya no está bloqueado."</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Error en la red o en la tarjeta SIM"</string>
     <string name="doneButton" msgid="7371209609238460207">"Finalizado"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Información del teléfono"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Información del aprovisionamiento del proveedor"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Activar aprovisionamiento del proveedor"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Sugerencia: Mejora la conectividad Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Tu señal de Bluetooth es débil. Intenta cambiar al altavoz."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Notificación de calidad de llamada"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Cuentas SIP obsoletas"</string>
 </resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 98871c4..d012d07 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -182,7 +182,7 @@
     <string name="connect_later" msgid="1950138106010005425">"No se puede conectar a la red en este momento. Inténtalo de nuevo más tarde."</string>
     <string name="registration_done" msgid="5337407023566953292">"Conexión con la red establecida"</string>
     <string name="already_auto" msgid="8607068290733079336">"Ya estás en la selección automática."</string>
-    <string name="select_automatically" msgid="779750291257872651">"Seleccionar una red automáticamente"</string>
+    <string name="select_automatically" msgid="779750291257872651">"Seleccionar red automáticamente"</string>
     <string name="manual_mode_disallowed_summary" msgid="3970048592179890197">"No disponible cuando se ha conectado con %1$s"</string>
     <string name="network_select_title" msgid="4117305053881611988">"Red"</string>
     <string name="register_automatically" msgid="3907580547590554834">"Registro automático..."</string>
@@ -281,22 +281,25 @@
     <string name="data_enabled" msgid="22525832097434368">"Habilitar datos"</string>
     <string name="data_enable_summary" msgid="696860063456536557">"Permitir uso de datos"</string>
     <string name="dialog_alert_title" msgid="5260471806940268478">"Atención"</string>
-    <string name="roaming" msgid="1576180772877858949">"Itinerancia"</string>
-    <string name="roaming_enable" msgid="6853685214521494819">"Conectarse a servicios de datos en itinerancia"</string>
-    <string name="roaming_disable" msgid="8856224638624592681">"Conectarse a servicios de datos en itinerancia"</string>
-    <string name="roaming_reenable_message" msgid="1951802463885727915">"La itinerancia de datos está desactivada. Toca para activarla."</string>
-    <string name="roaming_enabled_message" msgid="9022249120750897">"Pueden aplicarse cargos de itinerancia. Toca para modificar este ajuste."</string>
+    <string name="roaming" msgid="1576180772877858949">"Roaming"</string>
+    <string name="roaming_enable" msgid="6853685214521494819">"Conectarse a servicios de datos en roaming"</string>
+    <string name="roaming_disable" msgid="8856224638624592681">"Conectarse a servicios de datos en roaming"</string>
+    <string name="roaming_reenable_message" msgid="1951802463885727915">"El roaming de datos está desactivado. Toca para activarla."</string>
+    <string name="roaming_enabled_message" msgid="9022249120750897">"Pueden aplicarse cargos de roaming. Toca para modificar este ajuste."</string>
     <string name="roaming_notification_title" msgid="3590348480688047320">"Se ha perdido la conexión de datos móviles"</string>
-    <string name="roaming_on_notification_title" msgid="7451473196411559173">"La itinerancia de datos está activada"</string>
+    <string name="roaming_on_notification_title" msgid="7451473196411559173">"El roaming de datos está activado"</string>
     <string name="roaming_warning" msgid="7855681468067171971">"El coste de este servicio puede ser significativo."</string>
     <string name="roaming_check_price_warning" msgid="8212484083990570215">"Ponte en contacto con tu proveedor de red para consultar el precio."</string>
-    <string name="roaming_alert_title" msgid="5689615818220960940">"¿Permitir itinerancia de datos?"</string>
+    <string name="roaming_alert_title" msgid="5689615818220960940">"¿Permitir roaming de datos?"</string>
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Funcionalidad de SIM limitada"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Puede los servicios de datos y llamadas de <xliff:g id="CARRIER_NAME">%1$s</xliff:g> se bloqueen mientras se utilice el <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Puede que los servicios de datos y llamadas de <xliff:g id="CARRIER_NAME">%1$s</xliff:g> se bloqueen al usar otra SIM."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Cuentas SIP obsoletas encontradas y eliminadas"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"La plataforma Android ya no admite llamadas SIP.\nTus cuentas SIP (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) se han eliminado.\nConfirma la configuración predeterminada de tu cuenta de llamadas."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Ir a Ajustes"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Uso de datos de la aplicación"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"Se han usado <xliff:g id="ID_1">%1$s</xliff:g> en el periodo del <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <string name="advanced_options_title" msgid="9208195294513520934">"Avanzado"</string>
+    <string name="advanced_options_title" msgid="9208195294513520934">"Ajustes avanzados"</string>
     <string name="carrier_settings_euicc" msgid="1190237227261337749">"Operador"</string>
     <string name="keywords_carrier_settings_euicc" msgid="8540160967922063745">"operador, esim, sim, euicc, cambiar de operador, añadir operador"</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>
@@ -305,7 +308,7 @@
     <string name="data_usage_disable_mobile" msgid="5669109209055988308">"¿Quieres desactivar los datos móviles?"</string>
     <string name="sim_selection_required_pref" msgid="6985901872978341314">"Debes seleccionar una opción"</string>
     <string name="sim_change_data_title" msgid="9142726786345906606">"¿Cambiar la SIM de datos?"</string>
-    <string name="sim_change_data_message" msgid="3567358694255933280">"¿Quieres usar la tarjeta <xliff:g id="NEW_SIM">%1$s</xliff:g> en lugar de <xliff:g id="OLD_SIM">%2$s</xliff:g> para los datos móviles?"</string>
+    <string name="sim_change_data_message" msgid="3567358694255933280">"¿Usar la tarjeta <xliff:g id="NEW_SIM">%1$s</xliff:g> en lugar de <xliff:g id="OLD_SIM">%2$s</xliff:g> para los datos móviles?"</string>
     <string name="wifi_calling_settings_title" msgid="5800018845662016507">"Llamadas por Wi-Fi"</string>
     <string name="video_calling_settings_title" msgid="342829454913266078">"Videollamadas a través del operador"</string>
     <string name="gsm_umts_options" msgid="4968446771519376808">"Opciones GSM/UMTS"</string>
@@ -413,7 +416,7 @@
     <string name="network_4G" msgid="6800527815504223913">"4G (recomendado)"</string>
     <string name="network_global" msgid="3289646154407617631">"Mundial"</string>
     <string name="cdma_system_select_title" msgid="614165233552656431">"Selección del sistema"</string>
-    <string name="cdma_system_select_summary" msgid="3840420390242060407">"Cambiar el modo de itinerancia CDMA"</string>
+    <string name="cdma_system_select_summary" msgid="3840420390242060407">"Cambiar el modo de roaming CDMA"</string>
     <string name="cdma_system_select_dialogtitle" msgid="5524639510676501802">"Selección del sistema"</string>
   <string-array name="cdma_system_select_choices">
     <item msgid="462340042928284921">"Solo sistema doméstico"</item>
@@ -480,7 +483,7 @@
     <string name="simContacts_empty" msgid="1135632055473689521">"No hay ningún contacto en la tarjeta SIM."</string>
     <string name="simContacts_title" msgid="2714029230160136647">"Seleccionar contactos para importar"</string>
     <string name="simContacts_airplaneMode" msgid="4654884030631503808">"Desactiva el modo avión para importar contactos de la tarjeta SIM."</string>
-    <string name="enable_pin" msgid="967674051730845376">"Habilitar/inhabilitar PIN de tarjeta SIM"</string>
+    <string name="enable_pin" msgid="967674051730845376">"Habilitar/Inhabilitar PIN de tarjeta SIM"</string>
     <string name="change_pin" msgid="3657869530942905790">"Cambiar PIN de tarjeta SIM"</string>
     <string name="enter_pin_text" msgid="3182311451978663356">"PIN de la tarjeta SIM:"</string>
     <string name="oldPinLabel" msgid="8618515202411987721">"PIN antiguo"</string>
@@ -512,9 +515,10 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Contraseña incorrecta. SIM bloqueada. Introducir PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"El PUK2 se ha bloqueado de forma permanente."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Quedan <xliff:g id="NUMBER">%d</xliff:g> intentos."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 bloqueado. Ponte en contacto con tu proveedor de servicios para desbloquearlo."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 desbloqueado"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Error en la tarjeta SIM o en la red"</string>
-    <string name="doneButton" msgid="7371209609238460207">"Listo"</string>
+    <string name="doneButton" msgid="7371209609238460207">"Hecho"</string>
     <string name="voicemail_settings_number_label" msgid="1265118640154688162">"Número del buzón de voz"</string>
     <string name="card_title_dialing" msgid="8742182654254431781">"Llamando"</string>
     <string name="card_title_redialing" msgid="18130232613559964">"Marcando otra vez"</string>
@@ -592,7 +596,7 @@
     <string name="rtt_mode_title" msgid="3075948111362818043">"Llamada de texto en tiempo real (TTR)"</string>
     <string name="rtt_mode_summary" msgid="8631541375609989562">"Permitir mensajes en una llamada de voz"</string>
     <string name="rtt_mode_more_information" msgid="587500128658756318">"La función TTR (texto en tiempo real) ofrece asistencia a las personas sordas o con discapacidades auditivas, que tienen algún trastorno del habla o que necesitan una transcripción además de la voz para comunicarse.&lt;br&gt; &lt;a href=<xliff:g id="URL">http://support.google.com/mobile?p=telephony_rtt</xliff:g>&gt;Más información&lt;/a&gt;\n       &lt;br&gt;&lt;br&gt; - Las llamadas de TTR se guardan como la transcripción de un mensaje.\n       &lt;br&gt; - La función TTR no está disponible en videollamadas."</string>
-    <string name="no_rtt_when_roaming" msgid="5268008247378355389">"Nota: TTR no está disponible en itinerancia"</string>
+    <string name="no_rtt_when_roaming" msgid="5268008247378355389">"Nota: TTR no está disponible en roaming"</string>
   <string-array name="tty_mode_entries">
     <item msgid="3238070884803849303">"TTY desactivado"</item>
     <item msgid="1449091874731375214">"TTY completo"</item>
@@ -680,7 +684,7 @@
     <string name="status_hint_label_wifi_call" msgid="942993035689809853">"Llamada Wi-Fi"</string>
     <string name="emergency_action_launch_hint" msgid="2762016865340891314">"Toca de nuevo para abrir"</string>
     <string name="message_decode_error" msgid="1061856591500290887">"Se ha producido un error al decodificar el mensaje."</string>
-    <string name="callFailed_cdma_activation" msgid="5392057031552253550">"Una tarjeta SIM ha activado tu servicio y actualizado la función de itinerancia del teléfono."</string>
+    <string name="callFailed_cdma_activation" msgid="5392057031552253550">"Una tarjeta SIM ha activado tu servicio y actualizado la función de roaming del teléfono."</string>
     <string name="callFailed_cdma_call_limit" msgid="1074219746093031412">"Hay demasiadas llamadas activas. Finaliza o combina las llamadas que tienes antes de iniciar otra."</string>
     <string name="callFailed_imei_not_accepted" msgid="7257903653685147251">"No se puede establecer la conexión. Inserta una tarjeta SIM válida."</string>
     <string name="callFailed_wifi_lost" msgid="1788036730589163141">"Se ha perdido la conexión Wi-Fi. La llamada ha finalizado."</string>
@@ -700,16 +704,16 @@
     <string name="change_pin_confirm_pins_dont_match" msgid="305164501222587215">"Los PIN no coinciden"</string>
     <string name="change_pin_succeeded" msgid="2504705600693014403">"Se ha cambiado el PIN del buzón de voz"</string>
     <string name="change_pin_system_error" msgid="7772788809875146873">"No se ha podido configurar el PIN"</string>
-    <string name="mobile_data_status_roaming_turned_off_subtext" msgid="6840673347416227054">"Se ha desactivado la itinerancia de datos"</string>
-    <string name="mobile_data_status_roaming_turned_on_subtext" msgid="5615757897768777865">"Se ha activado la itinerancia de datos"</string>
-    <string name="mobile_data_status_roaming_without_plan_subtext" msgid="6536671968072284677">"En itinerancia actualmente, se necesita un plan de datos"</string>
-    <string name="mobile_data_status_roaming_with_plan_subtext" msgid="2576177169108123095">"En itinerancia actualmente, hay un plan de datos activo"</string>
+    <string name="mobile_data_status_roaming_turned_off_subtext" msgid="6840673347416227054">"Se ha desactivado el roaming de datos"</string>
+    <string name="mobile_data_status_roaming_turned_on_subtext" msgid="5615757897768777865">"Se ha activado el roaming de datos"</string>
+    <string name="mobile_data_status_roaming_without_plan_subtext" msgid="6536671968072284677">"En roaming actualmente, se necesita un plan de datos"</string>
+    <string name="mobile_data_status_roaming_with_plan_subtext" msgid="2576177169108123095">"En roaming actualmente, hay un plan de datos activo"</string>
     <string name="mobile_data_status_no_plan_subtext" msgid="170331026419263657">"No quedan datos móviles"</string>
     <string name="mobile_data_activate_prepaid" msgid="4276738964416795596">"No quedan datos móviles"</string>
     <string name="mobile_data_activate_prepaid_summary" msgid="6846085278531605925">"Añadir datos móviles a través de <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
-    <string name="mobile_data_activate_roaming_plan" msgid="922290995866269366">"No hay ningún plan de itinerancia"</string>
-    <string name="mobile_data_activate_roaming_plan_summary" msgid="5379228493306235969">"Añadir un plan de itinerancia a través de <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
-    <string name="mobile_data_activate_footer" msgid="7895874069807204548">"Puedes añadir datos móviles o un plan de itinerancia a través de tu operador, <xliff:g id="PROVIDER_NAME">%s</xliff:g>."</string>
+    <string name="mobile_data_activate_roaming_plan" msgid="922290995866269366">"No hay ningún plan de roaming"</string>
+    <string name="mobile_data_activate_roaming_plan_summary" msgid="5379228493306235969">"Añadir un plan de roaming a través de <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
+    <string name="mobile_data_activate_footer" msgid="7895874069807204548">"Puedes añadir datos móviles o un plan de roaming a través de tu operador, <xliff:g id="PROVIDER_NAME">%s</xliff:g>."</string>
     <string name="mobile_data_activate_diag_title" msgid="5401741936224757312">"¿Añadir datos?"</string>
     <string name="mobile_data_activate_diag_message" msgid="3527260988020415441">"Puede que tengas que añadir datos a través de <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
     <string name="mobile_data_activate_button" msgid="1139792516354374612">"AÑADIR DATOS"</string>
@@ -776,15 +780,15 @@
     <string name="call_barring_baoic" msgid="8668125428666851665">"Internacionales realizadas"</string>
     <string name="call_barring_baoic_enabled" msgid="1203758092657630123">"¿Quieres desbloquear las llamadas internacionales realizadas?"</string>
     <string name="call_barring_baoic_disabled" msgid="5656889339002997449">"¿Bloquear las llamadas internacionales realizadas?"</string>
-    <string name="call_barring_baoicr" msgid="8566167764432343487">"Itinerancia de llamadas internacionales realizadas"</string>
-    <string name="call_barring_baoicr_enabled" msgid="1615324165512798478">"¿Quieres desbloquear la itinerancia de las llamadas internacionales realizadas?"</string>
-    <string name="call_barring_baoicr_disabled" msgid="172010175248142831">"¿Bloquear la itinerancia de las llamadas internacionales realizadas?"</string>
+    <string name="call_barring_baoicr" msgid="8566167764432343487">"Roaming de llamadas internacionales realizadas"</string>
+    <string name="call_barring_baoicr_enabled" msgid="1615324165512798478">"¿Quieres desbloquear el roaming de las llamadas internacionales realizadas?"</string>
+    <string name="call_barring_baoicr_disabled" msgid="172010175248142831">"¿Bloquear el roaming de las llamadas internacionales realizadas?"</string>
     <string name="call_barring_baic" msgid="7941393541678658566">"Todas las entrantes"</string>
     <string name="call_barring_baic_enabled" msgid="4357332358020337470">"¿Quieres desbloquear todas las llamadas entrantes?"</string>
     <string name="call_barring_baic_disabled" msgid="2355945245938240958">"¿Bloquear todas las llamadas entrantes?"</string>
-    <string name="call_barring_baicr" msgid="8712249337313034226">"Itinerancia de llamadas internacionales entrantes"</string>
-    <string name="call_barring_baicr_enabled" msgid="64774270234828175">"¿Quieres desbloquear la itinerancia de las llamadas internacionales entrantes?"</string>
-    <string name="call_barring_baicr_disabled" msgid="3488129262744027262">"¿Bloquear la itinerancia de las llamadas internacionales entrantes?"</string>
+    <string name="call_barring_baicr" msgid="8712249337313034226">"Roaming de llamadas internacionales entrantes"</string>
+    <string name="call_barring_baicr_enabled" msgid="64774270234828175">"¿Quieres desbloquear el roaming de las llamadas internacionales entrantes?"</string>
+    <string name="call_barring_baicr_disabled" msgid="3488129262744027262">"¿Bloquear el roaming de las llamadas internacionales entrantes?"</string>
     <string name="call_barring_deactivate_all" msgid="7837931580047157328">"Desactivar todo"</string>
     <string name="call_barring_deactivate_all_description" msgid="4474119585042121604">"Desactivar todos los ajustes de bloqueo de llamadas"</string>
     <string name="call_barring_deactivate_success" msgid="3545644320298275337">"Se ha desactivado el bloqueo de llamadas"</string>
@@ -824,9 +828,9 @@
     <string name="callFailed_calling_disabled" msgid="5010992739401206283">"No se puede llamar porque se han inhabilitado las llamadas mediante la propiedad del sistema ro.telephony.disable-call."</string>
     <string name="callFailed_too_many_calls" msgid="5379426826618582180">"No se puede llamar porque ya hay dos llamadas en curso. Interrumpe una de ellas o combínalas en una conferencia para poder llamar de nuevo."</string>
     <string name="supp_service_over_ut_precautions" msgid="2145018231396701311">"Para utilizar <xliff:g id="SUPP_SERVICE">%s</xliff:g>, los datos móviles deben estar activados. Puedes cambiar esta opción en los ajustes de red móvil."</string>
-    <string name="supp_service_over_ut_precautions_roaming" msgid="670342104569972327">"Para utilizar <xliff:g id="SUPP_SERVICE">%s</xliff:g>, los datos móviles y la itinerancia de datos deben estar activados. Puedes cambiar estas opciones en los ajustes de red móvil."</string>
+    <string name="supp_service_over_ut_precautions_roaming" msgid="670342104569972327">"Para utilizar <xliff:g id="SUPP_SERVICE">%s</xliff:g>, los datos móviles y el roaming de datos deben estar activados. Puedes cambiar estas opciones en los ajustes de red móvil."</string>
     <string name="supp_service_over_ut_precautions_dual_sim" msgid="5166866975550910474">"Para utilizar <xliff:g id="SUPP_SERVICE">%1$s</xliff:g>, los datos móviles deben estar activados en la SIM <xliff:g id="SIM_NUMBER">%2$d</xliff:g>. Puedes cambiar esta opción en los ajustes de red móvil."</string>
-    <string name="supp_service_over_ut_precautions_roaming_dual_sim" msgid="6627654855191817965">"Para utilizar <xliff:g id="SUPP_SERVICE">%1$s</xliff:g>, los datos móviles y la itinerancia de datos deben estar activados en la SIM <xliff:g id="SIM_NUMBER">%2$d</xliff:g>. Puedes cambiar estas opciones en los ajustes de red móvil."</string>
+    <string name="supp_service_over_ut_precautions_roaming_dual_sim" msgid="6627654855191817965">"Para utilizar <xliff:g id="SUPP_SERVICE">%1$s</xliff:g>, los datos móviles y el roaming de datos deben estar activados en la SIM <xliff:g id="SIM_NUMBER">%2$d</xliff:g>. Puedes cambiar estas opciones en los ajustes de red móvil."</string>
     <string name="supp_service_over_ut_precautions_dialog_dismiss" msgid="5934541487903081652">"Cerrar"</string>
     <string name="radio_info_data_connection_enable" msgid="6183729739783252840">"Habilitar conexión de datos"</string>
     <string name="radio_info_data_connection_disable" msgid="6404751291511368706">"Inhabilitar conexión de datos"</string>
@@ -855,8 +859,8 @@
     <string name="radioInfo_service_out" msgid="287972405416142312">"Fuera de servicio"</string>
     <string name="radioInfo_service_emergency" msgid="4763879891415016848">"Solo llamadas de emergencia"</string>
     <string name="radioInfo_service_off" msgid="3456583511226783064">"Señal móvil desactivada"</string>
-    <string name="radioInfo_roaming_in" msgid="3156335577793145965">"Itinerancia"</string>
-    <string name="radioInfo_roaming_not" msgid="1904547918725478110">"Sin itinerancia"</string>
+    <string name="radioInfo_roaming_in" msgid="3156335577793145965">"Roaming"</string>
+    <string name="radioInfo_roaming_not" msgid="1904547918725478110">"Sin roaming"</string>
     <string name="radioInfo_phone_idle" msgid="2191653783170757819">"Inactivo"</string>
     <string name="radioInfo_phone_ringing" msgid="8100354169567413370">"Sonando"</string>
     <string name="radioInfo_phone_offhook" msgid="7564601639749936170">"Llamada en curso"</string>
@@ -879,7 +883,7 @@
     <string name="radio_info_cell_info_refresh_rate" msgid="670511448975997340">"Frecuencia de actualización de la información del teléfono:"</string>
     <string name="radio_info_cellinfo_label" msgid="8199062974670377659">"Información sobre las dimensiones de los teléfonos:"</string>
     <string name="radio_info_gprs_service_label" msgid="6819204246355412952">"Servicio de datos:"</string>
-    <string name="radio_info_roaming_label" msgid="6636932886446857120">"Itinerancia:"</string>
+    <string name="radio_info_roaming_label" msgid="6636932886446857120">"Roaming:"</string>
     <string name="radio_info_imei_label" msgid="8947899706930120368">"IMEI:"</string>
     <string name="radio_info_call_redirect_label" msgid="4526480903023362276">"Redirección de llamadas:"</string>
     <string name="radio_info_ppp_resets_label" msgid="9131901102339077661">"Número de PPP restablecido desde el inicio:"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Información sobre el teléfono"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Información de servicios del operador"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Activar servicios del operador"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Sugerencia: mejorar la conectividad Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Tu señal de Bluetooth es débil. Prueba a cambiar al altavoz."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Notificación de calidad de la llamada"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Cuentas SIP obsoletas"</string>
 </resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 986525e..bdc5241 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"SIM-i piiratud funktsionaalsus"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Operaatori <xliff:g id="CARRIER_NAME">%1$s</xliff:g> kõned ja andmesideteenused võivad numbri <xliff:g id="PHONE_NUMBER">%2$s</xliff:g> kasutamise ajal olla blokeeritud."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g>: kõned ja andmesideteenused võivad teise SIM-i kasutamisel olla blokeeritud."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Leiti katkestatud toega SIP-kontod ja need eemaldati"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Androidi platvorm ei toeta enam SIP-kõnesid.\nTeie olemasolevad SIP-kontod <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> on eemaldatud.\nKinnitage oma helistamise vaikekonto seadistus."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Ava seaded"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Rakenduste andmekasutus"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"Vahemikus <xliff:g id="ID_2">%2$s</xliff:g> on kasutatud <xliff:g id="ID_1">%1$s</xliff:g> mobiilset andmesidet"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Täpsemad"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Parool on vale. SIM on nüüd lukus. Sisestage PUK-kood 2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 on püsivalt blokeeritud."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Teil on veel <xliff:g id="NUMBER">%d</xliff:g> katset."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 on lukustatud. Avamiseks võtke ühendust teenusepakkujaga."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN-kood 2 ei ole enam blokeeritud"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Võrgu või SIM-kaardi viga"</string>
     <string name="doneButton" msgid="7371209609238460207">"Valmis"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Telefoniteave"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Operaatori ettevalmistamise teave"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Operaatori ettevalmistamise päästik"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Soovitus: parandage Bluetoothi ühenduvust"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Teie Bluetoothi signaal on nõrk. Lülitage valjuhääldile."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Kõnekvaliteedi märguanne"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Katkestatud toega SIP-kontod"</string>
 </resources>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 86cd334..8f02321 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -162,14 +162,14 @@
     <string name="no_change" msgid="3737264882821031892">"Ez da aldaketarik egin."</string>
     <string name="sum_voicemail_choose_provider" msgid="6750824719081403773">"Aukeratu erantzungailu-zerbitzua"</string>
     <string name="voicemail_default" msgid="6427575113775462077">"Operadorea"</string>
-    <string name="vm_change_pin_old_pin" msgid="7154951790929009241">"PIN kode zaharra"</string>
-    <string name="vm_change_pin_new_pin" msgid="2656200418481288069">"PIN kode berria"</string>
+    <string name="vm_change_pin_old_pin" msgid="7154951790929009241">"PIN zaharra"</string>
+    <string name="vm_change_pin_new_pin" msgid="2656200418481288069">"PIN berria"</string>
     <string name="vm_change_pin_progress_message" msgid="626015184502739044">"Itxaron, mesedez."</string>
-    <string name="vm_change_pin_error_too_short" msgid="1789139338449945483">"Laburregia da PIN kode berria."</string>
-    <string name="vm_change_pin_error_too_long" msgid="3634907034310018954">"Luzeegia da PIN kode berria."</string>
-    <string name="vm_change_pin_error_too_weak" msgid="8581892952627885719">"Ez da batere segurua PIN kode berria. Pasahitza segurua izan dadin, ez du izan behar zenbaki-segidarik edo errepikatutako zenbakirik."</string>
-    <string name="vm_change_pin_error_mismatch" msgid="5364847280026257331">"PIN kode zaharra ez dator bat."</string>
-    <string name="vm_change_pin_error_invalid" msgid="5230002671175580674">"Balio ez duten karaktereak ditu PIN kode berriak."</string>
+    <string name="vm_change_pin_error_too_short" msgid="1789139338449945483">"Laburregia da PIN berria."</string>
+    <string name="vm_change_pin_error_too_long" msgid="3634907034310018954">"Luzeegia da PIN berria."</string>
+    <string name="vm_change_pin_error_too_weak" msgid="8581892952627885719">"Ez da batere segurua PIN berria. Pasahitza segurua izan dadin, ez du izan behar zenbaki-segidarik edo errepikatutako zenbakirik."</string>
+    <string name="vm_change_pin_error_mismatch" msgid="5364847280026257331">"PIN zaharra ez dator bat."</string>
+    <string name="vm_change_pin_error_invalid" msgid="5230002671175580674">"Balio ez duten karaktereak ditu PIN berriak."</string>
     <string name="vm_change_pin_error_system_error" msgid="9116483527909681791">"Ezin da aldatu PIN kodea"</string>
     <string name="vvm_unsupported_message_format" msgid="4206402558577739713">"Ez dira onartzen mota honetako mezuak. Entzuteko, deitu <xliff:g id="NUMBER">%s</xliff:g> zenbakira."</string>
     <string name="network_settings_title" msgid="7560807107123171541">"Sare mugikorra"</string>
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"SIMaren funtzionaltasuna mugatua da"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Baliteke <xliff:g id="CARRIER_NAME">%1$s</xliff:g> bidezko deiak eta datu-zerbitzuak blokeatuta egotea <xliff:g id="PHONE_NUMBER">%2$s</xliff:g> telefono-zenbakia erabiltzean."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Baliteke <xliff:g id="CARRIER_NAME">%1$s</xliff:g> bidezko deiak eta datu-zerbitzuak blokeatuta egotea beste SIM txartel bat erabiltzean."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"SIP-eko kontu zaharkituak aurkitu eta kendu dira"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP bidezko deiak jadanik ez dira bateragarriak Android-en plataformarekin.\nZeneuzkan SIP-eko kontuak (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) kendu egin dira.\nBerretsi deietarako ezarri duzun kontu lehenetsia."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Joan ezarpenetara"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Aplikazioak erabilitako datuak"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"Datuen <xliff:g id="ID_1">%1$s</xliff:g> erabili dira data hauen artean: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Aurreratuak"</string>
@@ -356,7 +359,7 @@
   <string-array name="list_language_entries">
     <item msgid="2347238508726934281">"Ingelesa"</item>
     <item msgid="5172468397620875174">"Frantsesa"</item>
-    <item msgid="3978110664146015398">"Espainiera"</item>
+    <item msgid="3978110664146015398">"Gaztelania"</item>
     <item msgid="2637764545851526369">"Japoniera"</item>
     <item msgid="6103816221834932751">"Koreera"</item>
     <item msgid="3127367370005168399">"Txinera"</item>
@@ -472,7 +475,7 @@
     <string name="delete_fdn_contact" msgid="7027405651994507077">"Ezabatu markatze finkoko zenbakia"</string>
     <string name="deleting_fdn_contact" msgid="6872320570844460428">"Markatze finkoko zenbakia ezabatzen…"</string>
     <string name="fdn_contact_deleted" msgid="1680714996763848838">"Markatze finkoko zenbakia ezabatu da."</string>
-    <string name="pin2_invalid" msgid="2313954262684494442">"Ez da eguneratu markatze finkoko zenbakia, PIN kode okerra idatzi duzulako."</string>
+    <string name="pin2_invalid" msgid="2313954262684494442">"Ez da eguneratu markatze finkoko zenbakia, PIN okerra idatzi duzulako."</string>
     <string name="fdn_invalid_number" msgid="9067189814657840439">"Ez da eguneratu markatze finkoko zenbakia, zenbakiak <xliff:g id="FDN_NUMBER_LIMIT_LENGTH">%d</xliff:g> digitu baino gehiago dituelako."</string>
     <string name="pin2_or_fdn_invalid" msgid="7542639487955868181">"Ez da eguneratu markatze finkoko zenbakia. PIN2 kodea ez da zuzena edo telefono-zenbakia baztertu da."</string>
     <string name="fdn_failed" msgid="216592346853420250">"Markatze finkoko zenbakiaren eragiketak huts egin du."</string>
@@ -485,8 +488,8 @@
     <string name="enter_pin_text" msgid="3182311451978663356">"SIM txartelaren PIN kodea:"</string>
     <string name="oldPinLabel" msgid="8618515202411987721">"PIN zaharra"</string>
     <string name="newPinLabel" msgid="3585899083055354732">"PIN berria"</string>
-    <string name="confirmPinLabel" msgid="7783531218662473778">"Berretsi PIN kode berria"</string>
-    <string name="badPin" msgid="4549286285015892321">"Idatzi duzun PIN kode zaharra ez da zuzena. Saiatu berriro."</string>
+    <string name="confirmPinLabel" msgid="7783531218662473778">"Berretsi PIN berria"</string>
+    <string name="badPin" msgid="4549286285015892321">"Idatzi duzun PIN zaharra ez da zuzena. Saiatu berriro."</string>
     <string name="mismatchPin" msgid="1467254768290323845">"Idatzi dituzun PIN kodeak ez datoz bat. Saiatu berriro."</string>
     <string name="invalidPin" msgid="7363723429414001979">"Idatzi 4 eta 8 zenbaki arteko PIN kodea."</string>
     <string name="disable_sim_pin" msgid="3112303905548613752">"Garbitu SIM txartelaren PIN kodea"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Pasahitza ez da zuzena. SIM txartela blokeatu egin da. Idatzi PUK2 kodea."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 kodea betiko blokeatu da."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"<xliff:g id="NUMBER">%d</xliff:g> saiakera gelditzen zaizkizu."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 kodearekin blokeatuta dago SIM txartela. Desblokeatzeko, jarri operadorearekin harremanetan."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 kodea desblokeatu da"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Sarearen edo SIM txartelaren errorea"</string>
     <string name="doneButton" msgid="7371209609238460207">"Eginda"</string>
@@ -550,7 +554,7 @@
     <string name="incall_error_supp_service_hangup" msgid="836524952243836735">"Ezin dira deiak bereizi."</string>
     <string name="incall_error_supp_service_hold" msgid="8535056414643540997">"Ezin dira zain utzi deiak."</string>
     <string name="incall_error_wfc_only_no_wireless_network" msgid="5860742792811400109">"Deia egiteko, konektatu haririk gabeko sare batera."</string>
-    <string name="incall_error_promote_wfc" msgid="9164896813931363415">"Deia egiteko, gaitu Wi-Fi bidezko deiak."</string>
+    <string name="incall_error_promote_wfc" msgid="9164896813931363415">"Deia egiteko, gaitu wifi bidezko deiak."</string>
     <string name="emergency_information_hint" msgid="9208897544917793012">"Larrialdietarako informazioa"</string>
     <string name="emergency_information_owner_hint" msgid="6256909888049185316">"Jabea"</string>
     <string name="emergency_information_confirm_hint" msgid="5109017615894918914">"Informazioa ikusteko, sakatu berriro"</string>
@@ -692,12 +696,12 @@
     <string name="change_pin_continue_label" msgid="5177011752453506371">"Egin aurrera"</string>
     <string name="change_pin_cancel_label" msgid="2301711566758827936">"Utzi"</string>
     <string name="change_pin_ok_label" msgid="6861082678817785330">"Ados"</string>
-    <string name="change_pin_enter_old_pin_header" msgid="853151335217594829">"Berretsi PIN kode zaharra"</string>
+    <string name="change_pin_enter_old_pin_header" msgid="853151335217594829">"Berretsi PIN zaharra"</string>
     <string name="change_pin_enter_old_pin_hint" msgid="8801292976275169367">"Idatzi erantzungailuaren PIN kodea jarraitzeko."</string>
-    <string name="change_pin_enter_new_pin_header" msgid="4739465616733486118">"Ezarri PIN kode berria"</string>
+    <string name="change_pin_enter_new_pin_header" msgid="4739465616733486118">"Ezarri PIN berri bat"</string>
     <string name="change_pin_enter_new_pin_hint" msgid="2326038476516364210">"<xliff:g id="MIN">%1$d</xliff:g> eta <xliff:g id="MAX">%2$d</xliff:g> digituren artean izan behar ditu PIN kodeak."</string>
     <string name="change_pin_confirm_pin_header" msgid="2606303906320705726">"Berretsi PIN kodea"</string>
-    <string name="change_pin_confirm_pins_dont_match" msgid="305164501222587215">"Ez datoz bat PIN kodeak"</string>
+    <string name="change_pin_confirm_pins_dont_match" msgid="305164501222587215">"PINak ez datoz bat"</string>
     <string name="change_pin_succeeded" msgid="2504705600693014403">"Eguneratu da erantzungailuaren PIN kodea"</string>
     <string name="change_pin_system_error" msgid="7772788809875146873">"Ezin da ezarri PIN kodea"</string>
     <string name="mobile_data_status_roaming_turned_off_subtext" msgid="6840673347416227054">"Desaktibatuta daude ibiltaritzako datuak"</string>
@@ -871,7 +875,7 @@
     <string name="radioInfo_display_asu" msgid="2247752203249646956">"asu"</string>
     <string name="radioInfo_lac" msgid="3892986460272607013">"LAC"</string>
     <string name="radioInfo_cid" msgid="1423185536264406705">"CID"</string>
-    <string name="radio_info_subid" msgid="6839966868621703203">"Uneko azpiIDa:"</string>
+    <string name="radio_info_subid" msgid="6839966868621703203">"Oraingo azpiIDa:"</string>
     <string name="radio_info_dds" msgid="1122593144425697126">"Datu-konexioetarako SIM lehenetsiaren azpiIDa:"</string>
     <string name="radio_info_dl_kbps" msgid="2382922659525318726">"Deskargatzeko banda-zabalera (Kb/s):"</string>
     <string name="radio_info_ul_kbps" msgid="2102225400904799036">"Kargen banda-zabalera (Kb/s):"</string>
@@ -883,7 +887,7 @@
     <string name="radio_info_imei_label" msgid="8947899706930120368">"IMEI:"</string>
     <string name="radio_info_call_redirect_label" msgid="4526480903023362276">"Dei-desbideratzea:"</string>
     <string name="radio_info_ppp_resets_label" msgid="9131901102339077661">"PPP berrezarpen kopurua abiarazi ezkero:"</string>
-    <string name="radio_info_current_network_label" msgid="3052098695239642450">"Uneko sarea:"</string>
+    <string name="radio_info_current_network_label" msgid="3052098695239642450">"Oraingo sarea:"</string>
     <string name="radio_info_ppp_received_label" msgid="5753592451640644889">"Jasotako datuak:"</string>
     <string name="radio_info_gsm_service_label" msgid="6443348321714241328">"Ahots-deien zerbitzua:"</string>
     <string name="radio_info_signal_strength_label" msgid="5545444702102543260">"Seinalearen indarra:"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Telefonoaren informazioa"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Operadorearen hornikuntza-informazioa"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Abiarazi operadorearen hornikuntza"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Iradokizuna: hobetu Bluetooth-konexioa"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth seinalea ahula da. Erabili telefonoko bozgorailua."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Deien kalitateari buruzko jakinarazpena"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"SIP-eko kontu zaharkituak"</string>
 </resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 60c0de1..e64f3bc 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -186,9 +186,9 @@
     <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>
-    <string name="preferred_network_mode_title" msgid="5253395265169539830">"نوع شبکه برگزیده"</string>
+    <string name="preferred_network_mode_title" msgid="5253395265169539830">"نوع شبکه ترجیحی"</string>
     <string name="preferred_network_mode_summary" msgid="3787989000044330064">"تغییر حالت عملکرد شبکه"</string>
-    <string name="preferred_network_mode_dialogtitle" msgid="2781447433514459696">"نوع شبکه برگزیده"</string>
+    <string name="preferred_network_mode_dialogtitle" msgid="2781447433514459696">"نوع شبکه ترجیحی"</string>
     <string name="forbidden_network" msgid="5081729819561333023">"(ممنوع است)"</string>
     <string name="choose_network_title" msgid="5335832663422653082">"انتخاب شبکه"</string>
     <string name="network_disconnected" msgid="8844141106841160825">"قطع اتصال"</string>
@@ -231,44 +231,44 @@
     <item msgid="3869566732842046032">"NR/LTE/TDSCDMA/GSM/WCDMA"</item>
     <item msgid="3942770927563146543">"NR/LTE/TDSCDMA/CDMA/EvDo/GSM/WCDMA"</item>
   </string-array>
-    <string name="preferred_network_mode_wcdma_perf_summary" msgid="7851493369130750126">"‏حالت شبکه برگزیده: WCDMA برگزیده"</string>
-    <string name="preferred_network_mode_gsm_only_summary" msgid="4323367929994392830">"‏حالت شبکه برگزیده: فقط GSM"</string>
-    <string name="preferred_network_mode_wcdma_only_summary" msgid="3585482191951442207">"‏حالت شبکه برگزیده: فقط WCDMA"</string>
-    <string name="preferred_network_mode_gsm_wcdma_summary" msgid="2988950751948316810">"‏حالت شبکه برگزیده: GSM / WCDMA"</string>
+    <string name="preferred_network_mode_wcdma_perf_summary" msgid="7851493369130750126">"‏حالت شبکه ترجیحی: WCDMA برگزیده"</string>
+    <string name="preferred_network_mode_gsm_only_summary" msgid="4323367929994392830">"‏حالت شبکه ترجیحی: فقط GSM"</string>
+    <string name="preferred_network_mode_wcdma_only_summary" msgid="3585482191951442207">"‏حالت شبکه ترجیحی: فقط WCDMA"</string>
+    <string name="preferred_network_mode_gsm_wcdma_summary" msgid="2988950751948316810">"‏حالت شبکه ترجیحی: GSM / WCDMA"</string>
     <string name="preferred_network_mode_cdma_summary" msgid="9127141320343936911">"‏حالت شبکه ترجیحی: CDMA"</string>
     <string name="preferred_network_mode_cdma_evdo_summary" msgid="3629440709757307077">"‏حالت شبکه ترجیحی: CDMA / EvDo"</string>
-    <string name="preferred_network_mode_cdma_only_summary" msgid="211164451887102568">"‏حالت شبکه برگزیده: فقط CDMA"</string>
-    <string name="preferred_network_mode_evdo_only_summary" msgid="939116631952132878">"‏حالت شبکه برگزیده: فقط EvDo"</string>
-    <string name="preferred_network_mode_cdma_evdo_gsm_wcdma_summary" msgid="7891131456022601976">"‏حالت شبکه برگزیده: CDMA/EvDo/GSM/WCDMA"</string>
-    <string name="preferred_network_mode_lte_summary" msgid="8050539466545797149">"‏حالت شبکه برگزیده: LTE"</string>
-    <string name="preferred_network_mode_lte_gsm_wcdma_summary" msgid="2217794334331254936">"‏حالت شبکه برگزیده: GSM/WCDMA/LTE"</string>
-    <string name="preferred_network_mode_lte_cdma_evdo_summary" msgid="5559198623419981805">"‏حالت شبکه برگزیده: CDMA+LTE/EVDO"</string>
-    <string name="preferred_network_mode_lte_cdma_evdo_gsm_wcdma_summary" msgid="6707224437925495615">"‏حالت شبکه برگزیده: LTE/CDMA/EvDo/GSM/WCDMA"</string>
-    <string name="preferred_network_mode_global_summary" msgid="3847086258439582411">"حالت شبکه برگزیده: سراسری"</string>
-    <string name="preferred_network_mode_lte_wcdma_summary" msgid="7001804022020813865">"‏حالت شبکه برگزیده: LTE / WCDMA"</string>
-    <string name="preferred_network_mode_lte_gsm_umts_summary" msgid="6484203890156282179">"‏حالت شبکه برگزیده: LTE / GSM / UMTS"</string>
-    <string name="preferred_network_mode_lte_cdma_summary" msgid="8187929456614068518">"‏حالت شبکه برگزیده: LTE / CDMA"</string>
-    <string name="preferred_network_mode_tdscdma_summary" msgid="3602127224234207206">"‏حالت شبکه برگزیده: TDSCDMA"</string>
-    <string name="preferred_network_mode_tdscdma_wcdma_summary" msgid="7076968749402201123">"‏حالت شبکه برگزیده: TDSCDMA / WCDMA"</string>
-    <string name="preferred_network_mode_lte_tdscdma_summary" msgid="3001058390866953624">"‏حالت شبکه برگزیده: LTE / TDSCDMA"</string>
-    <string name="preferred_network_mode_tdscdma_gsm_summary" msgid="1716983444872465309">"‏حالت شبکه برگزیده: TDSCDMA / GSM"</string>
-    <string name="preferred_network_mode_lte_tdscdma_gsm_summary" msgid="1349057007230669585">"‏حالت شبکه برگزیده: LTE/GSM/TDSCDMA"</string>
-    <string name="preferred_network_mode_tdscdma_gsm_wcdma_summary" msgid="2092262901885164194">"‏حالت شبکه برگزیده: TDSCDMA/GSM/WCDMA"</string>
-    <string name="preferred_network_mode_lte_tdscdma_wcdma_summary" msgid="56422129430744466">"‏حالت شبکه برگزیده: LTE/TDSCDMA/WCDMA"</string>
-    <string name="preferred_network_mode_lte_tdscdma_gsm_wcdma_summary" msgid="2993923113350341106">"‏حالت شبکه برگزیده: LTE/TDSCDMA/GSM/WCDMA"</string>
-    <string name="preferred_network_mode_tdscdma_cdma_evdo_gsm_wcdma_summary" msgid="2779089629254220257">"‏حالت شبکه برگزیده: TDSCDMA/CDMA/EvDo/GSM/WCDMA"</string>
-    <string name="preferred_network_mode_lte_tdscdma_cdma_evdo_gsm_wcdma_summary" msgid="9065672185435798587">"‏حالت شبکه برگزیده: LTE/TDSCDMA/CDMA/EvDo/GSM/WCDMA"</string>
-    <string name="preferred_network_mode_nr_only_summary" msgid="1467452233297987391">"‏حالت شبکه برگزیده: فقط NR"</string>
-    <string name="preferred_network_mode_nr_lte_summary" msgid="5890170406507535976">"‏حالت شبکه برگزیده: NR / LTE"</string>
-    <string name="preferred_network_mode_nr_lte_cdma_evdo_summary" msgid="5507940227264296616">"‏حالت شبکه برگزیده: NR/LTE/CDMA/EvDo"</string>
-    <string name="preferred_network_mode_nr_lte_gsm_wcdma_summary" msgid="2811179121638665248">"‏حالت شبکه برگزیده: NR/LTE/GSM/WCDMA"</string>
-    <string name="preferred_network_mode_nr_lte_cdma_evdo_gsm_wcdma_summary" msgid="7631365223836621902">"‏حالت شبکه برگزیده: NR/LTE/CDMA/EvDo/GSM/WCDMA"</string>
-    <string name="preferred_network_mode_nr_lte_wcdma_summary" msgid="8696016062943591864">"‏حالت شبکه برگزیده: NR/LTE/WCDMA"</string>
-    <string name="preferred_network_mode_nr_lte_tdscdma_summary" msgid="1236182344680726751">"‏حالت شبکه برگزیده: NR/LTE/TDSCDMA"</string>
-    <string name="preferred_network_mode_nr_lte_tdscdma_gsm_summary" msgid="8384454155773415993">"‏حالت شبکه برگزیده: NR/LTE/TDSCDMA/GSM"</string>
-    <string name="preferred_network_mode_nr_lte_tdscdma_wcdma_summary" msgid="5912457779733343522">"‏حالت شبکه برگزیده: NR/LTE/TDSCDMA/WCDMA"</string>
-    <string name="preferred_network_mode_nr_lte_tdscdma_gsm_wcdma_summary" msgid="6769797110309412576">"‏حالت شبکه برگزیده: NR/LTE/TDSCDMA/GSM/WCDMA"</string>
-    <string name="preferred_network_mode_nr_lte_tdscdma_cdma_evdo_gsm_wcdma_summary" msgid="4260661428277578573">"‏حالت شبکه برگزیده: NR/LTE/TDSCDMA/CDMA/EvDo/GSM/WCDMA"</string>
+    <string name="preferred_network_mode_cdma_only_summary" msgid="211164451887102568">"‏حالت شبکه ترجیحی: فقط CDMA"</string>
+    <string name="preferred_network_mode_evdo_only_summary" msgid="939116631952132878">"‏حالت شبکه ترجیحی: فقط EvDo"</string>
+    <string name="preferred_network_mode_cdma_evdo_gsm_wcdma_summary" msgid="7891131456022601976">"‏حالت شبکه ترجیحی: CDMA/EvDo/GSM/WCDMA"</string>
+    <string name="preferred_network_mode_lte_summary" msgid="8050539466545797149">"‏حالت شبکه ترجیحی: LTE"</string>
+    <string name="preferred_network_mode_lte_gsm_wcdma_summary" msgid="2217794334331254936">"‏حالت شبکه ترجیحی: GSM/WCDMA/LTE"</string>
+    <string name="preferred_network_mode_lte_cdma_evdo_summary" msgid="5559198623419981805">"‏حالت شبکه ترجیحی: CDMA+LTE/EVDO"</string>
+    <string name="preferred_network_mode_lte_cdma_evdo_gsm_wcdma_summary" msgid="6707224437925495615">"‏حالت شبکه ترجیحی: LTE/CDMA/EvDo/GSM/WCDMA"</string>
+    <string name="preferred_network_mode_global_summary" msgid="3847086258439582411">"حالت شبکه ترجیحی: سراسری"</string>
+    <string name="preferred_network_mode_lte_wcdma_summary" msgid="7001804022020813865">"‏حالت شبکه ترجیحی: LTE / WCDMA"</string>
+    <string name="preferred_network_mode_lte_gsm_umts_summary" msgid="6484203890156282179">"‏حالت شبکه ترجیحی: LTE / GSM / UMTS"</string>
+    <string name="preferred_network_mode_lte_cdma_summary" msgid="8187929456614068518">"‏حالت شبکه ترجیحی: LTE / CDMA"</string>
+    <string name="preferred_network_mode_tdscdma_summary" msgid="3602127224234207206">"‏حالت شبکه ترجیحی: TDSCDMA"</string>
+    <string name="preferred_network_mode_tdscdma_wcdma_summary" msgid="7076968749402201123">"‏حالت شبکه ترجیحی: TDSCDMA / WCDMA"</string>
+    <string name="preferred_network_mode_lte_tdscdma_summary" msgid="3001058390866953624">"‏حالت شبکه ترجیحی: LTE / TDSCDMA"</string>
+    <string name="preferred_network_mode_tdscdma_gsm_summary" msgid="1716983444872465309">"‏حالت شبکه ترجیحی: TDSCDMA / GSM"</string>
+    <string name="preferred_network_mode_lte_tdscdma_gsm_summary" msgid="1349057007230669585">"‏حالت شبکه ترجیحی: LTE/GSM/TDSCDMA"</string>
+    <string name="preferred_network_mode_tdscdma_gsm_wcdma_summary" msgid="2092262901885164194">"‏حالت شبکه ترجیحی: TDSCDMA/GSM/WCDMA"</string>
+    <string name="preferred_network_mode_lte_tdscdma_wcdma_summary" msgid="56422129430744466">"‏حالت شبکه ترجیحی: LTE/TDSCDMA/WCDMA"</string>
+    <string name="preferred_network_mode_lte_tdscdma_gsm_wcdma_summary" msgid="2993923113350341106">"‏حالت شبکه ترجیحی: LTE/TDSCDMA/GSM/WCDMA"</string>
+    <string name="preferred_network_mode_tdscdma_cdma_evdo_gsm_wcdma_summary" msgid="2779089629254220257">"‏حالت شبکه ترجیحی: TDSCDMA/CDMA/EvDo/GSM/WCDMA"</string>
+    <string name="preferred_network_mode_lte_tdscdma_cdma_evdo_gsm_wcdma_summary" msgid="9065672185435798587">"‏حالت شبکه ترجیحی: LTE/TDSCDMA/CDMA/EvDo/GSM/WCDMA"</string>
+    <string name="preferred_network_mode_nr_only_summary" msgid="1467452233297987391">"‏حالت شبکه ترجیحی: فقط NR"</string>
+    <string name="preferred_network_mode_nr_lte_summary" msgid="5890170406507535976">"‏حالت شبکه ترجیحی: NR / LTE"</string>
+    <string name="preferred_network_mode_nr_lte_cdma_evdo_summary" msgid="5507940227264296616">"‏حالت شبکه ترجیحی: NR/LTE/CDMA/EvDo"</string>
+    <string name="preferred_network_mode_nr_lte_gsm_wcdma_summary" msgid="2811179121638665248">"‏حالت شبکه ترجیحی: NR/LTE/GSM/WCDMA"</string>
+    <string name="preferred_network_mode_nr_lte_cdma_evdo_gsm_wcdma_summary" msgid="7631365223836621902">"‏حالت شبکه ترجیحی: NR/LTE/CDMA/EvDo/GSM/WCDMA"</string>
+    <string name="preferred_network_mode_nr_lte_wcdma_summary" msgid="8696016062943591864">"‏حالت شبکه ترجیحی: NR/LTE/WCDMA"</string>
+    <string name="preferred_network_mode_nr_lte_tdscdma_summary" msgid="1236182344680726751">"‏حالت شبکه ترجیحی: NR/LTE/TDSCDMA"</string>
+    <string name="preferred_network_mode_nr_lte_tdscdma_gsm_summary" msgid="8384454155773415993">"‏حالت شبکه ترجیحی: NR/LTE/TDSCDMA/GSM"</string>
+    <string name="preferred_network_mode_nr_lte_tdscdma_wcdma_summary" msgid="5912457779733343522">"‏حالت شبکه ترجیحی: NR/LTE/TDSCDMA/WCDMA"</string>
+    <string name="preferred_network_mode_nr_lte_tdscdma_gsm_wcdma_summary" msgid="6769797110309412576">"‏حالت شبکه ترجیحی: NR/LTE/TDSCDMA/GSM/WCDMA"</string>
+    <string name="preferred_network_mode_nr_lte_tdscdma_cdma_evdo_gsm_wcdma_summary" msgid="4260661428277578573">"‏حالت شبکه ترجیحی: NR/LTE/TDSCDMA/CDMA/EvDo/GSM/WCDMA"</string>
     <string name="call_category" msgid="4394703838833058138">"درحال تماس"</string>
     <string name="network_operator_category" msgid="4992217193732304680">"شبکه"</string>
     <string name="enhanced_4g_lte_mode_title" msgid="4213420368777080540">"‏حالت پیشرفته 4G LTE"</string>
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"عملکرد محدود سیم‌کارت"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"ممکن است درحین استفاده از <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>، سرویس‌های داده و تماس <xliff:g id="CARRIER_NAME">%1$s</xliff:g> مسدود شوند."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"ممکن است درحین استفاده از سیم‌کارت دیگر، سرویس‌های داده و تماس <xliff:g id="CARRIER_NAME">%1$s</xliff:g> مسدود شوند."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"‏حساب‌های SIP منسوخ‌شده پیدا و برداشته شدند"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"‏تماس SIP دیگر در پلاتفرم Android پشتیبانی نمی‌شود.\nحساب‌های SIP موجود شما (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) برداشته شدند.\nلطفاً تنظیم حساب تماس پیش‌فرض را تأیید کنید."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"رفتن به تنظیمات"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"مصرف داده برنامه"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> داده تلفن همراه مصرف‌شده در <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"پیشرفته"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"‏گذرواژه نادرست است. اکنون سیم‌کارت قفل شده است. PUK2 را وارد کنید."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"‏PUK2 به صورت دائمی مسدود شده است."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"<xliff:g id="NUMBER">%d</xliff:g> تلاش باقی‌مانده دارید."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"‏PUK2 قفل است. برای باز کردن قفل، با ارائه دهنده خدمات تماس بگیرید."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"پین۲ دیگر مسدود نیست"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"خطای شبکه یا سیم‌کارت"</string>
     <string name="doneButton" msgid="7371209609238460207">"تمام"</string>
@@ -895,7 +899,7 @@
     <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>
-    <string name="radio_info_set_perferred_label" msgid="7408131389363136210">"تنظیم نوع شبکه برگزیده:"</string>
+    <string name="radio_info_set_perferred_label" msgid="7408131389363136210">"تنظیم نوع شبکه ترجیحی:"</string>
     <string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"‏پینگ کردن نام میزبان (www.google.com)‏ IPv4:"</string>
     <string name="radio_info_ping_hostname_v6" msgid="2748637889486554603">"‏پینگ کردن نام میزبان (www.google.com)‏ IPv6:"</string>
     <string name="radio_info_http_client_test" msgid="1329583721088428238">"‏آزمایش کارخواه HTTP:"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"اطلاعات تلفن"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"اطلاعات ارائه شرکت مخابراتی"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"شروع ارائه شرکت مخابراتی"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"پیشنهاد: اتصال بلوتوث را بهبود دهید"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"سیگنال بلوتوث شما ضعیف است. از بلندگوی تلفن استفاده کنید."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"اعلان کیفیت تماس"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"‏حساب‌های SIP منسوخ‌شده"</string>
 </resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 495d5fa..819186e 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -30,7 +30,7 @@
     <string name="mmiStarted" msgid="9212975136944568623">"MMI-koodi aloitettu"</string>
     <string name="ussdRunning" msgid="1163586813106772717">"USSD-koodi käytössä..."</string>
     <string name="mmiCancelled" msgid="5339191899200678272">"MMI-koodi peruutettu"</string>
-    <string name="cancel" msgid="8984206397635155197">"Peruuta"</string>
+    <string name="cancel" msgid="8984206397635155197">"Peru"</string>
     <string name="enter_input" msgid="6193628663039958990">"USSD-viestin pituuden täytyy olla <xliff:g id="MIN_LEN">%1$d</xliff:g>–<xliff:g id="MAX_LEN">%2$d</xliff:g> merkkiä. Yritä uudelleen."</string>
     <string name="manageConferenceLabel" msgid="8415044818156353233">"Hallinnoi puhelinneuvottelua"</string>
     <string name="ok" msgid="7818974223666140165">"OK"</string>
@@ -128,7 +128,7 @@
     <string name="cdma_call_waiting" msgid="4565070960879673216">"Otetaanko koputus käyttöön?"</string>
     <string name="enable_cdma_call_waiting_setting" msgid="5906811747921744307">"Sinulle ilmoitetaan puhelun aikana saapuvista puheluista"</string>
     <string name="enable_cdma_cw" msgid="811047045863422232">"Ota käyttöön"</string>
-    <string name="disable_cdma_cw" msgid="7119290446496301734">"Peruuta"</string>
+    <string name="disable_cdma_cw" msgid="7119290446496301734">"Peru"</string>
     <string name="cdma_call_waiting_in_ims_on" msgid="6390979414188659218">"IMS:n alainen CDMA-koputus pois käytöstä"</string>
     <string name="cdma_call_waiting_in_ims_off" msgid="1099246114368636334">"IMS:n alainen CDMA-koputus pois käytöstä"</string>
     <string name="updating_title" msgid="6130548922615719689">"Puheluasetukset"</string>
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Rajoituksia SIM-kortin toiminnassa"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Operaattorin <xliff:g id="CARRIER_NAME">%1$s</xliff:g> puhelut ja datapalvelut saatetaan estää, kun puhelinnumero <xliff:g id="PHONE_NUMBER">%2$s</xliff:g> on käytössä."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Operaattorin <xliff:g id="CARRIER_NAME">%1$s</xliff:g> puhelut ja datapalvelut voidaan estää, kun käytössä on toinen SIM."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Käytöstä poistettuja SIP-tilejä löytyi ja ne poistettiin"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Android-alusta ei enää tue SIP-puheluja.\nAiemmat SIP-tilit <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> on poistettu.\nVahvista oletusarvoisen puhelutilin asetus."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Avaa asetukset"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Sovelluksen datan käyttö"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> mobiilidataa käytetty <xliff:g id="ID_2">%2$s</xliff:g>."</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Lisäasetukset"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Virheellinen salasana. SIM-kortti on lukittu. Anna PUK2-koodi."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2-koodi on pysyvästi estetty."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"<xliff:g id="NUMBER">%d</xliff:g> yritystä jäljellä."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 lukittu. Ota yhteyttä palveluntarjoajaan lukituksen avaamiseksi."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2-koodi ei ole enää estetty"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Verkko- tai SIM-korttivirhe"</string>
     <string name="doneButton" msgid="7371209609238460207">"Valmis"</string>
@@ -690,7 +694,7 @@
     <string name="callFailed_wfc_service_not_available_in_this_location" msgid="3624536608369524988">"Wi-Fi-puheluja ei voi soittaa tässä paikassa."</string>
     <string name="change_pin_title" msgid="3564254326626797321">"Vaihda vastaajan PIN-koodi."</string>
     <string name="change_pin_continue_label" msgid="5177011752453506371">"Jatka"</string>
-    <string name="change_pin_cancel_label" msgid="2301711566758827936">"Peruuta"</string>
+    <string name="change_pin_cancel_label" msgid="2301711566758827936">"Peru"</string>
     <string name="change_pin_ok_label" msgid="6861082678817785330">"OK"</string>
     <string name="change_pin_enter_old_pin_header" msgid="853151335217594829">"Vahvista vanha PIN-koodi."</string>
     <string name="change_pin_enter_old_pin_hint" msgid="8801292976275169367">"Jatka antamalla vastaajasi PIN-koodi."</string>
@@ -768,8 +772,8 @@
     <string name="clh_callFailed_protocol_Error_unspecified_txt" msgid="9203320572562697755">"Soittaminen epäonnistui. Virhekoodi 111."</string>
     <string name="clh_callFailed_interworking_unspecified_txt" msgid="7969686413930847182">"Soittaminen epäonnistui. Virhekoodi 127."</string>
     <string name="labelCallBarring" msgid="4180377113052853173">"Puhelujen esto"</string>
-    <string name="sum_call_barring_enabled" msgid="5184331188926370824">"Käytössä"</string>
-    <string name="sum_call_barring_disabled" msgid="5699448000600153096">"Pois käytöstä"</string>
+    <string name="sum_call_barring_enabled" msgid="5184331188926370824">"Päällä"</string>
+    <string name="sum_call_barring_disabled" msgid="5699448000600153096">"Pois päältä"</string>
     <string name="call_barring_baoc" msgid="7400892586336429326">"Kaikki lähtevät"</string>
     <string name="call_barring_baoc_enabled" msgid="3131509193386668182">"Poistetaanko kaikkien lähtevien puheluiden esto käytöstä?"</string>
     <string name="call_barring_baoc_disabled" msgid="8534224684091141509">"Estetäänkö kaikki lähtevät puhelut?"</string>
@@ -839,7 +843,7 @@
     <string name="dsds_dialog_title" msgid="8494569893941847575">"Käynnistä uudelleen?"</string>
     <string name="dsds_dialog_message" msgid="4047480385678538850">"Laite on käynnistettävä uudelleen asetuksen muuttamiseksi."</string>
     <string name="dsds_dialog_confirm" msgid="9032004888134129885">"Käynnistä uudelleen"</string>
-    <string name="dsds_dialog_cancel" msgid="3245958947099586655">"Peruuta"</string>
+    <string name="dsds_dialog_cancel" msgid="3245958947099586655">"Peru"</string>
     <string name="radio_info_radio_power" msgid="8805595022160471587">"Mobiiliradion voimakkuus"</string>
     <string name="radioInfo_menu_viewADN" msgid="4533179730908559846">"Näytä SIM-kortin osoitekirja"</string>
     <string name="radioInfo_menu_viewFDN" msgid="1847236480527032061">"Näytä sallitut numerot"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Puhelimen tiedot"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Operaattorin hallintatiedot"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Ota operaattorin hallinta käyttöön"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Ehdotus: Paranna Bluetooth-yhteyttä"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth-signaali on heikko. Kokeile vaihtaa kaiutinpuhelimeen."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Puhelun laatua koskeva ilmoitus"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Käytöstä poistetut SIP-tilit"</string>
 </resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index c81450a..a35f8dd 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Fonctionnalité de carte SIM limitée"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Les services d\'appels et de données de <xliff:g id="CARRIER_NAME">%1$s</xliff:g> peuvent être bloqués lors de l\'utilisation du numéro <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Les données et appels de <xliff:g id="CARRIER_NAME">%1$s</xliff:g> peuvent être bloqués si vous utilisez une autre SIM."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Comptes SIP obsolètes trouvés et retirés"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Les appels SIP ne sont plus pris en charge par la plateforme Android.\nVos comptes SIP <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> existants ont été retirés.\nVeuillez confirmer votre paramètre de compte d\'appel par défaut."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Accéder aux paramètres"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Utilisation des données des applications"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> de données mobiles utilisées pendant la période suivante : <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Avancés"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Le mot de passe est erroné. La carte SIM a été verrouillée. Entrez  le code PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"Le code PUK2 est bloqué définitivement."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Il vous reste <xliff:g id="NUMBER">%d</xliff:g> tentatives."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"Verrouillage PUK2. Communiquez avec le fournisseur de services pour procéder au déverrouillage."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"Le NIP2 n\'est plus bloqué."</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Erreur de réseau ou de carte SIM"</string>
     <string name="doneButton" msgid="7371209609238460207">"Terminé"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Informations sur le téléphone"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Données d\'approvisionnement du fournisseur de services"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Déclencher l\'approvisionnement du fournisseur de services"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Suggestion : améliorer la connectivité Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Votre signal Bluetooth est faible. Essayez de passer au haut-parleur mains libres."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Notification de qualité d\'appel"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Comptes SIP obsolètes"</string>
 </resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 9b83fa8..a056294 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Fonction de la carte SIM limitée"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Les appels et les services de données disponibles avec <xliff:g id="CARRIER_NAME">%1$s</xliff:g> sont susceptibles d\'être bloqués lorsque vous utilisez le numéro <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Les appels et services de données <xliff:g id="CARRIER_NAME">%1$s</xliff:g> peuvent être bloqués avec une deuxième SIM."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Des comptes SIP obsolètes ont été trouvés et supprimés"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Les appels SIP ne sont plus compatibles avec la plate-forme Android.\nVos comptes SIP existants, <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>, ont été supprimés.\nVeuillez confirmer le compte utilisé par défaut pour passer les appels."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Accéder aux paramètres"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Consommation des données par les applications"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> de données mobiles utilisées pendant la période suivante : <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Paramètres avancés"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Le mot de passe est erroné. La carte SIM est désormais verrouillée. Veuillez saisir le code PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"La clé PUK2 est bloquée de manière définitive."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Il vous reste <xliff:g id="NUMBER">%d</xliff:g> tentatives."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"Verrouillage par clé PUK2. Contactez votre fournisseur de services pour déverrouiller la carte SIM."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"Le code PIN2 n\'est plus bloqué."</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Erreur de réseau ou de carte SIM."</string>
     <string name="doneButton" msgid="7371209609238460207">"OK"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Informations sur le téléphone"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Informations de gestion des comptes opérateur"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Déclencher la gestion des comptes opérateur"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Suggestion : Améliorez la connectivité Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Votre signal Bluetooth est faible. Essayez d\'utiliser le haut-parleur."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Notification concernant la qualité de l\'appel"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Comptes SIP obsolètes"</string>
 </resources>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 9aae097..2f6a0e3 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Funcionalidade da SIM limitada"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Pode que as chamadas e os servizos de datos de <xliff:g id="CARRIER_NAME">%1$s</xliff:g> se bloqueen cando se use o <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Pode que as chamadas e os servizos de datos de <xliff:g id="CARRIER_NAME">%1$s</xliff:g> se bloqueen ao usar outra SIM."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Quitáronse as contas SIP obsoletas que se atoparon"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Xa non se permiten as chamadas SIP na plataforma Android.\nQuitáronse as túas contas SIP (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>).\nConfirma a configuración predeterminada da conta para as chamadas."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Ir á configuración"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Uso de datos da aplicación"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"Datos móbiles usados no período do <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">"Configuración avanzada"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Contrasinal incorrecto. A SIM está bloqueada agora. Introduce o PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"O PUK2 está permanentemente bloqueado."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Quédanche <xliff:g id="NUMBER">%d</xliff:g> intentos."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 bloqueado. Contacta co fornecedor de servizo para desbloquealo."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"O PIN2 xa non está bloqueado"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Erro de rede ou de tarxeta SIM"</string>
     <string name="doneButton" msgid="7371209609238460207">"Feito"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Información do teléfono"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Información de aprovisionamento do operador"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Aprovisionamento do operador do activador"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Suxestión: Mellora a conectividade de Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"O teu sinal de Bluetooth é feble. Proba a cambiar ao altofalante."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Notificación sobre a calidade da chamada"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Contas SIP obsoletas"</string>
 </resources>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 0b655ad..d9390e6 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"સિમની મર્યાદિત કાર્યક્ષમતા"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g>નો ઉપયોગ કરતી વખતે <xliff:g id="CARRIER_NAME">%1$s</xliff:g>ના કૉલ અને ડેટાની સેવાઓ બ્લૉક થઈ શકે છે."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"બીજા સિમનો ઉપયોગ કરતી વખતે <xliff:g id="CARRIER_NAME">%1$s</xliff:g>ના કૉલ અને ડેટાની સેવાઓ બ્લૉક થઈ શકે છે."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"ટાળવામાં આવેલા SIP એકાઉન્ટ મળી આવ્યા અને તેને કાઢી નાખ્યા"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP કૉલિંગની સુવિધાને હવે Android પ્લૅટફૉર્મ દ્વારા સપોર્ટ આપવામાં આવતો નથી.\n તમારા વર્તમાન SIP એકાઉન્ટ <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> કાઢી નાખવામાં આવ્યા છે.\nકૃપા કરીને તમારા ડિફૉલ્ટ કૉલિંગ એકાઉન્ટનું સેટિંગ કન્ફર્મ કરો."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"સેટિંગ પર જાઓ"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"ઍપ ડેટા વપરાશ"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> મોબાઇલ ડેટાનો ઉપયોગ <xliff:g id="ID_2">%2$s</xliff:g> ના રોજ સુધી કરવામાં આવ્યો"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"વિગતવાર"</string>
@@ -313,14 +316,14 @@
     <string name="throttle_data_usage" msgid="1944145350660420711">"ડેટા વપરાશ"</string>
     <string name="throttle_current_usage" msgid="7483859109708658613">"વર્તમાન અવધિમાં વપરાયેલ ડેટા"</string>
     <string name="throttle_time_frame" msgid="1813452485948918791">"ડેટા ઉપયોગ અવધિ"</string>
-    <string name="throttle_rate" msgid="7641913901133634905">"માહિતી રેટ નીતિ"</string>
+    <string name="throttle_rate" msgid="7641913901133634905">"માહિતી રેટ પૉલિસી"</string>
     <string name="throttle_help" msgid="2624535757028809735">"વધુ જાણો"</string>
     <string name="throttle_status_subtext" msgid="1110276415078236687">"<xliff:g id="USED_2">%3$s</xliff:g> માંથી <xliff:g id="USED_0">%1$s</xliff:g> (<xliff:g id="USED_1">%2$d</xliff:g>٪) અવધિ મહત્તમ\nઆગલી અવધિ <xliff:g id="USED_3">%4$d</xliff:g> દિવસમાં પ્રારંભ થાય છે (<xliff:g id="USED_4">%5$s</xliff:g>)"</string>
     <string name="throttle_data_usage_subtext" msgid="3185429653996709840">"<xliff:g id="USED_2">%3$s</xliff:g> માંથી <xliff:g id="USED_0">%1$s</xliff:g> (<xliff:g id="USED_1">%2$d</xliff:g>٪) અવધિ મહત્તમ"</string>
     <string name="throttle_data_rate_reduced_subtext" msgid="8369839346277847725">"<xliff:g id="USED_0">%1$s</xliff:g> મહત્તમ વટાવી દીધું\nડેટા રેટ <xliff:g id="USED_1">%2$d</xliff:g> Kb/s સુધી ઘટાડાશે"</string>
     <string name="throttle_time_frame_subtext" msgid="6462089615392402127">"<xliff:g id="USED_0">%1$d</xliff:g>٪ ચક્ર વીતાવ્યું\nઆગલી અવધિ <xliff:g id="USED_1">%2$d</xliff:g> દિવસમાં પ્રારંભ થાય છે (<xliff:g id="USED_2">%3$s</xliff:g>)"</string>
     <string name="throttle_rate_subtext" msgid="7221971817325779535">"જો ડેટા સીમા વટાવવામાં આવે તો ડેટા રેટ <xliff:g id="USED">%1$d</xliff:g> Kb/s સુધી ઘટાડવામાં આવે છે"</string>
-    <string name="throttle_help_subtext" msgid="2817114897095534807">"તમારા કેરિઅરની મોબાઇલ નેટવર્ક ડેટા ઉપયોગ નીતિ વિશે વધુ માહિતી"</string>
+    <string name="throttle_help_subtext" msgid="2817114897095534807">"તમારા કૅરિઅરની મોબાઇલ નેટવર્ક ડેટા ઉપયોગ પૉલિસી વિશે વધુ માહિતી"</string>
     <string name="cell_broadcast_sms" msgid="4053449797289031063">"સેલ બ્રોડકાસ્ટ SMS"</string>
     <string name="enable_disable_cell_bc_sms" msgid="4759958924031721350">"સેલ બ્રોડકાસ્ટ SMS"</string>
     <string name="cell_bc_sms_enable" msgid="2019708772024632073">"સેલ બ્રોડકાસ્ટ SMS સક્ષમ કરેલ છે"</string>
@@ -417,7 +420,7 @@
     <string name="cdma_system_select_dialogtitle" msgid="5524639510676501802">"સિસ્ટમ પસંદગી"</string>
   <string-array name="cdma_system_select_choices">
     <item msgid="462340042928284921">"ફક્ત હોમ"</item>
-    <item msgid="6058010046783562674">"સ્વચલિત"</item>
+    <item msgid="6058010046783562674">"ઑટોમૅટિક"</item>
   </string-array>
     <string name="cdma_subscription_title" msgid="3449527179325589434">"CDMA સબ્સ્ક્રિપ્શન"</string>
     <string name="cdma_subscription_summary" msgid="5681152534466169001">"RUIM/SIM અને NV વચ્ચે બદલો"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"પાસવર્ડ ખોટો છે, SIM હમણાં અવરોધિત છે. PUK2 દાખલ કરો."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 ને કાયમી રૂપે અવરોધિત કરેલ છે."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"તમારી પાસે <xliff:g id="NUMBER">%d</xliff:g> પ્રયાસ બાકી છે."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 વડે લૉક થયું છે. અનલૉક કરવા માટે સેવા પ્રદાતાનો સંપર્ક કરો."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 હવે અવરોધિત નથી"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"નેટવર્ક અથવા SIM કાર્ડ ભૂલ"</string>
     <string name="doneButton" msgid="7371209609238460207">"થઈ ગયું"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"ફોનની માહિતી"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"કૅરિઅરની જોગવાઈ વિશે માહિતી"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"કૅરિઅરની જોગવાઈ ટ્રિગર કરો"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"સૂચન: બ્લૂટૂથ કનેક્ટિવિટી બહેતર બનાવો"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"તમારા બ્લૂટૂથનું સિગ્નલ નબળું છે. સ્પીકરફોન પર સ્વિચ કરવાનો પ્રયાસ કરો."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"કૉલની ક્વૉલિટી માટે નોટિફિકેશન"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"ટાળવામાં આવેલા SIP એકાઉન્ટ"</string>
 </resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index df0bbc8..e18e557 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"सिम से सीमित सुविधाएं मिलेंगी"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g> का इस्तेमाल करते समय <xliff:g id="CARRIER_NAME">%1$s</xliff:g> के कॉल और डेटा सेवाओं पर रोक लगाई जा सकती है."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"किसी और सिम का इस्तेमाल करते समय <xliff:g id="CARRIER_NAME">%1$s</xliff:g> की कॉल और डेटा सेवाएं ब्लॉक की जा सकती हैं."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"ऐसे SIP खाते मिले जिनका समर्थन रोक दिया गया है और उन्हें हटाया गया"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP कॉल की सुविधा, Android प्लैटफ़ॉर्म पर अब काम नहीं करेगी.\nआपके मौजूदा SIP खाते <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> हटा दिए गए हैं.\nकृपया कॉल करने की अपनी डिफ़ॉल्ट खाता सेटिंग की पुष्टि करें."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"सेटिंग पर जाएं"</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>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"पासवर्ड गलत है. सिम अब लॉक हो गई है. PUK2 डालें."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 स्थायी रूप से अवरोधित है."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"आपके पास <xliff:g id="NUMBER">%d</xliff:g> प्रयास शेष हैं."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"सिम कार्ड PUK2 से लॉक है. अनलॉक करने के लिए, सेवा देने वाली कंपनी से संपर्क करें."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 अब अवरोधित नहीं है"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"नेटवर्क या SIM कार्ड गड़बड़ी"</string>
     <string name="doneButton" msgid="7371209609238460207">"पूरा हो गया"</string>
@@ -673,7 +677,7 @@
     <string name="enable_video_calling_dialog_settings" msgid="8697890611305307110">"नेटवर्क सेटिंग"</string>
     <string name="enable_video_calling_dialog_close" msgid="4298929725917045270">"बंद करें"</string>
     <string name="sim_label_emergency_calls" msgid="9078241989421522310">"आपातकालीन कॉल"</string>
-    <string name="sim_description_emergency_calls" msgid="5146872803938897296">"केवल आपातकालीन कॉल"</string>
+    <string name="sim_description_emergency_calls" msgid="5146872803938897296">"सिर्फ़ आपातकालीन कॉल"</string>
     <string name="sim_description_default" msgid="7474671114363724971">"सिम कार्ड, स्‍लॉट: <xliff:g id="SLOT_ID">%s</xliff:g>"</string>
     <string name="accessibility_settings_activity_title" msgid="7883415189273700298">"सुलभता"</string>
     <string name="status_hint_label_incoming_wifi_call" msgid="2606052595898044071">"इससे वाई-फ़ाई कॉल"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"फ़ोन की जानकारी"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"मोबाइल और इंटरनेट सेवा देने वाली कंपनी के प्रावधान की जानकारी"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"मोबाइल और इंटरनेट सेवा देने वाली कंपनी के प्रावधान चालू करें"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"सुझाव: ब्लूटूथ कनेक्टिविटी बेहतर करें"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"आपका ब्लूटूथ सिग्नल कमज़ोर है. स्पीकरफ़ोन की सुविधा का इस्तेमाल करें."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"कॉल की क्वालिटी की सूचना"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"ऐसे SIP खाते जिनका समर्थन रोक दिया गया है"</string>
 </resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index afd4e62..b15a054 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Ograničene funkcije SIM-a"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Pozivi i podatkovne usluge operatera <xliff:g id="CARRIER_NAME">%1$s</xliff:g> mogu biti blokirane dok upotrebljavate broj <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Pozivi i podatkovne usluge operatera <xliff:g id="CARRIER_NAME">%1$s</xliff:g> mogu biti blokirane dok upotrebljavate drugi SIM."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Obustavljeni SIP računi pronađeni su i uklonjeni"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Pozivanje putem protokola SIP više nije podržano na Android platformi.\nVaši postojeći SIP računi <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> uklonjeni su.\nPotvrdite zadanu postavku za pozivanje na računu."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Otvorite postavke"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Potrošnja podatkovnog prometa za aplikacije"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"Potrošeno je <xliff:g id="ID_1">%1$s</xliff:g> mobilnih podataka u sljedećem razdoblju: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Napredno"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Zaporka nije točna. SIM je sada zaključan. Unesite PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 je trajno blokiran."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Imate još <xliff:g id="NUMBER">%d</xliff:g> pokušaja."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 zaključan. Obratite se davatelju usluga za otključavanje."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 više nije blokiran"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Pogreška mreže ili SIM kartice"</string>
     <string name="doneButton" msgid="7371209609238460207">"Gotovo"</string>
@@ -922,6 +926,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Informacije o telefonu"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Informacije o uslugama mobilnog operatera"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Pokretanje usluga mobilnog operatera"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Prijedlog: poboljšajte povezivost Bluetootha"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Signal Bluetootha je slab. Pokušajte se prebaciti na zvučnik."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Obavijest o kvaliteti poziva"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Obustavljeni SIP računi"</string>
 </resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index e053c54..d702818 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Korlátozott SIM-funkció"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"A(z) <xliff:g id="PHONE_NUMBER">%2$s</xliff:g> szám használatakor előfordulhat, hogy a(z) <xliff:g id="CARRIER_NAME">%1$s</xliff:g>-hívások és az adatszolgáltatások nem működnek."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Másik SIM esetén lehet, hogy a(z) <xliff:g id="CARRIER_NAME">%1$s</xliff:g>-hívások és -adatszolgáltatások nem működnek."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Elavult SIP-fiókokat találtunk, és eltávolítottuk őket"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Az Android platform már nem támogatja a SIP-hívást.\nA meglévő SIP-fiókjait (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) eltávolítottuk.\nErősítse meg az alapértelmezett hívásfiók beállítását."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Ugrás a beállításokhoz"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Alkalmazás adathasználata"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> mobiladat felhasználva a következő dátumok között: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Speciális"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"A jelszó helytelen. SIM kártya zárolva. Adja meg a PUK2 kódot."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"A PUK2 végleg letiltva."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"<xliff:g id="NUMBER">%d</xliff:g> próbálkozási lehetősége maradt."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"A PUK2 zárolva van. A feloldás ügyében forduljon a szolgáltatóhoz."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 kód letiltása feloldva"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Hálózattal vagy SIM kártyával kapcsolatos hiba"</string>
     <string name="doneButton" msgid="7371209609238460207">"Kész"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Telefon adatai"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Információ a szolgáltatói ellátásról"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Szolgáltatói ellátás aktiválása"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Javaslat: Gondoskodjon jobb Bluetooth-kapcsolatról"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Gyenge a Bluetooth-jel. Próbáljon kihangosítóra váltani."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Értesítés a hívás minőségéről"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Elavult SIP-fiókok"</string>
 </resources>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 6033af7..f005098 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -284,7 +284,7 @@
     <string name="roaming" msgid="1576180772877858949">"Ռոումինգ"</string>
     <string name="roaming_enable" msgid="6853685214521494819">"Միանալ տվյալների փոխանցման ծառայություններին ռոումինգում"</string>
     <string name="roaming_disable" msgid="8856224638624592681">"Միանալ տվյալների փոխանցման ծառայություններին ռոումինգում"</string>
-    <string name="roaming_reenable_message" msgid="1951802463885727915">"Ռոումինգում բջջային ինտերնետն անջատած է: Հպեք՝ միացնելու համար:"</string>
+    <string name="roaming_reenable_message" msgid="1951802463885727915">"Ռոումինգում բջջային ինտերնետն անջատված է: Հպեք՝ միացնելու համար:"</string>
     <string name="roaming_enabled_message" msgid="9022249120750897">"Ռոումինգի համար կարող է գումար գանձվել: Հպեք՝ փոփոխելու համար:"</string>
     <string name="roaming_notification_title" msgid="3590348480688047320">"Բջջային ինտերնետը կորավ"</string>
     <string name="roaming_on_notification_title" msgid="7451473196411559173">"Ինտերնետ ռոումինգը միացված է"</string>
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"SIM քարտի սահմանափակ գործառույթներ"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g> համարն օգտագործելիս <xliff:g id="CARRIER_NAME">%1$s</xliff:g>-ով զանգերն ու բջջային ինտերնետը կարող են արգելափակվել:"</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Այլ SIM քարտ օգտագործելիս <xliff:g id="CARRIER_NAME">%1$s</xliff:g>-ով զանգերն ու բջջային ինտերնետը կարող են արգելափակվել"</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Հնացած SIP հաշիվներ են հայտնաբերվել և հեռացվել"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP-ի միջոցով զանգերն այլևս չեն աջակցվում Android հարթակի կողմից։\nՁեր ունեցած SIP հաշիվները <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> հեռացվել են։\nՀաստատեք զանգերի կանխադրված ծառայության հաշվի կարգավորումները։"</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Անցնել կարգավորումներ"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Թրաֆիկի օգտագործումը հավելվածի կողմից"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"Oգտագործվել է <xliff:g id="ID_1">%1$s</xliff:g> բջջային ինտերնետ (<xliff:g id="ID_2">%2$s</xliff:g>)"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Լրացուցիչ"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Գաղտնաբառը սխալ է: SIM քարտն այժմ կողպված է: Մուտքագրեք PUK2 կոդը:"</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2-ն ընդմիշտ արգելափակված է:"</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Ձեզ մնացել է <xliff:g id="NUMBER">%d</xliff:g> փորձ:"</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"Կողպված է PUK2-ով։ Ապակողպելու համար կապվեք ձեր օպերատորի հետ։"</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 կոդն այլևս արգելափակված չէ"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Ցանցի կամ SIM քարտի սխալ"</string>
     <string name="doneButton" msgid="7371209609238460207">"Պատրաստ է"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Հեռախոսի տեղեկություններ"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Օպերատորի նախապատրաստման տվյալներ"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Գործարկել օպերատորի նախապատրաստումը"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Հուշում. բարելավեք Bluetooth կապը"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Ձեր Bluetooth-ի ազդանշանը թույլ է։ Փորձեք միացնել բարձրախոսը։"</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Զանգի որակի մասին ծանուցում"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Հնացած SIP հաշիվներ"</string>
 </resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index a23444c..47aa918 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -282,8 +282,8 @@
     <string name="data_enable_summary" msgid="696860063456536557">"Izinkan penggunaan data"</string>
     <string name="dialog_alert_title" msgid="5260471806940268478">"Perhatian"</string>
     <string name="roaming" msgid="1576180772877858949">"Roaming"</string>
-    <string name="roaming_enable" msgid="6853685214521494819">"Sambungkan ke layanan data ketika roaming"</string>
-    <string name="roaming_disable" msgid="8856224638624592681">"Sambungkan ke layanan data ketika roaming"</string>
+    <string name="roaming_enable" msgid="6853685214521494819">"Hubungkan ke layanan data ketika roaming"</string>
+    <string name="roaming_disable" msgid="8856224638624592681">"Hubungkan ke layanan data ketika roaming"</string>
     <string name="roaming_reenable_message" msgid="1951802463885727915">"Roaming data dinonaktifkan. Ketuk untuk mengaktifkan."</string>
     <string name="roaming_enabled_message" msgid="9022249120750897">"Biaya roaming dapat berlaku. Ketuk untuk mengubah."</string>
     <string name="roaming_notification_title" msgid="3590348480688047320">"Koneksi data seluler terputus"</string>
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Fungsi SIM terbatas"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Layanan data dan panggilan <xliff:g id="CARRIER_NAME">%1$s</xliff:g> dapat diblokir saat menggunakan <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Layanan data dan panggilan <xliff:g id="CARRIER_NAME">%1$s</xliff:g> dapat diblokir saat menggunakan SIM lain."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Akun SIP yang tidak digunakan lagi telah ditemukan dan dihapus"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Panggilan SIP tidak lagi didukung oleh platform Android.\nAkun SIP yang ada, <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>, telah dihapus.\nHarap konfirmasi setelan akun panggilan default Anda."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Buka setelan"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Penggunaan data aplikasi"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> data seluler digunakan pada <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Lanjutan"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Sandi salah. SIM kini dikunci. Masukkan PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 diblokir selamanya."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Tersisa <xliff:g id="NUMBER">%d</xliff:g> upaya lagi."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 terkunci. Hubungi penyedia layanan untuk membuka kunci."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 tidak lagi diblokir"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Kesalahan jaringan atau kartu SIM"</string>
     <string name="doneButton" msgid="7371209609238460207">"Selesai"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Info telepon"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Info Provisioning Operator"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Memicu Provisioning Operator"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Saran: Tingkatkan konektivitas Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Sinyal bluetooth Anda lemah. Coba beralih ke speaker ponsel."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Notifikasi Kualitas Panggilan"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Akun SIP yang tidak digunakan lagi"</string>
 </resources>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 1873079..cd40734 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Takmörkuð virkni SIM-korts"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Lokað gæti verið fyrir símtöl og gagnaþjónustu <xliff:g id="CARRIER_NAME">%1$s</xliff:g> þegar <xliff:g id="PHONE_NUMBER">%2$s</xliff:g> er notað."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Lokað gæti verið fyrir símtöl og gagnaþjónustu <xliff:g id="CARRIER_NAME">%1$s</xliff:g> þegar annað SIM-kort er notað."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Úreldir SIP-reikningar fundust og voru fjarlægðir"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Android kerfið styður ekki lengur SIP-símtöl.\nNúverandi SIP-reikningarnir þínir <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> voru fjarlægðir.\nStaðfestu stillingar sjálfgefna símareikningsins."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Opna stillingar"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Gagnanotkun forrits"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> af farsímagögnum notuð <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Ítarlegt"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Rangt aðgangsorð. SIM-kortinu var læst. Sláðu inn PUK2-númerið."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 er læst fyrir fullt og allt."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"<xliff:g id="NUMBER">%d</xliff:g> tilraunir eru eftir."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"Læst með PUK2. Hafðu samband við þjónustuaðila til að taka úr lás."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2-númer ekki lengur læst"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Villa í símkerfi eða á SIM-korti"</string>
     <string name="doneButton" msgid="7371209609238460207">"Lokið"</string>
@@ -791,7 +795,7 @@
     <string name="call_barring_change_pwd" msgid="1730691950940338387">"Breyta aðgangsorði"</string>
     <string name="call_barring_change_pwd_description" msgid="1274245130382054227">"Breyta aðgangsorði fyrir útilokun símtala"</string>
     <string name="call_barring_change_pwd_description_disabled" msgid="2911647051915343920">"Ekki var hægt að breyta aðgangsorði fyrir útilokun símtala"</string>
-    <string name="call_barring_pwd_not_match" msgid="7638198747579019826">"Aðgangsorðin stemma ekki."</string>
+    <string name="call_barring_pwd_not_match" msgid="7638198747579019826">"Aðgangsorðin stemma ekki"</string>
     <string name="call_barring_right_pwd_number" msgid="3860630926460851330">"Sláðu inn aðgangsorð sem inniheldur fjóra tölustafi."</string>
     <string name="call_barring_change_pwd_success" msgid="1837437691277936903">"Aðgangsorði breytt"</string>
     <string name="call_barring_old_pwd" msgid="5500085633281388281">"Eldra aðgangsorð"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Upplýsingar um síma"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Úthlutunarupplýsingar símafyrirtækis"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Kveikja á úthlutun símafyrirtækis"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Tillaga: Bæta tengigetu Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth-tengingin er léleg. Prófaðu að nota hátalara í staðinn."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Tilkynning um símtalsgæði"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Úreldir SIP-reikningar"</string>
 </resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index c45ec81..78ea899 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Funzionalità SIM limitata"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Le chiamate e i servizi dati di <xliff:g id="CARRIER_NAME">%1$s</xliff:g> potrebbero essere bloccati quando utilizzi il numero <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Le chiamate e i servizi dati <xliff:g id="CARRIER_NAME">%1$s</xliff:g> possono essere bloccati quando usi un\'altra SIM."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Account SIP deprecati trovati e rimossi"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Le chiamate SIP non sono più supportate dalla piattaforma Android.\nI tuoi account SIP esistenti (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) sono stati rimossi.\nConferma l\'impostazione del tuo account predefinito per le chiamate."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Vai alle impostazioni"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Utilizzo dei dati delle app"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"Dati mobili usati: <xliff:g id="ID_1">%1$s</xliff:g> nel periodo <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Avanzate"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Password errata. SIM bloccata. Inserisci codice PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 bloccato definitivamente."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Hai a disposizione ancora <xliff:g id="NUMBER">%d</xliff:g> tentativi."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 bloccato. Per sbloccarlo, contatta il fornitore di servizi."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 sbloccato"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Errore di rete o della SIM"</string>
     <string name="doneButton" msgid="7371209609238460207">"Fine"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Info telefono"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Informazioni sul provisioning operatore"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Attiva provisioning operatore"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Suggerimento: migliora la connettività Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Il segnale del Bluetooth è debole. Prova a passare al vivavoce."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Notifica sulla qualità della chiamata"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Account SIP deprecati"</string>
 </resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 9c4c0d9..d7cee71 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -31,29 +31,29 @@
     <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">"אישור"</string>
     <string name="audio_mode_speaker" msgid="243689733219312360">"רמקול"</string>
     <string name="audio_mode_earpiece" msgid="2823700267171134282">"אוזניה"</string>
-    <string name="audio_mode_wired_headset" msgid="5028010823105817443">"אוזניות קוויות"</string>
+    <string name="audio_mode_wired_headset" msgid="5028010823105817443">"אוזניות חוטיות"</string>
     <string name="audio_mode_bluetooth" msgid="25732183428018809">"Bluetooth"</string>
     <string name="wait_prompt_str" msgid="5136209532150094910">"לשלוח את הצלילים הבאים?\n"</string>
-    <string name="pause_prompt_str" msgid="2308897950360272213">"שולח צלילים\n"</string>
+    <string name="pause_prompt_str" msgid="2308897950360272213">"שליחת צלילים מתבצעת\n"</string>
     <string name="send_button" msgid="5070379600779031932">"שליחה"</string>
     <string name="pause_prompt_yes" msgid="8184132073048369575">"כן"</string>
     <string name="pause_prompt_no" msgid="2145264674774138579">"לא"</string>
     <string name="wild_prompt_str" msgid="5858910969703305375">"החלפת התו הכללי ב"</string>
     <string name="no_vm_number" msgid="6623853880546176930">"חסר מספר של דואר קולי"</string>
     <string name="no_vm_number_msg" msgid="5165161462411372504">"‏בכרטיס ה-SIM לא מאוחסן מספר של דואר קולי."</string>
-    <string name="add_vm_number_str" msgid="7368168964435881637">"הוסף מספר"</string>
+    <string name="add_vm_number_str" msgid="7368168964435881637">"הוספת מספר"</string>
     <string name="voice_number_setting_primary_user_only" msgid="3394706575741912843">"רק המשתמש הראשי יכול לשנות את ההגדרות של הדואר הקולי."</string>
-    <string name="puk_unlocked" msgid="4627340655215746511">"‏בוטלה החסימה של כרטיס SIM. מבטל את חסימת הטלפון..."</string>
-    <string name="label_ndp" msgid="7617392683877410341">"‏PIN לביטול נעילה של רשת SIM"</string>
+    <string name="puk_unlocked" msgid="4627340655215746511">"‏בוטלה החסימה של כרטיס SIM. ביטול חסימת הטלפון מתבצעת..."</string>
+    <string name="label_ndp" msgid="7617392683877410341">"‏קוד אימות לביטול נעילה של רשת SIM"</string>
     <string name="label_phoneid" msgid="8775611434123577808">"‏כרטיס ה-SIM נעול על ידי הספק הסלולרי"</string>
-    <string name="sim_ndp_unlock_text" msgid="7737338355451978338">"בטל נעילה"</string>
-    <string name="sim_ndp_dismiss_text" msgid="89667342248929777">"סגור"</string>
-    <string name="requesting_unlock" msgid="930512210309437741">"מבקש ביטול נעילת רשת..."</string>
+    <string name="sim_ndp_unlock_text" msgid="7737338355451978338">"ביטול נעילה"</string>
+    <string name="sim_ndp_dismiss_text" msgid="89667342248929777">"סגירה"</string>
+    <string name="requesting_unlock" msgid="930512210309437741">"בקשה לביטול נעילת הרשת מתבצעת..."</string>
     <string name="unlock_failed" msgid="7103543844840661366">"הבקשה לביטול נעילת הרשת נכשלה."</string>
     <string name="unlock_success" msgid="32681089371067565">"ביטול נעילת הרשת התבצע בהצלחה."</string>
     <string name="mobile_network_settings_not_available" msgid="8678168497517090039">"הגדרות של רשת סלולרית אינן זמינות עבור המשתמש הזה"</string>
@@ -64,17 +64,17 @@
     <string name="apn_settings" msgid="1978652203074756623">"שמות של נקודות גישה"</string>
     <string name="settings_label" msgid="9101778088412567956">"הגדרות רשת"</string>
     <string name="phone_accounts" msgid="1216879437523774604">"חשבונות להתקשרות"</string>
-    <string name="phone_accounts_make_calls_with" msgid="16747814788918145">"בצע שיחות באמצעות"</string>
-    <string name="phone_accounts_make_sip_calls_with" msgid="4691221006731847255">"‏בצע שיחות SIP באמצעות"</string>
-    <string name="phone_accounts_ask_every_time" msgid="6192347582666047168">"שאל לפני ביצוע"</string>
+    <string name="phone_accounts_make_calls_with" msgid="16747814788918145">"ביצוע שיחות באמצעות"</string>
+    <string name="phone_accounts_make_sip_calls_with" msgid="4691221006731847255">"‏ביצוע שיחות SIP באמצעות"</string>
+    <string name="phone_accounts_ask_every_time" msgid="6192347582666047168">"יש לשאול לפני ביצוע"</string>
     <string name="phone_accounts_default_account_label" msgid="5107598881335931101">"אין רשתות זמינות"</string>
     <string name="phone_accounts_settings_header" msgid="6296501692964706536">"הגדרות"</string>
-    <string name="phone_accounts_choose_accounts" msgid="4748805293314824974">"בחר חשבונות"</string>
+    <string name="phone_accounts_choose_accounts" msgid="4748805293314824974">"בחירת חשבונות"</string>
     <string name="phone_accounts_selection_header" msgid="2945830843104108440">"חשבונות טלפון"</string>
-    <string name="phone_accounts_add_sip_account" msgid="1437634802033309305">"‏הוסף חשבון SIP"</string>
-    <string name="phone_accounts_configure_account_settings" msgid="6622119715253196586">"קבע את הגדרות החשבון"</string>
+    <string name="phone_accounts_add_sip_account" msgid="1437634802033309305">"‏הוספת חשבון SIP"</string>
+    <string name="phone_accounts_configure_account_settings" msgid="6622119715253196586">"קביעת הגדרות החשבון"</string>
     <string name="phone_accounts_all_calling_accounts" msgid="1609600743500618823">"כל החשבונות לביצוע שיחות"</string>
-    <string name="phone_accounts_all_calling_accounts_summary" msgid="2214134955430107240">"בחר אילו חשבונות יכולים להתקשר"</string>
+    <string name="phone_accounts_all_calling_accounts_summary" msgid="2214134955430107240">"בחירת החשבונות שיכולים להתקשר"</string>
     <string name="wifi_calling" msgid="3650509202851355742">"‏שיחות ב-Wi-Fi"</string>
     <string name="connection_service_default_label" msgid="7332739049855715584">"שירות חיבור מובנה"</string>
     <string name="voicemail" msgid="7697769412804195032">"דואר קולי"</string>
@@ -93,7 +93,7 @@
     <string name="sum_cdma_call_settings" msgid="3185825305136993636">"‏הגדרות שיחה נוספות של CDMA בלבד"</string>
     <string name="labelNwService" msgid="6015891883487125120">"הגדרות שירות רשת"</string>
     <string name="labelCallerId" msgid="2090540744550903172">"שיחה מזוהה"</string>
-    <string name="sum_loading_settings" msgid="434063780286688775">"טוען הגדרות..."</string>
+    <string name="sum_loading_settings" msgid="434063780286688775">"טעינת ההגדרות מתבצעת..."</string>
     <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>
@@ -103,25 +103,25 @@
     <string name="call_forwarding_settings" msgid="8937130467468257671">"הגדרות של העברת שיחות"</string>
     <string name="call_forwarding_settings_with_label" msgid="2345432813399564272">"הגדרות של העברת שיחות (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
     <string name="labelCF" msgid="3578719437928476078">"העברת שיחות"</string>
-    <string name="labelCFU" msgid="8870170873036279706">"העבר תמיד"</string>
-    <string name="messageCFU" msgid="1361806450979589744">"השתמש תמיד במספר זה"</string>
-    <string name="sum_cfu_enabled_indicator" msgid="9030139213402432776">"מעביר את כל השיחות"</string>
-    <string name="sum_cfu_enabled" msgid="5806923046528144526">"מעביר את כל השיחות אל <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
+    <string name="labelCFU" msgid="8870170873036279706">"העברה תמיד"</string>
+    <string name="messageCFU" msgid="1361806450979589744">"יש להשתמש תמיד במספר זה"</string>
+    <string name="sum_cfu_enabled_indicator" msgid="9030139213402432776">"כל השיחות מועברות"</string>
+    <string name="sum_cfu_enabled" msgid="5806923046528144526">"כל השיחות מועברות אל <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
     <string name="sum_cfu_enabled_no_number" msgid="7287752761743377930">"המספר לא זמין"</string>
     <string name="sum_cfu_disabled" msgid="5010617134210809853">"כבוי"</string>
     <string name="labelCFB" msgid="615265213360512768">"כאשר לא פנוי"</string>
     <string name="messageCFB" msgid="1958017270393563388">"מספר כאשר לא פנוי"</string>
-    <string name="sum_cfb_enabled" msgid="332037613072049492">"מעביר אל <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
+    <string name="sum_cfb_enabled" msgid="332037613072049492">"העברה אל <xliff:g id="PHONENUMBER">{0}</xliff:g> מתבצעת"</string>
     <string name="sum_cfb_disabled" msgid="3589913334164866035">"כבוי"</string>
     <string name="disable_cfb_forbidden" msgid="4831494744351633961">"הספק שלך אינו תומך בהשבתה של העברת שיחות כאשר הטלפון תפוס."</string>
     <string name="labelCFNRy" msgid="3403533792248457946">"כאשר אין מענה"</string>
     <string name="messageCFNRy" msgid="7644434155765359009">"מספר לחיוג כשאין מענה"</string>
-    <string name="sum_cfnry_enabled" msgid="3000500837493854799">"מעביר אל <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
+    <string name="sum_cfnry_enabled" msgid="3000500837493854799">"העברה אל <xliff:g id="PHONENUMBER">{0}</xliff:g> מתבצעת"</string>
     <string name="sum_cfnry_disabled" msgid="1990563512406017880">"כבוי"</string>
     <string name="disable_cfnry_forbidden" msgid="3174731413216550689">"הספק אינו תומך בהשבתה של העברת שיחות כאשר אין מענה בטלפון."</string>
     <string name="labelCFNRc" msgid="4163399350778066013">"כאשר לא נגיש"</string>
     <string name="messageCFNRc" msgid="6980340731313007250">"המספר לחיוג כשלא ניתן להשיג"</string>
-    <string name="sum_cfnrc_enabled" msgid="1799069234006073477">"מעביר אל <xliff:g id="PHONENUMBER">{0}</xliff:g>"</string>
+    <string name="sum_cfnrc_enabled" msgid="1799069234006073477">"העברה אל <xliff:g id="PHONENUMBER">{0}</xliff:g> מתבצעת"</string>
     <string name="sum_cfnrc_disabled" msgid="739289696796917683">"כבוי"</string>
     <string name="disable_cfnrc_forbidden" msgid="775348748084726890">"הספק שלך לא תומך בהשבתה של העברת שיחות כאשר הטלפון כבוי."</string>
     <string name="registration_cf_forbidden" msgid="4386482610771190420">"הספק הסלולרי שלך לא תומך בהעברת שיחות."</string>
@@ -135,9 +135,9 @@
     <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>
     <string name="error_updating_title" msgid="2024290892676808965">"שגיאה בהגדרות שיחה"</string>
-    <string name="reading_settings" msgid="1605904432450871183">"קורא הגדרות…"</string>
-    <string name="updating_settings" msgid="3650396734816028808">"מעדכן הגדרות..."</string>
-    <string name="reverting_settings" msgid="7378668837291012205">"מאחזר הגדרות הקודמות…"</string>
+    <string name="reading_settings" msgid="1605904432450871183">"קריאת ההגדרות מתבצעת…"</string>
+    <string name="updating_settings" msgid="3650396734816028808">"עדכון ההגדרות מתבצע..."</string>
+    <string name="reverting_settings" msgid="7378668837291012205">"אחזור ההגדרות הקודמות מתבצע…"</string>
     <string name="response_error" msgid="3904481964024543330">"תגובה לא צפויה מהרשת."</string>
     <string name="exception_error" msgid="330994460090467">"‏שגיאת רשת או שגיאה של כרטיס SIM."</string>
     <string name="stk_cc_ss_to_dial_error" msgid="5147693491690618704">"‏בקשת SS שונתה לשיחה רגילה"</string>
@@ -145,41 +145,41 @@
     <string name="stk_cc_ss_to_ss_error" msgid="8297155544652134278">"‏היה שינוי לבקשת SS חדשה"</string>
     <string name="stk_cc_ss_to_dial_video_error" msgid="4255261231466032505">"‏בקשת SS שונתה לשיחת וידאו"</string>
     <string name="fdn_check_failure" msgid="1833769746374185247">"הגדרת מספרי החיוג הקבועים של אפליקציית הטלפון שלך מופעלת. כתוצאה מכך, חלק מהתכונות הקשורות לשיחות לא פועלות."</string>
-    <string name="radio_off_error" msgid="8321564164914232181">"הפעל את הרדיו לפני ההצגה של הגדרות אלה."</string>
+    <string name="radio_off_error" msgid="8321564164914232181">"יש להפעיל את הרדיו לפני ההצגה של הגדרות אלה."</string>
     <string name="close_dialog" msgid="1074977476136119408">"אישור"</string>
     <string name="enable" msgid="2636552299455477603">"הפעלה"</string>
-    <string name="disable" msgid="1122698860799462116">"כבה"</string>
-    <string name="change_num" msgid="6982164494063109334">"עדכן"</string>
+    <string name="disable" msgid="1122698860799462116">"כיבוי"</string>
+    <string name="change_num" msgid="6982164494063109334">"עדכון"</string>
   <string-array name="clir_display_values">
     <item msgid="8477364191403806960">"רשת ברירת מחדל"</item>
-    <item msgid="6813323051965618926">"הסתר מספר"</item>
-    <item msgid="9150034130629852635">"הצג מספר"</item>
+    <item msgid="6813323051965618926">"הסתרת מספר"</item>
+    <item msgid="9150034130629852635">"הצגת מספר"</item>
   </string-array>
     <string name="vm_changed" msgid="4739599044379692505">"המספר של הדואר הקולי השתנה."</string>
-    <string name="vm_change_failed" msgid="7877733929455763566">"לא ניתן לשנות את מספר הגישה לדואר הקולי.\nאם הבעיה נמשכת, פנה לספק."</string>
-    <string name="fw_change_failed" msgid="9179241823460192148">"לא ניתן לשנות את מספר ההעברה.\nאם הבעיה נמשכת, פנה לספק."</string>
+    <string name="vm_change_failed" msgid="7877733929455763566">"לא ניתן לשנות את מספר הגישה לדואר הקולי.\nאם הבעיה נמשכת, יש לפנות לספק."</string>
+    <string name="fw_change_failed" msgid="9179241823460192148">"לא ניתן לשנות את מספר ההעברה.\nאם הבעיה נמשכת, יש לפנות לספק."</string>
     <string name="fw_get_in_vm_failed" msgid="2432678237218183844">"לא ניתן לאחזר ולשמור את הגדרות מספר ההעברה הנוכחי.\nלעבור בכל זאת לספק החדש?"</string>
     <string name="no_change" msgid="3737264882821031892">"לא בוצעו שינויים."</string>
-    <string name="sum_voicemail_choose_provider" msgid="6750824719081403773">"בחר שירות של דואר קולי"</string>
+    <string name="sum_voicemail_choose_provider" msgid="6750824719081403773">"בחירת שירות דואר קולי"</string>
     <string name="voicemail_default" msgid="6427575113775462077">"הספק שלך"</string>
     <string name="vm_change_pin_old_pin" msgid="7154951790929009241">"קוד גישה ישן"</string>
     <string name="vm_change_pin_new_pin" msgid="2656200418481288069">"קוד גישה חדש"</string>
-    <string name="vm_change_pin_progress_message" msgid="626015184502739044">"אנא המתן."</string>
+    <string name="vm_change_pin_progress_message" msgid="626015184502739044">"יש להמתין."</string>
     <string name="vm_change_pin_error_too_short" msgid="1789139338449945483">"קוד הגישה החדש קצר מדי."</string>
     <string name="vm_change_pin_error_too_long" msgid="3634907034310018954">"קוד הגישה החדש ארוך מדי."</string>
     <string name="vm_change_pin_error_too_weak" msgid="8581892952627885719">"קוד הגישה החדש חלש מדי. בסיסמה חזקה אסור שיהיו ספרות ברצף מספרי או ספרות שחוזרות על עצמן."</string>
     <string name="vm_change_pin_error_mismatch" msgid="5364847280026257331">"קוד הגישה הישן אינו תואם."</string>
     <string name="vm_change_pin_error_invalid" msgid="5230002671175580674">"קוד הגישה החדש מכיל תווים לא חוקיים."</string>
     <string name="vm_change_pin_error_system_error" msgid="9116483527909681791">"לא ניתן לשנות את קוד הגישה"</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>
+    <string name="load_networks_progress" msgid="4051433047717401683">"החיפוש מתבצע..."</string>
     <string name="empty_networks_list" msgid="9216418268008582342">"לא נמצאו רשתות."</string>
     <string name="network_query_error" msgid="3862515805115145124">"לא נמצאו רשתות. אפשר לנסות שוב."</string>
-    <string name="register_on_network" msgid="4194770527833960423">"נרשם ב-<xliff:g id="NETWORK">%s</xliff:g>…"</string>
+    <string name="register_on_network" msgid="4194770527833960423">"ההרשמה ב-<xliff:g id="NETWORK">%s</xliff:g> מתבצעת…"</string>
     <string name="not_allowed" msgid="8541221928746104798">"‏כרטיס ה-SIM לא מאפשר חיבור לרשת זו."</string>
-    <string name="connect_later" msgid="1950138106010005425">"לא ניתן להתחבר לרשת זו כעת. נסה שוב מאוחר יותר."</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>
@@ -187,7 +187,7 @@
     <string name="network_select_title" msgid="4117305053881611988">"רשת"</string>
     <string name="register_automatically" msgid="3907580547590554834">"רישום אוטומטי..."</string>
     <string name="preferred_network_mode_title" msgid="5253395265169539830">"סוג רשת מועדף"</string>
-    <string name="preferred_network_mode_summary" msgid="3787989000044330064">"שנה את מצב ההפעלה של הרשת"</string>
+    <string name="preferred_network_mode_summary" msgid="3787989000044330064">"שינוי מצב ההפעלה של הרשת"</string>
     <string name="preferred_network_mode_dialogtitle" msgid="2781447433514459696">"סוג רשת מועדף"</string>
     <string name="forbidden_network" msgid="5081729819561333023">"(אסור)"</string>
     <string name="choose_network_title" msgid="5335832663422653082">"בחירת רשת"</string>
@@ -239,25 +239,25 @@
     <string name="preferred_network_mode_cdma_evdo_summary" msgid="3629440709757307077">"‏מצב רשת מועדפת: CDMA / EvDo"</string>
     <string name="preferred_network_mode_cdma_only_summary" msgid="211164451887102568">"‏מצב רשת מועדפת: CDMA בלבד"</string>
     <string name="preferred_network_mode_evdo_only_summary" msgid="939116631952132878">"‏מצב רשת מועדפת: EvDo בלבד"</string>
-    <string name="preferred_network_mode_cdma_evdo_gsm_wcdma_summary" msgid="7891131456022601976">"‏מצב רשת מועדף: CDMA/EvDo/GSM/WCDMA"</string>
-    <string name="preferred_network_mode_lte_summary" msgid="8050539466545797149">"‏מצב רשת מועדף: LTE"</string>
-    <string name="preferred_network_mode_lte_gsm_wcdma_summary" msgid="2217794334331254936">"‏מצב רשת מועדף: GSM/WCDMA/LTE"</string>
-    <string name="preferred_network_mode_lte_cdma_evdo_summary" msgid="5559198623419981805">"‏מצב רשת מועדף: CDMA+LTE/EVDO"</string>
+    <string name="preferred_network_mode_cdma_evdo_gsm_wcdma_summary" msgid="7891131456022601976">"‏מצב רשת מועדפת: CDMA/EvDo/GSM/WCDMA"</string>
+    <string name="preferred_network_mode_lte_summary" msgid="8050539466545797149">"‏מצב רשת מועדפת: LTE"</string>
+    <string name="preferred_network_mode_lte_gsm_wcdma_summary" msgid="2217794334331254936">"‏מצב רשת מועדפת: GSM/WCDMA/LTE"</string>
+    <string name="preferred_network_mode_lte_cdma_evdo_summary" msgid="5559198623419981805">"‏מצב רשת מועדפת: CDMA+LTE/EVDO"</string>
     <string name="preferred_network_mode_lte_cdma_evdo_gsm_wcdma_summary" msgid="6707224437925495615">"‏מצב רשת מועדפת: LTE‏/CDMA‏/EvDo‏/GSM‏/WCDMA"</string>
     <string name="preferred_network_mode_global_summary" msgid="3847086258439582411">"מצב רשת מועדף: גלובלי"</string>
-    <string name="preferred_network_mode_lte_wcdma_summary" msgid="7001804022020813865">"‏מצב רשת מועדף: LTE / WCDMA"</string>
-    <string name="preferred_network_mode_lte_gsm_umts_summary" msgid="6484203890156282179">"‏מצב רשת מועדף: LTE / GSM / UMTS"</string>
+    <string name="preferred_network_mode_lte_wcdma_summary" msgid="7001804022020813865">"‏מצב רשת מועדפת: LTE / WCDMA"</string>
+    <string name="preferred_network_mode_lte_gsm_umts_summary" msgid="6484203890156282179">"‏מצב רשת מועדפת: LTE / GSM / UMTS"</string>
     <string name="preferred_network_mode_lte_cdma_summary" msgid="8187929456614068518">"‏מצב רשת מועדפת: LTE / CDMA"</string>
-    <string name="preferred_network_mode_tdscdma_summary" msgid="3602127224234207206">"‏מצב רשת מועדף: TDSCDMA"</string>
-    <string name="preferred_network_mode_tdscdma_wcdma_summary" msgid="7076968749402201123">"‏מצב רשת מועדף: TDSCDMA / WCDMA"</string>
-    <string name="preferred_network_mode_lte_tdscdma_summary" msgid="3001058390866953624">"‏מצב רשת מועדף: LTE / TDSCDMA"</string>
-    <string name="preferred_network_mode_tdscdma_gsm_summary" msgid="1716983444872465309">"‏מצב רשת מועדף: TDSCDMA / GSM"</string>
-    <string name="preferred_network_mode_lte_tdscdma_gsm_summary" msgid="1349057007230669585">"‏מצב רשת מועדף: LTE/GSM/TDSCDMA"</string>
-    <string name="preferred_network_mode_tdscdma_gsm_wcdma_summary" msgid="2092262901885164194">"‏מצב רשת מועדף: TDSCDMA/GSM/WCDMA"</string>
-    <string name="preferred_network_mode_lte_tdscdma_wcdma_summary" msgid="56422129430744466">"‏מצב רשת מועדף: LTE/TDSCDMA/WCDMA"</string>
-    <string name="preferred_network_mode_lte_tdscdma_gsm_wcdma_summary" msgid="2993923113350341106">"‏מצב רשת מועדף: LTE/TDSCDMA/GSM/WCDMA"</string>
-    <string name="preferred_network_mode_tdscdma_cdma_evdo_gsm_wcdma_summary" msgid="2779089629254220257">"‏מצב רשת מועדף: TDSCDMA/CDMA/EvDo/GSM/WCDMA"</string>
-    <string name="preferred_network_mode_lte_tdscdma_cdma_evdo_gsm_wcdma_summary" msgid="9065672185435798587">"‏מצב רשת מועדף: LTE/TDSCDMA/CDMA/EvDo/GSM/WCDMA"</string>
+    <string name="preferred_network_mode_tdscdma_summary" msgid="3602127224234207206">"‏מצב רשת מועדפת: TDSCDMA"</string>
+    <string name="preferred_network_mode_tdscdma_wcdma_summary" msgid="7076968749402201123">"‏מצב רשת מועדפת: TDSCDMA / WCDMA"</string>
+    <string name="preferred_network_mode_lte_tdscdma_summary" msgid="3001058390866953624">"‏מצב רשת מועדפת: LTE / TDSCDMA"</string>
+    <string name="preferred_network_mode_tdscdma_gsm_summary" msgid="1716983444872465309">"‏מצב רשת מועדפת: TDSCDMA / GSM"</string>
+    <string name="preferred_network_mode_lte_tdscdma_gsm_summary" msgid="1349057007230669585">"‏מצב רשת מועדפת: LTE/GSM/TDSCDMA"</string>
+    <string name="preferred_network_mode_tdscdma_gsm_wcdma_summary" msgid="2092262901885164194">"‏מצב רשת מועדפת: TDSCDMA/GSM/WCDMA"</string>
+    <string name="preferred_network_mode_lte_tdscdma_wcdma_summary" msgid="56422129430744466">"‏מצב רשת מועדפת: LTE/TDSCDMA/WCDMA"</string>
+    <string name="preferred_network_mode_lte_tdscdma_gsm_wcdma_summary" msgid="2993923113350341106">"‏מצב רשת מועדפת: LTE/TDSCDMA/GSM/WCDMA"</string>
+    <string name="preferred_network_mode_tdscdma_cdma_evdo_gsm_wcdma_summary" msgid="2779089629254220257">"‏מצב רשת מועדפת: TDSCDMA/CDMA/EvDo/GSM/WCDMA"</string>
+    <string name="preferred_network_mode_lte_tdscdma_cdma_evdo_gsm_wcdma_summary" msgid="9065672185435798587">"‏מצב רשת מועדפת: LTE/TDSCDMA/CDMA/EvDo/GSM/WCDMA"</string>
     <string name="preferred_network_mode_nr_only_summary" msgid="1467452233297987391">"‏מצב רשת מועדפת: NR בלבד"</string>
     <string name="preferred_network_mode_nr_lte_summary" msgid="5890170406507535976">"‏מצב רשת מועדפת: NR ‏/ LTE"</string>
     <string name="preferred_network_mode_nr_lte_cdma_evdo_summary" msgid="5507940227264296616">"‏מצב רשת מועדפת: NR‏/LTE‏/CDMA‏/EvDo"</string>
@@ -273,17 +273,17 @@
     <string name="network_operator_category" msgid="4992217193732304680">"רשת"</string>
     <string name="enhanced_4g_lte_mode_title" msgid="4213420368777080540">"‏מצב 4G LTE משופר"</string>
     <!-- no translation found for enhanced_4g_lte_mode_title_variant:0 (7240155150166394308) -->
-    <string name="enhanced_4g_lte_mode_summary" msgid="7725708511804143638">"‏השתמש בשירותי LTE כדי לשפר את הקול ודרכי תקשורת אחרות (מומלץ)"</string>
+    <string name="enhanced_4g_lte_mode_summary" msgid="7725708511804143638">"‏שימוש בשירותי LTE כדי לשפר את הקול ודרכי תקשורת אחרות (מומלץ)"</string>
     <string name="enhanced_4g_lte_mode_summary_o2" msgid="2521108446409016542">"‏שימוש בשירותי 4G כדי לשפר את הקול ודרכי תקשורת אחרות (מומלץ)"</string>
     <!-- no translation found for enhanced_4g_lte_mode_sumary_variant:0 (2943982616649705147) -->
     <!-- no translation found for enhanced_4g_lte_mode_sumary_variant:1 (5262249464504131443) -->
     <!-- no translation found for enhanced_4g_lte_mode_sumary_variant:2 (6356974241850241718) -->
     <string name="data_enabled" msgid="22525832097434368">"נתונים מופעלים"</string>
-    <string name="data_enable_summary" msgid="696860063456536557">"התר שימוש בנתונים"</string>
+    <string name="data_enable_summary" msgid="696860063456536557">"התרת שימוש בחבילת הגלישה"</string>
     <string name="dialog_alert_title" msgid="5260471806940268478">"זהירות"</string>
     <string name="roaming" msgid="1576180772877858949">"נדידה"</string>
-    <string name="roaming_enable" msgid="6853685214521494819">"התחבר לשירותי נתונים בעת נדידה"</string>
-    <string name="roaming_disable" msgid="8856224638624592681">"התחבר לשירותי נתונים בעת נדידה"</string>
+    <string name="roaming_enable" msgid="6853685214521494819">"יש להתחבר לשירותי נתונים בעת נדידה"</string>
+    <string name="roaming_disable" msgid="8856224638624592681">"יש להתחבר לשירותי נתונים בעת נדידה"</string>
     <string name="roaming_reenable_message" msgid="1951802463885727915">"הנדידה מושבתת. אפשר להקיש כדי להפעיל אותה."</string>
     <string name="roaming_enabled_message" msgid="9022249120750897">"ייתכנו חיובי נדידה. יש להקיש כדי לשנות."</string>
     <string name="roaming_notification_title" msgid="3590348480688047320">"המכשיר התנתק מחבילת הגלישה"</string>
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"‏יכולת השימוש בכרטיס ה-SIM עלולה להיות מוגבלת"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"יכול להיות שאי אפשר יהיה להתקשר ולהשתמש בחבילת הגלישה דרך <xliff:g id="CARRIER_NAME">%1$s</xliff:g> בזמן השימוש במספר <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"‏ייתכן שאי אפשר להתקשר ולהשתמש בחבילת הגלישה דרך <xliff:g id="CARRIER_NAME">%1$s</xliff:g> בזמן השימוש בכרטיס SIM אחר."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"‏נמצאו חשבונות SIP שהוצאו משימוש והם הוסרו"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"‏שיחות SIP לא נתמכות יותר בפלטפורמת Android.\nלכן, חשבונות SIP שהיו לך <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> הוסרו.\nעליך לבחור איזה חשבון יוגדר כברירת מחדל לשיחות."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"פתיחת ההגדרות"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"שימוש בחבילת הגלישה דרך אפליקציות"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"השתמשת ב-<xliff:g id="ID_1">%1$s</xliff:g> מחבילת הגלישה בתאריכים <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"הגדרות מתקדמות"</string>
@@ -352,7 +355,7 @@
     <string name="international_enable" msgid="8943466745792690340">"חדשות פנים מופעלות"</string>
     <string name="international_disable" msgid="4803498658100318265">"חדשות בינלאומיות מושבתות"</string>
     <string name="list_language_title" msgid="1850167908665485738">"שפה"</string>
-    <string name="list_language_summary" msgid="7921756070782277559">"בחר את שפת החדשות"</string>
+    <string name="list_language_summary" msgid="7921756070782277559">"בחירת שפת החדשות"</string>
   <string-array name="list_language_entries">
     <item msgid="2347238508726934281">"אנגלית"</item>
     <item msgid="5172468397620875174">"צרפתית"</item>
@@ -413,7 +416,7 @@
     <string name="network_4G" msgid="6800527815504223913">"‏4G (מומלץ)"</string>
     <string name="network_global" msgid="3289646154407617631">"גלובלי"</string>
     <string name="cdma_system_select_title" msgid="614165233552656431">"בחירת מערכת"</string>
-    <string name="cdma_system_select_summary" msgid="3840420390242060407">"‏שנה את מצב נדידת CDMA"</string>
+    <string name="cdma_system_select_summary" msgid="3840420390242060407">"‏שינוי מצב נדידת CDMA"</string>
     <string name="cdma_system_select_dialogtitle" msgid="5524639510676501802">"בחירת מערכת"</string>
   <string-array name="cdma_system_select_choices">
     <item msgid="462340042928284921">"בית בלבד"</item>
@@ -430,8 +433,8 @@
     <item msgid="2953825013895327785">"0"</item>
     <item msgid="7970797749269738435">"1"</item>
   </string-array>
-    <string name="cdma_activate_device" msgid="5914720276140097632">"הפעל מכשיר"</string>
-    <string name="cdma_lte_data_service" msgid="359786441782404562">"הגדר שירות נתונים"</string>
+    <string name="cdma_activate_device" msgid="5914720276140097632">"הפעלת המכשיר"</string>
+    <string name="cdma_lte_data_service" msgid="359786441782404562">"הגדרת שירות נתונים"</string>
     <string name="carrier_settings_title" msgid="6292869148169850220">"הגדרות ספק"</string>
     <string name="fdn" msgid="2545904344666098749">"מספרי חיוג קבועים"</string>
     <string name="fdn_with_label" msgid="6412087553365709494">"מספרי חיוג קבועים (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
@@ -442,7 +445,7 @@
     <string name="fdn_disabled" msgid="6696468878037736600">"מספרי חיוג קבועים מושבתים"</string>
     <string name="enable_fdn" msgid="4830555730418033723">"הפעלת מספרי חיוג קבועים"</string>
     <string name="disable_fdn" msgid="3918794950264647541">"השבתת מספרי חיוג קבועים"</string>
-    <string name="change_pin2" msgid="3110844547237754871">"‏שינוי PIN2"</string>
+    <string name="change_pin2" msgid="3110844547237754871">"שינוי קוד אימות 2"</string>
     <string name="enable_fdn_ok" msgid="5080925177369329827">"השבת מספרי חיוג קבועים"</string>
     <string name="disable_fdn_ok" msgid="3745475926874838676">"הפעלה של מספרי חיוג קבועים"</string>
     <string name="sum_fdn" msgid="6152246141642323582">"ניהול מספרי חיוג קבועים"</string>
@@ -451,95 +454,96 @@
     <string name="voice_privacy" msgid="7346935172372181951">"פרטיות קול"</string>
     <string name="voice_privacy_summary" msgid="3556460926168473346">"הפעלת מצב פרטיות משופרת"</string>
     <string name="tty_mode_option_title" msgid="3843817710032641703">"‏מצב TTY"</string>
-    <string name="tty_mode_option_summary" msgid="4770510287236494371">"‏הגדר מצב TTY"</string>
+    <string name="tty_mode_option_summary" msgid="4770510287236494371">"‏הגדרת מצב TTY"</string>
     <string name="auto_retry_mode_title" msgid="2985801935424422340">"ניסיון חוזר אוטומטי"</string>
     <string name="auto_retry_mode_summary" msgid="2863919925349511402">"הפעלת מצב \'ניסיון חוזר\'."</string>
     <string name="tty_mode_not_allowed_video_call" msgid="6551976083652752815">"‏שינוי מצב TTY אינו מותר במהלך שיחת וידאו"</string>
-    <string name="menu_add" msgid="5616487894975773141">"הוסף איש קשר"</string>
-    <string name="menu_edit" msgid="3593856941552460706">"ערוך איש קשר"</string>
-    <string name="menu_delete" msgid="6326861853830546488">"מחק איש קשר"</string>
+    <string name="menu_add" msgid="5616487894975773141">"הוספת איש קשר"</string>
+    <string name="menu_edit" msgid="3593856941552460706">"עריכת איש קשר"</string>
+    <string name="menu_delete" msgid="6326861853830546488">"מחיקת איש קשר"</string>
     <string name="menu_dial" msgid="4178537318419450012">"חיוג לאיש הקשר"</string>
-    <string name="get_pin2" msgid="4221654606863196332">"‏הקלד PIN2"</string>
+    <string name="get_pin2" msgid="4221654606863196332">"יש להקליד קוד אימות 2"</string>
     <string name="name" msgid="1347432469852527784">"שם"</string>
     <string name="number" msgid="1564053487748491000">"מספר"</string>
-    <string name="save" msgid="983805790346099749">"שמור"</string>
-    <string name="add_fdn_contact" msgid="1169713422306640887">"הוסף מספר חיוג קבוע"</string>
-    <string name="adding_fdn_contact" msgid="3112531600824361259">"מוסיף מספר חיוג קבוע..."</string>
+    <string name="save" msgid="983805790346099749">"שמירה"</string>
+    <string name="add_fdn_contact" msgid="1169713422306640887">"הוספת מספר חיוג קבוע"</string>
+    <string name="adding_fdn_contact" msgid="3112531600824361259">"הוספת מספר חיוג קבוע מתבצעת..."</string>
     <string name="fdn_contact_added" msgid="2840016151693394596">"מספר חיוג קבוע נוסף."</string>
-    <string name="edit_fdn_contact" msgid="6030829994819587408">"ערוך מספר חיוג קבוע"</string>
-    <string name="updating_fdn_contact" msgid="6989341376868227150">"מעדכן מספר חיוג קבוע..."</string>
+    <string name="edit_fdn_contact" msgid="6030829994819587408">"עריכת מספר חיוג קבוע"</string>
+    <string name="updating_fdn_contact" msgid="6989341376868227150">"עדכון מספר החיוג הקבוע מתבצע..."</string>
     <string name="fdn_contact_updated" msgid="6876330243323118937">"מספר חיוג קבוע עודכן."</string>
-    <string name="delete_fdn_contact" msgid="7027405651994507077">"מחק מספר חיוג קבוע"</string>
-    <string name="deleting_fdn_contact" msgid="6872320570844460428">"מוחק מספר חיוג קבוע..."</string>
+    <string name="delete_fdn_contact" msgid="7027405651994507077">"מחיקת מספר חיוג קבוע"</string>
+    <string name="deleting_fdn_contact" msgid="6872320570844460428">"מחיקת מספר חיוג קבוע מתבצעת..."</string>
     <string name="fdn_contact_deleted" msgid="1680714996763848838">"מספר חיוג קבוע נמחק."</string>
     <string name="pin2_invalid" msgid="2313954262684494442">"רשימת מספרי חיוג קבועים לא עודכנה מכיוון שקוד הגישה שגוי."</string>
     <string name="fdn_invalid_number" msgid="9067189814657840439">"‏מספר ה-FDN לא עודכן כי הוא מכיל יותר מ-<xliff:g id="FDN_NUMBER_LIMIT_LENGTH">%d</xliff:g> ספרות."</string>
     <string name="pin2_or_fdn_invalid" msgid="7542639487955868181">"רשימת מספרי החיוג הקבועים לא עודכנה. קוד הגישה היה שגוי או שמספר הטלפון נדחה."</string>
     <string name="fdn_failed" msgid="216592346853420250">"‏פעולת FDN נכשלה."</string>
-    <string name="simContacts_emptyLoading" msgid="4989040293858675483">"‏קורא מכרטיס SIM…"</string>
+    <string name="simContacts_emptyLoading" msgid="4989040293858675483">"‏קריאה מכרטיס SIM מתבצעת…"</string>
     <string name="simContacts_empty" msgid="1135632055473689521">"‏אין אנשי קשר בכרטיס ה-SIM."</string>
-    <string name="simContacts_title" msgid="2714029230160136647">"בחר אנשי קשר לייבוא"</string>
-    <string name="simContacts_airplaneMode" msgid="4654884030631503808">"‏בטל את מצב טיסה כדי לייבא אנשי קשר מכרטיס ה-SIM."</string>
-    <string name="enable_pin" msgid="967674051730845376">"‏הפעלה/השבתה של ה-PIN של SIM"</string>
-    <string name="change_pin" msgid="3657869530942905790">"‏שנה PIN של SIM"</string>
-    <string name="enter_pin_text" msgid="3182311451978663356">"‏PIN של SIM:"</string>
-    <string name="oldPinLabel" msgid="8618515202411987721">"‏PIN ישן"</string>
-    <string name="newPinLabel" msgid="3585899083055354732">"‏PIN חדש"</string>
-    <string name="confirmPinLabel" msgid="7783531218662473778">"‏אשר PIN חדש"</string>
-    <string name="badPin" msgid="4549286285015892321">"‏ה-PIN הישן שהקלדת שגוי. נסה שוב."</string>
-    <string name="mismatchPin" msgid="1467254768290323845">"‏קודי ה-PIN שהקלדת אינם תואמים. נסה שוב."</string>
-    <string name="invalidPin" msgid="7363723429414001979">"‏הקלד PIN שאורכו 4 עד 8 ספרות."</string>
+    <string name="simContacts_title" msgid="2714029230160136647">"בחירת אנשי קשר לייבוא"</string>
+    <string name="simContacts_airplaneMode" msgid="4654884030631503808">"‏יש לבטל את מצב טיסה כדי לייבא אנשי קשר מכרטיס ה-SIM."</string>
+    <string name="enable_pin" msgid="967674051730845376">"‏הפעלה/השבתה של קוד האימות של SIM"</string>
+    <string name="change_pin" msgid="3657869530942905790">"‏שינוי קוד אימות של SIM"</string>
+    <string name="enter_pin_text" msgid="3182311451978663356">"‏קוד אימות של SIM:"</string>
+    <string name="oldPinLabel" msgid="8618515202411987721">"קוד אימות ישן"</string>
+    <string name="newPinLabel" msgid="3585899083055354732">"קוד אימות חדש"</string>
+    <string name="confirmPinLabel" msgid="7783531218662473778">"אישור קוד אימות חדש"</string>
+    <string name="badPin" msgid="4549286285015892321">"קוד האימות הישן שהקלדת שגוי. יש לנסות שוב."</string>
+    <string name="mismatchPin" msgid="1467254768290323845">"קודי האימות שהקלדת לא תואמים. יש לנסות שוב."</string>
+    <string name="invalidPin" msgid="7363723429414001979">"יש להקליד קוד אימות שאורכו 4 עד 8 ספרות."</string>
     <string name="disable_sim_pin" msgid="3112303905548613752">"‏ניקוי PIN עבור SIM"</string>
-    <string name="enable_sim_pin" msgid="445461050748318980">"‏הגדרת PIN עבור SIM"</string>
-    <string name="enable_in_progress" msgid="4135305985717272592">"‏מגדיר PIN…"</string>
-    <string name="enable_pin_ok" msgid="2877428038280804256">"‏PIN הוגדר"</string>
-    <string name="disable_pin_ok" msgid="888505244389647754">"‏ה-PIN נוקה"</string>
-    <string name="pin_failed" msgid="4527347792881939652">"‏PIN2 שגוי"</string>
-    <string name="pin_changed" msgid="7291153750090452808">"‏PIN עודכן"</string>
-    <string name="puk_requested" msgid="2061337960609806851">"‏סיסמה שגויה. PIN נעול כעת. יש להזין PUK."</string>
-    <string name="enter_pin2_text" msgid="7266379426804295979">"PIN2"</string>
-    <string name="oldPin2Label" msgid="4648543187859997203">"‏PIN2 ישן"</string>
-    <string name="newPin2Label" msgid="1840905981784453939">"‏PIN2 חדש"</string>
-    <string name="confirmPin2Label" msgid="4336025914667593762">"‏אשר PIN2 חדש"</string>
-    <string name="badPuk2" msgid="6438182906645832235">"‏PUK2 שגוי. נסה שוב."</string>
-    <string name="badPin2" msgid="2760917538643074635">"‏PIN2 הישן שגוי. נסה שוב."</string>
-    <string name="mismatchPin2" msgid="4952718725266700631">"‏קודי PIN2 לא תואמים. נסה שוב."</string>
-    <string name="invalidPin2" msgid="6467957903056379343">"‏הזן PIN2 שאורכו בין 4 ל-8 ספרות."</string>
-    <string name="invalidPuk2" msgid="713729511903849544">"‏הזן PUK2 שאורכו 8 ספרות."</string>
-    <string name="pin2_changed" msgid="5710551850481287821">"‏PIN2 עודכן"</string>
-    <string name="label_puk2_code" msgid="2852217004288085562">"‏הזן קוד PUK2"</string>
-    <string name="fdn_enable_puk2_requested" msgid="5793652792131588041">"‏סיסמה שגויה. PIN2 חסום כעת. כדי לנסות שוב, יש לשנות את PIN 2."</string>
-    <string name="puk2_requested" msgid="6992374450720307514">"‏סיסמה שגויה. ה-SIM נעול כעת. הזן PUK2."</string>
+    <string name="enable_sim_pin" msgid="445461050748318980">"‏הגדרת קוד אימות של SIM"</string>
+    <string name="enable_in_progress" msgid="4135305985717272592">"הגדרת קוד האימות מתבצעת…"</string>
+    <string name="enable_pin_ok" msgid="2877428038280804256">"קוד אימות הוגדר"</string>
+    <string name="disable_pin_ok" msgid="888505244389647754">"קוד האימות נמחק"</string>
+    <string name="pin_failed" msgid="4527347792881939652">"קוד אימות 2 שגוי"</string>
+    <string name="pin_changed" msgid="7291153750090452808">"קוד אימות עודכן"</string>
+    <string name="puk_requested" msgid="2061337960609806851">"‏סיסמה שגויה. קוד האימות נעול כעת. יש להזין PUK."</string>
+    <string name="enter_pin2_text" msgid="7266379426804295979">"קוד אימות 2"</string>
+    <string name="oldPin2Label" msgid="4648543187859997203">"קוד אימות 2 ישן"</string>
+    <string name="newPin2Label" msgid="1840905981784453939">"קוד אימות 2 חדש"</string>
+    <string name="confirmPin2Label" msgid="4336025914667593762">"אישור קוד אימות 2 חדש"</string>
+    <string name="badPuk2" msgid="6438182906645832235">"‏PUK2 שגוי. יש לנסות שוב."</string>
+    <string name="badPin2" msgid="2760917538643074635">"קוד אימות 2 הישן שגוי. יש לנסות שוב."</string>
+    <string name="mismatchPin2" msgid="4952718725266700631">"קודי אימות 2 לא תואמים. יש לנסות שוב."</string>
+    <string name="invalidPin2" msgid="6467957903056379343">"יש להזין קוד אימות 2 שאורכו בין 4 ל-8 ספרות."</string>
+    <string name="invalidPuk2" msgid="713729511903849544">"‏יש להזין PUK2 שאורכו 8 ספרות."</string>
+    <string name="pin2_changed" msgid="5710551850481287821">"קוד אימות 2 עודכן"</string>
+    <string name="label_puk2_code" msgid="2852217004288085562">"‏הזנת קוד PUK2"</string>
+    <string name="fdn_enable_puk2_requested" msgid="5793652792131588041">"‏סיסמה שגויה. קוד אימות 2 חסום כעת. כדי לנסות שוב, יש לשנות את PIN 2."</string>
+    <string name="puk2_requested" msgid="6992374450720307514">"‏סיסמה שגויה. ה-SIM נעול כעת. יש להזין PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"‏PUK2 נחסם לצמיתות."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"נותרו לך עוד <xliff:g id="NUMBER">%d</xliff:g> ניסיונות."</string>
-    <string name="pin2_unblocked" msgid="4481107908727789303">"‏PIN2 אינו חסום עוד"</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"‏נעילת PUK2. כדי לבטל את הנעילה, צריך לפנות לספק השירות."</string>
+    <string name="pin2_unblocked" msgid="4481107908727789303">"קוד אימות 2 אינו חסום עוד"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"‏שגיאת רשת או כרטיס SIM"</string>
     <string name="doneButton" msgid="7371209609238460207">"סיום"</string>
     <string name="voicemail_settings_number_label" msgid="1265118640154688162">"המספר של הדואר הקולי"</string>
-    <string name="card_title_dialing" msgid="8742182654254431781">"מחייג"</string>
-    <string name="card_title_redialing" msgid="18130232613559964">"מחייג שוב"</string>
+    <string name="card_title_dialing" msgid="8742182654254431781">"החיוג מתבצע"</string>
+    <string name="card_title_redialing" msgid="18130232613559964">"חיוג חוזר מתבצע"</string>
     <string name="card_title_conf_call" msgid="901197309274457427">"שיחת ועידה"</string>
     <string name="card_title_incoming_call" msgid="881424648458792430">"שיחה נכנסת"</string>
     <string name="card_title_call_ended" msgid="650223980095026340">"השיחה הסתיימה"</string>
     <string name="card_title_on_hold" msgid="9028319436626975207">"בהמתנה"</string>
-    <string name="card_title_hanging_up" msgid="814874106866647871">"מנתק"</string>
+    <string name="card_title_hanging_up" msgid="814874106866647871">"הניתוק מתבצע"</string>
     <string name="card_title_in_call" msgid="8231896539567594265">"בשיחה"</string>
     <string name="notification_voicemail_title" msgid="3932876181831601351">"דואר קולי חדש"</string>
     <string name="notification_voicemail_title_count" msgid="2806950319222327082">"דואר קולי חדש (<xliff:g id="COUNT">%d</xliff:g>)"</string>
-    <string name="notification_voicemail_text_format" msgid="5720947141702312537">"‏חייג ‎<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>‎"</string>
+    <string name="notification_voicemail_text_format" msgid="5720947141702312537">"‏חיוג ל-‎<xliff:g id="VOICEMAIL_NUMBER">%s</xliff:g>‎"</string>
     <string name="notification_voicemail_no_vm_number" msgid="3423686009815186750">"המספר של הדואר הקולי אינו ידוע"</string>
     <string name="notification_network_selection_title" msgid="255595526707809121">"אין שירות"</string>
     <string name="notification_network_selection_text" msgid="553288408722427659">"הרשת שנבחרה (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) לא זמינה"</string>
     <string name="incall_error_power_off" product="watch" msgid="7191184639454113633">"עליך להפעיל את הרשת הסלולרית, להשבית את מצב הטיסה או להשבית את מצב החיסכון בסוללה כדי להתקשר."</string>
-    <string name="incall_error_power_off" product="default" msgid="8131672264311208673">"בטל את מצב טיסה כדי לבצע שיחה."</string>
-    <string name="incall_error_power_off_wfc" msgid="9125661184694727052">"בטל את מצב טיסה או התחבר לרשת אלחוטית כדי לבצע שיחה."</string>
+    <string name="incall_error_power_off" product="default" msgid="8131672264311208673">"יש לבטל את מצב טיסה כדי לבצע שיחה."</string>
+    <string name="incall_error_power_off_wfc" msgid="9125661184694727052">"יש לבטל את מצב טיסה או להתחבר לרשת אלחוטית כדי לבצע שיחה."</string>
     <string name="incall_error_ecm_emergency_only" msgid="5622379058883722080">"עליך לצאת ממצב חירום של התקשרות חזרה כדי לבצע שיחות שאינן שיחות חירום."</string>
     <string name="incall_error_emergency_only" msgid="8786127461027964653">"לא רשום ברשת."</string>
     <string name="incall_error_out_of_service" msgid="1927265196942672791">"הרשת הסלולרית אינה זמינה."</string>
     <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_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>
@@ -549,8 +553,8 @@
     <string name="incall_error_supp_service_reject" msgid="3044363092441655912">"לא ניתן לדחות שיחה."</string>
     <string name="incall_error_supp_service_hangup" msgid="836524952243836735">"לא ניתן לשחרר שיחות."</string>
     <string name="incall_error_supp_service_hold" msgid="8535056414643540997">"לא ניתן להחזיק שיחות."</string>
-    <string name="incall_error_wfc_only_no_wireless_network" msgid="5860742792811400109">"התחבר לרשת אלחוטית כדי לבצע שיחה."</string>
-    <string name="incall_error_promote_wfc" msgid="9164896813931363415">"‏הפעל את \'שיחות Wi-Fi\' כדי להתקשר."</string>
+    <string name="incall_error_wfc_only_no_wireless_network" msgid="5860742792811400109">"יש להתחבר לרשת אלחוטית כדי לבצע שיחה."</string>
+    <string name="incall_error_promote_wfc" msgid="9164896813931363415">"‏יש להפעיל את \'שיחות Wi-Fi\' כדי להתקשר."</string>
     <string name="emergency_information_hint" msgid="9208897544917793012">"מידע למקרה חירום"</string>
     <string name="emergency_information_owner_hint" msgid="6256909888049185316">"בעלים"</string>
     <string name="emergency_information_confirm_hint" msgid="5109017615894918914">"אפשר להקיש שוב כדי להציג את הפרטים"</string>
@@ -558,19 +562,19 @@
     <string name="single_emergency_number_title" msgid="8413371079579067196">"מספר חירום"</string>
     <string name="numerous_emergency_numbers_title" msgid="8972398932506755510">"מספרי חירום"</string>
     <string name="emergency_call_shortcut_hint" msgid="1290485125107779500">"אפשר להקיש שוב כדי להתקשר אל <xliff:g id="EMERGENCY_NUMBER">%s</xliff:g>"</string>
-    <string name="emergency_enable_radio_dialog_message" msgid="1695305158151408629">"מפעיל את הרדיו…"</string>
-    <string name="emergency_enable_radio_dialog_retry" msgid="4329131876852608587">"אין שירות. מנסה שוב..."</string>
+    <string name="emergency_enable_radio_dialog_message" msgid="1695305158151408629">"הפעלת הרדיו מתבצעת…"</string>
+    <string name="emergency_enable_radio_dialog_retry" msgid="4329131876852608587">"אין שירות. ניסיון חוזר מתבצע..."</string>
     <string name="radio_off_during_emergency_call" msgid="8011154134040481609">"אי אפשר לעבור למצב טיסה בזמן שיחת חירום."</string>
     <string name="dial_emergency_error" msgid="825822413209026039">"לא ניתן להתקשר. <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> אינו מספר חירום."</string>
-    <string name="dial_emergency_empty_error" msgid="2785803395047793634">"לא ניתן להתקשר. חייג למספר חירום."</string>
+    <string name="dial_emergency_empty_error" msgid="2785803395047793634">"לא ניתן להתקשר. יש לחייג למספר חירום."</string>
     <string name="dial_emergency_calling_not_available" msgid="6485846193794727823">"אי אפשר לבצע שיחות חירום"</string>
     <string name="pin_puk_system_user_only" msgid="1045147220686867922">"‏רק הבעלים של המכשיר יכול להזין קודי אימות/PUK."</string>
     <string name="police_type_description" msgid="2819533883972081757">"משטרה"</string>
     <string name="ambulance_type_description" msgid="6798237503553180461">"אמבולנס"</string>
     <string name="fire_type_description" msgid="6565200468934914930">"אש"</string>
     <string name="description_concat_format" msgid="2014471565101724088">"‏%1$s,‏ %2$s"</string>
-    <string name="dialerKeyboardHintText" msgid="1115266533703764049">"השתמש במקלדת כדי לחייג"</string>
-    <string name="onscreenHoldText" msgid="4025348842151665191">"השהה"</string>
+    <string name="dialerKeyboardHintText" msgid="1115266533703764049">"שימוש במקלדת כדי לחייג"</string>
+    <string name="onscreenHoldText" msgid="4025348842151665191">"השהיה"</string>
     <string name="onscreenEndCallText" msgid="6138725377654842757">"סיום"</string>
     <string name="onscreenShowDialpadText" msgid="658465753816164079">"לוח חיוג"</string>
     <string name="onscreenMuteText" msgid="5470306116733843621">"השתקה"</string>
@@ -581,10 +585,10 @@
     <string name="onscreenManageConferenceText" msgid="4700574060601755137">"ניהול שיחת ועידה"</string>
     <string name="onscreenAudioText" msgid="7224226735052019986">"אודיו"</string>
     <string name="onscreenVideoCallText" msgid="1743992456126258698">"שיחת וידאו"</string>
-    <string name="importSimEntry" msgid="3892354284082689894">"ייבא"</string>
-    <string name="importAllSimEntries" msgid="2628391505643564007">"ייבא הכל"</string>
-    <string name="importingSimContacts" msgid="4995457122107888932">"‏מייבא אנשי קשר מ-SIM"</string>
-    <string name="importToFDNfromContacts" msgid="5068664870738407341">"ייבא מאנשי קשר"</string>
+    <string name="importSimEntry" msgid="3892354284082689894">"ייבוא"</string>
+    <string name="importAllSimEntries" msgid="2628391505643564007">"ייבוא הכול"</string>
+    <string name="importingSimContacts" msgid="4995457122107888932">"‏ייבוא אנשי קשר מ-SIM מתבצע"</string>
+    <string name="importToFDNfromContacts" msgid="5068664870738407341">"ייבוא מאנשי קשר"</string>
     <string name="singleContactImportedMsg" msgid="3619804066300998934">"איש הקשר יובא"</string>
     <string name="failedToImportSingleContactMsg" msgid="228095510489830266">"ייבוא איש הקשר נכשל"</string>
     <string name="hac_mode_title" msgid="4127986689621125468">"מכשירי שמיעה"</string>
@@ -600,16 +604,16 @@
     <item msgid="2131559553795606483">"TTY VCO"</item>
   </string-array>
     <string name="dtmf_tones_title" msgid="7874845461117175236">"‏צלילי DTMF"</string>
-    <string name="dtmf_tones_summary" msgid="2294822239899471201">"‏הגדר את האורך של צלילי DTMF"</string>
+    <string name="dtmf_tones_summary" msgid="2294822239899471201">"‏הגדרת האורך של צלילי DTMF"</string>
   <string-array name="dtmf_tone_entries">
     <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="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>
+    <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>
     <string name="ota_hfa_activation_dialog_message" msgid="7921718445773342996">"הטלפון מפעיל את שירותי הנתונים שלך לנייד.\n\nהתהליך עשוי לארוך עד 5 דקות."</string>
     <string name="ota_skip_activation_dialog_title" msgid="7666611236789203797">"לדלג על ההפעלה?"</string>
     <string name="ota_skip_activation_dialog_message" msgid="6691722887019708713">"‏אם מדלגים על ההפעלה, אי אפשר להתקשר או להתחבר לרשתות נתונים לנייד (אך אפשר להתחבר לרשתות Wi-Fi). עד להפעלת הטלפון תופיע הודעה שמבקשת לבצע את ההפעלה בכל פעם שמדליקים את הטלפון."</string>
@@ -617,26 +621,26 @@
     <string name="ota_activate" msgid="7939695753665438357">"הפעלה"</string>
     <string name="ota_title_activate_success" msgid="1272135024761004889">"הטלפון מופעל."</string>
     <string name="ota_title_problem_with_activation" msgid="7019745985413368726">"בעיה בהפעלה"</string>
-    <string name="ota_listen" msgid="2772252405488894280">"בצע את ההוראות הנאמרות עד שתשמע שההפעלה הושלמה."</string>
+    <string name="ota_listen" msgid="2772252405488894280">"יש לבצע את ההוראות הנאמרות עד ששומעים שההפעלה הושלמה."</string>
     <string name="ota_speaker" msgid="1086766980329820528">"רמקול"</string>
-    <string name="ota_progress" msgid="8837259285255700132">"מתכנת את הטלפון שלך…"</string>
+    <string name="ota_progress" msgid="8837259285255700132">"התכנות של הטלפון שלך מתבצע…"</string>
     <string name="ota_failure" msgid="5674217489921481576">"לא ניתן היה לתכנת את הטלפון שלך"</string>
-    <string name="ota_successful" msgid="1106825981548107774">"הטלפון שלך מופעל כעת. ייתכן שתמתין עד 15 דקות להפעלת השירות."</string>
+    <string name="ota_successful" msgid="1106825981548107774">"הטלפון שלך מופעל כעת. ייתכן שיהיה צורך להמתין עד 15 דקות להפעלת השירות."</string>
     <string name="ota_unsuccessful" msgid="8531037653803955754">"הטלפון שלך לא הופעל. \nייתכן שעליך למצוא מקום שהקליטה בו טובה יותר (ליד חלון או בחוץ). \n\nנסה שוב או התקשר לשירות לקוחות לקבלת אפשרויות נוספות."</string>
     <string name="ota_spc_failure" msgid="904092035241370080">"‏כשלים מרובים של SPC"</string>
     <string name="ota_call_end" msgid="8657746378290737034">"הקודם"</string>
-    <string name="ota_try_again" msgid="6914781945599998550">"נסו שוב"</string>
+    <string name="ota_try_again" msgid="6914781945599998550">"יש לנסות שוב"</string>
     <string name="ota_next" msgid="2041016619313475914">"הבא"</string>
     <string name="ecm_exit_dialog" msgid="4200691880721429078">"EcmExitDialog"</string>
-    <string name="phone_entered_ecm_text" msgid="8431238297843035842">"נכנס למצב חירום של התקשרות חזרה"</string>
+    <string name="phone_entered_ecm_text" msgid="8431238297843035842">"מתבצע מעבר למצב חירום של התקשרות חזרה"</string>
     <string name="phone_in_ecm_notification_title" msgid="6825016389926367946">"מצב חירום של התקשרות חזרה"</string>
     <string name="phone_in_ecm_call_notification_text" msgid="653972232922670335">"חיבור נתונים מושבת"</string>
     <string name="phone_in_ecm_notification_complete_time" msgid="7341624337163082759">"אין חיבור לרשת נתונים עד <xliff:g id="COMPLETETIME">%s</xliff:g>"</string>
     <plurals name="alert_dialog_exit_ecm" formatted="false" msgid="5425906903766466743">
-      <item quantity="two">הטלפון יהיה במצב חירום של התקשרות חזרה למשך <xliff:g id="COUNT_1">%s</xliff:g> דקות. בזמן שהטלפון במצב הזה, לא ניתן להשתמש באפליקציות הזקוקות לחיבור נתונים. האם ברצונך לצאת כעת?</item>
-      <item quantity="many">הטלפון יהיה במצב חירום של התקשרות חזרה למשך <xliff:g id="COUNT_1">%s</xliff:g> דקות. בזמן שהטלפון במצב הזה, לא ניתן להשתמש באפליקציות הזקוקות לחיבור נתונים. האם ברצונך לצאת כעת?</item>
-      <item quantity="other">הטלפון יהיה במצב חירום של התקשרות חזרה למשך <xliff:g id="COUNT_1">%s</xliff:g> דקות. בזמן שהטלפון במצב הזה, לא ניתן להשתמש באפליקציות הזקוקות לחיבור נתונים. האם ברצונך לצאת כעת?</item>
-      <item quantity="one">הטלפון יהיה במצב חירום של התקשרות חזרה למשך דקה <xliff:g id="COUNT_0">%s</xliff:g>. בזמן שהטלפון במצב הזה, לא ניתן להשתמש באפליקציות הזקוקות לחיבור נתונים. האם ברצונך לצאת כעת?</item>
+      <item quantity="two">הטלפון יהיה במצב חירום של התקשרות חזרה למשך <xliff:g id="COUNT_1">%s</xliff:g> דקות. בזמן שהטלפון במצב הזה, לא ניתן להשתמש באפליקציות הזקוקות לחבילת גלישה. האם ברצונך לצאת כעת?</item>
+      <item quantity="many">הטלפון יהיה במצב חירום של התקשרות חזרה למשך <xliff:g id="COUNT_1">%s</xliff:g> דקות. בזמן שהטלפון במצב הזה, לא ניתן להשתמש באפליקציות הזקוקות לחבילת גלישה. האם ברצונך לצאת כעת?</item>
+      <item quantity="other">הטלפון יהיה במצב חירום של התקשרות חזרה למשך <xliff:g id="COUNT_1">%s</xliff:g> דקות. בזמן שהטלפון במצב הזה, לא ניתן להשתמש באפליקציות הזקוקות לחבילת גלישה. האם ברצונך לצאת כעת?</item>
+      <item quantity="one">הטלפון יהיה במצב חירום של התקשרות חזרה למשך דקה <xliff:g id="COUNT_0">%s</xliff:g>. בזמן שהטלפון במצב הזה, לא ניתן להשתמש באפליקציות הזקוקות לחבילת גלישה. האם ברצונך לצאת כעת?</item>
     </plurals>
     <plurals name="alert_dialog_not_avaialble_in_ecm" formatted="false" msgid="1152682528741457004">
       <item quantity="two">הפעולה הנבחרת אינה זמינה במצב חירום של התקשרות חזרה. הטלפון יהיה במצב זה למשך <xliff:g id="COUNT_1">%s</xliff:g> דקות. האם ברצונך לצאת כעת?</item>
@@ -648,7 +652,7 @@
     <string name="progress_dialog_exiting_ecm" msgid="9159080081676927217">"יציאה ממצב התקשרות חזרה בחירום"</string>
     <string name="alert_dialog_yes" msgid="3532525979632841417">"כן"</string>
     <string name="alert_dialog_no" msgid="1075632654085988420">"לא"</string>
-    <string name="alert_dialog_dismiss" msgid="1336356286354517054">"סגור"</string>
+    <string name="alert_dialog_dismiss" msgid="1336356286354517054">"סגירה"</string>
     <string name="phone_in_ecm_call_notification_text_without_data_restriction_hint" msgid="3747860785153531225">"הטלפון במצב \'התקשרות בחזרה בחירום\'"</string>
     <string name="phone_in_ecm_notification_complete_time_without_data_restriction_hint" msgid="3690292264812050858">"עד <xliff:g id="COMPLETETIME">%s</xliff:g>"</string>
     <plurals name="alert_dialog_exit_ecm_without_data_restriction_hint" formatted="false" msgid="6477733043040328640">
@@ -663,10 +667,10 @@
     <string name="other_settings" msgid="8895088007393598447">"הגדרות שיחה אחרות"</string>
     <string name="calling_via_template" msgid="1791323450703751750">"שיחה באמצעות <xliff:g id="PROVIDER_NAME">%s</xliff:g>"</string>
     <string name="contactPhoto" msgid="7885089213135154834">"תמונה של איש קשר"</string>
-    <string name="goPrivate" msgid="4645108311382209551">"עבור לשיחה פרטית"</string>
-    <string name="selectContact" msgid="1527612842599767382">"בחר איש קשר"</string>
+    <string name="goPrivate" msgid="4645108311382209551">"מעבר לשיחה פרטית"</string>
+    <string name="selectContact" msgid="1527612842599767382">"בחירת איש קשר"</string>
     <string name="not_voice_capable" msgid="2819996734252084253">"חיוג קולי אינו נתמך"</string>
-    <string name="description_dial_button" msgid="8614631902795087259">"חייג"</string>
+    <string name="description_dial_button" msgid="8614631902795087259">"חויג"</string>
     <string name="description_dialpad_button" msgid="7395114120463883623">"הצגת לוח החיוג"</string>
     <string name="pane_title_emergency_dialpad" msgid="3627372514638694401">"לוח חיוג לחירום"</string>
     <string name="voicemail_visual_voicemail_switch_title" msgid="6610414098912832120">"דואר קולי ויזואלי"</string>
@@ -674,10 +678,10 @@
     <string name="voicemail_change_pin_dialog_title" msgid="4633077715231764435">"שינוי קוד הגישה"</string>
     <string name="preference_category_ringtone" msgid="8787281191375434976">"רינגטון ורטט"</string>
     <string name="pstn_connection_service_label" msgid="9200102709997537069">"‏כרטיסי SIM מובנים"</string>
-    <string name="enable_video_calling_title" msgid="7246600931634161830">"הפעל שיחות וידאו"</string>
+    <string name="enable_video_calling_title" msgid="7246600931634161830">"הפעלת שיחות וידאו"</string>
     <string name="enable_video_calling_dialog_msg" msgid="7141478720386203540">"‏כדי להפעיל שיחת וידאו, עליך להפעיל את \'מצב 4G LTE משופר\' ב\'הגדרות רשת\'."</string>
     <string name="enable_video_calling_dialog_settings" msgid="8697890611305307110">"הגדרות רשת"</string>
-    <string name="enable_video_calling_dialog_close" msgid="4298929725917045270">"סגור"</string>
+    <string name="enable_video_calling_dialog_close" msgid="4298929725917045270">"סגירה"</string>
     <string name="sim_label_emergency_calls" msgid="9078241989421522310">"שיחות חירום"</string>
     <string name="sim_description_emergency_calls" msgid="5146872803938897296">"שיחות חירום בלבד"</string>
     <string name="sim_description_default" msgid="7474671114363724971">"‏כרטיס SIM, חריץ: <xliff:g id="SLOT_ID">%s</xliff:g>"</string>
@@ -688,7 +692,7 @@
     <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>
+    <string name="callFailed_imei_not_accepted" msgid="7257903653685147251">"‏לא ניתן להתחבר. יש להכניס כרטיס SIM תקין."</string>
     <string name="callFailed_wifi_lost" msgid="1788036730589163141">"‏השיחה הסתיימה כי החיבור לרשת ה-Wi-Fi נותק."</string>
     <string name="dialFailed_low_battery" msgid="6857904237423407056">"אי אפשר לבצע את שיחת הווידאו מכיוון שהסוללה חלשה."</string>
     <string name="callFailed_low_battery" msgid="4056828320214416182">"שיחת הווידאו הסתיימה מכיוון שהסוללה חלשה."</string>
@@ -698,12 +702,12 @@
     <string name="change_pin_continue_label" msgid="5177011752453506371">"המשך"</string>
     <string name="change_pin_cancel_label" msgid="2301711566758827936">"ביטול"</string>
     <string name="change_pin_ok_label" msgid="6861082678817785330">"אישור"</string>
-    <string name="change_pin_enter_old_pin_header" msgid="853151335217594829">"אשר את קוד הגישה הישן"</string>
-    <string name="change_pin_enter_old_pin_hint" msgid="8801292976275169367">"הזן את קוד הגישה לדואר הקולי כדי להמשיך."</string>
-    <string name="change_pin_enter_new_pin_header" msgid="4739465616733486118">"הגדר קוד גישה חדש"</string>
+    <string name="change_pin_enter_old_pin_header" msgid="853151335217594829">"אישור קוד הגישה הישן"</string>
+    <string name="change_pin_enter_old_pin_hint" msgid="8801292976275169367">"יש להזין את קוד הגישה לדואר הקולי כדי להמשיך."</string>
+    <string name="change_pin_enter_new_pin_header" msgid="4739465616733486118">"הגדרת קוד גישה חדש"</string>
     <string name="change_pin_enter_new_pin_hint" msgid="2326038476516364210">"האורך של קוד הגישה חייב להיות <xliff:g id="MAX">%2$d</xliff:g>-<xliff:g id="MIN">%1$d</xliff:g> ספרות."</string>
-    <string name="change_pin_confirm_pin_header" msgid="2606303906320705726">"אשר את קוד הגישה"</string>
-    <string name="change_pin_confirm_pins_dont_match" msgid="305164501222587215">"קודי הגישה אינם תואמים"</string>
+    <string name="change_pin_confirm_pin_header" msgid="2606303906320705726">"אישור קוד הגישה"</string>
+    <string name="change_pin_confirm_pins_dont_match" msgid="305164501222587215">"קודי הגישה לא תואמים"</string>
     <string name="change_pin_succeeded" msgid="2504705600693014403">"קוד הגישה לדואר הקולי עודכן"</string>
     <string name="change_pin_system_error" msgid="7772788809875146873">"לא ניתן להגדיר את קוד הגישה"</string>
     <string name="mobile_data_status_roaming_turned_off_subtext" msgid="6840673347416227054">"חבילת הגלישה בנדידה הושבתה"</string>
@@ -902,7 +906,7 @@
     <string name="radio_info_data_network_type_label" msgid="8886597029237501929">"סוג רשת נתונים:"</string>
     <string name="phone_index_label" msgid="6222406512768964268">"בחירת אינדקס טלפון"</string>
     <string name="radio_info_set_perferred_label" msgid="7408131389363136210">"הגדרת סוג רשת מועדף:"</string>
-    <string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"‏נדנד לשם המארח (www.google.com) מסוג IPv4:"</string>
+    <string name="radio_info_ping_hostname_v4" msgid="6951237885381284790">"‏נדנוד לשם המארח (www.google.com) מסוג IPv4:"</string>
     <string name="radio_info_ping_hostname_v6" msgid="2748637889486554603">"‏נדנוד לשם המארח (www.google.com) מסוג IPv6:"</string>
     <string name="radio_info_http_client_test" msgid="1329583721088428238">"‏בדיקת לקוח HTTP:"</string>
     <string name="ping_test_label" msgid="448617502935719694">"הפעלת בדיקת פינג"</string>
@@ -917,14 +921,15 @@
     <string name="radio_info_nr_state" msgid="1337571996788535356">"‏מצב 5G NR:"</string>
     <string name="radio_info_nr_frequency" msgid="1201156032796584128">"‏תדירות 5G NR:"</string>
     <string name="band_mode_title" msgid="7988822920724576842">"הגדרת מצב תדרים של רדיו"</string>
-    <string name="band_mode_loading" msgid="795923726636735967">"טוען רשימת תדרים…"</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>
+    <string name="band_mode_succeeded" msgid="2230018000534761063">"המעבר הצליח"</string>
     <string name="phone_info_label" product="tablet" msgid="7477478709388477397">"פרטי טאבלט"</string>
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"פרטי טלפון"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"פרטי ניהול תצורה של ספק סלולרי"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"הפעלת ניהול תצורה של ספק סלולרי"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"‏הצעה: שיפור הקישוריות ל-Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"‏הקליטה של ה-Bluetooth חלשה. כדאי לעבור לדיבורית."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"התראה על איכות השיחה"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"‏חשבונות SIP שהוצאו משימוש"</string>
 </resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index b3f6683..c2adb1e 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"SIM 機能の制限"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> の通話およびデータサービスは、<xliff:g id="PHONE_NUMBER">%2$s</xliff:g> を使用している間はブロックされる可能性があります。"</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"別の SIM を使用中は、<xliff:g id="CARRIER_NAME">%1$s</xliff:g> の通話とデータサービスがブロックされる可能性があります。"</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"サポートが終了した SIP アカウントが検出されたため、削除いたしました"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP 通話は、Android プラットフォームではサポートされなくなりました。\nこのため、お客様の既存の SIP アカウント(<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>)を削除いたしました。\nデフォルトの通話アカウントの設定をご確認ください。"</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"設定に移動"</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>
@@ -487,7 +490,7 @@
     <string name="newPinLabel" msgid="3585899083055354732">"新しいPIN"</string>
     <string name="confirmPinLabel" msgid="7783531218662473778">"新しいPINを確認"</string>
     <string name="badPin" msgid="4549286285015892321">"入力した古いPINは正しくありません。入力し直してください。"</string>
-    <string name="mismatchPin" msgid="1467254768290323845">"入力したPINは一致しません。入力し直してください。"</string>
+    <string name="mismatchPin" msgid="1467254768290323845">"入力した PIN が一致しません。入力し直してください。"</string>
     <string name="invalidPin" msgid="7363723429414001979">"4~8桁の数字のPINを入力してください。"</string>
     <string name="disable_sim_pin" msgid="3112303905548613752">"SIM PINの消去"</string>
     <string name="enable_sim_pin" msgid="445461050748318980">"SIM PINの設定"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"パスワードが正しくありません。SIMはロックされました。PUK2を入力してください。"</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2は完全にブロックされています。"</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"入力できるのはあと<xliff:g id="NUMBER">%d</xliff:g>回です。"</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 でロックされています。ロックを解除するには、サービス プロバイダにお問い合わせください。"</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2はブロックされなくなりました"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"ネットワークまたはSIMカードのエラー"</string>
     <string name="doneButton" msgid="7371209609238460207">"完了"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"スマートフォン情報"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"携帯通信会社のプロビジョニング情報"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"携帯通信会社のプロビジョニングをトリガー"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Bluetooth の接続性を改善することをおすすめします"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth の信号強度が十分ではありません。スピーカーフォンに切り替えてみてください。"</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"通話品質に関するお知らせ"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"サポートが終了した SIP アカウント"</string>
 </resources>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index b784dfe..d37e85b 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"SIM ბარათის შეზღუდული ფუნქციონალობა"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g>-ის ზარებისა და მობილური ინტერნეტის სერვისები შეიძლება დაიბლოკოს <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>-ის გამოყენებისას."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> ზარები და მობ.ინტ. სერვისები შეიძლება დაიბლოკოს სხვა SIM ბარათის გამოყენებისას."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"მოძველებული SIP ანგარიშები მოიძებნა და წაიშალა"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP დარეკვა აღარ არის მხარდაჭერილი Android პლატფორმით.\nთქვენი არსებული SIP ანგარიშები <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> წაშლილია.\nგთხოვთ, დაადასტუროთ თქვენი დარეკვის ნაგულისხმევი ანგარიშის პარამეტრი."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"პარამეტრებზე გადასვლა"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"აპის მონაცემთა გამოყენება"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> მობილური ინტერნეტი გამოყენებულია: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"გაფართოებული"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"პაროლი არასწორია. SIM ახლა დაბლოკილია. შეიყვანეთ PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 სამუდამოდ დაიბლოკა."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n" თქვენ გაქვთ <xliff:g id="NUMBER">%d</xliff:g> დარჩენილი მცდელობა."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 ჩაკეტილია. განსაბლოკად დაუკავშირდით სერვისის პროვაიდერს."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 აღარ არის დაბლოკილი"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"ქსელის ან SIM ბარათის შეცდომა"</string>
     <string name="doneButton" msgid="7371209609238460207">"დასრულდა"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"ტელეფონის ინფორმაცია"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"ოპერატორის უზრუნველყოფის ინფორმაცია"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"ოპერატორის უზრუნველყოფის გააქტიურება"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"შეთავაზება: გააუმჯობესეთ Bluetooth კავშირი"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"თქვენი Bluetooth სიგნალი სუსტია. სცადეთ სპიკერფონზე გადართვა."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"შეტყობინება ზარის ხარისხის შესახებ"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"მოძველებული SIP ანგარიშები"</string>
 </resources>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 77219a3..9122726 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -285,7 +285,7 @@
     <string name="roaming_enable" msgid="6853685214521494819">"Роуминг кезінде дерек тасымалдау қызметтеріне қосылу"</string>
     <string name="roaming_disable" msgid="8856224638624592681">"Роуминг кезінде дерек тасымалдау қызметтеріне қосылу"</string>
     <string name="roaming_reenable_message" msgid="1951802463885727915">"Роуминг өшірулі. Қосу үшін түртіңіз."</string>
-    <string name="roaming_enabled_message" msgid="9022249120750897">"Роуминг ақылары алынуы мүмкін. Өзгерту үшін түртіңіз."</string>
+    <string name="roaming_enabled_message" msgid="9022249120750897">"Роуминг үшін ақы алынуы мүмкін. Өзгерту үшін түртіңіз."</string>
     <string name="roaming_notification_title" msgid="3590348480688047320">"Ұялы байланыс жоғалып кетті"</string>
     <string name="roaming_on_notification_title" msgid="7451473196411559173">"Деректер роумингі қосулы"</string>
     <string name="roaming_warning" msgid="7855681468067171971">"Қомақты ақы алынуы мүмкін."</string>
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"SIM картасының жұмысы шектеулі"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g> нөмірін пайдаланған кезде <xliff:g id="CARRIER_NAME">%1$s</xliff:g> қоңыраулары мен дерек тасымалдау қызметтері бөгелуі мүмкін."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Екінші SIM картасын пайдаланылғанда, <xliff:g id="CARRIER_NAME">%1$s</xliff:g> қоңырауы мен дерек қызметі бөгелуі мүмкін."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Қолданыстан шыққан SIP есептік жазбалары табылды және өшірілді"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Android платформасында бұдан былай SIP арқылы қоңырау шалу мүмкін емес.\nҚазіргі SIP есептік жазбаларыңыз (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) өшірілді.\nҚоңырау шалуға қолданылатын әдепкі есептік жазба параметрін растаңыз."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Параметрлерге өту"</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>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Құпия сөз дұрыс емес. SIM қазір бекітілген. PUK2 кодын енгізіңіз."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 уақытша бөгелген."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"<xliff:g id="NUMBER">%d</xliff:g> әрекет қалды."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 құлыптаулы. Құлпын ашу үшін қызмет көрсетушіге хабарласыңыз."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 енді бөгелмеген"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Желі немесе SIM картасының қатесі"</string>
     <string name="doneButton" msgid="7371209609238460207">"Дайын"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Телефон ақпараты"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Операторды инициализациялау туралы ақпарат"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Операторды инциализациялауды іске қосу"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Ұсыныс: Bluetooth байланысының сапасын жақсартыңыз"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth сигналы нашар. Спикерфонға ауысып көріңіз."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Қоңырау сапасы туралы хабарландыру"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Қолданыстан шыққан SIP есептік жазбалары"</string>
 </resources>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 3bb7e10..b0fad81 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"មុខងារ​ស៊ីម​មាន​កម្រិត"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"សេវាកម្ម​ទិន្នន័យ និង​ការហៅ​ទូរសព្ទ​តាម <xliff:g id="CARRIER_NAME">%1$s</xliff:g> អាច​ត្រូវ​បាន​ទប់ស្កាត់ ពេល​ប្រើប្រាស់ <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>។"</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"សេវាកម្មទិន្នន័យ និងការហៅទូរសព្ទតាម <xliff:g id="CARRIER_NAME">%1$s</xliff:g> អាចត្រូវបានទប់ស្កាត់ ពេលប្រើស៊ីម​ផ្សេង។"</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"បានរកឃើញ និងបានលុប​គណនី SIP ដែលបានបញ្ឈប់"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"ការហៅទូរសព្ទ​តាម SIP មិនអាចប្រើ​ជាមួយប្រព័ន្ធ Android បានទៀតទេ។\nគណនី SIP ដែលមានស្រាប់​របស់អ្នក <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> ត្រូវបានលុប។\nសូមបញ្ជាក់​ការកំណត់គណនីនៃ​ការហៅទូរសព្ទ​លំនាំដើមរបស់អ្នក។"</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"ចូលទៅកាន់ការកំណត់"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"ការប្រើប្រាស់​ទិន្នន័យកម្មវិធី"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"បានប្រើទិន្នន័យទូរសព្ទអស់ <xliff:g id="ID_1">%1$s</xliff:g> នៅថ្ងៃទី <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"កម្រិតខ្ពស់"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"ពាក្យសម្ងាត់មិនត្រឹមត្រូវ។ ឥឡូវនេះស៊ីមកាតត្រូវបានចាក់សោ។ បញ្ចូល PUK2។"</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"លេខ​កូដ PUK2 ត្រូវ​បាន​​ទប់ស្កាត់​ជា​អចិន្ត្រៃយ៍។"</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"អ្នកនៅសល់ការព្យាយាមបញ្ចូល <xliff:g id="NUMBER">%d</xliff:g> ដងទៀត។"</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"បាន​ចាក់​សោ​ដោយ​ប្រើ PUK2។ សូម​ទាក់ទង​ទៅ​ក្រុមហ៊ុនផ្ដល់សេវា ដើម្បី​ដោះ​សោ។"</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"លេខសម្ងាត់ PIN2 មិនត្រូវបានរារាំងទៀតទេ"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"បញ្ហាស៊ីមកាត ឬបណ្ដាញ។"</string>
     <string name="doneButton" msgid="7371209609238460207">"រួចរាល់"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"ព័ត៌មាន​ទូរសព្ទ"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"ព័ត៌មាន​អំពី​ការផ្តល់​ទិន្នន័យ​របស់​ក្រុមហ៊ុន​សេវា​ទូរសព្ទ"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"នាំ​ឱ្យ​មាន​ការផ្តល់​ទិន្នន័យ​ពី​ក្រុមហ៊ុន​សេវា​ទូរសព្ទ"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"ការណែនាំ៖ កែលម្អ​ការតភ្ជាប់ប៊្លូធូស"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"រលកសញ្ញាប៊្លូធូសរបស់អ្នក​មានកម្រិតខ្សោយ។ សូមសាកល្បងប្ដូរ​ទៅឧបករណ៍បំពងសំឡេងទូរសព្ទ។"</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"ការជូនដំណឹង​អំពីគុណភាព​ហៅទូរសព្ទ"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"គណនី SIP ដែលបានបញ្ឈប់"</string>
 </resources>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 40f397e..a12b794 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"ಸೀಮಿತ ಸಿಮ್ ಕಾರ್ಯನಿರ್ವಹಣೆ"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g> ಬಳಸುತ್ತಿರುವಾಗ <xliff:g id="CARRIER_NAME">%1$s</xliff:g> ಕರೆಗಳು ಮತ್ತು ಡೇಟಾ ಸೇವೆಗಳನ್ನು ನಿರ್ಬಂಧಿಸಬಹುದು."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"ಬೇರೊಂದು ಸಿಮ್ ಬಳಸುತ್ತಿರುವಾಗ <xliff:g id="CARRIER_NAME">%1$s</xliff:g> ಕರೆಗಳು ಮತ್ತು ಡೇಟಾ ಸೇವೆಗಳನ್ನು ನಿರ್ಬಂಧಿಸಬಹುದು."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"ತಡೆಹಿಡಿಯಲಾಗಿರುವ SIP ಖಾತೆಗಳನ್ನು ಪತ್ತೆಮಾಡಲಾಗಿದೆ ಮತ್ತು ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Android ಪ್ಲ್ಯಾಟ್‌ಫಾರ್ಮ್ ಇನ್ನು ಮುಂದೆ SIP ಕರೆ ಮಾಡುವಿಕೆಯನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ.\nನಿಮ್ಮ ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ SIP ಖಾತೆಗಳನ್ನು <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> ತೆಗೆದುಹಾಕಲಾಗಿದೆ.\nನಿಮ್ಮ ಡೀಫಾಲ್ಟ್ ಕರೆ ಮಾಡುವಿಕೆ ಖಾತೆ ಸೆಟ್ಟಿಂಗ್ ಅನ್ನು ಖಚಿತಪಡಿಸಿ."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಗೆ ಹೋಗಿ"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"ಅಪ್ಲಿಕೇಶನ್‌ ಡೇಟಾ ಬಳಕೆ"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> ಬಳಸಲಾದ ಮೊಬೈಲ್ ಡೇಟಾ <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"ಸುಧಾರಿತ"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"ಪಾಸ್‌ವರ್ಡ್ ತಪ್ಪಾಗಿದೆ. SIM ಅನ್ನು ಇದೀಗ ಲಾಕ್ ಮಾಡಲಾಗಿದೆ. PUK2 ನಮೂದಿಸಿ."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 ಅನ್ನು ಶಾಶ್ವತವಾಗಿ ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"ನೀವು <xliff:g id="NUMBER">%d</xliff:g> ಬಾಕಿ ಉಳಿದಿರುವ ಪ್ರಯತ್ನಗಳನ್ನು ಹೊಂದಿರುವಿರಿ."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 ಲಾಕ್ ಆಗಿದೆ. ಅನ್‌ಲಾಕ್ ಮಾಡಲು ಸೇವೆ ಒದಗಿಸುವವರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 ಇನ್ನು ಮುಂದೆ ನಿರ್ಬಂಧಿತವಾಗಿರುವುದಿಲ್ಲ"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"ನೆಟ್‌ವರ್ಕ್‌ ಅಥವಾ ಸಿಮ್ ಕಾರ್ಡ್‌ ದೋಷ"</string>
     <string name="doneButton" msgid="7371209609238460207">"ಮುಗಿದಿದೆ"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"ಫೋನ್ ಮಾಹಿತಿ"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"ವಾಹಕ ಪೂರೈಕೆಯ ಮಾಹಿತಿ"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"ಟ್ರಿಗರ್‌ ವಾಹಕ ಪೂರೈಕೆ"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"ಸಲಹೆ: ಬ್ಲೂಟೂತ್ ಕನೆಕ್ಷನ್ ಅನ್ನು ಸುಧಾರಿಸಿ"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"ನಿಮ್ಮ ಬ್ಲೂಟೂತ್ ಸಿಗ್ನಲ್ ದುರ್ಬಲವಾಗಿದೆ. ಸ್ಪೀಕರ್‌ಫೋನ್‌ಗೆ ಬದಲಾಯಿಸಲು ಪ್ರಯತ್ನಿಸಿ."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"ಕರೆ ಗುಣಮಟ್ಟದ ಅಧಿಸೂಚನೆ"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"ತಡೆಹಿಡಿಯಲಾಗಿರುವ SIP ಖಾತೆಗಳು"</string>
 </resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index e1d9495..63bb926 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"제한된 SIM 기능"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g>번을 사용하는 동안 <xliff:g id="CARRIER_NAME">%1$s</xliff:g> 통화 및 데이터 서비스가 차단될 수 있습니다."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"다른 SIM을 사용하는 동안 <xliff:g id="CARRIER_NAME">%1$s</xliff:g> 통화 및 데이터 서비스가 차단될 수 있습니다."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"지원 중단된 SIP 계정 발견 및 삭제됨"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP 통화가 더 이상 Android 플랫폼에서 지원되지 않습니다.\n기존 SIP 계정(<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>)이 삭제되었습니다.\n기본 통화 계정 설정을 확인해 주세요."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"설정으로 이동"</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>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"비밀번호가 잘못되어 SIM이 잠겼습니다. PUK2를 입력하세요."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2가 완전히 차단되었습니다."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"<xliff:g id="NUMBER">%d</xliff:g>번의 기회가 남았습니다."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2로 잠금 설정되었습니다. 모든 통신사에서 사용할 수 있도록 하려면 서비스 제공업체에 문의하세요."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 차단이 해제되었습니다."</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"네트워크 또는 SIM 카드 오류"</string>
     <string name="doneButton" msgid="7371209609238460207">"완료"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"휴대전화 정보"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"이동통신사 프로비저닝 정보"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"이동통신사 프로비저닝 실행"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"제안: 블루투스 연결 개선"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"블루투스 신호 강도가 약합니다. 스피커폰으로 전환해 보세요."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"통화 품질 알림"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"지원 중단된 SIP 계정"</string>
 </resources>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index be4439f..a6c691c 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -282,8 +282,8 @@
     <string name="data_enable_summary" msgid="696860063456536557">"Дайындарды пайдаланууга уруксат берүү"</string>
     <string name="dialog_alert_title" msgid="5260471806940268478">"Көңүл буруңуз"</string>
     <string name="roaming" msgid="1576180772877858949">"Роуминг"</string>
-    <string name="roaming_enable" msgid="6853685214521494819">"Роуминг учурунда дайын-даректерди өткөрүүчү кызматтарга туташуу"</string>
-    <string name="roaming_disable" msgid="8856224638624592681">"Роуминг учурунда дайын-даректерди өткөрүүчү кызматтарга туташуу"</string>
+    <string name="roaming_enable" msgid="6853685214521494819">"Роуминг учурунда маалыматтарды өткөрүүчү кызматтарга туташасыз"</string>
+    <string name="roaming_disable" msgid="8856224638624592681">"Роуминг учурунда маалыматтарды өткөрүүчү кызматтарга туташасыз"</string>
     <string name="roaming_reenable_message" msgid="1951802463885727915">"Интернет-роуминг өчүрүлгөн. Күйгүзүү үчүн басыңыз."</string>
     <string name="roaming_enabled_message" msgid="9022249120750897">"Роуминг акысын төлөп калышыңыз мүмкүн. Өзгөртүү үчүн таптап коюңуз."</string>
     <string name="roaming_notification_title" msgid="3590348480688047320">"Мобилдик интернет туташуусу үзүлдү"</string>
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"SIM-картанын функциялары чектелген"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g> номерин колдонгондо <xliff:g id="CARRIER_NAME">%1$s</xliff:g> чалуулары жана дайындар кызматтары бөгөттөлүшү мүмкүн."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Башка SIM картаны колднгндо <xliff:g id="CARRIER_NAME">%1$s</xliff:g> чалуулары жана дайындар кызмттары бөгөттлшү мүмкүн."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Колдонуудан чыккан SIP аккаунттары табылды жана өчүрүлдү"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP аркылуу чалуу Android платформасында мындан ары колдоого алынбайт.\nУчурдагы SIP аккаунттарыңыз (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) өчүрүлдү.\nЧалууларга колдонулган демейки аккаунтун жөндөөсүн ырастаңыз."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Жөндөөлөргө өтүү"</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>
@@ -408,13 +411,13 @@
     <string name="enable_disable_multi_category" msgid="5958248155437940625">"Мульти-категория"</string>
     <string name="multi_category_enable" msgid="4531915767817483960">"Мульти-категория иштетилген"</string>
     <string name="multi_category_disable" msgid="6325934413701238104">"Мульти-категория өчүрүлгөн"</string>
-    <string name="network_recommended" msgid="3444321100580250926">" (сунушталган)"</string>
+    <string name="network_recommended" msgid="3444321100580250926">" (сунушталат)"</string>
     <string name="network_lte" msgid="7206879277095094280">"LTE (сунушталат)"</string>
     <string name="network_4G" msgid="6800527815504223913">"4G (сунушталат)"</string>
     <string name="network_global" msgid="3289646154407617631">"Дүйнө жүзү"</string>
-    <string name="cdma_system_select_title" msgid="614165233552656431">"Тутум тандоо"</string>
+    <string name="cdma_system_select_title" msgid="614165233552656431">"Системаны тандоо"</string>
     <string name="cdma_system_select_summary" msgid="3840420390242060407">"CDMA роуминг тартибин алмаштыруу"</string>
-    <string name="cdma_system_select_dialogtitle" msgid="5524639510676501802">"Тутум тандоо"</string>
+    <string name="cdma_system_select_dialogtitle" msgid="5524639510676501802">"Системаны тандоо"</string>
   <string-array name="cdma_system_select_choices">
     <item msgid="462340042928284921">"Башкы бет гана"</item>
     <item msgid="6058010046783562674">"Автоматтык"</item>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Сырсөз туура эмес. SIM азыр кулпуланды. PUK2 киргизиңиз."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 биротоло бөгөттөлдү."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Сизде <xliff:g id="NUMBER">%d</xliff:g> аракет калды."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 кулпуланды. Кулпуну ачуу үчүн оператор менен байланышыңыз."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"Мындан ары PIN2 бөгөттөлбөйт"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Тармак же SIM карта катасы"</string>
     <string name="doneButton" msgid="7371209609238460207">"Даяр"</string>
@@ -669,7 +673,7 @@
     <string name="preference_category_ringtone" msgid="8787281191375434976">"Рингтон жана Титирөө"</string>
     <string name="pstn_connection_service_label" msgid="9200102709997537069">"Кыналган SIM карталар"</string>
     <string name="enable_video_calling_title" msgid="7246600931634161830">"Видео чалууну күйгүзүү"</string>
-    <string name="enable_video_calling_dialog_msg" msgid="7141478720386203540">"Видео чалууну күйгүзүү үчүн, тармак жөндөөлөрүнөн Жакшыртылган 4G LTE режимин иштетишиңиз керек."</string>
+    <string name="enable_video_calling_dialog_msg" msgid="7141478720386203540">"Видео чалууну күйгүзүү үчүн тармак жөндөөлөрүнөн Жакшыртылган 4G LTE режимин иштетишиңиз керек."</string>
     <string name="enable_video_calling_dialog_settings" msgid="8697890611305307110">"Тармак жөндөөлөрү"</string>
     <string name="enable_video_calling_dialog_close" msgid="4298929725917045270">"Жабуу"</string>
     <string name="sim_label_emergency_calls" msgid="9078241989421522310">"Шашылыш чалуулар"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Телефондун маалыматы"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Байланыш оператору жөнүндө маалымат"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Байланыш операторун өзгөртүү"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Сунуш: Bluetooth байлынышын жакшыртыңыз"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth сигналыңыз начар. Спикерфонго которулуп көрүңүз."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Чалуунун сапаты тууралуу билдирме"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Колдонуудан чыккан SIP аккаунттары"</string>
 </resources>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 7c50430..0fc3dfd 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"ຈຳກັດການເຮັດວຽກຂອງຊິມ"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"ບໍລິການໂທ ແລະ ອິນເຕີເນັດ <xliff:g id="CARRIER_NAME">%1$s</xliff:g> ອາດຖືກບລັອກໄວ້ເມື່ອໃຊ້ເບີໂທ <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"ບໍລິການໂທ ແລະ ອິນເຕີເນັດ <xliff:g id="CARRIER_NAME">%1$s</xliff:g> ອາດຖືກບລັອກໄວ້ເມື່ອໃຊ້ຊິມອື່ນ."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"ພົບ ແລະ ລຶບບັນຊີ SIP ທີ່ເຊົາສະໜັບສະໜູນອອກແລ້ວ"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"ແພລດຟອມ Android ບໍ່ຮອງຮັບການໂທ SIP ອີກຕໍ່ໄປ.\nບັນຊີ SIP ທີ່ທ່ານມີຢູ່ <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> ໄດ້ຖືກລຶບອອກແລ້ວ.\nກະລຸນາຢັ້ງຢືນການຕັ້ງຄ່າບັນຊີການໂທເລີ່ມຕົ້ນຂອງທ່ານ."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"ໄປທີ່ການຕັ້ງຄ່າ"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"ການໃຊ້ຂໍ້ມູນແອັບ"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"ໃຊ້ອິນເຕີເນັດໄປແລ້ວ <xliff:g id="ID_1">%1$s</xliff:g> ລະຫວ່າງ <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"ຂັ້ນສູງ"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"ລະ​ຫັດ​ຜ່ານ​ບໍ່​ຖືກ​ຕ້ອງ​. SIM ຖືກ​ລັອກ​ແລ້ວ​ດຽວ​ນີ້​. ປ້ອນ PUK2 ເຂົ້າ​ໄປ​."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 ຖືກບລັອກຢ່າງຖາວອນແລ້ວ."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"ທ່ານ​ຍັງ​ພະ​ຍາ​ຍາມ​ໄດ້​ອີກ <xliff:g id="NUMBER">%d</xliff:g> ຄັ້ງ."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"ລັອກ PUK2 ແລ້ວ. ຕິດຕໍ່ຜູ້ໃຫ້ບໍລິການເພື່ອປົດລັອກ."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 ບໍ່​ຖືກ​ບ​ລັອກ​ອີກ"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"ເຄືອຂ່າຍ ຫຼື SIM card ຜິດພາດ"</string>
     <string name="doneButton" msgid="7371209609238460207">"ແລ້ວໆ"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"ຂໍ້​ມູນ​ໂທລະ​ສັບ"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"ຂໍ້ມູນການເປີດນຳໃຊ້ເຄືອຂ່າຍ"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"ກະຕຸ້ນການຈັດຫາຜູ້ໃຫ້ບໍລິການ"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"ການແນະນຳ: ປັບປຸງການເຊື່ອມຕໍ່ Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"ສັນຍານ Bluetooth ຂອງທ່ານອ່ອນ. ລອງສະຫຼັບລຳໂພງໂທລະສັບ."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"ການແຈ້ງເຕືອນຄຸນນະພາບການໂທ"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"ບັນຊີ SIP ທີ່ເຊົາສະໜັບສະໜູນ"</string>
 </resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 961d380..07fbc2e 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Ribotos SIM kortelės funkcijos"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"„<xliff:g id="CARRIER_NAME">%1$s</xliff:g>“ skambučiai ir duomenų paslaugos gali būti užblokuotos naudojant numerį <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"„<xliff:g id="CARRIER_NAME">%1$s</xliff:g>“ duom. paslaug. ir skambučiai gali būti užblokuoti naudojant kitą SIM kortelę."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Nebenaudojamos SIP paskyros surastos ir pašalintos"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP skambinimas nebepalaikomas „Android“ platformoje.\nEsamos SIP paskyros <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> buvo pašalintos.\nPatvirtinkite savo numatytąjį skambinimo paskyros nustatymą."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Eiti į nustatymus"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Programos duomenų naudojimas"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_2">%2$s</xliff:g> išnaudota mobiliojo ryšio duomenų: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Išplėstiniai"</string>
@@ -315,9 +318,7 @@
     <string name="throttle_time_frame" msgid="1813452485948918791">"Duomenų naudojimo laikotarpis"</string>
     <string name="throttle_rate" msgid="7641913901133634905">"Duomenų perdavimo spartos politika"</string>
     <string name="throttle_help" msgid="2624535757028809735">"Sužinokite daugiau"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for throttle_status_subtext (1110276415078236687) -->
-    <skip />
+    <string name="throttle_status_subtext" msgid="1110276415078236687">"<xliff:g id="USED_0">%1$s</xliff:g> (<xliff:g id="USED_1">%2$d</xliff:g>٪) iš <xliff:g id="USED_2">%3$s</xliff:g> laikotarpio maksimumo\nKitas laikotarpis prasidės po <xliff:g id="USED_3">%4$d</xliff:g> dienų (<xliff:g id="USED_4">%5$s</xliff:g>)"</string>
     <string name="throttle_data_usage_subtext" msgid="3185429653996709840">"<xliff:g id="USED_0">%1$s</xliff:g> (<xliff:g id="USED_1">%2$d</xliff:g>٪) iš <xliff:g id="USED_2">%3$s</xliff:g> laikotarpio maksimumo"</string>
     <string name="throttle_data_rate_reduced_subtext" msgid="8369839346277847725">"Viršytas <xliff:g id="USED_0">%1$s</xliff:g> maksimumas\nDuomenų perdavimo sparta sumažinta iki <xliff:g id="USED_1">%2$d</xliff:g> Kb / sek."</string>
     <string name="throttle_time_frame_subtext" msgid="6462089615392402127">"Praėjo <xliff:g id="USED_0">%1$d</xliff:g>٪ ciklo\nKitas laikotarpis prasidės po<xliff:g id="USED_1">%2$d</xliff:g> dienų (<xliff:g id="USED_2">%3$s</xliff:g>)"</string>
@@ -514,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Netinkamas slaptažodis. SIM kortelė užrakinta. Įveskite PUK2 kodą."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 kodas visam laikui užblokuotas."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Liko <xliff:g id="NUMBER">%d</xliff:g> bandym."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 užrakintas. Kreipkitės į paslaugos teikėją, kad atrakintų."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 kodas atblokuotas"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Tinklo ar SIM kortelės klaida"</string>
     <string name="doneButton" msgid="7371209609238460207">"Atlikta"</string>
@@ -927,6 +929,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Telefono informacija"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Operatoriaus aprūpinimo informacija"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Aktyviklio operatoriaus aprūpinimas"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Pasiūlymas: pagerinkite „Bluetooth“ ryšį"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Silpnas „Bluetooth“ signalas. Pabandykite perjungti garsiakalbį."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Pranešimas apie skambučio kokybę"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Nebenaudojamos SIP paskyros"</string>
 </resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index a547255..eca2653 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Ierobežotas SIM kartes funkcijas"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Izmantojot tālruņa numuru <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>, mobilo sakaru operatora <xliff:g id="CARRIER_NAME">%1$s</xliff:g> zvani un datu pakalpojumi var būt bloķēti."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Izmantojot citu SIM karti, operatora <xliff:g id="CARRIER_NAME">%1$s</xliff:g> zvani un datu pakalpojumi var būt bloķēti."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Tika konstatēti un noņemti SIP konti, kuru darbība ir pārtraukta"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP zvanu izmantošana vairs netiek atbalstīta Android platformā.\nJūsu esošie SIP konti <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> ir noņemti.\nLūdzu, apstipriniet konta iestatījumu, kas pēc noklusējuma tiks izmantots zvaniem."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Atvērt iestatījumus"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Lietotnes datu lietojums"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> mobilo datu izmantoti šādā laika periodā: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Papildu"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Parole nav pareiza. SIM bloķēta. Ievadiet PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 kods ir neatgriezeniski bloķēts."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Jums atlikuši <xliff:g id="NUMBER">%d</xliff:g> mēģinājumi."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 ir bloķēts. Lai atbloķētu, sazinieties ar pakalpojuma sniedzēju."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 vairs nav bloķēts"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Tīkla vai SIM kartes kļūda"</string>
     <string name="doneButton" msgid="7371209609238460207">"Gatavs"</string>
@@ -922,6 +926,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Tālruņa informācija"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Informācija par mobilo sakaru operatora nodrošināšanu"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Aktivizēt operatora nodrošināšanu"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Ieteikums: uzlabojiet Bluetooth savienojamību"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth signāls ir vājš. Mēģiniet pārslēgties uz skaļruni."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Paziņojums par zvana kvalitāti"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"SIP konti, kuru darbība ir pārtraukta"</string>
 </resources>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 94cc190..fb5b630 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -191,7 +191,7 @@
     <string name="preferred_network_mode_dialogtitle" msgid="2781447433514459696">"Претпочитан тип мрежа"</string>
     <string name="forbidden_network" msgid="5081729819561333023">"(забрането)"</string>
     <string name="choose_network_title" msgid="5335832663422653082">"Изберете мрежа"</string>
-    <string name="network_disconnected" msgid="8844141106841160825">"Исклучено"</string>
+    <string name="network_disconnected" msgid="8844141106841160825">"Не е поврзано"</string>
     <string name="network_connected" msgid="2760235679963580224">"Поврзано"</string>
     <string name="network_connecting" msgid="160901383582774987">"Се поврзува…"</string>
     <string name="network_could_not_connect" msgid="6547460848093727998">"Не може да се поврзе"</string>
@@ -294,7 +294,10 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Ограничена функционалност на SIM-картичка"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Повиците и услугите за интернет на <xliff:g id="CARRIER_NAME">%1$s</xliff:g> може да бидат блокирани додека се користи <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Повиците и услугите за интернет на <xliff:g id="CARRIER_NAME">%1$s</xliff:g> може да се блокирани со друга SIM-картичка."</string>
-    <string name="data_usage_title" msgid="8438592133893837464">"Потрошен сообраќај на апликациите"</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Најдени и отстранети се неподдржани сметки на SIP"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Платформата Android веќе не поддржува повикување преку SIP.\nВашите постојни сметки на SIP <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> се отстранети.\nПотврдете ја вашата стандардна поставка за повикување."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Одете во „Поставки“"</string>
+    <string name="data_usage_title" msgid="8438592133893837464">"Потрошен интернет од апликации"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> потрошен мобилен интернет во периодот <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Напредни"</string>
     <string name="carrier_settings_euicc" msgid="1190237227261337749">"Оператор"</string>
@@ -317,9 +320,9 @@
     <string name="throttle_help" msgid="2624535757028809735">"Дознај повеќе"</string>
     <string name="throttle_status_subtext" msgid="1110276415078236687">"<xliff:g id="USED_0">%1$s</xliff:g> (<xliff:g id="USED_1">%2$d</xliff:g> ٪) од максимум <xliff:g id="USED_2">%3$s</xliff:g> за периодот\nСледниот период започнува за <xliff:g id="USED_3">%4$d</xliff:g> дена (<xliff:g id="USED_4">%5$s</xliff:g>)"</string>
     <string name="throttle_data_usage_subtext" msgid="3185429653996709840">"<xliff:g id="USED_0">%1$s</xliff:g> (<xliff:g id="USED_1">%2$d</xliff:g>٪) од <xliff:g id="USED_2">%3$s</xliff:g> максимален период"</string>
-    <string name="throttle_data_rate_reduced_subtext" msgid="8369839346277847725">"<xliff:g id="USED_0">%1$s</xliff:g> максимум е надминат\nСтапката на податоци е намалена на <xliff:g id="USED_1">%2$d</xliff:g> кб/с"</string>
+    <string name="throttle_data_rate_reduced_subtext" msgid="8369839346277847725">"<xliff:g id="USED_0">%1$s</xliff:g> максимум е надминат\nСтапката на податоци е намалена на <xliff:g id="USED_1">%2$d</xliff:g> Kb/s"</string>
     <string name="throttle_time_frame_subtext" msgid="6462089615392402127">"<xliff:g id="USED_0">%1$d</xliff:g>٪ од циклусот помина\nНаредниот период започнува за <xliff:g id="USED_1">%2$d</xliff:g> дена (<xliff:g id="USED_2">%3$s</xliff:g>)"</string>
-    <string name="throttle_rate_subtext" msgid="7221971817325779535">"Преносот на податоци се сведува на <xliff:g id="USED">%1$d</xliff:g> кб/с ако се надмине границата на потрошен интернет"</string>
+    <string name="throttle_rate_subtext" msgid="7221971817325779535">"Преносот на податоци се сведува на <xliff:g id="USED">%1$d</xliff:g> Kb/s ако се надмине границата на потрошен интернет"</string>
     <string name="throttle_help_subtext" msgid="2817114897095534807">"Повеќе информации за политиката на користање податоци на мобилната мрежа на вашиот оператор"</string>
     <string name="cell_broadcast_sms" msgid="4053449797289031063">"SMS за емитување од мобилен"</string>
     <string name="enable_disable_cell_bc_sms" msgid="4759958924031721350">"SMS за емитување од мобилен"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Лозинката е неточна. SIM-картичката е сега заклучена. Внесете го ПУК2-кодот."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"ПУК2 кодот е трајно блокиран."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Имате уште <xliff:g id="NUMBER">%d</xliff:g> обиди."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2-кодот е заклучен. Контактирајте со операторот за отклучување."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2-кодот веќе не е блокиран"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Грешка со мрежа или SIM картичка"</string>
     <string name="doneButton" msgid="7371209609238460207">"Готово"</string>
@@ -860,7 +864,7 @@
     <string name="radioInfo_phone_idle" msgid="2191653783170757819">"Мирување"</string>
     <string name="radioInfo_phone_ringing" msgid="8100354169567413370">"Ѕвонење"</string>
     <string name="radioInfo_phone_offhook" msgid="7564601639749936170">"Повик во тек"</string>
-    <string name="radioInfo_data_disconnected" msgid="8085447971880814541">"Исклучен"</string>
+    <string name="radioInfo_data_disconnected" msgid="8085447971880814541">"Не е поврзано"</string>
     <string name="radioInfo_data_connecting" msgid="925092271092152472">"Се поврзува"</string>
     <string name="radioInfo_data_connected" msgid="7637335645634239508">"Поврзан"</string>
     <string name="radioInfo_data_suspended" msgid="8695262782642002785">"Суспендиран"</string>
@@ -873,8 +877,8 @@
     <string name="radioInfo_cid" msgid="1423185536264406705">"CID"</string>
     <string name="radio_info_subid" msgid="6839966868621703203">"Тековен SUBID:"</string>
     <string name="radio_info_dds" msgid="1122593144425697126">"SUBID на стандардната SIM за мобилен интернет:"</string>
-    <string name="radio_info_dl_kbps" msgid="2382922659525318726">"Брзина на пренос при преземање (кбит/с):"</string>
-    <string name="radio_info_ul_kbps" msgid="2102225400904799036">"Брзина на пренос при прикачување (кбит/с):"</string>
+    <string name="radio_info_dl_kbps" msgid="2382922659525318726">"Брзина на пренос при преземање (kbps):"</string>
+    <string name="radio_info_ul_kbps" msgid="2102225400904799036">"Брзина на пренос при прикачување (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>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Информации за телефонот"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Информации за обезбедување оператор"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Активирајте обезбедување оператор"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Предлог: подобрете го поврзувањето на Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Вашиот сигнал на Bluetooth е слаб. Обидете се со префрлање на интерфон."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Известување за квалитет на повик"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Неподдржани сметки на SIP"</string>
 </resources>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 2cf119f..4f9b5ee 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"പരിമിതമായ സിം പ്രവർത്തനം"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g> ഉപയോഗിക്കുമ്പോൾ <xliff:g id="CARRIER_NAME">%1$s</xliff:g> കോളുകളും ഡാറ്റാ സേവനങ്ങളും ബ്ലോക്ക് ചെയ്‌തേക്കാം."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"മറ്റൊരു SIM ഉപയോഗിക്കുമ്പോൾ <xliff:g id="CARRIER_NAME">%1$s</xliff:g> കോളുകളും ഡാറ്റാ സേവനവും ബ്ലോക്ക് ചെയ്‌തേക്കാം."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"അവസാനിപ്പിച്ച SIP അക്കൗണ്ടുകൾ കണ്ടെത്തി നീക്കം ചെയ്‌തു"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP കോൾ ചെയ്യലിന് ഇനി Android പ്ലാറ്റ്‌ഫോമിൽ പിന്തുണയുണ്ടാകില്ല.\nനിലവിലെ നിങ്ങളുടെ SIP അക്കൗണ്ടുകളായ <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> എന്നിവ നീക്കം ചെയ്‌തിരിക്കുന്നു.\nനിങ്ങളുടെ ഡിഫോൾട്ട് കോൾ ചെയ്യൽ അക്കൗണ്ട് ക്രമീകരണം സ്ഥിരീകരിക്കുക."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"ക്രമീകരണത്തിലേക്ക് പോകുക"</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>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"പാസ്‌വേഡ് തെറ്റാണ്. SIM ഇപ്പോൾ ലോക്കുചെയ്‌തിരിക്കുന്നു. PUK2 നൽകുക."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 ശാശ്വതമായി തടഞ്ഞിരിക്കുന്നു."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"നിങ്ങൾക്ക് <xliff:g id="NUMBER">%d</xliff:g> ശ്രമങ്ങൾ ശേഷിക്കുന്നു."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 ലോക്ക് ആണ്. അൺലോക്ക് ചെയ്യാൻ സേവന ദാതാവിനെ ബന്ധപ്പെടുക."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 ഇനി തടയില്ല"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"നെറ്റ്‌വർക്ക് അല്ലെങ്കിൽ സിം കാർഡ് പിശക്"</string>
     <string name="doneButton" msgid="7371209609238460207">"പൂർത്തിയായി"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"ഫോൺ വിവരം"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"കാരിയർ പ്രൊവിഷനിംഗ് വിവരം"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"കാരിയർ പ്രൊവിഷനിംഗ് തുടങ്ങുക"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"നിർദ്ദേശം: Bluetooth കണക്‌റ്റിവിറ്റി മെച്ചപ്പെടുത്തുക"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"നിങ്ങളുടെ Bluetooth സിഗ്നൽ ദുർബലമാണ്. സ്‌പീക്കർഫോണിലേക്ക് മാറ്റി നോക്കൂ."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"കോൾ നിലവാര അറിയിപ്പ്"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"അവസാനിപ്പിച്ച SIP അക്കൗണ്ടുകൾ"</string>
 </resources>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index d25876c..d54d35d 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -49,7 +49,7 @@
     <string name="add_vm_number_str" msgid="7368168964435881637">"Дугаар нэмэх"</string>
     <string name="voice_number_setting_primary_user_only" msgid="3394706575741912843">"Дуут шуудангийн тохиргоог зөвхөн Үндсэн хэрэглэгч өөрчлөх боломжтой."</string>
     <string name="puk_unlocked" msgid="4627340655215746511">"Таны SIM карт тайлагдлаа. Таны утас тайлагдаж байна…"</string>
-    <string name="label_ndp" msgid="7617392683877410341">"SIM сүлжээ тайлах PIN"</string>
+    <string name="label_ndp" msgid="7617392683877410341">"SIM сүлжээ тайлах ПИН"</string>
     <string name="label_phoneid" msgid="8775611434123577808">"Операторын SIM түгжигдсэн"</string>
     <string name="sim_ndp_unlock_text" msgid="7737338355451978338">"Тайлах"</string>
     <string name="sim_ndp_dismiss_text" msgid="89667342248929777">"Алгасах"</string>
@@ -96,7 +96,7 @@
     <string name="sum_loading_settings" msgid="434063780286688775">"Тохиргоог ачаалж байна…"</string>
     <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="sum_default_caller_id" msgid="1767070797135682959">"Гарах дуудлагуудад миний дугаарыг харуулахдаа операторын өгөгдмөл тохиргоог ашиглах"</string>
     <string name="labelCW" msgid="8449327023861428622">"Дуудлага хүлээлгэх"</string>
     <string name="sum_cw_enabled" msgid="3977308526187139996">"Дуудлагын дундуур өөр дуудлага ирвэл надад мэдэгдэх"</string>
     <string name="sum_cw_disabled" msgid="3658094589461768637">"Дуудлагын дундуур өөр дуудлага ирвэл надад мэдэгдэх"</string>
@@ -162,15 +162,15 @@
     <string name="no_change" msgid="3737264882821031892">"Ямар ч өөрчлөлт хийгдсэнгүй."</string>
     <string name="sum_voicemail_choose_provider" msgid="6750824719081403773">"Дуут шуудангийн үйлчилгээг сонгох"</string>
     <string name="voicemail_default" msgid="6427575113775462077">"Таны оператор компани"</string>
-    <string name="vm_change_pin_old_pin" msgid="7154951790929009241">"Хуучин PIN"</string>
-    <string name="vm_change_pin_new_pin" msgid="2656200418481288069">"Шинэ PIN"</string>
+    <string name="vm_change_pin_old_pin" msgid="7154951790929009241">"Хуучин ПИН"</string>
+    <string name="vm_change_pin_new_pin" msgid="2656200418481288069">"Шинэ ПИН"</string>
     <string name="vm_change_pin_progress_message" msgid="626015184502739044">"Түр хүлээнэ үү."</string>
-    <string name="vm_change_pin_error_too_short" msgid="1789139338449945483">"Шинэ PIN хэт богино байна."</string>
-    <string name="vm_change_pin_error_too_long" msgid="3634907034310018954">"Шинэ PIN хэт урт байна."</string>
-    <string name="vm_change_pin_error_too_weak" msgid="8581892952627885719">"Шинэ PIN хэт амархан байна. Сайн нууц үгэнд үргэлжилсэн дараалал буюу давтагдсан цифр ордоггүй."</string>
-    <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="vm_change_pin_error_too_short" msgid="1789139338449945483">"Шинэ ПИН хэт богино байна."</string>
+    <string name="vm_change_pin_error_too_long" msgid="3634907034310018954">"Шинэ ПИН хэт урт байна."</string>
+    <string name="vm_change_pin_error_too_weak" msgid="8581892952627885719">"Шинэ ПИН хэт амархан байна. Сайн нууц үгэнд үргэлжилсэн дараалал буюу давтагдсан цифр ордоггүй."</string>
+    <string name="vm_change_pin_error_mismatch" msgid="5364847280026257331">"Хуучин ПИН таарахгүй байна."</string>
+    <string name="vm_change_pin_error_invalid" msgid="5230002671175580674">"Шинэ ПИН-д буруу тэмдэгт агуулагдаж байна."</string>
+    <string name="vm_change_pin_error_system_error" msgid="9116483527909681791">"ПИН-г өөрчлөх боломжгүй"</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>
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Хязгаарлагдмал SIM-н функц"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g>-г ашиглах явцад <xliff:g id="CARRIER_NAME">%1$s</xliff:g>-н дуудлага болон дата үйлчилгээг блоклож болзошгүй."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Өөр SIM ашиглах явцад <xliff:g id="CARRIER_NAME">%1$s</xliff:g>-н дуудлага болон дата үйлчилгээг хориглож болзошгүй."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"SIP-н зогсоосон бүртгэлүүд олдсон бөгөөд тэдгээрийг хассан"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP дуудлагыг цаашид Android платформоос дэмжихгүй.\nТаны одоо байгаа <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> SIP-н бүртгэлүүдийг хассан.\nДуудлагын бүртгэлийн өгөгдмөл тохиргоогоо баталгаажуулна уу."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Тохиргоо руу очих"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Аппын дата ашиглалт"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> мобайл дата ашигласан <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Нарийвчилсан"</string>
@@ -446,7 +449,7 @@
     <string name="enable_fdn_ok" msgid="5080925177369329827">"FDN идэвхгүйжүүлэх"</string>
     <string name="disable_fdn_ok" msgid="3745475926874838676">"FDN-г идэвхжүүлэх"</string>
     <string name="sum_fdn" msgid="6152246141642323582">"Тогтсон залгах дугааруудыг удирдах"</string>
-    <string name="sum_fdn_change_pin" msgid="3510994280557335727">"FDN хандалтын PIN-г өөрчлөх"</string>
+    <string name="sum_fdn_change_pin" msgid="3510994280557335727">"FDN хандалтын ПИН-г өөрчлөх"</string>
     <string name="sum_fdn_manage_list" msgid="3311397063233992907">"Утасны дугаарын жагсаалтыг удирдах"</string>
     <string name="voice_privacy" msgid="7346935172372181951">"Дууны нууцлал"</string>
     <string name="voice_privacy_summary" msgid="3556460926168473346">"Сайжруулсан нууцлалын төлвийг идэвхжүүлэх"</string>
@@ -472,7 +475,7 @@
     <string name="delete_fdn_contact" msgid="7027405651994507077">"Тогтвортой залгах дугаарыг устгах"</string>
     <string name="deleting_fdn_contact" msgid="6872320570844460428">"Тогтвортой залгах дугаарыг устгаж байна…"</string>
     <string name="fdn_contact_deleted" msgid="1680714996763848838">"Тогтвортой залгах дугаарыг устгав."</string>
-    <string name="pin2_invalid" msgid="2313954262684494442">"Та буруу PIN оруулсан учир FDN шинэчлэгдсэнгүй."</string>
+    <string name="pin2_invalid" msgid="2313954262684494442">"Та буруу ПИН оруулсан учир FDN шинэчлэгдсэнгүй."</string>
     <string name="fdn_invalid_number" msgid="9067189814657840439">"Дугаар <xliff:g id="FDN_NUMBER_LIMIT_LENGTH">%d</xliff:g> цифрээс хэтэрсэн тул FDN-г шинэчлээгүй."</string>
     <string name="pin2_or_fdn_invalid" msgid="7542639487955868181">"FDN шинэчлэгдсэнгүй. PIN2 буруу байсан, эсхүл утасны дугаар зөвшөөрөгдсөнгүй."</string>
     <string name="fdn_failed" msgid="216592346853420250">"ФДН ажиллуулах амжилтгүй."</string>
@@ -480,23 +483,23 @@
     <string name="simContacts_empty" msgid="1135632055473689521">"Таны SIM картанд харилцагчид байхгүй байна."</string>
     <string name="simContacts_title" msgid="2714029230160136647">"Оруулах харилцагчдыг сонгоно уу"</string>
     <string name="simContacts_airplaneMode" msgid="4654884030631503808">"SIM картнаас дугаар импортлохын тулд онгоцны горимыг унтраа"</string>
-    <string name="enable_pin" msgid="967674051730845376">"SIM PIN Идэвхжүүлэх/идэвхгүйжүүлэх"</string>
-    <string name="change_pin" msgid="3657869530942905790">"SIM PIN өөрчлөх"</string>
-    <string name="enter_pin_text" msgid="3182311451978663356">"SIM PIN:"</string>
-    <string name="oldPinLabel" msgid="8618515202411987721">"Хуучин PIN"</string>
-    <string name="newPinLabel" msgid="3585899083055354732">"Шинэ PIN"</string>
-    <string name="confirmPinLabel" msgid="7783531218662473778">"Шинэ PIN-г баталгаажуулах"</string>
-    <string name="badPin" msgid="4549286285015892321">"Таны оруулсан хуучин PIN буруу байна. Дахин оролдоно уу."</string>
-    <string name="mismatchPin" msgid="1467254768290323845">"Таны оруулсан PIN таарахгүй байна. Дахин оролдоно уу."</string>
-    <string name="invalidPin" msgid="7363723429414001979">"4-с 8 цифртэй PIN оруулна уу"</string>
-    <string name="disable_sim_pin" msgid="3112303905548613752">"SIM-ний PIN-г цэвэрлэх"</string>
+    <string name="enable_pin" msgid="967674051730845376">"SIM ПИН Идэвхжүүлэх/идэвхгүйжүүлэх"</string>
+    <string name="change_pin" msgid="3657869530942905790">"SIM ПИН өөрчлөх"</string>
+    <string name="enter_pin_text" msgid="3182311451978663356">"SIM ПИН:"</string>
+    <string name="oldPinLabel" msgid="8618515202411987721">"Хуучин ПИН"</string>
+    <string name="newPinLabel" msgid="3585899083055354732">"Шинэ ПИН"</string>
+    <string name="confirmPinLabel" msgid="7783531218662473778">"Шинэ ПИН-г баталгаажуулах"</string>
+    <string name="badPin" msgid="4549286285015892321">"Таны оруулсан хуучин ПИН буруу байна. Дахин оролдоно уу."</string>
+    <string name="mismatchPin" msgid="1467254768290323845">"Таны оруулсан ПИН таарахгүй байна. Дахин оролдоно уу."</string>
+    <string name="invalidPin" msgid="7363723429414001979">"4-с 8 цифртэй ПИН оруулна уу"</string>
+    <string name="disable_sim_pin" msgid="3112303905548613752">"SIM-ний ПИН-г цэвэрлэх"</string>
     <string name="enable_sim_pin" msgid="445461050748318980">"SIM-ний ПИН-г тохируулах"</string>
     <string name="enable_in_progress" msgid="4135305985717272592">"ПИН-г тохируулж байна..."</string>
-    <string name="enable_pin_ok" msgid="2877428038280804256">"PIN-г тохирууллаа"</string>
-    <string name="disable_pin_ok" msgid="888505244389647754">"PIN цэвэрлэгдсэн"</string>
-    <string name="pin_failed" msgid="4527347792881939652">"PIN буруу байна"</string>
-    <string name="pin_changed" msgid="7291153750090452808">"PIN шинэчлэгдсэн"</string>
-    <string name="puk_requested" msgid="2061337960609806851">"Нууц үг буруу байна. PIN код одоо түгжигдсэн. PUK кодыг авах хүсэлт тавигдсан."</string>
+    <string name="enable_pin_ok" msgid="2877428038280804256">"ПИН-г тохирууллаа"</string>
+    <string name="disable_pin_ok" msgid="888505244389647754">"ПИН цэвэрлэгдсэн"</string>
+    <string name="pin_failed" msgid="4527347792881939652">"ПИН буруу байна"</string>
+    <string name="pin_changed" msgid="7291153750090452808">"ПИН шинэчлэгдсэн"</string>
+    <string name="puk_requested" msgid="2061337960609806851">"Нууц үг буруу байна. ПИН код одоо түгжигдсэн. PUK кодыг авах хүсэлт тавигдсан."</string>
     <string name="enter_pin2_text" msgid="7266379426804295979">"PIN2"</string>
     <string name="oldPin2Label" msgid="4648543187859997203">"Хуучин PIN2"</string>
     <string name="newPin2Label" msgid="1840905981784453939">"Шинэ PIN2"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Нууц үг буруу байна. SIM карт одоо түгжигдсэн. PUK2 кодыг оруулна уу."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"ПҮК2 бүрмөсөн түгжигдсэн."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Танд <xliff:g id="NUMBER">%d</xliff:g> оролдлого үлдлээ."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2-г түгжсэн. Түгжээг тайлахын тулд үйлчилгээ үзүүлэгчтэй холбогдоно уу."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 түгжээгүй болсон"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Сүлжээ болон SIM картын алдаа"</string>
     <string name="doneButton" msgid="7371209609238460207">"Дууссан"</string>
@@ -664,8 +668,8 @@
     <string name="description_dialpad_button" msgid="7395114120463883623">"дугаар цуглуулах самбарыг харуулах"</string>
     <string name="pane_title_emergency_dialpad" msgid="3627372514638694401">"Яаралтай тусламжийн дугаар цуглуулах самбар"</string>
     <string name="voicemail_visual_voicemail_switch_title" msgid="6610414098912832120">"Визуал дуут шуудан"</string>
-    <string name="voicemail_set_pin_dialog_title" msgid="7005128605986960003">"PIN тохируулах"</string>
-    <string name="voicemail_change_pin_dialog_title" msgid="4633077715231764435">"PIN өөрчлөх"</string>
+    <string name="voicemail_set_pin_dialog_title" msgid="7005128605986960003">"ПИН тохируулах"</string>
+    <string name="voicemail_change_pin_dialog_title" msgid="4633077715231764435">"ПИН өөрчлөх"</string>
     <string name="preference_category_ringtone" msgid="8787281191375434976">"Хонхны ая &amp; Чичиргээ"</string>
     <string name="pstn_connection_service_label" msgid="9200102709997537069">"Суурилагдсан SIM карт"</string>
     <string name="enable_video_calling_title" msgid="7246600931634161830">"Видео дуудлагыг идэвхжүүлэх"</string>
@@ -688,18 +692,18 @@
     <string name="callFailed_low_battery" msgid="4056828320214416182">"Цэнэг бага байгаа тул видео дуудлагыг дуусгасан."</string>
     <string name="callFailed_emergency_call_over_wfc_not_available" msgid="5944309590693432042">"Энэ байрлалд Wi-Fi дуудлагаар яаралтай дуудлага хийх боломжгүй."</string>
     <string name="callFailed_wfc_service_not_available_in_this_location" msgid="3624536608369524988">"Энэ байрлалд Wi-Fi дуудлага хийх боломжгүй байна."</string>
-    <string name="change_pin_title" msgid="3564254326626797321">"Дуут шуудангийн PIN-г өөрчлөх"</string>
+    <string name="change_pin_title" msgid="3564254326626797321">"Дуут шуудангийн ПИН-г өөрчлөх"</string>
     <string name="change_pin_continue_label" msgid="5177011752453506371">"Үргэлжлүүлэх"</string>
     <string name="change_pin_cancel_label" msgid="2301711566758827936">"Цуцлах"</string>
     <string name="change_pin_ok_label" msgid="6861082678817785330">"OK"</string>
-    <string name="change_pin_enter_old_pin_header" msgid="853151335217594829">"Хуучин PIN-ээ баталгаажуулна уу"</string>
-    <string name="change_pin_enter_old_pin_hint" msgid="8801292976275169367">"Үргэлжлүүлэхийн тулд дуут шуудангийн PIN оруулна уу."</string>
-    <string name="change_pin_enter_new_pin_header" msgid="4739465616733486118">"Шинэ PIN тохируулах"</string>
-    <string name="change_pin_enter_new_pin_hint" msgid="2326038476516364210">"PIN <xliff:g id="MIN">%1$d</xliff:g> - <xliff:g id="MAX">%2$d</xliff:g> цифртэй байх ёстой."</string>
-    <string name="change_pin_confirm_pin_header" msgid="2606303906320705726">"PIN кодоо баталгаажуулна уу"</string>
-    <string name="change_pin_confirm_pins_dont_match" msgid="305164501222587215">"PIN код таарахгүй байна"</string>
-    <string name="change_pin_succeeded" msgid="2504705600693014403">"Дуут шуудангийн PIN шинэчлэгдсэн"</string>
-    <string name="change_pin_system_error" msgid="7772788809875146873">"PIN тохируулах боломжгүй"</string>
+    <string name="change_pin_enter_old_pin_header" msgid="853151335217594829">"Хуучин ПИН-ээ баталгаажуулна уу"</string>
+    <string name="change_pin_enter_old_pin_hint" msgid="8801292976275169367">"Үргэлжлүүлэхийн тулд дуут шуудангийн ПИН оруулна уу."</string>
+    <string name="change_pin_enter_new_pin_header" msgid="4739465616733486118">"Шинэ ПИН тохируулах"</string>
+    <string name="change_pin_enter_new_pin_hint" msgid="2326038476516364210">"ПИН <xliff:g id="MIN">%1$d</xliff:g> - <xliff:g id="MAX">%2$d</xliff:g> цифртэй байх ёстой."</string>
+    <string name="change_pin_confirm_pin_header" msgid="2606303906320705726">"ПИН кодоо баталгаажуулна уу"</string>
+    <string name="change_pin_confirm_pins_dont_match" msgid="305164501222587215">"ПИН код таарахгүй байна"</string>
+    <string name="change_pin_succeeded" msgid="2504705600693014403">"Дуут шуудангийн ПИН шинэчлэгдсэн"</string>
+    <string name="change_pin_system_error" msgid="7772788809875146873">"ПИН тохируулах боломжгүй"</string>
     <string name="mobile_data_status_roaming_turned_off_subtext" msgid="6840673347416227054">"Дата роумингийг идэвхгүй болгосон"</string>
     <string name="mobile_data_status_roaming_turned_on_subtext" msgid="5615757897768777865">"Дата роумингийг асаасан"</string>
     <string name="mobile_data_status_roaming_without_plan_subtext" msgid="6536671968072284677">"Одоо роумингтэй байна, дата багц шаардлагатай"</string>
@@ -903,7 +907,7 @@
     <string name="radio_info_smsc_label" msgid="3749927072726033763">"SMSC:"</string>
     <string name="radio_info_smsc_update_label" msgid="5141996256097115753">"Шинэчлэх"</string>
     <string name="radio_info_smsc_refresh_label" msgid="8409923721451604560">"Сэргээх"</string>
-    <string name="radio_info_toggle_dns_check_label" msgid="1394078554927787350">"DNS шалгалтыг унтраах/асаах"</string>
+    <string name="radio_info_toggle_dns_check_label" msgid="1394078554927787350">"DNS шалгалтыг асаах/унтраах"</string>
     <string name="oem_radio_info_label" msgid="2914167475119997456">"OEM-тодорхой Мэдээлэл/Тохиргоо"</string>
     <string name="radio_info_endc_available" msgid="4410653375290113436">"EN-DC боломжтой:"</string>
     <string name="radio_info_dcnr_restricted" msgid="2469125498066960807">"DCNR хязгаарласан:"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Утасны мэдээлэл"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Оператор компанийн хангалтын мэдээлэл"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Оператор компанийн хангалтыг эхлүүлэх"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Зөвлөмж: Bluetooth-н холболтыг сайжруулна уу"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Таны Bluetooth-н дохио сул байна. Чанга яригчтай утас руу сэлгэж үзнэ үү."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Дуудлагын чанарын мэдэгдэл"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"SIP-н зогсоосон бүртгэлүүд"</string>
 </resources>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index dc432d0..8a99cc6 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -105,23 +105,23 @@
     <string name="labelCF" msgid="3578719437928476078">"कॉल फॉरवर्डिंग"</string>
     <string name="labelCFU" msgid="8870170873036279706">"नेहमी फॉरवर्ड करा"</string>
     <string name="messageCFU" msgid="1361806450979589744">"नेहमी हा नंबर वापरा"</string>
-    <string name="sum_cfu_enabled_indicator" msgid="9030139213402432776">"सर्व कॉल अग्रेषित करत आहे"</string>
+    <string name="sum_cfu_enabled_indicator" msgid="9030139213402432776">"सर्व कॉल फॉरवर्ड करत आहे"</string>
     <string name="sum_cfu_enabled" msgid="5806923046528144526">"सर्व कॉल <xliff:g id="PHONENUMBER">{0}</xliff:g> वर फॉरवर्ड करत आहे"</string>
     <string name="sum_cfu_enabled_no_number" msgid="7287752761743377930">"नंबर अनुपलब्‍ध आहे"</string>
     <string name="sum_cfu_disabled" msgid="5010617134210809853">"बंद"</string>
     <string name="labelCFB" msgid="615265213360512768">"व्यस्त असताना"</string>
     <string name="messageCFB" msgid="1958017270393563388">"नंबर व्‍यस्‍त असताना"</string>
-    <string name="sum_cfb_enabled" msgid="332037613072049492">"<xliff:g id="PHONENUMBER">{0}</xliff:g> वर अग्रेषित करत आहे"</string>
+    <string name="sum_cfb_enabled" msgid="332037613072049492">"<xliff:g id="PHONENUMBER">{0}</xliff:g> वर फॉरवर्ड करत आहे"</string>
     <string name="sum_cfb_disabled" msgid="3589913334164866035">"बंद"</string>
     <string name="disable_cfb_forbidden" msgid="4831494744351633961">"तुमचा फोन व्‍यस्‍त असताना तुमचा ऑपरेटर कॉल अग्रेषण करणे अक्षम करण्‍यास समर्थन करीत नाही."</string>
     <string name="labelCFNRy" msgid="3403533792248457946">"उत्तर न दिल्यास"</string>
     <string name="messageCFNRy" msgid="7644434155765359009">"नंबर अनुत्तरित असताना"</string>
-    <string name="sum_cfnry_enabled" msgid="3000500837493854799">"<xliff:g id="PHONENUMBER">{0}</xliff:g> वर अग्रेषित करत आहे"</string>
+    <string name="sum_cfnry_enabled" msgid="3000500837493854799">"<xliff:g id="PHONENUMBER">{0}</xliff:g> वर फॉरवर्ड करत आहे"</string>
     <string name="sum_cfnry_disabled" msgid="1990563512406017880">"बंद"</string>
     <string name="disable_cfnry_forbidden" msgid="3174731413216550689">"तुमचा फोन उत्तर देत नसताना तुमचा ऑपरेटर कॉल अग्रेषण करणे अक्षम करण्‍यास समर्थन करीत नाही."</string>
     <string name="labelCFNRc" msgid="4163399350778066013">"आउट ऑफ रीच असताना"</string>
     <string name="messageCFNRc" msgid="6980340731313007250">"नंबर पोहचण्‍यायोग्‍य नसताना"</string>
-    <string name="sum_cfnrc_enabled" msgid="1799069234006073477">"<xliff:g id="PHONENUMBER">{0}</xliff:g> वर अग्रेषित करत आहे"</string>
+    <string name="sum_cfnrc_enabled" msgid="1799069234006073477">"<xliff:g id="PHONENUMBER">{0}</xliff:g> वर फॉरवर्ड करत आहे"</string>
     <string name="sum_cfnrc_disabled" msgid="739289696796917683">"बंद"</string>
     <string name="disable_cfnrc_forbidden" msgid="775348748084726890">"तुमचा फोन पोहचण्‍यायोग्‍य नसताना तुमचा वाहक कॉल अग्रेषण करणे अक्षम करण्‍यास समर्थन करीत नाही."</string>
     <string name="registration_cf_forbidden" msgid="4386482610771190420">"तुमचा वाहक कॉल फॉरवर्डिंग करण्यास सपोर्ट करत नाही."</string>
@@ -157,7 +157,7 @@
   </string-array>
     <string name="vm_changed" msgid="4739599044379692505">"व्हॉइसमेल नंबर बदलला."</string>
     <string name="vm_change_failed" msgid="7877733929455763566">"व्हॉइसमेल नंबर बदलू शकले नाही.\nही समस्‍या  कायम राहिल्‍यास आपल्‍या वाहकाशी संपर्क साधा."</string>
-    <string name="fw_change_failed" msgid="9179241823460192148">"अग्रेषित करण्‍याचा नंबर बदलू शकलो नाही.\n ही समस्‍या कायम राहिल्‍यास आपल्‍या वाहकाशी संपर्क साधा."</string>
+    <string name="fw_change_failed" msgid="9179241823460192148">"फॉरवर्ड करण्‍याचा नंबर बदलू शकलो नाही.\n ही समस्‍या कायम राहिल्‍यास आपल्‍या वाहकाशी संपर्क साधा."</string>
     <string name="fw_get_in_vm_failed" msgid="2432678237218183844">"वर्तमान अग्रेषण नंबर सेटिंग्‍ज पुनर्प्राप्त करू शकलो नाही आणि सेव्ह करू शकलो नाही.\nतरीही नवीन प्रदात्‍यावर स्‍विच करायचे?"</string>
     <string name="no_change" msgid="3737264882821031892">"कोणतेही बदल केले नाहीत."</string>
     <string name="sum_voicemail_choose_provider" msgid="6750824719081403773">"व्हॉइसमेल सेवा निवडा"</string>
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"मर्यादित सिम फंक्शनॅलिटी"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> चे कॉल आणि डेटा सेवा <xliff:g id="PHONE_NUMBER">%2$s</xliff:g> वापरताना ब्लॉक केले जाऊ शकतात."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> चे कॉल आणि डेटा सेवा दुसरे सिम वापरताना ब्लॉक केले जाऊ शकतात."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"आढळलेली आणि काढून टाकलेली कालबाह्य झालेली SIP खाती"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Android प्लॅटफॉर्मवर आता SIP कॉलिंगला सपोर्ट नाही.\nतुमची अस्तित्वात असलेली SIP खाती <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> काढून टाकली आहेत.\nकृपया तुमच्या कॉलिंगसंबंधित डीफॉल्ट खात्याचे सेटिंग कंफर्म करा."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"सेटिंग्जवर जा"</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>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"पासवर्ड चुकीचा आहे. सिम आता लॉक केले आहे. PUK2 प्रविष्‍ट करा."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 कायमचे अवरोधित केले आहे."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"आपल्‍याकडे <xliff:g id="NUMBER">%d</xliff:g> शिल्लक प्रयत्न आहेत."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 द्वारे लॉक केले आहे. ते अनलॉक करण्यासाठी सेवा पुरवठादाराशी संपर्क साधा."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"पिन2 अवरोधित केला नाही"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"नेटवर्क किंवा सिम कार्ड एरर"</string>
     <string name="doneButton" msgid="7371209609238460207">"पूर्ण झाले"</string>
@@ -519,7 +523,7 @@
     <string name="card_title_dialing" msgid="8742182654254431781">"डायल करत आहे"</string>
     <string name="card_title_redialing" msgid="18130232613559964">"रीडायल करत आहे"</string>
     <string name="card_title_conf_call" msgid="901197309274457427">"कॉंफरन्स कॉल"</string>
-    <string name="card_title_incoming_call" msgid="881424648458792430">"येणारे कॉल"</string>
+    <string name="card_title_incoming_call" msgid="881424648458792430">"इनकमिंग कॉल"</string>
     <string name="card_title_call_ended" msgid="650223980095026340">"कॉल संपला"</string>
     <string name="card_title_on_hold" msgid="9028319436626975207">"होल्ड वर"</string>
     <string name="card_title_hanging_up" msgid="814874106866647871">"हँग अप करणेे"</string>
@@ -780,8 +784,8 @@
     <string name="call_barring_baoicr_enabled" msgid="1615324165512798478">"जाणारे इंटरनॅशनल रोमिंग ब्लॉक करणे बंद करायचे का?"</string>
     <string name="call_barring_baoicr_disabled" msgid="172010175248142831">"जाणारे इंटरनॅशनल रोमिंग ब्लॉक करायचे का?"</string>
     <string name="call_barring_baic" msgid="7941393541678658566">"सर्व येणारे"</string>
-    <string name="call_barring_baic_enabled" msgid="4357332358020337470">"सर्व येणारे कॉल ब्लॉक करणे बंद करायचे का?"</string>
-    <string name="call_barring_baic_disabled" msgid="2355945245938240958">"सर्व येणारे कॉल ब्लॉक करायचे का?"</string>
+    <string name="call_barring_baic_enabled" msgid="4357332358020337470">"सर्व इनकमिंग कॉल ब्लॉक करणे बंद करायचे का?"</string>
+    <string name="call_barring_baic_disabled" msgid="2355945245938240958">"सर्व इनकमिंग कॉल ब्लॉक करायचे का?"</string>
     <string name="call_barring_baicr" msgid="8712249337313034226">"येणारे इंटरनॅशनल रोमिंग"</string>
     <string name="call_barring_baicr_enabled" msgid="64774270234828175">"सर्व येणारे इंटरनॅशनल रोमिंग ब्लॉक करणे बंद करायचे का?"</string>
     <string name="call_barring_baicr_disabled" msgid="3488129262744027262">"येणारे इंटरनॅशनल रोमिंग ब्लॉक करायचे का?"</string>
@@ -806,7 +810,7 @@
     <string name="supp_service_notification_call_waiting" msgid="4577403881609445324">"कॉल प्रतीक्षेत आहे."</string>
     <string name="supp_service_clir_suppression_rejected" msgid="6105737020194776121">"क्रमांक ब्‍लॉक करणे रद्द केले."</string>
     <string name="supp_service_closed_user_group_call" msgid="2811636666505250689">"बंद वापरकर्ता गट कॉल."</string>
-    <string name="supp_service_incoming_calls_barred" msgid="2034627421274447674">"येणारे कॉल अवरोधित केले."</string>
+    <string name="supp_service_incoming_calls_barred" msgid="2034627421274447674">"इनकमिंग कॉल अवरोधित केले."</string>
     <string name="supp_service_outgoing_calls_barred" msgid="5205725332394087112">"जाणारे कॉल अवरोधित केले."</string>
     <string name="supp_service_call_forwarding_active" msgid="7910162960395132464">"कॉल फॉरवर्ड होत आहे."</string>
     <string name="supp_service_additional_call_forwarded" msgid="8772753260008398632">"अतिरिक्‍त कॉल फॉरवर्ड केला."</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"फोनची माहिती"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"वाहक सुविधा माहिती"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"वाहक सुविधा ट्रिगर करा"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"सूचना: ब्लूटूथ कनेक्टिव्हिटीमध्ये सुधारणा करा"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"तुमचा ब्लूटूथ सिग्नल कमकुवत आहे. स्पीकरफोनवर स्विच करून पाहा."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"कॉल गुणवत्ता सूचना"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"कालबाह्य झालेली SIP खाती"</string>
 </resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 0f07649..9236de2 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Fungsi SIM terhad"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Perkhidmatan panggilan dan data <xliff:g id="CARRIER_NAME">%1$s</xliff:g> mungkin disekat semasa menggunakan <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Perkhidmatan panggilan dan data <xliff:g id="CARRIER_NAME">%1$s</xliff:g> mungkin disekat semasa menggunakan SIM lain."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Akaun SIP yang ditamatkan telah ditemukan dan dialih keluar"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Panggilan SIP tidak lagi disokong oleh platform Android.\nAkaun SIP <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> anda yang sedia ada telah dialih keluar.\nSila sahkan tetapan akaun panggilan lalai anda."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Akses tetapan"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Penggunaan data apl"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> data mudah alih digunakan <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Terperinci"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Kata laluan tidak betul. SIM kini dikunci. Masukkan PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 disekat secara kekal."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Anda mempunyai <xliff:g id="NUMBER">%d</xliff:g> percubaan lagi."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 dikunci. Hubungi penyedia perkhidmatan untuk membuka kunci."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 tidak disekat lagi"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Ralat rangkaian atau kad SIM"</string>
     <string name="doneButton" msgid="7371209609238460207">"Selesai"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Maklumat telefon"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Maklumat Peruntukan Pembawa"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Cetus Peruntukan Pembawa"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Cadangan: Tingkatkan kesambungan Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Isyarat bluetooth anda lemah. Cuba beralih kepada fon pembesar suara."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Pemberitahuan Kualiti Panggilan"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Akaun SIP ditamatkan"</string>
 </resources>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index efd3e6b..a967b2d 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -49,7 +49,7 @@
     <string name="add_vm_number_str" msgid="7368168964435881637">"နံပါတ်ထပ်ထည့်ရန်"</string>
     <string name="voice_number_setting_primary_user_only" msgid="3394706575741912843">"အသံမေးလ်ဆက်တင်များကို အဓိကအသုံးပြုသူသာ ပြင်ဆင်နိုင်ပါသည်။"</string>
     <string name="puk_unlocked" msgid="4627340655215746511">"သင့် ဆင်းမ်ကဒ်အား ပိတ်ဆို့မှုကို ဖယ်ရှားပြီးပါပြီ။ သင့်ဖုန်းဟာ သော့ဖွင့်နေပါသည်…"</string>
-    <string name="label_ndp" msgid="7617392683877410341">"ဆင်းမ်ကဒ် ကွန်ရက် သော့ဖွင့်သော ပင်နံပါတ်"</string>
+    <string name="label_ndp" msgid="7617392683877410341">"ဆင်းမ်ကတ် ကွန်ရက် သော့ဖွင့်သော ပင်နံပါတ်"</string>
     <string name="label_phoneid" msgid="8775611434123577808">"အော်ပရေတာအတွက် ဆင်းမ်ကတ်ကို လော့ခ်ချထားသည်"</string>
     <string name="sim_ndp_unlock_text" msgid="7737338355451978338">"ဖွင့်ရန်"</string>
     <string name="sim_ndp_dismiss_text" msgid="89667342248929777">"ပယ်ရန်"</string>
@@ -186,9 +186,9 @@
     <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>
-    <string name="preferred_network_mode_title" msgid="5253395265169539830">"ပိုနှစ်သက်သော ကွန်ရက်အမျိုးအစား"</string>
+    <string name="preferred_network_mode_title" msgid="5253395265169539830">"ဦးစားပေး ကွန်ရက်အမျိုးအစား"</string>
     <string name="preferred_network_mode_summary" msgid="3787989000044330064">"ကွန်ရက် လုပ်ဆောင်မှုစနစ်အား ပြောင်းပါ"</string>
-    <string name="preferred_network_mode_dialogtitle" msgid="2781447433514459696">"ပိုနှစ်သက်သော ကွန်ရက်အမျိုးအစား"</string>
+    <string name="preferred_network_mode_dialogtitle" msgid="2781447433514459696">"ဦးစားပေး ကွန်ရက်အမျိုးအစား"</string>
     <string name="forbidden_network" msgid="5081729819561333023">"(တားမြစ်ထားသည်)"</string>
     <string name="choose_network_title" msgid="5335832663422653082">"ကွန်ရက် ရွေးရန်"</string>
     <string name="network_disconnected" msgid="8844141106841160825">"ချိတ်ဆက်မှုပြတ်နေပါသည်"</string>
@@ -282,8 +282,8 @@
     <string name="data_enable_summary" msgid="696860063456536557">"ဒေတာ သုံးစွဲမှု ခွင့်ပြုရန်"</string>
     <string name="dialog_alert_title" msgid="5260471806940268478">"သတိပြုရန်"</string>
     <string name="roaming" msgid="1576180772877858949">"ပြင်ပကွန်ရက်နှင့် ချိတ်ဆက်ခြင်း"</string>
-    <string name="roaming_enable" msgid="6853685214521494819">"ကွန်ရက်ပြင်ပဒေတာကို သုံးနေစဉ် ဒေတာဝန်ဆောင်မှုများကို ချိတ်ဆက်ပါ"</string>
-    <string name="roaming_disable" msgid="8856224638624592681">"ကွန်ရက်ပြင်ပဒေတာကို သုံးနေစဉ် ဒေတာဝန်ဆောင်မှုများကို ချိတ်ဆက်ပါ"</string>
+    <string name="roaming_enable" msgid="6853685214521494819">"ပြင်ပကွန်ရက်သုံးလျှင် ဒေတာဝန်ဆောင်မှုများကို ချိတ်ဆက်ပါ"</string>
+    <string name="roaming_disable" msgid="8856224638624592681">"ပြင်ပကွန်ရက်သုံးလျှင် ဒေတာဝန်ဆောင်မှုများကို ချိတ်ဆက်ပါ"</string>
     <string name="roaming_reenable_message" msgid="1951802463885727915">"ကွန်ရက်ပြင်ပဒေတာသုံးခြင်းကို ပိတ်ထားပါသည်။ ဖွင့်ရန် တို့ပါ။"</string>
     <string name="roaming_enabled_message" msgid="9022249120750897">"ပြင်ပကွန်ရက်နှင့် ချိတ်ဆက်သော အသုံးပြုခများ ကျသင့်နိုင်သည်။ ပြင်ဆင်ရန် တို့ပါ။"</string>
     <string name="roaming_notification_title" msgid="3590348480688047320">"မိုဘိုင်းဒေတာ ချိတ်ဆက်မှု မရှိတော့ပါ"</string>
@@ -294,11 +294,14 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"ကန့်သတ်ထားသော ဆင်းမ်ကတ် လုပ်ဆောင်ချက်"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g>.အသုံးပြုနေစဉ် <xliff:g id="CARRIER_NAME">%1$s</xliff:g> ခေါ်ဆိုမှုများနှင့် ဒေတာဝန်ဆောင်မှုများကို ပိတ်ထားနိုင်သည်။"</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"အခြားဆင်းမ်ကတ် သုံးနေစဉ် <xliff:g id="CARRIER_NAME">%1$s</xliff:g> ခေါ်ဆိုမှုနှင့် ဒေတာဝန်ဆောင်မှုများ ပိတ်ထားနိုင်သည်။"</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"ရပ်ဆိုင်းထားသော SIP အကောင့်များ တွေ့ရှိပြီး ဖယ်ရှားလိုက်သည်"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP ခေါ်ဆိုမှုကို Android စနစ်က မပံ့ပိုးတော့ပါ။\nသင်၏လက်ရှိ SIP အကောင့် <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> ကို ဖယ်ရှားလိုက်သည်။\nသင့်မူရင်း ခေါ်ဆိုမှုအကောင့်ဆက်တင်ကို အတည်ပြုပါ။"</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"ဆက်တင်များသို့ သွားရန်"</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="carrier_settings_euicc" msgid="1190237227261337749">"ဝန်ဆောင်မှုပေးသူ"</string>
-    <string name="keywords_carrier_settings_euicc" msgid="8540160967922063745">"ဖုန်းဝန်ဆောင်မှုပေးသူ၊ esim၊ ဆင်းမ်ကဒ်၊ euicc၊ ဖုန်းလိုင်းများ ပြောင်းရန်၊ ဖုန်းလိုင်း ထည့်ရန်"</string>
+    <string name="keywords_carrier_settings_euicc" msgid="8540160967922063745">"ဖုန်းကုမ္ပဏီ၊ esim၊ ဆင်းမ်ကတ်၊ euicc၊ ဖုန်းလိုင်းများ ပြောင်းရန်၊ ဖုန်းလိုင်း ထည့်ရန်"</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>
     <string name="mobile_data_settings_title" msgid="7228249980933944101">"မိုဘိုင်းဒေတာ"</string>
     <string name="mobile_data_settings_summary" msgid="5012570152029118471">"မိုဘိုင်းကွန်ရက်သုံးပြီး ဒေတာကို ဝင်သုံးခွင့်ပေးပါ"</string>
@@ -481,8 +484,8 @@
     <string name="simContacts_title" msgid="2714029230160136647">"ထည့်ယူရန် လိပ်စာများ ရွေးပါ"</string>
     <string name="simContacts_airplaneMode" msgid="4654884030631503808">"SIM ကဒ်မှ အဆက်အသွယ်များ သွင်းယူရန် လေယာဉ်ပျံမုဒ် ပိတ်ထားပါ။"</string>
     <string name="enable_pin" msgid="967674051730845376">"ဆင်းမ် ပင်နံပါတ်ကို ပယ်ဖျက်၊ပြုလုပ်ရန်"</string>
-    <string name="change_pin" msgid="3657869530942905790">"ဆင်းမ်ကဒ် ပင်နံပါတ်ပြောင်းရန်"</string>
-    <string name="enter_pin_text" msgid="3182311451978663356">"ဆင်းမ်ကဒ် ပင်နံပါတ်:"</string>
+    <string name="change_pin" msgid="3657869530942905790">"ဆင်းမ်ကတ် ပင်နံပါတ်ပြောင်းရန်"</string>
+    <string name="enter_pin_text" msgid="3182311451978663356">"ဆင်းမ်ကတ် ပင်နံပါတ်:"</string>
     <string name="oldPinLabel" msgid="8618515202411987721">"ပင်နံပါတ် အဟောင်း"</string>
     <string name="newPinLabel" msgid="3585899083055354732">"ပင်နံပါတ် အသစ်"</string>
     <string name="confirmPinLabel" msgid="7783531218662473778">"ပင်နံပါတ်အသစ်အား သေချာကြောင်း လက်ခံပါ"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"လျှို့ဝှက်စကားလုံး မမှန်ကန်ပါ။ SIM ယခုသော့ခတ်လိုက်ပါပြီ။ PUK2 ရိုက်ထည့်ပါ။"</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 ကို ထာ၀ရ ပိတ်ထားပါသည်"</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"သင့်တွင် လက်ကျန် ကြိုးစားခွင့် <xliff:g id="NUMBER">%d</xliff:g> ရှိသည်။"</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 လော့ခ်ချထားသည်။ ဖွင့်ရန် ဝန်ဆောင်မှုပေးသူကို ဆက်သွယ်ပါ။"</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"ပင်နံပါတ်2 အား အဆက်မဖြတ်တော့ပါ။"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"ကွန်ရက် သို့မဟုတ် ဆင်းမ်ကဒ်မှားယွင်းမှု"</string>
     <string name="doneButton" msgid="7371209609238460207">"ပြီးပါပြီ"</string>
@@ -768,7 +772,7 @@
     <string name="clh_callFailed_protocol_Error_unspecified_txt" msgid="9203320572562697755">"ခေါ်ဆိုမှုကို မပြုလုပ်နိုင်ပါ။ အမှားကုဒ် ၁၁၁။"</string>
     <string name="clh_callFailed_interworking_unspecified_txt" msgid="7969686413930847182">"ခေါ်ဆိုမှုကို မပြုလုပ်နိုင်ပါ။ အမှားကုဒ် ၁၂၇။"</string>
     <string name="labelCallBarring" msgid="4180377113052853173">"ခေါ်ဆိုမှုကို ပိတ်ပင်ရန်"</string>
-    <string name="sum_call_barring_enabled" msgid="5184331188926370824">"ဖွင့်ထားသည်"</string>
+    <string name="sum_call_barring_enabled" msgid="5184331188926370824">"ဖွင့်"</string>
     <string name="sum_call_barring_disabled" msgid="5699448000600153096">"ပိတ်ထားသည်"</string>
     <string name="call_barring_baoc" msgid="7400892586336429326">"အထွက်ခေါ်ဆိုမှုအားလုံး"</string>
     <string name="call_barring_baoc_enabled" msgid="3131509193386668182">"အထွက်ခေါ်ဆိုမှုအားလုံး ပိတ်ထားခြင်းကို ပယ်ဖျက်မလား။"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"ဖုန်းအချက်အလက်"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"ဝန်ဆောင်မှုပေးသူက ပေးထားသည့် အချက်အလက်များ"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"အစပျိုးပေးသော ဖုန်းကုမ္ပဏီ ပံ့ပိုးမှု"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"အကြံပြုချက်- ဘလူးတုသ်ချိတ်ဆက်နိုင်မှုကို ပိုကောင်းအောင် ပြုလုပ်ပါ"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"သင်၏ ဘလူးတုသ်လိုင်းဆွဲအား မကောင်းပါ။ စပီကာဖုန်းသို့ ပြောင်းကြည့်ပါ။"</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"ခေါ်ဆိုမှုအရည်အသွေး အကြောင်းကြားချက်"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"ရပ်ဆိုင်းထားသော SIP အကောင့်များ"</string>
 </resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 93e734a..0db6b75 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Begrensede SIM-kortfunksjoner"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Det kan hende <xliff:g id="CARRIER_NAME">%1$s</xliff:g>-anrop og -datatjenester blir blokkert mens du bruker <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g>-anrop og -datatjenester kan bli blokkert mens du bruker et annet SIM-kort."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"De avviklede SIP-kontoene er funnet og fjernet"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP-anrop støttes ikke lenger på Android-plattformen.\nDe eksisterende SIP-kontoene dine (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) er fjernet.\nBekreft innstillingen din for standardkonto for anrop."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Gå til innstillingene"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Appens databruk"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> mobildata er brukt i perioden <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Avansert"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Passordet er feil. PIN-koden er nå blokkert. Skriv inn en PUK2-kode."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 er permanent sperret."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Du har <xliff:g id="NUMBER">%d</xliff:g> forsøk igjen."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2-låst. Kontakt tjenesteleverandøren for å låse opp."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2-koden er ikke lenger blokkert"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Feil på nettverket eller SIM-kortet"</string>
     <string name="doneButton" msgid="7371209609238460207">"Ferdig"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Telefoninformasjon"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Informasjon om operatørhåndtering"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Utløs operatørhåndtering"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Forslag: Gjør Bluetooth-tilkoblingen bedre"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth-signalet er svakt. Prøv å bytte til høyttaleren."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Varsel om anropskvalitet"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Avviklede SIP-kontoer"</string>
 </resources>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 817f24b..dd61d42 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -96,7 +96,7 @@
     <string name="sum_loading_settings" msgid="434063780286688775">"सेटिङहरू लोड हुँदै..."</string>
     <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="sum_default_caller_id" msgid="1767070797135682959">"बहिर्गमन कलमा मेरो नम्बर देखाउन डिफल्ट अपरेटर सेटिङहरू प्रयोग गर्नुहोस्"</string>
     <string name="labelCW" msgid="8449327023861428622">"कल प्रतीक्षा गर्दै"</string>
     <string name="sum_cw_enabled" msgid="3977308526187139996">"कल गरेको बखत मलाई आगमन कलको जानकारी गराउनुहोस्"</string>
     <string name="sum_cw_disabled" msgid="3658094589461768637">"कल गरेको बेलामा आगमन कलहरूको बारेमा मलाई जानकारी गराउनुहोस्"</string>
@@ -151,7 +151,7 @@
     <string name="disable" msgid="1122698860799462116">"निस्कृय पार्नुहोस्"</string>
     <string name="change_num" msgid="6982164494063109334">"अपडेट गर्नुहोस्"</string>
   <string-array name="clir_display_values">
-    <item msgid="8477364191403806960">"नेटवर्क पूर्वनिर्धारित"</item>
+    <item msgid="8477364191403806960">"नेटवर्क डिफल्ट"</item>
     <item msgid="6813323051965618926">"सङ्ख्या लुकाउनुहोस्"</item>
     <item msgid="9150034130629852635">"सङ्ख्या देखाउनुहोस्"</item>
   </string-array>
@@ -282,8 +282,8 @@
     <string name="data_enable_summary" msgid="696860063456536557">"डेटा उपयोगलाई अनुमति दिनुहोस्"</string>
     <string name="dialog_alert_title" msgid="5260471806940268478">"ध्यानाकर्षण"</string>
     <string name="roaming" msgid="1576180772877858949">"रोमिङ"</string>
-    <string name="roaming_enable" msgid="6853685214521494819">"रोमिङको समयमा डेटा सेवामा जडान गर्नुहोस्"</string>
-    <string name="roaming_disable" msgid="8856224638624592681">"रोमिङको समयमा डेटा सेवा जडान गर्नुहोस्"</string>
+    <string name="roaming_enable" msgid="6853685214521494819">"रोमिङको समयमा डेटा सेवामा कनेक्ट गर्नुहोस्"</string>
+    <string name="roaming_disable" msgid="8856224638624592681">"रोमिङको समयमा डेटा सेवा कनेक्ट गर्नुहोस्"</string>
     <string name="roaming_reenable_message" msgid="1951802463885727915">"डेटा रोमिङ सक्रिय गरियो। सक्रिय गर्न ट्याप गर्नुहोस्।"</string>
     <string name="roaming_enabled_message" msgid="9022249120750897">"रोमिङ शुल्क लाग्न सक्छ। परिमार्जन गर्न ट्याप गर्नुहोस्"</string>
     <string name="roaming_notification_title" msgid="3590348480688047320">"मोबाइल डेटाको जडान टुट्यो"</string>
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"SIM को सीमित सुविधा"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g>.प्रयोग गर्दा <xliff:g id="CARRIER_NAME">%1$s</xliff:g> का कल तथा डेटा सेवाहरूलाई रोक लगाइन सक्छ"</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"अर्को SIM प्रयोग गर्दा <xliff:g id="CARRIER_NAME">%1$s</xliff:g> कल तथा डेटा सेवहरूलाई रोक लगाइन सक्छ।"</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"चल्तीबाट हटाइएका SIP खाताहरू भेट्टाइयो र हटाइयो"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"अबदेखि Android प्लेटफर्ममा SIP कल गर्न मिल्दैन।\nतपाईंका हालका SIP खाताहरू <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> हटाइएका छन्।\nकृपया आफ्नो डिफल्ट कल गर्ने खाताको सेटिङ पुष्टि गर्नुहोस्।"</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"सेटिङमा जानुहोस्"</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>
@@ -481,7 +484,7 @@
     <string name="simContacts_title" msgid="2714029230160136647">"सम्पर्कहरू आयात गर्न चयन गर्नुहोस्"</string>
     <string name="simContacts_airplaneMode" msgid="4654884030631503808">"SIM कार्डबाट सम्पर्कहरू आयात गर्न हवाइजहाज मोड बन्द गर्नुहोस्।"</string>
     <string name="enable_pin" msgid="967674051730845376">"SIM PIN सक्षम/अक्षम गर्नुहोस्"</string>
-    <string name="change_pin" msgid="3657869530942905790">"SIM PIN परिवर्तन गर्नुहोस्"</string>
+    <string name="change_pin" msgid="3657869530942905790">"SIM को PIN परिवर्तन गर्नुहोस्"</string>
     <string name="enter_pin_text" msgid="3182311451978663356">"SIM PIN:"</string>
     <string name="oldPinLabel" msgid="8618515202411987721">"पुरानो PIN"</string>
     <string name="newPinLabel" msgid="3585899083055354732">"नयाँ PIN"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"पासवर्ड गलत छ। सिम अब ब्लक गरिएको छ। PUK2 प्रविष्टि गर्नुहोस्।"</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 स्थायी रूपमा रोक्का गरियो।"</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n" तपाईंसँग <xliff:g id="NUMBER">%d</xliff:g> प्रयास बाँकी छन्।"</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"SIM कार्ड PUK2 बाट लक गरिएको छ। अनलक गर्न सेवा प्रदायकलाई सम्पर्क गर्नुहोस्।"</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 अब ब्लक गरिएको छैन"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"नेटवर्क वा SIM कार्ड त्रुटि।"</string>
     <string name="doneButton" msgid="7371209609238460207">"सम्पन्न भयो"</string>
@@ -532,11 +536,11 @@
     <string name="notification_network_selection_text" msgid="553288408722427659">"चयन गरिएको नेटवर्क <xliff:g id="OPERATOR_NAME">%s</xliff:g> उपलब्ध छैन"</string>
     <string name="incall_error_power_off" product="watch" msgid="7191184639454113633">"मोबाइल नेटवर्क सक्रिय पार्नुहोस्, कल गर्न हवाइजहाज मोड वा ब्याट्री सेवर मोड निष्क्रिय पार्नुहोस्।"</string>
     <string name="incall_error_power_off" product="default" msgid="8131672264311208673">"एक कल गर्न हवाइजहाज मोड बन्द गर्नुहोस्।"</string>
-    <string name="incall_error_power_off_wfc" msgid="9125661184694727052">"एक कल गर्न हवाइजहाज मोड बन्द गर्नुहोस् वा एक ताररहितको सञ्जालमा जडान गर्नुहोस्।"</string>
+    <string name="incall_error_power_off_wfc" msgid="9125661184694727052">"एक कल गर्न हवाइजहाज मोड बन्द गर्नुहोस् वा एक ताररहितको सञ्जालमा कनेक्ट गर्नुहोस्।"</string>
     <string name="incall_error_ecm_emergency_only" msgid="5622379058883722080">"गैर-आपत्‌कालीन कल गर्न आपत्‌कालीन कलब्याक मोडबाट निस्कनुहोस्।"</string>
     <string name="incall_error_emergency_only" msgid="8786127461027964653">"नेटवर्कमा दर्ता भएको छैन।"</string>
     <string name="incall_error_out_of_service" msgid="1927265196942672791">"मोबाइल नेटवर्क उपलब्ध छैन।"</string>
-    <string name="incall_error_out_of_service_wfc" msgid="4497663185857190885">"मोबाइल नेटवर्क उपलब्ध छैन। कल गर्न तारविनाको नेटवर्कमा जडान गर्नुहोस्।"</string>
+    <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>
@@ -549,7 +553,7 @@
     <string name="incall_error_supp_service_reject" msgid="3044363092441655912">"कल अस्वीकार गर्न सक्दैन।"</string>
     <string name="incall_error_supp_service_hangup" msgid="836524952243836735">"कल (हरू) जारी गर्न सकिँदैन ।"</string>
     <string name="incall_error_supp_service_hold" msgid="8535056414643540997">"कल सञ्चालन गर्न सकिँदैन।"</string>
-    <string name="incall_error_wfc_only_no_wireless_network" msgid="5860742792811400109">"एक कल गर्न एक ताररहितको सञ्जालमा जडान गर्नुहोस्।"</string>
+    <string name="incall_error_wfc_only_no_wireless_network" msgid="5860742792811400109">"एक कल गर्न एक ताररहितको सञ्जालमा कनेक्ट गर्नुहोस्।"</string>
     <string name="incall_error_promote_wfc" msgid="9164896813931363415">"कल गर्नका लागि Wi-Fi कलिङ सक्षम गर्नुहोस्।"</string>
     <string name="emergency_information_hint" msgid="9208897544917793012">"आपत्‌कालीन जानकारी"</string>
     <string name="emergency_information_owner_hint" msgid="6256909888049185316">"मालिक"</string>
@@ -564,7 +568,7 @@
     <string name="dial_emergency_error" msgid="825822413209026039">"कल गर्न सकिँदैन। <xliff:g id="NON_EMERGENCY_NUMBER">%s</xliff:g> आपत्‌कालीन नम्बर होइन।"</string>
     <string name="dial_emergency_empty_error" msgid="2785803395047793634">"कल गर्न सकिँदैन। आपत्‌कालीन नम्बर डायल गर्नुहोस्।"</string>
     <string name="dial_emergency_calling_not_available" msgid="6485846193794727823">"आपत्‌कालीन कल सेवा उपलब्ध छैन"</string>
-    <string name="pin_puk_system_user_only" msgid="1045147220686867922">"यन्त्रका मालिक मात्र PIN/PUK कोडहरू प्रविष्टि गर्न सक्नुहुन्छ।"</string>
+    <string name="pin_puk_system_user_only" msgid="1045147220686867922">"डिभाइसका मालिक मात्र PIN/PUK कोडहरू प्रविष्टि गर्न सक्नुहुन्छ।"</string>
     <string name="police_type_description" msgid="2819533883972081757">"प्रहरी"</string>
     <string name="ambulance_type_description" msgid="6798237503553180461">"एम्बुलेन्स सेवा"</string>
     <string name="fire_type_description" msgid="6565200468934914930">"दमकल सेवा"</string>
@@ -589,7 +593,7 @@
     <string name="failedToImportSingleContactMsg" msgid="228095510489830266">"सम्पर्क आयात गर्न असफल"</string>
     <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_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="no_rtt_when_roaming" msgid="5268008247378355389">"टिपोट: रोमिङमा हुँदा RTT उपलब्ध हुँदैन"</string>
@@ -653,7 +657,7 @@
     </plurals>
     <string name="voicemail_provider" msgid="4158806657253745294">"सेवा"</string>
     <string name="voicemail_settings" msgid="4451045613238972776">"सेटअप"</string>
-    <string name="voicemail_number_not_set" msgid="8831561283386938155">"सेट गरेको छैन"</string>
+    <string name="voicemail_number_not_set" msgid="8831561283386938155">"सेट गरिएको छैन"</string>
     <string name="other_settings" msgid="8895088007393598447">"अन्य कल सेटिङहरू"</string>
     <string name="calling_via_template" msgid="1791323450703751750">"<xliff:g id="PROVIDER_NAME">%s</xliff:g> को मार्फत कल गर्दै"</string>
     <string name="contactPhoto" msgid="7885089213135154834">"सम्पर्क फोटो"</string>
@@ -818,7 +822,7 @@
     <string name="supp_service_forwarded_call" msgid="6475776013771821457">"कल फर्वार्ड गरियो।"</string>
     <string name="supp_service_conference_call" msgid="4004193534408317148">"सम्मेलन कलमा सामेल हुँदै छ।"</string>
     <string name="supp_service_held_call_released" msgid="2847835124639112410">"होल्डमा राखिएको कललाई विच्छेद गरियो।"</string>
-    <string name="callFailed_otasp_provisioning_in_process" msgid="3345666183602879326">"यन्त्रमा हाल सेवाहरूको व्यवस्था गरिँदै हुनाले कल गर्न सकिँदैन।"</string>
+    <string name="callFailed_otasp_provisioning_in_process" msgid="3345666183602879326">"डिभाइसमा हाल सेवाहरूको व्यवस्था गरिँदै हुनाले कल गर्न सकिँदैन।"</string>
     <string name="callFailed_already_dialing" msgid="7250591188960691086">"अर्को बहिर्गमन कल पहिले नै डायल भएका हुनाले कल गर्न सकिँदैन।"</string>
     <string name="callFailed_already_ringing" msgid="2376603543544289303">"जवाफ नदिइएको आगमन कल जारी रहेकाले नयाँ कल गर्न सकिँदैन। कुनै नयाँ कल गर्नुअघि आगमन कलको जवाफ दिनुहोस् वा त्यसलाई अस्वीकार गर्नुहोस्।"</string>
     <string name="callFailed_calling_disabled" msgid="5010992739401206283">"ro.telephony.disable-call प्रणालीको गुण प्रयोग गरेर कल गर्ने सुविधा असक्षम पारिएको हुनाले कल गर्न सकिँदैन।"</string>
@@ -872,7 +876,7 @@
     <string name="radioInfo_lac" msgid="3892986460272607013">"LAC"</string>
     <string name="radioInfo_cid" msgid="1423185536264406705">"CID"</string>
     <string name="radio_info_subid" msgid="6839966868621703203">"हालको subId:"</string>
-    <string name="radio_info_dds" msgid="1122593144425697126">"पूर्वनिर्धारित डेटा SIM को 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_phy_chan_config" msgid="1277949603275436081">"LTE को भौतिक च्यानलको कन्फिगरेसन:"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"फोनसम्बन्धी जानकारी"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"सेवा प्रदायकको प्रावधानसम्बन्धी जानकारी"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"सेवा प्रदायकको प्रावधानलाई ट्रिगर गर्नुहोस्"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"सुझाव: ब्लुटुथको कनेक्टिभिटीको गुणस्तर सुधार्नुहोस्"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"ब्लुटुथको सिग्नल कमजोर छ। स्पिकरफोन प्रयोग गरी हेर्नुहोस्।"</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"कलको गुणस्तरसम्बन्धी सूचना"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"चल्तीबाट हटाइएका SIP खाताहरू"</string>
 </resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 546efa7..7fa1a7b 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -32,7 +32,7 @@
     <string name="mmiCancelled" msgid="5339191899200678272">"MMI-code geannuleerd"</string>
     <string name="cancel" msgid="8984206397635155197">"Annuleren"</string>
     <string name="enter_input" msgid="6193628663039958990">"USSD-bericht moet tussen <xliff:g id="MIN_LEN">%1$d</xliff:g> en <xliff:g id="MAX_LEN">%2$d</xliff:g> tekens lang zijn. Probeer het opnieuw."</string>
-    <string name="manageConferenceLabel" msgid="8415044818156353233">"Conferencecall beheren"</string>
+    <string name="manageConferenceLabel" msgid="8415044818156353233">"Telefonische vergadering beheren"</string>
     <string name="ok" msgid="7818974223666140165">"OK"</string>
     <string name="audio_mode_speaker" msgid="243689733219312360">"Luidspreker"</string>
     <string name="audio_mode_earpiece" msgid="2823700267171134282">"Oortelefoon van handset"</string>
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Beperkte simkaartfunctionaliteit"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Gesprekken en dataservices van <xliff:g id="CARRIER_NAME">%1$s</xliff:g> kunnen worden geblokkeerd terwijl je <xliff:g id="PHONE_NUMBER">%2$s</xliff:g> gebruikt."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g>-gesprekken en -dataservices kunnen worden geblokkeerd voor een andere simkaart."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Beëindigde SIP-accounts gevonden en verwijderd"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP-gesprekken worden niet meer ondersteund door het Android-platform.\nJe bestaande SIP-accounts <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> zijn verwijderd.\nBevestig de instelling voor je standaard belaccount."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Naar instellingen"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Datagebruik van app"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> mobiele data gebruikt van <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Geavanceerd"</string>
@@ -512,13 +515,14 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Wachtwoord onjuist. Simkaart nu vergrendeld. Geef PUK2 op."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2-code is definitief geblokkeerd."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Je hebt nog <xliff:g id="NUMBER">%d</xliff:g> pogingen."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 vergrendeld. Neem contact op met je serviceprovider om te ontgrendelen."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 niet langer geblokkeerd"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Netwerk- of simkaartfout"</string>
     <string name="doneButton" msgid="7371209609238460207">"Klaar"</string>
     <string name="voicemail_settings_number_label" msgid="1265118640154688162">"Voicemailnummer"</string>
     <string name="card_title_dialing" msgid="8742182654254431781">"Kiezen"</string>
     <string name="card_title_redialing" msgid="18130232613559964">"Opnieuw bellen"</string>
-    <string name="card_title_conf_call" msgid="901197309274457427">"Conferencecall"</string>
+    <string name="card_title_conf_call" msgid="901197309274457427">"Telefonische vergadering"</string>
     <string name="card_title_incoming_call" msgid="881424648458792430">"Inkomend gesprek"</string>
     <string name="card_title_call_ended" msgid="650223980095026340">"Gesprek beëindigd"</string>
     <string name="card_title_on_hold" msgid="9028319436626975207">"In de wacht"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Telefooninformatie"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Gegevens voor providerregistratie"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Providerregistratie activeren"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Suggestie: verbeter de bluetooth-verbinding"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Het bluetooth-signaal is zwak. Schakel over naar bellen op luidspreker."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Melding over gesprekskwaliteit"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Beëindigde SIP-accounts"</string>
 </resources>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index b883db1..309f20f 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"ସୀମିତ SIM ପ୍ରକାର୍ଯ୍ୟ"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g> ବ୍ୟବହାର କରିବା ସମୟରେ <xliff:g id="CARRIER_NAME">%1$s</xliff:g> କଲ୍ ଏବଂ ଡାଟା ସେବାଗୁଡ଼ିକ ବ୍ଲକ୍ କରାଯାଇପାରେ।"</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"ଅନ୍ୟ SIM ବ୍ୟବହାର କରିବା ସମୟରେ <xliff:g id="CARRIER_NAME">%1$s</xliff:g> କଲ୍ ଏବଂ ଡାଟା ସେବାଗୁଡ଼ିକ ବ୍ଲକ୍ କରାଯାଇପାରେ।"</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"ଅସମର୍ଥିତ SIP ଆକାଉଣ୍ଟଗୁଡ଼ିକ ମିଳିଛି ଏବଂ କାଢ଼ି ଦିଆଯାଇଛି"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP କଲିଂ ଆଉ Android ପ୍ଲାଟଫର୍ମ ଦ୍ୱାରା ସମର୍ଥିତ ନୁହେଁ।\nଆପଣଙ୍କର ପୂର୍ବରୁ ଥିବା SIP ଆକାଉଣ୍ଟ <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>କୁ କାଢ଼ି ଦିଆଯାଇଛି।\nଦୟାକରି ଆପଣଙ୍କ ଡିଫଲ୍ଟ କଲିଂ ଆକାଉଣ୍ଟ ସେଟିଂକୁ ସୁନିଶ୍ଚିତ କରନ୍ତୁ।"</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"ସେଟିଂସକୁ ଯାଆନ୍ତୁ"</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>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"ଭୁଲ୍ ପାସ୍‌ୱର୍ଡ। ବର୍ତ୍ତମାନ SIM ଲକ୍ ହୋ‌ଇଗଲା। PUK2କୁ ପ୍ରବେଶ କରନ୍ତୁ।"</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2କୁ ସ୍ଥାୟୀରୂପେ ଅବରୋଧ କରାଯାଇଛି।"</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"ଆପଣଙ୍କର <xliff:g id="NUMBER">%d</xliff:g>ଟି ସୁଯୋଗ ବଳକା ଅଛି।"</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 ଲକ୍ କରାଯାଇଛି। ଅନଲକ୍ କରିବାକୁ ସେବା ପ୍ରଦାନକାରୀ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ।"</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2କୁ ଆଉ ଅବରୋଧ କରାଯାଇନାହିଁ"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"ନେଟ୍‌ୱର୍କ କିମ୍ବା SIM କାର୍ଡ ତ୍ରୁଟି"</string>
     <string name="doneButton" msgid="7371209609238460207">"ହୋଇଗଲା"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"ଫୋନ୍ ସୂଚନା"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"ମୋବାଇଲ୍ କମ୍ପାନୀ ପ୍ରାବଧାନ ସୂଚନା"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"କେରିଅର୍ ପ୍ରୋଭିଜନିଂ ଟ୍ରିଗର୍ କରନ୍ତୁ"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"ପରାମର୍ଶ: ବ୍ଲୁଟୁଥ୍ ସଂଯୋଗ ଉନ୍ନତ କରନ୍ତୁ"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"ଆପଣଙ୍କ ବ୍ଲୁଟୁଥ୍ ସିଗନାଲ୍ ଦୁର୍ବଳ ଅଛି। ସ୍ପିକରଫୋନକୁ ସ୍ୱିଚ୍ କରିବା ପାଇଁ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"କଲ୍ ଗୁଣବତ୍ତା ବିଜ୍ଞପ୍ତି"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"ଅସମର୍ଥିତ SIP ଆକାଉଣ୍ଟଗୁଡ଼ିକ"</string>
 </resources>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 2bf0d45..2fc2689 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"ਸੀਮਤ ਸਿਮ ਪ੍ਰਕਾਰਜਾਤਮਕਤਾ"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g> ਨੂੰ ਵਰਤਣ ਵੇਲੇ ਸ਼ਾਇਦ <xliff:g id="CARRIER_NAME">%1$s</xliff:g> ਦੀਆਂ ਕਾਲਾਂ ਅਤੇ ਡਾਟਾ ਸੇਵਾਵਾਂ ਨੂੰ ਬਲਾਕ ਕੀਤਾ ਜਾਵੇ।"</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"ਕੋਈ ਹੋਰ ਸਿਮ ਵਰਤਣ ਵੇਲੇ ਸ਼ਾਇਦ <xliff:g id="CARRIER_NAME">%1$s</xliff:g> ਦੀਆਂ ਕਾਲਾਂ ਅਤੇ ਡਾਟਾ ਸੇਵਾਵਾਂ ਨੂੰ ਬਲਾਕ ਕੀਤਾ ਜਾਵੇ।"</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"ਨਾਪਸੰਦ ਕੀਤੇ SIP ਖਾਤੇ ਮਿਲੇ ਅਤੇ ਹਟਾ ਦਿੱਤੇ ਗਏ"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP ਕਾਲਿੰਗ ਹੁਣ Android ਪਲੇਟਫਾਰਮ ਵੱਲੋਂ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ।\nਤੁਹਾਡੇ ਮੌਜੂਦਾ SIP ਖਾਤੇ <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> ਹਟਾ ਦਿੱਤੇ ਗਏ ਹਨ।\nਕਿਰਪਾ ਕਰਕੇ ਆਪਣੀ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਕਾਲਿੰਗ ਖਾਤਾ ਸੈਟਿੰਗ ਦੀ ਤਸਦੀਕ ਕਰੋ।"</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"ਸੈਟਿੰਗਾਂ \'ਤੇ ਜਾਓ"</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>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"ਪਾਸਵਰਡ ਗ਼ਲਤ। SIM ਹੁਣ ਲੌਕ ਹੈ। PUK2 ਦਰਜ ਕਰੋ।"</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 ਸਥਾਈ ਤੌਰ ਤੇ ਬਲੌਕ ਕੀਤਾ ਹੋਇਆ ਹੈ।"</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"ਤੁਹਾਡੇ ਕੋਲ <xliff:g id="NUMBER">%d</xliff:g> ਕੋਸ਼ਿਸ਼ਾਂ ਬਾਕੀ ਹਨ।"</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 ਲਾਕ ਹੈ। ਅਣਲਾਕ ਕਰਨ ਲਈ ਸੇਵਾ ਪ੍ਰਦਾਨਕ ਨੂੰ ਸੰਪਰਕ ਕਰੋ।"</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 ਹੁਣ ਬਲੌਕ ਨਹੀਂ ਹੈ"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"ਨੈਟਵਰਕ ਜਾਂ SIM ਕਾਰਡ ਤਰੁੱਟੀ"</string>
     <string name="doneButton" msgid="7371209609238460207">"ਹੋ ਗਿਆ"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"ਫ਼ੋਨ ਜਾਣਕਾਰੀ"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"ਕੈਰੀਅਰ ਪ੍ਰੋਵਿਜ਼ਨਿੰਗ ਜਾਣਕਾਰੀ"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"ਕੈਰੀਅਰ ਪ੍ਰਵਿਜ਼ਨਿੰਗ ਨੂੰ ਟ੍ਰਿਗਰ ਕਰੋ"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"ਸੁਝਾਅ: ਬਲੂਟੁੱਥ ਕਨੈਕਟੀਵਿਟੀ ਨੂੰ ਬਿਹਤਰ ਬਣਾਓ"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"ਤੁਹਾਡਾ ਬਲੂਟੁੱਥ ਸਿਗਨਲ ਕਮਜ਼ੋਰ ਹੈ। ਸਪੀਕਰਫ਼ੋਨ \'ਤੇ ਲਿਜਾ ਕੇ ਦੇਖੋ।"</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"ਕਾਲ ਦੀ ਕੁਆਲਿਟੀ ਸੰਬੰਧੀ ਸੂਚਨਾ"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"ਨਾਪਸੰਦ ਕੀਤੇ SIP ਖਾਤੇ"</string>
 </resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index f5ccdc6..d803c14 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Ograniczona funkcjonalność karty SIM"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Połączenia i usługi transmisji danych w sieci operatora <xliff:g id="CARRIER_NAME">%1$s</xliff:g> mogą być zablokowane, gdy używasz numeru <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Połączenia i transmisja danych w <xliff:g id="CARRIER_NAME">%1$s</xliff:g> mogą być zablokowane przy innej karcie SIM."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Wycofane konta SIP zostały znalezione i usunięte"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Połączenia SIP nie są już obsługiwane na platformie Android.\nTwoje konta SIP (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) zostały usunięte.\nPotwierdź ustawienie domyślnego konta do połączeń."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Otwórz ustawienia"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Użycie danych przez aplikację"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"Wykorzystano <xliff:g id="ID_1">%1$s</xliff:g> mobilnej transmisji danych w okresie <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Zaawansowane"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Nieprawidłowe hasło. Karta SIM została zablokowana. Wpisz PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 został trwale zablokowany."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Masz jeszcze <xliff:g id="NUMBER">%d</xliff:g> prób(y)."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"Kod PUK2 zablokowany. Aby odblokować, skontaktuj się z dostawcą usług."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 nie jest już zablokowany"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Błąd sieci lub karty SIM"</string>
     <string name="doneButton" msgid="7371209609238460207">"Gotowe"</string>
@@ -925,6 +929,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Informacje o telefonie"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Informacje o obsłudze administracyjnej operatora"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Uruchom obsługę administracyjną operatora"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Sugestia: popraw łączność Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Twój sygnał Bluetooth jest słaby. Spróbuj przełączyć na głośnik."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Powiadomienie o jakości połączenia"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Wycofane konta SIP"</string>
 </resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 8537eaf..ffce9e1 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Funcionalidade de SIM limitada"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"As chamadas e os serviços de dados da <xliff:g id="CARRIER_NAME">%1$s</xliff:g> podem ficar bloqueados ao utilizar o número <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"As chamadas e serviços de dados da <xliff:g id="CARRIER_NAME">%1$s</xliff:g> podem ser bloqueados se utilizar outro SIM."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Foram encontradas e removidas contas SIP descontinuadas"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"As chamadas SIP já não são suportadas pela plataforma Android.\nAs suas contas SIP existentes <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> foram removidas.\nConfirme a predefinição de conta de chamadas."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Aceder às definições"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Utilização de dados da app"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> de dados móveis utilizados entre <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Avançadas"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Palavra-passe incorreta. SIM bloqueado. Introduza o PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 bloqueado permanentemente."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Tem mais <xliff:g id="NUMBER">%d</xliff:g> tentativas."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 bloqueado. Contacte o fornecedor de serviços para desbloquear."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"O PIN2 deixou de estar bloqueado"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Erro do cartão SIM ou da rede"</string>
     <string name="doneButton" msgid="7371209609238460207">"Concluído"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Informações do telefone"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Informações de fornecimento do operador"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Acionar fornecimento do operador"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Sugestão: melhore a conetividade Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"O seu sinal Bluetooth é fraco. Tente mudar para o altifalante."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Notificação de qualidade da chamada"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Contas SIP descontinuadas"</string>
 </resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 9cafc46..0e6a812 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Funcionalidade limitada do chip"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Talvez os serviços de dados e as chamadas da operadora <xliff:g id="CARRIER_NAME">%1$s</xliff:g> sejam bloqueados se você usar <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Talvez os serviços de dados e as chamadas da <xliff:g id="CARRIER_NAME">%1$s</xliff:g> sejam bloqueados com outro chip."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Contas SIP suspensas encontradas e removidas"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"A chamada SIP não é mais compatível com a Plataforma Android.\nSuas contas SIP foram removidas: <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>.\nConfirme a configuração da conta padrão para chamadas."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Acessar configurações"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Uso de dados do app"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> de dados móveis usados em <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Avançadas"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Senha incorreta. Chip bloqueado. Insira o PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 permanentemente bloqueado."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Você tem <xliff:g id="NUMBER">%d</xliff:g> tentativas restantes."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 bloqueado. Entre em contato com o provedor de serviços para desbloqueá-lo."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 não mais bloqueado"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Erro de rede ou do chip"</string>
     <string name="doneButton" msgid="7371209609238460207">"Concluído"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Informações do telefone"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Informações de provisionamento da operadora"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Acionar provisionamento da operadora"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Sugestão: melhore a conectividade Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"O sinal do Bluetooth está fraco. Mude para o viva-voz."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Notificação sobre a qualidade da chamada"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Contas SIP suspensas"</string>
 </resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 55bb13f..654e70c 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Funcționalitate SIM limitată"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Este posibil ca apelurile și serviciile de date de la <xliff:g id="CARRIER_NAME">%1$s</xliff:g> să fie blocate în timp ce se folosește <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Apelurile și serviciile de date de la <xliff:g id="CARRIER_NAME">%1$s</xliff:g> pot fi blocate când folosiți alt card SIM"</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Au fost găsite și eliminate conturi SIP învechite"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Platforma Android nu mai acceptă apelarea SIP.\nConturile dvs. SIP existente <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> au fost eliminate.\nConfirmați setarea prestabilită pentru apelare a contului."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Accesați setările"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Date utilizate de aplicație"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> de date mobile utilizate în intervalul <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Avansate"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Parola nu este corectă. Cardul SIM a fost blocat. Introduceți codul PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"Codul PUK2 este blocat definitiv."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"V-au rămas <xliff:g id="NUMBER">%d</xliff:g> (de) încercări."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"Blocat cu PUK2. Contactați furnizorul de servicii pentru a debloca."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"Codul PIN2 nu mai este blocat"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Eroare de rețea sau de card SIM"</string>
     <string name="doneButton" msgid="7371209609238460207">"Terminat"</string>
@@ -922,6 +926,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Informații despre telefon"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Informații despre provizionarea operatorului"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Declanșați provizionarea operatorului"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Sugestie: îmbunătățiți conectivitatea Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Semnalul Bluetooth este slab. Încercați să folosiți difuzorul."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Notificare privind calitatea apelului"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Conturi SIP învechite"</string>
 </resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index b4e6da5..8a0e770 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -294,13 +294,16 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Ограниченная функциональность SIM-карты"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Звонки и мобильный Интернет через <xliff:g id="CARRIER_NAME">%1$s</xliff:g> могут блокироваться при использовании номера <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Звонки и Интернет через <xliff:g id="CARRIER_NAME">%1$s</xliff:g> могут блокироваться при использовании другой SIM-карты."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Найдены и удалены неподдерживаемые SIP-аккаунты"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"На платформе Android больше не поддерживаются звонки через протокол SIP.\nВаши SIP-аккаунты (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) удалены.\nПодтвердите аккаунт, который будет по умолчанию использоваться для звонков."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Перейти в настройки"</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="carrier_settings_euicc" msgid="1190237227261337749">"Оператор"</string>
     <string name="keywords_carrier_settings_euicc" msgid="8540160967922063745">"оператор, esim, sim, euicc, сменить оператора, добавить оператора"</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>
-    <string name="mobile_data_settings_title" msgid="7228249980933944101">"Мобильный Интернет"</string>
+    <string name="mobile_data_settings_title" msgid="7228249980933944101">"Мобильный интернет"</string>
     <string name="mobile_data_settings_summary" msgid="5012570152029118471">"Доступ к Интернету по мобильной сети"</string>
     <string name="data_usage_disable_mobile" msgid="5669109209055988308">"Отключить мобильный Интернет?"</string>
     <string name="sim_selection_required_pref" msgid="6985901872978341314">"Выберите SIM-карту"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Неверный пароль. SIM-карта заблокирована. Введите код PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"Код PUK2 заблокирован навсегда"</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Осталось попыток: <xliff:g id="NUMBER">%d</xliff:g>."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"Код PUK2 заблокирован. Чтобы разблокировать его, обратитесь к оператору."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"Код PIN2 разблокирован."</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Ошибка сети или SIM-карты"</string>
     <string name="doneButton" msgid="7371209609238460207">"Готово"</string>
@@ -925,6 +929,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Информация о телефоне"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Сведения о синхронизации оператора"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Запустить синхронизацию оператора"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Рекомендация: повысьте качество связи по Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Слабый сигнал Bluetooth. Попробуйте переключиться на громкую связь."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Уведомление о качестве связи"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Неподдерживаемые SIP-аккаунты"</string>
 </resources>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 174fd1c..26b8c22 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"සීමිත SIM ක්‍රියාකාරීත්වය"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g> භාවිත කරන අතරතුර <xliff:g id="CARRIER_NAME">%1$s</xliff:g> ඇමතුම් සහ දත්ත සේවා අවහිර විය හැකිය."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"වෙනත් SIM එකක් භාවිත කරන අතරතුර <xliff:g id="CARRIER_NAME">%1$s</xliff:g> ඇමතුම් සහ දත්ත සේවා අවහිර විය හැකිය."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"අතහැර දැමූ SIP ගිණුම් සොයා ගෙන ඉවත් කර ඇත"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP ඇමතීමට තවදුරටත් Android වේදිකාව මගින් සහාය නොදක්වයි.\nඔබේ පවතින SIP ගිණුම් <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> ඉවත් කර ඇත.\nඔබේ පෙරනිමි ඇමතීමේ ගිණුම් සැකසීම තහවුරු කරන්න."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"සැකසීම් වෙත යන්න"</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>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"මුරපදය වැරදිය. SIM දැන් අගුළු දමා තිබේ. PUK2 ඇතුළු කරන්න."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 සදාකාලිකවම අවහිරයි."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"ඔබට නැවත උත්සාහ කිරීම් <xliff:g id="NUMBER">%d</xliff:g> ක් තිබේ."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 අගුළු දැමිණි. අගුළු ඇරීමට සේවා සපයන්නා සම්බන්ධ කර ගන්න."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"තව දුරටත් PIN2 අවහිර කර නොතිබේ"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"ජාලයේ හෝ SIM කාඩ්පතෙහි දෝෂයක්"</string>
     <string name="doneButton" msgid="7371209609238460207">"හරි"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"දුරකථන තොරතුරු"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"වාහක ප්‍රතිපාදන තතු"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"උත්ප්‍රේරක වාහක ප්‍රතිපාදන"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"යෝජනාව: බ්ලූටූත් සබැඳුම වැඩි දියුණු කිරීම"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"ඔබේ බ්ලූටූත් සංඥාව දුර්වලයි. ස්පීකර් දුරකථනයට මාරු වීමට උත්සාහ කරන්න."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"ඇමතුම් ගුණත්ව දැනුම්දීම"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"අතහැර දැමූ SIP ගිණුම්"</string>
 </resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 8d996a1..4a4173f 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Obmedzená funkčnosť SIM karty"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Hovory a dátové služby operátora <xliff:g id="CARRIER_NAME">%1$s</xliff:g> môžu byť zablokované, keď používate číslo <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Hovory a dátové služby operátora <xliff:g id="CARRIER_NAME">%1$s</xliff:g> môžu byť s inou SIM kartou blokované."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Boli nájdené a odstránené účty SIP s ukončenou podporou"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Platforma Androidu už nepodporuje volanie SIP.\nVaše existujúce účty SIP <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> boli odstránené.\nPotvrďte svoje predvolené nastavenie účtu na volanie."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Prejsť do nastavení"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Spotreba dát aplikácií"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"Spotreba mobilných dát za obdobie <xliff:g id="ID_2">%2$s</xliff:g> je <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Rozšírené"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Heslo je nesprávne. SIM karta bola uzamknutá. Zadajte kód PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"Kód PUK2 je natrvalo blokovaný."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Zostávajú vám pokusy (počet: <xliff:g id="NUMBER">%d</xliff:g>)"</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"SIM karta je blokovaná kódom PUK2. Ak ju chcete odblokovať, kontaktujte poskytovateľa služieb."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"Kód PIN2 už nie je blokovaný"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Došlo k chybe siete alebo SIM karty"</string>
     <string name="doneButton" msgid="7371209609238460207">"Hotovo"</string>
@@ -925,6 +929,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Informácie o telefóne"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Informácie o poskytovaní služieb operátorom"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Spustiť poskytovanie služieb operátorom"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Návrh: zlepšenie pripojenia cez Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Signál Bluetooth je slabý. Skúste prepnúť na reproduktor."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Upozornenie o kvalite hovoru"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Účty SIP s ukončenou podporou"</string>
 </resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 5b88521..fba55c1 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Omejena funkcionalnost kartice SIM"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Klici in podatkovne storitve prek operaterja <xliff:g id="CARRIER_NAME">%1$s</xliff:g> bodo pri uporabi številke <xliff:g id="PHONE_NUMBER">%2$s</xliff:g> morda blokirane."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Klici in podatk. storitve <xliff:g id="CARRIER_NAME">%1$s</xliff:g> bodo pri uporabi druge kartice SIM morda blokirane."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Opuščeni računi SIP so bili odkriti in odstranjeni"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Platforma Android ne podpira več klicanja SIP.\nObstoječi računi SIP so bili odstranjeni: <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>.\nPotrdite privzeto nastavitev računa za klicanje."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Odpri nastavitve"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Prenos podatkov aplikacije"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"V obdobju <xliff:g id="ID_2">%2$s</xliff:g> je bilo v mobilnem omrežju prenesenih <xliff:g id="ID_1">%1$s</xliff:g> podatkov"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Dodatno"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Geslo je napačno. Kartica SIM je zaklenjena. Vnesite kodo PUK 2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"Koda PUK2 je trajno blokirana."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Imate še toliko poskusov: <xliff:g id="NUMBER">%d</xliff:g>."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"Zaklenjeno s kodo PUK2. Za odklepanje se obrnite na ponudnika storitev."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"Koda PIN 2 ni več blokirana"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Napaka omrežja ali kartice SIM"</string>
     <string name="doneButton" msgid="7371209609238460207">"Dokončano"</string>
@@ -925,6 +929,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Informacije o telefonu"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Podatki o omogočanju uporabe operaterja"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Sproži omogočanje uporabe operaterja"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Predlog: Izboljšajte povezavo Bluetooth."</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Signal povezave Bluetooth je šibek. Poskusite preklopiti na zvočnik."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Obvestilo o kakovosti klica"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Opuščeni računi SIP"</string>
 </resources>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 64f880d..acefc2c 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Funksionalitet i kufizuar i kartës SIM"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Shërbimet e telefonatave dhe të të dhënave të <xliff:g id="CARRIER_NAME">%1$s</xliff:g> mund të bllokohen kur përdoret <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Shërbimet e telefonatave dhe të dhënave të <xliff:g id="CARRIER_NAME">%1$s</xliff:g> bllokohen kur përdoret SIM tjetër."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Llogaritë e zhvlerësuara SIP janë gjetur dhe hequr"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Telefonatat me SIP nuk mbështeten më nga platforma Android.\nLlogaritë e tua ekzistuese SIP <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> janë hequr.\nKonfirmo cilësimin e parazgjedhur të llogarisë së telefonatave."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Shko te cilësimet"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Përdorimi i të dhënave të aplikacioneve"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> të dhëna celulare të përdorura mes datave <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Të përparuara"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Fjalëkalimi është i pasaktë. Karta SIM tani është e kyçur. Fut PUK2-shin."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2-shi është i bllokuar përgjithmonë."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Të kanë mbetur edhe <xliff:g id="NUMBER">%d</xliff:g> përpjekje."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"Kodi PUK2 i kyçur. Kontakto me ofruesin e shërbimit për ta shkyçur."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2-shi nuk është më i bllokuar"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Gabim në rrjet ose në kartën SIM"</string>
     <string name="doneButton" msgid="7371209609238460207">"U krye"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Informacioni i telefonit"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Informacionet e përgatitjes së operatorit celular"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Aktivizo përgatitjen e operatorit celular"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Sugjerim: Përmirëso lidhshmërinë me Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Sinjali i Bluetooth-it është i dobët. Provo të kalosh te altoparlanti."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Njoftim për cilësinë e telefonatës"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Llogaritë e zhvlerësuara SIP"</string>
 </resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index f242d5d..434e355 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Ограничена функционалност SIM-а"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> услуге за позиве и податке се блокирају док користите <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> услуге за позиве и податке се блокирају док користите други SIM."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Застарели SIP налози су пронађени и уклоњени"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Android платформа више не подржава SIP позивање.\nПостојећи SIP налози <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> су уклоњени.\nПотврдите подразумевано подешавање налога позивања."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Иди у подешавања"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Потрошња података апликације"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"Потрошили сте <xliff:g id="ID_1">%1$s</xliff:g> мобилних података у периоду <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Напредно"</string>
@@ -480,9 +483,9 @@
     <string name="simContacts_empty" msgid="1135632055473689521">"Нема контаката на SIM картици."</string>
     <string name="simContacts_title" msgid="2714029230160136647">"Избор контаката за увоз"</string>
     <string name="simContacts_airplaneMode" msgid="4654884030631503808">"Искључите режим рада у авиону да бисте увезли контакте са SIM картице."</string>
-    <string name="enable_pin" msgid="967674051730845376">"Омогућавање/онемогућавање SIM PIN-а"</string>
-    <string name="change_pin" msgid="3657869530942905790">"Промена SIM PIN-а"</string>
-    <string name="enter_pin_text" msgid="3182311451978663356">"SIM PIN:"</string>
+    <string name="enable_pin" msgid="967674051730845376">"Омогућавање/онемогућавање PIN-а за SIM"</string>
+    <string name="change_pin" msgid="3657869530942905790">"Промена PIN-а за SIM"</string>
+    <string name="enter_pin_text" msgid="3182311451978663356">"PIN за SIM:"</string>
     <string name="oldPinLabel" msgid="8618515202411987721">"Стари PIN"</string>
     <string name="newPinLabel" msgid="3585899083055354732">"Нови PIN"</string>
     <string name="confirmPinLabel" msgid="7783531218662473778">"Потврдите нови PIN"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Лозинка није тачна. SIM је сада блокиран. Унесите PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 је трајно блокиран."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Имате још <xliff:g id="NUMBER">%d</xliff:g> покушаја."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 је закључан. За откључавање се обратите добављачу услуге."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 више није блокиран"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Грешка на мрежи или SIM картици"</string>
     <string name="doneButton" msgid="7371209609238460207">"Готово"</string>
@@ -922,6 +926,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Информације о телефону"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Информације о додели мобилног оператера"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Покретање доделе мобилног оператера"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Предлог: побољшајте Bluetooth везу"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth сигнал је слаб. Пробајте да пређете на спикерфон."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Обавештење о квалитету позива"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Застарели SIP налози"</string>
 </resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 458a768..2ac04ab 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -75,7 +75,7 @@
     <string name="phone_accounts_configure_account_settings" msgid="6622119715253196586">"Konfigurera kontoinställningar"</string>
     <string name="phone_accounts_all_calling_accounts" msgid="1609600743500618823">"Alla konton för samtal"</string>
     <string name="phone_accounts_all_calling_accounts_summary" msgid="2214134955430107240">"Välj vilka konton som kan ringa samtal"</string>
-    <string name="wifi_calling" msgid="3650509202851355742">"Wi-Fi-samtal"</string>
+    <string name="wifi_calling" msgid="3650509202851355742">"wifi-samtal"</string>
     <string name="connection_service_default_label" msgid="7332739049855715584">"Inbyggd anslutningstjänst"</string>
     <string name="voicemail" msgid="7697769412804195032">"Röstbrevlåda"</string>
     <string name="voicemail_settings_with_label" msgid="4228431668214894138">"Röstbrevlåda (<xliff:g id="SUBSCRIPTIONLABEL">%s</xliff:g>)"</string>
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Begränsad SIM-funktionalitet"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Samtals- och datatjänster från <xliff:g id="CARRIER_NAME">%1$s</xliff:g> kan vara blockerade medan du använder <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Samtals- och datatjänster från <xliff:g id="CARRIER_NAME">%1$s</xliff:g> kan vara blockerade när annat SIM-kort används."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Hittade och tog bort utfasade SIP-konton"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Android-plattformen stöder inte längre SIP-samtal.\nDitt befintliga SIP-konto <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> har tagits bort.\nBekräfta kontoinställningen för standardsamtal."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Öppna inställningarna"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Appars dataanvändning"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> mobildata använt mellan <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Avancerat"</string>
@@ -306,7 +309,7 @@
     <string name="sim_selection_required_pref" msgid="6985901872978341314">"Du måste välja något"</string>
     <string name="sim_change_data_title" msgid="9142726786345906606">"Vill du ändra data-SIM?"</string>
     <string name="sim_change_data_message" msgid="3567358694255933280">"Vill du använda <xliff:g id="NEW_SIM">%1$s</xliff:g> i stället för <xliff:g id="OLD_SIM">%2$s</xliff:g> för mobildata?"</string>
-    <string name="wifi_calling_settings_title" msgid="5800018845662016507">"Wi-Fi-samtal"</string>
+    <string name="wifi_calling_settings_title" msgid="5800018845662016507">"wifi-samtal"</string>
     <string name="video_calling_settings_title" msgid="342829454913266078">"Videosamtal via operatören"</string>
     <string name="gsm_umts_options" msgid="4968446771519376808">"Alternativ för GSM/UMTS"</string>
     <string name="cdma_options" msgid="3669592472226145665">"CDMA-alternativ"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Felaktigt lösenord. SIM-kortet är nu låst. Ange PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK-kod 2 har blockerats permanent."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Du har <xliff:g id="NUMBER">%d</xliff:g> försök kvar."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 låst. Kontakta tjänsteleverantören för att låsa upp."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 är inte längre blockerad"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Nätverks- eller SIM-kortsfel"</string>
     <string name="doneButton" msgid="7371209609238460207">"Färdig"</string>
@@ -550,7 +554,7 @@
     <string name="incall_error_supp_service_hangup" msgid="836524952243836735">"Det gick inte att släppa samtal."</string>
     <string name="incall_error_supp_service_hold" msgid="8535056414643540997">"Det går inte att hålla kvar samtal."</string>
     <string name="incall_error_wfc_only_no_wireless_network" msgid="5860742792811400109">"Anslut till ett trådlöst nätverk om du vill ringa."</string>
-    <string name="incall_error_promote_wfc" msgid="9164896813931363415">"Aktivera Wi-Fi-samtal för att ringa."</string>
+    <string name="incall_error_promote_wfc" msgid="9164896813931363415">"Aktivera wifi-samtal för att ringa."</string>
     <string name="emergency_information_hint" msgid="9208897544917793012">"Nödinformation"</string>
     <string name="emergency_information_owner_hint" msgid="6256909888049185316">"Ägare"</string>
     <string name="emergency_information_confirm_hint" msgid="5109017615894918914">"Tryck igen för att visa information"</string>
@@ -612,7 +616,7 @@
     <string name="ota_hfa_activation_title" msgid="3300556778212729671">"Aktiveras ..."</string>
     <string name="ota_hfa_activation_dialog_message" msgid="7921718445773342996">"Din mobila datatjänst aktiveras av mobilen.\n\nDetta kan ta upp till fem minuter."</string>
     <string name="ota_skip_activation_dialog_title" msgid="7666611236789203797">"Vill du hoppa över aktiveringen?"</string>
-    <string name="ota_skip_activation_dialog_message" msgid="6691722887019708713">"Om du hoppar över aktiveringen kan du inte ringa samtal eller ansluta till mobila datanätverk (men du kan ansluta till Wi-Fi-nätverk). Du kommer att påminnas om att aktivera telefonen varje gång du sätter på den, tills du har aktiverat den."</string>
+    <string name="ota_skip_activation_dialog_message" msgid="6691722887019708713">"Om du hoppar över aktiveringen kan du inte ringa samtal eller ansluta till mobila datanätverk (men du kan ansluta till wifi-nätverk). Du kommer att påminnas om att aktivera telefonen varje gång du sätter på den, tills du har aktiverat den."</string>
     <string name="ota_skip_activation_dialog_skip_label" msgid="5908029466817825633">"Hoppa över"</string>
     <string name="ota_activate" msgid="7939695753665438357">"Aktivera"</string>
     <string name="ota_title_activate_success" msgid="1272135024761004889">"Telefonen är aktiverad."</string>
@@ -676,18 +680,18 @@
     <string name="sim_description_emergency_calls" msgid="5146872803938897296">"Endast nödsamtal"</string>
     <string name="sim_description_default" msgid="7474671114363724971">"SIM-kortsplats: <xliff:g id="SLOT_ID">%s</xliff:g>"</string>
     <string name="accessibility_settings_activity_title" msgid="7883415189273700298">"Tillgänglighet"</string>
-    <string name="status_hint_label_incoming_wifi_call" msgid="2606052595898044071">"Wi-Fi-samtal från"</string>
-    <string name="status_hint_label_wifi_call" msgid="942993035689809853">"Wi-Fi-samtal"</string>
+    <string name="status_hint_label_incoming_wifi_call" msgid="2606052595898044071">"wifi-samtal från"</string>
+    <string name="status_hint_label_wifi_call" msgid="942993035689809853">"wifi-samtal"</string>
     <string name="emergency_action_launch_hint" msgid="2762016865340891314">"Tryck igen för att öppna"</string>
     <string name="message_decode_error" msgid="1061856591500290887">"Ett fel inträffade när meddelandet avkodades."</string>
     <string name="callFailed_cdma_activation" msgid="5392057031552253550">"Ett SIM-kort har använts för att aktivera tjänsten och uppdatera roamingfunktionerna i mobilen."</string>
     <string name="callFailed_cdma_call_limit" msgid="1074219746093031412">"Det finns för många aktiva samtal. Avsluta eller slå samman pågående samtal innan du ringer ett nytt."</string>
     <string name="callFailed_imei_not_accepted" msgid="7257903653685147251">"Det går inte att ansluta. Sätt i ett giltigt SIM-kort."</string>
-    <string name="callFailed_wifi_lost" msgid="1788036730589163141">"Wi-Fi-anslutningen bruten. Samtal avslutat."</string>
+    <string name="callFailed_wifi_lost" msgid="1788036730589163141">"wifi-anslutningen bruten. Samtal avslutat."</string>
     <string name="dialFailed_low_battery" msgid="6857904237423407056">"Det går inte att ringa videosamtal på grund av svagt batteri."</string>
     <string name="callFailed_low_battery" msgid="4056828320214416182">"Videosamtalet slutade på grund av svagt batteri."</string>
-    <string name="callFailed_emergency_call_over_wfc_not_available" msgid="5944309590693432042">"Nödsamtal via Wi-Fi är inte tillgängligt på den här platsen."</string>
-    <string name="callFailed_wfc_service_not_available_in_this_location" msgid="3624536608369524988">"Wi-Fi-samtal är inte tillgängligt på den här platsen."</string>
+    <string name="callFailed_emergency_call_over_wfc_not_available" msgid="5944309590693432042">"Nödsamtal via wifi är inte tillgängligt på den här platsen."</string>
+    <string name="callFailed_wfc_service_not_available_in_this_location" msgid="3624536608369524988">"wifi-samtal är inte tillgängligt på den här platsen."</string>
     <string name="change_pin_title" msgid="3564254326626797321">"Ändra röstbrevlådans pinkod"</string>
     <string name="change_pin_continue_label" msgid="5177011752453506371">"Fortsätt"</string>
     <string name="change_pin_cancel_label" msgid="2301711566758827936">"Avbryt"</string>
@@ -832,7 +836,7 @@
     <string name="radio_info_data_connection_disable" msgid="6404751291511368706">"Inaktivera dataanslutning"</string>
     <string name="volte_provisioned_switch_string" msgid="4812874990480336178">"VoLTE-administrerad"</string>
     <string name="vt_provisioned_switch_string" msgid="8295542122512195979">"Videosamtal tillhandahålls"</string>
-    <string name="wfc_provisioned_switch_string" msgid="3835004640321078988">"Wi-Fi-samtal tillhandahålls"</string>
+    <string name="wfc_provisioned_switch_string" msgid="3835004640321078988">"wifi-samtal tillhandahålls"</string>
     <string name="eab_provisioned_switch_string" msgid="4449676720736033035">"EAB/Presence tillhandahålls"</string>
     <string name="cbrs_data_switch_string" msgid="6060356430838077653">"Data via CBRS"</string>
     <string name="dsds_switch_string" msgid="7564769822086764796">"Aktivera DSDS"</string>
@@ -850,7 +854,7 @@
     <string name="radio_info_ims_reg_status_not_registered" msgid="8045821447288876085">"Inte registrerad"</string>
     <string name="radio_info_ims_feature_status_available" msgid="6493200914756969292">"Tillgänglig"</string>
     <string name="radio_info_ims_feature_status_unavailable" msgid="8930391136839759778">"Inte tillgängligt"</string>
-    <string name="radio_info_ims_reg_status" msgid="25582845222446390">"IMS-registrering: <xliff:g id="STATUS">%1$s</xliff:g>\nRöst via LTE: <xliff:g id="AVAILABILITY_0">%2$s</xliff:g>\nRöst via Wi-Fi: <xliff:g id="AVAILABILITY_1">%3$s</xliff:g>\nVideosamtal: <xliff:g id="AVAILABILITY_2">%4$s</xliff:g>\nUT-gränssnitt: <xliff:g id="AVAILABILITY_3">%5$s</xliff:g>"</string>
+    <string name="radio_info_ims_reg_status" msgid="25582845222446390">"IMS-registrering: <xliff:g id="STATUS">%1$s</xliff:g>\nRöst via LTE: <xliff:g id="AVAILABILITY_0">%2$s</xliff:g>\nRöst via wifi: <xliff:g id="AVAILABILITY_1">%3$s</xliff:g>\nVideosamtal: <xliff:g id="AVAILABILITY_2">%4$s</xliff:g>\nUT-gränssnitt: <xliff:g id="AVAILABILITY_3">%5$s</xliff:g>"</string>
     <string name="radioInfo_service_in" msgid="45753418231446400">"I tjänst"</string>
     <string name="radioInfo_service_out" msgid="287972405416142312">"Ur funktion"</string>
     <string name="radioInfo_service_emergency" msgid="4763879891415016848">"Endast nödsamtal"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Telefoninformation"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Information om operatörsadministration"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Aktivera operatörsadministration"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Förslag: Förbättra Bluetooth-anslutningen"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Svag Bluetooth-signal. Försök med att växla till högtalartelefon."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Avisering om samtalskvalitet"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Utfasade SIP-konton"</string>
 </resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index acfeb3b..c292108 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Matumizi ya SIM yana kikomo"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Huenda huduma za simu na data za <xliff:g id="CARRIER_NAME">%1$s</xliff:g> zikazuiwa unapotumia <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Huenda huduma za simu na data za <xliff:g id="CARRIER_NAME">%1$s</xliff:g> zikazuiwa unapotumia SIM nyingine."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Akaunti za SIP ambazo zimefungwa zimepatikana na zikaondolewa"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Huduma ya kupiga simu kupitia SIP haipatikani tena kwenye mfumo wa Android.\nAkaunti zako za SIP zilizopo <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> zimeondolewa.\nTafadhali thibitisha mipangilio ya akaunti yako chaguomsingi ya kupiga simu."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Nenda kwenye mipangilio"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Matumizi ya data ya programu"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> za data ya mtandao wa simu zimetumika kuanzia <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Mipangilio ya Kina"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Nenosiri si sahihi. SIM sasa imefungwa. Weka PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 Imezuiwa kabisa."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Una majaribio <xliff:g id="NUMBER">%d</xliff:g> yaliyobaki."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"Imefungwa kwa sababu PUK2 si sahihi. Wasiliana na mtoa huduma ili aifungue."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 sasa haijazuiwa"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Hitilafu ya mtandao au SIM kadi"</string>
     <string name="doneButton" msgid="7371209609238460207">"Kwisha"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Maelezo ya simu"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Maelezo ya Utaratibu wa Kutayarisha Mtandao"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Anzisha Utaratibu wa Kutayarisha Mtandao"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Pendekezo: Boresha muunganisho wa Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Muunganisho wako wa bluetooth ni dhaifu. Jaribu kubadilisha ili utumie spika ya simu."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Arifa ya Ubora wa Simu"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Akaunti za SIP ambazo zimefungwa"</string>
 </resources>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 533988d..ab850a6 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"SIMமின் செயல்பாடு வரம்பிடப்பட்டது"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g> எண்ணைப் பயன்படுத்தும் போது <xliff:g id="CARRIER_NAME">%1$s</xliff:g> அழைப்புகளும் டேட்டா சேவைகளும் தடுக்கப்படக்கூடும்."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"வேறொரு SIMமில் <xliff:g id="CARRIER_NAME">%1$s</xliff:g> அழைப்புகளும் டேட்டா சேவைகளும் தடுக்கப்படக்கூடும்."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"நிறுத்தப்பட்ட SIP கணக்குகள் கண்டறியப்பட்டு அகற்றப்பட்டன"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Android பிளாட்ஃபார்மில் இனி SIP அழைப்பைச் செய்ய முடியாது.\nஏற்கெனவே உள்ள <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> SIP கணக்குகள் அகற்றப்பட்டன.\nஅழைப்பைச் செய்வதற்கான இயல்புக் கணக்கு அமைப்பை உறுதிசெய்யவும்."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"அமைப்புகளுக்குச் செல்"</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>
@@ -336,7 +339,7 @@
     <string name="maintenance_enable" msgid="2646784483222342290">"பராமரிப்பு இயக்கப்பட்டது"</string>
     <string name="maintenance_disable" msgid="2121032601497725602">"பராமரிப்பு முடக்கப்பட்டது"</string>
     <string name="general_news_settings" msgid="2670499575962080411">"பொது செய்திகள்"</string>
-    <string name="bf_news_settings" msgid="8571709425370794221">"வணிகம் மற்றும் நிதிசார்ந்த செய்திகள்"</string>
+    <string name="bf_news_settings" msgid="8571709425370794221">"பிசினஸ் மற்றும் நிதிசார்ந்த செய்திகள்"</string>
     <string name="sports_news_settings" msgid="2684364556989168438">"விளையாட்டுச் செய்திகள்"</string>
     <string name="entertainment_news_settings" msgid="4228527702346305543">"பொழுதுபோக்குச் செய்திகள்"</string>
     <string name="enable_disable_local" msgid="7654175079979415572">"உள்ளூர்"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"கடவுச்சொல் தவறானது. சிம் இப்போது பூட்டப்பட்டது. PUK2ஐ உள்ளிடவும்."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 தற்காலிகமாகத் தடுக்கப்பட்டது."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"நீங்கள் <xliff:g id="NUMBER">%d</xliff:g> முறை முயற்சிக்கலாம்."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 லாக் செய்யப்பட்டுள்ளது. அன்லாக் செய்ய சேவை வழங்குநரைத் தொடர்புகொள்ளவும்."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 இனி தடுக்கப்படாது"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"நெட்வொர்க் அல்லது சிம் கார்டு பிழை"</string>
     <string name="doneButton" msgid="7371209609238460207">"முடிந்தது"</string>
@@ -850,7 +854,7 @@
     <string name="radio_info_ims_reg_status_not_registered" msgid="8045821447288876085">"பதிவுசெய்யப்படவில்லை"</string>
     <string name="radio_info_ims_feature_status_available" msgid="6493200914756969292">"இருக்கிறது"</string>
     <string name="radio_info_ims_feature_status_unavailable" msgid="8930391136839759778">"கிடைக்கவில்லை"</string>
-    <string name="radio_info_ims_reg_status" msgid="25582845222446390">"IMS பதிவு: <xliff:g id="STATUS">%1$s</xliff:g>\nவாய்ஸ் ஓவர் LTE: <xliff:g id="AVAILABILITY_0">%2$s</xliff:g>\nவாய்ஸ் ஓவர் WiFi: <xliff:g id="AVAILABILITY_1">%3$s</xliff:g>\nவீடியோ அழைப்பு: <xliff:g id="AVAILABILITY_2">%4$s</xliff:g>\nUT இடைமுகம்: <xliff:g id="AVAILABILITY_3">%5$s</xliff:g>"</string>
+    <string name="radio_info_ims_reg_status" msgid="25582845222446390">"IMS பதிவு: <xliff:g id="STATUS">%1$s</xliff:g>\nவாய்ஸ் ஓவர் LTE: <xliff:g id="AVAILABILITY_0">%2$s</xliff:g>\nவாய்ஸ் ஓவர் வைஃபை: <xliff:g id="AVAILABILITY_1">%3$s</xliff:g>\nவீடியோ அழைப்பு: <xliff:g id="AVAILABILITY_2">%4$s</xliff:g>\nUT இடைமுகம்: <xliff:g id="AVAILABILITY_3">%5$s</xliff:g>"</string>
     <string name="radioInfo_service_in" msgid="45753418231446400">"சேவையில் உள்ளது"</string>
     <string name="radioInfo_service_out" msgid="287972405416142312">"சேவையில் இல்லை"</string>
     <string name="radioInfo_service_emergency" msgid="4763879891415016848">"அவசர அழைப்புகள் மட்டும்"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"மொபைலின் தகவல்"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Carrier Provisioning Info"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Trigger Carrier Provisioning"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"பரிந்துரை: புளூடூத் இணைப்பை மேம்படுத்துங்கள்"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"புளூடூத் சிக்னல் வலுவற்றதாக உள்ளது. ஸ்பீக்கர் ஃபோனிற்கு மாற்றவும்."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"அழைப்பின் தரம் தொடர்பான அறிவிப்பு"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"நிறுத்தப்பட்ட SIP கணக்குகள்"</string>
 </resources>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index a233f9e..311e0fd 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -144,7 +144,7 @@
     <string name="stk_cc_ss_to_ussd_error" msgid="8330749347425752192">"SS అభ్యర్థన USSD అభ్యర్థనకు మార్చబడింది"</string>
     <string name="stk_cc_ss_to_ss_error" msgid="8297155544652134278">"కొత్త SS అభ్యర్థనకు మార్చబడింది"</string>
     <string name="stk_cc_ss_to_dial_video_error" msgid="4255261231466032505">"SS అభ్యర్థన వీడియో కాల్‌కి మార్చబడింది"</string>
-    <string name="fdn_check_failure" msgid="1833769746374185247">"మీ ఫోన్ అనువర్తనం యొక్క ఫిక్స్‌డ్ డయలింగ్ నంబర్‌ల సెట్టింగ్ ప్రారంభించబడింది. తత్ఫలితంగా, కాల్ సంబంధిత లక్షణాల్లో కొన్ని పని చేయడం లేదు."</string>
+    <string name="fdn_check_failure" msgid="1833769746374185247">"మీ ఫోన్ యాప్‌ యొక్క ఫిక్స్‌డ్ డయలింగ్ నంబర్‌ల సెట్టింగ్ ప్రారంభించబడింది. తత్ఫలితంగా, కాల్ సంబంధిత లక్షణాల్లో కొన్ని పని చేయడం లేదు."</string>
     <string name="radio_off_error" msgid="8321564164914232181">"ఈ సెట్టింగ్‌లను వీక్షించడానికి ముందు రేడియోను ప్రారంభించండి."</string>
     <string name="close_dialog" msgid="1074977476136119408">"సరే"</string>
     <string name="enable" msgid="2636552299455477603">"ఆన్ చేయి"</string>
@@ -282,8 +282,8 @@
     <string name="data_enable_summary" msgid="696860063456536557">"డేటా వినియోగాన్ని అనుమతించు"</string>
     <string name="dialog_alert_title" msgid="5260471806940268478">"హెచ్చరిక"</string>
     <string name="roaming" msgid="1576180772877858949">"రోమింగ్"</string>
-    <string name="roaming_enable" msgid="6853685214521494819">"రోమింగ్‌లో ఉన్నప్పుడు డేటా సేవలకు కనెక్ట్ చేయి"</string>
-    <string name="roaming_disable" msgid="8856224638624592681">"రోమింగ్‌లో ఉన్నప్పుడు డేటా సేవలకు కనెక్ట్ చేయి"</string>
+    <string name="roaming_enable" msgid="6853685214521494819">"రోమింగ్‌లో ఉన్నప్పుడు డేటా సర్వీసులకు కనెక్ట్ చేయండి"</string>
+    <string name="roaming_disable" msgid="8856224638624592681">"రోమింగ్‌లో ఉన్నప్పుడు డేటా సర్వీసులకు కనెక్ట్ చేయండి"</string>
     <string name="roaming_reenable_message" msgid="1951802463885727915">"డేటా రోమింగ్ ఆఫ్ చేయబడింది. ఆన్ చేయడానికి నొక్కండి."</string>
     <string name="roaming_enabled_message" msgid="9022249120750897">"రోమింగ్ ఛార్జీలు వర్తించవచ్చు. మార్చడానికి నొక్కండి."</string>
     <string name="roaming_notification_title" msgid="3590348480688047320">"మొబైల్ డేటా కనెక్షన్‌ని కోల్పోయారు"</string>
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"SIM పనితీరు పరిమితంగా ఉంది"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g> ఉపయోగిస్తున్నప్పుడు <xliff:g id="CARRIER_NAME">%1$s</xliff:g> కాల్‌లు మరియు డేటా సేవలు బ్లాక్ చేయబడవచ్చు."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"వేరొక SIMను ఉపయోగిస్తున్నప్పుడు <xliff:g id="CARRIER_NAME">%1$s</xliff:g> కాల్‌లు మరియు డేటా సేవలు బ్లాక్ చేయబడవచ్చు."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"తీసివేయబడిన SIP ఖాతాలు కనుగొనబడ్డాయి, తీసివేయబడ్డాయి"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP కాలింగ్‌కు ఇకపై Android ప్లాట్‌ఫామ్‌లో సపోర్ట్ ఉండదు.\nఇప్పటికే ఉన్న మీ SIP ఖాతాలు<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> తీసివేయబడ్డాయి.\nదయచేసి మీ ఆటోమేటిక్ కాలింగ్ ఖాతా సెట్టింగ్‌ను ధృవీకరించండి."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"సెట్టింగ్‌లకు వెళ్లు"</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>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"పాస్‌వర్డ్ చెల్లదు. SIM ఇప్పుడు లాక్ చేయబడింది. PUK2ని నమోదు చేయండి."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 శాశ్వతంగా బ్లాక్ చేయబడింది."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"మీకు <xliff:g id="NUMBER">%d</xliff:g> ప్రయత్నాలు మిగిలి ఉన్నాయి."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 లాక్ చేయబడింది. అన్‌లాక్ చేయడానికి సర్వీస్ ప్రొవైడర్‌ను కాంటాక్ట్ చేయండి."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 ఇప్పుడు బ్లాక్ అయ్యి లేదు"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"నెట్‌వర్క్ లేదా SIM కార్డ్ లోపం"</string>
     <string name="doneButton" msgid="7371209609238460207">"పూర్తయింది"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"ఫోన్ సమాచారం"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"క్యారియర్ కేటాయింపు సమాచారం"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"క్యారియర్ కేటాయింపు సక్రియం చేయండి"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"సూచన: బ్లూటూత్ కనెక్టివిటీని మెరుగుపరచండి"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"మీ బ్లూటూత్ సిగ్నల్ బలహీనంగా ఉంది. స్పీకర్‌ఫోన్‌కు స్విచ్ అవ్వడానికి ట్రై చేయండి."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"కాల్ క్వాలిటీ నోటిఫికేషన్"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"తీసివేయబడిన SIP ఖాతాలు"</string>
 </resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 5a4e293..f709a7c 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"การใช้งาน SIM ถูกจำกัด"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"บริการโทรออกและอินเทอร์เน็ตของ <xliff:g id="CARRIER_NAME">%1$s</xliff:g> อาจถูกบล็อกขณะใช้ <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>"</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"บริการโทรออกและอินเทอร์เน็ตของ <xliff:g id="CARRIER_NAME">%1$s</xliff:g> อาจถูกบล็อกขณะใช้ SIM อื่น"</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"พบบัญชี SIP ที่เลิกใช้งานและนำออกแล้ว"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"แพลตฟอร์ม Android จะไม่รองรับการโทร SIP อีกต่อไป\nระบบนำบัญชี SIP ที่มีอยู่ของคุณ<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>ออกแล้ว\nโปรดยืนยันการตั้งค่าบัญชีการโทรเริ่มต้น"</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"ไปที่การตั้งค่า"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"การใช้อินเทอร์เน็ตของแอป"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"เน็ตมือถือ <xliff:g id="ID_1">%1$s</xliff:g> ที่ใช้ระหว่าง <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"ขั้นสูง"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"รหัสผ่านไม่ถูกต้อง ระบบล็อกซิมแล้ว โปรดป้อน PUK2"</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 ถูกบล็อกอย่างถาวร"</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"คุณพยายามได้อีก <xliff:g id="NUMBER">%d</xliff:g> ครั้ง"</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 ล็อกอยู่ ติดต่อผู้ให้บริการเพื่อปลดล็อก"</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"ไม่มีการบล็อก PIN2 อีกต่อไป"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"เครือข่ายหรือซิมการ์ดเกิดข้อผิดพลาด"</string>
     <string name="doneButton" msgid="7371209609238460207">"เสร็จสิ้น"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"ข้อมูลโทรศัพท์"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"ข้อมูลการจัดสรรผู้ให้บริการ"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"ทริกเกอร์การจัดสรรผู้ให้บริการ"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"คำแนะนำ: ปรับปรุงการเชื่อมต่อบลูทูธ"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"สัญญาณบลูทูธอ่อน ลองเปลี่ยนไปใช้ลำโพงแทน"</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"การแจ้งเตือนคุณภาพการโทร"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"บัญชี SIP ที่เลิกใช้งาน"</string>
 </resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 0d266a4..5d62d87 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Limitadong functionality ng SIM"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Ang mga tawag at serbisyo ng data ng <xliff:g id="CARRIER_NAME">%1$s</xliff:g> ay puwedeng ma-block habang ginagamit ang <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Ang tawag at serbisyo ng data ng <xliff:g id="CARRIER_NAME">%1$s</xliff:g> ay maba-block habang ginagamit ang isa pang SIM."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Nakita at naalis ang mga hindi na ginagamit na SIP account"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Hindi na sinusuportahan ng Android Platform ang pagtawag sa pamamagitan ng SIP.\nNaalis na ang iyong mga kasalukuyang SIP account na <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>.\nPakikumpirma ang iyong default na setting ng account para sa pagtawag."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Pumunta sa mga setting"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Paggamit ng data ng app"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> na mobile data ang nagamit noong <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Advanced"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Mali ang password. Naka-lock na ngayon ang SIM. Ilagay ang PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"Permanenteng na-block ang PUK2."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Mayroon ka na lang <xliff:g id="NUMBER">%d</xliff:g> (na) natitirang pagsubok."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"Na-lock ang PUK2. Makipag-ugnayan sa service provider para i-unlock."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"Hindi na naka-block ang PIN2"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Error sa network o SIM card"</string>
     <string name="doneButton" msgid="7371209609238460207">"Tapos na"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Impormasyon ng telepono"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Impormasyon sa Provisioning ng Carrier"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"I-trigger ang Provisioning ng Carrier"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Suhestyon: Pahusayin ang connectivity ng Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Mahina ang signal ng iyong bluetooth. Subukang lumipat sa speakerphone."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Notification sa Kalidad ng Tawag"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Mga hindi na ginagamit na SIP account"</string>
 </resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index c6ed1da..c5a1060 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Sınırlı SIM işlevselliği"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g> kullanılırken <xliff:g id="CARRIER_NAME">%1$s</xliff:g> aramaları ve veri hizmetleri engellenebilir."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Başka bir SIM kullanılırken <xliff:g id="CARRIER_NAME">%1$s</xliff:g> aramaları ve veri hizmetleri engellenebilir."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Kullanımdan kaldırılmış SIP hesapları bulundu ve kaldırıldı"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP araması, Android platformunda artık desteklenmemektedir.\nMevcut SIP hesaplarınız <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> kaldırıldı.\nLütfen varsayılan arama hesabı ayarınızı onaylayın."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Ayarlara git"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Uygulama veri kullanımı"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_2">%2$s</xliff:g> arasında <xliff:g id="ID_1">%1$s</xliff:g> mobil veri kullanıldı"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Gelişmiş"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Şifre hatalı. SIM kilitlendi. PUK2\'yi girin."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 kalıcı olarak engellendi."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"<xliff:g id="NUMBER">%d</xliff:g> deneme hakkınız kaldı."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 kilitlendi. Kilidi açmak için servis sağlayıcıyla iletişime geçin."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 artık engellenmiş durumda değil"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Ağ veya SIM kart hatası"</string>
     <string name="doneButton" msgid="7371209609238460207">"Bitti"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Telefon bilgileri"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Operatör Temel Hazırlık Bilgileri"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Tetikleyici Operatör Temel Hazırlığı"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Öneri: Bluetooth bağlantısını iyileştirin"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth sinyaliniz zayıf. Hoparlöre geçmeyi deneyin."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Arama Kalitesiyle İlgili Bildirim"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Kullanımdan kaldırılan SIP hesapları"</string>
 </resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 8e73fef..61c1c68 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Обмежені функції SIM-карти"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Виклики та сервіси даних оператора <xliff:g id="CARRIER_NAME">%1$s</xliff:g> може бути заблоковано, коли ви використовуєте <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Коли працює інша SIM-карта, виклики й сервіси даних <xliff:g id="CARRIER_NAME">%1$s</xliff:g> може бути заблоковано."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Знайдено та вилучено облікові записи SIP, що не підтримуються"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Платформа Android більше не підтримує виклики SIP.\nНаявні облікові записи SIP (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) вилучено. \nПеревірте налаштування для викликів за умовчанням в обліковому записі."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Перейти в налаштування"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Використання трафіку додатками"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> мобільного трафіку використано в період <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Додатково"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Неправильний пароль. SIM-карту заблоковано. Введіть PUK2-код."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2-код назавжди заблоковано."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"У вас залишилося стільки спроб: <xliff:g id="NUMBER">%d</xliff:g>."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"Заблоковано кодом PUK2. Щоб розблокувати, зв’яжіться з оператором."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2-код більше не заблоковано"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Помилка мережі або SIM-карти"</string>
     <string name="doneButton" msgid="7371209609238460207">"Готово"</string>
@@ -925,6 +929,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Інформація про телефон"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Інформація про ініціалізацію оператора"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Запустити ініціалізацію оператора"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Пропозиція: покращте якість підключення Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Сигнал Bluetooth заслабкий. Спробуйте переключитися на гучний зв\'язок."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Сповіщення про якість виклику"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Облікові записи SIP, що не підтримуються"</string>
 </resources>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 012142a..cfc2df8 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"‏SIM کے محدود فنکشن"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g> کے استعمال کے دوران <xliff:g id="CARRIER_NAME">%1$s</xliff:g> سے کالز اور ڈیٹا سروسز کو مسدود کیا جا سکتا ہے۔"</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"‏دوسرے SIM کے استعمال کے دوران <xliff:g id="CARRIER_NAME">%1$s</xliff:g> سے کالز اور ڈیٹا سروسز کو مسدود کیا جا سکتا ہے۔"</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"‏فرسودہ SIP اکاؤنٹس ملیں اور انہیں ہٹا دیا گیا"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"‏Android پلیٹ فارم کے ذریعے SIP کالنگ اب تعاون یافتہ نہیں ہے۔\nآپ کے موجودہ SIP اکاؤنٹس <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> ہٹا دیے گئے ہیں۔\nبراہ کرم اپنے ڈیفالٹ کالنگ اکاؤنٹ کی ترتیب کی تصدیق کریں۔"</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"ترتیبات پر جائیں"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"ایپ ڈیٹا کا استعمال"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> موبائل ڈیٹا <xliff:g id="ID_2">%2$s</xliff:g> میں استعمال ہوا"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"جدید ترین"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"‏پاس ورڈ غلط ہے۔ SIM اب مقفل ہے۔ PUK2 درج کریں۔"</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"‏PUK2 مستقل طور پر مسدود ہے۔"</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"آپ کے پاس <xliff:g id="NUMBER">%d</xliff:g> کوششیں بچی ہیں۔"</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"‏PUK2 مقفل ہے۔ غیر مقفل کرنے کے ليے سروس فراہم کنندہ سے رابطہ کریں۔"</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"‏PIN2 اب مسدود نہیں ہے"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"‏نیٹ ورک یا SIM کارڈ کی خرابی"</string>
     <string name="doneButton" msgid="7371209609238460207">"ہوگیا"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"فون کی معلومات"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"کیرئیر فراہمی معلومات"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"ٹرگر کیرئیر فراہمی"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"تجویز: بلوٹوتھ کنیکٹوٹی کو بہتر بنائیں"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"آپ کا بلوٹوتھ سگنل کمزور ہے۔ اسپیکر فون پر سوئچ کر کے آزمائیں۔"</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"کال کی کوالٹی کی اطلاع"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"‏فرسودہ SIP اکاؤنٹس"</string>
 </resources>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index fa3bd82..9ebb2ac 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"SIM kartada ayrim funksiyalar ishlamaydi"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"<xliff:g id="PHONE_NUMBER">%2$s</xliff:g> raqami ishlatilsa, <xliff:g id="CARRIER_NAME">%1$s</xliff:g> orqali chaqiruvlar va mobil internet bloklanishi mumkin."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Boshqa SIM karta ishlatilsa, <xliff:g id="CARRIER_NAME">%1$s</xliff:g> orqali chaqiruvlar va internet bloklanishi mumkin."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"To‘xtatilgan SIP hisoblar aniqlandi va olib tashlandi"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Endi Android platformasida SIP orqali chaqiruvlarni amalga oshirish imkonsiz.\nSIP hisoblaringiz (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) olib tashlandi.\nChaqiruvlar uchun standart holatda ishlatiladigan hisobingizni tasdiqlang."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Sozlamalarni ochish"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Ilova sarflagan trafik"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_2">%2$s</xliff:g> davomida sarflangan mobil trafik: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Kengaytirilgan sozlamalar"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Parol noto‘g‘ri. SIM karta qulflandi. PUK2 kodini kiriting."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 kod butunlay to‘sib qo‘yildi."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Sizda <xliff:g id="NUMBER">%d</xliff:g> ta urinish qoldi."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 bloklangan. Uni blokdan chiqarish uchun xizmat ta’minotchisiga murojaat qiling."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 kodi blokdan chiqarildi"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Tarmoq yoki SIM kartada xato"</string>
     <string name="doneButton" msgid="7371209609238460207">"Tayyor"</string>
@@ -530,7 +534,7 @@
     <string name="notification_voicemail_no_vm_number" msgid="3423686009815186750">"Ovozli pochta raqami noma’lum"</string>
     <string name="notification_network_selection_title" msgid="255595526707809121">"Xizmat mavjud emas"</string>
     <string name="notification_network_selection_text" msgid="553288408722427659">"Tanlangan tarmoq (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) mavjud emas"</string>
-    <string name="incall_error_power_off" product="watch" msgid="7191184639454113633">"Qo‘ng‘iroq qilish uchun mobil tarmoqni yoqing, parvoz yoki quvvat tejash rejimini o‘chirib qo‘ying."</string>
+    <string name="incall_error_power_off" product="watch" msgid="7191184639454113633">"Telefon qilish uchun mobil tarmoqni yoqing, parvoz yoki quvvat tejash rejimini oʻchiring."</string>
     <string name="incall_error_power_off" product="default" msgid="8131672264311208673">"Qo‘ng‘iroq qilish uchun parvoz rejimini o‘chiring"</string>
     <string name="incall_error_power_off_wfc" msgid="9125661184694727052">"Qo‘ng‘iroq qilish uchun parvoz rejimini o‘chiring yoki simsiz tarmoqqa ulaning."</string>
     <string name="incall_error_ecm_emergency_only" msgid="5622379058883722080">"Odatiy qo‘ng‘iroq qilish uchun favqulodda qayta qo‘ng‘iroq rejimidan chiqing."</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Telefon haqida"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Operator sinxronizatsiyasi haqida"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Operator sinxronizatsiyasini yoqish/o‘chirish"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Tavsiya: Aloqa sifatini Bluetooth orqali oshiring"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Bluetooth signali kuchsiz. Baland ovoz rejimini yoqish tavsiya etiladi."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Chaqiruv sifati haqida bildirishnoma"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"To‘xtatilgan SIP hisoblar"</string>
 </resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index bd8a291..a0fb9a0 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Chức năng SIM bị giới hạn"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Các cuộc gọi và dịch vụ dữ liệu của nhà mạng <xliff:g id="CARRIER_NAME">%1$s</xliff:g> có thể bị chặn khi bạn sử dụng số <xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Các cuộc gọi và dịch vụ dữ liệu của nhà mạng <xliff:g id="CARRIER_NAME">%1$s</xliff:g> có thể bị chặn khi bạn sử dụng một SIM khác."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Đã tìm thấy và xóa các tài khoản SIP không dùng nữa"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Nền tảng Android không còn hỗ trợ tính năng gọi điện qua SIP.\nCác tài khoản SIP hiện có của bạn <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> đã bị xóa.\nVui lòng xác nhận chế độ cài đặt tài khoản gọi mặc định của bạn."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Chuyển đến phần Cài đặt"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Mức sử dụng dữ liệu của ứng dụng"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"Đã dùng <xliff:g id="ID_1">%1$s</xliff:g> dữ liệu di động: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Nâng cao"</string>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Mật khẩu không đúng. SIM hiện đã bị chặn. Hãy nhập PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 bị chặn vĩnh viễn."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Bạn còn <xliff:g id="NUMBER">%d</xliff:g> lần thử."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 đang bị khóa. Hãy liên hệ với nhà cung cấp dịch vụ để mở khóa."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 không còn bị chặn"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Lỗi mạng hoặc thẻ SIM"</string>
     <string name="doneButton" msgid="7371209609238460207">"Xong"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Thông tin điện thoại"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Thông tin cấp phép của nhà mạng"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Kích hoạt dịch vụ cấp phép của nhà mạng"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Mục đề xuất: Cải thiện khả năng kết nối Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Tín hiệu Bluetooth của bạn đang yếu. Hãy thử chuyển sang loa ngoài."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Thông báo về chất lượng cuộc gọi"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Tài khoản SIP không dùng nữa"</string>
 </resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 2164961..50b4b27 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"SIM 卡功能受限"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"使用 <xliff:g id="PHONE_NUMBER">%2$s</xliff:g> 时,系统可能会屏蔽<xliff:g id="CARRIER_NAME">%1$s</xliff:g>通话和数据服务。"</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"使用另一张 SIM 卡时,系统可能会屏蔽<xliff:g id="CARRIER_NAME">%1$s</xliff:g>通话和数据服务。"</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"系统已找到并移除已弃用的 SIP 帐号"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"SIP 通话已不再受 Android 平台支持。\n您现有的 SIP 帐号 <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> 已被移除。\n请确认您的默认通话帐号设置。"</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"转到设置"</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>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"密码不正确。SIM 卡已锁定。请输入 PUK2 码。"</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2码已被永远锁定。"</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"您还可以尝试输入 <xliff:g id="NUMBER">%d</xliff:g> 次。"</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"SIM 卡已通过 PUK2 进行锁定。请联系服务提供商来解锁。"</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 码已解除锁定"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"网络或 SIM 卡错误"</string>
     <string name="doneButton" msgid="7371209609238460207">"完成"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"手机信息"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"运营商配置信息"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"触发运营商配置"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"建议:改善蓝牙连接"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"您的蓝牙信号较弱。请尝试切换为扬声器模式。"</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"通话质量通知"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"已弃用的 SIP 帐号"</string>
 </resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index c9c8f4f..4da008f 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"SIM 卡功能受限"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"「<xliff:g id="CARRIER_NAME">%1$s</xliff:g>」通話和數據服務在使用 <xliff:g id="PHONE_NUMBER">%2$s</xliff:g> 卡時可能遭到封鎖。"</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"「<xliff:g id="CARRIER_NAME">%1$s</xliff:g>」通話和數據服務在使用其他 SIM 卡時可能遭到封鎖。"</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"已找到並移除已停用的 SIP 帳戶"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Android 平台不再支援 SIP 通話。\n您現有的 SIP 帳戶 (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>) 已移除。\n請確認您的預設通話帳戶設定。"</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"前往設定"</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>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"密碼不正確。SIM 現在已被封鎖,請輸入 PUK2。"</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 碼已被永久封鎖。"</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"您還有 <xliff:g id="NUMBER">%d</xliff:g> 次嘗試機會。"</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"PUK2 已鎖定。請通知服務供應商解鎖。"</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 目前沒有封鎖"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"網絡或 SIM 卡錯誤。"</string>
     <string name="doneButton" msgid="7371209609238460207">"完成"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"手機資訊"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"流動網絡供應商佈建資料"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"觸發流動網絡供應商佈建程序"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"建議:改善 Bluetooth 連線品質"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"您的藍牙訊號微弱。請改用擴音器。"</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"通話品質通知"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"已停用的 SIP 帳戶"</string>
 </resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index a47f998..440bb1d 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"SIM 卡功能受限"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"使用 <xliff:g id="PHONE_NUMBER">%2$s</xliff:g> 時,「<xliff:g id="CARRIER_NAME">%1$s</xliff:g>」的通話和數據服務可能會遭到封鎖。"</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"當你使用另一張 SIM 卡時,「<xliff:g id="CARRIER_NAME">%1$s</xliff:g>」的通話和數據服務可能會遭到封鎖。"</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"系統找到不適用的 SIP 帳戶,並已將這些帳戶移除"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Android 平台不再支援 SIP 通話功能。\n系統已移除你現有的 SIP 帳戶 (<xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g>)。\n請確認你的預設通話帳戶設定。"</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"前往設定"</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>
@@ -512,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"密碼不正確,SIM 卡現已遭到封鎖。請輸入 PUK2。"</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"PUK2 遭到永久封鎖。"</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"您還可以嘗試 <xliff:g id="NUMBER">%d</xliff:g> 次。"</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"SIM 卡已遭 PUK2 碼鎖定,請要求服務供應商協助解鎖。"</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"PIN2 已解除封鎖"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"網路或 SIM 卡錯誤"</string>
     <string name="doneButton" msgid="7371209609238460207">"完成"</string>
@@ -530,7 +534,7 @@
     <string name="notification_voicemail_no_vm_number" msgid="3423686009815186750">"無語音信箱號碼"</string>
     <string name="notification_network_selection_title" msgid="255595526707809121">"沒有服務"</string>
     <string name="notification_network_selection_text" msgid="553288408722427659">"所選網路 (<xliff:g id="OPERATOR_NAME">%s</xliff:g>) 無法使用"</string>
-    <string name="incall_error_power_off" product="watch" msgid="7191184639454113633">"如要撥打電話,請開啟行動網路,並關閉飛航模式或節約耗電量模式。"</string>
+    <string name="incall_error_power_off" product="watch" msgid="7191184639454113633">"如要撥打電話,請開啟行動網路,並關閉飛航模式或省電模式。"</string>
     <string name="incall_error_power_off" product="default" msgid="8131672264311208673">"關閉飛航模式即可撥打電話。"</string>
     <string name="incall_error_power_off_wfc" msgid="9125661184694727052">"關閉飛航模式或連上無線網路即可撥打電話。"</string>
     <string name="incall_error_ecm_emergency_only" msgid="5622379058883722080">"結束緊急回撥模式,以便撥打非緊急電話。"</string>
@@ -919,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"手機資訊"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"電信業者佈建資訊"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"觸發電信業者佈建程序"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"建議:改善藍牙連線能力"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"你的藍牙訊號微弱,建議你改用擴音模式。"</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"通話品質通知"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"不適用的 SIP 帳戶"</string>
 </resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index dace3b1..3456b3a 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -294,6 +294,9 @@
     <string name="limited_sim_function_notification_title" msgid="612715399099846281">"Ukusebenza okukhawulelwe kwe-SIM"</string>
     <string name="limited_sim_function_with_phone_num_notification_message" msgid="5928988883403677610">"Amakholi e-<xliff:g id="CARRIER_NAME">%1$s</xliff:g> namasevisi edatha angavinjelwa kusetshenziswa u-<xliff:g id="PHONE_NUMBER">%2$s</xliff:g>."</string>
     <string name="limited_sim_function_notification_message" msgid="5338638075496721160">"Amakholi e-<xliff:g id="CARRIER_NAME">%1$s</xliff:g> namasevisi edatha angavinjelwa ngenkathi kusetshenziswa enye i-SIM."</string>
+    <string name="sip_accounts_removed_notification_title" msgid="3528076957535736095">"Ama-akhawunti we-SIP eshlisiwe atholakele futhi asusiwe"</string>
+    <string name="sip_accounts_removed_notification_message" msgid="1916856744869791592">"Ukufona kwe-SIP akusasekelwa ingxenyekazi ye-Android.\nAma-akhawunti akho e-SIP akhona <xliff:g id="REMOVED_SIP_ACCOUNTS">%s</xliff:g> ahanjisiwe.\nSicela uqinisekise isethingi le-akhawunti yakho lokufona elizenzakalelayo."</string>
+    <string name="sip_accounts_removed_notification_action" msgid="3772778402370555562">"Iya kumasethingi"</string>
     <string name="data_usage_title" msgid="8438592133893837464">"Ukusetshenziswa kwedatha yohlelo lokusebenza"</string>
     <string name="data_usage_template" msgid="6287906680674061783">"<xliff:g id="ID_1">%1$s</xliff:g> kusetshenziswe idatha yeselula engu-<xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="advanced_options_title" msgid="9208195294513520934">"Okuthuthukisiwe"</string>
@@ -315,9 +318,7 @@
     <string name="throttle_time_frame" msgid="1813452485948918791">"Isikhathi sokusebenzisa idatha"</string>
     <string name="throttle_rate" msgid="7641913901133634905">"Inqubomgomo yokukala idatha"</string>
     <string name="throttle_help" msgid="2624535757028809735">"Funda kabanzi"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for throttle_status_subtext (1110276415078236687) -->
-    <skip />
+    <string name="throttle_status_subtext" msgid="1110276415078236687">"<xliff:g id="USED_0">%1$s</xliff:g> (<xliff:g id="USED_1">%2$d</xliff:g>٪) ye <xliff:g id="USED_2">%3$s</xliff:g> isikhathi esingeqiwe \nIsikhathi esilandelayo siqala ezinsukwini ezingu-<xliff:g id="USED_3">%4$d</xliff:g> (<xliff:g id="USED_4">%5$s</xliff:g>)"</string>
     <string name="throttle_data_usage_subtext" msgid="3185429653996709840">"<xliff:g id="USED_0">%1$s</xliff:g> (<xliff:g id="USED_1">%2$d</xliff:g>٪) ye- <xliff:g id="USED_2">%3$s</xliff:g> isikhathi esiphezulu"</string>
     <string name="throttle_data_rate_reduced_subtext" msgid="8369839346277847725">"<xliff:g id="USED_0">%1$s</xliff:g>okuphezulu kufinyelelwe\nIsilinganiso sedatha sehliselwe ku- <xliff:g id="USED_1">%2$d</xliff:g> ama-Kb"</string>
     <string name="throttle_time_frame_subtext" msgid="6462089615392402127">"<xliff:g id="USED_0">%1$d</xliff:g>٪ lesikhathi esiphelile\nIsikhathi esilandelayo siqala ezinsukwini <xliff:g id="USED_1">%2$d</xliff:g> ezingu <xliff:g id="USED_2">%3$s</xliff:g>)"</string>
@@ -514,6 +515,7 @@
     <string name="puk2_requested" msgid="6992374450720307514">"Iphasiwedi ayilungile. I-SIM manje ikhiyiwe. Faka i-PUK2."</string>
     <string name="puk2_blocked" msgid="3131139031779319911">"I-PUK2 ivinjiwe unaphakade."</string>
     <string name="pin2_attempts" msgid="5625178102026453023">\n"Unemizamo engu-<xliff:g id="NUMBER">%d</xliff:g> esele."</string>
+    <string name="puk2_locked" msgid="6497760825455461057">"I-PUK2 ikhiyiwe. Xhumana nomhlinzeki wesevisi ukuze uvule."</string>
     <string name="pin2_unblocked" msgid="4481107908727789303">"I-PIN2 ayisavinjiwe"</string>
     <string name="pin2_error_exception" msgid="8116103864600823641">"Iphutha lenethiwekhi noma lekhadi le-SIM"</string>
     <string name="doneButton" msgid="7371209609238460207">"Kwenziwe"</string>
@@ -921,6 +923,7 @@
     <string name="phone_info_label" product="default" msgid="1784175881556791433">"Ulwazi lwefoni"</string>
     <string name="carrier_provisioning" msgid="2668065041869578376">"Ulwazi lokuhlinzeka lenkampani yenethiwekhi"</string>
     <string name="trigger_carrier_provisioning" msgid="1301829588620638234">"Cupha ukunikezwa kwenkampani yenethiwekhi"</string>
-    <string name="call_quality_notification_bluetooth_details" msgid="219073466476340435">"Isiphakamiso: Thuthukisa uxhumo lwe-Bluetooth"</string>
+    <string name="call_quality_notification_bluetooth_details" msgid="8348950331707346711">"Isignali yakho ye-bluetooth ayiqinile. Zama ukushintshela kusipikhasefoni."</string>
     <string name="call_quality_notification_name" msgid="3476828289553948830">"Isaziso Sekhwalithi"</string>
+    <string name="notification_channel_sip_account" msgid="1261816025156179637">"Ama-akhawunti we-SIP ehlisiwe"</string>
 </resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 7dd26bb..9f8cc81 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -304,10 +304,17 @@
         positive n - release in n milliseconds -->
     <integer name="config_gba_release_time">0</integer>
 
-    <!-- Whether or not to support RCS VoLTE single registration  -->
-    <bool name="config_rcsVolteSingleRegistrationEnabled">true</bool>
+    <!-- Whether or not to support RCS User Capability Exchange -->
+    <bool name="config_rcs_user_capability_exchange_enabled">true</bool>
 
     <!-- Whether or not to support device to device communication using RTP and DTMF communication
          transports. -->
     <bool name="config_use_device_to_device_communication">false</bool>
+
+    <!-- Whether or not to show notifications for when bluetooth connection is bad during a call -->
+    <bool name="enable_bluetooth_call_quality_notification">false</bool>
+
+    <!-- The package names which can request thermal mitigation. -->
+    <string-array name="thermal_mitigation_allowlisted_packages" translatable="false">
+    </string-array>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1613ca8..2077530 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -644,6 +644,13 @@
     <string name="limited_sim_function_with_phone_num_notification_message"><xliff:g id="carrier_name">%1$s</xliff:g> calls and data services may be blocked while using <xliff:g id="phone_number">%2$s</xliff:g>.</string>
     <!-- Notification message for limited sim function during dual sim [CHAR LIMIT=80]-->
     <string name="limited_sim_function_notification_message"><xliff:g id="carrier_name">%1$s</xliff:g> calls and data services may be blocked while using another SIM.</string>
+    <!-- Notification title for SIP accounts removed -->
+    <string name="sip_accounts_removed_notification_title">Deprecated SIP accounts found and removed</string>
+    <!-- Notification message for SIP accoutns removed -->
+    <string name="sip_accounts_removed_notification_message">
+        SIP calling is no longer supported by Android platform.\nYour existing SIP accounts <xliff:g id="removed_sip_accounts">%s</xliff:g> have been removed.\nPlease confirm your default calling account setting.
+    </string>
+    <string name="sip_accounts_removed_notification_action">Go to settings</string>
     <!-- Mobile network settings screen, data usage setting check box name -->
     <string name="data_usage_title">App data usage</string>
     <!-- Summary about how much data has been used in a date range [CHAR LIMIT=100] -->
@@ -1125,6 +1132,8 @@
     <string name="puk2_blocked">PUK2 is permanently blocked.</string>
     <!-- SIM PIN2 screen: error message -->
     <string name="pin2_attempts">\nYou have <xliff:g id="number">%d</xliff:g> remaining attempts.</string>
+    <!-- SIM PIN2 screen: error message displayed in a dialog -->
+    <string name="puk2_locked">PUK2 locked. Contact service provider to unlock.</string>
     <!-- SIM PIN screen: status message displayed in a popup (toast) -->
     <string name="pin2_unblocked">PIN2 no longer blocked</string>
     <!-- SIM PIN screen: error message shown in dialog when there is a network or sim error.
@@ -1186,6 +1195,8 @@
     <string name="incall_error_power_off" product="default">Turn off airplane mode to make a call.</string>
     <!-- In-call screen: call failure message displayed in an error dialog when in airplane mode, WFC is enabled, not wifi-only, and not connected to wireless networks. [CHAR_LIMIT=NONE] -->
     <string name="incall_error_power_off_wfc">Turn off airplane mode or connect to a wireless network to make a call.</string>
+    <!-- In-call screen: call failure message displayed in an error dialog when radio off due to thermal mitigation. [CHAR_LIMIT=NONE] -->
+    <string name="incall_error_power_off_thermal" product="default"><b>Phone too hot</b>\n\nCan\'t complete this call. Try again when your phone cools down.\n\nYou can still make emergency calls.</string>
     <!-- In-call screen: call failure message displayed when the user attempts to make a non-emergency call while the phone is in emergency callback mode. -->
     <string name="incall_error_ecm_emergency_only">Exit emergency callback mode to make a non-emergency call.</string>
     <!-- In-call screen: call failure message displayed in an error dialog.
@@ -2150,6 +2161,8 @@
     <string name="radio_info_nr_state">NR State:</string>
     <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
     <string name="radio_info_nr_frequency">NR Frequency:</string>
+    <!-- Radio Info screen. Label for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="radio_info_network_slicing_config" translatable="false">Network slicing config:</string>
 
     <!-- Band Mode Selection -->
     <!-- Band mode screen.  Title of activity. -->
@@ -2176,8 +2189,11 @@
     <!-- details of the message popped up when there is
     bad call quality caused by bluetooth connection-->
     <string name="call_quality_notification_bluetooth_details">
-        Suggestion: Improve Bluetooth connectivity</string>
+        Your bluetooth signal is weak.  Try switching to speakerphone.</string>
     <!-- name of the notification that pops up during
     a phone call when there is bad call quality -->
     <string name="call_quality_notification_name">Call Quality Notification</string>
+    <!-- Telephony notification channel name for a channel containing SIP accounts removed
+     notificatios -->
+    <string name="notification_channel_sip_account">Deprecated SIP accounts</string>
 </resources>
diff --git a/res/xml/carrier_ss_string.xml b/res/xml/carrier_ss_string.xml
index 29de13a..b7771cd 100644
--- a/res/xml/carrier_ss_string.xml
+++ b/res/xml/carrier_ss_string.xml
@@ -146,4 +146,32 @@
             <entry key="status_code" definition="ok">7</entry>
         </command_result>
     </feature>
+    <feature name="callwaiting">
+        <command name="query"><!--For example: *#102#-->
+            <service_code>102</service_code>
+            <action_code>*#</action_code>
+            <response_format number ="1"><!--For example: 120*4#-->
+                <entry position="4" key="status_code"/>
+            </response_format>
+        </command>
+        <command name="activate"><!--For example: *102#-->
+            <service_code>102</service_code>
+            <action_code>*</action_code>
+            <response_format number="1"><!--For example: 102*5#-->
+                <entry position="4" key="status_code"/>
+            </response_format>
+        </command>
+        <command name="deactivate"><!--For example: #102#-->
+            <service_code>102</service_code>
+            <action_code>#</action_code>
+            <response_format number="1"><!--For example: 148*4#-->
+                <entry position="4" key="status_code"/>
+            </response_format>
+        </command>
+        <command_result number="3">
+            <entry key="status_code" definition="activate">5</entry>
+            <entry key="status_code" definition="deactivate">4</entry>
+            <entry key="status_code" definition="unregister">0</entry>
+        </command_result>
+    </feature>
 </resources>
diff --git a/res/xml/carrier_ss_string_850.xml b/res/xml/carrier_ss_string_850.xml
index 01eeee5..ed31fae 100644
--- a/res/xml/carrier_ss_string_850.xml
+++ b/res/xml/carrier_ss_string_850.xml
@@ -94,4 +94,32 @@
             <entry key="status_code" definition="ok">7</entry>
         </command_result>
     </feature>
+    <feature name="callwaiting">
+        <command name="query"><!--For example: *#102#-->
+            <service_code>102</service_code>
+            <action_code>*#</action_code>
+            <response_format number ="1"><!--For example: 120*4#-->
+                <entry position="4" key="status_code"/>
+            </response_format>
+        </command>
+        <command name="activate"><!--For example: *102#-->
+            <service_code>102</service_code>
+            <action_code>*</action_code>
+            <response_format number="1"><!--For example: 102*5#-->
+                <entry position="4" key="status_code"/>
+            </response_format>
+        </command>
+        <command name="deactivate"><!--For example: #102#-->
+            <service_code>102</service_code>
+            <action_code>#</action_code>
+            <response_format number="1"><!--For example: 148*4#-->
+                <entry position="4" key="status_code"/>
+            </response_format>
+        </command>
+        <command_result number="3">
+            <entry key="status_code" definition="activate">5</entry>
+            <entry key="status_code" definition="deactivate">4</entry>
+            <entry key="status_code" definition="unregister">0</entry>
+        </command_result>
+    </feature>
 </resources>
diff --git a/res/xml/phone_account_settings.xml b/res/xml/phone_account_settings.xml
index a243a65..8722761 100644
--- a/res/xml/phone_account_settings.xml
+++ b/res/xml/phone_account_settings.xml
@@ -55,34 +55,4 @@
 
     </PreferenceCategory>
 
-    <PreferenceCategory
-        android:key="phone_accounts_sip_settings_category_key"
-        android:title="@string/sip_settings"
-        android:persistent="false">
-
-        <PreferenceScreen
-            android:title="@string/sip_accounts"
-            android:persistent="false">
-
-            <intent android:action="android.intent.action.MAIN"
-                android:targetPackage="com.android.phone"
-                android:targetClass="com.android.services.telephony.sip.SipSettings" />
-
-        </PreferenceScreen>
-
-        <ListPreference
-            android:key="use_sip_calling_options_key"
-            android:title="@string/sip_call_options_title"
-            android:persistent="true"
-            android:entries="@array/sip_call_options_entries"
-            android:entryValues="@array/sip_call_options_values"/>
-
-        <SwitchPreference
-            android:key="sip_receive_calls_key"
-            android:title="@string/sip_receive_calls"
-            android:summary="@string/sip_receive_calls_summary"
-            android:persistent="true"/>
-
-    </PreferenceCategory>
-
 </PreferenceScreen>
diff --git a/sip/res/values-gl/strings.xml b/sip/res/values-gl/strings.xml
index 1a40bf3..a6c15d5 100644
--- a/sip/res/values-gl/strings.xml
+++ b/sip/res/values-gl/strings.xml
@@ -57,7 +57,7 @@
     <string name="proxy_address_title" msgid="4120361943254795287">"Enderezo proxy saínte"</string>
     <string name="port_title" msgid="1703586046264385110">"Número de porto"</string>
     <string name="transport_title" msgid="1661659138226029178">"Tipo de transporte"</string>
-    <string name="send_keepalive_title" msgid="5319788151608946049">"Enviar sinal conexión"</string>
+    <string name="send_keepalive_title" msgid="5319788151608946049">"Enviar sinal de conexión permanente"</string>
     <string name="advanced_settings" msgid="2704644977548662872">"Configuración opcional"</string>
     <string name="auth_username_title" msgid="9002505242616662698">"Nome de usuario da autenticación"</string>
     <string name="auth_username_summary" msgid="6346313945275377230">"Nome de usuario utilizado para a autenticación"</string>
diff --git a/sip/res/values-gu/strings.xml b/sip/res/values-gu/strings.xml
index 9eb810b..70232ad 100644
--- a/sip/res/values-gu/strings.xml
+++ b/sip/res/values-gu/strings.xml
@@ -74,7 +74,7 @@
     <string name="no_internet_available" msgid="161720645084325479">"SIP કૉલ કરવા માટે, પ્રથમ તમારું ઇન્ટરનેટ કનેક્શન તપાસો."</string>
     <string name="no_wifi_available" msgid="1179092018692306312">"SIP કૉલ્સ માટે તમારે Wi-Fi નેટવર્કથી કનેક્ટ હોવું જરૂરી છે (વાયરલેસ અને નેટવર્ક સેટિંગ્સનો ઉપયોગ કરો)."</string>
     <string name="no_voip" msgid="3366395789297981738">"SIP કૉલિંગ સમર્થિત નથી"</string>
-    <string name="sip_system_decide" msgid="197230378376326430">"સ્વચલિત"</string>
+    <string name="sip_system_decide" msgid="197230378376326430">"ઑટોમૅટિક"</string>
     <string name="sip_always_send_keepalive" msgid="4986533673960084769">"હંમેશાં મોકલો"</string>
     <string name="sip_connection_service_label" msgid="8796284274240316006">"બિલ્ટ-ઇન SIP કૉલિંગ"</string>
 </resources>
diff --git a/sip/res/values-iw/strings.xml b/sip/res/values-iw/strings.xml
index 6e84560..34b1b20 100644
--- a/sip/res/values-iw/strings.xml
+++ b/sip/res/values-iw/strings.xml
@@ -24,29 +24,29 @@
     <string name="sip_call_options_title" msgid="5027066677561068192">"‏שימוש בשיחות SIP"</string>
     <string name="sip_call_options_wifi_only_title" msgid="6663105297927456484">"‏שימוש בשיחות SIP ‏(Wi-Fi בלבד)‏"</string>
     <string name="sip_call_options_entry_1" msgid="4722647332760934261">"לכל השיחות כאשר רשת הנתונים זמינה"</string>
-    <string name="sip_call_options_entry_2" msgid="7338504256051655013">"‏עבור שיחות SIP בלבד"</string>
+    <string name="sip_call_options_entry_2" msgid="7338504256051655013">"‏מעבר לשיחות SIP בלבד"</string>
     <string name="sip_call_options_wifi_only_entry_1" msgid="922329055414010991">"לכל השיחות"</string>
     <string name="add_sip_account" msgid="5754758646745144384">"חשבון חדש"</string>
     <string name="remove_sip_account" msgid="8272617403399636513">"הסרת חשבון"</string>
     <string name="sip_account_list" msgid="2596262496233721769">"‏חשבונות SIP"</string>
-    <string name="saving_account" msgid="3390358043846687266">"שומר את החשבון..."</string>
-    <string name="removing_account" msgid="1544132880414780408">"מסיר את החשבון..."</string>
-    <string name="sip_menu_save" msgid="4377112554203123060">"שמור"</string>
-    <string name="sip_menu_discard" msgid="1883166691772895243">"מחק"</string>
-    <string name="alert_dialog_close" msgid="1734746505531110706">"סגור את הפרופיל"</string>
+    <string name="saving_account" msgid="3390358043846687266">"שמירת החשבון מתבצעת..."</string>
+    <string name="removing_account" msgid="1544132880414780408">"הסרת החשבון מתבצעת..."</string>
+    <string name="sip_menu_save" msgid="4377112554203123060">"שמירה"</string>
+    <string name="sip_menu_discard" msgid="1883166691772895243">"מחיקה"</string>
+    <string name="alert_dialog_close" msgid="1734746505531110706">"סגירת הפרופיל"</string>
     <string name="alert_dialog_ok" msgid="7806760618798687406">"אישור"</string>
-    <string name="close_profile" msgid="3756064641769751774">"סגור"</string>
-    <string name="registration_status_checking_status" msgid="884179594507591180">"בודק מצב..."</string>
-    <string name="registration_status_registering" msgid="7986331597809521791">"מבצע רישום..."</string>
-    <string name="registration_status_still_trying" msgid="7178623685868766282">"עדיין מנסה..."</string>
+    <string name="close_profile" msgid="3756064641769751774">"סגירה"</string>
+    <string name="registration_status_checking_status" msgid="884179594507591180">"בדיקת המצב מתבצעת..."</string>
+    <string name="registration_status_registering" msgid="7986331597809521791">"הרישום מתבצע..."</string>
+    <string name="registration_status_still_trying" msgid="7178623685868766282">"הניסיון מתבצע..."</string>
     <string name="registration_status_not_receiving" msgid="3873074208531938401">"לא מקבל שיחות."</string>
     <string name="registration_status_no_data" msgid="2987064560116584121">"רישום החשבון הופסק כיוון שאין חיבור לאינטרנט."</string>
     <string name="registration_status_no_wifi_data" msgid="685470618241482948">"‏רישום החשבון הופסק כיוון שאין חיבור Wi-Fi."</string>
     <string name="registration_status_not_running" msgid="6236403137652262659">"רישום החשבון נכשל."</string>
-    <string name="registration_status_done" msgid="6787397199273357721">"מקבל שיחות."</string>
+    <string name="registration_status_done" msgid="6787397199273357721">"קבלת שיחות."</string>
     <string name="registration_status_failed_try_later" msgid="7855389184910312091">"רישום החשבון נכשל: (<xliff:g id="REGISTRATION_ERROR_MESSAGE">%s</xliff:g>); ניסיון חוזר יבוצע מאוחר יותר"</string>
     <string name="registration_status_invalid_credentials" msgid="8896714049938660777">"רישום החשבון נכשל: שם המשתמש או הסיסמה שגויים."</string>
-    <string name="registration_status_server_unreachable" msgid="3832339558868965604">"רישום החשבון נכשל: בדוק את שם השרת."</string>
+    <string name="registration_status_server_unreachable" msgid="3832339558868965604">"רישום החשבון נכשל: יש לבדוק את שם השרת."</string>
     <string name="third_party_account_summary" msgid="5918779106950859167">"האפליקציה <xliff:g id="ACCOUNT_OWNER">%s</xliff:g> משתמשת כרגע בחשבון הזה."</string>
     <string name="sip_edit_title" msgid="7438891546610820307">"‏פרטי חשבון SIP"</string>
     <string name="sip_edit_new_title" msgid="8394790068979636381">"‏פרטי חשבון SIP"</string>
@@ -57,7 +57,7 @@
     <string name="proxy_address_title" msgid="4120361943254795287">"‏כתובת שרת Proxy יוצא"</string>
     <string name="port_title" msgid="1703586046264385110">"מספר יציאה"</string>
     <string name="transport_title" msgid="1661659138226029178">"סוג העברה"</string>
-    <string name="send_keepalive_title" msgid="5319788151608946049">"שלח אות חיבור"</string>
+    <string name="send_keepalive_title" msgid="5319788151608946049">"שליחת אות חיבור"</string>
     <string name="advanced_settings" msgid="2704644977548662872">"הגדרות אופציונליות"</string>
     <string name="auth_username_title" msgid="9002505242616662698">"שם משתמש לאימות"</string>
     <string name="auth_username_summary" msgid="6346313945275377230">"שם משתמש המשמש לאימות"</string>
@@ -67,14 +67,14 @@
     <string name="display_name_summary" msgid="6749135030093260358">"‏&lt;זהה לשם משתמש&gt;"</string>
     <string name="optional_summary" msgid="620379377865437488">"‏&lt;אופציונלי&gt;"</string>
     <string name="advanced_settings_show" msgid="2318728080037568529">"איפה כל ההגדרות?"</string>
-    <string name="advanced_settings_hide" msgid="6200816937370652083">"▽ גע כדי להסתיר את הכול"</string>
-    <string name="all_empty_alert" msgid="6085603517610199098">"‏הזן את פרטי חשבון ה-SIP החדש."</string>
+    <string name="advanced_settings_hide" msgid="6200816937370652083">"▽ יש לגעת כדי להסתיר את הכול"</string>
+    <string name="all_empty_alert" msgid="6085603517610199098">"‏יש להזין את פרטי חשבון ה-SIP החדש."</string>
     <string name="empty_alert" msgid="3693655518612836718">"<xliff:g id="INPUT_FIELD_NAME">%s</xliff:g> הוא שדה חובה ולא ניתן להשאיר אותו ריק."</string>
     <string name="not_a_valid_port" msgid="3664668836663491376">"מספר היציאה צריך להיות בין 1000 ל-65534."</string>
-    <string name="no_internet_available" msgid="161720645084325479">"‏כדי לבצע שיחת SIP, ראשית בדוק את חיבור האינטרנט שלך."</string>
-    <string name="no_wifi_available" msgid="1179092018692306312">"‏עליך להיות מחובר לרשת Wi-Fi כדי לבצע שיחות SIP (השתמש ב\'הגדרות רשת ותקשורת אלחוטית\')."</string>
+    <string name="no_internet_available" msgid="161720645084325479">"‏כדי לבצע שיחת SIP, ראשית יש לבדוק את חיבור האינטרנט שלך."</string>
+    <string name="no_wifi_available" msgid="1179092018692306312">"‏יש צורך בחיבור לרשת Wi-Fi כדי לבצע שיחות SIP (יש להשתמש ב\'הגדרות רשת ותקשורת אלחוטית\')."</string>
     <string name="no_voip" msgid="3366395789297981738">"‏שיחות SIP לא נתמכות"</string>
     <string name="sip_system_decide" msgid="197230378376326430">"אוטומטי"</string>
-    <string name="sip_always_send_keepalive" msgid="4986533673960084769">"שלח תמיד"</string>
+    <string name="sip_always_send_keepalive" msgid="4986533673960084769">"שליחה תמיד"</string>
     <string name="sip_connection_service_label" msgid="8796284274240316006">"‏שיחות SIP מובנות"</string>
 </resources>
diff --git a/sip/res/values-kn/strings.xml b/sip/res/values-kn/strings.xml
index cf7cd59..99fe93e 100644
--- a/sip/res/values-kn/strings.xml
+++ b/sip/res/values-kn/strings.xml
@@ -19,7 +19,7 @@
     <string name="sip_settings" msgid="7452587325305604702">"ಎಸ್‌ಐಪಿ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="sip_accounts" msgid="7297896885665783239">"ಎಸ್‌ಐಪಿ ಖಾತೆಗಳು"</string>
     <string name="sip_accounts_title" msgid="3061686404598143943">"ಖಾತೆಗಳು"</string>
-    <string name="sip_receive_calls" msgid="3403644006618369349">"ಒಳಬರುವ ಕರೆಗಳನ್ನು ಸ್ವೀಕರಿಸು"</string>
+    <string name="sip_receive_calls" msgid="3403644006618369349">"ಒಳಬರುವ ಕರೆಗಳನ್ನು ಸ್ವೀಕರಿಸಿ"</string>
     <string name="sip_receive_calls_summary" msgid="5306603671778761443">"ಹೆಚ್ಚು ಬ್ಯಾಟರಿಯನ್ನು ಬಳಸುತ್ತದೆ"</string>
     <string name="sip_call_options_title" msgid="5027066677561068192">"ಎಸ್‌ಐಪಿ ಕರೆ ಮಾಡುವಿಕೆಯನ್ನು ಬಳಸಿ"</string>
     <string name="sip_call_options_wifi_only_title" msgid="6663105297927456484">"ಎಸ್‌ಐಪಿ ಕರೆ ಮಾಡುವಿಕೆಯನ್ನು ಬಳಸಿ (ವೈ-ಫೈ ಮಾತ್ರ)"</string>
diff --git a/sip/res/values-ky/strings.xml b/sip/res/values-ky/strings.xml
index 6916f24..ab37d84 100644
--- a/sip/res/values-ky/strings.xml
+++ b/sip/res/values-ky/strings.xml
@@ -29,8 +29,8 @@
     <string name="add_sip_account" msgid="5754758646745144384">"Аккаунт кошуу"</string>
     <string name="remove_sip_account" msgid="8272617403399636513">"Аккаунтту өчүрүү"</string>
     <string name="sip_account_list" msgid="2596262496233721769">"SIP аккаунттары"</string>
-    <string name="saving_account" msgid="3390358043846687266">"Каттоо эсеби сакталууда…"</string>
-    <string name="removing_account" msgid="1544132880414780408">"Каттоо эсеби жок кылынууда…"</string>
+    <string name="saving_account" msgid="3390358043846687266">"Аккаунт сакталууда…"</string>
+    <string name="removing_account" msgid="1544132880414780408">"Аккаунт жок кылынууда…"</string>
     <string name="sip_menu_save" msgid="4377112554203123060">"Сактоо"</string>
     <string name="sip_menu_discard" msgid="1883166691772895243">"Жокко чыгаруу"</string>
     <string name="alert_dialog_close" msgid="1734746505531110706">"Профилди жабуу"</string>
diff --git a/sip/res/values-mr/strings.xml b/sip/res/values-mr/strings.xml
index 60d9c88..6eb7245 100644
--- a/sip/res/values-mr/strings.xml
+++ b/sip/res/values-mr/strings.xml
@@ -19,7 +19,7 @@
     <string name="sip_settings" msgid="7452587325305604702">"SIP सेटिंग्ज"</string>
     <string name="sip_accounts" msgid="7297896885665783239">"SIP खाती"</string>
     <string name="sip_accounts_title" msgid="3061686404598143943">"खाती"</string>
-    <string name="sip_receive_calls" msgid="3403644006618369349">"येणारे कॉल घ्या"</string>
+    <string name="sip_receive_calls" msgid="3403644006618369349">"इनकमिंग कॉल घ्या"</string>
     <string name="sip_receive_calls_summary" msgid="5306603671778761443">"अधिक बॅटरी वापरते"</string>
     <string name="sip_call_options_title" msgid="5027066677561068192">"SIP कॉलिंग वापरा"</string>
     <string name="sip_call_options_wifi_only_title" msgid="6663105297927456484">"SIP कॉलिंग वापरा (वाय-फाय केवळ)"</string>
diff --git a/sip/res/values-ne/strings.xml b/sip/res/values-ne/strings.xml
index 9fc1263..e8938aa 100644
--- a/sip/res/values-ne/strings.xml
+++ b/sip/res/values-ne/strings.xml
@@ -31,7 +31,7 @@
     <string name="sip_account_list" msgid="2596262496233721769">"SIP खाताहरू"</string>
     <string name="saving_account" msgid="3390358043846687266">"खाता बचत गर्दै..."</string>
     <string name="removing_account" msgid="1544132880414780408">"खाता हटाउँदै..."</string>
-    <string name="sip_menu_save" msgid="4377112554203123060">"सुरक्षित गर्नुहोस्"</string>
+    <string name="sip_menu_save" msgid="4377112554203123060">"सेभ गर्नुहोस्"</string>
     <string name="sip_menu_discard" msgid="1883166691772895243">"त्याग्नुहोस्"</string>
     <string name="alert_dialog_close" msgid="1734746505531110706">"प्रोफाइल बन्द गर्नुहोस्"</string>
     <string name="alert_dialog_ok" msgid="7806760618798687406">"ठिक छ"</string>
@@ -51,7 +51,7 @@
     <string name="sip_edit_title" msgid="7438891546610820307">"SIP खाता विवरणहरू"</string>
     <string name="sip_edit_new_title" msgid="8394790068979636381">"SIP खाता विवरणहरू"</string>
     <string name="domain_address_title" msgid="8238078615181248579">"सर्भर"</string>
-    <string name="username_title" msgid="298416796886107970">"एक-पटके पाठ सन्देश"</string>
+    <string name="username_title" msgid="298416796886107970">"एक-पटके टेक्स्ट म्यासेज"</string>
     <string name="password_title" msgid="8035579335591959021">"पासवर्ड"</string>
     <string name="display_name_title" msgid="3730105783656830160">"नाम प्रदर्शन गर्नुहोस्"</string>
     <string name="proxy_address_title" msgid="4120361943254795287">"बाहिरका प्रोक्सी ठेगाना"</string>
@@ -59,12 +59,12 @@
     <string name="transport_title" msgid="1661659138226029178">"यातायात प्रकार"</string>
     <string name="send_keepalive_title" msgid="5319788151608946049">"चालु राख्नको लागि पठाउनुहोस्"</string>
     <string name="advanced_settings" msgid="2704644977548662872">"वैकल्पिक सेटिङहरू"</string>
-    <string name="auth_username_title" msgid="9002505242616662698">"प्रमाणीकरणको एक-पटके पाठ सन्देश"</string>
-    <string name="auth_username_summary" msgid="6346313945275377230">"प्रमाणीकरणको लागि एक-पटके पाठ सन्देश प्रयोग भएको"</string>
+    <string name="auth_username_title" msgid="9002505242616662698">"प्रमाणीकरणको एक-पटके टेक्स्ट म्यासेज"</string>
+    <string name="auth_username_summary" msgid="6346313945275377230">"प्रमाणीकरणको लागि एक-पटके टेक्स्ट म्यासेज प्रयोग भएको"</string>
     <string name="default_preference_summary_username" msgid="8788114717555599222">"&lt;सेट गरिएको छैन&gt;"</string>
     <string name="default_preference_summary_password" msgid="3695366978153175549">"&lt;सेट गरिएको छैन&gt;"</string>
     <string name="default_preference_summary_domain_address" msgid="443247296785732364">"&lt;सेट गरिएको छैन&gt;"</string>
-    <string name="display_name_summary" msgid="6749135030093260358">"&lt;एक-पटके पाठ सन्देश जस्तै&gt;"</string>
+    <string name="display_name_summary" msgid="6749135030093260358">"&lt;एक-पटके टेक्स्ट म्यासेज जस्तै&gt;"</string>
     <string name="optional_summary" msgid="620379377865437488">"&lt;वैकल्पिकgt;"</string>
     <string name="advanced_settings_show" msgid="2318728080037568529">"▷ छोएर सबै देखाउनुहोस्"</string>
     <string name="advanced_settings_hide" msgid="6200816937370652083">"▽ छोएर सबै लुकाउनुहोस्"</string>
diff --git a/sip/res/values-sq/strings.xml b/sip/res/values-sq/strings.xml
index 5c50085..e2e2ff1 100644
--- a/sip/res/values-sq/strings.xml
+++ b/sip/res/values-sq/strings.xml
@@ -54,7 +54,7 @@
     <string name="username_title" msgid="298416796886107970">"Emri i përdoruesit"</string>
     <string name="password_title" msgid="8035579335591959021">"Fjalëkalimi"</string>
     <string name="display_name_title" msgid="3730105783656830160">"Shfaq emrin"</string>
-    <string name="proxy_address_title" msgid="4120361943254795287">"Adresa e përfaqësuesit dalës"</string>
+    <string name="proxy_address_title" msgid="4120361943254795287">"Adresa e proxy-it dalës"</string>
     <string name="port_title" msgid="1703586046264385110">"Numri i portës"</string>
     <string name="transport_title" msgid="1661659138226029178">"Lloji i transportit"</string>
     <string name="send_keepalive_title" msgid="5319788151608946049">"Kontrolli i veprimtarisë"</string>
diff --git a/sip/res/values-sv/strings.xml b/sip/res/values-sv/strings.xml
index 2976425..a59eb21 100644
--- a/sip/res/values-sv/strings.xml
+++ b/sip/res/values-sv/strings.xml
@@ -41,7 +41,7 @@
     <string name="registration_status_still_trying" msgid="7178623685868766282">"Försöker fortfarande ..."</string>
     <string name="registration_status_not_receiving" msgid="3873074208531938401">"Tar inte emot samtal."</string>
     <string name="registration_status_no_data" msgid="2987064560116584121">"Kontoregistreringen avbröts eftersom det inte finns någon internetanslutning."</string>
-    <string name="registration_status_no_wifi_data" msgid="685470618241482948">"Kontoregistreringen avbröts eftersom det inte finns någon Wi-Fi-anslutning."</string>
+    <string name="registration_status_no_wifi_data" msgid="685470618241482948">"Kontoregistreringen avbröts eftersom det inte finns någon wifi-anslutning."</string>
     <string name="registration_status_not_running" msgid="6236403137652262659">"Det gick inte att registrera kontot."</string>
     <string name="registration_status_done" msgid="6787397199273357721">"Tar emot samtal."</string>
     <string name="registration_status_failed_try_later" msgid="7855389184910312091">"Det gick inte att registrera kontot: (<xliff:g id="REGISTRATION_ERROR_MESSAGE">%s</xliff:g>); försöker igen senare"</string>
@@ -72,7 +72,7 @@
     <string name="empty_alert" msgid="3693655518612836718">"<xliff:g id="INPUT_FIELD_NAME">%s</xliff:g> är ett obligatoriskt fält som måste fyllas i."</string>
     <string name="not_a_valid_port" msgid="3664668836663491376">"Portnumret ska vara mellan 1000 och 65534."</string>
     <string name="no_internet_available" msgid="161720645084325479">"Om du vill ringa ett SIP-samtal kontrollerar du först att du är ansluten till internet."</string>
-    <string name="no_wifi_available" msgid="1179092018692306312">"Du måste vara ansluten till Wi-Fi för att kunna ringa SIP-samtal (du ändrar detta i inställningarna för Trådlöst och nätverk)."</string>
+    <string name="no_wifi_available" msgid="1179092018692306312">"Du måste vara ansluten till wifi för att kunna ringa SIP-samtal (du ändrar detta i inställningarna för Trådlöst och nätverk)."</string>
     <string name="no_voip" msgid="3366395789297981738">"SIP-samtal stöds inte"</string>
     <string name="sip_system_decide" msgid="197230378376326430">"Automatiskt"</string>
     <string name="sip_always_send_keepalive" msgid="4986533673960084769">"Skicka alltid"</string>
diff --git a/sip/res/values-ta/strings.xml b/sip/res/values-ta/strings.xml
index 0cb2b75..657d296 100644
--- a/sip/res/values-ta/strings.xml
+++ b/sip/res/values-ta/strings.xml
@@ -41,7 +41,7 @@
     <string name="registration_status_still_trying" msgid="7178623685868766282">"இன்னும் முயற்சிக்கிறது…"</string>
     <string name="registration_status_not_receiving" msgid="3873074208531938401">"அழைப்புகளைப் பெறவில்லை."</string>
     <string name="registration_status_no_data" msgid="2987064560116584121">"இணைய இணைப்பு இல்லாததால், கணக்குப் பதிவு நிறுத்தப்பட்டது."</string>
-    <string name="registration_status_no_wifi_data" msgid="685470618241482948">"Wi-Fi இணைப்பு இல்லாததால், கணக்குப் பதிவு நிறுத்தப்பட்டது."</string>
+    <string name="registration_status_no_wifi_data" msgid="685470618241482948">"வைஃபை இணைப்பு இல்லாததால், கணக்குப் பதிவு நிறுத்தப்பட்டது."</string>
     <string name="registration_status_not_running" msgid="6236403137652262659">"கணக்குப் பதிவு தோல்வி."</string>
     <string name="registration_status_done" msgid="6787397199273357721">"அழைப்புகளைப் பெறுதல்."</string>
     <string name="registration_status_failed_try_later" msgid="7855389184910312091">"கணக்குப் பதிவு தோல்வி: (<xliff:g id="REGISTRATION_ERROR_MESSAGE">%s</xliff:g>); பிறகு முயலவும்"</string>
diff --git a/sip/res/values-te/strings.xml b/sip/res/values-te/strings.xml
index 9abb6f5..b15b514 100644
--- a/sip/res/values-te/strings.xml
+++ b/sip/res/values-te/strings.xml
@@ -47,7 +47,7 @@
     <string name="registration_status_failed_try_later" msgid="7855389184910312091">"ఖాతా నమోదు విఫలమైంది: (<xliff:g id="REGISTRATION_ERROR_MESSAGE">%s</xliff:g>); తర్వాత ప్రయత్నిస్తుంది"</string>
     <string name="registration_status_invalid_credentials" msgid="8896714049938660777">"ఖాతా నమోదు విఫలమైంది: చెల్లని వినియోగదారు పేరు లేదా పాస్‌వర్డ్."</string>
     <string name="registration_status_server_unreachable" msgid="3832339558868965604">"ఖాతా నమోదు విఫలమైంది: సర్వర్ పేరు తనిఖీ చేయండి."</string>
-    <string name="third_party_account_summary" msgid="5918779106950859167">"ఈ ఖాతా ప్రస్తుతం <xliff:g id="ACCOUNT_OWNER">%s</xliff:g> అనువర్తనం ద్వారా ఉపయోగంలో ఉంది."</string>
+    <string name="third_party_account_summary" msgid="5918779106950859167">"ఈ ఖాతా ప్రస్తుతం <xliff:g id="ACCOUNT_OWNER">%s</xliff:g> యాప్‌ ద్వారా ఉపయోగంలో ఉంది."</string>
     <string name="sip_edit_title" msgid="7438891546610820307">"SIP ఖాతా వివరాలు"</string>
     <string name="sip_edit_new_title" msgid="8394790068979636381">"SIP ఖాతా వివరాలు"</string>
     <string name="domain_address_title" msgid="8238078615181248579">"సర్వర్"</string>
diff --git a/sip/src/com/android/services/telephony/sip/SipAccountRegistry.java b/sip/src/com/android/services/telephony/sip/SipAccountRegistry.java
index 1cf7f4b..2845dac 100644
--- a/sip/src/com/android/services/telephony/sip/SipAccountRegistry.java
+++ b/sip/src/com/android/services/telephony/sip/SipAccountRegistry.java
@@ -16,8 +16,12 @@
 
 package com.android.services.telephony.sip;
 
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.Context;
-import android.net.sip.SipException;
+import android.content.Intent;
 import android.net.sip.SipManager;
 import android.net.sip.SipProfile;
 import android.telecom.PhoneAccount;
@@ -25,9 +29,13 @@
 import android.telecom.TelecomManager;
 import android.util.Log;
 
+import com.android.phone.R;
+
+import java.io.IOException;
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.stream.Collectors;
 
 /**
  * Manages the {@link PhoneAccount} entries for SIP calling.
@@ -45,41 +53,6 @@
         }
 
         /**
-         * Starts the SIP service associated with the SIP profile.
-         *
-         * @param sipManager The SIP manager.
-         * @param context The context.
-         * @param isReceivingCalls {@code True} if the sip service is being started to make and
-         *          receive calls.  {@code False} if the sip service is being started only for
-         *          outgoing calls.
-         * @return {@code True} if the service started successfully.
-         */
-        boolean startSipService(SipManager sipManager, Context context, boolean isReceivingCalls) {
-            if (VERBOSE) log("startSipService, profile: " + mProfile);
-            try {
-                // Stop the Sip service for the profile if it is already running.  This is important
-                // if we are changing the state of the "receive calls" option.
-                sipManager.close(mProfile.getUriString());
-
-                // Start the sip service for the profile.
-                if (isReceivingCalls) {
-                    sipManager.open(
-                            mProfile,
-                            SipUtil.createIncomingCallPendingIntent(context,
-                                    mProfile.getProfileName()),
-                            null);
-                } else {
-                    sipManager.open(mProfile);
-                }
-                return true;
-            } catch (SipException e) {
-                log("startSipService, profile: " + mProfile.getProfileName() +
-                        ", exception: " + e);
-            }
-            return false;
-        }
-
-        /**
          * Stops the SIP service associated with the SIP profile.  The {@code SipAccountRegistry} is
          * informed when the service has been stopped via an intent which triggers
          * {@link SipAccountRegistry#removeSipProfile(String)}.
@@ -102,9 +75,16 @@
     private static final String PREFIX = "[SipAccountRegistry] ";
     private static final boolean VERBOSE = false; /* STOP SHIP if true */
     private static final SipAccountRegistry INSTANCE = new SipAccountRegistry();
+    private static final String NOTIFICATION_TAG = SipAccountRegistry.class.getSimpleName();
+    private static final int SIP_ACCOUNTS_REMOVED_NOTIFICATION_ID = 1;
+
+    private static final String CHANNEL_ID_SIP_ACCOUNTS_REMOVED = "sipAccountsRemoved";
 
     private final List<AccountEntry> mAccounts = new CopyOnWriteArrayList<>();
 
+    private NotificationChannel mNotificationChannel;
+    private NotificationManager mNm;
+
     private SipAccountRegistry() {}
 
     public static SipAccountRegistry getInstance() {
@@ -115,8 +95,20 @@
      * Sets up the Account registry and performs any upgrade operations before it is used.
      */
     public void setup(Context context) {
+        setupNotificationChannel(context);
         verifyAndPurgeInvalidPhoneAccounts(context);
-        startSipProfilesAsync(context, (String) null, false);
+        startSipProfilesAsync(context);
+    }
+
+    private void setupNotificationChannel(Context context) {
+        mNotificationChannel = new NotificationChannel(
+                CHANNEL_ID_SIP_ACCOUNTS_REMOVED,
+                context.getText(R.string.notification_channel_sip_account),
+                NotificationManager.IMPORTANCE_HIGH);
+        mNm = context.getSystemService(NotificationManager.class);
+        if (mNm != null) {
+            mNm.createNotificationChannel(mNotificationChannel);
+        }
     }
 
     /**
@@ -149,8 +141,8 @@
      * @param sipProfileName The name of the {@link SipProfile} to start, or {@code null} for all.
      * @param enableProfile Sip account should be enabled
      */
-    void startSipService(Context context, String sipProfileName, boolean enableProfile) {
-        startSipProfilesAsync(context, sipProfileName, enableProfile);
+    void startSipService(Context context, String sipProfileName, boolean enabledProfile) {
+        startSipProfilesAsync(context);
     }
 
     /**
@@ -193,33 +185,20 @@
     }
 
     /**
-     * Causes the SIP service to be restarted for all {@link SipProfile}s.  For example, if the user
-     * toggles the "receive calls" option for SIP, this method handles restarting the SIP services
-     * in the new mode.
-     *
-     * @param context The context.
-     */
-    public void restartSipService(Context context) {
-        startSipProfiles(context, null, false);
-    }
-
-    /**
      * Performs an asynchronous call to
      * {@link SipAccountRegistry#startSipProfiles(android.content.Context, String)}, starting the
      * specified SIP profile and registering its {@link android.telecom.PhoneAccount}.
      *
      * @param context The context.
-     * @param sipProfileName Name of the SIP profile.
-     * @param enableProfile Sip account should be enabled.
      */
     private void startSipProfilesAsync(
-            final Context context, final String sipProfileName, final boolean enableProfile) {
+            final Context context) {
         if (VERBOSE) log("startSipProfiles, start auto registration");
 
         new Thread(new Runnable() {
             @Override
             public void run() {
-                startSipProfiles(context, sipProfileName, enableProfile);
+                startSipProfiles(context);
             }}
         ).start();
     }
@@ -230,48 +209,54 @@
      * register the associated SIP account.
      *
      * @param context The context.
-     * @param sipProfileName A specific SIP profile Name to start, or {@code null} to start all.
-     * @param enableProfile Sip account should be enabled.
      */
-    private void startSipProfiles(Context context, String sipProfileName, boolean enableProfile) {
-        final SipPreferences sipPreferences = new SipPreferences(context);
-        boolean isReceivingCalls = sipPreferences.isReceivingCallsEnabled();
-        TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
-        SipManager sipManager = SipManager.newInstance(context);
+    private void startSipProfiles(Context context) {
         SipProfileDb profileDb = new SipProfileDb(context);
         List<SipProfile> sipProfileList = profileDb.retrieveSipProfileList();
 
-        for (SipProfile profile : sipProfileList) {
-            // Register a PhoneAccount for the profile and optionally enable the primary
-            // profile.
-            if (sipProfileName == null || sipProfileName.equals(profile.getProfileName())) {
-                PhoneAccount phoneAccount = SipUtil.createPhoneAccount(context, profile);
-                telecomManager.registerPhoneAccount(phoneAccount);
-                if (enableProfile) {
-                    telecomManager.enablePhoneAccount(phoneAccount.getAccountHandle(), true);
+        // If there're SIP profiles existing in DB, display a notification and delete all these
+        // profiles.
+        if (!sipProfileList.isEmpty()) {
+            for (SipProfile profile : sipProfileList) {
+                stopSipService(context, profile.getProfileName());
+                removeSipProfile(profile.getProfileName());
+                try {
+                    profileDb.deleteProfile(profile);
+                } catch (IOException e) {
+                    // Ignore
                 }
-                startSipServiceForProfile(profile, sipManager, context, isReceivingCalls);
             }
+            sendSipAccountsRemovedNotification(context, sipProfileList);
         }
     }
 
-    /**
-     * Starts the SIP service for a sip profile and saves a new {@code AccountEntry} in the
-     * registry.
-     *
-     * @param profile The {@link SipProfile} to start.
-     * @param sipManager The SIP manager.
-     * @param context The context.
-     * @param isReceivingCalls {@code True} if the profile should be started such that it can
-     *      receive incoming calls.
-     */
-    private void startSipServiceForProfile(SipProfile profile, SipManager sipManager,
-            Context context, boolean isReceivingCalls) {
-        removeSipProfile(profile.getUriString());
+    private void sendSipAccountsRemovedNotification(Context context, List<SipProfile> profiles) {
+        String sipAccounts = profiles.stream().map(p -> p.getProfileName())
+                .collect(Collectors.joining(","));
 
-        AccountEntry entry = new AccountEntry(profile);
-        if (entry.startSipService(sipManager, context, isReceivingCalls)) {
-            mAccounts.add(entry);
+        Intent intent = new Intent(TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS);
+        intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
+
+        Notification.Action action = new Notification.Action.Builder(R.drawable.ic_sim_card,
+                context.getString(R.string.sip_accounts_removed_notification_action),
+                pendingIntent).build();
+        Notification.Builder builder = new Notification.Builder(context)
+                .setSmallIcon(R.drawable.ic_sim_card)
+                .setChannelId(CHANNEL_ID_SIP_ACCOUNTS_REMOVED)
+                .setContentTitle(context.getText(R.string.sip_accounts_removed_notification_title))
+                .setStyle(new Notification.BigTextStyle()
+                .bigText(context.getString(
+                        R.string.sip_accounts_removed_notification_message,
+                        sipAccounts)))
+                .setAutoCancel(true)
+                .addAction(action);
+        Notification notification = builder.build();
+        if (mNm != null) {
+            mNm.notify(NOTIFICATION_TAG, SIP_ACCOUNTS_REMOVED_NOTIFICATION_ID,
+                    notification);
+        } else {
+            log("NotificationManager is null when send the notification of removed SIP accounts");
         }
     }
 
diff --git a/sip/src/com/android/services/telephony/sip/SipPhoneAccountSettingsActivity.java b/sip/src/com/android/services/telephony/sip/SipPhoneAccountSettingsActivity.java
deleted file mode 100644
index a6f6381..0000000
--- a/sip/src/com/android/services/telephony/sip/SipPhoneAccountSettingsActivity.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2014 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.sip;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.net.sip.SipProfile;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.telecom.PhoneAccountHandle;
-import android.telecom.TelecomManager;
-import android.util.Log;
-
-/**
- * This activity receives the standard telecom intent to open settings for a PhoneAccount. It
- * translates the incoming phone account to a SIP profile and opens the corresponding
- * PreferenceActivity for said profile.
- */
-public final class SipPhoneAccountSettingsActivity extends Activity {
-    private static final String TAG = "SipSettingsActivity";
-
-    /** ${inheritDoc} */
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        Intent intent = getIntent();
-        Log.i(TAG, "" + intent);
-        if (intent != null) {
-            PhoneAccountHandle accountHandle = (PhoneAccountHandle)
-                    intent.getParcelableExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
-            Log.i(TAG, "" + accountHandle);
-
-            if (accountHandle != null) {
-                SipProfileDb profileDb = new SipProfileDb(this);
-                String profileName = SipUtil.getSipProfileNameFromPhoneAccount(accountHandle);
-                SipProfile profile = profileDb.retrieveSipProfileFromName(profileName);
-                if (profile != null) {
-                    Intent settingsIntent = new Intent(this, SipEditor.class);
-                    settingsIntent.putExtra(SipSettings.KEY_SIP_PROFILE, (Parcelable) profile);
-                    startActivity(settingsIntent);
-                }
-            }
-        }
-
-        finish();
-    }
-}
diff --git a/sip/src/com/android/services/telephony/sip/SipUtil.java b/sip/src/com/android/services/telephony/sip/SipUtil.java
index 5c7409f..a5793d0 100644
--- a/sip/src/com/android/services/telephony/sip/SipUtil.java
+++ b/sip/src/com/android/services/telephony/sip/SipUtil.java
@@ -63,7 +63,9 @@
         Intent intent = new Intent(context, SipIncomingCallReceiver.class);
         intent.setAction(SipManager.ACTION_SIP_INCOMING_CALL);
         intent.putExtra(EXTRA_PHONE_ACCOUNT, SipUtil.createAccountHandle(context, sipProfileName));
-        return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+        return PendingIntent.getBroadcast(context, 0, intent,
+                // Mutable because information associated with the call is passed back here.
+                PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
     }
 
     public static boolean isPhoneIdle(Context context) {
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index ef83ead..09f1072 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -30,6 +30,9 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
 import android.os.PersistableBundle;
 import android.os.UserManager;
 import android.preference.Preference;
@@ -40,8 +43,8 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
-import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 import android.telephony.ims.ProvisioningManager;
 import android.telephony.ims.feature.ImsFeature;
@@ -103,6 +106,7 @@
     private ImsManager mImsMgr;
     private SubscriptionInfoHelper mSubscriptionInfoHelper;
     private TelecomManager mTelecomManager;
+    private TelephonyCallback mTelephonyCallback;
 
     private SwitchPreference mButtonAutoRetry;
     private PreferenceScreen mVoicemailSettingsScreen;
@@ -263,6 +267,7 @@
         mSubscriptionInfoHelper.setActionBarTitle(
                 getActionBar(), getResourcesForSubId(), R.string.call_settings_with_label);
         mTelecomManager = getSystemService(TelecomManager.class);
+        mTelephonyCallback = new CallFeaturesTelephonyCallback();
     }
 
     private void updateImsManager(Phone phone) {
@@ -279,13 +284,18 @@
     private void listenPhoneState(boolean listen) {
         TelephonyManager telephonyManager = getSystemService(TelephonyManager.class)
                 .createForSubscriptionId(mPhone.getSubId());
-        telephonyManager.listen(mPhoneStateListener, listen
-                ? PhoneStateListener.LISTEN_CALL_STATE : PhoneStateListener.LISTEN_NONE);
+        if (listen) {
+            telephonyManager.registerTelephonyCallback(
+                    new HandlerExecutor(new Handler(Looper.getMainLooper())), mTelephonyCallback);
+        } else {
+            telephonyManager.unregisterTelephonyCallback(mTelephonyCallback);
+        }
     }
 
-    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+    private final class CallFeaturesTelephonyCallback extends TelephonyCallback implements
+            TelephonyCallback.CallStateListener {
         @Override
-        public void onCallStateChanged(int state, String incomingNumber) {
+        public void onCallStateChanged(int state) {
             if (DBG) log("PhoneStateListener onCallStateChanged: state is " + state);
             boolean isCallStateIdle = state == TelephonyManager.CALL_STATE_IDLE;
             if (mEnableVideoCalling != null) {
@@ -295,7 +305,7 @@
                 mButtonWifiCalling.setEnabled(isCallStateIdle);
             }
         }
-    };
+    }
 
     private final ProvisioningManager.Callback mProvisioningCallback =
             new ProvisioningManager.Callback() {
diff --git a/src/com/android/phone/CallNotifier.java b/src/com/android/phone/CallNotifier.java
index 6c18623..a8ef220 100644
--- a/src/com/android/phone/CallNotifier.java
+++ b/src/com/android/phone/CallNotifier.java
@@ -25,13 +25,14 @@
 import android.media.ToneGenerator;
 import android.os.AsyncResult;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.Message;
 import android.os.SystemProperties;
 import android.telecom.TelecomManager;
-import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -68,8 +69,8 @@
     /** The singleton instance. */
     private static CallNotifier sInstance;
 
-    private Map<Integer, CallNotifierPhoneStateListener> mPhoneStateListeners =
-            new ArrayMap<Integer, CallNotifierPhoneStateListener>();
+    private Map<Integer, CallNotifierTelephonyCallback> mTelephonyCallback =
+            new ArrayMap<Integer, CallNotifierTelephonyCallback>();
     private Map<Integer, Boolean> mCFIStatus = new ArrayMap<Integer, Boolean>();
     private Map<Integer, Boolean> mMWIStatus = new ArrayMap<Integer, Boolean>();
     private PhoneGlobals mApplication;
@@ -566,7 +567,7 @@
         // slot 0 first then slot 1. This is needed to ensure that when CFI or MWI is enabled for
         // both slots, user always sees icon related to slot 0 on left side followed by that of
         // slot 1.
-        List<Integer> subIdList = new ArrayList<Integer>(mPhoneStateListeners.keySet());
+        List<Integer> subIdList = new ArrayList<Integer>(mTelephonyCallback.keySet());
         Collections.sort(subIdList, new Comparator<Integer>() {
             public int compare(Integer sub1, Integer sub2) {
                 int slotId1 = SubscriptionController.getInstance().getSlotIndex(sub1);
@@ -583,10 +584,9 @@
                 mApplication.notificationMgr.updateMwi(subId, false);
                 mApplication.notificationMgr.updateCfi(subId, false);
 
-                // Listening to LISTEN_NONE removes the listener.
-                mTelephonyManager.listen(
-                        mPhoneStateListeners.get(subId), PhoneStateListener.LISTEN_NONE);
-                mPhoneStateListeners.remove(subId);
+                // Unregister the listener.
+                mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback.get(subId));
+                mTelephonyCallback.remove(subId);
             } else {
                 Log.d(LOG_TAG, "updatePhoneStateListeners: update CF notifications.");
 
@@ -616,12 +616,11 @@
         // Register new phone listeners for active subscriptions.
         for (int i = 0; i < subInfos.size(); i++) {
             int subId = subInfos.get(i).getSubscriptionId();
-            if (!mPhoneStateListeners.containsKey(subId)) {
-                CallNotifierPhoneStateListener listener = new CallNotifierPhoneStateListener(subId);
-                mTelephonyManager.createForSubscriptionId(subId).listen(listener,
-                        PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
-                        | PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR);
-                mPhoneStateListeners.put(subId, listener);
+            if (!mTelephonyCallback.containsKey(subId)) {
+                CallNotifierTelephonyCallback listener = new CallNotifierTelephonyCallback(subId);
+                mTelephonyManager.createForSubscriptionId(subId).registerTelephonyCallback(
+                        new HandlerExecutor(this), listener);
+                mTelephonyCallback.put(subId, listener);
             }
         }
     }
@@ -768,10 +767,13 @@
                 }
             };
 
-    private class CallNotifierPhoneStateListener extends PhoneStateListener {
+    private class CallNotifierTelephonyCallback extends TelephonyCallback implements
+            TelephonyCallback.MessageWaitingIndicatorListener,
+            TelephonyCallback.CallForwardingIndicatorListener {
+
         private final int mSubId;
 
-        CallNotifierPhoneStateListener(int subId) {
+        CallNotifierTelephonyCallback(int subId) {
             super();
             this.mSubId = subId;
         }
@@ -790,7 +792,7 @@
             mCFIStatus.put(this.mSubId, visible);
             updatePhoneStateListeners(false, UPDATE_TYPE_CFI, this.mSubId);
         }
-    };
+    }
 
     private void log(String msg) {
         Log.d(LOG_TAG, msg);
diff --git a/src/com/android/phone/CallWaitingSwitchPreference.java b/src/com/android/phone/CallWaitingSwitchPreference.java
index 41442fe..01dd3b2 100644
--- a/src/com/android/phone/CallWaitingSwitchPreference.java
+++ b/src/com/android/phone/CallWaitingSwitchPreference.java
@@ -1,18 +1,21 @@
 package com.android.phone;
 
+import static com.android.phone.TimeConsumingPreferenceActivity.EXCEPTION_ERROR;
 import static com.android.phone.TimeConsumingPreferenceActivity.RESPONSE_ERROR;
 
 import android.content.Context;
-import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
 import android.preference.SwitchPreference;
+import android.telephony.TelephonyManager;
 import android.util.AttributeSet;
 import android.util.Log;
 
-import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.Phone;
 
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
 public class CallWaitingSwitchPreference extends SwitchPreference {
     private static final String LOG_TAG = "CallWaitingSwitchPreference";
     private final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
@@ -20,6 +23,11 @@
     private final MyHandler mHandler = new MyHandler();
     private Phone mPhone;
     private TimeConsumingPreferenceListener mTcpListener;
+    private Executor mExecutor;
+    private TelephonyManager mTelephonyManager;
+    private boolean mIsDuringUpdateProcess = false;
+    private int mUpdateStatus = TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR;
+    private int mQueryStatus = TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR;
 
     public CallWaitingSwitchPreference(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
@@ -37,101 +45,84 @@
             TimeConsumingPreferenceListener listener, boolean skipReading, Phone phone) {
         mPhone = phone;
         mTcpListener = listener;
+        mExecutor = Executors.newSingleThreadExecutor();
+        mTelephonyManager = getContext().getSystemService(
+                TelephonyManager.class).createForSubscriptionId(phone.getSubId());
 
         if (!skipReading) {
-            mPhone.getCallWaiting(mHandler.obtainMessage(MyHandler.MESSAGE_GET_CALL_WAITING,
-                    MyHandler.MESSAGE_GET_CALL_WAITING, MyHandler.MESSAGE_GET_CALL_WAITING));
+            Log.d(LOG_TAG, "init getCallWaitingStatus");
+            mTelephonyManager.getCallWaitingStatus(mExecutor, this::queryStatusCallBack);
             if (mTcpListener != null) {
                 mTcpListener.onStarted(this, true);
             }
         }
     }
 
+    private void queryStatusCallBack(int result) {
+        Log.d(LOG_TAG, "queryStatusCallBack: CW state " + result);
+        mQueryStatus = result;
+        mHandler.sendMessage(mHandler.obtainMessage(MyHandler.MESSAGE_UPDATE_CALL_WAITING));
+    }
+
+    private void updateStatusCallBack(int result) {
+        Log.d(LOG_TAG, "updateStatusCallBack: CW state " + result + ", and re get");
+        mUpdateStatus = result;
+        mTelephonyManager.getCallWaitingStatus(mExecutor, this::queryStatusCallBack);
+    }
+
     @Override
     protected void onClick() {
         super.onClick();
-
-        mPhone.setCallWaiting(isChecked(),
-                mHandler.obtainMessage(MyHandler.MESSAGE_SET_CALL_WAITING));
+        mTelephonyManager.setCallWaitingEnabled(isChecked(), mExecutor, this::updateStatusCallBack);
         if (mTcpListener != null) {
+            mIsDuringUpdateProcess = true;
             mTcpListener.onStarted(this, false);
         }
     }
 
     private class MyHandler extends Handler {
-        static final int MESSAGE_GET_CALL_WAITING = 0;
-        static final int MESSAGE_SET_CALL_WAITING = 1;
+        static final int MESSAGE_UPDATE_CALL_WAITING = 0;
 
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case MESSAGE_GET_CALL_WAITING:
-                    handleGetCallWaitingResponse(msg);
-                    break;
-                case MESSAGE_SET_CALL_WAITING:
-                    handleSetCallWaitingResponse(msg);
+                case MESSAGE_UPDATE_CALL_WAITING:
+                    updateUi();
                     break;
             }
         }
 
-        private void handleGetCallWaitingResponse(Message msg) {
-            AsyncResult ar = (AsyncResult) msg.obj;
-
+        private void updateUi() {
             if (mTcpListener != null) {
-                if (msg.arg2 == MESSAGE_SET_CALL_WAITING) {
+                if (mIsDuringUpdateProcess) {
                     mTcpListener.onFinished(CallWaitingSwitchPreference.this, false);
                 } else {
                     mTcpListener.onFinished(CallWaitingSwitchPreference.this, true);
                 }
             }
 
-            if (ar.exception instanceof CommandException) {
-                if (DBG) {
-                    Log.d(LOG_TAG, "handleGetCallWaitingResponse: CommandException=" +
-                            ar.exception);
-                }
+            if (mIsDuringUpdateProcess && (
+                    mUpdateStatus == TelephonyManager.CALL_WAITING_STATUS_NOT_SUPPORTED
+                            || mUpdateStatus
+                            == TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR)) {
+                Log.d(LOG_TAG, "handleSetCallWaitingResponse: Exception");
                 if (mTcpListener != null) {
-                    mTcpListener.onException(CallWaitingSwitchPreference.this,
-                            (CommandException)ar.exception);
+                    mTcpListener.onError(CallWaitingSwitchPreference.this, EXCEPTION_ERROR);
                 }
-            } else if (ar.userObj instanceof Throwable || ar.exception != null) {
-                // Still an error case but just not a CommandException.
-                if (DBG) {
-                    Log.d(LOG_TAG, "handleGetCallWaitingResponse: Exception" + ar.exception);
-                }
+            } else if (mQueryStatus == TelephonyManager.CALL_WAITING_STATUS_NOT_SUPPORTED
+                    || mQueryStatus == TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR) {
+                Log.d(LOG_TAG, "handleGetCallWaitingResponse: Exception");
                 if (mTcpListener != null) {
                     mTcpListener.onError(CallWaitingSwitchPreference.this, RESPONSE_ERROR);
                 }
             } else {
-                if (DBG) {
-                    Log.d(LOG_TAG, "handleGetCallWaitingResponse: CW state successfully queried.");
-                }
-                int[] cwArray = (int[])ar.result;
-                // If cwArray[0] is = 1, then cwArray[1] must follow,
-                // with the TS 27.007 service class bit vector of services
-                // for which call waiting is enabled.
-                try {
-                    setChecked(((cwArray[0] == 1) && ((cwArray[1] & 0x01) == 0x01)));
-                } catch (ArrayIndexOutOfBoundsException e) {
-                    Log.e(LOG_TAG, "handleGetCallWaitingResponse: improper result: err ="
-                            + e.getMessage());
+                if (mQueryStatus == TelephonyManager.CALL_WAITING_STATUS_ENABLED) {
+                    setChecked(true);
+                } else {
+                    setChecked(false);
                 }
             }
-        }
-
-        private void handleSetCallWaitingResponse(Message msg) {
-            AsyncResult ar = (AsyncResult) msg.obj;
-
-            if (ar.exception != null) {
-                if (DBG) {
-                    Log.d(LOG_TAG, "handleSetCallWaitingResponse: ar.exception=" + ar.exception);
-                }
-                //setEnabled(false);
-            }
-            if (DBG) Log.d(LOG_TAG, "handleSetCallWaitingResponse: re get");
-
-            mPhone.getCallWaiting(obtainMessage(MESSAGE_GET_CALL_WAITING,
-                    MESSAGE_SET_CALL_WAITING, MESSAGE_SET_CALL_WAITING, ar.exception));
+            mIsDuringUpdateProcess = false;
         }
     }
 }
diff --git a/src/com/android/phone/CallWaitingUssdResultReceiver.java b/src/com/android/phone/CallWaitingUssdResultReceiver.java
new file mode 100644
index 0000000..b9049e9
--- /dev/null
+++ b/src/com/android/phone/CallWaitingUssdResultReceiver.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2021 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.phone;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.telephony.TelephonyManager;
+import android.telephony.UssdResponse;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.telephony.IIntegerConsumer;
+
+import java.util.HashMap;
+
+/**
+ * Handling the call waiting USSD result.
+ */
+public class CallWaitingUssdResultReceiver extends ResultReceiver {
+    private static final String LOG_TAG = "CwUssdResultReceiver";
+
+    private IIntegerConsumer mCallback;
+    private CarrierXmlParser mCarrierXmlParser;
+    private CarrierXmlParser.SsEntry.SSAction mSsAction;
+
+    CallWaitingUssdResultReceiver(Handler handler, IIntegerConsumer callback,
+            CarrierXmlParser carrierXmlParser, CarrierXmlParser.SsEntry.SSAction action) {
+        super(handler);
+        mCallback = callback;
+        mCarrierXmlParser = carrierXmlParser;
+        mSsAction = action;
+    }
+
+    @Override
+    protected void onReceiveResult(int resultCode, Bundle ussdResponse) {
+        log("USSD:" + resultCode);
+        checkNotNull(ussdResponse, "ussdResponse cannot be null.");
+        UssdResponse response = ussdResponse.getParcelable(
+                TelephonyManager.USSD_RESPONSE);
+
+        if (resultCode == TelephonyManager.USSD_RETURN_SUCCESS) {
+            int callWaitingStatus = getStatusFromResponse(response);
+            try {
+                mCallback.accept(callWaitingStatus);
+            } catch (RemoteException e) {
+                log("Fail to notify getCallWaitingStatus due to " + e);
+            }
+        } else {
+            try {
+                mCallback.accept(TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR);
+            } catch (RemoteException e) {
+                log("Fail to notify getCallWaitingStatus due to " + e);
+            }
+        }
+    }
+
+    private int getStatusFromResponse(UssdResponse response) {
+        int callWaitingStatus = TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR;
+
+        CarrierXmlParser.SsFeature callWaitingFeature = mCarrierXmlParser.getFeature(
+                CarrierXmlParser.FEATURE_CALL_WAITING);
+        if (callWaitingFeature == null) {
+            return callWaitingStatus;
+        }
+
+        HashMap<String, String> analysisResult = callWaitingFeature
+                .getResponseSet(mSsAction, response.getReturnMessage().toString());
+        if (analysisResult.get(CarrierXmlParser.TAG_RESPONSE_STATUS_ERROR) != null) {
+            return callWaitingStatus;
+        }
+
+        if (analysisResult != null && analysisResult.size() != 0) {
+            String tmpStatusStr = analysisResult.get(
+                    CarrierXmlParser.TAG_RESPONSE_STATUS);
+
+            if (!TextUtils.isEmpty(tmpStatusStr)) {
+                if (tmpStatusStr.equals(
+                        CarrierXmlParser.TAG_COMMAND_RESULT_DEFINITION_ACTIVATE)) {
+                    callWaitingStatus =
+                            TelephonyManager.CALL_WAITING_STATUS_ENABLED;
+                } else if (tmpStatusStr.equals(
+                        CarrierXmlParser.TAG_COMMAND_RESULT_DEFINITION_DEACTIVATE)) {
+                    callWaitingStatus =
+                            TelephonyManager.CALL_WAITING_STATUS_DISABLED;
+                }
+            }
+        }
+        return callWaitingStatus;
+    }
+
+    private static void log(String msg) {
+        Log.d(LOG_TAG, msg);
+    }
+}
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 37a0618..a463243 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -36,6 +36,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.Process;
@@ -55,6 +56,7 @@
 import android.util.LocalLog;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.ICarrierConfigLoader;
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.Phone;
@@ -166,6 +168,7 @@
     private static final int EVENT_BIND_DEFAULT_FOR_NO_SIM_CONFIG_TIMEOUT = 21;
     // Fetching config timed out from the default app for no SIM config.
     private static final int EVENT_FETCH_DEFAULT_FOR_NO_SIM_CONFIG_TIMEOUT = 22;
+    // NOTE: any new EVENT_* values must be added to method eventToString().
 
     private static final int BIND_TIMEOUT_MILLIS = 30000;
 
@@ -201,10 +204,14 @@
     // 3. clearing config (e.g. due to sim removal)
     // 4. encountering bind or IPC error
     private class ConfigHandler extends Handler {
+        ConfigHandler(@NonNull Looper looper) {
+            super(looper);
+        }
+
         @Override
         public void handleMessage(Message msg) {
             final int phoneId = msg.arg1;
-            logdWithLocalLog("mHandler: " + msg.what + " phoneId: " + phoneId);
+            logdWithLocalLog("mHandler: " + eventToString(msg.what) + " phoneId: " + phoneId);
             if (!SubscriptionManager.isValidPhoneId(phoneId)
                     && msg.what != EVENT_MULTI_SIM_CONFIG_CHANGED) {
                 return;
@@ -247,6 +254,12 @@
                 }
 
                 case EVENT_DO_FETCH_DEFAULT: {
+                    // Clear in-memory cache for carrier app config, so when carrier app gets
+                    // uninstalled, no stale config is left.
+                    if (mConfigFromCarrierApp[phoneId] != null
+                            && getCarrierPackageForPhoneId(phoneId) == null) {
+                        mConfigFromCarrierApp[phoneId] = null;
+                    }
                     // Restore persistent override values.
                     PersistableBundle config = restoreConfigFromXml(
                             mPlatformCarrierConfigPackage, OVERRIDE_PACKAGE_ADDITION, phoneId);
@@ -275,9 +288,13 @@
                                 phoneId,
                                 EVENT_CONNECTED_TO_DEFAULT)) {
                             sendMessageDelayed(
-                                    obtainMessage(EVENT_BIND_DEFAULT_TIMEOUT, phoneId, -1),
+                                    obtainMessage(EVENT_BIND_DEFAULT_TIMEOUT, phoneId, -1 /*arg2*/,
+                                            getMessageToken(phoneId)),
                                     BIND_TIMEOUT_MILLIS);
                         } else {
+                            // Put a stub bundle in place so that the rest of the logic continues
+                            // smoothly.
+                            mConfigFromDefaultApp[phoneId] = new PersistableBundle();
                             // Send broadcast if bind fails.
                             notifySubscriptionInfoUpdater(phoneId);
                             // TODO: We *must* call unbindService even if bindService returns false.
@@ -290,7 +307,7 @@
                 }
 
                 case EVENT_CONNECTED_TO_DEFAULT: {
-                    removeMessages(EVENT_BIND_DEFAULT_TIMEOUT);
+                    removeMessages(EVENT_BIND_DEFAULT_TIMEOUT, getMessageToken(phoneId));
                     final CarrierServiceConnection conn = (CarrierServiceConnection) msg.obj;
                     // If new service connection has been created, unbind.
                     if (mServiceConnection[phoneId] != conn || conn.service == null) {
@@ -304,12 +321,13 @@
                                 @Override
                                 public void onReceiveResult(int resultCode, Bundle resultData) {
                                     unbindIfBound(mContext, conn, phoneId);
+                                    removeMessages(EVENT_FETCH_DEFAULT_TIMEOUT,
+                                            getMessageToken(phoneId));
                                     // If new service connection has been created, this is stale.
                                     if (mServiceConnection[phoneId] != conn) {
                                         loge("Received response for stale request.");
                                         return;
                                     }
-                                    removeMessages(EVENT_FETCH_DEFAULT_TIMEOUT);
                                     if (resultCode == RESULT_ERROR || resultData == null) {
                                         // On error, abort config fetching.
                                         loge("Failed to get carrier config");
@@ -341,7 +359,8 @@
                         break; // So we don't set a timeout.
                     }
                     sendMessageDelayed(
-                            obtainMessage(EVENT_FETCH_DEFAULT_TIMEOUT, phoneId, -1),
+                            obtainMessage(EVENT_FETCH_DEFAULT_TIMEOUT, phoneId, -1 /*arg2*/,
+                                    getMessageToken(phoneId)),
                             BIND_TIMEOUT_MILLIS);
                     break;
                 }
@@ -349,7 +368,7 @@
                 case EVENT_BIND_DEFAULT_TIMEOUT:
                 case EVENT_FETCH_DEFAULT_TIMEOUT: {
                     loge("Bind/fetch time out from " + mPlatformCarrierConfigPackage);
-                    removeMessages(EVENT_FETCH_DEFAULT_TIMEOUT);
+                    removeMessages(EVENT_FETCH_DEFAULT_TIMEOUT, getMessageToken(phoneId));
                     // If we attempted to bind to the app, but the service connection is null due to
                     // the race condition that clear config event happens before bind/fetch complete
                     // then config was cleared while we were waiting and we should not continue.
@@ -359,6 +378,8 @@
                         unbindIfBound(mContext, mServiceConnection[phoneId], phoneId);
                         broadcastConfigChangedIntent(phoneId);
                     }
+                    // Put a stub bundle in place so that the rest of the logic continues smoothly.
+                    mConfigFromDefaultApp[phoneId] = new PersistableBundle();
                     notifySubscriptionInfoUpdater(phoneId);
                     break;
                 }
@@ -399,9 +420,13 @@
                         if (carrierPackageName != null && bindToConfigPackage(carrierPackageName,
                                 phoneId, EVENT_CONNECTED_TO_CARRIER)) {
                             sendMessageDelayed(
-                                    obtainMessage(EVENT_BIND_CARRIER_TIMEOUT, phoneId, -1),
+                                    obtainMessage(EVENT_BIND_CARRIER_TIMEOUT, phoneId, -1 /*arg2*/,
+                                            getMessageToken(phoneId)),
                                     BIND_TIMEOUT_MILLIS);
                         } else {
+                            // Put a stub bundle in place so that the rest of the logic continues
+                            // smoothly.
+                            mConfigFromCarrierApp[phoneId] = new PersistableBundle();
                             // Send broadcast if bind fails.
                             broadcastConfigChangedIntent(phoneId);
                             loge("Bind to carrier app: " + carrierPackageName + " fails");
@@ -412,7 +437,7 @@
                 }
 
                 case EVENT_CONNECTED_TO_CARRIER: {
-                    removeMessages(EVENT_BIND_CARRIER_TIMEOUT);
+                    removeMessages(EVENT_BIND_CARRIER_TIMEOUT, getMessageToken(phoneId));
                     final CarrierServiceConnection conn = (CarrierServiceConnection) msg.obj;
                     // If new service connection has been created, unbind.
                     if (mServiceConnection[phoneId] != conn || conn.service == null) {
@@ -426,12 +451,13 @@
                                 @Override
                                 public void onReceiveResult(int resultCode, Bundle resultData) {
                                     unbindIfBound(mContext, conn, phoneId);
+                                    removeMessages(EVENT_FETCH_CARRIER_TIMEOUT,
+                                            getMessageToken(phoneId));
                                     // If new service connection has been created, this is stale.
                                     if (mServiceConnection[phoneId] != conn) {
                                         loge("Received response for stale request.");
                                         return;
                                     }
-                                    removeMessages(EVENT_FETCH_CARRIER_TIMEOUT);
                                     if (resultCode == RESULT_ERROR || resultData == null) {
                                         // On error, abort config fetching.
                                         loge("Failed to get carrier config from carrier app: "
@@ -444,7 +470,15 @@
                                             resultData.getParcelable(KEY_CONFIG_BUNDLE);
                                     saveConfigToXml(getCarrierPackageForPhoneId(phoneId), "",
                                             phoneId, carrierId, config);
-                                    mConfigFromCarrierApp[phoneId] = config;
+                                    if (config != null) {
+                                        mConfigFromCarrierApp[phoneId] = config;
+                                    } else {
+                                        logdWithLocalLog("Config from carrier app is null "
+                                                + "for phoneId " + phoneId);
+                                        // Put a stub bundle in place so that the rest of the logic
+                                        // continues smoothly.
+                                        mConfigFromCarrierApp[phoneId] = new PersistableBundle();
+                                    }
                                     sendMessage(
                                             obtainMessage(
                                                     EVENT_FETCH_CARRIER_DONE, phoneId, -1));
@@ -464,15 +498,17 @@
                         break; // So we don't set a timeout.
                     }
                     sendMessageDelayed(
-                            obtainMessage(EVENT_FETCH_CARRIER_TIMEOUT, phoneId, -1),
+                            obtainMessage(EVENT_FETCH_CARRIER_TIMEOUT, phoneId, -1 /*arg2*/,
+                                    getMessageToken(phoneId)),
                             BIND_TIMEOUT_MILLIS);
                     break;
                 }
 
                 case EVENT_BIND_CARRIER_TIMEOUT:
                 case EVENT_FETCH_CARRIER_TIMEOUT: {
-                    loge("Bind/fetch from carrier app timeout");
-                    removeMessages(EVENT_FETCH_CARRIER_TIMEOUT);
+                    loge("Bind/fetch from carrier app timeout, package="
+                            + getCarrierPackageForPhoneId(phoneId));
+                    removeMessages(EVENT_FETCH_CARRIER_TIMEOUT, getMessageToken(phoneId));
                     // If we attempted to bind to the app, but the service connection is null due to
                     // the race condition that clear config event happens before bind/fetch complete
                     // then config was cleared while we were waiting and we should not continue.
@@ -482,6 +518,8 @@
                         unbindIfBound(mContext, mServiceConnection[phoneId], phoneId);
                         broadcastConfigChangedIntent(phoneId);
                     }
+                    // Put a stub bundle in place so that the rest of the logic continues smoothly.
+                    mConfigFromCarrierApp[phoneId] = new PersistableBundle();
                     notifySubscriptionInfoUpdater(phoneId);
                     break;
                 }
@@ -643,11 +681,13 @@
      * Constructs a CarrierConfigLoader, registers it as a service, and registers a broadcast
      * receiver for relevant events.
      */
-    private CarrierConfigLoader(Context context) {
+    @VisibleForTesting
+    /* package */ CarrierConfigLoader(Context context,
+            SubscriptionInfoUpdater subscriptionInfoUpdater, @NonNull Looper looper) {
         mContext = context;
         mPlatformCarrierConfigPackage =
                 mContext.getString(R.string.platform_carrier_config_package);
-        mHandler = new ConfigHandler();
+        mHandler = new ConfigHandler(looper);
 
         IntentFilter bootFilter = new IntentFilter();
         bootFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
@@ -674,11 +714,8 @@
         mFromSystemUnlocked = new boolean[numPhones];
         mServiceConnectionForNoSimConfig = new CarrierServiceConnection[numPhones];
         mServiceBoundForNoSimConfig = new boolean[numPhones];
-        // Make this service available through ServiceManager.
-        TelephonyFrameworkInitializer
-                .getTelephonyServiceManager().getCarrierConfigServiceRegisterer().register(this);
         logd("CarrierConfigLoader has started");
-        mSubscriptionInfoUpdater = PhoneFactory.getSubscriptionInfoUpdater();
+        mSubscriptionInfoUpdater = subscriptionInfoUpdater;
         mHandler.sendEmptyMessage(EVENT_CHECK_SYSTEM_UPDATE);
     }
 
@@ -687,11 +724,14 @@
      *
      * This is only done once, at startup, from {@link com.android.phone.PhoneApp#onCreate}.
      */
-    /* package */
-    static CarrierConfigLoader init(Context context) {
+    /* package */ static CarrierConfigLoader init(Context context) {
         synchronized (CarrierConfigLoader.class) {
             if (sInstance == null) {
-                sInstance = new CarrierConfigLoader(context);
+                sInstance = new CarrierConfigLoader(context,
+                        PhoneFactory.getSubscriptionInfoUpdater(), Looper.myLooper());
+                // Make this service available through ServiceManager.
+                TelephonyFrameworkInitializer.getTelephonyServiceManager()
+                        .getCarrierConfigServiceRegisterer().register(sInstance);
             } else {
                 Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
             }
@@ -699,7 +739,8 @@
         }
     }
 
-    private void clearConfigForPhone(int phoneId, boolean fetchNoSimConfig) {
+    @VisibleForTesting
+    /* package */ void clearConfigForPhone(int phoneId, boolean fetchNoSimConfig) {
         /* Ignore clear configuration request if device is being shutdown. */
         Phone phone = PhoneFactory.getPhone(phoneId);
         if (phone != null) {
@@ -821,7 +862,8 @@
         }
     }
 
-    private CarrierIdentifier getCarrierIdentifierForPhoneId(int phoneId) {
+    @VisibleForTesting
+    /* package */ CarrierIdentifier getCarrierIdentifierForPhoneId(int phoneId) {
         String mcc = "";
         String mnc = "";
         String imsi = "";
@@ -848,10 +890,17 @@
     }
 
     /** Returns the package name of a priveleged carrier app, or null if there is none. */
+    @Nullable
     private String getCarrierPackageForPhoneId(int phoneId) {
-        List<String> carrierPackageNames = TelephonyManager.from(mContext)
+        List<String> carrierPackageNames;
+        final long token = Binder.clearCallingIdentity();
+        try {
+            carrierPackageNames = TelephonyManager.from(mContext)
                 .getCarrierPackageNamesForIntentAndPhone(
-                        new Intent(CarrierService.CARRIER_SERVICE_INTERFACE), phoneId);
+                    new Intent(CarrierService.CARRIER_SERVICE_INTERFACE), phoneId);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
         if (carrierPackageNames != null && carrierPackageNames.size() > 0) {
             return carrierPackageNames.get(0);
         } else {
@@ -976,12 +1025,14 @@
         }
     }
 
-    private void saveConfigToXml(String packageName, @NonNull String extraString, int phoneId,
+    @VisibleForTesting
+    /* package */ void saveConfigToXml(String packageName, @NonNull String extraString, int phoneId,
             CarrierIdentifier carrierId, PersistableBundle config) {
         saveConfigToXml(packageName, extraString, phoneId, carrierId, config, false);
     }
 
-    private void saveNoSimConfigToXml(String packageName, PersistableBundle config) {
+    @VisibleForTesting
+    /* package */ void saveNoSimConfigToXml(String packageName, PersistableBundle config) {
         saveConfigToXml(packageName, "", -1, null, config, true);
     }
 
@@ -1130,12 +1181,6 @@
      * have a saved config file to use instead.
      */
     private void updateConfigForPhoneId(int phoneId) {
-        // Clear in-memory cache for carrier app config, so when carrier app gets uninstalled, no
-        // stale config is left.
-        if (mConfigFromCarrierApp[phoneId] != null &&
-                getCarrierPackageForPhoneId(phoneId) == null) {
-            mConfigFromCarrierApp[phoneId] = null;
-        }
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_DO_FETCH_DEFAULT, phoneId, -1));
     }
 
@@ -1148,44 +1193,46 @@
 
     @Override
     @NonNull
-    public PersistableBundle getConfigForSubId(int subId, String callingPackage) {
-        return getConfigForSubIdWithFeature(subId, callingPackage, null);
+    public PersistableBundle getConfigForSubId(int subscriptionId, String callingPackage) {
+        return getConfigForSubIdWithFeature(subscriptionId, callingPackage, null);
     }
 
     @Override
     @NonNull
-    public PersistableBundle getConfigForSubIdWithFeature(int subId, String callingPackage,
+    public PersistableBundle getConfigForSubIdWithFeature(int subscriptionId, String callingPackage,
             String callingFeatureId) {
-        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId, callingPackage,
-                callingFeatureId, "getCarrierConfig")) {
+        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subscriptionId,
+                callingPackage, callingFeatureId, "getCarrierConfig")) {
             return new PersistableBundle();
         }
 
-        int phoneId = SubscriptionManager.getPhoneId(subId);
+        int phoneId = SubscriptionManager.getPhoneId(subscriptionId);
         PersistableBundle retConfig = CarrierConfigManager.getDefaultConfig();
         if (SubscriptionManager.isValidPhoneId(phoneId)) {
             PersistableBundle config = mConfigFromDefaultApp[phoneId];
             if (config != null) {
                 retConfig.putAll(config);
-                if (getCarrierPackageForPhoneId(phoneId) == null) {
-                    retConfig.putBoolean(
-                            CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
-                }
             }
             config = mConfigFromCarrierApp[phoneId];
             if (config != null) {
                 retConfig.putAll(config);
-                retConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
             }
             config = mPersistentOverrideConfigs[phoneId];
             if (config != null) {
                 retConfig.putAll(config);
-                retConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
             }
             config = mOverrideConfigs[phoneId];
             if (config != null) {
                 retConfig.putAll(config);
             }
+            // Ignore the theoretical case of the default app not being present since that won't
+            // work in CarrierConfigLoader today.
+            final boolean allConfigsApplied =
+                    (mConfigFromCarrierApp[phoneId] != null
+                        || getCarrierPackageForPhoneId(phoneId) == null)
+                    && mConfigFromDefaultApp[phoneId] != null;
+            retConfig.putBoolean(
+                    CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, allConfigsApplied);
         } else {
             if (mNoSimConfig != null) {
                 retConfig.putAll(mNoSimConfig);
@@ -1203,27 +1250,32 @@
         int phoneId = SubscriptionManager.getPhoneId(subscriptionId);
         if (!SubscriptionManager.isValidPhoneId(phoneId)) {
             logd("Ignore invalid phoneId: " + phoneId + " for subId: " + subscriptionId);
-            return;
+            throw new IllegalArgumentException(
+                    "Invalid phoneId " + phoneId + " for subId " + subscriptionId);
         }
-        overrideConfig(mOverrideConfigs, phoneId, overrides);
+        // Post to run on handler thread on which all states should be confined.
+        mHandler.post(() -> {
+            overrideConfig(mOverrideConfigs, phoneId, overrides);
 
-        if (persistent) {
-            overrideConfig(mPersistentOverrideConfigs, phoneId, overrides);
+            if (persistent) {
+                overrideConfig(mPersistentOverrideConfigs, phoneId, overrides);
 
-            if (overrides != null) {
-                final CarrierIdentifier carrierId = getCarrierIdentifierForPhoneId(phoneId);
-                saveConfigToXml(mPlatformCarrierConfigPackage, OVERRIDE_PACKAGE_ADDITION, phoneId,
-                        carrierId, mPersistentOverrideConfigs[phoneId]);
-            } else {
-                final String iccid = getIccIdForPhoneId(phoneId);
-                final int cid = getSpecificCarrierIdForPhoneId(phoneId);
-                String fileName = getFilenameForConfig(mPlatformCarrierConfigPackage,
-                        OVERRIDE_PACKAGE_ADDITION, iccid, cid);
-                File fileToDelete = new File(mContext.getFilesDir(), fileName);
-                fileToDelete.delete();
+                if (overrides != null) {
+                    final CarrierIdentifier carrierId = getCarrierIdentifierForPhoneId(phoneId);
+                    saveConfigToXml(mPlatformCarrierConfigPackage, OVERRIDE_PACKAGE_ADDITION,
+                            phoneId,
+                            carrierId, mPersistentOverrideConfigs[phoneId]);
+                } else {
+                    final String iccid = getIccIdForPhoneId(phoneId);
+                    final int cid = getSpecificCarrierIdForPhoneId(phoneId);
+                    String fileName = getFilenameForConfig(mPlatformCarrierConfigPackage,
+                            OVERRIDE_PACKAGE_ADDITION, iccid, cid);
+                    File fileToDelete = new File(mContext.getFilesDir(), fileName);
+                    fileToDelete.delete();
+                }
             }
-        }
-        notifySubscriptionInfoUpdater(phoneId);
+            notifySubscriptionInfoUpdater(phoneId);
+        });
     }
 
     private void overrideConfig(@NonNull PersistableBundle[] currentOverrides, int phoneId,
@@ -1238,17 +1290,18 @@
     }
 
     @Override
-    public void notifyConfigChangedForSubId(int subId) {
-        int phoneId = SubscriptionManager.getPhoneId(subId);
-        if (!SubscriptionManager.isValidPhoneId(phoneId)) {
-            logd("Ignore invalid phoneId: " + phoneId + " for subId: " + subId);
-            return;
-        }
-
+    public void notifyConfigChangedForSubId(int subscriptionId) {
         // Requires the calling app to be either a carrier privileged app for this subId or
         // system privileged app with MODIFY_PHONE_STATE permission.
-        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mContext, subId,
-                "Require carrier privileges or MODIFY_PHONE_STATE permission.");
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mContext,
+                subscriptionId, "Require carrier privileges or MODIFY_PHONE_STATE permission.");
+
+        int phoneId = SubscriptionManager.getPhoneId(subscriptionId);
+        if (!SubscriptionManager.isValidPhoneId(phoneId)) {
+            logd("Ignore invalid phoneId: " + phoneId + " for subId: " + subscriptionId);
+            throw new IllegalArgumentException(
+                    "Invalid phoneId " + phoneId + " for subId " + subscriptionId);
+        }
 
         // This method should block until deleting has completed, so that an error which prevents us
         // from clearing the cache is passed back to the carrier app. With the files successfully
@@ -1265,7 +1318,7 @@
                 android.Manifest.permission.MODIFY_PHONE_STATE, null);
         logdWithLocalLog("Update config for phoneId: " + phoneId + " simState: " + simState);
         if (!SubscriptionManager.isValidPhoneId(phoneId)) {
-            return;
+            throw new IllegalArgumentException("Invalid phoneId: " + phoneId);
         }
         // requires Java 7 for switch on string.
         switch (simState) {
@@ -1291,6 +1344,31 @@
         return mPlatformCarrierConfigPackage;
     }
 
+    @VisibleForTesting
+    /* package */ Handler getHandler() {
+        return mHandler;
+    }
+
+    @VisibleForTesting
+    /* package */ PersistableBundle getConfigFromDefaultApp(int phoneId) {
+        return mConfigFromDefaultApp[phoneId];
+    }
+
+    @VisibleForTesting
+    /* package */ PersistableBundle getConfigFromCarrierApp(int phoneId) {
+        return mConfigFromCarrierApp[phoneId];
+    }
+
+    @VisibleForTesting
+     /* package */ PersistableBundle getNoSimConfig() {
+        return mNoSimConfig;
+    }
+
+    @VisibleForTesting
+    /* package */ PersistableBundle getOverrideConfig(int phoneId) {
+        return mOverrideConfigs[phoneId];
+    }
+
     private void unbindIfBound(Context context, CarrierServiceConnection conn,
             int phoneId) {
         if (mServiceBound[phoneId]) {
@@ -1308,6 +1386,19 @@
     }
 
     /**
+     * Returns a boxed Integer object for phoneId, services as message token to distinguish messages
+     * with same code when calling {@link Handler#removeMessages(int, Object)}.
+     */
+    private Integer getMessageToken(int phoneId) {
+        if (phoneId < -128 || phoneId > 127) {
+            throw new IllegalArgumentException("phoneId should be in range [-128, 127], inclusive");
+        }
+        // Integer#valueOf guarantees the integers within [-128, 127] are cached and thus memory
+        // comparison (==) returns true for the same integer.
+        return Integer.valueOf(phoneId);
+    }
+
+    /**
      * If {@code args} contains {@link #DUMP_ARG_REQUESTING_PACKAGE} and a following package name,
      * we'll also call {@link IBinder#dump} on the default carrier service (if bound) and the
      * specified carrier service (if bound). Typically, this is done for connectivity bug reports
@@ -1577,6 +1668,56 @@
         }
     }
 
+    // Get readable string for the message code supported in this class.
+    private static String eventToString(int code) {
+        switch (code) {
+            case EVENT_CLEAR_CONFIG:
+                return "EVENT_CLEAR_CONFIG";
+            case EVENT_CONNECTED_TO_DEFAULT:
+                return "EVENT_CONNECTED_TO_DEFAULT";
+            case EVENT_CONNECTED_TO_CARRIER:
+                return "EVENT_CONNECTED_TO_CARRIER";
+            case EVENT_FETCH_DEFAULT_DONE:
+                return "EVENT_FETCH_DEFAULT_DONE";
+            case EVENT_FETCH_CARRIER_DONE:
+                return "EVENT_FETCH_CARRIER_DONE";
+            case EVENT_DO_FETCH_DEFAULT:
+                return "EVENT_DO_FETCH_DEFAULT";
+            case EVENT_DO_FETCH_CARRIER:
+                return "EVENT_DO_FETCH_CARRIER";
+            case EVENT_PACKAGE_CHANGED:
+                return "EVENT_PACKAGE_CHANGED";
+            case EVENT_BIND_DEFAULT_TIMEOUT:
+                return "EVENT_BIND_DEFAULT_TIMEOUT";
+            case EVENT_BIND_CARRIER_TIMEOUT:
+                return "EVENT_BIND_CARRIER_TIMEOUT";
+            case EVENT_CHECK_SYSTEM_UPDATE:
+                return "EVENT_CHECK_SYSTEM_UPDATE";
+            case EVENT_SYSTEM_UNLOCKED:
+                return "EVENT_SYSTEM_UNLOCKED";
+            case EVENT_FETCH_DEFAULT_TIMEOUT:
+                return "EVENT_FETCH_DEFAULT_TIMEOUT";
+            case EVENT_FETCH_CARRIER_TIMEOUT:
+                return "EVENT_FETCH_CARRIER_TIMEOUT";
+            case EVENT_SUBSCRIPTION_INFO_UPDATED:
+                return "EVENT_SUBSCRIPTION_INFO_UPDATED";
+            case EVENT_MULTI_SIM_CONFIG_CHANGED:
+                return "EVENT_MULTI_SIM_CONFIG_CHANGED";
+            case EVENT_DO_FETCH_DEFAULT_FOR_NO_SIM_CONFIG:
+                return "EVENT_DO_FETCH_DEFAULT_FOR_NO_SIM_CONFIG";
+            case EVENT_FETCH_DEFAULT_FOR_NO_SIM_CONFIG_DONE:
+                return "EVENT_FETCH_DEFAULT_FOR_NO_SIM_CONFIG_DONE";
+            case EVENT_CONNECTED_TO_DEFAULT_FOR_NO_SIM_CONFIG:
+                return "EVENT_CONNECTED_TO_DEFAULT_FOR_NO_SIM_CONFIG";
+            case EVENT_BIND_DEFAULT_FOR_NO_SIM_CONFIG_TIMEOUT:
+                return "EVENT_BIND_DEFAULT_FOR_NO_SIM_CONFIG_TIMEOUT";
+            case EVENT_FETCH_DEFAULT_FOR_NO_SIM_CONFIG_TIMEOUT:
+                return "EVENT_FETCH_DEFAULT_FOR_NO_SIM_CONFIG_TIMEOUT";
+            default:
+                return "UNKNOWN(" + code + ")";
+        }
+    }
+
     private void logd(String msg) {
         Log.d(LOG_TAG, msg);
     }
diff --git a/src/com/android/phone/CarrierXmlParser.java b/src/com/android/phone/CarrierXmlParser.java
index 18602c9..6e01b43 100644
--- a/src/com/android/phone/CarrierXmlParser.java
+++ b/src/com/android/phone/CarrierXmlParser.java
@@ -67,6 +67,7 @@
 
     // To define feature's item name in xml
     public static final String FEATURE_CALL_FORWARDING = "callforwarding";
+    public static final String FEATURE_CALL_WAITING = "callwaiting";
     public static final String FEATURE_CALLER_ID = "callerid";
 
     // COMMAND_NAME is xml's command name.
diff --git a/src/com/android/phone/EmergencyCallbackModeExitDialog.java b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
index 591f435..adba850 100644
--- a/src/com/android/phone/EmergencyCallbackModeExitDialog.java
+++ b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
@@ -16,6 +16,8 @@
 
 package com.android.phone;
 
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
@@ -74,7 +76,7 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
+        getWindow().addPrivateFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
         mPhone = PhoneGlobals.getInstance().getPhoneInEcm();
         // Check if phone is in Emergency Callback Mode. If not, exit.
         if (mPhone == null || !mPhone.isInEcm()) {
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index 2c87f7c..2e4ee94 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -16,6 +16,7 @@
 
 package com.android.phone;
 
+import android.Manifest;
 import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Binder;
@@ -26,6 +27,7 @@
 import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.ims.DelegateRequest;
 import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsContactUceCapability;
 import android.telephony.ims.RcsUceAdapter.PublishState;
 import android.telephony.ims.RegistrationManager;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
@@ -54,6 +56,7 @@
 import com.android.services.telephony.rcs.UceControllerManager;
 
 import java.util.List;
+import java.util.Set;
 
 /**
  * Implementation of the IImsRcsController interface.
@@ -67,6 +70,8 @@
     private PhoneGlobals mApp;
     private TelephonyRcsService mRcsService;
     private ImsResolver mImsResolver;
+    // set by shell cmd phone src set-device-enabled true/false
+    private Boolean mSingleRegistrationOverride;
 
     /**
      * Initialize the singleton ImsRcsController instance.
@@ -89,7 +94,7 @@
         mApp = app;
         TelephonyFrameworkInitializer
                 .getTelephonyServiceManager().getTelephonyImsServiceRegisterer().register(this);
-        mImsResolver = mApp.getImsResolver();
+        mImsResolver = ImsResolver.getInstance();
     }
 
     /**
@@ -98,7 +103,8 @@
      */
     @Override
     public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback callback) {
-        enforceReadPrivilegedPermission("registerImsRegistrationCallback");
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "registerImsRegistrationCallback");
         final long token = Binder.clearCallingIdentity();
         try {
             getRcsFeatureController(subId).registerImsRegistrationCallback(subId, callback);
@@ -115,7 +121,8 @@
      */
     @Override
     public void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback callback) {
-        enforceReadPrivilegedPermission("unregisterImsRegistrationCallback");
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "unregisterImsRegistrationCallback");
         final long token = Binder.clearCallingIdentity();
         try {
             getRcsFeatureController(subId).unregisterImsRegistrationCallback(subId, callback);
@@ -131,7 +138,8 @@
      */
     @Override
     public void getImsRcsRegistrationState(int subId, IIntegerConsumer consumer) {
-        enforceReadPrivilegedPermission("getImsRcsRegistrationState");
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "getImsRcsRegistrationState");
         final long token = Binder.clearCallingIdentity();
         try {
             getRcsFeatureController(subId).getRegistrationState(regState -> {
@@ -152,7 +160,8 @@
      */
     @Override
     public void getImsRcsRegistrationTransportType(int subId, IIntegerConsumer consumer) {
-        enforceReadPrivilegedPermission("getImsRcsRegistrationTransportType");
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "getImsRcsRegistrationTransportType");
         final long token = Binder.clearCallingIdentity();
         try {
             getRcsFeatureController(subId).getRegistrationTech(regTech -> {
@@ -215,7 +224,7 @@
      *
      * @param subId the subscription ID
      * @param capability the RCS capability to query.
-     * @param radioTech the radio tech that this capability failed for
+     * @param radioTech the radio technology type that we are querying.
      * @return true if the RCS capability is capable for this subscription, false otherwise.
      */
     @Override
@@ -241,15 +250,17 @@
      * @param subId the subscription ID
      * @param capability the RCS capability to query.
      * @return true if the RCS capability is currently available for the associated subscription,
+     * @param radioTech the radio technology type that we are querying.
      * false otherwise.
      */
     @Override
     public boolean isAvailable(int subId,
-            @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
+            @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
         enforceReadPrivilegedPermission("isAvailable");
         final long token = Binder.clearCallingIdentity();
         try {
-            return getRcsFeatureController(subId).isAvailable(capability);
+            return getRcsFeatureController(subId).isAvailable(capability, radioTech);
         } catch (ImsException e) {
             Log.e(TAG, "isAvailable: sudId=" + subId
                     + ", capability=" + capability + ", " + e.getMessage());
@@ -262,7 +273,8 @@
     @Override
     public void requestCapabilities(int subId, String callingPackage, String callingFeatureId,
             List<Uri> contactNumbers, IRcsUceControllerCallback c) {
-        enforceReadPrivilegedPermission("requestCapabilities");
+        enforceAccessUserCapabilityExchangePermission("requestCapabilities");
+        enforceReadContactsPermission("requestCapabilities");
         final long token = Binder.clearCallingIdentity();
         try {
             UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
@@ -282,7 +294,8 @@
     @Override
     public void requestAvailability(int subId, String callingPackage,
             String callingFeatureId, Uri contactNumber, IRcsUceControllerCallback c) {
-        enforceReadPrivilegedPermission("requestAvailability");
+        enforceAccessUserCapabilityExchangePermission("requestAvailability");
+        enforceReadContactsPermission("requestAvailability");
         final long token = Binder.clearCallingIdentity();
         try {
             UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
@@ -318,6 +331,109 @@
         }
     }
 
+    /**
+     * Add new feature tags to the Set used to calculate the capabilities in PUBLISH.
+     */
+    // Used for SHELL command only right now.
+    public RcsContactUceCapability addUceRegistrationOverrideShell(int subId,
+            Set<String> featureTags) throws ImsException {
+        // Permission check happening in PhoneInterfaceManager.
+        UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                UceControllerManager.class);
+        if (uceCtrlManager == null) {
+            return null;
+        }
+        return uceCtrlManager.addUceRegistrationOverride(featureTags);
+    }
+
+    /**
+     * Remove existing feature tags to the Set used to calculate the capabilities in PUBLISH.
+     */
+    // Used for SHELL command only right now.
+    public RcsContactUceCapability removeUceRegistrationOverrideShell(int subId,
+            Set<String> featureTags) throws ImsException {
+        // Permission check happening in PhoneInterfaceManager.
+        UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                UceControllerManager.class);
+        if (uceCtrlManager == null) {
+            return null;
+        }
+        return uceCtrlManager.removeUceRegistrationOverride(featureTags);
+    }
+
+    /**
+     * Clear all overrides in the Set used to calculate the capabilities in PUBLISH.
+     */
+    // Used for SHELL command only right now.
+    public RcsContactUceCapability clearUceRegistrationOverrideShell(int subId)
+            throws ImsException {
+        // Permission check happening in PhoneInterfaceManager.
+        UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                UceControllerManager.class);
+        if (uceCtrlManager == null) {
+            return null;
+        }
+        return uceCtrlManager.clearUceRegistrationOverride();
+    }
+
+    /**
+     * @return current RcsContactUceCapability instance that will be used for PUBLISH.
+     */
+    // Used for SHELL command only right now.
+    public RcsContactUceCapability getLatestRcsContactUceCapabilityShell(int subId)
+            throws ImsException {
+        // Permission check happening in PhoneInterfaceManager.
+        UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                UceControllerManager.class);
+        if (uceCtrlManager == null) {
+            return null;
+        }
+        return uceCtrlManager.getLatestRcsContactUceCapability();
+    }
+
+    /**
+     * @return the PIDf XML used in the last PUBLISH procedure or "none" if the device is not
+     * published. Returns {@code null} if the operation failed due to an error.
+     */
+    // Used for SHELL command only right now.
+    public String getLastUcePidfXmlShell(int subId) throws ImsException {
+        // Permission check happening in PhoneInterfaceManager.
+        UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                UceControllerManager.class);
+        if (uceCtrlManager == null) {
+            return null;
+        }
+        String pidfXml = uceCtrlManager.getLastPidfXml();
+        return pidfXml == null ? "none" : pidfXml;
+    }
+
+    /**
+     * Remove UCE requests cannot be sent to the network status.
+     * @return true if this command is successful.
+     */
+    // Used for SHELL command only right now.
+    public boolean removeUceRequestDisallowedStatus(int subId) throws ImsException {
+        UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                UceControllerManager.class);
+        if (uceCtrlManager == null) {
+            return false;
+        }
+        return uceCtrlManager.removeUceRequestDisallowedStatus();
+    }
+
+    /**
+     * Set the timeout for contact capabilities request.
+     */
+    // Used for SHELL command only right now.
+    public boolean setCapabilitiesRequestTimeout(int subId, long timeoutAfter) throws ImsException {
+        UceControllerManager uceCtrlManager = getRcsFeatureController(subId).getFeature(
+                UceControllerManager.class);
+        if (uceCtrlManager == null) {
+            return false;
+        }
+        return uceCtrlManager.setCapabilitiesRequestTimeout(timeoutAfter);
+    }
+
     @Override
     public void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
         enforceReadPrivilegedPermission("registerUcePublishStateCallback");
@@ -385,7 +501,13 @@
 
     @Override
     public boolean isSipDelegateSupported(int subId) {
-        enforceReadPrivilegedPermission("isSipDelegateSupported");
+        TelephonyPermissions.enforceAnyPermissionGranted(mApp, Binder.getCallingUid(),
+                "isSipDelegateSupported",
+                Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION,
+                Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+        if (!isImsSingleRegistrationSupportedOnDevice()) {
+            return false;
+        }
         final long token = Binder.clearCallingIdentity();
         try {
             SipTransportController transport = getRcsFeatureController(subId).getFeature(
@@ -410,7 +532,12 @@
     public void createSipDelegate(int subId, DelegateRequest request, String packageName,
             ISipDelegateConnectionStateCallback delegateState,
             ISipDelegateMessageCallback delegateMessage) {
-        enforceModifyPermission();
+        enforceImsSingleRegistrationPermission("createSipDelegate");
+        if (!isImsSingleRegistrationSupportedOnDevice()) {
+            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                    "SipDelegate creation is only supported for devices supporting IMS single "
+                            + "registration");
+        }
         if (!UserHandle.getUserHandleForUid(Binder.getCallingUid()).isSystem()) {
             throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
                     "SipDelegate creation is only available to primary user.");
@@ -443,7 +570,7 @@
 
     @Override
     public void destroySipDelegate(int subId, ISipDelegate connection, int reason) {
-        enforceModifyPermission();
+        enforceImsSingleRegistrationPermission("destroySipDelegate");
 
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -461,7 +588,7 @@
     @Override
     public void triggerNetworkRegistration(int subId, ISipDelegate connection, int sipCode,
             String sipReason) {
-        enforceModifyPermission();
+        enforceImsSingleRegistrationPermission("triggerNetworkRegistration");
 
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -523,6 +650,15 @@
     }
 
     /**
+     * @throws SecurityException if the caller does not have the required
+     *     PERFORM_IMS_SINGLE_REGISTRATION permission.
+     */
+    private void enforceImsSingleRegistrationPermission(String message) {
+        mApp.enforceCallingOrSelfPermission(
+                Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION, message);
+    }
+
+    /**
      * Make sure the caller has the MODIFY_PHONE_STATE permission.
      *
      * @throws SecurityException if the caller does not have the required permission
@@ -532,6 +668,26 @@
     }
 
     /**
+     * Make sure the caller has the ACCESS_RCS_USER_CAPABILITY_EXCHANGE permission.
+     *
+     * @throws SecurityException if the caller does not have the required permission.
+     */
+    private void enforceAccessUserCapabilityExchangePermission(String message) {
+        mApp.enforceCallingOrSelfPermission(
+                android.Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, message);
+    }
+
+    /**
+     * Make sure the caller has the READ_CONTACTS permission.
+     *
+     * @throws SecurityException if the caller does not have the required permission.
+     */
+    private void enforceReadContactsPermission(String message) {
+        mApp.enforceCallingOrSelfPermission(
+                android.Manifest.permission.READ_CONTACTS, message);
+    }
+
+    /**
      * Retrieve ImsPhone instance.
      *
      * @param subId the subscription ID
@@ -586,7 +742,21 @@
         return c;
     }
 
+    private boolean isImsSingleRegistrationSupportedOnDevice() {
+        return mSingleRegistrationOverride != null ? mSingleRegistrationOverride
+                : mApp.getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION);
+    }
+
     void setRcsService(TelephonyRcsService rcsService) {
         mRcsService = rcsService;
     }
+
+    /**
+     * Override device RCS single registration support check for CTS testing or remove override
+     * if the Boolean is set to null.
+     */
+    void setDeviceSingleRegistrationSupportOverride(Boolean deviceOverrideValue) {
+        mSingleRegistrationOverride = deviceOverrideValue;
+    }
 }
diff --git a/src/com/android/phone/ImsUtil.java b/src/com/android/phone/ImsUtil.java
index 0825cfb..ba4ad38 100644
--- a/src/com/android/phone/ImsUtil.java
+++ b/src/com/android/phone/ImsUtil.java
@@ -165,7 +165,7 @@
     private static boolean getLastKnownRoamingState(int phoneId) {
         try {
             ImsPhone imsPhone = (ImsPhone) (PhoneFactory.getPhone(phoneId).getImsPhone());
-            return imsPhone.getRoamingState();
+            return imsPhone.getLastKnownRoamingState();
         } catch (NullPointerException | ClassCastException e) {
             return false;
         }
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index 4fb61f0..f2641a1 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -565,9 +565,8 @@
                     .setChannelId(NotificationChannelController.CHANNEL_ID_CALL_FORWARD)
                     .setOnlyAlertOnce(isRefresh);
 
-            Intent intent = new Intent(Intent.ACTION_MAIN);
+            Intent intent = new Intent(TelecomManager.ACTION_SHOW_CALL_SETTINGS);
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
-            intent.setClassName("com.android.phone", "com.android.phone.CallFeaturesSetting");
             SubscriptionInfoHelper.addExtrasToIntent(
                     intent, mSubscriptionManager.getActiveSubscriptionInfo(subId));
             builder.setContentIntent(PendingIntent.getActivity(mContext, subId /* requestCode */,
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 23f6d7e..130c775 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -57,6 +57,7 @@
 import android.widget.Toast;
 
 import com.android.ims.ImsFeatureBinderRepository;
+import com.android.internal.os.BinderCallsStats;
 import com.android.internal.telephony.CallManager;
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.MmiCode;
@@ -72,6 +73,8 @@
 import com.android.internal.telephony.ims.ImsResolver;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
+import com.android.internal.telephony.uicc.UiccCard;
+import com.android.internal.telephony.uicc.UiccProfile;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.phone.settings.SettingsConstants;
 import com.android.phone.vvm.CarrierVvmPackageInstalledReceiver;
@@ -154,7 +157,6 @@
     CallNotifier notifier;
     CallerInfoCache callerInfoCache;
     NotificationMgr notificationMgr;
-    ImsResolver mImsResolver;
     TelephonyRcsService mTelephonyRcsService;
     public PhoneInterfaceManager phoneMgr;
     public ImsRcsController imsRcsController;
@@ -209,6 +211,7 @@
             new CarrierVvmPackageInstalledReceiver();
 
     private final SettingsObserver mSettingsObserver;
+    private BinderCallsStats.SettingsObserver mBinderCallsSettingsObserver;
 
     private static class EventSimStateChangedBag {
         final int mPhoneId;
@@ -220,6 +223,53 @@
         }
     }
 
+    // Some carrier config settings disable the network lock screen, so we call handleSimLock
+    // when either SIM_LOCK or CARRIER_CONFIG changes so that no matter which one happens first,
+    // we still do the right thing
+    private void handleSimLock(int subType, Phone phone) {
+        PersistableBundle cc = getCarrierConfigForSubId(phone.getSubId());
+        if (!CarrierConfigManager.isConfigForIdentifiedCarrier(cc)) {
+            // If we only have the default carrier config just return, to avoid popping up the
+            // the SIM lock screen when it's disabled by the carrier.
+            Log.i(LOG_TAG, "Not showing 'SIM network unlock' screen. Carrier config not loaded");
+            return;
+        }
+        if (cc.getBoolean(CarrierConfigManager.KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL)) {
+            // Some products don't have the concept of a "SIM network lock"
+            Log.i(LOG_TAG, "Not showing 'SIM network unlock' screen. Disabled by carrier config");
+            return;
+        }
+
+        // if passed in subType is unknown, retrieve it here.
+        if (subType == -1) {
+            final UiccCard uiccCard = phone.getUiccCard();
+            if (uiccCard == null) {
+                Log.e(LOG_TAG,
+                        "handleSimLock: uiccCard for phone " + phone.getPhoneId() + " is null");
+                return;
+            }
+            final UiccProfile uiccProfile = uiccCard.getUiccProfile();
+            if (uiccProfile == null) {
+                Log.e(LOG_TAG,
+                        "handleSimLock: uiccProfile for phone " + phone.getPhoneId() + " is null");
+                return;
+            }
+            subType = uiccProfile.getApplication(
+                    uiccProfile.mCurrentAppType).getPersoSubState().ordinal();
+        }
+        // Normal case: show the "SIM network unlock" PIN entry screen.
+        // The user won't be able to do anything else until
+        // they enter a valid SIM network PIN.
+        Log.i(LOG_TAG, "show sim depersonal panel");
+        IccNetworkDepersonalizationPanel.showDialog(phone, subType);
+    }
+
+    private boolean isSimLocked(Phone phone) {
+        TelephonyManager tm = getSystemService(TelephonyManager.class);
+        return tm.createForSubscriptionId(phone.getSubId()).getSimState()
+                == TelephonyManager.SIM_STATE_NETWORK_LOCKED;
+    }
+
     Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
@@ -229,20 +279,9 @@
                 // TODO: This event should be handled by the lock screen, just
                 // like the "SIM missing" and "Sim locked" cases (bug 1804111).
                 case EVENT_SIM_NETWORK_LOCKED:
-                    if (getCarrierConfig().getBoolean(
-                            CarrierConfigManager.KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL)) {
-                        // Some products don't have the concept of a "SIM network lock"
-                        Log.i(LOG_TAG, "Ignoring EVENT_SIM_NETWORK_LOCKED event; "
-                              + "not showing 'SIM network unlock' PIN entry screen");
-                    } else {
-                        // Normal case: show the "SIM network unlock" PIN entry screen.
-                        // The user won't be able to do anything else until
-                        // they enter a valid SIM network PIN.
-                        Log.i(LOG_TAG, "show sim depersonal panel");
-                        Phone phone = (Phone) ((AsyncResult) msg.obj).userObj;
-                        int subType = (Integer)((AsyncResult)msg.obj).result;
-                        IccNetworkDepersonalizationPanel.showDialog(phone, subType);
-                    }
+                    int subType = (Integer) ((AsyncResult) msg.obj).result;
+                    Phone phone = (Phone) ((AsyncResult) msg.obj).userObj;
+                    handleSimLock(subType, phone);
                     break;
 
                 case EVENT_DATA_ROAMING_DISCONNECTED:
@@ -315,6 +354,12 @@
                     // The voicemail number could be overridden by carrier config, so need to
                     // refresh the message waiting (voicemail) indicator.
                     refreshMwiIndicator(subId);
+                    phone = getPhone(subId);
+                    if (phone != null && isSimLocked(phone)) {
+                        // pass in subType=-1 so handleSimLock can find the actual subType if
+                        // needed. This is safe as valid values for subType are >= 0
+                        handleSimLock(-1, phone);
+                    }
                     break;
             }
         }
@@ -362,10 +407,10 @@
                         R.string.config_ims_mmtel_package);
                 String defaultImsRcsPackage = getResources().getString(
                         R.string.config_ims_rcs_package);
-                mImsResolver = new ImsResolver(this, defaultImsMmtelPackage,
+                ImsResolver.make(this, defaultImsMmtelPackage,
                         defaultImsRcsPackage, PhoneFactory.getPhones().length,
                         new ImsFeatureBinderRepository());
-                mImsResolver.initialize();
+                ImsResolver.getInstance().initialize();
 
                 // With the IMS phone created, load static config.xml values from the phone process
                 // so that it can be provided to the ImsPhoneCallTracker.
@@ -487,6 +532,12 @@
                     SettingsConstants.HAC_KEY + "=" + (hac == SettingsConstants.HAC_ENABLED
                             ? SettingsConstants.HAC_VAL_ON : SettingsConstants.HAC_VAL_OFF));
         }
+
+        // Start tracking Binder latency for the phone process.
+        mBinderCallsSettingsObserver = new BinderCallsStats.SettingsObserver(
+            getApplicationContext(),
+            new BinderCallsStats(new BinderCallsStats.Injector()),
+            com.android.internal.os.BinderLatencyProto.Dims.TELEPHONY);
     }
 
     /**
@@ -512,10 +563,6 @@
         return PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subId));
     }
 
-    public ImsResolver getImsResolver() {
-        return mImsResolver;
-    }
-
     /* package */ CallManager getCallManager() {
         return mCM;
     }
@@ -944,6 +991,23 @@
     }
 
     /**
+     * @return whether the device supports RCS User Capability Exchange or not.
+     */
+    public boolean getDeviceUceEnabled() {
+        return (mTelephonyRcsService == null) ? false : mTelephonyRcsService.isDeviceUceEnabled();
+    }
+
+    /**
+     * Set the device supports RCS User Capability Exchange.
+     * @param isEnabled true if the device supports UCE.
+     */
+    public void setDeviceUceEnabled(boolean isEnabled) {
+        if (mTelephonyRcsService != null) {
+            mTelephonyRcsService.setDeviceUceEnabled(isEnabled);
+        }
+    }
+
+    /**
      * Dump the state of the object, add calls to other objects as desired.
      *
      * @param fd File descriptor
@@ -964,7 +1028,7 @@
         pw.println("ImsResolver:");
         pw.increaseIndent();
         try {
-            if (mImsResolver != null) mImsResolver.dump(fd, pw, args);
+            if (ImsResolver.getInstance() != null) ImsResolver.getInstance().dump(fd, pw, args);
         } catch (Exception e) {
             e.printStackTrace();
         }
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index b5ee6ca..cb4f72e 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -18,14 +18,18 @@
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
+import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_CDMA;
+import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_GSM;
 import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_IMS;
 import static com.android.internal.telephony.PhoneConstants.SUBSCRIPTION_KEY;
 
+import android.Manifest;
 import android.Manifest.permission;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
+import android.app.compat.CompatChanges;
 import android.app.role.RoleManager;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -52,6 +56,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceSpecificException;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.WorkSource;
@@ -66,7 +71,6 @@
 import android.telephony.Annotation.ApnType;
 import android.telephony.Annotation.ThermalMitigationResult;
 import android.telephony.CallForwardingInfo;
-import android.telephony.CarrierBandwidth;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CarrierRestrictionRules;
 import android.telephony.CellIdentity;
@@ -88,7 +92,6 @@
 import android.telephony.PhoneNumberRange;
 import android.telephony.RadioAccessFamily;
 import android.telephony.RadioAccessSpecifier;
-import android.telephony.RadioInterfaceCapabilities;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SignalStrengthUpdateRequest;
@@ -105,12 +108,14 @@
 import android.telephony.UssdResponse;
 import android.telephony.VisualVoicemailSmsFilterSettings;
 import android.telephony.data.ApnSetting;
+import android.telephony.data.NetworkSlicingConfig;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.gba.GbaAuthRequest;
 import android.telephony.gba.UaSecurityProtocolIdentifier;
 import android.telephony.ims.ImsException;
 import android.telephony.ims.ProvisioningManager;
 import android.telephony.ims.RcsClientConfiguration;
+import android.telephony.ims.RcsContactUceCapability;
 import android.telephony.ims.RegistrationManager;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsConfig;
@@ -135,13 +140,14 @@
 import com.android.internal.telephony.CallForwardInfo;
 import com.android.internal.telephony.CallManager;
 import com.android.internal.telephony.CallStateException;
-import com.android.internal.telephony.CarrierInfoManager;
+import com.android.internal.telephony.CallTracker;
 import com.android.internal.telephony.CarrierResolver;
 import com.android.internal.telephony.CellNetworkScanResult;
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.DefaultPhoneNotifier;
 import com.android.internal.telephony.GbaManager;
+import com.android.internal.telephony.GsmCdmaPhone;
 import com.android.internal.telephony.HalVersion;
 import com.android.internal.telephony.IBooleanConsumer;
 import com.android.internal.telephony.ICallForwardingInfoCallback;
@@ -159,6 +165,8 @@
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.ProxyController;
 import com.android.internal.telephony.RIL;
+import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.RadioInterfaceCapabilityController;
 import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.SmsController;
 import com.android.internal.telephony.SmsPermissions;
@@ -328,6 +336,10 @@
     private static final int EVENT_CLEAR_SIGNAL_STRENGTH_UPDATE_REQUEST_DONE = 106;
     private static final int CMD_SET_ALLOWED_NETWORK_TYPES_FOR_REASON = 107;
     private static final int EVENT_SET_ALLOWED_NETWORK_TYPES_FOR_REASON_DONE = 108;
+    private static final int CMD_PREPARE_UNATTENDED_REBOOT = 109;
+    private static final int CMD_GET_SLICING_CONFIG = 110;
+    private static final int EVENT_GET_SLICING_CONFIG_DONE = 111;
+    private static final int CMD_ERASE_DATA_SHARED_PREFERENCES = 112;
 
     // Parameters of select command.
     private static final int SELECT_COMMAND = 0xA4;
@@ -337,16 +349,19 @@
 
     /** The singleton instance. */
     private static PhoneInterfaceManager sInstance;
+    private static List<String> sThermalMitigationAllowlistedPackages = new ArrayList<>();
 
     private PhoneGlobals mApp;
     private CallManager mCM;
     private ImsResolver mImsResolver;
     private UserManager mUserManager;
     private AppOpsManager mAppOps;
+    private PackageManager mPm;
     private MainThreadHandler mMainThreadHandler;
     private SubscriptionController mSubscriptionController;
     private SharedPreferences mTelephonySharedPreferences;
     private PhoneConfigurationManager mPhoneConfigurationManager;
+    private final RadioInterfaceCapabilityController mRadioInterfaceCapabilities;
 
     /** User Activity */
     private AtomicBoolean mNotifyUserActivity;
@@ -371,6 +386,7 @@
     private static final int MANUFACTURER_CODE_LENGTH = 8;
 
     private static final int SET_DATA_THROTTLING_MODEM_THREW_INVALID_PARAMS = -1;
+    private static final int MODEM_DOES_NOT_SUPPORT_DATA_THROTTLING_ERROR_CODE = -2;
 
     /**
      * Experiment flag to enable erase modem config on reset network, default value is false
@@ -378,6 +394,8 @@
     public static final String RESET_NETWORK_ERASE_MODEM_CONFIG_ENABLED =
             "reset_network_erase_modem_config_enabled";
 
+    private static final int SET_NETWORK_SELECTION_MODE_AUTOMATIC_TIMEOUT_MS = 2000; // 2 seconds
+
     /**
      * A request object to use for transmitting data to an ICC.
      */
@@ -865,6 +883,10 @@
                                 request.result =
                                         TelephonyManager
                                                 .ENABLE_NR_DUAL_CONNECTIVITY_RADIO_NOT_AVAILABLE;
+                            } else if (error == CommandException.Error.REQUEST_NOT_SUPPORTED) {
+                                request.result =
+                                        TelephonyManager
+                                                .ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED;
                             }
                             loge("enableNrDualConnectivity" + ": CommandException: "
                                     + ar.exception);
@@ -1147,7 +1169,6 @@
                     }
                     break;
                 }
-
                 case EVENT_PERFORM_NETWORK_SCAN_DONE:
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
@@ -1547,7 +1568,7 @@
                     PhoneConfigurationManager.getInstance()
                             .enablePhone(request.phone, enable, onCompleted);
                     break;
-                case EVENT_ENABLE_MODEM_DONE:
+                case EVENT_ENABLE_MODEM_DONE: {
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
                     request.result = (ar.exception == null);
@@ -1562,6 +1583,7 @@
                     }
                     notifyRequester(request);
                     break;
+                }
                 case CMD_GET_MODEM_STATUS:
                     request = (MainThreadRequest) msg.obj;
                     onCompleted = obtainMessage(EVENT_GET_MODEM_STATUS_DONE, request);
@@ -1625,8 +1647,8 @@
                     if (ar.exception == null && ar.result != null) {
                         request.result = ar.result;
                     } else {
-                        request.result = new IllegalArgumentException(
-                                "Failed to retrieve system selection channels");
+                        request.result = new IllegalStateException(
+                                "Failed to retrieve system selecton channels");
                         if (ar.result == null) {
                             loge("getSystemSelectionChannels: Empty response");
                         } else {
@@ -1688,6 +1710,12 @@
                     handleNullReturnEvent(msg, "eraseModemConfig");
                     break;
 
+                case CMD_ERASE_DATA_SHARED_PREFERENCES:
+                    request = (MainThreadRequest) msg.obj;
+                    request.result = defaultPhone.eraseDataInSharedPreferences();
+                    notifyRequester(request);
+                    break;
+
                 case CMD_CHANGE_ICC_LOCK_PASSWORD:
                     request = (MainThreadRequest) msg.obj;
                     onCompleted = obtainMessage(EVENT_CHANGE_ICC_LOCK_PASSWORD_DONE, request);
@@ -1700,27 +1728,44 @@
                     request = (MainThreadRequest) ar.userObj;
                     if (ar.exception == null) {
                         request.result = TelephonyManager.CHANGE_ICC_LOCK_SUCCESS;
+                        // If the operation is successful, update the PIN storage
+                        Pair<String, String> passwords = (Pair<String, String>) request.argument;
+                        int phoneId = getPhoneFromRequest(request).getPhoneId();
+                        UiccController.getInstance().getPinStorage()
+                                .storePin(passwords.second, phoneId);
                     } else {
                         request.result = msg.arg1;
                     }
                     notifyRequester(request);
                     break;
 
-                case CMD_SET_ICC_LOCK_ENABLED:
+                case CMD_SET_ICC_LOCK_ENABLED: {
                     request = (MainThreadRequest) msg.obj;
                     onCompleted = obtainMessage(EVENT_SET_ICC_LOCK_ENABLED_DONE, request);
                     Pair<Boolean, String> enabled = (Pair<Boolean, String>) request.argument;
                     getPhoneFromRequest(request).getIccCard().setIccLockEnabled(
                             enabled.first, enabled.second, onCompleted);
                     break;
+                }
                 case EVENT_SET_ICC_LOCK_ENABLED_DONE:
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
                     if (ar.exception == null) {
                         request.result = TelephonyManager.CHANGE_ICC_LOCK_SUCCESS;
+                        // If the operation is successful, update the PIN storage
+                        Pair<Boolean, String> enabled = (Pair<Boolean, String>) request.argument;
+                        int phoneId = getPhoneFromRequest(request).getPhoneId();
+                        if (enabled.first) {
+                            UiccController.getInstance().getPinStorage()
+                                    .storePin(enabled.second, phoneId);
+                        } else {
+                            UiccController.getInstance().getPinStorage().clearPin(phoneId);
+                        }
                     } else {
                         request.result = msg.arg1;
                     }
+
+
                     notifyRequester(request);
                     break;
 
@@ -1767,6 +1812,8 @@
                                 .THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE;
                         } else if (error == CommandException.Error.INVALID_ARGUMENTS) {
                             request.result = SET_DATA_THROTTLING_MODEM_THREW_INVALID_PARAMS;
+                        } else if (error == CommandException.Error.REQUEST_NOT_SUPPORTED) {
+                            request.result = MODEM_DOES_NOT_SUPPORT_DATA_THROTTLING_ERROR_CODE;
                         } else {
                             request.result =
                                     TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_ERROR;
@@ -1881,6 +1928,49 @@
                     break;
                 }
 
+                case CMD_GET_SLICING_CONFIG: {
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_GET_SLICING_CONFIG_DONE, request);
+                    request.phone.getSlicingConfig(onCompleted);
+                    break;
+                }
+                case EVENT_GET_SLICING_CONFIG_DONE: {
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    ResultReceiver result = (ResultReceiver) request.argument;
+
+                    NetworkSlicingConfig slicingConfig = null;
+                    Bundle bundle = new Bundle();
+                    int resultCode = 0;
+                    if (ar.exception != null) {
+                        Log.e(LOG_TAG, "Exception retrieving slicing configuration="
+                                + ar.exception);
+                        resultCode = TelephonyManager.NetworkSlicingException.ERROR_MODEM_ERROR;
+                    } else if (ar.result == null) {
+                        Log.w(LOG_TAG, "Timeout Waiting for slicing configuration!");
+                        resultCode = TelephonyManager.NetworkSlicingException.ERROR_TIMEOUT;
+                    } else {
+                        // use the result as returned
+                        resultCode = TelephonyManager.NetworkSlicingException.SUCCESS;
+                        slicingConfig = (NetworkSlicingConfig) ar.result;
+                    }
+
+                    if (slicingConfig == null) {
+                        slicingConfig = new NetworkSlicingConfig();
+                    }
+                    bundle.putParcelable(TelephonyManager.KEY_SLICING_CONFIG_HANDLE, slicingConfig);
+                    result.send(resultCode, bundle);
+                    notifyRequester(request);
+                    break;
+                }
+
+                case CMD_PREPARE_UNATTENDED_REBOOT:
+                    request = (MainThreadRequest) msg.obj;
+                    request.result =
+                            UiccController.getInstance().getPinStorage().prepareUnattendedReboot();
+                    notifyRequester(request);
+                    break;
+
                 default:
                     Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
                     break;
@@ -1916,8 +2006,8 @@
      * @see #sendRequestAsync
      */
     private Object sendRequest(int command, Object argument) {
-        return sendRequest(
-                command, argument, SubscriptionManager.INVALID_SUBSCRIPTION_ID, null, null);
+        return sendRequest(command, argument, SubscriptionManager.INVALID_SUBSCRIPTION_ID, null,
+                null, -1 /*timeoutInMs*/);
     }
 
     /**
@@ -1927,7 +2017,7 @@
      */
     private Object sendRequest(int command, Object argument, WorkSource workSource) {
         return sendRequest(command, argument,  SubscriptionManager.INVALID_SUBSCRIPTION_ID,
-                null, workSource);
+                null, workSource, -1 /*timeoutInMs*/);
     }
 
     /**
@@ -1936,7 +2026,18 @@
      * @see #sendRequestAsync
      */
     private Object sendRequest(int command, Object argument, Integer subId) {
-        return sendRequest(command, argument, subId, null, null);
+        return sendRequest(command, argument, subId, null, null, -1 /*timeoutInMs*/);
+    }
+
+    /**
+     * Posts the specified command to be executed on the main thread,
+     * waits for the request to complete for at most {@code timeoutInMs}, and returns the result
+     * if not timeout or null otherwise.
+     * @see #sendRequestAsync
+     */
+    private @Nullable Object sendRequest(int command, Object argument, Integer subId,
+            long timeoutInMs) {
+        return sendRequest(command, argument, subId, null, null, timeoutInMs);
     }
 
     /**
@@ -1945,7 +2046,7 @@
      * @see #sendRequestAsync
      */
     private Object sendRequest(int command, Object argument, int subId, WorkSource workSource) {
-        return sendRequest(command, argument, subId, null, workSource);
+        return sendRequest(command, argument, subId, null, workSource, -1 /*timeoutInMs*/);
     }
 
     /**
@@ -1954,17 +2055,18 @@
      * @see #sendRequestAsync
      */
     private Object sendRequest(int command, Object argument, Phone phone, WorkSource workSource) {
-        return sendRequest(
-                command, argument, SubscriptionManager.INVALID_SUBSCRIPTION_ID, phone, workSource);
+        return sendRequest(command, argument, SubscriptionManager.INVALID_SUBSCRIPTION_ID, phone,
+                workSource, -1 /*timeoutInMs*/);
     }
 
     /**
-     * Posts the specified command to be executed on the main thread,
-     * waits for the request to complete, and returns the result.
+     * Posts the specified command to be executed on the main thread. If {@code timeoutInMs} is
+     * negative, waits for the request to complete, and returns the result. Otherwise, wait for
+     * maximum of {@code timeoutInMs} milliseconds, interrupt and return null.
      * @see #sendRequestAsync
      */
-    private Object sendRequest(
-            int command, Object argument, Integer subId, Phone phone, WorkSource workSource) {
+    private @Nullable Object sendRequest(int command, Object argument, Integer subId, Phone phone,
+            WorkSource workSource, long timeoutInMs) {
         if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
             throw new RuntimeException("This method will deadlock if called from the main thread.");
         }
@@ -1981,16 +2083,36 @@
         Message msg = mMainThreadHandler.obtainMessage(command, request);
         msg.sendToTarget();
 
-        // Wait for the request to complete
+
         synchronized (request) {
-            while (request.result == null) {
-                try {
-                    request.wait();
-                } catch (InterruptedException e) {
-                    // Do nothing, go back and wait until the request is complete
+            if (timeoutInMs >= 0) {
+                // Wait for at least timeoutInMs before returning null request result
+                long now = SystemClock.elapsedRealtime();
+                long deadline = now + timeoutInMs;
+                while (request.result == null && now < deadline) {
+                    try {
+                        request.wait(deadline - now);
+                    } catch (InterruptedException e) {
+                        // Do nothing, go back and check if request is completed or timeout
+                    } finally {
+                        now = SystemClock.elapsedRealtime();
+                    }
+                }
+            } else {
+                // Wait for the request to complete
+                while (request.result == null) {
+                    try {
+                        request.wait();
+                    } catch (InterruptedException e) {
+                        // Do nothing, go back and wait until the request is complete
+                    }
                 }
             }
         }
+        if (request.result == null) {
+            Log.wtf(LOG_TAG,
+                    "sendRequest: Blocking command timed out. Something has gone terribly wrong.");
+        }
         return request.result;
     }
 
@@ -2042,15 +2164,17 @@
     private PhoneInterfaceManager(PhoneGlobals app) {
         mApp = app;
         mCM = PhoneGlobals.getInstance().mCM;
-        mImsResolver = PhoneGlobals.getInstance().getImsResolver();
+        mImsResolver = ImsResolver.getInstance();
         mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
         mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
+        mPm = app.getSystemService(PackageManager.class);
         mMainThreadHandler = new MainThreadHandler();
         mSubscriptionController = SubscriptionController.getInstance();
         mTelephonySharedPreferences =
                 PreferenceManager.getDefaultSharedPreferences(mApp);
         mNetworkScanRequestTracker = new NetworkScanRequestTracker();
         mPhoneConfigurationManager = PhoneConfigurationManager.getInstance();
+        mRadioInterfaceCapabilities = RadioInterfaceCapabilityController.getInstance();
         mNotifyUserActivity = new AtomicBoolean(false);
 
         publish();
@@ -2094,18 +2218,14 @@
         return PhoneFactory.getPhone(mSubscriptionController.getPhoneId(subId));
     }
 
-    private void sendEraseModemConfig(Phone phone) {
-        if (phone != null) {
-            TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
-                  mApp, phone.getSubId(), "eraseModemConfig");
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                Boolean success = (Boolean) sendRequest(CMD_ERASE_MODEM_CONFIG, null);
-                if (DBG) log("eraseModemConfig:" + ' ' + (success ? "ok" : "fail"));
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
+    private void sendEraseModemConfig(@NonNull Phone phone) {
+        Boolean success = (Boolean) sendRequest(CMD_ERASE_MODEM_CONFIG, null);
+        if (DBG) log("eraseModemConfig:" + ' ' + (success ? "ok" : "fail"));
+    }
+
+    private void sendEraseDataInSharedPreferences(@NonNull Phone phone) {
+        Boolean success = (Boolean) sendRequest(CMD_ERASE_DATA_SHARED_PREFERENCES, null);
+        if (DBG) log("eraseDataInSharedPreferences:" + ' ' + (success ? "ok" : "fail"));
     }
 
     private boolean isImsAvailableOnDevice() {
@@ -2209,7 +2329,8 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            final UnlockSim checkSimPin = new UnlockSim(getPhone(subId).getIccCard());
+            Phone phone = getPhone(subId);
+            final UnlockSim checkSimPin = new UnlockSim(phone.getPhoneId(), phone.getIccCard());
             checkSimPin.start();
             return checkSimPin.unlockSim(null, pin);
         } finally {
@@ -2222,7 +2343,8 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            final UnlockSim checkSimPuk = new UnlockSim(getPhone(subId).getIccCard());
+            Phone phone = getPhone(subId);
+            final UnlockSim checkSimPuk = new UnlockSim(phone.getPhoneId(), phone.getIccCard());
             checkSimPuk.start();
             return checkSimPuk.unlockSim(puk, pin);
         } finally {
@@ -2237,6 +2359,7 @@
     private static class UnlockSim extends Thread {
 
         private final IccCard mSimCard;
+        private final int mPhoneId;
 
         private boolean mDone = false;
         private int mResult = PhoneConstants.PIN_GENERAL_FAILURE;
@@ -2248,7 +2371,8 @@
         // For async handler to identify request type
         private static final int SUPPLY_PIN_COMPLETE = 100;
 
-        public UnlockSim(IccCard simCard) {
+        UnlockSim(int phoneId, IccCard simCard) {
+            mPhoneId = phoneId;
             mSimCard = simCard;
         }
 
@@ -2330,6 +2454,11 @@
             int[] resultArray = new int[2];
             resultArray[0] = mResult;
             resultArray[1] = mRetryCount;
+
+            if (mResult == PhoneConstants.PIN_RESULT_SUCCESS && pin.length() > 0) {
+                UiccController.getInstance().getPinStorage().storePin(pin, mPhoneId);
+            }
+
             return resultArray;
         }
     }
@@ -2641,14 +2770,46 @@
         }
     }
 
+    /**
+     * @deprecated  This method is deprecated and is only being kept due to an UnsupportedAppUsage
+     * tag on getCallState Binder call.
+     */
+    @Deprecated
+    @Override
     public int getCallState() {
-        return getCallStateForSlot(getSlotForDefaultSubscription());
-    }
-
-    public int getCallStateForSlot(int slotIndex) {
+        if (CompatChanges.isChangeEnabled(
+                TelecomManager.ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION,
+                Binder.getCallingUid())) {
+            // Do not allow this API to be called on API version 31+, it should only be
+            // called on old apps using this Binder call directly.
+            throw new SecurityException("This method can only be used for applications "
+                    + "targeting API version 30 or less.");
+        }
         final long identity = Binder.clearCallingIdentity();
         try {
-            Phone phone = PhoneFactory.getPhone(slotIndex);
+            Phone phone = getPhone(getDefaultSubscription());
+            return phone == null ? TelephonyManager.CALL_STATE_IDLE :
+                    PhoneConstantConversions.convertCallState(phone.getState());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public int getCallStateForSubscription(int subId, String callingPackage, String featureId) {
+        if (CompatChanges.isChangeEnabled(
+                TelecomManager.ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION,
+                Binder.getCallingUid())) {
+            // Check READ_PHONE_STATE for API version 31+
+            if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, subId, callingPackage,
+                    featureId, "getCallStateForSubscription")) {
+                throw new SecurityException("getCallState requires READ_PHONE_STATE for apps "
+                        + "targeting API level 31+.");
+            }
+        }
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            Phone phone = getPhone(subId);
             return phone == null ? TelephonyManager.CALL_STATE_IDLE :
                     PhoneConstantConversions.convertCallState(phone.getState());
         } finally {
@@ -2749,9 +2910,7 @@
             if (sst == null) return "";
             LocaleTracker lt = sst.getLocaleTracker();
             if (lt == null) return "";
-            if (!TextUtils.isEmpty(lt.getCurrentCountry())) return lt.getCurrentCountry();
-            EmergencyNumberTracker ent = phone.getEmergencyNumberTracker();
-            return (ent == null) ? "" : ent.getEmergencyCountryIso();
+            return lt.getCurrentCountry();
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -2946,6 +3105,7 @@
             return null;
         }
         int subId = phone.getSubId();
+        enforceCallingPackage(callingPackage, Binder.getCallingUid(), "getImeiForSlot");
         if (!TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mApp, subId,
                 callingPackage, callingFeatureId, "getImeiForSlot")) {
             return null;
@@ -3091,6 +3251,26 @@
     //
 
     /**
+     * Make sure the caller is the calling package itself
+     *
+     * @throws SecurityException if the caller is not the calling package
+     */
+    private void enforceCallingPackage(String callingPackage, int callingUid, String message) {
+        int packageUid = -1;
+        PackageManager pm = mApp.getBaseContext().createContextAsUser(
+                UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager();
+        try {
+            packageUid = pm.getPackageUid(callingPackage, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            // packageUid is -1
+        }
+        if (packageUid != callingUid) {
+            throw new SecurityException(message + ": Package " + callingPackage
+                    + " does not belong to " + callingUid);
+        }
+    }
+
+    /**
      * Make sure the caller has the MODIFY_PHONE_STATE permission.
      *
      * @throws SecurityException if the caller does not have the required permission
@@ -3128,6 +3308,10 @@
         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.NETWORK_SETTINGS, null);
     }
 
+    private void enforceRebootPermission() {
+        mApp.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
+    }
+
     private String createTelUrl(String number) {
         if (TextUtils.isEmpty(number)) {
             return null;
@@ -3615,7 +3799,7 @@
     @Override
     public int getNetworkSelectionMode(int subId) {
         TelephonyPermissions
-                    .enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                    .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                     mApp, subId, "getNetworkSelectionMode");
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -3652,7 +3836,7 @@
     @Override
     public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback c)
             throws RemoteException {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "registerImsRegistrationCallback");
 
         if (!ImsManager.isImsSupportedOnDevice(mApp)) {
@@ -3678,7 +3862,7 @@
      */
     @Override
     public void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback c) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "unregisterImsRegistrationCallback");
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
@@ -3735,7 +3919,7 @@
      */
     @Override
     public void getImsMmTelRegistrationTransportType(int subId, IIntegerConsumer consumer) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getImsMmTelRegistrationTransportType");
         if (!ImsManager.isImsSupportedOnDevice(mApp)) {
             throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
@@ -3775,7 +3959,7 @@
     @Override
     public void registerMmTelCapabilityCallback(int subId, IImsCapabilityCallback c)
             throws RemoteException {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "registerMmTelCapabilityCallback");
         if (!ImsManager.isImsSupportedOnDevice(mApp)) {
             throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
@@ -3800,7 +3984,7 @@
      */
     @Override
     public void unregisterMmTelCapabilityCallback(int subId, IImsCapabilityCallback c) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "unregisterMmTelCapabilityCallback");
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
@@ -3900,7 +4084,7 @@
      */
     @Override
     public boolean isAdvancedCallingSettingEnabled(int subId) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isAdvancedCallingSettingEnabled");
 
         // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -3937,7 +4121,7 @@
      */
     @Override
     public boolean isVtSettingEnabled(int subId) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isVtSettingEnabled");
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -3971,7 +4155,7 @@
      */
     @Override
     public boolean isVoWiFiSettingEnabled(int subId) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isVoWiFiSettingEnabled");
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -4007,7 +4191,7 @@
      */
     @Override
     public boolean isCrossSimCallingEnabledByUser(int subId) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isCrossSimCallingEnabledByUser");
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -4049,8 +4233,9 @@
      * @param subId The subscription to use to check the configuration.
      */
     @Override
+
     public boolean isVoWiFiRoamingSettingEnabled(int subId) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isVoWiFiRoamingSettingEnabled");
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -4102,7 +4287,7 @@
      */
     @Override
     public int getVoWiFiModeSetting(int subId) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getVoWiFiModeSetting");
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -4184,7 +4369,7 @@
      */
     @Override
     public boolean isTtyOverVolteEnabled(int subId) {
-        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isTtyOverVolteEnabled");
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -4327,7 +4512,9 @@
     public void setImsProvisioningStatusForCapability(int subId, int capability, int tech,
             boolean isProvisioned) {
         if (tech != ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN
-                && tech != ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
+                && tech != ImsRegistrationImplBase.REGISTRATION_TECH_LTE
+                && tech != ImsRegistrationImplBase.REGISTRATION_TECH_NR
+                && tech != ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
             throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
         }
         checkModifyPhoneStatePermission(subId, "setImsProvisioningStatusForCapability");
@@ -4337,6 +4524,12 @@
             if (!isImsProvisioningRequired(subId, capability, true)) {
                 return;
             }
+            if (tech == ImsRegistrationImplBase.REGISTRATION_TECH_NR
+                    || tech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
+                loge("setImsProvisioningStatusForCapability: called for technology that does "
+                        + "not support provisioning - " + tech);
+                return;
+            }
 
             // this capability requires provisioning, route to the correct API.
             ImsManager ims = ImsManager.getInstance(mApp, getSlotIndex(subId));
@@ -4364,7 +4557,7 @@
                     }
                     cacheMmTelCapabilityProvisioning(subId, capability, tech, isProvisioned);
                     try {
-                        ims.changeMmTelCapability(capability, tech, isProvisioned);
+                        ims.changeMmTelCapability(isProvisioned, capability, tech);
                     } catch (com.android.ims.ImsException e) {
                         loge("setImsProvisioningStatusForCapability: couldn't change UT capability"
                                 + ", Exception" + e.getMessage());
@@ -4386,7 +4579,9 @@
     @Override
     public boolean getImsProvisioningStatusForCapability(int subId, int capability, int tech) {
         if (tech != ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN
-                && tech != ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
+                && tech != ImsRegistrationImplBase.REGISTRATION_TECH_LTE
+                && tech != ImsRegistrationImplBase.REGISTRATION_TECH_NR
+                && tech != ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
             throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
         }
         enforceReadPrivilegedPermission("getProvisioningStatusForCapability");
@@ -4397,6 +4592,13 @@
                 return true;
             }
 
+            if (tech == ImsRegistrationImplBase.REGISTRATION_TECH_NR
+                    || tech == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM) {
+                loge("getImsProvisioningStatusForCapability: called for technology that does "
+                        + "not support provisioning - " + tech);
+                return true;
+            }
+
             ImsManager ims = ImsManager.getInstance(mApp, getSlotIndex(subId));
             switch (capability) {
                 case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE: {
@@ -4568,7 +4770,7 @@
                         + subId + "' for key:" + key);
                 return ImsConfigImplBase.CONFIG_RESULT_UNKNOWN;
             }
-            return ImsManager.getInstance(mApp, slotId).getConfigInterface().getConfigInt(key);
+            return ImsManager.getInstance(mApp, slotId).getConfigInt(key);
         } catch (com.android.ims.ImsException e) {
             Log.w(LOG_TAG, "getImsProvisioningInt: ImsService is not available for subscription '"
                     + subId + "' for key:" + key);
@@ -4593,7 +4795,7 @@
                         + subId + "' for key:" + key);
                 return ProvisioningManager.STRING_QUERY_RESULT_ERROR_GENERIC;
             }
-            return ImsManager.getInstance(mApp, slotId).getConfigInterface().getConfigString(key);
+            return ImsManager.getInstance(mApp, slotId).getConfigString(key);
         } catch (com.android.ims.ImsException e) {
             Log.w(LOG_TAG, "getImsProvisioningString: ImsService is not available for sub '"
                     + subId + "' for key:" + key);
@@ -4619,10 +4821,10 @@
                         + subId + "' for key:" + key);
                 return ImsConfigImplBase.CONFIG_RESULT_FAILED;
             }
-            return ImsManager.getInstance(mApp, slotId).getConfigInterface().setConfig(key, value);
-        } catch (com.android.ims.ImsException e) {
+            return ImsManager.getInstance(mApp, slotId).setConfig(key, value);
+        } catch (com.android.ims.ImsException | RemoteException e) {
             Log.w(LOG_TAG, "setImsProvisioningInt: ImsService unavailable for sub '" + subId
-                    + "' for key:" + key);
+                    + "' for key:" + key, e);
             return ImsConfigImplBase.CONFIG_RESULT_FAILED;
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -4645,10 +4847,10 @@
                         + subId + "' for key:" + key);
                 return ImsConfigImplBase.CONFIG_RESULT_FAILED;
             }
-            return ImsManager.getInstance(mApp, slotId).getConfigInterface().setConfig(key, value);
-        } catch (com.android.ims.ImsException e) {
+            return ImsManager.getInstance(mApp, slotId).setConfig(key, value);
+        } catch (com.android.ims.ImsException | RemoteException e) {
             Log.w(LOG_TAG, "setImsProvisioningString: ImsService unavailable for sub '" + subId
-                    + "' for key:" + key);
+                    + "' for key:" + key, e);
             return ImsConfigImplBase.CONFIG_RESULT_FAILED;
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -4706,8 +4908,8 @@
      */
     @Override
     public int getDataNetworkType(String callingPackage, String callingFeatureId) {
-        return getDataNetworkTypeForSubscriber(getDefaultSubscription(), callingPackage,
-                callingFeatureId);
+        return getDataNetworkTypeForSubscriber(mSubscriptionController.getDefaultDataSubId(),
+                callingPackage, callingFeatureId);
     }
 
     /**
@@ -5576,7 +5778,7 @@
             @ImsFeature.FeatureType int featureType) {
         int[] subIds = SubscriptionManager.getSubId(slotId);
         TelephonyPermissions
-                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                 mApp, (subIds != null ? subIds[0] : SubscriptionManager.INVALID_SUBSCRIPTION_ID),
                 "getBoundImsServicePackage");
 
@@ -5662,7 +5864,8 @@
                 return;
             }
             if (DBG) log("setNetworkSelectionModeAutomatic: subId " + subId);
-            sendRequest(CMD_SET_NETWORK_SELECTION_MODE_AUTOMATIC, null, subId);
+            sendRequest(CMD_SET_NETWORK_SELECTION_MODE_AUTOMATIC, null, subId,
+                    SET_NETWORK_SELECTION_MODE_AUTOMATIC_TIMEOUT_MS);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -5712,7 +5915,7 @@
     @Override
     public String getManualNetworkSelectionPlmn(int subId) {
         TelephonyPermissions
-                    .enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                    .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
                     mApp, subId, "getManualNetworkSelectionPlmn");
 
         final long identity = Binder.clearCallingIdentity();
@@ -5861,10 +6064,9 @@
      */
     @Override
     public void getCallWaitingStatus(int subId, IIntegerConsumer callback) {
-        enforceReadPrivilegedPermission("getCallForwarding");
+        enforceReadPrivilegedPermission("getCallWaitingStatus");
         long identity = Binder.clearCallingIdentity();
         try {
-
             Phone phone = getPhone(subId);
             if (phone == null) {
                 try {
@@ -5874,11 +6076,35 @@
                 }
                 return;
             }
-
-            Consumer<Integer> argument = FunctionalUtils.ignoreRemoteException(callback::accept);
+            CarrierConfigManager configManager = new CarrierConfigManager(phone.getContext());
+            PersistableBundle c = configManager.getConfigForSubId(subId);
+            boolean requireUssd = c.getBoolean(
+                    CarrierConfigManager.KEY_USE_CALL_WAITING_USSD_BOOL, false);
 
             if (DBG) log("getCallWaitingStatus: subId " + subId);
-            sendRequestAsync(CMD_GET_CALL_WAITING, argument, phone, null);
+            if (requireUssd) {
+                CarrierXmlParser carrierXmlParser = new CarrierXmlParser(phone.getContext(),
+                        getSubscriptionCarrierId(subId));
+                String newUssdCommand = "";
+                try {
+                    newUssdCommand = carrierXmlParser.getFeature(
+                            CarrierXmlParser.FEATURE_CALL_WAITING)
+                            .makeCommand(CarrierXmlParser.SsEntry.SSAction.QUERY, null);
+                } catch (NullPointerException e) {
+                    loge("Failed to generate USSD number" + e);
+                }
+                ResultReceiver wrappedCallback = new CallWaitingUssdResultReceiver(
+                        mMainThreadHandler, callback, carrierXmlParser,
+                        CarrierXmlParser.SsEntry.SSAction.QUERY);
+                final String ussdCommand = newUssdCommand;
+                Executors.newSingleThreadExecutor().execute(() -> {
+                    handleUssdRequest(subId, ussdCommand, wrappedCallback);
+                });
+            } else {
+                Consumer<Integer> argument = FunctionalUtils.ignoreRemoteException(
+                        callback::accept);
+                sendRequestAsync(CMD_GET_CALL_WAITING, argument, phone, null);
+            }
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -5904,10 +6130,38 @@
                 return;
             }
 
-            Pair<Boolean, Consumer<Integer>> arguments = Pair.create(enable,
-                    FunctionalUtils.ignoreRemoteException(callback::accept));
+            CarrierConfigManager configManager = new CarrierConfigManager(phone.getContext());
+            PersistableBundle c = configManager.getConfigForSubId(subId);
+            boolean requireUssd = c.getBoolean(
+                    CarrierConfigManager.KEY_USE_CALL_WAITING_USSD_BOOL, false);
 
-            sendRequestAsync(CMD_SET_CALL_WAITING, arguments, phone, null);
+            if (DBG) log("getCallWaitingStatus: subId " + subId);
+            if (requireUssd) {
+                CarrierXmlParser carrierXmlParser = new CarrierXmlParser(phone.getContext(),
+                        getSubscriptionCarrierId(subId));
+                CarrierXmlParser.SsEntry.SSAction ssAction =
+                        enable ? CarrierXmlParser.SsEntry.SSAction.UPDATE_ACTIVATE
+                                : CarrierXmlParser.SsEntry.SSAction.UPDATE_DEACTIVATE;
+                String newUssdCommand = "";
+                try {
+                    newUssdCommand = carrierXmlParser.getFeature(
+                            CarrierXmlParser.FEATURE_CALL_WAITING)
+                            .makeCommand(ssAction, null);
+                } catch (NullPointerException e) {
+                    loge("Failed to generate USSD number" + e);
+                }
+                ResultReceiver wrappedCallback = new CallWaitingUssdResultReceiver(
+                        mMainThreadHandler, callback, carrierXmlParser, ssAction);
+                final String ussdCommand = newUssdCommand;
+                Executors.newSingleThreadExecutor().execute(() -> {
+                    handleUssdRequest(subId, ussdCommand, wrappedCallback);
+                });
+            } else {
+                Pair<Boolean, Consumer<Integer>> arguments = Pair.create(enable,
+                        FunctionalUtils.ignoreRemoteException(callback::accept));
+
+                sendRequestAsync(CMD_SET_CALL_WAITING, arguments, phone, null);
+            }
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -6016,7 +6270,7 @@
     @Override
     public int getAllowedNetworkTypesBitmask(int subId) {
         TelephonyPermissions
-                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "getAllowedNetworkTypesBitmask");
 
         final long identity = Binder.clearCallingIdentity();
@@ -6041,9 +6295,8 @@
     @Override
     public long getAllowedNetworkTypesForReason(int subId,
             @TelephonyManager.AllowedNetworkTypesReason int reason) {
-        TelephonyPermissions
-                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
-                        mApp, subId, "getAllowedNetworkTypesForReason");
+        TelephonyPermissions.enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "getAllowedNetworkTypesForReason");
         final long identity = Binder.clearCallingIdentity();
         try {
             return getPhoneFromSubId(subId).getAllowedNetworkTypes(reason);
@@ -6070,6 +6323,11 @@
             @TelephonyManager.NrDualConnectivityState int nrDualConnectivityState) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "enableNRDualConnectivity");
+        if (!isRadioInterfaceCapabilitySupported(
+                TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE)) {
+            return TelephonyManager.ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED;
+        }
+
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -6090,8 +6348,12 @@
     @Override
     public boolean isNrDualConnectivityEnabled(int subId) {
         TelephonyPermissions
-                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "isNRDualConnectivityEnabled");
+        if (!isRadioInterfaceCapabilitySupported(
+                TelephonyManager.CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE)) {
+            return false;
+        }
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -6105,28 +6367,6 @@
     }
 
     /**
-     * get carrier bandwidth per primary and secondary carrier
-     * @param subId subscription id of the sim card
-     * @return CarrierBandwidth with bandwidth of both primary and secondary carrier..
-     */
-    @Override
-    public CarrierBandwidth getCarrierBandwidth(int subId) {
-        TelephonyPermissions
-                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
-                        mApp, subId, "isNRDualConnectivityEnabled");
-        WorkSource workSource = getWorkSource(Binder.getCallingUid());
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            CarrierBandwidth carrierBandwidth =
-                    getPhoneFromSubId(subId).getCarrierBandwidth();
-            if (DBG) log("getCarrierBandwidth: " + carrierBandwidth);
-            return carrierBandwidth;
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    /**
      * Set the allowed network types of the device and
      * provide the reason triggering the allowed network change.
      *
@@ -6142,14 +6382,23 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setAllowedNetworkTypesForReason");
         if (!TelephonyManager.isValidAllowedNetworkTypesReason(reason)) {
-            Rlog.e(LOG_TAG, "Invalid allowed network type reason: " + reason);
+            loge("setAllowedNetworkTypesForReason: Invalid allowed network type reason: " + reason);
+            return false;
+        }
+        if (!SubscriptionManager.isUsableSubscriptionId(subId)) {
+            loge("setAllowedNetworkTypesForReason: Invalid subscriptionId:" + subId);
             return false;
         }
 
-        if (DBG) {
-            log("setAllowedNetworkTypesForReason: " + reason
-                    + " value: " + allowedNetworkTypes);
+        log("setAllowedNetworkTypesForReason: " + reason + " value: "
+                + TelephonyManager.convertNetworkTypeBitmaskToString(allowedNetworkTypes));
+
+
+        if (allowedNetworkTypes == getPhoneFromSubId(subId).getAllowedNetworkTypes(reason)) {
+            log("setAllowedNetworkTypesForReason: " + reason + "does not change value");
+            return true;
         }
+
         final long identity = Binder.clearCallingIdentity();
         try {
             Boolean success = (Boolean) sendRequest(
@@ -6356,8 +6605,9 @@
 
     private int getCarrierPrivilegeStatusFromCarrierConfigRules(int privilegeFromSim, int uid,
             Phone phone) {
-        if (uid == Process.SYSTEM_UID || uid == Process.PHONE_UID) {
-            // Skip the check if it's one of these special uids
+        if (uid == Process.PHONE_UID) {
+            // Skip the check if it's the phone UID (system UID removed in b/184713596)
+            // TODO (b/184954344): Check for system/phone UID at call site instead of here
             return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
         }
 
@@ -6455,6 +6705,7 @@
 
     @Override
     public int checkCarrierPrivilegesForPackage(int subId, String pkgName) {
+        enforceReadPrivilegedPermission("checkCarrierPrivilegesForPackage");
         if (TextUtils.isEmpty(pkgName)) {
             return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
         }
@@ -6472,6 +6723,7 @@
 
     @Override
     public int checkCarrierPrivilegesForPackageAnyPhone(String pkgName) {
+        // TODO(b/186774706): Remove @RequiresPermission from TelephonyManager API
         if (TextUtils.isEmpty(pkgName))
             return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
         int result = TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
@@ -6495,6 +6747,7 @@
 
     @Override
     public List<String> getCarrierPackageNamesForIntentAndPhone(Intent intent, int phoneId) {
+        enforceReadPrivilegedPermission("getCarrierPackageNamesForIntentAndPhone");
         if (!SubscriptionManager.isValidPhoneId(phoneId)) {
             loge("phoneId " + phoneId + " is not valid.");
             return null;
@@ -6509,6 +6762,7 @@
 
     @Override
     public List<String> getPackagesWithCarrierPrivileges(int phoneId) {
+        enforceReadPrivilegedPermission("getPackagesWithCarrierPrivileges");
         PackageManager pm = mApp.getPackageManager();
         List<String> privilegedPackages = new ArrayList<>();
         List<PackageInfo> packages = null;
@@ -6907,20 +7161,11 @@
     }
 
     @Override
-    public void setRadioCapability(RadioAccessFamily[] rafs) {
-        try {
-            ProxyController.getInstance().setRadioCapability(rafs);
-        } catch (RuntimeException e) {
-            Log.w(LOG_TAG, "setRadioCapability: Runtime Exception");
-        }
-    }
-
-    @Override
     public int getRadioAccessFamily(int phoneId, String callingPackage) {
         Phone phone = PhoneFactory.getPhone(phoneId);
         try {
             TelephonyPermissions
-                    .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                    .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                             mApp, phone.getSubId(), "getRadioAccessFamily");
         } catch (SecurityException e) {
             EventLog.writeEvent(0x534e4554, "150857259", -1, "Missing Permission");
@@ -6933,7 +7178,7 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             TelephonyPermissions
-                    .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                    .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                             mApp, phone.getSubId(), "getRadioAccessFamily");
             raf = ProxyController.getInstance().getRadioAccessFamily(phoneId);
         } finally {
@@ -7220,16 +7465,6 @@
     }
 
     @Override
-    public int getSubIdForPhoneAccount(PhoneAccount phoneAccount) {
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            return PhoneUtils.getSubIdForPhoneAccount(phoneAccount);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
     public int getSubIdForPhoneAccountHandle(
             PhoneAccountHandle phoneAccountHandle, String callingPackage, String callingFeatureId) {
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, getDefaultSubscription(),
@@ -7246,8 +7481,11 @@
 
     @Override
     public @Nullable PhoneAccountHandle getPhoneAccountHandleForSubscriptionId(int subscriptionId) {
-        enforceReadPrivilegedPermission("getPhoneAccountHandleForSubscriptionId, "
-                + "subscriptionId: " + subscriptionId);
+        TelephonyPermissions
+                .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                mApp,
+                subscriptionId,
+                "getPhoneAccountHandleForSubscriptionId, " + "subscriptionId: " + subscriptionId);
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subscriptionId);
@@ -7318,7 +7556,11 @@
         if (mUserManager.hasUserRestriction(UserManager.DISALLOW_NETWORK_RESET)) {
             return;
         }
-
+        Phone defaultPhone = getDefaultPhone();
+        if (defaultPhone != null) {
+            TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+                    mApp, getDefaultPhone().getSubId(), "factoryReset");
+        }
         final long identity = Binder.clearCallingIdentity();
 
         try {
@@ -7327,17 +7569,16 @@
                 setDataEnabledForReason(subId, TelephonyManager.DATA_ENABLED_REASON_USER,
                         getDefaultDataEnabled());
                 setNetworkSelectionModeAutomatic(subId);
-                setAllowedNetworkTypesForReason(subId,
-                        TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER,
-                        RadioAccessFamily.getRafFromNetworkType(getDefaultNetworkType(subId)));
-                setAllowedNetworkTypesForReason(subId,
-                        TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER,
-                        RadioAccessFamily.getRafFromNetworkType(getDefaultNetworkType(subId)));
-                setAllowedNetworkTypesForReason(subId,
-                        TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER,
-                        RadioAccessFamily.getRafFromNetworkType(getDefaultNetworkType(subId)));
+                Phone phone = getPhone(subId);
+                if (phone != null) {
+                    SubscriptionManager.setSubscriptionProperty(subId,
+                            SubscriptionManager.ALLOWED_NETWORK_TYPES,
+                            "user=" + RadioAccessFamily.getRafFromNetworkType(
+                                    RILConstants.PREFERRED_NETWORK_MODE));
+                    phone.loadAllowedNetworksFromSubscriptionDatabase();
+                }
                 setDataRoamingEnabled(subId, getDefaultDataRoamingEnabled(subId));
-                CarrierInfoManager.deleteAllCarrierKeysForImsiEncryption(mApp);
+                getPhone(subId).resetCarrierKeysForImsiEncryption();
             }
             // There has been issues when Sms raw table somehow stores orphan
             // fragments. They lead to garbled message when new fragments come
@@ -7350,12 +7591,17 @@
                 ImsManager.getInstance(mApp, slotId).factoryReset();
             }
 
+            if (defaultPhone == null) {
+                return;
+            }
             // Erase modem config if erase modem on network setting is enabled.
             String configValue = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TELEPHONY,
                     RESET_NETWORK_ERASE_MODEM_CONFIG_ENABLED);
             if (configValue != null && Boolean.parseBoolean(configValue)) {
-              sendEraseModemConfig(getDefaultPhone());
+                sendEraseModemConfig(defaultPhone);
             }
+
+            sendEraseDataInSharedPreferences(defaultPhone);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -8190,7 +8436,7 @@
             mApp.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE,
                     null);
         } catch (Exception e) {
-            TelephonyPermissions.enforeceCallingOrSelfReadPhoneStatePermissionOrCarrierPrivilege(
+            TelephonyPermissions.enforceCallingOrSelfReadPhoneStatePermissionOrCarrierPrivilege(
                     mApp, subId, "isDataRoamingEnabled");
         }
 
@@ -8235,7 +8481,7 @@
     @Override
     public boolean isManualNetworkSelectionAllowed(int subId) {
         TelephonyPermissions
-                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "isManualNetworkSelectionAllowed");
 
         boolean isAllowed = true;
@@ -8521,7 +8767,7 @@
     @Override
     public int getCdmaRoamingMode(int subId) {
         TelephonyPermissions
-                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getCdmaRoamingMode");
 
         final long identity = Binder.clearCallingIdentity();
@@ -8548,7 +8794,7 @@
     @Override
     public int getCdmaSubscriptionMode(int subId) {
         TelephonyPermissions
-                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "getCdmaSubscriptionMode");
 
         final long identity = Binder.clearCallingIdentity();
@@ -8602,7 +8848,7 @@
         final Phone defaultPhone = getDefaultPhone();
         if (!exactMatch) {
             TelephonyPermissions
-                    .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                    .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                             mApp, defaultPhone.getSubId(), "isEmergencyNumber(Potential)");
         }
         final long identity = Binder.clearCallingIdentity();
@@ -8621,6 +8867,31 @@
     }
 
     /**
+     * Start emergency callback mode for GsmCdmaPhone for testing.
+     */
+    @Override
+    public void startEmergencyCallbackMode() {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+                "startEmergencyCallbackMode");
+        enforceModifyPermission();
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            for (Phone phone : PhoneFactory.getPhones()) {
+                Rlog.d(LOG_TAG, "startEmergencyCallbackMode phone type: " + phone.getPhoneType());
+                if (phone != null && ((phone.getPhoneType() == PHONE_TYPE_GSM)
+                        || (phone.getPhoneType() == PHONE_TYPE_CDMA))) {
+                    GsmCdmaPhone gsmCdmaPhone = (GsmCdmaPhone) phone;
+                    gsmCdmaPhone.obtainMessage(
+                            GsmCdmaPhone.EVENT_EMERGENCY_CALLBACK_MODE_ENTER).sendToTarget();
+                    Rlog.d(LOG_TAG, "startEmergencyCallbackMode: triggered");
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
      * Update emergency number list for test mode.
      */
     @Override
@@ -9068,14 +9339,16 @@
     @Override
     public List<RadioAccessSpecifier> getSystemSelectionChannels(int subId) {
         TelephonyPermissions
-                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, subId, "getSystemSelectionChannels");
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
         final long identity = Binder.clearCallingIdentity();
         try {
-            List<RadioAccessSpecifier> specifiers =
-                    (List<RadioAccessSpecifier>) sendRequest(CMD_GET_SYSTEM_SELECTION_CHANNELS,
-                    null, subId, workSource);
+            Object result = sendRequest(CMD_GET_SYSTEM_SELECTION_CHANNELS, null, subId, workSource);
+            if (result instanceof IllegalStateException) {
+                throw (IllegalStateException) result;
+            }
+            List<RadioAccessSpecifier> specifiers = (List<RadioAccessSpecifier>) result;
             if (DBG) log("getSystemSelectionChannels: " + specifiers);
             return specifiers;
         } finally {
@@ -9174,7 +9447,7 @@
     }
 
     @Override
-    public void setMobileDataPolicyEnabledStatus(int subscriptionId, int policy,
+    public void setMobileDataPolicyEnabled(int subscriptionId, int policy,
             boolean enabled) {
         enforceModifyPermission();
 
@@ -9404,22 +9677,23 @@
 
     @Override
     public boolean isRadioInterfaceCapabilitySupported(
-            @NonNull @TelephonyManager.RadioInterfaceCapability String capability) {
-        RadioInterfaceCapabilities radioInterfaceCapabilities =
-                mPhoneConfigurationManager.getRadioInterfaceCapabilities();
+            final @NonNull @TelephonyManager.RadioInterfaceCapability String capability) {
+        Set<String> radioInterfaceCapabilities =
+                mRadioInterfaceCapabilities.getCapabilities();
         if (radioInterfaceCapabilities == null) {
             throw new RuntimeException("radio interface capabilities are not available");
-        } else {
-            return radioInterfaceCapabilities.isSupported(capability);
         }
+        return radioInterfaceCapabilities.contains(capability);
     }
 
     @Override
     public void bootstrapAuthenticationRequest(int subId, int appType, Uri nafUrl,
             UaSecurityProtocolIdentifier securityProtocol,
-            boolean forceBootStrapping, IBootstrapAuthenticationCallback callback)
-            throws RemoteException {
-        enforceModifyPermission();
+            boolean forceBootStrapping, IBootstrapAuthenticationCallback callback) {
+        TelephonyPermissions.enforceAnyPermissionGrantedOrCarrierPrivileges(mApp, subId,
+                Binder.getCallingUid(), "bootstrapAuthenticationRequest",
+                Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION,
+                Manifest.permission.MODIFY_PHONE_STATE);
         if (DBG) {
             log("bootstrapAuthenticationRequest, subId:" + subId + ", appType:"
                     + appType + ", NAF:" + nafUrl + ", sp:" + securityProtocol
@@ -9473,6 +9747,13 @@
 
     private int handleDataThrottlingRequest(int subId,
             DataThrottlingRequest dataThrottlingRequest) {
+        boolean isDataThrottlingSupported = isRadioInterfaceCapabilitySupported(
+                TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING);
+        if (!isDataThrottlingSupported && dataThrottlingRequest.getDataThrottlingAction()
+                != DataThrottlingRequest.DATA_THROTTLING_ACTION_NO_DATA_THROTTLING) {
+            throw new IllegalArgumentException("modem does not support data throttling");
+        }
+
         // Ensure that radio is on. If not able to power on due to phone being unavailable, return
         // THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE.
         if (!setRadioPowerForThermal(subId, true)) {
@@ -9481,12 +9762,52 @@
 
         setDataEnabledForReason(subId, TelephonyManager.DATA_ENABLED_REASON_THERMAL, true);
 
-        int thermalMitigationResult =
+        if (isDataThrottlingSupported) {
+            int thermalMitigationResult =
                 (int) sendRequest(CMD_SET_DATA_THROTTLING, dataThrottlingRequest, subId);
-        if (thermalMitigationResult == SET_DATA_THROTTLING_MODEM_THREW_INVALID_PARAMS) {
-            throw new IllegalArgumentException("modem returned INVALID_ARGUMENTS");
+            if (thermalMitigationResult == SET_DATA_THROTTLING_MODEM_THREW_INVALID_PARAMS) {
+                throw new IllegalArgumentException("modem returned INVALID_ARGUMENTS");
+            } else if (thermalMitigationResult
+                    == MODEM_DOES_NOT_SUPPORT_DATA_THROTTLING_ERROR_CODE) {
+                log("Modem likely does not support data throttling on secondary carrier. Data " +
+                        "throttling action = " + dataThrottlingRequest.getDataThrottlingAction());
+                return TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_ERROR;
+            }
+            return thermalMitigationResult;
         }
-        return thermalMitigationResult;
+
+        return TelephonyManager.THERMAL_MITIGATION_RESULT_SUCCESS;
+    }
+
+    private static List<String> getThermalMitigationAllowlist(Context context) {
+        if (sThermalMitigationAllowlistedPackages.isEmpty()) {
+            for (String pckg : context.getResources()
+                    .getStringArray(R.array.thermal_mitigation_allowlisted_packages)) {
+                sThermalMitigationAllowlistedPackages.add(pckg);
+            }
+        }
+
+        return sThermalMitigationAllowlistedPackages;
+    }
+
+    /**
+     * Used by shell commands to add an authorized package name for thermal mitigation.
+     * @param packageName name of package to be allowlisted
+     * @param context
+     */
+    static void addPackageToThermalMitigationAllowlist(String packageName, Context context) {
+        sThermalMitigationAllowlistedPackages = getThermalMitigationAllowlist(context);
+        sThermalMitigationAllowlistedPackages.add(packageName);
+    }
+
+    /**
+     * Used by shell commands to remove an authorized package name for thermal mitigation.
+     * @param packageName name of package to remove from allowlist
+     * @param context
+     */
+    static void removePackageFromThermalMitigationAllowlist(String packageName, Context context) {
+        sThermalMitigationAllowlistedPackages = getThermalMitigationAllowlist(context);
+        sThermalMitigationAllowlistedPackages.remove(packageName);
     }
 
     /**
@@ -9494,6 +9815,7 @@
      *
      * @param subId the id of the subscription.
      * @param thermalMitigationRequest holds all necessary information to be passed down to modem.
+     * @param callingPackage the package name of the calling package.
      *
      * @return thermalMitigationResult enum as defined in android.telephony.Annotation.
      */
@@ -9501,9 +9823,17 @@
     @ThermalMitigationResult
     public int sendThermalMitigationRequest(
             int subId,
-            ThermalMitigationRequest thermalMitigationRequest) throws IllegalArgumentException {
+            ThermalMitigationRequest thermalMitigationRequest,
+            String callingPackage) throws IllegalArgumentException {
         enforceModifyPermission();
 
+        mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+        if (!getThermalMitigationAllowlist(getDefaultPhone().getContext())
+                .contains(callingPackage)) {
+            throw new SecurityException("Calling package must be configured in the device config. "
+                    + "calling package: " + callingPackage);
+        }
+
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
         final long identity = Binder.clearCallingIdentity();
 
@@ -9695,7 +10025,10 @@
      */
     @Override
     public boolean isRcsVolteSingleRegistrationCapable(int subId) {
-        enforceReadPrivilegedPermission("isRcsVolteSingleRegistrationCapable");
+        TelephonyPermissions.enforceAnyPermissionGrantedOrCarrierPrivileges(mApp, subId,
+                Binder.getCallingUid(), "isRcsVolteSingleRegistrationCapable",
+                Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION,
+                permission.READ_PRIVILEGED_PHONE_STATE);
 
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
@@ -9717,9 +10050,12 @@
      * Register RCS provisioning callback.
      */
     @Override
-    public void registerRcsProvisioningChangedCallback(int subId,
+    public void registerRcsProvisioningCallback(int subId,
             IRcsConfigCallback callback) {
-        enforceReadPrivilegedPermission("registerRcsProvisioningChangedCallback");
+        TelephonyPermissions.enforceAnyPermissionGrantedOrCarrierPrivileges(mApp, subId,
+                Binder.getCallingUid(), "registerRcsProvisioningCallback",
+                Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION,
+                permission.READ_PRIVILEGED_PHONE_STATE);
 
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
@@ -9732,7 +10068,7 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             if (!RcsProvisioningMonitor.getInstance()
-                    .registerRcsProvisioningChangedCallback(subId, callback)) {
+                    .registerRcsProvisioningCallback(subId, callback)) {
                 throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
                         "Service not available for the subscription.");
             }
@@ -9745,9 +10081,12 @@
      * Unregister RCS provisioning callback.
      */
     @Override
-    public void unregisterRcsProvisioningChangedCallback(int subId,
+    public void unregisterRcsProvisioningCallback(int subId,
             IRcsConfigCallback callback) {
-        enforceReadPrivilegedPermission("unregisterRcsProvisioningChangedCallback");
+        TelephonyPermissions.enforceAnyPermissionGrantedOrCarrierPrivileges(mApp, subId,
+                Binder.getCallingUid(), "unregisterRcsProvisioningCallback",
+                Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION,
+                permission.READ_PRIVILEGED_PHONE_STATE);
 
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
@@ -9760,7 +10099,7 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             RcsProvisioningMonitor.getInstance()
-                    .unregisterRcsProvisioningChangedCallback(subId, callback);
+                    .unregisterRcsProvisioningCallback(subId, callback);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -9770,8 +10109,9 @@
      * trigger RCS reconfiguration.
      */
     public void triggerRcsReconfiguration(int subId) {
-        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
-                mApp, subId, "triggerRcsReconfiguration");
+        TelephonyPermissions.enforceAnyPermissionGranted(mApp, Binder.getCallingUid(),
+                "triggerRcsReconfiguration",
+                Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION);
 
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
@@ -9793,8 +10133,9 @@
      * Provide the client configuration parameters of the RCS application.
      */
     public void setRcsClientConfiguration(int subId, RcsClientConfiguration rcc) {
-        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
-                mApp, subId, "setRcsClientConfiguration");
+        TelephonyPermissions.enforceAnyPermissionGranted(mApp, Binder.getCallingUid(),
+                "setRcsClientConfiguration",
+                Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION);
 
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
@@ -9821,6 +10162,28 @@
     }
 
     /**
+     * Enables or disables the test mode for RCS VoLTE single registration.
+     */
+    @Override
+    public void setRcsSingleRegistrationTestModeEnabled(boolean enabled) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+                "setRcsSingleRegistrationTestModeEnabled");
+
+        RcsProvisioningMonitor.getInstance().setTestModeEnabled(enabled);
+    }
+
+    /**
+     * Gets the test mode for RCS VoLTE single registration.
+     */
+    @Override
+    public boolean getRcsSingleRegistrationTestModeEnabled() {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+                "getRcsSingleRegistrationTestModeEnabled");
+
+        return RcsProvisioningMonitor.getInstance().getTestModeEnabled();
+    }
+
+    /**
      * Overrides the config of RCS VoLTE single registration enabled for the device.
      */
     @Override
@@ -9832,6 +10195,7 @@
         Boolean enabled = "NULL".equalsIgnoreCase(enabledStr) ? null
                 : Boolean.parseBoolean(enabledStr);
         RcsProvisioningMonitor.getInstance().overrideDeviceSingleRegistrationEnabled(enabled);
+        mApp.imsRcsController.setDeviceSingleRegistrationSupportOverride(enabled);
     }
 
     /**
@@ -9842,7 +10206,7 @@
     @Override
     public void sendDeviceToDeviceMessage(int message, int value) {
         TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
-                "setCarrierSingleRegistrationEnabledOverride");
+                "sendDeviceToDeviceMessage");
         enforceModifyPermission();
 
         final long identity = Binder.clearCallingIdentity();
@@ -9859,6 +10223,55 @@
         }
     }
 
+    /**
+     * Sets the specified device to device transport active.
+     * @param transport The transport to set active.
+     */
+    @Override
+    public void setActiveDeviceToDeviceTransport(@NonNull String transport) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+                "setActiveDeviceToDeviceTransport");
+        enforceModifyPermission();
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            TelephonyConnectionService service =
+                    TelecomAccountRegistry.getInstance(null).getTelephonyConnectionService();
+            if (service == null) {
+                Rlog.e(LOG_TAG, "setActiveDeviceToDeviceTransport: not in a call.");
+                return;
+            }
+            service.setActiveDeviceToDeviceTransport(transport);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void setDeviceToDeviceForceEnabled(boolean isForceEnabled) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+                "setDeviceToDeviceForceEnabled");
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            Arrays.stream(PhoneFactory.getPhones()).forEach(
+                    p -> {
+                        Phone thePhone = p.getImsPhone();
+                        if (thePhone != null && thePhone instanceof ImsPhone) {
+                            ImsPhone imsPhone = (ImsPhone) thePhone;
+                            CallTracker tracker = imsPhone.getCallTracker();
+                            if (tracker != null && tracker instanceof ImsPhoneCallTracker) {
+                                ImsPhoneCallTracker imsPhoneCallTracker =
+                                        (ImsPhoneCallTracker) tracker;
+                                imsPhoneCallTracker.setDeviceToDeviceForceEnabled(isForceEnabled);
+                            }
+                        }
+                    }
+            );
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
 
     /**
      * Gets the config of RCS VoLTE single registration enabled for the device.
@@ -9894,6 +10307,30 @@
     }
 
     /**
+     * Overrides the ims feature validation result
+     */
+    @Override
+    public boolean setImsFeatureValidationOverride(int subId, String enabledStr) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+                "setImsFeatureValidationOverride");
+
+        Boolean enabled = "NULL".equalsIgnoreCase(enabledStr) ? null
+                : Boolean.parseBoolean(enabledStr);
+        return RcsProvisioningMonitor.getInstance().overrideImsFeatureValidation(
+                subId, enabled);
+    }
+
+    /**
+     * Gets the ims feature validation override value
+     */
+    @Override
+    public boolean getImsFeatureValidationOverride(int subId) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+                "getImsFeatureValidationOverride");
+        return RcsProvisioningMonitor.getInstance().getImsFeatureValidationOverride(subId);
+    }
+
+    /**
      * Get the mobile provisioning url that is used to launch a browser to allow users to manage
      * their mobile plan.
      */
@@ -9939,6 +10376,159 @@
     }
 
     @Override
+    public boolean getDeviceUceEnabled() {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getDeviceUceEnabled");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mApp.getDeviceUceEnabled();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void setDeviceUceEnabled(boolean isEnabled) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setDeviceUceEnabled");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mApp.setDeviceUceEnabled(isEnabled);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Add new feature tags to the Set used to calculate the capabilities in PUBLISH.
+     * @return current RcsContactUceCapability instance that will be used for PUBLISH.
+     */
+    // Used for SHELL command only right now.
+    @Override
+    public RcsContactUceCapability addUceRegistrationOverrideShell(int subId,
+            List<String> featureTags) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+                "addUceRegistrationOverrideShell");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mApp.imsRcsController.addUceRegistrationOverrideShell(subId,
+                    new ArraySet<>(featureTags));
+        } catch (ImsException e) {
+            throw new ServiceSpecificException(e.getCode(), e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Remove existing feature tags to the Set used to calculate the capabilities in PUBLISH.
+     * @return current RcsContactUceCapability instance that will be used for PUBLISH.
+     */
+    // Used for SHELL command only right now.
+    @Override
+    public RcsContactUceCapability removeUceRegistrationOverrideShell(int subId,
+            List<String> featureTags) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+                "removeUceRegistrationOverrideShell");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mApp.imsRcsController.removeUceRegistrationOverrideShell(subId,
+                    new ArraySet<>(featureTags));
+        } catch (ImsException e) {
+            throw new ServiceSpecificException(e.getCode(), e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Clear all overrides in the Set used to calculate the capabilities in PUBLISH.
+     * @return current RcsContactUceCapability instance that will be used for PUBLISH.
+     */
+    // Used for SHELL command only right now.
+    @Override
+    public RcsContactUceCapability clearUceRegistrationOverrideShell(int subId) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+                "clearUceRegistrationOverrideShell");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mApp.imsRcsController.clearUceRegistrationOverrideShell(subId);
+        } catch (ImsException e) {
+            throw new ServiceSpecificException(e.getCode(), e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * @return current RcsContactUceCapability instance that will be used for PUBLISH.
+     */
+    // Used for SHELL command only right now.
+    @Override
+    public RcsContactUceCapability getLatestRcsContactUceCapabilityShell(int subId) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+                "getLatestRcsContactUceCapabilityShell");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mApp.imsRcsController.getLatestRcsContactUceCapabilityShell(subId);
+        } catch (ImsException e) {
+            throw new ServiceSpecificException(e.getCode(), e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Returns the last PIDF XML sent to the network during the last PUBLISH or "none" if the
+     * device does not have an active PUBLISH.
+     */
+    // Used for SHELL command only right now.
+    @Override
+    public String getLastUcePidfXmlShell(int subId) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "uceGetLastPidfXml");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mApp.imsRcsController.getLastUcePidfXmlShell(subId);
+        } catch (ImsException e) {
+            throw new ServiceSpecificException(e.getCode(), e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Remove UCE requests cannot be sent to the network status.
+     */
+    // Used for SHELL command only right now.
+    @Override
+    public boolean removeUceRequestDisallowedStatus(int subId) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "uceRemoveDisallowedStatus");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mApp.imsRcsController.removeUceRequestDisallowedStatus(subId);
+        } catch (ImsException e) {
+            throw new ServiceSpecificException(e.getCode(), e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Remove UCE requests cannot be sent to the network status.
+     */
+    // Used for SHELL command only.
+    @Override
+    public boolean setCapabilitiesRequestTimeout(int subId, long timeoutAfterMs) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setCapRequestTimeout");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mApp.imsRcsController.setCapabilitiesRequestTimeout(subId, timeoutAfterMs);
+        } catch (ImsException e) {
+            throw new ServiceSpecificException(e.getCode(), e.getMessage());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
     public void setSignalStrengthUpdateRequest(int subId, SignalStrengthUpdateRequest request,
             String callingPackage) {
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
@@ -10044,4 +10634,40 @@
             Binder.restoreCallingIdentity(identity);
         }
     }
+
+    /**
+     * Prepare TelephonyManager for an unattended reboot. The reboot is
+     * required to be done shortly after the API is invoked.
+     */
+    @Override
+    @TelephonyManager.PrepareUnattendedRebootResult
+    public int prepareForUnattendedReboot() {
+        enforceRebootPermission();
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return (int) sendRequest(CMD_PREPARE_UNATTENDED_REBOOT, null);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Request to get the current slicing configuration including URSP rules and
+     * NSSAIs (configured, allowed and rejected).
+     *
+     * Requires carrier privileges or READ_PRIVILEGED_PHONE_STATE permission.
+     */
+    @Override
+    public void getSlicingConfig(ResultReceiver callback) {
+        enforceReadPrivilegedPermission("getSlicingConfig");
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            Phone phone = getDefaultPhone();
+            sendRequestAsync(CMD_GET_SLICING_CONFIG, callback, phone, null);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
 }
diff --git a/src/com/android/phone/RcsProvisioningMonitor.java b/src/com/android/phone/RcsProvisioningMonitor.java
index 7b51eeb..4c5a8e9 100644
--- a/src/com/android/phone/RcsProvisioningMonitor.java
+++ b/src/com/android/phone/RcsProvisioningMonitor.java
@@ -16,12 +16,14 @@
 
 package com.android.phone;
 
+import android.Manifest;
 import android.app.role.OnRoleHoldersChangedListener;
 import android.app.role.RoleManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -37,15 +39,20 @@
 import android.telephony.ims.RcsConfig;
 import android.telephony.ims.aidl.IImsConfig;
 import android.telephony.ims.aidl.IRcsConfigCallback;
-import android.telephony.ims.feature.ImsFeature;
 import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.SparseArray;
 
+import com.android.ims.FeatureConnector;
+import com.android.ims.FeatureUpdates;
+import com.android.ims.RcsFeatureManager;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.util.HandlerExecutor;
 import com.android.internal.util.CollectionUtils;
 import com.android.telephony.Rlog;
 
+import java.util.Arrays;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
@@ -65,6 +72,8 @@
     private static final int EVENT_RECONFIG_REQUEST = 5;
     private static final int EVENT_DEVICE_CONFIG_OVERRIDE = 6;
     private static final int EVENT_CARRIER_CONFIG_OVERRIDE = 7;
+    private static final int EVENT_RESET = 8;
+    private static final int EVENT_FEATURE_ENABLED_OVERRIDE = 9;
 
     private final PhoneGlobals mPhone;
     private final Handler mHandler;
@@ -74,13 +83,18 @@
     private Boolean mDeviceSingleRegistrationEnabledOverride;
     private final HashMap<Integer, Boolean> mCarrierSingleRegistrationEnabledOverride =
             new HashMap<>();
+    private final ConcurrentHashMap<Integer, Boolean> mImsFeatureValidationOverride =
+            new ConcurrentHashMap<>();
     private String mDmaPackageName;
+    private final SparseArray<RcsFeatureListener> mRcsFeatureListeners = new SparseArray<>();
+    private volatile boolean mTestModeEnabled;
 
     private final CarrierConfigManager mCarrierConfigManager;
     private final DmaChangedListener mDmaChangedListener;
     private final SubscriptionManager mSubscriptionManager;
     private final TelephonyRegistryManager mTelephonyRegistryManager;
     private final RoleManagerAdapter mRoleManager;
+    private FeatureConnectorFactory<RcsFeatureManager> mFeatureFactory;
 
     private static RcsProvisioningMonitor sInstance;
 
@@ -145,6 +159,7 @@
 
         @Override
         public void handleMessage(Message msg) {
+            logv("handleMessage: " + msg);
             switch (msg.what) {
                 case EVENT_SUB_CHANGED:
                     onSubChanged();
@@ -176,6 +191,9 @@
                         onCarrierConfigChange();
                     }
                     break;
+                case EVENT_RESET:
+                    reset();
+                    break;
                 default:
                     loge("Unhandled event " + msg.what);
             }
@@ -186,13 +204,20 @@
         private int mSubId;
         private volatile int mSingleRegistrationCapability;
         private volatile byte[] mConfig;
-        private HashSet<IRcsConfigCallback> mRcsConfigCallbacks;
+        private ArraySet<IRcsConfigCallback> mRcsConfigCallbacks;
+        private IImsConfig mIImsConfig;
+        private boolean mHasReconfigRequest;
 
         RcsProvisioningInfo(int subId, int singleRegistrationCapability, byte[] config) {
             mSubId = subId;
             mSingleRegistrationCapability = singleRegistrationCapability;
             mConfig = config;
-            mRcsConfigCallbacks = new HashSet<>();
+            mRcsConfigCallbacks = new ArraySet<>();
+            registerRcsFeatureListener(this);
+        }
+
+        int getSubId() {
+            return mSubId;
         }
 
         void setSingleRegistrationCapability(int singleRegistrationCapability) {
@@ -204,7 +229,14 @@
         }
 
         void setConfig(byte[] config) {
-            mConfig = config;
+            if (!Arrays.equals(mConfig, config)) {
+                mConfig = config;
+                if (mConfig != null) {
+                    notifyRcsAutoConfigurationReceived();
+                } else {
+                    notifyRcsAutoConfigurationRemoved();
+                }
+            }
         }
 
         byte[] getConfig() {
@@ -212,15 +244,14 @@
         }
 
         boolean addRcsConfigCallback(IRcsConfigCallback cb) {
-            IImsConfig imsConfig = getIImsConfig(mSubId, ImsFeature.FEATURE_RCS);
-            if (imsConfig == null) {
+            if (mIImsConfig == null) {
                 logd("fail to addRcsConfigCallback as imsConfig is null");
                 return false;
             }
 
             synchronized (mRcsConfigCallbacks) {
                 try {
-                    imsConfig.addRcsConfigCallback(cb);
+                    mIImsConfig.addRcsConfigCallback(cb);
                 } catch (RemoteException e) {
                     loge("fail to addRcsConfigCallback due to " + e);
                     return false;
@@ -232,12 +263,11 @@
 
         boolean removeRcsConfigCallback(IRcsConfigCallback cb) {
             boolean result = true;
-            IImsConfig imsConfig = getIImsConfig(mSubId, ImsFeature.FEATURE_RCS);
 
             synchronized (mRcsConfigCallbacks) {
-                if (imsConfig != null) {
+                if (mIImsConfig != null) {
                     try {
-                        imsConfig.removeRcsConfigCallback(cb);
+                        mIImsConfig.removeRcsConfigCallback(cb);
                     } catch (RemoteException e) {
                         loge("fail to removeRcsConfigCallback due to " + e);
                     }
@@ -256,16 +286,89 @@
             return result;
         }
 
+        void triggerRcsReconfiguration() {
+            if (mIImsConfig != null) {
+                try {
+                    logv("triggerRcsReconfiguration for sub:" + mSubId);
+                    mIImsConfig.triggerRcsReconfiguration();
+                    mHasReconfigRequest = false;
+                } catch (RemoteException e) {
+                    loge("triggerRcsReconfiguration failed due to " + e);
+                }
+            } else {
+                logd("triggerRcsReconfiguration failed due to IImsConfig null.");
+                mHasReconfigRequest = true;
+            }
+        }
+
+        void destroy() {
+            unregisterRcsFeatureListener(this);
+            clear();
+            mIImsConfig = null;
+            mRcsConfigCallbacks = null;
+        }
+
         void clear() {
             setConfig(null);
+            clearCallbacks();
+        }
+
+        void onRcsStatusChanged(IImsConfig binder) {
+            logv("onRcsStatusChanged for sub:" + mSubId + ", IImsConfig?" + binder);
+            if (mIImsConfig != binder) {
+                mIImsConfig = binder;
+                if (mIImsConfig != null) {
+                    if (mHasReconfigRequest) {
+                        triggerRcsReconfiguration();
+                    } else {
+                        notifyRcsAutoConfigurationReceived();
+                    }
+                } else {
+                    // clear callbacks if rcs disconnected
+                    clearCallbacks();
+                }
+            }
+        }
+
+        private void notifyRcsAutoConfigurationReceived() {
+            if (mConfig == null) {
+                logd("Rcs config is null for sub : " + mSubId);
+                return;
+            }
+
+            if (mIImsConfig != null) {
+                try {
+                    logv("notifyRcsAutoConfigurationReceived for sub:" + mSubId);
+                    mIImsConfig.notifyRcsAutoConfigurationReceived(mConfig, false);
+                } catch (RemoteException e) {
+                    loge("notifyRcsAutoConfigurationReceived failed due to " + e);
+                }
+            } else {
+                logd("notifyRcsAutoConfigurationReceived failed due to IImsConfig null.");
+            }
+        }
+
+        private void notifyRcsAutoConfigurationRemoved() {
+            if (mIImsConfig != null) {
+                try {
+                    logv("notifyRcsAutoConfigurationRemoved for sub:" + mSubId);
+                    mIImsConfig.notifyRcsAutoConfigurationRemoved();
+                } catch (RemoteException e) {
+                    loge("notifyRcsAutoConfigurationRemoved failed due to " + e);
+                }
+            } else {
+                logd("notifyRcsAutoConfigurationRemoved failed due to IImsConfig null.");
+            }
+        }
+
+        private void clearCallbacks() {
             synchronized (mRcsConfigCallbacks) {
-                IImsConfig imsConfig = getIImsConfig(mSubId, ImsFeature.FEATURE_RCS);
                 Iterator<IRcsConfigCallback> it = mRcsConfigCallbacks.iterator();
                 while (it.hasNext()) {
                     IRcsConfigCallback cb = it.next();
-                    if (imsConfig != null) {
+                    if (mIImsConfig != null) {
                         try {
-                            imsConfig.removeRcsConfigCallback(cb);
+                            mIImsConfig.removeRcsConfigCallback(cb);
                         } catch (RemoteException e) {
                             loge("fail to removeRcsConfigCallback due to " + e);
                         }
@@ -282,7 +385,61 @@
     }
 
     @VisibleForTesting
-    public RcsProvisioningMonitor(PhoneGlobals app, Looper looper, RoleManagerAdapter roleManager) {
+    public interface FeatureConnectorFactory<U extends FeatureUpdates> {
+        /**
+         * @return a {@link FeatureConnector} associated for the given {@link FeatureUpdates}
+         * and slot index.
+         */
+        FeatureConnector<U> create(Context context, int slotIndex,
+                FeatureConnector.Listener<U> listener, Executor executor, String logPrefix);
+    }
+
+    private final class RcsFeatureListener implements FeatureConnector.Listener<RcsFeatureManager> {
+        private final ArraySet<RcsProvisioningInfo> mRcsProvisioningInfos = new ArraySet<>();
+        private RcsFeatureManager mRcsFeatureManager;
+        private FeatureConnector<RcsFeatureManager> mConnector;
+
+        RcsFeatureListener(int slotId) {
+            mConnector = mFeatureFactory.create(
+                    mPhone, slotId, this, new HandlerExecutor(mHandler), TAG);
+            mConnector.connect();
+        }
+
+        void destroy() {
+            mConnector.disconnect();
+            mConnector = null;
+            mRcsFeatureManager = null;
+            mRcsProvisioningInfos.clear();
+        }
+
+        void addRcsProvisioningInfo(RcsProvisioningInfo info) {
+            if (!mRcsProvisioningInfos.contains(info)) {
+                mRcsProvisioningInfos.add(info);
+                info.onRcsStatusChanged(mRcsFeatureManager == null ? null
+                        : mRcsFeatureManager.getConfig());
+            }
+        }
+
+        void removeRcsProvisioningInfo(RcsProvisioningInfo info) {
+            mRcsProvisioningInfos.remove(info);
+        }
+
+        @Override
+        public void connectionReady(RcsFeatureManager manager) {
+            mRcsFeatureManager = manager;
+            mRcsProvisioningInfos.forEach(v -> v.onRcsStatusChanged(manager.getConfig()));
+        }
+
+        @Override
+        public void connectionUnavailable(int reason) {
+            mRcsFeatureManager = null;
+            mRcsProvisioningInfos.forEach(v -> v.onRcsStatusChanged(null));
+        }
+    }
+
+    @VisibleForTesting
+    public RcsProvisioningMonitor(PhoneGlobals app, Looper looper, RoleManagerAdapter roleManager,
+            FeatureConnectorFactory<RcsFeatureManager> factory) {
         mPhone = app;
         mHandler = new MyHandler(looper);
         mCarrierConfigManager = mPhone.getSystemService(CarrierConfigManager.class);
@@ -291,15 +448,9 @@
         mRoleManager = roleManager;
         mDmaPackageName = getDmaPackageName();
         logv("DMA is " + mDmaPackageName);
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
-        mPhone.registerReceiver(mReceiver, filter);
-        mTelephonyRegistryManager.addOnSubscriptionsChangedListener(
-                mSubChangedListener, mSubChangedListener.getHandlerExecutor());
         mDmaChangedListener = new DmaChangedListener();
-        mDmaChangedListener.register();
-        //initialize configs for all active sub
-        onSubChanged();
+        mFeatureFactory = factory;
+        init();
     }
 
     /**
@@ -311,7 +462,7 @@
             HandlerThread handlerThread = new HandlerThread(TAG);
             handlerThread.start();
             sInstance = new RcsProvisioningMonitor(app, handlerThread.getLooper(),
-                    new RoleManagerAdapterImpl(app));
+                    new RoleManagerAdapterImpl(app), RcsFeatureManager::getConnector);
         }
         return sInstance;
     }
@@ -323,15 +474,44 @@
         return sInstance;
     }
 
+    private void init() {
+        logd("init.");
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+        mPhone.registerReceiver(mReceiver, filter);
+        mTelephonyRegistryManager.addOnSubscriptionsChangedListener(
+                mSubChangedListener, mSubChangedListener.getHandlerExecutor());
+        mDmaChangedListener.register();
+        //initialize configs for all active sub
+        onSubChanged();
+    }
+
+    private void release() {
+        logd("release.");
+        mDmaChangedListener.unregister();
+        mTelephonyRegistryManager.removeOnSubscriptionsChangedListener(mSubChangedListener);
+        mPhone.unregisterReceiver(mReceiver);
+        for (int i = 0; i < mRcsFeatureListeners.size(); i++) {
+            mRcsFeatureListeners.valueAt(i).destroy();
+        }
+        mRcsFeatureListeners.clear();
+        mRcsProvisioningInfos.forEach((k, v)->v.destroy());
+        mRcsProvisioningInfos.clear();
+        mCarrierSingleRegistrationEnabledOverride.clear();
+    }
+
+    private void reset() {
+        release();
+        init();
+    }
+
     /**
      * destroy the instance
      */
     @VisibleForTesting
     public void destroy() {
         logd("destroy it.");
-        mDmaChangedListener.unregister();
-        mTelephonyRegistryManager.removeOnSubscriptionsChangedListener(mSubChangedListener);
-        mPhone.unregisterReceiver(mReceiver);
+        release();
         mHandler.getLooper().quit();
     }
 
@@ -359,8 +539,19 @@
      */
     public boolean isRcsVolteSingleRegistrationEnabled(int subId) {
         if (mRcsProvisioningInfos.containsKey(subId)) {
-            return mRcsProvisioningInfos.get(subId).getSingleRegistrationCapability()
-                    == ProvisioningManager.STATUS_CAPABLE;
+            if (mRcsProvisioningInfos.get(subId).getSingleRegistrationCapability()
+                    == ProvisioningManager.STATUS_CAPABLE) {
+                try {
+                    RcsConfig rcsConfig = new RcsConfig(getConfig(subId));
+                    return rcsConfig.isRcsVolteSingleRegistrationSupported(
+                            mPhone.getPhone(subId).getServiceState().getRoaming());
+                } catch (IllegalArgumentException e) {
+                    logd("fail to get rcs config for sub:" + subId);
+                } catch (NullPointerException e) {
+                    // should not happen
+                    logd("fail to get roaming state for sub: " + subId);
+                }
+            }
         }
         return false;
     }
@@ -381,13 +572,13 @@
     }
 
     /**
-     * Called when the application registers rcs provisioning changed callback
+     * Called when the application registers rcs provisioning callback
      */
-    public boolean registerRcsProvisioningChangedCallback(int subId, IRcsConfigCallback cb) {
+    public boolean registerRcsProvisioningCallback(int subId, IRcsConfigCallback cb) {
         RcsProvisioningInfo info = mRcsProvisioningInfos.get(subId);
         // should not happen in normal case
         if (info == null) {
-            logd("fail to register rcs provisioning changed due to subscription unavailable");
+            logd("fail to register rcs provisioning callback due to subscription unavailable");
             return false;
         }
 
@@ -395,9 +586,9 @@
     }
 
     /**
-     * Called when the application unregisters rcs provisioning changed callback
+     * Called when the application unregisters rcs provisioning callback
      */
-    public boolean unregisterRcsProvisioningChangedCallback(int subId, IRcsConfigCallback cb) {
+    public boolean unregisterRcsProvisioningCallback(int subId, IRcsConfigCallback cb) {
         RcsProvisioningInfo info = mRcsProvisioningInfos.get(subId);
         // should not happen in normal case
         if (info == null) {
@@ -409,6 +600,27 @@
     }
 
     /**
+     * Enables or disables test mode.
+     *
+     * <p> If test mode is enabled, any rcs config change will not update the database.
+     */
+    public void setTestModeEnabled(boolean enabled) {
+        logv("setTestModeEnabled as " + enabled);
+        if (mTestModeEnabled != enabled) {
+            mTestModeEnabled = enabled;
+            mHandler.sendMessage(mHandler.obtainMessage(EVENT_RESET));
+        }
+    }
+
+
+    /**
+     * Returns whether the test mode is enabled.
+     */
+    public boolean getTestModeEnabled() {
+        return mTestModeEnabled;
+    }
+
+    /**
      * override the device config whether single registration is enabled
      */
     public void overrideDeviceSingleRegistrationEnabled(Boolean enabled) {
@@ -428,6 +640,18 @@
     }
 
     /**
+     * override the rcs feature validation result for a subscription
+     */
+    public boolean overrideImsFeatureValidation(int subId, Boolean enabled) {
+        if (enabled == null) {
+            mImsFeatureValidationOverride.remove(subId);
+        } else {
+            mImsFeatureValidationOverride.put(subId, enabled);
+        }
+        return true;
+    }
+
+    /**
      * Returns the device config whether single registration is enabled
      */
     public boolean getDeviceSingleRegistrationEnabled() {
@@ -449,6 +673,13 @@
         return false;
     }
 
+    /**
+     * Returns the rcs feature validation override value, null if it is not set.
+     */
+    public Boolean getImsFeatureValidationOverride(int subId) {
+        return mImsFeatureValidationOverride.get(subId);
+    }
+
     private void onDefaultMessagingApplicationChanged() {
         final String packageName = getDmaPackageName();
         if (!TextUtils.equals(mDmaPackageName, packageName)) {
@@ -461,61 +692,29 @@
                 v.clear();
                 if (isAcsUsed(k)) {
                     logv("acs used, trigger to re-configure.");
-                    notifyRcsAutoConfigurationRemoved(k);
-                    triggerRcsReconfiguration(k);
+                    updateConfigForSub(k, null, true);
+                    v.triggerRcsReconfiguration();
                 } else {
+                    logv("acs not used, set cached config and notify.");
                     v.setConfig(cachedConfig);
-                    logv("acs not used, notify.");
-                    notifyRcsAutoConfigurationReceived(k, v.getConfig(), false);
                 }
             });
         }
     }
 
-    private void notifyRcsAutoConfigurationReceived(int subId, byte[] config,
-            boolean isCompressed) {
-        if (config == null) {
-            logd("Rcs config is null for sub : " + subId);
-            return;
-        }
-
-        IImsConfig imsConfig = getIImsConfig(subId, ImsFeature.FEATURE_RCS);
-        if (imsConfig != null) {
-            try {
-                imsConfig.notifyRcsAutoConfigurationReceived(config, isCompressed);
-            } catch (RemoteException e) {
-                loge("fail to notify rcs configuration received!");
-            }
-        } else {
-            logd("getIImsConfig returns null.");
+    private void updateConfigForSub(int subId, byte[] config, boolean isCompressed) {
+        logv("updateConfigForSub, subId:" + subId + ", mTestModeEnabled:" + mTestModeEnabled);
+        if (!mTestModeEnabled) {
+            RcsConfig.updateConfigForSub(mPhone, subId, config, isCompressed);
         }
     }
 
-    private void notifyRcsAutoConfigurationRemoved(int subId) {
-        RcsConfig.updateConfigForSub(mPhone, subId, null, true);
-        IImsConfig imsConfig = getIImsConfig(subId, ImsFeature.FEATURE_RCS);
-        if (imsConfig != null) {
-            try {
-                imsConfig.notifyRcsAutoConfigurationRemoved();
-            } catch (RemoteException e) {
-                loge("fail to notify rcs configuration removed!");
-            }
-        } else {
-            logd("getIImsConfig returns null.");
+    private byte[] loadConfigForSub(int subId) {
+        logv("loadConfigForSub, subId:" + subId + ", mTestModeEnabled:" + mTestModeEnabled);
+        if (!mTestModeEnabled) {
+            return RcsConfig.loadRcsConfigForSub(mPhone, subId, false);
         }
-    }
-
-    private void triggerRcsReconfiguration(int subId) {
-        IImsConfig imsConfig = getIImsConfig(subId, ImsFeature.FEATURE_RCS);
-        if (imsConfig != null) {
-            try {
-                imsConfig.triggerRcsReconfiguration();
-            } catch (RemoteException e) {
-                loge("fail to trigger rcs reconfiguration!");
-            }
-        } else {
-            logd("getIImsConfig returns null.");
-        }
+        return null;
     }
 
     private boolean isAcsUsed(int subId) {
@@ -543,7 +742,8 @@
         boolean isSingleRegistrationEnabledOnDevice =
                 mDeviceSingleRegistrationEnabledOverride != null
                 ? mDeviceSingleRegistrationEnabledOverride
-                : mPhone.getResources().getBoolean(R.bool.config_rcsVolteSingleRegistrationEnabled);
+                : mPhone.getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION);
 
         int value = (isSingleRegistrationEnabledOnDevice ? 0
                 : ProvisioningManager.STATUS_DEVICE_NOT_CAPABLE) | (
@@ -566,26 +766,25 @@
 
     private void onSubChanged() {
         final int[] activeSubs = mSubscriptionManager.getActiveSubscriptionIdList();
-        final HashSet<Integer> subsToBeDeactivated = new HashSet<>(mRcsProvisioningInfos.keySet());
+        final ArraySet<Integer> subsToBeDeactivated =
+                new ArraySet<>(mRcsProvisioningInfos.keySet());
 
         for (int i : activeSubs) {
             subsToBeDeactivated.remove(i);
             if (!mRcsProvisioningInfos.containsKey(i)) {
-                byte[] data = RcsConfig.loadRcsConfigForSub(mPhone, i, false);
+                byte[] data = loadConfigForSub(i);
                 int capability = getSingleRegistrationCapableValue(i);
                 logv("new info is created for sub : " + i + ", single registration capability :"
                         + capability + ", rcs config : " + data);
                 mRcsProvisioningInfos.put(i, new RcsProvisioningInfo(i, capability, data));
-                notifyRcsAutoConfigurationReceived(i, data, false);
                 notifyDmaForSub(i, capability);
             }
         }
 
         subsToBeDeactivated.forEach(i -> {
             RcsProvisioningInfo info = mRcsProvisioningInfos.remove(i);
-            notifyRcsAutoConfigurationRemoved(i);
             if (info != null) {
-                info.clear();
+                info.destroy();
             }
         });
     }
@@ -594,11 +793,8 @@
         logv("onConfigReceived, subId:" + subId + ", config:"
                 + config + ", isCompressed:" + isCompressed);
         RcsProvisioningInfo info = mRcsProvisioningInfos.get(subId);
-        if (info != null) {
-            info.setConfig(isCompressed ? RcsConfig.decompressGzip(config) : config);
-        }
-        RcsConfig.updateConfigForSub(mPhone, subId, config, isCompressed);
-        notifyRcsAutoConfigurationReceived(subId, config, isCompressed);
+        info.setConfig(isCompressed ? RcsConfig.decompressGzip(config) : config);
+        updateConfigForSub(subId, config, isCompressed);
     }
 
     private void onReconfigRequest(int subId) {
@@ -606,9 +802,10 @@
         RcsProvisioningInfo info = mRcsProvisioningInfos.get(subId);
         if (info != null) {
             info.setConfig(null);
+            // clear rcs config stored in db
+            updateConfigForSub(subId, null, true);
+            info.triggerRcsReconfiguration();
         }
-        notifyRcsAutoConfigurationRemoved(subId);
-        triggerRcsReconfiguration(subId);
     }
 
     private void notifyDmaForSub(int subId, int capability) {
@@ -618,12 +815,13 @@
         intent.putExtra(ProvisioningManager.EXTRA_SUBSCRIPTION_ID, subId);
         intent.putExtra(ProvisioningManager.EXTRA_STATUS, capability);
         logv("notify " + intent);
-        mPhone.sendBroadcast(intent);
-    }
-
-    private IImsConfig getIImsConfig(int subId, int feature) {
-        return mPhone.getImsResolver().getImsConfig(
-                SubscriptionManager.getSlotIndex(subId), feature);
+        // Only send permission to the default sms app if it has the correct permissions
+        // except test mode enabled
+        if (!mTestModeEnabled) {
+            mPhone.sendBroadcast(intent, Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION);
+        } else {
+            mPhone.sendBroadcast(intent);
+        }
     }
 
     private String getDmaPackageName() {
@@ -635,6 +833,23 @@
         }
     }
 
+    void registerRcsFeatureListener(RcsProvisioningInfo info) {
+        int slotId = SubscriptionManager.getSlotIndex(info.getSubId());
+        RcsFeatureListener cb = mRcsFeatureListeners.get(slotId);
+        if (cb == null) {
+            cb = new RcsFeatureListener(slotId);
+            mRcsFeatureListeners.put(slotId, cb);
+        }
+        cb.addRcsProvisioningInfo(info);
+    }
+
+    void unregisterRcsFeatureListener(RcsProvisioningInfo info) {
+        // make sure the info to be removed in any case, even the slotId changed or invalid.
+        for (int i  = 0; i < mRcsFeatureListeners.size(); i++) {
+            mRcsFeatureListeners.valueAt(i).removeRcsProvisioningInfo(info);
+        }
+    }
+
     private static boolean booleanEquals(Boolean val1, Boolean val2) {
         return (val1 == null && val2 == null)
                 || (Boolean.TRUE.equals(val1) && Boolean.TRUE.equals(val2))
diff --git a/src/com/android/phone/ServiceStateProvider.java b/src/com/android/phone/ServiceStateProvider.java
index a7d27d5..32562fa 100644
--- a/src/com/android/phone/ServiceStateProvider.java
+++ b/src/com/android/phone/ServiceStateProvider.java
@@ -18,6 +18,9 @@
 
 import static android.provider.Telephony.ServiceStateTable;
 import static android.provider.Telephony.ServiceStateTable.CONTENT_URI;
+import static android.provider.Telephony.ServiceStateTable.DATA_NETWORK_TYPE;
+import static android.provider.Telephony.ServiceStateTable.DATA_REG_STATE;
+import static android.provider.Telephony.ServiceStateTable.DUPLEX_MODE;
 import static android.provider.Telephony.ServiceStateTable.IS_MANUAL_NETWORK_SELECTION;
 import static android.provider.Telephony.ServiceStateTable.VOICE_REG_STATE;
 import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionId;
@@ -61,18 +64,6 @@
     public static final String SERVICE_STATE = "service_state";
 
     /**
-     * An integer value indicating the current data service state.
-     * <p>
-     * Valid values: {@link ServiceState#STATE_IN_SERVICE},
-     * {@link ServiceState#STATE_OUT_OF_SERVICE}, {@link ServiceState#STATE_EMERGENCY_ONLY},
-     * {@link ServiceState#STATE_POWER_OFF}.
-     * <p>
-     * This is the same as {@link ServiceState#getDataRegState()}.
-     * @hide
-     */
-    public static final String DATA_REG_STATE = "data_reg_state";
-
-    /**
      * An integer value indicating the current voice roaming type.
      * <p>
      * This is the same as {@link ServiceState#getVoiceRoamingType()}.
@@ -257,6 +248,8 @@
         IS_USING_CARRIER_AGGREGATION,
         OPERATOR_ALPHA_LONG_RAW,
         OPERATOR_ALPHA_SHORT_RAW,
+        DATA_NETWORK_TYPE,
+        DUPLEX_MODE,
     };
 
     @Override
@@ -392,6 +385,8 @@
             final int is_using_carrier_aggregation = (ss.isUsingCarrierAggregation()) ? 1 : 0;
             final String operator_alpha_long_raw = ss.getOperatorAlphaLongRaw();
             final String operator_alpha_short_raw = ss.getOperatorAlphaShortRaw();
+            final int data_network_type = ss.getDataNetworkType();
+            final int duplex_mode = ss.getDuplexMode();
 
             return buildSingleRowResult(projection, sColumns, new Object[] {
                     voice_reg_state,
@@ -418,6 +413,8 @@
                     is_using_carrier_aggregation,
                     operator_alpha_long_raw,
                     operator_alpha_short_raw,
+                    data_network_type,
+                    duplex_mode,
             });
         }
     }
@@ -480,6 +477,10 @@
             context.getContentResolver().notifyChange(
                     getUriForSubscriptionIdAndField(subId, DATA_ROAMING_TYPE), null, false);
         }
+        if (firstUpdate || dataNetworkTypeChanged(oldSS, newSS)) {
+            context.getContentResolver().notifyChange(
+                    getUriForSubscriptionIdAndField(subId, DATA_NETWORK_TYPE), null, false);
+        }
     }
 
     private static boolean voiceRegStateChanged(ServiceState oldSS, ServiceState newSS) {
@@ -498,6 +499,10 @@
         return oldSS.getDataRoamingType() != newSS.getDataRoamingType();
     }
 
+    private static boolean dataNetworkTypeChanged(ServiceState oldSS, ServiceState newSS) {
+        return oldSS.getDataNetworkType() != newSS.getDataNetworkType();
+    }
+
     /**
      * Notify interested apps that the ServiceState has changed.
      *
@@ -517,7 +522,8 @@
         // If oldSS is null and newSS is not (e.g. first update of service state) this will also
         // notify
         if (oldSS == null || voiceRegStateChanged(oldSS, newSS) || dataRegStateChanged(oldSS, newSS)
-                || voiceRoamingTypeChanged(oldSS, newSS) || dataRoamingTypeChanged(oldSS, newSS)) {
+                || voiceRoamingTypeChanged(oldSS, newSS) || dataRoamingTypeChanged(oldSS, newSS)
+                || dataNetworkTypeChanged(oldSS, newSS)) {
             context.getContentResolver().notifyChange(getUriForSubscriptionId(subId), null, false);
         }
     }
diff --git a/src/com/android/phone/SimPhonebookProvider.java b/src/com/android/phone/SimPhonebookProvider.java
new file mode 100644
index 0000000..b921398
--- /dev/null
+++ b/src/com/android/phone/SimPhonebookProvider.java
@@ -0,0 +1,917 @@
+/*
+ * 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.phone;
+
+import static com.android.internal.telephony.IccProvider.STR_NEW_TAG;
+import static com.android.internal.telephony.IccProvider.STR_NEW_NUMBER;
+
+import android.Manifest;
+import android.annotation.TestApi;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.UriMatcher;
+import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.RemoteException;
+import android.provider.SimPhonebookContract;
+import android.provider.SimPhonebookContract.ElementaryFiles;
+import android.provider.SimPhonebookContract.SimRecords;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.TelephonyManager;
+import android.util.ArraySet;
+import android.util.Pair;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.IIccPhoneBook;
+import com.android.internal.telephony.uicc.AdnRecord;
+import com.android.internal.telephony.uicc.IccConstants;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.MoreExecutors;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Supplier;
+
+/**
+ * Provider for contact records stored on the SIM card.
+ *
+ * @see SimPhonebookContract
+ */
+public class SimPhonebookProvider extends ContentProvider {
+
+    @VisibleForTesting
+    static final String[] ELEMENTARY_FILES_ALL_COLUMNS = {
+            ElementaryFiles.SLOT_INDEX,
+            ElementaryFiles.SUBSCRIPTION_ID,
+            ElementaryFiles.EF_TYPE,
+            ElementaryFiles.MAX_RECORDS,
+            ElementaryFiles.RECORD_COUNT,
+            ElementaryFiles.NAME_MAX_LENGTH,
+            ElementaryFiles.PHONE_NUMBER_MAX_LENGTH
+    };
+    @VisibleForTesting
+    static final String[] SIM_RECORDS_ALL_COLUMNS = {
+            SimRecords.SUBSCRIPTION_ID,
+            SimRecords.ELEMENTARY_FILE_TYPE,
+            SimRecords.RECORD_NUMBER,
+            SimRecords.NAME,
+            SimRecords.PHONE_NUMBER
+    };
+    private static final String TAG = "SimPhonebookProvider";
+    private static final Set<String> ELEMENTARY_FILES_COLUMNS_SET =
+            ImmutableSet.copyOf(ELEMENTARY_FILES_ALL_COLUMNS);
+    private static final Set<String> SIM_RECORDS_WRITABLE_COLUMNS = ImmutableSet.of(
+            SimRecords.NAME, SimRecords.PHONE_NUMBER
+    );
+
+    private static final int WRITE_TIMEOUT_SECONDS = 30;
+
+    private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
+
+    private static final int ELEMENTARY_FILES = 100;
+    private static final int ELEMENTARY_FILES_ITEM = 101;
+    private static final int SIM_RECORDS = 200;
+    private static final int SIM_RECORDS_ITEM = 201;
+
+    static {
+        URI_MATCHER.addURI(SimPhonebookContract.AUTHORITY,
+                ElementaryFiles.ELEMENTARY_FILES_PATH_SEGMENT, ELEMENTARY_FILES);
+        URI_MATCHER.addURI(
+                SimPhonebookContract.AUTHORITY,
+                ElementaryFiles.ELEMENTARY_FILES_PATH_SEGMENT + "/"
+                        + SimPhonebookContract.SUBSCRIPTION_ID_PATH_SEGMENT + "/#/*",
+                ELEMENTARY_FILES_ITEM);
+        URI_MATCHER.addURI(SimPhonebookContract.AUTHORITY,
+                SimPhonebookContract.SUBSCRIPTION_ID_PATH_SEGMENT + "/#/*", SIM_RECORDS);
+        URI_MATCHER.addURI(SimPhonebookContract.AUTHORITY,
+                SimPhonebookContract.SUBSCRIPTION_ID_PATH_SEGMENT + "/#/*/#", SIM_RECORDS_ITEM);
+    }
+
+    // Only allow 1 write at a time to prevent races; the mutations are based on reads of the
+    // existing list of records which means concurrent writes would be problematic.
+    private final Lock mWriteLock = new ReentrantLock(true);
+    private SubscriptionManager mSubscriptionManager;
+    private Supplier<IIccPhoneBook> mIccPhoneBookSupplier;
+    private ContentNotifier mContentNotifier;
+
+    static int efIdForEfType(@ElementaryFiles.EfType int efType) {
+        switch (efType) {
+            case ElementaryFiles.EF_ADN:
+                return IccConstants.EF_ADN;
+            case ElementaryFiles.EF_FDN:
+                return IccConstants.EF_FDN;
+            case ElementaryFiles.EF_SDN:
+                return IccConstants.EF_SDN;
+            default:
+                return 0;
+        }
+    }
+
+    private static void validateProjection(Set<String> allowed, String[] projection) {
+        if (projection == null || allowed.containsAll(Arrays.asList(projection))) {
+            return;
+        }
+        Set<String> invalidColumns = new LinkedHashSet<>(Arrays.asList(projection));
+        invalidColumns.removeAll(allowed);
+        throw new IllegalArgumentException(
+                "Unsupported columns: " + Joiner.on(",").join(invalidColumns));
+    }
+
+    private static int getRecordSize(int[] recordsSize) {
+        return recordsSize[0];
+    }
+
+    private static int getRecordCount(int[] recordsSize) {
+        return recordsSize[2];
+    }
+
+    /** Returns the IccPhoneBook used to load the AdnRecords. */
+    private static IIccPhoneBook getIccPhoneBook() {
+        return IIccPhoneBook.Stub.asInterface(TelephonyFrameworkInitializer
+                .getTelephonyServiceManager().getIccPhoneBookServiceRegisterer().get());
+    }
+
+    @Override
+    public boolean onCreate() {
+        ContentResolver resolver = getContext().getContentResolver();
+        return onCreate(getContext().getSystemService(SubscriptionManager.class),
+                SimPhonebookProvider::getIccPhoneBook,
+                uri -> resolver.notifyChange(uri, null));
+    }
+
+    @TestApi
+    boolean onCreate(SubscriptionManager subscriptionManager,
+            Supplier<IIccPhoneBook> iccPhoneBookSupplier, ContentNotifier notifier) {
+        if (subscriptionManager == null) {
+            return false;
+        }
+        mSubscriptionManager = subscriptionManager;
+        mIccPhoneBookSupplier = iccPhoneBookSupplier;
+        mContentNotifier = notifier;
+
+        mSubscriptionManager.addOnSubscriptionsChangedListener(MoreExecutors.directExecutor(),
+                new SubscriptionManager.OnSubscriptionsChangedListener() {
+                    boolean mFirstCallback = true;
+                    private int[] mNotifiedSubIds = {};
+
+                    @Override
+                    public void onSubscriptionsChanged() {
+                        if (mFirstCallback) {
+                            mFirstCallback = false;
+                            return;
+                        }
+                        int[] activeSubIds = mSubscriptionManager.getActiveSubscriptionIdList();
+                        if (!Arrays.equals(mNotifiedSubIds, activeSubIds)) {
+                            notifier.notifyChange(SimPhonebookContract.AUTHORITY_URI);
+                            mNotifiedSubIds = Arrays.copyOf(activeSubIds, activeSubIds.length);
+                        }
+                    }
+                });
+        return true;
+    }
+
+    @Nullable
+    @Override
+    public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
+        if (SimRecords.GET_ENCODED_NAME_LENGTH_METHOD_NAME.equals(method)) {
+            // No permissions checks needed. This isn't leaking any sensitive information since the
+            // name we are checking is provided by the caller.
+            return callForEncodedNameLength(arg);
+        }
+        return super.call(method, arg, extras);
+    }
+
+    private Bundle callForEncodedNameLength(String name) {
+        Bundle result = new Bundle();
+        result.putInt(SimRecords.EXTRA_ENCODED_NAME_LENGTH, getEncodedNameLength(name));
+        return result;
+    }
+
+    private int getEncodedNameLength(String name) {
+        if (Strings.isNullOrEmpty(name)) {
+            return 0;
+        } else {
+            byte[] encoded = AdnRecord.encodeAlphaTag(name);
+            return encoded.length;
+        }
+    }
+
+    @Nullable
+    @Override
+    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable Bundle queryArgs,
+            @Nullable CancellationSignal cancellationSignal) {
+        if (queryArgs != null && (queryArgs.containsKey(ContentResolver.QUERY_ARG_SQL_SELECTION)
+                || queryArgs.containsKey(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS)
+                || queryArgs.containsKey(ContentResolver.QUERY_ARG_SQL_LIMIT))) {
+            throw new IllegalArgumentException(
+                    "A SQL selection was provided but it is not supported by this provider.");
+        }
+        switch (URI_MATCHER.match(uri)) {
+            case ELEMENTARY_FILES:
+                return queryElementaryFiles(projection);
+            case ELEMENTARY_FILES_ITEM:
+                return queryElementaryFilesItem(PhonebookArgs.forElementaryFilesItem(uri),
+                        projection);
+            case SIM_RECORDS:
+                return querySimRecords(PhonebookArgs.forSimRecords(uri, queryArgs), projection);
+            case SIM_RECORDS_ITEM:
+                return querySimRecordsItem(PhonebookArgs.forSimRecordsItem(uri, queryArgs),
+                        projection);
+            default:
+                throw new IllegalArgumentException("Unsupported Uri " + uri);
+        }
+    }
+
+    @Nullable
+    @Override
+    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
+            @Nullable String[] selectionArgs, @Nullable String sortOrder,
+            @Nullable CancellationSignal cancellationSignal) {
+        throw new UnsupportedOperationException("Only query with Bundle is supported");
+    }
+
+    @Nullable
+    @Override
+    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
+            @Nullable String[] selectionArgs, @Nullable String sortOrder) {
+        throw new UnsupportedOperationException("Only query with Bundle is supported");
+    }
+
+    private Cursor queryElementaryFiles(String[] projection) {
+        validateProjection(ELEMENTARY_FILES_COLUMNS_SET, projection);
+        if (projection == null) {
+            projection = ELEMENTARY_FILES_ALL_COLUMNS;
+        }
+
+        MatrixCursor result = new MatrixCursor(projection);
+
+        List<SubscriptionInfo> activeSubscriptions = getActiveSubscriptionInfoList();
+        for (SubscriptionInfo subInfo : activeSubscriptions) {
+            try {
+                addEfToCursor(result, subInfo, ElementaryFiles.EF_ADN);
+                addEfToCursor(result, subInfo, ElementaryFiles.EF_FDN);
+                addEfToCursor(result, subInfo, ElementaryFiles.EF_SDN);
+            } catch (RemoteException e) {
+                // Return an empty cursor. If service to access it is throwing remote
+                // exceptions then it's basically the same as not having a SIM.
+                return new MatrixCursor(projection, 0);
+            }
+        }
+        return result;
+    }
+
+    private Cursor queryElementaryFilesItem(PhonebookArgs args, String[] projection) {
+        validateProjection(ELEMENTARY_FILES_COLUMNS_SET, projection);
+        if (projection == null) {
+            projection = ELEMENTARY_FILES_ALL_COLUMNS;
+        }
+
+        MatrixCursor result = new MatrixCursor(projection);
+        try {
+            addEfToCursor(
+                    result, getActiveSubscriptionInfo(args.subscriptionId), args.efType);
+        } catch (RemoteException e) {
+            // Return an empty cursor. If service to access it is throwing remote
+            // exceptions then it's basically the same as not having a SIM.
+            return new MatrixCursor(projection, 0);
+        }
+        return result;
+    }
+
+    private void addEfToCursor(MatrixCursor result, SubscriptionInfo subscriptionInfo,
+            int efType) throws RemoteException {
+        int[] recordsSize = mIccPhoneBookSupplier.get().getAdnRecordsSizeForSubscriber(
+                subscriptionInfo.getSubscriptionId(), efIdForEfType(efType));
+        addEfToCursor(result, subscriptionInfo, efType, recordsSize);
+    }
+
+    private void addEfToCursor(MatrixCursor result, SubscriptionInfo subscriptionInfo,
+            int efType, int[] recordsSize) throws RemoteException {
+        // If the record count is zero then the SIM doesn't support the elementary file so just
+        // omit it.
+        if (recordsSize == null || getRecordCount(recordsSize) == 0) {
+            return;
+        }
+        MatrixCursor.RowBuilder row = result.newRow()
+                .add(ElementaryFiles.SLOT_INDEX, subscriptionInfo.getSimSlotIndex())
+                .add(ElementaryFiles.SUBSCRIPTION_ID, subscriptionInfo.getSubscriptionId())
+                .add(ElementaryFiles.EF_TYPE, efType)
+                .add(ElementaryFiles.MAX_RECORDS, getRecordCount(recordsSize))
+                .add(ElementaryFiles.NAME_MAX_LENGTH,
+                        AdnRecord.getMaxAlphaTagBytes(getRecordSize(recordsSize)))
+                .add(ElementaryFiles.PHONE_NUMBER_MAX_LENGTH,
+                        AdnRecord.getMaxPhoneNumberDigits());
+        if (result.getColumnIndex(ElementaryFiles.RECORD_COUNT) != -1) {
+            int efid = efIdForEfType(efType);
+            List<AdnRecord> existingRecords = mIccPhoneBookSupplier.get()
+                    .getAdnRecordsInEfForSubscriber(subscriptionInfo.getSubscriptionId(), efid);
+            int nonEmptyCount = 0;
+            for (AdnRecord record : existingRecords) {
+                if (!record.isEmpty()) {
+                    nonEmptyCount++;
+                }
+            }
+            row.add(ElementaryFiles.RECORD_COUNT, nonEmptyCount);
+        }
+    }
+
+    private Cursor querySimRecords(PhonebookArgs args, String[] projection) {
+        validateSubscriptionAndEf(args);
+        if (projection == null) {
+            projection = SIM_RECORDS_ALL_COLUMNS;
+        }
+
+        List<AdnRecord> records = loadRecordsForEf(args);
+        if (records == null) {
+            return new MatrixCursor(projection, 0);
+        }
+        MatrixCursor result = new MatrixCursor(projection, records.size());
+        List<Pair<AdnRecord, MatrixCursor.RowBuilder>> rowBuilders = new ArrayList<>(
+                records.size());
+        for (AdnRecord record : records) {
+            if (!record.isEmpty()) {
+                rowBuilders.add(Pair.create(record, result.newRow()));
+            }
+        }
+        // This is kind of ugly but avoids looking up columns in an inner loop.
+        for (String column : projection) {
+            switch (column) {
+                case SimRecords.SUBSCRIPTION_ID:
+                    for (Pair<AdnRecord, MatrixCursor.RowBuilder> row : rowBuilders) {
+                        row.second.add(args.subscriptionId);
+                    }
+                    break;
+                case SimRecords.ELEMENTARY_FILE_TYPE:
+                    for (Pair<AdnRecord, MatrixCursor.RowBuilder> row : rowBuilders) {
+                        row.second.add(args.efType);
+                    }
+                    break;
+                case SimRecords.RECORD_NUMBER:
+                    for (Pair<AdnRecord, MatrixCursor.RowBuilder> row : rowBuilders) {
+                        row.second.add(row.first.getRecId());
+                    }
+                    break;
+                case SimRecords.NAME:
+                    for (Pair<AdnRecord, MatrixCursor.RowBuilder> row : rowBuilders) {
+                        row.second.add(row.first.getAlphaTag());
+                    }
+                    break;
+                case SimRecords.PHONE_NUMBER:
+                    for (Pair<AdnRecord, MatrixCursor.RowBuilder> row : rowBuilders) {
+                        row.second.add(row.first.getNumber());
+                    }
+                    break;
+                default:
+                    Rlog.w(TAG, "Column " + column + " is unsupported for " + args.uri);
+                    break;
+            }
+        }
+        return result;
+    }
+
+    private Cursor querySimRecordsItem(PhonebookArgs args, String[] projection) {
+        if (projection == null) {
+            projection = SIM_RECORDS_ALL_COLUMNS;
+        }
+        validateSubscriptionAndEf(args);
+        AdnRecord record = loadRecord(args);
+
+        MatrixCursor result = new MatrixCursor(projection, 1);
+        if (record == null || record.isEmpty()) {
+            return result;
+        }
+        result.newRow()
+                .add(SimRecords.SUBSCRIPTION_ID, args.subscriptionId)
+                .add(SimRecords.ELEMENTARY_FILE_TYPE, args.efType)
+                .add(SimRecords.RECORD_NUMBER, record.getRecId())
+                .add(SimRecords.NAME, record.getAlphaTag())
+                .add(SimRecords.PHONE_NUMBER, record.getNumber());
+        return result;
+    }
+
+    @Nullable
+    @Override
+    public String getType(@NonNull Uri uri) {
+        switch (URI_MATCHER.match(uri)) {
+            case ELEMENTARY_FILES:
+                return ElementaryFiles.CONTENT_TYPE;
+            case ELEMENTARY_FILES_ITEM:
+                return ElementaryFiles.CONTENT_ITEM_TYPE;
+            case SIM_RECORDS:
+                return SimRecords.CONTENT_TYPE;
+            case SIM_RECORDS_ITEM:
+                return SimRecords.CONTENT_ITEM_TYPE;
+            default:
+                throw new IllegalArgumentException("Unsupported Uri " + uri);
+        }
+    }
+
+    @Nullable
+    @Override
+    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
+        return insert(uri, values, null);
+    }
+
+    @Nullable
+    @Override
+    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values, @Nullable Bundle extras) {
+        switch (URI_MATCHER.match(uri)) {
+            case SIM_RECORDS:
+                return insertSimRecord(PhonebookArgs.forSimRecords(uri, extras), values);
+            case ELEMENTARY_FILES:
+            case ELEMENTARY_FILES_ITEM:
+            case SIM_RECORDS_ITEM:
+                throw new UnsupportedOperationException(uri + " does not support insert");
+            default:
+                throw new IllegalArgumentException("Unsupported Uri " + uri);
+        }
+    }
+
+    private Uri insertSimRecord(PhonebookArgs args, ContentValues values) {
+        validateWritableEf(args, "insert");
+        validateSubscriptionAndEf(args);
+
+        if (values == null || values.isEmpty()) {
+            return null;
+        }
+        validateValues(args, values);
+        String newName = Strings.nullToEmpty(values.getAsString(SimRecords.NAME));
+        String newPhoneNumber = Strings.nullToEmpty(values.getAsString(SimRecords.PHONE_NUMBER));
+
+        acquireWriteLockOrThrow();
+        try {
+            List<AdnRecord> records = loadRecordsForEf(args);
+            if (records == null) {
+                Rlog.e(TAG, "Failed to load existing records for " + args.uri);
+                return null;
+            }
+            AdnRecord emptyRecord = null;
+            for (AdnRecord record : records) {
+                if (record.isEmpty()) {
+                    emptyRecord = record;
+                    break;
+                }
+            }
+            if (emptyRecord == null) {
+                // When there are no empty records that means the EF is full.
+                throw new IllegalStateException(
+                        args.uri + " is full. Please delete records to add new ones.");
+            }
+            boolean success = updateRecord(args, emptyRecord, args.pin2, newName, newPhoneNumber);
+            if (!success) {
+                Rlog.e(TAG, "Insert failed for " + args.uri);
+                // Something didn't work but since we don't have any more specific
+                // information to provide to the caller it's better to just return null
+                // rather than throwing and possibly crashing their process.
+                return null;
+            }
+            notifyChange();
+            return SimRecords.getItemUri(args.subscriptionId, args.efType, emptyRecord.getRecId());
+        } finally {
+            releaseWriteLock();
+        }
+    }
+
+    @Override
+    public int delete(@NonNull Uri uri, @Nullable String selection,
+            @Nullable String[] selectionArgs) {
+        throw new UnsupportedOperationException("Only delete with Bundle is supported");
+    }
+
+    @Override
+    public int delete(@NonNull Uri uri, @Nullable Bundle extras) {
+        switch (URI_MATCHER.match(uri)) {
+            case SIM_RECORDS_ITEM:
+                return deleteSimRecordsItem(PhonebookArgs.forSimRecordsItem(uri, extras));
+            case ELEMENTARY_FILES:
+            case ELEMENTARY_FILES_ITEM:
+            case SIM_RECORDS:
+                throw new UnsupportedOperationException(uri + " does not support delete");
+            default:
+                throw new IllegalArgumentException("Unsupported Uri " + uri);
+        }
+    }
+
+    private int deleteSimRecordsItem(PhonebookArgs args) {
+        validateWritableEf(args, "delete");
+        validateSubscriptionAndEf(args);
+
+        acquireWriteLockOrThrow();
+        try {
+            AdnRecord record = loadRecord(args);
+            if (record == null || record.isEmpty()) {
+                return 0;
+            }
+            if (!updateRecord(args, record, args.pin2, "", "")) {
+                Rlog.e(TAG, "Failed to delete " + args.uri);
+            }
+            notifyChange();
+        } finally {
+            releaseWriteLock();
+        }
+        return 1;
+    }
+
+
+    @Override
+    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable Bundle extras) {
+        switch (URI_MATCHER.match(uri)) {
+            case SIM_RECORDS_ITEM:
+                return updateSimRecordsItem(PhonebookArgs.forSimRecordsItem(uri, extras), values);
+            case ELEMENTARY_FILES:
+            case ELEMENTARY_FILES_ITEM:
+            case SIM_RECORDS:
+                throw new UnsupportedOperationException(uri + " does not support update");
+            default:
+                throw new IllegalArgumentException("Unsupported Uri " + uri);
+        }
+    }
+
+    @Override
+    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
+            @Nullable String[] selectionArgs) {
+        throw new UnsupportedOperationException("Only Update with bundle is supported");
+    }
+
+    private int updateSimRecordsItem(PhonebookArgs args, ContentValues values) {
+        validateWritableEf(args, "update");
+        validateSubscriptionAndEf(args);
+
+        if (values == null || values.isEmpty()) {
+            return 0;
+        }
+        validateValues(args, values);
+        String newName = Strings.nullToEmpty(values.getAsString(SimRecords.NAME));
+        String newPhoneNumber = Strings.nullToEmpty(values.getAsString(SimRecords.PHONE_NUMBER));
+
+        acquireWriteLockOrThrow();
+
+        try {
+            AdnRecord record = loadRecord(args);
+
+            // Note we allow empty records to be updated. This is a bit weird because they are
+            // not returned by query methods but this allows a client application assign a name
+            // to a specific record number. This may be desirable in some phone app use cases since
+            // the record number is often used as a quick dial index.
+            if (record == null) {
+                return 0;
+            }
+            if (!updateRecord(args, record, args.pin2, newName, newPhoneNumber)) {
+                Rlog.e(TAG, "Failed to update " + args.uri);
+                return 0;
+            }
+            notifyChange();
+        } finally {
+            releaseWriteLock();
+        }
+        return 1;
+    }
+
+    void validateSubscriptionAndEf(PhonebookArgs args) {
+        SubscriptionInfo info =
+                args.subscriptionId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
+                        ? getActiveSubscriptionInfo(args.subscriptionId)
+                        : null;
+        if (info == null) {
+            throw new IllegalArgumentException("No active SIM with subscription ID "
+                    + args.subscriptionId);
+        }
+
+        int[] recordsSize = getRecordsSizeForEf(args);
+        if (recordsSize == null || recordsSize[1] == 0) {
+            throw new IllegalArgumentException(args.efName
+                    + " is not supported for SIM with subscription ID " + args.subscriptionId);
+        }
+    }
+
+    private void acquireWriteLockOrThrow() {
+        try {
+            if (!mWriteLock.tryLock(WRITE_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
+                throw new IllegalStateException("Timeout waiting to write");
+            }
+        } catch (InterruptedException e) {
+            throw new IllegalStateException("Write failed");
+        }
+    }
+
+    private void releaseWriteLock() {
+        mWriteLock.unlock();
+    }
+
+    private void validateWritableEf(PhonebookArgs args, String operationName) {
+        if (args.efType == ElementaryFiles.EF_FDN) {
+            if (hasPermissionsForFdnWrite(args)) {
+                return;
+            }
+        }
+        if (args.efType != ElementaryFiles.EF_ADN) {
+            throw new UnsupportedOperationException(
+                    args.uri + " does not support " + operationName);
+        }
+    }
+
+    private boolean hasPermissionsForFdnWrite(PhonebookArgs args) {
+        TelephonyManager telephonyManager = Objects.requireNonNull(
+                getContext().getSystemService(TelephonyManager.class));
+        String callingPackage = getCallingPackage();
+        int granted = PackageManager.PERMISSION_DENIED;
+        if (callingPackage != null) {
+            granted = getContext().getPackageManager().checkPermission(
+                    Manifest.permission.MODIFY_PHONE_STATE, callingPackage);
+        }
+        return granted == PackageManager.PERMISSION_GRANTED
+                || telephonyManager.hasCarrierPrivileges(args.subscriptionId);
+
+    }
+
+
+    private boolean updateRecord(PhonebookArgs args, AdnRecord existingRecord, String pin2,
+            String newName, String newPhone) {
+        try {
+            ContentValues values = new ContentValues();
+            values.put(STR_NEW_TAG, newName);
+            values.put(STR_NEW_NUMBER, newPhone);
+            return mIccPhoneBookSupplier.get().updateAdnRecordsInEfByIndexForSubscriber(
+                    args.subscriptionId, existingRecord.getEfid(), values,
+                    existingRecord.getRecId(),
+                    pin2);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    private void validatePhoneNumber(@Nullable String phoneNumber) {
+        if (phoneNumber == null || phoneNumber.isEmpty()) {
+            throw new IllegalArgumentException(SimRecords.PHONE_NUMBER + " is required.");
+        }
+        int actualLength = phoneNumber.length();
+        // When encoded the "+" prefix sets a bit and so doesn't count against the maximum length
+        if (phoneNumber.startsWith("+")) {
+            actualLength--;
+        }
+        if (actualLength > AdnRecord.getMaxPhoneNumberDigits()) {
+            throw new IllegalArgumentException(SimRecords.PHONE_NUMBER + " is too long.");
+        }
+        for (int i = 0; i < phoneNumber.length(); i++) {
+            char c = phoneNumber.charAt(i);
+            if (!PhoneNumberUtils.isNonSeparator(c)) {
+                throw new IllegalArgumentException(
+                        SimRecords.PHONE_NUMBER + " contains unsupported characters.");
+            }
+        }
+    }
+
+    private void validateValues(PhonebookArgs args, ContentValues values) {
+        if (!SIM_RECORDS_WRITABLE_COLUMNS.containsAll(values.keySet())) {
+            Set<String> unsupportedColumns = new ArraySet<>(values.keySet());
+            unsupportedColumns.removeAll(SIM_RECORDS_WRITABLE_COLUMNS);
+            throw new IllegalArgumentException("Unsupported columns: " + Joiner.on(',')
+                    .join(unsupportedColumns));
+        }
+
+        String phoneNumber = values.getAsString(SimRecords.PHONE_NUMBER);
+        validatePhoneNumber(phoneNumber);
+
+        String name = values.getAsString(SimRecords.NAME);
+        int length = getEncodedNameLength(name);
+        int[] recordsSize = getRecordsSizeForEf(args);
+        if (recordsSize == null) {
+            throw new IllegalStateException(
+                    "Failed to get " + ElementaryFiles.NAME_MAX_LENGTH + " from SIM");
+        }
+        int maxLength = AdnRecord.getMaxAlphaTagBytes(getRecordSize(recordsSize));
+
+        if (length > maxLength) {
+            throw new IllegalArgumentException(SimRecords.NAME + " is too long.");
+        }
+    }
+
+    private List<SubscriptionInfo> getActiveSubscriptionInfoList() {
+        // Getting the SubscriptionInfo requires READ_PHONE_STATE but we're only returning
+        // the subscription ID and slot index which are not sensitive information.
+        CallingIdentity identity = clearCallingIdentity();
+        try {
+            return mSubscriptionManager.getActiveSubscriptionInfoList();
+        } finally {
+            restoreCallingIdentity(identity);
+        }
+    }
+
+    private SubscriptionInfo getActiveSubscriptionInfo(int subId) {
+        // Getting the SubscriptionInfo requires READ_PHONE_STATE.
+        CallingIdentity identity = clearCallingIdentity();
+        try {
+            return mSubscriptionManager.getActiveSubscriptionInfo(subId);
+        } finally {
+            restoreCallingIdentity(identity);
+        }
+    }
+
+    private List<AdnRecord> loadRecordsForEf(PhonebookArgs args) {
+        try {
+            return mIccPhoneBookSupplier.get().getAdnRecordsInEfForSubscriber(
+                    args.subscriptionId, args.efid);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    private AdnRecord loadRecord(PhonebookArgs args) {
+        List<AdnRecord> records = loadRecordsForEf(args);
+        if (records == null || args.recordNumber > records.size()) {
+            return null;
+        }
+        AdnRecord result = records.get(args.recordNumber - 1);
+        // This should be true but the service could have a different implementation.
+        if (result.getRecId() == args.recordNumber) {
+            return result;
+        }
+        for (AdnRecord record : records) {
+            if (record.getRecId() == args.recordNumber) {
+                return result;
+            }
+        }
+        return null;
+    }
+
+
+    private int[] getRecordsSizeForEf(PhonebookArgs args) {
+        try {
+            return mIccPhoneBookSupplier.get().getAdnRecordsSizeForSubscriber(
+                    args.subscriptionId, args.efid);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    void notifyChange() {
+        mContentNotifier.notifyChange(SimPhonebookContract.AUTHORITY_URI);
+    }
+
+    /** Testable wrapper around {@link ContentResolver#notifyChange(Uri, ContentObserver)} */
+    @TestApi
+    interface ContentNotifier {
+        void notifyChange(Uri uri);
+    }
+
+    /**
+     * Holds the arguments extracted from the Uri and query args for accessing the referenced
+     * phonebook data on a SIM.
+     */
+    private static class PhonebookArgs {
+        public final Uri uri;
+        public final int subscriptionId;
+        public final String efName;
+        public final int efType;
+        public final int efid;
+        public final int recordNumber;
+        public final String pin2;
+
+        PhonebookArgs(Uri uri, int subscriptionId, String efName,
+                @ElementaryFiles.EfType int efType, int efid, int recordNumber,
+                @Nullable Bundle queryArgs) {
+            this.uri = uri;
+            this.subscriptionId = subscriptionId;
+            this.efName = efName;
+            this.efType = efType;
+            this.efid = efid;
+            this.recordNumber = recordNumber;
+            pin2 = efType == ElementaryFiles.EF_FDN && queryArgs != null
+                    ? queryArgs.getString(SimRecords.QUERY_ARG_PIN2)
+                    : null;
+        }
+
+        static PhonebookArgs createFromEfName(Uri uri, int subscriptionId,
+                String efName, int recordNumber, @Nullable Bundle queryArgs) {
+            int efType;
+            int efid;
+            if (efName != null) {
+                switch (efName) {
+                    case ElementaryFiles.PATH_SEGMENT_EF_ADN:
+                        efType = ElementaryFiles.EF_ADN;
+                        efid = IccConstants.EF_ADN;
+                        break;
+                    case ElementaryFiles.PATH_SEGMENT_EF_FDN:
+                        efType = ElementaryFiles.EF_FDN;
+                        efid = IccConstants.EF_FDN;
+                        break;
+                    case ElementaryFiles.PATH_SEGMENT_EF_SDN:
+                        efType = ElementaryFiles.EF_SDN;
+                        efid = IccConstants.EF_SDN;
+                        break;
+                    default:
+                        throw new IllegalArgumentException(
+                                "Unrecognized elementary file " + efName);
+                }
+            } else {
+                efType = ElementaryFiles.EF_UNKNOWN;
+                efid = 0;
+            }
+            return new PhonebookArgs(uri, subscriptionId, efName, efType, efid, recordNumber,
+                    queryArgs);
+        }
+
+        /**
+         * Pattern: elementary_files/subid/${subscriptionId}/${efName}
+         *
+         * e.g. elementary_files/subid/1/adn
+         *
+         * @see ElementaryFiles#getItemUri(int, int)
+         * @see #ELEMENTARY_FILES_ITEM
+         */
+        static PhonebookArgs forElementaryFilesItem(Uri uri) {
+            int subscriptionId = parseSubscriptionIdFromUri(uri, 2);
+            String efName = uri.getPathSegments().get(3);
+            return PhonebookArgs.createFromEfName(
+                    uri, subscriptionId, efName, -1, null);
+        }
+
+        /**
+         * Pattern: subid/${subscriptionId}/${efName}
+         *
+         * <p>e.g. subid/1/adn
+         *
+         * @see SimRecords#getContentUri(int, int)
+         * @see #SIM_RECORDS
+         */
+        static PhonebookArgs forSimRecords(Uri uri, Bundle queryArgs) {
+            int subscriptionId = parseSubscriptionIdFromUri(uri, 1);
+            String efName = uri.getPathSegments().get(2);
+            return PhonebookArgs.createFromEfName(uri, subscriptionId, efName, -1, queryArgs);
+        }
+
+        /**
+         * Pattern: subid/${subscriptionId}/${efName}/${recordNumber}
+         *
+         * <p>e.g. subid/1/adn/10
+         *
+         * @see SimRecords#getItemUri(int, int, int)
+         * @see #SIM_RECORDS_ITEM
+         */
+        static PhonebookArgs forSimRecordsItem(Uri uri, Bundle queryArgs) {
+            int subscriptionId = parseSubscriptionIdFromUri(uri, 1);
+            String efName = uri.getPathSegments().get(2);
+            int recordNumber = parseRecordNumberFromUri(uri, 3);
+            return PhonebookArgs.createFromEfName(uri, subscriptionId, efName, recordNumber,
+                    queryArgs);
+        }
+
+        private static int parseSubscriptionIdFromUri(Uri uri, int pathIndex) {
+            if (pathIndex == -1) {
+                return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+            }
+            String segment = uri.getPathSegments().get(pathIndex);
+            try {
+                return Integer.parseInt(segment);
+            } catch (NumberFormatException e) {
+                throw new IllegalArgumentException("Invalid subscription ID: " + segment);
+            }
+        }
+
+        private static int parseRecordNumberFromUri(Uri uri, int pathIndex) {
+            try {
+                return Integer.parseInt(uri.getPathSegments().get(pathIndex));
+            } catch (NumberFormatException e) {
+                throw new IllegalArgumentException(
+                        "Invalid record index: " + uri.getLastPathSegment());
+            }
+        }
+    }
+}
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index 3130913..090f405 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -28,15 +28,25 @@
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.ServiceSpecificException;
 import android.provider.BlockedNumberContract;
+import android.telephony.BarringInfo;
 import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.TelephonyRegistryManager;
 import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsContactUceCapability;
 import android.telephony.ims.feature.ImsFeature;
 import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
+import android.util.SparseArray;
 
+import com.android.ims.rcs.uce.util.FeatureTags;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
@@ -48,9 +58,12 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.TreeSet;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
@@ -71,17 +84,20 @@
     private static final String CALL_COMPOSER_SUBCOMMAND = "callcomposer";
     private static final String IMS_SUBCOMMAND = "ims";
     private static final String NUMBER_VERIFICATION_SUBCOMMAND = "numverify";
+    private static final String EMERGENCY_CALLBACK_MODE = "emergency-callback-mode";
     private static final String EMERGENCY_NUMBER_TEST_MODE = "emergency-number-test-mode";
     private static final String END_BLOCK_SUPPRESSION = "end-block-suppression";
     private static final String RESTART_MODEM = "restart-modem";
+    private static final String UNATTENDED_REBOOT = "unattended-reboot";
     private static final String CARRIER_CONFIG_SUBCOMMAND = "cc";
     private static final String DATA_TEST_MODE = "data";
     private static final String ENABLE = "enable";
     private static final String DISABLE = "disable";
     private static final String QUERY = "query";
 
-    private static final String CALL_COMPOSER_TEST_MODE = "test_mode";
+    private static final String CALL_COMPOSER_TEST_MODE = "test-mode";
     private static final String CALL_COMPOSER_SIMULATE_CALL = "simulate-outgoing-call";
+    private static final String CALL_COMPOSER_USER_SETTING = "user-setting";
 
     private static final String IMS_SET_IMS_SERVICE = "set-ims-service";
     private static final String IMS_GET_IMS_SERVICE = "get-ims-service";
@@ -109,19 +125,51 @@
     private static final String SRC_GET_DEVICE_ENABLED = "get-device-enabled";
     private static final String SRC_SET_CARRIER_ENABLED = "set-carrier-enabled";
     private static final String SRC_GET_CARRIER_ENABLED = "get-carrier-enabled";
+    private static final String SRC_SET_TEST_ENABLED = "set-test-enabled";
+    private static final String SRC_GET_TEST_ENABLED = "get-test-enabled";
+    private static final String SRC_SET_FEATURE_ENABLED = "set-feature-validation";
+    private static final String SRC_GET_FEATURE_ENABLED = "get-feature-validation";
 
     private static final String D2D_SUBCOMMAND = "d2d";
     private static final String D2D_SEND = "send";
+    private static final String D2D_TRANSPORT = "transport";
+    private static final String D2D_SET_DEVICE_SUPPORT = "set-device-support";
+
+    private static final String BARRING_SUBCOMMAND = "barring";
+    private static final String BARRING_SEND_INFO = "send";
 
     private static final String RCS_UCE_COMMAND = "uce";
     private static final String UCE_GET_EAB_CONTACT = "get-eab-contact";
     private static final String UCE_REMOVE_EAB_CONTACT = "remove-eab-contact";
+    private static final String UCE_GET_DEVICE_ENABLED = "get-device-enabled";
+    private static final String UCE_SET_DEVICE_ENABLED = "set-device-enabled";
+    private static final String UCE_OVERRIDE_PUBLISH_CAPS = "override-published-caps";
+    private static final String UCE_GET_LAST_PIDF_XML = "get-last-publish-pidf";
+    private static final String UCE_REMOVE_REQUEST_DISALLOWED_STATUS =
+            "remove-request-disallowed-status";
+    private static final String UCE_SET_CAPABILITY_REQUEST_TIMEOUT =
+            "set-capabilities-request-timeout";
 
+    // Check if a package has carrier privileges on any SIM, regardless of subId/phoneId.
+    private static final String HAS_CARRIER_PRIVILEGES_COMMAND = "has-carrier-privileges";
+
+    private static final String DISABLE_PHYSICAL_SUBSCRIPTION = "disable-physical-subscription";
+    private static final String ENABLE_PHYSICAL_SUBSCRIPTION = "enable-physical-subscription";
+
+    private static final String THERMAL_MITIGATION_COMMAND = "thermal-mitigation";
+    private static final String ALLOW_THERMAL_MITIGATION_PACKAGE_SUBCOMMAND = "allow-package";
+    private static final String DISALLOW_THERMAL_MITIGATION_PACKAGE_SUBCOMMAND = "disallow-package";
+
+    private static final String GET_ALLOWED_NETWORK_TYPES_FOR_USER =
+            "get-allowed-network-types-for-users";
+    private static final String SET_ALLOWED_NETWORK_TYPES_FOR_USER =
+            "set-allowed-network-types-for-users";
     // Take advantage of existing methods that already contain permissions checks when possible.
     private final ITelephony mInterface;
 
     private SubscriptionManager mSubscriptionManager;
     private CarrierConfigManager mCarrierConfigManager;
+    private TelephonyRegistryManager mTelephonyRegistryManager;
     private Context mContext;
 
     private enum CcType {
@@ -167,12 +215,56 @@
         }
     };
 
+    /**
+     * Map from a shorthand string to the feature tags required in registration required in order
+     * for the RCS feature to be considered "capable".
+     */
+    private static final Map<String, Set<String>> TEST_FEATURE_TAG_MAP;
+    static {
+        ArrayMap<String, Set<String>> map = new ArrayMap<>(18);
+        map.put("chat_v1", Collections.singleton(FeatureTags.FEATURE_TAG_CHAT_IM));
+        map.put("chat_v2", Collections.singleton(FeatureTags.FEATURE_TAG_CHAT_SESSION));
+        map.put("ft", Collections.singleton(FeatureTags.FEATURE_TAG_FILE_TRANSFER));
+        map.put("ft_sms", Collections.singleton(FeatureTags.FEATURE_TAG_FILE_TRANSFER_VIA_SMS));
+        map.put("mmtel", Collections.singleton(FeatureTags.FEATURE_TAG_MMTEL));
+        map.put("mmtel_vt", new ArraySet<>(Arrays.asList(FeatureTags.FEATURE_TAG_MMTEL,
+                FeatureTags.FEATURE_TAG_VIDEO)));
+        map.put("geo_push", Collections.singleton(FeatureTags.FEATURE_TAG_GEO_PUSH));
+        map.put("geo_push_sms", Collections.singleton(FeatureTags.FEATURE_TAG_GEO_PUSH_VIA_SMS));
+        map.put("call_comp",
+                Collections.singleton(FeatureTags.FEATURE_TAG_CALL_COMPOSER_ENRICHED_CALLING));
+        map.put("call_comp_mmtel",
+                Collections.singleton(FeatureTags.FEATURE_TAG_CALL_COMPOSER_VIA_TELEPHONY));
+        map.put("call_post", Collections.singleton(FeatureTags.FEATURE_TAG_POST_CALL));
+        map.put("map", Collections.singleton(FeatureTags.FEATURE_TAG_SHARED_MAP));
+        map.put("sketch", Collections.singleton(FeatureTags.FEATURE_TAG_SHARED_SKETCH));
+        // Feature tags defined twice for chatbot session because we want v1 and v2 based on bot
+        // version
+        map.put("chatbot", new ArraySet<>(Arrays.asList(
+                FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_SESSION,
+                FeatureTags.FEATURE_TAG_CHATBOT_VERSION_SUPPORTED)));
+        map.put("chatbot_v2", new ArraySet<>(Arrays.asList(
+                FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_SESSION,
+                FeatureTags.FEATURE_TAG_CHATBOT_VERSION_SUPPORTED)));
+        map.put("chatbot_sa", new ArraySet<>(Arrays.asList(
+                FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_STANDALONE_MSG,
+                FeatureTags.FEATURE_TAG_CHATBOT_VERSION_SUPPORTED)));
+        map.put("chatbot_sa_v2", new ArraySet<>(Arrays.asList(
+                FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_STANDALONE_MSG,
+                FeatureTags.FEATURE_TAG_CHATBOT_VERSION_SUPPORTED)));
+        map.put("chatbot_role", Collections.singleton(FeatureTags.FEATURE_TAG_CHATBOT_ROLE));
+        TEST_FEATURE_TAG_MAP = Collections.unmodifiableMap(map);
+    }
+
+
     public TelephonyShellCommand(ITelephony binder, Context context) {
         mInterface = binder;
         mCarrierConfigManager =
                 (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
         mSubscriptionManager = (SubscriptionManager)
                 context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+        mTelephonyRegistryManager = (TelephonyRegistryManager)
+                context.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE);
         mContext = context;
     }
 
@@ -190,6 +282,8 @@
                 return handleRcsUceCommand();
             case NUMBER_VERIFICATION_SUBCOMMAND:
                 return handleNumberVerificationCommand();
+            case EMERGENCY_CALLBACK_MODE:
+                return handleEmergencyCallbackModeCommand();
             case EMERGENCY_NUMBER_TEST_MODE:
                 return handleEmergencyNumberTestModeCommand();
             case CARRIER_CONFIG_SUBCOMMAND: {
@@ -203,12 +297,27 @@
                 return handleGbaCommand();
             case D2D_SUBCOMMAND:
                 return handleD2dCommand();
+            case BARRING_SUBCOMMAND:
+                return handleBarringCommand();
             case SINGLE_REGISTATION_CONFIG:
                 return handleSingleRegistrationConfigCommand();
             case RESTART_MODEM:
                 return handleRestartModemCommand();
             case CALL_COMPOSER_SUBCOMMAND:
                 return handleCallComposerCommand();
+            case UNATTENDED_REBOOT:
+                return handleUnattendedReboot();
+            case HAS_CARRIER_PRIVILEGES_COMMAND:
+                return handleHasCarrierPrivilegesCommand();
+            case THERMAL_MITIGATION_COMMAND:
+                return handleThermalMitigationCommand();
+            case DISABLE_PHYSICAL_SUBSCRIPTION:
+                return handleEnablePhysicalSubscription(false);
+            case ENABLE_PHYSICAL_SUBSCRIPTION:
+                return handleEnablePhysicalSubscription(true);
+            case GET_ALLOWED_NETWORK_TYPES_FOR_USER:
+            case SET_ALLOWED_NETWORK_TYPES_FOR_USER:
+                return handleAllowedNetworkTypesCommand(cmd);
             default: {
                 return handleDefaultCommands(cmd);
             }
@@ -239,6 +348,14 @@
         pw.println("    RCS VoLTE Single Registration Config Commands.");
         pw.println("  restart-modem");
         pw.println("    Restart modem command.");
+        pw.println("  unattended-reboot");
+        pw.println("    Prepare for unattended reboot.");
+        pw.println("  has-carrier-privileges [package]");
+        pw.println("    Query carrier privilege status for a package. Prints true or false.");
+        pw.println("  get-allowed-network-types-for-users");
+        pw.println("    Get the Allowed Network Types.");
+        pw.println("  set-allowed-network-types-for-users");
+        pw.println("    Set the Allowed Network Types.");
         onHelpIms();
         onHelpUce();
         onHelpEmergencyNumber();
@@ -248,6 +365,8 @@
         onHelpGba();
         onHelpSrc();
         onHelpD2D();
+        onHelpDisableOrEnablePhysicalSubscription();
+        onHelpAllowedNetworkTypes();
     }
 
     private void onHelpD2D() {
@@ -264,6 +383,25 @@
                         MESSAGE_DEVICE_BATTERY_STATE));
         pw.println("    Type: " + MESSAGE_DEVICE_NETWORK_COVERAGE + " - "
                 + Communicator.messageToString(MESSAGE_DEVICE_NETWORK_COVERAGE));
+        pw.println("  d2d transport TYPE");
+        pw.println("    Forces the specified D2D transport TYPE to be active.  Use the");
+        pw.println("    short class name of the transport; i.e. DtmfTransport or RtpTransport.");
+        pw.println("  d2d set-device-support true/default");
+        pw.println("    true - forces device support to be enabled for D2D.");
+        pw.println("    default - clear any previously set force-enable of D2D, reverting to ");
+        pw.println("    the current device's configuration.");
+    }
+
+    private void onHelpBarring() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("Barring Commands:");
+        pw.println("  barring send -s SLOT_ID -b BARRING_TYPE -c IS_CONDITIONALLY_BARRED"
+                + " -t CONDITIONAL_BARRING_TIME_SECS");
+        pw.println("    Notifies of a barring info change for the specified slot id.");
+        pw.println("    BARRING_TYPE: 0 for BARRING_TYPE_NONE");
+        pw.println("    BARRING_TYPE: 1 for BARRING_TYPE_UNCONDITIONAL");
+        pw.println("    BARRING_TYPE: 2 for BARRING_TYPE_CONDITIONAL");
+        pw.println("    BARRING_TYPE: -1 for BARRING_TYPE_UNKNOWN");
     }
 
     private void onHelpIms() {
@@ -317,6 +455,31 @@
         pw.println("      -s: The SIM slot ID to read carrier config value for. If no option");
         pw.println("          is specified, it will choose the default voice SIM slot.");
         pw.println("      PHONE_NUMBER: The phone numbers to be removed from the EAB databases");
+        pw.println("  uce get-device-enabled");
+        pw.println("    Get the config to check whether the device supports RCS UCE or not.");
+        pw.println("  uce set-device-enabled true|false");
+        pw.println("    Set the device config for RCS User Capability Exchange to the value.");
+        pw.println("    The value could be true, false.");
+        pw.println("  uce override-published-caps [-s SLOT_ID] add|remove|clear [CAPABILITIES]");
+        pw.println("    Override the existing SIP PUBLISH with different capabilities.");
+        pw.println("    Options are:");
+        pw.println("      -s: The SIM slot ID to read carrier config value for. If no option");
+        pw.println("          is specified, it will choose the default voice SIM slot.");
+        pw.println("      add [CAPABILITY]: add a new capability");
+        pw.println("      remove [CAPABILITY]: remove a capability");
+        pw.println("      clear: clear all capability overrides");
+        pw.println("      CAPABILITY: \":\" separated list of capabilities.");
+        pw.println("          Valid options are: [mmtel(_vt), chat_v1, chat_v2, ft, ft_sms,");
+        pw.println("          geo_push, geo_push_sms, call_comp, call_post, map, sketch, chatbot,");
+        pw.println("          chatbot_sa, chatbot_role] as well as full length");
+        pw.println("          featureTag=\"featureValue\" feature tags that are not defined here.");
+        pw.println("  uce get-last-publish-pidf [-s SLOT_ID]");
+        pw.println("    Get the PIDF XML included in the last SIP PUBLISH, or \"none\" if no ");
+        pw.println("    PUBLISH is active");
+        pw.println("  uce remove-request-disallowed-status [-s SLOT_ID]");
+        pw.println("    Remove the UCE is disallowed to execute UCE requests status");
+        pw.println("  uce set-capabilities-request-timeout [-s SLOT_ID] [REQUEST_TIMEOUT_MS]");
+        pw.println("    Set the timeout for contact capabilities request.");
     }
 
     private void onHelpNumberVerification() {
@@ -330,6 +493,25 @@
         pw.println("    1 if the call would have been intercepted, 0 otherwise.");
     }
 
+    private void onHelpThermalMitigation() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("Thermal mitigation commands");
+        pw.println("  thermal-mitigation allow-package PACKAGE_NAME");
+        pw.println("    Set the package as one of authorized packages for thermal mitigation.");
+        pw.println("  thermal-mitigation disallow-package PACKAGE_NAME");
+        pw.println("    Remove the package from one of the authorized packages for thermal "
+                + "mitigation.");
+    }
+
+    private void onHelpDisableOrEnablePhysicalSubscription() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("Disable or enable a physical subscription");
+        pw.println("  disable-physical-subscription SUB_ID");
+        pw.println("    Disable the physical subscription with the provided subId, if allowed.");
+        pw.println("  enable-physical-subscription SUB_ID");
+        pw.println("    Enable the physical subscription with the provided subId, if allowed.");
+    }
+
     private void onHelpDataTestMode() {
         PrintWriter pw = getOutPrintWriter();
         pw.println("Mobile Data Test Mode Commands:");
@@ -415,6 +597,11 @@
     private void onHelpSrc() {
         PrintWriter pw = getOutPrintWriter();
         pw.println("RCS VoLTE Single Registration Config Commands:");
+        pw.println("  src set-test-enabled true|false");
+        pw.println("    Sets the test mode enabled for RCS VoLTE single registration.");
+        pw.println("    The value could be true, false, or null(undefined).");
+        pw.println("  src get-test-enabled");
+        pw.println("    Gets the test mode for RCS VoLTE single registration.");
         pw.println("  src set-device-enabled true|false|null");
         pw.println("    Sets the device config for RCS VoLTE single registration to the value.");
         pw.println("    The value could be true, false, or null(undefined).");
@@ -431,6 +618,41 @@
         pw.println("    Options are:");
         pw.println("      -s: The SIM slot ID to read the config value for. If no option");
         pw.println("          is specified, it will choose the default voice SIM slot.");
+        pw.println("  src set-feature-validation [-s SLOT_ID] true|false|null");
+        pw.println("    Sets ims feature validation result.");
+        pw.println("    The value could be true, false, or null(undefined).");
+        pw.println("    Options are:");
+        pw.println("      -s: The SIM slot ID to set the config value for. If no option");
+        pw.println("          is specified, it will choose the default voice SIM slot.");
+        pw.println("  src get-feature-validation [-s SLOT_ID]");
+        pw.println("    Gets ims feature validation override value.");
+        pw.println("    Options are:");
+        pw.println("      -s: The SIM slot ID to read the config value for. If no option");
+        pw.println("          is specified, it will choose the default voice SIM slot.");
+    }
+
+    private void onHelpAllowedNetworkTypes() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("Allowed Network Types Commands:");
+        pw.println("  get-allowed-network-types-for-users [-s SLOT_ID]");
+        pw.println("    Print allowed network types value.");
+        pw.println("    Options are:");
+        pw.println("      -s: The SIM slot ID to read allowed network types value for. If no");
+        pw.println("          option is specified, it will choose the default voice SIM slot.");
+        pw.println("  set-allowed-network-types-for-users [-s SLOT_ID] [NETWORK_TYPES_BITMASK]");
+        pw.println("    Sets allowed network types to NETWORK_TYPES_BITMASK.");
+        pw.println("    Options are:");
+        pw.println("      -s: The SIM slot ID to set allowed network types value for. If no");
+        pw.println("          option is specified, it will choose the default voice SIM slot.");
+        pw.println("    NETWORK_TYPES_BITMASK specifies the new network types value and this type");
+        pw.println("      is bitmask in binary format. Reference the NetworkTypeBitMask");
+        pw.println("      at TelephonyManager.java");
+        pw.println("      For example:");
+        pw.println("        NR only                    : 10000000000000000000");
+        pw.println("        NR|LTE                     : 11000001000000000000");
+        pw.println("        NR|LTE|CDMA|EVDO|GSM|WCDMA : 11001111101111111111");
+        pw.println("        LTE|CDMA|EVDO|GSM|WCDMA    : 01001111101111111111");
+        pw.println("        LTE only                   : 01000001000000000000");
     }
 
     private int handleImsCommand() {
@@ -499,6 +721,19 @@
         return 0;
     }
 
+    private int handleEmergencyCallbackModeCommand() {
+        PrintWriter errPw = getErrPrintWriter();
+        try {
+            mInterface.startEmergencyCallbackMode();
+            Log.d(LOG_TAG, "handleEmergencyCallbackModeCommand: triggered");
+        } catch (RemoteException ex) {
+            Log.w(LOG_TAG, "emergency-callback-mode error: " + ex.getMessage());
+            errPw.println("Exception: " + ex.getMessage());
+            return -1;
+        }
+        return 0;
+    }
+
     private int handleEmergencyNumberTestModeCommand() {
         PrintWriter errPw = getErrPrintWriter();
         String opt = getNextOption();
@@ -611,6 +846,71 @@
         return -1;
     }
 
+    private boolean subIsEsim(int subId) {
+        SubscriptionInfo info = mSubscriptionManager.getActiveSubscriptionInfo(subId);
+        if (info != null) {
+            return info.isEmbedded();
+        }
+        return false;
+    }
+
+    private int handleEnablePhysicalSubscription(boolean enable) {
+        PrintWriter errPw = getErrPrintWriter();
+        int subId = 0;
+        try {
+            subId = Integer.parseInt(getNextArgRequired());
+        } catch (NumberFormatException e) {
+            errPw.println((enable ? "enable" : "disable")
+                    + "-physical-subscription requires an integer as a subId.");
+            return -1;
+        }
+        // Verify that the user is allowed to run the command. Only allowed in rooted device in a
+        // non user build.
+        if (Binder.getCallingUid() != Process.ROOT_UID || TelephonyUtils.IS_USER) {
+            errPw.println("cc: Permission denied.");
+            return -1;
+        }
+        // Verify that the subId represents a physical sub
+        if (subIsEsim(subId)) {
+            errPw.println("SubId " + subId + " is not for a physical subscription");
+            return -1;
+        }
+        Log.d(LOG_TAG, (enable ? "Enabling" : "Disabling")
+                + " physical subscription with subId=" + subId);
+        mSubscriptionManager.setUiccApplicationsEnabled(subId, enable);
+        return 0;
+    }
+
+    private int handleThermalMitigationCommand() {
+        String arg = getNextArg();
+        String packageName = getNextArg();
+        if (arg == null || packageName == null) {
+            onHelpThermalMitigation();
+            return 0;
+        }
+
+        if (!checkShellUid()) {
+            return -1;
+        }
+
+        switch (arg) {
+            case ALLOW_THERMAL_MITIGATION_PACKAGE_SUBCOMMAND: {
+                PhoneInterfaceManager.addPackageToThermalMitigationAllowlist(packageName, mContext);
+                return 0;
+            }
+            case DISALLOW_THERMAL_MITIGATION_PACKAGE_SUBCOMMAND: {
+                PhoneInterfaceManager.removePackageFromThermalMitigationAllowlist(packageName,
+                        mContext);
+                return 0;
+            }
+            default:
+                onHelpThermalMitigation();
+        }
+
+        return -1;
+
+    }
+
     private int handleD2dCommand() {
         String arg = getNextArg();
         if (arg == null) {
@@ -622,6 +922,12 @@
             case D2D_SEND: {
                 return handleD2dSendCommand();
             }
+            case D2D_TRANSPORT: {
+                return handleD2dTransportCommand();
+            }
+            case D2D_SET_DEVICE_SUPPORT: {
+                return handleD2dDeviceSupportedCommand();
+            }
         }
 
         return -1;
@@ -629,11 +935,9 @@
 
     private int handleD2dSendCommand() {
         PrintWriter errPw = getErrPrintWriter();
-        String opt;
         int messageType = -1;
         int messageValue = -1;
 
-
         String arg = getNextArg();
         if (arg == null) {
             onHelpD2D();
@@ -657,7 +961,7 @@
             errPw.println("message value must be a valid integer");
             return -1;
         }
-        
+
         try {
             mInterface.sendDeviceToDeviceMessage(messageType, messageValue);
         } catch (RemoteException e) {
@@ -669,6 +973,132 @@
         return 0;
     }
 
+    private int handleD2dTransportCommand() {
+        PrintWriter errPw = getErrPrintWriter();
+
+        String arg = getNextArg();
+        if (arg == null) {
+            onHelpD2D();
+            return 0;
+        }
+
+        try {
+            mInterface.setActiveDeviceToDeviceTransport(arg);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "d2d transport error: " + e.getMessage());
+            errPw.println("Exception: " + e.getMessage());
+            return -1;
+        }
+        return 0;
+    }
+    private int handleBarringCommand() {
+        String arg = getNextArg();
+        if (arg == null) {
+            onHelpBarring();
+            return 0;
+        }
+
+        switch (arg) {
+            case BARRING_SEND_INFO: {
+                return handleBarringSendCommand();
+            }
+        }
+        return -1;
+    }
+
+    private int handleBarringSendCommand() {
+        PrintWriter errPw = getErrPrintWriter();
+        int slotId = getDefaultSlot();
+        int subId = SubscriptionManager.getSubId(slotId)[0];
+        @BarringInfo.BarringServiceInfo.BarringType int barringType =
+                BarringInfo.BarringServiceInfo.BARRING_TYPE_UNCONDITIONAL;
+        boolean isConditionallyBarred = false;
+        int conditionalBarringTimeSeconds = 0;
+
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "-s": {
+                    try {
+                        slotId = Integer.parseInt(getNextArgRequired());
+                        subId = SubscriptionManager.getSubId(slotId)[0];
+                    } catch (NumberFormatException e) {
+                        errPw.println("barring send requires an integer as a SLOT_ID.");
+                        return -1;
+                    }
+                    break;
+                }
+                case "-b": {
+                    try {
+                        barringType = Integer.parseInt(getNextArgRequired());
+                        if (barringType < -1 || barringType > 2) {
+                            throw new NumberFormatException();
+                        }
+
+                    } catch (NumberFormatException e) {
+                        errPw.println("barring send requires an integer in range [-1,2] as "
+                                + "a BARRING_TYPE.");
+                        return -1;
+                    }
+                    break;
+                }
+                case "-c": {
+                    try {
+                        isConditionallyBarred = Boolean.parseBoolean(getNextArgRequired());
+                    } catch (Exception e) {
+                        errPw.println("barring send requires a boolean after -c indicating"
+                                + " conditional barring");
+                        return -1;
+                    }
+                    break;
+                }
+                case "-t": {
+                    try {
+                        conditionalBarringTimeSeconds = Integer.parseInt(getNextArgRequired());
+                    } catch (NumberFormatException e) {
+                        errPw.println("barring send requires an integer for time of barring"
+                                + " in seconds after -t for conditional barring");
+                        return -1;
+                    }
+                    break;
+                }
+            }
+        }
+        SparseArray<BarringInfo.BarringServiceInfo> barringServiceInfos = new SparseArray<>();
+        BarringInfo.BarringServiceInfo bsi = new BarringInfo.BarringServiceInfo(
+                barringType, isConditionallyBarred, 0, conditionalBarringTimeSeconds);
+        barringServiceInfos.append(0, bsi);
+        BarringInfo barringInfo = new BarringInfo(null, barringServiceInfos);
+        try {
+            mTelephonyRegistryManager.notifyBarringInfoChanged(slotId, subId, barringInfo);
+        } catch (Exception e) {
+            Log.w(LOG_TAG, "barring send error: " + e.getMessage());
+            errPw.println("Exception: " + e.getMessage());
+            return -1;
+        }
+        return 0;
+    }
+
+    private int handleD2dDeviceSupportedCommand() {
+        PrintWriter errPw = getErrPrintWriter();
+
+        String arg = getNextArg();
+        if (arg == null) {
+            onHelpD2D();
+            return 0;
+        }
+
+        boolean isEnabled = "true".equals(arg.toLowerCase());
+        try {
+            mInterface.setDeviceToDeviceForceEnabled(isEnabled);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Error forcing D2D enabled: " + e.getMessage());
+            errPw.println("Exception: " + e.getMessage());
+            return -1;
+        }
+        return 0;
+    }
+
     // ims set-ims-service
     private int handleImsSetServiceCommand() {
         PrintWriter errPw = getErrPrintWriter();
@@ -1459,6 +1889,20 @@
         return result ? 0 : -1;
     }
 
+    private int handleUnattendedReboot() {
+        // Verify that the user is allowed to run the command. Only allowed in rooted device in a
+        // non user build.
+        if (Binder.getCallingUid() != Process.ROOT_UID || TelephonyUtils.IS_USER) {
+            getErrPrintWriter().println("UnattendedReboot: Permission denied.");
+            return -1;
+        }
+
+        int result = TelephonyManager.getDefault().prepareForUnattendedReboot();
+        getOutPrintWriter().println("result: " + result);
+
+        return result != TelephonyManager.PREPARE_UNATTENDED_REBOOT_ERROR ? 0 : -1;
+    }
+
     private int handleGbaCommand() {
         String arg = getNextArg();
         if (arg == null) {
@@ -1602,6 +2046,12 @@
         }
 
         switch (arg) {
+            case SRC_SET_TEST_ENABLED: {
+                return handleSrcSetTestEnabledCommand();
+            }
+            case SRC_GET_TEST_ENABLED: {
+                return handleSrcGetTestEnabledCommand();
+            }
             case SRC_SET_DEVICE_ENABLED: {
                 return handleSrcSetDeviceEnabledCommand();
             }
@@ -1614,6 +2064,12 @@
             case SRC_GET_CARRIER_ENABLED: {
                 return handleSrcGetCarrierEnabledCommand();
             }
+            case SRC_SET_FEATURE_ENABLED: {
+                return handleSrcSetFeatureValidationCommand();
+            }
+            case SRC_GET_FEATURE_ENABLED: {
+                return handleSrcGetFeatureValidationCommand();
+            }
         }
 
         return -1;
@@ -1622,8 +2078,8 @@
     private int handleRcsUceCommand() {
         String arg = getNextArg();
         if (arg == null) {
-            Log.w(LOG_TAG, "cannot get uce parameter");
-            return -1;
+            onHelpUce();
+            return 0;
         }
 
         switch (arg) {
@@ -1631,6 +2087,18 @@
                 return handleRemovingEabContactCommand();
             case UCE_GET_EAB_CONTACT:
                 return handleGettingEabContactCommand();
+            case UCE_GET_DEVICE_ENABLED:
+                return handleUceGetDeviceEnabledCommand();
+            case UCE_SET_DEVICE_ENABLED:
+                return handleUceSetDeviceEnabledCommand();
+            case UCE_OVERRIDE_PUBLISH_CAPS:
+                return handleUceOverridePublishCaps();
+            case UCE_GET_LAST_PIDF_XML:
+                return handleUceGetPidfXml();
+            case UCE_REMOVE_REQUEST_DISALLOWED_STATUS:
+                return handleUceRemoveRequestDisallowedStatus();
+            case UCE_SET_CAPABILITY_REQUEST_TIMEOUT:
+                return handleUceSetCapRequestTimeout();
         }
         return -1;
     }
@@ -1657,7 +2125,7 @@
         if (VDBG) {
             Log.v(LOG_TAG, "uce remove-eab-contact -s " + subId + ", result: " + result);
         }
-        return result;
+        return 0;
     }
 
     private int handleGettingEabContactCommand() {
@@ -1682,6 +2150,211 @@
         return 0;
     }
 
+    private int handleUceGetDeviceEnabledCommand() {
+        boolean result = false;
+        try {
+            result = mInterface.getDeviceUceEnabled();
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "uce get-device-enabled, error " + e.getMessage());
+            return -1;
+        }
+        if (VDBG) {
+            Log.v(LOG_TAG, "uce get-device-enabled, returned: " + result);
+        }
+        getOutPrintWriter().println(result);
+        return 0;
+    }
+
+    private int handleUceSetDeviceEnabledCommand() {
+        String enabledStr = getNextArg();
+        if (TextUtils.isEmpty(enabledStr)) {
+            return -1;
+        }
+
+        try {
+            boolean isEnabled = Boolean.parseBoolean(enabledStr);
+            mInterface.setDeviceUceEnabled(isEnabled);
+            if (VDBG) {
+                Log.v(LOG_TAG, "uce set-device-enabled " + enabledStr + ", done");
+            }
+        } catch (NumberFormatException | RemoteException e) {
+            Log.w(LOG_TAG, "uce set-device-enabled " + enabledStr + ", error " + e.getMessage());
+            getErrPrintWriter().println("Exception: " + e.getMessage());
+            return -1;
+        }
+        return 0;
+    }
+
+    private int handleUceRemoveRequestDisallowedStatus() {
+        int subId = getSubId("uce remove-request-disallowed-status");
+        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            Log.w(LOG_TAG, "uce remove-request-disallowed-status, Invalid subscription ID");
+            return -1;
+        }
+        boolean result;
+        try {
+            result = mInterface.removeUceRequestDisallowedStatus(subId);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "uce remove-request-disallowed-status, error " + e.getMessage());
+            return -1;
+        }
+        if (VDBG) {
+            Log.v(LOG_TAG, "uce remove-request-disallowed-status, returned: " + result);
+        }
+        getOutPrintWriter().println(result);
+        return 0;
+    }
+
+    private int handleUceSetCapRequestTimeout() {
+        int subId = getSubId("uce set-capabilities-request-timeout");
+        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            Log.w(LOG_TAG, "uce set-capabilities-request-timeout, Invalid subscription ID");
+            return -1;
+        }
+        long timeoutAfterMs = Long.valueOf(getNextArg());
+        boolean result;
+        try {
+            result = mInterface.setCapabilitiesRequestTimeout(subId, timeoutAfterMs);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "uce set-capabilities-request-timeout, error " + e.getMessage());
+            return -1;
+        }
+        if (VDBG) {
+            Log.v(LOG_TAG, "uce set-capabilities-request-timeout, returned: " + result);
+        }
+        getOutPrintWriter().println(result);
+        return 0;
+    }
+
+    private int handleSrcSetTestEnabledCommand() {
+        String enabledStr = getNextArg();
+        if (enabledStr == null) {
+            return -1;
+        }
+
+        try {
+            mInterface.setRcsSingleRegistrationTestModeEnabled(Boolean.parseBoolean(enabledStr));
+            if (VDBG) {
+                Log.v(LOG_TAG, "src set-test-enabled " + enabledStr + ", done");
+            }
+            getOutPrintWriter().println("Done");
+        } catch (NumberFormatException | RemoteException e) {
+            Log.w(LOG_TAG, "src set-test-enabled " + enabledStr + ", error" + e.getMessage());
+            getErrPrintWriter().println("Exception: " + e.getMessage());
+            return -1;
+        }
+        return 0;
+    }
+
+    private int handleSrcGetTestEnabledCommand() {
+        boolean result = false;
+        try {
+            result = mInterface.getRcsSingleRegistrationTestModeEnabled();
+        } catch (RemoteException e) {
+            return -1;
+        }
+        if (VDBG) {
+            Log.v(LOG_TAG, "src get-test-enabled, returned: " + result);
+        }
+        getOutPrintWriter().println(result);
+        return 0;
+    }
+
+    private int handleUceOverridePublishCaps() {
+        int subId = getSubId("uce override-published-caps");
+        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            return -1;
+        }
+        //uce override-published-caps [-s SLOT_ID] add|remove|clear|list [CAPABILITIES]
+        String operation = getNextArgRequired();
+        String caps = getNextArg();
+        if (!"add".equals(operation) && !"remove".equals(operation) && !"clear".equals(operation)
+                && !"list".equals(operation)) {
+            getErrPrintWriter().println("Invalid operation: " + operation);
+            return -1;
+        }
+
+        // add/remove requires capabilities to be specified.
+        if ((!"clear".equals(operation) && !"list".equals(operation)) && TextUtils.isEmpty(caps)) {
+            getErrPrintWriter().println("\"" + operation + "\" requires capabilities to be "
+                    + "specified");
+            return -1;
+        }
+
+        ArraySet<String> capSet = new ArraySet<>();
+        if (!TextUtils.isEmpty(caps)) {
+            String[] capArray = caps.split(":");
+            for (String cap : capArray) {
+                // Allow unknown tags to be passed in as well.
+                capSet.addAll(TEST_FEATURE_TAG_MAP.getOrDefault(cap, Collections.singleton(cap)));
+            }
+        }
+
+        RcsContactUceCapability result = null;
+        try {
+            switch (operation) {
+                case "add":
+                    result = mInterface.addUceRegistrationOverrideShell(subId,
+                            new ArrayList<>(capSet));
+                    break;
+                case "remove":
+                    result = mInterface.removeUceRegistrationOverrideShell(subId,
+                            new ArrayList<>(capSet));
+                    break;
+                case "clear":
+                    result = mInterface.clearUceRegistrationOverrideShell(subId);
+                    break;
+                case "list":
+                    result = mInterface.getLatestRcsContactUceCapabilityShell(subId);
+                    break;
+            }
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "uce override-published-caps, error " + e.getMessage());
+            getErrPrintWriter().println("Exception: " + e.getMessage());
+            return -1;
+        } catch (ServiceSpecificException sse) {
+            // Reconstruct ImsException
+            ImsException imsException = new ImsException(sse.getMessage(), sse.errorCode);
+            Log.w(LOG_TAG, "uce override-published-caps, error " + imsException);
+            getErrPrintWriter().println("Exception: " + imsException);
+            return -1;
+        }
+        if (result == null) {
+            getErrPrintWriter().println("Service not available");
+            return -1;
+        }
+        getOutPrintWriter().println(result);
+        return 0;
+    }
+
+    private int handleUceGetPidfXml() {
+        int subId = getSubId("uce get-last-publish-pidf");
+        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            return -1;
+        }
+
+        String result;
+        try {
+            result = mInterface.getLastUcePidfXmlShell(subId);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "uce get-last-publish-pidf, error " + e.getMessage());
+            getErrPrintWriter().println("Exception: " + e.getMessage());
+            return -1;
+        } catch (ServiceSpecificException sse) {
+            // Reconstruct ImsException
+            ImsException imsException = new ImsException(sse.getMessage(), sse.errorCode);
+            Log.w(LOG_TAG, "uce get-last-publish-pidf error " + imsException);
+            getErrPrintWriter().println("Exception: " + imsException);
+            return -1;
+        }
+        if (result == null) {
+            getErrPrintWriter().println("Service not available");
+            return -1;
+        }
+        getOutPrintWriter().println(result);
+        return 0;
+    }
+
     private int handleSrcSetDeviceEnabledCommand() {
         String enabledStr = getNextArg();
         if (enabledStr == null) {
@@ -1765,6 +2438,56 @@
         return 0;
     }
 
+    private int handleSrcSetFeatureValidationCommand() {
+        //the release time value could be -1
+        int subId = getRemainingArgsCount() > 1 ? getSubId("src set-feature-validation")
+                : SubscriptionManager.getDefaultSubscriptionId();
+        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            return -1;
+        }
+
+        String enabledStr = getNextArg();
+        if (enabledStr == null) {
+            return -1;
+        }
+
+        try {
+            boolean result =
+                    mInterface.setImsFeatureValidationOverride(subId, enabledStr);
+            if (VDBG) {
+                Log.v(LOG_TAG, "src set-feature-validation -s " + subId + " "
+                        + enabledStr + ", result=" + result);
+            }
+            getOutPrintWriter().println(result);
+        } catch (NumberFormatException | RemoteException e) {
+            Log.w(LOG_TAG, "src set-feature-validation -s " + subId + " "
+                    + enabledStr + ", error" + e.getMessage());
+            getErrPrintWriter().println("Exception: " + e.getMessage());
+            return -1;
+        }
+        return 0;
+    }
+
+    private int handleSrcGetFeatureValidationCommand() {
+        int subId = getSubId("src get-feature-validation");
+        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            return -1;
+        }
+
+        Boolean result = false;
+        try {
+            result = mInterface.getImsFeatureValidationOverride(subId);
+        } catch (RemoteException e) {
+            return -1;
+        }
+        if (VDBG) {
+            Log.v(LOG_TAG, "src get-feature-validation -s " + subId + ", returned: " + result);
+        }
+        getOutPrintWriter().println(result);
+        return 0;
+    }
+
+
     private void onHelpCallComposer() {
         PrintWriter pw = getOutPrintWriter();
         pw.println("Call composer commands");
@@ -1775,6 +2498,9 @@
         pw.println("  callcomposer simulate-outgoing-call [subId] [UUID]");
         pw.println("    Simulates an outgoing call being placed with the picture ID as");
         pw.println("    the provided UUID. This triggers storage to the call log.");
+        pw.println("  callcomposer user-setting [subId] enable|disable|query");
+        pw.println("    Enables or disables the user setting for call composer, as set by");
+        pw.println("    TelephonyManager#setCallComposerStatus.");
     }
 
     private int handleCallComposerCommand() {
@@ -1818,8 +2544,162 @@
                 }
                 break;
             }
+            case CALL_COMPOSER_USER_SETTING: {
+                try {
+                    int subscriptionId = Integer.valueOf(getNextArg());
+                    String enabledStr = getNextArg();
+                    if (ENABLE.equals(enabledStr)) {
+                        mInterface.setCallComposerStatus(subscriptionId,
+                                TelephonyManager.CALL_COMPOSER_STATUS_ON);
+                    } else if (DISABLE.equals(enabledStr)) {
+                        mInterface.setCallComposerStatus(subscriptionId,
+                                TelephonyManager.CALL_COMPOSER_STATUS_OFF);
+                    } else if (QUERY.equals(enabledStr)) {
+                        getOutPrintWriter().println(mInterface.getCallComposerStatus(subscriptionId)
+                                == TelephonyManager.CALL_COMPOSER_STATUS_ON);
+                    } else {
+                        onHelpCallComposer();
+                        return 1;
+                    }
+                } catch (RemoteException e) {
+                    e.printStackTrace(getOutPrintWriter());
+                    return 1;
+                }
+                break;
+            }
+        }
+        return 0;
+    }
+
+    private int handleHasCarrierPrivilegesCommand() {
+        String packageName = getNextArgRequired();
+
+        boolean hasCarrierPrivileges;
+        final long token = Binder.clearCallingIdentity();
+        try {
+            hasCarrierPrivileges =
+                    mInterface.checkCarrierPrivilegesForPackageAnyPhone(packageName)
+                            == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, HAS_CARRIER_PRIVILEGES_COMMAND + " exception", e);
+            getErrPrintWriter().println("Exception: " + e.getMessage());
+            return -1;
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
 
+        getOutPrintWriter().println(hasCarrierPrivileges);
         return 0;
     }
+
+    private int handleAllowedNetworkTypesCommand(String command) {
+        if (!checkShellUid()) {
+            return -1;
+        }
+
+        PrintWriter errPw = getErrPrintWriter();
+        String tag = command + ": ";
+        String opt;
+        int subId = -1;
+        Log.v(LOG_TAG, command + " start");
+
+        while ((opt = getNextOption()) != null) {
+            if (opt.equals("-s")) {
+                try {
+                    subId = slotStringToSubId(tag, getNextArgRequired());
+                    if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+                        errPw.println(tag + "No valid subscription found.");
+                        return -1;
+                    }
+                } catch (IllegalArgumentException e) {
+                    // Missing slot id
+                    errPw.println(tag + "SLOT_ID expected after -s.");
+                    return -1;
+                }
+            } else {
+                errPw.println(tag + "Unknown option " + opt);
+                return -1;
+            }
+        }
+
+        if (GET_ALLOWED_NETWORK_TYPES_FOR_USER.equals(command)) {
+            return handleGetAllowedNetworkTypesCommand(subId);
+        }
+        if (SET_ALLOWED_NETWORK_TYPES_FOR_USER.equals(command)) {
+            return handleSetAllowedNetworkTypesCommand(subId);
+        }
+        return -1;
+    }
+
+    private int handleGetAllowedNetworkTypesCommand(int subId) {
+        PrintWriter errPw = getErrPrintWriter();
+
+        long result = -1;
+        try {
+            if (mInterface != null) {
+                result = mInterface.getAllowedNetworkTypesForReason(subId,
+                        TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "getAllowedNetworkTypesForReason RemoteException" + e);
+            errPw.println(GET_ALLOWED_NETWORK_TYPES_FOR_USER + "RemoteException " + e);
+            return -1;
+        }
+
+        getOutPrintWriter().println(TelephonyManager.convertNetworkTypeBitmaskToString(result));
+        return 0;
+    }
+
+    private int handleSetAllowedNetworkTypesCommand(int subId) {
+        PrintWriter errPw = getErrPrintWriter();
+
+        String bitmaskString = getNextArg();
+        if (TextUtils.isEmpty(bitmaskString)) {
+            errPw.println(SET_ALLOWED_NETWORK_TYPES_FOR_USER + " No NETWORK_TYPES_BITMASK");
+            return -1;
+        }
+        long allowedNetworkTypes = convertNetworkTypeBitmaskFromStringToLong(bitmaskString);
+        if (allowedNetworkTypes < 0) {
+            errPw.println(SET_ALLOWED_NETWORK_TYPES_FOR_USER + " No valid NETWORK_TYPES_BITMASK");
+            return -1;
+        }
+        boolean result = false;
+        try {
+            if (mInterface != null) {
+                result = mInterface.setAllowedNetworkTypesForReason(subId,
+                        TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER, allowedNetworkTypes);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "setAllowedNetworkTypesForReason RemoteException" + e);
+            errPw.println(SET_ALLOWED_NETWORK_TYPES_FOR_USER + " RemoteException " + e);
+            return -1;
+        }
+
+        String resultMessage = SET_ALLOWED_NETWORK_TYPES_FOR_USER + " failed";
+        if (result) {
+            resultMessage = SET_ALLOWED_NETWORK_TYPES_FOR_USER + " completed";
+        }
+        getOutPrintWriter().println(resultMessage);
+        return 0;
+    }
+
+    private long convertNetworkTypeBitmaskFromStringToLong(String bitmaskString) {
+        if (TextUtils.isEmpty(bitmaskString)) {
+            return -1;
+        }
+        if (VDBG) {
+            Log.v(LOG_TAG, "AllowedNetworkTypes:" + bitmaskString
+                            + ", length: " + bitmaskString.length());
+        }
+        try {
+            return Long.parseLong(bitmaskString, 2);
+        } catch (NumberFormatException e) {
+            Log.e(LOG_TAG, "AllowedNetworkTypes: " + e);
+            return -1;
+        }
+    }
 }
diff --git a/src/com/android/phone/callcomposer/CallComposerPictureManager.java b/src/com/android/phone/callcomposer/CallComposerPictureManager.java
index b27a27c..efb149e 100644
--- a/src/com/android/phone/callcomposer/CallComposerPictureManager.java
+++ b/src/com/android/phone/callcomposer/CallComposerPictureManager.java
@@ -17,8 +17,10 @@
 package com.android.phone.callcomposer;
 
 import android.content.Context;
+import android.location.Location;
 import android.net.Uri;
 import android.os.OutcomeReceiver;
+import android.os.PersistableBundle;
 import android.os.UserHandle;
 import android.provider.CallLog;
 import android.telephony.CarrierConfigManager;
@@ -32,14 +34,12 @@
 import androidx.annotation.NonNull;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.phone.callcomposer.CallComposerPictureTransfer.PictureCallback;
 import com.android.phone.R;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
 import java.util.HashMap;
-import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executor;
@@ -52,6 +52,7 @@
 public class CallComposerPictureManager {
     private static final String TAG = CallComposerPictureManager.class.getSimpleName();
     private static final SparseArray<CallComposerPictureManager> sInstances = new SparseArray<>();
+    private static final String THREE_GPP_BOOTSTRAPPING = "3GPP-bootstrapping";
 
     public static CallComposerPictureManager getInstance(Context context, int subscriptionId) {
         synchronized (sInstances) {
@@ -81,6 +82,13 @@
     @VisibleForTesting
     public static boolean sTestMode = false;
     public static final String FAKE_SERVER_URL = "https://example.com/FAKE.png";
+    public static final String FAKE_SUBJECT = "This is a test call subject";
+    public static final Location FAKE_LOCATION = new Location("");
+    static {
+        // Meteor Crater, AZ
+        FAKE_LOCATION.setLatitude(35.027526);
+        FAKE_LOCATION.setLongitude(-111.021696);
+    }
 
     public interface CallLogProxy {
         default void storeCallComposerPictureAsUser(Context context,
@@ -88,7 +96,8 @@
                 InputStream input,
                 Executor executor,
                 OutcomeReceiver<Uri, CallLog.CallComposerLoggingException> callback) {
-            CallLog.storeCallComposerPictureAsUser(context, user, input, executor, callback);
+            CallLog.storeCallComposerPicture(context.createContextAsUser(user, 0),
+                    input, executor, callback);
         }
     }
 
@@ -96,7 +105,7 @@
 
     private final HashMap<UUID, String> mCachedServerUrls = new HashMap<>();
     private final HashMap<UUID, ImageData> mCachedImages = new HashMap<>();
-    private final Map<String, GbaCredentials> mCachedCredentials = new HashMap<>();
+    private GbaCredentials mCachedCredentials = null;
     private final int mSubscriptionId;
     private final TelephonyManager mTelephonyManager;
     private final Context mContext;
@@ -119,7 +128,8 @@
             return;
         }
 
-        String uploadUrl = mTelephonyManager.getCarrierConfig().getString(
+        PersistableBundle carrierConfig = mTelephonyManager.getCarrierConfig();
+        String uploadUrl = carrierConfig.getString(
                 CarrierConfigManager.KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING);
         if (TextUtils.isEmpty(uploadUrl)) {
             Log.e(TAG, "Call composer upload URL not configured in carrier config");
@@ -133,7 +143,7 @@
                 mSubscriptionId, uploadUrl, sExecutorService);
 
         AtomicBoolean hasRetried = new AtomicBoolean(false);
-        transfer.setCallback(new PictureCallback() {
+        transfer.setCallback(new CallComposerPictureTransfer.PictureCallback() {
             @Override
             public void onError(int error) {
                 callback.accept(Pair.create(null, error));
@@ -149,7 +159,7 @@
                 }
                 GbaCredentialsSupplier supplier =
                         (realm, executor) ->
-                                getGbaCredentials(credentialRefresh, realm, executor);
+                                getGbaCredentials(credentialRefresh, carrierConfig, executor);
 
                 sExecutorService.schedule(() -> transfer.uploadPicture(imageData, supplier),
                         backoffMillis, TimeUnit.MILLISECONDS);
@@ -166,7 +176,7 @@
         });
 
         transfer.uploadPicture(imageData,
-                (realm, executor) -> getGbaCredentials(false, realm, executor));
+                (realm, executor) -> getGbaCredentials(false, carrierConfig, executor));
     }
 
     public void handleDownloadFromServer(CallComposerPictureTransfer.Factory transferFactory,
@@ -179,11 +189,12 @@
             return;
         }
 
+        PersistableBundle carrierConfig = mTelephonyManager.getCarrierConfig();
         CallComposerPictureTransfer transfer = transferFactory.create(mContext,
                 mSubscriptionId, remoteUrl, sExecutorService);
 
         AtomicBoolean hasRetried = new AtomicBoolean(false);
-        transfer.setCallback(new PictureCallback() {
+        transfer.setCallback(new CallComposerPictureTransfer.PictureCallback() {
             @Override
             public void onError(int error) {
                 callback.accept(Pair.create(null, error));
@@ -199,7 +210,7 @@
                 }
                 GbaCredentialsSupplier supplier =
                         (realm, executor) ->
-                                getGbaCredentials(credentialRefresh, realm, executor);
+                                getGbaCredentials(credentialRefresh, carrierConfig, executor);
 
                 sExecutorService.schedule(() -> transfer.downloadPicture(supplier),
                         backoffMillis, TimeUnit.MILLISECONDS);
@@ -229,7 +240,8 @@
             }
         });
 
-        transfer.downloadPicture(((realm, executor) -> getGbaCredentials(false, realm, executor)));
+        transfer.downloadPicture(((realm, executor) ->
+                getGbaCredentials(false, carrierConfig, executor)));
     }
 
     public void storeUploadedPictureToCallLog(UUID id, Consumer<Uri> callback) {
@@ -290,32 +302,36 @@
     }
 
     private CompletableFuture<GbaCredentials> getGbaCredentials(
-            boolean forceRefresh, String nafId, Executor executor) {
-        synchronized (mCachedCredentials) {
-            if (!forceRefresh && mCachedCredentials.containsKey(nafId)) {
-                return CompletableFuture.completedFuture(mCachedCredentials.get(nafId));
+            boolean forceRefresh, PersistableBundle config, Executor executor) {
+        synchronized (this) {
+            if (!forceRefresh && mCachedCredentials != null) {
+                return CompletableFuture.completedFuture(mCachedCredentials);
             }
+
             if (forceRefresh) {
-                mCachedCredentials.remove(nafId);
+                mCachedCredentials = null;
             }
         }
 
         UaSecurityProtocolIdentifier securityProtocolIdentifier =
                 new UaSecurityProtocolIdentifier.Builder()
-                        .setOrg(UaSecurityProtocolIdentifier.ORG_3GPP)
-                        .setProtocol(UaSecurityProtocolIdentifier
-                                .UA_SECURITY_PROTOCOL_3GPP_HTTP_DIGEST_AUTHENTICATION)
+                        .setOrg(config.getInt(
+                                CarrierConfigManager.KEY_GBA_UA_SECURITY_ORGANIZATION_INT))
+                        .setProtocol(config.getInt(
+                                CarrierConfigManager.KEY_GBA_UA_SECURITY_PROTOCOL_INT))
+                        .setTlsCipherSuite(config.getInt(
+                                CarrierConfigManager.KEY_GBA_UA_TLS_CIPHER_SUITE_INT))
                         .build();
         CompletableFuture<GbaCredentials> resultFuture = new CompletableFuture<>();
 
-        mTelephonyManager.bootstrapAuthenticationRequest(TelephonyManager.APPTYPE_UNKNOWN,
-                Uri.parse(nafId), securityProtocolIdentifier, forceRefresh, executor,
+        mTelephonyManager.bootstrapAuthenticationRequest(TelephonyManager.APPTYPE_ISIM,
+                getNafUri(config), securityProtocolIdentifier, forceRefresh, executor,
                 new TelephonyManager.BootstrapAuthenticationCallback() {
                     @Override
                     public void onKeysAvailable(byte[] gbaKey, String transactionId) {
                         GbaCredentials creds = new GbaCredentials(transactionId, gbaKey);
-                        synchronized (mCachedCredentials) {
-                            mCachedCredentials.put(nafId, creds);
+                        synchronized (CallComposerPictureManager.this) {
+                            mCachedCredentials = creds;
                         }
                         resultFuture.complete(creds);
                     }
@@ -330,6 +346,30 @@
         return resultFuture;
     }
 
+    private static Uri getNafUri(PersistableBundle carrierConfig) {
+        String uploadUriString = carrierConfig.getString(
+                CarrierConfigManager.KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING);
+        Uri uploadUri = Uri.parse(uploadUriString);
+        String nafPrefix;
+        switch (carrierConfig.getInt(CarrierConfigManager.KEY_GBA_MODE_INT)) {
+            case CarrierConfigManager.GBA_U:
+                nafPrefix = THREE_GPP_BOOTSTRAPPING + "-uicc";
+                break;
+            case CarrierConfigManager.GBA_DIGEST:
+                nafPrefix = THREE_GPP_BOOTSTRAPPING + "-digest";
+                break;
+            case CarrierConfigManager.GBA_ME:
+            default:
+                nafPrefix = THREE_GPP_BOOTSTRAPPING;
+        }
+        String newAuthority = nafPrefix + "@" + uploadUri.getAuthority();
+        Uri nafUri = new Uri.Builder().scheme(uploadUri.getScheme())
+                .encodedAuthority(newAuthority)
+                .build();
+        Log.i(TAG, "using NAF uri " + nafUri + " for GBA");
+        return nafUri;
+    }
+
     @VisibleForTesting
     static ScheduledExecutorService getExecutor() {
         return sExecutorService;
diff --git a/src/com/android/phone/callcomposer/CallComposerPictureTransfer.java b/src/com/android/phone/callcomposer/CallComposerPictureTransfer.java
index 1a176dd..e4458cd 100644
--- a/src/com/android/phone/callcomposer/CallComposerPictureTransfer.java
+++ b/src/com/android/phone/callcomposer/CallComposerPictureTransfer.java
@@ -21,6 +21,7 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
+import android.os.Build;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
@@ -49,6 +50,9 @@
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.nio.charset.Charset;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
 import java.util.Iterator;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
@@ -140,7 +144,10 @@
                         mExecutorService);
         networkUrlFuture.thenAcceptAsync((result) -> {
             if (result != null) mCallback.onUploadSuccessful(result);
-        }, mExecutorService);
+        }, mExecutorService).exceptionally((ex) -> {
+            logException("Exception uploading image" , ex);
+            return null;
+        });
     }
 
     public void downloadPicture(GbaCredentialsSupplier credentialsSupplier) {
@@ -203,6 +210,9 @@
             }
             if (fromAuth != null) mCallback.onDownloadSuccessful(fromAuth);
             mCallback.onDownloadSuccessful(fromImmediate);
+        }).exceptionally((ex) -> {
+            logException("Exception downloading image" , ex);
+            return null;
         });
     }
 
@@ -223,7 +233,7 @@
         return resultFuture;
     }
 
-    private static HttpURLConnection prepareInitialPost(Network network, String uploadUrl) {
+    private HttpURLConnection prepareInitialPost(Network network, String uploadUrl) {
         try {
             HttpURLConnection connection =
                     (HttpURLConnection) network.openConnection(new URL(uploadUrl));
@@ -231,7 +241,7 @@
             connection.setInstanceFollowRedirects(false);
             connection.setConnectTimeout(HTTP_TIMEOUT_MILLIS);
             connection.setReadTimeout(HTTP_TIMEOUT_MILLIS);
-            connection.setRequestProperty("User-Agent", THREE_GPP_GBA);
+            connection.setRequestProperty("User-Agent", getUserAgent());
             return connection;
         } catch (MalformedURLException e) {
             Log.e(TAG, "Malformed URL: " + uploadUrl);
@@ -242,14 +252,14 @@
         }
     }
 
-    private static HttpURLConnection prepareImageDownloadRequest(Network network, String imageUrl) {
+    private HttpURLConnection prepareImageDownloadRequest(Network network, String imageUrl) {
         try {
             HttpURLConnection connection =
                     (HttpURLConnection) network.openConnection(new URL(imageUrl));
             connection.setRequestMethod("GET");
             connection.setConnectTimeout(HTTP_TIMEOUT_MILLIS);
             connection.setReadTimeout(HTTP_TIMEOUT_MILLIS);
-            connection.setRequestProperty("User-Agent", THREE_GPP_GBA);
+            connection.setRequestProperty("User-Agent", getUserAgent());
             return connection;
         } catch (MalformedURLException e) {
             Log.e(TAG, "Malformed URL: " + imageUrl);
@@ -387,7 +397,7 @@
             public void sendDispositionHeader(OutputStream out) throws IOException {
                 super.sendDispositionHeader(out);
                 if (filename != null) {
-                    String fileNameSuffix = ";filename=\"" + filename + "\"";
+                    String fileNameSuffix = "; filename=\"" + filename + "\"";
                     out.write(fileNameSuffix.getBytes());
                 }
             }
@@ -416,6 +426,11 @@
         HttpURLConnection connection = prepareInitialPost(network, mUrl);
         connection.setDoOutput(true);
         connection.addRequestProperty("Authorization", authHeader);
+        connection.addRequestProperty("Content-Length",
+                String.valueOf(multipartEntity.getContentLength()));
+        connection.addRequestProperty("Content-Type", multipartEntity.getContentType().getValue());
+        connection.addRequestProperty("Accept-Encoding", "*");
+
         try (OutputStream requestBodyOut = connection.getOutputStream()) {
             multipartEntity.writeTo(requestBodyOut);
         } catch (IOException e) {
@@ -425,6 +440,8 @@
 
         try {
             int response = connection.getResponseCode();
+            Log.i(TAG, "Received response code: " + response
+                    + ", message=" + connection.getResponseMessage());
             if (response == 401 || response == 403) {
                 deliverFailure(TelephonyManager.CallComposerException.ERROR_AUTHENTICATION_FAILED);
                 return null;
@@ -493,6 +510,22 @@
         return sb.toString();
     }
 
+    private String getUserAgent() {
+        String carrierName = mContext.getSystemService(TelephonyManager.class)
+                .createForSubscriptionId(mSubscriptionId)
+                .getSimOperatorName();
+        String buildId = Build.ID;
+        String buildDate = DateTimeFormatter.ofPattern("yyyy-MM-dd")
+                .withZone(ZoneId.systemDefault())
+                .format(Instant.ofEpochMilli(Build.TIME));
+        String buildVersion = Build.VERSION.RELEASE_OR_CODENAME;
+        String deviceName = Build.DEVICE;
+        return String.format("%s %s %s %s %s %s %s",
+                carrierName, buildId, buildDate, "Android", buildVersion,
+                deviceName, THREE_GPP_GBA);
+
+    }
+
     private static void logException(String message, Throwable e) {
         StringWriter log = new StringWriter();
         log.append(message);
diff --git a/src/com/android/phone/callcomposer/DigestAuthUtils.java b/src/com/android/phone/callcomposer/DigestAuthUtils.java
index 52a278b..2f081f7 100644
--- a/src/com/android/phone/callcomposer/DigestAuthUtils.java
+++ b/src/com/android/phone/callcomposer/DigestAuthUtils.java
@@ -56,9 +56,13 @@
         if (!TextUtils.isEmpty(parsedHeader.getAlgorithm())
                 && !MD5_ALGORITHM.equals(parsedHeader.getAlgorithm().toLowerCase())) {
             Log.e(TAG, "This client only supports MD5 auth");
+            return "";
         }
-
-        Log.i(TAG, "nonce=" + parsedHeader.getNonce());
+        if (!TextUtils.isEmpty(parsedHeader.getQop())
+                && !AUTH_QOP.equals(parsedHeader.getQop().toLowerCase())) {
+            Log.e(TAG, "This client only supports the auth qop");
+            return "";
+        }
 
         String clientNonce = makeClientNonce();
 
@@ -71,7 +75,9 @@
             replyHeader.setScheme(parsedHeader.getScheme());
             replyHeader.setUsername(credentials.getTransactionId());
             replyHeader.setURI(new WorkaroundURI(uri));
+            replyHeader.setRealm(parsedHeader.getRealm());
             replyHeader.setQop(AUTH_QOP);
+            replyHeader.setNonce(parsedHeader.getNonce());
             replyHeader.setCNonce(clientNonce);
             replyHeader.setNonceCount(1);
             replyHeader.setResponse(response);
@@ -83,7 +89,7 @@
             return null;
         }
 
-        return replyHeader.encode();
+        return replyHeader.encodeBody();
     }
 
     public static String computeResponse(String serverNonce, String clientNonce, String qop,
diff --git a/src/com/android/phone/settings/AccessibilitySettingsFragment.java b/src/com/android/phone/settings/AccessibilitySettingsFragment.java
index ad3f133..475d878 100644
--- a/src/com/android/phone/settings/AccessibilitySettingsFragment.java
+++ b/src/com/android/phone/settings/AccessibilitySettingsFragment.java
@@ -19,6 +19,9 @@
 import android.content.Context;
 import android.media.AudioManager;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
 import android.os.PersistableBundle;
 import android.preference.Preference;
 import android.preference.PreferenceFragment;
@@ -27,8 +30,8 @@
 import android.provider.Settings;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.CarrierConfigManager;
-import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -56,15 +59,12 @@
 
     private static final int WFC_QUERY_TIMEOUT_MILLIS = 20;
 
-    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
-        /**
-         * Disable the TTY setting when in/out of a call (and if carrier doesn't
-         * support VoLTE with TTY).
-         * @see android.telephony.PhoneStateListener#onCallStateChanged(int,
-         * java.lang.String)
-         */
+    private final TelephonyCallback mTelephonyCallback = new AccessibilityTelephonyCallback();
+
+    private final class AccessibilityTelephonyCallback extends TelephonyCallback implements
+            TelephonyCallback.CallStateListener {
         @Override
-        public void onCallStateChanged(int state, String incomingNumber) {
+        public void onCallStateChanged(int state) {
             if (DBG) Log.d(LOG_TAG, "PhoneStateListener.onCallStateChanged: state=" + state);
             Preference pref = getPreferenceScreen().findPreference(BUTTON_TTY_KEY);
             if (pref != null) {
@@ -78,7 +78,7 @@
                         || (telephonyManager.getCallState() == TelephonyManager.CALL_STATE_IDLE));
             }
         }
-    };
+    }
 
     private Context mContext;
     private AudioManager mAudioManager;
@@ -148,7 +148,8 @@
         super.onResume();
         TelephonyManager tm =
                 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
-        tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+        tm.registerTelephonyCallback(new HandlerExecutor(new Handler(Looper.getMainLooper())),
+                mTelephonyCallback);
     }
 
     @Override
@@ -156,7 +157,7 @@
         super.onPause();
         TelephonyManager tm =
                 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
-        tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+        tm.unregisterTelephonyCallback(mTelephonyCallback);
     }
 
     @Override
diff --git a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
index 3811a77..224a1f9 100644
--- a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
+++ b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
@@ -6,14 +6,11 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Icon;
-import android.net.sip.SipManager;
 import android.os.Bundle;
 import android.os.UserManager;
-import android.preference.ListPreference;
 import android.preference.Preference;
 import android.preference.PreferenceCategory;
 import android.preference.PreferenceFragment;
-import android.preference.SwitchPreference;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
@@ -28,9 +25,6 @@
 import com.android.phone.PhoneUtils;
 import com.android.phone.R;
 import com.android.phone.SubscriptionInfoHelper;
-import com.android.services.telephony.sip.SipAccountRegistry;
-import com.android.services.telephony.sip.SipPreferences;
-import com.android.services.telephony.sip.SipUtil;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -48,11 +42,6 @@
 
     private static final String ALL_CALLING_ACCOUNTS_KEY = "phone_accounts_all_calling_accounts";
 
-    private static final String SIP_SETTINGS_CATEGORY_PREF_KEY =
-            "phone_accounts_sip_settings_category_key";
-    private static final String USE_SIP_PREF_KEY = "use_sip_calling_options_key";
-    private static final String SIP_RECEIVE_CALLS_PREF_KEY = "sip_receive_calls_key";
-
     private static final String MAKE_AND_RECEIVE_CALLS_CATEGORY_KEY =
             "make_and_receive_calls_settings_category_key";
     private static final String DEFAULT_OUTGOING_ACCOUNT_KEY = "default_outgoing_account";
@@ -84,10 +73,6 @@
     private PreferenceCategory mMakeAndReceiveCallsCategory;
     private boolean mMakeAndReceiveCallsCategoryPresent;
 
-    private ListPreference mUseSipCalling;
-    private SwitchPreference mSipReceiveCallsPreference;
-    private SipPreferences mSipPreferences;
-
     private final SubscriptionManager.OnSubscriptionsChangedListener
             mOnSubscriptionsChangeListener =
             new SubscriptionManager.OnSubscriptionsChangedListener() {
@@ -154,39 +139,6 @@
         updateAccounts();
         updateMakeCallsOptions();
 
-        if (isPrimaryUser() && SipUtil.isVoipSupported(getActivity())) {
-            mSipPreferences = new SipPreferences(getActivity());
-
-            mUseSipCalling = (ListPreference)
-                    getPreferenceScreen().findPreference(USE_SIP_PREF_KEY);
-            mUseSipCalling.setEntries(!SipManager.isSipWifiOnly(getActivity())
-                    ? R.array.sip_call_options_wifi_only_entries
-                    : R.array.sip_call_options_entries);
-            mUseSipCalling.setOnPreferenceChangeListener(this);
-
-            int optionsValueIndex =
-                    mUseSipCalling.findIndexOfValue(mSipPreferences.getSipCallOption());
-            if (optionsValueIndex == -1) {
-                // If the option is invalid (eg. deprecated value), default to SIP_ADDRESS_ONLY.
-                mSipPreferences.setSipCallOption(
-                        getResources().getString(R.string.sip_address_only));
-                optionsValueIndex =
-                        mUseSipCalling.findIndexOfValue(mSipPreferences.getSipCallOption());
-            }
-            mUseSipCalling.setValueIndex(optionsValueIndex);
-            mUseSipCalling.setSummary(mUseSipCalling.getEntry());
-
-            mSipReceiveCallsPreference = (SwitchPreference)
-                    getPreferenceScreen().findPreference(SIP_RECEIVE_CALLS_PREF_KEY);
-            mSipReceiveCallsPreference.setEnabled(SipUtil.isPhoneIdle(getActivity()));
-            mSipReceiveCallsPreference.setChecked(
-                    mSipPreferences.isReceivingCallsEnabled());
-            mSipReceiveCallsPreference.setOnPreferenceChangeListener(this);
-        } else {
-            getPreferenceScreen().removePreference(
-                    getPreferenceScreen().findPreference(SIP_SETTINGS_CATEGORY_PREF_KEY));
-        }
-
         SubscriptionManager.from(getActivity()).addOnSubscriptionsChangedListener(
                 mOnSubscriptionsChangeListener);
     }
@@ -207,21 +159,6 @@
      */
     @Override
     public boolean onPreferenceChange(Preference pref, Object objValue) {
-        if (pref == mUseSipCalling) {
-            String option = objValue.toString();
-            mSipPreferences.setSipCallOption(option);
-            mUseSipCalling.setValueIndex(mUseSipCalling.findIndexOfValue(option));
-            mUseSipCalling.setSummary(mUseSipCalling.getEntry());
-            return true;
-        } else if (pref == mSipReceiveCallsPreference) {
-            final boolean isEnabled = !mSipReceiveCallsPreference.isChecked();
-            new Thread(new Runnable() {
-                public void run() {
-                    handleSipReceiveCallsOption(isEnabled);
-                }
-            }).start();
-            return true;
-        }
         return false;
     }
 
@@ -256,22 +193,6 @@
     @Override
     public void onAccountChanged(AccountSelectionPreference pref) {}
 
-    private synchronized void handleSipReceiveCallsOption(boolean isEnabled) {
-        Context context = getActivity();
-        if (context == null) {
-            // Return if the fragment is detached from parent activity before executed by thread.
-            return;
-        }
-
-        mSipPreferences.setReceivingCallsEnabled(isEnabled);
-
-        SipUtil.useSipToReceiveIncomingCalls(context, isEnabled);
-
-        // Restart all Sip services to ensure we reflect whether we are receiving calls.
-        SipAccountRegistry sipAccountRegistry = SipAccountRegistry.getInstance();
-        sipAccountRegistry.restartSipService(context);
-    }
-
     /**
      * Queries the telcomm manager to update the default outgoing account selection preference
      * with the list of outgoing accounts and the current default outgoing account.
@@ -409,32 +330,24 @@
             mAccountList.removeAll();
             List<PhoneAccountHandle> allNonSimAccounts =
                     getCallingAccounts(false /* includeSims */, true /* includeDisabled */);
-            // Check to see if we should show the entire section at all.
-            if (shouldShowConnectionServiceList(allNonSimAccounts)) {
-                List<PhoneAccountHandle> enabledAccounts =
-                        getCallingAccounts(true /* includeSims */, false /* includeDisabled */);
-                // Initialize the account list with the set of enabled & SIM accounts.
-                initAccountList(enabledAccounts);
 
-                // Only show the 'Make Calls With..." option if there are multiple accounts.
-                if (enabledAccounts.size() > 1) {
-                    mMakeAndReceiveCallsCategory.addPreference(mDefaultOutgoingAccount);
-                    mMakeAndReceiveCallsCategoryPresent = true;
-                    mDefaultOutgoingAccount.setListener(this);
-                    updateDefaultOutgoingAccountsModel();
-                } else {
-                    mMakeAndReceiveCallsCategory.removePreference(mDefaultOutgoingAccount);
-                }
+            List<PhoneAccountHandle> enabledAccounts =
+                    getCallingAccounts(true /* includeSims */, false /* includeDisabled */);
+            // Initialize the account list with the set of enabled & SIM accounts.
+            initAccountList(enabledAccounts);
 
-                // If there are no third party (nonSim) accounts,
-                // then don't show enable/disable dialog.
-                if (!allNonSimAccounts.isEmpty()) {
-                    mAccountList.addPreference(mAllCallingAccounts);
-                } else {
-                    mAccountList.removePreference(mAllCallingAccounts);
-                }
+            // Always show the 'Make Calls With..." option
+            mMakeAndReceiveCallsCategory.addPreference(mDefaultOutgoingAccount);
+            mMakeAndReceiveCallsCategoryPresent = true;
+            mDefaultOutgoingAccount.setListener(this);
+            updateDefaultOutgoingAccountsModel();
+
+            // If there are no third party (nonSim) accounts,
+            // then don't show enable/disable dialog.
+            if (!allNonSimAccounts.isEmpty()) {
+                mAccountList.addPreference(mAllCallingAccounts);
             } else {
-                getPreferenceScreen().removePreference(mAccountList);
+                mAccountList.removePreference(mAllCallingAccounts);
             }
         }
     }
diff --git a/src/com/android/phone/settings/RadioInfo.java b/src/com/android/phone/settings/RadioInfo.java
index 1e17668..8c53971 100644
--- a/src/com/android/phone/settings/RadioInfo.java
+++ b/src/com/android/phone/settings/RadioInfo.java
@@ -36,6 +36,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.SystemProperties;
@@ -55,14 +56,14 @@
 import android.telephony.CellSignalStrengthLte;
 import android.telephony.CellSignalStrengthWcdma;
 import android.telephony.DataSpecificRegistrationInfo;
+import android.telephony.data.NetworkSlicingConfig;
 import android.telephony.NetworkRegistrationInfo;
-import android.telephony.PhoneStateListener;
 import android.telephony.PhysicalChannelConfig;
-import android.telephony.PreciseCallState;
 import android.telephony.RadioAccessFamily;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -210,6 +211,11 @@
     private static final int MENU_ITEM_GET_IMS_STATUS      = 4;
     private static final int MENU_ITEM_TOGGLE_DATA         = 5;
 
+    private static final String CARRIER_PROVISIONING_ACTION =
+            "com.android.phone.settings.CARRIER_PROVISIONING";
+    private static final String TRIGGER_CARRIER_PROVISIONING_ACTION =
+            "com.android.phone.settings.TRIGGER_CARRIER_PROVISIONING";
+
     private TextView mDeviceId; //DeviceId is the IMEI in GSM and the MEID in CDMA
     private TextView mLine1Number;
     private TextView mSubscriptionId;
@@ -240,6 +246,7 @@
     private TextView mNrAvailable;
     private TextView mNrState;
     private TextView mNrFrequency;
+    private TextView mNetworkSlicingConfig;
     private EditText mSmsc;
     private Switch mRadioPowerOnSwitch;
     private Button mCellInfoRefreshRateButton;
@@ -295,10 +302,19 @@
     };
 
     // not final because we need to recreate this object to register on a new subId (b/117555407)
-    private PhoneStateListener mPhoneStateListener = new RadioInfoPhoneStateListener();
-    private class RadioInfoPhoneStateListener extends PhoneStateListener {
+    private TelephonyCallback mTelephonyCallback = new RadioInfoTelephonyCallback();
+    private class RadioInfoTelephonyCallback extends TelephonyCallback implements
+            TelephonyCallback.DataConnectionStateListener,
+            TelephonyCallback.DataActivityListener,
+            TelephonyCallback.CallStateListener,
+            TelephonyCallback.MessageWaitingIndicatorListener,
+            TelephonyCallback.CallForwardingIndicatorListener,
+            TelephonyCallback.CellInfoListener,
+            TelephonyCallback.SignalStrengthsListener,
+            TelephonyCallback.ServiceStateListener {
+
         @Override
-        public void onDataConnectionStateChanged(int state) {
+        public void onDataConnectionStateChanged(int state, int networkType) {
             updateDataState();
             updateNetworkType();
         }
@@ -309,17 +325,12 @@
         }
 
         @Override
-        public void onCallStateChanged(int state, String incomingNumber) {
+        public void onCallStateChanged(int state) {
             updateNetworkType();
             updatePhoneState(state);
         }
 
         @Override
-        public void onPreciseCallStateChanged(PreciseCallState preciseState) {
-            updateNetworkType();
-        }
-
-        @Override
         public void onMessageWaitingIndicatorChanged(boolean mwi) {
             mMwiValue = mwi;
             updateMessageWaiting();
@@ -485,6 +496,7 @@
         mNrState = (TextView) findViewById(R.id.nr_state);
         mNrFrequency = (TextView) findViewById(R.id.nr_frequency);
         mPhyChanConfig = (TextView) findViewById(R.id.phy_chan_config);
+        mNetworkSlicingConfig = (TextView) findViewById(R.id.network_slicing_config);
 
         // hide 5G stats on devices that don't support 5G
         if ((mTelephonyManager.getSupportedRadioAccessFamily()
@@ -499,6 +511,8 @@
             mNrState.setVisibility(View.GONE);
             ((TextView) findViewById(R.id.nr_frequency_label)).setVisibility(View.GONE);
             mNrFrequency.setVisibility(View.GONE);
+            ((TextView) findViewById(R.id.network_slicing_config_label)).setVisibility(View.GONE);
+            mNetworkSlicingConfig.setVisibility(View.GONE);
         }
 
         mPreferredNetworkType = (Spinner) findViewById(R.id.preferredNetworkType);
@@ -567,11 +581,20 @@
         mDnsCheckToggleButton = (Button) findViewById(R.id.dns_check_toggle);
         mDnsCheckToggleButton.setOnClickListener(mDnsCheckButtonHandler);
         mCarrierProvisioningButton = (Button) findViewById(R.id.carrier_provisioning);
-        mCarrierProvisioningButton.setOnClickListener(mCarrierProvisioningButtonHandler);
+        if (!TextUtils.isEmpty(getCarrierProvisioningAppString())) {
+            mCarrierProvisioningButton.setOnClickListener(mCarrierProvisioningButtonHandler);
+        } else {
+            mCarrierProvisioningButton.setEnabled(false);
+        }
+
         mTriggerCarrierProvisioningButton = (Button) findViewById(
                 R.id.trigger_carrier_provisioning);
-        mTriggerCarrierProvisioningButton.setOnClickListener(
-                mTriggerCarrierProvisioningButtonHandler);
+        if (!TextUtils.isEmpty(getCarrierProvisioningAppString())) {
+            mTriggerCarrierProvisioningButton.setOnClickListener(
+                    mTriggerCarrierProvisioningButtonHandler);
+        } else {
+            mTriggerCarrierProvisioningButton.setEnabled(false);
+        }
 
         mOemInfoButton = (Button) findViewById(R.id.oem_info);
         mOemInfoButton.setOnClickListener(mOemInfoButtonHandler);
@@ -675,7 +698,7 @@
 
         log("onPause: unregister phone & data intents");
 
-        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+        mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback);
         mTelephonyManager.setCellInfoListRate(sCellInfoListRateDisabled);
         mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
 
@@ -776,7 +799,7 @@
     }
 
     private void unregisterPhoneStateListener() {
-        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+        mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback);
         mPhone.unregisterForPhysicalChannelConfig(mHandler);
 
         // clear all fields so they are blank until the next listener event occurs
@@ -798,21 +821,11 @@
         mPhyChanConfig.setText("");
     }
 
-    // register mPhoneStateListener for relevant fields using the current TelephonyManager
+    // register mTelephonyCallback for relevant fields using the current TelephonyManager
     private void registerPhoneStateListener() {
-        mPhoneStateListener = new RadioInfoPhoneStateListener();
-        mTelephonyManager.listen(mPhoneStateListener,
-                  PhoneStateListener.LISTEN_CALL_STATE
-        //b/27803938 - RadioInfo currently cannot read PRECISE_CALL_STATE
-        //      | PhoneStateListener.LISTEN_PRECISE_CALL_STATE
-                | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
-                | PhoneStateListener.LISTEN_DATA_ACTIVITY
-                | PhoneStateListener.LISTEN_CELL_LOCATION
-                | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
-                | PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
-                | PhoneStateListener.LISTEN_CELL_INFO
-                | PhoneStateListener.LISTEN_SERVICE_STATE
-                | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
+        mTelephonyCallback = new RadioInfoTelephonyCallback();
+        mTelephonyManager.registerTelephonyCallback(new HandlerExecutor(mHandler),
+                mTelephonyCallback);
     }
 
     private void updateDnsCheckState() {
@@ -1117,6 +1130,10 @@
             mNrState.setText(NetworkRegistrationInfo.nrStateToString(ss.getNrState()));
             mNrFrequency.setText(ServiceState.frequencyRangeToString(ss.getNrFrequencyRange()));
         }
+
+        NetworkSlicingConfig slicingConfig = new NetworkSlicingConfig();
+        mNetworkSlicingConfig.setText(slicingConfig.toString());
+
     }
 
     private void updateProperties() {
@@ -1323,7 +1340,7 @@
             new MenuItem.OnMenuItemClickListener() {
         public boolean onMenuItemClick(MenuItem item) {
             boolean isImsRegistered = mPhone.isImsRegistered();
-            boolean availableVolte = mPhone.isVolteEnabled();
+            boolean availableVolte = mPhone.isVoiceOverCellularImsEnabled();
             boolean availableWfc = mPhone.isWifiCallingEnabled();
             boolean availableVt = mPhone.isVideoEnabled();
             boolean availableUt = mPhone.isUtEnabled();
@@ -1616,21 +1633,23 @@
         }
     };
 
-    OnClickListener mCarrierProvisioningButtonHandler = new OnClickListener() {
-        public void onClick(View v) {
-            final Intent intent = new Intent("com.android.settings.CARRIER_PROVISIONING");
-            final ComponentName serviceComponent = ComponentName.unflattenFromString(
-                    "com.android.omadm.service/.DMIntentReceiver");
+    OnClickListener mCarrierProvisioningButtonHandler = v -> {
+        String carrierProvisioningApp = getCarrierProvisioningAppString();
+        if (!TextUtils.isEmpty(carrierProvisioningApp)) {
+            final Intent intent = new Intent(CARRIER_PROVISIONING_ACTION);
+            final ComponentName serviceComponent =
+                    ComponentName.unflattenFromString(carrierProvisioningApp);
             intent.setComponent(serviceComponent);
             sendBroadcast(intent);
         }
     };
 
-    OnClickListener mTriggerCarrierProvisioningButtonHandler = new OnClickListener() {
-        public void onClick(View v) {
-            final Intent intent = new Intent("com.android.settings.TRIGGER_CARRIER_PROVISIONING");
-            final ComponentName serviceComponent = ComponentName.unflattenFromString(
-                    "com.android.omadm.service/.DMIntentReceiver");
+    OnClickListener mTriggerCarrierProvisioningButtonHandler = v -> {
+        String carrierProvisioningApp = getCarrierProvisioningAppString();
+        if (!TextUtils.isEmpty(carrierProvisioningApp)) {
+            final Intent intent = new Intent(TRIGGER_CARRIER_PROVISIONING_ACTION);
+            final ComponentName serviceComponent =
+                    ComponentName.unflattenFromString(carrierProvisioningApp);
             intent.setComponent(serviceComponent);
             sendBroadcast(intent);
         }
@@ -1695,6 +1714,19 @@
         }
     };
 
+    private String getCarrierProvisioningAppString() {
+        if (mPhone != null) {
+            CarrierConfigManager configManager =
+                    mPhone.getContext().getSystemService(CarrierConfigManager.class);
+            PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId());
+            if (b != null) {
+                return b.getString(
+                        CarrierConfigManager.KEY_CARRIER_PROVISIONING_APP_STRING, "");
+            }
+        }
+        return "";
+    }
+
     boolean isCbrsSupported() {
         return getResources().getBoolean(
               com.android.internal.R.bool.config_cbrs_supported);
diff --git a/src/com/android/phone/settings/fdn/BaseFdnContactScreen.java b/src/com/android/phone/settings/fdn/BaseFdnContactScreen.java
new file mode 100644
index 0000000..5beff34
--- /dev/null
+++ b/src/com/android/phone/settings/fdn/BaseFdnContactScreen.java
@@ -0,0 +1,210 @@
+/*
+ * 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.phone.settings.fdn;
+
+import static android.view.Window.PROGRESS_VISIBILITY_OFF;
+import static android.view.Window.PROGRESS_VISIBILITY_ON;
+
+import android.app.Activity;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.content.AsyncQueryHandler;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.Window;
+import android.widget.Toast;
+
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.Phone;
+import com.android.phone.PhoneGlobals;
+import com.android.phone.R;
+import com.android.phone.SubscriptionInfoHelper;
+
+/**
+ * Base activity for FDN contact screen.
+ */
+public abstract class BaseFdnContactScreen extends Activity
+        implements Pin2LockedDialogFragment.Listener {
+    protected static final String LOG_TAG = PhoneGlobals.LOG_TAG;
+    protected static final boolean DBG = false;
+
+    protected static final int EVENT_PIN2_ENTRY_COMPLETE = 10;
+    protected static final int PIN2_REQUEST_CODE = 100;
+
+    protected static final String INTENT_EXTRA_NAME = "name";
+    protected static final String INTENT_EXTRA_NUMBER = "number";
+
+    protected String mName;
+    protected String mNumber;
+    protected String mPin2;
+
+    protected SubscriptionInfoHelper mSubscriptionInfoHelper;
+    protected BaseFdnContactScreen.QueryHandler mQueryHandler;
+
+    protected Handler mHandler = new Handler();
+    protected Phone mPhone;
+
+    protected abstract void pin2AuthenticationSucceed();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        resolveIntent();
+        getWindow().requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+    }
+
+    protected void authenticatePin2() {
+        Intent intent = new Intent();
+        intent.setClass(this, GetPin2Screen.class);
+        intent.setData(FdnList.getContentUri(mSubscriptionInfoHelper));
+        startActivityForResult(intent, PIN2_REQUEST_CODE);
+    }
+
+    protected void displayProgress(boolean flag) {
+        getWindow().setFeatureInt(
+                Window.FEATURE_INDETERMINATE_PROGRESS,
+                flag ? PROGRESS_VISIBILITY_ON : PROGRESS_VISIBILITY_OFF);
+    }
+
+    protected void handleResult(boolean success) {
+    }
+
+    protected void handleResult(boolean success, boolean invalidNumber) {
+    }
+
+    protected void log(String msg) {
+        Log.d(LOG_TAG, getClass().getSimpleName() + " : " + msg);
+    }
+
+    // Add method to check if Pin2 supplied is correct.
+    protected void processPin2(String pin) {
+        Message onComplete = mFDNHandler
+                .obtainMessage(EVENT_PIN2_ENTRY_COMPLETE);
+        mPhone.getIccCard().supplyPin2(pin, onComplete);
+    }
+
+    protected void resolveIntent() {
+        Intent intent = getIntent();
+
+        mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, intent);
+        mPhone = mSubscriptionInfoHelper.getPhone();
+
+        mName =  intent.getStringExtra(INTENT_EXTRA_NAME);
+        mNumber =  intent.getStringExtra(INTENT_EXTRA_NUMBER);
+    }
+
+    /**
+     * Removed the status field, with preference to displaying a toast
+     * to match the rest of settings UI.
+     */
+    protected void showStatus(CharSequence statusMsg) {
+        if (statusMsg != null) {
+            Toast.makeText(this, statusMsg, Toast.LENGTH_LONG).show();
+        }
+    }
+
+    private Handler mFDNHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case EVENT_PIN2_ENTRY_COMPLETE:
+                    AsyncResult ar = (AsyncResult) msg.obj;
+                    if (ar.exception != null) {
+                        // see if PUK2 is requested and alert the user accordingly.
+                        CommandException ce = (CommandException) ar.exception;
+                        if (ce.getCommandError() == CommandException.Error.SIM_PUK2) {
+                            // make sure we set the PUK2 state so that we can skip some
+                            // redundant behaviour.
+                            showPin2LockedDialog();
+                        } else {
+                            final int attemptsRemaining = msg.arg1;
+                            if (attemptsRemaining > 0) {
+                                Toast.makeText(
+                                        BaseFdnContactScreen.this,
+                                        getString(R.string.pin2_invalid)
+                                                + getString(R.string.pin2_attempts,
+                                                        attemptsRemaining), Toast.LENGTH_LONG)
+                                        .show();
+                                finish();
+                            }
+                        }
+                    } else {
+                        pin2AuthenticationSucceed();
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+
+    protected class QueryHandler extends AsyncQueryHandler {
+        protected QueryHandler(ContentResolver cr) {
+            super(cr);
+        }
+
+        @Override
+        protected void onInsertComplete(int token, Object cookie, Uri uri) {
+            if (DBG) log("onInsertComplete");
+            displayProgress(false);
+            handleResult(uri != null, false);
+        }
+
+        @Override
+        protected void onUpdateComplete(int token, Object cookie, int result) {
+            if (DBG) log("onUpdateComplete");
+            displayProgress(false);
+            handleResult(result > 0, false);
+        }
+
+        @Override
+        protected void onDeleteComplete(int token, Object cookie, int result) {
+            if (DBG) log("onDeleteComplete");
+            displayProgress(false);
+            handleResult(result > 0);
+        }
+    }
+
+    private void showPin2LockedDialog() {
+        final FragmentManager fragmentManager = getFragmentManager();
+        Pin2LockedDialogFragment dialogFragment = (Pin2LockedDialogFragment) fragmentManager
+                .findFragmentByTag(Pin2LockedDialogFragment.TAG_PIN2_LOCKED_DIALOG);
+        if (dialogFragment == null) {
+            dialogFragment = new Pin2LockedDialogFragment();
+            Bundle args = new Bundle();
+            args.putInt(Pin2LockedDialogFragment.KEY_DIALOG_ID,
+                    Pin2LockedDialogFragment.DIALOG_ID_PUK2_REQUESTED_ON_PIN_ENTRY);
+            dialogFragment.setArguments(args);
+            dialogFragment.show(fragmentManager, Pin2LockedDialogFragment.TAG_PIN2_LOCKED_DIALOG);
+        } else {
+            FragmentTransaction transaction = fragmentManager.beginTransaction();
+            transaction.show(dialogFragment);
+            transaction.commitNow();
+        }
+    }
+
+    @Override
+    public void onRequestPuk2(int id) {
+        finish();
+    }
+}
diff --git a/src/com/android/phone/settings/fdn/DeleteFdnContactScreen.java b/src/com/android/phone/settings/fdn/DeleteFdnContactScreen.java
index 8b17cfb..7cd4c93 100644
--- a/src/com/android/phone/settings/fdn/DeleteFdnContactScreen.java
+++ b/src/com/android/phone/settings/fdn/DeleteFdnContactScreen.java
@@ -16,58 +16,24 @@
 
 package com.android.phone.settings.fdn;
 
-import static android.view.Window.PROGRESS_VISIBILITY_OFF;
-import static android.view.Window.PROGRESS_VISIBILITY_ON;
-
-import android.app.Activity;
-import android.content.AsyncQueryHandler;
-import android.content.ContentResolver;
 import android.content.Intent;
-import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
 import android.text.TextUtils;
-import android.util.Log;
-import android.view.Window;
-import android.widget.Toast;
 
-import com.android.phone.PhoneGlobals;
 import com.android.phone.R;
-import com.android.phone.SubscriptionInfoHelper;
 
 /**
  * Activity to let the user delete an FDN contact.
  */
-public class DeleteFdnContactScreen extends Activity {
-    private static final String LOG_TAG = PhoneGlobals.LOG_TAG;
-    private static final boolean DBG = false;
-
-    private static final String INTENT_EXTRA_NAME = "name";
-    private static final String INTENT_EXTRA_NUMBER = "number";
-
-    private static final int PIN2_REQUEST_CODE = 100;
-
-    private SubscriptionInfoHelper mSubscriptionInfoHelper;
-
-    private String mName;
-    private String mNumber;
-    private String mPin2;
-
-    protected QueryHandler mQueryHandler;
-
-    private Handler mHandler = new Handler();
+public class DeleteFdnContactScreen extends BaseFdnContactScreen {
 
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        resolveIntent();
-
         // Starts PIN2 authentication only for the first time.
         if (icicle == null) authenticatePin2();
-
-        getWindow().requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
         setContentView(R.layout.delete_fdn_contact_screen);
     }
 
@@ -80,9 +46,7 @@
                 Bundle extras = (intent != null) ? intent.getExtras() : null;
                 if (extras != null) {
                     mPin2 = extras.getString("pin2");
-                    showStatus(getResources().getText(
-                            R.string.deleting_fdn_contact));
-                    deleteContact();
+                    processPin2(mPin2);
                 } else {
                     // if they cancelled, then we just cancel too.
                     if (DBG) log("onActivityResult: CANCELLED");
@@ -93,13 +57,9 @@
         }
     }
 
-    private void resolveIntent() {
-        Intent intent = getIntent();
-
-        mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, intent);
-
-        mName =  intent.getStringExtra(INTENT_EXTRA_NAME);
-        mNumber =  intent.getStringExtra(INTENT_EXTRA_NUMBER);
+    @Override
+    protected void resolveIntent() {
+        super.resolveIntent();
 
         if (TextUtils.isEmpty(mNumber)) {
             finish();
@@ -127,29 +87,8 @@
         displayProgress(true);
     }
 
-    private void authenticatePin2() {
-        Intent intent = new Intent();
-        intent.setClass(this, GetPin2Screen.class);
-        intent.setData(FdnList.getContentUri(mSubscriptionInfoHelper));
-        startActivityForResult(intent, PIN2_REQUEST_CODE);
-    }
-
-    private void displayProgress(boolean flag) {
-        getWindow().setFeatureInt(
-                Window.FEATURE_INDETERMINATE_PROGRESS,
-                flag ? PROGRESS_VISIBILITY_ON : PROGRESS_VISIBILITY_OFF);
-    }
-
-    // Replace the status field with a toast to make things appear similar
-    // to the rest of the settings.  Removed the useless status field.
-    private void showStatus(CharSequence statusMsg) {
-        if (statusMsg != null) {
-            Toast.makeText(this, statusMsg, Toast.LENGTH_SHORT)
-            .show();
-        }
-    }
-
-    private void handleResult(boolean success) {
+    @Override
+    protected void handleResult(boolean success) {
         if (success) {
             if (DBG) log("handleResult: success!");
             showStatus(getResources().getText(R.string.fdn_contact_deleted));
@@ -157,43 +96,12 @@
             if (DBG) log("handleResult: failed!");
             showStatus(getResources().getText(R.string.pin2_invalid));
         }
-
-        mHandler.postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                finish();
-            }
-        }, 2000);
-
+        mHandler.postDelayed(() -> finish(), 2000);
     }
 
-    private class QueryHandler extends AsyncQueryHandler {
-        public QueryHandler(ContentResolver cr) {
-            super(cr);
-        }
-
-        @Override
-        protected void onQueryComplete(int token, Object cookie, Cursor c) {
-        }
-
-        @Override
-        protected void onInsertComplete(int token, Object cookie, Uri uri) {
-        }
-
-        @Override
-        protected void onUpdateComplete(int token, Object cookie, int result) {
-        }
-
-        @Override
-        protected void onDeleteComplete(int token, Object cookie, int result) {
-            if (DBG) log("onDeleteComplete");
-            displayProgress(false);
-            handleResult(result > 0);
-        }
-
-    }
-
-    private void log(String msg) {
-        Log.d(LOG_TAG, "[DeleteFdnContact] " + msg);
+    @Override
+    protected void pin2AuthenticationSucceed() {
+        showStatus(getResources().getText(R.string.deleting_fdn_contact));
+        deleteContact();
     }
 }
diff --git a/src/com/android/phone/settings/fdn/EditFdnContactScreen.java b/src/com/android/phone/settings/fdn/EditFdnContactScreen.java
index 140cc74..468d38f 100644
--- a/src/com/android/phone/settings/fdn/EditFdnContactScreen.java
+++ b/src/com/android/phone/settings/fdn/EditFdnContactScreen.java
@@ -16,21 +16,18 @@
 
 package com.android.phone.settings.fdn;
 
-import static android.view.Window.PROGRESS_VISIBILITY_OFF;
-import static android.view.Window.PROGRESS_VISIBILITY_ON;
 
-import android.app.Activity;
-import android.content.AsyncQueryHandler;
-import android.content.ContentResolver;
+import static android.app.Activity.RESULT_OK;
+
 import android.content.ContentValues;
 import android.content.Intent;
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.PersistableBundle;
 import android.provider.ContactsContract.CommonDataKinds;
+import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
 import android.text.Editable;
 import android.text.Selection;
@@ -42,50 +39,31 @@
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
-import android.view.Window;
 import android.widget.Button;
 import android.widget.EditText;
 import android.widget.LinearLayout;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import com.android.internal.telephony.PhoneFactory;
 import com.android.phone.PhoneGlobals;
 import com.android.phone.R;
-import com.android.phone.SubscriptionInfoHelper;
-import android.telephony.CarrierConfigManager;
 
 /**
  * Activity to let the user add or edit an FDN contact.
  */
-public class EditFdnContactScreen extends Activity {
-    private static final String LOG_TAG = PhoneGlobals.LOG_TAG;
-    private static final boolean DBG = false;
+public class EditFdnContactScreen extends BaseFdnContactScreen {
 
     // Menu item codes
     private static final int MENU_IMPORT = 1;
     private static final int MENU_DELETE = 2;
 
-    private static final String INTENT_EXTRA_NAME = "name";
-    private static final String INTENT_EXTRA_NUMBER = "number";
-
-    private static final int PIN2_REQUEST_CODE = 100;
-
-    private SubscriptionInfoHelper mSubscriptionInfoHelper;
-
-    private String mName;
-    private String mNumber;
-    private String mPin2;
     private boolean mAddContact;
-    private QueryHandler mQueryHandler;
 
     private EditText mNameField;
     private EditText mNumberField;
     private LinearLayout mPinFieldContainer;
     private Button mButton;
 
-    private Handler mHandler = new Handler();
-
     /**
      * Constants used in importing from contacts
      */
@@ -108,13 +86,10 @@
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        resolveIntent();
-
-        getWindow().requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
         setContentView(R.layout.edit_fdn_contact_screen);
         setupView();
         setTitle(mAddContact ? R.string.add_fdn_contact : R.string.edit_fdn_contact);
-        PersistableBundle b = null;
+        PersistableBundle b;
         if (mSubscriptionInfoHelper.hasSubId()) {
             b = PhoneGlobals.getInstance().getCarrierConfigForSubId(
                     mSubscriptionInfoHelper.getSubId());
@@ -145,11 +120,7 @@
                 Bundle extras = (intent != null) ? intent.getExtras() : null;
                 if (extras != null) {
                     mPin2 = extras.getString("pin2");
-                    if (mAddContact) {
-                        addContact();
-                    } else {
-                        updateContact();
-                    }
+                    processPin2(mPin2);
                 } else if (resultCode != RESULT_OK) {
                     // if they cancelled, then we just cancel too.
                     if (DBG) log("onActivityResult: cancelled.");
@@ -231,20 +202,15 @@
         return super.onOptionsItemSelected(item);
     }
 
-    private void resolveIntent() {
-        Intent intent = getIntent();
-
-        mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, intent);
-
-        mName =  intent.getStringExtra(INTENT_EXTRA_NAME);
-        mNumber =  intent.getStringExtra(INTENT_EXTRA_NUMBER);
-
+    @Override
+    protected void resolveIntent() {
+        super.resolveIntent();
         mAddContact = TextUtils.isEmpty(mNumber);
     }
 
     /**
      * We have multiple layouts, one to indicate that the user needs to
-     * open the keyboard to enter information (if the keybord is hidden).
+     * open the keyboard to enter information (if the keyboard is hidden).
      * So, we need to make sure that the layout here matches that in the
      * layout file.
      */
@@ -374,36 +340,18 @@
         finish();
     }
 
-    private void authenticatePin2() {
-        Intent intent = new Intent();
-        intent.setClass(this, GetPin2Screen.class);
-        intent.setData(FdnList.getContentUri(mSubscriptionInfoHelper));
-        startActivityForResult(intent, PIN2_REQUEST_CODE);
-    }
-
-    private void displayProgress(boolean flag) {
+    @Override
+    protected void displayProgress(boolean flag) {
+        super.displayProgress(flag);
         // indicate we are busy.
         mDataBusy = flag;
-        getWindow().setFeatureInt(
-                Window.FEATURE_INDETERMINATE_PROGRESS,
-                mDataBusy ? PROGRESS_VISIBILITY_ON : PROGRESS_VISIBILITY_OFF);
         // make sure we don't allow calls to save when we're
         // not ready for them.
         mButton.setClickable(!mDataBusy);
     }
 
-    /**
-     * Removed the status field, with preference to displaying a toast
-     * to match the rest of settings UI.
-     */
-    private void showStatus(CharSequence statusMsg) {
-        if (statusMsg != null) {
-            Toast.makeText(this, statusMsg, Toast.LENGTH_LONG)
-                    .show();
-        }
-    }
-
-    private void handleResult(boolean success, boolean invalidNumber) {
+    @Override
+    protected void handleResult(boolean success, boolean invalidNumber) {
         if (success) {
             if (DBG) log("handleResult: success!");
             showStatus(getResources().getText(mAddContact ?
@@ -426,13 +374,7 @@
             }
         }
 
-        mHandler.postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                finish();
-            }
-        }, 2000);
-
+        mHandler.postDelayed(() -> finish(), 2000);
     }
 
     private final View.OnClickListener mClicked = new View.OnClickListener() {
@@ -486,35 +428,12 @@
         }
     };
 
-    private class QueryHandler extends AsyncQueryHandler {
-        public QueryHandler(ContentResolver cr) {
-            super(cr);
+    @Override
+    protected void pin2AuthenticationSucceed() {
+        if (mAddContact) {
+            addContact();
+        } else {
+            updateContact();
         }
-
-        @Override
-        protected void onQueryComplete(int token, Object cookie, Cursor c) {
-        }
-
-        @Override
-        protected void onInsertComplete(int token, Object cookie, Uri uri) {
-            if (DBG) log("onInsertComplete");
-            displayProgress(false);
-            handleResult(uri != null, false);
-        }
-
-        @Override
-        protected void onUpdateComplete(int token, Object cookie, int result) {
-            if (DBG) log("onUpdateComplete");
-            displayProgress(false);
-            handleResult(result > 0, false);
-        }
-
-        @Override
-        protected void onDeleteComplete(int token, Object cookie, int result) {
-        }
-    }
-
-    private void log(String msg) {
-        Log.d(LOG_TAG, "[EditFdnContact] " + msg);
     }
 }
diff --git a/src/com/android/phone/settings/fdn/FdnSetting.java b/src/com/android/phone/settings/fdn/FdnSetting.java
index 8b5afa4..8f46c85 100644
--- a/src/com/android/phone/settings/fdn/FdnSetting.java
+++ b/src/com/android/phone/settings/fdn/FdnSetting.java
@@ -17,8 +17,8 @@
 package com.android.phone.settings.fdn;
 
 import android.app.ActionBar;
-import android.app.AlertDialog;
-import android.content.DialogInterface;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
 import android.os.AsyncResult;
 import android.os.Bundle;
 import android.os.Handler;
@@ -27,7 +27,6 @@
 import android.preference.PreferenceScreen;
 import android.util.Log;
 import android.view.MenuItem;
-import android.view.WindowManager;
 import android.widget.Toast;
 
 import com.android.internal.telephony.CommandException;
@@ -42,7 +41,7 @@
  * Rewritten to look and behave closer to the other preferences.
  */
 public class FdnSetting extends PreferenceActivity
-        implements EditPinPreference.OnPinEnteredListener, DialogInterface.OnCancelListener {
+        implements EditPinPreference.OnPinEnteredListener, Pin2LockedDialogFragment.Listener {
 
     private static final String LOG_TAG = PhoneGlobals.LOG_TAG;
     private static final boolean DBG = false;
@@ -56,6 +55,7 @@
      */
     private static final int EVENT_PIN2_ENTRY_COMPLETE = 100;
     private static final int EVENT_PIN2_CHANGE_COMPLETE = 200;
+    private static final int EVENT_PIN2_CHANGE_COMPLETE_TOGGLE_FDN = 300;
 
     // String keys for preference lookup
     private static final String BUTTON_FDN_ENABLE_KEY = "button_fdn_enable_key";
@@ -82,8 +82,11 @@
     private static final String PIN_CHANGE_STATE_KEY = "pin_change_state_key";
     private static final String OLD_PIN_KEY = "old_pin_key";
     private static final String NEW_PIN_KEY = "new_pin_key";
+    private static final String PUK_KEY = "puk_key";
     private static final String DIALOG_MESSAGE_KEY = "dialog_message_key";
     private static final String DIALOG_PIN_ENTRY_KEY = "dialog_pin_entry_key";
+    private static final String FDN_DIALOG_MESSAGE_KEY = "fdn_dialog_message_key";
+    private static final String FDN_DIALOG_PIN_ENTRY_KEY = "fdn_dialog_pin_entry_key";
 
     // size limits for the pin.
     private static final int MIN_PIN_LENGTH = 4;
@@ -94,10 +97,10 @@
      */
     @Override
     public void onPinEntered(EditPinPreference preference, boolean positiveResult) {
-        if (preference == mButtonEnableFDN) {
+        if (preference == mButtonEnableFDN && (!mIsPuk2Locked || !positiveResult)) {
             toggleFDNEnable(positiveResult);
-        } else if (preference == mButtonChangePin2){
-            updatePINChangeState(positiveResult);
+        } else {
+            updatePINChangeState(preference, positiveResult);
         }
     }
 
@@ -106,6 +109,12 @@
      */
     private void toggleFDNEnable(boolean positiveResult) {
         if (!positiveResult) {
+            // reset the state on cancel, either to expect PUK2 or PIN2
+            if (!mIsPuk2Locked) {
+                resetPinChangeState();
+            } else {
+                resetPinChangeStateForPUK2();
+            }
             return;
         }
 
@@ -129,10 +138,10 @@
     /**
      * Attempt to change the pin.
      */
-    private void updatePINChangeState(boolean positiveResult) {
+    private void updatePINChangeState(EditPinPreference button, boolean positiveResult) {
         if (DBG) log("updatePINChangeState positive=" + positiveResult
                 + " mPinChangeState=" + mPinChangeState
-                + " mSkipOldPin=" + mIsPuk2Locked);
+                + " mIsPuk2Locked=" + mIsPuk2Locked);
 
         if (!positiveResult) {
             // reset the state on cancel, either to expect PUK2 or PIN2
@@ -155,80 +164,95 @@
         // appears with text to indicate what the issue is.
         switch (mPinChangeState) {
             case PIN_CHANGE_OLD:
-                mOldPin = mButtonChangePin2.getText();
-                mButtonChangePin2.setText("");
+                mOldPin = button.getText();
+                button.setText("");
                 // if the pin is not valid, display a message and reset the state.
                 if (validatePin (mOldPin, false)) {
                     mPinChangeState = PIN_CHANGE_NEW;
-                    displayPinChangeDialog();
+                    displayPinChangeDialog(button);
                 } else {
-                    displayPinChangeDialog(R.string.invalidPin2, true);
+                    displayPinChangeDialog(button, R.string.invalidPin2, true);
                 }
                 break;
             case PIN_CHANGE_NEW:
-                mNewPin = mButtonChangePin2.getText();
-                mButtonChangePin2.setText("");
+                mNewPin = button.getText();
+                button.setText("");
                 // if the new pin is not valid, display a message and reset the state.
                 if (validatePin (mNewPin, false)) {
                     mPinChangeState = PIN_CHANGE_REENTER;
-                    displayPinChangeDialog();
+                    displayPinChangeDialog(button);
                 } else {
-                    displayPinChangeDialog(R.string.invalidPin2, true);
+                    displayPinChangeDialog(button, R.string.invalidPin2, true);
                 }
                 break;
             case PIN_CHANGE_REENTER:
-                // if the re-entered pin is not valid, display a message and reset the state.
-                if (!mNewPin.equals(mButtonChangePin2.getText())) {
-                    mPinChangeState = PIN_CHANGE_NEW;
-                    mButtonChangePin2.setText("");
-                    displayPinChangeDialog(R.string.mismatchPin2, true);
+                if (validatePin(button.getText(), false)) {
+                    // if the re-entered pin is not valid, display a message and reset the state.
+                    if (!mNewPin.equals(button.getText())) {
+                        mPinChangeState = PIN_CHANGE_NEW;
+                        button.setText("");
+                        displayPinChangeDialog(button, R.string.mismatchPin2, true);
+                    } else {
+                        // If the PIN is valid, then we submit the change PIN request or
+                        // display the PUK2 dialog if we KNOW that we're PUK2 locked.
+                        button.setText("");
+                        Message onComplete = mFDNHandler.obtainMessage(
+                                EVENT_PIN2_CHANGE_COMPLETE);
+                        if (!mIsPuk2Locked) {
+                            mPhone.getIccCard().changeIccFdnPassword(mOldPin,
+                                    mNewPin, onComplete);
+                        } else {
+                            mPhone.getIccCard().supplyPuk2(mPuk2, mNewPin,
+                                    onComplete);
+                        }
+                    }
                 } else {
-                    // If the PIN is valid, then we submit the change PIN request.
-                    mButtonChangePin2.setText("");
-                    Message onComplete = mFDNHandler.obtainMessage(
-                            EVENT_PIN2_CHANGE_COMPLETE);
-                    mPhone.getIccCard().changeIccFdnPassword(
-                            mOldPin, mNewPin, onComplete);
+                    button.setText("");
+                    displayPinChangeDialog(button, R.string.invalidPin2, true);
                 }
                 break;
-            case PIN_CHANGE_PUK: {
-                    // Doh! too many incorrect requests, PUK requested.
-                    mPuk2 = mButtonChangePin2.getText();
-                    mButtonChangePin2.setText("");
-                    // if the puk is not valid, display
-                    // a message and reset the state.
-                    if (validatePin (mPuk2, true)) {
-                        mPinChangeState = PIN_CHANGE_NEW_PIN_FOR_PUK;
-                        displayPinChangeDialog();
-                    } else {
-                        displayPinChangeDialog(R.string.invalidPuk2, true);
-                    }
+            case PIN_CHANGE_PUK:
+                // Doh! too many incorrect requests, PUK requested.
+                mPuk2 = button.getText();
+                button.setText("");
+                // if the puk is not valid, display
+                // a message and reset the state.
+                if (validatePin(mPuk2, true)) {
+                    mPinChangeState = PIN_CHANGE_NEW_PIN_FOR_PUK;
+                    displayPinChangeDialog(button);
+                } else {
+                    displayPinChangeDialog(button, R.string.invalidPuk2, true);
                 }
                 break;
             case PIN_CHANGE_NEW_PIN_FOR_PUK:
-                mNewPin = mButtonChangePin2.getText();
-                mButtonChangePin2.setText("");
+                mNewPin = button.getText();
+                button.setText("");
                 // if the new pin is not valid, display
                 // a message and reset the state.
                 if (validatePin (mNewPin, false)) {
                     mPinChangeState = PIN_CHANGE_REENTER_PIN_FOR_PUK;
-                    displayPinChangeDialog();
+                    displayPinChangeDialog(button);
                 } else {
-                    displayPinChangeDialog(R.string.invalidPin2, true);
+                    displayPinChangeDialog(button, R.string.invalidPin2, true);
                 }
                 break;
             case PIN_CHANGE_REENTER_PIN_FOR_PUK:
                 // if the re-entered pin is not valid, display
                 // a message and reset the state.
-                if (!mNewPin.equals(mButtonChangePin2.getText())) {
+                if (!mNewPin.equals(button.getText())) {
                     mPinChangeState = PIN_CHANGE_NEW_PIN_FOR_PUK;
-                    mButtonChangePin2.setText("");
-                    displayPinChangeDialog(R.string.mismatchPin2, true);
+                    button.setText("");
+                    displayPinChangeDialog(button, R.string.mismatchPin2, true);
                 } else {
                     // Both puk2 and new pin2 are ready to submit
-                    mButtonChangePin2.setText("");
-                    Message onComplete = mFDNHandler.obtainMessage(
-                            EVENT_PIN2_CHANGE_COMPLETE);
+                    Message onComplete = null;
+                    if (button == mButtonChangePin2) {
+                        button.setText("");
+                        onComplete = mFDNHandler.obtainMessage(EVENT_PIN2_CHANGE_COMPLETE);
+                    } else {
+                        onComplete = mFDNHandler.obtainMessage(
+                                EVENT_PIN2_CHANGE_COMPLETE_TOGGLE_FDN);
+                    }
                     mPhone.getIccCard().supplyPuk2(mPuk2, mNewPin, onComplete);
                 }
                 break;
@@ -246,6 +270,7 @@
                 // when we are enabling FDN, either we are unsuccessful and display
                 // a toast, or just update the UI.
                 case EVENT_PIN2_ENTRY_COMPLETE: {
+                        if (DBG) log("Handle EVENT_PIN2_ENTRY_COMPLETE");
                         AsyncResult ar = (AsyncResult) msg.obj;
                         if (ar.exception != null) {
                             if (ar.exception instanceof CommandException) {
@@ -255,11 +280,8 @@
                                         ((CommandException) ar.exception).getCommandError();
                                 switch (e) {
                                     case SIM_PUK2:
-                                        // make sure we set the PUK2 state so that we can skip
-                                        // some redundant behaviour.
-                                        displayMessage(R.string.fdn_enable_puk2_requested,
-                                                attemptsRemaining);
-                                        resetPinChangeStateForPUK2();
+                                        showPin2OrPuk2LockedDialog(Pin2LockedDialogFragment
+                                                .DIALOG_ID_PUK2_REQUESTED_ON_PIN_ENTRY);
                                         break;
                                     case PASSWORD_INCORRECT:
                                         displayMessage(R.string.pin2_invalid, attemptsRemaining);
@@ -279,7 +301,8 @@
                 // when changing the pin we need to pay attention to whether or not
                 // the error requests a PUK (usually after too many incorrect tries)
                 // Set the state accordingly.
-                case EVENT_PIN2_CHANGE_COMPLETE: {
+                case EVENT_PIN2_CHANGE_COMPLETE:
+                case EVENT_PIN2_CHANGE_COMPLETE_TOGGLE_FDN: {
                         if (DBG)
                             log("Handle EVENT_PIN2_CHANGE_COMPLETE");
                         AsyncResult ar = (AsyncResult) msg.obj;
@@ -291,34 +314,24 @@
                                 CommandException ce = (CommandException) ar.exception;
                                 if (ce.getCommandError() == CommandException.Error.SIM_PUK2) {
                                     // throw an alert dialog on the screen, displaying the
-                                    // request for a PUK2.  set the cancel listener to
-                                    // FdnSetting.onCancel().
-                                    AlertDialog a = new AlertDialog.Builder(FdnSetting.this)
-                                        .setMessage(R.string.puk2_requested)
-                                        .setCancelable(true)
-                                        .setOnCancelListener(FdnSetting.this)
-                                        .setNeutralButton(android.R.string.ok,
-                                                new DialogInterface.OnClickListener() {
-                                                    @Override
-                                                    public void onClick(DialogInterface dialog,
-                                                            int which) {
-                                                        resetPinChangeStateForPUK2();
-                                                        displayPinChangeDialog(0,true);
-                                                    }
-                                                })
-                                        .create();
-                                    a.getWindow().addFlags(
-                                            WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-                                    a.show();
+                                    // request for a PUK2.
+                                    showPin2OrPuk2LockedDialog(Pin2LockedDialogFragment
+                                            .DIALOG_ID_PUK2_REQUESTED_ON_PIN_CHANGED);
                                 } else {
-                                    // set the correct error message depending upon the state.
-                                    // Reset the state depending upon or knowledge of the PUK state.
-                                    if (!mIsPuk2Locked) {
-                                        displayMessage(R.string.badPin2, attemptsRemaining);
-                                        resetPinChangeState();
+                                    if (mIsPuk2Locked && attemptsRemaining == 0) {
+                                        showPin2OrPuk2LockedDialog(Pin2LockedDialogFragment
+                                                .DIALOG_ID_PUK2_LOCKED_OUT);
                                     } else {
-                                        displayMessage(R.string.badPuk2, attemptsRemaining);
-                                        resetPinChangeStateForPUK2();
+                                        // set the correct error message depending upon the state.
+                                        // Reset the state depending upon or knowledge of the PUK
+                                        // state.
+                                        if (!mIsPuk2Locked) {
+                                            displayMessage(R.string.badPin2, attemptsRemaining);
+                                            resetPinChangeState();
+                                        } else {
+                                            displayMessage(R.string.badPuk2, attemptsRemaining);
+                                            resetPinChangeStateForPUK2();
+                                        }
                                     }
                                 }
                             } else {
@@ -332,28 +345,25 @@
                             }
 
                             // reset to normal behaviour on successful change.
+                            if (msg.what == EVENT_PIN2_CHANGE_COMPLETE_TOGGLE_FDN) {
+                                log("Handle EVENT_PIN2_CHANGE_COMPLETE_TOGGLE_FDN");
+                                // activate/deactivate FDN
+                                toggleFDNEnable(true);
+                            }
                             resetPinChangeState();
                         }
                     }
+                    mButtonChangePin2.setText("");
+                    mButtonEnableFDN.setText("");
                     break;
             }
         }
     };
 
     /**
-     * Cancel listener for the PUK2 request alert dialog.
-     */
-    @Override
-    public void onCancel(DialogInterface dialog) {
-        // set the state of the preference and then display the dialog.
-        resetPinChangeStateForPUK2();
-        displayPinChangeDialog(0, true);
-    }
-
-    /**
      * Display a toast for message, like the rest of the settings.
      */
-    private final void displayMessage(int strId, int attemptsRemaining) {
+    private void displayMessage(int strId, int attemptsRemaining) {
         String s = getString(strId);
         if ((strId == R.string.badPin2) || (strId == R.string.badPuk2) ||
                 (strId == R.string.pin2_invalid)) {
@@ -367,22 +377,27 @@
         Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
     }
 
-    private final void displayMessage(int strId) {
+    private void displayMessage(int strId) {
         displayMessage(strId, -1);
     }
 
     /**
      * The next two functions are for updating the message field on the dialog.
      */
-    private final void displayPinChangeDialog() {
-        displayPinChangeDialog(0, true);
+    private void displayPinChangeDialog(EditPinPreference button) {
+        displayPinChangeDialog(button, 0, true);
     }
 
-    private final void displayPinChangeDialog(int strId, boolean shouldDisplay) {
+    private void displayPinChangeDialog(EditPinPreference button,
+            int strId, boolean shouldDisplay) {
         int msgId;
         switch (mPinChangeState) {
             case PIN_CHANGE_OLD:
-                msgId = R.string.oldPin2Label;
+                if (button == mButtonEnableFDN) {
+                    msgId = R.string.enter_pin2_text;
+                } else {
+                    msgId = R.string.oldPin2Label;
+                }
                 break;
             case PIN_CHANGE_NEW:
             case PIN_CHANGE_NEW_PIN_FOR_PUK:
@@ -400,14 +415,14 @@
 
         // append the note / additional message, if needed.
         if (strId != 0) {
-            mButtonChangePin2.setDialogMessage(getText(msgId) + "\n" + getText(strId));
+            button.setDialogMessage(getText(msgId) + "\n" + getText(strId));
         } else {
-            mButtonChangePin2.setDialogMessage(msgId);
+            button.setDialogMessage(msgId);
         }
 
         // only display if requested.
         if (shouldDisplay) {
-            mButtonChangePin2.showPinDialog();
+            button.showPinDialog();
         }
     }
 
@@ -417,7 +432,8 @@
     private final void resetPinChangeState() {
         if (DBG) log("resetPinChangeState");
         mPinChangeState = PIN_CHANGE_OLD;
-        displayPinChangeDialog(0, false);
+        displayPinChangeDialog(mButtonEnableFDN, 0, false);
+        displayPinChangeDialog(mButtonChangePin2, 0, false);
         mOldPin = mNewPin = "";
         mIsPuk2Locked = false;
     }
@@ -428,7 +444,8 @@
     private final void resetPinChangeStateForPUK2() {
         if (DBG) log("resetPinChangeStateForPUK2");
         mPinChangeState = PIN_CHANGE_PUK;
-        displayPinChangeDialog(0, false);
+        displayPinChangeDialog(mButtonEnableFDN, 0, false);
+        displayPinChangeDialog(mButtonChangePin2, 0, false);
         mOldPin = mNewPin = mPuk2 = "";
         mIsPuk2Locked = true;
     }
@@ -472,7 +489,10 @@
     * Reflect the updated change PIN2 state in the UI.
     */
     private void updateChangePIN2() {
-        if (mPhone.getIccCard().getIccPin2Blocked()) {
+        if (mPhone.getIccCard().getIccPuk2Blocked()) {
+            showPin2OrPuk2LockedDialog(Pin2LockedDialogFragment.DIALOG_ID_PUK2_LOCKED_OUT);
+            resetPinChangeStateForPUK2();
+        } else if (mPhone.getIccCard().getIccPin2Blocked()) {
             // If the pin2 is blocked, the state of the change pin2 dialog
             // should be set for puk2 use (that is, the user should be prompted
             // to enter puk2 code instead of old pin2).
@@ -514,8 +534,14 @@
             mPinChangeState = icicle.getInt(PIN_CHANGE_STATE_KEY);
             mOldPin = icicle.getString(OLD_PIN_KEY);
             mNewPin = icicle.getString(NEW_PIN_KEY);
-            mButtonChangePin2.setDialogMessage(icicle.getString(DIALOG_MESSAGE_KEY));
-            mButtonChangePin2.setText(icicle.getString(DIALOG_PIN_ENTRY_KEY));
+            mPuk2 = icicle.getString(PUK_KEY);
+            mButtonChangePin2.setDialogMessage(
+                    icicle.getString(DIALOG_MESSAGE_KEY));
+            mButtonChangePin2.setText(
+                    icicle.getString(DIALOG_PIN_ENTRY_KEY));
+            mButtonEnableFDN.setDialogMessage(
+                    icicle.getString(FDN_DIALOG_MESSAGE_KEY));
+            mButtonEnableFDN.setText(icicle.getString(FDN_DIALOG_PIN_ENTRY_KEY));
         }
 
         ActionBar actionBar = getActionBar();
@@ -545,8 +571,19 @@
         out.putInt(PIN_CHANGE_STATE_KEY, mPinChangeState);
         out.putString(OLD_PIN_KEY, mOldPin);
         out.putString(NEW_PIN_KEY, mNewPin);
-        out.putString(DIALOG_MESSAGE_KEY, mButtonChangePin2.getDialogMessage().toString());
-        out.putString(DIALOG_PIN_ENTRY_KEY, mButtonChangePin2.getText());
+        out.putString(PUK_KEY, mPuk2);
+        if (mButtonChangePin2.isEnabled()) {
+            out.putString(DIALOG_MESSAGE_KEY, mButtonChangePin2.getDialogMessage().toString());
+            out.putString(DIALOG_PIN_ENTRY_KEY, mButtonChangePin2.getText());
+        }
+        if (mButtonEnableFDN.isEnabled()) {
+            CharSequence dialogMsg = mButtonEnableFDN.getDialogMessage();
+            if (dialogMsg != null) {
+                out.putString(FDN_DIALOG_MESSAGE_KEY,
+                        mButtonEnableFDN.getDialogMessage().toString());
+            }
+            out.putString(FDN_DIALOG_PIN_ENTRY_KEY, mButtonEnableFDN.getText());
+        }
     }
 
     @Override
@@ -562,5 +599,31 @@
     private void log(String msg) {
         Log.d(LOG_TAG, "FdnSetting: " + msg);
     }
+
+    @Override
+    public void onRequestPuk2(int id) {
+        resetPinChangeStateForPUK2();
+        final EditPinPreference button =
+                (id == Pin2LockedDialogFragment.DIALOG_ID_PUK2_REQUESTED_ON_PIN_CHANGED)
+                        ? mButtonChangePin2 : mButtonEnableFDN;
+        displayPinChangeDialog(button, 0, true);
+    }
+
+    private void showPin2OrPuk2LockedDialog(int id) {
+        final FragmentManager fragmentManager = getFragmentManager();
+        Pin2LockedDialogFragment dialogFragment = (Pin2LockedDialogFragment) fragmentManager
+                .findFragmentByTag(Pin2LockedDialogFragment.TAG_PIN2_LOCKED_DIALOG);
+        if (dialogFragment == null) {
+            dialogFragment = new Pin2LockedDialogFragment();
+            Bundle args = new Bundle();
+            args.putInt(Pin2LockedDialogFragment.KEY_DIALOG_ID, id);
+            dialogFragment.setArguments(args);
+            dialogFragment.show(fragmentManager, Pin2LockedDialogFragment.TAG_PIN2_LOCKED_DIALOG);
+        } else {
+            FragmentTransaction transaction = fragmentManager.beginTransaction();
+            transaction.show(dialogFragment);
+            transaction.commitNow();
+        }
+    }
 }
 
diff --git a/src/com/android/phone/settings/fdn/Pin2LockedDialogFragment.java b/src/com/android/phone/settings/fdn/Pin2LockedDialogFragment.java
new file mode 100644
index 0000000..ff16a7f
--- /dev/null
+++ b/src/com/android/phone/settings/fdn/Pin2LockedDialogFragment.java
@@ -0,0 +1,102 @@
+/*
+ * 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.phone.settings.fdn;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+import com.android.phone.R;
+
+/**
+ * Dialog Fragment that displays dialogs indicating that PIN2/PUK2 has been locked out.
+ *
+ * 1. When user fails PIN2 authentication and PIN2 is locked, show the dialog indicating that PIN2
+ * is locked and PUK2 must be entered.
+ * 2. When user fails PUK2 authentication and PUK2 is locked, show the dialog indicating that PUK2
+ * is locked and user must contact service provider to unlock PUK2.
+ */
+public class Pin2LockedDialogFragment extends DialogFragment {
+
+    static final String TAG_PIN2_LOCKED_DIALOG = "tag_pin2_locked_dialog";
+    static final String KEY_DIALOG_ID = "key_dialog_id";
+
+    // AlertDialog IDs
+    static final int DIALOG_ID_PUK2_LOCKED_OUT = 10;
+    static final int DIALOG_ID_PUK2_REQUESTED_ON_PIN_ENTRY = 11;
+    static final int DIALOG_ID_PUK2_REQUESTED_ON_PIN_CHANGED = 12;
+
+    private Listener mListener;
+    private int mId;
+
+    interface Listener {
+        void onRequestPuk2(int id);
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        super.onCreateDialog(savedInstanceState);
+        Activity activity = getActivity();
+        if (!(activity instanceof Listener)) {
+            return null;
+        }
+        mListener = (Listener) activity;
+        mId = getArguments().getInt(KEY_DIALOG_ID);
+
+        if (mId == DIALOG_ID_PUK2_LOCKED_OUT) {
+            AlertDialog alert = new AlertDialog.Builder(activity)
+                    .setMessage(R.string.puk2_locked)
+                    .setCancelable(true)
+                    .create();
+            alert.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+            alert.setButton(DialogInterface.BUTTON_NEUTRAL, getText(R.string.ok),
+                    (dialog, which) -> {
+                    });
+            return alert;
+        }
+
+        if (mId == DIALOG_ID_PUK2_REQUESTED_ON_PIN_CHANGED
+                || mId == DIALOG_ID_PUK2_REQUESTED_ON_PIN_ENTRY) {
+            AlertDialog alert = new AlertDialog.Builder(activity)
+                    .setMessage(R.string.puk2_requested)
+                    .setCancelable(true)
+                    .create();
+            alert.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+            alert.setButton(DialogInterface.BUTTON_NEUTRAL, getText(R.string.ok),
+                    (dialog, which) -> {
+                        mListener.onRequestPuk2(mId);
+                        dialog.dismiss();
+                    });
+            return alert;
+        }
+        return null;
+    }
+
+    @Override
+    public void onCancel(DialogInterface dialog) {
+        if (mId == DIALOG_ID_PUK2_REQUESTED_ON_PIN_CHANGED
+                || mId == DIALOG_ID_PUK2_REQUESTED_ON_PIN_ENTRY) {
+            mListener.onRequestPuk2(mId);
+        }
+        dialog.dismiss();
+    }
+}
+
diff --git a/src/com/android/phone/vvm/VvmSimStateTracker.java b/src/com/android/phone/vvm/VvmSimStateTracker.java
index c648d9c..a77bd7b 100644
--- a/src/com/android/phone/vvm/VvmSimStateTracker.java
+++ b/src/com/android/phone/vvm/VvmSimStateTracker.java
@@ -20,13 +20,16 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
 import android.os.SystemProperties;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
-import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -68,7 +71,8 @@
      * Waits for the account to become {@link ServiceState#STATE_IN_SERVICE} and notify the
      * connected event. Will unregister itself once the event has been triggered.
      */
-    private class ServiceStateListener extends PhoneStateListener {
+    private class ServiceStateListener extends TelephonyCallback implements
+            TelephonyCallback.ServiceStateListener  {
 
         private final PhoneAccountHandle mPhoneAccountHandle;
         private final Context mContext;
@@ -84,7 +88,8 @@
                 VvmLog.e(TAG, "Cannot create TelephonyManager from " + mPhoneAccountHandle);
                 return;
             }
-            telephonyManager.listen(this, PhoneStateListener.LISTEN_SERVICE_STATE);
+            telephonyManager.registerTelephonyCallback(
+                    new HandlerExecutor(new Handler(Looper.getMainLooper())), this);
         }
 
         public void unlisten() {
@@ -92,7 +97,7 @@
             // PhoneStateListener, and mPhoneAccountHandle might be invalid at this point
             // (e.g. SIM removal)
             mContext.getSystemService(TelephonyManager.class)
-                    .listen(this, PhoneStateListener.LISTEN_NONE);
+                    .unregisterTelephonyCallback(this);
             sListeners.put(mPhoneAccountHandle, null);
         }
 
diff --git a/src/com/android/services/telephony/CallQualityManager.java b/src/com/android/services/telephony/CallQualityManager.java
deleted file mode 100644
index 01b5bae..0000000
--- a/src/com/android/services/telephony/CallQualityManager.java
+++ /dev/null
@@ -1,109 +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;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.os.Bundle;
-import android.telecom.BluetoothCallQualityReport;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.phone.R;
-
-/**
- * class to handle call quality events that are received by telecom and telephony
- */
-public class CallQualityManager {
-    private static final String TAG = CallQualityManager.class.getCanonicalName();
-    private static final String CALL_QUALITY_REPORT_CHANNEL = "call_quality_report_channel";
-
-    /** notification ids */
-    public static final int BLUETOOTH_CHOPPY_VOICE_NOTIFICATION_ID = 700;
-
-    public static final String CALL_QUALITY_CHANNEL_ID = "CallQualityNotification";
-
-    private final Context mContext;
-    private final NotificationChannel mNotificationChannel;
-    private final NotificationManager mNotificationManager;
-
-    public CallQualityManager(Context context) {
-        mContext = context;
-        mNotificationChannel = new NotificationChannel(CALL_QUALITY_CHANNEL_ID,
-                mContext.getString(R.string.call_quality_notification_name),
-                NotificationManager.IMPORTANCE_HIGH);
-        mNotificationManager = (NotificationManager)
-                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-        mNotificationManager.createNotificationChannel(mNotificationChannel);
-    }
-
-    /**
-     * method that is called whenever a
-     * {@code BluetoothCallQualityReport.EVENT_SEND_BLUETOOTH_CALL_QUALITY_REPORT} is received
-     * @param extras Bundle that includes serialized {@code BluetoothCallQualityReport} parcelable
-     */
-    @VisibleForTesting
-    public void onBluetoothCallQualityReported(Bundle extras) {
-        if (extras == null) {
-            Log.d(TAG, "onBluetoothCallQualityReported: no extras provided");
-        }
-
-        BluetoothCallQualityReport callQualityReport = extras.getParcelable(
-                BluetoothCallQualityReport.EXTRA_BLUETOOTH_CALL_QUALITY_REPORT);
-
-        if (callQualityReport.isChoppyVoice()) {
-            onChoppyVoice();
-        }
-        // TODO: once other signals are also sent, we will add more actions here
-    }
-
-    /**
-     * method to post a notification to user suggesting ways to improve call quality in case of
-     * bluetooth choppy voice
-     */
-    @VisibleForTesting
-    public void onChoppyVoice() {
-        String title = "Call Quality Improvement";
-        //TODO: update call_quality_bluetooth_enhancement_suggestion with below before submitting:
-//        "Voice is not being transmitted properly via your bluetooth device."
-//                + "To improve, try:\n"
-//                + "1. moving your phone closer to your bluetooth device\n"
-//                + "2. using a different bluetooth device, or your phone's speaker\n";
-        popUpNotification(title,
-                mContext.getText(R.string.call_quality_notification_bluetooth_details));
-    }
-
-    private void popUpNotification(String title, CharSequence details) {
-        int iconId = android.R.drawable.stat_notify_error;
-
-        Notification notification = new Notification.Builder(mContext)
-                .setSmallIcon(iconId)
-                .setWhen(System.currentTimeMillis())
-                .setAutoCancel(true)
-                .setContentTitle(title)
-                .setContentText(details)
-                .setStyle(new Notification.BigTextStyle().bigText(details))
-                .setOngoing(true)
-                .setChannelId(CALL_QUALITY_CHANNEL_ID)
-                .setOnlyAlertOnce(true)
-                .build();
-
-        mNotificationManager.notify(TAG, BLUETOOTH_CHOPPY_VOICE_NOTIFICATION_ID, notification);
-    }
-}
diff --git a/src/com/android/services/telephony/CdmaConferenceController.java b/src/com/android/services/telephony/CdmaConferenceController.java
index 8523a5f..a076ec8 100644
--- a/src/com/android/services/telephony/CdmaConferenceController.java
+++ b/src/com/android/services/telephony/CdmaConferenceController.java
@@ -20,11 +20,14 @@
 import android.telecom.Connection;
 import android.telecom.DisconnectCause;
 import android.telecom.PhoneAccountHandle;
+import android.util.ArraySet;
 
 import com.android.phone.PhoneUtils;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Manages CDMA conference calls. CDMA conference calls are much more limited than GSM conference
@@ -83,6 +86,9 @@
 
     private final Handler mHandler = new Handler();
 
+    private final Set<CdmaConnection> mPendingAddConnections = Collections.synchronizedSet(
+            new ArraySet<>());
+
     public CdmaConferenceController(TelephonyConnectionService connectionService) {
         mConnectionService = connectionService;
     }
@@ -91,7 +97,7 @@
     private CdmaConference mConference;
 
     void add(final CdmaConnection connection) {
-        if (mCdmaConnections.contains(connection)) {
+        if (mCdmaConnections.contains(connection) || !mPendingAddConnections.add(connection)) {
             // Adding a duplicate realistically shouldn't happen.
             Log.w(this, "add - connection already tracked; connection=%s", connection);
             return;
@@ -140,6 +146,7 @@
 
     private void addInternal(CdmaConnection connection) {
         mCdmaConnections.add(connection);
+        mPendingAddConnections.remove(connection);
         connection.addTelephonyConnectionListener(mTelephonyConnectionListener);
         recalculateConference();
     }
diff --git a/src/com/android/services/telephony/DisconnectCauseUtil.java b/src/com/android/services/telephony/DisconnectCauseUtil.java
index f243462..9c89e9e 100644
--- a/src/com/android/services/telephony/DisconnectCauseUtil.java
+++ b/src/com/android/services/telephony/DisconnectCauseUtil.java
@@ -23,6 +23,7 @@
 import android.telecom.DisconnectCause;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
+import android.telephony.ims.ImsReasonInfo;
 
 import com.android.internal.telephony.CallFailCause;
 import com.android.internal.telephony.Phone;
@@ -64,13 +65,13 @@
     * message and tone.
     *
     * @param telephonyDisconnectCause The code for the reason for the disconnect.
-    * @param telephonyPerciseDisconnectCause The code for the percise reason for the disconnect.
+    * @param telephonyPreciseDisconnectCause The code for the precise reason for the disconnect.
     * @param reason Description of the reason for the disconnect, not intended for the user to see..
     */
     public static DisconnectCause toTelecomDisconnectCause(
-            int telephonyDisconnectCause, int telephonyPerciseDisconnectCause, String reason) {
-        return toTelecomDisconnectCause(telephonyDisconnectCause, telephonyPerciseDisconnectCause,
-                reason, SubscriptionManager.getDefaultVoicePhoneId());
+            int telephonyDisconnectCause, int telephonyPreciseDisconnectCause, String reason) {
+        return toTelecomDisconnectCause(telephonyDisconnectCause, telephonyPreciseDisconnectCause,
+                reason, SubscriptionManager.getDefaultVoicePhoneId(), null);
     }
 
     /**
@@ -84,30 +85,33 @@
     public static DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause,
             String reason, int phoneId) {
         return toTelecomDisconnectCause(telephonyDisconnectCause, CallFailCause.NOT_VALID,
-                reason, phoneId);
+                reason, phoneId, null);
     }
 
    /**
     * Converts from a disconnect code in {@link android.telephony.DisconnectCause} into a more
     * generic {@link android.telecom.DisconnectCause}.object, possibly populated with a localized
     * message and tone for Slot.
-    *
     * @param telephonyDisconnectCause The code for the reason for the disconnect.
-    * @param telephonyPerciseDisconnectCause The code for the percise reason for the disconnect.
-    * @param reason Description of the reason for the disconnect, not intended for the user to see..
+    * @param telephonyPreciseDisconnectCause The code for the precise reason for the disconnect.
+    * @param reason Description of the reason for the disconnect, not intended for the user to see.
     * @param phoneId To support localized message based on phoneId
+    * @param imsReasonInfo
     */
     public static DisconnectCause toTelecomDisconnectCause(
-            int telephonyDisconnectCause, int telephonyPerciseDisconnectCause, String reason,
-            int phoneId) {
+            int telephonyDisconnectCause, int telephonyPreciseDisconnectCause, String reason,
+            int phoneId, ImsReasonInfo imsReasonInfo) {
         Context context = PhoneGlobals.getInstance();
         return new DisconnectCause(
                 toTelecomDisconnectCauseCode(telephonyDisconnectCause),
                 toTelecomDisconnectCauseLabel(context, telephonyDisconnectCause,
-                        telephonyPerciseDisconnectCause),
+                        telephonyPreciseDisconnectCause),
                 toTelecomDisconnectCauseDescription(context, telephonyDisconnectCause, phoneId),
                 toTelecomDisconnectReason(context,telephonyDisconnectCause, reason, phoneId),
-                toTelecomDisconnectCauseTone(telephonyDisconnectCause, phoneId));
+                toTelecomDisconnectCauseTone(telephonyDisconnectCause, phoneId),
+                telephonyDisconnectCause,
+                telephonyPreciseDisconnectCause,
+                imsReasonInfo);
     }
 
     /**
@@ -233,10 +237,10 @@
      * Returns a label for to the disconnect cause to be shown to the user.
      */
     private static CharSequence toTelecomDisconnectCauseLabel(
-            Context context, int telephonyDisconnectCause, int telephonyPerciseDisconnectCause) {
+            Context context, int telephonyDisconnectCause, int telephonyPreciseDisconnectCause) {
         CharSequence label;
-        if (telephonyPerciseDisconnectCause != CallFailCause.NOT_VALID) {
-            label = getLabelFromPreciseDisconnectCause(context, telephonyPerciseDisconnectCause,
+        if (telephonyPreciseDisconnectCause != CallFailCause.NOT_VALID) {
+            label = getLabelFromPreciseDisconnectCause(context, telephonyPreciseDisconnectCause,
                     telephonyDisconnectCause);
         } else {
             label = getLabelFromDisconnectCause(context, telephonyDisconnectCause);
@@ -654,12 +658,14 @@
                 break;
 
             case android.telephony.DisconnectCause.POWER_OFF:
-                // Radio is explictly powered off because the device is in airplane mode.
+                // Radio is explicitly powered off because the device's radio is off.
 
                 // TODO: Offer the option to turn the radio on, and automatically retry the call
                 // once network registration is complete.
 
-                if (ImsUtil.shouldPromoteWfc(context, phoneId)) {
+                if (isRadioOffForThermalMitigation(phoneId)) {
+                    resourceId = R.string.incall_error_power_off_thermal;
+                } else if (ImsUtil.shouldPromoteWfc(context, phoneId)) {
                     resourceId = R.string.incall_error_promote_wfc;
                 } else if (ImsUtil.isWfcModeWifiOnly(context, phoneId)) {
                     resourceId = R.string.incall_error_wfc_only_no_wireless_network;
@@ -781,7 +787,12 @@
             default:
                 break;
         }
-        return resourceId == null ? "" : context.getResources().getString(resourceId);
+        return resourceId == null ? "" : context.getResources().getText(resourceId);
+    }
+
+    private static boolean isRadioOffForThermalMitigation(int phoneId) {
+        Phone phone = PhoneFactory.getPhone(phoneId);
+        return phone.isRadioOffForThermalMitigation();
     }
 
     /**
diff --git a/src/com/android/services/telephony/PstnIncomingCallNotifier.java b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
index 235cbce..4191367 100644
--- a/src/com/android/services/telephony/PstnIncomingCallNotifier.java
+++ b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
@@ -301,6 +301,12 @@
                     if (CallComposerPictureManager.sTestMode) {
                         imsCallProfile.setCallExtra(ImsCallProfile.EXTRA_PICTURE_URL,
                                 CallComposerPictureManager.FAKE_SERVER_URL);
+                        imsCallProfile.setCallExtraInt(ImsCallProfile.EXTRA_PRIORITY,
+                                TelecomManager.PRIORITY_URGENT);
+                        imsCallProfile.setCallExtra(ImsCallProfile.EXTRA_CALL_SUBJECT,
+                                CallComposerPictureManager.FAKE_SUBJECT);
+                        imsCallProfile.setCallExtraParcelable(ImsCallProfile.EXTRA_LOCATION,
+                                CallComposerPictureManager.FAKE_LOCATION);
                     }
 
                     extras.putInt(TelecomManager.EXTRA_PRIORITY,
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 7fd9d39..7252f94 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -32,6 +32,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.PersistableBundle;
@@ -42,11 +43,11 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
-import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 import android.telephony.ims.ImsException;
 import android.telephony.ims.ImsMmTelManager;
@@ -113,6 +114,8 @@
     private static final String EXTRA_SUPPORTS_VIDEO_CALLING_FALLBACK =
             "android.telecom.extra.SUPPORTS_VIDEO_CALLING_FALLBACK";
 
+    private Handler mHandler;
+
     final class AccountEntry implements PstnPhoneCapabilitiesNotifier.Listener {
         private final Phone mPhone;
         private PhoneAccount mAccount;
@@ -1081,7 +1084,11 @@
         }
     };
 
-    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+    private final TelephonyCallback mTelephonyCallback = new TelecomAccountTelephonyCallback();
+
+    private class TelecomAccountTelephonyCallback extends TelephonyCallback implements
+            TelephonyCallback.ActiveDataSubscriptionIdListener,
+            TelephonyCallback.ServiceStateListener {
         @Override
         public void onServiceStateChanged(ServiceState serviceState) {
             int newState = serviceState.getState();
@@ -1107,7 +1114,7 @@
                 }
             }
         }
-    };
+    }
 
     private static TelecomAccountRegistry sInstance;
     private final Context mContext;
@@ -1147,6 +1154,7 @@
         mTelephonyManager = TelephonyManager.from(context);
         mSubscriptionManager = SubscriptionManager.from(context);
         mHandlerThread.start();
+        mHandler = new Handler(Looper.getMainLooper());
         mRegisterSubscriptionListenerBackoff = new ExponentialBackoff(
                 REGISTER_START_DELAY_MS,
                 REGISTER_MAXIMUM_DELAY_MS,
@@ -1374,8 +1382,8 @@
 
         // We also need to listen for changes to the service state (e.g. emergency -> in service)
         // because this could signal a removal or addition of a SIM in a single SIM phone.
-        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE
-                | PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
+        mTelephonyManager.registerTelephonyCallback(new HandlerExecutor(mHandler),
+                mTelephonyCallback);
 
         // Listen for user switches.  When the user switches, we need to ensure that if the current
         // use is not the primary user we disable video calling.
@@ -1395,8 +1403,7 @@
 
     private void registerContentObservers() {
         // Listen to the RTT system setting so that we update it when the user flips it.
-        ContentObserver rttUiSettingObserver = new ContentObserver(
-                new Handler(Looper.getMainLooper())) {
+        ContentObserver rttUiSettingObserver = new ContentObserver(mHandler) {
             @Override
             public void onChange(boolean selfChange) {
                 synchronized (mAccountsLock) {
@@ -1412,8 +1419,7 @@
                 rttSettingUri, false, rttUiSettingObserver);
 
         // Listen to the changes to the user's Contacts Discovery Setting.
-        ContentObserver contactDiscoveryObserver = new ContentObserver(
-                new Handler(Looper.getMainLooper())) {
+        ContentObserver contactDiscoveryObserver = new ContentObserver(mHandler) {
             @Override
             public void onChange(boolean selfChange) {
                 synchronized (mAccountsLock) {
diff --git a/src/com/android/services/telephony/TelephonyConferenceController.java b/src/com/android/services/telephony/TelephonyConferenceController.java
index fd16d4b..228541a 100644
--- a/src/com/android/services/telephony/TelephonyConferenceController.java
+++ b/src/com/android/services/telephony/TelephonyConferenceController.java
@@ -177,7 +177,7 @@
     }
 
     private void recalculateConference() {
-        Set<Connection> conferencedConnections = new HashSet<>();
+        Set<TelephonyConnection> conferencedConnections = new HashSet<>();
         int numGsmConnections = 0;
 
         for (TelephonyConnection connection : mTelephonyConnections) {
@@ -254,7 +254,7 @@
                     PhoneAccountHandle phoneAccountHandle = null;
                     if (!conferencedConnections.isEmpty()) {
                         TelephonyConnection telephonyConnection =
-                                (TelephonyConnection) conferencedConnections.iterator().next();
+                                conferencedConnections.iterator().next();
                         phoneAccountHandle = PhoneUtils.makePstnPhoneAccountHandle(
                                 telephonyConnection.getPhone());
                     }
@@ -262,11 +262,31 @@
                     mTelephonyConference = new TelephonyConference(phoneAccountHandle);
                     Log.i(this, "Creating new TelephonyConference to hold conferenced connections."
                             + " conference=" + mTelephonyConference);
-                    for (Connection connection : conferencedConnections) {
+                    boolean isDowngradedConference = false;
+                    for (TelephonyConnection connection : conferencedConnections) {
                         Log.d(this, "Adding a connection to a conference call: %s %s",
                                 mTelephonyConference, connection);
+                        if ((connection.getConnectionProperties()
+                                & Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0) {
+                            // Remove all instances of PROPERTY_IS_DOWNGRADED_CONFERENCE. This
+                            // property should only be set on the parent call (i.e. the newly
+                            // created TelephonyConference.
+                            Log.d(this, "Removing PROPERTY_IS_DOWNGRADED_CONFERENCE from connection"
+                                    + " %s", connection);
+                            int newProperties = connection.getConnectionProperties()
+                                    & ~Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE;
+                            connection.setTelephonyConnectionProperties(newProperties);
+                            isDowngradedConference = true;
+                        }
                         mTelephonyConference.addTelephonyConnection(connection);
                     }
+                    // Reapply the downgraded-conference flag to the parent conference if it was on
+                    // one of the children.
+                    if (isDowngradedConference) {
+                        mTelephonyConference.setConnectionProperties(
+                                mTelephonyConference.getConnectionProperties()
+                                        | Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE);
+                    }
                     mTelephonyConference.updateCallRadioTechAfterCreation();
                     mConnectionService.addConference(mTelephonyConference);
                 } else {
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index c41bf1a..64c008a 100755
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.drawable.Icon;
@@ -27,9 +28,11 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Messenger;
 import android.os.PersistableBundle;
-import android.telecom.BluetoothCallQualityReport;
 import android.telecom.CallAudioState;
+import android.telecom.CallDiagnostics;
+import android.telecom.CallScreeningService;
 import android.telecom.Conference;
 import android.telecom.Connection;
 import android.telecom.ConnectionService;
@@ -46,10 +49,12 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.ims.ImsCallProfile;
+import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.ImsStreamMediaProfile;
 import android.telephony.ims.RtpHeaderExtension;
 import android.telephony.ims.RtpHeaderExtensionType;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Pair;
 
 import com.android.ims.ImsCall;
@@ -65,8 +70,13 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.d2d.Communicator;
+import com.android.internal.telephony.d2d.DtmfAdapter;
+import com.android.internal.telephony.d2d.DtmfTransport;
+import com.android.internal.telephony.d2d.MessageTypeAndValueHelper;
 import com.android.internal.telephony.d2d.RtpAdapter;
 import com.android.internal.telephony.d2d.RtpTransport;
+import com.android.internal.telephony.d2d.Timeouts;
+import com.android.internal.telephony.d2d.TransportProtocol;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneCall;
@@ -89,6 +99,8 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.stream.Collectors;
 
 /**
  * Base class for CDMA and GSM connections.
@@ -127,6 +139,8 @@
     private static final int MSG_ON_CONNECTION_EVENT = 19;
     private static final int MSG_REDIAL_CONNECTION_CHANGED = 20;
     private static final int MSG_REJECT = 21;
+    private static final int MSG_DTMF_DONE = 22;
+    private static final int MSG_MEDIA_ATTRIBUTES_CHANGED = 23;
 
     private static final String JAPAN_COUNTRY_CODE_WITH_PLUS_SIGN = "+81";
     private static final String JAPAN_ISO_COUNTRY_CODE = "JP";
@@ -242,6 +256,10 @@
                     setAudioQuality(audioQuality);
                     break;
 
+                case MSG_MEDIA_ATTRIBUTES_CHANGED:
+                    refreshCodec();
+                    break;
+
                 case MSG_SET_CONFERENCE_PARTICIPANTS:
                     List<ConferenceParticipant> participants = (List<ConferenceParticipant>) msg.obj;
                     updateConferenceParticipants(participants);
@@ -297,6 +315,9 @@
                     int rejectReason = (int) msg.obj;
                     reject(rejectReason);
                     break;
+                case MSG_DTMF_DONE:
+                    Log.i(this, "MSG_DTMF_DONE");
+                    break;
 
                 case MSG_SET_CALL_RADIO_TECH:
                     int vrat = (int) msg.obj;
@@ -335,6 +356,8 @@
         }
     };
 
+    private final Messenger mHandlerMessenger = new Messenger(mHandler);
+
     /**
      * Handles {@link SuppServiceNotification}s pertinent to Telephony.
      * @param ssn the notification.
@@ -504,7 +527,7 @@
 
         @Override
         public void onStateChanged(android.telecom.Connection c, int state) {
-            mCommunicator.onStateChanged(c, state);
+            mCommunicator.onStateChanged(c.getTelecomCallId(), state);
         }
     }
 
@@ -579,6 +602,12 @@
         public void onAudioQualityChanged(int audioQuality) {
             mHandler.obtainMessage(MSG_SET_AUDIO_QUALITY, audioQuality).sendToTarget();
         }
+
+        @Override
+        public void onMediaAttributesChanged() {
+            mHandler.obtainMessage(MSG_MEDIA_ATTRIBUTES_CHANGED).sendToTarget();
+        }
+
         /**
          * Handles a change in the state of conference participant(s), as reported by the
          * {@link com.android.internal.telephony.Connection}.
@@ -736,6 +765,15 @@
                     extensionData.size());
             mRtpTransport.onRtpHeaderExtensionsReceived(extensionData);
         }
+
+        @Override
+        public void onReceivedDtmfDigit(char digit) {
+            if (mDtmfTransport == null) {
+                return;
+            }
+            Log.i(this, "onReceivedDtmfDigit: digit=%c", digit);
+            mDtmfTransport.onDtmfReceived(digit);
+        }
     };
 
     private TelephonyConnectionService mTelephonyConnectionService;
@@ -846,6 +884,8 @@
 
     private RtpTransport mRtpTransport;
 
+    private DtmfTransport mDtmfTransport;
+
     /**
      * Facilitates device to device communication.
      */
@@ -857,8 +897,6 @@
     private final Set<TelephonyConnectionListener> mTelephonyListeners = Collections.newSetFromMap(
             new ConcurrentHashMap<TelephonyConnectionListener, Boolean>(8, 0.9f, 1));
 
-    private CallQualityManager mCallQualityManager;
-
     protected TelephonyConnection(com.android.internal.telephony.Connection originalConnection,
             String callId, @android.telecom.Call.Details.CallDirection int callDirection) {
         setCallDirection(callDirection);
@@ -868,14 +906,17 @@
         }
     }
 
+    @VisibleForTesting
+    protected TelephonyConnection() {
+        // Do nothing
+    }
+
     @Override
     public void onCallEvent(String event, Bundle extras) {
         switch (event) {
-            case BluetoothCallQualityReport.EVENT_BLUETOOTH_CALL_QUALITY_REPORT:
-                if (mCallQualityManager == null) {
-                    mCallQualityManager = new CallQualityManager(getPhone().getContext());
-                }
-                mCallQualityManager.onBluetoothCallQualityReported(extras);
+            case Connection.EVENT_DEVICE_TO_DEVICE_MESSAGE:
+                // A Device to device message is being sent by a CallDiagnosticService.
+                handleOutgoingDeviceToDeviceMessage(extras);
                 break;
             default:
                 break;
@@ -1131,12 +1172,33 @@
     }
 
     @Override
-    public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts) {
+    public void onCallFilteringCompleted(CallFilteringCompletionInfo callFilteringCompletionInfo) {
+        // Check what the call screening service has to say, if it's a system dialer.
+        boolean isAllowedToDisplayPicture;
+        String callScreeningPackage =
+                callFilteringCompletionInfo.getCallScreeningComponent() == null
+                        ? null
+                        : callFilteringCompletionInfo.getCallScreeningComponent().getPackageName();
+        boolean isResponseFromSystemDialer =
+                Objects.equals(getPhone().getContext()
+                        .getSystemService(TelecomManager.class).getSystemDialerPackage(),
+                        callScreeningPackage);
+        CallScreeningService.CallResponse callScreeningResponse =
+                callFilteringCompletionInfo.getCallResponse();
+
+        if (isResponseFromSystemDialer && callScreeningResponse != null
+                && callScreeningResponse.getCallComposerAttachmentsToShow() >= 0) {
+            isAllowedToDisplayPicture = (callScreeningResponse.getCallComposerAttachmentsToShow()
+                    & CallScreeningService.CallResponse.CALL_COMPOSER_ATTACHMENT_PICTURE) != 0;
+        } else {
+            isAllowedToDisplayPicture = callFilteringCompletionInfo.isInContacts();
+        }
+
         if (isImsConnection()) {
             ImsPhone imsPhone = (getPhone() instanceof ImsPhone) ? (ImsPhone) getPhone() : null;
             if (imsPhone != null
                     && imsPhone.getCallComposerStatus() == TelephonyManager.CALL_COMPOSER_STATUS_ON
-                    && !isBlocked && isInContacts) {
+                    && !callFilteringCompletionInfo.isBlocked() && isAllowedToDisplayPicture) {
                 ImsPhoneConnection originalConnection = (ImsPhoneConnection) mOriginalConnection;
                 ImsCallProfile profile = originalConnection.getImsCall().getCallProfile();
                 String serverUrl = CallComposerPictureManager.sTestMode
@@ -1181,6 +1243,8 @@
         Log.v(this, "performAnswer");
         if (isValidRingingCall() && getPhone() != null) {
             try {
+                mTelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(
+                        getPhoneAccountHandle());
                 getPhone().acceptCall(videoState);
             } catch (CallStateException e) {
                 Log.e(this, e, "Failed to accept call.");
@@ -1373,7 +1437,7 @@
 
         newProperties = changeBitmask(newProperties, PROPERTY_HIGH_DEF_AUDIO,
                 hasHighDefAudioProperty());
-        newProperties = changeBitmask(newProperties, PROPERTY_WIFI, isWifi());
+        newProperties = changeBitmask(newProperties, PROPERTY_WIFI, isWifi() && !isCrossSimCall());
         newProperties = changeBitmask(newProperties, PROPERTY_IS_EXTERNAL_CALL,
                 isExternalConnection());
         newProperties = changeBitmask(newProperties, PROPERTY_HAS_CDMA_VOICE_PRIVACY,
@@ -1446,6 +1510,17 @@
         // Subclass can override this to do cleanup.
     }
 
+    public void registerForCallEvents(Phone phone) {
+        phone.registerForPreciseCallStateChanged(mHandler, MSG_PRECISE_CALL_STATE_CHANGED, null);
+        phone.registerForHandoverStateChanged(mHandler, MSG_HANDOVER_STATE_CHANGED, null);
+        phone.registerForRedialConnectionChanged(mHandler, MSG_REDIAL_CONNECTION_CHANGED, null);
+        phone.registerForRingbackTone(mHandler, MSG_RINGBACK_TONE, null);
+        phone.registerForSuppServiceNotification(mHandler, MSG_SUPP_SERVICE_NOTIFY, null);
+        phone.registerForOnHoldTone(mHandler, MSG_ON_HOLD_TONE, null);
+        phone.registerForInCallVoicePrivacyOn(mHandler, MSG_CDMA_VOICE_PRIVACY_ON, null);
+        phone.registerForInCallVoicePrivacyOff(mHandler, MSG_CDMA_VOICE_PRIVACY_OFF, null);
+    }
+
     void setOriginalConnection(com.android.internal.telephony.Connection originalConnection) {
         Log.v(this, "new TelephonyConnection, originalConnection: " + originalConnection);
         if (mOriginalConnection != null && originalConnection != null
@@ -1462,17 +1537,8 @@
         mOriginalConnectionExtras.clear();
         mOriginalConnection = originalConnection;
         mOriginalConnection.setTelecomCallId(getTelecomCallId());
-        getPhone().registerForPreciseCallStateChanged(
-                mHandler, MSG_PRECISE_CALL_STATE_CHANGED, null);
-        getPhone().registerForHandoverStateChanged(
-                mHandler, MSG_HANDOVER_STATE_CHANGED, null);
-        getPhone().registerForRedialConnectionChanged(
-                mHandler, MSG_REDIAL_CONNECTION_CHANGED, null);
-        getPhone().registerForRingbackTone(mHandler, MSG_RINGBACK_TONE, null);
-        getPhone().registerForSuppServiceNotification(mHandler, MSG_SUPP_SERVICE_NOTIFY, null);
-        getPhone().registerForOnHoldTone(mHandler, MSG_ON_HOLD_TONE, null);
-        getPhone().registerForInCallVoicePrivacyOn(mHandler, MSG_CDMA_VOICE_PRIVACY_ON, null);
-        getPhone().registerForInCallVoicePrivacyOff(mHandler, MSG_CDMA_VOICE_PRIVACY_OFF, null);
+        registerForCallEvents(getPhone());
+
         mOriginalConnection.addPostDialListener(mPostDialListener);
         mOriginalConnection.addListener(mOriginalConnectionListener);
 
@@ -1630,6 +1696,7 @@
                 Connection.AUDIO_CODEC_NONE);
         if (newCodecType != oldCodecType) {
             newExtras.putInt(Connection.EXTRA_AUDIO_CODEC, newCodecType);
+            Log.i(this, "refreshCodec: codec changed; old=%d, new=%d", oldCodecType, newCodecType);
             changed = true;
         }
         if (isImsConnection()) {
@@ -1637,6 +1704,8 @@
             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);
+                Log.i(this, "refreshCodec: bitrate changed; old=%f, new=%f", oldBitrate,
+                        newBitrate);
                 changed = true;
             }
 
@@ -1645,6 +1714,8 @@
                     0.0f);
             if (Math.abs(newBandwidth - oldBandwidth) > THRESHOLD) {
                 newExtras.putFloat(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ, newBandwidth);
+                Log.i(this, "refreshCodec: bandwidth changed; old=%f, new=%f", oldBandwidth,
+                        newBandwidth);
                 changed = true;
             }
         } else {
@@ -1655,6 +1726,12 @@
         }
 
         if (changed) {
+            Log.i(this, "refreshCodec: Codec:"
+                    + newExtras.getInt(Connection.EXTRA_AUDIO_CODEC, Connection.AUDIO_CODEC_NONE)
+                    + ", Bitrate:"
+                    + newExtras.getFloat(Connection.EXTRA_AUDIO_CODEC_BITRATE_KBPS, 0.0f)
+                    + ", Bandwidth:"
+                    + newExtras.getFloat(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ, 0.0f));
             putTelephonyExtras(newExtras);
         }
     }
@@ -1986,20 +2063,21 @@
     }
 
     /**
+     * Sets whether to treat this call as an emergency call or not.
+     * @param shouldTreatAsEmergencyCall
+     */
+    @VisibleForTesting
+    public void setShouldTreatAsEmergencyCall(boolean shouldTreatAsEmergencyCall) {
+        mTreatAsEmergencyCall = shouldTreatAsEmergencyCall;
+    }
+
+    /**
      * Un-sets the underlying radio connection.
      */
     void clearOriginalConnection() {
         if (mOriginalConnection != null) {
             if (getPhone() != null) {
-                getPhone().unregisterForPreciseCallStateChanged(mHandler);
-                getPhone().unregisterForRingbackTone(mHandler);
-                getPhone().unregisterForHandoverStateChanged(mHandler);
-                getPhone().unregisterForRedialConnectionChanged(mHandler);
-                getPhone().unregisterForDisconnect(mHandler);
-                getPhone().unregisterForSuppServiceNotification(mHandler);
-                getPhone().unregisterForOnHoldTone(mHandler);
-                getPhone().unregisterForInCallVoicePrivacyOn(mHandler);
-                getPhone().unregisterForInCallVoicePrivacyOff(mHandler);
+                unregisterForCallEvents(getPhone());
             }
             mOriginalConnection.removePostDialListener(mPostDialListener);
             mOriginalConnection.removeListener(mOriginalConnectionListener);
@@ -2007,6 +2085,18 @@
         }
     }
 
+    public void unregisterForCallEvents(Phone phone) {
+        phone.unregisterForPreciseCallStateChanged(mHandler);
+        phone.unregisterForRingbackTone(mHandler);
+        phone.unregisterForHandoverStateChanged(mHandler);
+        phone.unregisterForRedialConnectionChanged(mHandler);
+        phone.unregisterForDisconnect(mHandler);
+        phone.unregisterForSuppServiceNotification(mHandler);
+        phone.unregisterForOnHoldTone(mHandler);
+        phone.unregisterForInCallVoicePrivacyOn(mHandler);
+        phone.unregisterForInCallVoicePrivacyOff(mHandler);
+    }
+
     protected void hangup(int telephonyDisconnectCode) {
         if (mOriginalConnection != null) {
             mHangupDisconnectCause = telephonyDisconnectCode;
@@ -2317,12 +2407,18 @@
                                     + " -> " + mHangupDisconnectCause);
                             disconnectCause = mHangupDisconnectCause;
                         }
+                        ImsReasonInfo imsReasonInfo = null;
+                        if (isImsConnection()) {
+                            ImsPhoneConnection imsPhoneConnection =
+                                    (ImsPhoneConnection) mOriginalConnection;
+                            imsReasonInfo = imsPhoneConnection.getImsReasonInfo();
+                        }
                         setTelephonyConnectionDisconnected(
                                 DisconnectCauseUtil.toTelecomDisconnectCause(
                                         disconnectCause,
                                         preciseDisconnectCause,
                                         mOriginalConnection.getVendorDisconnectCause(),
-                                        getPhone().getPhoneId()));
+                                        getPhone().getPhoneId(), imsReasonInfo));
                         close();
                     }
                     break;
@@ -2331,7 +2427,7 @@
             }
 
             if (mCommunicator != null) {
-                mCommunicator.onStateChanged(this, getState());
+                mCommunicator.onStateChanged(getTelecomCallId(), getState());
             }
         }
     }
@@ -2785,7 +2881,7 @@
     }
 
     private void updateStatusHints() {
-        if (isWifi() && getPhone() != null) {
+        if (isWifi() && !isCrossSimCall() && getPhone() != null) {
             int labelId = isValidRingingCall()
                     ? R.string.status_hint_label_incoming_wifi_call
                     : R.string.status_hint_label_wifi_call;
@@ -3114,7 +3210,7 @@
     }
 
     /**
-     * Set this {@link TelephonyConnection} to a held state.
+     * Set this {@link TelephonyConnection} to a disconnected state.
      * <p>
      * Note: This should be used instead of
      * {@link #setDisconnected(android.telecom.DisconnectCause)} to ensure listeners are notified.
@@ -3264,42 +3360,87 @@
     private void maybeConfigureDeviceToDeviceCommunication() {
         if (!getPhone().getContext().getResources().getBoolean(
                 R.bool.config_use_device_to_device_communication)) {
-            Log.d(this, "maybeConfigureDeviceToDeviceCommunication: not using D2D.");
+            Log.i(this, "maybeConfigureDeviceToDeviceCommunication: not using D2D.");
+            notifyD2DAvailabilityChanged(false);
             return;
         }
         if (!isImsConnection()) {
-            Log.d(this, "maybeConfigureDeviceToDeviceCommunication: not an IMS connection.");
+            Log.i(this, "maybeConfigureDeviceToDeviceCommunication: not an IMS connection.");
+            if (mCommunicator != null) {
+                mCommunicator = null;
+            }
+            notifyD2DAvailabilityChanged(false);
             return;
         }
-        // Implement abstracted out RTP functionality the RTP transport depends on.
-        RtpAdapter rtpAdapter = new RtpAdapter() {
-            @Override
-            public Set<RtpHeaderExtensionType> getAcceptedRtpHeaderExtensions() {
-                if (!isImsConnection()) {
-                    return Collections.EMPTY_SET;
-                }
-                ImsPhoneConnection originalConnection =
-                        (ImsPhoneConnection) mOriginalConnection;
-                return originalConnection.getAcceptedRtpHeaderExtensions();
-            }
+        if (mTreatAsEmergencyCall || mIsNetworkIdentifiedEmergencyCall) {
+            Log.i(this, "maybeConfigureDeviceToDeviceCommunication: emergency call; no D2D");
+            notifyD2DAvailabilityChanged(false);
+            return;
+        }
 
-            @Override
-            public void sendRtpHeaderExtensions(
-                    @NonNull Set<RtpHeaderExtension> rtpHeaderExtensions) {
-                if (!isImsConnection()) {
-                    Log.w(TelephonyConnection.this, "sendRtpHeaderExtensions: not an ims conn.");
+        ArrayList<TransportProtocol> supportedTransports = new ArrayList<>(2);
+
+        if (supportsD2DUsingRtp()) {
+            Log.i(this, "maybeConfigureDeviceToDeviceCommunication: carrier supports RTP.");
+            // Implement abstracted out RTP functionality the RTP transport depends on.
+            RtpAdapter rtpAdapter = new RtpAdapter() {
+                @Override
+                public Set<RtpHeaderExtensionType> getAcceptedRtpHeaderExtensions() {
+                    ImsPhoneConnection originalConnection =
+                            (ImsPhoneConnection) mOriginalConnection;
+                    return originalConnection.getAcceptedRtpHeaderExtensions();
                 }
-                Log.d(TelephonyConnection.this, "sendRtpHeaderExtensions: sending %d messages",
-                        rtpHeaderExtensions.size());
+
+                @Override
+                public void sendRtpHeaderExtensions(
+                        @NonNull Set<RtpHeaderExtension> rtpHeaderExtensions) {
+                    Log.i(TelephonyConnection.this, "sendRtpHeaderExtensions: sending: %s",
+                            rtpHeaderExtensions.stream()
+                                    .map(r -> r.toString())
+                                    .collect(Collectors.joining(",")));
+                    ImsPhoneConnection originalConnection =
+                            (ImsPhoneConnection) mOriginalConnection;
+                    originalConnection.sendRtpHeaderExtensions(rtpHeaderExtensions);
+                }
+            };
+            mRtpTransport = new RtpTransport(rtpAdapter, null /* TODO: not needed yet */, mHandler,
+                    supportsSdpNegotiationOfRtpHeaderExtensions());
+            supportedTransports.add(mRtpTransport);
+        }
+        if (supportsD2DUsingDtmf()) {
+            Log.i(this, "maybeConfigureDeviceToDeviceCommunication: carrier supports DTMF.");
+            DtmfAdapter dtmfAdapter = digit -> {
+                Log.i(TelephonyConnection.this, "sendDtmf: send digit %c", digit);
                 ImsPhoneConnection originalConnection =
                         (ImsPhoneConnection) mOriginalConnection;
-                originalConnection.sendRtpHeaderExtensions(rtpHeaderExtensions);
-            }
-        };
-        mRtpTransport = new RtpTransport(rtpAdapter, null /* TODO: not needed yet */, mHandler);
-        mCommunicator = new Communicator(List.of(mRtpTransport), this);
-        mD2DCallStateAdapter = new D2DCallStateAdapter(mCommunicator);
-        addTelephonyConnectionListener(mD2DCallStateAdapter);
+                Message dtmfComplete = mHandler.obtainMessage(MSG_DTMF_DONE);
+                dtmfComplete.replyTo = mHandlerMessenger;
+                originalConnection.getImsCall().sendDtmf(digit, dtmfComplete);
+            };
+            ContentResolver cr = getPhone().getContext().getContentResolver();
+            mDtmfTransport = new DtmfTransport(dtmfAdapter, new Timeouts.Adapter(cr),
+                    Executors.newSingleThreadScheduledExecutor());
+            supportedTransports.add(mDtmfTransport);
+        }
+        if (supportedTransports.size() > 0) {
+            mCommunicator = new Communicator(supportedTransports, this);
+            mD2DCallStateAdapter = new D2DCallStateAdapter(mCommunicator);
+            addTelephonyConnectionListener(mD2DCallStateAdapter);
+        } else {
+            Log.i(this, "maybeConfigureDeviceToDeviceCommunication: no transports; disabled.");
+            notifyD2DAvailabilityChanged(false);
+        }
+    }
+
+    /**
+     * Notifies upper layers of the availability of D2D communication.
+     * @param isAvailable {@code true} if D2D is available, {@code false} otherwise.
+     */
+    private void notifyD2DAvailabilityChanged(boolean isAvailable) {
+        Bundle extras = new Bundle();
+        extras.putBoolean(Connection.EXTRA_IS_DEVICE_TO_DEVICE_COMMUNICATION_AVAILABLE,
+                isAvailable);
+        putTelephonyExtras(extras);
     }
 
     /**
@@ -3317,18 +3458,60 @@
     @Override
     public void onMessagesReceived(@NonNull Set<Communicator.Message> messages) {
         Log.i(this, "onMessagesReceived: got d2d messages: %s", messages);
-        // TODO: Actually do something WITH the messages.
-
-        // TODO: Remove this prior to launch.
-        // This is just here for debug purposes; send as a connection event so that it
-        // will be output in the Telecom logs.
+        // Send connection events up to Telecom so that we can relay the messages to a valid
+        // CallDiagnosticService which is bound.
         for (Communicator.Message msg : messages) {
-            sendConnectionEvent("D2D_" + Communicator.messageToString(msg.getType())
-                + "_" + Communicator.valueToString(msg.getType(), msg.getValue()), null);
+            Integer dcMsgType = MessageTypeAndValueHelper.MSG_TYPE_TO_DC_MSG_TYPE.getValue(
+                    msg.getType());
+            if (dcMsgType == null) {
+                // Invalid msg type, skip.
+                continue;
+            }
+
+            Integer dcMsgValue;
+            switch (msg.getType()) {
+                case CallDiagnostics.MESSAGE_CALL_AUDIO_CODEC:
+                    dcMsgValue = MessageTypeAndValueHelper.CODEC_TO_DC_CODEC.getValue(
+                            msg.getValue());
+                    break;
+                case CallDiagnostics.MESSAGE_CALL_NETWORK_TYPE:
+                    dcMsgValue = MessageTypeAndValueHelper.RAT_TYPE_TO_DC_NETWORK_TYPE.getValue(
+                            msg.getValue());
+                    break;
+                case CallDiagnostics.MESSAGE_DEVICE_BATTERY_STATE:
+                    dcMsgValue = MessageTypeAndValueHelper.BATTERY_STATE_TO_DC_BATTERY_STATE
+                            .getValue(msg.getValue());
+                    break;
+                case CallDiagnostics.MESSAGE_DEVICE_NETWORK_COVERAGE:
+                    dcMsgValue = MessageTypeAndValueHelper.COVERAGE_TO_DC_COVERAGE
+                            .getValue(msg.getValue());
+                    break;
+                default:
+                    Log.w(this, "onMessagesReceived: msg=%d - invalid msg", msg.getValue());
+                    continue;
+            }
+            if (dcMsgValue == null) {
+                Log.w(this, "onMessagesReceived: msg=%d/%d - invalid msg value", msg.getType(),
+                        msg.getValue());
+                continue;
+            }
+            Bundle extras = new Bundle();
+            extras.putInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE, dcMsgType);
+            extras.putInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE, dcMsgValue);
+            sendConnectionEvent(Connection.EVENT_DEVICE_TO_DEVICE_MESSAGE, extras);
         }
     }
 
     /**
+     * Handles report from {@link Communicator} when the availability of D2D changes.
+     * @param isAvailable {@code true} if D2D is available, {@code false} if unavailable.
+     */
+    @Override
+    public void onD2DAvailabilitychanged(boolean isAvailable) {
+        notifyD2DAvailabilityChanged(isAvailable);
+    }
+
+    /**
      * Called by a {@link ConnectionService} to notify Telecom that a {@link Conference#onMerge()}
      * operation has started.
      */
@@ -3504,4 +3687,82 @@
     public TelecomAccountRegistry getTelecomAccountRegistry(Context context) {
         return TelecomAccountRegistry.getInstance(context);
     }
+
+    /**
+     * @return {@code true} if the carrier supports D2D using RTP header extensions, {@code false}
+     * otherwise.
+     */
+    private boolean supportsD2DUsingRtp() {
+        PersistableBundle b = getCarrierConfig();
+        return b != null && b.getBoolean(
+                CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL);
+    }
+
+    /**
+     * @return {@code true} if the carrier supports D2D using DTMF digits, {@code false} otherwise.
+     */
+    private boolean supportsD2DUsingDtmf() {
+        PersistableBundle b = getCarrierConfig();
+        return b != null && b.getBoolean(
+                CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL);
+    }
+
+    /**
+     * @return {@code true} if the carrier supports using SDP negotiation for the RTP header
+     * extensions used in D2D comms, {@code false} otherwise.
+     */
+    private boolean supportsSdpNegotiationOfRtpHeaderExtensions() {
+        PersistableBundle b = getCarrierConfig();
+        return b != null && b.getBoolean(
+                CarrierConfigManager
+                        .KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL);
+    }
+
+    /**
+     * Handles a device to device message which a {@link CallDiagnostics} wishes to send.
+     * @param extras the call event extras bundle.
+     */
+    private void handleOutgoingDeviceToDeviceMessage(Bundle extras) {
+        int messageType = extras.getInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE);
+        int messageValue = extras.getInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE);
+
+        Integer internalMessageValue;
+        switch (messageType) {
+            case CallDiagnostics.MESSAGE_CALL_AUDIO_CODEC:
+                internalMessageValue = MessageTypeAndValueHelper.CODEC_TO_DC_CODEC.getKey(
+                        messageValue);
+                break;
+            case CallDiagnostics.MESSAGE_CALL_NETWORK_TYPE:
+                internalMessageValue = MessageTypeAndValueHelper.RAT_TYPE_TO_DC_NETWORK_TYPE.getKey(
+                        messageValue);
+                break;
+            case CallDiagnostics.MESSAGE_DEVICE_BATTERY_STATE:
+                internalMessageValue = MessageTypeAndValueHelper.BATTERY_STATE_TO_DC_BATTERY_STATE
+                        .getKey(messageValue);
+                break;
+            case CallDiagnostics.MESSAGE_DEVICE_NETWORK_COVERAGE:
+                internalMessageValue = MessageTypeAndValueHelper.COVERAGE_TO_DC_COVERAGE
+                        .getKey(messageValue);
+                break;
+            default:
+                Log.w(this, "handleOutgoingDeviceToDeviceMessage: msg=%d - invalid msg",
+                        messageType);
+                return;
+        }
+        Integer internalMessageType = MessageTypeAndValueHelper.MSG_TYPE_TO_DC_MSG_TYPE.getKey(
+                messageType);
+        if (internalMessageValue == null) {
+            Log.w(this, "handleOutgoingDeviceToDeviceMessage: msg=%d/%d - invalid value",
+                    messageType, messageValue);
+            return;
+        }
+
+        if (mCommunicator != null) {
+            Log.w(this, "handleOutgoingDeviceToDeviceMessage: msg=%d/%d - sending",
+                    internalMessageType, internalMessageValue);
+            Set<Communicator.Message> set = new ArraySet<>();
+            set.add(new Communicator.Message(internalMessageType, internalMessageValue));
+            mCommunicator.sendMessages(set);
+        }
+    }
 }
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 219c782..2e82743 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -28,9 +28,6 @@
 import android.content.IntentFilter;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
 import android.os.ParcelUuid;
 import android.telecom.Conference;
 import android.telecom.Connection;
@@ -84,9 +81,10 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Queue;
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
 import java.util.regex.Pattern;
 
 import javax.annotation.Nullable;
@@ -95,7 +93,7 @@
  * Service for making GSM and CDMA connections.
  */
 public class TelephonyConnectionService extends ConnectionService {
-
+    private static final String LOG_TAG = TelephonyConnectionService.class.getSimpleName();
     // Timeout before we continue with the emergency call without waiting for DDS switch response
     // from the modem.
     private static final int DEFAULT_DATA_SWITCH_TIMEOUT_MS = 1000;
@@ -175,8 +173,6 @@
     // destroyed.
     @VisibleForTesting
     public Pair<WeakReference<TelephonyConnection>, Queue<Phone>> mEmergencyRetryCache;
-    private Handler mDdsSwitchHandler;
-    private HandlerThread mHandlerThread;
     private DeviceState mDeviceState = new DeviceState();
 
     /**
@@ -363,27 +359,6 @@
     };
 
     /**
-     * Factory for Handler creation in order to remove flakiness during t esting.
-     */
-    @VisibleForTesting
-    public interface HandlerFactory {
-        HandlerThread createHandlerThread(String name);
-        Handler createHandler(Looper looper);
-    }
-
-    private HandlerFactory mHandlerFactory = new HandlerFactory() {
-        @Override
-        public HandlerThread createHandlerThread(String name) {
-            return new HandlerThread(name);
-        }
-
-        @Override
-        public Handler createHandler(Looper looper) {
-            return new Handler(looper);
-        }
-    };
-
-    /**
      * DisconnectCause depends on PhoneGlobals in order to get a system context. Mock out
      * dependency for testing.
      */
@@ -474,14 +449,6 @@
     }
 
     /**
-     * Override Handler creation factory for testing.
-     */
-    @VisibleForTesting
-    public void setHandlerFactory(HandlerFactory handlerFactory) {
-        mHandlerFactory = handlerFactory;
-    }
-
-    /**
      * Override DisconnectCause creation for testing.
      */
     @VisibleForTesting
@@ -532,16 +499,11 @@
         IntentFilter intentFilter = new IntentFilter(
                 TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED);
         registerReceiver(mTtyBroadcastReceiver, intentFilter);
-        mHandlerThread = mHandlerFactory.createHandlerThread("DdsSwitchHandlerThread");
-        mHandlerThread.start();
-        Looper looper = mHandlerThread.getLooper();
-        mDdsSwitchHandler = mHandlerFactory.createHandler(looper);
     }
 
     @Override
     public boolean onUnbind(Intent intent) {
         unregisterReceiver(mTtyBroadcastReceiver);
-        mHandlerThread.quitSafely();
         return super.onUnbind(intent);
     }
 
@@ -875,14 +837,9 @@
             } else {
                 final Connection resultConnection = getTelephonyConnection(request, numberToDial,
                         true, handle, phone);
-                mDdsSwitchHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        boolean result = delayDialForDdsSwitch(phone);
-                        Log.i(this,
-                                "onCreateOutgoingConn - delayDialForDdsSwitch result = " + result);
+                delayDialForDdsSwitch(phone, (result) -> {
+                    Log.i(this, "onCreateOutgoingConn - delayDialForDdsSwitch result = " + result);
                         placeOutgoingConnection(request, resultConnection, phone);
-                    }
                 });
                 return resultConnection;
             }
@@ -964,18 +921,14 @@
                 adjustAndPlaceOutgoingConnection(phone, originalConnection, request, numberToDial,
                         handle, originalPhoneType, false);
             } else {
-                mDdsSwitchHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        boolean result = delayDialForDdsSwitch(phone);
-                        Log.i(this, "handleOnComplete - delayDialForDdsSwitch result = " + result);
-                        adjustAndPlaceOutgoingConnection(phone, originalConnection, request,
-                                numberToDial, handle, originalPhoneType, true);
-                        mIsEmergencyCallPending = false;
-                    }
+                delayDialForDdsSwitch(phone, result -> {
+                    Log.i(this, "handleOnComplete - delayDialForDdsSwitch "
+                            + "result = " + result);
+                    adjustAndPlaceOutgoingConnection(phone, originalConnection, request,
+                            numberToDial, handle, originalPhoneType, true);
+                    mIsEmergencyCallPending = false;
                 });
             }
-
         } else {
             Log.w(this, "onCreateOutgoingConnection, failed to turn on radio");
             closeOrDestroyConnection(originalConnection,
@@ -1258,10 +1211,15 @@
                 createConnectionFor(phone, originalConnection, false /* isOutgoing */,
                         request.getAccountHandle(), request.getTelecomCallId(),
                         request.isAdhocConferenceCall());
+
         handleIncomingRtt(request, originalConnection);
         if (connection == null) {
             return Connection.createCanceledConnection();
         } else {
+            // Add extra to call if answering this incoming call would cause an in progress call on
+            // another subscription to be disconnected.
+            maybeIndicateAnsweringWillDisconnect(connection, request.getAccountHandle());
+
             connection.setTtyEnabled(mDeviceState.isTtyModeEnabled(getApplicationContext()));
             return connection;
         }
@@ -1600,7 +1558,7 @@
         Phone imsPhone = phone.getImsPhone();
 
         return imsPhone != null
-                && (imsPhone.isVolteEnabled() || imsPhone.isWifiCallingEnabled())
+                && (imsPhone.isVoiceOverCellularImsEnabled() || imsPhone.isWifiCallingEnabled())
                 && (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE);
     }
 
@@ -1728,7 +1686,7 @@
                     });
         }
 
-        com.android.internal.telephony.Connection originalConnection = null;
+        final com.android.internal.telephony.Connection originalConnection;
         try {
             if (phone != null) {
                 EmergencyNumber emergencyNumber =
@@ -1770,14 +1728,18 @@
                         }
                     }
                 }
+                connection.registerForCallEvents(phone);
                 originalConnection = phone.dial(number, new ImsPhone.ImsDialArgs.Builder()
                         .setVideoState(videoState)
                         .setIntentExtras(extras)
                         .setRttTextStream(connection.getRttTextStream())
                         .build());
+            } else {
+                originalConnection = null;
             }
         } catch (CallStateException e) {
             Log.e(this, e, "placeOutgoingConnection, phone.dial exception: " + e);
+            connection.unregisterForCallEvents(phone);
             handleCallStateException(e, connection, phone);
             return;
         }
@@ -1800,12 +1762,17 @@
                 startActivity(intent);
             }
             Log.d(this, "placeOutgoingConnection, phone.dial returned null");
+            connection.unregisterForCallEvents(phone);
             connection.setTelephonyConnectionDisconnected(
                     mDisconnectCauseFactory.toTelecomDisconnectCause(telephonyDisconnectCause,
                             "Connection is null", phone.getPhoneId()));
             connection.close();
         } else {
-            connection.setOriginalConnection(originalConnection);
+            getMainThreadHandler().post(() -> {
+                if (connection.getOriginalConnection() == null) {
+                    connection.setOriginalConnection(originalConnection);
+                }
+            });
         }
     }
 
@@ -1963,18 +1930,32 @@
     /**
      * If needed, block until the the default data is is switched for outgoing emergency call, or
      * timeout expires.
+     * @param phone The Phone to switch the DDS on.
+     * @param completeConsumer The consumer to call once the default data subscription has been
+     *                         switched, provides {@code true} result if the switch happened
+     *                         successfully or {@code false} if the operation timed out/failed.
      */
-    private boolean delayDialForDdsSwitch(Phone phone) {
+    private void delayDialForDdsSwitch(Phone phone, Consumer<Boolean> completeConsumer) {
         if (phone == null) {
-            return true;
+            // Do not block indefinitely.
+            completeConsumer.accept(false);
         }
         try {
-            return possiblyOverrideDefaultDataForEmergencyCall(phone).get(
-                    DEFAULT_DATA_SWITCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            // Waiting for PhoneSwitcher to complete the operation.
+            CompletableFuture<Boolean> future = possiblyOverrideDefaultDataForEmergencyCall(phone);
+            // In the case that there is an issue or bug in PhoneSwitcher logic, do not wait
+            // indefinitely for the future to complete. Instead, set a timeout that will complete
+            // the future as to not block the outgoing call indefinitely.
+            CompletableFuture<Boolean> timeout = new CompletableFuture<>();
+            phone.getContext().getMainThreadHandler().postDelayed(
+                    () -> timeout.complete(false), DEFAULT_DATA_SWITCH_TIMEOUT_MS);
+            // Also ensure that the Consumer is completed on the main thread.
+            future.acceptEitherAsync(timeout, completeConsumer,
+                    phone.getContext().getMainExecutor());
         } catch (Exception e) {
             Log.w(this, "delayDialForDdsSwitch - exception= "
                     + e.getMessage());
-            return false;
+
         }
     }
 
@@ -2524,18 +2505,42 @@
        getAllConnections().stream()
                .filter(f -> f instanceof TelephonyConnection)
                .forEach(t -> {
-                        TelephonyConnection tc = (TelephonyConnection) t;
-                        Communicator c = tc.getCommunicator();
-                        if (c == null) {
-                            Log.w(this, "sendTestDeviceToDeviceMessage: D2D not enabled");
-                            return;
-                        }
+                   TelephonyConnection tc = (TelephonyConnection) t;
+                   if (!tc.isImsConnection()) {
+                       Log.w(this, "sendTestDeviceToDeviceMessage: not an IMS connection");
+                       return;
+                   }
+                   Communicator c = tc.getCommunicator();
+                   if (c == null) {
+                       Log.w(this, "sendTestDeviceToDeviceMessage: D2D not enabled");
+                       return;
+                   }
 
-                        c.sendMessages(new HashSet<Communicator.Message>() {{
-                            add(new Communicator.Message(message, value));
-                        }});
+                   c.sendMessages(new HashSet<Communicator.Message>() {{
+                       add(new Communicator.Message(message, value));
+                   }});
 
-       });
+               });
+    }
+
+    /**
+     * Overrides the current D2D transport, forcing the specified one to be active.  Used for test.
+     * @param transport The class simple name of the transport to make active.
+     */
+    public void setActiveDeviceToDeviceTransport(@NonNull String transport) {
+        getAllConnections().stream()
+                .filter(f -> f instanceof TelephonyConnection)
+                .forEach(t -> {
+                    TelephonyConnection tc = (TelephonyConnection) t;
+                    Communicator c = tc.getCommunicator();
+                    if (c == null) {
+                        Log.w(this, "setActiveDeviceToDeviceTransport: D2D not enabled");
+                        return;
+                    }
+                    Log.i(this, "setActiveDeviceToDeviceTransport: callId=%s, set to: %s",
+                            tc.getTelecomCallId(), transport);
+                    c.setTransportActive(transport);
+                });
     }
 
     private PhoneAccountHandle adjustAccountHandle(Phone phone,
@@ -2553,4 +2558,79 @@
         }
         return origAccountHandle;
     }
+
+    /**
+     * For the passed in incoming {@link TelephonyConnection}, add
+     * {@link Connection#EXTRA_ANSWERING_DROPS_FG_CALL} if there are ongoing calls on another
+     * subscription (ie phone account handle) than the one passed in.
+     * @param connection The connection.
+     * @param phoneAccountHandle The {@link PhoneAccountHandle} the incoming call originated on;
+     *                           this is passed in because
+     *                           {@link Connection#getPhoneAccountHandle()} is not set until after
+     *                           {@link ConnectionService#onCreateIncomingConnection(
+     *                           PhoneAccountHandle, ConnectionRequest)} returns.
+     */
+    public void maybeIndicateAnsweringWillDisconnect(@NonNull TelephonyConnection connection,
+            @NonNull PhoneAccountHandle phoneAccountHandle) {
+        if (isCallPresentOnOtherSub(phoneAccountHandle)) {
+            Log.i(this, "maybeIndicateAnsweringWillDisconnect; answering call %s will cause a call "
+                    + "on another subscription to drop.", connection.getTelecomCallId());
+            Bundle extras = new Bundle();
+            extras.putBoolean(Connection.EXTRA_ANSWERING_DROPS_FG_CALL, true);
+            connection.putExtras(extras);
+        }
+    }
+
+    /**
+     * Checks to see if there are calls present on a sub other than the one passed in.
+     * @param incomingHandle The new incoming connection {@link PhoneAccountHandle}
+     */
+    private boolean isCallPresentOnOtherSub(@NonNull PhoneAccountHandle incomingHandle) {
+        return getAllConnections().stream()
+                .filter(c ->
+                        // Exclude multiendpoint calls as they're not on this device.
+                        (c.getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) == 0
+                        // Include any calls not on same sub as current connection.
+                        && !Objects.equals(c.getPhoneAccountHandle(), incomingHandle))
+                .count() > 0;
+    }
+
+    /**
+     * Where there are ongoing calls on another subscription other than the one specified,
+     * disconnect these calls.  This is used where there is an incoming call on one sub, but there
+     * are ongoing calls on another sub which need to be disconnected.
+     * @param incomingHandle The incoming {@link PhoneAccountHandle}.
+     */
+    public void maybeDisconnectCallsOnOtherSubs(@NonNull PhoneAccountHandle incomingHandle) {
+        Log.i(this, "maybeDisconnectCallsOnOtherSubs: check for calls not on %s", incomingHandle);
+        maybeDisconnectCallsOnOtherSubs(getAllConnections(), incomingHandle);
+    }
+
+    /**
+     * Used by {@link #maybeDisconnectCallsOnOtherSubs(PhoneAccountHandle)} to perform call
+     * disconnection.  This method exists as a convenience so that it is possible to unit test
+     * the core functionality.
+     * @param connections the calls to check.
+     * @param incomingHandle the incoming handle.
+     */
+    @VisibleForTesting
+    public static void maybeDisconnectCallsOnOtherSubs(@NonNull Collection<Connection> connections,
+            @NonNull PhoneAccountHandle incomingHandle) {
+        connections.stream()
+                .filter(c ->
+                        // Exclude multiendpoint calls as they're not on this device.
+                        (c.getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) == 0
+                                // Include any calls not on same sub as current connection.
+                                && !Objects.equals(c.getPhoneAccountHandle(), incomingHandle))
+                .forEach(c -> {
+                    if (c instanceof TelephonyConnection) {
+                        TelephonyConnection tc = (TelephonyConnection) c;
+                        if (!tc.shouldTreatAsEmergencyCall()) {
+                            Log.i(LOG_TAG, "maybeDisconnectCallsOnOtherSubs: disconnect %s due to "
+                                    + "incoming call on other sub.", tc.getTelecomCallId());
+                            tc.onDisconnect();
+                        }
+                    }
+                });
+    }
 }
diff --git a/src/com/android/services/telephony/rcs/DelegateBinderStateManager.java b/src/com/android/services/telephony/rcs/DelegateBinderStateManager.java
index 9d2c5d6..1a016ee 100644
--- a/src/com/android/services/telephony/rcs/DelegateBinderStateManager.java
+++ b/src/com/android/services/telephony/rcs/DelegateBinderStateManager.java
@@ -19,6 +19,7 @@
 import android.telephony.ims.DelegateRegistrationState;
 import android.telephony.ims.DelegateRequest;
 import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConfiguration;
 import android.telephony.ims.SipDelegateConnection;
 import android.telephony.ims.SipDelegateImsConfiguration;
 import android.telephony.ims.SipDelegateManager;
@@ -52,6 +53,11 @@
          * The SipDelegate has notified telephony that the IMS configuration has changed.
          */
         void onImsConfigurationChanged(SipDelegateImsConfiguration config);
+
+        /**
+         * The SipDelegate has notified telephony that the IMS configuration has changed.
+         */
+        void onConfigurationChanged(SipDelegateConfiguration config);
     }
 
     /** Allow for mocks to be created for testing. */
diff --git a/src/com/android/services/telephony/rcs/DelegateStateTracker.java b/src/com/android/services/telephony/rcs/DelegateStateTracker.java
index 18ad98e..321c7ba 100644
--- a/src/com/android/services/telephony/rcs/DelegateStateTracker.java
+++ b/src/com/android/services/telephony/rcs/DelegateStateTracker.java
@@ -19,6 +19,7 @@
 import android.os.RemoteException;
 import android.telephony.ims.DelegateRegistrationState;
 import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConfiguration;
 import android.telephony.ims.SipDelegateImsConfiguration;
 import android.telephony.ims.aidl.ISipDelegate;
 import android.telephony.ims.aidl.ISipDelegateConnectionStateCallback;
@@ -159,6 +160,20 @@
         }
     }
 
+    /**
+     * THe underlying SipDelegate has reported that the IMS configuration has changed.
+     * @param config The config to be sent to the IMS application.
+     */
+    @Override
+    public void onConfigurationChanged(SipDelegateConfiguration config) {
+        logi("onImsConfigurationChanged: Sending new IMS configuration.");
+        try {
+            mAppStateCallback.onConfigurationChanged(config);
+        } catch (RemoteException e) {
+            logw("onImsConfigurationChanged: IMS application is dead: " + e);
+        }
+    }
+
     /** Write state about this tracker into the PrintWriter to be included in the dumpsys */
     public void dump(PrintWriter printWriter) {
         printWriter.println("Last reg state: " + mLastRegState);
diff --git a/src/com/android/services/telephony/rcs/MessageTransportStateTracker.java b/src/com/android/services/telephony/rcs/MessageTransportStateTracker.java
deleted file mode 100644
index c42472d..0000000
--- a/src/com/android/services/telephony/rcs/MessageTransportStateTracker.java
+++ /dev/null
@@ -1,479 +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.os.Binder;
-import android.os.RemoteException;
-import android.telephony.ims.DelegateMessageCallback;
-import android.telephony.ims.DelegateRegistrationState;
-import android.telephony.ims.FeatureTagState;
-import android.telephony.ims.SipDelegateImsConfiguration;
-import android.telephony.ims.SipDelegateManager;
-import android.telephony.ims.SipMessage;
-import android.telephony.ims.aidl.ISipDelegate;
-import android.telephony.ims.aidl.ISipDelegateMessageCallback;
-import android.telephony.ims.stub.SipDelegate;
-import android.util.LocalLog;
-import android.util.Log;
-
-import java.io.PrintWriter;
-import java.util.Set;
-import java.util.concurrent.Executor;
-import java.util.function.Consumer;
-
-/**
- * Tracks the SIP message path both from the IMS application to the SipDelegate and from the
- * SipDelegate back to the IMS Application.
- * <p>
- * Responsibilities include:
- * 1) Queue incoming and outgoing SIP messages and deliver to IMS application and SipDelegate in
- *        order. If there is an error delivering the message, notify the caller.
- * 2) TODO Perform basic validation of outgoing messages.
- * 3) TODO Record the status of ongoing SIP Dialogs and trigger the completion of pending
- *         consumers when they are finished or call closeDialog to clean up the SIP
- *         dialogs that did not complete within the allotted timeout time.
- * <p>
- * Note: This handles incoming binder calls, so all calls from other processes should be handled on
- * the provided Executor.
- */
-public class MessageTransportStateTracker implements DelegateBinderStateManager.StateCallback {
-    private static final String TAG = "MessageST";
-
-    /**
-     * Communicates the result of verifying whether a SIP message should be sent based on the
-     * contents of the SIP message as well as if the transport is in an available state for the
-     * intended recipient of the message.
-     */
-    private static class VerificationResult {
-        public static final VerificationResult SUCCESS = new VerificationResult();
-
-        /**
-         * If {@code true}, the requested SIP message has been verified to be sent to the remote. If
-         * {@code false}, the SIP message has failed verification and should not be sent to the
-         * result. The {@link #restrictedReason} field will contain the reason for the verification
-         * failure.
-         */
-        public final boolean isVerified;
-
-        /**
-         * The reason associated with why the SIP message was not verified and generated a
-         * {@code false} result for {@link #isVerified}.
-         */
-        public final int restrictedReason;
-
-        /**
-         * Communicates a verified result of success. Use {@link #SUCCESS} instead.
-         */
-        private VerificationResult() {
-            isVerified = true;
-            restrictedReason = SipDelegateManager.MESSAGE_FAILURE_REASON_UNKNOWN;
-        }
-
-        /**
-         * The result of verifying that the SIP Message should be sent.
-         * @param reason The reason associated with why the SIP message was not verified and
-         *               generated a {@code false} result for {@link #isVerified}.
-         */
-        VerificationResult(@SipDelegateManager.MessageFailureReason int reason) {
-            isVerified = false;
-            restrictedReason = reason;
-        }
-    }
-
-    // SipDelegateConnection(IMS Application) -> SipDelegate(ImsService)
-    private final ISipDelegate.Stub mSipDelegateConnection = new ISipDelegate.Stub() {
-        /**
-         * The IMS application is acknowledging that it has successfully received and processed an
-         * incoming SIP message sent by the SipDelegate in
-         * {@link ISipDelegateMessageCallback#onMessageReceived(SipMessage)}.
-         */
-        @Override
-        public void notifyMessageReceived(String viaTransactionId) {
-            long token = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> {
-                    if (mSipDelegate == null) {
-                        logw("notifyMessageReceived called when SipDelegate is not associated for "
-                                + "transaction id: " + viaTransactionId);
-                        return;
-                    }
-                    try {
-                        // TODO track the SIP Dialogs created/destroyed on the associated
-                        // SipDelegate.
-                        mSipDelegate.notifyMessageReceived(viaTransactionId);
-                    } catch (RemoteException e) {
-                        logw("SipDelegate not available when notifyMessageReceived was called "
-                                + "for transaction id: " + viaTransactionId);
-                    }
-                });
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        /**
-         * The IMS application is acknowledging that it received an incoming SIP message sent by the
-         * SipDelegate in {@link ISipDelegateMessageCallback#onMessageReceived(SipMessage)} but it
-         * was unable to process it.
-         */
-        @Override
-        public void notifyMessageReceiveError(String viaTransactionId, int reason) {
-            long token = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> {
-                    if (mSipDelegate == null) {
-                        logw("notifyMessageReceiveError called when SipDelegate is not associated "
-                                + "for transaction id: " + viaTransactionId);
-                        return;
-                    }
-                    try {
-                        // TODO track the SIP Dialogs created/destroyed on the associated
-                        // SipDelegate.
-                        mSipDelegate.notifyMessageReceiveError(viaTransactionId, reason);
-                    } catch (RemoteException e) {
-                        logw("SipDelegate not available when notifyMessageReceiveError was called "
-                                + "for transaction id: " + viaTransactionId);
-                    }
-                });
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        /**
-         * The IMS application is sending an outgoing SIP message to the SipDelegate to be processed
-         * and sent over the network.
-         */
-        @Override
-        public void sendMessage(SipMessage sipMessage, long configVersion) {
-            long token = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> {
-                    VerificationResult result = verifyOutgoingMessage(sipMessage);
-                    if (!result.isVerified) {
-                        notifyDelegateSendError("Outgoing messages restricted", sipMessage,
-                                result.restrictedReason);
-                        return;
-                    }
-                    try {
-                        // TODO track the SIP Dialogs created/destroyed on the associated
-                        // SipDelegate.
-                        mSipDelegate.sendMessage(sipMessage, configVersion);
-                        logi("sendMessage: message sent - " + sipMessage + ", configVersion: "
-                                + configVersion);
-                    } catch (RemoteException e) {
-                        notifyDelegateSendError("RemoteException: " + e, sipMessage,
-                                SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD);
-                    }
-                });
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        /**
-         * The SipDelegateConnection is requesting that the resources associated with an ongoing SIP
-         * dialog be released as the SIP dialog is now closed.
-         */
-        @Override
-        public void closeDialog(String callId) {
-            long token = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> {
-                    if (mSipDelegate == null) {
-                        logw("closeDialog called when SipDelegate is not associated, callId: "
-                                + callId);
-                        return;
-                    }
-                    try {
-                        // TODO track the SIP Dialogs created/destroyed on the associated
-                        // SipDelegate.
-                        mSipDelegate.closeDialog(callId);
-                    } catch (RemoteException e) {
-                        logw("SipDelegate not available when closeDialog was called "
-                                + "for call id: " + callId);
-                    }
-                });
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-    };
-
-    // SipDelegate(ImsService) -> SipDelegateConnection(IMS Application)
-    private final ISipDelegateMessageCallback.Stub mDelegateConnectionMessageCallback =
-            new ISipDelegateMessageCallback.Stub() {
-        /**
-         * An Incoming SIP Message has been received by the SipDelegate and is being routed
-         * to the IMS application for processing.
-         * <p>
-         * IMS application will call {@link ISipDelegate#notifyMessageReceived(String)} to
-         * acknowledge receipt of this incoming message.
-         */
-        @Override
-        public void onMessageReceived(SipMessage message) {
-            long token = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> {
-                    VerificationResult result = verifyIncomingMessage(message);
-                    if (!result.isVerified) {
-                        notifyAppReceiveError("Incoming messages restricted", message,
-                                result.restrictedReason);
-                        return;
-                    }
-                    try {
-                        // TODO track the SIP Dialogs created/destroyed on the associated
-                        //  SipDelegate.
-                        mAppCallback.onMessageReceived(message);
-                        logi("onMessageReceived: received " + message);
-                    } catch (RemoteException e) {
-                        notifyAppReceiveError("RemoteException: " + e, message,
-                                SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD);
-                    }
-                });
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        /**
-         * An outgoing SIP message sent previously by the SipDelegateConnection to the SipDelegate
-         * using {@link ISipDelegate#sendMessage(SipMessage, int)} as been successfully sent.
-         */
-        @Override
-        public void onMessageSent(String viaTransactionId) {
-            long token = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> {
-                    if (mSipDelegate == null) {
-                        logw("Unexpected state, onMessageSent called when SipDelegate is not "
-                                + "associated");
-                    }
-                    try {
-                        mAppCallback.onMessageSent(viaTransactionId);
-                    } catch (RemoteException e) {
-                        logw("Error sending onMessageSent to SipDelegateConnection, remote not"
-                                + "available for transaction ID: " + viaTransactionId);
-                    }
-                });
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        /**
-         * An outgoing SIP message sent previously by the SipDelegateConnection to the SipDelegate
-         * using {@link ISipDelegate#sendMessage(SipMessage, int)} failed to be sent.
-         */
-        @Override
-        public void onMessageSendFailure(String viaTransactionId, int reason) {
-            long token = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> {
-                    if (mSipDelegate == null) {
-                        logw("Unexpected state, onMessageSendFailure called when SipDelegate is not"
-                                + "associated");
-                    }
-                    try {
-                        mAppCallback.onMessageSendFailure(viaTransactionId, reason);
-                    } catch (RemoteException e) {
-                        logw("Error sending onMessageSendFailure to SipDelegateConnection, remote"
-                                + " not available for transaction ID: " + viaTransactionId);
-                    }
-                });
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-    };
-
-    private final ISipDelegateMessageCallback mAppCallback;
-    private final Executor mExecutor;
-    private final int mSubId;
-    private final LocalLog mLocalLog = new LocalLog(SipTransportController.LOG_SIZE);
-
-    private ISipDelegate mSipDelegate;
-    private Consumer<Boolean> mPendingClosedConsumer;
-    private int mDelegateClosingReason = -1;
-    private int mDelegateClosedReason = -1;
-
-    public MessageTransportStateTracker(int subId, Executor executor,
-            ISipDelegateMessageCallback appMessageCallback) {
-        mSubId = subId;
-        mAppCallback = appMessageCallback;
-        mExecutor = executor;
-    }
-
-    @Override
-    public void onRegistrationStateChanged(DelegateRegistrationState registrationState) {
-        // TODO: integrate registration changes to SipMessage verification checks.
-    }
-
-    @Override
-    public void onImsConfigurationChanged(SipDelegateImsConfiguration config) {
-        // Not needed for this Tracker
-    }
-
-    /**
-     * Open the transport and allow SIP messages to be sent/received on the delegate specified.
-     * @param delegate The delegate connection to send SIP messages to on the ImsService.
-     * @param deniedFeatureTags Feature tags that have been denied. Outgoing SIP messages relating
-     *         to these tags will be denied.
-     */
-    public void openTransport(ISipDelegate delegate, Set<FeatureTagState> deniedFeatureTags) {
-        mSipDelegate = delegate;
-        mDelegateClosingReason = -1;
-        mDelegateClosedReason = -1;
-        // TODO: integrate denied tags to SipMessage verification checks.
-    }
-
-    /** Dump state about this tracker that should be included in the dumpsys */
-    public void dump(PrintWriter printWriter) {
-        printWriter.println("Most recent logs:");
-        mLocalLog.dump(printWriter);
-    }
-
-    /**
-     * @return SipDelegate implementation to be sent to IMS application.
-     */
-    public ISipDelegate getDelegateConnection() {
-        return mSipDelegateConnection;
-    }
-
-    /**
-     * @return MessageCallback implementation to be sent to the ImsService.
-     */
-    public ISipDelegateMessageCallback getMessageCallback() {
-        return mDelegateConnectionMessageCallback;
-    }
-
-    /**
-     * Gradually close all SIP Dialogs by:
-     * 1) denying all new outgoing SIP Dialog requests with the reason specified and
-     * 2) only allowing existing SIP Dialogs to continue.
-     * <p>
-     * This will allow traffic to continue on existing SIP Dialogs until a BYE is sent and the
-     * SIP Dialogs are closed or a timeout is hit and {@link SipDelegate#closeDialog(String)} is
-     * forcefully called on all open SIP Dialogs.
-     * <p>
-     * Any outgoing out-of-dialog traffic on this transport will be denied with the provided reason.
-     * <p>
-     * Incoming out-of-dialog traffic will continue to be set up until the SipDelegate is fully
-     * closed.
-     * @param delegateClosingReason The reason code to return to
-     * {@link DelegateMessageCallback#onMessageSendFailure(String, int)} if a new out-of-dialog SIP
-     *         message is received while waiting for existing Dialogs.
-     * @param closedReason reason to return to new outgoing SIP messages via
-     *         {@link SipDelegate#notifyMessageReceiveError(String, int)} once the transport
-     *         transitions to the fully closed state.
-     * @param resultConsumer The consumer called when the message transport has been closed. It will
-     *         return {@code true} if the procedure completed successfully or {@link false} if the
-     *         transport needed to be closed forcefully due to the application not responding before
-     *         a timeout occurred.
-     */
-    public void closeGracefully(int delegateClosingReason, int closedReason,
-            Consumer<Boolean> resultConsumer) {
-        mDelegateClosingReason = delegateClosingReason;
-        mPendingClosedConsumer = resultConsumer;
-        mExecutor.execute(() -> {
-            // TODO: Track SIP Dialogs and complete when there are no SIP dialogs open anymore or
-            //  the timeout occurs.
-            mPendingClosedConsumer.accept(true);
-            mPendingClosedConsumer = null;
-            closeTransport(closedReason);
-        });
-    }
-
-    /**
-     * Close all ongoing SIP Dialogs immediately and respond to any incoming/outgoing messages with
-     * the provided reason.
-     * @param closedReason The failure reason to provide to incoming/outgoing SIP messages
-     *         if an attempt is made to send/receive a message after this method is called.
-     */
-    public void close(int closedReason) {
-        closeTransport(closedReason);
-    }
-
-    // Clean up all state related to the existing SipDelegate immediately.
-    private void closeTransport(int closedReason) {
-        // TODO: add logic to forcefully close open SIP dialogs once they are being tracked.
-        mSipDelegate = null;
-        if (mPendingClosedConsumer != null) {
-            mExecutor.execute(() -> {
-                logw("closeTransport: transport close forced with pending consumer.");
-                mPendingClosedConsumer.accept(false /*closedGracefully*/);
-                mPendingClosedConsumer = null;
-            });
-        }
-        mDelegateClosingReason = -1;
-        mDelegateClosedReason = closedReason;
-    }
-
-    private VerificationResult verifyOutgoingMessage(SipMessage message) {
-        if (mDelegateClosingReason > -1) {
-            return new VerificationResult(mDelegateClosingReason);
-        }
-        if (mDelegateClosedReason > -1) {
-            return new VerificationResult(mDelegateClosedReason);
-        }
-        if (mSipDelegate == null) {
-            logw("sendMessage called when SipDelegate is not associated." + message);
-            return new VerificationResult(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD);
-        }
-        return VerificationResult.SUCCESS;
-    }
-
-    private VerificationResult verifyIncomingMessage(SipMessage message) {
-        // Do not restrict incoming based on closing reason.
-        if (mDelegateClosedReason > -1) {
-            return new VerificationResult(mDelegateClosedReason);
-        }
-        return VerificationResult.SUCCESS;
-    }
-
-    private void notifyDelegateSendError(String logReason, SipMessage message, int reasonCode) {
-        // TODO parse SipMessage header for viaTransactionId.
-        logw("Error sending SipMessage[id: " + null + ", code: " + reasonCode + "] -> SipDelegate "
-                + "for reason: " + logReason);
-        try {
-            mAppCallback.onMessageSendFailure(null, reasonCode);
-        } catch (RemoteException e) {
-            logw("notifyDelegateSendError, SipDelegate is not available: " + e);
-        }
-    }
-
-    private void notifyAppReceiveError(String logReason, SipMessage message, int reasonCode) {
-        // TODO parse SipMessage header for viaTransactionId.
-        logw("Error sending SipMessage[id: " + null + ", code: " + reasonCode + "] -> "
-                + "SipDelegateConnection for reason: " + logReason);
-        try {
-            mSipDelegate.notifyMessageReceiveError(null, reasonCode);
-        } catch (RemoteException e) {
-            logw("notifyAppReceiveError, SipDelegate is not available: " + e);
-        }
-    }
-
-    private void logi(String log) {
-        Log.w(SipTransportController.LOG_TAG, TAG + "[" + mSubId + "] " + log);
-        mLocalLog.log("[I] " + log);
-    }
-
-    private void logw(String log) {
-        Log.w(SipTransportController.LOG_TAG, TAG + "[" + mSubId + "] " + log);
-        mLocalLog.log("[W] " + log);
-    }
-}
diff --git a/src/com/android/services/telephony/rcs/MessageTransportWrapper.java b/src/com/android/services/telephony/rcs/MessageTransportWrapper.java
new file mode 100644
index 0000000..0d4265a
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/MessageTransportWrapper.java
@@ -0,0 +1,493 @@
+/*
+ * 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.os.Binder;
+import android.os.RemoteException;
+import android.telephony.ims.DelegateMessageCallback;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConfiguration;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
+import android.telephony.ims.stub.SipDelegate;
+import android.util.LocalLog;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.SipMessageParsingUtils;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.phone.RcsProvisioningMonitor;
+import com.android.services.telephony.rcs.validator.ValidationResult;
+
+import java.io.PrintWriter;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.function.Consumer;
+
+/**
+ * Wraps the SIP message path both from the IMS application to the SipDelegate and from the
+ * SipDelegate back to the IMS Application.
+ * <p>
+ * Queues incoming and outgoing SIP messages on an Executor and deliver to IMS application and
+ * SipDelegate in order. If there is an error delivering the message, the caller is notified.
+ * Uses {@link TransportSipMessageValidator} to track ongoing SIP dialogs and verify outgoing
+ * messages.
+ * <p>
+ * Note: This handles incoming binder calls, so all calls from other processes should be handled on
+ * the provided Executor.
+ */
+public class MessageTransportWrapper implements DelegateBinderStateManager.StateCallback {
+    private static final String TAG = "MessageTW";
+
+    // SipDelegateConnection(IMS Application) -> SipDelegate(ImsService)
+    private final ISipDelegate.Stub mSipDelegateConnection = new ISipDelegate.Stub() {
+        /**
+         * The IMS application is acknowledging that it has successfully received and processed an
+         * incoming SIP message sent by the SipDelegate in
+         * {@link ISipDelegateMessageCallback#onMessageReceived(SipMessage)}.
+         */
+        @Override
+        public void notifyMessageReceived(String viaTransactionId) {
+            long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> {
+                    if (mSipDelegate == null) {
+                        logw("notifyMessageReceived called when SipDelegate is not associated for "
+                                + "transaction id: " + viaTransactionId);
+                        return;
+                    }
+                    try {
+                        mSipSessionTracker.acknowledgePendingMessage(viaTransactionId);
+                        mSipDelegate.notifyMessageReceived(viaTransactionId);
+                    } catch (RemoteException e) {
+                        logw("SipDelegate not available when notifyMessageReceived was called "
+                                + "for transaction id: " + viaTransactionId);
+                    }
+                });
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        /**
+         * The IMS application is acknowledging that it received an incoming SIP message sent by the
+         * SipDelegate in {@link ISipDelegateMessageCallback#onMessageReceived(SipMessage)} but it
+         * was unable to process it.
+         */
+        @Override
+        public void notifyMessageReceiveError(String viaTransactionId, int reason) {
+            long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> {
+                    if (mSipDelegate == null) {
+                        logw("notifyMessageReceiveError called when SipDelegate is not associated "
+                                + "for transaction id: " + viaTransactionId);
+                        return;
+                    }
+                    try {
+                        mSipSessionTracker.notifyPendingMessageFailed(viaTransactionId);
+                        mSipDelegate.notifyMessageReceiveError(viaTransactionId, reason);
+                    } catch (RemoteException e) {
+                        logw("SipDelegate not available when notifyMessageReceiveError was called "
+                                + "for transaction id: " + viaTransactionId);
+                    }
+                });
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        /**
+         * The IMS application is sending an outgoing SIP message to the SipDelegate to be processed
+         * and sent over the network.
+         */
+        @Override
+        public void sendMessage(SipMessage sipMessage, long configVersion) {
+            long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> {
+                    ValidationResult result =
+                            mSipSessionTracker.verifyOutgoingMessage(sipMessage, configVersion);
+                    result = maybeOverrideValidationForTesting(result);
+                    if (!result.isValidated) {
+                        notifyDelegateSendError("Outgoing - " + result.logReason,
+                                sipMessage, result.restrictedReason);
+                        return;
+                    }
+                    try {
+                        if (mSipDelegate == null) {
+                            logw("sendMessage called when SipDelegate is not associated."
+                                    + sipMessage);
+                            notifyDelegateSendError("No SipDelegate", sipMessage,
+                                    SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD);
+
+                            return;
+                        }
+                        mSipDelegate.sendMessage(sipMessage, configVersion);
+                    } catch (RemoteException e) {
+                        notifyDelegateSendError("RemoteException: " + e, sipMessage,
+                                SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD);
+                    }
+                });
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        /**
+         * The SipDelegateConnection is requesting that the resources associated with an ongoing SIP
+         * dialog be released as the SIP dialog is now closed.
+         */
+        @Override
+        public void cleanupSession(String callId) {
+            long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> cleanupSessionInternal(callId));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    };
+
+    // SipDelegate(ImsService) -> SipDelegateConnection(IMS Application)
+    private final ISipDelegateMessageCallback.Stub mDelegateConnectionMessageCallback =
+            new ISipDelegateMessageCallback.Stub() {
+        /**
+         * An Incoming SIP Message has been received by the SipDelegate and is being routed
+         * to the IMS application for processing.
+         * <p>
+         * IMS application will call {@link ISipDelegate#notifyMessageReceived(String)} to
+         * acknowledge receipt of this incoming message.
+         */
+        @Override
+        public void onMessageReceived(SipMessage message) {
+            long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> {
+                    ValidationResult result = mSipSessionTracker.verifyIncomingMessage(message);
+                    if (!result.isValidated) {
+                        notifyAppReceiveError("Incoming - " + result.logReason, message,
+                                result.restrictedReason);
+                        return;
+                    }
+                    try {
+                        mAppCallback.onMessageReceived(message);
+                    } catch (RemoteException e) {
+                        notifyAppReceiveError("RemoteException: " + e, message,
+                                SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD);
+                    }
+                });
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        /**
+         * An outgoing SIP message sent previously by the SipDelegateConnection to the SipDelegate
+         * using {@link ISipDelegate#sendMessage(SipMessage, long)} as been successfully sent.
+         */
+        @Override
+        public void onMessageSent(String viaTransactionId) {
+            long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> {
+                    if (mSipDelegate == null) {
+                        logw("Unexpected state, onMessageSent called when SipDelegate is not "
+                                + "associated");
+                    }
+                    try {
+                        mSipSessionTracker.acknowledgePendingMessage(viaTransactionId);
+                        mAppCallback.onMessageSent(viaTransactionId);
+                    } catch (RemoteException e) {
+                        logw("Error sending onMessageSent to SipDelegateConnection, remote not"
+                                + "available for transaction ID: " + viaTransactionId);
+                    }
+                });
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        /**
+         * An outgoing SIP message sent previously by the SipDelegateConnection to the SipDelegate
+         * using {@link ISipDelegate#sendMessage(SipMessage, long)} failed to be sent.
+         */
+        @Override
+        public void onMessageSendFailure(String viaTransactionId, int reason) {
+            long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> {
+                    if (mSipDelegate == null) {
+                        logw("Unexpected state, onMessageSendFailure called when SipDelegate is not"
+                                + "associated");
+                    }
+                    try {
+                        mSipSessionTracker.notifyPendingMessageFailed(viaTransactionId);
+                        mAppCallback.onMessageSendFailure(viaTransactionId, reason);
+                    } catch (RemoteException e) {
+                        logw("Error sending onMessageSendFailure to SipDelegateConnection, remote"
+                                + " not available for transaction ID: " + viaTransactionId);
+                    }
+                });
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    };
+
+    /**
+     * Interface for injecting validator override dependencies for testing.
+     */
+    @VisibleForTesting
+    public interface ValidatorOverride {
+        /**
+         * @return {@code null} if the validation result should not be overridden, {@code true} if
+         * the validation result should always pass, {@code false} if the validation result should
+         * always fail.
+         */
+        Boolean getValidatorOverrideState();
+    }
+
+    private final ValidatorOverride mValidatorOverride;
+    private final ISipDelegateMessageCallback mAppCallback;
+    private final Executor mExecutor;
+    private final int mSubId;
+    private final TransportSipMessageValidator mSipSessionTracker;
+    private final LocalLog mLocalLog = new LocalLog(SipTransportController.LOG_SIZE);
+
+    private ISipDelegate mSipDelegate;
+
+    public MessageTransportWrapper(int subId, ScheduledExecutorService executor,
+            ISipDelegateMessageCallback appMessageCallback) {
+        mSubId = subId;
+        mAppCallback = appMessageCallback;
+        mExecutor = executor;
+        mSipSessionTracker = new TransportSipMessageValidator(subId, executor);
+        mValidatorOverride = () -> RcsProvisioningMonitor.getInstance()
+                .getImsFeatureValidationOverride(mSubId);
+    }
+
+    /**
+     * Mock out dependencies for unit testing.
+     */
+    @VisibleForTesting
+    public MessageTransportWrapper(int subId, ScheduledExecutorService executor,
+            ISipDelegateMessageCallback appMessageCallback,
+            TransportSipMessageValidator sipSessionTracker) {
+        mSubId = subId;
+        mAppCallback = appMessageCallback;
+        mExecutor = executor;
+        mSipSessionTracker = sipSessionTracker;
+        // Remove links to static methods calls querying overrides for testing.
+        mValidatorOverride = () -> null;
+    }
+
+    @Override
+    public void onRegistrationStateChanged(DelegateRegistrationState registrationState) {
+        mSipSessionTracker.onRegistrationStateChanged((callIds) -> {
+            for (String id : callIds)  {
+                cleanupSessionInternal(id);
+            }
+        }, registrationState);
+    }
+
+    @Override
+    public void onImsConfigurationChanged(SipDelegateImsConfiguration config) {
+        mSipSessionTracker.onImsConfigurationChanged(config);
+    }
+
+    @Override
+    public void onConfigurationChanged(SipDelegateConfiguration config) {
+        mSipSessionTracker.onConfigurationChanged(config);
+    }
+
+    /**
+     * Open the transport and allow SIP messages to be sent/received on the delegate specified.
+     * @param delegate The delegate connection to send SIP messages to on the ImsService.
+     * @param supportedFeatureTags Feature tags that are supported. Outgoing SIP messages relating
+     *                             to these tags will be allowed.
+     * @param deniedFeatureTags Feature tags that have been denied. Outgoing SIP messages relating
+     *         to these tags will be denied.
+     */
+    public void openTransport(ISipDelegate delegate, Set<String> supportedFeatureTags,
+            Set<FeatureTagState> deniedFeatureTags) {
+        logi("openTransport: delegate=" + delegate + ", supportedTags=" + supportedFeatureTags
+                + ", deniedTags=" + deniedFeatureTags);
+        mSipSessionTracker.onTransportOpened(supportedFeatureTags, deniedFeatureTags);
+        mSipDelegate = delegate;
+    }
+
+    /** Dump state about this tracker that should be included in the dumpsys */
+    public void dump(PrintWriter printWriter) {
+        IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
+        pw.println("Most recent logs:");
+        mLocalLog.dump(printWriter);
+        pw.println();
+        pw.println("Dialog Tracker:");
+        pw.increaseIndent();
+        mSipSessionTracker.dump(pw);
+        pw.decreaseIndent();
+    }
+
+    /**
+     * @return SipDelegate implementation to be sent to IMS application.
+     */
+    public ISipDelegate getDelegateConnection() {
+        return mSipDelegateConnection;
+    }
+
+    /**
+     * @return The remote application's message callback.
+     */
+    public ISipDelegateMessageCallback getAppMessageCallback() {
+        return mAppCallback;
+    }
+
+    /**
+     * @return MessageCallback implementation to be sent to the ImsService.
+     */
+    public ISipDelegateMessageCallback getMessageCallback() {
+        return mDelegateConnectionMessageCallback;
+    }
+
+    /**
+     * Gradually close all SIP Sessions by:
+     * 1) denying all new outgoing SIP Dialog requests with the reason specified and
+     * 2) only allowing existing SIP Sessions to continue.
+     * <p>
+     * This will allow traffic to continue on existing SIP Sessions until a BYE is sent and the
+     * corresponding SIP Dialogs are closed or a timeout is hit and
+     * {@link SipDelegate#cleanupSession(String)} (String)} is forcefully called on all open SIP
+     * sessions.
+     * <p>
+     * Any outgoing out-of-dialog traffic on this transport will be denied with the provided reason.
+     * <p>
+     * Incoming out-of-dialog traffic will continue to be set up until the SipDelegate is fully
+     * closed.
+     * @param delegateClosingReason The reason code to return to
+     * {@link DelegateMessageCallback#onMessageSendFailure(String, int)} if a new out-of-dialog SIP
+     *         message is received while waiting for existing Dialogs.
+     * @param closedReason reason to return to new outgoing SIP messages via
+     *         {@link SipDelegate#notifyMessageReceiveError(String, int)} once the transport
+     *         transitions to the fully closed state.
+     * @param resultConsumer The consumer called when the message transport has been closed. It will
+     *         return {@code true} if the procedure completed successfully or {@link false} if the
+     *         transport needed to be closed forcefully due to the application not responding before
+     *         a timeout occurred.
+     */
+    public void closeGracefully(int delegateClosingReason, int closedReason,
+            Consumer<Boolean> resultConsumer) {
+        logi("closeGracefully: closingReason=" + delegateClosingReason + ", closedReason="
+                + closedReason + ", resultConsumer(" + resultConsumer.hashCode() + ")");
+        mSipSessionTracker.closeSessionsGracefully((openCallIds) -> {
+            logi("closeGracefully resultConsumer(" + resultConsumer.hashCode()
+                    + "): open call IDs:{" + openCallIds + "}");
+            closeTransport(openCallIds);
+            // propagate event to the consumer
+            resultConsumer.accept(openCallIds.isEmpty() /*successfullyClosed*/);
+        }, delegateClosingReason, closedReason);
+    }
+
+    /**
+     * Close all ongoing SIP sessions immediately and respond to any incoming/outgoing messages with
+     * the provided reason.
+     * @param closedReason The failure reason to provide to incoming/outgoing SIP messages
+     *         if an attempt is made to send/receive a message after this method is called.
+     */
+    public void close(int closedReason) {
+        Set<String> openSessions = mSipSessionTracker.closeSessions(closedReason);
+        logi("close: closedReason=" + closedReason + "open call IDs:{" + openSessions + "}");
+        closeTransport(openSessions);
+    }
+
+    // Clean up all state related to the existing SipDelegate immediately.
+    private void closeTransport(Set<String> openCallIds) {
+        for (String id : openCallIds) {
+            cleanupSessionInternal(id);
+        }
+        mSipDelegate = null;
+    }
+
+    private void cleanupSessionInternal(String callId) {
+        logi("cleanupSessionInternal: clean up session with callId: " + callId);
+        try {
+            if (mSipDelegate == null) {
+                logw("cleanupSessionInternal: SipDelegate is not associated, callId: " + callId);
+            } else {
+                // This will close the transport, so call cleanup on ImsService first.
+                mSipDelegate.cleanupSession(callId);
+            }
+        } catch (RemoteException e) {
+            logw("cleanupSessionInternal: remote not available when cleanupSession was called "
+                    + "for call id: " + callId);
+        }
+        mSipSessionTracker.onSipSessionCleanup(callId);
+    }
+
+    private ValidationResult maybeOverrideValidationForTesting(ValidationResult result) {
+        Boolean isValidatedOverride = mValidatorOverride.getValidatorOverrideState();
+        if (isValidatedOverride == null) {
+            return result;
+        }
+        if (isValidatedOverride) {
+            return ValidationResult.SUCCESS;
+        } else if (result.isValidated) {
+            // if override is set to false and the original result was validated, return a new
+            // restricted result with UNKNOWN reason.
+            return new ValidationResult(SipDelegateManager.MESSAGE_FAILURE_REASON_UNKNOWN,
+                    "validation failed due to a testing override being set");
+        }
+        return result;
+    }
+
+    private void notifyDelegateSendError(String logReason, SipMessage message, int reasonCode) {
+        String transactionId = SipMessageParsingUtils.getTransactionId(message.getHeaderSection());
+        logi("Error sending SipMessage[id: " + transactionId + ", code: " + reasonCode
+                + "] -> SipDelegate for reason: " + logReason);
+        try {
+            mAppCallback.onMessageSendFailure(transactionId, reasonCode);
+        } catch (RemoteException e) {
+            logw("notifyDelegateSendError, SipDelegate is not available: " + e);
+        }
+    }
+
+    private void notifyAppReceiveError(String logReason, SipMessage message, int reasonCode) {
+        String transactionId = SipMessageParsingUtils.getTransactionId(message.getHeaderSection());
+        logi("Error sending SipMessage[id: " + transactionId + ", code: " + reasonCode + "] -> "
+                + "SipDelegateConnection for reason: " + logReason);
+        try {
+            mSipDelegate.notifyMessageReceiveError(transactionId, reasonCode);
+        } catch (RemoteException e) {
+            logw("notifyAppReceiveError, SipDelegate is not available: " + e);
+        }
+    }
+
+    private void logi(String log) {
+        Log.w(SipTransportController.LOG_TAG, TAG + "[" + mSubId + "] " + log);
+        mLocalLog.log("[I] " + log);
+    }
+
+    private void logw(String log) {
+        Log.w(SipTransportController.LOG_TAG, TAG + "[" + mSubId + "] " + log);
+        mLocalLog.log("[W] " + log);
+    }
+}
diff --git a/src/com/android/services/telephony/rcs/RcsFeatureController.java b/src/com/android/services/telephony/rcs/RcsFeatureController.java
index 304a74d..7834903 100644
--- a/src/com/android/services/telephony/rcs/RcsFeatureController.java
+++ b/src/com/android/services/telephony/rcs/RcsFeatureController.java
@@ -20,9 +20,7 @@
 import android.content.Context;
 import android.net.Uri;
 import android.telephony.ims.ImsException;
-import android.telephony.ims.ImsRcsManager;
 import android.telephony.ims.ImsReasonInfo;
-import android.telephony.ims.RegistrationManager;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsRegistrationCallback;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -65,12 +63,16 @@
         void onRcsDisconnected();
 
         /**
-         * The subscription associated with the slot this controller is bound to has changed or its
-         * carrier configuration has changed.
+         * The subscription associated with the slot this controller is bound to has changed.
          */
         void onAssociatedSubscriptionUpdated(int subId);
 
         /**
+         * The carrier configuration associated with the active subscription id has changed.
+         */
+        void onCarrierConfigChanged();
+
+        /**
          * Called when the feature should be destroyed.
          */
         void onDestroy();
@@ -120,6 +122,7 @@
     private final Object mLock = new Object();
     private FeatureConnector<RcsFeatureManager> mFeatureConnector;
     private RcsFeatureManager mFeatureManager;
+    private int mAssociatedSubId;
 
     private FeatureConnector.Listener<RcsFeatureManager> mFeatureConnectorListener =
             new FeatureConnector.Listener<RcsFeatureManager>() {
@@ -130,6 +133,7 @@
                         logw("connectionReady returned null RcsFeatureManager");
                         return;
                     }
+                    logd("connectionReady");
                     try {
                         // May throw ImsException if for some reason the connection to the
                         // ImsService is gone.
@@ -148,6 +152,7 @@
                     if (reason == FeatureConnector.UNAVAILABLE_REASON_SERVER_UNAVAILABLE) {
                         loge("unexpected - connectionUnavailable due to server unavailable");
                     }
+                    logd("connectionUnavailable");
                     // Call before disabling connection to manager.
                     removeConnectionToService();
                     updateConnectionStatus(null /*manager*/);
@@ -173,9 +178,10 @@
                 }
             };
 
-    public RcsFeatureController(Context context, int slotId) {
+    public RcsFeatureController(Context context, int slotId, int associatedSubId) {
         mContext = context;
         mSlotId = slotId;
+        mAssociatedSubId = associatedSubId;
         mImsRcsRegistrationHelper = mRegistrationHelperFactory.create(mRcsRegistrationUpdate,
                 mContext.getMainExecutor());
     }
@@ -184,9 +190,11 @@
      * Should only be used to inject registration helpers for testing.
      */
     @VisibleForTesting
-    public RcsFeatureController(Context context, int slotId, RegistrationHelperFactory f) {
+    public RcsFeatureController(Context context, int slotId, int associatedSubId,
+            RegistrationHelperFactory f) {
         mContext = context;
         mSlotId = slotId;
+        mAssociatedSubId = associatedSubId;
         mRegistrationHelperFactory = f;
         mImsRcsRegistrationHelper = mRegistrationHelperFactory.create(mRcsRegistrationUpdate,
                 mContext.getMainExecutor());
@@ -250,17 +258,12 @@
     }
 
     /**
-     * Update the subscription associated with this controller.
+     * Update the Features associated with this controller due to the associated subscription
+     * changing.
      */
     public void updateAssociatedSubscription(int newSubId) {
-        RcsFeatureManager manager = getFeatureManager();
-        if (manager != null) {
-            try {
-                manager.updateCapabilities();
-            } catch (ImsException e) {
-                Log.w(LOG_TAG, "associatedSubscriptionChanged failed:" + e);
-            }
-        }
+        mAssociatedSubId = newSubId;
+        updateCapabilities();
         synchronized (mLock) {
             for (Feature c : mFeatures.values()) {
                 c.onAssociatedSubscriptionUpdated(newSubId);
@@ -269,6 +272,19 @@
     }
 
     /**
+     * Update the features associated with this controller due to the carrier configuration
+     * changing.
+     */
+    public void onCarrierConfigChangedForSubscription() {
+        updateCapabilities();
+        synchronized (mLock) {
+            for (Feature c : mFeatures.values()) {
+                c.onCarrierConfigChanged();
+            }
+        }
+    }
+
+    /**
      * Call before this controller is destroyed to tear down associated features.
      */
     public void destroy() {
@@ -316,8 +332,8 @@
     }
 
     /**
-     * Register an {@link ImsRcsManager.AvailabilityCallback} with the associated RcsFeature,
-     * which will provide availability updates.
+     * Register an {@link ImsRcsManager.OnAvailabilityChangedListener} with the associated
+     * RcsFeature, which will provide availability updates.
      */
     public void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback)
             throws ImsException {
@@ -330,7 +346,7 @@
     }
 
     /**
-     * Remove a registered {@link ImsRcsManager.AvailabilityCallback} from the RcsFeature.
+     * Remove a registered {@link ImsRcsManager.OnAvailabilityChangedListener} from the RcsFeature.
      */
     public void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) {
         RcsFeatureManager manager = getFeatureManager();
@@ -355,13 +371,14 @@
     /**
      * Query the availability of an IMS RCS capability.
      */
-    public boolean isAvailable(int capability) throws android.telephony.ims.ImsException {
+    public boolean isAvailable(int capability, int radioTech)
+            throws android.telephony.ims.ImsException {
         RcsFeatureManager manager = getFeatureManager();
         if (manager == null) {
             throw new ImsException("Service is not available",
                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
         }
-        return manager.isAvailable(capability);
+        return manager.isAvailable(capability, radioTech);
     }
 
     /**
@@ -382,14 +399,27 @@
         callback.accept(mImsRcsRegistrationHelper.getImsRegistrationState());
     }
 
+    private void updateCapabilities() {
+        RcsFeatureManager manager = getFeatureManager();
+        if (manager != null) {
+            try {
+                manager.updateCapabilities(mAssociatedSubId);
+            } catch (ImsException e) {
+                Log.w(LOG_TAG, "updateCapabilities failed:" + e);
+            }
+        }
+    }
+
     private void setupConnectionToService(RcsFeatureManager manager) throws ImsException {
+        logd("setupConnectionToService");
         // Open persistent listener connection, sends RcsFeature#onFeatureReady.
         manager.openConnection();
-        manager.updateCapabilities();
+        manager.updateCapabilities(mAssociatedSubId);
         manager.registerImsRegistrationCallback(mImsRcsRegistrationHelper.getCallbackBinder());
     }
 
     private void removeConnectionToService() {
+        logd("removeConnectionToService");
         RcsFeatureManager manager = getFeatureManager();
         if (manager != null) {
             manager.unregisterImsRegistrationCallback(
@@ -444,6 +474,10 @@
         }
     }
 
+    private void logd(String log) {
+        Log.d(LOG_TAG, getLogPrefix().append(log).toString());
+    }
+
     private void logw(String log) {
         Log.w(LOG_TAG, getLogPrefix().append(log).toString());
     }
diff --git a/src/com/android/services/telephony/rcs/SipDelegateBinderConnection.java b/src/com/android/services/telephony/rcs/SipDelegateBinderConnection.java
index 3f6f269..168a432 100644
--- a/src/com/android/services/telephony/rcs/SipDelegateBinderConnection.java
+++ b/src/com/android/services/telephony/rcs/SipDelegateBinderConnection.java
@@ -22,6 +22,7 @@
 import android.telephony.ims.DelegateRegistrationState;
 import android.telephony.ims.DelegateRequest;
 import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConfiguration;
 import android.telephony.ims.SipDelegateImsConfiguration;
 import android.telephony.ims.SipDelegateManager;
 import android.telephony.ims.aidl.IImsRegistration;
@@ -97,7 +98,8 @@
                     long token = Binder.clearCallingIdentity();
                     try {
                         mExecutor.execute(() -> {
-                            logi("onImsConfigurationChanged");
+                            logi("onImsConfigurationChanged: version="
+                                    + registeredSipConfig.getVersion());
                             for (StateCallback c : mStateCallbacks) {
                                 c.onImsConfigurationChanged(registeredSipConfig);
                             }
@@ -108,6 +110,22 @@
                 }
 
                 @Override
+                public void onConfigurationChanged(
+                        SipDelegateConfiguration registeredSipConfig) {
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        mExecutor.execute(() -> {
+                            logi("onConfigurationChanged");
+                            for (StateCallback c : mStateCallbacks) {
+                                c.onConfigurationChanged(registeredSipConfig);
+                            }
+                        });
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }
+
+                @Override
                 public void onDestroyed(int reason) {
                     long token = Binder.clearCallingIdentity();
                     try {
diff --git a/src/com/android/services/telephony/rcs/SipDelegateController.java b/src/com/android/services/telephony/rcs/SipDelegateController.java
index 2d6d4f0..30edca1 100644
--- a/src/com/android/services/telephony/rcs/SipDelegateController.java
+++ b/src/com/android/services/telephony/rcs/SipDelegateController.java
@@ -28,6 +28,7 @@
 import android.telephony.ims.aidl.ISipTransport;
 import android.telephony.ims.stub.DelegateConnectionStateCallback;
 import android.telephony.ims.stub.SipDelegate;
+import android.util.ArraySet;
 import android.util.LocalLog;
 import android.util.Log;
 import android.util.Pair;
@@ -42,6 +43,7 @@
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ScheduledExecutorService;
+import java.util.stream.Collectors;
 
 /**
  * Created when an IMS application wishes to open up a {@link SipDelegateConnection} and manages the
@@ -78,7 +80,7 @@
     private final String mPackageName;
     private final DelegateRequest mInitialRequest;
     private final ScheduledExecutorService mExecutorService;
-    private final MessageTransportStateTracker mMessageTransportStateTracker;
+    private final MessageTransportWrapper mMessageTransportWrapper;
     private final DelegateStateTracker mDelegateStateTracker;
     private final DelegateBinderStateManager.Factory mBinderConnectionFactory;
     private final LocalLog mLocalLog = new LocalLog(SipTransportController.LOG_SIZE);
@@ -97,10 +99,10 @@
         mExecutorService = executorService;
         mBinderConnectionFactory = new BinderConnectionFactory(transportImpl, registrationImpl);
 
-        mMessageTransportStateTracker = new MessageTransportStateTracker(mSubId, executorService,
+        mMessageTransportWrapper = new MessageTransportWrapper(mSubId, executorService,
                 messageCallback);
         mDelegateStateTracker = new DelegateStateTracker(mSubId, stateCallback,
-                mMessageTransportStateTracker.getDelegateConnection());
+                mMessageTransportWrapper.getDelegateConnection());
     }
 
     /**
@@ -109,14 +111,14 @@
     @VisibleForTesting
     public SipDelegateController(int subId, DelegateRequest initialRequest, String packageName,
             ScheduledExecutorService executorService,
-            MessageTransportStateTracker messageTransportStateTracker,
+            MessageTransportWrapper messageTransportWrapper,
             DelegateStateTracker delegateStateTracker,
             DelegateBinderStateManager.Factory connectionFactory) {
         mSubId = subId;
         mInitialRequest = initialRequest;
         mPackageName = packageName;
         mExecutorService = executorService;
-        mMessageTransportStateTracker = messageTransportStateTracker;
+        mMessageTransportWrapper = messageTransportWrapper;
         mDelegateStateTracker = delegateStateTracker;
         mBinderConnectionFactory = connectionFactory;
     }
@@ -136,8 +138,18 @@
         return mPackageName;
     }
 
+    /**
+     * @return The ImsService's SIP delegate binder impl associated with this controller.
+     */
     public ISipDelegate getSipDelegateInterface() {
-        return mMessageTransportStateTracker.getDelegateConnection();
+        return mMessageTransportWrapper.getDelegateConnection();
+    }
+
+    /**
+     * @return The IMS app's message callback binder.
+     */
+    public ISipDelegateMessageCallback getAppMessageCallback() {
+        return mMessageTransportWrapper.getAppMessageCallback();
     }
 
     /**
@@ -173,7 +185,12 @@
             }
             mBinderConnection = connection;
             logi("create: created, delegate denied: " + resultPair.second);
-            mMessageTransportStateTracker.openTransport(resultPair.first, resultPair.second);
+            Set<String> allowedTags = new ArraySet<>(supportedSet);
+            // Start with the supported set and remove all tags that were denied.
+            allowedTags.removeAll(resultPair.second.stream().map(FeatureTagState::getFeatureTag)
+                    .collect(Collectors.toSet()));
+            mMessageTransportWrapper.openTransport(resultPair.first, allowedTags,
+                    resultPair.second);
             mDelegateStateTracker.sipDelegateConnected(resultPair.second);
             return true;
         });
@@ -252,7 +269,7 @@
                         DelegateRegistrationState.DEREGISTERING_REASON_DESTROY_PENDING,
                         destroyReason);
         return pendingOperationComplete.thenApplyAsync((reasonFromDelegate) -> {
-            logi("destroy, operation complete, notifying trackers, reason" + reasonFromDelegate);
+            logi("destroy, operation complete, notifying trackers, reason " + reasonFromDelegate);
             mDelegateStateTracker.sipDelegateDestroyed(reasonFromDelegate);
             return reasonFromDelegate;
         }, mExecutorService);
@@ -313,10 +330,10 @@
         CompletableFuture<Boolean> pendingTransportClosed = new CompletableFuture<>();
         if (force) {
             logi("destroySipDelegate, forced");
-            mMessageTransportStateTracker.close(messageDestroyedReason);
+            mMessageTransportWrapper.close(messageDestroyedReason);
             pendingTransportClosed.complete(true);
         } else {
-            mMessageTransportStateTracker.closeGracefully(messageDestroyingReason,
+            mMessageTransportWrapper.closeGracefully(messageDestroyingReason,
                     messageDestroyedReason, pendingTransportClosed::complete);
         }
 
@@ -346,7 +363,7 @@
             DelegateBinderStateManager connection) {
         CompletableFuture<Pair<ISipDelegate, Set<FeatureTagState>>> createdFuture =
                 new CompletableFuture<>();
-        boolean isStarted = connection.create(mMessageTransportStateTracker.getMessageCallback(),
+        boolean isStarted = connection.create(mMessageTransportWrapper.getMessageCallback(),
                 (delegate, delegateDeniedTags) ->
                         createdFuture.complete(new Pair<>(delegate, delegateDeniedTags)));
         if (!isStarted) {
@@ -362,7 +379,7 @@
 
         List<DelegateBinderStateManager.StateCallback> stateCallbacks = new ArrayList<>(2);
         stateCallbacks.add(mDelegateStateTracker);
-        stateCallbacks.add(mMessageTransportStateTracker);
+        stateCallbacks.add(mMessageTransportWrapper);
 
         return mBinderConnectionFactory.create(mSubId,
                 new DelegateRequest(supportedSet), deniedSet, mExecutorService, stateCallbacks);
@@ -390,7 +407,7 @@
         pw.println();
         pw.println("MessageStateTracker:");
         pw.increaseIndent();
-        mMessageTransportStateTracker.dump(pw);
+        mMessageTransportWrapper.dump(pw);
         pw.decreaseIndent();
 
         pw.decreaseIndent();
diff --git a/src/com/android/services/telephony/rcs/SipDialog.java b/src/com/android/services/telephony/rcs/SipDialog.java
new file mode 100644
index 0000000..508d515
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/SipDialog.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2021 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.telephony.ims.SipMessage;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.SipMessageParsingUtils;
+
+import java.time.Instant;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Track the state of a SIP Dialog.
+ * <p>
+ * SIP Dialogs follow the following initialization flow:
+ * <pre>
+ * (INVITE) ---> EARLY -(2XX)-> CONFIRMED -(BYE)-----> CLOSED
+ *          ^     |   \                           ^
+ *          |--(1XX)   -(3XX-7XX) ----------------|
+ * </pre>
+ * <p> A special note on forking INVITE requests:
+ * During the EARLY phase, a 1XX or 2XX response can carry a To header tag param. This To header
+ * tag param will be set once a INVITE reaches the remote and responds. If the proxy has multiple
+ * endpoints available for the same contact, the INVITE may fork and multiple responses may be
+ * received for the same INVITE request, each with a different To header tag parameter (but the
+ * same call-id). This will generate another SIP dialog within the same SIP session.
+ */
+public class SipDialog {
+
+    /**
+     * The device has sent out a dialog starting event and is awaiting a confirmation.
+     */
+    public static final int STATE_EARLY = 0;
+
+    /**
+     * The device has received a 2XX response to the early dialog.
+     */
+    public static final int STATE_CONFIRMED = 1;
+
+    /**
+     * The device has received either a 3XX+ response to a pending dialog request or a BYE
+     * request has been sent on this dialog.
+     */
+    public static final int STATE_CLOSED = 2;
+
+    private final String mBranchId;
+    private final String mCallId;
+    private final String mFromTag;
+    private final Set<String> mAcceptContactFeatureTags;
+    private String mToTag;
+    private int mState = STATE_EARLY;
+    private Instant mLastInteraction;
+
+    /**
+     * @return A SipDialog instance representing the SIP request.
+     */
+    public static SipDialog fromSipMessage(SipMessage m) {
+        if (!SipMessageParsingUtils.isSipRequest(m.getStartLine())) return null;
+        String fromTag = SipMessageParsingUtils.getFromTag(m.getHeaderSection());
+        Set<String> acceptContactTags = SipMessageParsingUtils.getAcceptContactFeatureTags(
+                m.getHeaderSection());
+        return new SipDialog(m.getViaBranchParameter(), m.getCallIdParameter(), fromTag,
+                acceptContactTags);
+    }
+
+    /**
+     * Track a new SIP dialog, which will be starting in {@link #STATE_EARLY}.
+     *
+     * @param branchId The via branch param of the INVITE request, which is used to match
+     *                 responses.
+     * @param callId   The callId of the SIP dialog.
+     * @param fromTag  The from header's tag parameter.
+     */
+    private SipDialog(String branchId, String callId, String fromTag, Set<String> featureTags) {
+        mBranchId = branchId;
+        mCallId = callId;
+        mFromTag = fromTag;
+        mAcceptContactFeatureTags = featureTags;
+        mLastInteraction = Instant.now();
+    }
+
+    /**
+     * @return The call id associated with the SIP dialog.
+     */
+    public String getCallId() {
+        return mCallId;
+    }
+
+    /**
+     * @return the state of the SIP dialog, either {@link #STATE_EARLY},
+     * {@link #STATE_CONFIRMED}, {@link #STATE_CLOSED}.
+     */
+    public int getState() {
+        return mState;
+    }
+
+    /**
+     * @return The to header's tag parameter if this dialog has gotten a response from the remote
+     * party or {@code null} if it has not.
+     */
+    public String getToTag() {
+        return mToTag;
+    }
+
+    /**
+     * @return The feature tags contained in the "Accept-Contact" header.
+     */
+    public Set<String> getAcceptContactFeatureTags() {
+        return mAcceptContactFeatureTags;
+    }
+
+    /**
+     * @return A new instance with branch param, call-id value, and from tag param populated.
+     */
+    public SipDialog forkDialog() {
+        return new SipDialog(mBranchId, mCallId, mFromTag, mAcceptContactFeatureTags);
+    }
+
+    /**
+     * A early response has been received (101-199) and contains a to tag, which will create a
+     * dialog.
+     * @param toTag The to tag of the SIP response.
+     */
+    public void earlyResponse(String toTag) {
+        if (TextUtils.isEmpty(toTag) || mState != STATE_EARLY) {
+            // invalid state
+            return;
+        }
+        mLastInteraction = Instant.now();
+        // Only accept To tag if one has not been assigned yet.
+        if (mToTag == null) {
+            mToTag = toTag;
+        }
+    }
+
+    /**
+     * The early dialog has been confirmed and
+     * @param toTag The To header's tag parameter.
+     */
+    public void confirm(String toTag) {
+        if (mState != STATE_EARLY) {
+            // Invalid state
+            return;
+        }
+        mLastInteraction = Instant.now();
+        mState = STATE_CONFIRMED;
+        // Only accept a To tag if one has not been assigned yet.
+        if (mToTag == null) {
+            mToTag = toTag;
+        }
+    }
+
+    /**
+     * Close the SIP dialog
+     */
+    public void close() {
+        mLastInteraction = Instant.now();
+        mState = STATE_CLOSED;
+    }
+
+    /**
+     * @return {@code true} if a SIP response's branch, call-id, and from tags match this dialog,
+     * {@code false} if it does not. This may match multiple Dialogs in the case of SIP INVITE
+     * forking.
+     */
+    public boolean isResponseAssociatedWithDialog(SipMessage m) {
+        if (!mBranchId.equals(m.getViaBranchParameter())) return false;
+        if (!mCallId.equals(m.getCallIdParameter())) return false;
+        String fromTag = SipMessageParsingUtils.getFromTag(m.getHeaderSection());
+        return mFromTag.equals(fromTag);
+    }
+
+    /**
+     * @return {@code true} if the SIP request is part of the SIP Dialog, {@code false} if it is
+     * not.
+     */
+    public boolean isRequestAssociatedWithDialog(SipMessage m) {
+        if (!mCallId.equals(m.getCallIdParameter())) return false;
+        String fromTag = SipMessageParsingUtils.getFromTag(m.getHeaderSection());
+        String toTag = SipMessageParsingUtils.getToTag(m.getHeaderSection());
+        // Requests can only be associated if both to and from tag of message are populated. The
+        // dialog's to tag must also be non-null meaning we got a response from the remote.
+        if (fromTag == null || toTag == null || mToTag == null) return false;
+        // For outgoing requests, recorded from tag will match their from tag and for incoming
+        // requests recorded from tag will match their to tag. Same with our recorded to tag.
+        return (mFromTag.equals(fromTag) || mFromTag.equals(toTag))
+                && (mToTag.equals(toTag) || mToTag.equals(fromTag));
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder b = new StringBuilder("SipDialog[");
+        switch (mState) {
+            case STATE_EARLY:
+                b.append("early");
+                break;
+            case STATE_CONFIRMED:
+                b.append("confirmed");
+                break;
+            case STATE_CLOSED:
+                b.append("closed");
+                break;
+            default:
+                b.append(mState);
+        }
+        b.append("] bId=");
+        b.append(mBranchId);
+        b.append(", cId=");
+        b.append(mCallId);
+        b.append(", f=");
+        b.append(mFromTag);
+        b.append(", t=");
+        b.append(mToTag);
+        b.append(", Last Interaction: ");
+        b.append(mLastInteraction);
+        return b.toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SipDialog sipDialog = (SipDialog) o;
+        // Does not include mState and last interaction time, as a dialog is only the same if
+        // its branch, callId, and to/from tags are equal.
+        return mBranchId.equals(sipDialog.mBranchId)
+                && Objects.equals(mCallId, sipDialog.mCallId)
+                && Objects.equals(mFromTag, sipDialog.mFromTag)
+                && Objects.equals(mToTag, sipDialog.mToTag);
+    }
+
+    @Override
+    public int hashCode() {
+        // Does not include mState and last interaction time, as a dialog is only the same if
+        // its branch, callId, and to/from tags are equal.
+        return Objects.hash(mBranchId, mCallId, mFromTag, mToTag);
+    }
+}
diff --git a/src/com/android/services/telephony/rcs/SipSessionTracker.java b/src/com/android/services/telephony/rcs/SipSessionTracker.java
new file mode 100644
index 0000000..77bf3f3
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/SipSessionTracker.java
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2021 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.telephony.ims.SipMessage;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.LocalLog;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.SipMessageParsingUtils;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Tracks the state of SIP sessions started by a SIP INVITE (see RFC 3261)
+ * <p>
+ * Each SIP session created will consist of one or more SIP with, each dialog in the session
+ * having the same call-ID. Each SIP dialog will be in one of three states: EARLY, CONFIRMED, and
+ * CLOSED.
+ * <p>
+ * The SIP session will be closed once all of the associated dialogs are closed.
+ */
+public class SipSessionTracker {
+    private static final String TAG = "SessionT";
+
+    /**
+     * SIP request methods that will start a new SIP Dialog and move it into the PENDING state
+     * while we wait for a response. Note: INVITE is not the only SIP dialog that will create a
+     * dialog, however it is the only one that we wish to track for this use case.
+     */
+    public static final String[] SIP_REQUEST_DIALOG_START_METHODS = new String[] { "invite" };
+
+    /**
+     * The SIP request method that will close a SIP Dialog in the ACTIVE state with the same
+     * Call-Id.
+     */
+    private static final String SIP_CLOSE_DIALOG_REQUEST_METHOD = "bye";
+
+    private final LocalLog mLocalLog = new LocalLog(SipTransportController.LOG_SIZE);
+    private final ArrayList<SipDialog> mTrackedDialogs = new ArrayList<>();
+    // Operations that are pending an ack from the remote application processing the message before
+    // they can be applied here. Maps the via header branch parameter of the message to the
+    // associated pending operation.
+    private final ArrayMap<String, Runnable> mPendingAck = new ArrayMap<>();
+
+    /**
+     * Filter a SIP message to determine if it will result in a new SIP dialog. This will need to be
+     * successfully acknowledged by the remote IMS stack using
+     * {@link #acknowledgePendingMessage(String)} before we do any further processing.
+     *
+     * @param message The Incoming SIP message.
+     */
+    public void filterSipMessage(SipMessage message) {
+        final Runnable r;
+        if (startsEarlyDialog(message)) {
+            r = getCreateDialogRunnable(message);
+        } else if (closesDialog(message)) {
+            r = getCloseDialogRunnable(message);
+        } else if (SipMessageParsingUtils.isSipResponse(message.getStartLine())) {
+            r = getDialogStateChangeRunnable(message);
+        } else {
+            r = null;
+        }
+
+        if (r != null) {
+            if (mPendingAck.containsKey(message.getViaBranchParameter())) {
+                Runnable lastEvent = mPendingAck.get(message.getViaBranchParameter());
+                logw("Adding new message when there was already a pending event for branch: "
+                        + message.getViaBranchParameter());
+                Runnable concatRunnable = () -> {
+                    // No choice but to concatenate the Runnables together.
+                    if (lastEvent != null) lastEvent.run();
+                    r.run();
+                };
+                mPendingAck.put(message.getViaBranchParameter(), concatRunnable);
+            } else {
+                mPendingAck.put(message.getViaBranchParameter(), r);
+            }
+        }
+    }
+
+    /**
+     * The pending SIP message has been received by the remote IMS stack. We can now track dialogs
+     * associated with this message.
+     * message.
+     * @param viaBranchId The SIP message's Via header's branch parameter, which is used as a
+     *                    unique token.
+     */
+    public void acknowledgePendingMessage(String viaBranchId) {
+        Runnable r = mPendingAck.get(viaBranchId);
+        if (r != null) {
+            mPendingAck.remove(viaBranchId);
+            r.run();
+        }
+    }
+
+    /**
+     * The pending SIP message has failed to be sent to the remote so remove the pending task.
+     * @param viaBranchId The failed message's Via header's branch parameter.
+     */
+    public void pendingMessageFailed(String viaBranchId) {
+        mPendingAck.remove(viaBranchId);
+    }
+
+    /**
+     * A SIP session tracked by the remote application's IMS stack has been closed, so we can stop
+     * tracking it.
+     * @param callId The callId of the SIP session that has been closed.
+     */
+    public void cleanupSession(String callId) {
+        List<SipDialog> dialogsToCleanup = mTrackedDialogs.stream()
+                .filter(d -> d.getCallId().equals(callId))
+                .collect(Collectors.toList());
+        if (dialogsToCleanup.isEmpty()) return;
+        logi("Cleanup dialogs associated with call id: " + callId);
+        for (SipDialog d : dialogsToCleanup) {
+            d.close();
+            logi("Dialog closed: " + d);
+        }
+        mTrackedDialogs.removeAll(dialogsToCleanup);
+    }
+
+    /**
+     * @return the call IDs of the dialogs associated with the provided feature tags.
+     */
+    public Set<String> getCallIdsAssociatedWithFeatureTag(Set<String> featureTags) {
+        if (featureTags.isEmpty()) return Collections.emptySet();
+        Set<String> associatedIds = new ArraySet<>();
+        for (String featureTag : featureTags) {
+            for (SipDialog dialog : mTrackedDialogs) {
+                boolean isAssociated = dialog.getAcceptContactFeatureTags().stream().anyMatch(
+                        d -> d.equalsIgnoreCase(featureTag));
+                if (isAssociated) associatedIds.add(dialog.getCallId());
+            }
+        }
+        return associatedIds;
+    }
+
+    /**
+     * @return All dialogs that have not received a final response yet 2XX or 3XX+.
+     */
+    public Set<SipDialog> getEarlyDialogs() {
+        return mTrackedDialogs.stream().filter(d -> d.getState() == SipDialog.STATE_EARLY)
+                .collect(Collectors.toSet());
+    }
+
+    /**
+     * @return All confirmed dialogs that have received a 2XX response and are active.
+     */
+    public Set<SipDialog> getConfirmedDialogs() {
+        return mTrackedDialogs.stream().filter(d -> d.getState() == SipDialog.STATE_CONFIRMED)
+                .collect(Collectors.toSet());
+    }
+
+    /**
+     * @return Dialogs that have been closed via a BYE or 3XX+ response and
+     * {@link #cleanupSession(String)} has not been called yet.
+     */
+    @VisibleForTesting
+    public Set<SipDialog> getClosedDialogs() {
+        return mTrackedDialogs.stream().filter(d -> d.getState() == SipDialog.STATE_CLOSED)
+                .collect(Collectors.toSet());
+    }
+
+    /**
+     * @return All of the tracked dialogs, even the ones that have been closed but
+     * {@link #cleanupSession(String)} has not been called.
+     */
+    public Set<SipDialog> getTrackedDialogs() {
+        return new ArraySet<>(mTrackedDialogs);
+    }
+
+    /**
+     * Clears all tracked sessions.
+     */
+    public void clearAllSessions() {
+        mTrackedDialogs.clear();
+        mPendingAck.clear();
+    }
+
+    /**
+     * Dump the state of this tracker to the provided PrintWriter.
+     */
+    public void dump(PrintWriter printWriter) {
+        IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
+        pw.println("SipSessionTracker:");
+        pw.increaseIndent();
+        pw.print("Early Call IDs: ");
+        pw.println(getEarlyDialogs().stream().map(SipDialog::getCallId)
+                .collect(Collectors.toSet()));
+        pw.print("Confirmed Call IDs: ");
+        pw.println(getConfirmedDialogs().stream().map(SipDialog::getCallId)
+                .collect(Collectors.toSet()));
+        pw.print("Closed Call IDs: ");
+        pw.println(getClosedDialogs().stream().map(SipDialog::getCallId)
+                .collect(Collectors.toSet()));
+        pw.println("Tracked Dialogs:");
+        pw.increaseIndent();
+        for (SipDialog d : mTrackedDialogs) {
+            pw.println(d);
+        }
+        pw.decreaseIndent();
+        pw.println();
+        pw.println("Local Logs");
+        mLocalLog.dump(pw);
+        pw.decreaseIndent();
+    }
+
+    /**
+     * @return {@code true}, if the SipMessage passed in should start a new SIP dialog,
+     * {@code false} if it should not.
+     */
+    private boolean startsEarlyDialog(SipMessage m) {
+        if (!SipMessageParsingUtils.isSipRequest(m.getStartLine())) {
+            return false;
+        }
+        String[] startLineSegments = SipMessageParsingUtils.splitStartLineAndVerify(
+                m.getStartLine());
+        if (startLineSegments == null) {
+            return false;
+        }
+        return Arrays.stream(SIP_REQUEST_DIALOG_START_METHODS)
+                .anyMatch(r -> r.equalsIgnoreCase(startLineSegments[0]));
+    }
+
+    /**
+     * @return {@code true}, if the SipMessage passed in should close a confirmed dialog,
+     * {@code false} if it should not.
+     */
+    private boolean closesDialog(SipMessage m) {
+        if (!SipMessageParsingUtils.isSipRequest(m.getStartLine())) {
+            return false;
+        }
+        String[] startLineSegments = SipMessageParsingUtils.splitStartLineAndVerify(
+                m.getStartLine());
+        if (startLineSegments == null) {
+            return false;
+        }
+        return SIP_CLOSE_DIALOG_REQUEST_METHOD.equalsIgnoreCase(startLineSegments[0]);
+    }
+
+    private Runnable getCreateDialogRunnable(SipMessage m) {
+        return () -> {
+            List<SipDialog> duplicateDialogs = mTrackedDialogs.stream()
+                    .filter(d -> d.getCallId().equals(m.getCallIdParameter()))
+                    .collect(Collectors.toList());
+            if (duplicateDialogs.size() > 0) {
+                logi("trying to create a dialog for a call ID that already exists, skip: "
+                        + duplicateDialogs);
+                return;
+            }
+            SipDialog dialog = SipDialog.fromSipMessage(m);
+            logi("Starting new SipDialog: " + dialog);
+            mTrackedDialogs.add(dialog);
+        };
+    }
+
+    private Runnable getCloseDialogRunnable(SipMessage m) {
+        return () -> {
+            List<SipDialog> dialogsToClose = mTrackedDialogs.stream()
+                    .filter(d -> d.isRequestAssociatedWithDialog(m))
+                    .collect(Collectors.toList());
+            if (dialogsToClose.isEmpty()) return;
+            logi("Closing dialogs associated with: " + m);
+            for (SipDialog d : dialogsToClose) {
+                d.close();
+                logi("Dialog closed: " + d);
+            }
+        };
+    }
+
+    private Runnable getDialogStateChangeRunnable(SipMessage m) {
+        return () -> {
+            // This will return a dialog and all of its potential forks
+            List<SipDialog> associatedDialogs = mTrackedDialogs.stream()
+                    .filter(d -> d.isResponseAssociatedWithDialog(m))
+                    .collect(Collectors.toList());
+            if (associatedDialogs.isEmpty()) return;
+            String messageToTag = SipMessageParsingUtils.getToTag(m.getHeaderSection());
+            // If the to tag matches (or message to tag doesn't exist in dialog yet because this is
+            // the first response), then we are done.
+            SipDialog match = associatedDialogs.stream()
+                    .filter(d -> d.getToTag() == null || d.getToTag().equals(messageToTag))
+                    .findFirst().orElse(null);
+            if (match == null) {
+                // If it doesn't then we have a situation where we need to fork the existing dialog.
+                // The dialog used to fork doesn't matter, since the required params are the same,
+                // so simply use the first one in the returned list.
+                logi("Dialog forked");
+                match = associatedDialogs.get(0).forkDialog();
+                mTrackedDialogs.add(match);
+            }
+            if (match != null) {
+                logi("Dialog: " + match + " is associated with: " + m);
+                updateSipDialogState(match, m);
+                logi("Dialog state updated to " + match);
+            } else {
+                logi("No Dialogs are associated with: " + m);
+            }
+        };
+    }
+
+    private void updateSipDialogState(SipDialog d, SipMessage m) {
+        String[] startLineSegments = SipMessageParsingUtils.splitStartLineAndVerify(
+                m.getStartLine());
+        if (startLineSegments == null) {
+            logw("Could not parse start line for SIP message: " + m.getStartLine());
+            return;
+        }
+        int statusCode = 0;
+        try {
+            statusCode = Integer.parseInt(startLineSegments[1]);
+        } catch (NumberFormatException e) {
+            logw("Could not parse status code for SIP message: " + m.getStartLine());
+            return;
+        }
+        String toTag = SipMessageParsingUtils.getToTag(m.getHeaderSection());
+        logi("updateSipDialogState: message has statusCode: " + statusCode + ", and to tag: "
+                + toTag);
+        // If specifically 100 Trying, then do not do anything.
+        if (statusCode <= 100) return;
+        // If 300+, then this dialog has received an error response and should move to closed state.
+        if (statusCode >= 300) {
+            d.close();
+            return;
+        }
+        if (toTag == null) logw("updateSipDialogState: No to tag for message: " + m);
+        if (statusCode >= 200) {
+            d.confirm(toTag);
+            return;
+        }
+        // 1XX responses still require updates to dialogs.
+        d.earlyResponse(toTag);
+    }
+
+    private void logi(String log) {
+        Log.w(SipTransportController.LOG_TAG, TAG + ": " + log);
+        mLocalLog.log("[I] " + log);
+    }
+
+    private void logw(String log) {
+        Log.w(SipTransportController.LOG_TAG, TAG + ": " + log);
+        mLocalLog.log("[W] " + log);
+    }
+}
diff --git a/src/com/android/services/telephony/rcs/SipTransportController.java b/src/com/android/services/telephony/rcs/SipTransportController.java
index 028e49f..f37c360 100644
--- a/src/com/android/services/telephony/rcs/SipTransportController.java
+++ b/src/com/android/services/telephony/rcs/SipTransportController.java
@@ -19,8 +19,11 @@
 import android.app.role.OnRoleHoldersChangedListener;
 import android.app.role.RoleManager;
 import android.content.Context;
+import android.os.PersistableBundle;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.telephony.CarrierConfigManager;
 import android.telephony.ims.DelegateRequest;
 import android.telephony.ims.FeatureTagState;
 import android.telephony.ims.ImsException;
@@ -44,12 +47,14 @@
 import com.android.ims.RcsFeatureManager;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.phone.RcsProvisioningMonitor;
 
 import com.google.common.base.Objects;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.Callable;
@@ -80,8 +85,8 @@
  */
 public class SipTransportController implements RcsFeatureController.Feature,
         OnRoleHoldersChangedListener {
+    public static final String LOG_TAG = "SipTransportC";
     static final int LOG_SIZE = 50;
-    static final String LOG_TAG = "SipTransportC";
 
     /**See {@link TimerAdapter#getReevaluateThrottleTimerMilliseconds()}.*/
     private static final int REEVALUATE_THROTTLE_DEFAULT_MS = 1000;
@@ -168,6 +173,34 @@
     }
 
     /**
+     * Extends RemoteCallbackList to track callbacks to the IMS applications with
+     * SipDelegateConnections and cleans them up if they die.
+     */
+    private class TrackedAppBinders extends RemoteCallbackList<ISipDelegateMessageCallback> {
+        @Override
+        public void onCallbackDied(ISipDelegateMessageCallback callback, Object cookie) {
+            mExecutorService.execute(() -> {
+                if (cookie instanceof SipDelegateController) {
+                    SipDelegateController c = (SipDelegateController) cookie;
+                    logi("onCallbackDied - Cleaning up delegates associated with package: "
+                            + c.getPackageName());
+                    boolean isNewDestroyQueued = addPendingDestroy(c,
+                            SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD);
+                    if (isNewDestroyQueued) {
+                        CompletableFuture<Void> f = new CompletableFuture<>();
+                        scheduleReevaluateNow(f);
+                        f.thenRun(() -> logi("onCallbackDied - clean up complete for package: "
+                                + c.getPackageName()));
+                    }
+                } else {
+                    logw("onCallbackDied: unexpected - cookie is not an instance of "
+                            + "SipDelegateController");
+                }
+            });
+        }
+    }
+
+    /**
      * Used in {@link #destroySipDelegate(int, ISipDelegate, int)} to store pending destroy
      * requests.
      */
@@ -186,13 +219,16 @@
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
             DestroyRequest that = (DestroyRequest) o;
-            return reason == that.reason
-                    && controller.equals(that.controller);
+            // Only use controller for comparison, as we want to only have one DestroyRequest active
+            // per controller.
+            return controller.equals(that.controller);
         }
 
         @Override
         public int hashCode() {
-            return java.util.Objects.hash(controller, reason);
+            // Only use controller for hash, as we want to only have one DestroyRequest active per
+            // controller.
+            return java.util.Objects.hash(controller);
         }
 
         @Override
@@ -229,6 +265,8 @@
     private final List<SipDelegateController> mDelegatePendingCreate = new ArrayList<>();
     // SipDelegateControllers that are pending to be destroyed.
     private final List<DestroyRequest> mDelegatePendingDestroy = new ArrayList<>();
+    // Cache of Binders to remote IMS applications for tracking their potential death
+    private final TrackedAppBinders mActiveAppBinders = new TrackedAppBinders();
 
     // Future scheduled for operations that require the list of SipDelegateControllers to
     // be evaluated. When the timer expires and triggers the reevaluate method, this controller
@@ -247,6 +285,10 @@
     private RcsFeatureManager mRcsManager;
     // Cached package name of the app that is considered the default SMS app.
     private String mCachedSmsRolePackageName = "";
+    // Callback to monitor rcs provisioning change
+    private CarrierConfigManager mCarrierConfigManager;
+    // Cached allowed feature tags from carrier config
+    private ArraySet<String> mFeatureTagsAllowed = new ArraySet<>();
 
     /**
      * Create an instance of SipTransportController.
@@ -261,6 +303,7 @@
         mRoleManagerAdapter = new RoleManagerAdapterImpl(context);
         mTimerAdapter = new TimerAdapterImpl();
         mExecutorService = Executors.newSingleThreadScheduledExecutor();
+        mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class);
     }
 
     /**
@@ -277,6 +320,7 @@
         mTimerAdapter = timerAdapter;
         mDelegateControllerFactory = delegateFactory;
         mExecutorService = executor;
+        mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class);
         logi("created");
     }
 
@@ -296,6 +340,11 @@
     }
 
     @Override
+    public void onCarrierConfigChanged() {
+        mExecutorService.submit(this::onCarrierConfigChangedInternal);
+    }
+
+    @Override
     public void onDestroy() {
         mExecutorService.submit(()-> {
             // Ensure new create/destroy requests are denied.
@@ -645,6 +694,11 @@
             return;
         }
 
+        // Remove tracking for all SipDelegates being destroyed first
+        for (DestroyRequest d : mDelegatePendingDestroy) {
+            logi("reevaluateDelegates: starting destroy for: " + d.controller.getPackageName());
+            mActiveAppBinders.unregister(d.controller.getAppMessageCallback());
+        }
         // Destroy all pending destroy delegates first. Order doesn't matter.
         List<CompletableFuture<Void>> pendingDestroyList = mDelegatePendingDestroy.stream()
                 .map(d -> triggerDestroy(d.controller, d.reason)).collect(
@@ -658,7 +712,8 @@
         // Add newly created SipDelegates to end of queue before evaluating associated features.
         mDelegatePriorityQueue.addAll(mDelegatePendingCreate);
         for (SipDelegateController c : mDelegatePendingCreate) {
-            logi("reevaluateDelegates: pending create: " + c);
+            logi("reevaluateDelegates: pending create: " + c.getPackageName());
+            mActiveAppBinders.register(c.getAppMessageCallback(), c);
         }
         mDelegatePendingCreate.clear();
 
@@ -689,8 +744,12 @@
 
         // Executor doesn't matter here, schedule an event to update the IMS registration.
         mEvaluateCompleteFuture = pendingChange
-                .thenAccept((associatedFeatures) -> {
-                    logi("reevaluateDelegates: reevaluate complete," + " feature tags associated: "
+                .whenComplete((f, ex) -> {
+                    if (ex != null) {
+                        logw("reevaluateDelegates: Exception caught: " + ex);
+                    }
+                }).thenAccept((associatedFeatures) -> {
+                    logi("reevaluateDelegates: reevaluate complete, feature tags associated: "
                             + associatedFeatures);
                     scheduleUpdateRegistration();
                 });
@@ -808,21 +867,14 @@
         }
 
         ArraySet<String> previouslyGrantedTags = new ArraySet<>(alreadyRequestedTags);
-        // deny tags already used by other delegates
-        Set<FeatureTagState> deniedTags = new ArraySet<>();
-        for (String s : requestedFeatureTags) {
-            if (previouslyGrantedTags.contains(s)) {
-                deniedTags.add(new FeatureTagState(s,
-                        SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE));
-            }
-        }
-        Set<String> nonDeniedTags = requestedFeatureTags.stream()
-                .filter(r -> !previouslyGrantedTags.contains(r))
-                .collect(Collectors.toSet());
+        ArraySet<String> candidateFeatureTags = new ArraySet<>(requestedFeatureTags);
+        Set<FeatureTagState> deniedTags =
+                updateSupportedTags(candidateFeatureTags, previouslyGrantedTags);
+
         // Add newly granted tags to the already requested tags list.
-        previouslyGrantedTags.addAll(nonDeniedTags);
+        previouslyGrantedTags.addAll(candidateFeatureTags);
         CompletableFuture<Boolean> pendingChange = controller.changeSupportedFeatureTags(
-                nonDeniedTags, deniedTags);
+                candidateFeatureTags, deniedTags);
         logi("changeSupportedFeatureTags pendingChange=" + pendingChange);
         // do not worry about executor used here, this stage used to interpret result + add log.
         return pendingChange.thenApply((completedSuccessfully) ->  {
@@ -833,6 +885,51 @@
     }
 
     /**
+     * Update candidate feature tags according to feature tags allowed by carrier config,
+     * and previously granted by other SipDelegates.
+     *
+     * @param candidateFeatureTags The candidate feature tags to be updated. It will be
+     * updated as needed per the carrier config and previously granted feature tags.
+     * @param previouslyGrantedTags The feature tags already granted by other SipDelegates.
+     * @return The set of denied feature tags.
+     */
+    private Set<FeatureTagState> updateSupportedTags(Set<String> candidateFeatureTags,
+            Set<String> previouslyGrantedTags) {
+        Boolean overrideRes = RcsProvisioningMonitor.getInstance()
+                .getImsFeatureValidationOverride(mSubId);
+        // deny tags already used by other delegates
+        Set<FeatureTagState> deniedTags = new ArraySet<>();
+
+        // match config if feature validation is not overridden
+        if (overrideRes == null) {
+            Iterator<String> it = candidateFeatureTags.iterator();
+            while (it.hasNext()) {
+                String tag = it.next();
+                if (previouslyGrantedTags.contains(tag)) {
+                    logi(tag + " has already been granted previously.");
+                    it.remove();
+                    deniedTags.add(new FeatureTagState(tag,
+                            SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE));
+                } else if (!mFeatureTagsAllowed.contains(tag.trim().toLowerCase())) {
+                    logi(tag + " is not allowed per config.");
+                    it.remove();
+                    deniedTags.add(new FeatureTagState(tag,
+                              SipDelegateManager.DENIED_REASON_NOT_ALLOWED));
+                }
+            }
+        } else if (Boolean.FALSE.equals(overrideRes)) {
+            logi("all features are denied for test purpose.");
+            for (String s : candidateFeatureTags) {
+                deniedTags.add(new FeatureTagState(s,
+                        SipDelegateManager.DENIED_REASON_NOT_ALLOWED));
+            }
+            candidateFeatureTags.clear();
+        }
+
+        return deniedTags;
+    }
+
+    /**
      * Run a Callable on the ExecutorService Thread and wait for the result.
      * If an ImsException is thrown, catch it and rethrow it to caller.
      */
@@ -903,8 +1000,7 @@
     }
 
     /**
-     * Called when either the sub ID associated with the slot has changed or the carrier
-     * configuration associated with the same subId has changed.
+     * Called when the sub ID associated with the slot has changed.
      */
     private void onSubIdChanged(int newSubId) {
         logi("subId changed, " + mSubId + "->" + newSubId);
@@ -913,10 +1009,25 @@
             mSubId = newSubId;
             scheduleDestroyDelegates(
                     SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN);
-            return;
         }
-        // TODO: if subId hasn't changed this means that we should load in any new carrier configs
-        // that we care about and apply.
+
+        onCarrierConfigChangedInternal();
+    }
+
+    /**
+     * Called when the carrier configuration associated with the same subId has changed.
+     */
+    private void onCarrierConfigChangedInternal() {
+        logi("Carrier Config changed for subId: " + mSubId);
+        mFeatureTagsAllowed.clear();
+        PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
+        String[] tagConfigs = carrierConfig.getStringArray(
+                CarrierConfigManager.Ims.KEY_RCS_FEATURE_TAG_ALLOWED_STRING_ARRAY);
+        if (tagConfigs != null && tagConfigs.length > 0) {
+            for (String tag : tagConfigs) {
+                mFeatureTagsAllowed.add(tag.trim().toLowerCase());
+            }
+        }
     }
 
     /**
diff --git a/src/com/android/services/telephony/rcs/TelephonyRcsService.java b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
index a6789f5..034382c 100644
--- a/src/com/android/services/telephony/rcs/TelephonyRcsService.java
+++ b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
@@ -33,6 +33,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.PhoneConfigurationManager;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.phone.R;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -54,7 +55,7 @@
         /**
          * @return an {@link RcsFeatureController} associated with the slot specified.
          */
-        RcsFeatureController createController(Context context, int slotId);
+        RcsFeatureController createController(Context context, int slotId, int subId);
 
         /**
          * @return an instance of {@link UceControllerManager} associated with the slot specified.
@@ -70,8 +71,8 @@
 
     private FeatureFactory mFeatureFactory = new FeatureFactory() {
         @Override
-        public RcsFeatureController createController(Context context, int slotId) {
-            return new RcsFeatureController(context, slotId);
+        public RcsFeatureController createController(Context context, int slotId, int subId) {
+            return new RcsFeatureController(context, slotId, subId);
         }
 
         @Override
@@ -87,6 +88,22 @@
         }
     };
 
+    /**
+     * Used to inject device resource for testing.
+     */
+    @VisibleForTesting
+    public interface ResourceProxy {
+        /**
+         * @return an whether the device supports User Capability Exchange.
+         */
+        boolean getDeviceUceEnabled(Context context);
+    }
+
+    private static ResourceProxy sResourceProxy = context -> {
+        return context.getResources().getBoolean(
+                R.bool.config_rcs_user_capability_exchange_enabled);
+    };
+
     // Notifies this service that there has been a change in available slots.
     private static final int HANDLER_MSIM_CONFIGURATION_CHANGE = 1;
 
@@ -96,6 +113,11 @@
 
     // Maps slot ID -> RcsFeatureController.
     private SparseArray<RcsFeatureController> mFeatureControllers;
+    // Maps slotId -> associatedSubIds
+    private SparseArray<Integer> mSlotToAssociatedSubIds;
+
+    // Whether the device supports User Capability Exchange
+    private boolean mRcsUceEnabled;
 
     private BroadcastReceiver mCarrierConfigChangedReceiver = new BroadcastReceiver() {
         @Override
@@ -112,7 +134,7 @@
                         SubscriptionManager.INVALID_PHONE_INDEX);
                 int subId = bundle.getInt(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-                updateFeatureControllerSubscription(slotId, subId);
+                onCarrierConfigChangedForSlot(slotId, subId);
             }
         }
     };
@@ -139,6 +161,18 @@
         mContext = context;
         mNumSlots = numSlots;
         mFeatureControllers = new SparseArray<>(numSlots);
+        mSlotToAssociatedSubIds = new SparseArray<>(numSlots);
+        mRcsUceEnabled = sResourceProxy.getDeviceUceEnabled(mContext);
+    }
+
+    @VisibleForTesting
+    public TelephonyRcsService(Context context, int numSlots, ResourceProxy resourceProxy) {
+        mContext = context;
+        mNumSlots = numSlots;
+        mFeatureControllers = new SparseArray<>(numSlots);
+        mSlotToAssociatedSubIds = new SparseArray<>(numSlots);
+        sResourceProxy = resourceProxy;
+        mRcsUceEnabled = sResourceProxy.getDeviceUceEnabled(mContext);
     }
 
     /**
@@ -188,6 +222,8 @@
                     // Do not add feature controllers for inactive subscriptions
                     if (c.hasActiveFeatures()) {
                         mFeatureControllers.put(i, c);
+                        // Do not change mSlotToAssociatedSubIds, it will be updated upon carrier
+                        // config change.
                     }
                 }
             } else {
@@ -195,6 +231,7 @@
                     RcsFeatureController c = mFeatureControllers.get(i);
                     if (c != null) {
                         mFeatureControllers.remove(i);
+                        mSlotToAssociatedSubIds.remove(i);
                         c.destroy();
                     }
                 }
@@ -202,19 +239,29 @@
         }
     }
 
-    private void updateFeatureControllerSubscription(int slotId, int newSubId) {
+    /**
+     * ACTION_CARRIER_CONFIG_CHANGED was received by this service for a specific slot.
+     * @param slotId The slotId associated with the event.
+     * @param subId The subId associated with the event. May cause the subId associated with the
+     *              RcsFeatureController to change if the subscription itself has changed.
+     */
+    private void onCarrierConfigChangedForSlot(int slotId, int subId) {
         synchronized (mLock) {
             RcsFeatureController f = mFeatureControllers.get(slotId);
-            Log.i(LOG_TAG, "updateFeatureControllerSubscription: slotId=" + slotId + " newSubId="
-                    + newSubId + ", existing feature=" + (f != null));
-            if (SubscriptionManager.isValidSubscriptionId(newSubId)) {
+            final int oldSubId = mSlotToAssociatedSubIds.get(slotId,
+                    SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+            mSlotToAssociatedSubIds.put(slotId, subId);
+            Log.i(LOG_TAG, "updateFeatureControllerSubscription: slotId=" + slotId
+                    + ", oldSubId= " + oldSubId + ", subId=" + subId + ", existing feature="
+                    + (f != null));
+            if (SubscriptionManager.isValidSubscriptionId(subId)) {
                 if (f == null) {
                     // A controller doesn't exist for this slot yet.
-                    f = mFeatureFactory.createController(mContext, slotId);
-                    updateSupportedFeatures(f, slotId, newSubId);
+                    f = mFeatureFactory.createController(mContext, slotId, subId);
+                    updateSupportedFeatures(f, slotId, subId);
                     if (f.hasActiveFeatures()) mFeatureControllers.put(slotId, f);
                 } else {
-                    updateSupportedFeatures(f, slotId, newSubId);
+                    updateSupportedFeatures(f, slotId, subId);
                     // Do not keep an empty container around.
                     if (!f.hasActiveFeatures()) {
                         f.destroy();
@@ -222,19 +269,25 @@
                     }
                 }
             }
-            if (f != null) f.updateAssociatedSubscription(newSubId);
+            if (f != null) {
+                if (oldSubId == subId) {
+                    f.onCarrierConfigChangedForSubscription();
+                } else {
+                    f.updateAssociatedSubscription(subId);
+                }
+            }
         }
     }
 
     private RcsFeatureController constructFeatureController(int slotId) {
-        RcsFeatureController c = mFeatureFactory.createController(mContext, slotId);
         int subId = getSubscriptionFromSlot(slotId);
+        RcsFeatureController c = mFeatureFactory.createController(mContext, slotId, subId);
         updateSupportedFeatures(c, slotId, subId);
         return c;
     }
 
     private void updateSupportedFeatures(RcsFeatureController c, int slotId, int subId) {
-        if (doesSubscriptionSupportPresence(subId)) {
+        if (isDeviceUceEnabled() && doesSubscriptionSupportPresence(subId)) {
             if (c.getFeature(UceControllerManager.class) == null) {
                 c.addFeature(mFeatureFactory.createUceControllerManager(mContext, slotId, subId),
                         UceControllerManager.class);
@@ -259,6 +312,20 @@
         if (c.hasActiveFeatures()) c.connect();
     }
 
+    /**
+     * Get whether the device supports RCS User Capability Exchange or not.
+     */
+    public boolean isDeviceUceEnabled() {
+        return mRcsUceEnabled;
+    }
+
+    /**
+     * Set the device supports RCS User Capability Exchange.
+     */
+    public void setDeviceUceEnabled(boolean isEnabled) {
+        mRcsUceEnabled = isEnabled;
+    }
+
     private boolean doesSubscriptionSupportPresence(int subId) {
         if (!SubscriptionManager.isValidSubscriptionId(subId)) return false;
         CarrierConfigManager carrierConfigManager =
diff --git a/src/com/android/services/telephony/rcs/TransportSipMessageValidator.java b/src/com/android/services/telephony/rcs/TransportSipMessageValidator.java
new file mode 100644
index 0000000..777026c
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/TransportSipMessageValidator.java
@@ -0,0 +1,551 @@
+/*
+ * Copyright (C) 2021 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.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConfiguration;
+import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+import android.util.LocalLog;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.services.telephony.rcs.validator.IncomingTransportStateValidator;
+import com.android.services.telephony.rcs.validator.MalformedSipMessageValidator;
+import com.android.services.telephony.rcs.validator.OutgoingTransportStateValidator;
+import com.android.services.telephony.rcs.validator.RestrictedOutgoingSipRequestValidator;
+import com.android.services.telephony.rcs.validator.RestrictedOutgoingSubscribeValidator;
+import com.android.services.telephony.rcs.validator.SipMessageValidator;
+import com.android.services.telephony.rcs.validator.ValidationResult;
+
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Track incoming and outgoing SIP messages passing through this delegate and verify these messages
+ * by doing the following:
+ *  <ul>
+ *    <li>Track the SipDelegate's registration state to ensure that a registration event has
+ *    occurred before allowing outgoing messages. Once it has occurred, filter outgoing SIP messages
+ *    based on the open/restricted feature tag registration state.</li>
+ *    <li>Track the SipDelegate's IMS configuration version and deny any outgoing SipMessages
+ *    associated with a stale IMS configuration version.</li>
+ *    <li>Track the SipDelegate open/close state to allow/deny outgoing messages based on the
+ *    session's state.</li>
+ *    <li>Validate outgoing SIP messages for both restricted request methods as well as restricted/
+ *    malformed headers.</li>
+ * </ul>
+ */
+public class TransportSipMessageValidator {
+
+    private static final String LOG_TAG = "SipMessageV";
+
+    /**
+     * the time in milliseconds that we will wait for SIP sessions to close before we will timeout
+     * and force the sessions to be cleaned up.
+     */
+    private static final int PENDING_CLOSE_TIMEOUT_MS = 1000;
+    /**
+     * time in milliseconds that we will wait for SIP sessions to be closed before we timeout and
+     * force the sessions associated with the deregistering feature tags to be cleaned up.
+     */
+    private static final int PENDING_REGISTRATION_CHANGE_TIMEOUT_MS = 1000;
+
+    /**
+     * Timeouts used in this class that are visible for testing.
+     */
+    @VisibleForTesting
+    public interface Timeouts {
+        /**
+         * @return the time in milliseconds that we will wait for SIP sessions to close before we
+         * will timeout and force the sessions to be cleaned up.
+         */
+        int getPendingCloseTimeoutMs();
+
+        /**
+         * @return the time in milliseconds that we will wait for SIP sessions to be closed before
+         * we timeout and force the sessions associated with the deregistering feature tags to be
+         * cleaned up.
+         */
+        int getPendingRegistrationChangeTimeoutMs();
+    }
+
+    /**
+     * Tracks a pending task that has been scheduled on the associated Executor.
+     */
+    private abstract static class PendingTask implements Runnable {
+
+        private ScheduledFuture<?> mFuture;
+
+        public void scheduleDelayed(ScheduledExecutorService executor, int timeMs) {
+            mFuture = executor.schedule(this, timeMs, TimeUnit.MILLISECONDS);
+        }
+
+        public boolean isDone() {
+            return mFuture != null && mFuture.isDone();
+        }
+
+        public void cancel() {
+            if (mFuture == null) return;
+            mFuture.cancel(false /*interrupt*/);
+        }
+    }
+
+    /**
+     * Tracks a pending reg cleanup task that has been scheduled on the associated Executor.
+     */
+    private abstract static class PendingRegCleanupTask extends PendingTask {
+        public final Set<String> pendingCallIds;
+        public final Set<String> featureTags;
+
+        PendingRegCleanupTask(Set<String> tags, Set<String> callIds) {
+            featureTags = tags;
+            pendingCallIds = callIds;
+        }
+    }
+
+    private final int mSubId;
+    private final ScheduledExecutorService mExecutor;
+    private final LocalLog mLocalLog = new LocalLog(SipTransportController.LOG_SIZE);
+    private final SipSessionTracker mSipSessionTracker;
+    // Validators
+    private final IncomingTransportStateValidator mIncomingTransportStateValidator;
+    private final OutgoingTransportStateValidator mOutgoingTransportStateValidator;
+    private final SipMessageValidator mOutgoingMessageValidator;
+    private final SipMessageValidator mIncomingMessageValidator;
+
+    private Set<String> mSupportedFeatureTags;
+    private Set<FeatureTagState> mDeniedFeatureTags;
+    private long mConfigVersion = -1;
+    private Consumer<Set<String>> mClosingCompleteConsumer;
+    private PendingTask mPendingClose;
+    private PendingRegCleanupTask mPendingRegCleanup;
+    private Consumer<Set<String>> mRegistrationAppliedConsumer;
+
+    public TransportSipMessageValidator(int subId, ScheduledExecutorService executor) {
+        mSubId = subId;
+        mExecutor = executor;
+        mSipSessionTracker = new SipSessionTracker();
+        mOutgoingTransportStateValidator = new OutgoingTransportStateValidator(mSipSessionTracker);
+        mIncomingTransportStateValidator = new IncomingTransportStateValidator();
+        mOutgoingMessageValidator = new MalformedSipMessageValidator().andThen(
+                new RestrictedOutgoingSipRequestValidator()).andThen(
+                new RestrictedOutgoingSubscribeValidator()).andThen(
+                        mOutgoingTransportStateValidator);
+        mIncomingMessageValidator = mIncomingTransportStateValidator;
+    }
+
+    @VisibleForTesting
+    public TransportSipMessageValidator(int subId, ScheduledExecutorService executor,
+            SipSessionTracker sipSessionTracker,
+            OutgoingTransportStateValidator outgoingStateValidator,
+            IncomingTransportStateValidator incomingStateValidator) {
+        mSubId = subId;
+        mExecutor = executor;
+        mSipSessionTracker = sipSessionTracker;
+        mOutgoingTransportStateValidator = outgoingStateValidator;
+        mIncomingTransportStateValidator = incomingStateValidator;
+        mOutgoingMessageValidator = mOutgoingTransportStateValidator;
+        mIncomingMessageValidator = mIncomingTransportStateValidator;
+    }
+
+    /**
+     * Notify this tracker that a registration state change has occurred.
+     * <p>
+     * In some scenarios, this will require that existing SIP dialogs are closed (for example, when
+     * moving a feature tag from REGISTERED->DEREGISTERING). This method allows the caller to
+     * provide a Consumer that will be called when either there are no SIP dialogs active on
+     * DEREGISTERING feature tags, or a timeout has occurred. In the case that a timeout has
+     * occurred, this Consumer will accept a list of callIds that will be manually closed by the
+     * framework to unblock the IMS stack.
+     * <p>
+     * @param stateChangeComplete A one time Consumer that when completed, will contain a List of
+     *         callIds corresponding to SIP Dialogs that have not been closed yet. It is the callers
+     *         responsibility to close the dialogs associated with the provided callIds. If another
+     *         state update occurs before the previous was completed, the previous consumer will be
+     *         completed with an empty list and the new Consumer will be executed when the new state
+     *         changes.
+     * @param regState The new registration state.
+     */
+    public void onRegistrationStateChanged(Consumer<Set<String>> stateChangeComplete,
+            DelegateRegistrationState regState) {
+        if (mRegistrationAppliedConsumer != null) {
+            logw("onRegistrationStateChanged: pending registration change, completing now.");
+            // complete the pending consumer with no dialogs pending, this will be re-evaluated
+            // and new configuration will be applied.
+            cleanupAndNotifyRegistrationAppliedConsumer(Collections.emptySet());
+        }
+        Set<String> restrictedTags = Stream.concat(
+                regState.getDeregisteringFeatureTags().stream(),
+                regState.getDeregisteredFeatureTags().stream()).map(FeatureTagState::getFeatureTag)
+                .collect(Collectors.toSet());
+        mOutgoingTransportStateValidator.restrictFeatureTags(restrictedTags);
+        mRegistrationAppliedConsumer = stateChangeComplete;
+        if (mPendingClose == null || mPendingClose.isDone()) {
+            // Only update the pending registration cleanup task if we do not already have a pending
+            // close in progress.
+            updatePendingRegCleanupTask(restrictedTags);
+        } else {
+            logi("skipping update reg cleanup due to pending close task.");
+        }
+    }
+
+    /**
+     * Notify this tracker that the IMS configuration has changed.
+     *
+     * Parameters contained in the IMS configuration will be used to validate outgoing messages,
+     * such as the configuration version.
+     * @param c The newest IMS configuration.
+     */
+    public void onImsConfigurationChanged(SipDelegateImsConfiguration c) {
+        if (c.getVersion() == mConfigVersion) {
+            return;
+        }
+        logi("onImsConfigurationChanged: " + mConfigVersion + "->" + c.getVersion());
+        mConfigVersion = c.getVersion();
+    }
+
+    /**
+     * Notify this tracker that the IMS configuration has changed.
+     *
+     * Parameters contained in the IMS configuration will be used to validate outgoing messages,
+     * such as the configuration version.
+     * @param c The newest IMS configuration.
+     */
+    public void onConfigurationChanged(SipDelegateConfiguration c) {
+        if (c.getVersion() == mConfigVersion) {
+            return;
+        }
+        logi("onConfigurationChanged: " + mConfigVersion + "->" + c.getVersion());
+        mConfigVersion = c.getVersion();
+    }
+
+    /**
+     * A new message transport has been opened to a SipDelegate.
+     * <p>
+     * Initializes this tracker and resets any state required to process messages.
+     * @param supportedFeatureTags feature tags that are supported and should pass message
+     *                             verification.
+     * @param deniedFeatureTags feature tags that were denied and should fail message verification.
+     */
+    public void onTransportOpened(Set<String> supportedFeatureTags,
+            Set<FeatureTagState> deniedFeatureTags) {
+        logi("onTransportOpened: moving to open state");
+        mSupportedFeatureTags = supportedFeatureTags;
+        mDeniedFeatureTags = deniedFeatureTags;
+        mOutgoingTransportStateValidator.open(supportedFeatureTags, deniedFeatureTags.stream().map(
+                FeatureTagState::getFeatureTag).collect(Collectors.toSet()));
+        mIncomingTransportStateValidator.open();
+    }
+
+    /**
+     * A SIP session has been cleaned up and should no longer be tracked.
+     * @param callId The call ID associated with the SIP session.
+     */
+    public void onSipSessionCleanup(String callId) {
+        mSipSessionTracker.cleanupSession(callId);
+        onCallIdsChanged();
+    }
+
+    /**
+     * Move this tracker into a restricted state, where only outgoing SIP messages associated with
+     * an ongoing SIP Session may be sent. Any out-of-dialog outgoing SIP messages will be denied.
+     * This does not affect incoming SIP messages (for example, an incoming SIP INVITE).
+     * <p>
+     * This tracker will stay in this state until either all open SIP Sessions are closed by the
+     * remote application, or a timeout occurs. Once this happens, the provided Consumer will accept
+     * a List of call IDs associated with the open SIP Sessions that did not close before the
+     * timeout. The caller must then close all open SIP Sessions before closing the transport.
+     * @param closingCompleteConsumer A Consumer that will be called when the transport can be
+     *         closed and may contain a list of callIds associated with SIP sessions that have not
+     *         been closed.
+     * @param closingReason The reason that will be provided if an outgoing out-of-dialog SIP
+     *         message is sent while the transport is closing.
+     * @param closedReason The reason that will be provided if any outgoing SIP message is sent
+     *         once the transport is closed.
+     */
+    public void closeSessionsGracefully(Consumer<Set<String>> closingCompleteConsumer,
+            int closingReason, int closedReason) {
+        if (closingCompleteConsumer == null) {
+            logw("closeSessionsGracefully: unexpected - called with null consumer... closing now");
+            closeSessions(closedReason);
+            return;
+        }
+        if (mClosingCompleteConsumer != null) {
+            // In this case, all we can do is combine the consumers and wait for the other pending
+            // close to complete, finishing both.
+            logw("closeSessionsGracefully: unexpected - existing close pending, combining"
+                    + " consumers.");
+            mClosingCompleteConsumer = callIds -> {
+                mClosingCompleteConsumer.accept(callIds);
+                closingCompleteConsumer.accept(callIds);
+            };
+            return;
+        } else {
+            mClosingCompleteConsumer = closingCompleteConsumer;
+        }
+        if (getTrackedSipSessionCallIds().isEmpty()) {
+            logi("closeSessionsGracefully: moving to closed state now, reason=" + closedReason);
+            closeSessionsInternal(closedReason);
+            cancelClosingTimeoutAndSendComplete(Collections.emptySet());
+            return;
+        }
+        cancelPendingRegCleanupTask();
+        logi("closeSessionsGracefully: moving to restricted state, reason=" + closingReason);
+        mOutgoingTransportStateValidator.restrict(closingReason);
+        mPendingClose = new PendingTask() {
+            @Override
+            public void run() {
+                closeSessions(closingReason);
+            }
+        };
+        mPendingClose.scheduleDelayed(mExecutor, PENDING_CLOSE_TIMEOUT_MS);
+    }
+
+    /**
+     * Close the transport now. If there are any open SIP sessions and this is closed due to a
+     * configuration change (SIM subscription change, user disabled RCS, the service is dead,
+     * etc...) then we will return the call IDs of all open sessions and ask them to be closed.
+     * @param closedReason The error reason for why the message transport was closed that will be
+     *         sent back to the caller if a new SIP message is sent.
+     * @return A List of call IDs associated with sessions that were still open at the time that the
+     * tracker closed the transport.
+     */
+    public Set<String> closeSessions(int closedReason) {
+        Set<String> openCallIds = getTrackedSipSessionCallIds();
+        logi("closeSessions: moving to closed state, reason=" + closedReason + ", open call ids: "
+                + openCallIds);
+        closeSessionsInternal(closedReason);
+        boolean consumerHandledPendingSessions = cancelClosingTimeoutAndSendComplete(openCallIds);
+        if (consumerHandledPendingSessions) {
+            logw("closeSessions: call ID closure handled through consumer");
+            // sent the open call IDs through the pending complete mechanism to unblock any previous
+            // graceful close command and close them early.
+            return Collections.emptySet();
+        }
+        return openCallIds;
+    }
+
+    /**
+     * Verify a new outgoing SIP message before sending to the SipDelegate (ImsService).
+     * @param message The SIP message being verified
+     * @return The result of verifying the outgoing message.
+     */
+
+    public ValidationResult verifyOutgoingMessage(SipMessage message, long configVersion) {
+        if (mConfigVersion != configVersion) {
+            return new ValidationResult(
+                    SipDelegateManager.MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION,
+                    "stale IMS configuration: "  + configVersion + ", expected: "
+                            + mConfigVersion);
+        }
+        ValidationResult result = mOutgoingMessageValidator.validate(message);
+        logi("verifyOutgoingMessage: " + result + ", message=" + message);
+        if (result.isValidated) mSipSessionTracker.filterSipMessage(message);
+        return result;
+    }
+
+    /**
+     * Verify a new incoming SIP message before sending it to the
+     * DelegateConnectionMessageCallback (remote application).
+     * @param message The SipMessage to verify.
+     * @return The result of verifying the incoming message.
+     */
+    public ValidationResult verifyIncomingMessage(SipMessage message) {
+        ValidationResult result = mIncomingMessageValidator.validate(message);
+        logi("verifyIncomingMessage: " + result + ", message=" + message);
+        if (result.isValidated) mSipSessionTracker.filterSipMessage(message);
+        return result;
+    }
+
+    /**
+     * Acknowledge that a pending incoming or outgoing SIP message has been delivered successfully
+     * to the remote.
+     * @param transactionId The transaction ID associated with the message.
+     */
+    public void acknowledgePendingMessage(String transactionId) {
+        logi("acknowledgePendingMessage: id=" + transactionId);
+        mSipSessionTracker.acknowledgePendingMessage(transactionId);
+        onCallIdsChanged();
+    }
+
+    /**
+     * A pending incoming or outgoing SIP message has failed and should not be tracked.
+     * @param transactionId The transaction ID associated with the message.
+     */
+    public void notifyPendingMessageFailed(String transactionId) {
+        logi("notifyPendingMessageFailed: id=" + transactionId);
+        mSipSessionTracker.pendingMessageFailed(transactionId);
+    }
+
+    /** Dump state about this tracker that should be included in the dumpsys */
+    public void dump(PrintWriter printWriter) {
+        IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
+        pw.println("Supported Tags:" + mSupportedFeatureTags);
+        pw.println("Denied Tags:" + mDeniedFeatureTags);
+        pw.println(mOutgoingTransportStateValidator);
+        pw.println(mIncomingTransportStateValidator);
+        pw.println("Reg consumer pending: " + (mRegistrationAppliedConsumer != null));
+        pw.println("Close consumer pending: " + (mClosingCompleteConsumer != null));
+        pw.println();
+        mSipSessionTracker.dump(pw);
+        pw.println();
+        pw.println("Most recent logs:");
+        mLocalLog.dump(printWriter);
+    }
+
+    /**
+     * A event has occurred that can change the list of active call IDs.
+     */
+    private void onCallIdsChanged() {
+        if (getTrackedSipSessionCallIds().isEmpty() && mPendingClose != null
+                && !mPendingClose.isDone()) {
+            logi("onCallIdsChanged: no open sessions, completing any pending close events.");
+            // do not wait for timeout if pending sessions closed.
+            mPendingClose.cancel();
+            mPendingClose.run();
+        }
+        if (mPendingRegCleanup != null && !mPendingRegCleanup.isDone()) {
+            logi("onCallIdsChanged: updating pending reg cleanup task.");
+            // Recalculate the open call IDs based on the same feature tag set in the case that the
+            // call ID change has caused a change in pending reg cleanup task.
+            updatePendingRegCleanupTask(mPendingRegCleanup.featureTags);
+        }
+    }
+
+    /**
+     * If there are any pending registration clean up tasks, cancel them and clean up consumers.
+     */
+    private void cancelPendingRegCleanupTask() {
+        if (mPendingRegCleanup != null && !mPendingRegCleanup.isDone()) {
+            logi("cancelPendingRegCleanupTask: cancelling...");
+            mPendingRegCleanup.cancel();
+        }
+        cleanupAndNotifyRegistrationAppliedConsumer(Collections.emptySet());
+    }
+
+    /**
+     * Update the pending registration change clean up task based on the new set of restricted
+     * feature tags generated from the deregistering/deregistered feature tags.
+     *
+     * <p>
+     * This set of restricted tags will generate a set of call IDs associated to dialogs that
+     * are active and associated with the restricted tags. If there is no pending cleanup task, it
+     * will create a new one. If there was already a pending reg cleanup task, it will compare them
+     * and create a new one and cancel the old one if the new set of call ids is different from the
+     * old one.
+     */
+    private void updatePendingRegCleanupTask(Set<String> restrictedTags) {
+        Set<String> pendingCallIds = mSipSessionTracker.getCallIdsAssociatedWithFeatureTag(
+                restrictedTags);
+        if (pendingCallIds.isEmpty()) {
+            if (mPendingRegCleanup != null && !mPendingRegCleanup.isDone()) {
+                logi("updatePendingRegCleanupTask: no remaining call ids, finish cleanup task "
+                        + "now.");
+                mPendingRegCleanup.cancel();
+                mPendingRegCleanup.run();
+            } else {
+                if (mRegistrationAppliedConsumer != null) {
+                    logi("updatePendingRegCleanupTask: notify no pending call ids.");
+                    cleanupAndNotifyRegistrationAppliedConsumer(Collections.emptySet());
+                }
+            }
+            return;
+        }
+        if (mPendingRegCleanup != null && !mPendingRegCleanup.isDone()) {
+            if (mPendingRegCleanup.pendingCallIds.equals(pendingCallIds)) {
+                logi("updatePendingRegCleanupTask: pending reg change has same set of pending call"
+                        + " IDs, so keeping pending task");
+                return;
+            }
+            logi("updatePendingRegCleanupTask: cancelling, call ids have changed.");
+            mPendingRegCleanup.cancel();
+        }
+        mPendingRegCleanup = new PendingRegCleanupTask(restrictedTags, pendingCallIds) {
+            @Override
+            public void run() {
+                cleanupAndNotifyRegistrationAppliedConsumer(pendingCallIds);
+            }
+        };
+        logi("updatePendingRegCleanupTask: scheduling for call ids: " + pendingCallIds);
+        mPendingRegCleanup.scheduleDelayed(mExecutor, PENDING_REGISTRATION_CHANGE_TIMEOUT_MS);
+    }
+
+    /**
+     * Notify the pending registration applied consumer of the call ids that need to be cleaned up.
+     */
+    private void cleanupAndNotifyRegistrationAppliedConsumer(Set<String> pendingCallIds) {
+        if (mRegistrationAppliedConsumer != null) {
+            mRegistrationAppliedConsumer.accept(pendingCallIds);
+            mRegistrationAppliedConsumer = null;
+        }
+    }
+
+    /**
+     * Cancel any pending timeout to close pending sessions and send the provided call IDs to any
+     * pending closing complete consumer.
+     * @return {@code true} if a consumer was notified, {@code false} if there were no consumers.
+     */
+    private boolean cancelClosingTimeoutAndSendComplete(Set<String> openCallIds) {
+        if (mPendingClose != null && !mPendingClose.isDone()) {
+            logi("completing pending close consumer");
+            mPendingClose.cancel();
+        }
+        // Complete the pending consumer with no open sessions.
+        if (mClosingCompleteConsumer != null) {
+            mClosingCompleteConsumer.accept(openCallIds);
+            mClosingCompleteConsumer = null;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Close and clear all stateful trackers and validators.
+     */
+    private void closeSessionsInternal(int closedReason) {
+        cancelPendingRegCleanupTask();
+        mOutgoingTransportStateValidator.close(closedReason);
+        mIncomingTransportStateValidator.close(closedReason);
+        mSipSessionTracker.clearAllSessions();
+    }
+
+    private Set<String> getTrackedSipSessionCallIds() {
+        return mSipSessionTracker.getTrackedDialogs().stream().map(SipDialog::getCallId)
+                .collect(Collectors.toSet());
+    }
+
+    private void logi(String log) {
+        Log.i(SipTransportController.LOG_TAG, LOG_TAG + "[" + mSubId + "] " + log);
+        mLocalLog.log("[I] " + log);
+    }
+
+    private void logw(String log) {
+        Log.w(SipTransportController.LOG_TAG, LOG_TAG + "[" + mSubId + "] " + log);
+        mLocalLog.log("[W] " + log);
+    }
+}
diff --git a/src/com/android/services/telephony/rcs/UceControllerManager.java b/src/com/android/services/telephony/rcs/UceControllerManager.java
index 09288f1..995d685 100644
--- a/src/com/android/services/telephony/rcs/UceControllerManager.java
+++ b/src/com/android/services/telephony/rcs/UceControllerManager.java
@@ -16,22 +16,26 @@
 
 package com.android.services.telephony.rcs;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.net.Uri;
+import android.telephony.SubscriptionManager;
 import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsContactUceCapability;
 import android.telephony.ims.RcsUceAdapter;
 import android.telephony.ims.RcsUceAdapter.PublishState;
 import android.telephony.ims.aidl.IRcsUceControllerCallback;
 import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
 
 import com.android.ims.RcsFeatureManager;
 import com.android.ims.rcs.uce.UceController;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.PrintWriter;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -50,34 +54,38 @@
     private final Context mContext;
     private final ExecutorService mExecutorService;
 
-    private volatile UceController mUceController;
-    private volatile RcsFeatureManager mRcsFeatureManager;
+    private volatile @Nullable UceController mUceController;
+    private volatile @Nullable RcsFeatureManager mRcsFeatureManager;
 
     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);
+        initUceController(subId);
     }
 
     /**
      * Constructor to inject dependencies for testing.
      */
     @VisibleForTesting
-    public UceControllerManager(Context context, int slotId, int subId, ExecutorService executor) {
+    public UceControllerManager(Context context, int slotId, ExecutorService executor,
+            UceController uceController) {
         mSlotId = slotId;
         mContext = context;
         mExecutorService = executor;
-        mUceController = new UceController(mContext, subId);
+        mUceController = uceController;
     }
 
     @Override
     public void onRcsConnected(RcsFeatureManager manager) {
         mExecutorService.submit(() -> {
             mRcsFeatureManager = manager;
-            mUceController.onRcsConnected(manager);
+            if (mUceController != null) {
+                mUceController.onRcsConnected(manager);
+            } else {
+                Log.d(LOG_TAG, "onRcsConnected: UceController is null");
+            }
         });
     }
 
@@ -85,43 +93,61 @@
     public void onRcsDisconnected() {
         mExecutorService.submit(() -> {
             mRcsFeatureManager = null;
-            mUceController.onRcsDisconnected();
+            if (mUceController != null) {
+                mUceController.onRcsDisconnected();
+            } else {
+                Log.d(LOG_TAG, "onRcsDisconnected: UceController is null");
+            }
         });
     }
 
     @Override
     public void onDestroy() {
-        Log.d(LOG_TAG, "onDestroy");
-        mExecutorService.submit(() -> mUceController.onDestroy());
+        mExecutorService.submit(() -> {
+            Log.d(LOG_TAG, "onDestroy");
+            if (mUceController != null) {
+                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.
+     * This method will be called when the subscription ID associated with the slot has
+     * changed.
      */
     @Override
-    public void onAssociatedSubscriptionUpdated(int subId) {
+    public void onAssociatedSubscriptionUpdated(int newSubId) {
         mExecutorService.submit(() -> {
             Log.i(LOG_TAG, "onAssociatedSubscriptionUpdated: slotId=" + mSlotId
-                    + ", subId=" + subId);
+                    + ", newSubId=" + newSubId);
 
-            // Destroy existing UceController and create a new one.
-            mUceController.onDestroy();
-            mUceController = new UceController(mContext, subId);
+            // Check and create the UceController with the new updated subscription ID.
+            initUceController(newSubId);
 
             // The RCS should be connected when the mRcsFeatureManager is not null. Set it to the
             // new UceController instance.
-            if (mRcsFeatureManager != null) {
+            if (mUceController != null && mRcsFeatureManager != null) {
                 mUceController.onRcsConnected(mRcsFeatureManager);
             }
         });
     }
 
-    @VisibleForTesting
-    public void setUceController(UceController uceController) {
-        mUceController = uceController;
+    /**
+     * This method will be called when the carrier config of the subscription associated with this
+     * manager has changed.
+     */
+    @Override
+    public void onCarrierConfigChanged() {
+        mExecutorService.submit(() -> {
+            Log.i(LOG_TAG, "onCarrierConfigChanged");
+            if (mUceController != null) {
+                mUceController.onCarrierConfigChanged();
+            } else {
+                Log.d(LOG_TAG, "onCarrierConfigChanged: UceController is null");
+            }
+        });
     }
 
     /**
@@ -181,15 +207,15 @@
      * @throws ImsException if the ImsService connected to this controller is currently down.
      */
     public @PublishState int getUcePublishState() throws ImsException {
-        Future future = mExecutorService.submit(() -> {
+        Future<Integer> future = mExecutorService.submit(() -> {
             checkUceControllerState();
             return mUceController.getUcePublishState();
         });
 
         try {
-            return (Integer) future.get();
+            return future.get();
         } catch (ExecutionException | InterruptedException e) {
-            Log.w(LOG_TAG, "requestNetworkAvailability exception: " + e);
+            Log.w(LOG_TAG, "getUcePublishState exception: " + e);
             Throwable cause = e.getCause();
             if (cause instanceof ImsException) {
                 throw (ImsException) cause;
@@ -199,6 +225,167 @@
     }
 
     /**
+     * Add new feature tags to the Set used to calculate the capabilities in PUBLISH.
+     */
+    public RcsContactUceCapability addUceRegistrationOverride(
+            Set<String> featureTags) throws ImsException {
+        Future<RcsContactUceCapability> future = mExecutorService.submit(() -> {
+            checkUceControllerState();
+            return mUceController.addRegistrationOverrideCapabilities(featureTags);
+        });
+
+        try {
+            return future.get();
+        } catch (ExecutionException | InterruptedException e) {
+            Log.w(LOG_TAG, "addUceRegistrationOverride exception: " + e);
+            Throwable cause = e.getCause();
+            if (cause instanceof ImsException) {
+                throw (ImsException) cause;
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Remove existing feature tags to the Set used to calculate the capabilities in PUBLISH.
+     */
+    public RcsContactUceCapability removeUceRegistrationOverride(
+            Set<String> featureTags) throws ImsException {
+        Future<RcsContactUceCapability> future = mExecutorService.submit(() -> {
+            checkUceControllerState();
+            return mUceController.removeRegistrationOverrideCapabilities(featureTags);
+        });
+
+        try {
+            return future.get();
+        } catch (ExecutionException | InterruptedException e) {
+            Log.w(LOG_TAG, "removeUceRegistrationOverride exception: " + e);
+            Throwable cause = e.getCause();
+            if (cause instanceof ImsException) {
+                throw (ImsException) cause;
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Clear all overrides in the Set used to calculate the capabilities in PUBLISH.
+     */
+    public RcsContactUceCapability clearUceRegistrationOverride() throws ImsException {
+        Future<RcsContactUceCapability> future = mExecutorService.submit(() -> {
+            checkUceControllerState();
+            return mUceController.clearRegistrationOverrideCapabilities();
+        });
+
+        try {
+            return future.get();
+        } catch (ExecutionException | InterruptedException e) {
+            Log.w(LOG_TAG, "clearUceRegistrationOverride exception: " + e);
+            Throwable cause = e.getCause();
+            if (cause instanceof ImsException) {
+                throw (ImsException) cause;
+            }
+            return null;
+        }
+    }
+
+    /**
+     * @return current RcsContactUceCapability instance that will be used for PUBLISH.
+     */
+    public RcsContactUceCapability getLatestRcsContactUceCapability() throws ImsException {
+        Future<RcsContactUceCapability> future = mExecutorService.submit(() -> {
+            checkUceControllerState();
+            return mUceController.getLatestRcsContactUceCapability();
+        });
+
+        try {
+            return future.get();
+        } catch (ExecutionException | InterruptedException e) {
+            Log.w(LOG_TAG, "getLatestRcsContactUceCapability exception: " + e);
+            Throwable cause = e.getCause();
+            if (cause instanceof ImsException) {
+                throw (ImsException) cause;
+            }
+            return null;
+        }
+    }
+
+    /**
+     *
+     * @return The last PIDF XML sent to the IMS stack to be published.
+     */
+    public String getLastPidfXml() throws ImsException {
+        Future<String> future = mExecutorService.submit(() -> {
+            checkUceControllerState();
+            return mUceController.getLastPidfXml();
+        });
+
+        try {
+            return future.get();
+        } catch (ExecutionException | InterruptedException e) {
+            Log.w(LOG_TAG, "getLastPidfXml exception: " + e);
+            Throwable cause = e.getCause();
+            if (cause instanceof ImsException) {
+                throw (ImsException) cause;
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Remove UCE requests cannot be sent to the network status.
+     * @return true if this command is successful.
+     */
+    public boolean removeUceRequestDisallowedStatus() throws ImsException {
+        Future<Boolean> future = mExecutorService.submit(() -> {
+            if (mUceController == null) {
+                throw new ImsException("UCE controller is null",
+                        ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+            }
+            mUceController.removeRequestDisallowedStatus();
+            return true;
+        });
+
+        try {
+            return future.get();
+        } catch (ExecutionException | InterruptedException e) {
+            Log.w(LOG_TAG, "removeUceRequestDisallowedStatus exception: " + e);
+            Throwable cause = e.getCause();
+            if (cause instanceof ImsException) {
+                throw (ImsException) cause;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Set the timeout for contact capabilities request.
+     * @param timeoutAfterMs How long when the capabilities request will time up.
+     * @return true if this command is successful.
+     */
+    public boolean setCapabilitiesRequestTimeout(long timeoutAfterMs)  throws ImsException {
+        Future<Boolean> future = mExecutorService.submit(() -> {
+            if (mUceController == null) {
+                throw new ImsException("UCE controller is null",
+                        ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+            }
+            mUceController.setCapabilitiesRequestTimeout(timeoutAfterMs);
+            return true;
+        });
+
+        try {
+            return future.get();
+        } catch (ExecutionException | InterruptedException e) {
+            Log.w(LOG_TAG, "setCapabilitiesRequestTimeout exception: " + e);
+            Throwable cause = e.getCause();
+            if (cause instanceof ImsException) {
+                throw (ImsException) cause;
+            }
+            return false;
+        }
+    }
+
+    /**
      * Register the Publish state changed callback.
      *
      * @throws ImsException if the ImsService connected to this controller is currently down.
@@ -239,6 +426,31 @@
         }
     }
 
+    /**
+     * Initialize the UceController instance associated with the given subscription ID.
+     * The existing UceController will be destroyed if the original subscription ID is different
+     * from the new subscription ID.
+     * If the new subscription ID is invalid, the UceController instance will be null.
+     */
+    private void initUceController(int newSubId) {
+        Log.d(LOG_TAG, "initUceController: newSubId=" + newSubId + ", current UceController subId="
+                + ((mUceController == null) ? "null" : mUceController.getSubId()));
+        if (mUceController == null) {
+            // Create new UceController only when the subscription ID is valid.
+            if (SubscriptionManager.isValidSubscriptionId(newSubId)) {
+                mUceController = new UceController(mContext, newSubId);
+            }
+        } else if (mUceController.getSubId() != newSubId) {
+            // The subscription ID is updated. Remove the old UceController instance.
+            mUceController.onDestroy();
+            mUceController = null;
+            // Create new UceController only when the subscription ID is valid.
+            if (SubscriptionManager.isValidSubscriptionId(newSubId)) {
+                mUceController = new UceController(mContext, newSubId);
+            }
+        }
+    }
+
     private boolean checkUceControllerState() throws ImsException {
         if (mUceController == null || mUceController.isUnavailable()) {
             throw new ImsException("UCE controller is unavailable",
@@ -247,14 +459,26 @@
         return true;
     }
 
+    /**
+     * Get the UceController instance.
+     * <p>
+     * Used for testing ONLY.
+     */
+    @VisibleForTesting
+    public UceController getUceController() {
+        return mUceController;
+    }
 
     @Override
     public void dump(PrintWriter printWriter) {
         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
         pw.println("UceControllerManager" + "[" + mSlotId + "]:");
         pw.increaseIndent();
-        pw.println("UceController available = " + mUceController != null);
-        //TODO: Add dump for UceController
+        if (mUceController != null) {
+            mUceController.dump(pw);
+        } else {
+            pw.println("UceController is null.");
+        }
         pw.decreaseIndent();
     }
 }
diff --git a/src/com/android/services/telephony/rcs/validator/IncomingTransportStateValidator.java b/src/com/android/services/telephony/rcs/validator/IncomingTransportStateValidator.java
new file mode 100644
index 0000000..24ab45e
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/validator/IncomingTransportStateValidator.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2021 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.validator;
+
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+import android.util.ArrayMap;
+
+/**
+ * Tracks the incoming SIP message transport state from the ImsService to the remote IMS
+ * application. Validates incoming SIP messages based on this state.
+ */
+public class IncomingTransportStateValidator implements SipMessageValidator {
+
+    /**
+     * The message transport is closed, meaning there can be no more incoming messages
+     */
+    private static final int STATE_CLOSED = 0;
+
+    /**
+     * The message transport is open and incoming traffic is not restricted.
+     */
+    private static final int STATE_OPEN = 1;
+
+    private static final ArrayMap<Integer, String> ENUM_TO_STRING_MAP  = new ArrayMap<>(2);
+    static {
+        ENUM_TO_STRING_MAP.append(STATE_CLOSED, "CLOSED");
+        ENUM_TO_STRING_MAP.append(STATE_OPEN, "OPEN");
+    }
+
+    private int mState = STATE_CLOSED;
+    private int mReason = SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED;
+
+    /**
+     * The SIP message transport is open and will successfully validate SIP messages.
+     */
+    public void open() {
+        mState = STATE_OPEN;
+        mReason = -1;
+    }
+
+    /**
+     * The SIP message transport is closed for incoming SIP messages.
+     * @param reason The error reason sent in response to any incoming SIP messages requests.
+     */
+    public void close(int reason) {
+        mState = STATE_CLOSED;
+        mReason = reason;
+    }
+
+    @Override
+    public ValidationResult validate(SipMessage message) {
+        if (mState != STATE_OPEN) {
+            return new ValidationResult(mReason,
+                    "incoming transport closed");
+        }
+        return ValidationResult.SUCCESS;
+    }
+
+    @Override
+    public String toString() {
+        return "Incoming Transport State: " + ENUM_TO_STRING_MAP.getOrDefault(mState,
+                String.valueOf(mState)) + ", reason: "
+                + SipDelegateManager.MESSAGE_FAILURE_REASON_STRING_MAP.getOrDefault(mReason,
+                String.valueOf(mReason));
+    }
+}
diff --git a/src/com/android/services/telephony/rcs/validator/MalformedSipMessageValidator.java b/src/com/android/services/telephony/rcs/validator/MalformedSipMessageValidator.java
new file mode 100644
index 0000000..a76ac3e
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/validator/MalformedSipMessageValidator.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 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.validator;
+
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+
+import com.android.internal.telephony.SipMessageParsingUtils;
+
+/**
+ * Validates that the SipMessage is not malformed before sending the message to the vendor
+ * ImsService by ensuring:
+ * <ul>The SipMessage is a valid SIP request or SIP response.</ul>
+ */
+public class MalformedSipMessageValidator implements SipMessageValidator {
+
+    @Override
+    public ValidationResult validate(SipMessage message) {
+        // Verify the request and response start lines are valid.
+        if (!SipMessageParsingUtils.isSipRequest(message.getStartLine())
+                && !SipMessageParsingUtils.isSipResponse(message.getStartLine())) {
+            return new ValidationResult(
+                    SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_START_LINE,
+                    "malformed start line: " + message.getStartLine());
+        }
+        return ValidationResult.SUCCESS;
+    }
+}
diff --git a/src/com/android/services/telephony/rcs/validator/OutgoingTransportStateValidator.java b/src/com/android/services/telephony/rcs/validator/OutgoingTransportStateValidator.java
new file mode 100644
index 0000000..72d22f8
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/validator/OutgoingTransportStateValidator.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2021 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.validator;
+
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.telephony.SipMessageParsingUtils;
+import com.android.services.telephony.rcs.SipDialog;
+import com.android.services.telephony.rcs.SipSessionTracker;
+import com.android.services.telephony.rcs.SipTransportController;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Tracks the state of the outgoing SIP message transport from the remote IMS application to the
+ * ImsService. Used to validate outgoing SIP messages based off of this state.
+ */
+public class OutgoingTransportStateValidator implements SipMessageValidator {
+
+    /**
+     * The message transport is closed, meaning there can be no more outgoing messages
+     */
+    private static final int STATE_CLOSED = 0;
+
+    /**
+     * The message transport is restricted to only in-dialog outgoing traffic
+     */
+    private static final int STATE_RESTRICTED = 1;
+
+    /**
+     * The message transport is open and outgoing traffic is not restricted.
+     */
+    private static final int STATE_OPEN = 2;
+
+    private static final ArrayMap<Integer, String> ENUM_TO_STRING_MAP  = new ArrayMap<>(3);
+    static {
+        ENUM_TO_STRING_MAP.append(STATE_CLOSED, "CLOSED");
+        ENUM_TO_STRING_MAP.append(STATE_RESTRICTED, "RESTRICTED");
+        ENUM_TO_STRING_MAP.append(STATE_OPEN, "OPEN");
+    }
+
+    private final SipSessionTracker mSipSessionTracker;
+    private int mState = STATE_CLOSED;
+    private int mReason = SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED;
+    private Set<String> mAllowedTags = Collections.emptySet();
+    private Set<String> mDeniedTags = Collections.emptySet();
+    private Set<String> mRestrictedFeatureTags;
+
+    public OutgoingTransportStateValidator(SipSessionTracker sessionTracker) {
+        mSipSessionTracker = sessionTracker;
+    }
+
+    /**
+     * The SIP message transport is open and will successfully validate both in and out of dialog
+     * SIP messages.
+     */
+    public void open(Set<String> allowedFeatureTags, Set<String> deniedFeatureTags) {
+        mState = STATE_OPEN;
+        mReason = -1;
+        // This is for validation, so try to reduce matching errors due to upper/lower case.
+        mAllowedTags = allowedFeatureTags.stream().map(String::trim).map(String::toLowerCase)
+                .collect(Collectors.toSet());
+        mDeniedTags = deniedFeatureTags.stream().map(String::trim).map(String::toLowerCase)
+                .collect(Collectors.toSet());
+        mRestrictedFeatureTags = null;
+    }
+
+    /**
+     * Restrict the starting of dialogs for specific feature tags, excluding requests associated
+     * with ongoing sessions.
+     * @param restrictedFeatureTags The feature tags that are deregistering or deregistered and can
+     *                              not have new dialogs associated with them.
+     */
+    public void restrictFeatureTags(Set<String> restrictedFeatureTags) {
+        // This is for validation, so try to reduce matching errors due to upper/lower case.
+        mRestrictedFeatureTags = restrictedFeatureTags.stream().map(String::trim)
+                .map(String::toLowerCase).collect(Collectors.toSet());
+    }
+
+    /**
+     * The SIP message transport is restricted and only allows in-dialog outgoing messages.
+     * @param reason The reason that will be returned to outgoing out-of-dialog SIP messages that
+     *               are denied.
+     */
+    public void restrict(int reason) {
+        mState = STATE_RESTRICTED;
+        mReason = reason;
+    }
+
+    /**
+     * The SIP message transport is closed for outgoing SIP messages.
+     * @param reason The error reason sent in response to any outgoing SIP messages requests.
+     */
+    public void close(int reason) {
+        mState = STATE_CLOSED;
+        mReason = reason;
+        mAllowedTags = Collections.emptySet();
+    }
+
+    @Override
+    public ValidationResult validate(SipMessage message) {
+        switch (mState) {
+            case STATE_CLOSED:
+                return new ValidationResult(mReason, "outgoing transport closed.");
+            case STATE_RESTRICTED:
+                return verifyRestrictedMessage(message);
+            case STATE_OPEN:
+                return verifyOpenMessage(message);
+            default:
+                Log.w(SipTransportController.LOG_TAG, "OutgoingTSV - warning, unexpected state");
+                return ValidationResult.SUCCESS;
+        }
+    }
+
+    public Set<String> getAllowedCallIds() {
+        return Stream.concat(mSipSessionTracker.getEarlyDialogs().stream(),
+                mSipSessionTracker.getConfirmedDialogs().stream()).map(SipDialog::getCallId)
+                .collect(Collectors.toSet());
+    }
+
+    @Override
+    public String toString() {
+        return "Outgoing Transport State: " + ENUM_TO_STRING_MAP.getOrDefault(mState,
+                String.valueOf(mState)) + ", reason: "
+                + SipDelegateManager.MESSAGE_FAILURE_REASON_STRING_MAP.getOrDefault(mReason,
+                String.valueOf(mReason)) + ", allowed tags: " + mAllowedTags + ", restricted tags: "
+                + mRestrictedFeatureTags + ", denied tags: " + mDeniedTags;
+    }
+
+    private ValidationResult verifyOpenMessage(SipMessage m) {
+        // No need to validate responses to requests.
+        if (!SipMessageParsingUtils.isSipRequest(m.getStartLine())) {
+            return ValidationResult.SUCCESS;
+        }
+        if (mRestrictedFeatureTags == null) {
+            return new ValidationResult(SipDelegateManager.MESSAGE_FAILURE_REASON_NOT_REGISTERED,
+                    "no reg state from vendor");
+        }
+        String[] segments = SipMessageParsingUtils.splitStartLineAndVerify(m.getStartLine());
+        if (segments == null) {
+            return new ValidationResult(
+                    SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_START_LINE,
+                    "couldn't parse start line: " + m.getStartLine());
+        }
+        // Only need to validate requests that start dialogs.
+        boolean startsDialog = Arrays.stream(SipSessionTracker.SIP_REQUEST_DIALOG_START_METHODS)
+                .anyMatch(req -> req.equals(segments[0].trim().toLowerCase()));
+        // If part of an existing dialog, then no need to validate.
+        boolean needsFeatureValidation = startsDialog && !getAllowedCallIds()
+                .contains(m.getCallIdParameter());
+        if (needsFeatureValidation) {
+            return validateMessageFeatureTag(m);
+        }
+        return ValidationResult.SUCCESS;
+    }
+
+    /**
+     * Compares the "Accept-Contact" header against the supported/denied feature tags and ensures
+     * that there are no restricted or denied tags included.
+     */
+    private ValidationResult validateMessageFeatureTag(SipMessage m) {
+        Set<String> featureTags = SipMessageParsingUtils.getAcceptContactFeatureTags(
+                m.getHeaderSection());
+        // Get rid of potential formatting issues first.
+        featureTags = featureTags.stream().map(String::toLowerCase).map(String::trim)
+                .collect(Collectors.toSet());
+        long acceptedFeatureTagCount = featureTags.stream()
+                .filter(f -> mAllowedTags.contains(f)).count();
+        long deniedFeatureTagCount = featureTags.stream()
+                .filter(f -> mDeniedTags.contains(f)).count();
+        long restrictedFeatureTagCount = featureTags.stream()
+                .filter(f -> mRestrictedFeatureTags.contains(f)).count();
+        // we should not have any feature tags that are denied/restricted and there should be at
+        // least one accepted tag
+        if (deniedFeatureTagCount > 0) {
+            return new ValidationResult(
+                    SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG,
+                    "contains denied tags in Accept-Contact: " + featureTags);
+        }
+        if (restrictedFeatureTagCount > 0) {
+            return new ValidationResult(
+                    SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG,
+                    "contains restricted tags in Accept-Contact: " + featureTags);
+        }
+
+        if (acceptedFeatureTagCount == 0) {
+            return new ValidationResult(
+                    SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG,
+                    "No Accept-Contact feature tags are in accepted feature tag list: "
+                            + featureTags);
+        }
+
+        return ValidationResult.SUCCESS;
+    }
+
+    private ValidationResult verifyRestrictedMessage(SipMessage m) {
+        // The validator is in the restricted state, so only in dialog requests and all responses
+        // are allowed.
+        if (!SipMessageParsingUtils.isSipRequest(m.getStartLine())) {
+            return ValidationResult.SUCCESS;
+        }
+        String callId = m.getCallIdParameter();
+        if (TextUtils.isEmpty(callId)) {
+            return new ValidationResult(mReason, "empty call id");
+        }
+        Set<String> mAllowedCallIds = getAllowedCallIds();
+        if (!mAllowedCallIds.contains(callId)) {
+            return new ValidationResult(mReason, "call id " + callId + " is not associated with"
+                    + " any active sessions");
+        }
+        return ValidationResult.SUCCESS;
+    }
+}
diff --git a/src/com/android/services/telephony/rcs/validator/RestrictedOutgoingSipRequestValidator.java b/src/com/android/services/telephony/rcs/validator/RestrictedOutgoingSipRequestValidator.java
new file mode 100644
index 0000000..2c2632f
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/validator/RestrictedOutgoingSipRequestValidator.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 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.validator;
+
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+
+import com.android.internal.telephony.SipMessageParsingUtils;
+
+import java.util.Arrays;
+
+/**
+ * Validate that any outgoing SIP request message does not contain methods that are only generated
+ * internally by the ImsService implementation.
+ */
+public class RestrictedOutgoingSipRequestValidator implements SipMessageValidator {
+
+    /**
+     * These SIP requests are always handled by the ImsService and are restricted to being
+     * generated internally. Messages with these request methods should fail validation.
+     */
+    private static final String[] IMS_SERVICE_HANDLED_REQUEST_METHODS = new String[]{
+            "register", "options", "publish"};
+
+    @Override
+    public ValidationResult validate(SipMessage message) {
+        String startLine = message.getStartLine();
+        if (SipMessageParsingUtils.isSipRequest(startLine)) {
+            String[] segments = SipMessageParsingUtils.splitStartLineAndVerify(startLine);
+            if (segments == null) {
+                return new ValidationResult(
+                        SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_START_LINE,
+                        "malformed start line: " + message.getStartLine());
+            }
+            if (Arrays.stream(IMS_SERVICE_HANDLED_REQUEST_METHODS).anyMatch(
+                    s -> segments[0].toLowerCase().contains(s))) {
+                return new ValidationResult(
+                        SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_START_LINE,
+                        "restricted method: " + segments[0]);
+            }
+        }
+        return ValidationResult.SUCCESS;
+    }
+}
diff --git a/src/com/android/services/telephony/rcs/validator/RestrictedOutgoingSubscribeValidator.java b/src/com/android/services/telephony/rcs/validator/RestrictedOutgoingSubscribeValidator.java
new file mode 100644
index 0000000..41074ed
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/validator/RestrictedOutgoingSubscribeValidator.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 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.validator;
+
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+import android.util.Pair;
+
+import com.android.internal.telephony.SipMessageParsingUtils;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Ensure that if there is an outgoing SUBSCRIBE request, that it does not contain the "Event"
+ * header "presence"
+ */
+public class RestrictedOutgoingSubscribeValidator implements SipMessageValidator {
+
+    private static final String SUBSCRIBE_REQUEST = "subscribe";
+    private static final String SUBSCRIBE_EVENT_HEADER = "event";
+    private static final String[] RESTRICTED_EVENTS = new String[]{ "presence" };
+
+
+    @Override
+    public ValidationResult validate(SipMessage message) {
+        if (!SipMessageParsingUtils.isSipRequest(message.getStartLine())) {
+            return ValidationResult.SUCCESS;
+        }
+        String[] requestSegments = SipMessageParsingUtils.splitStartLineAndVerify(
+                message.getStartLine());
+        if (requestSegments == null) {
+            return new ValidationResult(
+                    SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_START_LINE,
+                    "malformed start line: " + message.getStartLine());
+        }
+        // Request-Line  =  Method SP Request-URI SP SIP-Version CRLF, verify Method
+        if (!requestSegments[0].equalsIgnoreCase(SUBSCRIBE_REQUEST)) {
+            return ValidationResult.SUCCESS;
+        }
+
+        List<Pair<String, String>> eventHeaders = SipMessageParsingUtils.parseHeaders(
+                message.getHeaderSection(), true /*stopAtFirstMatch*/, SUBSCRIBE_EVENT_HEADER);
+        if (eventHeaders.size() == 0) {
+            return ValidationResult.SUCCESS;
+        }
+        boolean isRestricted = eventHeaders.stream().map(e -> e.second)
+                .anyMatch(e -> Arrays.asList(RESTRICTED_EVENTS).contains(e.trim().toLowerCase()));
+
+        return isRestricted ? new ValidationResult(
+                SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS,
+                "matched a restricted header field: " + eventHeaders.stream().map(e -> e.second)
+                        .collect(Collectors.toSet())) :
+                ValidationResult.SUCCESS;
+    }
+}
diff --git a/src/com/android/services/telephony/rcs/validator/SipMessageValidator.java b/src/com/android/services/telephony/rcs/validator/SipMessageValidator.java
new file mode 100644
index 0000000..6bbfbf4
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/validator/SipMessageValidator.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 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.validator;
+
+import android.telephony.ims.SipMessage;
+
+/**
+ * Validates a SipMessage and returns the result via an instance of {@link ValidationResult}.
+ */
+public interface SipMessageValidator {
+    /**
+     * Validate that the SipMessage is allowed to be sent to the remote.
+     * @param message The SipMessage being validated.
+     * @return A {@link ValidationResult} that represents whether or not the message was validated.
+     * If not validated, it also returns a reason why the SIP message was not validated.
+     */
+    ValidationResult validate(SipMessage message);
+
+    /**
+     * Compose a SipMessageValidator out of two validators, this validator running before the next
+     * validator.
+     * @param next The next validator that will be run if this validator validates the message
+     *             successfully.
+     * @return A new SipMessageValidator composed of this validator and the next one.
+     */
+    default SipMessageValidator andThen(SipMessageValidator next) {
+        return (SipMessage m) -> {
+            ValidationResult result = validate(m);
+            if (!result.isValidated) return result;
+            return next.validate(m);
+        };
+    }
+}
diff --git a/src/com/android/services/telephony/rcs/validator/ValidationResult.java b/src/com/android/services/telephony/rcs/validator/ValidationResult.java
new file mode 100644
index 0000000..e434163
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/validator/ValidationResult.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 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.validator;
+
+import android.telephony.ims.SipDelegateManager;
+
+/**
+ * Communicates the result of validating whether a SIP message should be sent to a remote based on
+ * the contents of the SIP message as well as if the transport is in an appropriate state for the
+ * intended recipient of the message.
+ */
+public class ValidationResult {
+    public static final ValidationResult SUCCESS = new ValidationResult();
+
+    /**
+     * If {@code true}, the requested SIP message has been validated and may be sent to the remote.
+     * If {@code false}, the SIP message has failed validation and should not be sent to the
+     * remote. The {@link #restrictedReason} field will contain the reason for the validation
+     * failure.
+     */
+    public final boolean isValidated;
+
+    /**
+     * The reason associated with why the SIP message was not verified and generated a
+     * {@code false} result for {@link #isValidated}.
+     */
+    public final int restrictedReason;
+
+    /**
+     * The human readable reason for why the validation failed for logging.
+     */
+    public final String logReason;
+
+    /**
+     * Communicates a validated result of success. Use {@link #SUCCESS} instead.
+     */
+    private ValidationResult() {
+        isValidated = true;
+        restrictedReason = SipDelegateManager.MESSAGE_FAILURE_REASON_UNKNOWN;
+        logReason = "";
+    }
+
+    /**
+     * The result of validating that the SIP Message should be sent.
+     *
+     * @param reason The reason associated with why the SIP message was not validated and
+     *               generated a {@code false} result for {@link #isValidated}.
+     */
+    public ValidationResult(@SipDelegateManager.MessageFailureReason int reason, String log) {
+        isValidated = false;
+        restrictedReason = reason;
+        logReason = log;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder b = new StringBuilder();
+        b.append("ValidationResult{");
+        b.append("validated=");
+        b.append(isValidated);
+        if (!isValidated) {
+            b.append(", restrictedReason=");
+            b.append(restrictedReason);
+            b.append(", logReason=");
+            b.append(logReason);
+        }
+        b.append('}');
+        return b.toString();
+    }
+}
diff --git a/testapps/EmbmsServiceTestApp/Android.bp b/testapps/EmbmsServiceTestApp/Android.bp
index e4a54cb..584e5bd 100644
--- a/testapps/EmbmsServiceTestApp/Android.bp
+++ b/testapps/EmbmsServiceTestApp/Android.bp
@@ -1,4 +1,13 @@
 // Build the Sample Embms Services
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "packages_services_Telephony_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["packages_services_Telephony_license"],
+}
+
 android_app {
     name: "EmbmsTestService",
     srcs: ["src/**/*.java"],
diff --git a/testapps/EmbmsTestDownloadApp/Android.bp b/testapps/EmbmsTestDownloadApp/Android.bp
index 63f4e83..c1b9425 100644
--- a/testapps/EmbmsTestDownloadApp/Android.bp
+++ b/testapps/EmbmsTestDownloadApp/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "packages_services_Telephony_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["packages_services_Telephony_license"],
+}
+
 src_dirs = ["src"]
 res_dirs = ["res"]
 android_test {
diff --git a/testapps/EmbmsTestStreamingApp/Android.bp b/testapps/EmbmsTestStreamingApp/Android.bp
index 814c5ca..9f082ee 100644
--- a/testapps/EmbmsTestStreamingApp/Android.bp
+++ b/testapps/EmbmsTestStreamingApp/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "packages_services_Telephony_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["packages_services_Telephony_license"],
+}
+
 android_test {
     name: "EmbmsTestStreamingApp",
     srcs: ["src/**/*.java"],
diff --git a/testapps/GbaTestApp/Android.bp b/testapps/GbaTestApp/Android.bp
index cb6df4e..b3c45dd 100644
--- a/testapps/GbaTestApp/Android.bp
+++ b/testapps/GbaTestApp/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "packages_services_Telephony_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["packages_services_Telephony_license"],
+}
+
 android_test {
     name: "GbaTestApp",
     static_libs: [
diff --git a/testapps/GbaTestApp/res/values-es/strings.xml b/testapps/GbaTestApp/res/values-es/strings.xml
index bde5d92..269ea86 100644
--- a/testapps/GbaTestApp/res/values-es/strings.xml
+++ b/testapps/GbaTestApp/res/values-es/strings.xml
@@ -10,7 +10,7 @@
     <string name="button_name_exit" msgid="8025683733431538975">"Salir"</string>
     <string name="label_test_result" msgid="892984695972956196">"Resultado de la prueba"</string>
     <string name="button_name_clear" msgid="436313515327318537">"Restablecer"</string>
-    <string name="button_name_done" msgid="6030406534322497491">"Listo"</string>
+    <string name="button_name_done" msgid="6030406534322497491">"Hecho"</string>
     <string name="title_activity_carrier_config" msgid="7807803900475957717">"CarrierConfigActivity"</string>
     <string name="title_activity_service_config" msgid="5394662739555577279">"ServiceConfigActivity"</string>
     <string name="title_activity_test_config" msgid="7585204485536359713">"TestConfigActivity"</string>
diff --git a/testapps/ImsTestService/Android.bp b/testapps/ImsTestService/Android.bp
index a0b4edb..7073749 100644
--- a/testapps/ImsTestService/Android.bp
+++ b/testapps/ImsTestService/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "packages_services_Telephony_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["packages_services_Telephony_license"],
+}
+
 android_app {
     name: "ImsTestApp",
     static_libs: [
diff --git a/testapps/SmsManagerTestApp/Android.bp b/testapps/SmsManagerTestApp/Android.bp
index 5333eab..4d4afcb 100644
--- a/testapps/SmsManagerTestApp/Android.bp
+++ b/testapps/SmsManagerTestApp/Android.bp
@@ -1,5 +1,16 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "packages_services_Telephony_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["packages_services_Telephony_license"],
+}
+
 android_app {
     name: "SmsManagerTestApp",
     srcs: ["src/**/*.java"],
-    sdk_version: "current",
+    platform_apis: true,
+    certificate: "platform",
+    privileged: true,
 }
diff --git a/testapps/SmsManagerTestApp/AndroidManifest.xml b/testapps/SmsManagerTestApp/AndroidManifest.xml
index 7dc717f..1be2b2d 100644
--- a/testapps/SmsManagerTestApp/AndroidManifest.xml
+++ b/testapps/SmsManagerTestApp/AndroidManifest.xml
@@ -21,6 +21,7 @@
          android:targetSdkVersion="29"/>
     <uses-permission android:name="android.permission.SEND_SMS"/>
     <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+    <uses-permission android:name="android.permission.PERFORM_IMS_SINGLE_REGISTRATION"/>
     <application android:label="SmsManagerTestApp">
         <activity android:name=".SmsManagerTestApp"
              android:label="SmsManagerTestApp"
@@ -54,6 +55,7 @@
 
         <!-- Fake BroadcastReceiver that listens for incoming SMS messages -->
         <receiver android:name=".SmsReceiver"
+                  android:exported="true"
                   android:permission="android.permission.BROADCAST_SMS">
             <intent-filter>
                 <action android:name="android.provider.Telephony.SMS_DELIVER" />
@@ -62,7 +64,8 @@
 
         <!-- Fake BroadcastReceiver that listens for incoming MMS messages -->
         <receiver android:name=".MmsReceiver"
-                  android:permission="android.permission.BROADCAST_WAP_PUSH">
+                  android:permission="android.permission.BROADCAST_WAP_PUSH"
+                  android:exported="true">
             <intent-filter>
                 <action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
                 <data android:mimeType="application/vnd.wap.mms-message" />
@@ -70,7 +73,7 @@
         </receiver>
 
         <!-- Fake Activity that allows the user to send new SMS/MMS messages -->
-        <activity android:name=".ComposeSmsActivity" >
+        <activity android:name=".ComposeSmsActivity" android:exported="true" >
             <intent-filter>
                 <action android:name="android.intent.action.SEND" />
                 <action android:name="android.intent.action.SENDTO" />
diff --git a/testapps/SmsManagerTestApp/res/layout/activity_main.xml b/testapps/SmsManagerTestApp/res/layout/activity_main.xml
index 185e0e2..ddaf3a6 100644
--- a/testapps/SmsManagerTestApp/res/layout/activity_main.xml
+++ b/testapps/SmsManagerTestApp/res/layout/activity_main.xml
@@ -75,5 +75,11 @@
             android:layout_height="wrap_content"
             android:paddingRight="4dp"
             android:text="@string/disable_persistent_service"/>
+        <Button
+            android:id="@+id/check_single_reg_permission"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingRight="4dp"
+            android:text="@string/check_single_reg_permission"/>
     </LinearLayout>
 </LinearLayout>
\ No newline at end of file
diff --git a/testapps/SmsManagerTestApp/res/values/donottranslate_strings.xml b/testapps/SmsManagerTestApp/res/values/donottranslate_strings.xml
index c8b9f91..ca07c54 100644
--- a/testapps/SmsManagerTestApp/res/values/donottranslate_strings.xml
+++ b/testapps/SmsManagerTestApp/res/values/donottranslate_strings.xml
@@ -21,4 +21,5 @@
     <string name="get_sub_for_result_button">Ask user for sub id.</string>
     <string name="enable_persistent_service">Enable Persistent Service</string>
     <string name="disable_persistent_service">Disable Persistent Service</string>
+    <string name="check_single_reg_permission">Check RCS Single Reg Perm</string>
 </resources>
\ No newline at end of file
diff --git a/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestApp.java b/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestApp.java
index 785fe3f..cc3769e 100644
--- a/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestApp.java
+++ b/testapps/SmsManagerTestApp/src/com/android/phone/testapps/smsmanagertestapp/SmsManagerTestApp.java
@@ -44,6 +44,10 @@
     private static final ComponentName SETTINGS_SUB_PICK_ACTIVITY = new ComponentName(
             "com.android.settings", "com.android.settings.sim.SimDialogActivity");
 
+    // Can't import PERFORM_IMS_SINGLE_REGISTRATION const directly beause it's a @SystemApi
+    private static final String PERFORM_IMS_SINGLE_REGISTRATION =
+            "android.permission.PERFORM_IMS_SINGLE_REGISTRATION";
+
     /*
      * Forwarded constants from SimDialogActivity.
      */
@@ -70,6 +74,8 @@
                 .setOnClickListener(this::setPersistentServiceComponentEnabled);
         findViewById(R.id.disable_persistent_service)
                 .setOnClickListener(this::setPersistentServiceComponentDisabled);
+        findViewById(R.id.check_single_reg_permission)
+                .setOnClickListener(this::checkSingleRegPermission);
         mPhoneNumber = (EditText) findViewById(R.id.phone_number_text);
     }
 
@@ -201,6 +207,17 @@
                 PackageManager.DONT_KILL_APP);
     }
 
+    private void checkSingleRegPermission(View view) {
+        if (checkSelfPermission(PERFORM_IMS_SINGLE_REGISTRATION)
+                == PackageManager.PERMISSION_GRANTED) {
+            Toast.makeText(this, "Single Reg permission granted",
+                    Toast.LENGTH_SHORT).show();
+        } else {
+            Toast.makeText(this, "Single Reg permission NOT granted",
+                    Toast.LENGTH_SHORT).show();
+        }
+
+    }
 
     private Intent getSendStatusIntent() {
         // Encode requestId in intent data
diff --git a/testapps/TelephonyManagerTestApp/Android.bp b/testapps/TelephonyManagerTestApp/Android.bp
index 8a37c99..e95d62f 100644
--- a/testapps/TelephonyManagerTestApp/Android.bp
+++ b/testapps/TelephonyManagerTestApp/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "packages_services_Telephony_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["packages_services_Telephony_license"],
+}
+
 android_test {
     name: "TelephonyManagerTestApp",
     srcs: ["src/**/*.java"],
diff --git a/testapps/TelephonyRegistryTestApp/Android.bp b/testapps/TelephonyRegistryTestApp/Android.bp
index fec5286..2439461 100644
--- a/testapps/TelephonyRegistryTestApp/Android.bp
+++ b/testapps/TelephonyRegistryTestApp/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "packages_services_Telephony_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["packages_services_Telephony_license"],
+}
+
 android_test {
     name: "TelephonyRegistryTestApp",
     srcs: ["src/**/*.java"],
diff --git a/testapps/TestRcsApp/OWNERS b/testapps/TestRcsApp/OWNERS
new file mode 100644
index 0000000..1d0d52b
--- /dev/null
+++ b/testapps/TestRcsApp/OWNERS
@@ -0,0 +1,3 @@
+allenwtsu@google.com
+calvinpan@google.com
+jamescflin@google.com
diff --git a/testapps/TestRcsApp/TestApp/Android.bp b/testapps/TestRcsApp/TestApp/Android.bp
new file mode 100644
index 0000000..cda7d17
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/Android.bp
@@ -0,0 +1,40 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "packages_services_Telephony_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["packages_services_Telephony_license"],
+}
+
+android_app {
+    name: "TestRcsApp",
+
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    static_libs: [
+        "androidx-constraintlayout_constraintlayout",
+        "aosp_test_rcs_client_base",
+        "androidx.appcompat_appcompat",
+        "libphonenumber-platform"
+    ],
+
+    libs: ["org.apache.http.legacy"],
+
+    certificate: "platform",
+    privileged: true,
+    product_specific: true,
+
+    sdk_version: "system_current",
+    min_sdk_version: "30",
+    required: ["privapp-permissions-com.google.android.sample.rcsclient.xml"]
+}
+
+prebuilt_etc {
+    name: "privapp-permissions-com.google.android.sample.rcsclient.xml",
+    src: "etc/permissions/privapp-permissions-com.google.android.sample.rcsclient.xml",
+    sub_dir:"permissions",
+    product_specific: true,
+}
diff --git a/testapps/TestRcsApp/TestApp/AndroidManifest.xml b/testapps/TestRcsApp/TestApp/AndroidManifest.xml
new file mode 100644
index 0000000..7538df7
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/AndroidManifest.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //packages/services/Telephony/testapps/TestRcsApp/TestApp/AndroidManifest.xml
+**
+** Copyright 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.
+*/
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.google.android.sample.rcsclient"
+    android:versionCode="13"
+    android:versionName="1.0.12">
+
+    <uses-sdk
+        android:minSdkVersion="30"
+        android:targetSdkVersion="30" />
+
+    <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
+    <!-- Granted by SMS role-->
+    <uses-permission android:name="android.permission.PERFORM_IMS_SINGLE_REGISTRATION" />
+    <!-- Required for UCE and granted by SMS role -->
+    <uses-permission android:name="android.permission.READ_CONTACTS" />
+    <uses-permission android:name="android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:roundIcon="@mipmap/ic_launcher_round"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme">
+        <activity android:name=".MainActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".DelegateActivity" />
+        <activity android:name=".UceActivity" />
+        <activity android:name=".GbaActivity" />
+        <activity android:name=".PhoneNumberActivity" />
+        <activity android:name=".ChatActivity" />
+        <activity android:name=".ContactListActivity" />
+        <activity android:name=".ProvisioningActivity" />
+        <activity android:name=".FileUploadActivity" />
+
+        <provider
+            android:name=".util.ChatProvider"
+            android:authorities="rcsprovider" />
+
+
+        <!-- In order to make this App eligible to be selected as the default Message App, the
+             following components are required to be declared even if they are not implemented.
+        -->
+
+        <!-- BroadcastReceiver that listens for incoming SMS messages -->
+        <receiver
+            android:name=".SmsReceiver"
+            android:permission="android.permission.BROADCAST_SMS">
+            <intent-filter>
+                <action android:name="android.provider.Telephony.SMS_DELIVER" />
+            </intent-filter>
+        </receiver>
+
+        <!-- BroadcastReceiver that listens for incoming MMS messages -->
+        <receiver
+            android:name=".MmsReceiver"
+            android:permission="android.permission.BROADCAST_WAP_PUSH">
+            <intent-filter>
+                <action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
+                <data android:mimeType="application/vnd.wap.mms-message" />
+            </intent-filter>
+        </receiver>
+
+        <!-- Activity that allows the user to send new SMS/MMS messages -->
+        <activity android:name=".ComposeSmsActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.SEND" />
+                <action android:name="android.intent.action.SENDTO" />
+
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+
+                <data android:scheme="sms" />
+                <data android:scheme="smsto" />
+                <data android:scheme="mms" />
+                <data android:scheme="mmsto" />
+            </intent-filter>
+        </activity>
+
+        <!-- Service that delivers messages from the phone "quick response" -->
+        <service
+            android:name=".HeadlessSmsSendService"
+            android:exported="true"
+            android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE">
+            <intent-filter>
+                <action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
+                <category android:name="android.intent.category.DEFAULT" />
+
+                <data android:scheme="sms" />
+                <data android:scheme="smsto" />
+                <data android:scheme="mms" />
+                <data android:scheme="mmsto" />
+            </intent-filter>
+        </service>
+
+    </application>
+
+</manifest>
diff --git a/testapps/TestRcsApp/TestApp/etc/permissions/privapp-permissions-com.google.android.sample.rcsclient.xml b/testapps/TestRcsApp/TestApp/etc/permissions/privapp-permissions-com.google.android.sample.rcsclient.xml
new file mode 100644
index 0000000..4064db4
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/etc/permissions/privapp-permissions-com.google.android.sample.rcsclient.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 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.
+  -->
+<permissions>
+    <privapp-permissions package="com.google.android.sample.rcsclient">
+        <permission name="android.permission.MODIFY_PHONE_STATE"/>
+        <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+        <permission name="android.permission.ACCESS_NETWORK_STATE"/>
+        <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
+    </privapp-permissions>
+</permissions>
\ No newline at end of file
diff --git a/testapps/TestRcsApp/TestApp/res/drawable-v24/ic_launcher_foreground.xml b/testapps/TestRcsApp/TestApp/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..fc0c6ab
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,42 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <path
+        android:fillType="evenOdd"
+        android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,
+        49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
+        android:strokeWidth="1"
+        android:strokeColor="#00000000">
+        <aapt:attr name="android:fillColor">
+            <gradient
+                android:endX="78.5885"
+                android:endY="90.9159"
+                android:startX="48.7653"
+                android:startY="61.0927"
+                android:type="linear">
+                <item
+                    android:color="#44000000"
+                    android:offset="0.0" />
+                <item
+                    android:color="#00000000"
+                    android:offset="1.0" />
+            </gradient>
+        </aapt:attr>
+    </path>
+    <path
+        android:fillColor="#FFFFFF"
+        android:fillType="nonZero"
+        android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,
+        50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,
+        37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,
+        42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,
+        40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,
+        52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,
+        56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,
+        52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
+        android:strokeWidth="1"
+        android:strokeColor="#00000000" />
+</vector>
diff --git a/testapps/TestRcsApp/TestApp/res/drawable/ic_launcher_background.xml b/testapps/TestRcsApp/TestApp/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <path
+        android:fillColor="#3DDC84"
+        android:pathData="M0,0h108v108h-108z" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M9,0L9,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,0L19,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,0L29,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,0L39,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,0L49,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,0L59,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,0L69,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,0L79,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M89,0L89,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M99,0L99,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,9L108,9"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,19L108,19"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,29L108,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,39L108,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,49L108,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,59L108,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,69L108,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,79L108,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,89L108,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,99L108,99"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,29L89,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,39L89,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,49L89,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,59L89,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,69L89,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,79L89,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,19L29,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,19L39,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,19L49,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,19L59,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,19L69,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,19L79,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+</vector>
diff --git a/testapps/TestRcsApp/TestApp/res/layout/activity_main.xml b/testapps/TestRcsApp/TestApp/res/layout/activity_main.xml
new file mode 100644
index 0000000..939feb0
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/layout/activity_main.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".MainActivity">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <Button
+            android:id="@+id/provision"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/provisioning_test"
+            android:textAlignment="center"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/delegate"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/delegate_test"
+            android:textAlignment="center"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/uce"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/uce_test"
+            android:textAlignment="center"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/gba"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/gba_test"
+            android:textAlignment="center"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/msgClient"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/test_msg_client"
+            android:textAlignment="center"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/uploadFile"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/upload_file_gba"
+            android:textAlignment="center"
+            android:textAllCaps="false" />
+
+        <TextView
+            android:id="@+id/version_info"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/version_info"
+            android:textAlignment="center"
+            android:paddingTop="7dp"/>
+    </LinearLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/testapps/TestRcsApp/TestApp/res/layout/chat_layout.xml b/testapps/TestRcsApp/TestApp/res/layout/chat_layout.xml
new file mode 100644
index 0000000..e184b04
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/layout/chat_layout.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/session_tips"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="15dp"
+        android:textStyle="bold" />
+
+    <LinearLayout
+        android:id="@id/title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:layout_below="@+id/session_tips">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/to"
+            android:textSize="15dp"
+            android:textStyle="bold" />
+
+        <EditText
+            android:id="@+id/destNum"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:inputType="phone"
+            android:digits="0123456789+"
+            android:hint="+15555551212" />
+    </LinearLayout>
+
+
+    <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@+id/title">
+
+        <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+            android:id="@+id/relative_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"></RelativeLayout>
+    </ScrollView>
+
+    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true">
+
+        <EditText
+            android:id="@+id/new_msg"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_toLeftOf="@+id/chat_btn"
+            android:text="@string/chat_message" />
+
+        <Button
+            android:id="@+id/chat_btn"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentRight="true"
+            android:text="@string/send" />
+    </RelativeLayout>
+
+</RelativeLayout>
diff --git a/testapps/TestRcsApp/TestApp/res/layout/contact_list.xml b/testapps/TestRcsApp/TestApp/res/layout/contact_list.xml
new file mode 100644
index 0000000..0117549
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/layout/contact_list.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/tips"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="15dp"
+        android:textStyle="bold" />
+
+    <Button
+        android:id="@+id/start_chat_btn"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="10dp"
+        android:layout_marginBottom="10dp"
+        android:text="@string/start_chat"
+        android:textAllCaps="false" />
+
+    <ListView
+        android:id="@+id/listview"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_alignParentBottom="true" />
+
+</LinearLayout>
diff --git a/testapps/TestRcsApp/TestApp/res/layout/delegate_layout.xml b/testapps/TestRcsApp/TestApp/res/layout/delegate_layout.xml
new file mode 100644
index 0000000..94d6efa
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/layout/delegate_layout.xml
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".DelegateActivity">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:orientation="vertical">
+
+                <CheckBox
+                    android:id="@+id/standalone-pager"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/standalone_pager" />
+
+                <CheckBox
+                    android:id="@+id/standalone-large"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/standalone_large" />
+
+                <CheckBox
+                    android:id="@+id/standalone-deferred"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/standalone_deferred" />
+
+                <CheckBox
+                    android:id="@+id/standalone-pager-large"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/standalone_pager_large" />
+
+                <CheckBox
+                    android:id="@+id/chat"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/chat" />
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:orientation="vertical">
+
+                <CheckBox
+                    android:id="@+id/file_transfer"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/file_transfer" />
+
+                <CheckBox
+                    android:id="@+id/geolocation_sms"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/geolocation_sms" />
+
+                <CheckBox
+                    android:id="@+id/chatbot_session"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/chatbot_session" />
+
+                <CheckBox
+                    android:id="@+id/chatbot_standalone"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/chatbot_standalone" />
+
+                <CheckBox
+                    android:id="@+id/chatbot_version"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/chatbot_version" />
+            </LinearLayout>
+        </LinearLayout>
+
+        <Button
+            android:id="@+id/init_btn"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:text="@string/initialize_delegate"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/destroy_btn"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:text="@string/destroy_delegate"
+            android:textAllCaps="false" />
+
+        <TextView
+            android:id="@+id/delegate_callback_result"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="30dp"
+            android:scrollbars="vertical"
+            android:text="@string/callback_result"
+            android:textSize="15dp"
+            android:textStyle="bold" />
+
+    </LinearLayout>
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/testapps/TestRcsApp/TestApp/res/layout/file_upload_layout.xml b/testapps/TestRcsApp/TestApp/res/layout/file_upload_layout.xml
new file mode 100644
index 0000000..a41376b
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/layout/file_upload_layout.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 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
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".FileUploadActivity">
+
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/server"
+                android:textSize="15dp"
+                android:textStyle="bold" />
+
+            <EditText
+                android:id="@+id/ft_uri"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textSize="15dp" />
+        </LinearLayout>
+
+        <Button
+            android:id="@+id/browse_btn"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:text="@string/browse"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/upload_btn"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:text="@string/upload"
+            android:textAllCaps="false" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <TextView
+                android:text="@string/file_name"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textSize="15dp"
+                android:textStyle="bold"/>
+            <TextView
+                android:id="@+id/file_name"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textSize="15dp"
+                android:textStyle="bold"/>
+        </LinearLayout>
+
+        <TextView
+            android:id="@+id/upload_file_result"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/result"
+            android:scrollbars="vertical"
+            android:layout_marginTop="20dp"
+            android:textSize="15dp"
+            android:textStyle="bold" />
+
+    </LinearLayout>
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/testapps/TestRcsApp/TestApp/res/layout/gba_layout.xml b/testapps/TestRcsApp/TestApp/res/layout/gba_layout.xml
new file mode 100644
index 0000000..f9866e8
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/layout/gba_layout.xml
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".GbaActivity">
+
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/organization"
+                android:textSize="15dp"
+                android:textStyle="bold" />
+
+            <Spinner
+                android:id="@+id/organization_list"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:layout_marginBottom="10dp"
+            android:orientation="horizontal">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/uicc_type"
+                android:textSize="15dp"
+                android:textStyle="bold" />
+
+            <Spinner
+                android:id="@+id/uicc_list"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:layout_marginBottom="10dp"
+            android:orientation="horizontal">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/protocol"
+                android:textSize="15dp"
+                android:textStyle="bold" />
+
+            <Spinner
+                android:id="@+id/protocol_list"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:layout_marginBottom="10dp"
+            android:orientation="horizontal">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/tls_cs"
+                android:textSize="15dp"
+                android:textStyle="bold" />
+
+            <EditText
+                android:id="@+id/tls_id"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:inputType="number"
+                android:text="47"
+                android:textSize="15dp" />
+        </LinearLayout>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/naf"
+            android:textSize="15dp"
+            android:textStyle="bold" />
+
+        <EditText
+            android:id="@+id/naf_url"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="15dp" />
+
+        <Button
+            android:id="@+id/gba_btn"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:layout_marginBottom="10dp"
+            android:text="@string/gba_bootstrap"
+            android:textAllCaps="false" />
+
+        <TextView
+            android:id="@+id/gba_result"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:scrollbars="vertical"
+            android:text="@string/result"
+            android:textSize="15dp"
+            android:textStyle="bold" />
+    </LinearLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/testapps/TestRcsApp/TestApp/res/layout/number_to_chat.xml b/testapps/TestRcsApp/TestApp/res/layout/number_to_chat.xml
new file mode 100644
index 0000000..7e31581
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/layout/number_to_chat.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/to"
+            android:textSize="15dp"
+            android:textStyle="bold" />
+
+        <EditText
+            android:id="@+id/destNum"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:inputType="phone"
+            android:digits="0123456789+"
+            android:hint="+15555551212" />
+    </LinearLayout>
+
+    <Button
+        android:id="@+id/launch_chat_btn"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/ok" />
+
+</LinearLayout>
diff --git a/testapps/TestRcsApp/TestApp/res/layout/provision_layout.xml b/testapps/TestRcsApp/TestApp/res/layout/provision_layout.xml
new file mode 100644
index 0000000..47f534a
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/layout/provision_layout.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ProvisionActivity">
+
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/rcs_profile"
+                android:textSize="15dp"
+                android:textStyle="bold" />
+
+            <Spinner
+                android:id="@+id/rcs_profile_list"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+        </LinearLayout>
+
+        <Button
+            android:id="@+id/provisioning_register_btn"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:text="@string/register_provisioning_callback"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/provisioning_unregister_btn"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:text="@string/unregister_provisioning_callback"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/provisioning_singlereg_btn"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:layout_marginBottom="10dp"
+            android:text="@string/isRcsVolteSingleRegCapable"
+            android:textAllCaps="false" />
+
+        <TextView
+            android:id="@+id/provisioning_singlereg_result"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/result"
+            android:textSize="15dp"
+            android:textStyle="bold" />
+
+        <TextView
+            android:id="@+id/provisioning_callback_result"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:scrollbars="vertical"
+            android:text="@string/callback_result"
+            android:textSize="15dp"
+            android:textStyle="bold" />
+
+    </LinearLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/testapps/TestRcsApp/TestApp/res/layout/uce_layout.xml b/testapps/TestRcsApp/TestApp/res/layout/uce_layout.xml
new file mode 100644
index 0000000..a4e6ff2
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/layout/uce_layout.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".UceActivity">
+
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/uce_description"
+            android:textSize="15dp"
+            android:textStyle="bold" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/number"
+                android:textSize="15dp"
+                android:textStyle="bold" />
+
+            <EditText
+                android:id="@+id/number_list"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:inputType="phone"
+                android:digits="0123456789+,"
+                android:hint="+16505551212,+16505551213" />
+        </LinearLayout>
+
+        <Button
+            android:id="@+id/capability_btn"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:text="@string/request_capability"
+            android:textAllCaps="false" />
+
+        <Button
+            android:id="@+id/availability_btn"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:layout_marginBottom="10dp"
+            android:text="@string/request_availability"
+            android:textAllCaps="false" />
+
+        <TextView
+            android:id="@+id/capability_result"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/result"
+            android:scrollbars="vertical"
+            android:textSize="15dp"
+            android:textStyle="bold" />
+    </LinearLayout>
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/testapps/TestRcsApp/TestApp/res/mipmap-anydpi-v26/ic_launcher.xml b/testapps/TestRcsApp/TestApp/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/testapps/TestRcsApp/TestApp/res/mipmap-anydpi-v26/ic_launcher_round.xml b/testapps/TestRcsApp/TestApp/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/testapps/TestRcsApp/TestApp/res/values-as/strings.xml b/testapps/TestRcsApp/TestApp/res/values-as/strings.xml
new file mode 100644
index 0000000..2fac78c
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-as/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"RcsClient"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"প্ৰ’ভিজনিং পৰীক্ষা"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"অধিকাৰ প্ৰদান কৰাৰ পৰীক্ষা"</string>
+    <string name="uce_test" msgid="6466919844953883038">"UCE পৰীক্ষা"</string>
+    <string name="gba_test" msgid="8786745991279224003">"GBA পৰীক্ষা"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"TestMessageClient"</string>
+    <string name="db_client" msgid="5131401341721936367">"DBClient"</string>
+    <string name="result" msgid="435222369457564619">"ফলাফল:"</string>
+    <string name="callback_result" msgid="7162990408241244749">"কলবেকৰ ফলাফল:"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"initializeSipDelegate"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"destroySipDelegate"</string>
+    <string name="uce_description" msgid="1117661457446955869">"সন্ধানৰ সামৰ্থ্যত নম্বৰটো দিয়ক আৰু বহুকেইটা থাকিলে \',\'ৰ দ্বাৰা পৃথক কৰক।"</string>
+    <string name="number" msgid="9096578126043995890">"সংখ্যা:"</string>
+    <string name="request_capability" msgid="7644752985651089411">"requestCapability"</string>
+    <string name="request_availability" msgid="6387685341316953239">"requestNetworkAvailability"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"bootstrapAuthenticationRequest"</string>
+    <string name="start_chat" msgid="96121236227112396">"চাট আৰম্ভ কৰক"</string>
+    <string name="to" msgid="1156781884609653284">"প্ৰতি:"</string>
+    <string name="chat_message" msgid="3945528453939746046">"চাট বার্তা"</string>
+    <string name="send" msgid="8797467615003240434">"পঠিয়াওক"</string>
+    <string name="ok" msgid="2206671077510386729">"ঠিক আছে"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"ছেশ্বন আৰম্ভ কৰাত সফল হৈছে"</string>
+    <string name="session_failed" msgid="4524986240102190509">"ছেশ্বন আৰম্ভ কৰাত বিফল হৈছে"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"ছেশ্বন সাজু নহয়"</string>
+    <string name="organization" msgid="913518283470119113">"সংস্থা:"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"UICCৰ ধৰণ:"</string>
+    <string name="protocol" msgid="8626139294022821930">"প্ৰট’কল:"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"TLS চাইফাৰ ছুইট:"</string>
+    <string name="naf" msgid="7620247915001854662">"NAF URI:"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"ষ্টেণ্ডএল’ন পেজাৰ"</string>
+    <string name="standalone_large" msgid="336170805696694543">"ষ্টেণ্ডএল’ন ডাঙৰ"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"ষ্টেণ্ডএল’ন স্থগিত ৰখা হৈছে"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"ষ্টেণ্ডএল’ন ডাঙৰ পেজাৰ"</string>
+    <string name="chat" msgid="5500583808773151982">"চাট"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"ফাইল স্থানান্তৰণ"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"জিঅ’লোকেশ্বনৰ এছএমএছ"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"চাটব’ট ছেশ্বন"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"ষ্টেণ্ডএল’ন চাটব’ট"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"চাটব’টৰ সংস্কৰণ"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"প্ৰ’ভিজনিং সম্পন্ন হৈছে"</string>
+    <string name="registration_done" msgid="492878616836655421">"পঞ্জীয়ন সম্পন্ন হৈছে"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"একো নাই"</item>
+    <item msgid="7841117287115880098">"3GPP"</item>
+    <item msgid="5871357530951196401">"3GPP2"</item>
+    <item msgid="4979706772323445701">"OMA"</item>
+    <item msgid="1202873556413702218">"GSMA"</item>
+    <item msgid="2237392451840970059">"স্থানীয়"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"SUBSCRIBER_CERTIFICATE"</item>
+    <item msgid="6305648228108532104">"MBMS"</item>
+    <item msgid="4369286857827087787">"HTTP_DIGEST_AUTH"</item>
+    <item msgid="1047245839171009229">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="1728229445161381738">"GENERIC_PUSH_LAYER"</item>
+    <item msgid="8586727908453122385">"IMS_MEDIA_PLANE"</item>
+    <item msgid="5721752897501838331">"GENERATION_TMPI"</item>
+    <item msgid="1622910302975417378">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="7519270727796373646">"TLS_DEFAULT"</item>
+    <item msgid="7444251498958422990">"TLS_BROWSER"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"অজ্ঞাত"</item>
+    <item msgid="1715684709703792003">"ছিম"</item>
+    <item msgid="4592543026990143036">"USIM"</item>
+    <item msgid="8763183137100323328">"RSIM"</item>
+    <item msgid="3778400020205644807">"CSIM"</item>
+    <item msgid="3774607711141191499">"ISIM"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values-be/strings.xml b/testapps/TestRcsApp/TestApp/res/values-be/strings.xml
new file mode 100644
index 0000000..5196cf4
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-be/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"RcsClient"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"Тэст ініцыялізацыі"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"Дэлегаванне тэста"</string>
+    <string name="uce_test" msgid="6466919844953883038">"Тэст UCE"</string>
+    <string name="gba_test" msgid="8786745991279224003">"Тэст GBA"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"TestMessageClient"</string>
+    <string name="db_client" msgid="5131401341721936367">"DBClient"</string>
+    <string name="result" msgid="435222369457564619">"Вынік:"</string>
+    <string name="callback_result" msgid="7162990408241244749">"Вынік зваротнага выкліку"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"initializeSipDelegate"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"destroySipDelegate"</string>
+    <string name="uce_description" msgid="1117661457446955869">"Увядзіце нумар для запыту магчымасці. Калі нумароў некалькі, раздзяліце іх коскамі."</string>
+    <string name="number" msgid="9096578126043995890">"Нумар:"</string>
+    <string name="request_capability" msgid="7644752985651089411">"requestCapability"</string>
+    <string name="request_availability" msgid="6387685341316953239">"requestNetworkAvailability"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"bootstrapAuthenticationRequest"</string>
+    <string name="start_chat" msgid="96121236227112396">"Пачаць чат"</string>
+    <string name="to" msgid="1156781884609653284">"Каму:"</string>
+    <string name="chat_message" msgid="3945528453939746046">"Паведамленне чата"</string>
+    <string name="send" msgid="8797467615003240434">"Адправіць"</string>
+    <string name="ok" msgid="2206671077510386729">"ОК"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"Ініцыялізацыя сеанса выканана"</string>
+    <string name="session_failed" msgid="4524986240102190509">"Збой ініцыялізацыі сеанса"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"Сеанс не гатовы"</string>
+    <string name="organization" msgid="913518283470119113">"Арганізацыя:"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"Тып UICC:"</string>
+    <string name="protocol" msgid="8626139294022821930">"Пратакол:"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"Набор шыфраў TLS:"</string>
+    <string name="naf" msgid="7620247915001854662">"NAF URI:"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"Аўтаномны пэйджар"</string>
+    <string name="standalone_large" msgid="336170805696694543">"Аўтаномны вялікі"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"Аўтаномны адкладзены"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"Аўтаномны вялікі пэйджар"</string>
+    <string name="chat" msgid="5500583808773151982">"Чат"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"Перадача файлаў"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"SMS геалакацыі"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"Сеанс чат-бота"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"Аўтаномны чат-бот"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"Версія чат-бота"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"Ініцыялізацыя выканана"</string>
+    <string name="registration_done" msgid="492878616836655421">"Рэгістрацыя выканана"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"НЯМА"</item>
+    <item msgid="7841117287115880098">"3GPP"</item>
+    <item msgid="5871357530951196401">"3GPP2"</item>
+    <item msgid="4979706772323445701">"OMA"</item>
+    <item msgid="1202873556413702218">"GSMA"</item>
+    <item msgid="2237392451840970059">"МЯСЦОВАЯ СЕТКА"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"SUBSCRIBER_CERTIFICATE"</item>
+    <item msgid="6305648228108532104">"MBMS"</item>
+    <item msgid="4369286857827087787">"HTTP_DIGEST_AUTH"</item>
+    <item msgid="1047245839171009229">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="1728229445161381738">"GENERIC_PUSH_LAYER"</item>
+    <item msgid="8586727908453122385">"IMS_MEDIA_PLANE"</item>
+    <item msgid="5721752897501838331">"GENERATION_TMPI"</item>
+    <item msgid="1622910302975417378">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="7519270727796373646">"TLS_DEFAULT"</item>
+    <item msgid="7444251498958422990">"TLS_BROWSER"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"НЕВЯДОМА"</item>
+    <item msgid="1715684709703792003">"SIM"</item>
+    <item msgid="4592543026990143036">"USIM"</item>
+    <item msgid="8763183137100323328">"RSIM"</item>
+    <item msgid="3778400020205644807">"CSIM"</item>
+    <item msgid="3774607711141191499">"ISIM"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values-bs/strings.xml b/testapps/TestRcsApp/TestApp/res/values-bs/strings.xml
new file mode 100644
index 0000000..8787d26
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-bs/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"RcsClient"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"Test za dodjeljivanje"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"Test ovlaštenika"</string>
+    <string name="uce_test" msgid="6466919844953883038">"Test UCE"</string>
+    <string name="gba_test" msgid="8786745991279224003">"Test GBA"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"TestMessageClient"</string>
+    <string name="db_client" msgid="5131401341721936367">"DBClient"</string>
+    <string name="result" msgid="435222369457564619">"Rezultat:"</string>
+    <string name="callback_result" msgid="7162990408241244749">"Rezultat povratnog poziva:"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"initializeSipDelegate"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"destroySipDelegate"</string>
+    <string name="uce_description" msgid="1117661457446955869">"Unesite broj da pošaljte upit o kapacitetu te upite razdvojite \",\" ako ih ima više."</string>
+    <string name="number" msgid="9096578126043995890">"Broj:"</string>
+    <string name="request_capability" msgid="7644752985651089411">"requestCapability"</string>
+    <string name="request_availability" msgid="6387685341316953239">"requestNetworkAvailability"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"bootstrapAuthenticationRequest"</string>
+    <string name="start_chat" msgid="96121236227112396">"Započni chat"</string>
+    <string name="to" msgid="1156781884609653284">"Za:"</string>
+    <string name="chat_message" msgid="3945528453939746046">"Poruka chata"</string>
+    <string name="send" msgid="8797467615003240434">"Pošalji"</string>
+    <string name="ok" msgid="2206671077510386729">"Uredu"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"Pokretanje sesije je uspjelo"</string>
+    <string name="session_failed" msgid="4524986240102190509">"Pokretanje sesije nije uspjelo"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"Sesija nije spremna"</string>
+    <string name="organization" msgid="913518283470119113">"Organizacija:"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"Vrsta UICC:"</string>
+    <string name="protocol" msgid="8626139294022821930">"Protokol:"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"Paket TLS Cipher:"</string>
+    <string name="naf" msgid="7620247915001854662">"NAF URI:"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"Samostalni pejdžer"</string>
+    <string name="standalone_large" msgid="336170805696694543">"Samostalno veliko"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"Samostalno odgođeno"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"Samostalni veliki pejdžer"</string>
+    <string name="chat" msgid="5500583808773151982">"Chat"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"Prijenos fajlova"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"SMS za geolokaciju"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"Sesija bota za chat"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"Samostalni bot za chat"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"Vezija bota za chat"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"Dodjeljivanje je završeno"</string>
+    <string name="registration_done" msgid="492878616836655421">"Registracija je završena"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"NEMA"</item>
+    <item msgid="7841117287115880098">"3GPP"</item>
+    <item msgid="5871357530951196401">"3GPP2"</item>
+    <item msgid="4979706772323445701">"OMA"</item>
+    <item msgid="1202873556413702218">"GSMA"</item>
+    <item msgid="2237392451840970059">"LOKALNO"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"CERTIFIKAT_PRETPLATNIKA"</item>
+    <item msgid="6305648228108532104">"MBMS"</item>
+    <item msgid="4369286857827087787">"HTTP_DIGEST_AUTH"</item>
+    <item msgid="1047245839171009229">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="1728229445161381738">"GENERIC_PUSH_LAYER"</item>
+    <item msgid="8586727908453122385">"IMS_MEDIA_PLANE"</item>
+    <item msgid="5721752897501838331">"GENERATION_TMPI"</item>
+    <item msgid="1622910302975417378">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="7519270727796373646">"TLS_DEFAULT"</item>
+    <item msgid="7444251498958422990">"TLS_BROWSER"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"NEPOZNATO"</item>
+    <item msgid="1715684709703792003">"SIM"</item>
+    <item msgid="4592543026990143036">"USIM"</item>
+    <item msgid="8763183137100323328">"RSIM"</item>
+    <item msgid="3778400020205644807">"CSIM"</item>
+    <item msgid="3774607711141191499">"ISIM"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values-en-rAU/strings.xml b/testapps/TestRcsApp/TestApp/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..bec17d3
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-en-rAU/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"Rcs client"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"Provisioning test"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"Delegate test"</string>
+    <string name="uce_test" msgid="6466919844953883038">"UCE test"</string>
+    <string name="gba_test" msgid="8786745991279224003">"GBA test"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"TestMessageClient"</string>
+    <string name="db_client" msgid="5131401341721936367">"DB client"</string>
+    <string name="result" msgid="435222369457564619">"Result:"</string>
+    <string name="callback_result" msgid="7162990408241244749">"Callback result:"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"initialiseSipDelegate"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"destroySipDelegate"</string>
+    <string name="uce_description" msgid="1117661457446955869">"Enter the number to query capability and separate by \',\' if multiple ones."</string>
+    <string name="number" msgid="9096578126043995890">"Number:"</string>
+    <string name="request_capability" msgid="7644752985651089411">"requestCapability"</string>
+    <string name="request_availability" msgid="6387685341316953239">"requestNetworkAvailability"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"bootstrapAuthenticationRequest"</string>
+    <string name="start_chat" msgid="96121236227112396">"Start chat"</string>
+    <string name="to" msgid="1156781884609653284">"To:"</string>
+    <string name="chat_message" msgid="3945528453939746046">"Chat message"</string>
+    <string name="send" msgid="8797467615003240434">"Send"</string>
+    <string name="ok" msgid="2206671077510386729">"OK"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"Session init succeeded"</string>
+    <string name="session_failed" msgid="4524986240102190509">"Session init failed"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"Session not ready"</string>
+    <string name="organization" msgid="913518283470119113">"Organisation:"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"UICC type:"</string>
+    <string name="protocol" msgid="8626139294022821930">"Protocol:"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"TLS cipher suite:"</string>
+    <string name="naf" msgid="7620247915001854662">"NAF URI:"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"Standalone pager"</string>
+    <string name="standalone_large" msgid="336170805696694543">"Standalone large"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"Standalone deferred"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"Standalone large pager"</string>
+    <string name="chat" msgid="5500583808773151982">"Chat"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"File transfer"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"Geolocation SMS"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"Chatbot session"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"Chatbot standalone"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"Chatbot version"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"Provisioning done"</string>
+    <string name="registration_done" msgid="492878616836655421">"Registration done"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"NONE"</item>
+    <item msgid="7841117287115880098">"3GPP"</item>
+    <item msgid="5871357530951196401">"3GPP2"</item>
+    <item msgid="4979706772323445701">"OMA"</item>
+    <item msgid="1202873556413702218">"GSMA"</item>
+    <item msgid="2237392451840970059">"LOCAL"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"SUBSCRIBER_CERTIFICATE"</item>
+    <item msgid="6305648228108532104">"MBMS"</item>
+    <item msgid="4369286857827087787">"HTTP_DIGEST_AUTH"</item>
+    <item msgid="1047245839171009229">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="1728229445161381738">"GENERIC_PUSH_LAYER"</item>
+    <item msgid="8586727908453122385">"IMS_MEDIA_PLANE"</item>
+    <item msgid="5721752897501838331">"GENERATION_TMPI"</item>
+    <item msgid="1622910302975417378">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="7519270727796373646">"TLS_DEFAULT"</item>
+    <item msgid="7444251498958422990">"TLS_BROWSER"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"UNKNOWN"</item>
+    <item msgid="1715684709703792003">"SIM"</item>
+    <item msgid="4592543026990143036">"USIM"</item>
+    <item msgid="8763183137100323328">"RSIM"</item>
+    <item msgid="3778400020205644807">"CSIM"</item>
+    <item msgid="3774607711141191499">"ISIM"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values-en-rCA/strings.xml b/testapps/TestRcsApp/TestApp/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..bec17d3
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-en-rCA/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"Rcs client"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"Provisioning test"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"Delegate test"</string>
+    <string name="uce_test" msgid="6466919844953883038">"UCE test"</string>
+    <string name="gba_test" msgid="8786745991279224003">"GBA test"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"TestMessageClient"</string>
+    <string name="db_client" msgid="5131401341721936367">"DB client"</string>
+    <string name="result" msgid="435222369457564619">"Result:"</string>
+    <string name="callback_result" msgid="7162990408241244749">"Callback result:"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"initialiseSipDelegate"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"destroySipDelegate"</string>
+    <string name="uce_description" msgid="1117661457446955869">"Enter the number to query capability and separate by \',\' if multiple ones."</string>
+    <string name="number" msgid="9096578126043995890">"Number:"</string>
+    <string name="request_capability" msgid="7644752985651089411">"requestCapability"</string>
+    <string name="request_availability" msgid="6387685341316953239">"requestNetworkAvailability"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"bootstrapAuthenticationRequest"</string>
+    <string name="start_chat" msgid="96121236227112396">"Start chat"</string>
+    <string name="to" msgid="1156781884609653284">"To:"</string>
+    <string name="chat_message" msgid="3945528453939746046">"Chat message"</string>
+    <string name="send" msgid="8797467615003240434">"Send"</string>
+    <string name="ok" msgid="2206671077510386729">"OK"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"Session init succeeded"</string>
+    <string name="session_failed" msgid="4524986240102190509">"Session init failed"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"Session not ready"</string>
+    <string name="organization" msgid="913518283470119113">"Organisation:"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"UICC type:"</string>
+    <string name="protocol" msgid="8626139294022821930">"Protocol:"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"TLS cipher suite:"</string>
+    <string name="naf" msgid="7620247915001854662">"NAF URI:"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"Standalone pager"</string>
+    <string name="standalone_large" msgid="336170805696694543">"Standalone large"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"Standalone deferred"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"Standalone large pager"</string>
+    <string name="chat" msgid="5500583808773151982">"Chat"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"File transfer"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"Geolocation SMS"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"Chatbot session"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"Chatbot standalone"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"Chatbot version"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"Provisioning done"</string>
+    <string name="registration_done" msgid="492878616836655421">"Registration done"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"NONE"</item>
+    <item msgid="7841117287115880098">"3GPP"</item>
+    <item msgid="5871357530951196401">"3GPP2"</item>
+    <item msgid="4979706772323445701">"OMA"</item>
+    <item msgid="1202873556413702218">"GSMA"</item>
+    <item msgid="2237392451840970059">"LOCAL"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"SUBSCRIBER_CERTIFICATE"</item>
+    <item msgid="6305648228108532104">"MBMS"</item>
+    <item msgid="4369286857827087787">"HTTP_DIGEST_AUTH"</item>
+    <item msgid="1047245839171009229">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="1728229445161381738">"GENERIC_PUSH_LAYER"</item>
+    <item msgid="8586727908453122385">"IMS_MEDIA_PLANE"</item>
+    <item msgid="5721752897501838331">"GENERATION_TMPI"</item>
+    <item msgid="1622910302975417378">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="7519270727796373646">"TLS_DEFAULT"</item>
+    <item msgid="7444251498958422990">"TLS_BROWSER"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"UNKNOWN"</item>
+    <item msgid="1715684709703792003">"SIM"</item>
+    <item msgid="4592543026990143036">"USIM"</item>
+    <item msgid="8763183137100323328">"RSIM"</item>
+    <item msgid="3778400020205644807">"CSIM"</item>
+    <item msgid="3774607711141191499">"ISIM"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values-en-rGB/strings.xml b/testapps/TestRcsApp/TestApp/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..bec17d3
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-en-rGB/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"Rcs client"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"Provisioning test"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"Delegate test"</string>
+    <string name="uce_test" msgid="6466919844953883038">"UCE test"</string>
+    <string name="gba_test" msgid="8786745991279224003">"GBA test"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"TestMessageClient"</string>
+    <string name="db_client" msgid="5131401341721936367">"DB client"</string>
+    <string name="result" msgid="435222369457564619">"Result:"</string>
+    <string name="callback_result" msgid="7162990408241244749">"Callback result:"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"initialiseSipDelegate"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"destroySipDelegate"</string>
+    <string name="uce_description" msgid="1117661457446955869">"Enter the number to query capability and separate by \',\' if multiple ones."</string>
+    <string name="number" msgid="9096578126043995890">"Number:"</string>
+    <string name="request_capability" msgid="7644752985651089411">"requestCapability"</string>
+    <string name="request_availability" msgid="6387685341316953239">"requestNetworkAvailability"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"bootstrapAuthenticationRequest"</string>
+    <string name="start_chat" msgid="96121236227112396">"Start chat"</string>
+    <string name="to" msgid="1156781884609653284">"To:"</string>
+    <string name="chat_message" msgid="3945528453939746046">"Chat message"</string>
+    <string name="send" msgid="8797467615003240434">"Send"</string>
+    <string name="ok" msgid="2206671077510386729">"OK"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"Session init succeeded"</string>
+    <string name="session_failed" msgid="4524986240102190509">"Session init failed"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"Session not ready"</string>
+    <string name="organization" msgid="913518283470119113">"Organisation:"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"UICC type:"</string>
+    <string name="protocol" msgid="8626139294022821930">"Protocol:"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"TLS cipher suite:"</string>
+    <string name="naf" msgid="7620247915001854662">"NAF URI:"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"Standalone pager"</string>
+    <string name="standalone_large" msgid="336170805696694543">"Standalone large"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"Standalone deferred"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"Standalone large pager"</string>
+    <string name="chat" msgid="5500583808773151982">"Chat"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"File transfer"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"Geolocation SMS"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"Chatbot session"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"Chatbot standalone"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"Chatbot version"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"Provisioning done"</string>
+    <string name="registration_done" msgid="492878616836655421">"Registration done"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"NONE"</item>
+    <item msgid="7841117287115880098">"3GPP"</item>
+    <item msgid="5871357530951196401">"3GPP2"</item>
+    <item msgid="4979706772323445701">"OMA"</item>
+    <item msgid="1202873556413702218">"GSMA"</item>
+    <item msgid="2237392451840970059">"LOCAL"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"SUBSCRIBER_CERTIFICATE"</item>
+    <item msgid="6305648228108532104">"MBMS"</item>
+    <item msgid="4369286857827087787">"HTTP_DIGEST_AUTH"</item>
+    <item msgid="1047245839171009229">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="1728229445161381738">"GENERIC_PUSH_LAYER"</item>
+    <item msgid="8586727908453122385">"IMS_MEDIA_PLANE"</item>
+    <item msgid="5721752897501838331">"GENERATION_TMPI"</item>
+    <item msgid="1622910302975417378">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="7519270727796373646">"TLS_DEFAULT"</item>
+    <item msgid="7444251498958422990">"TLS_BROWSER"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"UNKNOWN"</item>
+    <item msgid="1715684709703792003">"SIM"</item>
+    <item msgid="4592543026990143036">"USIM"</item>
+    <item msgid="8763183137100323328">"RSIM"</item>
+    <item msgid="3778400020205644807">"CSIM"</item>
+    <item msgid="3774607711141191499">"ISIM"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values-en-rIN/strings.xml b/testapps/TestRcsApp/TestApp/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..bec17d3
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-en-rIN/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"Rcs client"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"Provisioning test"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"Delegate test"</string>
+    <string name="uce_test" msgid="6466919844953883038">"UCE test"</string>
+    <string name="gba_test" msgid="8786745991279224003">"GBA test"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"TestMessageClient"</string>
+    <string name="db_client" msgid="5131401341721936367">"DB client"</string>
+    <string name="result" msgid="435222369457564619">"Result:"</string>
+    <string name="callback_result" msgid="7162990408241244749">"Callback result:"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"initialiseSipDelegate"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"destroySipDelegate"</string>
+    <string name="uce_description" msgid="1117661457446955869">"Enter the number to query capability and separate by \',\' if multiple ones."</string>
+    <string name="number" msgid="9096578126043995890">"Number:"</string>
+    <string name="request_capability" msgid="7644752985651089411">"requestCapability"</string>
+    <string name="request_availability" msgid="6387685341316953239">"requestNetworkAvailability"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"bootstrapAuthenticationRequest"</string>
+    <string name="start_chat" msgid="96121236227112396">"Start chat"</string>
+    <string name="to" msgid="1156781884609653284">"To:"</string>
+    <string name="chat_message" msgid="3945528453939746046">"Chat message"</string>
+    <string name="send" msgid="8797467615003240434">"Send"</string>
+    <string name="ok" msgid="2206671077510386729">"OK"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"Session init succeeded"</string>
+    <string name="session_failed" msgid="4524986240102190509">"Session init failed"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"Session not ready"</string>
+    <string name="organization" msgid="913518283470119113">"Organisation:"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"UICC type:"</string>
+    <string name="protocol" msgid="8626139294022821930">"Protocol:"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"TLS cipher suite:"</string>
+    <string name="naf" msgid="7620247915001854662">"NAF URI:"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"Standalone pager"</string>
+    <string name="standalone_large" msgid="336170805696694543">"Standalone large"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"Standalone deferred"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"Standalone large pager"</string>
+    <string name="chat" msgid="5500583808773151982">"Chat"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"File transfer"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"Geolocation SMS"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"Chatbot session"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"Chatbot standalone"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"Chatbot version"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"Provisioning done"</string>
+    <string name="registration_done" msgid="492878616836655421">"Registration done"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"NONE"</item>
+    <item msgid="7841117287115880098">"3GPP"</item>
+    <item msgid="5871357530951196401">"3GPP2"</item>
+    <item msgid="4979706772323445701">"OMA"</item>
+    <item msgid="1202873556413702218">"GSMA"</item>
+    <item msgid="2237392451840970059">"LOCAL"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"SUBSCRIBER_CERTIFICATE"</item>
+    <item msgid="6305648228108532104">"MBMS"</item>
+    <item msgid="4369286857827087787">"HTTP_DIGEST_AUTH"</item>
+    <item msgid="1047245839171009229">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="1728229445161381738">"GENERIC_PUSH_LAYER"</item>
+    <item msgid="8586727908453122385">"IMS_MEDIA_PLANE"</item>
+    <item msgid="5721752897501838331">"GENERATION_TMPI"</item>
+    <item msgid="1622910302975417378">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="7519270727796373646">"TLS_DEFAULT"</item>
+    <item msgid="7444251498958422990">"TLS_BROWSER"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"UNKNOWN"</item>
+    <item msgid="1715684709703792003">"SIM"</item>
+    <item msgid="4592543026990143036">"USIM"</item>
+    <item msgid="8763183137100323328">"RSIM"</item>
+    <item msgid="3778400020205644807">"CSIM"</item>
+    <item msgid="3774607711141191499">"ISIM"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values-en-rXC/strings.xml b/testapps/TestRcsApp/TestApp/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..ef9b6cb
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-en-rXC/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‏‏‏‎‎‏‎‏‎‏‎‎‏‎‏‎‏‎‏‏‏‎‎‎‏‎‏‎‎‏‏‎‎‎‏‎‎‎‎‏‎‎‎‏‏‎‎‎‏‏‎‏‎‎‏‏‏‏‎RcsClient‎‏‎‎‏‎"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‎‎‏‎‏‏‏‏‏‎‎‎‎‎‎‏‎‏‎‏‎‏‏‏‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‏‎‎‏‏‏‏‎‎Provisioning Test‎‏‎‎‏‎"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‏‎‎‎‎‎‏‏‎‎‎‏‏‎‏‎‏‏‏‎‎‏‎‏‏‏‏‎‏‎‎‎‎‎‏‎‎‏‏‏‏‎‏‏‏‎‏‏‏‎‏‏‎Delegate Test‎‏‎‎‏‎"</string>
+    <string name="uce_test" msgid="6466919844953883038">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‏‎‏‎‏‎‏‏‏‏‏‎‏‎‏‎‏‏‎‎‏‏‎‏‎‎‎‎‎‎‏‏‎‎‏‏‏‏‎‎UCE Test‎‏‎‎‏‎"</string>
+    <string name="gba_test" msgid="8786745991279224003">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‎‎‎‎‏‏‎‎‏‎‏‎‏‏‏‎‏‏‎‎‏‏‎‎‎‏‎‏‏‏‎‎‏‎‎‏‎‎‏‎‎‏‎‎‏‏‎‎‎‎‏‏‎GBA Test‎‏‎‎‏‎"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‏‎‏‏‎‏‏‏‎‏‏‎‎‎‎‏‏‎‏‎‎‎‏‏‎‏‎‎‎‏‎‏‏‏‎‏‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‎‎TestMessageClient‎‏‎‎‏‎"</string>
+    <string name="db_client" msgid="5131401341721936367">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‏‏‏‎‎‏‏‎‏‏‎‎‏‏‎‎‏‏‎‎‏‎‏‎‏‎‏‎‎‏‏‏‏‎‏‎‏‎‎‏‏‎‎‏‎‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎DBClient‎‏‎‎‏‎"</string>
+    <string name="result" msgid="435222369457564619">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‎‎‎‎‏‎‏‎‎‎‏‏‏‎‎‎‎‏‏‏‎‎‎‎‎‏‏‎‏‎‎‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‏‏‏‏‎‎‏‎‏‏‎Result:‎‏‎‎‏‎"</string>
+    <string name="callback_result" msgid="7162990408241244749">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‎‎‎‎‎‎‎‏‏‎‏‏‏‎‏‎‎‏‏‎‎‏‏‏‏‎‎‏‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‏‎Callback Result:‎‏‎‎‏‎"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‎‏‏‏‏‎‏‎‏‎‏‎‏‏‎‏‏‎‏‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‎‏‎‎‎‎‏‎‏‎‎‏‎‎‎‏‏‎‎initializeSipDelegate‎‏‎‎‏‎"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‏‏‏‎‏‎‎‏‎‏‎‎‎‏‎‏‏‎‎‏‎‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‎‏‎‎‎‎‎‏‎‎‏‏‎‏‎‎‎‏‏‏‎‏‏‎destroySipDelegate‎‏‎‎‏‎"</string>
+    <string name="uce_description" msgid="1117661457446955869">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‏‏‎‎‎‎‎‏‎‏‎‏‏‏‎‏‏‎‎‏‎‏‏‎‎‏‎‎‎‎‏‏‎‎‏‎‎‎‏‎‏‎‏‏‎‏‎‏‏‎‏‎‏‏‏‎‏‎Enter the number to query capability and separate by \',\' if multiple ones.‎‏‎‎‏‎"</string>
+    <string name="number" msgid="9096578126043995890">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‏‎‎‏‏‏‎‎‎‎‏‏‎‏‏‏‏‎‏‏‏‏‎‎‏‎‎Number:‎‏‎‎‏‎"</string>
+    <string name="request_capability" msgid="7644752985651089411">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‏‏‏‏‎‎‏‏‏‏‎‎‏‎‏‎‏‏‎‎‎‏‏‏‎‎‏‎‏‎‏‎‏‏‏‎‎‎‏‎‎‎‎‎‎‎‎‎‎‏‏‎requestCapability‎‏‎‎‏‎"</string>
+    <string name="request_availability" msgid="6387685341316953239">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‏‎‎‏‎‏‏‎‎‏‏‏‎‏‏‏‏‏‏‎‎‏‎‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‏‎‏‏‎‏‎‎‏‎‎‏‎‏‏‏‎requestNetworkAvailability‎‏‎‎‏‎"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‏‎‎‎‎‏‎‏‎‏‎‎‎‏‎‏‏‎‏‎‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‏‏‎‎‏‎‎‎‎‏‎bootstrapAuthenticationRequest‎‏‎‎‏‎"</string>
+    <string name="start_chat" msgid="96121236227112396">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‎‏‏‎‏‎‏‎‏‎‏‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‏‎‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎Start Chat‎‏‎‎‏‎"</string>
+    <string name="to" msgid="1156781884609653284">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‎‎‎‎‎‎‏‏‎‏‏‎‏‏‎‏‏‎‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‏‏‎‎‎‎‏‏‏‏‏‎‏‎‏‎‎‎‏‎‎‏‎‎‎To:‎‏‎‎‏‎"</string>
+    <string name="chat_message" msgid="3945528453939746046">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‎‎‎‏‎‏‎‏‎‏‎‏‎‎‏‏‏‎‎‏‎‏‏‏‏‏‎‎‎‏‎‎‎‏‏‎‎‏‎‏‎‏‎‎‏‏‏‏‏‏‏‎‎Chat Message‎‏‎‎‏‎"</string>
+    <string name="send" msgid="8797467615003240434">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‏‏‎‎‎‏‎‎‎‏‎‏‏‏‏‎‏‎‏‎‎‏‏‎‏‎‎‎‏‎‏‎‏‏‎‎‏‏‏‏‏‏‏‎‎‏‎‎Send‎‏‎‎‏‎"</string>
+    <string name="ok" msgid="2206671077510386729">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‎‎‏‏‏‎‏‎‎‏‏‏‏‎‎‏‎‎‎‏‎‏‎‎‏‎‏‎‎‎‎‏‎‏‎‎‏‎OK‎‏‎‎‏‎"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‎‏‏‎‏‏‎‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‎‏‏‎‎‏‎‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎Session init succeeded‎‏‎‎‏‎"</string>
+    <string name="session_failed" msgid="4524986240102190509">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‎‏‏‎‎‏‏‏‎‏‏‎‏‎‎‏‎‎‏‎‎‏‏‎‏‏‎‏‎‏‏‎‏‎Session init failed‎‏‎‎‏‎"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‎‎‎‎‏‏‎‏‎‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‎‏‎‏‎‎‏‏‏‎‎‏‎‏‎‎‎‏‎‏‎‏‏‏‏‏‏‎‎‎Session not ready‎‏‎‎‏‎"</string>
+    <string name="organization" msgid="913518283470119113">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‎‎‏‎‏‎‏‏‎‏‎‏‏‏‏‎‎‎‎‎‎‎‏‎‎‏‏‏‏‎‏‏‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‎‎‎‏‏‎‎‏‎‎‏‎Organization:‎‏‎‎‏‎"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‎‏‎‎‏‏‎‎‏‏‏‎‏‏‎‎‎‏‏‎‏‎‏‏‏‏‎‏‏‏‏‎‏‏‏‎‎‎UICC Type:‎‏‎‎‏‎"</string>
+    <string name="protocol" msgid="8626139294022821930">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‏‎‎‎‏‏‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‏‏‎‏‎‎‎‏‎‎‏‏‏‎‏‎‎‎‎‎‏‎‏‎‏‎‎Protocol:‎‏‎‎‏‎"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‎‏‎‏‏‎‎‎‏‏‎‎‏‎‎‎‎‏‎‎‎‏‎‏‏‏‏‎‏‎‎‏‎‎‏‏‏‎‎‎‎‎‏‎‎‎‏‎‏‎‎‎‏‎‎TLS Cipher Suite:‎‏‎‎‏‎"</string>
+    <string name="naf" msgid="7620247915001854662">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‏‏‎‎‎‏‏‎‏‎‏‏‎‏‏‎‏‎‎‎‎‏‏‎‎‎‏‎‏‏‏‎‏‎‏‏‎‎‎‏‏‎‎NAF URI:‎‏‎‎‏‎"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‏‎‏‏‎‎‎‎‎‎‏‏‏‎‏‏‏‎‎‏‎‎‎‎‏‏‎‏‏‏‎‎‎‏‎‎‏‏‏‏‎‎‎‎‎‏‎‏‎‏‏‎‎‏‏‏‏‏‎Standalone Pager‎‏‎‎‏‎"</string>
+    <string name="standalone_large" msgid="336170805696694543">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‎‏‎‏‎‏‎‏‎‎‏‎‏‎‎‎‏‏‎‎‏‎‎‎‏‎‎‎‏‎‎‎‏‎‏‏‎‏‎‎‎‏‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎Standalone Large‎‏‎‎‏‎"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‏‎‏‎‎‏‏‎‏‏‎‎‎‏‏‏‏‎‏‏‏‏‎‎‎‏‏‎‎‎‎‏‏‏‏‎‎‏‎‏‏‎‏‏‎‎‎Standalone Deferred‎‏‎‎‏‎"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‎‏‎‎‏‎‎‎‎‎‏‎‏‎‎‎‏‏‎‎‎‏‏‎‏‎‏‏‏‎‎‏‎‎‏‏‏‏‎‎‎‎‎‎‎‏‎‎‏‎‏‏‎Standalone Large Pager‎‏‎‎‏‎"</string>
+    <string name="chat" msgid="5500583808773151982">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‏‎‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‎‏‎‏‏‎‎‏‏‎‎‎‎‎‏‎‏‏‏‎‎‏‏‏‎‏‏‏‎‎Chat‎‏‎‎‏‎"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‏‎‎‎‏‎‎‏‎‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‏‏‏‏‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‏‎File Transfer‎‏‎‎‏‎"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‎‏‏‎‏‏‎‎‏‎‎‎‏‎‎‏‏‏‎‎‏‎‎‎‏‏‎‎‏‏‏‎‏‎‎‎‎‏‎‎‏‎‏‏‏‎Geolocation SMS‎‏‎‎‏‎"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‎‏‎‏‏‎‏‏‏‏‎‎‏‏‎‎‏‏‏‏‎‏‏‏‏‏‏‎‎‎‎‎‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‏‏‎‏‎Chatbot Session‎‏‎‎‏‎"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‏‎‏‎‎‎‎‎‎‎‎‎‎‎‎‎‏‏‏‏‎‎‎‏‏‎‏‎‏‏‎‎‏‏‎‏‏‎‏‏‏‎‎‎‏‏‎‎‏‎Chatbot Standalone‎‏‎‎‏‎"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‏‎‎‎‏‎‏‏‎‎‏‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‎‎‎‎‎Chatbot Version‎‏‎‎‏‎"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‏‎‏‎‏‏‎‏‎‏‏‎‏‏‏‏‎‎‏‏‏‏‎‎‎‎‎‎‎‏‎‎‎‎‏‏‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‏‎‎‎‏‎‎‎‎‎Provisioning Done‎‏‎‎‏‎"</string>
+    <string name="registration_done" msgid="492878616836655421">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎‎‎‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‏‎‏‏‏‏‎‏‎‎‏‎‎‏‎‎‏‏‏‏‎‏‎Registration Done‎‏‎‎‏‎"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎‏‎‏‏‎‏‎‏‏‏‏‎‎‎‎‏‎‎‏‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‎‎‎‏‏‏‎‎‎NONE‎‏‎‎‏‎"</item>
+    <item msgid="7841117287115880098">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‎‎‎‏‎‎‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‏‏‎‏‏‎‎‏‎‏‏‎‏‏‏‎‏‏‏‎‏‏‎‏‎‏‎‎‎‏‎‎3GPP‎‏‎‎‏‎"</item>
+    <item msgid="5871357530951196401">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‎‎‏‏‏‏‎‏‏‏‏‎‎‎‏‎3GPP2‎‏‎‎‏‎"</item>
+    <item msgid="4979706772323445701">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‎‏‏‎‏‏‎‏‏‏‏‎‎‎‏‏‏‎‏‏‎‏‎‎‎‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‎‏‎‏‏‏‏‎‎‎‏‎‏‎OMA‎‏‎‎‏‎"</item>
+    <item msgid="1202873556413702218">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‎‎‏‎‏‏‎‎‎‏‎‏‏‏‎‏‏‏‎‎‏‎‎‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‎‏‎‏‎‎GSMA‎‏‎‎‏‎"</item>
+    <item msgid="2237392451840970059">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‏‎‎‎‎‏‎‎‏‎‏‎‎‏‎‏‎‏‎‏‏‎‎‎‏‏‏‏‏‎‎‎‎‏‏‎‏‎‏‎‎‏‎‏‏‎LOCAL‎‏‎‎‏‎"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‎‎‏‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‏‎‎‎‎‏‎‎‎‎‏‏‎‏‎‎‏‎‏‎‎‎‎‏‎‏‏‎‎‎‏‏‎SUBSCRIBER_CERTIFICATE‎‏‎‎‏‎"</item>
+    <item msgid="6305648228108532104">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‎‎‎‎‏‎‎‎‏‎‏‎‎‏‏‎‏‎‎‏‏‏‏‎‎‏‏‎‏‎‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‏‏‎‎‎‏‎‎‎‎MBMS‎‏‎‎‏‎"</item>
+    <item msgid="4369286857827087787">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‎‎‎‏‎‏‏‎‏‎‎‏‏‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‏‎‏‎‏‏‎HTTP_DIGEST_AUTH‎‏‎‎‏‎"</item>
+    <item msgid="1047245839171009229">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‎‏‎‎‎‏‎‎‎‏‎‎‏‎‎‎‎‏‎‎‎‏‎‏‎‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‏‎‎‏‏‏‎‏‎‏‏‎‎‏‏‎‏‎3GPP_HTTP_BASED_MBMS‎‏‎‎‏‎"</item>
+    <item msgid="1728229445161381738">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‏‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‏‏‎‏‏‏‎‏‏‏‏‎‏‏‎‏‎‏‎‎GENERIC_PUSH_LAYER‎‏‎‎‏‎"</item>
+    <item msgid="8586727908453122385">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‎‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‎‏‏‏‎‎‏‎‎‏‎‎‎‎‎‎‎‎‎‎‏‎‏‎‏‎‎‎‏‎IMS_MEDIA_PLANE‎‏‎‎‏‎"</item>
+    <item msgid="5721752897501838331">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‏‎‎‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎GENERATION_TMPI‎‏‎‎‏‎"</item>
+    <item msgid="1622910302975417378">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‏‏‎‏‎‎‎‎‏‎‏‏‎‏‏‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‎‏‏‏‏‎‎‏‎‏‎‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‏‎‎3GPP_HTTP_BASED_MBMS‎‏‎‎‏‎"</item>
+    <item msgid="7519270727796373646">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‎‏‏‏‎‏‎‎‎‎‏‏‏‎‎‎‏‏‎‏‎‏‏‏‎‎‎‏‎‎‏‎‏‏‏‎‏‏‏‎‎‎‏‎‎‎‏‏‏‎‎TLS_DEFAULT‎‏‎‎‏‎"</item>
+    <item msgid="7444251498958422990">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‏‏‏‎‏‎‎‏‎‏‏‎‏‎‎‏‏‎‎‎‎‎‎‎‏‏‎‎‎‎‏‎‏‏‎‎‎‎‎‎‎‏‏‏‏‎‎‏‏‏‎‎TLS_BROWSER‎‏‎‎‏‎"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‏‏‎‏‎‎‎‏‏‎‎‏‏‎‎‏‎‎‎‏‏‎‏‎‏‎‏‏‎‏‎‎‎‏‎‎‎‏‏‏‏‎‎UNKNOWN‎‏‎‎‏‎"</item>
+    <item msgid="1715684709703792003">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‏‏‏‏‎‏‎‏‎‏‏‎‎‎‏‎‎‏‏‏‎‏‎‎‎‏‎‎‎‏‏‏‎‏‏‎‏‎‏‏‎‏‎‏‏‎‎‎‎‎‏‏‎SIM‎‏‎‎‏‎"</item>
+    <item msgid="4592543026990143036">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‏‏‏‎‏‏‎‏‏‏‎‎‎‏‏‏‎‏‎‏‏‎‎‎‏‏‏‏‎‎‎USIM‎‏‎‎‏‎"</item>
+    <item msgid="8763183137100323328">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‏‎‏‎‎‎‏‎‏‎‎‏‎‏‎‎‎‏‎‎‏‎‎‎‏‎‎‏‎‎‎‏‏‏‎‏‏‎‏‏‎‏‎‎‎‎‎‎‎‎‎‎RSIM‎‏‎‎‏‎"</item>
+    <item msgid="3778400020205644807">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‏‎‏‏‏‏‏‎‎‏‎‎‏‎‏‏‎‎‏‏‎‎‏‎‏‎‏‏‏‏‏‎‏‎‏‏‏‏‏‎‏‎‏‏‎‎‎‎‎‎‎‏‏‏‎CSIM‎‏‎‎‏‎"</item>
+    <item msgid="3774607711141191499">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‏‎‎‎‏‎‎‎‎‏‏‎‎‏‏‎‏‏‎‏‏‎‏‏‏‎‏‏‎‏‎‎‎‎‎‏‎‏‎‏‎‏‏‏‏‏‎‏‎‎‏‎‏‏‎ISIM‎‏‎‎‏‎"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values-gu/strings.xml b/testapps/TestRcsApp/TestApp/res/values-gu/strings.xml
new file mode 100644
index 0000000..4842877
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-gu/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"RcsClient"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"જોગવાઈનું પરીક્ષણ"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"અધિકાર સોંપવાનું પરીક્ષણ"</string>
+    <string name="uce_test" msgid="6466919844953883038">"UCE પરીક્ષણ"</string>
+    <string name="gba_test" msgid="8786745991279224003">"GBA પરીક્ષણ"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"TestMessageClient"</string>
+    <string name="db_client" msgid="5131401341721936367">"DBClient"</string>
+    <string name="result" msgid="435222369457564619">"પરિણામ:"</string>
+    <string name="callback_result" msgid="7162990408241244749">"કૉલબૅકનું પરિણામ:"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"initializeSipDelegate"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"destroySipDelegate"</string>
+    <string name="uce_description" msgid="1117661457446955869">"ક્વેરીની ક્ષમતામાં નંબર દાખલ કરો અને એક કરતાં વધુ હોય તો વડે \',\' જુદાં પાડો."</string>
+    <string name="number" msgid="9096578126043995890">"નંબર:"</string>
+    <string name="request_capability" msgid="7644752985651089411">"requestCapability"</string>
+    <string name="request_availability" msgid="6387685341316953239">"requestNetworkAvailability"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"bootstrapAuthenticationRequest"</string>
+    <string name="start_chat" msgid="96121236227112396">"ચૅટ શરૂ કરો"</string>
+    <string name="to" msgid="1156781884609653284">"પ્રતિ:"</string>
+    <string name="chat_message" msgid="3945528453939746046">"ચૅટ સંદેશ"</string>
+    <string name="send" msgid="8797467615003240434">"મોકલો"</string>
+    <string name="ok" msgid="2206671077510386729">"ઓકે"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"સત્ર શરૂ કરવાનું સફળ થયું"</string>
+    <string name="session_failed" msgid="4524986240102190509">"સત્ર શરૂ કરવાનું નિષ્ફળ રહ્યું"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"સત્ર તૈયાર નથી"</string>
+    <string name="organization" msgid="913518283470119113">"સંસ્થા:"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"UICCનો પ્રકાર:"</string>
+    <string name="protocol" msgid="8626139294022821930">"પ્રોટોકૉલ:"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"TLS સાઇફર સ્યૂટ:"</string>
+    <string name="naf" msgid="7620247915001854662">"NAF URI:"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"સ્ટેન્ડઅલોન પેજર"</string>
+    <string name="standalone_large" msgid="336170805696694543">"સ્ટેન્ડઅલોન મોટું"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"સ્ટેન્ડઅલોન સ્થગિત થયું"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"સ્ટેન્ડઅલોન મોટું પેજર"</string>
+    <string name="chat" msgid="5500583808773151982">"ચૅટ"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"ફાઇલ ટ્રાન્સફર"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"ભૌગોલિક સ્થાન SMS"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"ચૅટબૉટ સત્ર"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"ચૅટબૉટ સ્ટેન્ડઅલોન"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"ચૅટબૉટ વર્ઝન"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"જોગવાઈ કરી"</string>
+    <string name="registration_done" msgid="492878616836655421">"રજિસ્ટ્રેશન થઈ ગયું"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"કોઈ નહીં"</item>
+    <item msgid="7841117287115880098">"3GPP"</item>
+    <item msgid="5871357530951196401">"3GPP2"</item>
+    <item msgid="4979706772323445701">"OMA"</item>
+    <item msgid="1202873556413702218">"GSMA"</item>
+    <item msgid="2237392451840970059">"સ્થાનિક"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"SUBSCRIBER_CERTIFICATE"</item>
+    <item msgid="6305648228108532104">"MBMS"</item>
+    <item msgid="4369286857827087787">"HTTP_DIGEST_AUTH"</item>
+    <item msgid="1047245839171009229">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="1728229445161381738">"GENERIC_PUSH_LAYER"</item>
+    <item msgid="8586727908453122385">"IMS_MEDIA_PLANE"</item>
+    <item msgid="5721752897501838331">"GENERATION_TMPI"</item>
+    <item msgid="1622910302975417378">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="7519270727796373646">"TLS_DEFAULT"</item>
+    <item msgid="7444251498958422990">"TLS_BROWSER"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"અજાણ"</item>
+    <item msgid="1715684709703792003">"SIM"</item>
+    <item msgid="4592543026990143036">"USIM"</item>
+    <item msgid="8763183137100323328">"RSIM"</item>
+    <item msgid="3778400020205644807">"CSIM"</item>
+    <item msgid="3774607711141191499">"ISIM"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values-ko/strings.xml b/testapps/TestRcsApp/TestApp/res/values-ko/strings.xml
new file mode 100644
index 0000000..38f1bd5
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-ko/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"RcsClient"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"프로비저닝 테스트"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"테스트 위임"</string>
+    <string name="uce_test" msgid="6466919844953883038">"UCE 테스트"</string>
+    <string name="gba_test" msgid="8786745991279224003">"GBA 테스트"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"TestMessageClient"</string>
+    <string name="db_client" msgid="5131401341721936367">"DBClient"</string>
+    <string name="result" msgid="435222369457564619">"결과:"</string>
+    <string name="callback_result" msgid="7162990408241244749">"콜백 결과:"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"initializeSipDelegate"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"destroySipDelegate"</string>
+    <string name="uce_description" msgid="1117661457446955869">"기능을 쿼리할 숫자를 입력하고, 여러 개일 경우 \',\' 기호로 구분하세요."</string>
+    <string name="number" msgid="9096578126043995890">"번호:"</string>
+    <string name="request_capability" msgid="7644752985651089411">"requestCapability"</string>
+    <string name="request_availability" msgid="6387685341316953239">"requestNetworkAvailability"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"bootstrapAuthenticationRequest"</string>
+    <string name="start_chat" msgid="96121236227112396">"채팅 시작"</string>
+    <string name="to" msgid="1156781884609653284">"대상:"</string>
+    <string name="chat_message" msgid="3945528453939746046">"채팅 메시지"</string>
+    <string name="send" msgid="8797467615003240434">"보내기"</string>
+    <string name="ok" msgid="2206671077510386729">"확인"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"세션 초기화 성공"</string>
+    <string name="session_failed" msgid="4524986240102190509">"세션 초기화 실패"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"세션이 준비되지 않음"</string>
+    <string name="organization" msgid="913518283470119113">"조직:"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"UICC 유형:"</string>
+    <string name="protocol" msgid="8626139294022821930">"프로토콜:"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"TLS 암호화 스위트:"</string>
+    <string name="naf" msgid="7620247915001854662">"NAF URI:"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"독립형 페이저"</string>
+    <string name="standalone_large" msgid="336170805696694543">"독립형 대형"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"독립형 지연됨"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"독립형 대형 페이저"</string>
+    <string name="chat" msgid="5500583808773151982">"채팅"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"파일 전송"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"위치정보 SMS"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"챗봇 세션"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"독립형 챗봇"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"챗봇 버전"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"프로비저닝 완료"</string>
+    <string name="registration_done" msgid="492878616836655421">"등록 완료"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"없음"</item>
+    <item msgid="7841117287115880098">"3GPP"</item>
+    <item msgid="5871357530951196401">"3GPP2"</item>
+    <item msgid="4979706772323445701">"OMA"</item>
+    <item msgid="1202873556413702218">"GSMA"</item>
+    <item msgid="2237392451840970059">"로컬"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"SUBSCRIBER_CERTIFICATE"</item>
+    <item msgid="6305648228108532104">"MBMS"</item>
+    <item msgid="4369286857827087787">"HTTP_DIGEST_AUTH"</item>
+    <item msgid="1047245839171009229">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="1728229445161381738">"GENERIC_PUSH_LAYER"</item>
+    <item msgid="8586727908453122385">"IMS_MEDIA_PLANE"</item>
+    <item msgid="5721752897501838331">"GENERATION_TMPI"</item>
+    <item msgid="1622910302975417378">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="7519270727796373646">"TLS_DEFAULT"</item>
+    <item msgid="7444251498958422990">"TLS_BROWSER"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"알 수 없음"</item>
+    <item msgid="1715684709703792003">"SIM"</item>
+    <item msgid="4592543026990143036">"USIM"</item>
+    <item msgid="8763183137100323328">"RSIM"</item>
+    <item msgid="3778400020205644807">"CSIM"</item>
+    <item msgid="3774607711141191499">"ISIM"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values-lo/strings.xml b/testapps/TestRcsApp/TestApp/res/values-lo/strings.xml
new file mode 100644
index 0000000..6050bad
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-lo/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"RcsClient"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"Provisioning Test"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"ທົດສອບຕົວແທນ"</string>
+    <string name="uce_test" msgid="6466919844953883038">"ທົດສອບ UCE"</string>
+    <string name="gba_test" msgid="8786745991279224003">"GBA Test"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"TestMessageClient"</string>
+    <string name="db_client" msgid="5131401341721936367">"DBClient"</string>
+    <string name="result" msgid="435222369457564619">"ຜົນໄດ້ຮັບ:"</string>
+    <string name="callback_result" msgid="7162990408241244749">"ຜົນການໂທກັບ:"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"initializeSipDelegate"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"destroySipDelegate"</string>
+    <string name="uce_description" msgid="1117661457446955869">"ປ້ອນໝາຍເລກໃສ່ຄຳຊອກຫາ ແລະ ຂັ້ນດ້ວຍ \',\' ຖ້າມີຫຼາຍກວ່າໜຶ່ງ."</string>
+    <string name="number" msgid="9096578126043995890">"ໝາຍເລກ:"</string>
+    <string name="request_capability" msgid="7644752985651089411">"requestCapability"</string>
+    <string name="request_availability" msgid="6387685341316953239">"requestNetworkAvailability"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"bootstrapAuthenticationRequest"</string>
+    <string name="start_chat" msgid="96121236227112396">"ເລີ່ມການສົນທະນາ"</string>
+    <string name="to" msgid="1156781884609653284">"ຫາ:"</string>
+    <string name="chat_message" msgid="3945528453939746046">"ຂໍ້ຄວາມສົນທະນາ"</string>
+    <string name="send" msgid="8797467615003240434">"ສົ່ງ"</string>
+    <string name="ok" msgid="2206671077510386729">"ຕົກລົງ"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"ເລີ່ມເຊດຊັນສຳເລັດ"</string>
+    <string name="session_failed" msgid="4524986240102190509">"ເລີ່ມເຊດຊັນຜິດພາດ"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"ເຊດຊັນບໍ່ທັນພ້ອມ"</string>
+    <string name="organization" msgid="913518283470119113">"ອົງການ:"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"ປະເພດ UICC:"</string>
+    <string name="protocol" msgid="8626139294022821930">"ໂປຣໂຕຄໍ:"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"ຊຸດ TLS Cipher:"</string>
+    <string name="naf" msgid="7620247915001854662">"NAF URI:"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"Standalone Pager"</string>
+    <string name="standalone_large" msgid="336170805696694543">"Standalone Large"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"Standalone Deferred"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"Standalone Large Pager"</string>
+    <string name="chat" msgid="5500583808773151982">"ສົນທະນາ"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"ການໂອນໄຟລ໌"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"ທີ່ຕັ້ງພູມສາດ SMS"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"ເຊດຊັນ Chatbot"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"Chatbot ແບບດ່ຽວ"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"ເວີຊັນ Chatbot"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"Provisioning Done"</string>
+    <string name="registration_done" msgid="492878616836655421">"Registration Done"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"ບໍ່ມີ"</item>
+    <item msgid="7841117287115880098">"3GPP"</item>
+    <item msgid="5871357530951196401">"3GPP2"</item>
+    <item msgid="4979706772323445701">"OMA"</item>
+    <item msgid="1202873556413702218">"GSMA"</item>
+    <item msgid="2237392451840970059">"ພາຍໃນ"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"SUBSCRIBER_CERTIFICATE"</item>
+    <item msgid="6305648228108532104">"MBMS"</item>
+    <item msgid="4369286857827087787">"HTTP_DIGEST_AUTH"</item>
+    <item msgid="1047245839171009229">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="1728229445161381738">"GENERIC_PUSH_LAYER"</item>
+    <item msgid="8586727908453122385">"IMS_MEDIA_PLANE"</item>
+    <item msgid="5721752897501838331">"GENERATION_TMPI"</item>
+    <item msgid="1622910302975417378">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="7519270727796373646">"TLS_DEFAULT"</item>
+    <item msgid="7444251498958422990">"TLS_BROWSER"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"ບໍ່ຮູ້ຈັກ"</item>
+    <item msgid="1715684709703792003">"SIM"</item>
+    <item msgid="4592543026990143036">"USIM"</item>
+    <item msgid="8763183137100323328">"RSIM"</item>
+    <item msgid="3778400020205644807">"CSIM"</item>
+    <item msgid="3774607711141191499">"ISIM"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values-ml/strings.xml b/testapps/TestRcsApp/TestApp/res/values-ml/strings.xml
new file mode 100644
index 0000000..00d9a4e
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-ml/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"RcsClient"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"പ്രൊവിഷനിംഗ് ടെസ്‌റ്റ്"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"ഡെലിഗേറ്റ് ടെസ്‌റ്റ്"</string>
+    <string name="uce_test" msgid="6466919844953883038">"UCE ടെസ്‌റ്റ്"</string>
+    <string name="gba_test" msgid="8786745991279224003">"GBA ടെസ്‌റ്റ്"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"TestMessageClient"</string>
+    <string name="db_client" msgid="5131401341721936367">"DBClient"</string>
+    <string name="result" msgid="435222369457564619">"ഫലം:"</string>
+    <string name="callback_result" msgid="7162990408241244749">"തിരികെ വിളിക്കൽ ഫലം:"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"initializeSipDelegate"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"destroySipDelegate"</string>
+    <string name="uce_description" msgid="1117661457446955869">"ചോദ്യത്തിന്റെ കാര്യക്ഷമത എന്നതിലേക്ക് നമ്പർ നൽകുക, ഒന്നിലധികം ഉണ്ടെങ്കിൽ അവയെ \',\' ഉപയോഗിച്ച് വേർതിരിക്കുക."</string>
+    <string name="number" msgid="9096578126043995890">"നമ്പർ:"</string>
+    <string name="request_capability" msgid="7644752985651089411">"requestCapability"</string>
+    <string name="request_availability" msgid="6387685341316953239">"requestNetworkAvailability"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"bootstrapAuthenticationRequest"</string>
+    <string name="start_chat" msgid="96121236227112396">"ചാറ്റ് ആരംഭിക്കുക"</string>
+    <string name="to" msgid="1156781884609653284">"സ്വീകർത്താവ്:"</string>
+    <string name="chat_message" msgid="3945528453939746046">"Chat സന്ദേശം"</string>
+    <string name="send" msgid="8797467615003240434">"അയയ്ക്കുക"</string>
+    <string name="ok" msgid="2206671077510386729">"ശരി"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"സെഷൻ ആരംഭിച്ചു"</string>
+    <string name="session_failed" msgid="4524986240102190509">"സെഷൻ ആരംഭിക്കാനായില്ല"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"സെഷൻ തയ്യാറല്ല"</string>
+    <string name="organization" msgid="913518283470119113">"ഓർഗനൈസേഷൻ:"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"UICC തരം:"</string>
+    <string name="protocol" msgid="8626139294022821930">"പ്രോട്ടോക്കോൾ:"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"TLS Cipher Suite:"</string>
+    <string name="naf" msgid="7620247915001854662">"NAF URI:"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"സ്‌റ്റാൻഡ്എലോൺ പേജർ"</string>
+    <string name="standalone_large" msgid="336170805696694543">"സ്‌റ്റാൻഡ്എലോൺ ലാർജ്"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"സ്‌റ്റാൻഡ്എലോൺ ഡെഫേഡ്"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"സ്‌റ്റാൻഡ്എലോൺ ലാർജ് പേജർ"</string>
+    <string name="chat" msgid="5500583808773151982">"Chat"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"ഫയൽ കൈമാറൽ"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"ജിയോലൊക്കേഷൻ SMS"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"ചാറ്റ്ബോട്ട് സെഷൻ"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"ചാറ്റ്ബോട്ട് സ്‌റ്റാൻഡ്എലോൺ"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"ചാറ്റ്ബോട്ട് പതിപ്പ്"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"പ്രൊവിഷനിംഗ് പൂർത്തിയായി"</string>
+    <string name="registration_done" msgid="492878616836655421">"രജിസ്‌ട്രേഷൻ പൂർത്തിയായി"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"ഒന്നുമില്ല"</item>
+    <item msgid="7841117287115880098">"3GPP"</item>
+    <item msgid="5871357530951196401">"3GPP2"</item>
+    <item msgid="4979706772323445701">"OMA"</item>
+    <item msgid="1202873556413702218">"GSMA"</item>
+    <item msgid="2237392451840970059">"LOCAL"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"SUBSCRIBER_CERTIFICATE"</item>
+    <item msgid="6305648228108532104">"MBMS"</item>
+    <item msgid="4369286857827087787">"HTTP_DIGEST_AUTH"</item>
+    <item msgid="1047245839171009229">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="1728229445161381738">"GENERIC_PUSH_LAYER"</item>
+    <item msgid="8586727908453122385">"IMS_MEDIA_PLANE"</item>
+    <item msgid="5721752897501838331">"GENERATION_TMPI"</item>
+    <item msgid="1622910302975417378">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="7519270727796373646">"TLS_DEFAULT"</item>
+    <item msgid="7444251498958422990">"TLS_BROWSER"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"UNKNOWN"</item>
+    <item msgid="1715684709703792003">"സിം"</item>
+    <item msgid="4592543026990143036">"USIM"</item>
+    <item msgid="8763183137100323328">"RSIM"</item>
+    <item msgid="3778400020205644807">"CSIM"</item>
+    <item msgid="3774607711141191499">"ISIM"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values-ms/strings.xml b/testapps/TestRcsApp/TestApp/res/values-ms/strings.xml
new file mode 100644
index 0000000..758b2bd
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-ms/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"RcsClient"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"Ujian Peruntukan"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"Tugaskan Ujian"</string>
+    <string name="uce_test" msgid="6466919844953883038">"Ujian UCE"</string>
+    <string name="gba_test" msgid="8786745991279224003">"Ujian GBA"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"TestMessageClient"</string>
+    <string name="db_client" msgid="5131401341721936367">"DBClient"</string>
+    <string name="result" msgid="435222369457564619">"Hasil:"</string>
+    <string name="callback_result" msgid="7162990408241244749">"Hasil Panggil Balik:"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"initializeSipDelegate"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"destroySipDelegate"</string>
+    <string name="uce_description" msgid="1117661457446955869">"Masukkan nombor untuk bertanya keupayaan dan pisahkan dengan \',\' jika terdapat berbilang nombor."</string>
+    <string name="number" msgid="9096578126043995890">"Nombor:"</string>
+    <string name="request_capability" msgid="7644752985651089411">"requestCapability"</string>
+    <string name="request_availability" msgid="6387685341316953239">"requestNetworkAvailability"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"bootstrapAuthenticationRequest"</string>
+    <string name="start_chat" msgid="96121236227112396">"Mulakan Sembang"</string>
+    <string name="to" msgid="1156781884609653284">"Kepada:"</string>
+    <string name="chat_message" msgid="3945528453939746046">"Mesej Sembang"</string>
+    <string name="send" msgid="8797467615003240434">"Hantar"</string>
+    <string name="ok" msgid="2206671077510386729">"OK"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"Pemulaan sesi berjaya"</string>
+    <string name="session_failed" msgid="4524986240102190509">"Pemulaan sesi gagal"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"Sesi belum sedia"</string>
+    <string name="organization" msgid="913518283470119113">"Organisasi:"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"Jenis UICC:"</string>
+    <string name="protocol" msgid="8626139294022821930">"Protokol:"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"Set Sifer TLS:"</string>
+    <string name="naf" msgid="7620247915001854662">"URI NAF:"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"Alat Kelui Kendiri"</string>
+    <string name="standalone_large" msgid="336170805696694543">"Apl Kendiri Besar"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"Apl Kendiri Ditunda"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"Alat Kelui Kendiri Besar"</string>
+    <string name="chat" msgid="5500583808773151982">"Sembang"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"Pemindahan Fail"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"SMS Geolokasi"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"Sesi Chatbot"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"Apl Kendiri Chatbot"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"Versi Chatbot"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"Peruntukan Selesai"</string>
+    <string name="registration_done" msgid="492878616836655421">"Pendaftaran Selesai"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"TIADA"</item>
+    <item msgid="7841117287115880098">"3GPP"</item>
+    <item msgid="5871357530951196401">"3GPP2"</item>
+    <item msgid="4979706772323445701">"OMA"</item>
+    <item msgid="1202873556413702218">"GSMA"</item>
+    <item msgid="2237392451840970059">"SETEMPAT"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"SUBSCRIBER_CERTIFICATE"</item>
+    <item msgid="6305648228108532104">"MBMS"</item>
+    <item msgid="4369286857827087787">"HTTP_DIGEST_AUTH"</item>
+    <item msgid="1047245839171009229">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="1728229445161381738">"GENERIC_PUSH_LAYER"</item>
+    <item msgid="8586727908453122385">"IMS_MEDIA_PLANE"</item>
+    <item msgid="5721752897501838331">"GENERATION_TMPI"</item>
+    <item msgid="1622910302975417378">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="7519270727796373646">"TLS_DEFAULT"</item>
+    <item msgid="7444251498958422990">"TLS_BROWSER"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"TIDAK DIKETAHUI"</item>
+    <item msgid="1715684709703792003">"SIM"</item>
+    <item msgid="4592543026990143036">"USIM"</item>
+    <item msgid="8763183137100323328">"RSIM"</item>
+    <item msgid="3778400020205644807">"CSIM"</item>
+    <item msgid="3774607711141191499">"ISIM"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values-ne/strings.xml b/testapps/TestRcsApp/TestApp/res/values-ne/strings.xml
new file mode 100644
index 0000000..51cf376
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-ne/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"RcsClient"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"प्रबन्ध मिलाउने परीक्षण"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"परीक्षण गर्ने जिम्मा दिनुहोस्"</string>
+    <string name="uce_test" msgid="6466919844953883038">"UCE परीक्षण"</string>
+    <string name="gba_test" msgid="8786745991279224003">"GBA परीक्षण"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"TestMessageClient"</string>
+    <string name="db_client" msgid="5131401341721936367">"DBClient"</string>
+    <string name="result" msgid="435222369457564619">"परिणाम:"</string>
+    <string name="callback_result" msgid="7162990408241244749">"कलब्याकसम्बन्धी परिणाम:"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"initializeSipDelegate"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"destroySipDelegate"</string>
+    <string name="uce_description" msgid="1117661457446955869">"क्वेरी क्यापाबिलिटीको नम्बर हाल्नुहोस्। एकभन्दा बढी नम्बर छन् भने \',\' ले छुट्याउनुहोस्।"</string>
+    <string name="number" msgid="9096578126043995890">"नम्बर:"</string>
+    <string name="request_capability" msgid="7644752985651089411">"requestCapability"</string>
+    <string name="request_availability" msgid="6387685341316953239">"requestNetworkAvailability"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"bootstrapAuthenticationRequest"</string>
+    <string name="start_chat" msgid="96121236227112396">"च्याट गर्नुहोस्"</string>
+    <string name="to" msgid="1156781884609653284">"प्रापक:"</string>
+    <string name="chat_message" msgid="3945528453939746046">"च्याट म्यासेज"</string>
+    <string name="send" msgid="8797467615003240434">"पठाउनुहोस्"</string>
+    <string name="ok" msgid="2206671077510386729">"ठिक छ"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"सत्र सफलतापूर्वक सुरु गरियो"</string>
+    <string name="session_failed" msgid="4524986240102190509">"सत्र सुरु गर्न सकिएन"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"सत्र तयार छैन"</string>
+    <string name="organization" msgid="913518283470119113">"सङ्गठन:"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"UICC को प्रकार:"</string>
+    <string name="protocol" msgid="8626139294022821930">"प्रोटोकोल:"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"TLS Cipher Suite:"</string>
+    <string name="naf" msgid="7620247915001854662">"NAF URI:"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"स्ट्यान्डअलोन पेजर"</string>
+    <string name="standalone_large" msgid="336170805696694543">"स्ट्यान्डअलोन ठुलो"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"स्ट्यान्डअलोन विलम्बित गरिएको"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"स्ट्यान्डअलोन ठुलो पेजर"</string>
+    <string name="chat" msgid="5500583808773151982">"Chat"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"फाइल स्थानान्तरण"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"भूस्थानसम्बन्धी SMS"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"च्याटबटसँगको सत्र"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"च्याटबट स्ट्यान्डअलोन"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"च्याटबटको संस्करण"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"प्रबन्ध मिलाउने कार्य पूरा भयो"</string>
+    <string name="registration_done" msgid="492878616836655421">"दर्ता गर्ने कार्य पूरा भयो"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"कुनै पनि होइन"</item>
+    <item msgid="7841117287115880098">"3GPP"</item>
+    <item msgid="5871357530951196401">"3GPP2"</item>
+    <item msgid="4979706772323445701">"OMA"</item>
+    <item msgid="1202873556413702218">"GSMA"</item>
+    <item msgid="2237392451840970059">"स्थानीय"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"SUBSCRIBER_CERTIFICATE"</item>
+    <item msgid="6305648228108532104">"MBMS"</item>
+    <item msgid="4369286857827087787">"HTTP_DIGEST_AUTH"</item>
+    <item msgid="1047245839171009229">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="1728229445161381738">"GENERIC_PUSH_LAYER"</item>
+    <item msgid="8586727908453122385">"IMS_MEDIA_PLANE"</item>
+    <item msgid="5721752897501838331">"GENERATION_TMPI"</item>
+    <item msgid="1622910302975417378">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="7519270727796373646">"TLS_DEFAULT"</item>
+    <item msgid="7444251498958422990">"TLS_BROWSER"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"अज्ञात"</item>
+    <item msgid="1715684709703792003">"SIM"</item>
+    <item msgid="4592543026990143036">"USIM"</item>
+    <item msgid="8763183137100323328">"RSIM"</item>
+    <item msgid="3778400020205644807">"CSIM"</item>
+    <item msgid="3774607711141191499">"ISIM"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values-or/strings.xml b/testapps/TestRcsApp/TestApp/res/values-or/strings.xml
new file mode 100644
index 0000000..5c9491b
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-or/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"RcsClient"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"ପ୍ରୋଭିଜନିଂ ଟେଷ୍ଟ"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"ଟେଷ୍ଟ ଡେଲିଗେଟ୍ କରନ୍ତୁ"</string>
+    <string name="uce_test" msgid="6466919844953883038">"UCE ଟେଷ୍ଟ"</string>
+    <string name="gba_test" msgid="8786745991279224003">"GBA ଟେଷ୍ଟ"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"TestMessageClient"</string>
+    <string name="db_client" msgid="5131401341721936367">"DBClient"</string>
+    <string name="result" msgid="435222369457564619">"ଫଳାଫଳ:"</string>
+    <string name="callback_result" msgid="7162990408241244749">"କଲବ୍ୟାକ୍ ଫଳାଫଳ:"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"initializeSipDelegate"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"destroySipDelegate"</string>
+    <string name="uce_description" msgid="1117661457446955869">"ସାମର୍ଥ୍ୟ ପାଇଁ କ୍ୱେରୀ କରିବାକୁ ନମ୍ବର ଲେଖନ୍ତୁ ଏବଂ ଯଦି ଏକାଧିକ ନମ୍ବର ଅଛି ତେବେ \',\' ଦ୍ୱାରା ଅଲଗା କରନ୍ତୁ।"</string>
+    <string name="number" msgid="9096578126043995890">"ନମ୍ବର:"</string>
+    <string name="request_capability" msgid="7644752985651089411">"requestCapability"</string>
+    <string name="request_availability" msgid="6387685341316953239">"requestNetworkAvailability"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"bootstrapAuthenticationRequest"</string>
+    <string name="start_chat" msgid="96121236227112396">"ଚାଟ୍ ଆରମ୍ଭ କରନ୍ତୁ"</string>
+    <string name="to" msgid="1156781884609653284">"ପ୍ରାପ୍ତେଷୁ:"</string>
+    <string name="chat_message" msgid="3945528453939746046">"ଚାଟ୍ ମେସେଜ୍"</string>
+    <string name="send" msgid="8797467615003240434">"ପଠାନ୍ତୁ"</string>
+    <string name="ok" msgid="2206671077510386729">"ଠିକ୍ ଅଛି"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"ସେସନ୍ ଆରମ୍ଭ ସଫଳ ହୋଇଛି"</string>
+    <string name="session_failed" msgid="4524986240102190509">"ସେସନ୍ ଆରମ୍ଭ ବିଫଳ ହୋଇଛି"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"ସେସନ୍ ପ୍ରସ୍ତୁତ ନୁହେଁ"</string>
+    <string name="organization" msgid="913518283470119113">"ସଂସ୍ଥା:"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"UICC ପ୍ରକାର:"</string>
+    <string name="protocol" msgid="8626139294022821930">"ପ୍ରୋଟୋକଲ୍:"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"TLS ସାଇଫର୍ ସୁଟ୍:"</string>
+    <string name="naf" msgid="7620247915001854662">"NAF URI:"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"ଷ୍ଟାଣ୍ଡଏଲୋନ୍ ପେଜର୍"</string>
+    <string name="standalone_large" msgid="336170805696694543">"ଷ୍ଟାଣ୍ଡଏଲୋନ୍ ବଡ଼"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"ଷ୍ଟାଣ୍ଡଏଲୋନ୍ ସ୍ଥଗିତ ହୋଇଛି"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"ଷ୍ଟାଣ୍ଡଏଲୋନ୍ ବଡ଼ ପେଜର୍"</string>
+    <string name="chat" msgid="5500583808773151982">"ଚାଟ୍"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"ଫାଇଲ୍ ଟ୍ରାନ୍ସଫର୍"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"ଜିଓଲୋକେସନ୍ SMS"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"ଚାଟବଟ୍ ସେସନ୍"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"ଚାଟବଟ୍ ଷ୍ଟାଣ୍ଡଏଲୋନ୍"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"ଚାଟବଟ୍ ସଂସ୍କରଣ"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"ପ୍ରୋଭିଜନିଂ ହୋଇଗଲା"</string>
+    <string name="registration_done" msgid="492878616836655421">"ପଞ୍ଜିକରଣ ହୋଇଗଲା"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"କିଛି ନାହିଁ"</item>
+    <item msgid="7841117287115880098">"3GPP"</item>
+    <item msgid="5871357530951196401">"3GPP2"</item>
+    <item msgid="4979706772323445701">"OMA"</item>
+    <item msgid="1202873556413702218">"GSMA"</item>
+    <item msgid="2237392451840970059">"ସ୍ଥାନୀୟ"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"SUBSCRIBER_CERTIFICATE"</item>
+    <item msgid="6305648228108532104">"MBMS"</item>
+    <item msgid="4369286857827087787">"HTTP_DIGEST_AUTH"</item>
+    <item msgid="1047245839171009229">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="1728229445161381738">"GENERIC_PUSH_LAYER"</item>
+    <item msgid="8586727908453122385">"IMS_MEDIA_PLANE"</item>
+    <item msgid="5721752897501838331">"GENERATION_TMPI"</item>
+    <item msgid="1622910302975417378">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="7519270727796373646">"TLS_DEFAULT"</item>
+    <item msgid="7444251498958422990">"TLS_BROWSER"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"ଅଜଣା"</item>
+    <item msgid="1715684709703792003">"SIM"</item>
+    <item msgid="4592543026990143036">"USIM"</item>
+    <item msgid="8763183137100323328">"RSIM"</item>
+    <item msgid="3778400020205644807">"CSIM"</item>
+    <item msgid="3774607711141191499">"ISIM"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values-ro/strings.xml b/testapps/TestRcsApp/TestApp/res/values-ro/strings.xml
new file mode 100644
index 0000000..593cf46
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-ro/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"RcsClient"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"Test de configurare a accesului pentru utilizatori"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"Test delegat"</string>
+    <string name="uce_test" msgid="6466919844953883038">"Test UCE"</string>
+    <string name="gba_test" msgid="8786745991279224003">"Test GBA"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"TestMessageClient"</string>
+    <string name="db_client" msgid="5131401341721936367">"DBClient"</string>
+    <string name="result" msgid="435222369457564619">"Rezultat:"</string>
+    <string name="callback_result" msgid="7162990408241244749">"Rezultatul apelării inverse:"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"initializeSipDelegate"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"destroySipDelegate"</string>
+    <string name="uce_description" msgid="1117661457446955869">"Introduceți numărul pentru capacitatea de interogare și separați prin „,” dacă aveți mai multe."</string>
+    <string name="number" msgid="9096578126043995890">"Număr:"</string>
+    <string name="request_capability" msgid="7644752985651089411">"requestCapability"</string>
+    <string name="request_availability" msgid="6387685341316953239">"requestNetworkAvailability"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"bootstrapAuthenticationRequest"</string>
+    <string name="start_chat" msgid="96121236227112396">"Începeți conversația"</string>
+    <string name="to" msgid="1156781884609653284">"Către:"</string>
+    <string name="chat_message" msgid="3945528453939746046">"Mesaj prin chat"</string>
+    <string name="send" msgid="8797467615003240434">"Trimiteți"</string>
+    <string name="ok" msgid="2206671077510386729">"OK"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"S-a inițializat sesiunea"</string>
+    <string name="session_failed" msgid="4524986240102190509">"Sesiunea nu a fost inițializată"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"Sesiunea nu este gata"</string>
+    <string name="organization" msgid="913518283470119113">"Organizație:"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"Tip UICC:"</string>
+    <string name="protocol" msgid="8626139294022821930">"Protocol:"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"Suita cifrului TLS:"</string>
+    <string name="naf" msgid="7620247915001854662">"URI NAF:"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"Pager autonom"</string>
+    <string name="standalone_large" msgid="336170805696694543">"Autonom mare"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"Autonom amânat"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"Pager autonom mare"</string>
+    <string name="chat" msgid="5500583808773151982">"Chat"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"Transfer de fișier"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"SMS pentru localizare geografică"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"Sesiune chatbot"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"Chatbot autonom"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"Versiune chatbot"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"S-a configurat accesul pentru utilizatori"</string>
+    <string name="registration_done" msgid="492878616836655421">"S-a înregistrat"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"NICIUNA"</item>
+    <item msgid="7841117287115880098">"3GPP"</item>
+    <item msgid="5871357530951196401">"3GPP2"</item>
+    <item msgid="4979706772323445701">"OMA"</item>
+    <item msgid="1202873556413702218">"GSMA"</item>
+    <item msgid="2237392451840970059">"LOCAL"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"SUBSCRIBER_CERTIFICATE"</item>
+    <item msgid="6305648228108532104">"MBMS"</item>
+    <item msgid="4369286857827087787">"HTTP_DIGEST_AUTH"</item>
+    <item msgid="1047245839171009229">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="1728229445161381738">"GENERIC_PUSH_LAYER"</item>
+    <item msgid="8586727908453122385">"IMS_MEDIA_PLANE"</item>
+    <item msgid="5721752897501838331">"GENERATION_TMPI"</item>
+    <item msgid="1622910302975417378">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="7519270727796373646">"TLS_DEFAULT"</item>
+    <item msgid="7444251498958422990">"TLS_BROWSER"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"NECUNOSCUTĂ"</item>
+    <item msgid="1715684709703792003">"SIM"</item>
+    <item msgid="4592543026990143036">"USIM"</item>
+    <item msgid="8763183137100323328">"RSIM"</item>
+    <item msgid="3778400020205644807">"CSIM"</item>
+    <item msgid="3774607711141191499">"ISIM"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values-ru/strings.xml b/testapps/TestRcsApp/TestApp/res/values-ru/strings.xml
new file mode 100644
index 0000000..1276015
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-ru/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"RcsClient"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"Тест инициализации"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"Делегирование теста"</string>
+    <string name="uce_test" msgid="6466919844953883038">"Тест UCE"</string>
+    <string name="gba_test" msgid="8786745991279224003">"Тест GBA"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"TestMessageClient"</string>
+    <string name="db_client" msgid="5131401341721936367">"DBClient"</string>
+    <string name="result" msgid="435222369457564619">"Результат:"</string>
+    <string name="callback_result" msgid="7162990408241244749">"Результат обратного вызова:"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"initializeSipDelegate"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"destroySipDelegate"</string>
+    <string name="uce_description" msgid="1117661457446955869">"Введите номер для запроса возможности. Разделяйте номера запятыми, если их несколько."</string>
+    <string name="number" msgid="9096578126043995890">"Номер:"</string>
+    <string name="request_capability" msgid="7644752985651089411">"requestCapability"</string>
+    <string name="request_availability" msgid="6387685341316953239">"requestNetworkAvailability"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"bootstrapAuthenticationRequest"</string>
+    <string name="start_chat" msgid="96121236227112396">"Начать чат"</string>
+    <string name="to" msgid="1156781884609653284">"Кому:"</string>
+    <string name="chat_message" msgid="3945528453939746046">"Сообщение чата"</string>
+    <string name="send" msgid="8797467615003240434">"Отправить"</string>
+    <string name="ok" msgid="2206671077510386729">"ОК"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"Инициализация сеанса выполнена"</string>
+    <string name="session_failed" msgid="4524986240102190509">"Сбой инициализации сеанса"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"Сеанс не готов\\"</string>
+    <string name="organization" msgid="913518283470119113">"Организация:"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"Тип UICC:"</string>
+    <string name="protocol" msgid="8626139294022821930">"Протокол:"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"Шифронабор TLS:"</string>
+    <string name="naf" msgid="7620247915001854662">"URI NAF:"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"Автономный пейджер"</string>
+    <string name="standalone_large" msgid="336170805696694543">"Автономный большой"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"Автономный отложенный"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"Автономный большой пейджер"</string>
+    <string name="chat" msgid="5500583808773151982">"Чат"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"Передача файла"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"SMS геолокации"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"Сеанс чат-бота"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"Автономный чат-бот"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"Версия чат-бота"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"Инициализация выполнена"</string>
+    <string name="registration_done" msgid="492878616836655421">"Регистрация выполнена"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"НЕТ"</item>
+    <item msgid="7841117287115880098">"3GPP"</item>
+    <item msgid="5871357530951196401">"3GPP2"</item>
+    <item msgid="4979706772323445701">"OMA"</item>
+    <item msgid="1202873556413702218">"GSMA"</item>
+    <item msgid="2237392451840970059">"МЕСТНАЯ СЕТЬ"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"SUBSCRIBER_CERTIFICATE"</item>
+    <item msgid="6305648228108532104">"MBMS"</item>
+    <item msgid="4369286857827087787">"HTTP_DIGEST_AUTH"</item>
+    <item msgid="1047245839171009229">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="1728229445161381738">"GENERIC_PUSH_LAYER"</item>
+    <item msgid="8586727908453122385">"IMS_MEDIA_PLANE"</item>
+    <item msgid="5721752897501838331">"GENERATION_TMPI"</item>
+    <item msgid="1622910302975417378">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="7519270727796373646">"TLS_DEFAULT"</item>
+    <item msgid="7444251498958422990">"TLS_BROWSER"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"НЕИЗВЕСТНО"</item>
+    <item msgid="1715684709703792003">"SIM"</item>
+    <item msgid="4592543026990143036">"USIM"</item>
+    <item msgid="8763183137100323328">"RSIM"</item>
+    <item msgid="3778400020205644807">"CSIM"</item>
+    <item msgid="3774607711141191499">"ISIM"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values-sl/strings.xml b/testapps/TestRcsApp/TestApp/res/values-sl/strings.xml
new file mode 100644
index 0000000..ca62620
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-sl/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"RcsClient"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"Preizkus priprave za uporabo"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"Preizkus dodelitve"</string>
+    <string name="uce_test" msgid="6466919844953883038">"Preizkus UCE"</string>
+    <string name="gba_test" msgid="8786745991279224003">"Preizkus GBA"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"TestMessageClient"</string>
+    <string name="db_client" msgid="5131401341721936367">"DBClient"</string>
+    <string name="result" msgid="435222369457564619">"Rezultat:"</string>
+    <string name="callback_result" msgid="7162990408241244749">"Rezultat povratnega klica:"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"initializeSipDelegate"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"destroySipDelegate"</string>
+    <string name="uce_description" msgid="1117661457446955869">"Vnesite številko, da pošljete poizvedbo za zmogljivost. Če jih je več, jih ločite z vejicami."</string>
+    <string name="number" msgid="9096578126043995890">"Številka:"</string>
+    <string name="request_capability" msgid="7644752985651089411">"requestCapability"</string>
+    <string name="request_availability" msgid="6387685341316953239">"requestNetworkAvailability"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"bootstrapAuthenticationRequest"</string>
+    <string name="start_chat" msgid="96121236227112396">"Zaženi Chat"</string>
+    <string name="to" msgid="1156781884609653284">"Za:"</string>
+    <string name="chat_message" msgid="3945528453939746046">"Sporočilo v storitvi Chat"</string>
+    <string name="send" msgid="8797467615003240434">"Pošlji"</string>
+    <string name="ok" msgid="2206671077510386729">"V redu"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"Inicializacija seje je uspela"</string>
+    <string name="session_failed" msgid="4524986240102190509">"Inicializacija seje ni uspela"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"Seja ni pripravljena"</string>
+    <string name="organization" msgid="913518283470119113">"Organizacija:"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"Vrsta UICC:"</string>
+    <string name="protocol" msgid="8626139294022821930">"Protokol:"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"Zbirka šifriranja TLS:"</string>
+    <string name="naf" msgid="7620247915001854662">"URI NAF:"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"Samostojni pozivnik"</string>
+    <string name="standalone_large" msgid="336170805696694543">"Samostojno veliko"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"Samostojno odloženo"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"Samostojni veliki pozivnik"</string>
+    <string name="chat" msgid="5500583808773151982">"Chat"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"Prenos datotek"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"Geolokacijski SMS"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"Seja bota za klepet"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"Samostojni bot za klepet"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"Različica bota za klepet"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"Priprava za uporabo je končana"</string>
+    <string name="registration_done" msgid="492878616836655421">"Registracija je končana"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"NONE"</item>
+    <item msgid="7841117287115880098">"3GPP"</item>
+    <item msgid="5871357530951196401">"3GPP2"</item>
+    <item msgid="4979706772323445701">"OMA"</item>
+    <item msgid="1202873556413702218">"GSMA"</item>
+    <item msgid="2237392451840970059">"LOCAL"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"SUBSCRIBER_CERTIFICATE"</item>
+    <item msgid="6305648228108532104">"MBMS"</item>
+    <item msgid="4369286857827087787">"HTTP_DIGEST_AUTH"</item>
+    <item msgid="1047245839171009229">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="1728229445161381738">"GENERIC_PUSH_LAYER"</item>
+    <item msgid="8586727908453122385">"IMS_MEDIA_PLANE"</item>
+    <item msgid="5721752897501838331">"GENERATION_TMPI"</item>
+    <item msgid="1622910302975417378">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="7519270727796373646">"TLS_DEFAULT"</item>
+    <item msgid="7444251498958422990">"TLS_BROWSER"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"UNKNOWN"</item>
+    <item msgid="1715684709703792003">"SIM"</item>
+    <item msgid="4592543026990143036">"USIM"</item>
+    <item msgid="8763183137100323328">"RSIM"</item>
+    <item msgid="3778400020205644807">"CSIM"</item>
+    <item msgid="3774607711141191499">"ISIM"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values-te/strings.xml b/testapps/TestRcsApp/TestApp/res/values-te/strings.xml
new file mode 100644
index 0000000..7a0643d
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-te/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"RcsClient"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"నిర్వహణ టెస్ట్"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"డెలిగేట్ టెస్ట్"</string>
+    <string name="uce_test" msgid="6466919844953883038">"UCE టెస్ట్"</string>
+    <string name="gba_test" msgid="8786745991279224003">"GBA టెస్ట్"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"TestMessageClient"</string>
+    <string name="db_client" msgid="5131401341721936367">"DBClient"</string>
+    <string name="result" msgid="435222369457564619">"ఫలితం:"</string>
+    <string name="callback_result" msgid="7162990408241244749">"కాల్‌బ్యాక్ ఫలితం:"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"initializeSipDelegate"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"destroySipDelegate"</string>
+    <string name="uce_description" msgid="1117661457446955869">"క్వెరీ సామర్థ్యం కోసం నంబర్‌ను ఎంటర్ చేసి, అవి ఒకటి కంటే ఎక్కువ ఉంటే \',\'తో వేరు చేయండి."</string>
+    <string name="number" msgid="9096578126043995890">"నంబర్:"</string>
+    <string name="request_capability" msgid="7644752985651089411">"requestCapability"</string>
+    <string name="request_availability" msgid="6387685341316953239">"requestNetworkAvailability"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"bootstrapAuthenticationRequest"</string>
+    <string name="start_chat" msgid="96121236227112396">"చాట్‌ను ప్రారంభించండి"</string>
+    <string name="to" msgid="1156781884609653284">"వీరికి:"</string>
+    <string name="chat_message" msgid="3945528453939746046">"చాట్ మెసేజ్"</string>
+    <string name="send" msgid="8797467615003240434">"పంపు"</string>
+    <string name="ok" msgid="2206671077510386729">"సరే"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"సెషన్ ప్రారంభం విజయవంతమైంది"</string>
+    <string name="session_failed" msgid="4524986240102190509">"సెషన్ ప్రారంభం విఫలమైంది"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"సెషన్ సిద్ధంగా ఉంది"</string>
+    <string name="organization" msgid="913518283470119113">"సంస్థ:"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"UICC రకం:"</string>
+    <string name="protocol" msgid="8626139294022821930">"ప్రోటోకాల్:"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"TLS Cipher Suite:"</string>
+    <string name="naf" msgid="7620247915001854662">"NAF URI:"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"స్టాండ్ఎలోన్ పేజర్"</string>
+    <string name="standalone_large" msgid="336170805696694543">"స్టాండ్ఎలోన్ లార్జ్"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"స్టాండ్ఎలోన్ వాయిదా వేయబడింది"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"స్టాండ్ఎలోన్ లార్జ్ పేజర్"</string>
+    <string name="chat" msgid="5500583808773151982">"చాట్"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"ఫైల్ బదిలీ"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"భౌగోళిక స్థాన SMS"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"చాట్‌బాట్ సెషన్"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"చాట్‌బాట్ స్టాండ్ఎలోన్"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"చాట్‌బాట్ వెర్షన్"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"నిర్వహణ పూర్తయింది"</string>
+    <string name="registration_done" msgid="492878616836655421">"రిజిస్ట్రేషన్ పూర్తయింది"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"ఏదీ కాదు"</item>
+    <item msgid="7841117287115880098">"3GPP"</item>
+    <item msgid="5871357530951196401">"3GPP2"</item>
+    <item msgid="4979706772323445701">"OMA"</item>
+    <item msgid="1202873556413702218">"GSMA"</item>
+    <item msgid="2237392451840970059">"LOCAL"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"SUBSCRIBER_CERTIFICATE"</item>
+    <item msgid="6305648228108532104">"MBMS"</item>
+    <item msgid="4369286857827087787">"HTTP_DIGEST_AUTH"</item>
+    <item msgid="1047245839171009229">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="1728229445161381738">"GENERIC_PUSH_LAYER"</item>
+    <item msgid="8586727908453122385">"IMS_MEDIA_PLANE"</item>
+    <item msgid="5721752897501838331">"GENERATION_TMPI"</item>
+    <item msgid="1622910302975417378">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="7519270727796373646">"TLS_DEFAULT"</item>
+    <item msgid="7444251498958422990">"TLS_BROWSER"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"తెలియదు"</item>
+    <item msgid="1715684709703792003">"SIM"</item>
+    <item msgid="4592543026990143036">"USIM"</item>
+    <item msgid="8763183137100323328">"RSIM"</item>
+    <item msgid="3778400020205644807">"CSIM"</item>
+    <item msgid="3774607711141191499">"ISIM"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values-tl/strings.xml b/testapps/TestRcsApp/TestApp/res/values-tl/strings.xml
new file mode 100644
index 0000000..e2dc0cb
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-tl/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"RcsClient"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"Pagsusuri ng Provisioning"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"Pagsusuri ng Pinaglaanan"</string>
+    <string name="uce_test" msgid="6466919844953883038">"Pagsusuri ng UCE"</string>
+    <string name="gba_test" msgid="8786745991279224003">"Pagsusuri ng GBA"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"TestMessageClient"</string>
+    <string name="db_client" msgid="5131401341721936367">"DBClient"</string>
+    <string name="result" msgid="435222369457564619">"Resulta:"</string>
+    <string name="callback_result" msgid="7162990408241244749">"Resulta ng Callback:"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"initializeSipDelegate"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"destroySipDelegate"</string>
+    <string name="uce_description" msgid="1117661457446955869">"Ilagay ang numero para i-query ang kakayahan at paghiwa-hiwalayin gamit ang \',\' kung marami ito."</string>
+    <string name="number" msgid="9096578126043995890">"Numero:"</string>
+    <string name="request_capability" msgid="7644752985651089411">"requestCapability"</string>
+    <string name="request_availability" msgid="6387685341316953239">"requestNetworkAvailability"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"bootstrapAuthenticationRequest"</string>
+    <string name="start_chat" msgid="96121236227112396">"Simulan ang Chat"</string>
+    <string name="to" msgid="1156781884609653284">"Para sa:"</string>
+    <string name="chat_message" msgid="3945528453939746046">"Mensahe sa Chat"</string>
+    <string name="send" msgid="8797467615003240434">"Ipadala"</string>
+    <string name="ok" msgid="2206671077510386729">"OK"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"Matagumpay na nasimulan ang session"</string>
+    <string name="session_failed" msgid="4524986240102190509">"Hindi nasimulan ang session"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"Hindi pa handa ang session"</string>
+    <string name="organization" msgid="913518283470119113">"Organisasyon:"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"Uri ng UICC:"</string>
+    <string name="protocol" msgid="8626139294022821930">"Protocol:"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"TLS Cipher Suite:"</string>
+    <string name="naf" msgid="7620247915001854662">"URI ng NAF:"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"Standalone Pager"</string>
+    <string name="standalone_large" msgid="336170805696694543">"Standalone Large"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"Standalone Deferred"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"Standalone Large Pager"</string>
+    <string name="chat" msgid="5500583808773151982">"Chat"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"Paglilipat ng File"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"SMS ng Geolocation"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"Chatbot Session"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"Chatbot Standalone"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"Bersyon ng Chatbot"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"Tapos na ang Provisioning"</string>
+    <string name="registration_done" msgid="492878616836655421">"Tapos na ang Pagpaparehistro"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"WALA"</item>
+    <item msgid="7841117287115880098">"3GPP"</item>
+    <item msgid="5871357530951196401">"3GPP2"</item>
+    <item msgid="4979706772323445701">"OMA"</item>
+    <item msgid="1202873556413702218">"GSMA"</item>
+    <item msgid="2237392451840970059">"LOKAL"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"SUBSCRIBER_CERTIFICATE"</item>
+    <item msgid="6305648228108532104">"MBMS"</item>
+    <item msgid="4369286857827087787">"HTTP_DIGEST_AUTH"</item>
+    <item msgid="1047245839171009229">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="1728229445161381738">"GENERIC_PUSH_LAYER"</item>
+    <item msgid="8586727908453122385">"IMS_MEDIA_PLANE"</item>
+    <item msgid="5721752897501838331">"GENERATION_TMPI"</item>
+    <item msgid="1622910302975417378">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="7519270727796373646">"TLS_DEFAULT"</item>
+    <item msgid="7444251498958422990">"TLS_BROWSER"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"HINDI ALAM"</item>
+    <item msgid="1715684709703792003">"SIM"</item>
+    <item msgid="4592543026990143036">"USIM"</item>
+    <item msgid="8763183137100323328">"RSIM"</item>
+    <item msgid="3778400020205644807">"CSIM"</item>
+    <item msgid="3774607711141191499">"ISIM"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values-zh-rHK/strings.xml b/testapps/TestRcsApp/TestApp/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..ec7f74a
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-zh-rHK/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"RcsClient"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"佈建測試"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"委派測試"</string>
+    <string name="uce_test" msgid="6466919844953883038">"UCE 測試"</string>
+    <string name="gba_test" msgid="8786745991279224003">"GBA 測試"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"TestMessageClient"</string>
+    <string name="db_client" msgid="5131401341721936367">"DBClient"</string>
+    <string name="result" msgid="435222369457564619">"結果:"</string>
+    <string name="callback_result" msgid="7162990408241244749">"回撥電話結果:"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"initializeSipDelegate"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"destroySipDelegate"</string>
+    <string name="uce_description" msgid="1117661457446955869">"輸入要查詢功能的號碼;如果有多組號碼,請以半形逗號 (,) 分隔。"</string>
+    <string name="number" msgid="9096578126043995890">"號碼:"</string>
+    <string name="request_capability" msgid="7644752985651089411">"requestCapability"</string>
+    <string name="request_availability" msgid="6387685341316953239">"requestNetworkAvailability"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"bootstrapAuthenticationRequest"</string>
+    <string name="start_chat" msgid="96121236227112396">"發起即時通訊"</string>
+    <string name="to" msgid="1156781884609653284">"收件者:"</string>
+    <string name="chat_message" msgid="3945528453939746046">"即時通訊訊息"</string>
+    <string name="send" msgid="8797467615003240434">"傳送"</string>
+    <string name="ok" msgid="2206671077510386729">"確定"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"工作階段初始化失敗成功"</string>
+    <string name="session_failed" msgid="4524986240102190509">"工作階段初始化失敗"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"工作階段尚未就緒"</string>
+    <string name="organization" msgid="913518283470119113">"機構:"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"UICC 類型:"</string>
+    <string name="protocol" msgid="8626139294022821930">"通訊協定:"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"TLS 加密套裝組合:"</string>
+    <string name="naf" msgid="7620247915001854662">"NAF URI:"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"一般版 UI:呼叫器"</string>
+    <string name="standalone_large" msgid="336170805696694543">"一般版 UI:大型"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"一般版 UI:已延後"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"一般版 UI:大型呼叫器"</string>
+    <string name="chat" msgid="5500583808773151982">"即時通訊"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"檔案傳輸"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"地理位置簡訊"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"聊天機器人工作階段"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"聊天機器人一般版 UI"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"聊天機器人版本"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"已完成佈建"</string>
+    <string name="registration_done" msgid="492878616836655421">"已完成註冊"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"無"</item>
+    <item msgid="7841117287115880098">"3GPP"</item>
+    <item msgid="5871357530951196401">"3GPP2"</item>
+    <item msgid="4979706772323445701">"OMA"</item>
+    <item msgid="1202873556413702218">"GSMA"</item>
+    <item msgid="2237392451840970059">"本地"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"SUBSCRIBER_CERTIFICATE"</item>
+    <item msgid="6305648228108532104">"MBMS"</item>
+    <item msgid="4369286857827087787">"HTTP_DIGEST_AUTH"</item>
+    <item msgid="1047245839171009229">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="1728229445161381738">"GENERIC_PUSH_LAYER"</item>
+    <item msgid="8586727908453122385">"IMS_MEDIA_PLANE"</item>
+    <item msgid="5721752897501838331">"GENERATION_TMPI"</item>
+    <item msgid="1622910302975417378">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="7519270727796373646">"TLS_DEFAULT"</item>
+    <item msgid="7444251498958422990">"TLS_BROWSER"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"不明"</item>
+    <item msgid="1715684709703792003">"SIM"</item>
+    <item msgid="4592543026990143036">"USIM"</item>
+    <item msgid="8763183137100323328">"RSIM"</item>
+    <item msgid="3778400020205644807">"CSIM"</item>
+    <item msgid="3774607711141191499">"ISIM"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values-zh-rTW/strings.xml b/testapps/TestRcsApp/TestApp/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..ec7f74a
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-zh-rTW/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"RcsClient"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"佈建測試"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"委派測試"</string>
+    <string name="uce_test" msgid="6466919844953883038">"UCE 測試"</string>
+    <string name="gba_test" msgid="8786745991279224003">"GBA 測試"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"TestMessageClient"</string>
+    <string name="db_client" msgid="5131401341721936367">"DBClient"</string>
+    <string name="result" msgid="435222369457564619">"結果:"</string>
+    <string name="callback_result" msgid="7162990408241244749">"回撥電話結果:"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"initializeSipDelegate"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"destroySipDelegate"</string>
+    <string name="uce_description" msgid="1117661457446955869">"輸入要查詢功能的號碼;如果有多組號碼,請以半形逗號 (,) 分隔。"</string>
+    <string name="number" msgid="9096578126043995890">"號碼:"</string>
+    <string name="request_capability" msgid="7644752985651089411">"requestCapability"</string>
+    <string name="request_availability" msgid="6387685341316953239">"requestNetworkAvailability"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"bootstrapAuthenticationRequest"</string>
+    <string name="start_chat" msgid="96121236227112396">"發起即時通訊"</string>
+    <string name="to" msgid="1156781884609653284">"收件者:"</string>
+    <string name="chat_message" msgid="3945528453939746046">"即時通訊訊息"</string>
+    <string name="send" msgid="8797467615003240434">"傳送"</string>
+    <string name="ok" msgid="2206671077510386729">"確定"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"工作階段初始化失敗成功"</string>
+    <string name="session_failed" msgid="4524986240102190509">"工作階段初始化失敗"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"工作階段尚未就緒"</string>
+    <string name="organization" msgid="913518283470119113">"機構:"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"UICC 類型:"</string>
+    <string name="protocol" msgid="8626139294022821930">"通訊協定:"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"TLS 加密套裝組合:"</string>
+    <string name="naf" msgid="7620247915001854662">"NAF URI:"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"一般版 UI:呼叫器"</string>
+    <string name="standalone_large" msgid="336170805696694543">"一般版 UI:大型"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"一般版 UI:已延後"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"一般版 UI:大型呼叫器"</string>
+    <string name="chat" msgid="5500583808773151982">"即時通訊"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"檔案傳輸"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"地理位置簡訊"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"聊天機器人工作階段"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"聊天機器人一般版 UI"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"聊天機器人版本"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"已完成佈建"</string>
+    <string name="registration_done" msgid="492878616836655421">"已完成註冊"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"無"</item>
+    <item msgid="7841117287115880098">"3GPP"</item>
+    <item msgid="5871357530951196401">"3GPP2"</item>
+    <item msgid="4979706772323445701">"OMA"</item>
+    <item msgid="1202873556413702218">"GSMA"</item>
+    <item msgid="2237392451840970059">"本地"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"SUBSCRIBER_CERTIFICATE"</item>
+    <item msgid="6305648228108532104">"MBMS"</item>
+    <item msgid="4369286857827087787">"HTTP_DIGEST_AUTH"</item>
+    <item msgid="1047245839171009229">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="1728229445161381738">"GENERIC_PUSH_LAYER"</item>
+    <item msgid="8586727908453122385">"IMS_MEDIA_PLANE"</item>
+    <item msgid="5721752897501838331">"GENERATION_TMPI"</item>
+    <item msgid="1622910302975417378">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="7519270727796373646">"TLS_DEFAULT"</item>
+    <item msgid="7444251498958422990">"TLS_BROWSER"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"不明"</item>
+    <item msgid="1715684709703792003">"SIM"</item>
+    <item msgid="4592543026990143036">"USIM"</item>
+    <item msgid="8763183137100323328">"RSIM"</item>
+    <item msgid="3778400020205644807">"CSIM"</item>
+    <item msgid="3774607711141191499">"ISIM"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values-zu/strings.xml b/testapps/TestRcsApp/TestApp/res/values-zu/strings.xml
new file mode 100644
index 0000000..d05fa16
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values-zu/strings.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name" msgid="1411081210960307023">"I-RcsClient"</string>
+    <string name="provisioning_test" msgid="1035549386706009246">"Isivivinyo Sokunikezwa"</string>
+    <string name="delegate_test" msgid="8212368545489779643">"Susa Isivivinyo"</string>
+    <string name="uce_test" msgid="6466919844953883038">"Isivivinyo se-UCE"</string>
+    <string name="gba_test" msgid="8786745991279224003">"Isivivinyo se-GBA"</string>
+    <string name="test_msg_client" msgid="5345166277460949996">"TestMessageClient"</string>
+    <string name="db_client" msgid="5131401341721936367">"I-DBClient"</string>
+    <string name="result" msgid="435222369457564619">"Umphumela:"</string>
+    <string name="callback_result" msgid="7162990408241244749">"Imiphumela yokuphinda ushaye:"</string>
+    <string name="initialize_delegate" msgid="1772072838514674246">"initializeSipDelegate"</string>
+    <string name="destroy_delegate" msgid="1678202834033613371">"destroySipDelegate"</string>
+    <string name="uce_description" msgid="1117661457446955869">"Faka inombolo ukubuza amandla futhi uhlukanise ngokuthi \',\' uma kukuningi."</string>
+    <string name="number" msgid="9096578126043995890">"Inombolo:"</string>
+    <string name="request_capability" msgid="7644752985651089411">"requestCapability"</string>
+    <string name="request_availability" msgid="6387685341316953239">"requestNetworkAvailability"</string>
+    <string name="gba_bootstrap" msgid="549563024448934689">"bootstrapAuthenticationRequest"</string>
+    <string name="start_chat" msgid="96121236227112396">"Qalisa Ingxoxo"</string>
+    <string name="to" msgid="1156781884609653284">"Ku:"</string>
+    <string name="chat_message" msgid="3945528453939746046">"Umlayezo Wengxoxo"</string>
+    <string name="send" msgid="8797467615003240434">"Thumela"</string>
+    <string name="ok" msgid="2206671077510386729">"KULUNGILE"</string>
+    <string name="session_succeeded" msgid="4692992136758212220">"Ukuqaliswa kweseshini kuphumelele"</string>
+    <string name="session_failed" msgid="4524986240102190509">"Ukuqaliswa kweseshini kwehlulekile"</string>
+    <string name="session_not_ready" msgid="4612635394279901948">"Iseshini ayilungile"</string>
+    <string name="organization" msgid="913518283470119113">"Inhlangano:"</string>
+    <string name="uicc_type" msgid="5584433205228698588">"Uhlobo lwe-UICC:"</string>
+    <string name="protocol" msgid="8626139294022821930">"Iphrothokholi:"</string>
+    <string name="tls_cs" msgid="5410567052979996834">"I-TLS Cipher Suite:"</string>
+    <string name="naf" msgid="7620247915001854662">"I-NAF URI:"</string>
+    <string name="standalone_pager" msgid="1351341861641131423">"Ipheja ye-Standalone"</string>
+    <string name="standalone_large" msgid="336170805696694543">"I-Standalone Enkulu"</string>
+    <string name="standalone_deferred" msgid="4906576900729108844">"I-Standalone Iyekiswe ukubhekiswa"</string>
+    <string name="standalone_pager_large" msgid="5662294171123433547">"Ipheja ye-Standalone Enkulu"</string>
+    <string name="chat" msgid="5500583808773151982">"Ingxoxo"</string>
+    <string name="file_transfer" msgid="5522080545470609737">"Ukudluliselwa kwefayela"</string>
+    <string name="geolocation_sms" msgid="4463328438708195479">"I-SMS ye-Geolocation"</string>
+    <string name="chatbot_session" msgid="5639314220589146717">"Iseshini ye-Chatbot"</string>
+    <string name="chatbot_standalone" msgid="8159185526765238041">"I-Chatbot Standalone"</string>
+    <string name="chatbot_version" msgid="2431939061806512544">"Inguqulo ye-Chatbot"</string>
+    <string name="provisioning_done" msgid="204618840179891984">"Ukunikezwa kwenziwe"</string>
+    <string name="registration_done" msgid="492878616836655421">"Ukubhalisa Kwenziwe"</string>
+  <string-array name="organization">
+    <item msgid="9074061482474975772">"LUTHO"</item>
+    <item msgid="7841117287115880098">"I-3GPP"</item>
+    <item msgid="5871357530951196401">"I-3GPP2"</item>
+    <item msgid="4979706772323445701">"I-OMA"</item>
+    <item msgid="1202873556413702218">"I-GSMA"</item>
+    <item msgid="2237392451840970059">"OKWENDAWO"</item>
+  </string-array>
+  <string-array name="protocol">
+    <item msgid="7955884038486794595">"SUBSCRIBER_CERTIFICATE"</item>
+    <item msgid="6305648228108532104">"I-MBMS"</item>
+    <item msgid="4369286857827087787">"HTTP_DIGEST_AUTH"</item>
+    <item msgid="1047245839171009229">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="1728229445161381738">"GENERIC_PUSH_LAYER"</item>
+    <item msgid="8586727908453122385">"IMS_MEDIA_PLANE"</item>
+    <item msgid="5721752897501838331">"GENERATION_TMPI"</item>
+    <item msgid="1622910302975417378">"3GPP_HTTP_BASED_MBMS"</item>
+    <item msgid="7519270727796373646">"TLS_DEFAULT"</item>
+    <item msgid="7444251498958422990">"TLS_BROWSER"</item>
+  </string-array>
+  <string-array name="uicc_type">
+    <item msgid="5878327014492655902">"AKWAZIWA"</item>
+    <item msgid="1715684709703792003">"I-SIM"</item>
+    <item msgid="4592543026990143036">"I-USIM"</item>
+    <item msgid="8763183137100323328">"I-RSIM"</item>
+    <item msgid="3778400020205644807">"I-CSIM"</item>
+    <item msgid="3774607711141191499">"I-ISIM"</item>
+  </string-array>
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values/colors.xml b/testapps/TestRcsApp/TestApp/res/values/colors.xml
new file mode 100644
index 0000000..3d5cded
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values/colors.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="colorPrimary">#008577</color>
+    <color name="colorPrimaryDark">#00574B</color>
+    <color name="colorAccent">#D81B60</color>
+</resources>
+
diff --git a/testapps/TestRcsApp/TestApp/res/values/donottranslate_strings.xml b/testapps/TestRcsApp/TestApp/res/values/donottranslate_strings.xml
new file mode 100644
index 0000000..f52b70d
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values/donottranslate_strings.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 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
+  -->
+
+<resources>
+    <string name="app_name">RcsClient</string>
+    <string name="provisioning_test">Provisioning Test</string>
+    <string name="delegate_test">Delegate Test</string>
+    <string name="uce_test">UCE Test</string>
+    <string name="gba_test">GBA Test</string>
+    <string name="test_msg_client">TestMessageClient</string>
+    <string name="db_client">DBClient</string>
+    <string name="rcs_profile">RcsProfile:</string>
+    <string name="register_provisioning_callback">registerProvisioningCallback</string>
+    <string name="unregister_provisioning_callback">unRegisterProvisioningCallback</string>
+    <string name="isRcsVolteSingleRegCapable">isRcsVolteSingleRegCapable</string>
+    <string name="result">Result:</string>
+    <string name="callback_result">Callback Result:</string>
+    <string name="initialize_delegate">initializeSipDelegate</string>
+    <string name="destroy_delegate">destroySipDelegate</string>
+    <string name="uce_description">Enter the number to query capability and separate by \',\' if
+        multiple ones.</string>
+    <string name="number">Number: </string>
+    <string name="request_capability">requestCapability</string>
+    <string name="request_availability">requestNetworkAvailability (1st number)</string>
+    <string name="gba_bootstrap">bootstrapAuthenticationRequest</string>
+    <string name="start_chat">Start Chat</string>
+    <string name="to">To:</string>
+    <string name="chat_message">Chat Message</string>
+    <string name="send">Send</string>
+    <string name="ok">OK</string>
+    <string name="session_initiating">Initializing chat session..</string>
+    <string name="session_timeout">Session initialization timeout</string>
+    <string name="session_succeeded">Session initialization succeeded</string>
+    <string name="session_failed">Session initialization failed</string>
+    <string name="session_broken_or_not_ready">Session broken or not ready</string>
+    <string name="organization">Organization:</string>
+    <string name="uicc_type">UICC Type:</string>
+    <string name="protocol">Protocol:</string>
+    <string name="tls_cs">TLS Cipher Suite:</string>
+    <string name="naf">NAF URI:</string>
+    <string name="standalone_pager">Standalone Pager</string>
+    <string name="standalone_large">Standalone Large</string>
+    <string name="standalone_deferred">Standalone Deferred</string>
+    <string name="standalone_pager_large">Standalone Large Pager</string>
+    <string name="chat">Chat</string>
+    <string name="file_transfer">File Transfer</string>
+    <string name="geolocation_sms">Geolocation SMS</string>
+    <string name="chatbot_session">Chatbot Session</string>
+    <string name="chatbot_standalone">Chatbot Standalone</string>
+    <string name="chatbot_version">Chatbot Version</string>
+    <string name="start_provisioning">Start Provisioning....</string>
+    <string name="provisioning_timeout">Provisioning timeout.</string>
+    <string name="provisioning_done">Provisioning done, Start registering...</string>
+    <string name="registration_timeout">Registration timeout</string>
+    <string name="registration_done">Registration done. Enjoy chat!</string>
+    <string name="registration_failed">Registration failed</string>
+    <string name="attach">+</string>
+    <string name="browse">Browse</string>
+    <string name="upload">Upload</string>
+    <string name="upload_file_gba">Upload File with GBA</string>
+    <string name="invalid_parameters">Invalid Parameters</string>
+    <string name="server">Server:</string>
+    <string name="file_name">File Name:</string>
+    <string name="server_empty">Server is empty</string>
+    <string name="file_empty">File is empty</string>
+    <string name="version_info">Version: %s</string>
+
+    <string-array name="rcs_profile">
+        <item>UP_1.0</item>
+        <item>UP_2.3</item>
+    </string-array>
+    <string-array name="organization">
+        <item>NONE</item>
+        <item>3GPP</item>
+        <item>3GPP2</item>
+        <item>OMA</item>
+        <item>GSMA</item>
+        <item>LOCAL</item>
+    </string-array>
+    <string-array name="protocol">
+        <item>SUBSCRIBER_CERTIFICATE</item>
+        <item>MBMS</item>
+        <item>HTTP_DIGEST_AUTH</item>
+        <item>3GPP_HTTP_BASED_MBMS</item>
+        <item>GENERIC_PUSH_LAYER</item>
+        <item>IMS_MEDIA_PLANE</item>
+        <item>GENERATION_TMPI</item>
+        <item>3GPP_HTTP_BASED_MBMS</item>
+        <item>TLS_DEFAULT</item>
+        <item>TLS_BROWSER</item>
+    </string-array>
+    <string-array name="uicc_type">
+        <item>UNKNOWN</item>
+        <item>SIM</item>
+        <item>USIM</item>
+        <item>RSIM</item>
+        <item>CSIM</item>
+        <item>ISIM</item>
+    </string-array>
+    <string-array name="server">
+        <item>STAGING</item>
+        <item>PRODUCTION</item>
+    </string-array>
+
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/res/values/styles.xml b/testapps/TestRcsApp/TestApp/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/res/values/styles.xml
@@ -0,0 +1,11 @@
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+        <!-- Customize your theme here. -->
+        <item name="colorPrimary">@color/colorPrimary</item>
+        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+        <item name="colorAccent">@color/colorAccent</item>
+    </style>
+
+</resources>
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java
new file mode 100644
index 0000000..40a108d
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ChatActivity.java
@@ -0,0 +1,319 @@
+/*
+ * 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.google.android.sample.rcsclient;
+
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.telephony.SubscriptionManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.google.android.sample.rcsclient.util.ChatManager;
+import com.google.android.sample.rcsclient.util.ChatProvider;
+import com.google.android.sample.rcsclient.util.NumberUtils;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.MoreExecutors;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/** An activity to show chat message with specific number. */
+public class ChatActivity extends AppCompatActivity {
+
+    public static final String EXTRA_REMOTE_PHONE_NUMBER = "REMOTE_PHONE_NUMBER";
+    private static final String TAG = "TestRcsApp.ChatActivity";
+    private static final int INIT_LIST = 1;
+    private static final int SHOW_STATUS = 2;
+    private static final int EMPTY_MSG = 3;
+    private static final float TEXT_SIZE = 20.0f;
+    private static final int MARGIN_SIZE = 20;
+    private static final long TIMEOUT_IN_MS = 10000L;
+    private final ExecutorService mFixedThreadPool = Executors.newFixedThreadPool(3);
+    private boolean mSessionInitResult = false;
+    private Button mSend;
+    private String mDestNumber;
+    private TextView mDestNumberView, mTips;
+    private EditText mNewMessage;
+    private ChatObserver mChatObserver;
+    private Handler mHandler;
+    private int mSubId;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.i(TAG, "onCreate");
+        setContentView(R.layout.chat_layout);
+        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+        getSupportActionBar().setDisplayShowHomeEnabled(true);
+
+        mHandler = new Handler() {
+            public void handleMessage(Message msg) {
+                super.handleMessage(msg);
+                Log.d(TAG, "handleMessage:" + msg.what);
+                switch (msg.what) {
+                    case INIT_LIST:
+                        initChatMessageLayout((Cursor) msg.obj);
+                        break;
+                    case SHOW_STATUS:
+                        mTips.setText(msg.obj.toString());
+                        break;
+                    case EMPTY_MSG:
+                        mNewMessage.setText("");
+                        break;
+                    default:
+                        Log.d(TAG, "unknown msg:" + msg.what);
+                        break;
+                }
+
+            }
+        };
+        mDestNumberView = findViewById(R.id.destNum);
+        mTips = findViewById(R.id.session_tips);
+        initDestNumber();
+        mChatObserver = new ChatObserver(mHandler);
+    }
+
+    private void initDestNumber() {
+        Intent intent = getIntent();
+        mDestNumber = intent.getStringExtra(EXTRA_REMOTE_PHONE_NUMBER);
+        mDestNumberView.setText(mDestNumber);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        initChatButton();
+        queryChatData();
+        getContentResolver().registerContentObserver(ChatProvider.CHAT_URI, false,
+                mChatObserver);
+    }
+
+    private void initChatButton() {
+        mNewMessage = findViewById(R.id.new_msg);
+        mSend = findViewById(R.id.chat_btn);
+
+        mSubId = SubscriptionManager.getDefaultSmsSubscriptionId();
+        if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+            Log.e(TAG, "invalid subId:" + mSubId);
+            return;
+        }
+        try {
+            // Reformat so that the number matches the one sent to the network.
+            String formattedNumber = NumberUtils.formatNumber(this, mDestNumber);
+            if (formattedNumber != null) {
+                mDestNumber = formattedNumber;
+            }
+            mDestNumberView.setText(mDestNumber);
+            mTips.setText(ChatActivity.this.getResources().getString(R.string.session_initiating));
+            mHandler.sendMessageDelayed(mHandler.obtainMessage(SHOW_STATUS,
+                    ChatActivity.this.getResources().getString(R.string.session_timeout)),
+                    TIMEOUT_IN_MS);
+            ChatManager.getInstance(getApplicationContext(), mSubId).initChatSession(
+                    mDestNumber, new SessionStateCallback() {
+                        @Override
+                        public void onSuccess() {
+                            Log.i(TAG, "session init succeeded");
+                            String success = ChatActivity.this.getResources().getString(
+                                    R.string.session_succeeded);
+                            if (mHandler.hasMessages(SHOW_STATUS)) {
+                                mHandler.removeMessages(SHOW_STATUS);
+                            }
+                            mHandler.sendMessage(mHandler.obtainMessage(SHOW_STATUS, success));
+                            mSessionInitResult = true;
+                        }
+
+                        @Override
+                        public void onFailure() {
+                            Log.i(TAG, "session init failed");
+                            String failure = ChatActivity.this.getResources().getString(
+                                    R.string.session_failed);
+                            if (mHandler.hasMessages(SHOW_STATUS)) {
+                                mHandler.removeMessages(SHOW_STATUS);
+                            }
+                            mHandler.sendMessage(mHandler.obtainMessage(SHOW_STATUS, failure));
+                            mSessionInitResult = false;
+                        }
+                    });
+
+            mSend.setOnClickListener(view -> {
+                if (!ChatManager.getInstance(getApplicationContext(), mSubId).isRegistered()
+                        || !mSessionInitResult) {
+                    Toast.makeText(ChatActivity.this,
+                            getResources().getString(R.string.session_broken_or_not_ready),
+                            Toast.LENGTH_SHORT).show();
+                    Log.i(TAG, "session broken or not ready");
+                    return;
+                }
+                mFixedThreadPool.execute(() -> {
+                    if (TextUtils.isEmpty(mDestNumber)) {
+                        Log.i(TAG, "Destination number is empty");
+                    } else {
+                        Log.i(TAG, "send message");
+                        sendChatMessage();
+                    }
+                });
+            });
+        } catch (Exception e) {
+            Log.e(TAG, "Exception: " + e);
+            e.printStackTrace();
+            Toast.makeText(this, getResources().getString(R.string.session_failed),
+                    Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    private void sendChatMessage() {
+        Uri result = ChatManager.getInstance(getApplicationContext(), mSubId).addNewMessage(
+                mNewMessage.getText().toString(), ChatManager.SELF, mDestNumber);
+        String chatId = result.getPathSegments().get(1);
+        Futures.addCallback(
+                ChatManager.getInstance(getApplicationContext(),
+                        mSubId).sendMessage(
+                        mDestNumber,
+                        mNewMessage.getText().toString()),
+                new FutureCallback<Void>() {
+                    @Override
+                    public void onSuccess(Void param) {
+                        Log.i(TAG, "send chat msg successfully");
+                        ChatManager.getInstance(getApplicationContext(), mSubId).updateMsgResult(
+                                chatId, true);
+                    }
+
+                    @Override
+                    public void onFailure(Throwable t) {
+                        Log.i(TAG, "fail to send chat message:" + t);
+                        ChatManager.getInstance(getApplicationContext(), mSubId).updateMsgResult(
+                                chatId, false);
+                    }
+                },
+                MoreExecutors.directExecutor());
+        mHandler.sendMessage(mHandler.obtainMessage(EMPTY_MSG));
+    }
+
+    private void initChatMessageLayout(Cursor cursor) {
+        Log.i(TAG, "initChatMessageLayout");
+        RelativeLayout rl = findViewById(R.id.relative_layout);
+        int id = 1;
+        if (cursor != null && cursor.moveToNext()) {
+            TextView chatMessage = initChatMessageItem(cursor, id++, true);
+            rl.addView(chatMessage);
+        }
+        while (cursor != null && cursor.moveToNext()) {
+            TextView chatMessage = initChatMessageItem(cursor, id++, false);
+            rl.addView(chatMessage);
+        }
+        if (cursor != null) {
+            cursor.close();
+        }
+    }
+
+    private TextView initChatMessageItem(Cursor cursor, int id, boolean isFirst) {
+        TextView chatMsg = new TextView(this);
+        chatMsg.setId(id);
+        chatMsg.setText(
+                cursor.getString(cursor.getColumnIndex(ChatProvider.RcsColumns.CHAT_MESSAGE)));
+        chatMsg.setTextSize(TEXT_SIZE);
+        chatMsg.setTypeface(null, Typeface.BOLD);
+        RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+        lp.setMargins(0, MARGIN_SIZE, 0, 0);
+        if (messageFromSelf(cursor)) {
+            lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
+            int result = cursor.getInt(cursor.getColumnIndex(ChatProvider.RcsColumns.RESULT));
+            chatMsg.setBackgroundColor(result == 1 ? Color.GREEN : Color.RED);
+        } else {
+            lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
+            chatMsg.setBackgroundColor(Color.LTGRAY);
+        }
+        if (!isFirst) {
+            lp.addRule(RelativeLayout.BELOW, id - 1);
+        }
+        chatMsg.setLayoutParams(lp);
+        return chatMsg;
+    }
+
+    private boolean messageFromSelf(Cursor cursor) {
+        return ChatManager.SELF.equals(
+                cursor.getString(cursor.getColumnIndex(ChatProvider.RcsColumns.SRC_PHONE_NUMBER)));
+    }
+
+    private void queryChatData() {
+        mFixedThreadPool.execute(() -> {
+            Cursor cursor = getContentResolver().query(ChatProvider.CHAT_URI,
+                    new String[]{ChatProvider.RcsColumns.SRC_PHONE_NUMBER,
+                            ChatProvider.RcsColumns.DEST_PHONE_NUMBER,
+                            ChatProvider.RcsColumns.CHAT_MESSAGE,
+                            ChatProvider.RcsColumns.RESULT},
+                    ChatProvider.RcsColumns.SRC_PHONE_NUMBER + "=? OR "
+                            + ChatProvider.RcsColumns.DEST_PHONE_NUMBER + "=?",
+                    new String[]{mDestNumber, mDestNumber},
+                    ChatProvider.RcsColumns.MSG_TIMESTAMP + " ASC");
+            mHandler.sendMessage(mHandler.obtainMessage(INIT_LIST, cursor));
+        });
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        Log.i(TAG, "onStop");
+        getContentResolver().unregisterContentObserver(mChatObserver);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        Log.i(TAG, "onDestroy");
+        ChatManager.getInstance(getApplicationContext(), mSubId).terminateSession(mDestNumber);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == android.R.id.home) {
+            finish();
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    private class ChatObserver extends ContentObserver {
+        ChatObserver(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            Log.i(TAG, "onChange");
+            queryChatData();
+        }
+    }
+}
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ContactListActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ContactListActivity.java
new file mode 100644
index 0000000..b641606
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ContactListActivity.java
@@ -0,0 +1,311 @@
+/*
+ * 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.google.android.sample.rcsclient;
+
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.android.libraries.rcs.simpleclient.SimpleRcsClient.State;
+
+import com.google.android.sample.rcsclient.util.ChatManager;
+import com.google.android.sample.rcsclient.util.ChatProvider;
+
+import java.util.ArrayList;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/** An activity to show the contacts which UE ever chatted before. */
+public class ContactListActivity extends AppCompatActivity {
+
+    private static final String TAG = "TestRcsApp.ContactListActivity";
+    private static final int RENDER_LISTVIEW = 1;
+    private static final int SHOW_STATUS = 2;
+    private static final long TIMEOUT_IN_MS = 10000L;
+    private final ExecutorService mSingleThread = Executors.newSingleThreadExecutor();
+    private TextView mTips;
+    private Button mStartChatButton;
+    private Handler mHandler;
+    private SummaryObserver mSummaryObserver;
+    private ArrayAdapter mAdapter;
+    private ListView mListview;
+    private State mState;
+    private ArrayList<ContactAttributes> mContactList;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.i(TAG, "onCreate");
+        setContentView(R.layout.contact_list);
+
+        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+        getSupportActionBar().setDisplayShowHomeEnabled(true);
+
+        mContactList = new ArrayList<>();
+        mTips = findViewById(R.id.tips);
+        mListview = findViewById(R.id.listview);
+        mStartChatButton = findViewById(R.id.start_chat_btn);
+        mStartChatButton.setOnClickListener(view -> {
+            Intent intent = new Intent(ContactListActivity.this, PhoneNumberActivity.class);
+            ContactListActivity.this.startActivity(intent);
+        });
+        setButtonClickable(false);
+
+        mHandler = new Handler() {
+            public void handleMessage(Message message) {
+                Log.i(TAG, "handleMessage:" + message.what);
+                switch (message.what) {
+                    case RENDER_LISTVIEW:
+                        renderListView();
+                        break;
+                    case SHOW_STATUS:
+                        mTips.setText(message.obj.toString());
+                        break;
+                    default:
+                        Log.i(TAG, "unknown msg:" + message.what);
+                }
+            }
+        };
+        initListView();
+        initSipDelegate();
+        mSummaryObserver = new SummaryObserver(mHandler);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        Log.i(TAG, "onStart");
+        querySummaryData();
+        getContentResolver().registerContentObserver(ChatProvider.SUMMARY_URI, false,
+                mSummaryObserver);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        Log.i(TAG, "onStop");
+        getContentResolver().unregisterContentObserver(mSummaryObserver);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        Log.i(TAG, "onDestroy");
+        dispose();
+    }
+
+    private void dispose() {
+        int subId = SubscriptionManager.getDefaultSmsSubscriptionId();
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            Log.e(TAG, "invalid subId:" + subId);
+            return;
+        }
+        ChatManager chatManager = ChatManager.getInstance(this, subId);
+        chatManager.deregister();
+    }
+
+    private void initListView() {
+        Log.i(TAG, "initListView");
+
+        mAdapter = new ArrayAdapter<ContactAttributes>(this,
+                android.R.layout.simple_list_item_2,
+                android.R.id.text1) {
+            @Override
+            public View getView(int pos, View convert, ViewGroup group) {
+                View v = super.getView(pos, convert, group);
+                TextView t1 = (TextView) v.findViewById(android.R.id.text1);
+                TextView t2 = (TextView) v.findViewById(android.R.id.text2);
+                t1.setText(getItem(pos).phoneNumber);
+                t2.setText(getItem(pos).message);
+                if (!getItem(pos).isRead) {
+                    t1.setTypeface(null, Typeface.BOLD);
+                    t2.setTypeface(null, Typeface.BOLD);
+                }
+                return v;
+            }
+        };
+        mListview.setAdapter(mAdapter);
+    }
+
+    private void querySummaryData() {
+        mSingleThread.execute(() -> {
+            Cursor cursor = getContentResolver().query(ChatProvider.SUMMARY_URI,
+                    new String[]{ChatProvider.SummaryColumns.REMOTE_PHONE_NUMBER,
+                            ChatProvider.SummaryColumns.LATEST_MESSAGE,
+                            ChatProvider.SummaryColumns.IS_READ}, null, null, null);
+
+            mContactList.clear();
+            while (cursor.moveToNext()) {
+                String phoneNumber = getPhoneNumber(cursor);
+                String latestMessage = getLatestMessage(cursor);
+                boolean isRead = getIsRead(cursor);
+                mContactList.add(new ContactAttributes(phoneNumber, latestMessage, isRead));
+            }
+            mHandler.sendMessage(mHandler.obtainMessage(RENDER_LISTVIEW));
+            cursor.close();
+        });
+    }
+
+    private void renderListView() {
+        mAdapter.clear();
+        mAdapter.addAll(mContactList);
+    }
+
+    private void setListViewClickable(boolean clickable) {
+        if (clickable) {
+            mListview.setOnItemClickListener((parent, view, position, id) -> {
+                Intent intent = new Intent(ContactListActivity.this, ChatActivity.class);
+                intent.putExtra(ChatActivity.EXTRA_REMOTE_PHONE_NUMBER,
+                        mContactList.get(position).phoneNumber);
+                ContactListActivity.this.startActivity(intent);
+            });
+        } else {
+            mListview.setOnItemClickListener(null);
+        }
+    }
+
+    private void initSipDelegate() {
+        int subId = SubscriptionManager.getDefaultSmsSubscriptionId();
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            Log.e(TAG, "invalid subId:" + subId);
+            return;
+        }
+        Log.i(TAG, "initSipDelegate");
+        ChatManager chatManager = ChatManager.getInstance(this, subId);
+        chatManager.setRcsStateChangedCallback((oldState, newState) -> {
+            //Show toast when provisioning or registration is done.
+            mState = newState;
+            String tips = "";
+            String timeoutTips = "";
+            switch (newState) {
+                case PROVISIONING:
+                    tips = ContactListActivity.this.getResources().getString(
+                            R.string.start_provisioning);
+                    mHandler.sendMessage(mHandler.obtainMessage(SHOW_STATUS, tips));
+                    timeoutTips = ContactListActivity.this.getResources().getString(
+                            R.string.provisioning_timeout);
+                    mHandler.sendMessageDelayed(mHandler.obtainMessage(SHOW_STATUS, timeoutTips),
+                            TIMEOUT_IN_MS);
+                    break;
+                case REGISTERING:
+                    tips = ContactListActivity.this.getResources().getString(
+                            R.string.provisioning_done);
+                    if (mHandler.hasMessages(SHOW_STATUS)) {
+                        mHandler.removeMessages(SHOW_STATUS);
+                    }
+                    mHandler.sendMessage(mHandler.obtainMessage(SHOW_STATUS, tips));
+                    timeoutTips = ContactListActivity.this.getResources().getString(
+                            R.string.registration_timeout);
+                    mHandler.sendMessageDelayed(mHandler.obtainMessage(SHOW_STATUS, timeoutTips),
+                            TIMEOUT_IN_MS);
+                    break;
+                case REGISTERED:
+                    tips = ContactListActivity.this.getResources().getString(
+                            R.string.registration_done);
+                    if (mHandler.hasMessages(SHOW_STATUS)) {
+                        mHandler.removeMessages(SHOW_STATUS);
+                    }
+                    mHandler.sendMessage(mHandler.obtainMessage(SHOW_STATUS, tips));
+                    setButtonClickable(true);
+                    setListViewClickable(true);
+                    break;
+                case NOT_REGISTERED:
+                    tips = ContactListActivity.this.getResources().getString(
+                            R.string.registration_failed);
+                    mHandler.sendMessage(mHandler.obtainMessage(SHOW_STATUS, tips));
+                    setButtonClickable(false);
+                    setListViewClickable(false);
+                    break;
+
+                default:
+                    Log.i(TAG, "unknown state:" + newState);
+            }
+        });
+        chatManager.register();
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == android.R.id.home) {
+            finish();
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+
+    private String getPhoneNumber(Cursor cursor) {
+        return cursor.getString(
+                cursor.getColumnIndex(ChatProvider.SummaryColumns.REMOTE_PHONE_NUMBER));
+    }
+
+    private String getLatestMessage(Cursor cursor) {
+        return cursor.getString(cursor.getColumnIndex(ChatProvider.SummaryColumns.LATEST_MESSAGE));
+    }
+
+    private boolean getIsRead(Cursor cursor) {
+        return 1 == cursor.getInt(cursor.getColumnIndex(ChatProvider.SummaryColumns.IS_READ));
+    }
+
+    private void setButtonClickable(boolean clickable) {
+        if (clickable) {
+            mStartChatButton.setAlpha(1);
+            mStartChatButton.setClickable(true);
+        } else {
+            mStartChatButton.setAlpha(.5f);
+            mStartChatButton.setClickable(false);
+        }
+    }
+
+    class ContactAttributes {
+        public String phoneNumber;
+        public String message;
+        public boolean isRead;
+
+        ContactAttributes(String phoneNumber, String message, boolean isRead) {
+            this.phoneNumber = phoneNumber;
+            this.message = message;
+            this.isRead = isRead;
+        }
+    }
+
+    private class SummaryObserver extends ContentObserver {
+        SummaryObserver(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            querySummaryData();
+        }
+    }
+}
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java
new file mode 100644
index 0000000..1407671
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/DelegateActivity.java
@@ -0,0 +1,359 @@
+/*
+ * 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.google.android.sample.rcsclient;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.telephony.SmsManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.DelegateRequest;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsManager;
+import android.telephony.ims.SipDelegateConfiguration;
+import android.telephony.ims.SipDelegateConnection;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+import android.telephony.ims.stub.DelegateConnectionMessageCallback;
+import android.telephony.ims.stub.DelegateConnectionStateCallback;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.MenuItem;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/** An activity to verify SipDelegate creation and destruction. */
+public class DelegateActivity extends AppCompatActivity {
+
+    private static final String TAG = "TestRcsApp.DelegateActivity";
+    private static final String ICSI = "+g.3gpp.icsi-ref=";
+    private static final String IARI = "+g.3gpp.iari-ref=";
+
+    //https://www.gsma.com/futurenetworks/wp-content/uploads/2019/10/RCC.07-v11.0.pdf
+    private static final String SESSION_TAG =
+            "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session\"";
+    private static final String STANDALONE_PAGER =
+            "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg\"";
+    private static final String STANDALONE_LARGE =
+            "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.largemsg\"";
+    private static final String STANDALONE_DEFERRED =
+            "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.deferred\"";
+    private static final String STANDALONE_LARGE_PAGER = "+g.gsma.rcs.cpm.pager-large";
+
+
+    private static final String FILE_TRANSFER =
+            "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.fthttp\"";
+    private static final String GEOLOCATION_SMS =
+            "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.geosms\"";
+
+    private static final String CHATBOT_SESSION =
+            "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.chatbot\"";
+    private static final String CHATBOT_STANDALONE =
+            "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.chatbot.sa\"";
+    private static final String CHATBOT_VERSION = "+g.gsma.rcs.botversion=\"#=1,#=2\"";
+
+
+    private static final int MSG_RESULT = 1;
+    private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
+    // Callback for incoming messages on the modem connection
+    private final DelegateConnectionMessageCallback mMessageCallback =
+            new DelegateConnectionMessageCallback() {
+                @Override
+                public void onMessageReceived(@NonNull SipMessage message) {
+                    Log.i(TAG, "onMessageReceived:" + message);
+                }
+
+                @Override
+                public void onMessageSendFailure(@NonNull String viaTransactionId, int reason) {
+                    Log.i(TAG, "onMessageSendFailure, viaTransactionId:" + viaTransactionId
+                            + " reason:" + reason);
+                }
+
+                @Override
+                public void onMessageSent(@NonNull String viaTransactionId) {
+                    Log.i(TAG, "onMessageSent, viaTransactionId:" + viaTransactionId);
+                }
+
+            };
+    private String mCallbackResultStr = "";
+    private int mDefaultSmsSubId;
+    private SipDelegateManager mSipDelegateManager;
+    private SipDelegateConnection mSipDelegateConnection;
+    private Button mInitButton;
+    private Button mDestroyButton;
+    private TextView mCallbackResult;
+    private CheckBox mChatCb, mStandalonePagerCb, mStandaloneLargeCb, mStandaloneDeferredCb,
+            mStandaloneLargePagerCb, mFileTransferCb, mGeolocationSmsCb, mChatbotSessionCb,
+            mChatbotStandaloneCb, mChatbotVersionCb;
+    private Handler mHandler;
+    private final DelegateConnectionStateCallback mConnectionCallback =
+            new DelegateConnectionStateCallback() {
+
+                @Override
+                public void onCreated(SipDelegateConnection c) {
+                    mSipDelegateConnection = c;
+                    mCallbackResultStr += "onCreated\r\n\r\n";
+                    Log.i(TAG, mCallbackResultStr);
+                    mHandler.sendMessage(mHandler.obtainMessage(MSG_RESULT));
+                }
+
+                @Override
+                public void onConfigurationChanged(
+                        SipDelegateConfiguration registeredSipConfig) {
+                    mCallbackResultStr += "onConfigurationChanged SipDelegateConfiguration:"
+                            + "\r\n\r\n";
+                    Log.i(TAG, "onConfigurationChanged: " + registeredSipConfig);
+                    mHandler.sendMessage(mHandler.obtainMessage(MSG_RESULT));
+                    dumpConfig(registeredSipConfig);
+                }
+
+
+                @Override
+                public void onFeatureTagStatusChanged(
+                        @NonNull DelegateRegistrationState registrationState,
+                        @NonNull Set<FeatureTagState> deniedFeatureTags) {
+                    StringBuilder stringBuilder = new StringBuilder(
+                            "onFeatureTagStatusChanged ").append(
+                            " deniedFeatureTags:[");
+                    Iterator<FeatureTagState> iterator = deniedFeatureTags.iterator();
+                    while (iterator.hasNext()) {
+                        FeatureTagState featureTagState = iterator.next();
+                        stringBuilder.append(featureTagState.getFeatureTag()).append(" ").append(
+                                featureTagState.getState());
+                    }
+                    Set<String> registeredFt = registrationState.getRegisteredFeatureTags();
+                    Iterator<String> iteratorStr = registeredFt.iterator();
+                    stringBuilder.append("] registeredFT:[");
+                    while (iteratorStr.hasNext()) {
+                        String ft = iteratorStr.next();
+                        stringBuilder.append(ft).append(" ");
+                    }
+                    stringBuilder.append("]\r\n\r\n");
+                    mCallbackResultStr += stringBuilder.toString();
+                    Log.i(TAG, mCallbackResultStr);
+                    mHandler.sendMessage(mHandler.obtainMessage(MSG_RESULT));
+                }
+
+                @Override
+                public void onDestroyed(int reason) {
+                    mCallbackResultStr = "onDestroyed reason:" + reason;
+                    Log.i(TAG, mCallbackResultStr);
+                    mHandler.sendMessage(mHandler.obtainMessage(MSG_RESULT));
+                }
+            };
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.delegate_layout);
+
+        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+        getSupportActionBar().setDisplayShowHomeEnabled(true);
+
+        mHandler = new Handler() {
+            public void handleMessage(Message message) {
+                switch (message.what) {
+                    case MSG_RESULT:
+                        mCallbackResult.setText(mCallbackResultStr);
+                        break;
+                }
+            }
+        };
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        init();
+    }
+
+    private void init() {
+        mInitButton = findViewById(R.id.init_btn);
+        mDestroyButton = findViewById(R.id.destroy_btn);
+        mCallbackResult = findViewById(R.id.delegate_callback_result);
+        mChatCb = findViewById(R.id.chat);
+        mStandalonePagerCb = findViewById(R.id.standalone_pager);
+        mStandaloneLargeCb = findViewById(R.id.standalone_large);
+        mStandaloneDeferredCb = findViewById(R.id.standalone_deferred);
+        mStandaloneLargePagerCb = findViewById(R.id.standalone_pager_large);
+
+        mFileTransferCb = findViewById(R.id.file_transfer);
+        mGeolocationSmsCb = findViewById(R.id.geolocation_sms);
+        mChatbotSessionCb = findViewById(R.id.chatbot_session);
+        mChatbotStandaloneCb = findViewById(R.id.chatbot_standalone);
+        mChatbotVersionCb = findViewById(R.id.chatbot_version);
+
+        mChatCb.setChecked(true);
+
+        mDefaultSmsSubId = SmsManager.getDefaultSmsSubscriptionId();
+        mCallbackResult.setMovementMethod(new ScrollingMovementMethod());
+
+        ImsManager imsManager = this.getSystemService(ImsManager.class);
+        if (SubscriptionManager.isValidSubscriptionId(mDefaultSmsSubId)) {
+            mSipDelegateManager = imsManager.getSipDelegateManager(mDefaultSmsSubId);
+        }
+        setClickable(mDestroyButton, false);
+
+        mInitButton.setOnClickListener(view -> {
+            mCallbackResultStr = "";
+            if (mSipDelegateManager != null) {
+                Set<String> featureTags = getFeatureTags();
+                try {
+                    Log.i(TAG, "createSipDelegate");
+                    dumpFt(featureTags);
+                    mSipDelegateManager.createSipDelegate(new DelegateRequest(featureTags),
+                            mExecutorService, mConnectionCallback, mMessageCallback);
+                } catch (ImsException e) {
+                    //e.printStackTrace();
+                    mCallbackResult.setText(e.toString());
+                    Log.e(TAG, e.toString());
+                }
+                setClickable(mInitButton, false);
+                setClickable(mDestroyButton, true);
+            }
+        });
+
+        mDestroyButton.setOnClickListener(view -> {
+            mCallbackResultStr = "";
+            if (mSipDelegateManager != null && mSipDelegateConnection != null) {
+                Log.i(TAG, "destroySipDelegate");
+                mSipDelegateManager.destroySipDelegate(mSipDelegateConnection,
+                        SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+                setClickable(mInitButton, true);
+                setClickable(mDestroyButton, false);
+            }
+        });
+    }
+
+    private Set<String> getFeatureTags() {
+        HashSet<String> fts = new HashSet<>();
+        if (mChatCb.isChecked()) {
+            fts.add(SESSION_TAG);
+        }
+        if (mStandalonePagerCb.isChecked()) {
+            fts.add(STANDALONE_PAGER);
+        }
+        if (mStandaloneLargeCb.isChecked()) {
+            fts.add(STANDALONE_LARGE);
+        }
+        if (mStandaloneDeferredCb.isChecked()) {
+            fts.add(STANDALONE_DEFERRED);
+        }
+        if (mStandaloneLargePagerCb.isChecked()) {
+            fts.add(STANDALONE_LARGE_PAGER);
+        }
+        if (mFileTransferCb.isChecked()) {
+            fts.add(FILE_TRANSFER);
+        }
+        if (mGeolocationSmsCb.isChecked()) {
+            fts.add(GEOLOCATION_SMS);
+        }
+        if (mChatbotSessionCb.isChecked()) {
+            fts.add(CHATBOT_SESSION);
+        }
+        if (mChatbotStandaloneCb.isChecked()) {
+            fts.add(CHATBOT_STANDALONE);
+        }
+        if (mChatbotVersionCb.isChecked()) {
+            fts.add(CHATBOT_VERSION);
+        }
+        return fts;
+    }
+
+    private void dumpFt(Set<String> fts) {
+        Iterator<String> iterator = fts.iterator();
+        StringBuilder res = new StringBuilder();
+        while (iterator.hasNext()) {
+            res.append(iterator.next()).append("\r\n");
+        }
+        Log.i(TAG, "FeatureTag: " + res.toString());
+    }
+
+    private void setClickable(Button button, boolean clickable) {
+        if (clickable) {
+            button.setAlpha(1);
+            button.setClickable(true);
+        } else {
+            button.setAlpha(.5f);
+            button.setClickable(false);
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == android.R.id.home) {
+            finish();
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        if (mSipDelegateManager != null && mSipDelegateConnection != null) {
+            Log.i(TAG, "onStop() destroySipDelegate");
+            mSipDelegateManager.destroySipDelegate(mSipDelegateConnection,
+                    SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+            setClickable(mInitButton, true);
+            setClickable(mDestroyButton, false);
+        }
+
+    }
+
+    private void dumpConfig(SipDelegateConfiguration config) {
+        String result = "SipDelegateConfiguration{"
+                + "mVersion=" + config.getVersion()
+                + ", \n\tmTransportType=" + config.getTransportType()
+                + ", \n\tmLocalIpAddr=" + config.getLocalAddress()
+                + ", \n\tmSipServerAddr=" + config.getSipServerAddress()
+                + ", \n\tmIsSipCompactFormEnabled=" + config.isSipCompactFormEnabled()
+                + ", \n\tmIsSipKeepaliveEnabled=" + config.isSipKeepaliveEnabled()
+                + ", \n\tmMaxUdpPayloadSize=" + config.getMaxUdpPayloadSizeBytes()
+                + ", \n\tmPublicUserIdentifier=" + config.getPublicUserIdentifier()
+                + ", \n\tmPrivateUserIdentifier=" + config.getPrivateUserIdentifier()
+                + ", \n\tmHomeDomain=" + config.getHomeDomain()
+                + ", \n\tmImei=" + config.getImei()
+                + ", \n\tmGruu=" + config.getPublicGruuUri()
+                + ", \n\tmSipAuthHeader=" + config.getSipAuthenticationHeader()
+                + ", \n\tmSipAuthNonce=" + config.getSipAuthenticationNonce()
+                + ", \n\tmServiceRouteHeader=" + config.getSipServiceRouteHeader()
+                + ", \n\tmPathHeader=" + config.getSipPathHeader()
+                + ", \n\tmUserAgentHeader=" + config.getSipUserAgentHeader()
+                + ", \n\tmContactUserParam=" + config.getSipContactUserParameter()
+                + ", \n\tmPaniHeader=" + config.getSipPaniHeader()
+                + ", \n\tmPlaniHeader=" + config.getSipPlaniHeader()
+                + ", \n\tmCniHeader=" + config.getSipCniHeader()
+                + ", \n\tmAssociatedUriHeader=" + config.getSipAssociatedUriHeader()
+                + ", \n\tmIpSecConfiguration=" + config.getIpSecConfiguration()
+                + ", \n\tmNatConfiguration=" + config.getNatSocketAddress() + '}';
+        Log.i(TAG, result);
+    }
+
+}
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java
new file mode 100644
index 0000000..b9078f8
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/FileUploadActivity.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2021 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.google.android.sample.rcsclient;
+
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.provider.OpenableColumns;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SmsManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.ProvisioningManager.RcsProvisioningCallback;
+import android.telephony.ims.RcsClientConfiguration;
+import android.text.TextUtils;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.util.Xml;
+import android.view.MenuItem;
+import android.webkit.MimeTypeMap;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.android.libraries.rcs.simpleclient.filetransfer.FileTransferController;
+import com.android.libraries.rcs.simpleclient.filetransfer.FileTransferControllerImpl;
+import com.android.libraries.rcs.simpleclient.filetransfer.requestexecutor.GbaAuthenticationProvider;
+import com.android.libraries.rcs.simpleclient.filetransfer.requestexecutor.GbaRequestExecutor;
+import com.android.libraries.rcs.simpleclient.filetransfer.requestexecutor.HttpRequestExecutor;
+
+import com.google.common.io.ByteStreams;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.UUID;
+
+/** An activity to verify file upload with GBA authentication. */
+public class FileUploadActivity extends AppCompatActivity {
+
+    private static final String TAG = "TestRcsApp.FileUploadActivity";
+    private static final String NAF_PREFIX = "https://3GPP-bootstrapping@";
+    private static final int PICKFILE_RESULT = 1;
+    private static final String HTTP_URI = "ftHTTPCSURI";
+    private static final String PARM = "parm";
+    private static final String NAME = "name";
+    private static final String VALUE = "value";
+
+
+    private ProvisioningManager mProvisioningManager;
+    private int mDefaultSmsSubId;
+    private File mFile;
+    private Button mUpload, mBrowse;
+    private TextView mUploadResult;
+    private TextView mFileName;
+    private EditText mServerUri;
+    private RcsProvisioningCallback mCallback =
+            new RcsProvisioningCallback() {
+                @Override
+                public void onConfigurationChanged(@NonNull byte[] configXml) {
+                    String configResult = new String(configXml);
+                    String server = getFtServerUri(configXml);
+                    Log.i(TAG, "RcsProvisioningCallback.onConfigurationChanged called with xml:");
+                    Log.i(TAG, configResult);
+                    Log.i(TAG, "serverUri:" + server);
+                    mServerUri.setText(server);
+                }
+
+                @Override
+                public void onConfigurationReset() {
+                    Log.i(TAG, "RcsProvisioningCallback.onConfigurationReset called.");
+                }
+
+                @Override
+                public void onRemoved() {
+                    Log.i(TAG, "RcsProvisioningCallback.onRemoved called.");
+                }
+            };
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.file_upload_layout);
+
+        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+        getSupportActionBar().setDisplayShowHomeEnabled(true);
+
+        initLayout();
+        registerProvisioning();
+    }
+
+    private void initLayout() {
+        mServerUri = findViewById(R.id.ft_uri);
+        mUpload = findViewById(R.id.upload_btn);
+        mBrowse = findViewById(R.id.browse_btn);
+        mFileName = findViewById(R.id.file_name);
+        mUploadResult = findViewById(R.id.upload_file_result);
+        mUploadResult.setMovementMethod(new ScrollingMovementMethod());
+
+        mBrowse.setOnClickListener(view -> {
+            Intent chooseFile = new Intent(Intent.ACTION_GET_CONTENT);
+            chooseFile.setType("*/*");
+            chooseFile = Intent.createChooser(chooseFile, "Choose a file");
+            startActivityForResult(chooseFile, PICKFILE_RESULT);
+        });
+
+        mUpload.setOnClickListener(view -> {
+            if (TextUtils.isEmpty(mServerUri.getText())) {
+                Toast.makeText(FileUploadActivity.this,
+                        getResources().getString(R.string.server_empty),
+                        Toast.LENGTH_SHORT).show();
+                return;
+            }
+            if (mFile == null) {
+                Toast.makeText(FileUploadActivity.this,
+                        getResources().getString(R.string.file_empty),
+                        Toast.LENGTH_SHORT).show();
+                return;
+            }
+
+            Log.i(TAG, "upload file");
+            try {
+                FileTransferController fileTransferController = initFileTransferController();
+                if (fileTransferController == null) {
+                    Log.i(TAG, "FileTransferController null");
+                    return;
+                }
+                mUploadResult.setText("");
+                Futures.addCallback(
+                        fileTransferController.uploadFile(UUID.randomUUID().toString(),
+                                mFile),
+                        new FutureCallback<String>() {
+                            @Override
+                            public void onSuccess(String xml) {
+                                String text;
+                                if (TextUtils.isEmpty(xml)) {
+                                    text = "onFailure: Empty Xml";
+                                    Log.i(TAG, text);
+                                    mUploadResult.setText(text);
+                                    return;
+                                }
+                                text = "onSuccess\r\n" + xml;
+                                Log.i(TAG, text);
+                                mUploadResult.setText(text);
+                            }
+
+                            @Override
+                            public void onFailure(Throwable t) {
+                                String text = "onFailure:" + t;
+                                Log.i(TAG, text);
+                                mUploadResult.setText(text);
+                            }
+                        },
+                        getMainExecutor());
+            } catch (IOException e) {
+                Log.e(TAG, e.getMessage());
+            }
+        });
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        switch (requestCode) {
+            case PICKFILE_RESULT:
+                if (resultCode == RESULT_OK) {
+                    Uri fileUri = data.getData();
+                    String fileName = getFileName(fileUri);
+                    mFileName.setText(fileName);
+                    try {
+                        mFile = uriToFile(fileUri);
+                        Log.i(TAG, "mFile:" + mFile);
+                    } catch (Exception e) {
+                        Log.e(TAG, e.getMessage());
+                        e.printStackTrace();
+                    }
+                }
+                break;
+        }
+    }
+
+    private void registerProvisioning() {
+        mDefaultSmsSubId = SmsManager.getDefaultSmsSubscriptionId();
+        Log.i(TAG, "mDefaultSmsSubId:" + mDefaultSmsSubId);
+        if (SubscriptionManager.isValidSubscriptionId(mDefaultSmsSubId)) {
+            try {
+                mProvisioningManager = ProvisioningManager.createForSubscriptionId(
+                        mDefaultSmsSubId);
+                mProvisioningManager.setRcsClientConfiguration(getDefaultClientConfiguration());
+                mProvisioningManager.registerRcsProvisioningCallback(getMainExecutor(), mCallback);
+            } catch (ImsException e) {
+                Log.e(TAG, e.getMessage());
+            }
+        }
+    }
+
+    private RcsClientConfiguration getDefaultClientConfiguration() {
+        SharedPreferences pref = getSharedPreferences("CONFIG", MODE_PRIVATE);
+
+        return new RcsClientConfiguration(
+                /*rcsVersion=*/ pref.getString("RCS_VERSION", "6.0"),
+                /*rcsProfile=*/ pref.getString("RCS_PROFILE", "UP_1.0"),
+                /*clientVendor=*/ "Goog",
+                /*clientVersion=*/ "RCSAndrd-1.0");
+    }
+
+    private FileTransferController initFileTransferController() {
+        mDefaultSmsSubId = SmsManager.getDefaultSmsSubscriptionId();
+        if (SubscriptionManager.isValidSubscriptionId(mDefaultSmsSubId)) {
+            TelephonyManager telephonyManager = getSystemService(
+                    TelephonyManager.class).createForSubscriptionId(mDefaultSmsSubId);
+            PersistableBundle carrierConfig = telephonyManager.getCarrierConfig();
+            String uploadUrl = carrierConfig.getString(
+                    CarrierConfigManager.KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING);
+            String carrierName = telephonyManager.getSimOperatorName();
+
+            HttpRequestExecutor executor = new GbaRequestExecutor(
+                    new GbaAuthenticationProvider(getSystemService(TelephonyManager.class),
+                            NAF_PREFIX + uploadUrl, getMainExecutor()));
+            return new FileTransferControllerImpl(executor, mServerUri.getText().toString(),
+                    carrierName);
+        } else {
+            Log.i(TAG, "Invalid subId:" + mDefaultSmsSubId);
+            return null;
+        }
+    }
+
+    private String getFileName(Uri uri) throws IllegalArgumentException {
+        Cursor cursor = getContentResolver().query(uri, null, null, null, null);
+
+        if (cursor.getCount() <= 0) {
+            cursor.close();
+            throw new IllegalArgumentException("Can't obtain file name, cursor is empty");
+        }
+        cursor.moveToFirst();
+        String fileName = cursor.getString(
+                cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME));
+        cursor.close();
+
+        return fileName;
+    }
+
+    private File uriToFile(Uri uri) {
+        File file = null;
+        if (uri == null) return file;
+        if (uri.getScheme().equals(ContentResolver.SCHEME_FILE)) {
+            file = new File(uri.getPath());
+        } else if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
+            ContentResolver contentResolver = getContentResolver();
+            String cachedName = System.currentTimeMillis() + Math.round((Math.random() + 1) * 1000)
+                    + "." + MimeTypeMap.getSingleton().getExtensionFromMimeType(
+                    contentResolver.getType(uri));
+
+            try {
+                InputStream is = contentResolver.openInputStream(uri);
+                File cache = new File(getExternalCacheDir().getAbsolutePath(), cachedName);
+                FileOutputStream fos = new FileOutputStream(cache);
+                ByteStreams.copy(is, fos);
+                file = cache;
+                fos.close();
+                is.close();
+            } catch (IOException e) {
+                Log.i(TAG, e.getMessage());
+            }
+        }
+        return file;
+    }
+
+    private String getContentType(Uri uri) {
+        MimeTypeMap mime = MimeTypeMap.getSingleton();
+        return mime.getExtensionFromMimeType(getContentResolver().getType(uri));
+    }
+
+
+    /**
+     * According GSMA RCC.72, get FileTransfer URI from the config xml whose content includes the
+     * following parameter.
+     * <parm name="ftHTTPCSURI"
+     * value="https://ftcontentserver.rcs.mnc008.mcc123.pub.3gppnetwork.org/content/"/>
+     */
+    private String getFtServerUri(byte[] xml) {
+        try {
+            InputStream inputStream = new ByteArrayInputStream(xml);
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(inputStream, "utf-8");
+
+            int eventType = parser.getEventType();
+            while (eventType != XmlPullParser.END_DOCUMENT) {
+                switch (eventType) {
+                    case XmlPullParser.START_TAG:
+                        if (parser.getName().equals(PARM)) {
+                            String name = parser.getAttributeValue(null, NAME);
+                            if (HTTP_URI.equalsIgnoreCase(name)) {
+                                return parser.getAttributeValue(null, VALUE);
+                            }
+                        }
+                }
+                eventType = parser.next();
+            }
+        } catch (Exception e) {
+            Log.e(TAG, e.getMessage());
+            return "";
+        }
+        return "";
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        //delete cache files
+        File cache = new File(getExternalCacheDir().getAbsolutePath());
+        File[] files = cache.listFiles();
+        for (File file : files) {
+            file.delete();
+        }
+        if (mProvisioningManager != null) {
+            mProvisioningManager.unregisterRcsProvisioningCallback(mCallback);
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == android.R.id.home) {
+            finish();
+        }
+        return super.onOptionsItemSelected(item);
+    }
+}
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java
new file mode 100644
index 0000000..9ee2a35
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/GbaActivity.java
@@ -0,0 +1,306 @@
+/*
+ * 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.google.android.sample.rcsclient;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SmsManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.BootstrapAuthenticationCallback;
+import android.telephony.gba.UaSecurityProtocolIdentifier;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import java.util.Locale;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/** An activity to verify GBA authentication. */
+public class GbaActivity extends AppCompatActivity {
+
+    private static final String TAG = "TestRcsApp.GbaActivity";
+    private static final String NAF_PREFIX = "https://3GPP-bootstrapping@";
+
+    private static final int MSG_RESULT = 1;
+    private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
+    private Button mGbaButton;
+    private TextView mCallbackResult;
+    private Spinner mOrganizationSpinner, mProtocolSpinner, mUiccSpinner;
+    private EditText mTlsCs;
+    private EditText mNaf;
+    private Handler mHandler;
+    private int mOrganization;
+    private int mProtocol;
+    private int mUiccType;
+
+    private static String bytesToHex(byte[] bytes) {
+        StringBuilder result = new StringBuilder();
+        for (byte aByte : bytes) {
+            result.append(String.format(Locale.US, "%02X", aByte));
+        }
+        return result.toString();
+    }
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.gba_layout);
+
+        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+        getSupportActionBar().setDisplayShowHomeEnabled(true);
+        initLayout();
+        mHandler = new Handler() {
+            public void handleMessage(Message message) {
+                switch (message.what) {
+                    case MSG_RESULT:
+                        mCallbackResult.setText(message.obj.toString());
+                        break;
+                }
+            }
+        };
+    }
+
+    private void initLayout() {
+        mGbaButton = findViewById(R.id.gba_btn);
+        mCallbackResult = findViewById(R.id.gba_result);
+        mCallbackResult.setMovementMethod(new ScrollingMovementMethod());
+        mTlsCs = findViewById(R.id.tls_id);
+        mNaf = findViewById(R.id.naf_url);
+
+        initOrganization();
+        initProtocol();
+        initUicctype();
+
+        int defaultSmsSubId = SmsManager.getDefaultSmsSubscriptionId();
+        if (!SubscriptionManager.isValidSubscriptionId(defaultSmsSubId)) {
+            Log.i(TAG, "invalid subId:" + defaultSmsSubId);
+            return;
+        }
+        TelephonyManager telephonyManager = getSystemService(
+                TelephonyManager.class).createForSubscriptionId(defaultSmsSubId);
+        PersistableBundle carrierConfig = telephonyManager.getCarrierConfig();
+        String uploadUrl = carrierConfig.getString(
+                CarrierConfigManager.KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING);
+        mNaf.setText(NAF_PREFIX + uploadUrl);
+
+        mGbaButton.setOnClickListener(view -> {
+            Log.i(TAG, "trigger bootstrapAuthenticationRequest");
+            UaSecurityProtocolIdentifier.Builder builder =
+                    new UaSecurityProtocolIdentifier.Builder();
+            try {
+                builder.setOrg(mOrganization)
+                        .setProtocol(mProtocol)
+                        .setTlsCipherSuite(Integer.parseInt(mTlsCs.getText().toString()));
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, e.getMessage());
+                return;
+            }
+            UaSecurityProtocolIdentifier spId = builder.build();
+            telephonyManager.bootstrapAuthenticationRequest(mUiccType,
+                    Uri.parse(mNaf.getText().toString()),
+                    spId,
+                    true,
+                    mExecutorService,
+                    new BootstrapAuthenticationCallback() {
+                        @Override
+                        public void onKeysAvailable(byte[] gbaKey, String btId) {
+                            String result = "OnKeysAvailable key:" + bytesToHex(gbaKey)
+                                    + "\r\n btId:" + btId;
+                            Log.i(TAG, result);
+                            mHandler.sendMessage(mHandler.obtainMessage(MSG_RESULT, result));
+                        }
+
+                        @Override
+                        public void onAuthenticationFailure(int reason) {
+                            String result = "OnAuthenticationFailure reason: " + reason;
+                            Log.i(TAG, result);
+                            mHandler.sendMessage(mHandler.obtainMessage(MSG_RESULT, result));
+                        }
+                    });
+        });
+    }
+
+    private void initOrganization() {
+        mOrganizationSpinner = findViewById(R.id.organization_list);
+        ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
+                R.array.organization, android.R.layout.simple_spinner_item);
+        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        mOrganizationSpinner.setAdapter(adapter);
+        mOrganizationSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+                Log.i(TAG, "Organization position:" + position);
+                switch (position) {
+                    case 0:
+                        mOrganization = UaSecurityProtocolIdentifier.ORG_NONE;
+                        break;
+                    case 1:
+                        mOrganization = UaSecurityProtocolIdentifier.ORG_3GPP;
+                        break;
+                    case 2:
+                        mOrganization = UaSecurityProtocolIdentifier.ORG_3GPP2;
+                        break;
+                    case 3:
+                        mOrganization = UaSecurityProtocolIdentifier.ORG_GSMA;
+                        break;
+                    case 4:
+                        mOrganization = UaSecurityProtocolIdentifier.ORG_LOCAL;
+                        break;
+                    default:
+                        Log.e(TAG, "invalid position:" + position);
+                }
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> parent) {
+                // TODO Auto-generated method stub
+            }
+        });
+        mOrganizationSpinner.setSelection(1);
+    }
+
+    private void initProtocol() {
+        mProtocolSpinner = findViewById(R.id.protocol_list);
+        ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
+                R.array.protocol, android.R.layout.simple_spinner_item);
+        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        mProtocolSpinner.setAdapter(adapter);
+        mProtocolSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+                Log.i(TAG, "Protocol position:" + position);
+                switch (position) {
+                    case 0:
+                        mProtocol = UaSecurityProtocolIdentifier
+                                .UA_SECURITY_PROTOCOL_3GPP_SUBSCRIBER_CERTIFICATE;
+                        break;
+                    case 1:
+                        mProtocol = UaSecurityProtocolIdentifier
+                                .UA_SECURITY_PROTOCOL_3GPP_MBMS;
+                        break;
+                    case 2:
+                        mProtocol = UaSecurityProtocolIdentifier
+                                .UA_SECURITY_PROTOCOL_3GPP_HTTP_DIGEST_AUTHENTICATION;
+                        break;
+                    case 3:
+                        mProtocol = UaSecurityProtocolIdentifier
+                                .UA_SECURITY_PROTOCOL_3GPP_HTTP_BASED_MBMS;
+                        break;
+                    case 4:
+                        mProtocol = UaSecurityProtocolIdentifier
+                                .UA_SECURITY_PROTOCOL_3GPP_SIP_BASED_MBMS;
+                        break;
+                    case 5:
+                        mProtocol = UaSecurityProtocolIdentifier
+                                .UA_SECURITY_PROTOCOL_3GPP_GENERIC_PUSH_LAYER;
+                        break;
+                    case 6:
+                        mProtocol = UaSecurityProtocolIdentifier
+                                .UA_SECURITY_PROTOCOL_3GPP_IMS_MEDIA_PLANE;
+                        break;
+                    case 7:
+                        mProtocol = UaSecurityProtocolIdentifier
+                                .UA_SECURITY_PROTOCOL_3GPP_GENERATION_TMPI;
+                        break;
+                    case 8:
+                        mProtocol = UaSecurityProtocolIdentifier
+                                .UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT;
+                        break;
+                    case 9:
+                        mProtocol = UaSecurityProtocolIdentifier
+                                .UA_SECURITY_PROTOCOL_3GPP_TLS_BROWSER;
+                        break;
+                    default:
+                        Log.e(TAG, "invalid position:" + position);
+                }
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> parent) {
+                // TODO Auto-generated method stub
+            }
+        });
+        mProtocolSpinner.setSelection(8);
+    }
+
+    private void initUicctype() {
+        mUiccSpinner = findViewById(R.id.uicc_list);
+        ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
+                R.array.uicc_type, android.R.layout.simple_spinner_item);
+        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        mUiccSpinner.setAdapter(adapter);
+        mUiccSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+                Log.i(TAG, "uicc position:" + position);
+                switch (position) {
+                    case 0:
+                        mUiccType = TelephonyManager.APPTYPE_UNKNOWN;
+                        break;
+                    case 1:
+                        mUiccType = TelephonyManager.APPTYPE_SIM;
+                        break;
+                    case 2:
+                        mUiccType = TelephonyManager.APPTYPE_USIM;
+                        break;
+                    case 3:
+                        mUiccType = TelephonyManager.APPTYPE_RUIM;
+                        break;
+                    case 4:
+                        mUiccType = TelephonyManager.APPTYPE_CSIM;
+                        break;
+                    case 5:
+                        mUiccType = TelephonyManager.APPTYPE_ISIM;
+                        break;
+                    default:
+                        Log.e(TAG, "invalid position:" + position);
+                }
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> parent) {
+                // TODO Auto-generated method stub
+            }
+        });
+        mUiccSpinner.setSelection(5);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == android.R.id.home) {
+            finish();
+        }
+        return super.onOptionsItemSelected(item);
+    }
+}
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/MainActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/MainActivity.java
new file mode 100644
index 0000000..89c5268
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/MainActivity.java
@@ -0,0 +1,113 @@
+/*
+ * 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.google.android.sample.rcsclient;
+
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.MenuItem;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+/** An activity to show function list. */
+public class MainActivity extends AppCompatActivity {
+    private static final String TAG = "TestRcsApp.MainActivity";
+    private Button mProvisionButton;
+    private Button mDelegateButton;
+    private Button mUceButton;
+    private Button mGbaButton;
+    private Button mMessageClientButton;
+    private Button mFileUploadButton;
+    private TextView mVersionInfo;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+        getSupportActionBar().setDisplayShowHomeEnabled(true);
+
+        mProvisionButton = (Button) this.findViewById(R.id.provision);
+        mDelegateButton = (Button) this.findViewById(R.id.delegate);
+        mMessageClientButton = (Button) this.findViewById(R.id.msgClient);
+        mUceButton = (Button) this.findViewById(R.id.uce);
+        mGbaButton = (Button) this.findViewById(R.id.gba);
+        mFileUploadButton = findViewById(R.id.uploadFile);
+        mVersionInfo = this.findViewById(R.id.version_info);
+        mProvisionButton.setOnClickListener(view -> {
+            Intent intent = new Intent(this, ProvisioningActivity.class);
+            MainActivity.this.startActivity(intent);
+        });
+
+        mDelegateButton.setOnClickListener(view -> {
+            Intent intent = new Intent(this, DelegateActivity.class);
+            MainActivity.this.startActivity(intent);
+        });
+
+        mUceButton.setOnClickListener(view -> {
+            Intent intent = new Intent(this, UceActivity.class);
+            MainActivity.this.startActivity(intent);
+        });
+
+        mGbaButton.setOnClickListener(view -> {
+            Intent intent = new Intent(this, GbaActivity.class);
+            MainActivity.this.startActivity(intent);
+        });
+        mMessageClientButton.setOnClickListener(view -> {
+            Intent intent = new Intent(this, ContactListActivity.class);
+            MainActivity.this.startActivity(intent);
+        });
+        mFileUploadButton.setOnClickListener(view -> {
+            Intent intent = new Intent(this, FileUploadActivity.class);
+            MainActivity.this.startActivity(intent);
+        });
+
+        String appVersionName = getVersionCode(getPackageName());
+        if (!TextUtils.isEmpty(appVersionName)) {
+            String version = String.format(getResources().getString(R.string.version_info),
+                    appVersionName);
+            mVersionInfo.setText(version);
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == android.R.id.home) {
+            finish();
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    private String getVersionCode(String packageName) {
+        try {
+            // get android:versionName from the android manifest
+            PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), 0 /*flags*/);
+            return info.versionName;
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.w(TAG, "couldn't get version info for package name:" + packageName);
+        }
+        return null;
+    }
+}
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/PhoneNumberActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/PhoneNumberActivity.java
new file mode 100644
index 0000000..b432979
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/PhoneNumberActivity.java
@@ -0,0 +1,81 @@
+/*
+ * 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.google.android.sample.rcsclient;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.MenuItem;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.google.android.sample.rcsclient.util.NumberUtils;
+
+/** An activity to let user input phone number to chat. */
+public class PhoneNumberActivity extends AppCompatActivity {
+
+    private static final String TAG = "TestRcsApp.PhoneNumberActivity";
+    private Button mChatButton;
+    private EditText mPhoneNumber;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.number_to_chat);
+        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+        getSupportActionBar().setDisplayShowHomeEnabled(true);
+
+        mChatButton = this.findViewById(R.id.launch_chat_btn);
+        mPhoneNumber = findViewById(R.id.destNum);
+        mChatButton.setOnClickListener(view -> {
+            String formattedNumber = NumberUtils.formatNumber(PhoneNumberActivity.this,
+                    mPhoneNumber.getText().toString());
+            if (formattedNumber != null) {
+                Intent intent = new Intent(PhoneNumberActivity.this, ChatActivity.class);
+                intent.putExtra(ChatActivity.EXTRA_REMOTE_PHONE_NUMBER, formattedNumber);
+                PhoneNumberActivity.this.startActivity(intent);
+            } else {
+                Toast.makeText(this, "Invalid Number format!",
+                        Toast.LENGTH_LONG).show();
+            }
+        });
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == android.R.id.home) {
+            finish();
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        Log.i(TAG, "onStop");
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onStop();
+        Log.i(TAG, "onDestroy");
+    }
+}
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java
new file mode 100644
index 0000000..dae2835
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/ProvisioningActivity.java
@@ -0,0 +1,292 @@
+/*
+ * 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.google.android.sample.rcsclient;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.telephony.SmsManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.ProvisioningManager.RcsProvisioningCallback;
+import android.telephony.ims.RcsClientConfiguration;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+
+/** An activity to verify provisioning. */
+public class ProvisioningActivity extends AppCompatActivity {
+
+    private static final String TAG = "TestRcsApp.ProvisioningActivity";
+    private static final String UP_10 = "UP_1.0";
+    private static final String UP_23 = "UP_2.3";
+    private static final String V_6 = "6.0";
+    private static final String V_9 = "9.0";
+    private static final String RCS_CONFIG = "CONFIG";
+    private static final String RCS_PROFILE = "RCS_PROFILE";
+    private static final String RCS_VERSION = "RCS_VERSION";
+    private static final int MSG_RESULT = 1;
+
+    private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
+    private int mDefaultSmsSubId;
+    private ProvisioningManager mProvisioningManager;
+    private Spinner mRcsProfileSpinner;
+    private String mRcsVersion;
+    private String mRcsProfile;
+    private Button mRegisterButton;
+    private Button mUnRegisterButton;
+    private Button mIsCapableButton;
+    private TextView mSingleRegResult;
+    private TextView mCallbackResult;
+    private SharedPreferences mPref;
+    private SingleRegCapabilityReceiver mSingleRegCapabilityReceiver;
+    private boolean mIsRegistered = false;
+    private Handler mHandler;
+    private RcsProvisioningCallback mCallback =
+            new RcsProvisioningCallback() {
+                @Override
+                public void onConfigurationChanged(@NonNull byte[] configXml) {
+                    String configResult = new String(configXml);
+                    Log.i(TAG, "RcsProvisioningCallback.onConfigurationChanged called with xml:");
+                    Log.i(TAG, configResult);
+                    mHandler.sendMessage(mHandler.obtainMessage(MSG_RESULT,
+                            "onConfigurationChanged \r\n" + configResult));
+                }
+
+                @Override
+                public void onConfigurationReset() {
+                    Log.i(TAG, "RcsProvisioningCallback.onConfigurationReset called.");
+                    mHandler.sendMessage(
+                            mHandler.obtainMessage(MSG_RESULT, "onConfigurationReset"));
+                }
+
+                @Override
+                public void onRemoved() {
+                    Log.i(TAG, "RcsProvisioningCallback.onRemoved called.");
+                    mHandler.sendMessage(mHandler.obtainMessage(MSG_RESULT, "onRemoved"));
+                }
+            };
+
+    // Static configuration.
+    private RcsClientConfiguration getDefaultClientConfiguration() {
+        return new RcsClientConfiguration(
+                /*rcsVersion=*/ mRcsVersion,
+                /*rcsProfile=*/ mRcsProfile,
+                /*clientVendor=*/ "Goog",
+                /*clientVersion=*/ "RCSAndrd-1.0");
+    }
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.provision_layout);
+
+        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+        getSupportActionBar().setDisplayShowHomeEnabled(true);
+        mSingleRegCapabilityReceiver = new SingleRegCapabilityReceiver();
+        this.registerReceiver(mSingleRegCapabilityReceiver, new IntentFilter(
+                ProvisioningManager.ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE));
+        mHandler = new Handler() {
+            public void handleMessage(Message message) {
+                switch (message.what) {
+                    case MSG_RESULT:
+                        mCallbackResult.setText(message.obj.toString());
+                        break;
+                }
+            }
+        };
+        mPref = getSharedPreferences(RCS_CONFIG, MODE_PRIVATE);
+        initRcsProfile();
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mDefaultSmsSubId = SmsManager.getDefaultSmsSubscriptionId();
+        Log.i(TAG, "defaultSmsSubId:" + mDefaultSmsSubId);
+        if (SubscriptionManager.isValidSubscriptionId(mDefaultSmsSubId)) {
+            mProvisioningManager = ProvisioningManager.createForSubscriptionId(mDefaultSmsSubId);
+            init();
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        this.unregisterReceiver(mSingleRegCapabilityReceiver);
+        if (mIsRegistered) {
+            mProvisioningManager.unregisterRcsProvisioningCallback(mCallback);
+        }
+    }
+
+    private void init() {
+        mRegisterButton = findViewById(R.id.provisioning_register_btn);
+        mUnRegisterButton = findViewById(R.id.provisioning_unregister_btn);
+        mIsCapableButton = findViewById(R.id.provisioning_singlereg_btn);
+        mSingleRegResult = findViewById(R.id.provisioning_singlereg_result);
+        mCallbackResult = findViewById(R.id.provisioning_callback_result);
+        mCallbackResult.setMovementMethod(new ScrollingMovementMethod());
+
+        boolean isSingleRegCapable = false;
+        try {
+            mProvisioningManager.isRcsVolteSingleRegistrationCapable();
+        } catch (ImsException e) {
+            Log.i(TAG, e.getMessage());
+        }
+        if (isSingleRegCapable && !mIsRegistered) {
+            setClickable(mRegisterButton, true);
+        }
+
+        mRegisterButton.setOnClickListener(view -> {
+            if (mProvisioningManager != null) {
+                Log.i(TAG, "Using configuration: " + getDefaultClientConfiguration());
+                try {
+                    Log.i(TAG, "setRcsClientConfiguration()");
+                    Log.i(TAG, "registerRcsProvisioningCallback()");
+                    mProvisioningManager.setRcsClientConfiguration(getDefaultClientConfiguration());
+                    mProvisioningManager.registerRcsProvisioningCallback(mExecutorService,
+                            mCallback);
+                    mIsRegistered = true;
+                } catch (ImsException e) {
+                    Log.e(TAG, e.getMessage());
+                }
+                setClickable(mRegisterButton, false);
+                setClickable(mUnRegisterButton, true);
+            } else {
+                Log.i(TAG, "provisioningManager null");
+            }
+        });
+        mUnRegisterButton.setOnClickListener(view -> {
+            if (mProvisioningManager != null) {
+                mProvisioningManager.unregisterRcsProvisioningCallback(mCallback);
+                setClickable(mRegisterButton, false);
+                setClickable(mRegisterButton, true);
+                mIsRegistered = false;
+            }
+        });
+        mIsCapableButton.setOnClickListener(view -> {
+            if (mProvisioningManager != null) {
+                try {
+                    boolean capable = mProvisioningManager.isRcsVolteSingleRegistrationCapable();
+                    mSingleRegResult.setText(String.valueOf(capable));
+                    Log.i(TAG, "isRcsVolteSingleRegistrationCapable:" + capable);
+                } catch (ImsException e) {
+                    Log.e(TAG, e.getMessage());
+                }
+            }
+        });
+    }
+
+    private void setClickable(Button button, boolean clickable) {
+        if (clickable) {
+            button.setAlpha(1);
+            button.setClickable(true);
+        } else {
+            button.setAlpha(.5f);
+            button.setClickable(false);
+        }
+    }
+
+    private void initRcsProfile() {
+        mRcsProfileSpinner = findViewById(R.id.rcs_profile_list);
+        ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
+                R.array.rcs_profile, android.R.layout.simple_spinner_item);
+        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        mRcsProfileSpinner.setAdapter(adapter);
+        mRcsProfileSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+                Log.i(TAG, "rcs profile position:" + position);
+                switch (position) {
+                    case 0:
+                        mRcsProfile = UP_10;
+                        mRcsVersion = V_6;
+                        break;
+                    case 1:
+                        mRcsProfile = UP_23;
+                        mRcsVersion = V_9;
+                        break;
+                    default:
+                        Log.e(TAG, "invalid position:" + position);
+                        return;
+                }
+                mPref.edit().putString(RCS_PROFILE, mRcsProfile)
+                        .putString(RCS_VERSION, mRcsVersion)
+                        .commit();
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> parent) {
+                // TODO Auto-generated method stub
+            }
+        });
+        mRcsProfile = mPref.getString(RCS_PROFILE, UP_10);
+        mRcsVersion = mPref.getString(RCS_VERSION, V_6);
+        mRcsProfileSpinner.setSelection(mRcsProfile.equals(UP_10) ? 0 : 1);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == android.R.id.home) {
+            finish();
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    class SingleRegCapabilityReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            Log.i(TAG, "onReceive action:" + action);
+            if (ProvisioningManager.ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE.equals(
+                    action)) {
+                int status = intent.getIntExtra(ProvisioningManager.EXTRA_STATUS,
+                        ProvisioningManager.STATUS_DEVICE_NOT_CAPABLE);
+                Log.i(TAG, "singleRegCap status:" + status);
+                if (mRegisterButton != null && !mIsRegistered) {
+                    if (status == ProvisioningManager.STATUS_DEVICE_NOT_CAPABLE) {
+                        setClickable(mRegisterButton, true);
+                    } else {
+                        setClickable(mRegisterButton, false);
+                    }
+                }
+
+            }
+        }
+    }
+
+}
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/RcsStateChangedCallback.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/RcsStateChangedCallback.java
new file mode 100644
index 0000000..fd36f01
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/RcsStateChangedCallback.java
@@ -0,0 +1,25 @@
+/*
+ * 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.google.android.sample.rcsclient;
+
+import com.android.libraries.rcs.simpleclient.SimpleRcsClient.State;
+
+/** A callback used to notify RCS state change. */
+public interface RcsStateChangedCallback {
+    /** callback for RCS state change. */
+    void notifyStateChange(State oldState, State newState);
+}
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/SessionStateCallback.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/SessionStateCallback.java
new file mode 100644
index 0000000..3881775
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/SessionStateCallback.java
@@ -0,0 +1,26 @@
+/*
+ * 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.google.android.sample.rcsclient;
+
+/** A callback used to inform chat session creation result. */
+public interface SessionStateCallback {
+    /** callback for successful session creation */
+    void onSuccess();
+
+    /**callback for failed session creation. */
+    void onFailure();
+}
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java
new file mode 100644
index 0000000..0fae8f6
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/UceActivity.java
@@ -0,0 +1,244 @@
+/*
+ * 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.google.android.sample.rcsclient;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.telephony.SmsManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsManager;
+import android.telephony.ims.ImsRcsManager;
+import android.telephony.ims.RcsContactPresenceTuple;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.RcsUceAdapter;
+import android.text.TextUtils;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.MenuItem;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.google.android.sample.rcsclient.util.NumberUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** An activity to verify UCE. */
+public class UceActivity extends AppCompatActivity {
+
+    private static final String TAG = "TestRcsApp.UceActivity";
+    private static final String TELURI_PREFIX = "tel:";
+    private Button mCapabilityButton;
+    private Button mAvailabilityButton;
+    private TextView mCapabilityResult;
+    private EditText mNumbers;
+    private int mDefaultSmsSubId;
+    private ImsRcsManager mImsRcsManager;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.uce_layout);
+
+        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+        getSupportActionBar().setDisplayShowHomeEnabled(true);
+
+        initLayout();
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mDefaultSmsSubId = SmsManager.getDefaultSmsSubscriptionId();
+        Log.i(TAG, "defaultSmsSubId:" + mDefaultSmsSubId);
+        if (SubscriptionManager.isValidSubscriptionId(mDefaultSmsSubId)) {
+            mImsRcsManager = getImsRcsManager(mDefaultSmsSubId);
+            if (mImsRcsManager != null) {
+                initLayout();
+            }
+        }
+    }
+
+    private void initLayout() {
+        mCapabilityButton = findViewById(R.id.capability_btn);
+        mAvailabilityButton = findViewById(R.id.availability_btn);
+        mCapabilityResult = findViewById(R.id.capability_result);
+        mCapabilityResult.setMovementMethod(new ScrollingMovementMethod());
+
+        mCapabilityButton.setOnClickListener(view -> {
+            List<Uri> contactList = getContectList();
+            if (contactList.size() == 0) {
+                Log.i(TAG, "empty contact list");
+                return;
+            }
+            mCapabilityResult.setText("pending...\n");
+            try {
+                mImsRcsManager.getUceAdapter().requestCapabilities(contactList, getMainExecutor(),
+                        new RcsUceAdapter.CapabilitiesCallback() {
+                            public void onCapabilitiesReceived(
+                                    List<RcsContactUceCapability> contactCapabilities) {
+                                StringBuilder b = new StringBuilder("onCapabilitiesReceived:\n");
+                                for (RcsContactUceCapability c : contactCapabilities) {
+                                    b.append(getReadableCapability(c));
+                                    b.append("\n");
+                                }
+                                mCapabilityResult.append(b.toString() + "\n");
+                                Log.i(TAG, b.toString());
+                            }
+
+                            public void onComplete() {
+                                Log.i(TAG, "onComplete()");
+                                mCapabilityResult.append("onComplete");
+
+                            }
+
+                            public void onError(int errorCode, long retryAfterMilliseconds) {
+                                String result =
+                                        "onError() errorCode:" + errorCode + " retryAfterMs:"
+                                                + retryAfterMilliseconds + "\n";
+                                Log.i(TAG, result);
+                                mCapabilityResult.append(result);
+                            }
+                        });
+            } catch (ImsException e) {
+                mCapabilityResult.setText("ImsException:" + e);
+            }
+        });
+
+        mAvailabilityButton.setOnClickListener(view -> {
+            List<Uri> contactList = getContectList();
+            if (contactList.size() == 0) {
+                Log.i(TAG, "empty contact list");
+                return;
+            }
+            mCapabilityResult.setText("pending...\n");
+            try {
+                mImsRcsManager.getUceAdapter().requestAvailability(contactList.get(0),
+                        getMainExecutor(), new RcsUceAdapter.CapabilitiesCallback() {
+                            public void onCapabilitiesReceived(
+                                    List<RcsContactUceCapability> contactCapabilities) {
+                                StringBuilder b = new StringBuilder("onCapabilitiesReceived:\n");
+                                for (RcsContactUceCapability c : contactCapabilities) {
+                                    b.append(getReadableCapability(c));
+                                    b.append("\n");
+                                }
+                                mCapabilityResult.append(b.toString() + "\n");
+                                Log.i(TAG, b.toString());
+                            }
+
+                            public void onComplete() {
+                                Log.i(TAG, "onComplete()");
+                                mCapabilityResult.append("onComplete");
+
+                            }
+
+                            public void onError(int errorCode, long retryAfterMilliseconds) {
+                                String result =
+                                        "onError() errorCode:" + errorCode + " retryAfterMs:"
+                                                + retryAfterMilliseconds + "\n";
+                                Log.i(TAG, result);
+                                mCapabilityResult.append(result);
+                            }
+                        });
+            } catch (ImsException e) {
+                mCapabilityResult.setText("ImsException:" + e);
+            }
+        });
+    }
+
+    private List<Uri> getContectList() {
+        mNumbers = findViewById(R.id.number_list);
+        String[] numbers;
+        ArrayList<Uri> contactList = new ArrayList<>();
+        if (!TextUtils.isEmpty(mNumbers.getText().toString())) {
+            String numberList = mNumbers.getText().toString().trim();
+            numbers = numberList.split(",");
+            for (String number : numbers) {
+                String formattedNumber = NumberUtils.formatNumber(this, number);
+                if (formattedNumber != null) {
+                    contactList.add(Uri.parse(TELURI_PREFIX + formattedNumber));
+                } else {
+                    Log.w(TAG, "number formatted improperly, skipping: " + number);
+                }
+            }
+        }
+
+        return contactList;
+    }
+
+    private ImsRcsManager getImsRcsManager(int subId) {
+        ImsManager imsManager = getSystemService(ImsManager.class);
+        if (imsManager != null) {
+            try {
+                return imsManager.getImsRcsManager(subId);
+            } catch (Exception e) {
+                Log.e(TAG, "fail to getImsRcsManager " + e.getMessage());
+                return null;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == android.R.id.home) {
+            finish();
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    private String getReadableCapability(RcsContactUceCapability c) {
+        StringBuilder b = new StringBuilder("RcsContactUceCapability: uri=");
+        b.append(c.getContactUri());
+        b.append(", requestResult=");
+        b.append(c.getRequestResult());
+        b.append(", sourceType=");
+        b.append(c.getSourceType());
+        if (c.getCapabilityMechanism() == RcsContactUceCapability.CAPABILITY_MECHANISM_PRESENCE) {
+            b.append(", tuples={");
+            for (RcsContactPresenceTuple t : c.getCapabilityTuples()) {
+                b.append("[uri=");
+                b.append(t.getContactUri());
+                b.append(", serviceId=");
+                b.append(t.getServiceId());
+                b.append(", serviceVersion=");
+                b.append(t.getServiceVersion());
+                if (t.getServiceCapabilities() != null) {
+                    RcsContactPresenceTuple.ServiceCapabilities servCaps =
+                            t.getServiceCapabilities();
+                    b.append(", servCaps=(audio=");
+                    b.append(servCaps.isAudioCapable());
+                    b.append(", video=");
+                    b.append(servCaps.isVideoCapable());
+                    b.append(", servCaps=(supported=");
+                    b.append(servCaps.getSupportedDuplexModes());
+                    b.append("), servCaps=(unsupported=");
+                    b.append(servCaps.getUnsupportedDuplexModes());
+                    b.append("))");
+                }
+                b.append("]");
+            }
+            b.append("}");
+        }
+        return b.toString();
+    }
+}
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/ChatManager.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/ChatManager.java
new file mode 100644
index 0000000..ed22f03
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/ChatManager.java
@@ -0,0 +1,321 @@
+/*
+ * 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.google.android.sample.rcsclient.util;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.telephony.ims.ImsManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.libraries.rcs.simpleclient.SimpleRcsClient;
+import com.android.libraries.rcs.simpleclient.SimpleRcsClient.State;
+import com.android.libraries.rcs.simpleclient.provisioning.ProvisioningController;
+import com.android.libraries.rcs.simpleclient.provisioning.StaticConfigProvisioningController;
+import com.android.libraries.rcs.simpleclient.registration.RegistrationController;
+import com.android.libraries.rcs.simpleclient.registration.RegistrationControllerImpl;
+import com.android.libraries.rcs.simpleclient.service.chat.MinimalCpmChatService;
+import com.android.libraries.rcs.simpleclient.service.chat.SimpleChatSession;
+
+import com.google.android.sample.rcsclient.RcsStateChangedCallback;
+import com.google.android.sample.rcsclient.SessionStateCallback;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+
+import gov.nist.javax.sip.address.AddressFactoryImpl;
+
+import java.text.ParseException;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import javax.sip.address.AddressFactory;
+import javax.sip.address.URI;
+
+/**
+ * This class takes advantage of rcs library to manage chat session and send/receive chat message.
+ */
+public class ChatManager {
+    public static final String SELF = "self";
+    private static final String TAG = "TestRcsApp.ChatManager";
+    private static final String TELURI_PREFIX = "tel:";
+    private static AddressFactory sAddressFactory = new AddressFactoryImpl();
+    private static HashMap<Integer, ChatManager> sChatManagerInstances = new HashMap<>();
+    private final ExecutorService mFixedThreadPool = Executors.newFixedThreadPool(5);
+    private Context mContext;
+    private ProvisioningController mProvisioningController;
+    private RegistrationController mRegistrationController;
+    private MinimalCpmChatService mImsService;
+    private SimpleRcsClient mSimpleRcsClient;
+    private State mState;
+    private int mSubId;
+    private HashMap<String, SimpleChatSession> mContactSessionMap = new HashMap<>();
+    private RcsStateChangedCallback mRcsStateChangedCallback;
+
+    private ChatManager(Context context, int subId) {
+        mContext = context;
+        mSubId = subId;
+        mProvisioningController = StaticConfigProvisioningController.createForSubscriptionId(subId,
+                context);
+        ImsManager imsManager = mContext.getSystemService(ImsManager.class);
+        mRegistrationController = new RegistrationControllerImpl(subId, mFixedThreadPool,
+                imsManager);
+        mImsService = new MinimalCpmChatService(context);
+        mSimpleRcsClient = SimpleRcsClient.newBuilder()
+                .registrationController(mRegistrationController)
+                .provisioningController(mProvisioningController)
+                .imsService(mImsService).build();
+
+        mState = State.NEW;
+        // register callback for state change
+        mSimpleRcsClient.onStateChanged((oldState, newState) -> {
+            Log.i(TAG, "notifyStateChange() oldState:" + oldState + " newState:" + newState);
+            mState = newState;
+            mRcsStateChangedCallback.notifyStateChange(oldState, newState);
+        });
+        mImsService.setListener((session) -> {
+            Log.i(TAG, "onIncomingSession():" + session.getRemoteUri());
+            String phoneNumber = getNumberFromUri(session.getRemoteUri().toString());
+            mContactSessionMap.put(phoneNumber, session);
+            session.setListener(
+                    // implement onMessageReceived()
+                    (message) -> {
+                        mFixedThreadPool.execute(() -> {
+                            String msg = message.content();
+                            if (TextUtils.isEmpty(phoneNumber)) {
+                                Log.i(TAG, "dest number is empty, uri:"
+                                        + session.getRemoteUri());
+                            } else {
+                                addNewMessage(msg, phoneNumber, SELF);
+                            }
+                        });
+                    });
+        });
+    }
+
+    /**
+     * Create ChatManager with a specific subId.
+     */
+    public static ChatManager getInstance(Context context, int subId) {
+        synchronized (sChatManagerInstances) {
+            if (sChatManagerInstances.containsKey(subId)) {
+                return sChatManagerInstances.get(subId);
+            }
+            ChatManager chatManager = new ChatManager(context, subId);
+            sChatManagerInstances.put(subId, chatManager);
+            return chatManager;
+        }
+    }
+
+    /**
+     * Try to parse the given uri.
+     *
+     * @throws IllegalArgumentException in case of parsing error.
+     */
+    public static URI createUri(String uri) {
+        try {
+            return sAddressFactory.createURI(uri);
+        } catch (ParseException exception) {
+            throw new IllegalArgumentException("URI cannot be created", exception);
+        }
+    }
+
+    private static String getNumberFromUri(String number) {
+        String[] numberParts = number.split("[@;:]");
+        if (numberParts.length < 2) {
+            return null;
+        }
+        return numberParts[1];
+    }
+
+    /**
+     * set callback for RCS state change.
+     */
+    public void setRcsStateChangedCallback(RcsStateChangedCallback callback) {
+        mRcsStateChangedCallback = callback;
+    }
+
+    /**
+     * Start to register by doing provisioning and creating SipDelegate
+     */
+    public void register() {
+        Log.i(TAG, "do start(), State State = " + mState);
+        if (mState == State.NEW) {
+            mSimpleRcsClient.start();
+        }
+    }
+
+    /**
+     * Deregister chat feature.
+     */
+    public void deregister() {
+        Log.i(TAG, "deregister");
+        sChatManagerInstances.remove(mSubId);
+        mSimpleRcsClient.stop();
+    }
+
+    /**
+     * Initiate 1 to 1 chat session.
+     *
+     * @param contact  destination phone number.
+     * @param callback callback for session state.
+     */
+    public void initChatSession(String contact, SessionStateCallback callback) {
+        if (mState != State.REGISTERED) {
+            Log.i(TAG, "Could not init session due to State = " + mState);
+            return;
+        }
+        Log.i(TAG, "initChatSession contact: " + contact);
+        if (mContactSessionMap.containsKey(contact)) {
+            callback.onSuccess();
+            Log.i(TAG, "contact exists");
+            return;
+        }
+        Futures.addCallback(
+                mImsService.startOriginatingChatSession(TELURI_PREFIX + contact),
+                new FutureCallback<SimpleChatSession>() {
+                    @Override
+                    public void onSuccess(SimpleChatSession chatSession) {
+                        String phoneNumber = getNumberFromUri(
+                                chatSession.getRemoteUri().toString());
+                        mContactSessionMap.put(phoneNumber, chatSession);
+                        chatSession.setListener(
+                                // implement onMessageReceived()
+                                (message) -> {
+                                    mFixedThreadPool.execute(() -> {
+                                        String msg = message.content();
+                                        if (TextUtils.isEmpty(phoneNumber)) {
+                                            Log.i(TAG, "dest number is empty, uri:"
+                                                    + chatSession.getRemoteUri());
+                                        } else {
+                                            addNewMessage(msg, phoneNumber, SELF);
+                                        }
+                                    });
+
+                                });
+                        callback.onSuccess();
+                    }
+
+                    @Override
+                    public void onFailure(Throwable t) {
+                        callback.onFailure();
+                    }
+                },
+                MoreExecutors.directExecutor());
+    }
+
+    /**
+     * Send a chat message.
+     *
+     * @param contact destination phone number.
+     * @param message chat message.
+     */
+    public ListenableFuture<Void> sendMessage(String contact, String message) {
+        SimpleChatSession chatSession = mContactSessionMap.get(contact);
+        if (chatSession == null) {
+            Log.i(TAG, "session is unavailable for contact = " + contact);
+            return Futures.immediateFailedFuture(
+                    new IllegalStateException("Chat session does not exist"));
+        }
+        return chatSession.sendMessage(message);
+    }
+
+    public boolean isRegistered() {
+        return (mState == State.REGISTERED);
+    }
+
+    /**
+     * Terminate the chat session.
+     *
+     * @param contact destination phone number.
+     */
+    public void terminateSession(String contact) {
+        Log.i(TAG, "terminateSession");
+        SimpleChatSession chatSession = mContactSessionMap.get(contact);
+        if (chatSession == null) {
+            Log.i(TAG, "session is unavailable for contact = " + contact);
+            return;
+        }
+        chatSession.terminate();
+        mContactSessionMap.remove(contact);
+    }
+
+    /**
+     * Insert chat information into database.
+     *
+     * @param message chat message.
+     * @param src source phone number.
+     * @param dest destination phone number.
+     */
+    public Uri addNewMessage(String message, String src, String dest) {
+        long currentTime = Instant.now().getEpochSecond();
+        ContentValues contentValues = new ContentValues();
+        contentValues.put(ChatProvider.RcsColumns.SRC_PHONE_NUMBER, src);
+        contentValues.put(ChatProvider.RcsColumns.DEST_PHONE_NUMBER, dest);
+        contentValues.put(ChatProvider.RcsColumns.CHAT_MESSAGE, message);
+        contentValues.put(ChatProvider.RcsColumns.MSG_TIMESTAMP, currentTime);
+        contentValues.put(ChatProvider.RcsColumns.IS_READ, Boolean.TRUE);
+        // insert chat table
+        Uri result = mContext.getContentResolver().insert(ChatProvider.CHAT_URI, contentValues);
+
+        ContentValues summary = new ContentValues();
+        summary.put(ChatProvider.SummaryColumns.LATEST_MESSAGE, message);
+        summary.put(ChatProvider.SummaryColumns.MSG_TIMESTAMP, currentTime);
+        summary.put(ChatProvider.SummaryColumns.IS_READ, Boolean.TRUE);
+
+        String remoteNumber = src.equals(SELF) ? dest : src;
+        if (remoteNumberExists(remoteNumber)) {
+            mContext.getContentResolver().update(ChatProvider.SUMMARY_URI, summary,
+                    ChatProvider.SummaryColumns.REMOTE_PHONE_NUMBER + "=?",
+                    new String[]{remoteNumber});
+        } else {
+            summary.put(ChatProvider.SummaryColumns.REMOTE_PHONE_NUMBER, remoteNumber);
+            mContext.getContentResolver().insert(ChatProvider.SUMMARY_URI, summary);
+        }
+        return result;
+    }
+
+    /**
+     * Update MSRP chat message sent result.
+     */
+    public void updateMsgResult(String id, boolean success) {
+        ContentValues contentValues = new ContentValues();
+        contentValues.put(ChatProvider.RcsColumns.RESULT, success);
+        mContext.getContentResolver().update(ChatProvider.CHAT_URI, contentValues,
+                ChatProvider.RcsColumns._ID + "=?", new String[]{id});
+    }
+
+    /**
+     * Check if the number exists in the database.
+     */
+    public boolean remoteNumberExists(String number) {
+        Cursor cursor = mContext.getContentResolver().query(ChatProvider.SUMMARY_URI, null,
+                ChatProvider.SummaryColumns.REMOTE_PHONE_NUMBER + "=?", new String[]{number},
+                null);
+        if (cursor != null) {
+            int count = cursor.getCount();
+            return count > 0;
+        }
+        return false;
+    }
+
+}
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/ChatProvider.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/ChatProvider.java
new file mode 100644
index 0000000..98f3ceb
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/ChatProvider.java
@@ -0,0 +1,225 @@
+/*
+ * 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.google.android.sample.rcsclient.util;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.provider.BaseColumns;
+import android.text.TextUtils;
+import android.util.Log;
+
+/** A database to store chat message. */
+public class ChatProvider extends ContentProvider {
+    public static final Uri CHAT_URI = Uri.parse("content://rcsprovider/chat");
+    public static final Uri SUMMARY_URI = Uri.parse("content://rcsprovider/summary");
+    public static final String AUTHORITY = "rcsprovider";
+    private static final String TAG = "TestRcsApp.ChatProvider";
+    private static final int DATABASE_VERSION = 2;
+    private static final String CHAT_TABLE_NAME = "chat";
+    private static final String SUMMARY_TABLE_NAME = "summary";
+
+    private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
+    private static final int URI_CHAT = 1;
+    private static final int URI_SUMMARY = 2;
+    private static final String QUERY_CHAT_TABLE = " SELECT * FROM " + CHAT_TABLE_NAME;
+
+    static {
+        URI_MATCHER.addURI(AUTHORITY, "chat", URI_CHAT);
+        URI_MATCHER.addURI(AUTHORITY, "summary", URI_SUMMARY);
+    }
+
+    private RcsDatabaseHelper mRcsHelper;
+
+    @Override
+    public boolean onCreate() {
+        mRcsHelper = new RcsDatabaseHelper(getContext());
+        return true;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+        SQLiteDatabase db = mRcsHelper.getReadableDatabase();
+        int match = URI_MATCHER.match(uri);
+
+        Log.d(TAG, "Query URI: " + match);
+        switch (match) {
+            case URI_CHAT:
+                qb.setTables(CHAT_TABLE_NAME);
+                return qb.query(db, projection, selection, selectionArgs, null, null, sortOrder);
+            case URI_SUMMARY:
+                qb.setTables(SUMMARY_TABLE_NAME);
+                return qb.query(db, projection, selection, selectionArgs, null, null, sortOrder);
+            default:
+                Log.e(TAG, "no such uri");
+                return null;
+        }
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues contentValues) {
+        SQLiteDatabase db = mRcsHelper.getWritableDatabase();
+        int match = URI_MATCHER.match(uri);
+        long id;
+        switch (match) {
+            case URI_CHAT:
+                id = db.insert(CHAT_TABLE_NAME, "", contentValues);
+                break;
+            case URI_SUMMARY:
+                id = db.insert(SUMMARY_TABLE_NAME, "", contentValues);
+                break;
+            default:
+                Log.e(TAG, "no such uri");
+                throw new SQLException("no such uri");
+        }
+        if (id > 0) {
+            Uri msgUri = Uri.withAppendedPath(uri, String.valueOf(id));
+            getContext().getContentResolver().notifyChange(uri, null);
+            Log.i(TAG, "msgUri:" + msgUri);
+            return msgUri;
+        } else {
+            throw new SQLException("fail to add chat message");
+        }
+    }
+
+    @Override
+    public int delete(Uri uri, String s, String[] strings) {
+        return 0;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues contentValues, String selection,
+            String[] selectionArgs) {
+        SQLiteDatabase db = mRcsHelper.getWritableDatabase();
+        int match = URI_MATCHER.match(uri);
+        int result = 0;
+        String tableName = "";
+        switch (match) {
+            case URI_CHAT:
+                tableName = CHAT_TABLE_NAME;
+                break;
+            case URI_SUMMARY:
+                tableName = SUMMARY_TABLE_NAME;
+                break;
+        }
+        if (!TextUtils.isEmpty(tableName)) {
+            result = db.updateWithOnConflict(tableName, contentValues,
+                    selection, selectionArgs, SQLiteDatabase.CONFLICT_REPLACE);
+            getContext().getContentResolver().notifyChange(uri, null);
+            Log.d(TAG, "Update uri: " + match + " result: " + result);
+        } else {
+            Log.d(TAG, "Update. Not support URI.");
+        }
+        return result;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        return null;
+    }
+
+    /** Define columns for the chat table. */
+    public static class RcsColumns implements BaseColumns {
+        public static final String SRC_PHONE_NUMBER = "source_phone_number";
+        public static final String DEST_PHONE_NUMBER = "destination_phone_number";
+        public static final String CHAT_MESSAGE = "chat_message";
+        public static final String MSG_TIMESTAMP = "msg_timestamp";
+        public static final String IS_READ = "is_read";
+        public static final String RESULT = "result";
+    }
+
+    /** Define columns for the summary table. */
+    public static class SummaryColumns implements BaseColumns {
+        public static final String REMOTE_PHONE_NUMBER = "remote_phone_number";
+        public static final String LATEST_MESSAGE = "latest_message";
+        public static final String MSG_TIMESTAMP = "msg_timestamp";
+        public static final String IS_READ = "is_read";
+    }
+
+    /** Database helper */
+    public static final class RcsDatabaseHelper extends SQLiteOpenHelper {
+        public static final String SQL_CREATE_RCS_TABLE = "CREATE TABLE "
+                + CHAT_TABLE_NAME
+                + " ("
+                + RcsColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+                + RcsColumns.SRC_PHONE_NUMBER + " Text DEFAULT NULL, "
+                + RcsColumns.DEST_PHONE_NUMBER + " Text DEFAULT NULL, "
+                + RcsColumns.CHAT_MESSAGE + " Text DEFAULT NULL, "
+                + RcsColumns.MSG_TIMESTAMP + " LONG DEFAULT NULL, "
+                + RcsColumns.IS_READ + " BOOLEAN DEFAULT false, "
+                + RcsColumns.RESULT + " BOOLEAN DEFAULT true);";
+
+        public static final String SQL_CREATE_SUMMARY_TABLE = "CREATE TABLE "
+                + SUMMARY_TABLE_NAME
+                + " ("
+                + SummaryColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+                + SummaryColumns.REMOTE_PHONE_NUMBER + " Text DEFAULT NULL, "
+                + SummaryColumns.LATEST_MESSAGE + " Text DEFAULT NULL, "
+                + SummaryColumns.MSG_TIMESTAMP + " LONG DEFAULT NULL, "
+                + SummaryColumns.IS_READ + " BOOLEAN DEFAULT false);";
+        private static final String DB_NAME = "RcsDatabase";
+
+        public RcsDatabaseHelper(Context context) {
+            super(context, DB_NAME, null, DATABASE_VERSION);
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            db.execSQL(SQL_CREATE_RCS_TABLE);
+            db.execSQL(SQL_CREATE_SUMMARY_TABLE);
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            Log.d(TAG, "DB upgrade from " + oldVersion + " to " + newVersion);
+            db.beginTransaction();
+            try {
+                switch (oldVersion) {
+                    case 1:
+                        upgradeDatabaseToVersion2(db);
+                        break;
+                    default: // fall out
+                }
+                db.setTransactionSuccessful();
+            } catch (Exception ex) {
+                Log.e(TAG, ex.getMessage(), ex);
+            } finally {
+                db.endTransaction();
+            }
+        }
+
+        private static void upgradeDatabaseToVersion2(SQLiteDatabase db) {
+            try {
+                Log.d(TAG, "upgradeDatabaseToVersion2");
+                String alterTable = "ALTER TABLE " + CHAT_TABLE_NAME + " ADD COLUMN ";
+                db.execSQL(alterTable + RcsColumns.RESULT + " BOOLEAN DEFAULT true");
+            } catch (SQLiteException e) {
+                Log.w(TAG, "[upgradeDatabaseToVersion10] Exception adding column: " + e);
+            }
+        }
+    }
+}
diff --git a/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/NumberUtils.java b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/NumberUtils.java
new file mode 100644
index 0000000..14d3b9c
--- /dev/null
+++ b/testapps/TestRcsApp/TestApp/src/com/google/android/sample/rcsclient/util/NumberUtils.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 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.google.android.sample.rcsclient.util;
+
+import android.content.Context;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.android.i18n.phonenumbers.NumberParseException;
+import com.android.i18n.phonenumbers.PhoneNumberUtil;
+import com.android.i18n.phonenumbers.Phonenumber;
+
+public class NumberUtils {
+
+    /**
+     * Format a number in E164 format.
+     * <p>
+     * Note: if the number can not be formatted, this method will return null.
+     */
+    public static String formatNumber(Context context, String number) {
+        TelephonyManager manager = context.getSystemService(TelephonyManager.class);
+        String simCountryIso = manager.getSimCountryIso().toUpperCase();
+        PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+        try {
+            Phonenumber.PhoneNumber phoneNumber = util.parse(number, simCountryIso);
+            return util.format(phoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164);
+        } catch (NumberParseException e) {
+            Log.w("NumberUtils", "formatNumber: could not format " + number + ", error: " + e);
+        }
+        return null;
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/Android.bp b/testapps/TestRcsApp/aosp_test_rcsclient/Android.bp
new file mode 100644
index 0000000..215c692
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/Android.bp
@@ -0,0 +1,45 @@
+
+
+package {
+    default_applicable_licenses: [
+        "packages_services_Telephony_testapps_TestRcsApp_aosp_test_rcsclient_license",
+    ],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+    name: "packages_services_Telephony_testapps_TestRcsApp_aosp_test_rcsclient_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+    ],
+    license_text: [
+        "LICENSE",
+    ],
+}
+
+android_library {
+    name: "aosp_test_rcs_client_base",
+
+    srcs: ["src/com/android/libraries/rcs/**/*.java"],
+
+    static_libs: [
+        "androidx.annotation_annotation",
+        "androidx.concurrent_concurrent-futures",
+        "guava",
+        "nist-sip",
+    ],
+
+    libs: [
+        "auto_value_annotations",
+        "org.apache.http.legacy",
+    ],
+
+    plugins: [
+        "auto_value_plugin",
+    ],
+
+    sdk_version: "system_current",
+    min_sdk_version: "30",
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/AndroidManifest.xml b/testapps/TestRcsApp/aosp_test_rcsclient/AndroidManifest.xml
new file mode 100644
index 0000000..b167aa8
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //packages/services/Telephony/testapps/TestRcsApp/aosp_test_rcsclient/AndroidManifest.xml
+**
+** Copyright 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.
+*/
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.libraries.rcs.simpleclient"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <uses-sdk
+        android:minSdkVersion="21"
+        android:targetSdkVersion="23" />
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" />
+</manifest>
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/LICENSE b/testapps/TestRcsApp/aosp_test_rcsclient/LICENSE
new file mode 100644
index 0000000..b9b9d2a
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/LICENSE
@@ -0,0 +1,176 @@
+Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
\ No newline at end of file
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/protocol/cpim/SimpleCpimMessageTest.java b/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/protocol/cpim/SimpleCpimMessageTest.java
new file mode 100644
index 0000000..2dda33f
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/protocol/cpim/SimpleCpimMessageTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 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.libraries.rcs.simpleclient.protocol.cpim;
+
+import static com.google.common.truth.Truth.assertThat;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SimpleCpimMessageTest {
+    private static final String SAMPLE_CPIM =
+            "From: MR SANDERS <im:piglet@100akerwood.com>\r\n"
+                    + "To: Depressed Donkey <im:eeyore@100akerwood.com>\r\n"
+                    + "DateTime: 2000-12-13T13:40:00-08:00\r\n"
+                    + "Subject: the weather will be fine today\r\n"
+                    + "Subject:;lang=fr beau temps prevu pour aujourd'hui\r\n"
+                    + "NS: MyFeatures <mid:MessageFeatures@id.foo.com>\r\n"
+                    + "Require: MyFeatures.VitalMessageOption\r\n"
+                    + "MyFeatures.VitalMessageOption: Confirmation-requested\r\n"
+                    + "MyFeatures.WackyMessageOption: Use-silly-font\r\n"
+                    + "\r\n"
+                    + "Content-type: text/plain; charset=utf-8\r\n"
+                    + "Content-ID: <1234567890@foo.com>\r\n"
+                    + "\r\n"
+                    + "body";
+
+    @Test
+    public void parse_successful() throws Exception {
+        SimpleCpimMessage cpim = SimpleCpimMessage.parse(SAMPLE_CPIM.getBytes(UTF_8));
+
+        assertThat(cpim.namespaces()).containsEntry("MyFeatures", "mid:MessageFeatures@id.foo.com");
+        assertThat(cpim.headers()).containsEntry("Require", "MyFeatures.VitalMessageOption");
+        assertThat(cpim.contentType()).isEqualTo("text/plain; charset=utf-8");
+        assertThat(cpim.content()).isEqualTo("body");
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpChunkTest.java b/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpChunkTest.java
new file mode 100644
index 0000000..90f1714
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpChunkTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.msrp;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpChunk.Continuation;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class MsrpChunkTest {
+
+    private static MsrpChunk writeAndReadChunk(MsrpChunk chunk) throws IOException {
+        ByteArrayOutputStream bo = new ByteArrayOutputStream();
+        MsrpSerializer.serialize(bo, chunk);
+
+        ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
+        return MsrpParser.parse(bi);
+    }
+
+    @Test
+    public void whenSerializeParseRequest_success() throws IOException {
+        MsrpChunk chunk = MsrpChunk.newBuilder()
+                .method(MsrpChunk.Method.SEND)
+                .addHeader("To-Path", "msrp://123.1.11:9/testreceiver;tcp")
+                .addHeader("From-Path", "msrp://123.1.11:9/testsender;tcp")
+                .addHeader("Byte-Range", "1-*/*")
+                .transactionId("123123")
+                .addHeader("Content-Type", "text/plain")
+                .content("Hallo Welt\r\n".getBytes(UTF_8))
+                .continuation(Continuation.COMPLETE)
+                .build();
+
+        MsrpChunk chunk2 = writeAndReadChunk(chunk);
+
+        assertThat(chunk2).isEqualTo(chunk);
+    }
+
+    @Test
+    public void whenSerializeParseEmptyRequest_success() throws IOException {
+        MsrpChunk chunk = MsrpChunk.newBuilder()
+                .method(MsrpChunk.Method.SEND)
+                .transactionId("testtransaction")
+                .addHeader("To-Path", "msrp://123.1.11:9/testreceiver;tcp")
+                .addHeader("From-Path", "msrp://123.1.11:9/testsender;tcp")
+                .continuation(Continuation.COMPLETE)
+                .build();
+
+        MsrpChunk chunk2 = writeAndReadChunk(chunk);
+
+        assertThat(chunk2).isEqualTo(chunk);
+    }
+
+    @Test
+    public void whenSerializeParseResponse_success() throws IOException {
+        MsrpChunk chunk = MsrpChunk.newBuilder()
+                .responseCode(200)
+                .responseReason("OK")
+                .transactionId("testtransaction")
+                .addHeader("To-Path", "msrp://123.1.11:9/testreceiver;tcp")
+                .addHeader("From-Path", "msrp://123.1.11:9/testsender;tcp")
+                .continuation(Continuation.COMPLETE)
+                .build();
+
+        MsrpChunk chunk2 = writeAndReadChunk(chunk);
+
+        assertThat(chunk2).isEqualTo(chunk);
+    }
+
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSessionTest.java b/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSessionTest.java
new file mode 100644
index 0000000..dc60d37
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSessionTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.msrp;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpChunk.Continuation;
+
+import java.io.IOException;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.net.Socket;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/**
+ *
+ */
+@RunWith(AndroidJUnit4.class)
+public class MsrpSessionTest {
+    @Rule
+    public final MockitoRule mockito = MockitoJUnit.rule();
+
+    @Mock
+    private Socket socket;
+
+    @Before
+    public void setUp() throws IOException {
+        PipedInputStream input = new PipedInputStream();
+        when(socket.getInputStream()).thenReturn(input);
+        when(socket.getOutputStream()).thenReturn(new PipedOutputStream(input));
+        when(socket.isConnected()).thenReturn(true);
+    }
+
+    @Test
+    public void foo() throws IOException, ExecutionException, InterruptedException {
+
+        AtomicReference<MsrpChunk> receivedRequest = new AtomicReference<>();
+
+        final MsrpSession session =
+                new MsrpSession(
+                        socket,
+                        (m) -> {
+                            receivedRequest.set(m);
+                        });
+
+        MsrpChunk request = generateRequest();
+        Future<MsrpChunk> future = session.send(request);
+
+        Executors.newSingleThreadExecutor().execute(session::run);
+
+        MsrpChunk response = future.get();
+
+        assertThat(request).isEqualTo(receivedRequest.get());
+        assertThat(response).isEqualTo(generateSuccessResponse());
+    }
+
+    private MsrpChunk generateRequest() {
+        return MsrpChunk.newBuilder()
+                .transactionId("txid")
+                .method(MsrpChunk.Method.SEND)
+                .addHeader(MsrpConstants.HEADER_TO_PATH, "msrp://test:1234/sessionA;tcp")
+                .addHeader(MsrpConstants.HEADER_FROM_PATH, "msrp://test:1234/sessionB;tcp")
+                .addHeader(MsrpConstants.HEADER_BYTE_RANGE, "1-*/*")
+                .addHeader(MsrpConstants.HEADER_MESSAGE_ID, "abcde")
+                .addHeader(MsrpConstants.HEADER_CONTENT_TYPE, "text/plain")
+                .content("Hallo Welt\r\n".getBytes(StandardCharsets.UTF_8))
+                .continuation(Continuation.COMPLETE)
+                .build();
+    }
+
+    private MsrpChunk generateSuccessResponse() {
+        return MsrpChunk.newBuilder()
+                .transactionId("txid")
+                .responseCode(200)
+                .responseReason("OK")
+                .addHeader(MsrpConstants.HEADER_TO_PATH, "msrp://test:1234/sessionB;tcp")
+                .addHeader(MsrpConstants.HEADER_FROM_PATH, "msrp://test:1234/sessionA;tcp")
+                .continuation(Continuation.COMPLETE)
+                .build();
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/protocol/sdp/SimpleSdpMessageTest.java b/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/protocol/sdp/SimpleSdpMessageTest.java
new file mode 100644
index 0000000..4b5f31a
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/protocol/sdp/SimpleSdpMessageTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.sdp;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import java.io.ByteArrayInputStream;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SimpleSdpMessageTest {
+    private static final String SAMPLE_SDP_REGEX =
+            "v=0\r\n"
+                    + "o=TestRcsClient .+ .+ IN IP4 192.168.1.1\r\n"
+                    + "s=-\r\n"
+                    + "c=IN IP4 192.168.1.1\r\n"
+                    + "t=0 0\r\n"
+                    + "m=message 9 TCP/MSRP \\*\r\n"
+                    + "a=path:msrp://192.168.1.1:9/.+;tcp\r\n"
+                    + "a=setup:active\r\n"
+                    + "a=accept-types:message/cpim application/im-iscomposing\\+xml\r\n"
+                    + "a=accept-wrapped-types:text/plain message/imdn\\+xml"
+                    + " application/vnd.gsma.rcs-ft-http\\+xml application/vnd.gsma"
+                    + ".rcspushlocation\\+xml\r\n"
+                    + "a=sendrecv\r\n";
+
+    @Test
+    public void createForMsrp_returnExpectedSdpString() {
+        SimpleSdpMessage sdp =
+                SdpUtils.createSdpForMsrp(/* address= */ "192.168.1.1", /* isTls= */ false);
+
+        assertThat(sdp.encode()).matches(SAMPLE_SDP_REGEX);
+    }
+
+    @Test
+    public void encodeAndParse_shouldBeEqualToOriginal() throws Exception {
+        SimpleSdpMessage original =
+                SdpUtils.createSdpForMsrp(/* address= */ "192.168.1.1", /* isTls= */ false);
+
+        SimpleSdpMessage parsedSdp =
+                SimpleSdpMessage.parse(new ByteArrayInputStream(original.encode().getBytes(UTF_8)));
+
+        assertThat(parsedSdp).isEqualTo(original);
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/protocol/sip/SipUtilsTest.java b/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/protocol/sip/SipUtilsTest.java
new file mode 100644
index 0000000..be043f5
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/protocol/sip/SipUtilsTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.sip;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.google.common.collect.Lists;
+
+import gov.nist.javax.sip.message.SIPRequest;
+
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SipUtilsTest {
+    private static final String LOCAL_URI = "tel:+1234567890";
+    private static final String REMOTE_URI = "tel:+1234567891";
+    private static final String CONVERSATION_ID = "abcd-1234";
+
+    SipSessionConfiguration configuration =
+            new SipSessionConfiguration() {
+                @Override
+                public long getVersion() {
+                    return 0;
+                }
+
+                @Override
+                public String getOutboundProxyAddr() {
+                    return "3001:4870:e00b:5e94:21b8:8d20:c425:5e6c";
+                }
+
+                @Override
+                public int getOutboundProxyPort() {
+                    return 5060;
+                }
+
+                @Override
+                public String getLocalIpAddress() {
+                    return "2001:4870:e00b:5e94:21b8:8d20:c425:5e6c";
+                }
+
+                @Override
+                public int getLocalPort() {
+                    return 5060;
+                }
+
+                @Override
+                public String getSipTransport() {
+                    return "TCP";
+                }
+
+                @Override
+                public String getPublicUserIdentity() {
+                    return "sip:+1234567890@foo.bar";
+                }
+
+                @Override
+                public String getDomain() {
+                    return "foo.bar";
+                }
+
+                @Override
+                public List<String> getAssociatedUris() {
+                    return Lists.newArrayList(LOCAL_URI, "sip:+1234567890@foo.bar");
+                }
+
+                @Override
+                public String getSecurityVerifyHeader() {
+                    return "ipsec-3gpp;q=0.5;alg=hmac-sha-1-96;prot=esp;mod=trans;ealg=null;"
+                            + "spi-c=983227540;spi-s=2427966379;port-c=65528;port-s=65529";
+                }
+
+                @Override
+                public List<String> getServiceRouteHeaders() {
+                    return Lists.newArrayList();
+                }
+
+                @Override
+                public String getContactUser() {
+                    return "abcd-efgh";
+                }
+
+                @Override
+                public String getImei() {
+                    return "35293211-111080-0";
+                }
+
+                @Override
+                public String getPaniHeader() {
+                    return null;
+                }
+
+                @Override
+                public String getPlaniHeader() {
+                    return null;
+                }
+
+                @Override
+                public int getMaxPayloadSizeOnUdp() {
+                    return 0;
+                }
+            };
+
+    @Test
+    public void buildInvite_returnExpectedInviteMessage() throws Exception {
+        SIPRequest request = SipUtils.buildInvite(configuration, REMOTE_URI, CONVERSATION_ID);
+
+        assertThat(request.getRequestURI().toString()).isEqualTo(REMOTE_URI);
+        assertThat(request.getFrom().getAddress().getURI().toString()).isEqualTo(LOCAL_URI);
+        assertThat(request.getTo().getAddress().getURI().toString()).isEqualTo(REMOTE_URI);
+        assertThat(request.hasHeader("Conversation-ID")).isTrue();
+        assertThat(request.hasHeader("Contribution-ID")).isTrue();
+        assertThat(request.hasHeader("Accept-Contact")).isTrue();
+        assertThat(request.hasHeader("Security-Verify")).isTrue();
+    }
+
+    @Test
+    public void buildInvite_sizeIsGreaterThanMaxPayloadSize_transportShouldBeTcp()
+            throws Exception {
+        SIPRequest request = SipUtils.buildInvite(configuration, REMOTE_URI, CONVERSATION_ID);
+
+        // The size is always greater than maxPayloadSizeOnUdp = 0
+        assertThat(request.getTopmostVia().getTransport()).isEqualTo("TCP");
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningControllerTest.java b/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningControllerTest.java
new file mode 100644
index 0000000..b9065de
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningControllerTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.libraries.rcs.simpleclient.provisioning;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.support.annotation.RequiresPermission;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import java.util.Optional;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class StaticConfigProvisioningControllerTest {
+
+    private static final byte[] CONFIG_DATA = "<xml></xml>".getBytes();
+
+    private StaticConfigProvisioningController client;
+    private Optional<byte[]> configXmlData = Optional.empty();
+    private ProvisioningStateChangeCallback cb =
+            configXml -> configXmlData = Optional.ofNullable(configXml);
+
+    @Before
+    public void setUp() {
+        client = StaticConfigProvisioningController.createForSubscriptionId(/*subscriptionId=*/ 2);
+        client.onConfigurationChange(cb);
+    }
+
+    @Test
+    @RequiresPermission(value = "Manifest.permission.READ_PRIVILEGED_PHONE_STATE")
+    public void whenGetConfigCalled_returnsCorrectXmlData() throws Exception {
+        client.register();
+        client.getProvisioningManager().getCallbackForTests().onConfigurationChanged(CONFIG_DATA);
+
+        assertThat(client.isRcsVolteSingleRegistrationCapable()).isTrue();
+        assertThat(client.getLatestConfiguration()).isEqualTo(CONFIG_DATA);
+        assertThat(configXmlData.get()).isEqualTo(CONFIG_DATA);
+        client.unRegister();
+    }
+
+    @Test
+    @RequiresPermission(value = "Manifest.permission.READ_PRIVILEGED_PHONE_STATE")
+    public void whenGetConfigCalled_throwsErrorWhenNoConfigPresent() throws Exception {
+        client.register();
+        client.triggerReconfiguration();
+
+        assertThat(client.isRcsVolteSingleRegistrationCapable()).isTrue();
+        assertThrows(IllegalStateException.class, () -> client.getLatestConfiguration());
+        assertThat(configXmlData.isPresent()).isFalse();
+
+        client.unRegister();
+    }
+
+    @Test
+    @RequiresPermission(value = "Manifest.permission.READ_PRIVILEGED_PHONE_STATE")
+    public void unRegister_failsWhenCalledWithoutRegister() {
+        assertThrows(IllegalStateException.class, () -> client.unRegister());
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/service/chat/SimpleChatSessionTest.java b/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/service/chat/SimpleChatSessionTest.java
new file mode 100644
index 0000000..2723940
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/javatests/com/android/libraries/rcs/simpleclient/service/chat/SimpleChatSessionTest.java
@@ -0,0 +1,212 @@
+/*
+ * 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.libraries.rcs.simpleclient.service.chat;
+
+import static com.google.common.labs.truth.FutureSubject.assertThat;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.libraries.rcs.simpleclient.SimpleRcsClientContext;
+import com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpManager;
+import com.android.libraries.rcs.simpleclient.protocol.sdp.SdpUtils;
+import com.android.libraries.rcs.simpleclient.protocol.sip.SipSession;
+import com.android.libraries.rcs.simpleclient.protocol.sip.SipSessionConfiguration;
+import com.android.libraries.rcs.simpleclient.protocol.sip.SipSessionListener;
+
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import gov.nist.javax.sip.message.SIPRequest;
+import gov.nist.javax.sip.message.SIPResponse;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+import javax.sip.message.Message;
+import javax.sip.message.Request;
+
+@RunWith(AndroidJUnit4.class)
+public class SimpleChatSessionTest {
+    private static final String LOCAL_URI = "tel:+1234567890";
+    private static final String REMOTE_URI = "tel:+1234567891";
+    private final MsrpManager msrpManager =
+            new MsrpManager(ApplicationProvider.getApplicationContext());
+    SipSessionConfiguration configuration =
+            new SipSessionConfiguration() {
+                @Override
+                public long getVersion() {
+                    return 0;
+                }
+
+                @Override
+                public String getOutboundProxyAddr() {
+                    return "3001:4870:e00b:5e94:21b8:8d20:c425:5e6c";
+                }
+
+                @Override
+                public int getOutboundProxyPort() {
+                    return 5060;
+                }
+
+                @Override
+                public String getLocalIpAddress() {
+                    return "2001:4870:e00b:5e94:21b8:8d20:c425:5e6c";
+                }
+
+                @Override
+                public int getLocalPort() {
+                    return 5060;
+                }
+
+                @Override
+                public String getSipTransport() {
+                    return "TCP";
+                }
+
+                @Override
+                public String getPublicUserIdentity() {
+                    return "sip:+1234567890@foo.bar";
+                }
+
+                @Override
+                public String getDomain() {
+                    return "foo.bar";
+                }
+
+                @Override
+                public List<String> getAssociatedUris() {
+                    return Lists.newArrayList(LOCAL_URI, "sip:+1234567890@foo.bar");
+                }
+
+                @Override
+                public String getSecurityVerifyHeader() {
+                    return "ipsec-3gpp;q=0.5;alg=hmac-sha-1-96;prot=esp;mod=trans;ealg=null;"
+                            + "spi-c=983227540;spi-s=2427966379;port-c=65528;port-s=65529";
+                }
+
+                @Override
+                public List<String> getServiceRouteHeaders() {
+                    return Lists.newArrayList();
+                }
+
+                @Override
+                public String getContactUser() {
+                    return "abcd-efgh";
+                }
+
+                @Override
+                public String getImei() {
+                    return "35293211-111080-0";
+                }
+
+                @Override
+                public String getPaniHeader() {
+                    return "IEEE-802.11;i-wlan-node-id=PANIC01EB5B0";
+                }
+
+                @Override
+                public String getPlaniHeader() {
+                    return "IEEE-802.11;i-wlan-node-id=PLANI01EB5B0";
+                }
+
+                @Override
+                public String getUserAgentHeader() {
+                    return "Test-Client";
+                }
+
+                @Override
+                public int getMaxPayloadSizeOnUdp() {
+                    return 0;
+                }
+            };
+    private final SimpleRcsClientContext context =
+            new SimpleRcsClientContext(
+                    /* provisioningController= */ null,
+                    /* registrationController= */ null,
+                    /* imsService= */ null,
+                    new SipSession() {
+                        @Override
+                        public SipSessionConfiguration getSessionConfiguration() {
+                            return configuration;
+                        }
+
+                        @Override
+                        public ListenableFuture<Boolean> send(Message message) {
+                            return Futures.immediateFuture(true);
+                        }
+
+                        @Override
+                        public void setSessionListener(SipSessionListener listener) {
+                        }
+                    });
+
+    @Test
+    public void start_reply200_returnSuccessfulFuture() throws Exception {
+        SimpleChatSession session =
+                new SimpleChatSession(
+                        context,
+                        new MinimalCpmChatService(ApplicationProvider.getApplicationContext()) {
+                            @Override
+                            ListenableFuture<Boolean> sendSipRequest(SIPRequest msg,
+                                    SimpleChatSession session) {
+                                if (msg.getMethod().equals(Request.INVITE)) {
+                                    SIPResponse response = msg.createResponse(/* statusCode= */
+                                            200);
+                                    response.setMessageContent(
+                                            /* type= */ "application",
+                                            /* subType= */ "sdp",
+                                            SdpUtils.createSdpForMsrp(/* address= */
+                                                    "127.0.0.1", /* isTls= */ false)
+                                                    .encode());
+                                    session.receiveMessage(response);
+                                }
+                                return Futures.immediateFuture(true);
+                            }
+                        },
+                        msrpManager);
+
+        // session.start should return the successful void future.
+        assertThat(session.start(REMOTE_URI)).whenDone().isSuccessful();
+    }
+
+    @Test
+    public void start_reply404_returnFailedFuture() throws Exception {
+        SimpleChatSession session =
+                new SimpleChatSession(
+                        context,
+                        new MinimalCpmChatService(ApplicationProvider.getApplicationContext()) {
+                            @Override
+                            ListenableFuture<Boolean> sendSipRequest(SIPRequest msg,
+                                    SimpleChatSession session) {
+                                if (msg.getMethod().equals(Request.INVITE)) {
+                                    SIPResponse response = msg.createResponse(/* statusCode= */
+                                            404);
+                                    session.receiveMessage(response);
+                                }
+                                return Futures.immediateFuture(true);
+                            }
+                        },
+                        msrpManager);
+
+        // session.start should return the failed future with the exception.
+        assertThat(session.start(REMOTE_URI)).whenDone().isFailedWith(ChatServiceException.class);
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/SimpleRcsClient.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/SimpleRcsClient.java
new file mode 100644
index 0000000..0469bc0
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/SimpleRcsClient.java
@@ -0,0 +1,187 @@
+/*
+ * 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.libraries.rcs.simpleclient;
+
+import android.os.Build.VERSION_CODES;
+import android.telephony.ims.ImsException;
+import android.util.Log;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.libraries.rcs.simpleclient.protocol.sip.SipSession;
+import com.android.libraries.rcs.simpleclient.provisioning.ProvisioningController;
+import com.android.libraries.rcs.simpleclient.registration.RegistrationController;
+import com.android.libraries.rcs.simpleclient.registration.RegistrationStateChangeCallback;
+import com.android.libraries.rcs.simpleclient.service.ImsService;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Simple RCS client implementation.
+ *
+ * State is covered by a context instance.
+ */
+@RequiresApi(api = VERSION_CODES.R)
+public class SimpleRcsClient {
+    private static final String TAG = SimpleRcsClient.class.getSimpleName();
+    private final AtomicReference<State> state = new AtomicReference<>(State.NEW);
+    private ProvisioningController provisioningController;
+    private RegistrationController registrationController;
+    private ImsService imsService;
+    private SimpleRcsClientContext context;
+    private StateChangedCallback stateChangedCallback;
+
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    public SimpleRcsClientContext getContext() {
+        return context;
+    }
+
+    public void start() {
+        provision();
+    }
+
+    public void stop() {
+        Log.i(TAG, "stop..");
+        registrationController.deregister();
+        provisioningController.unRegister();
+        provisioningController = null;
+        registrationController = null;
+        imsService = null;
+    }
+
+    public void onStateChanged(StateChangedCallback cb) {
+        this.stateChangedCallback = cb;
+    }
+
+    private boolean enterState(State expected, State newState) {
+        boolean result = state.compareAndSet(expected, newState);
+
+        if (result && stateChangedCallback != null) {
+            try {
+                stateChangedCallback.notifyStateChange(expected, newState);
+            } catch (Exception e) {
+                Log.e(TAG, "Exception on calling state change callback", e);
+            }
+        }
+        Log.i(TAG, "expected:" + expected + " new:" + newState + " res:" + result);
+        return result;
+    }
+
+    private void provision() {
+        if (!enterState(State.NEW, State.PROVISIONING)) {
+            return;
+        }
+        provisioningController.onConfigurationChange(configXml -> {
+            register();
+        });
+        try {
+            provisioningController.triggerProvisioning();
+        } catch (ImsException e) {
+            // TODO: ...
+        }
+    }
+
+    private void register() {
+        if (!enterState(State.PROVISIONING, State.REGISTERING)) {
+            return;
+        }
+
+        registrationController.register(imsService,
+                new RegistrationStateChangeCallback() {
+                    @Override
+                    public void notifyRegStateChanged(ImsService imsService) {
+
+                    }
+
+                    @Override
+                    public void onSuccess(SipSession sipSession) {
+                        Log.i(TAG, "onSuccess");
+                        registered(sipSession);
+                    }
+
+                    @Override
+                    public void onFailure(String reason) {
+                        Log.i(TAG, "onFailure reason:" + reason);
+                        notRegistered();
+                    }
+                });
+    }
+
+    private void registered(SipSession session) {
+        if (state.get().equals(State.REGISTERING) || state.get().equals(State.NOT_REGISTERED)) {
+            enterState(state.get(), State.REGISTERED);
+        }
+
+        context = new SimpleRcsClientContext(provisioningController, registrationController,
+                imsService,
+                session);
+
+        imsService.start(context);
+    }
+
+    private void notRegistered() {
+        enterState(State.REGISTERED, State.NOT_REGISTERED);
+    }
+
+    /**
+     * Possible client states.
+     */
+    public enum State {
+        NEW,
+        PROVISIONING,
+        REGISTERING,
+        REGISTERED,
+        NOT_REGISTERED,
+    }
+
+    /**
+     * Builder for creating new SimpleRcsClient instances.
+     */
+    public static class Builder {
+
+        private ProvisioningController provisioningController;
+        private RegistrationController registrationController;
+        private ImsService imsService;
+
+        public Builder provisioningController(ProvisioningController controller) {
+            this.provisioningController = controller;
+            return this;
+        }
+
+        public Builder registrationController(RegistrationController controller) {
+            this.registrationController = controller;
+            return this;
+        }
+
+        public Builder imsService(ImsService imsService) {
+            this.imsService = imsService;
+            return this;
+        }
+
+        public SimpleRcsClient build() {
+            SimpleRcsClient client = new SimpleRcsClient();
+            client.registrationController = registrationController;
+            client.provisioningController = provisioningController;
+            client.imsService = imsService;
+
+            return client;
+        }
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/SimpleRcsClientContext.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/SimpleRcsClientContext.java
new file mode 100644
index 0000000..1be6403
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/SimpleRcsClientContext.java
@@ -0,0 +1,63 @@
+/*
+ * 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.libraries.rcs.simpleclient;
+
+import com.android.libraries.rcs.simpleclient.protocol.sip.SipSession;
+import com.android.libraries.rcs.simpleclient.provisioning.ProvisioningController;
+import com.android.libraries.rcs.simpleclient.registration.RegistrationController;
+import com.android.libraries.rcs.simpleclient.service.ImsService;
+
+/**
+ * State container for a {@link SimpleRcsClient} instance.
+ */
+public class SimpleRcsClientContext {
+
+    private final ProvisioningController provisioningController;
+
+    private final RegistrationController registrationController;
+
+    private final ImsService imsService;
+
+    private final SipSession sipSession;
+
+    public SimpleRcsClientContext(
+            ProvisioningController provisioningController,
+            RegistrationController registrationController,
+            ImsService imsService,
+            SipSession sipSession) {
+        this.provisioningController = provisioningController;
+        this.registrationController = registrationController;
+        this.imsService = imsService;
+        this.sipSession = sipSession;
+    }
+
+    public ProvisioningController getProvisioningController() {
+        return provisioningController;
+    }
+
+    public RegistrationController getRegistrationController() {
+        return registrationController;
+    }
+
+    public ImsService getImsService() {
+        return imsService;
+    }
+
+    public SipSession getSipSession() {
+        return sipSession;
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/StateChangedCallback.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/StateChangedCallback.java
new file mode 100644
index 0000000..87f6566
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/StateChangedCallback.java
@@ -0,0 +1,26 @@
+/*
+ * 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.libraries.rcs.simpleclient;
+
+import com.android.libraries.rcs.simpleclient.SimpleRcsClient.State;
+
+/**
+ * Callback for processing state changes.
+ */
+public interface StateChangedCallback {
+    void notifyStateChange(State oldState, State newState);
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/FileTransferController.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/FileTransferController.java
new file mode 100644
index 0000000..f6548d8
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/FileTransferController.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 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.libraries.rcs.simpleclient.filetransfer;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+/** File transfer functionality. */
+public interface FileTransferController {
+
+    /**
+     * Downloads a file from the content server.
+     *
+     * @param fileUrl http URL to the file content on the server.
+     * @return the response for the file download.
+     */
+    ListenableFuture<InputStream> downloadFile(String fileUrl);
+
+    /**
+     * Uploads a file to the content server.
+     *
+     * @param transactionId the transaction id of the file upload.
+     * @param file          the file to be uploaded.
+     * @return the XML response for the file upload, as defined in RCC.07.0-v19.0. This can then be
+     * parsed by the FileInfoParse to get the URL to be used for the download.
+     */
+    ListenableFuture<String> uploadFile(
+            String transactionId, File file)
+            throws IOException;
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/FileTransferControllerImpl.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/FileTransferControllerImpl.java
new file mode 100644
index 0000000..dde340c
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/FileTransferControllerImpl.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 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.libraries.rcs.simpleclient.filetransfer;
+
+import com.android.libraries.rcs.simpleclient.filetransfer.requestexecutor.HttpRequestExecutor;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+/** FileTransferController implementation. */
+public class FileTransferControllerImpl implements FileTransferController {
+
+    private final FileUploadController fileUploadController;
+
+    public FileTransferControllerImpl(HttpRequestExecutor requestExecutor,
+            String contentServerUri, String carrierName) {
+        this.fileUploadController = new FileUploadController(requestExecutor, contentServerUri,
+                carrierName);
+    }
+
+    @Override
+    public ListenableFuture<InputStream> downloadFile(String fileUrl) {
+        throw new UnsupportedOperationException("File download not supported");
+    }
+
+    @Override
+    public ListenableFuture<String> uploadFile(
+            String transactionId, File file)
+            throws IOException {
+        return fileUploadController.uploadFile(transactionId, file);
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/FileUploadController.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/FileUploadController.java
new file mode 100644
index 0000000..0d45828
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/FileUploadController.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2021 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.libraries.rcs.simpleclient.filetransfer;
+
+import android.net.Uri;
+import android.os.Build;
+import android.util.Log;
+
+import com.android.internal.http.multipart.FilePart;
+import com.android.internal.http.multipart.MultipartEntity;
+import com.android.internal.http.multipart.Part;
+import com.android.internal.http.multipart.StringPart;
+import com.android.libraries.rcs.simpleclient.filetransfer.requestexecutor.HttpRequestExecutor;
+
+import com.google.common.io.ByteStreams;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.params.AuthPolicy;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.impl.auth.DigestScheme;
+import org.apache.http.impl.auth.RFC2617Scheme;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.concurrent.Executors;
+
+/** File upload functionality. */
+final class FileUploadController {
+
+    private static final String TAG = "FileUploadController";
+    private static final String ATTRIBUTE_PREEMPTIVE_AUTH = "preemptive-auth";
+    private static final String PARAM_NONCE = "nonce";
+    private static final String PARAM_REALM = "realm";
+    private static final String FILE_PART_NAME = "File";
+    private static final String TRANSFER_ID_PART_NAME = "tid";
+    private static final String CONTENT_TYPE = "text/plain";
+    private static final String THREE_GPP_GBA = "3gpp-gba";
+    private static final int HTTPS_PORT = 443;
+
+    private final HttpRequestExecutor requestExecutor;
+    private final String contentServerUri;
+    private final ListeningExecutorService executor =
+            MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(4));
+    private String mCarrierName;
+
+    FileUploadController(HttpRequestExecutor requestExecutor, String contentServerUri,
+            String carrierName) {
+        this.requestExecutor = requestExecutor;
+        this.contentServerUri = contentServerUri;
+        this.mCarrierName = carrierName;
+    }
+
+    public ListenableFuture<String> uploadFile(
+            String transactionId, File file) {
+        DefaultHttpClient httpClient = getSecureHttpClient();
+
+        Log.i(TAG, "sendEmptyPost");
+        // Send an empty post.
+        ListenableFuture<HttpResponse> initialResponseFuture = sendEmptyPost(httpClient);
+
+        BasicHttpContext httpContext = new BasicHttpContext();
+        ListenableFuture<AuthScheme> prepareAuthFuture =
+                Futures.transform(
+                        initialResponseFuture,
+                        initialResponse -> {
+                            Log.i(TAG, "Response for the empty post: "
+                                    + initialResponse.getStatusLine());
+                            if (initialResponse.getStatusLine().getStatusCode()
+                                    != HttpURLConnection.HTTP_UNAUTHORIZED) {
+                                throw new IllegalArgumentException(
+                                        "Expected HTTP_UNAUTHORIZED, but got "
+                                                + initialResponse.getStatusLine());
+                            }
+                            try {
+                                initialResponse.getEntity().consumeContent();
+                            } catch (IOException e) {
+                                throw new IllegalArgumentException(e);
+                            }
+
+                            // Override nonce and realm in the HTTP context.
+                            RFC2617Scheme authScheme = createAuthScheme(initialResponse);
+                            httpContext.setAttribute(ATTRIBUTE_PREEMPTIVE_AUTH, authScheme);
+                            return authScheme;
+                        },
+                        executor);
+
+        // Executing the post with credentials.
+        return Futures.transformAsync(
+                prepareAuthFuture,
+                authScheme ->
+                        executeAuthenticatedPost(
+                                httpClient, httpContext, authScheme, transactionId, file),
+                executor);
+    }
+
+    private RFC2617Scheme createAuthScheme(HttpResponse initialResponse) {
+        if (!initialResponse.containsHeader(AUTH.WWW_AUTH)) {
+            throw new IllegalArgumentException(
+                    AUTH.WWW_AUTH + " header not found in the original response.");
+        }
+
+        Header authHeader = initialResponse.getFirstHeader(AUTH.WWW_AUTH);
+        String scheme = authHeader.getValue();
+
+        if (scheme.contains(AuthPolicy.DIGEST)) {
+            DigestScheme digestScheme = new DigestScheme();
+            try {
+                digestScheme.processChallenge(authHeader);
+            } catch (MalformedChallengeException e) {
+                throw new IllegalArgumentException(e);
+            }
+            return digestScheme;
+        } else {
+            throw new IllegalArgumentException("Unable to create authentication scheme " + scheme);
+        }
+    }
+
+    private DefaultHttpClient getSecureHttpClient() {
+        SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory();
+        Uri uri = Uri.parse(contentServerUri);
+        int port = uri.getPort();
+        if (port <= 0) {
+            port = HTTPS_PORT;
+        }
+
+        Scheme scheme = new Scheme("https", socketFactory, port);
+        DefaultHttpClient httpClient = new DefaultHttpClient();
+        ClientConnectionManager manager = httpClient.getConnectionManager();
+        SchemeRegistry registry = manager.getSchemeRegistry();
+        registry.register(scheme);
+
+        return httpClient;
+    }
+
+    private ListenableFuture<HttpResponse> sendEmptyPost(HttpClient httpClient) {
+        Log.i(TAG, "Sending an empty post: ");
+        HttpPost emptyPost = new HttpPost(contentServerUri);
+        emptyPost.setHeader("User-Agent", getUserAgent());
+        return executor.submit(() -> httpClient.execute(emptyPost));
+    }
+
+    private ListenableFuture<String> executeAuthenticatedPost(
+            DefaultHttpClient httpClient,
+            HttpContext context,
+            AuthScheme authScheme,
+            String transactionId,
+            File file)
+            throws IOException {
+
+        Part[] parts = {
+                new StringPart(TRANSFER_ID_PART_NAME, transactionId),
+                new FilePart(FILE_PART_NAME, file)
+        };
+        MultipartEntity entity = new MultipartEntity(parts);
+
+        HttpPost postRequest = new HttpPost(contentServerUri);
+        postRequest.setHeader("User-Agent", getUserAgent());
+        postRequest.setEntity(entity);
+        Log.i(TAG, "Created file upload POST:" + contentServerUri);
+
+        ListenableFuture<HttpResponse> responseFuture =
+                requestExecutor.executeAuthenticatedRequest(httpClient, context, postRequest,
+                        authScheme);
+
+        Futures.addCallback(
+                responseFuture,
+                new FutureCallback<HttpResponse>() {
+                    @Override
+                    public void onSuccess(HttpResponse response) {
+                        Log.i(TAG, "onSuccess:" + response.toString());
+                        Log.i(TAG, "statusLine:" + response.getStatusLine());
+                        Log.i(TAG, "statusCode:" + response.getStatusLine().getStatusCode());
+                        Log.i(TAG, "contentLentgh:" + response.getEntity().getContentLength());
+                        Log.i(TAG, "contentType:" + response.getEntity().getContentType());
+                    }
+
+                    @Override
+                    public void onFailure(Throwable t) {
+                        Log.e(TAG, "onFailure", t);
+                        throw new IllegalArgumentException(t);
+                    }
+                },
+                executor);
+
+        return Futures.transform(
+                responseFuture,
+                response -> {
+                    try {
+                        return consumeResponse(response);
+                    } catch (IOException e) {
+                        throw new IllegalArgumentException(e);
+                    }
+                },
+                executor);
+    }
+
+    public String consumeResponse(HttpResponse response) throws IOException {
+        int statusCode = response.getStatusLine().getStatusCode();
+        if (statusCode != HttpURLConnection.HTTP_OK) {
+            throw new IllegalArgumentException(
+                    "Server responded with error code " + statusCode + "!");
+        }
+        HttpEntity responseEntity = response.getEntity();
+
+        if (responseEntity == null) {
+            throw new IOException("Did not receive a response body.");
+        }
+
+        return readResponseData(responseEntity.getContent());
+    }
+
+    public String readResponseData(InputStream inputStream) throws IOException {
+        Log.i(TAG, "readResponseData");
+        ByteArrayOutputStream data = new ByteArrayOutputStream();
+        ByteStreams.copy(inputStream, data);
+
+        data.flush();
+        Log.i(TAG, "Parsed HTTP POST response: " + data.toString());
+
+        return data.toString();
+    }
+
+    private String getUserAgent() {
+        String buildId = Build.ID;
+        String buildDate = DateTimeFormatter.ofPattern("yyyy-MM-dd")
+                .withZone(ZoneId.systemDefault())
+                .format(Instant.ofEpochMilli(Build.TIME));
+        String buildVersion = Build.VERSION.RELEASE_OR_CODENAME;
+        String deviceName = Build.DEVICE;
+        String userAgent = String.format("%s %s %s %s %s %s %s",
+                mCarrierName, buildId, buildDate, "Android", buildVersion,
+                deviceName, THREE_GPP_GBA);
+        Log.i(TAG, "UserAgent:" + userAgent);
+        return userAgent;
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java
new file mode 100644
index 0000000..008fb76
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaAuthenticationProvider.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2021 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.libraries.rcs.simpleclient.filetransfer.requestexecutor;
+
+import android.net.Uri;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
+import android.telephony.gba.TlsParams;
+import android.telephony.gba.UaSecurityProtocolIdentifier;
+import android.util.Log;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.io.BaseEncoding;
+import com.google.common.util.concurrent.SettableFuture;
+
+import org.apache.http.auth.Credentials;
+
+import java.security.Principal;
+import java.util.concurrent.Executor;
+
+/** Provides GBA authentication credentials. */
+public class GbaAuthenticationProvider {
+
+    private static final String TAG = "GbaAuthenticationProvider";
+    private final TelephonyManager telephonyManager;
+    private final String contentServerUrl;
+    private final Executor executor;
+
+    public GbaAuthenticationProvider(
+            TelephonyManager telephonyManager, String contentServerUrl, Executor executor) {
+        this.telephonyManager = telephonyManager;
+        this.contentServerUrl = contentServerUrl;
+        this.executor = executor;
+    }
+
+    public SettableFuture<Credentials> provideCredentials(boolean forceBootstrapping) {
+        SettableFuture<Credentials> credentialsFuture = SettableFuture.create();
+
+        UaSecurityProtocolIdentifier.Builder builder =
+                new UaSecurityProtocolIdentifier.Builder();
+        try {
+            PersistableBundle carrierConfig = telephonyManager.getCarrierConfig();
+            int organization = carrierConfig.getInt(
+                    CarrierConfigManager.KEY_GBA_UA_SECURITY_ORGANIZATION_INT);
+            int protocol = carrierConfig.getInt(
+                    CarrierConfigManager.KEY_GBA_UA_SECURITY_PROTOCOL_INT);
+            int cipherSuite = carrierConfig.getInt(
+                    CarrierConfigManager.KEY_GBA_UA_TLS_CIPHER_SUITE_INT);
+            Log.i(TAG, "organization:" + organization + ", protocol:" + protocol + ", cipherSuite:"
+                    + cipherSuite + ", contentServerUrl:" + contentServerUrl);
+
+            builder.setOrg(organization)
+                    .setProtocol(protocol);
+            if (cipherSuite == TlsParams.TLS_NULL_WITH_NULL_NULL) {
+                builder.setTlsCipherSuite(TlsParams.TLS_RSA_WITH_AES_128_CBC_SHA);
+            } else {
+                builder.setTlsCipherSuite(cipherSuite);
+            }
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, e.getMessage());
+            credentialsFuture.setException(e);
+            return credentialsFuture;
+        }
+        UaSecurityProtocolIdentifier spId = builder.build();
+        TelephonyManager.BootstrapAuthenticationCallback callback =
+                new TelephonyManager.BootstrapAuthenticationCallback() {
+                    @Override
+                    public void onKeysAvailable(byte[] gbaKey, String btId) {
+                        Log.i(TAG, "onKeysAvailable: String key:[" + new String(gbaKey) + "] btid:["
+                                + btId + "]" + "Base64 key:[" + BaseEncoding.base64().encode(gbaKey)
+                                + "]");
+                        credentialsFuture.set(GbaCredentials.create(btId, gbaKey));
+                    }
+
+                    @Override
+                    public void onAuthenticationFailure(int reason) {
+                        Log.i(TAG, "onAuthenticationFailure:" + reason);
+                        credentialsFuture.setException(
+                                new BootstrapAuthenticationException(reason));
+                    }
+                };
+        telephonyManager.bootstrapAuthenticationRequest(
+                TelephonyManager.APPTYPE_ISIM,
+                Uri.parse(contentServerUrl),
+                spId,
+                forceBootstrapping,
+                executor,
+                callback);
+
+        return credentialsFuture;
+    }
+
+    @SuppressWarnings("AndroidJdkLibsChecker")
+    @AutoValue
+    abstract static class GbaCredentials implements Credentials {
+
+        public static GbaCredentials create(String btId, byte[] gbaKey) {
+            return new AutoValue_GbaAuthenticationProvider_GbaCredentials(
+                    GbaPrincipal.create(btId), BaseEncoding.base64().encode(gbaKey));
+        }
+
+        @Override
+        public abstract Principal getUserPrincipal();
+
+        @Override
+        public abstract String getPassword();
+    }
+
+    @AutoValue
+    abstract static class GbaPrincipal implements Principal {
+
+        public static GbaPrincipal create(String name) {
+            return new AutoValue_GbaAuthenticationProvider_GbaPrincipal(name);
+        }
+
+        @Override
+        public abstract String getName();
+    }
+
+    static class BootstrapAuthenticationException extends Exception {
+        BootstrapAuthenticationException(int reason) {
+            super("Bootstrap authentication request failure: " + reason);
+        }
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaRequestExecutor.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaRequestExecutor.java
new file mode 100644
index 0000000..83d5a8a
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/GbaRequestExecutor.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2021 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.libraries.rcs.simpleclient.filetransfer.requestexecutor;
+
+import android.util.Log;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.AuthState;
+import org.apache.http.auth.Credentials;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.protocol.HttpContext;
+
+import java.net.HttpURLConnection;
+import java.util.concurrent.Executors;
+
+/** Executes GBA authenticated HTTP requests. */
+public class GbaRequestExecutor implements HttpRequestExecutor {
+
+    private static final String TAG = "GbaRequestExecutor";
+    private final GbaAuthenticationProvider gbaAuthenticationProvider;
+    private final ListeningExecutorService executor =
+            MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(4));
+
+    public GbaRequestExecutor(GbaAuthenticationProvider gbaAuthenticationProvider) {
+        this.gbaAuthenticationProvider = gbaAuthenticationProvider;
+    }
+
+    @Override
+    @SuppressWarnings("CheckReturnValue")
+    public ListenableFuture<HttpResponse> executeAuthenticatedRequest(
+            DefaultHttpClient httpClient, HttpContext context, HttpRequestBase request,
+            AuthScheme authScheme) {
+
+        // Set authentication for the client.
+        ListenableFuture<Credentials> credentialsFuture =
+                gbaAuthenticationProvider.provideCredentials(/*forceBootrapping*/ false);
+
+        ListenableFuture<HttpResponse> responseFuture =
+                Futures.transformAsync(
+                        credentialsFuture,
+                        credentials -> {
+                            Log.i(TAG,
+                                    "Obtained credentialsFuture, making the POST with credentials"
+                                            + ".");
+                            httpClient.addRequestInterceptor((req, ctx) -> {
+                                AuthState authState = (AuthState) context.getAttribute(
+                                        ClientContext.TARGET_AUTH_STATE);
+                                authState.setAuthScope(AuthScope.ANY);
+                                authState.setAuthScheme(authScheme);
+                                authState.setCredentials(credentials);
+                            }, /* index= */ 0);
+
+                            // Make the first request.
+                            return executor.submit(() -> httpClient.execute(request, context));
+                        },
+                        executor);
+
+        return Futures.transformAsync(
+                responseFuture,
+                response -> {
+
+                    // If the response code is 401, the keys might be invalid so force boostrapping.
+                    if (response.getStatusLine().getStatusCode()
+                            != HttpURLConnection.HTTP_UNAUTHORIZED) {
+                        return Futures.immediateFuture(response);
+                    }
+                    Log.i(TAG, "Obtained 401 for the authneticated request. Forcing boostrapping.");
+
+                    ListenableFuture<Credentials> forceBootstrappedCredentialsFuture =
+                            gbaAuthenticationProvider.provideCredentials(/*forceBoostrapping*/
+                                    true);
+
+                    return Futures.transformAsync(
+                            forceBootstrappedCredentialsFuture,
+                            forceBootstrappedCredentials -> {
+                                httpClient
+                                        .getCredentialsProvider()
+                                        .setCredentials(AuthScope.ANY,
+                                                forceBootstrappedCredentials);
+
+                                // Make a second request.
+                                Log.i(TAG,
+                                        "Obtained new credentialsFuture, making POST with the new"
+                                                + " credentials.");
+                                return Futures.submit(() -> httpClient.execute(request, context),
+                                        executor);
+                            },
+                            executor);
+                },
+                executor);
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/HttpRequestExecutor.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/HttpRequestExecutor.java
new file mode 100644
index 0000000..0026790
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/filetransfer/requestexecutor/HttpRequestExecutor.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 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.libraries.rcs.simpleclient.filetransfer.requestexecutor;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.protocol.HttpContext;
+
+import java.io.IOException;
+
+/** Executes authenticated HTTP requests. */
+public interface HttpRequestExecutor {
+
+    ListenableFuture<HttpResponse> executeAuthenticatedRequest(
+            DefaultHttpClient httpClient, HttpContext context, HttpRequestBase request,
+            AuthScheme authScheme)
+            throws IOException;
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/cpim/CpimUtils.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/cpim/CpimUtils.java
new file mode 100644
index 0000000..6bb8eec
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/cpim/CpimUtils.java
@@ -0,0 +1,51 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.cpim;
+
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Random;
+
+/** Collections of utility functions for CPIM */
+public class CpimUtils {
+    private static final String ANONYMOUS_URI = "<sip:anonymous@anonymous.invalid>";
+    public static final String CPIM_CONTENT_TYPE = "message/cpim";
+
+    private CpimUtils() {
+    }
+
+    @SuppressWarnings("AndroidJdkLibsChecker")
+    public static SimpleCpimMessage createForText(String text) {
+        return SimpleCpimMessage.newBuilder()
+                .addNamespace("imdn", "urn:ietf:params:imdn")
+                .addHeader("imdn.Message-ID", generateImdnMessageId())
+                .addHeader("imdn.Disposition-Notification", "positive-delivery, display")
+                .addHeader("To", ANONYMOUS_URI)
+                .addHeader("From", ANONYMOUS_URI)
+                .addHeader("DateTime", ZonedDateTime.now(ZoneId.systemDefault()).format(
+                        DateTimeFormatter.ISO_INSTANT))
+                .setContentType("text/plain")
+                .setContent(text)
+                .build();
+    }
+
+    private static String generateImdnMessageId() {
+        Random random = new Random();
+        return "Test_" + random.nextInt(1000000);
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/cpim/SimpleCpimMessage.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/cpim/SimpleCpimMessage.java
new file mode 100644
index 0000000..e2efafe
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/cpim/SimpleCpimMessage.java
@@ -0,0 +1,141 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.cpim;
+
+import android.text.TextUtils;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Ascii;
+import com.google.common.base.Utf8;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.CharStreams;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * The CPIM implementation as per RFC 3862. This class supports minimal fields that is required to
+ * represent a simple message for test purpose.
+ */
+@AutoValue
+public abstract class SimpleCpimMessage {
+    private static final String CRLF = "\r\n";
+    private static final String COLSP = ": ";
+    private static final Pattern NAMESPACE_HEADER_PATTERN =
+            Pattern.compile("NS:\\s+(\\S+)\\s+<(.+)>");
+    private static final Pattern HEADER_PATTERN = Pattern.compile("([^\\s:]+):\\s+(.+)");
+
+    public abstract ImmutableMap<String, String> namespaces();
+
+    public abstract ImmutableMap<String, String> headers();
+
+    public abstract String contentType();
+
+    public abstract String content();
+
+    public String encode() {
+        StringBuilder builder = new StringBuilder();
+        for (Map.Entry<String, String> entry : namespaces().entrySet()) {
+            builder
+                    .append("NS: ")
+                    .append(entry.getKey())
+                    .append(" <")
+                    .append(entry.getValue())
+                    .append(">")
+                    .append(CRLF);
+        }
+
+        for (Map.Entry<String, String> entry : headers().entrySet()) {
+            builder.append(entry.getKey()).append(COLSP).append(entry.getValue()).append(CRLF);
+        }
+
+        builder.append(CRLF);
+        builder.append("Content-Type").append(COLSP).append(contentType());
+        builder.append(CRLF);
+        builder.append("Content-Length").append(COLSP).append(Utf8.encodedLength(content()));
+        builder.append(CRLF).append(CRLF);
+        builder.append(content());
+
+        return builder.toString();
+    }
+
+    public static SimpleCpimMessage parse(byte[] content) throws IOException {
+        BufferedReader reader =
+                new BufferedReader(new InputStreamReader(new ByteArrayInputStream(content)));
+        Builder builder = newBuilder();
+
+        String line = reader.readLine();
+        while (!TextUtils.isEmpty(line)) {
+            Matcher namespaceMatcher = NAMESPACE_HEADER_PATTERN.matcher(line);
+            Matcher headerMatcher = HEADER_PATTERN.matcher(line);
+            if (namespaceMatcher.matches()) {
+                builder.addNamespace(namespaceMatcher.group(1), namespaceMatcher.group(2));
+            } else if (headerMatcher.matches()) {
+                builder.addHeader(headerMatcher.group(1), headerMatcher.group(2));
+            }
+
+            line = reader.readLine();
+        }
+
+        line = reader.readLine();
+        while (!TextUtils.isEmpty(line)) {
+            Matcher headerMatcher = HEADER_PATTERN.matcher(line);
+            if (headerMatcher.matches()) {
+                if (Ascii.equalsIgnoreCase("content-type", headerMatcher.group(1))) {
+                    builder.setContentType(headerMatcher.group(2));
+                }
+            }
+
+            line = reader.readLine();
+        }
+
+        String body = CharStreams.toString(reader);
+        builder.setContent(body);
+
+        return builder.build();
+    }
+
+    @AutoValue.Builder
+    public abstract static class Builder {
+        public abstract ImmutableMap.Builder<String, String> namespacesBuilder();
+
+        public abstract ImmutableMap.Builder<String, String> headersBuilder();
+
+        public abstract Builder setContentType(String value);
+
+        public abstract Builder setContent(String value);
+
+        public abstract SimpleCpimMessage build();
+
+        public Builder addNamespace(String name, String value) {
+            namespacesBuilder().put(name, value);
+            return this;
+        }
+
+        public Builder addHeader(String name, String value) {
+            headersBuilder().put(name, value);
+            return this;
+        }
+    }
+
+    public static SimpleCpimMessage.Builder newBuilder() {
+        return new AutoValue_SimpleCpimMessage.Builder();
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/ImsPdnNetworkFetcher.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/ImsPdnNetworkFetcher.java
new file mode 100644
index 0000000..57bb75a
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/ImsPdnNetworkFetcher.java
@@ -0,0 +1,88 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.msrp;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+
+import androidx.annotation.RequiresPermission;
+
+import com.google.common.util.concurrent.FluentFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.SettableFuture;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/** A really incomplete implementation for fetching networks from ConnectivityManager. */
+public final class ImsPdnNetworkFetcher {
+
+    private final Context context;
+
+    public ImsPdnNetworkFetcher(Context context) {
+        this.context = context;
+    }
+
+    private static NetworkRequest createNetworkRequest() {
+        NetworkRequest.Builder builder =
+                new NetworkRequest.Builder().addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
+        return builder.build();
+    }
+
+    ListenableFuture<Network> getImsPdnNetwork() {
+        return requestNetwork();
+    }
+
+    ListenableFuture<List<String>> getImsPdnIpAddresses() {
+        return FluentFuture.from(getImsPdnNetwork())
+                .transform(this::getNetworkIpAddresses, MoreExecutors.directExecutor());
+    }
+
+    @RequiresPermission("android.permission.ACCESS_NETWORK_STATE")
+    List<String> getNetworkIpAddresses(Network network) {
+        return getConnectivityManager().getLinkProperties(network).getLinkAddresses().stream()
+                .map(link -> link.getAddress().getHostAddress())
+                .collect(Collectors.toList());
+    }
+
+    private ListenableFuture<Network> requestNetwork() {
+        SettableFuture<Network> result = SettableFuture.create();
+        ConnectivityManager cm = getConnectivityManager();
+
+        ConnectivityManager.NetworkCallback cb =
+                new ConnectivityManager.NetworkCallback() {
+                    @Override
+                    public void onAvailable(Network network) {
+                        if (!result.isDone() && !result.isCancelled()) {
+                            result.set(network);
+                        }
+                        cm.unregisterNetworkCallback(this);
+                    }
+                };
+
+        cm.requestNetwork(createNetworkRequest(), cb);
+        return result;
+    }
+
+    ConnectivityManager getConnectivityManager() {
+        return context.getSystemService(ConnectivityManager.class);
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpChunk.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpChunk.java
new file mode 100644
index 0000000..0d9e62f
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpChunk.java
@@ -0,0 +1,154 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.msrp;
+
+import static com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpConstants.FLAG_ABORT_CHUNK;
+import static com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpConstants.FLAG_LAST_CHUNK;
+import static com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpConstants.FLAG_MORE_CHUNK;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Single MSRP chunk containing a request or a response.
+ */
+@AutoValue
+public abstract class MsrpChunk {
+
+    public static Builder newBuilder() {
+        return new AutoValue_MsrpChunk.Builder()
+                .method(Method.UNKNOWN)
+                .responseCode(0)
+                .responseReason("")
+                .content(new byte[0])
+                .continuation(Continuation.UNKNOWN);
+    }
+
+    public abstract Method method();
+
+    public abstract String transactionId();
+
+    public abstract Continuation continuation();
+
+    public abstract int responseCode();
+
+    public abstract String responseReason();
+
+    public abstract ImmutableList<MsrpChunkHeader> headers();
+
+    public abstract byte[] content();
+
+    public MsrpChunkHeader header(String headerName) {
+        for (MsrpChunkHeader header : headers()) {
+            if (header.name().equals(headerName)) {
+                return header;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Methods for requests
+     */
+    public enum Method {
+        UNKNOWN,
+        SEND,
+        REPORT,
+    }
+
+
+    /**
+     * Continuation flag for the chunk
+     */
+    public enum Continuation {
+        UNKNOWN(0),
+        COMPLETE(FLAG_LAST_CHUNK),
+        MORE(FLAG_MORE_CHUNK),
+        ABORTED(FLAG_ABORT_CHUNK);
+
+        private final int value;
+
+        Continuation(int value) {
+            this.value = value;
+        }
+
+        public static Continuation valueOf(int read) {
+            if (read == COMPLETE.value) {
+                return COMPLETE;
+            }
+            if (read == MORE.value) {
+                return MORE;
+            }
+            if (read == ABORTED.value) {
+                return ABORTED;
+            }
+            return UNKNOWN;
+        }
+
+        public byte toByte() {
+            return (byte) value;
+        }
+    }
+
+    /**
+     * Builder for new MSRP chunk.
+     */
+    @AutoValue.Builder
+    public abstract static class Builder {
+
+        public abstract Builder method(Method method);
+
+        public abstract Builder transactionId(String id);
+
+        public abstract String transactionId();
+
+        public abstract Continuation continuation();
+
+        public abstract Builder continuation(Continuation continuation);
+
+        public abstract Builder responseCode(int continuation);
+
+        public abstract Builder responseReason(String reason);
+
+        public abstract Builder content(byte[] content);
+
+        public Builder addHeader(MsrpChunkHeader header) {
+            headersBuilder().add(header);
+            return this;
+        }
+
+        public Builder addHeader(String name, String value) {
+            headersBuilder().add(MsrpChunkHeader.newBuilder().name(name).value(value).build());
+            return this;
+        }
+
+        abstract ImmutableList.Builder<MsrpChunkHeader> headersBuilder();
+
+        MsrpChunkHeader header(String name) {
+            for (MsrpChunkHeader header : headersBuilder().build()) {
+                if (header.name().equals(name)) {
+                    return header;
+                }
+            }
+            return null;
+        }
+
+        public abstract MsrpChunk build();
+
+
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpChunkHeader.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpChunkHeader.java
new file mode 100644
index 0000000..fad17e0
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpChunkHeader.java
@@ -0,0 +1,47 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.msrp;
+
+import com.google.auto.value.AutoValue;
+
+/**
+ * Single header in MSRP chunk (From-Path, To-Path, ...)
+ */
+@AutoValue
+public abstract class MsrpChunkHeader {
+
+    public static Builder newBuilder() {
+        return new AutoValue_MsrpChunkHeader.Builder();
+    }
+
+    public abstract String name();
+
+    public abstract String value();
+
+    /**
+     * Builder for  new MSRP header.
+     */
+    @AutoValue.Builder
+    public static abstract class Builder {
+
+        public abstract Builder name(String name);
+
+        public abstract Builder value(String value);
+
+        public abstract MsrpChunkHeader build();
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpConstants.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpConstants.java
new file mode 100644
index 0000000..ba424c6
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpConstants.java
@@ -0,0 +1,55 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.msrp;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * Several constants used for MSRP parsing and serializing.
+ */
+public class MsrpConstants {
+    public static final byte[] HEADER_DELIMITER_BYTES = ": ".getBytes();
+    public static final String MSRP_PROTOCOL = "MSRP";
+    public static final byte[] MSRP_PROTOCOL_BYTES = MSRP_PROTOCOL.getBytes(UTF_8);
+    public static final String NEW_LINE = "\r\n";
+    public static final byte[] NEW_LINE_BYTES = NEW_LINE.getBytes(UTF_8);
+    public static final String END_MSRP_MSG = "-------";
+    public static final byte[] END_MSRP_MSG_BYTES = END_MSRP_MSG.getBytes(UTF_8);
+    public static final String NEW_LINE_END_MSRP_MSG = NEW_LINE + END_MSRP_MSG;
+    public static final int END_MSRP_MSG_LENGTH = END_MSRP_MSG.length();
+    public static final int FLAG_LAST_CHUNK = '$';
+    public static final int FLAG_MORE_CHUNK = '+';
+    public static final int FLAG_ABORT_CHUNK = '#';
+    public static final byte CHAR_SP = ' ';
+    public static final byte CHAR_LF = '\r';
+    public static final byte CHAR_MIN = '-';
+    public static final byte CHAR_DOUBLE_POINT = ':';
+    public static final String HEADER_BYTE_RANGE = "Byte-Range";
+    public static final String HEADER_CONTENT_TYPE = "Content-Type";
+    public static final String HEADER_MESSAGE_ID = "Message-ID";
+    public static final String HEADER_TO_PATH = "To-Path";
+    public static final String HEADER_FROM_PATH = "From-Path";
+    public static final String HEADER_FAILURE_REPORT = "Failure-Report";
+    public static final String HEADER_SUCCESS_REPORT = "Success-Report";
+    public static final String REPORT_VALUE_YES = "yes";
+    public static final String REPORT_VALUE_NO = "no";
+
+    public static final int RESPONSE_CODE_OK = 200;
+
+    private MsrpConstants() {
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpManager.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpManager.java
new file mode 100644
index 0000000..58a6eef
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpManager.java
@@ -0,0 +1,69 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.msrp;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.Network;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+
+/** Provides creating and managing {@link MsrpSession} */
+public class MsrpManager {
+    private final ImsPdnNetworkFetcher imsPdnNetworkFetcher;
+    private Context context;
+
+    public MsrpManager(Context context) {
+        this.context = context;
+        imsPdnNetworkFetcher = new ImsPdnNetworkFetcher(context);
+    }
+
+    private MsrpSession createMsrpSession(ConnectivityManager manager,
+            Network network, String host, int port, String localIp, int localPort,
+            MsrpSessionListener listener) throws IOException {
+        Socket socket = network.getSocketFactory().createSocket(host, port,
+                InetAddress.getByName(localIp), localPort);
+        MsrpSession msrpSession = new MsrpSession(manager, context,
+                network, socket, listener);
+        Thread thread = new Thread(msrpSession::run);
+        thread.start();
+        return msrpSession;
+    }
+
+    public ListenableFuture<MsrpSession> createMsrpSession(
+            String host, int port, String localIp, int localPort, MsrpSessionListener listener) {
+        return Futures.transformAsync(
+                imsPdnNetworkFetcher.getImsPdnNetwork(),
+                network -> {
+                    if (network != null) {
+                        return Futures.immediateFuture(
+                                createMsrpSession(imsPdnNetworkFetcher.getConnectivityManager(),
+                                        network, host, port, localIp, localPort, listener));
+                    } else {
+                        return Futures.immediateFailedFuture(
+                                new IllegalStateException("Network is null"));
+                    }
+                },
+                MoreExecutors.directExecutor());
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpParser.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpParser.java
new file mode 100644
index 0000000..3376544
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpParser.java
@@ -0,0 +1,355 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.msrp;
+
+import static com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpConstants.CHAR_DOUBLE_POINT;
+import static com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpConstants.CHAR_LF;
+import static com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpConstants.CHAR_MIN;
+import static com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpConstants.CHAR_SP;
+import static com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpConstants.END_MSRP_MSG_LENGTH;
+import static com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpConstants.HEADER_BYTE_RANGE;
+import static com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpConstants.NEW_LINE_END_MSRP_MSG;
+
+import com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpChunk.Continuation;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+/**
+ * Simple parser for reading MSRP messages from a stream.
+ */
+public final class MsrpParser {
+
+    private MsrpParser() {
+    }
+
+    public static MsrpChunk parse(final InputStream stream) throws IOException {
+        MsrpChunk.Builder transaction = MsrpChunk.newBuilder();
+
+        // Read a chunk (blocking method)
+        int i = stream.read();
+
+        final StringBuilder value = new StringBuilder();
+        // Read MSRP tag
+        skipWithDelimiter(stream, CHAR_SP);
+
+        if (i == -1) {
+            // End of stream
+            return null;
+        }
+
+        // Read the transaction ID
+        do {
+            i = stream.read();
+            if (i != CHAR_SP) {
+                value.append((char) i);
+            }
+        } while ((i != CHAR_SP) && (i != -1));
+
+        if (i == -1) {
+            return null;
+        }
+
+        final String txId = value.toString();
+        value.setLength(0);
+
+        // Read response code or method name
+        MsrpChunk.Method method = MsrpChunk.Method.UNKNOWN;
+        int responseCode = -1;
+        for (i = stream.read(); (i != CHAR_LF) && (i != -1); i = stream.read()) {
+            if (i == CHAR_SP && responseCode == -1) {
+                // There is a space -> it's a response
+                try {
+                    responseCode = Integer.parseInt(value.toString());
+                } catch (NumberFormatException nfe) {
+                    // This is an invalid response.
+                    return null;
+                }
+                value.setLength(0);
+                continue;
+            }
+            value.append((char) i);
+        }
+
+        if (responseCode == -1) {
+            try {
+                responseCode = Integer.parseInt(value.toString());
+                value.setLength(0);
+            } catch (final NumberFormatException e) {
+                method = MsrpChunk.Method.valueOf(value.toString());
+            }
+        }
+
+        i = stream.read();
+
+        if (i == -1) {
+            // End of stream
+            return null;
+        }
+
+        final boolean isResponse = responseCode > -1;
+        if (isResponse) {
+            transaction.transactionId(txId).responseCode(responseCode).responseReason(
+                    value.toString());
+        } else {
+            transaction.transactionId(txId).method(method);
+        }
+
+        value.setLength(0);
+
+        // Read MSRP headers
+        readHeaders(stream, transaction, value);
+
+        // We already received end of message
+        if (transaction.continuation() != Continuation.UNKNOWN) {
+            return transaction.build();
+        }
+
+        i = stream.read();
+        if (i == -1) {
+            // End of stream
+            return null;
+        }
+
+        // Process MSRP request
+        if (method == MsrpChunk.Method.SEND) {
+            readChunk(stream, transaction);
+        }
+
+        return transaction.build();
+    }
+
+    private static void readHeaders(
+            final InputStream stream, final MsrpChunk.Builder transaction,
+            final StringBuilder value)
+            throws IOException {
+        for (int i = stream.read(); (i != CHAR_LF) && (i != -1); ) {
+
+            for (; (i != CHAR_DOUBLE_POINT) && (i != -1); i = stream.read()) {
+                value.append((char) i);
+            }
+
+            final String headerName = value.toString();
+            value.setLength(0);
+
+            stream.read(); // skip space
+
+            for (i = stream.read(); (i != CHAR_LF) && (i != -1); i = stream.read()) {
+                value.append((char) i);
+            }
+
+            final String headerValue = value.toString();
+            value.setLength(0);
+
+            transaction.addHeader(headerName, headerValue);
+
+            stream.read();
+
+            // It's the end of the header part
+            i = stream.read();
+            if (i == CHAR_MIN) {
+                final int length = END_MSRP_MSG_LENGTH - 1 + transaction.transactionId().length();
+                stream.skip(length);
+                transaction.continuation(Continuation.valueOf(stream.read()));
+
+                // For response
+                for (; (i != CHAR_LF) && (i != -1); i = stream.read()) {
+                }
+                break;
+            }
+        }
+    }
+
+    private static void readChunk(final InputStream stream, final MsrpChunk.Builder chunk)
+            throws IOException {
+        final String byteRange = chunk.header(HEADER_BYTE_RANGE).value();
+
+        if (byteRange == null) {
+            throw new IllegalStateException("expected non-null byteRange");
+        }
+        final int chunkSize = getChunkSize(byteRange);
+        final long totalSize = getTotalSize(byteRange);
+
+        if (totalSize == Integer.MIN_VALUE || chunkSize < -1) {
+            throw new IOException("Invalid byte range: " + byteRange);
+        }
+
+        if (chunkSize == -1) {
+            readUnknownChunk(stream, chunk);
+        } else {
+            readKnownChunk(stream, chunk, chunkSize);
+            skipEndLine(stream, chunk);
+        }
+
+        readContinuationFlag(stream, chunk);
+    }
+
+    private static void readKnownChunk(
+            final InputStream stream, final MsrpChunk.Builder chunk, final int chunkSize)
+            throws IOException {
+        // Read the data
+        final byte[] data = new byte[chunkSize];
+        int nbRead = 0;
+        int nbData = -1;
+        while ((nbRead < chunkSize)
+                && ((nbData = stream.read(data, nbRead, chunkSize - nbRead)) != -1)) {
+            nbRead += nbData;
+        }
+
+        chunk.content(data);
+
+        stream.read();
+        stream.read();
+    }
+
+    private static void readUnknownChunk(final InputStream stream, final MsrpChunk.Builder chunk)
+            throws IOException {
+
+        final byte[] bufferArray = new byte[4096];
+        final byte[] endOfChunkPattern =
+                (NEW_LINE_END_MSRP_MSG + chunk.transactionId()).getBytes();
+        int pp = 0;
+
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+        final ByteBuffer buffer = ByteBuffer.wrap(bufferArray);
+        while (true) {
+            final int i = stream.read();
+
+            if (i < 0) {
+                throw new IOException("EOS reached");
+            }
+
+            if (i == endOfChunkPattern[pp]) {
+                pp++;
+            } else if (i == endOfChunkPattern[0]) {
+                pp = 1;
+            } else {
+                pp = 0;
+            }
+
+            buffer.put((byte) i);
+
+            if (pp == endOfChunkPattern.length) {
+                outputStream.write(bufferArray, 0, buffer.position() - endOfChunkPattern.length);
+                break;
+            }
+
+            if (buffer.remaining() == 0) {
+                if (pp > 0) {
+                    outputStream.write(bufferArray, 0, bufferArray.length - pp);
+                    System.arraycopy(endOfChunkPattern, 0, bufferArray, 0, pp);
+                    buffer.position(pp);
+                } else {
+                    outputStream.write(bufferArray, 0, bufferArray.length);
+                    buffer.rewind();
+                }
+            }
+        }
+
+        chunk.content(outputStream.toByteArray());
+    }
+
+    private static void skipEndLine(final InputStream stream, final MsrpChunk.Builder chunk)
+            throws IOException {
+        // skip the "-------" + txid
+        final int length = END_MSRP_MSG_LENGTH + chunk.transactionId().length();
+        final byte[] endline = new byte[256];
+        readFromStream(stream, endline, 0, length);
+    }
+
+    private static void readContinuationFlag(
+            final InputStream stream, final MsrpChunk.Builder transaction) throws IOException {
+        transaction.continuation(Continuation.valueOf(stream.read()));
+        stream.read();
+        stream.read();
+    }
+
+    /**
+     * Get the chunk size
+     *
+     * @param header MSRP header
+     * @return Size in bytes
+     */
+    private static int getChunkSize(final String header) {
+        final int index1 = header.indexOf("-");
+        final int index2 = header.indexOf("/");
+        if ((index1 != -1) && (index2 != -1)) {
+            final String lowByteValue = header.substring(0, index1);
+            final String highByteValue = header.substring(index1 + 1, index2);
+
+            if ("*".equals(highByteValue)) {
+                return -1;
+            } else {
+                try {
+                    final int lowByte = Integer.parseInt(lowByteValue);
+                    final int highByte = Integer.parseInt(highByteValue);
+                    if (lowByte > highByte) {
+                        return Integer.MIN_VALUE;
+                    }
+                    return (highByte - lowByte) + 1;
+                } catch (NumberFormatException e) {
+                    throw new IllegalStateException("Could not read chunksize!");
+                }
+            }
+        }
+        return Integer.MIN_VALUE;
+    }
+
+    /**
+     * Get the total size
+     *
+     * @param header MSRP header
+     * @return Size in bytes
+     */
+    private static long getTotalSize(final String header) {
+        final int index = header.indexOf("/");
+        if (index != -1) {
+            if ("*".equals(header.substring(index + 1))) {
+                return -1;
+            }
+            try {
+                return Long.parseLong(header.substring(index + 1));
+            } catch (NumberFormatException e) {
+                throw new IllegalStateException("Could not read total size!");
+            }
+        }
+        return Integer.MIN_VALUE;
+    }
+
+    private static void readFromStream(
+            InputStream stream, final byte[] buffer, final int offset, final int length)
+            throws IOException {
+        int read = 0;
+        while (read < length) {
+            try {
+                read += stream.read(buffer, offset + read, length - read);
+            } catch (IndexOutOfBoundsException e) {
+                throw new IOException("Invalid ID length", e);
+            }
+        }
+    }
+
+    private static int skipWithDelimiter(InputStream stream, byte delimiter) throws IOException {
+        int i = stream.read();
+        for (; (i != delimiter) && (i != -1); i = stream.read()) {
+        }
+        return i;
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSerializer.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSerializer.java
new file mode 100644
index 0000000..bd4daa5
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSerializer.java
@@ -0,0 +1,81 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.msrp;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Serializer for writing messages
+ */
+public final class MsrpSerializer {
+
+    private MsrpSerializer() {
+    }
+
+    public static void serialize(OutputStream outputStream, MsrpChunk message) throws IOException {
+
+        writeRequestLine(outputStream, message);
+        for (MsrpChunkHeader header : message.headers()) {
+            writeHeader(outputStream, header);
+        }
+
+        if (message.content().length > 0) {
+            outputStream.write(MsrpConstants.NEW_LINE_BYTES);
+            outputStream.write(message.content());
+            outputStream.write(MsrpConstants.NEW_LINE_BYTES);
+        }
+
+        writeEndLine(outputStream, message);
+    }
+
+    private static void writeRequestLine(OutputStream outputStream, MsrpChunk chunk)
+            throws IOException {
+
+        outputStream.write(MsrpConstants.MSRP_PROTOCOL_BYTES);
+        outputStream.write(MsrpConstants.CHAR_SP);
+        outputStream.write(chunk.transactionId().getBytes());
+        outputStream.write(MsrpConstants.CHAR_SP);
+
+        if (chunk.method() != MsrpChunk.Method.UNKNOWN) {
+            outputStream.write(chunk.method().name().getBytes(UTF_8));
+        } else {
+            outputStream.write(
+                    (chunk.responseCode() + " " + chunk.responseReason()).getBytes(UTF_8));
+        }
+
+        outputStream.write(MsrpConstants.NEW_LINE_BYTES);
+    }
+
+    private static void writeHeader(OutputStream outputStream, MsrpChunkHeader header)
+            throws IOException {
+        outputStream.write(header.name().getBytes(UTF_8));
+        outputStream.write(MsrpConstants.HEADER_DELIMITER_BYTES);
+        outputStream.write(header.value().getBytes(UTF_8));
+        outputStream.write(MsrpConstants.NEW_LINE_BYTES);
+    }
+
+    private static void writeEndLine(OutputStream outputStream, MsrpChunk chunk)
+            throws IOException {
+        outputStream.write(MsrpConstants.END_MSRP_MSG_BYTES);
+        outputStream.write(chunk.transactionId().getBytes(UTF_8));
+        outputStream.write(chunk.continuation().toByte());
+        outputStream.write(MsrpConstants.NEW_LINE_BYTES);
+    }
+}
\ No newline at end of file
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java
new file mode 100644
index 0000000..1c461fe
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSession.java
@@ -0,0 +1,258 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.msrp;
+
+import static com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpChunk.Method.SEND;
+import static com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpChunk.Method.UNKNOWN;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.QosCallback;
+import android.net.QosCallbackException;
+import android.net.QosSession;
+import android.net.QosSessionAttributes;
+import android.net.QosSocketInfo;
+import android.util.Log;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.concurrent.futures.CallbackToFutureAdapter;
+import androidx.concurrent.futures.CallbackToFutureAdapter.Completer;
+
+import com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpChunk.Continuation;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Provides MSRP sending and receiving messages ability.
+ */
+public class MsrpSession {
+    private static final String DEDICATED_BEARER_SUCCESS = "Dedicated bearer succeeded";
+    private static final String DEDICATED_BEARER_ERROR = "Dedicated bearer error";
+    private final Network network;
+    private final Socket socket;
+    private final InputStream input;
+    private final OutputStream output;
+    private final AtomicBoolean isOpen = new AtomicBoolean(true);
+    private final ConcurrentHashMap<String, MsrpTransaction> transactions =
+            new ConcurrentHashMap<>();
+    private final MsrpSessionListener listener;
+    private final ConnectivityManager connectivityManager;
+    private final String LOG_TAG = MsrpSession.class.getSimpleName();
+    private final Context context;
+
+    /** Creates a new MSRP session on the given listener and the provided streams. */
+    MsrpSession(ConnectivityManager connectivityManager, Context context, Network network,
+            Socket socket, MsrpSessionListener listener) throws IOException {
+        this.connectivityManager = connectivityManager;
+        this.context = context;
+        this.network = network;
+        this.socket = socket;
+        this.input = socket.getInputStream();
+        this.output = socket.getOutputStream();
+        this.listener = listener;
+
+        listenForBearer();
+    }
+
+    private final QosCallback qosCallback = new QosCallback() {
+        @Override
+        public void onError(@NonNull QosCallbackException exception) {
+            Toast.makeText(context, DEDICATED_BEARER_ERROR, Toast.LENGTH_SHORT).show();
+            Log.e(LOG_TAG, "onError: " + exception.toString());
+            super.onError(exception);
+        }
+
+        @Override
+        public void onQosSessionAvailable(@NonNull QosSession session,
+                @NonNull QosSessionAttributes sessionAttributes) {
+            Toast.makeText(context, DEDICATED_BEARER_SUCCESS, Toast.LENGTH_SHORT).show();
+            Log.d(LOG_TAG, "onQosSessionAvailable: " + session.toString() + ", "
+                    + sessionAttributes.toString());
+            super.onQosSessionAvailable(session, sessionAttributes);
+        }
+
+        @Override
+        public void onQosSessionLost(@NonNull QosSession session) {
+            Toast.makeText(context, DEDICATED_BEARER_ERROR, Toast.LENGTH_SHORT).show();
+            Log.e(LOG_TAG, "onQosSessionLost: " + session.toString());
+            super.onQosSessionLost(session);
+        }
+    };
+
+    private void listenForBearer() {
+        try {
+            connectivityManager.registerQosCallback(new QosSocketInfo(network, socket),
+                    MoreExecutors.directExecutor(), qosCallback);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Sends the given MSRP chunk.
+     */
+    public ListenableFuture<MsrpChunk> send(MsrpChunk request) {
+        if (request.method() == UNKNOWN) {
+            throw new IllegalArgumentException("Given chunk must be a request");
+        }
+
+        if (!isOpen.get()) {
+            throw new IllegalStateException("Session terminated");
+        }
+
+        if (!socket.isConnected()) {
+            throw new IllegalStateException("Socket is not connected");
+        }
+
+        if (request.method() == SEND) {
+            return CallbackToFutureAdapter.getFuture(
+                    completer -> {
+                        final MsrpTransaction transaction = new MsrpTransaction(completer);
+                        transactions.put(request.transactionId(), transaction);
+                        try {
+                            synchronized (output) {
+                                MsrpSerializer.serialize(output, request);
+                            }
+                            output.flush();
+                        } catch (IOException e) {
+                            completer.setException(e);
+                        }
+                        return "MsrpSession.send(" + request.transactionId() + ")";
+                    }
+            );
+        } else {
+            try {
+                synchronized (output) {
+                    MsrpSerializer.serialize(output, request);
+                }
+                return Futures.immediateFuture(request);
+            } catch (IOException e) {
+                return Futures.immediateFailedFuture(e);
+            }
+        }
+    }
+
+    /**
+     * Blocking method which reads from the provided InputStream until the session
+     * is terminated or the stream read throws an exception.
+     */
+    public void run() {
+        new StreamReader(this).run();
+    }
+
+    public void terminate() throws IOException {
+        if (isOpen.getAndSet(false)) {
+            output.flush();
+        }
+        connectivityManager.unregisterQosCallback(qosCallback);
+        socket.close();
+    }
+
+    /**
+     * Reads and parses MSRP messages from the session input stream.
+     */
+    private static class StreamReader {
+
+        private final MsrpSession session;
+        private final InputStream stream;
+        private final AtomicBoolean active;
+
+        StreamReader(MsrpSession session) {
+            this.session = session;
+            this.stream = session.input;
+            this.active = session.isOpen;
+        }
+
+        void run() {
+            while (active.get()) {
+                MsrpChunk chunk = null;
+                try {
+                    chunk = MsrpParser.parse(stream);
+
+                    if (chunk.method() == UNKNOWN) {
+                        completeTransaction(chunk);
+                    } else {
+                        receiveRequest(chunk);
+                    }
+                } catch (IOException e) {
+                    active.compareAndSet(true, false);
+                }
+            }
+        }
+
+        private void receiveRequest(MsrpChunk chunk) throws IOException {
+            sendResponse(chunk);
+            session.listener.onChunkReceived(chunk);
+        }
+
+        private void completeTransaction(MsrpChunk chunk) {
+            MsrpTransaction transaction = session.transactions.remove(chunk.transactionId());
+            if (transaction != null) {
+                transaction.complete(chunk);
+            }
+        }
+
+        private void sendResponse(MsrpChunk chunk) throws IOException {
+            // check if response is required
+            MsrpChunkHeader failureReport = chunk.header(MsrpConstants.HEADER_FAILURE_REPORT);
+            if (failureReport == null || failureReport.value().equals("yes")) {
+                MsrpChunkHeader toPath = chunk.header(MsrpConstants.HEADER_TO_PATH);
+                MsrpChunkHeader fromPath = chunk.header(MsrpConstants.HEADER_FROM_PATH);
+
+                MsrpChunk response = MsrpChunk.newBuilder()
+                        .transactionId(chunk.transactionId())
+                        .responseCode(200)
+                        .responseReason("OK")
+                        .addHeader(MsrpConstants.HEADER_TO_PATH, fromPath.value())
+                        .addHeader(MsrpConstants.HEADER_FROM_PATH, toPath.value())
+                        .continuation(Continuation.COMPLETE)
+                        .build();
+
+                synchronized (session.output) {
+                    MsrpSerializer.serialize(session.output, response);
+                    session.output.flush();
+                }
+            }
+        }
+    }
+
+    /**
+     * Transaction holder.
+     */
+    private static class MsrpTransaction {
+        private final Completer<MsrpChunk> completed;
+
+        public MsrpTransaction(Completer<MsrpChunk> chunkCompleter) {
+            this.completed = chunkCompleter;
+        }
+
+        public void complete(MsrpChunk response) {
+            completed.set(response);
+        }
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSessionListener.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSessionListener.java
new file mode 100644
index 0000000..4235c25
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpSessionListener.java
@@ -0,0 +1,24 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.msrp;
+
+/**
+ * Listener for MSRP session events.
+ */
+public interface MsrpSessionListener {
+    void onChunkReceived(MsrpChunk chunk);
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpUtils.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpUtils.java
new file mode 100644
index 0000000..c135bc0
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/msrp/MsrpUtils.java
@@ -0,0 +1,60 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.msrp;
+
+import com.android.libraries.rcs.simpleclient.protocol.sip.SipUtils;
+import java.security.SecureRandom;
+
+/** Collections of utility functions for MSRP */
+public final class MsrpUtils {
+
+    private static final SecureRandom random = new SecureRandom();
+
+    private MsrpUtils() {
+    }
+
+    /** Generate a path attribute defined in RFC 4975 for the given address, port. */
+    public static String generatePath(String address, int port, boolean isSecure) {
+        StringBuilder builder = new StringBuilder();
+
+        if (SipUtils.isIPv6Address(address)) {
+            address = "[" + address + "]";
+        }
+
+        builder
+                .append(isSecure ? "msrps" : "msrp")
+                .append("://")
+                .append(address)
+                .append(":")
+                .append(port)
+                .append("/")
+                .append(System.currentTimeMillis())
+                .append(";tcp");
+
+        return builder.toString();
+    }
+
+    public static String generateRandomId() {
+        byte[] randomBytes = new byte[8];
+        random.nextBytes(randomBytes);
+        String hex = "";
+        for (byte b : randomBytes) {
+            hex = hex + String.format("%02x", b);
+        }
+        return hex;
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sdp/SdpMedia.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sdp/SdpMedia.java
new file mode 100644
index 0000000..bdb34ba
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sdp/SdpMedia.java
@@ -0,0 +1,119 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.sdp;
+
+import android.text.TextUtils;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableMap;
+
+import java.text.ParseException;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The media part of SDP implementation as per RFC 4566. This class supports minimal fields that is
+ * required to represent MSRP session.
+ */
+@AutoValue
+public abstract class SdpMedia {
+    private static final String CRLF = "\r\n";
+
+    public static Builder parseMediaLine(String line) throws ParseException {
+        List<String> elements = Splitter.on(" ").limit(4).splitToList(line);
+
+        // The valid media line should have 4 elements:
+        // m=<name> <port> <protocol> <format>
+        if (elements.size() != 4) {
+            throw new ParseException("Invalid media line", 0);
+        }
+
+        // Parse each field from the media line.
+        Builder builder = SdpMedia.newBuilder();
+        builder
+                .setName(elements.get(0))
+                .setPort(Integer.parseInt(elements.get(1)))
+                .setProtocol(elements.get(2))
+                .setFormat(elements.get(3));
+
+        return builder;
+    }
+
+    public static Builder newBuilder() {
+        return new AutoValue_SdpMedia.Builder();
+    }
+
+    public abstract String name();
+
+    public abstract int port();
+
+    public abstract String protocol();
+
+    public abstract String format();
+
+    public abstract ImmutableMap<String, String> attributes();
+
+    /** Encode the media section as a string. */
+    public String encode() {
+        StringBuilder builder = new StringBuilder();
+        builder
+                .append("m=")
+                .append(name())
+                .append(" ")
+                .append(port())
+                .append(" ")
+                .append(protocol())
+                .append(" ")
+                .append(format())
+                .append(CRLF);
+
+        for (Map.Entry<String, String> attribute : attributes().entrySet()) {
+            builder.append("a=").append(attribute.getKey());
+            if (!TextUtils.isEmpty(attribute.getValue())) {
+                builder.append(":").append(attribute.getValue());
+            }
+            builder.append(CRLF);
+        }
+
+        return builder.toString();
+    }
+
+    @AutoValue.Builder
+    public abstract static class Builder {
+        public abstract Builder setName(String name);
+
+        public abstract Builder setPort(int port);
+
+        public abstract Builder setProtocol(String protocol);
+
+        public abstract Builder setFormat(String payload);
+
+        public abstract ImmutableMap.Builder<String, String> attributesBuilder();
+
+        public Builder addAttribute(String name, String value) {
+            attributesBuilder().put(name, value);
+            return this;
+        }
+
+        public Builder addAttribute(String name) {
+            return addAttribute(name, "");
+        }
+
+        public abstract SdpMedia build();
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sdp/SdpUtils.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sdp/SdpUtils.java
new file mode 100644
index 0000000..e290b29
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sdp/SdpUtils.java
@@ -0,0 +1,116 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.sdp;
+
+import static com.android.libraries.rcs.simpleclient.protocol.sip.SipUtils.isIPv6Address;
+
+import com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpUtils;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableSet;
+
+/** Collections of utility functions for SDP */
+public final class SdpUtils {
+    public static final String SDP_CONTENT_TYPE = "application";
+    public static final String SDP_CONTENT_SUB_TYPE = "sdp";
+
+    private static final ImmutableSet<String> DEFAULT_ACCEPT_TYPES =
+            ImmutableSet.of("message/cpim", "application/im-iscomposing+xml");
+    private static final ImmutableSet<String> DEFAULT_ACCEPT_WRAPPED_TYPES =
+            ImmutableSet.of(
+                    "text/plain",
+                    "message/imdn+xml",
+                    "application/vnd.gsma.rcs-ft-http+xml",
+                    "application/vnd.gsma.rcspushlocation+xml");
+
+    private static final String DEFAULT_NAME = "message";
+    private static final String DEFAULT_SETUP = "active";
+    private static final String DEFAULT_DIRECTION = "sendrecv";
+    private static final int DEFAULT_MSRP_PORT = 9;
+    private static final String PROTOCOL_TCP_MSRP = "TCP/MSRP";
+    private static final String PROTOCOL_TLS_MSRP = "TCP/TLS/MSRP";
+    private static final String DEFAULT_FORMAT = "*";
+
+    private static final String ATTRIBUTE_PATH = "path";
+    private static final String ATTRIBUTE_SETUP = "setup";
+    private static final String ATTRIBUTE_ACCEPT_TYPES = "accept-types";
+    private static final String ATTRIBUTE_ACCEPT_WRAPPED_TYPES = "accept-wrapped-types";
+
+    private SdpUtils() {
+    }
+
+    /**
+     * Create a simple SDP message for MSRP. Most attributes except address and transport type
+     * will be
+     * generated automatically.
+     *
+     * @param address The local IP address of the MSRP connection.
+     * @param isTls   True if the MSRP connection uses TLS.
+     */
+    public static SimpleSdpMessage createSdpForMsrp(String address, boolean isTls) {
+        return SimpleSdpMessage.newBuilder()
+                .setVersion("0")
+                .setOrigin(generateOrigin(address))
+                .setSession("-")
+                .setConnection(generateConnection(address))
+                .setTime("0 0")
+                .addMedia(createSdpMediaForMsrp(address, isTls))
+                .build();
+    }
+
+    private static String generateOrigin(String address) {
+        StringBuilder builder = new StringBuilder();
+        builder
+                .append("TestRcsClient ")
+                .append(System.currentTimeMillis())
+                .append(" ")
+                .append(System.currentTimeMillis())
+                .append(" IN ")
+                .append(isIPv6Address(address) ? "IP6 " : "IP4 ")
+                .append(address);
+
+        return builder.toString();
+    }
+
+    private static String generateConnection(String address) {
+        return "IN " + (isIPv6Address(address) ? "IP6 " : "IP4 ") + address;
+    }
+
+    /**
+     * Create a media part of the SDP message for MSRP. Most attributes except address and transport
+     * type will be generated automatically.
+     *
+     * @param address The local IP address of the MSRP connection.
+     * @param isTls   True if the MSRP connection uses TLS.
+     */
+    public static SdpMedia createSdpMediaForMsrp(String address, boolean isTls) {
+        return SdpMedia.newBuilder()
+                .setName(DEFAULT_NAME)
+                .setPort(DEFAULT_MSRP_PORT)
+                .setProtocol(isTls ? PROTOCOL_TLS_MSRP : PROTOCOL_TCP_MSRP)
+                .setFormat(DEFAULT_FORMAT)
+                .addAttribute(ATTRIBUTE_PATH,
+                        MsrpUtils.generatePath(address, DEFAULT_MSRP_PORT, isTls))
+                .addAttribute(ATTRIBUTE_SETUP, DEFAULT_SETUP)
+                .addAttribute(ATTRIBUTE_ACCEPT_TYPES, Joiner.on(" ").join(DEFAULT_ACCEPT_TYPES))
+                .addAttribute(
+                        ATTRIBUTE_ACCEPT_WRAPPED_TYPES,
+                        Joiner.on(" ").join(DEFAULT_ACCEPT_WRAPPED_TYPES))
+                .addAttribute(DEFAULT_DIRECTION)
+                .build();
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sdp/SimpleSdpMessage.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sdp/SimpleSdpMessage.java
new file mode 100644
index 0000000..4abbf87
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sdp/SimpleSdpMessage.java
@@ -0,0 +1,196 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.sdp;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.text.ParseException;
+import java.util.List;
+import java.util.Optional;
+import java.util.OptionalInt;
+
+/**
+ * The SDP implementation as per RFC 4566. This class supports minimal fields that is required to
+ * represent MSRP session.
+ */
+@AutoValue
+public abstract class SimpleSdpMessage {
+    private static final String CRLF = "\r\n";
+
+    private static final String PREFIX_VERSION = "v";
+    private static final String PREFIX_ORIGIN = "o";
+    private static final String PREFIX_SESSION = "s";
+    private static final String PREFIX_CONNECTION = "c";
+    private static final String PREFIX_TIME = "t";
+    private static final String PREFIX_MEDIA = "m";
+    private static final String PREFIX_ATTRIBUTE = "a";
+    private static final String EQUAL = "=";
+
+    public static SimpleSdpMessage parse(InputStream stream) throws ParseException, IOException {
+        Builder builder = new AutoValue_SimpleSdpMessage.Builder();
+        SdpMedia.Builder currentMediaBuilder = null;
+        BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
+
+        String line = reader.readLine();
+        while (line != null) {
+            List<String> parts = Splitter.on("=").trimResults().limit(2).splitToList(line);
+            if (parts.size() != 2) {
+                throw new ParseException("Invalid SDP format", 0);
+            }
+            String prefix = parts.get(0);
+            String value = parts.get(1);
+
+            switch (prefix) {
+                case PREFIX_VERSION:
+                    builder.setVersion(value);
+                    break;
+                case PREFIX_ORIGIN:
+                    builder.setOrigin(value);
+                    break;
+                case PREFIX_SESSION:
+                    builder.setSession(value);
+                    break;
+                case PREFIX_CONNECTION:
+                    builder.setConnection(value);
+                    break;
+                case PREFIX_TIME:
+                    builder.setTime(value);
+                    break;
+                case PREFIX_MEDIA:
+                    if (currentMediaBuilder != null) {
+                        builder.addMedia(currentMediaBuilder.build());
+                    }
+                    currentMediaBuilder = SdpMedia.parseMediaLine(value);
+                    break;
+                case PREFIX_ATTRIBUTE:
+                    if (currentMediaBuilder != null) {
+                        List<String> kv = Splitter.on(":").trimResults().limit(2).splitToList(
+                                value);
+                        currentMediaBuilder.addAttribute(kv.get(0), kv.size() < 2 ? "" : kv.get(1));
+                    }
+                    break;
+                default:
+                    // Rest of the fields are ignored as they're not used for describing MSRP
+                    // session.
+                    break;
+            }
+            line = reader.readLine();
+        }
+
+        if (currentMediaBuilder != null) {
+            builder.addMedia(currentMediaBuilder.build());
+        }
+
+        return builder.build();
+    }
+
+    private static String encodeLine(String prefix, String value) {
+        return prefix + EQUAL + value + CRLF;
+    }
+
+    public static Builder newBuilder() {
+        return new AutoValue_SimpleSdpMessage.Builder();
+    }
+
+    public abstract String version();
+
+    public abstract String origin();
+
+    public abstract String session();
+
+    public abstract String connection();
+
+    public abstract String time();
+
+    public abstract ImmutableList<SdpMedia> media();
+
+    /** Return the IP address in the connection line. */
+    public Optional<String> getAddress() {
+        if (connection() == null) {
+            return Optional.empty();
+        }
+
+        List<String> parts = Splitter.on(" ").limit(3).trimResults().splitToList(connection());
+        if (parts.size() != 3) {
+            return Optional.empty();
+        }
+
+        return Optional.of(parts.get(2));
+    }
+
+    /** Return the port in the first media line. */
+    public OptionalInt getPort() {
+        if (media().isEmpty()) {
+            return OptionalInt.empty();
+        }
+
+        return OptionalInt.of(media().get(0).port());
+    }
+
+    public Optional<String> getPath() {
+        if (media().isEmpty()) {
+            return Optional.empty();
+        }
+
+        return Optional.ofNullable(media().get(0).attributes().get("path"));
+    }
+
+    /** Encode the entire SDP fields as a string. */
+    public String encode() {
+        StringBuilder builder = new StringBuilder();
+        builder
+                .append(encodeLine(PREFIX_VERSION, version()))
+                .append(encodeLine(PREFIX_ORIGIN, origin()))
+                .append(encodeLine(PREFIX_SESSION, session()))
+                .append(encodeLine(PREFIX_CONNECTION, connection()))
+                .append(encodeLine(PREFIX_TIME, time()));
+
+        for (SdpMedia media : media()) {
+            builder.append(media.encode());
+        }
+
+        return builder.toString();
+    }
+
+    @AutoValue.Builder
+    public abstract static class Builder {
+        public abstract Builder setVersion(String version);
+
+        public abstract Builder setOrigin(String origin);
+
+        public abstract Builder setSession(String session);
+
+        public abstract Builder setConnection(String connection);
+
+        public abstract Builder setTime(String connection);
+
+        public abstract ImmutableList.Builder<SdpMedia> mediaBuilder();
+
+        public Builder addMedia(SdpMedia media) {
+            mediaBuilder().add(media);
+            return this;
+        }
+
+        public abstract SimpleSdpMessage build();
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sip/SipSession.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sip/SipSession.java
new file mode 100644
index 0000000..9629961
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sip/SipSession.java
@@ -0,0 +1,33 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.sip;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import javax.sip.message.Message;
+
+/**
+ * Abstraction of the underlying SIP channel for sending and receiving SIP messages.
+ */
+public interface SipSession {
+
+    SipSessionConfiguration getSessionConfiguration();
+
+    ListenableFuture<Boolean> send(Message message);
+
+    void setSessionListener(SipSessionListener listener);
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sip/SipSessionConfiguration.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sip/SipSessionConfiguration.java
new file mode 100644
index 0000000..59a0541
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sip/SipSessionConfiguration.java
@@ -0,0 +1,60 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.sip;
+
+import java.util.List;
+
+public interface SipSessionConfiguration {
+    public long getVersion();
+
+    String getOutboundProxyAddr();
+
+    int getOutboundProxyPort();
+
+    String getLocalIpAddress();
+
+    int getLocalPort();
+
+    String getSipTransport();
+
+    String getPublicUserIdentity();
+
+    String getDomain();
+
+    List<String> getAssociatedUris();
+
+    String getSecurityVerifyHeader();
+
+    List<String> getServiceRouteHeaders();
+
+    String getContactUser();
+
+    String getImei();
+
+    String getPaniHeader();
+
+    String getPlaniHeader();
+
+    /**
+     * @return the user agent header from the  ims config.
+     */
+    String getUserAgentHeader();
+
+    default int getMaxPayloadSizeOnUdp() {
+        return 0;
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sip/SipSessionListener.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sip/SipSessionListener.java
new file mode 100644
index 0000000..5fe61e6
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sip/SipSessionListener.java
@@ -0,0 +1,27 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.sip;
+
+import javax.sip.message.Message;
+
+/**
+ * Listener for incoming messages on a {@link SipSession}.
+ */
+public interface SipSessionListener {
+
+    void onMessageReceived(Message sipMessage);
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sip/SipUtils.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sip/SipUtils.java
new file mode 100644
index 0000000..13fa53a
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/protocol/sip/SipUtils.java
@@ -0,0 +1,348 @@
+/*
+ * 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.libraries.rcs.simpleclient.protocol.sip;
+
+import static com.android.libraries.rcs.simpleclient.protocol.sdp.SdpUtils.SDP_CONTENT_SUB_TYPE;
+import static com.android.libraries.rcs.simpleclient.protocol.sdp.SdpUtils.SDP_CONTENT_TYPE;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.android.libraries.rcs.simpleclient.protocol.sdp.SimpleSdpMessage;
+
+import com.google.common.base.Ascii;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.net.InetAddresses;
+
+import gov.nist.javax.sip.Utils;
+import gov.nist.javax.sip.address.AddressFactoryImpl;
+import gov.nist.javax.sip.header.ContentType;
+import gov.nist.javax.sip.header.HeaderFactoryImpl;
+import gov.nist.javax.sip.header.Via;
+import gov.nist.javax.sip.header.extensions.SessionExpires;
+import gov.nist.javax.sip.header.ims.PPreferredIdentityHeader;
+import gov.nist.javax.sip.header.ims.PPreferredServiceHeader;
+import gov.nist.javax.sip.header.ims.SecurityVerifyHeader;
+import gov.nist.javax.sip.message.SIPMessage;
+import gov.nist.javax.sip.message.SIPRequest;
+import gov.nist.javax.sip.message.SIPResponse;
+
+import java.net.Inet6Address;
+import java.text.ParseException;
+import java.util.List;
+import java.util.UUID;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.address.AddressFactory;
+import javax.sip.address.SipURI;
+import javax.sip.address.URI;
+import javax.sip.header.ContactHeader;
+import javax.sip.header.Header;
+import javax.sip.header.HeaderFactory;
+import javax.sip.header.ViaHeader;
+import javax.sip.message.Request;
+import javax.sip.message.Response;
+
+/** Collections of utility functions for SIP */
+public final class SipUtils {
+    private static final String TAG = "SipUtils";
+    private static final String SUPPORTED_TIMER_TAG = "timer";
+    private static final String ICSI_REF_PARAM_NAME = "+g.3gpp.icsi-ref";
+    private static final String SIP_INSTANCE_PARAM_NAME = "+sip.instance";
+    private static final String CPM_SESSION_FEATURE_TAG_PARAM_VALUE =
+            "\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session\"";
+    private static final String CPM_SESSION_FEATURE_TAG_FULL_STRING =
+            "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session\"";
+    private static final String CPM_SESSION_SERVICE_NAME =
+            "urn:urn-7:3gpp-service.ims.icsi.oma.cpm.session";
+    private static final String CONTRIBUTION_ID_HEADER_NAME = "Contribution-ID";
+    private static final String CONVERSATION_ID_HEADER_NAME = "Conversation-ID";
+    private static final String ACCEPT_CONTACT_HEADER_NAME = "Accept-Contact";
+    private static final String PANI_HEADER_NAME = "P-Access-Network-Info";
+    private static final String PLANI_HEADER_NAME = "P-Last-Access-Network-Info";
+    private static final String USER_AGENT_HEADER = "RcsTestClient";
+
+    private static AddressFactory sAddressFactory = new AddressFactoryImpl();
+    private static HeaderFactory sHeaderFactory = new HeaderFactoryImpl();
+
+    private SipUtils() {
+    }
+
+    /**
+     * Try to parse the given uri.
+     *
+     * @throws IllegalArgumentException in case of parsing error.
+     */
+    public static URI createUri(String uri) {
+        try {
+            return sAddressFactory.createURI(uri);
+        } catch (ParseException exception) {
+            throw new IllegalArgumentException("URI cannot be created", exception);
+        }
+    }
+
+    /**
+     * Create SIP INVITE request for a CPM 1:1 chat.
+     *
+     * @param configuration  The SipSessionConfiguration instance used for populating SIP headers.
+     * @param targetUri      The uri to be targeted.
+     * @param conversationId The id to be contained in Conversation-ID header.
+     */
+    public static SIPRequest buildInvite(
+            SipSessionConfiguration configuration,
+            String targetUri,
+            String conversationId,
+            byte[] content)
+            throws ParseException {
+        String address = configuration.getLocalIpAddress();
+        int port = configuration.getLocalPort();
+        String transport = configuration.getSipTransport();
+        List<String> associatedUris = configuration.getAssociatedUris();
+        String preferredUri = Iterables.getFirst(associatedUris,
+                configuration.getPublicUserIdentity());
+
+        SIPRequest request = new SIPRequest();
+        request.setMethod(Request.INVITE);
+
+        URI remoteUri = createUri(targetUri);
+        request.setRequestURI(remoteUri);
+        request.setFrom(
+                sHeaderFactory.createFromHeader(
+                        sAddressFactory.createAddress(preferredUri),
+                        Utils.getInstance().generateTag()));
+        request.setTo(
+                sHeaderFactory.createToHeader(sAddressFactory.createAddress(remoteUri), null));
+
+        ViaHeader viaHeader = null;
+
+        try {
+            // Set a default Max-Forwards header.
+            request.setMaxForwards(sHeaderFactory.createMaxForwardsHeader(70));
+            request.setCSeq(sHeaderFactory.createCSeqHeader(1L, Request.INVITE));
+            viaHeader =
+                    sHeaderFactory.createViaHeader(
+                            address, port, transport, Utils.getInstance().generateBranchId());
+            request.setVia(ImmutableList.of(viaHeader));
+
+            // Set a default Session-Expires header.
+            SessionExpires sessionExpires = new SessionExpires();
+            sessionExpires.setRefresher("uac");
+            sessionExpires.setExpires(1800);
+            request.setHeader(sessionExpires);
+
+            // Set a Contact header.
+            request.setHeader(generateContactHeader(configuration));
+
+            // Set PANI and PLANI if exists
+            if (configuration.getPaniHeader() != null) {
+                request.setHeader(
+                        sHeaderFactory.createHeader(PANI_HEADER_NAME,
+                                configuration.getPaniHeader()));
+            }
+            if (configuration.getPlaniHeader() != null) {
+                request.setHeader(
+                        sHeaderFactory.createHeader(PLANI_HEADER_NAME,
+                                configuration.getPlaniHeader()));
+            }
+        } catch (InvalidArgumentException e) {
+            // Nothing to do here
+            Log.e(TAG, e.getMessage());
+        }
+
+        request.setCallId(UUID.randomUUID().toString());
+        request.setHeader(sHeaderFactory.createHeader(CONVERSATION_ID_HEADER_NAME, conversationId));
+        request.setHeader(
+                sHeaderFactory.createHeader(CONTRIBUTION_ID_HEADER_NAME,
+                        UUID.randomUUID().toString()));
+
+        String acceptContact = "*;" + CPM_SESSION_FEATURE_TAG_FULL_STRING;
+        request.setHeader(sHeaderFactory.createHeader(ACCEPT_CONTACT_HEADER_NAME, acceptContact));
+        request.setHeader(sHeaderFactory.createSupportedHeader(SUPPORTED_TIMER_TAG));
+        request.setHeader(sHeaderFactory.createHeader(PPreferredIdentityHeader.NAME, preferredUri));
+        request.setHeader(
+                sHeaderFactory.createHeader(PPreferredServiceHeader.NAME,
+                        CPM_SESSION_SERVICE_NAME));
+
+        // Set a Security-Verify header if exist.
+        String securityVerify = configuration.getSecurityVerifyHeader();
+        if (!TextUtils.isEmpty(securityVerify)) {
+            request.setHeader(
+                    sHeaderFactory.createHeader(SecurityVerifyHeader.NAME, securityVerify));
+        }
+
+        // Add Route headers.
+        List<String> serviceRoutes = configuration.getServiceRouteHeaders();
+        if (!serviceRoutes.isEmpty()) {
+            for (String sr : serviceRoutes) {
+                request.addHeader(
+                        sHeaderFactory.createRouteHeader(sAddressFactory.createAddress(sr)));
+            }
+        }
+
+        String userAgent = configuration.getUserAgentHeader();
+        userAgent = (userAgent == null) ? USER_AGENT_HEADER : userAgent;
+        request.addHeader(sHeaderFactory.createUserAgentHeader(ImmutableList.of(userAgent)));
+
+        request.setMessageContent(SDP_CONTENT_TYPE, SDP_CONTENT_SUB_TYPE, content);
+
+        if (viaHeader != null && Ascii.equalsIgnoreCase("udp", transport)) {
+            String newTransport =
+                    determineTransportBySize(configuration, request.encodeAsBytes("udp").length);
+            if (!Ascii.equalsIgnoreCase(transport, newTransport)) {
+                viaHeader.setTransport(newTransport);
+            }
+        }
+
+        return request;
+    }
+
+    private static ContactHeader generateContactHeader(SipSessionConfiguration configuration)
+            throws ParseException {
+        String host = configuration.getLocalIpAddress();
+        if (isIPv6Address(host)) {
+            host = "[" + host + "]";
+        }
+
+        String userPart = configuration.getContactUser();
+        SipURI uri = sAddressFactory.createSipURI(userPart, host);
+        try {
+            uri.setPort(configuration.getLocalPort());
+            uri.setTransportParam(configuration.getSipTransport());
+        } catch (Exception e) {
+            // Shouldn't be here.
+        }
+
+        ContactHeader contactHeader =
+                sHeaderFactory.createContactHeader(sAddressFactory.createAddress(uri));
+
+        // Add +sip.instance param.
+        String sipInstance = "\"<urn:gsma:imei:" + configuration.getImei() + ">\"";
+        contactHeader.setParameter(SIP_INSTANCE_PARAM_NAME, sipInstance);
+
+        // Add CPM feature tag.
+        uri.setTransportParam(configuration.getSipTransport());
+        contactHeader.setParameter(ICSI_REF_PARAM_NAME, CPM_SESSION_FEATURE_TAG_PARAM_VALUE);
+
+        return contactHeader;
+    }
+
+    /**
+     * Create a SIP BYE request for terminating the chat session.
+     *
+     * @param invite the initial INVITE request of the chat session.
+     */
+    public static SIPRequest buildBye(SIPRequest invite) throws ParseException {
+        SIPRequest request = new SIPRequest();
+        request.setRequestURI(invite.getRequestURI());
+        request.setMethod(Request.BYE);
+        try {
+            long cSeqNumber = invite.getCSeq().getSeqNumber();
+            request.setHeader(sHeaderFactory.createCSeqHeader(cSeqNumber, Request.BYE));
+        } catch (InvalidArgumentException e) {
+            // Nothing to do here
+        }
+
+        request.setCallId(invite.getCallId());
+
+        Via via = (Via) invite.getTopmostVia().clone();
+        via.removeParameter("branch");
+        via.setBranch(Utils.getInstance().generateBranchId());
+        request.addHeader(via);
+        request.addHeader(
+                sHeaderFactory.createFromHeader(invite.getFrom().getAddress(),
+                        invite.getFrom().getTag()));
+        request.addHeader(
+                sHeaderFactory.createToHeader(invite.getTo().getAddress(),
+                        invite.getTo().getTag()));
+
+        return request;
+    }
+
+    /**
+     * Create SIP INVITE response for a CPM 1:1 chat.
+     *
+     * @param configuration The SipSessionConfiguration instance used for populating SIP headers.
+     * @param invite        the initial INVITE request of the chat session.
+     * @param code          The status code of the response.
+     */
+    public static SIPResponse buildInviteResponse(
+            SipSessionConfiguration configuration,
+            SIPRequest invite,
+            int code,
+            @Nullable SimpleSdpMessage sdp)
+            throws ParseException {
+        SIPResponse response = invite.createResponse(code);
+        if (code == Response.OK) {
+            response.setMessageContent(SDP_CONTENT_TYPE, SDP_CONTENT_SUB_TYPE, sdp.encode());
+        }
+        response.setToTag(Utils.getInstance().generateTag());
+
+        // Set a Contact header.
+        response.setHeader(generateContactHeader(configuration));
+
+        // Set Conversation-ID and Contribution-ID
+        Header conversationIdHeader = invite.getHeader(CONVERSATION_ID_HEADER_NAME);
+        if (conversationIdHeader != null) {
+            response.setHeader((Header) conversationIdHeader.clone());
+        }
+        Header contributionIdHeader = invite.getHeader(CONTRIBUTION_ID_HEADER_NAME);
+        if (conversationIdHeader != null) {
+            response.setHeader((Header) contributionIdHeader.clone());
+        }
+
+        // Set P-Preferred-Identity
+        List<String> associatedUris = configuration.getAssociatedUris();
+        String preferredUri = Iterables.getFirst(associatedUris,
+                configuration.getPublicUserIdentity());
+        response.setHeader(
+                sHeaderFactory.createHeader(PPreferredIdentityHeader.NAME, preferredUri));
+
+        // Set PANI and PLANI if exists
+        if (configuration.getPaniHeader() != null) {
+            response.setHeader(
+                    sHeaderFactory.createHeader(PANI_HEADER_NAME, configuration.getPaniHeader()));
+        }
+        if (configuration.getPlaniHeader() != null) {
+            response.setHeader(
+                    sHeaderFactory.createHeader(PLANI_HEADER_NAME, configuration.getPlaniHeader()));
+        }
+        return response;
+    }
+
+    public static boolean isIPv6Address(String address) {
+        return InetAddresses.forString(address) instanceof Inet6Address;
+    }
+
+    /** Return whether the SIP message has a SDP content or not */
+    public static boolean hasSdpContent(SIPMessage message) {
+        ContentType contentType = message.getContentTypeHeader();
+        return contentType != null
+                && TextUtils.equals(contentType.getContentType(), SDP_CONTENT_TYPE)
+                && TextUtils.equals(contentType.getContentSubType(), SDP_CONTENT_SUB_TYPE);
+    }
+
+    private static String determineTransportBySize(SipSessionConfiguration configuration,
+            int size) {
+        if (size > configuration.getMaxPayloadSizeOnUdp()) {
+            return "tcp";
+        }
+        return "udp";
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/ProvisioningController.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/ProvisioningController.java
new file mode 100644
index 0000000..f987c67
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/ProvisioningController.java
@@ -0,0 +1,44 @@
+/*
+ * 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.libraries.rcs.simpleclient.provisioning;
+
+import android.telephony.ims.ImsException;
+
+/**
+ * Access to provisioning functionality and data.
+ */
+public interface ProvisioningController {
+
+    /**
+     * Triggers a new provisioning request. If the device is not already provisioned, it requests
+     * the
+     * provisioning flow and sets up callbacks. If the provisioning is already present, it
+     * requests a
+     * new provisioning config from the server.
+     *
+     * @throws ImsException if there is an error.
+     */
+    void triggerProvisioning() throws ImsException;
+
+    /** Is Single-Reg enabled for the default call SIM ? */
+    boolean isRcsVolteSingleRegistrationCapable() throws ImsException;
+
+    void onConfigurationChange(ProvisioningStateChangeCallback cb);
+
+    // Unregister the callback to the framework's provisioning change.
+    void unRegister();
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/ProvisioningControllerImpl.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/ProvisioningControllerImpl.java
new file mode 100644
index 0000000..06d3835
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/ProvisioningControllerImpl.java
@@ -0,0 +1,44 @@
+/*
+ * 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.libraries.rcs.simpleclient.provisioning;
+
+
+/**
+ * Actual implementation build upon ProvisioningManager.
+ */
+public class ProvisioningControllerImpl implements ProvisioningController {
+
+    @Override
+    public void triggerProvisioning() {
+        throw new IllegalStateException("Not implemented!");
+    }
+
+    @Override
+    public void onConfigurationChange(ProvisioningStateChangeCallback cb) {
+        throw new IllegalStateException("Not implemented!");
+    }
+
+    @Override
+    public boolean isRcsVolteSingleRegistrationCapable() {
+        throw new IllegalStateException("Not implemented.");
+    }
+
+    @Override
+    public void unRegister() {
+        throw new IllegalStateException("Not implemented.");
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/ProvisioningStateChangeCallback.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/ProvisioningStateChangeCallback.java
new file mode 100644
index 0000000..17a0291
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/ProvisioningStateChangeCallback.java
@@ -0,0 +1,24 @@
+/*
+ * 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.libraries.rcs.simpleclient.provisioning;
+
+/**
+ * A callback for provisioning state change notifications.
+ */
+public interface ProvisioningStateChangeCallback {
+    void notifyConfigChanged(byte[] configXml);
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java
new file mode 100644
index 0000000..b8b1f21
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/provisioning/StaticConfigProvisioningController.java
@@ -0,0 +1,183 @@
+/*
+ * 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.libraries.rcs.simpleclient.provisioning;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Build.VERSION_CODES;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.ProvisioningManager.RcsProvisioningCallback;
+import android.telephony.ims.RcsClientConfiguration;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RequiresPermission;
+import androidx.annotation.VisibleForTesting;
+
+import java.util.Optional;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * "Fake" provisioning implementation for supplying a static config when testing ProvisioningManager
+ * is unnecessary. State changes are invoked manually.
+ */
+public class StaticConfigProvisioningController implements ProvisioningController {
+
+    private static final String TAG = StaticConfigProvisioningController.class.getSimpleName();
+    private final ProvisioningManager provisioningManager;
+    private final ExecutorService executorService = Executors.newSingleThreadExecutor();
+    private Optional<RcsProvisioningCallback> storedCallback = Optional.empty();
+    private Optional<ProvisioningStateChangeCallback> stateChangeCallback = Optional.empty();
+    private Optional<byte[]> configXmlData = Optional.empty();
+    private Context context;
+
+    private StaticConfigProvisioningController(int subId, Context context) {
+        this.provisioningManager = ProvisioningManager.createForSubscriptionId(subId);
+        this.context = context;
+    }
+
+    @RequiresApi(api = VERSION_CODES.R)
+    public static StaticConfigProvisioningController createWithDefaultSubscriptionId(
+            Context context) {
+        return new StaticConfigProvisioningController(
+                SubscriptionManager.getActiveDataSubscriptionId(), context);
+    }
+
+    /** Create ProvisioningController */
+    public static StaticConfigProvisioningController createForSubscriptionId(int subscriptionId,
+            Context context) {
+        return new StaticConfigProvisioningController(subscriptionId, context);
+    }
+
+    // Static configuration.
+    private RcsClientConfiguration getDefaultClientConfiguration() {
+        SharedPreferences pref = context.getSharedPreferences("CONFIG", context.MODE_PRIVATE);
+
+        return new RcsClientConfiguration(
+                /*rcsVersion=*/ pref.getString("RCS_VERSION", "6.0"),
+                /*rcsProfile=*/ pref.getString("RCS_PROFILE", "UP_1.0"),
+                /*clientVendor=*/ "Goog",
+                /*clientVersion=*/ "RCSAndrd-1.0");
+    }
+
+    @Override
+    @RequiresPermission(value = "Manifest.permission.READ_PRIVILEGED_PHONE_STATE")
+    public void triggerProvisioning() throws ImsException {
+        boolean isRegistered = false;
+        synchronized (this) {
+            isRegistered = storedCallback.isPresent();
+        }
+
+        if (isRegistered) {
+            triggerReconfiguration();
+        } else {
+            register();
+        }
+    }
+
+    @Override
+    public void onConfigurationChange(ProvisioningStateChangeCallback cb) {
+        stateChangeCallback = Optional.of(cb);
+    }
+
+    @RequiresPermission(value = "Manifest.permission.READ_PRIVILEGED_PHONE_STATE")
+    public void register() throws ImsException {
+        register(getDefaultClientConfiguration());
+    }
+
+    @SuppressWarnings("LogConditional")
+    // TODO(b/171976006) Use 'tools:ignore=' in manifest instead.
+    @RequiresPermission(value = "Manifest.permission.READ_PRIVILEGED_PHONE_STATE")
+    public void register(@NonNull RcsClientConfiguration clientConfiguration) throws ImsException {
+        Log.i(TAG, "Using configuration: " + clientConfiguration.toString());
+        provisioningManager.setRcsClientConfiguration(clientConfiguration);
+
+        RcsProvisioningCallback callback =
+                new RcsProvisioningCallback() {
+                    @Override
+                    public void onConfigurationChanged(@NonNull byte[] configXml) {
+                        Log.i(TAG, "RcsProvisioningCallback.onConfigurationChanged called.");
+                        synchronized (this) {
+                            configXmlData = Optional.of(configXml);
+                        }
+                        stateChangeCallback.ifPresent(cb -> cb.notifyConfigChanged(configXml));
+                    }
+
+                    @RequiresApi(api = VERSION_CODES.R)
+                    @Override
+                    public void onConfigurationReset() {
+                        Log.i(TAG, "RcsProvisioningCallback.onConfigurationReset called.");
+                        synchronized (this) {
+                            configXmlData = Optional.empty();
+                        }
+                        stateChangeCallback.ifPresent(cb -> cb.notifyConfigChanged(null));
+                    }
+
+                    @RequiresApi(api = VERSION_CODES.R)
+                    @Override
+                    public void onRemoved() {
+                        Log.i(TAG, "RcsProvisioningCallback.onRemoved called.");
+                        synchronized (this) {
+                            configXmlData = Optional.empty();
+                        }
+                        stateChangeCallback.ifPresent(cb -> cb.notifyConfigChanged(null));
+                    }
+                };
+
+        Log.i(TAG, "Registering the callback.");
+        synchronized (this) {
+            provisioningManager.registerRcsProvisioningCallback(executorService, callback);
+            storedCallback = Optional.of(callback);
+        }
+    }
+
+    @RequiresPermission(value = "Manifest.permission.READ_PRIVILEGED_PHONE_STATE")
+    public void unRegister() {
+        synchronized (this) {
+            RcsProvisioningCallback callback =
+                    storedCallback.orElseThrow(
+                            () -> new IllegalStateException("No callback present."));
+            provisioningManager.unregisterRcsProvisioningCallback(callback);
+            storedCallback = Optional.empty();
+        }
+    }
+
+    @Override
+    @RequiresPermission(value = "Manifest.permission.READ_PRIVILEGED_PHONE_STATE")
+    public boolean isRcsVolteSingleRegistrationCapable() throws ImsException {
+        return provisioningManager.isRcsVolteSingleRegistrationCapable();
+    }
+
+    public synchronized byte[] getLatestConfiguration() {
+        return configXmlData.orElseThrow(() -> new IllegalStateException("No config present"));
+    }
+
+    @VisibleForTesting
+    @RequiresPermission(value = "Manifest.permission.READ_PRIVILEGED_PHONE_STATE")
+    void triggerReconfiguration() {
+        provisioningManager.triggerRcsReconfiguration();
+    }
+
+    @VisibleForTesting
+    ProvisioningManager getProvisioningManager() {
+        return provisioningManager;
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/MessageConverter.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/MessageConverter.java
new file mode 100644
index 0000000..e3a091d
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/MessageConverter.java
@@ -0,0 +1,132 @@
+/*
+ * 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.libraries.rcs.simpleclient.registration;
+
+import android.telephony.ims.SipMessage;
+
+import gov.nist.javax.sip.header.SIPHeader;
+import gov.nist.javax.sip.message.SIPMessage;
+import gov.nist.javax.sip.parser.ParseExceptionListener;
+import gov.nist.javax.sip.parser.StringMsgParser;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.text.ParseException;
+import java.util.Iterator;
+
+import javax.sip.header.ContentLengthHeader;
+import javax.sip.message.Message;
+import javax.sip.message.Request;
+import javax.sip.message.Response;
+
+/***
+ * Class responsible of converting an RCS SIP Message
+ * {@link Message} to a Platform SIP message
+ * {@link SipMessage} and vice versa.
+ */
+public final class MessageConverter {
+
+    private MessageConverter() {
+    }
+
+    public static SipMessage toPlatformMessage(Message message) {
+        String startLine;
+        if (message instanceof Request) {
+            startLine = getRequestStartLine((Request) message);
+        } else {
+            startLine = getResponseStartLine((Response) message);
+        }
+
+        StringBuilder headers = new StringBuilder();
+        for (Iterator<SIPHeader> it = ((SIPMessage) message).getHeaders(); it.hasNext(); ) {
+            SIPHeader header = it.next();
+            if (header instanceof ContentLengthHeader) {
+                continue;
+            }
+            headers.append(header);
+        }
+
+        int length = message.getRawContent() != null ? message.getRawContent().length : 0;
+        headers
+                .append(SIPHeader.CONTENT_LENGTH)
+                .append(": ")
+                .append(length)
+                .append("\r\n");
+
+        byte[] rawContent = message.getRawContent();
+        rawContent = rawContent == null ? new byte[0] : message.getRawContent();
+        return new SipMessage(startLine, headers.toString(), rawContent);
+    }
+
+    public static Message toStackMessage(SipMessage message) throws ParseException {
+        // The AOSP version of nist-sip has a parseSIPMessage() method that has a different
+        // contract.
+        // Fallback to parseSIPMessage(byte[] msgBuffer) in case the first attempt fails.
+        Method method;
+        try {
+            method =
+                    StringMsgParser.class.getDeclaredMethod(
+                            "parseSIPMessage",
+                            byte[].class,
+                            boolean.class,
+                            boolean.class,
+                            ParseExceptionListener.class);
+            return (Message)
+                    method.invoke(
+                            new StringMsgParser(),
+                            message.toEncodedMessage(),
+                            true,
+                            false,
+                            (ParseExceptionListener)
+                                    (ex, sipMessage, headerClass, headerText, messageText) -> {
+                                        throw ex;
+                                    });
+        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+            try {
+                method = StringMsgParser.class.getDeclaredMethod("parseSIPMessage", byte[].class);
+                return (Message) method.invoke(new StringMsgParser(), message.toEncodedMessage());
+            } catch (IllegalAccessException | InvocationTargetException
+                    | NoSuchMethodException ex) {
+                ex.printStackTrace();
+                throw new ParseException("Failed to invoke parseSIPMessage", 0);
+            }
+        }
+    }
+
+    private static String getRequestStartLine(Request request) {
+        StringBuilder startLine = new StringBuilder();
+
+        startLine.append(request.getMethod());
+        startLine.append(" ");
+        startLine.append(request.getRequestURI());
+        startLine.append(" SIP/2.0\r\n");
+
+        return startLine.toString();
+    }
+
+    private static String getResponseStartLine(Response response) {
+        StringBuilder startLine = new StringBuilder();
+
+        startLine.append("SIP/2.0 ");
+        startLine.append(response.getStatusCode());
+        startLine.append(" ");
+        startLine.append(response.getReasonPhrase());
+        startLine.append("\r\n");
+
+        return startLine.toString();
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationController.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationController.java
new file mode 100644
index 0000000..8bbe327
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationController.java
@@ -0,0 +1,35 @@
+/*
+ * 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.libraries.rcs.simpleclient.registration;
+
+import com.android.libraries.rcs.simpleclient.service.ImsService;
+
+/**
+ * Access to registration functionality.
+ */
+public interface RegistrationController {
+
+    /**
+     * Register the given ImsService with the backend and use the callback to return a SipSession
+     * for sending and receiving SIP messages.
+     */
+    void register(ImsService imsService, RegistrationStateChangeCallback callback);
+
+    void deregister();
+
+    void onRegistrationStateChange(RegistrationStateChangeCallback callback);
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java
new file mode 100644
index 0000000..6a70f3d
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationControllerImpl.java
@@ -0,0 +1,399 @@
+/*
+ * 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.libraries.rcs.simpleclient.registration;
+
+import android.os.Build.VERSION_CODES;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.DelegateRequest;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsManager;
+import android.telephony.ims.SipDelegateConfiguration;
+import android.telephony.ims.SipDelegateConnection;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+import android.telephony.ims.stub.DelegateConnectionMessageCallback;
+import android.telephony.ims.stub.DelegateConnectionStateCallback;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+
+import com.android.libraries.rcs.simpleclient.protocol.sip.SipSession;
+import com.android.libraries.rcs.simpleclient.protocol.sip.SipSessionConfiguration;
+import com.android.libraries.rcs.simpleclient.protocol.sip.SipSessionListener;
+import com.android.libraries.rcs.simpleclient.service.ImsService;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+
+import java.text.ParseException;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+
+import javax.sip.message.Message;
+
+/**
+ * Actual implementation built upon SipDelegateConnection as a SIP transport.
+ * Feature tag registration state changes will trigger callbacks SimpleRcsClient to
+ * enable/disable related ImsServices.
+ */
+@RequiresApi(api = VERSION_CODES.R)
+public class RegistrationControllerImpl implements RegistrationController {
+    private static final String TAG = RegistrationControllerImpl.class.getCanonicalName();
+
+    private final Executor executor;
+    private final int subscriptionId;
+    private SipDelegateManager sipDelegateManager;
+    private RegistrationContext context;
+    private RegistrationStateChangeCallback callback;
+
+    public RegistrationControllerImpl(int subscriptionId, Executor executor,
+            ImsManager imsManager) {
+        this.subscriptionId = subscriptionId;
+        this.executor = executor;
+        this.sipDelegateManager = imsManager.getSipDelegateManager(subscriptionId);
+    }
+
+    @Override
+    public void register(ImsService imsService, RegistrationStateChangeCallback callback) {
+        Log.i(TAG, "register");
+        this.callback = callback;
+        context = new RegistrationContext(this, imsService);
+        context.register();
+    }
+
+    @Override
+    public void deregister() {
+        Log.i(TAG, "deregister");
+        if (context != null && context.sipDelegateConnection != null) {
+            sipDelegateManager.destroySipDelegate(context.sipDelegateConnection,
+                    SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        }
+    }
+
+    @Override
+    public void onRegistrationStateChange(RegistrationStateChangeCallback callback) {
+        throw new IllegalStateException("Not implemented!");
+    }
+
+    /**
+     * Envelopes the registration data for a single ImsService instance.
+     */
+    private class RegistrationContext implements SipSession, SipSessionConfiguration {
+
+        private final RegistrationControllerImpl controller;
+        private final ImsService imsService;
+        private final SettableFuture<SipSession> sessionFuture = SettableFuture.create();
+
+        protected SipDelegateConnection sipDelegateConnection;
+        private SipDelegateConfiguration mConfiguration;
+        private final DelegateConnectionStateCallback connectionCallback =
+                new DelegateConnectionStateCallback() {
+
+                    @Override
+                    public void onCreated(SipDelegateConnection c) {
+                        sipDelegateConnection = c;
+                    }
+
+                    @Override
+                    public void onConfigurationChanged(
+                            SipDelegateConfiguration registeredSipConfig) {
+                        Log.d(
+                                TAG,
+                                "onSipConfigurationChanged: version="
+                                        + registeredSipConfig.getVersion());
+                        Log.i(TAG, "onSipConfigurationChanged: " + registeredSipConfig);
+                        dumpConfig(registeredSipConfig);
+                        RegistrationContext.this.mConfiguration = registeredSipConfig;
+                    }
+
+                    @Override
+                    public void onFeatureTagStatusChanged(
+                            @NonNull DelegateRegistrationState registrationState,
+                            @NonNull Set<FeatureTagState> deniedFeatureTags) {
+                        dumpFeatureTagState(registrationState, deniedFeatureTags);
+                        if (registrationState
+                                .getRegisteredFeatureTags()
+                                .containsAll(imsService.getFeatureTags())) {
+                            // registered;
+                            callback.onSuccess(RegistrationContext.this);
+                        } else {
+                            callback.onFailure("feature tag not registered");
+                        }
+                    }
+
+                    @Override
+                    public void onDestroyed(int reason) {
+                        Log.d(TAG, "onDestroyed:" + reason);
+                        callback.onFailure("delegate destroyed");
+
+                    }
+                };
+        private SipSessionListener sipSessionListener;
+        // Callback for incoming messages on the modem connection
+        private final DelegateConnectionMessageCallback messageCallback =
+                new DelegateConnectionMessageCallback() {
+                    @Override
+                    public void onMessageReceived(@NonNull SipMessage message) {
+                        message = repairHeaderSection(message);
+                        SipSessionListener listener = sipSessionListener;
+                        if (listener != null) {
+                            try {
+                                listener.onMessageReceived(
+                                        MessageConverter.toStackMessage(message));
+                            } catch (ParseException e) {
+                                // TODO: logging here
+                            }
+                        }
+                    }
+
+                    @Override
+                    public void onMessageSendFailure(@NonNull String viaTransactionId, int reason) {
+                        Log.i(TAG, "onMessageSendFailure: viaTransactionId:"
+                                + viaTransactionId + ", reason:" + reason);
+                    }
+
+                    @Override
+                    public void onMessageSent(@NonNull String viaTransactionId) {
+                        Log.i(TAG, "onMessageSent: viaTransactionId:" + viaTransactionId);
+                    }
+
+                };
+
+        public RegistrationContext(RegistrationControllerImpl controller,
+                ImsService imsService) {
+            this.controller = controller;
+            this.imsService = imsService;
+        }
+
+        public ListenableFuture<SipSession> getFuture() {
+            return sessionFuture;
+        }
+
+        @Override
+        public SipSessionConfiguration getSessionConfiguration() {
+            return this;
+        }
+
+        public void register() {
+            Log.i(TAG, "createSipDelegate");
+            DelegateRequest request = new DelegateRequest(imsService.getFeatureTags());
+            try {
+                controller.sipDelegateManager.createSipDelegate(
+                        request, controller.executor, connectionCallback, messageCallback);
+            } catch (ImsException e) {
+                // TODO: ...
+            }
+        }
+
+        private void dumpFeatureTagState(DelegateRegistrationState registrationState,
+                Set<FeatureTagState> deniedFeatureTags) {
+            StringBuilder stringBuilder = new StringBuilder(
+                    "onFeatureTagStatusChanged ").append(
+                    " deniedFeatureTags:[");
+            Iterator<FeatureTagState> iterator = deniedFeatureTags.iterator();
+            while (iterator.hasNext()) {
+                FeatureTagState featureTagState = iterator.next();
+                stringBuilder.append(featureTagState.getFeatureTag()).append(" ").append(
+                        featureTagState.getState());
+            }
+            Set<String> registeredFt = registrationState.getRegisteredFeatureTags();
+            Iterator<String> iteratorStr = registeredFt.iterator();
+            stringBuilder.append("] registeredFT:[");
+            while (iteratorStr.hasNext()) {
+                String ft = iteratorStr.next();
+                stringBuilder.append(ft).append(" ");
+            }
+            stringBuilder.append("]");
+            String result = stringBuilder.toString();
+            Log.i(TAG, result);
+        }
+
+        private void dumpConfig(SipDelegateConfiguration config) {
+            String result = "SipDelegateConfiguration{"
+                    + "mVersion=" + config.getVersion()
+                    + ", \n\tmTransportType=" + config.getTransportType()
+                    + ", \n\tmLocalAddr=" + config.getLocalAddress()
+                    + ", \n\tmSipServerAddr=" + config.getSipServerAddress()
+                    + ", \n\tmIsSipCompactFormEnabled=" + config.isSipCompactFormEnabled()
+                    + ", \n\tmIsSipKeepaliveEnabled=" + config.isSipKeepaliveEnabled()
+                    + ", \n\tmMaxUdpPayloadSize=" + config.getMaxUdpPayloadSizeBytes()
+                    + ", \n\tmPublicUserIdentifier=" + config.getPublicUserIdentifier()
+                    + ", \n\tmPrivateUserIdentifier=" + config.getPrivateUserIdentifier()
+                    + ", \n\tmHomeDomain=" + config.getHomeDomain()
+                    + ", \n\tmImei=" + config.getImei()
+                    + ", \n\tmGruu=" + config.getPublicGruuUri()
+                    + ", \n\tmSipAuthHeader=" + config.getSipAuthenticationHeader()
+                    + ", \n\tmSipAuthNonce=" + config.getSipAuthenticationNonce()
+                    + ", \n\tmServiceRouteHeader=" + config.getSipServiceRouteHeader()
+                    + ", \n\tmPathHeader=" + config.getSipPathHeader()
+                    + ", \n\tmUserAgentHeader=" + config.getSipUserAgentHeader()
+                    + ", \n\tmContactUserParam=" + config.getSipContactUserParameter()
+                    + ", \n\tmPaniHeader=" + config.getSipPaniHeader()
+                    + ", \n\tmPlaniHeader=" + config.getSipPlaniHeader()
+                    + ", \n\tmCniHeader=" + config.getSipCniHeader()
+                    + ", \n\tmAssociatedUriHeader=" + config.getSipAssociatedUriHeader()
+                    + ", \n\tmIpSecConfiguration=" + config.getIpSecConfiguration()
+                    + ", \n\tmNatConfiguration=" + config.getNatSocketAddress() + '}';
+            Log.i(TAG, result);
+        }
+
+        @Override
+        public void setSessionListener(SipSessionListener listener) {
+            sipSessionListener = listener;
+        }
+
+        @Override
+        public ListenableFuture<Boolean> send(Message message) {
+            sipDelegateConnection.sendMessage(MessageConverter.toPlatformMessage(message),
+                    getVersion());
+            // TODO: check on transaction
+            return Futures.immediateFuture(true);
+        }
+
+        // Config values here.
+
+        @Override
+        public long getVersion() {
+            return mConfiguration.getVersion();
+        }
+
+        @Override
+        public String getOutboundProxyAddr() {
+            return mConfiguration.getSipServerAddress().getAddress().getHostAddress();
+        }
+
+        @Override
+        public int getOutboundProxyPort() {
+            return mConfiguration.getSipServerAddress().getPort();
+        }
+
+        @Override
+        public String getLocalIpAddress() {
+            return mConfiguration.getLocalAddress().getAddress().getHostAddress();
+        }
+
+        @Override
+        public int getLocalPort() {
+            return mConfiguration.getLocalAddress().getPort();
+        }
+
+        @Override
+        public String getSipTransport() {
+            int sipTransport = mConfiguration.getTransportType();
+            return (sipTransport == SipDelegateConfiguration.SIP_TRANSPORT_TCP) ? "TCP" : "UDP";
+        }
+
+        @Override
+        public String getPublicUserIdentity() {
+            return mConfiguration.getPublicUserIdentifier();
+        }
+
+        @Override
+        public String getDomain() {
+            return mConfiguration.getHomeDomain();
+        }
+
+        @Override
+        public List<String> getAssociatedUris() {
+            String associatedUris = mConfiguration.getSipAssociatedUriHeader();
+            if (!TextUtils.isEmpty(associatedUris)) {
+                return Splitter.on(',').trimResults(CharMatcher.anyOf("<>")).splitToList(
+                        associatedUris);
+            }
+
+            return ImmutableList.of();
+        }
+
+        @Override
+        public String getSecurityVerifyHeader() {
+            SipDelegateConfiguration.IpSecConfiguration c = mConfiguration.getIpSecConfiguration();
+            if (c == null) {
+                return null;
+            }
+            return c.getSipSecurityVerifyHeader();
+        }
+
+        @Override
+        public List<String> getServiceRouteHeaders() {
+            String serviceRoutes =
+                    mConfiguration.getSipServiceRouteHeader();
+            if (TextUtils.isEmpty(serviceRoutes)) {
+                return Collections.emptyList();
+            }
+            return Splitter.on(',').trimResults().splitToList(serviceRoutes);
+        }
+
+        @Override
+        public String getContactUser() {
+            return mConfiguration.getSipContactUserParameter();
+        }
+
+        @Override
+        public String getImei() {
+            return mConfiguration.getImei();
+        }
+
+        @Override
+        public String getPaniHeader() {
+            return mConfiguration.getSipPaniHeader();
+        }
+
+        @Override
+        public String getPlaniHeader() {
+            return mConfiguration.getSipPlaniHeader();
+        }
+
+        @Override
+        public String getUserAgentHeader() {
+            return mConfiguration.getSipUserAgentHeader();
+        }
+
+        @Override
+        public int getMaxPayloadSizeOnUdp() {
+            return mConfiguration.getMaxUdpPayloadSizeBytes() > 0
+                    ? mConfiguration.getMaxUdpPayloadSizeBytes() : 1500;
+        }
+
+        /**
+         * There is a modem issue where "ia:" is returned back instead of "Via:". Fix that locally
+         * for now.
+         * @return A SipMessage with the corrected header section.
+         */
+        private SipMessage repairHeaderSection(SipMessage message) {
+            String headers = message.getHeaderSection();
+
+            if (headers.startsWith("ia:")) {
+                headers = "V" + headers;
+                Log.i(TAG, "repairHeaderSection: detected malformed via: "
+                        + message.getHeaderSection().substring(0, 10) + "->"
+                        + headers.substring(0, 10));
+            }
+            return new SipMessage(message.getStartLine(), headers, message.getContent());
+        }
+    }
+}
+
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationStateChangeCallback.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationStateChangeCallback.java
new file mode 100644
index 0000000..570b313
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/registration/RegistrationStateChangeCallback.java
@@ -0,0 +1,40 @@
+/*
+ * 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.libraries.rcs.simpleclient.registration;
+
+import com.android.libraries.rcs.simpleclient.protocol.sip.SipSession;
+import com.android.libraries.rcs.simpleclient.service.ImsService;
+
+/**
+ * Callback for Registration state changes.
+ */
+public interface RegistrationStateChangeCallback {
+
+    /**
+     * The given feature tags are registered with the backend and the service would be able to
+     * send and receive messages.
+     *
+     * @param imsService the newly registered service.
+     */
+    void notifyRegStateChanged(ImsService imsService);
+
+    /**callback for successful session creation */
+    void onSuccess(SipSession sipSession);
+
+    /**callback for failed session creation. */
+    void onFailure(String reason);
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/ImsService.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/ImsService.java
new file mode 100644
index 0000000..e4dca1a
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/ImsService.java
@@ -0,0 +1,51 @@
+/*
+ * 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.libraries.rcs.simpleclient.service;
+
+import com.android.libraries.rcs.simpleclient.SimpleRcsClientContext;
+
+import java.util.Set;
+
+/**
+ * Covers service state and feature tag association.
+ */
+public interface ImsService {
+
+    /**
+     * Associated feature tags.
+     * Services will started and stopped according to the registration state of the feature tags.
+     */
+    Set<String> getFeatureTags();
+
+    /**
+     * Services started when their feature tags are enabled from the
+     * {@link com.android.libraries.rcs.simpleclient.registration.RegistrationController}.
+     * Context is made available to the ImsService here.
+     */
+    void start(SimpleRcsClientContext context);
+
+    /**
+     * Services stopped when their feature tags are disabled from
+     * {@link com.android.libraries.rcs.simpleclient.registration.RegistrationController}
+     */
+    void stop();
+
+    /**
+     * Simple callback mechanism for monitoring feature tag/ims service state.
+     */
+    void onStateChange(StateChangeCallback cb);
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/StateChangeCallback.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/StateChangeCallback.java
new file mode 100644
index 0000000..038023a
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/StateChangeCallback.java
@@ -0,0 +1,24 @@
+/*
+ * 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.libraries.rcs.simpleclient.service;
+
+/**
+ * Callback for ImsService state changes.
+ */
+public interface StateChangeCallback {
+
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/ChatServiceException.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/ChatServiceException.java
new file mode 100644
index 0000000..bc2c611
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/ChatServiceException.java
@@ -0,0 +1,94 @@
+/*
+ * 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.libraries.rcs.simpleclient.service.chat;
+
+import android.text.TextUtils;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class defines an exception that can be thrown during the operation in {@link
+ * MinimalCpmChatService}
+ */
+public final class ChatServiceException extends Exception {
+
+    public static final int CODE_ERROR_UNSPECIFIED = 0;
+    public static final int CODE_ERROR_SEND_MESSAGE_FAILED = 1;
+    private int mCode = CODE_ERROR_UNSPECIFIED;
+
+    /**
+     * A new {@link ChatServiceException} with an unspecified {@link ErrorCode} code.
+     *
+     * @param message an optional message to detail the error condition more specifically.
+     */
+    public ChatServiceException(@Nullable String message) {
+        super(getMessage(message, CODE_ERROR_UNSPECIFIED));
+    }
+
+    /**
+     * A new {@link ChatServiceException} that includes an {@link ErrorCode} error code.
+     *
+     * @param message an optional message to detail the error condition more specifically.
+     */
+    public ChatServiceException(@Nullable String message, @ErrorCode int code) {
+        super(getMessage(message, code));
+        mCode = code;
+    }
+
+    /**
+     * A new {@link ChatServiceException} that includes an {@link ErrorCode} error code and a {@link
+     * Throwable} that contains the original error that was thrown to lead to this Exception.
+     *
+     * @param message an optional message to detail the error condition more specifically.
+     * @param cause   the {@link Throwable} that caused this {@link ChatServiceException} to be
+     *                created.
+     */
+    public ChatServiceException(
+            @Nullable String message, @ErrorCode int code, @Nullable Throwable cause) {
+        super(getMessage(message, code), cause);
+        mCode = code;
+    }
+
+    private static String getMessage(String message, int code) {
+        StringBuilder builder;
+        if (!TextUtils.isEmpty(message)) {
+            builder = new StringBuilder(message);
+            builder.append(" (code: ");
+            builder.append(code);
+            builder.append(")");
+            return builder.toString();
+        } else {
+            return "code: " + code;
+        }
+    }
+
+    @ErrorCode
+    public int getCode() {
+        return mCode;
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            CODE_ERROR_UNSPECIFIED,
+    })
+    public @interface ErrorCode {
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/ChatServiceListener.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/ChatServiceListener.java
new file mode 100644
index 0000000..5fb9dee
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/ChatServiceListener.java
@@ -0,0 +1,27 @@
+/*
+ * 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.libraries.rcs.simpleclient.service.chat;
+
+/** Listener for chat service events */
+public interface ChatServiceListener {
+
+    /**
+     * Received a new incoming chat session from the RCS server. The session is ready to exchange
+     * messages since it is already established once this callback is called.
+     */
+    void onIncomingSession(SimpleChatSession session);
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/ChatSessionListener.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/ChatSessionListener.java
new file mode 100644
index 0000000..eab571e
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/ChatSessionListener.java
@@ -0,0 +1,30 @@
+/*
+ * 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.libraries.rcs.simpleclient.service.chat;
+
+import com.android.libraries.rcs.simpleclient.protocol.cpim.SimpleCpimMessage;
+
+/** Listener for chat session events */
+public interface ChatSessionListener {
+
+    /**
+     * Received a new CPIM message via the {@link SimpleChatSession} associated with this listener.
+     *
+     * @param message Received message in the form of {@link SimpleCpimMessage}
+     */
+    void onMessageReceived(SimpleCpimMessage message);
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/MinimalCpmChatService.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/MinimalCpmChatService.java
new file mode 100644
index 0000000..4b913f7
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/MinimalCpmChatService.java
@@ -0,0 +1,206 @@
+/*
+ * 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.libraries.rcs.simpleclient.service.chat;
+
+import android.content.Context;
+import android.telephony.ims.SipDelegateConnection;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.android.libraries.rcs.simpleclient.SimpleRcsClientContext;
+import com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpManager;
+import com.android.libraries.rcs.simpleclient.protocol.sip.SipSession;
+import com.android.libraries.rcs.simpleclient.protocol.sip.SipSessionListener;
+import com.android.libraries.rcs.simpleclient.protocol.sip.SipUtils;
+import com.android.libraries.rcs.simpleclient.service.ImsService;
+import com.android.libraries.rcs.simpleclient.service.StateChangeCallback;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+
+import gov.nist.javax.sip.message.SIPRequest;
+import gov.nist.javax.sip.message.SIPResponse;
+
+import java.text.ParseException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.sip.message.Request;
+import javax.sip.message.Response;
+
+/**
+ * Minimal CPM chat session service that provides the interface creating a {@link SimpleChatSession}
+ * instance using {@link SipDelegateConnection}.
+ */
+public class MinimalCpmChatService implements ImsService {
+    public static final String CPM_SESSION_TAG =
+            "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session\"";
+    private static final String TAG = MinimalCpmChatService.class.getSimpleName();
+    private final Map<String, SimpleChatSession> mTransactions = new HashMap<>();
+    private final Map<String, SimpleChatSession> mDialogs = new HashMap<>();
+
+    private final MsrpManager mMsrpManager;
+    private SimpleRcsClientContext mContext;
+
+    @Nullable
+    private ChatServiceListener mListener;
+
+    private final SipSessionListener mSipSessionListener =
+            sipMessage -> {
+                if (sipMessage instanceof SIPRequest) {
+                    handleRequest((SIPRequest) sipMessage);
+                } else if (sipMessage instanceof SIPResponse) {
+                    handleResponse((SIPResponse) sipMessage);
+                }
+            };
+
+    public MinimalCpmChatService(Context context) {
+        mMsrpManager = new MsrpManager(context);
+    }
+
+    @Override
+    public Set<String> getFeatureTags() {
+        return ImmutableSet.of(CPM_SESSION_TAG);
+    }
+
+    @Override
+    public void start(SimpleRcsClientContext context) {
+        mContext = context;
+        context.getSipSession().setSessionListener(mSipSessionListener);
+    }
+
+    @Override
+    public void stop() {
+    }
+
+    @Override
+    public void onStateChange(StateChangeCallback cb) {
+    }
+
+    /**
+     * Start an originating 1:1 chat session interacting with the RCS server.
+     *
+     * @param telUriContact The remote contact in the from of TEL URI
+     * @return The future will be completed with SimpleChatSession once the session is established
+     * successfully. If the session fails for any reason, return the failed future with {@link
+     * ChatServiceException}
+     */
+    public ListenableFuture<SimpleChatSession> startOriginatingChatSession(String telUriContact) {
+        Log.i(TAG, "startOriginatingChatSession");
+        SimpleChatSession session = new SimpleChatSession(mContext, this, mMsrpManager);
+        return Futures.transform(
+                session.start(telUriContact), v -> session, MoreExecutors.directExecutor());
+    }
+
+    ListenableFuture<Boolean> sendSipRequest(SIPRequest msg, SimpleChatSession session) {
+        Log.i(TAG, "sendSipRequest:\r\n" + msg);
+        if (!TextUtils.equals(msg.getMethod(), Request.ACK)) {
+            mTransactions.put(msg.getTransactionId(), session);
+        }
+
+        if (TextUtils.equals(msg.getMethod(), Request.BYE)) {
+            mDialogs.remove(msg.getDialogId(/* isServer= */ false));
+        }
+
+        SipSession sipSession = mContext.getSipSession();
+        return sipSession.send(msg);
+    }
+
+    ListenableFuture<Boolean> sendSipResponse(SIPResponse msg, SimpleChatSession session) {
+        Log.i(TAG, "sendSipResponse:\r\n" + msg);
+        if (TextUtils.equals(msg.getCSeq().getMethod(), Request.BYE)) {
+            mDialogs.remove(msg.getDialogId(/* isServer= */ true));
+        } else if (TextUtils.equals(msg.getCSeq().getMethod(), Request.INVITE)
+                && msg.getStatusCode() == Response.OK) {
+            // Cache the dialog in order to route in-dialog request to the corresponding session.
+            mDialogs.put(msg.getDialogId(/* isServer= */ true), session);
+        }
+        SipSession sipSession = mContext.getSipSession();
+        return sipSession.send(msg);
+    }
+
+    private void handleRequest(SIPRequest request) {
+        Log.i(TAG, "handleRequest:\r\n" + request);
+        String dialogId = request.getDialogId(/* isServer= */ true);
+        if (mDialogs.containsKey(dialogId)) {
+            SimpleChatSession session = mDialogs.get(dialogId);
+            session.receiveMessage(request);
+        } else if (TextUtils.equals(request.getMethod(), Request.INVITE)) {
+            SimpleChatSession session = new SimpleChatSession(mContext, this, mMsrpManager);
+            session
+                    .start(request)
+                    .addListener(
+                            () -> {
+                                ChatServiceListener listener = mListener;
+                                if (listener != null) {
+                                    listener.onIncomingSession(session);
+                                }
+                            },
+                            MoreExecutors.directExecutor());
+        } else {
+            // Reject non-INVITE request.
+            try {
+                SIPResponse response =
+                        SipUtils.buildInviteResponse(
+                                mContext.getSipSession().getSessionConfiguration(),
+                                request,
+                                Response.METHOD_NOT_ALLOWED,
+                                null);
+                sendSipResponse(response, /* session= */ null)
+                        .addListener(() -> {
+                        }, MoreExecutors.directExecutor());
+            } catch (ParseException e) {
+                Log.e(TAG, "Exception while sending response", e);
+            }
+        }
+    }
+
+    private void handleResponse(SIPResponse response) {
+        Log.i(TAG, "handleResponse:\r\n" + response);
+        // catch the exception because abnormal response always causes App to crash.
+        try {
+            SimpleChatSession session = mTransactions.get(response.getTransactionId());
+            if (session != null) {
+                if (response.isFinalResponse()) {
+                    mTransactions.remove(response.getTransactionId());
+
+                    // Cache the dialog in order to route in-dialog request to the corresponding
+                    // session.
+                    if (TextUtils.equals(response.getCSeq().getMethod(), Request.INVITE)
+                            && response.getStatusCode() == Response.OK) {
+                        mDialogs.put(response.getDialogId(/* isServer= */ false), session);
+                    }
+                }
+
+                session.receiveMessage(response);
+            }
+        } catch (Exception e) {
+            Log.e(TAG, e.getMessage());
+            e.printStackTrace();
+        }
+    }
+
+    /** Set new listener for the chat service. */
+    public void setListener(@Nullable ChatServiceListener listener) {
+        mListener = listener;
+    }
+}
diff --git a/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/SimpleChatSession.java b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/SimpleChatSession.java
new file mode 100644
index 0000000..fbeb205
--- /dev/null
+++ b/testapps/TestRcsApp/aosp_test_rcsclient/src/com/android/libraries/rcs/simpleclient/service/chat/SimpleChatSession.java
@@ -0,0 +1,498 @@
+/*
+ * 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.libraries.rcs.simpleclient.service.chat;
+
+import static com.android.libraries.rcs.simpleclient.protocol.cpim.CpimUtils.CPIM_CONTENT_TYPE;
+import static com.android.libraries.rcs.simpleclient.service.chat.ChatServiceException.CODE_ERROR_SEND_MESSAGE_FAILED;
+import static com.android.libraries.rcs.simpleclient.service.chat.ChatServiceException.CODE_ERROR_UNSPECIFIED;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.android.libraries.rcs.simpleclient.SimpleRcsClientContext;
+import com.android.libraries.rcs.simpleclient.protocol.cpim.CpimUtils;
+import com.android.libraries.rcs.simpleclient.protocol.cpim.SimpleCpimMessage;
+import com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpChunk;
+import com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpChunk.Continuation;
+import com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpChunkHeader;
+import com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpConstants;
+import com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpManager;
+import com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpSession;
+import com.android.libraries.rcs.simpleclient.protocol.msrp.MsrpUtils;
+import com.android.libraries.rcs.simpleclient.protocol.sdp.SdpUtils;
+import com.android.libraries.rcs.simpleclient.protocol.sdp.SimpleSdpMessage;
+import com.android.libraries.rcs.simpleclient.protocol.sip.SipSessionConfiguration;
+import com.android.libraries.rcs.simpleclient.protocol.sip.SipUtils;
+import com.android.libraries.rcs.simpleclient.service.chat.ChatServiceException.ErrorCode;
+
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.SettableFuture;
+
+import gov.nist.javax.sip.header.To;
+import gov.nist.javax.sip.header.ims.PAssertedIdentityHeader;
+import gov.nist.javax.sip.message.SIPRequest;
+import gov.nist.javax.sip.message.SIPResponse;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.UUID;
+
+import javax.sip.address.URI;
+import javax.sip.message.Message;
+import javax.sip.message.Request;
+import javax.sip.message.Response;
+
+/**
+ * Simple chat session implementation in order to send/receive a text message via SIP/MSRP
+ * connection. Currently, this supports only a outgoing CPM session.
+ */
+public class SimpleChatSession {
+    private static final String TAG = SimpleChatSession.class.getSimpleName();
+    private final SimpleRcsClientContext mContext;
+    private final MinimalCpmChatService mService;
+    private final MsrpManager mMsrpManager;
+    private final String mConversationId = UUID.randomUUID().toString();
+    private SettableFuture<Void> mStartFuture;
+    @Nullable
+    private SIPRequest mInviteRequest;
+    @Nullable
+    private URI mRemoteUri;
+    @Nullable
+    private SimpleSdpMessage mRemoteSdp;
+    @Nullable
+    private SimpleSdpMessage mLocalSdp;
+    @Nullable
+    private MsrpSession mMsrpSession;
+    @Nullable
+    private ChatSessionListener mListener;
+
+
+    public SimpleChatSession(
+            SimpleRcsClientContext context, MinimalCpmChatService service,
+            MsrpManager msrpManager) {
+        mService = service;
+        mContext = context;
+        mMsrpManager = msrpManager;
+    }
+
+    public URI getRemoteUri() {
+        return mRemoteUri;
+    }
+
+    /** Send a text message via MSRP session associated with this session. */
+    public ListenableFuture<Void> sendMessage(String msg) {
+        MsrpSession session = mMsrpSession;
+        if (session == null || mRemoteSdp == null || mLocalSdp == null) {
+            Log.e(TAG, "Session is not established");
+            return Futures.immediateFailedFuture(
+                    new IllegalStateException("Session is not established"));
+        }
+
+        // Build a new CPIM message and send it out through the MSRP session.
+        SimpleCpimMessage cpim = CpimUtils.createForText(msg);
+        Log.i(TAG, "Encoded CPIM:" + cpim.encode());
+
+        byte[] content = cpim.encode().getBytes(UTF_8);
+        MsrpChunk msrpChunk =
+                MsrpChunk.newBuilder()
+                        .method(MsrpChunk.Method.SEND)
+                        .transactionId(MsrpUtils.generateRandomId())
+                        .content(content)
+                        .continuation(Continuation.COMPLETE)
+                        .addHeader(MsrpConstants.HEADER_TO_PATH, mRemoteSdp.getPath().get())
+                        .addHeader(MsrpConstants.HEADER_FROM_PATH, mLocalSdp.getPath().get())
+                        .addHeader(MsrpConstants.HEADER_FAILURE_REPORT,
+                                MsrpConstants.REPORT_VALUE_YES)
+                        .addHeader(MsrpConstants.HEADER_SUCCESS_REPORT,
+                                MsrpConstants.REPORT_VALUE_NO)
+                        .addHeader(
+                                MsrpConstants.HEADER_BYTE_RANGE,
+                                String.format("1-%d/%d", content.length, content.length))
+                        .addHeader(MsrpConstants.HEADER_MESSAGE_ID, MsrpUtils.generateRandomId())
+                        .addHeader(MsrpConstants.HEADER_CONTENT_TYPE, CPIM_CONTENT_TYPE)
+                        .build();
+
+        Log.i(TAG, "Send a MSRP chunk: " + msrpChunk);
+        return Futures.transformAsync(session.send(msrpChunk), result -> {
+            if (result == null) {
+                return Futures.immediateFailedFuture(
+                        new ChatServiceException("Failed to send a chunk",
+                                CODE_ERROR_SEND_MESSAGE_FAILED));
+            }
+            if (result.responseCode() != 200) {
+                Log.d(TAG, "Received error response id=" + result.transactionId()
+                        + " code=" + result.responseCode());
+                return Futures.immediateFailedFuture(
+                        new ChatServiceException("Msrp response code: " + result.responseCode(),
+                                CODE_ERROR_SEND_MESSAGE_FAILED));
+            }
+            return Futures.immediateFuture(null);
+        }, MoreExecutors.directExecutor());
+    }
+
+    /** Start outgoing chat session. */
+    ListenableFuture<Void> start(String telUriContact) {
+        if (mStartFuture != null) {
+            return Futures.immediateFailedFuture(
+                    new ChatServiceException("Session already started"));
+        }
+
+        SettableFuture<Void> future = SettableFuture.create();
+        mStartFuture = future;
+        mRemoteUri = SipUtils.createUri(telUriContact);
+        try {
+            SipSessionConfiguration configuration =
+                    mContext.getSipSession().getSessionConfiguration();
+            SimpleSdpMessage sdp = SdpUtils.createSdpForMsrp(configuration.getLocalIpAddress(),
+                    false);
+            SIPRequest invite =
+                    SipUtils.buildInvite(
+                            mContext.getSipSession().getSessionConfiguration(),
+                            telUriContact,
+                            mConversationId,
+                            sdp.encode().getBytes(UTF_8));
+            mInviteRequest = invite;
+            mLocalSdp = sdp;
+            Futures.addCallback(
+                    mService.sendSipRequest(invite, this),
+                    new FutureCallback<Boolean>() {
+                        @Override
+                        public void onSuccess(Boolean result) {
+                            Log.i(TAG, "onSuccess:" + result);
+                            if (!result) {
+                                notifyFailure("Failed to send INVITE", CODE_ERROR_UNSPECIFIED);
+                            }
+                        }
+
+                        @Override
+                        public void onFailure(Throwable t) {
+                            Log.i(TAG, "onFailure:" + t.getMessage());
+                            notifyFailure("Failed to send INVITE", CODE_ERROR_UNSPECIFIED);
+                        }
+                    },
+                    MoreExecutors.directExecutor());
+        } catch (ParseException e) {
+            Log.e(TAG, e.getMessage());
+            e.printStackTrace();
+            return Futures.immediateFailedFuture(
+                    new ChatServiceException("Failed to build INVITE"));
+        }
+
+        return future;
+    }
+
+    /** Start incoming chat session. */
+    ListenableFuture<Void> start(SIPRequest invite) {
+        mInviteRequest = invite;
+        int statusCode = Response.OK;
+        if (!SipUtils.hasSdpContent(invite)) {
+            statusCode = Response.NOT_ACCEPTABLE_HERE;
+        } else {
+            try {
+                mRemoteSdp = SimpleSdpMessage.parse(
+                        new ByteArrayInputStream(invite.getRawContent()));
+            } catch (ParseException | IOException e) {
+                statusCode = Response.BAD_REQUEST;
+            }
+        }
+
+        updateRemoteUri(mInviteRequest);
+
+        SipSessionConfiguration configuration = mContext.getSipSession().getSessionConfiguration();
+        SimpleSdpMessage sdp = SdpUtils.createSdpForMsrp(configuration.getLocalIpAddress(), false);
+
+        // Automatically reply back to the invite by building a pre-canned response.
+        try {
+            SIPResponse response = SipUtils.buildInviteResponse(configuration, invite, statusCode,
+                    sdp);
+            mLocalSdp = sdp;
+            return Futures.transform(
+                    mService.sendSipResponse(response, this), result -> null,
+                    MoreExecutors.directExecutor());
+        } catch (ParseException e) {
+            Log.e(TAG, "Exception while building response", e);
+            return Futures.immediateFailedFuture(e);
+        }
+    }
+
+    /** Terminate the current SIP session. */
+    public ListenableFuture<Void> terminate() {
+        if (mInviteRequest == null) {
+            return Futures.immediateFuture(null);
+        }
+        try {
+            if (mMsrpSession != null) {
+                mMsrpSession.terminate();
+            }
+        } catch (IOException e) {
+            return Futures.immediateFailedFuture(
+                    new ChatServiceException(
+                            "Exception while terminating MSRP session", CODE_ERROR_UNSPECIFIED));
+        }
+        try {
+
+            SettableFuture<Void> future = SettableFuture.create();
+            Futures.addCallback(
+                    mService.sendSipRequest(SipUtils.buildBye(mInviteRequest), this),
+                    new FutureCallback<Boolean>() {
+                        @Override
+                        public void onSuccess(Boolean result) {
+                            future.set(null);
+                        }
+
+                        @Override
+                        public void onFailure(Throwable t) {
+                            future.setException(
+                                    new ChatServiceException("Failed to send BYE",
+                                            CODE_ERROR_UNSPECIFIED, t));
+                        }
+                    },
+                    MoreExecutors.directExecutor());
+            return future;
+        } catch (ParseException e) {
+            return Futures.immediateFailedFuture(
+                    new ChatServiceException("Failed to build BYE", CODE_ERROR_UNSPECIFIED));
+        }
+    }
+
+    void receiveMessage(Message msg) {
+        if (msg instanceof SIPRequest) {
+            handleSipRequest((SIPRequest) msg);
+        } else {
+            handleSipResponse((SIPResponse) msg);
+        }
+    }
+
+    private void handleSipRequest(SIPRequest request) {
+        SIPResponse response;
+        if (TextUtils.equals(request.getMethod(), Request.ACK)) {
+            // Terminating session established, start a msrp session.
+            if (mRemoteSdp != null) {
+                startMsrpSession(mRemoteSdp);
+            }
+            return;
+        }
+
+        if (TextUtils.equals(request.getMethod(), Request.BYE)) {
+            response = request.createResponse(Response.OK);
+        } else {
+            // Currently we support only INVITE and BYE.
+            response = request.createResponse(Response.METHOD_NOT_ALLOWED);
+        }
+        Futures.addCallback(
+                mService.sendSipResponse(response, this),
+                new FutureCallback<Boolean>() {
+                    @Override
+                    public void onSuccess(Boolean result) {
+                        if (result) {
+                            Log.d(
+                                    TAG,
+                                    "Response to Call-Id: "
+                                            + response.getCallId().getCallId()
+                                            + " sent successfully");
+                        } else {
+                            Log.d(TAG, "Failed to send response");
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable t) {
+                        Log.d(TAG, "Exception while sending response: ", t);
+                    }
+                },
+                MoreExecutors.directExecutor());
+    }
+
+    private void handleSipResponse(SIPResponse response) {
+        int code = response.getStatusCode();
+
+        // Nothing to do for a provisional response.
+        if (response.isFinalResponse()) {
+            if (code == Response.OK) {
+                handle200OK(response);
+            } else {
+                handleNon200(response);
+            }
+        }
+    }
+
+    private void handleNon200(SIPResponse response) {
+        Log.d(TAG, "Received error response code=" + response.getStatusCode());
+        notifyFailure("Received non-200 INVITE response", CODE_ERROR_UNSPECIFIED);
+    }
+
+    private void handle200OK(SIPResponse response) {
+        if (!SipUtils.hasSdpContent(response)) {
+            notifyFailure("Content is not a SDP", CODE_ERROR_UNSPECIFIED);
+            return;
+        }
+
+        SimpleSdpMessage sdp;
+        try {
+            sdp = SimpleSdpMessage.parse(new ByteArrayInputStream(response.getRawContent()));
+        } catch (ParseException | IOException e) {
+            notifyFailure("Invalid SDP in INVITE", CODE_ERROR_UNSPECIFIED);
+            return;
+        }
+
+        if (mInviteRequest == null) {
+            notifyFailure("No INVITE request sent out", CODE_ERROR_UNSPECIFIED);
+            return;
+        }
+
+        SIPRequest ack = mInviteRequest.createAckRequest((To) response.getToHeader());
+        Futures.addCallback(
+                mService.sendSipRequest(ack, this),
+                new FutureCallback<Boolean>() {
+                    @Override
+                    public void onSuccess(Boolean result) {
+                        if (result) {
+                            startMsrpSession(sdp);
+                            mRemoteSdp = sdp;
+                        } else {
+                            notifyFailure("Failed to send ACK", CODE_ERROR_UNSPECIFIED);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(Throwable t) {
+                        notifyFailure("Failed to send ACK", CODE_ERROR_UNSPECIFIED);
+                    }
+                },
+                MoreExecutors.directExecutor());
+    }
+
+    private void notifyFailure(String message, @ErrorCode int code) {
+        if (mStartFuture != null) {
+            mStartFuture.setException(new ChatServiceException(message, code));
+            mStartFuture = null;
+        }
+    }
+
+    private void notifySuccess() {
+        if (mStartFuture != null) {
+            mStartFuture.set(null);
+            mStartFuture = null;
+        }
+    }
+
+    private void startMsrpSession(SimpleSdpMessage remoteSdp) {
+        Log.d(TAG, "Start MSRP session: " + remoteSdp);
+        if (remoteSdp.getAddress().isPresent() && remoteSdp.getPort().isPresent()) {
+            String localIp = getLocalIp();
+            Futures.addCallback(
+                    mMsrpManager.createMsrpSession(
+                            remoteSdp.getAddress().get(), remoteSdp.getPort().getAsInt(), localIp,
+                            0 /* localPort */, this::receiveMsrpChunk),
+                    new FutureCallback<MsrpSession>() {
+                        @Override
+                        public void onSuccess(MsrpSession result) {
+                            mMsrpSession = result;
+                            sendEmptyPacket();
+                            notifySuccess();
+                        }
+
+                        @Override
+                        public void onFailure(Throwable t) {
+                            Log.e(TAG, "Failed to create msrp session", t);
+                            notifyFailure("Failed to establish msrp session",
+                                    CODE_ERROR_UNSPECIFIED);
+                            terminate()
+                                    .addListener(
+                                            () -> Log.d(TAG, "Session terminated"),
+                                            MoreExecutors.directExecutor());
+                        }
+                    },
+                    MoreExecutors.directExecutor());
+        } else {
+            Log.e(TAG, "Address or port is not present");
+        }
+    }
+
+    private void sendEmptyPacket() {
+        MsrpChunk msrpChunk =
+                MsrpChunk.newBuilder()
+                        .method(MsrpChunk.Method.SEND)
+                        .transactionId(MsrpUtils.generateRandomId())
+                        .continuation(Continuation.COMPLETE)
+                        .addHeader(MsrpConstants.HEADER_TO_PATH, mRemoteSdp.getPath().get())
+                        .addHeader(MsrpConstants.HEADER_FROM_PATH, mLocalSdp.getPath().get())
+                        .addHeader(MsrpConstants.HEADER_FAILURE_REPORT,
+                                MsrpConstants.REPORT_VALUE_NO)
+                        .addHeader(MsrpConstants.HEADER_SUCCESS_REPORT,
+                                MsrpConstants.REPORT_VALUE_NO)
+                        .addHeader(MsrpConstants.HEADER_BYTE_RANGE, "1/0-0")
+                        .addHeader(MsrpConstants.HEADER_MESSAGE_ID, MsrpUtils.generateRandomId())
+                        .build();
+
+        mMsrpSession.send(msrpChunk);
+    }
+
+    private String getLocalIp() {
+        SipSessionConfiguration configuration = mContext.getSipSession().getSessionConfiguration();
+        return configuration.getLocalIpAddress();
+    }
+
+    private void receiveMsrpChunk(MsrpChunk chunk) {
+        Log.d(TAG, "Received msrp= " + chunk + " conversation=" + mConversationId);
+
+        MsrpChunkHeader contentTypeHeader = chunk.header("Content-Type");
+        if (chunk.content().length == 0 || contentTypeHeader == null) {
+            Log.i(TAG, "No content or Content-Type header, drop it");
+            return;
+        }
+
+        String contentType = contentTypeHeader.value();
+        if ("message/cpim".equals(contentType)) {
+            Log.d(TAG, "Received CPIM: " + new String(chunk.content(), UTF_8));
+            try {
+                SimpleCpimMessage cpim = SimpleCpimMessage.parse(chunk.content());
+                if (mListener != null) {
+                    mListener.onMessageReceived(cpim);
+                }
+            } catch (Exception e) {
+                Log.e(TAG, "Error while parsing cpim message.", e);
+            }
+        } else {
+            Log.w(TAG, contentType + " is not supported.");
+        }
+    }
+
+
+    /** Set new listener for this session. */
+    public void setListener(@Nullable ChatSessionListener listener) {
+        mListener = listener;
+    }
+
+    private void updateRemoteUri(SIPRequest request) {
+        PAssertedIdentityHeader pAssertedIdentityHeader =
+                (PAssertedIdentityHeader) request.getHeader("P-Asserted-Identity");
+        if (pAssertedIdentityHeader == null) {
+            mRemoteUri = request.getFrom().getAddress().getURI();
+        } else {
+            mRemoteUri = pAssertedIdentityHeader.getAddress().getURI();
+        }
+    }
+}
diff --git a/tests/Android.bp b/tests/Android.bp
index 13c0dc8..c180476 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "packages_services_Telephony_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["packages_services_Telephony_license"],
+}
+
 android_test {
     name: "TeleServiceTests",
 
@@ -33,9 +42,13 @@
     instrumentation_for: "TeleService",
 
     static_libs: [
+        "androidx.test.core",
+        "androidx.test.espresso.core",
+        "androidx.test.ext.junit",
         "androidx.test.rules",
         "mockito-target-minus-junit4",
-        "androidx.test.espresso.core",
+        "telephony-common-testing",
+        "testng",
         "truth-prebuilt",
 	"testables",
     ],
diff --git a/tests/src/com/android/TestContext.java b/tests/src/com/android/TestContext.java
index 9d712d3..5456469 100644
--- a/tests/src/com/android/TestContext.java
+++ b/tests/src/com/android/TestContext.java
@@ -24,7 +24,10 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.Binder;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.PersistableBundle;
 import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
@@ -32,16 +35,22 @@
 import android.telephony.TelephonyManager;
 import android.telephony.ims.ImsManager;
 import android.test.mock.MockContext;
+import android.util.Log;
 import android.util.SparseArray;
 
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.stubbing.Answer;
 
+import java.util.HashSet;
 import java.util.concurrent.Executor;
 
 public class TestContext extends MockContext {
 
+    private static final String TAG = "TestContext";
+    // Stub used to grant all permissions
+    public static final String STUB_PERMISSION_ENABLE_ALL = "stub_permission_enable_all";
+
     @Mock CarrierConfigManager mMockCarrierConfigManager;
     @Mock TelecomManager mMockTelecomManager;
     @Mock TelephonyManager mMockTelephonyManager;
@@ -50,6 +59,8 @@
 
     private SparseArray<PersistableBundle> mCarrierConfigs = new SparseArray<>();
 
+    private final HashSet<String> mPermissionTable = new HashSet<>();
+
     public TestContext() {
         MockitoAnnotations.initMocks(this);
         doAnswer((Answer<PersistableBundle>) invocation -> {
@@ -150,6 +161,11 @@
         return null;
     }
 
+    @Override
+    public Handler getMainThreadHandler() {
+        return new Handler(Looper.getMainLooper());
+    }
+
     /**
      * @return CarrierConfig PersistableBundle for the subscription specified.
      */
@@ -161,4 +177,67 @@
         }
         return b;
     }
+
+    @Override
+    public void enforceCallingOrSelfPermission(String permission, String message) {
+        if (checkCallingOrSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException(permission + " denied: " + message);
+        }
+    }
+
+    @Override
+    public void enforcePermission(String permission, int pid, int uid, String message) {
+        enforceCallingOrSelfPermission(permission, message);
+    }
+
+    @Override
+    public void enforceCallingPermission(String permission, String message) {
+        enforceCallingOrSelfPermission(permission, message);
+    }
+
+    @Override
+    public int checkCallingOrSelfPermission(String permission) {
+        return checkPermission(permission, Binder.getCallingPid(), Binder.getCallingUid());
+    }
+
+    @Override
+    public int checkPermission(String permission, int pid, int uid) {
+        synchronized (mPermissionTable) {
+            if (mPermissionTable.contains(permission)
+                    || mPermissionTable.contains(STUB_PERMISSION_ENABLE_ALL)) {
+                logd("checkCallingOrSelfPermission: " + permission + " return GRANTED");
+                return PackageManager.PERMISSION_GRANTED;
+            } else {
+                logd("checkCallingOrSelfPermission: " + permission + " return DENIED");
+                return PackageManager.PERMISSION_DENIED;
+            }
+        }
+    }
+
+    public void grantPermission(String permission) {
+        synchronized (mPermissionTable) {
+            if (mPermissionTable != null && permission != null) {
+                mPermissionTable.remove(STUB_PERMISSION_ENABLE_ALL);
+                mPermissionTable.add(permission);
+            }
+        }
+    }
+
+    public void revokePermission(String permission) {
+        synchronized (mPermissionTable) {
+            if (mPermissionTable != null && permission != null) {
+                mPermissionTable.remove(permission);
+            }
+        }
+    }
+
+    public void revokeAllPermissions() {
+        synchronized (mPermissionTable) {
+            mPermissionTable.clear();
+        }
+    }
+
+    private static void logd(String s) {
+        Log.d(TAG, s);
+    }
 }
diff --git a/tests/src/com/android/TestExecutorService.java b/tests/src/com/android/TestExecutorService.java
index fec502a..7685c6d 100644
--- a/tests/src/com/android/TestExecutorService.java
+++ b/tests/src/com/android/TestExecutorService.java
@@ -16,9 +16,11 @@
 
 package com.android;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Delayed;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
@@ -37,6 +39,8 @@
 
         private final Callable<T> mTask;
         private final long mDelayMs;
+        // Wrap callable in a CompletableFuture to support delays in execution.
+        private final CompletableFuture<T> mFuture = new CompletableFuture<>();
 
         CompletedFuture(Callable<T> task) {
             mTask = task;
@@ -50,36 +54,29 @@
 
         @Override
         public boolean cancel(boolean mayInterruptIfRunning) {
-            return false;
+            return mFuture.cancel(mayInterruptIfRunning);
         }
 
         @Override
         public boolean isCancelled() {
-            return false;
+            return mFuture.isCancelled();
         }
 
         @Override
         public boolean isDone() {
-            return true;
+            return mFuture.isDone();
         }
 
         @Override
         public T get() throws InterruptedException, ExecutionException {
-            try {
-                return mTask.call();
-            } catch (Exception e) {
-                throw new ExecutionException(e);
-            }
+            return mFuture.get();
         }
 
         @Override
         public T get(long timeout, TimeUnit unit)
                 throws InterruptedException, ExecutionException, TimeoutException {
-            try {
-                return mTask.call();
-            } catch (Exception e) {
-                throw new ExecutionException(e);
-            }
+            // delays not implemented, this should complete via completeTask for better control.
+            return mFuture.get(timeout, unit);
         }
 
         @Override
@@ -99,35 +96,71 @@
             if (o.getDelay(TimeUnit.MILLISECONDS) < mDelayMs) return 1;
             return 0;
         }
+
+        public void completeTask() {
+            try {
+                mFuture.complete(mTask.call());
+            } catch (Exception e) {
+                mFuture.completeExceptionally(e);
+            }
+        }
+    }
+
+    private final ArrayList<Runnable> mPendingRunnables = new ArrayList<>();
+    private final boolean mWaitToComplete;
+    private boolean mIsShutdown = false;
+
+    public TestExecutorService() {
+        mWaitToComplete = false;
+    }
+
+    /**
+     * Create a test executor service that also allows the constructor to provide a parameter to
+     * control when pending Runnables are executed.
+     * @param waitToComplete If true, this executor will wait to complete any pending Runnables
+     *                       until {@link #executePending()}} is called.
+     */
+    public TestExecutorService(boolean waitToComplete) {
+        mWaitToComplete = waitToComplete;
     }
 
     @Override
     public void shutdown() {
+        mIsShutdown = true;
+        for (Runnable r : mPendingRunnables) {
+            r.run();
+        }
     }
 
     @Override
     public List<Runnable> shutdownNow() {
-        return null;
+        mIsShutdown = true;
+        List<Runnable> runnables = new ArrayList<>(mPendingRunnables);
+        mPendingRunnables.clear();
+        return runnables;
     }
 
     @Override
     public boolean isShutdown() {
-        return false;
+        return mIsShutdown;
     }
 
     @Override
     public boolean isTerminated() {
-        return false;
+        return mIsShutdown;
     }
 
     @Override
     public boolean awaitTermination(long timeout, TimeUnit unit) {
-        return false;
+        shutdown();
+        return true;
     }
 
     @Override
     public <T> Future<T> submit(Callable<T> task) {
-        return new CompletedFuture<>(task);
+        CompletedFuture<T> f = new CompletedFuture<>(task);
+        onExecute(f::completeTask);
+        return f;
     }
 
     @Override
@@ -137,8 +170,12 @@
 
     @Override
     public Future<?> submit(Runnable task) {
-        task.run();
-        return new CompletedFuture<>(() -> null);
+        CompletedFuture<Void> f = new CompletedFuture<>(() -> {
+            task.run();
+            return null;
+        });
+        onExecute(f::completeTask);
+        return f;
     }
 
     @Override
@@ -164,14 +201,21 @@
 
     @Override
     public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
-        // No need to worry about delays yet
-        command.run();
-        return new CompletedFuture<>(() -> null, delay);
+        long millisDelay = TimeUnit.MILLISECONDS.convert(delay, unit);
+        CompletedFuture<Void> f = new CompletedFuture<>(() -> {
+            command.run();
+            return null;
+        }, millisDelay);
+        onExecute(f::completeTask);
+        return f;
     }
 
     @Override
     public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
-        return new CompletedFuture<>(callable, delay);
+        long millisDelay = TimeUnit.MILLISECONDS.convert(delay, unit);
+        CompletedFuture<V> f = new CompletedFuture<>(callable, millisDelay);
+        onExecute(f::completeTask);
+        return f;
     }
 
     @Override
@@ -188,6 +232,21 @@
 
     @Override
     public void execute(Runnable command) {
-        command.run();
+        onExecute(command);
+    }
+
+    private void onExecute(Runnable command) {
+        if (mWaitToComplete) {
+            mPendingRunnables.add(command);
+        } else {
+            command.run();
+        }
+    }
+
+    public void executePending() {
+        for (Runnable r : mPendingRunnables) {
+            r.run();
+        }
+        mPendingRunnables.clear();
     }
 }
diff --git a/tests/src/com/android/phone/CarrierConfigLoaderTest.java b/tests/src/com/android/phone/CarrierConfigLoaderTest.java
new file mode 100644
index 0000000..9c605da
--- /dev/null
+++ b/tests/src/com/android/phone/CarrierConfigLoaderTest.java
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2021 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.phone;
+
+import static com.android.TestContext.STUB_PERMISSION_ENABLE_ALL;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.os.UserHandle;
+import android.service.carrier.CarrierIdentifier;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.testing.TestableLooper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.SubscriptionInfoUpdater;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
+
+/**
+ * Unit Test for CarrierConfigLoader.
+ */
+@RunWith(AndroidJUnit4.class)
+public class CarrierConfigLoaderTest extends TelephonyTestBase {
+
+    private static final int DEFAULT_PHONE_ID = 0;
+    private static final int DEFAULT_SUB_ID = SubscriptionManager.getDefaultSubscriptionId();
+    private static final String PLATFORM_CARRIER_CONFIG_PACKAGE = "com.android.carrierconfig";
+    private static final long PLATFORM_CARRIER_CONFIG_PACKAGE_VERSION_CODE = 1;
+    private static final String CARRIER_CONFIG_EXAMPLE_KEY =
+            CarrierConfigManager.KEY_CARRIER_USSD_METHOD_INT;
+    private static final int CARRIER_CONFIG_EXAMPLE_VALUE =
+            CarrierConfigManager.USSD_OVER_CS_PREFERRED;
+
+    @Mock Resources mResources;
+    @Mock PackageManager mPackageManager;
+    @Mock PackageInfo mPackageInfo;
+    @Mock SubscriptionInfoUpdater mSubscriptionInfoUpdater;
+    @Mock SharedPreferences mSharedPreferences;
+
+    private TelephonyManager mTelephonyManager;
+    private CarrierConfigLoader mCarrierConfigLoader;
+    private Handler mHandler;
+    private HandlerThread mHandlerThread;
+    private TestableLooper mTestableLooper;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        doReturn(mSharedPreferences).when(mContext).getSharedPreferences(anyString(), anyInt());
+        doReturn(Build.FINGERPRINT).when(mSharedPreferences).getString(eq("build_fingerprint"),
+                any());
+        doReturn(mPackageManager).when(mContext).getPackageManager();
+        doReturn(mResources).when(mContext).getResources();
+        doReturn(InstrumentationRegistry.getTargetContext().getFilesDir()).when(
+                mContext).getFilesDir();
+        doReturn(PLATFORM_CARRIER_CONFIG_PACKAGE).when(mResources).getString(
+                eq(R.string.platform_carrier_config_package));
+        mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+        doReturn(1).when(mTelephonyManager).getSupportedModemCount();
+        doReturn(1).when(mTelephonyManager).getActiveModemCount();
+        doReturn("spn").when(mTelephonyManager).getSimOperatorNameForPhone(anyInt());
+        doReturn("310260").when(mTelephonyManager).getSimOperatorNumericForPhone(anyInt());
+        doReturn(mPackageInfo).when(mPackageManager).getPackageInfo(
+                eq(PLATFORM_CARRIER_CONFIG_PACKAGE), eq(0) /*flags*/);
+        doReturn(PLATFORM_CARRIER_CONFIG_PACKAGE_VERSION_CODE).when(
+                mPackageInfo).getLongVersionCode();
+
+        mHandlerThread = new HandlerThread("CarrierConfigLoaderTest");
+        mHandlerThread.start();
+
+        mTestableLooper = new TestableLooper(mHandlerThread.getLooper());
+        mCarrierConfigLoader = new CarrierConfigLoader(mContext, mSubscriptionInfoUpdater,
+                mTestableLooper.getLooper());
+        mHandler = mCarrierConfigLoader.getHandler();
+
+        // Clear all configs to have the same starting point.
+        mCarrierConfigLoader.clearConfigForPhone(DEFAULT_PHONE_ID, false);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mContext.revokeAllPermissions();
+        mTestableLooper.destroy();
+        mHandlerThread.quit();
+        super.tearDown();
+    }
+
+    /**
+     * Verifies that SecurityException should throw when call #updateConfigForPhoneId() without
+     * MODIFY_PHONE_STATE permission.
+     */
+    @Test
+    public void testUpdateConfigForPhoneId_noPermission() throws Exception {
+        assertThrows(SecurityException.class,
+                () -> mCarrierConfigLoader.updateConfigForPhoneId(DEFAULT_PHONE_ID,
+                        IccCardConstants.INTENT_VALUE_ICC_ABSENT));
+    }
+
+    /**
+     * Verifies that IllegalArgumentException should throw when call #updateConfigForPhoneId() with
+     * invalid phoneId.
+     */
+    @Test
+    public void testUpdateConfigForPhoneId_invalidPhoneId() throws Exception {
+        mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
+
+        assertThrows(IllegalArgumentException.class,
+                () -> mCarrierConfigLoader.updateConfigForPhoneId(
+                        SubscriptionManager.INVALID_PHONE_INDEX,
+                        IccCardConstants.INTENT_VALUE_ICC_ABSENT));
+    }
+
+    /**
+     * Verifies that when call #updateConfigForPhoneId() with SIM absence, both carrier config from
+     * default app and carrier should be cleared but no-sim config should be loaded.
+     */
+    @Test
+    public void testUpdateConfigForPhoneId_simAbsent() throws Exception {
+        // Bypass case if default subId is not supported by device to reduce flakiness
+        if (!SubscriptionManager.isValidPhoneId(SubscriptionManager.getPhoneId(DEFAULT_SUB_ID))) {
+            return;
+        }
+        mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
+        doNothing().when(mContext).sendBroadcastAsUser(any(Intent.class), any(UserHandle.class));
+
+        // Prepare a cached config to fetch from xml
+        PersistableBundle config = getTestConfig();
+        mCarrierConfigLoader.saveNoSimConfigToXml(PLATFORM_CARRIER_CONFIG_PACKAGE, config);
+        mCarrierConfigLoader.updateConfigForPhoneId(DEFAULT_PHONE_ID,
+                IccCardConstants.INTENT_VALUE_ICC_ABSENT);
+        mTestableLooper.processAllMessages();
+
+        assertThat(mCarrierConfigLoader.getConfigFromDefaultApp(DEFAULT_PHONE_ID)).isNull();
+        assertThat(mCarrierConfigLoader.getConfigFromCarrierApp(DEFAULT_PHONE_ID)).isNull();
+        assertThat(mCarrierConfigLoader.getNoSimConfig().getInt(CARRIER_CONFIG_EXAMPLE_KEY))
+                .isEqualTo(CARRIER_CONFIG_EXAMPLE_VALUE);
+        verify(mContext).sendBroadcastAsUser(any(Intent.class), any(UserHandle.class));
+    }
+
+    /**
+     * Verifies that with cached config in XML, calling #updateConfigForPhoneId() with SIM loaded
+     * will return the right config in the XML.
+     */
+    @Test
+    public void testUpdateConfigForPhoneId_simLoaded_withCachedConfigInXml() throws Exception {
+        // Bypass case if default subId is not supported by device to reduce flakiness
+        if (!SubscriptionManager.isValidPhoneId(SubscriptionManager.getPhoneId(DEFAULT_SUB_ID))) {
+            return;
+        }
+        mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
+
+        // Prepare to make sure we can save the config into the XML file which used as cache
+        List<String> carrierPackages = List.of(PLATFORM_CARRIER_CONFIG_PACKAGE);
+        doReturn(carrierPackages).when(mTelephonyManager).getCarrierPackageNamesForIntentAndPhone(
+                nullable(Intent.class), anyInt());
+
+        // Save the sample config into the XML file
+        PersistableBundle config = getTestConfig();
+        CarrierIdentifier carrierId = mCarrierConfigLoader.getCarrierIdentifierForPhoneId(
+                DEFAULT_PHONE_ID);
+        mCarrierConfigLoader.saveConfigToXml(PLATFORM_CARRIER_CONFIG_PACKAGE, "",
+                DEFAULT_PHONE_ID, carrierId, config);
+        mCarrierConfigLoader.updateConfigForPhoneId(DEFAULT_PHONE_ID,
+                IccCardConstants.INTENT_VALUE_ICC_LOADED);
+        mTestableLooper.processAllMessages();
+
+        assertThat(mCarrierConfigLoader.getConfigFromDefaultApp(DEFAULT_PHONE_ID).getInt(
+                CARRIER_CONFIG_EXAMPLE_KEY)).isEqualTo(CARRIER_CONFIG_EXAMPLE_VALUE);
+
+    }
+
+    /**
+     * Verifies that SecurityException should throw if call #overrideConfig() without
+     * MODIFY_PHONE_STATE permission.
+     */
+    @Test
+    public void testOverrideConfig_noPermission() throws Exception {
+        assertThrows(SecurityException.class,
+                () -> mCarrierConfigLoader.overrideConfig(DEFAULT_SUB_ID, PersistableBundle.EMPTY,
+                        false));
+    }
+
+    /**
+     * Verifies IllegalArgumentException should throw if call #overrideConfig() with invalid subId.
+     */
+    @Test
+    public void testOverrideConfig_invalidSubId() throws Exception {
+        mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
+
+        assertThrows(IllegalArgumentException.class, () -> mCarrierConfigLoader.overrideConfig(
+                SubscriptionManager.INVALID_SUBSCRIPTION_ID, new PersistableBundle(), false));
+    }
+
+    /**
+     * Verifies that override config is not null when calling #overrideConfig with null bundle.
+     */
+    @Test
+    public void testOverrideConfig_withNullBundle() throws Exception {
+        // Bypass case if default subId is not supported by device to reduce flakiness
+        if (!SubscriptionManager.isValidPhoneId(SubscriptionManager.getPhoneId(DEFAULT_SUB_ID))) {
+            return;
+        }
+        mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
+
+        mCarrierConfigLoader.overrideConfig(DEFAULT_SUB_ID, null /*overrides*/,
+                false/*persistent*/);
+        mTestableLooper.processAllMessages();
+
+        assertThat(mCarrierConfigLoader.getOverrideConfig(DEFAULT_PHONE_ID).isEmpty()).isTrue();
+        verify(mSubscriptionInfoUpdater).updateSubscriptionByCarrierConfigAndNotifyComplete(
+                eq(DEFAULT_PHONE_ID), eq(PLATFORM_CARRIER_CONFIG_PACKAGE),
+                any(PersistableBundle.class), any(Message.class));
+    }
+
+    /**
+     * Verifies that override config is not null when calling #overrideConfig with non-null bundle.
+     */
+    @Test
+    public void testOverrideConfig_withNonNullBundle() throws Exception {
+        // Bypass case if default subId is not supported by device to reduce flakiness
+        if (!SubscriptionManager.isValidPhoneId(SubscriptionManager.getPhoneId(DEFAULT_SUB_ID))) {
+            return;
+        }
+        mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
+
+        PersistableBundle config = getTestConfig();
+        mCarrierConfigLoader.overrideConfig(DEFAULT_SUB_ID, config /*overrides*/,
+                false/*persistent*/);
+        mTestableLooper.processAllMessages();
+
+        assertThat(mCarrierConfigLoader.getOverrideConfig(DEFAULT_PHONE_ID).getInt(
+                CARRIER_CONFIG_EXAMPLE_KEY)).isEqualTo(CARRIER_CONFIG_EXAMPLE_VALUE);
+        verify(mSubscriptionInfoUpdater).updateSubscriptionByCarrierConfigAndNotifyComplete(
+                eq(DEFAULT_PHONE_ID), eq(PLATFORM_CARRIER_CONFIG_PACKAGE),
+                any(PersistableBundle.class), any(Message.class));
+    }
+
+    /**
+     * Verifies that IllegalArgumentException should throw when calling
+     * #notifyConfigChangedForSubId() with invalid subId.
+     */
+    @Test
+    public void testNotifyConfigChangedForSubId_invalidSubId() throws Exception {
+        mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
+
+        assertThrows(IllegalArgumentException.class,
+                () -> mCarrierConfigLoader.notifyConfigChangedForSubId(
+                        SubscriptionManager.INVALID_SUBSCRIPTION_ID));
+    }
+
+    // TODO(b/184040111): Enable test case when support disabling carrier privilege
+    // Phone/System UID always has carrier privilege (TelephonyPermission#getCarrierPrivilegeStatus)
+    // when running the test here.
+    /**
+     * Verifies that SecurityException should throw when calling notifyConfigChangedForSubId without
+     * MODIFY_PHONE_STATE permission.
+     */
+    @Ignore
+    public void testNotifyConfigChangedForSubId_noPermission() throws Exception {
+        setCarrierPrivilegesForSubId(false, DEFAULT_SUB_ID);
+
+        assertThrows(SecurityException.class,
+                () -> mCarrierConfigLoader.notifyConfigChangedForSubId(DEFAULT_SUB_ID));
+    }
+
+    /**
+     * Verifies that SecurityException should throw when calling getDefaultCarrierServicePackageName
+     * without READ_PRIVILEGED_PHONE_STATE permission.
+     */
+    @Test
+    public void testGetDefaultCarrierServicePackageName_noPermission() {
+        assertThrows(SecurityException.class,
+                () -> mCarrierConfigLoader.getDefaultCarrierServicePackageName());
+    }
+
+    /**
+     * Verifies that the right default carrier service package name is return when calling
+     * getDefaultCarrierServicePackageName with permission.
+     */
+    @Test
+    public void testGetDefaultCarrierServicePackageName_withPermission() {
+        mContext.grantPermission(STUB_PERMISSION_ENABLE_ALL);
+
+        assertThat(mCarrierConfigLoader.getDefaultCarrierServicePackageName())
+                .isEqualTo(PLATFORM_CARRIER_CONFIG_PACKAGE);
+    }
+
+    // TODO(b/184040111): Enable test case when support disabling carrier privilege
+    // Phone/System UID always has carrier privilege (TelephonyPermission#getCarrierPrivilegeStatus)
+    // when running the test here.
+    /**
+     * Verifies that without permission, #getConfigForSubId will return an empty PersistableBundle.
+     */
+    @Ignore
+    public void testGetConfigForSubId_noPermission() {
+        // Bypass case if default subId is not supported by device to reduce flakiness
+        if (!SubscriptionManager.isValidPhoneId(SubscriptionManager.getPhoneId(DEFAULT_SUB_ID))) {
+            return;
+        }
+        setCarrierPrivilegesForSubId(false, DEFAULT_SUB_ID);
+
+        assertThat(mCarrierConfigLoader.getConfigForSubId(DEFAULT_SUB_ID,
+                PLATFORM_CARRIER_CONFIG_PACKAGE)).isEqualTo(PersistableBundle.EMPTY);
+    }
+
+    /**
+     * Verifies that when have no DUMP permission, the #dump() method shows permission denial.
+     */
+    @Test
+    public void testDump_noPermission() {
+        StringWriter stringWriter = new StringWriter();
+        mCarrierConfigLoader.dump(new FileDescriptor(), new PrintWriter(stringWriter),
+                new String[0]);
+        stringWriter.flush();
+
+        assertThat(stringWriter.toString()).contains("Permission Denial:");
+    }
+
+    /**
+     * Verifies that when have DUMP permission, the #dump() method can dump the CarrierConfigLoader.
+     */
+    @Test
+    public void testDump_withPermission() {
+        mContext.grantPermission(android.Manifest.permission.DUMP);
+
+        StringWriter stringWriter = new StringWriter();
+        mCarrierConfigLoader.dump(new FileDescriptor(), new PrintWriter(stringWriter),
+                new String[0]);
+        stringWriter.flush();
+
+        String dumpContent = stringWriter.toString();
+        assertThat(dumpContent).contains("CarrierConfigLoader:");
+        assertThat(dumpContent).doesNotContain("Permission Denial:");
+    }
+
+    private static PersistableBundle getTestConfig() {
+        PersistableBundle config = new PersistableBundle();
+        config.putInt(CARRIER_CONFIG_EXAMPLE_KEY, CARRIER_CONFIG_EXAMPLE_VALUE);
+        return config;
+    }
+
+    private void setCarrierPrivilegesForSubId(boolean hasCarrierPrivileges, int subId) {
+        TelephonyManager mockTelephonyManager = Mockito.mock(TelephonyManager.class);
+        doReturn(mockTelephonyManager).when(mTelephonyManager).createForSubscriptionId(subId);
+        doReturn(hasCarrierPrivileges ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
+                : TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS).when(
+                mockTelephonyManager).getCarrierPrivilegeStatus(anyInt());
+    }
+}
diff --git a/tests/src/com/android/phone/RcsProvisioningMonitorTest.java b/tests/src/com/android/phone/RcsProvisioningMonitorTest.java
index 02d2f8a..57e01f0 100644
--- a/tests/src/com/android/phone/RcsProvisioningMonitorTest.java
+++ b/tests/src/com/android/phone/RcsProvisioningMonitorTest.java
@@ -38,6 +38,7 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.net.Uri;
@@ -54,20 +55,21 @@
 import android.telephony.ims.RcsConfig;
 import android.telephony.ims.aidl.IImsConfig;
 import android.telephony.ims.aidl.IRcsConfigCallback;
-import android.telephony.ims.feature.ImsFeature;
 import android.test.mock.MockContentProvider;
 import android.test.mock.MockContentResolver;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.TestableLooper;
 import android.util.Log;
 
+import com.android.ims.FeatureConnector;
+import com.android.ims.RcsFeatureManager;
 import com.android.internal.telephony.ITelephony;
-import com.android.internal.telephony.ims.ImsResolver;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.invocation.InvocationOnMock;
@@ -83,25 +85,56 @@
  */
 public class RcsProvisioningMonitorTest {
     private static final String TAG = "RcsProvisioningMonitorTest";
-    private static final String SAMPLE_CONFIG = "<RCSConfig>\n"
-            + "\t<rcsVolteSingleRegistration>1</rcsVolteSingleRegistration>\n"
-            + "\t<SERVICES>\n"
-            + "\t\t<SupportedRCSProfileVersions>UP_2.0</SupportedRCSProfileVersions>\n"
-            + "\t\t<ChatAuth>1</ChatAuth>\n"
-            + "\t\t<GroupChatAuth>1</GroupChatAuth>\n"
-            + "\t\t<ftAuth>1</ftAuth>\n"
-            + "\t\t<standaloneMsgAuth>1</standaloneMsgAuth>\n"
-            + "\t\t<geolocPushAuth>1</geolocPushAuth>\n"
-            + "\t\t<Ext>\n"
-            + "\t\t\t<DataOff>\n"
-            + "\t\t\t\t<rcsMessagingDataOff>1</rcsMessagingDataOff>\n"
-            + "\t\t\t\t<fileTransferDataOff>1</fileTransferDataOff>\n"
-            + "\t\t\t\t<mmsDataOff>1</mmsDataOff>\n"
-            + "\t\t\t\t<syncDataOff>1</syncDataOff>\n"
-            + "\t\t\t</DataOff>\n"
-            + "\t\t</Ext>\n"
-            + "\t</SERVICES>\n"
-            + "</RCSConfig>";
+    private static final String CONFIG_DEFAULT = "<?xml version=\"1.0\"?>\n"
+            + "<wap-provisioningdoc version=\"1.1\">\n"
+            + "\t<characteristic type=\"APPLICATION\">\n"
+            + "\t\t<parm name=\"AppID\" value=\"urn:oma:mo:ext-3gpp-ims:1.0\"/>\n"
+            + "\t\t<characteristic type=\"3GPP_IMS\">\n"
+            + "\t\t\t<parm name=\"AppID\" value=\"ap2001\"/>\n"
+            + "\t\t\t<parm name=\"Name\" value=\"RCS IMS Settings\"/>\n"
+            + "\t\t\t<characteristic type=\"Ext\">\n"
+            + "\t\t\t\t<characteristic type=\"GSMA\">\n"
+            + "\t\t\t\t\t<parm name=\"AppRef\" value=\"IMS-Setting\"/>\n"
+            + "\t\t\t\t\t<parm name=\"rcsVolteSingleRegistration\" value=\"1\"/>\n"
+            + "\t\t\t\t</characteristic>\n"
+            + "\t\t\t</characteristic>\n"
+            + "\t\t</characteristic>\n"
+            + "\t\t<characteristic type=\"SERVICES\">\n"
+            + "\t\t\t<parm name=\"SupportedRCSProfileVersions\" value=\"UP2.3\"/>\n"
+            + "\t\t\t<parm name=\"ChatAuth\" value=\"1\"/>\n"
+            + "\t\t\t<parm name=\"GroupChatAuth\" value=\"1\"/>\n"
+            + "\t\t\t<parm name=\"ftAuth\" value=\"1\"/>\n"
+            + "\t\t\t<parm name=\"standaloneMsgAuth\" value=\"1\"/>\n"
+            + "\t\t\t<parm name=\"geolocPushAuth\" value=\"1\"/>\n"
+            + "\t\t\t<characteristic type=\"Ext\">\n"
+            + "\t\t\t\t<characteristic type=\"DataOff\">\n"
+            + "\t\t\t\t\t<parm name=\"rcsMessagingDataOff\" value=\"1\"/>\n"
+            + "\t\t\t\t\t<parm name=\"fileTransferDataOff\" value=\"1\"/>\n"
+            + "\t\t\t\t\t<parm name=\"mmsDataOff\" value=\"1\"/>\n"
+            + "\t\t\t\t\t<parm name=\"syncDataOff\" value=\"1\"/>\n"
+            + "\t\t\t\t\t<characteristic type=\"Ext\"/>\n"
+            + "\t\t\t\t</characteristic>\n"
+            + "\t\t\t</characteristic>\n"
+            + "\t\t</characteristic>\n"
+            + "\t</characteristic>\n"
+            + "</wap-provisioningdoc>\n";
+
+    private static final String CONFIG_SINGLE_REGISTRATION_DISABLED = "<?xml version=\"1.0\"?>\n"
+            + "<wap-provisioningdoc version=\"1.1\">\n"
+            + "\t<characteristic type=\"APPLICATION\">\n"
+            + "\t\t<parm name=\"AppID\" value=\"urn:oma:mo:ext-3gpp-ims:1.0\"/>\n"
+            + "\t\t<characteristic type=\"3GPP_IMS\">\n"
+            + "\t\t\t<parm name=\"AppID\" value=\"ap2001\"/>\n"
+            + "\t\t\t<parm name=\"Name\" value=\"RCS IMS Settings\"/>\n"
+            + "\t\t\t<characteristic type=\"Ext\">\n"
+            + "\t\t\t\t<characteristic type=\"GSMA\">\n"
+            + "\t\t\t\t\t<parm name=\"AppRef\" value=\"IMS-Setting\"/>\n"
+            + "\t\t\t\t\t<parm name=\"rcsVolteSingleRegistration\" value=\"0\"/>\n"
+            + "\t\t\t\t</characteristic>\n"
+            + "\t\t\t</characteristic>\n"
+            + "\t\t</characteristic>\n"
+            + "\t</characteristic>\n"
+            + "</wap-provisioningdoc>\n";
     private static final int FAKE_SUB_ID_BASE = 0x0FFFFFF0;
     private static final String DEFAULT_MESSAGING_APP1 = "DMA1";
     private static final String DEFAULT_MESSAGING_APP2 = "DMA2";
@@ -129,7 +162,13 @@
     @Mock
     private ITelephony.Stub mITelephony;
     @Mock
-    private ImsResolver mImsResolver;
+    private RcsFeatureManager mFeatureManager;
+    @Mock
+    private RcsProvisioningMonitor.FeatureConnectorFactory<RcsFeatureManager> mFeatureFactory;
+    @Mock
+    private FeatureConnector<RcsFeatureManager> mFeatureConnector;
+    @Captor
+    ArgumentCaptor<FeatureConnector.Listener<RcsFeatureManager>> mConnectorListener;
     @Mock
     private IImsConfig.Stub mIImsConfig;
     @Mock
@@ -138,6 +177,8 @@
     private PhoneGlobals mPhone;
     @Mock
     private IRcsConfigCallback mCallback;
+    @Mock
+    private PackageManager mPackageManager;
 
     private Executor mExecutor = new Executor() {
         @Override
@@ -181,8 +222,9 @@
         MockitoAnnotations.initMocks(this);
 
         when(mPhone.getResources()).thenReturn(mResources);
-        when(mResources.getBoolean(
-                eq(R.bool.config_rcsVolteSingleRegistrationEnabled))).thenReturn(true);
+        when(mPhone.getPackageManager()).thenReturn(mPackageManager);
+        when(mPackageManager.hasSystemFeature(
+                eq(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION))).thenReturn(true);
         when(mPhone.getMainExecutor()).thenReturn(mExecutor);
         when(mPhone.getSystemServiceName(eq(CarrierConfigManager.class)))
                 .thenReturn(Context.CARRIER_CONFIG_SERVICE);
@@ -241,9 +283,8 @@
         when(mCursor.moveToFirst()).thenReturn(true);
         when(mCursor.getColumnIndexOrThrow(any())).thenReturn(1);
         when(mCursor.getBlob(anyInt())).thenReturn(
-                RcsConfig.compressGzip(SAMPLE_CONFIG.getBytes()));
-        when(mPhone.getImsResolver()).thenReturn(mImsResolver);
-        when(mImsResolver.getImsConfig(anyInt(), anyInt())).thenReturn(mIImsConfig);
+                RcsConfig.compressGzip(CONFIG_DEFAULT.getBytes()));
+
         mHandlerThread = new HandlerThread("RcsProvisioningMonitorTest");
         mHandlerThread.start();
     }
@@ -264,19 +305,18 @@
     @Test
     @SmallTest
     public void testInitWithSavedConfig() throws Exception {
-        createMonitor(3);
         ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
+        createMonitor(3);
+
         for (int i = 0; i < 3; i++) {
-            assertTrue(Arrays.equals(SAMPLE_CONFIG.getBytes(),
+            assertTrue(Arrays.equals(CONFIG_DEFAULT.getBytes(),
                     mRcsProvisioningMonitor.getConfig(FAKE_SUB_ID_BASE + i)));
         }
 
-        verify(mPhone, times(3)).sendBroadcast(captorIntent.capture());
+        verify(mPhone, times(3)).sendBroadcast(captorIntent.capture(), any());
         Intent capturedIntent = captorIntent.getAllValues().get(1);
         assertEquals(ProvisioningManager.ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE,
                 capturedIntent.getAction());
-        PhoneGlobals.getInstance().getImsResolver();
-        verify(mPhone, atLeastOnce()).getImsResolver();
         verify(mIImsConfig, times(3)).notifyRcsAutoConfigurationReceived(any(), anyBoolean());
     }
 
@@ -287,8 +327,9 @@
         ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
         createMonitor(3);
 
-        verify(mPhone, times(3)).sendBroadcast(captorIntent.capture());
+        verify(mPhone, times(3)).sendBroadcast(captorIntent.capture(), any());
         Intent capturedIntent = captorIntent.getAllValues().get(1);
+
         assertEquals(ProvisioningManager.ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE,
                 capturedIntent.getAction());
         //Should not notify null config
@@ -300,13 +341,12 @@
     public void testSubInfoChanged() throws Exception {
         createMonitor(3);
         ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
-        mExecutor.execute(() -> mSubChangedListener.onSubscriptionsChanged());
-        processAllMessages();
+
         for (int i = 0; i < 3; i++) {
-            assertTrue(Arrays.equals(SAMPLE_CONFIG.getBytes(),
+            assertTrue(Arrays.equals(CONFIG_DEFAULT.getBytes(),
                     mRcsProvisioningMonitor.getConfig(FAKE_SUB_ID_BASE + i)));
         }
-        verify(mPhone, times(3)).sendBroadcast(captorIntent.capture());
+        verify(mPhone, times(3)).sendBroadcast(captorIntent.capture(), any());
         Intent capturedIntent = captorIntent.getAllValues().get(1);
         assertEquals(ProvisioningManager.ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE,
                 capturedIntent.getAction());
@@ -349,8 +389,8 @@
         processAllMessages();
         byte[] configCached = mRcsProvisioningMonitor.getConfig(FAKE_SUB_ID_BASE);
 
-        assertTrue(Arrays.equals(SAMPLE_CONFIG.getBytes(), configCached));
-        verify(mIImsConfig, never()).notifyRcsAutoConfigurationRemoved();
+        assertTrue(Arrays.equals(CONFIG_DEFAULT.getBytes(), configCached));
+        verify(mIImsConfig, times(1)).notifyRcsAutoConfigurationRemoved();
         // The api should be called 2 times, one happens when monitor is initilized,
         // Another happens when DMS is changed.
         verify(mIImsConfig, times(2))
@@ -361,14 +401,14 @@
     @SmallTest
     public void testCarrierConfigChanged() throws Exception {
         createMonitor(1);
-        when(mResources.getBoolean(
-                eq(R.bool.config_rcsVolteSingleRegistrationEnabled))).thenReturn(true);
+        when(mPackageManager.hasSystemFeature(
+                eq(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION))).thenReturn(true);
         ArgumentCaptor<Intent> captorIntent = ArgumentCaptor.forClass(Intent.class);
         mBundle.putBoolean(
                 CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, true);
         broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
         processAllMessages();
-        verify(mPhone, atLeastOnce()).sendBroadcast(captorIntent.capture());
+        verify(mPhone, atLeastOnce()).sendBroadcast(captorIntent.capture(), any());
         Intent capturedIntent = captorIntent.getValue();
         assertEquals(capturedIntent.getAction(),
                 ProvisioningManager.ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE);
@@ -381,7 +421,7 @@
                 CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false);
         broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
         processAllMessages();
-        verify(mPhone, atLeastOnce()).sendBroadcast(captorIntent.capture());
+        verify(mPhone, atLeastOnce()).sendBroadcast(captorIntent.capture(), any());
         capturedIntent = captorIntent.getValue();
         assertEquals(capturedIntent.getAction(),
                 ProvisioningManager.ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE);
@@ -391,11 +431,11 @@
                 capturedIntent.getIntExtra(ProvisioningManager.EXTRA_STATUS, -1));
 
 
-        when(mResources.getBoolean(
-                eq(R.bool.config_rcsVolteSingleRegistrationEnabled))).thenReturn(false);
+        when(mPackageManager.hasSystemFeature(
+                eq(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION))).thenReturn(false);
         broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
         processAllMessages();
-        verify(mPhone, atLeastOnce()).sendBroadcast(captorIntent.capture());
+        verify(mPhone, atLeastOnce()).sendBroadcast(captorIntent.capture(), any());
         capturedIntent = captorIntent.getValue();
         assertEquals(capturedIntent.getAction(),
                 ProvisioningManager.ACTION_RCS_SINGLE_REGISTRATION_CAPABILITY_UPDATE);
@@ -412,14 +452,12 @@
         createMonitor(1);
         final ArgumentCaptor<byte[]> argumentBytes = ArgumentCaptor.forClass(byte[].class);
 
-        mRcsProvisioningMonitor.updateConfig(FAKE_SUB_ID_BASE, SAMPLE_CONFIG.getBytes(), false);
+        mRcsProvisioningMonitor.updateConfig(FAKE_SUB_ID_BASE, CONFIG_DEFAULT.getBytes(), false);
         processAllMessages();
 
-        verify(mImsResolver, atLeastOnce()).getImsConfig(
-                anyInt(), eq(ImsFeature.FEATURE_RCS));
         verify(mIImsConfig, atLeastOnce()).notifyRcsAutoConfigurationReceived(
                 argumentBytes.capture(), eq(false));
-        assertTrue(Arrays.equals(SAMPLE_CONFIG.getBytes(), argumentBytes.getValue()));
+        assertTrue(Arrays.equals(CONFIG_DEFAULT.getBytes(), argumentBytes.getValue()));
     }
 
     @Test
@@ -430,8 +468,6 @@
         mRcsProvisioningMonitor.requestReconfig(FAKE_SUB_ID_BASE);
         processAllMessages();
 
-        verify(mImsResolver, atLeastOnce()).getImsConfig(
-                anyInt(), eq(ImsFeature.FEATURE_RCS));
         verify(mIImsConfig, times(1)).notifyRcsAutoConfigurationRemoved();
         verify(mIImsConfig, times(1)).triggerRcsReconfiguration();
     }
@@ -441,14 +477,16 @@
     public void testIsRcsVolteSingleRegistrationEnabled() throws Exception {
         createMonitor(1);
 
-        when(mResources.getBoolean(
-                eq(R.bool.config_rcsVolteSingleRegistrationEnabled))).thenReturn(true);
+        when(mPackageManager.hasSystemFeature(
+                eq(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION))).thenReturn(false);
         mBundle.putBoolean(
-                CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, true);
+                CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false);
         broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
         processAllMessages();
-        assertTrue(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+        assertFalse(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
 
+        when(mPackageManager.hasSystemFeature(
+                eq(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION))).thenReturn(true);
         mBundle.putBoolean(
                 CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false);
         broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
@@ -456,13 +494,34 @@
         assertFalse(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
 
 
-        when(mResources.getBoolean(
-                eq(R.bool.config_rcsVolteSingleRegistrationEnabled))).thenReturn(false);
+        when(mPackageManager.hasSystemFeature(
+                eq(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION))).thenReturn(false);
         mBundle.putBoolean(
                 CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, true);
         broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
         processAllMessages();
         assertFalse(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+
+        when(mPackageManager.hasSystemFeature(
+                eq(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION))).thenReturn(true);
+        mBundle.putBoolean(
+                CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, true);
+        broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
+        processAllMessages();
+        assertTrue(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+
+        mRcsProvisioningMonitor.updateConfig(FAKE_SUB_ID_BASE, null, false);
+        processAllMessages();
+        assertFalse(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+
+        mRcsProvisioningMonitor.updateConfig(FAKE_SUB_ID_BASE, CONFIG_DEFAULT.getBytes(), false);
+        processAllMessages();
+        assertTrue(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+
+        mRcsProvisioningMonitor.updateConfig(FAKE_SUB_ID_BASE,
+                CONFIG_SINGLE_REGISTRATION_DISABLED.getBytes(), false);
+        processAllMessages();
+        assertFalse(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
     }
 
     @Test
@@ -470,13 +529,13 @@
     public void testRegisterThenUnregisterCallback() throws Exception {
         createMonitor(1);
 
-        boolean result = mRcsProvisioningMonitor.registerRcsProvisioningChangedCallback(
+        boolean result = mRcsProvisioningMonitor.registerRcsProvisioningCallback(
                 FAKE_SUB_ID_BASE, mCallback);
 
         assertTrue(result);
         verify(mIImsConfig, times(1)).addRcsConfigCallback(eq(mCallback));
 
-        result = mRcsProvisioningMonitor.unregisterRcsProvisioningChangedCallback(
+        result = mRcsProvisioningMonitor.unregisterRcsProvisioningCallback(
                 FAKE_SUB_ID_BASE, mCallback);
 
         assertTrue(result);
@@ -489,7 +548,7 @@
     public void testCallbackRemovedWhenSubInfoChanged() throws Exception {
         createMonitor(1);
 
-        boolean result = mRcsProvisioningMonitor.registerRcsProvisioningChangedCallback(
+        boolean result = mRcsProvisioningMonitor.registerRcsProvisioningCallback(
                 FAKE_SUB_ID_BASE, mCallback);
         makeFakeActiveSubIds(0);
         mExecutor.execute(() -> mSubChangedListener.onSubscriptionsChanged());
@@ -505,7 +564,7 @@
     public void testCallbackRemovedWhenDmaChanged() throws Exception {
         createMonitor(1);
 
-        boolean result = mRcsProvisioningMonitor.registerRcsProvisioningChangedCallback(
+        boolean result = mRcsProvisioningMonitor.registerRcsProvisioningCallback(
                 FAKE_SUB_ID_BASE, mCallback);
         updateDefaultMessageApplication(DEFAULT_MESSAGING_APP2);
         processAllMessages();
@@ -515,19 +574,182 @@
         verify(mCallback, times(1)).onRemoved();
     }
 
-    private void createMonitor(int subCount) {
+    @Test
+    @SmallTest
+    public void testRcsConnectedAndDisconnected() throws Exception {
+        createMonitor(1);
+        mRcsProvisioningMonitor.registerRcsProvisioningCallback(
+                FAKE_SUB_ID_BASE, mCallback);
+
+        verify(mIImsConfig, times(1))
+                .notifyRcsAutoConfigurationReceived(any(), anyBoolean());
+
+        mConnectorListener.getValue().connectionUnavailable(0);
+
+        verify(mCallback, times(1)).onRemoved();
+    }
+
+    @Test
+    @SmallTest
+    public void testOverrideDeviceSingleRegistrationEnabled() throws Exception {
+        createMonitor(1);
+
+        when(mPackageManager.hasSystemFeature(
+                eq(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION))).thenReturn(true);
+        mBundle.putBoolean(
+                CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, true);
+        broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
+        processAllMessages();
+        assertTrue(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+
+        mRcsProvisioningMonitor.overrideDeviceSingleRegistrationEnabled(false);
+        processAllMessages();
+        assertFalse(mRcsProvisioningMonitor.getDeviceSingleRegistrationEnabled());
+        assertFalse(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+
+        mRcsProvisioningMonitor.overrideDeviceSingleRegistrationEnabled(null);
+        processAllMessages();
+        assertTrue(mRcsProvisioningMonitor.getDeviceSingleRegistrationEnabled());
+        assertTrue(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+
+        when(mPackageManager.hasSystemFeature(
+                eq(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION))).thenReturn(false);
+        //use carrier config change to refresh the value as system feature is static
+        mBundle.putBoolean(
+                CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, true);
+        broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
+        processAllMessages();
+
+        assertFalse(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+
+        mRcsProvisioningMonitor.overrideDeviceSingleRegistrationEnabled(true);
+        processAllMessages();
+        assertTrue(mRcsProvisioningMonitor.getDeviceSingleRegistrationEnabled());
+        assertTrue(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+
+        mRcsProvisioningMonitor.overrideDeviceSingleRegistrationEnabled(null);
+        processAllMessages();
+        assertFalse(mRcsProvisioningMonitor.getDeviceSingleRegistrationEnabled());
+        assertFalse(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+    }
+
+    @Test
+    @SmallTest
+    public void testTestModeEnabledAndDisabled() throws Exception {
+        when(mCursor.getBlob(anyInt())).thenReturn(null);
+        createMonitor(1);
+
+        verify(mCursor, times(1)).getBlob(anyInt());
+
+        mRcsProvisioningMonitor.setTestModeEnabled(true);
+        processAllMessages();
+
+        //should not query db in test mode
+        verify(mCursor, times(1)).getBlob(anyInt());
+        assertNull(mRcsProvisioningMonitor.getConfig(FAKE_SUB_ID_BASE));
+
+        mRcsProvisioningMonitor.updateConfig(FAKE_SUB_ID_BASE, CONFIG_DEFAULT.getBytes(), false);
+        processAllMessages();
+
+        //config cahced in monitor should be updated, but db should not
+        assertNull(mProvider.getContentValues());
+        assertTrue(Arrays.equals(CONFIG_DEFAULT.getBytes(),
+                mRcsProvisioningMonitor.getConfig(FAKE_SUB_ID_BASE)));
+
+        //verify if monitor goes back to normal mode
+        mRcsProvisioningMonitor.setTestModeEnabled(false);
+        processAllMessages();
+
+        verify(mCursor, times(2)).getBlob(anyInt());
+        assertNull(mRcsProvisioningMonitor.getConfig(FAKE_SUB_ID_BASE));
+
+        mRcsProvisioningMonitor.updateConfig(FAKE_SUB_ID_BASE, CONFIG_DEFAULT.getBytes(), false);
+        processAllMessages();
+
+        assertTrue(Arrays.equals(CONFIG_DEFAULT.getBytes(),
+                mRcsProvisioningMonitor.getConfig(FAKE_SUB_ID_BASE)));
+        assertTrue(Arrays.equals(RcsConfig.compressGzip(CONFIG_DEFAULT.getBytes()),
+                (byte[]) mProvider.getContentValues().get(SimInfo.COLUMN_RCS_CONFIG)));
+    }
+
+    @Test
+    @SmallTest
+    public void testOverrideCarrierSingleRegistrationEnabled() throws Exception {
+        createMonitor(1);
+
+        when(mPackageManager.hasSystemFeature(
+                eq(PackageManager.FEATURE_TELEPHONY_IMS_SINGLE_REGISTRATION))).thenReturn(true);
+        mBundle.putBoolean(
+                CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, true);
+        broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
+        processAllMessages();
+        assertTrue(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+
+        mRcsProvisioningMonitor
+                .overrideCarrierSingleRegistrationEnabled(FAKE_SUB_ID_BASE, false);
+        processAllMessages();
+        assertFalse(mRcsProvisioningMonitor.getCarrierSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+        assertFalse(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+
+        mRcsProvisioningMonitor
+                .overrideCarrierSingleRegistrationEnabled(FAKE_SUB_ID_BASE, null);
+        processAllMessages();
+        assertTrue(mRcsProvisioningMonitor.getCarrierSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+        assertTrue(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+
+        mBundle.putBoolean(
+                CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false);
+        broadcastCarrierConfigChange(FAKE_SUB_ID_BASE);
+        processAllMessages();
+        assertFalse(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+
+        mRcsProvisioningMonitor
+                .overrideCarrierSingleRegistrationEnabled(FAKE_SUB_ID_BASE, true);
+        processAllMessages();
+        assertTrue(mRcsProvisioningMonitor.getCarrierSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+        assertTrue(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+
+        mRcsProvisioningMonitor
+                .overrideCarrierSingleRegistrationEnabled(FAKE_SUB_ID_BASE, null);
+        processAllMessages();
+        assertFalse(mRcsProvisioningMonitor.getCarrierSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+        assertFalse(mRcsProvisioningMonitor.isRcsVolteSingleRegistrationEnabled(FAKE_SUB_ID_BASE));
+    }
+
+    @Test
+    @SmallTest
+    public void testOverrideImsFeatureValidation() throws Exception {
+        createMonitor(1);
+
+        mRcsProvisioningMonitor.overrideImsFeatureValidation(FAKE_SUB_ID_BASE, false);
+        assertFalse(mRcsProvisioningMonitor.getImsFeatureValidationOverride(FAKE_SUB_ID_BASE));
+
+        mRcsProvisioningMonitor.overrideImsFeatureValidation(FAKE_SUB_ID_BASE, true);
+        assertTrue(mRcsProvisioningMonitor.getImsFeatureValidationOverride(FAKE_SUB_ID_BASE));
+
+        mRcsProvisioningMonitor.overrideImsFeatureValidation(FAKE_SUB_ID_BASE, null);
+        assertNull(mRcsProvisioningMonitor.getImsFeatureValidationOverride(FAKE_SUB_ID_BASE));
+    }
+
+    private void createMonitor(int subCount) throws Exception {
         if (Looper.myLooper() == null) {
             Looper.prepare();
         }
         makeFakeActiveSubIds(subCount);
+        when(mFeatureFactory.create(any(), anyInt(), mConnectorListener.capture(), any(), any()))
+                .thenReturn(mFeatureConnector);
+        when(mFeatureManager.getConfig()).thenReturn(mIImsConfig);
         mRcsProvisioningMonitor = new RcsProvisioningMonitor(mPhone, mHandlerThread.getLooper(),
-                mRoleManager);
+                mRoleManager, mFeatureFactory);
         mHandler = mRcsProvisioningMonitor.getHandler();
         try {
             mLooper = new TestableLooper(mHandler.getLooper());
         } catch (Exception e) {
             logd("Unable to create looper from handler.");
         }
+        mConnectorListener.getValue().connectionReady(mFeatureManager);
+
+        verify(mFeatureConnector, atLeastOnce()).connect();
     }
 
     private void broadcastCarrierConfigChange(int subId) {
diff --git a/tests/src/com/android/phone/ServiceStateProviderTest.java b/tests/src/com/android/phone/ServiceStateProviderTest.java
index 32e5f26..d85976a 100644
--- a/tests/src/com/android/phone/ServiceStateProviderTest.java
+++ b/tests/src/com/android/phone/ServiceStateProviderTest.java
@@ -18,6 +18,7 @@
 
 import static android.provider.Telephony.ServiceStateTable;
 import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionId;
+import static android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_HOME;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -31,8 +32,11 @@
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.net.Uri;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.NetworkRegistrationInfo;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.test.mock.MockContentResolver;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -60,7 +64,7 @@
     private final String[] mTestProjection =
     {
         ServiceStateTable.VOICE_REG_STATE,
-        ServiceStateProvider.DATA_REG_STATE,
+        ServiceStateTable.DATA_REG_STATE,
         ServiceStateProvider.VOICE_OPERATOR_ALPHA_LONG,
         ServiceStateProvider.VOICE_OPERATOR_ALPHA_SHORT,
         ServiceStateTable.VOICE_OPERATOR_NUMERIC,
@@ -81,15 +85,24 @@
         ServiceStateProvider.IS_USING_CARRIER_AGGREGATION,
         ServiceStateProvider.OPERATOR_ALPHA_LONG_RAW,
         ServiceStateProvider.OPERATOR_ALPHA_SHORT_RAW,
+        ServiceStateTable.DATA_NETWORK_TYPE,
+        ServiceStateTable.DUPLEX_MODE,
     };
 
+    // Exception used internally to verify if the Resolver#notifyChange has been called.
+    private class TestNotifierException extends RuntimeException {
+        TestNotifierException() {
+            super();
+        }
+    }
+
     @Before
     public void setUp() throws Exception {
         mContext = mock(Context.class);
         mContentResolver = new MockContentResolver() {
             @Override
             public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
-                throw new RuntimeException("notifyChange!");
+                throw new TestNotifierException();
             }
         };
         doReturn(mContentResolver).when(mContext).getContentResolver();
@@ -99,6 +112,15 @@
         mTestServiceStateForSubId1 = new ServiceState();
         mTestServiceStateForSubId1.setStateOff();
 
+        // Add NRI to trigger SS with non-default values (e.g. duplex mode)
+        NetworkRegistrationInfo nriWwan = new NetworkRegistrationInfo.Builder()
+                .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+                .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                .build();
+        mTestServiceStateForSubId1.addNetworkRegistrationInfo(nriWwan);
+        mTestServiceStateForSubId1.setChannelNumber(65536); // EutranBand.BAND_65, DUPLEX_MODE_FDD
+
         // Mock out the actual phone state
         ServiceStateProvider provider = new ServiceStateProvider() {
             @Override
@@ -183,6 +205,8 @@
         final int isUsingCarrierAggregation = (ss.isUsingCarrierAggregation()) ? 1 : 0;
         final String operatorAlphaLongRaw = ss.getOperatorAlphaLongRaw();
         final String operatorAlphaShortRaw = ss.getOperatorAlphaShortRaw();
+        final int dataNetworkType = ss.getDataNetworkType();
+        final int duplexMode = ss.getDuplexMode();
 
         assertEquals(voiceRegState, cursor.getInt(0));
         assertEquals(dataRegState, cursor.getInt(1));
@@ -206,6 +230,8 @@
         assertEquals(isUsingCarrierAggregation, cursor.getInt(19));
         assertEquals(operatorAlphaLongRaw, cursor.getString(20));
         assertEquals(operatorAlphaShortRaw, cursor.getString(21));
+        assertEquals(dataNetworkType, cursor.getInt(22));
+        assertEquals(duplexMode, cursor.getInt(23));
     }
 
     /**
@@ -226,21 +252,12 @@
         newSS.setCdmaSystemAndNetworkId(0, 0);
 
         // Test that notifyChange is not called for these fields
-        boolean notifyChangeWasCalled = false;
-        try {
-            ServiceStateProvider.notifyChangeForSubIdAndField(mContext, oldSS, newSS, subId);
-        } catch (RuntimeException e) {
-            final String message = e.getMessage();
-            if (message != null &&  message.equals("notifyChange!")) {
-                notifyChangeWasCalled = true;
-            }
-        }
-        assertFalse(notifyChangeWasCalled);
+        assertFalse(notifyChangeCalledForSubIdAndField(oldSS, newSS, subId));
     }
 
     @Test
     @SmallTest
-    public void testNotifyChanged() {
+    public void testNotifyChanged_noStateUpdated() {
         int subId = 0;
 
         ServiceState oldSS = new ServiceState();
@@ -251,57 +268,106 @@
         copyOfOldSS.setStateOutOfService();
         copyOfOldSS.setVoiceRegState(ServiceState.STATE_OUT_OF_SERVICE);
 
+        // Test that notifyChange is not called with no change in notifyChangeForSubIdAndField
+        assertFalse(notifyChangeCalledForSubId(oldSS, copyOfOldSS, subId));
+
+        // Test that notifyChange is not called with no change in notifyChangeForSubId
+        assertFalse(notifyChangeCalledForSubIdAndField(oldSS, copyOfOldSS, subId));
+    }
+
+    @Test
+    @SmallTest
+    public void testNotifyChanged_voiceRegStateUpdated() {
+        int subId = 0;
+
+        ServiceState oldSS = new ServiceState();
+        oldSS.setStateOutOfService();
+        oldSS.setVoiceRegState(ServiceState.STATE_OUT_OF_SERVICE);
+
         ServiceState newSS = new ServiceState();
         newSS.setStateOutOfService();
         newSS.setVoiceRegState(ServiceState.STATE_POWER_OFF);
 
-        // Test that notifyChange is not called with no change in notifyChangeForSubIdAndField
-        boolean notifyChangeWasCalled = false;
-        try {
-            ServiceStateProvider.notifyChangeForSubIdAndField(mContext, oldSS, copyOfOldSS, subId);
-        } catch (RuntimeException e) {
-            final String message = e.getMessage();
-            if (message != null &&  message.equals("notifyChange!")) {
-                notifyChangeWasCalled = true;
-            }
-        }
-        assertFalse(notifyChangeWasCalled);
-
-        // Test that notifyChange is not called with no change in notifyChangeForSubId
-        notifyChangeWasCalled = false;
-        try {
-            ServiceStateProvider.notifyChangeForSubId(mContext, oldSS, copyOfOldSS, subId);
-        } catch (RuntimeException e) {
-            final String message = e.getMessage();
-            if (message != null &&  message.equals("notifyChange!")) {
-                notifyChangeWasCalled = true;
-            }
-        }
-        assertFalse(notifyChangeWasCalled);
-
         // Test that notifyChange is called by notifyChangeForSubIdAndField when the voice_reg_state
         // changes
-        notifyChangeWasCalled = false;
-        try {
-            ServiceStateProvider.notifyChangeForSubIdAndField(mContext, oldSS, newSS, subId);
-        } catch (RuntimeException e) {
-            final String message = e.getMessage();
-            if (message != null &&  message.equals("notifyChange!")) {
-                notifyChangeWasCalled = true;
-            }
-        }
-        assertTrue(notifyChangeWasCalled);
+        assertTrue(notifyChangeCalledForSubId(oldSS, newSS, subId));
 
         // Test that notifyChange is called by notifyChangeForSubId when the voice_reg_state changes
-        notifyChangeWasCalled = false;
+        assertTrue(notifyChangeCalledForSubIdAndField(oldSS, newSS, subId));
+    }
+
+    @Test
+    @SmallTest
+    public void testNotifyChanged_dataNetworkTypeUpdated() {
+        int subId = 0;
+
+        // While we don't have a method to directly set dataNetworkType, we emulate a ServiceState
+        // change that will trigger the change of dataNetworkType, according to the logic in
+        // ServiceState#getDataNetworkType
+        ServiceState oldSS = new ServiceState();
+        oldSS.setStateOutOfService();
+
+        ServiceState newSS = new ServiceState();
+        newSS.setStateOutOfService();
+
+        NetworkRegistrationInfo nriWwan = new NetworkRegistrationInfo.Builder()
+                .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+                .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                .setRegistrationState(REGISTRATION_STATE_HOME)
+                .build();
+        newSS.addNetworkRegistrationInfo(nriWwan);
+
+        // Test that notifyChange is called by notifyChangeForSubId when the
+        // data_network_type changes
+        assertTrue(notifyChangeCalledForSubId(oldSS, newSS, subId));
+
+        // Test that notifyChange is called by notifyChangeForSubIdAndField when the
+        // data_network_type changes
+        assertTrue(notifyChangeCalledForSubIdAndField(oldSS, newSS, subId));
+    }
+
+    @Test
+    @SmallTest
+    public void testNotifyChanged_dataRegStateUpdated() {
+        int subId = 0;
+
+        ServiceState oldSS = new ServiceState();
+        oldSS.setStateOutOfService();
+        oldSS.setDataRegState(ServiceState.STATE_OUT_OF_SERVICE);
+
+        ServiceState newSS = new ServiceState();
+        newSS.setStateOutOfService();
+        newSS.setDataRegState(ServiceState.STATE_POWER_OFF);
+
+        // Test that notifyChange is called by notifyChangeForSubId
+        // when the data_reg_state changes
+        assertTrue(notifyChangeCalledForSubId(oldSS, newSS, subId));
+
+        // Test that notifyChange is called by notifyChangeForSubIdAndField
+        // when the data_reg_state changes
+        assertTrue(notifyChangeCalledForSubIdAndField(oldSS, newSS, subId));
+    }
+
+    // Check if notifyChange was called by notifyChangeForSubId
+    private boolean notifyChangeCalledForSubId(ServiceState oldSS,
+            ServiceState newSS, int subId) {
         try {
             ServiceStateProvider.notifyChangeForSubId(mContext, oldSS, newSS, subId);
-        } catch (RuntimeException e) {
-            final String message = e.getMessage();
-            if (message != null &&  message.equals("notifyChange!")) {
-                notifyChangeWasCalled = true;
-            }
+        } catch (TestNotifierException e) {
+            return true;
         }
-        assertTrue(notifyChangeWasCalled);
+        return false;
+    }
+
+    // Check if notifyChange was called by notifyChangeForSubIdAndField
+    private boolean notifyChangeCalledForSubIdAndField(ServiceState oldSS,
+            ServiceState newSS, int subId) {
+        try {
+            ServiceStateProvider.notifyChangeForSubIdAndField(mContext, oldSS, newSS, subId);
+        } catch (TestNotifierException e) {
+            return true;
+        }
+        return false;
     }
 }
diff --git a/tests/src/com/android/phone/SimPhonebookProviderTest.java b/tests/src/com/android/phone/SimPhonebookProviderTest.java
new file mode 100644
index 0000000..f897fac
--- /dev/null
+++ b/tests/src/com/android/phone/SimPhonebookProviderTest.java
@@ -0,0 +1,1496 @@
+/*
+ * 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.phone;
+
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_ADN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_FDN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.EF_SDN;
+
+import static com.android.internal.telephony.testing.CursorSubject.assertThat;
+import static com.android.internal.telephony.testing.TelephonyAssertions.assertThrows;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.SimPhonebookContract;
+import android.provider.SimPhonebookContract.ElementaryFiles;
+import android.provider.SimPhonebookContract.SimRecords;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.util.Pair;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.provider.ProviderTestRule;
+
+import com.android.internal.telephony.IIccPhoneBook;
+import com.android.internal.telephony.uicc.AdnCapacity;
+import com.android.internal.telephony.uicc.AdnRecord;
+import com.android.internal.telephony.uicc.IccConstants;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.truth.Correspondence;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.AdditionalAnswers;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+@RunWith(AndroidJUnit4.class)
+public final class SimPhonebookProviderTest {
+
+    private static final String EMOJI = new String(Character.toChars(0x1F642));
+    private static final Correspondence<AdnRecord, AdnRecord> ADN_RECORD_IS_EQUAL =
+            Correspondence.from(AdnRecord::isEqual, "isEqual");
+
+    @Rule
+    public final ProviderTestRule mProviderRule = new ProviderTestRule.Builder(
+            TestableSimPhonebookProvider.class, SimPhonebookContract.AUTHORITY).build();
+
+    private ContentResolver mResolver;
+    private FakeIccPhoneBook mIccPhoneBook;
+    private SubscriptionManager mMockSubscriptionManager;
+
+    private static List<SubscriptionInfo> createSubscriptionsWithIds(int... subscriptionIds) {
+        ImmutableList.Builder<SubscriptionInfo> builder = ImmutableList.builderWithExpectedSize(
+                subscriptionIds.length);
+        for (int i = 0; i < subscriptionIds.length; i++) {
+            builder.add(createSubscriptionInfo(i, subscriptionIds[i]));
+        }
+        return builder.build();
+    }
+
+    private static SubscriptionInfo createSubscriptionInfo(int slotIndex, int subscriptiondId) {
+        return new SubscriptionInfo(
+                subscriptiondId, "", slotIndex, null, null, 0, 0, null, 0, null, null, null, null,
+                false, null, null);
+    }
+
+    @Before
+    public void setUp() {
+        mMockSubscriptionManager = spy(
+                Objects.requireNonNull(ApplicationProvider.getApplicationContext()
+                        .getSystemService(SubscriptionManager.class)));
+        mIccPhoneBook = new FakeIccPhoneBook();
+        mResolver = mProviderRule.getResolver();
+
+        TestableSimPhonebookProvider.setup(mResolver, mMockSubscriptionManager, mIccPhoneBook);
+    }
+
+    @Test
+    public void query_entityFiles_returnsCursorWithCorrectProjection() {
+        // Null projection
+        try (Cursor cursor = mResolver.query(ElementaryFiles.CONTENT_URI, null, null,
+                null)) {
+            assertThat(Objects.requireNonNull(cursor).getColumnNames()).asList()
+                    .containsExactlyElementsIn(
+                            SimPhonebookProvider.ELEMENTARY_FILES_ALL_COLUMNS);
+        }
+
+        // Empty projection
+        try (Cursor cursor = mResolver.query(ElementaryFiles.CONTENT_URI, new String[0], null,
+                null)) {
+            assertThat(cursor).hasColumnNames();
+        }
+
+        // Single column
+        try (Cursor cursor = mResolver.query(ElementaryFiles.CONTENT_URI, new String[]{
+                ElementaryFiles.EF_TYPE
+        }, null, null)) {
+            assertThat(cursor).hasColumnNames(ElementaryFiles.EF_TYPE);
+        }
+
+        // Duplicate column
+        try (Cursor cursor = mResolver.query(ElementaryFiles.CONTENT_URI, new String[]{
+                ElementaryFiles.SUBSCRIPTION_ID, ElementaryFiles.SUBSCRIPTION_ID
+        }, null, null)) {
+            assertThat(cursor).hasColumnNames(ElementaryFiles.SUBSCRIPTION_ID,
+                    ElementaryFiles.SUBSCRIPTION_ID);
+        }
+
+        // Random order of all columns
+        String[] projection = Arrays.copyOf(
+                SimPhonebookProvider.ELEMENTARY_FILES_ALL_COLUMNS,
+                SimPhonebookProvider.ELEMENTARY_FILES_ALL_COLUMNS.length);
+        Collections.shuffle(Arrays.asList(projection));
+        try (Cursor cursor = mResolver.query(ElementaryFiles.CONTENT_URI, projection, null, null)) {
+            assertThat(cursor).hasColumnNames(projection);
+        }
+    }
+
+    @Test
+    public void query_entityFiles_unrecognizedColumn_throwsIllegalArgumentException() {
+        assertThrows(IllegalArgumentException.class, () ->
+                mResolver.query(ElementaryFiles.CONTENT_URI, new String[]{"invalid_column"}, null,
+                        null));
+    }
+
+    @Test
+    public void query_entityFiles_noSim_returnsEmptyCursor() {
+        when(mMockSubscriptionManager.getActiveSubscriptionInfoList()).thenReturn(
+                ImmutableList.of());
+
+        try (Cursor cursor = mResolver.query(ElementaryFiles.CONTENT_URI, null, null, null)) {
+            assertThat(cursor).hasCount(0);
+        }
+    }
+
+    @Test
+    public void query_entityFiles_multiSim_returnsCursorWithRowForEachSimEf() {
+        setupSimsWithSubscriptionIds(2, 3, 7);
+
+        mIccPhoneBook.setRecordsSize(2, IccConstants.EF_ADN, 10, 25);
+        mIccPhoneBook.setRecordsSize(2, IccConstants.EF_FDN, 5, 20);
+        mIccPhoneBook.setRecordsSize(2, IccConstants.EF_SDN, 15, 20);
+        mIccPhoneBook.setRecordsSize(3, IccConstants.EF_ADN, 100, 30);
+        // These Will be omitted from results because zero size indicates the EF is not supported.
+        mIccPhoneBook.setRecordsSize(3, IccConstants.EF_FDN, 0, 0);
+        mIccPhoneBook.setRecordsSize(3, IccConstants.EF_SDN, 0, 0);
+        mIccPhoneBook.setRecordsSize(7, IccConstants.EF_ADN, 0, 0);
+        mIccPhoneBook.setRecordsSize(7, IccConstants.EF_FDN, 0, 0);
+        mIccPhoneBook.setRecordsSize(7, IccConstants.EF_SDN, 0, 0);
+
+        String[] projection = {
+                ElementaryFiles.SLOT_INDEX, ElementaryFiles.SUBSCRIPTION_ID,
+                ElementaryFiles.EF_TYPE, ElementaryFiles.MAX_RECORDS,
+                ElementaryFiles.NAME_MAX_LENGTH, ElementaryFiles.PHONE_NUMBER_MAX_LENGTH
+        };
+        try (Cursor cursor = mResolver.query(ElementaryFiles.CONTENT_URI, projection, null, null)) {
+            assertThat(cursor).hasColumnNames(projection);
+
+            assertThat(cursor)
+                    .atRow(0).hasRowValues(0, 2, ElementaryFiles.EF_ADN, 10, 11, 20)
+                    .atRow(1).hasRowValues(0, 2, ElementaryFiles.EF_FDN, 5, 6, 20)
+                    .atRow(2).hasRowValues(0, 2, ElementaryFiles.EF_SDN, 15, 6, 20)
+                    .atRow(3).hasRowValues(1, 3, ElementaryFiles.EF_ADN, 100, 16, 20);
+        }
+    }
+
+    @Test
+    public void query_entityFiles_simWithZeroSizes_returnsEmptyCursor() {
+        setupSimsWithSubscriptionIds(1);
+
+        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 0, 0);
+        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_FDN, 0, 0);
+        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_SDN, 0, 0);
+
+        try (Cursor cursor = mResolver.query(ElementaryFiles.CONTENT_URI, null, null, null)) {
+            assertThat(cursor).hasCount(0);
+        }
+    }
+
+    @Test
+    public void query_entityFilesItem_nullProjection_returnsCursorWithCorrectProjection() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+
+        // Null projection
+        try (Cursor cursor = mResolver.query(ElementaryFiles.getItemUri(1, EF_ADN), null, null,
+                null)) {
+            assertThat(Objects.requireNonNull(cursor).getColumnNames()).asList()
+                    .containsExactlyElementsIn(
+                            SimPhonebookProvider.ELEMENTARY_FILES_ALL_COLUMNS);
+        }
+    }
+
+    @Test
+    public void query_adnRecords_returnsCursorWithMatchingProjection() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+        Uri contentAdn = SimRecords.getContentUri(1, EF_ADN);
+
+        // Null projection
+        try (Cursor cursor = mResolver.query(contentAdn, null, null, null)) {
+            assertThat(Objects.requireNonNull(cursor).getColumnNames()).asList()
+                    .containsExactlyElementsIn(SimPhonebookProvider.SIM_RECORDS_ALL_COLUMNS);
+        }
+
+        // Empty projection
+        try (Cursor cursor = mResolver.query(contentAdn, new String[0], null, null)) {
+            assertThat(cursor).hasColumnNames();
+        }
+
+        // Single column
+        try (Cursor cursor = mResolver.query(contentAdn, new String[]{
+                SimRecords.PHONE_NUMBER
+        }, null, null)) {
+            assertThat(cursor).hasColumnNames(SimRecords.PHONE_NUMBER);
+        }
+
+        // Duplicate column
+        try (Cursor cursor = mResolver.query(contentAdn, new String[]{
+                SimRecords.PHONE_NUMBER, SimRecords.PHONE_NUMBER
+        }, null, null)) {
+            assertThat(cursor).hasColumnNames(SimRecords.PHONE_NUMBER, SimRecords.PHONE_NUMBER);
+        }
+
+        // Random order of all columns
+        String[] projection = Arrays.copyOf(
+                SimPhonebookProvider.SIM_RECORDS_ALL_COLUMNS,
+                SimPhonebookProvider.SIM_RECORDS_ALL_COLUMNS.length);
+        Collections.shuffle(Arrays.asList(projection));
+        try (Cursor cursor = mResolver.query(contentAdn, projection, null, null)) {
+            assertThat(cursor).hasColumnNames(projection);
+        }
+    }
+
+    @Test
+    public void query_adnRecords_noRecords_returnsEmptyCursor() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+
+        try (Cursor cursor = mResolver.query(SimRecords.getContentUri(1, EF_ADN), null, null,
+                null)) {
+            assertThat(cursor).hasCount(0);
+        }
+    }
+
+    @Test
+    public void query_simRecords_nullRecordList_returnsEmptyCursor() throws Exception {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+        // Use a mock so that a null list can be returned
+        IIccPhoneBook mockIccPhoneBook = mock(
+                IIccPhoneBook.class, AdditionalAnswers.delegatesTo(mIccPhoneBook));
+        when(mockIccPhoneBook.getAdnRecordsInEfForSubscriber(anyInt(), anyInt())).thenReturn(null);
+        TestableSimPhonebookProvider.setup(mResolver, mMockSubscriptionManager, mockIccPhoneBook);
+
+        try (Cursor adnCursor = mResolver.query(SimRecords.getContentUri(1, EF_ADN), null, null,
+                null);
+             Cursor fdnCursor = mResolver.query(SimRecords.getContentUri(1, EF_FDN), null, null,
+                     null);
+             Cursor sdnCursor = mResolver.query(SimRecords.getContentUri(1, EF_SDN), null, null,
+                     null)
+        ) {
+            assertThat(adnCursor).hasCount(0);
+            assertThat(fdnCursor).hasCount(0);
+            assertThat(sdnCursor).hasCount(0);
+        }
+    }
+
+    @Test
+    public void query_simRecords_singleSim_returnsDataForCorrectEf() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Person Adn1", "8005550101");
+        mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Person Adn2", "8005550102");
+        mIccPhoneBook.addRecord(1, IccConstants.EF_FDN, "Person Fdn", "8005550103");
+        mIccPhoneBook.addRecord(1, IccConstants.EF_SDN, "Person Sdn", "8005550104");
+        mIccPhoneBook.setDefaultSubscriptionId(1);
+
+        String[] projection = {
+                SimRecords.SUBSCRIPTION_ID,
+                SimRecords.ELEMENTARY_FILE_TYPE,
+                SimRecords.RECORD_NUMBER,
+                SimRecords.NAME,
+                SimRecords.PHONE_NUMBER
+        };
+        try (Cursor adnCursor = mResolver.query(SimRecords.getContentUri(1, EF_ADN),
+                projection, null, null);
+             Cursor fdnCursor = mResolver.query(SimRecords.getContentUri(1, EF_FDN),
+                     projection, null, null);
+             Cursor sdnCursor = mResolver.query(SimRecords.getContentUri(1, EF_SDN),
+                     projection, null, null)
+        ) {
+
+            assertThat(adnCursor)
+                    .atRow(0).hasRowValues(1, ElementaryFiles.EF_ADN, 1, "Person Adn1",
+                    "8005550101")
+                    .atRow(1).hasRowValues(1, ElementaryFiles.EF_ADN, 2, "Person Adn2",
+                    "8005550102");
+            assertThat(fdnCursor)
+                    .atRow(0).hasRowValues(1, ElementaryFiles.EF_FDN, 1, "Person Fdn",
+                    "8005550103");
+            assertThat(sdnCursor)
+                    .atRow(0).hasRowValues(1, ElementaryFiles.EF_SDN, 1, "Person Sdn",
+                    "8005550104");
+        }
+    }
+
+    @Test
+    public void query_adnRecords_returnsAdnData() {
+        setupSimsWithSubscriptionIds(1, 2, 4);
+        mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Person Sim1", "8005550101");
+        mIccPhoneBook.addRecord(1, IccConstants.EF_FDN, "Omitted Sim1", "8005550199");
+        mIccPhoneBook.addRecord(2, IccConstants.EF_ADN, "Person Sim2a", "8005550103");
+        mIccPhoneBook.addRecord(2, IccConstants.EF_ADN, "Person Sim2b", "8005550104");
+        mIccPhoneBook.addRecord(2, IccConstants.EF_ADN, "Person Sim2c", "8005550105");
+        mIccPhoneBook.addRecord(2, IccConstants.EF_SDN, "Omitted Sim2", "8005550198");
+        mIccPhoneBook.addRecord(4, IccConstants.EF_ADN, "Person Sim4", "8005550106");
+        mIccPhoneBook.setDefaultSubscriptionId(1);
+
+        String[] projection = {
+                SimRecords.SUBSCRIPTION_ID,
+                SimRecords.ELEMENTARY_FILE_TYPE,
+                SimRecords.RECORD_NUMBER,
+                SimRecords.NAME,
+                SimRecords.PHONE_NUMBER
+        };
+        try (Cursor cursorSim1 = mResolver.query(SimRecords.getContentUri(1, EF_ADN),
+                projection, null, null);
+             Cursor cursorSim2 = mResolver.query(SimRecords.getContentUri(2, EF_ADN),
+                     projection, null, null);
+             Cursor cursorSim4 = mResolver.query(SimRecords.getContentUri(4, EF_ADN),
+                     projection, null, null)
+        ) {
+
+            assertThat(cursorSim1).hasData(new Object[][]{
+                    {1, ElementaryFiles.EF_ADN, 1, "Person Sim1", "8005550101"},
+            });
+            assertThat(cursorSim2).hasData(new Object[][]{
+                    {2, ElementaryFiles.EF_ADN, 1, "Person Sim2a", "8005550103"},
+                    {2, ElementaryFiles.EF_ADN, 2, "Person Sim2b", "8005550104"},
+                    {2, ElementaryFiles.EF_ADN, 3, "Person Sim2c", "8005550105"},
+            });
+            assertThat(cursorSim4).hasData(new Object[][]{
+                    {4, ElementaryFiles.EF_ADN, 1, "Person Sim4", "8005550106"},
+            });
+        }
+    }
+
+    @Test
+    public void query_fdnRecords_returnsFdnData() {
+        setupSimsWithSubscriptionIds(1, 2, 4);
+        mIccPhoneBook.makeAllEfsSupported(1, 2, 4);
+        mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Person Sim1", "8005550101");
+        mIccPhoneBook.addRecord(2, IccConstants.EF_ADN, "Person Sim2a", "8005550103");
+        mIccPhoneBook.addRecord(2, IccConstants.EF_FDN, "Person Sim2b", "8005550104");
+        mIccPhoneBook.addRecord(2, IccConstants.EF_FDN, "Person Sim2c", "8005550105");
+        mIccPhoneBook.addRecord(4, IccConstants.EF_SDN, "Person Sim4", "8005550106");
+        mIccPhoneBook.setDefaultSubscriptionId(1);
+
+        String[] projection = {
+                SimRecords.SUBSCRIPTION_ID,
+                SimRecords.ELEMENTARY_FILE_TYPE,
+                SimRecords.RECORD_NUMBER,
+                SimRecords.NAME,
+                SimRecords.PHONE_NUMBER
+        };
+        try (Cursor cursorSim1Fdn = mResolver.query(SimRecords.getContentUri(1, EF_FDN),
+                projection, null, null);
+             Cursor cursorSim2Fdn = mResolver.query(SimRecords.getContentUri(2, EF_FDN),
+                     projection, null, null);
+             Cursor cursorSim4Fdn = mResolver.query(SimRecords.getContentUri(4, EF_FDN),
+                     projection, null, null)
+        ) {
+
+            assertThat(cursorSim1Fdn).hasCount(0);
+            assertThat(cursorSim2Fdn).hasData(new Object[][]{
+                    {2, ElementaryFiles.EF_FDN, 1, "Person Sim2b", "8005550104"},
+                    {2, ElementaryFiles.EF_FDN, 2, "Person Sim2c", "8005550105"},
+            });
+            assertThat(cursorSim4Fdn).hasCount(0);
+        }
+    }
+
+    @Test
+    public void query_sdnRecords_returnsSdnData() {
+        setupSimsWithSubscriptionIds(1, 2, 4);
+        mIccPhoneBook.makeAllEfsSupported(1, 2, 4);
+        mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Person Adn1", "8005550101");
+        mIccPhoneBook.addRecord(1, IccConstants.EF_FDN, "Person Fdn1", "8005550102");
+        mIccPhoneBook.addRecord(1, IccConstants.EF_SDN, "Person Sdn1", "8005550103");
+        mIccPhoneBook.addRecord(2, IccConstants.EF_ADN, "Person Adn2a", "8005550104");
+        mIccPhoneBook.addRecord(2, IccConstants.EF_FDN, "Person Fdn2b", "8005550105");
+        mIccPhoneBook.addRecord(4, IccConstants.EF_SDN, "Person Sdn4a", "8005550106");
+        mIccPhoneBook.addRecord(4, IccConstants.EF_SDN, "Person Sdn4b", "8005550107");
+        mIccPhoneBook.setDefaultSubscriptionId(1);
+
+        String[] projection = {
+                SimRecords.SUBSCRIPTION_ID,
+                SimRecords.ELEMENTARY_FILE_TYPE,
+                SimRecords.RECORD_NUMBER,
+                SimRecords.NAME,
+                SimRecords.PHONE_NUMBER
+        };
+        try (Cursor cursorSim1Sdn = mResolver.query(SimRecords.getContentUri(1, EF_SDN),
+                projection, null, null);
+             Cursor cursorSim2Sdn = mResolver.query(SimRecords.getContentUri(2, EF_SDN),
+                     projection, null, null);
+             Cursor cursorSim4Sdn = mResolver.query(SimRecords.getContentUri(4, EF_SDN),
+                     projection, null, null)
+        ) {
+
+            assertThat(cursorSim1Sdn)
+                    .atRow(0).hasRowValues(1, ElementaryFiles.EF_SDN, 1, "Person Sdn1",
+                    "8005550103");
+            assertThat(cursorSim2Sdn).hasCount(0);
+            assertThat(cursorSim4Sdn)
+                    .atRow(0).hasRowValues(4, ElementaryFiles.EF_SDN, 1, "Person Sdn4a",
+                    "8005550106")
+                    .atRow(1).hasRowValues(4, ElementaryFiles.EF_SDN, 2, "Person Sdn4b",
+                    "8005550107");
+        }
+    }
+
+    @Test
+    public void query_adnRecords_nonExistentSim_throwsCorrectException() {
+        setupSimsWithSubscriptionIds(1);
+
+        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+                () -> mResolver.query(SimRecords.getContentUri(123, EF_ADN), null, null, null));
+        assertThat(e).hasMessageThat().isEqualTo("No active SIM with subscription ID 123");
+    }
+
+    @Test
+    public void insert_nonExistentSim_throwsCorrectException() {
+        setupSimsWithSubscriptionIds(1);
+        ContentValues values = new ContentValues();
+        values.put(SimRecords.NAME, "Name");
+        values.put(SimRecords.PHONE_NUMBER, "123");
+
+        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+                () -> mResolver.insert(SimRecords.getContentUri(123, EF_ADN), values));
+        assertThat(e).hasMessageThat().isEqualTo("No active SIM with subscription ID 123");
+    }
+
+    @Test
+    public void update_nonExistentSim_throwsCorrectException() {
+        setupSimsWithSubscriptionIds(1);
+        ContentValues values = new ContentValues();
+        values.put(SimRecords.NAME, "Name");
+        values.put(SimRecords.PHONE_NUMBER, "123");
+
+        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+                () -> mResolver.update(SimRecords.getItemUri(123, EF_ADN, 1), values, null));
+        assertThat(e).hasMessageThat().isEqualTo("No active SIM with subscription ID 123");
+    }
+
+    @Test
+    public void delete_nonExistentSim_throwsCorrectException() {
+        setupSimsWithSubscriptionIds(1);
+
+        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+                () -> mResolver.delete(SimRecords.getItemUri(123, EF_ADN, 1), null));
+        assertThat(e).hasMessageThat().isEqualTo("No active SIM with subscription ID 123");
+    }
+
+    @Test
+    public void query_adnRecords_zeroSizeEf_throwsCorrectException() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 0, 0);
+
+        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+                () -> mResolver.query(SimRecords.getContentUri(1, EF_ADN), null, null, null));
+        assertThat(e).hasMessageThat().isEqualTo(
+                "adn is not supported for SIM with subscription ID 1");
+    }
+
+    @Test
+    public void query_itemUri_returnsCorrectRow() {
+        setupSimsWithSubscriptionIds(1, 2);
+        mIccPhoneBook.addRecord(1,
+                new AdnRecord(IccConstants.EF_ADN, 1, "Name@Adn1[1]", "8005550101"));
+        mIccPhoneBook.addRecord(1,
+                new AdnRecord(IccConstants.EF_ADN, 2, "Name@Adn1[2]", "8005550102"));
+        mIccPhoneBook.addRecord(1,
+                new AdnRecord(IccConstants.EF_ADN, 3, "Name@Adn1[3]", "8005550103"));
+        mIccPhoneBook.addRecord(2,
+                new AdnRecord(IccConstants.EF_ADN, 3, "Name@Adn2[3]", "8005550104"));
+        mIccPhoneBook.addRecord(1,
+                new AdnRecord(IccConstants.EF_FDN, 1, "Name@Fdn1[1]", "8005550105"));
+        mIccPhoneBook.addRecord(2,
+                new AdnRecord(IccConstants.EF_SDN, 1, "Name@Sdn2[1]", "8005550106"));
+
+        String[] projection = {
+                SimRecords.SUBSCRIPTION_ID, SimRecords.ELEMENTARY_FILE_TYPE,
+                SimRecords.RECORD_NUMBER, SimRecords.NAME, SimRecords.PHONE_NUMBER
+        };
+        try (Cursor item1 = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1),
+                projection, null, null);
+             Cursor item2 = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 3),
+                     projection, null, null);
+             Cursor item3 = mResolver.query(SimRecords.getItemUri(2, ElementaryFiles.EF_ADN, 3),
+                     projection, null, null);
+             Cursor item4 = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_FDN, 1),
+                     projection, null, null);
+             Cursor item5 = mResolver.query(SimRecords.getItemUri(2, ElementaryFiles.EF_SDN, 1),
+                     projection, null, null)
+        ) {
+            assertThat(item1).hasSingleRow(1, ElementaryFiles.EF_ADN, 1, "Name@Adn1[1]",
+                    "8005550101");
+            assertThat(item2).hasSingleRow(1, ElementaryFiles.EF_ADN, 3, "Name@Adn1[3]",
+                    "8005550103");
+            assertThat(item3).hasSingleRow(2, ElementaryFiles.EF_ADN, 3, "Name@Adn2[3]",
+                    "8005550104");
+            assertThat(item4).hasSingleRow(1, ElementaryFiles.EF_FDN, 1, "Name@Fdn1[1]",
+                    "8005550105");
+            assertThat(item5).hasSingleRow(2, ElementaryFiles.EF_SDN, 1, "Name@Sdn2[1]",
+                    "8005550106");
+        }
+    }
+
+    @Test
+    public void query_itemUriNullProjection_returnsCursorWithAllColumns() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+
+        try (Cursor cursor = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1),
+                null, null, null)
+        ) {
+            assertThat(Objects.requireNonNull(
+                    cursor).getColumnNames()).asList().containsExactlyElementsIn(
+                    SimPhonebookProvider.SIM_RECORDS_ALL_COLUMNS);
+        }
+    }
+
+    @Test
+    public void query_itemUriEmptyRecord_returnsEmptyCursor() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 30);
+        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_FDN, 1, 30);
+        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_SDN, 1, 30);
+
+        try (Cursor adnItem = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1),
+                null, null, null);
+             Cursor fdnItem = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_FDN, 1),
+                     null, null, null);
+             Cursor sdnItem = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_SDN, 1),
+                     null, null, null)
+        ) {
+            assertThat(adnItem).hasCount(0);
+            assertThat(fdnItem).hasCount(0);
+            assertThat(sdnItem).hasCount(0);
+        }
+    }
+
+    @Test
+    public void query_itemUriIndexExceedsMax_returnsEmptyCursor() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 30);
+        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_FDN, 1, 30);
+        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_SDN, 1, 30);
+
+        try (Cursor adnItem = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 2),
+                null, null, null);
+             Cursor fdnItem = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_FDN, 2),
+                     null, null, null);
+             Cursor sdnItem = mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_SDN, 2),
+                     null, null, null)
+        ) {
+            assertThat(adnItem).hasCount(0);
+            assertThat(fdnItem).hasCount(0);
+            assertThat(sdnItem).hasCount(0);
+        }
+    }
+
+    @Test
+    public void query_invalidItemIndex_throwsIllegalArgumentException() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+
+        assertThrows(IllegalArgumentException.class, () ->
+                mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, -1),
+                        null, null, null));
+        assertThrows(IllegalArgumentException.class, () ->
+                mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_FDN, -1),
+                        null, null, null));
+        assertThrows(IllegalArgumentException.class, () ->
+                mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_SDN, -1),
+                        null, null, null));
+        assertThrows(IllegalArgumentException.class, () ->
+                mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 0),
+                        null, null, null));
+        assertThrows(IllegalArgumentException.class, () ->
+                mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_FDN, 0),
+                        null, null, null));
+        assertThrows(IllegalArgumentException.class, () ->
+                mResolver.query(SimRecords.getItemUri(1, ElementaryFiles.EF_SDN, 0),
+                        null, null, null));
+    }
+
+    @Test
+    public void insert_adnRecord_addsAdnRecordAndReturnsUriForNewRecord() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+
+        ContentValues values = new ContentValues();
+        values.put(SimRecords.NAME, "First Last");
+        values.put(SimRecords.PHONE_NUMBER, "8005550101");
+
+        Uri uri = mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values);
+
+        List<AdnRecord> records = mIccPhoneBook.getAdnRecordsInEfForSubscriber(
+                1, IccConstants.EF_ADN).stream()
+                .filter(((Predicate<AdnRecord>) AdnRecord::isEmpty).negate())
+                .collect(Collectors.toList());
+
+        assertThat(records)
+                .comparingElementsUsing(ADN_RECORD_IS_EQUAL)
+                .containsExactly(new AdnRecord(IccConstants.EF_ADN, 1, "First Last", "8005550101"));
+
+        assertThat(uri).isEqualTo(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1));
+    }
+
+    @Test
+    public void insert_adnRecordWithExistingRecords_returnsUriWithCorrectIndex() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.setDefaultSubscriptionId(1);
+        mIccPhoneBook.addRecord(new AdnRecord(IccConstants.EF_ADN, 2, "Existing1", "8005550101"));
+        mIccPhoneBook.addRecord(new AdnRecord(IccConstants.EF_ADN, 3, "Existing2", "8005550102"));
+        mIccPhoneBook.addRecord(new AdnRecord(IccConstants.EF_ADN, 5, "Existing3", "8005550103"));
+
+        ContentValues values = new ContentValues();
+        values.put(SimRecords.NAME, "New1");
+        values.put(SimRecords.PHONE_NUMBER, "8005550104");
+
+        Uri insert1 = mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values);
+        values.put(SimRecords.NAME, "New2");
+        values.put(SimRecords.PHONE_NUMBER, "8005550105");
+        Uri insert2 = mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values);
+        values.put(SimRecords.NAME, "New3");
+        values.put(SimRecords.PHONE_NUMBER, "8005550106");
+        Uri insert3 = mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values);
+
+        assertThat(
+                mIccPhoneBook.getAdnRecordsInEfForSubscriber(1, IccConstants.EF_ADN).subList(0, 6))
+                .comparingElementsUsing(ADN_RECORD_IS_EQUAL)
+                .containsExactly(
+                        new AdnRecord(IccConstants.EF_ADN, 1, "New1", "8005550104"),
+                        new AdnRecord(IccConstants.EF_ADN, 2, "Existing1", "8005550101"),
+                        new AdnRecord(IccConstants.EF_ADN, 3, "Existing2", "8005550102"),
+                        new AdnRecord(IccConstants.EF_ADN, 4, "New2", "8005550105"),
+                        new AdnRecord(IccConstants.EF_ADN, 5, "Existing3", "8005550103"),
+                        new AdnRecord(IccConstants.EF_ADN, 6, "New3", "8005550106"));
+        assertThat(insert1).isEqualTo(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1));
+        assertThat(insert2).isEqualTo(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 4));
+        assertThat(insert3).isEqualTo(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 6));
+    }
+
+    @Test
+    public void insert_efFull_throwsCorrectException() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 30);
+        mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Existing", "8005550101");
+
+        ContentValues values = new ContentValues();
+        values.put(SimRecords.NAME, "New");
+        values.put(SimRecords.PHONE_NUMBER, "8005550102");
+
+        Uri uri = SimRecords.getContentUri(1, EF_ADN);
+        IllegalStateException e = assertThrows(IllegalStateException.class,
+                () -> mResolver.insert(uri, values));
+        assertThat(e).hasMessageThat().isEqualTo(
+                uri + " is full. Please delete records to add new ones.");
+    }
+
+    @Test
+    public void insert_nameWithNonGsmCharacters_addsAdnRecord() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+
+        ContentValues values = new ContentValues();
+        String name = "abc日本" + EMOJI;
+        values.put(SimRecords.NAME, name);
+        values.put(SimRecords.PHONE_NUMBER, "8005550101");
+
+        Uri uri = mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values);
+
+        List<AdnRecord> records = mIccPhoneBook.getAdnRecordsInEfForSubscriber(
+                1, IccConstants.EF_ADN).stream()
+                .filter(((Predicate<AdnRecord>) AdnRecord::isEmpty).negate())
+                .collect(Collectors.toList());
+
+        assertThat(records)
+                .comparingElementsUsing(ADN_RECORD_IS_EQUAL)
+                .containsExactly(new AdnRecord(IccConstants.EF_ADN, 1, name, "8005550101"));
+
+        assertThat(uri).isEqualTo(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1));
+    }
+
+    @Test
+    public void insert_nullValues_returnsNull() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+
+        Uri result = mResolver.insert(SimRecords.getContentUri(1, EF_ADN), null);
+
+        assertThat(result).isNull();
+    }
+
+    @Test
+    public void update_nullValues_returnsZero() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+        mIccPhoneBook.addAdnRecord(1, "Name", "5550101");
+
+        int result = mResolver.update(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1), null,
+                null);
+
+        assertThat(result).isEqualTo(0);
+    }
+
+    @Test
+    public void insert_emptyValues_returnsNull() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+
+        Uri result = mResolver.insert(SimRecords.getContentUri(1, EF_ADN), new ContentValues());
+
+        assertThat(result).isNull();
+    }
+
+    @Test
+    public void insert_nameOmitted_createsRecordWithJustPhoneNumber() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+
+        ContentValues values = new ContentValues();
+        // No name
+        values.put(SimRecords.PHONE_NUMBER, "18005550101");
+
+        mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values);
+
+        // Null name
+        values.putNull(SimRecords.NAME);
+        values.put(SimRecords.PHONE_NUMBER, "18005550102");
+        mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values);
+
+        // Empty name
+        values.put(SimRecords.NAME, "");
+        values.put(SimRecords.PHONE_NUMBER, "18005550103");
+        mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values);
+
+        assertThat(mIccPhoneBook.getAllValidRecords())
+                .comparingElementsUsing(ADN_RECORD_IS_EQUAL)
+                .containsExactly(
+                        new AdnRecord(IccConstants.EF_ADN, 1, "", "18005550101"),
+                        new AdnRecord(IccConstants.EF_ADN, 2, "", "18005550102"),
+                        new AdnRecord(IccConstants.EF_ADN, 3, "", "18005550103"));
+    }
+
+    @Test
+    public void insert_phoneNumberOmitted_throwsCorrectException() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 25);
+
+        ContentValues values = new ContentValues();
+        values.put(SimRecords.NAME, "Name");
+
+        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+                () -> mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values));
+        assertThat(e).hasMessageThat().isEqualTo(SimRecords.PHONE_NUMBER + " is required.");
+    }
+
+    @Test
+    public void insert_nameTooLong_throwsCorrectException() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 25);
+
+        ContentValues values = new ContentValues();
+        // Name is limited to 11 characters when the max record size is 25
+        values.put(SimRecords.NAME, "1234567890ab");
+        values.put(SimRecords.PHONE_NUMBER, "8005550102");
+
+        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+                () -> mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values));
+
+        assertThat(e).hasMessageThat().isEqualTo(SimRecords.NAME + " is too long.");
+
+        // 2 bytes per character and 4 for the emoji. So this is 14 characters long.
+        values.put(SimRecords.NAME, "abc日本" + EMOJI);
+        e = assertThrows(IllegalArgumentException.class,
+                () -> mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values));
+
+        assertThat(e).hasMessageThat().isEqualTo(SimRecords.NAME + " is too long.");
+    }
+
+    @Test
+    public void insert_phoneNumberTooLong_throwsCorrectException() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 25);
+
+        ContentValues values = new ContentValues();
+        values.put(SimRecords.NAME, "Name");
+        // 21 digits is longer than max of 20
+        values.put(SimRecords.PHONE_NUMBER, "123456789012345678901");
+
+        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+                () -> mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values));
+
+        assertThat(e).hasMessageThat().isEqualTo(SimRecords.PHONE_NUMBER + " is too long.");
+    }
+
+    @Test
+    public void insert_numberWithInvalidCharacters_throwsCorrectException() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 32);
+
+        ContentValues values = new ContentValues();
+        values.put(SimRecords.NAME, "Name");
+        values.put(SimRecords.PHONE_NUMBER, "(800)555-0190 x7777");
+
+        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+                () -> mResolver.insert(SimRecords.getContentUri(1, ElementaryFiles.EF_ADN),
+                        values,
+                        null));
+        assertThat(e).hasMessageThat().isEqualTo(
+                SimRecords.PHONE_NUMBER + " contains unsupported characters.");
+
+        // The insert didn't actually change the data.
+        assertThat(mIccPhoneBook.getAllValidRecords()).isEmpty();
+    }
+
+    @Test
+    public void insert_unsupportedColumn_throwsCorrectException() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 25);
+
+        ContentValues values = new ContentValues();
+        values.put(SimRecords.NAME, "Name");
+        values.put(SimRecords.PHONE_NUMBER, "18005550101");
+        values.put(SimRecords.RECORD_NUMBER, 8);
+        values.put("extra_phone2", "18005550102");
+
+        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+                () -> mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values));
+        assertThat(e).hasMessageThat().isEqualTo("Unsupported columns: "
+                + SimRecords.RECORD_NUMBER + ",extra_phone2");
+    }
+
+    @Test
+    public void update_existingRecord_updatesRecord() {
+        setupSimsWithSubscriptionIds(1, 2);
+        AdnRecord[] unchanged = new AdnRecord[]{
+                new AdnRecord(IccConstants.EF_ADN, 3, "Other1", "8005550102"),
+                new AdnRecord(IccConstants.EF_ADN, 2, "Other2", "8005550103"),
+                new AdnRecord(IccConstants.EF_FDN, 2, "Other3", "8005550104")
+        };
+        mIccPhoneBook.addRecord(1, unchanged[0]);
+        mIccPhoneBook.addRecord(2, unchanged[1]);
+        mIccPhoneBook.addRecord(2, unchanged[2]);
+        mIccPhoneBook.addRecord(1,
+                new AdnRecord(IccConstants.EF_ADN, 2, "Initial Name", "8005550101"));
+
+        ContentValues values = new ContentValues();
+        values.put(SimRecords.NAME, "Updated Name");
+        values.put(SimRecords.PHONE_NUMBER, "8005550105");
+
+        int result = mResolver.update(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 2), values,
+                null);
+
+        assertThat(result).isEqualTo(1);
+
+        List<AdnRecord> finalRecords = mIccPhoneBook.getAllValidRecords();
+
+        assertThat(finalRecords).comparingElementsUsing(ADN_RECORD_IS_EQUAL)
+                .containsAtLeastElementsIn(unchanged);
+        assertThat(finalRecords).comparingElementsUsing(ADN_RECORD_IS_EQUAL)
+                .doesNotContain(
+                        new AdnRecord(IccConstants.EF_ADN, 2, "Initial Name", "80005550101"));
+        assertThat(finalRecords).comparingElementsUsing(ADN_RECORD_IS_EQUAL)
+                .contains(new AdnRecord(IccConstants.EF_ADN, 2, "Updated Name", "8005550105"));
+    }
+
+    @Test
+    public void update_emptyRecord_updatesRecord() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+
+        ContentValues values = new ContentValues();
+        values.put(SimRecords.NAME, "name");
+        values.put(SimRecords.PHONE_NUMBER, "18005550101");
+        // No record actually exists with record number 10 but we allow clients to update it
+        // as a way to set the information at a specific record number.
+        int result = mResolver.update(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 10),
+                values, null);
+
+        assertThat(result).isEqualTo(1);
+        List<AdnRecord> finalRecords = mIccPhoneBook.getAllValidRecords();
+        assertThat(finalRecords).comparingElementsUsing(ADN_RECORD_IS_EQUAL)
+                .containsExactly(new AdnRecord(IccConstants.EF_ADN, 10, "name", "18005550101"));
+    }
+
+    @Test
+    public void delete_existingRecord_deletesRecord() {
+        setupSimsWithSubscriptionIds(1, 2);
+        AdnRecord[] unchanged = new AdnRecord[]{
+                new AdnRecord(IccConstants.EF_ADN, 3, "Other1", "8005550102"),
+                new AdnRecord(IccConstants.EF_ADN, 2, "Other2", "8005550103"),
+                new AdnRecord(IccConstants.EF_FDN, 2, "Other3", "8005550104")
+        };
+        mIccPhoneBook.addRecord(1,
+                new AdnRecord(IccConstants.EF_ADN, 2, "Initial Name", "8005550101"));
+        mIccPhoneBook.addRecord(1, unchanged[0]);
+        mIccPhoneBook.addRecord(2, unchanged[1]);
+        mIccPhoneBook.addRecord(2, unchanged[2]);
+
+        int result = mResolver.delete(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 2), null);
+
+        assertThat(result).isEqualTo(1);
+
+        assertThat(mIccPhoneBook.getAllValidRecords()).comparingElementsUsing(ADN_RECORD_IS_EQUAL)
+                .containsExactlyElementsIn(unchanged);
+    }
+
+    @Test
+    public void update_indexExceedingMax_returnsZero() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 30);
+
+        ContentValues values = new ContentValues();
+        values.put(SimRecords.NAME, "name");
+        values.put(SimRecords.PHONE_NUMBER, "18005551010");
+        int result = mResolver.update(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 2),
+                values, null);
+
+        assertThat(result).isEqualTo(0);
+    }
+
+    @Test
+    public void update_indexOverflow_throwsIllegalArgumentException() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+
+        ContentValues values = new ContentValues();
+        values.put(SimRecords.NAME, "name");
+        values.put(SimRecords.PHONE_NUMBER, "18005551010");
+        assertThrows(IllegalArgumentException.class, () -> mResolver.update(
+                SimRecords.getContentUri(1, EF_ADN).buildUpon().appendPath(
+                        String.valueOf((Long.MAX_VALUE))).build(),
+                values, null));
+    }
+
+    @Test
+    public void delete_emptyRecord_returnsZero() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+
+        int result = mResolver.delete(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 2), null);
+
+        assertThat(result).isEqualTo(0);
+    }
+
+    @Test
+    public void delete_indexExceedingMax_returnsZero() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 30);
+
+        int result = mResolver.delete(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 2), null);
+
+        assertThat(result).isEqualTo(0);
+    }
+
+    @Test
+    public void delete_indexOverflow_throwsIllegalArgumentException() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+
+        assertThrows(IllegalArgumentException.class, () -> mResolver.delete(
+                SimRecords.getContentUri(1, EF_ADN).buildUpon().appendPath(
+                        String.valueOf((Long.MAX_VALUE))).build(),
+                null));
+    }
+
+    @Test
+    public void update_nameOrNumberTooLong_throwsCorrectException() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 25);
+        mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Initial", "8005550101");
+
+        ContentValues values = new ContentValues();
+        // Name is limited to 11 characters
+        values.put(SimRecords.NAME, "1234567890ab");
+        values.put(SimRecords.PHONE_NUMBER, "8005550102");
+
+        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+                () -> mResolver.update(SimRecords.getItemUri(
+                        1, ElementaryFiles.EF_ADN, 1), values, null));
+        assertThat(e).hasMessageThat().isEqualTo(SimRecords.NAME + " is too long.");
+
+        values.put(SimRecords.NAME, "abc");
+        values.put(SimRecords.PHONE_NUMBER, "123456789012345678901");
+
+        e = assertThrows(IllegalArgumentException.class,
+                () -> mResolver.update(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1),
+                        values,
+                        null));
+        assertThat(e).hasMessageThat().isEqualTo(SimRecords.PHONE_NUMBER + " is too long.");
+        // The updates didn't actually change the data
+        assertThat(mIccPhoneBook.getAllValidRecords())
+                .comparingElementsUsing(Correspondence.from(AdnRecord::isEqual, "isEqual"))
+                .containsExactly(new AdnRecord(IccConstants.EF_ADN, 1, "Initial", "8005550101"));
+    }
+
+    @Test
+    public void update_numberWithInvalidCharacters_throwsCorrectException() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.setRecordsSize(1, IccConstants.EF_ADN, 1, 32);
+        mIccPhoneBook.addRecord(1, IccConstants.EF_ADN, "Initial", "8005550101");
+
+        ContentValues values = new ContentValues();
+        values.put(SimRecords.NAME, "Name");
+        values.put(SimRecords.PHONE_NUMBER, "(800)555-0190 x7777");
+
+        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+                () -> mResolver.update(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1),
+                        values,
+                        null));
+        assertThat(e).hasMessageThat().isEqualTo(
+                SimRecords.PHONE_NUMBER + " contains unsupported characters.");
+
+        // The update didn't actually change the data.
+        assertThat(mIccPhoneBook.getAllValidRecords())
+                .comparingElementsUsing(Correspondence.from(AdnRecord::isEqual, "isEqual"))
+                .containsExactly(new AdnRecord(IccConstants.EF_ADN, 1, "Initial", "8005550101"));
+    }
+
+    @Test
+    public void insert_nonAdnDirUris_throwsUnsupportedOperationException() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+
+        ContentValues values = new ContentValues();
+        values.put(SimRecords.NAME, "Name");
+        values.put(SimRecords.PHONE_NUMBER, "8005550101");
+
+        assertThrows(UnsupportedOperationException.class, () ->
+                mResolver.insert(ElementaryFiles.CONTENT_URI, values));
+        assertThrows(UnsupportedOperationException.class,
+                () -> mResolver.insert(SimRecords.getContentUri(1, EF_FDN), values));
+        assertThrows(UnsupportedOperationException.class,
+                () -> mResolver.insert(SimRecords.getContentUri(1, EF_SDN), values));
+        assertThrows(UnsupportedOperationException.class, () ->
+                mResolver.insert(SimRecords.getItemUri(1, ElementaryFiles.EF_FDN, 1), values));
+        assertThrows(UnsupportedOperationException.class, () ->
+                mResolver.insert(SimRecords.getItemUri(1, ElementaryFiles.EF_SDN, 1), values));
+    }
+
+    @Test
+    public void update_nonAdnDirUris_throwsUnsupportedOperationException() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+
+        ContentValues values = new ContentValues();
+        values.put(SimRecords.NAME, "Name");
+        values.put(SimRecords.PHONE_NUMBER, "8005550101");
+
+        assertThrows(UnsupportedOperationException.class, () ->
+                mResolver.update(ElementaryFiles.CONTENT_URI, values, null));
+        assertThrows(UnsupportedOperationException.class,
+                () -> mResolver.update(SimRecords.getContentUri(1, EF_FDN), values, null));
+        assertThrows(UnsupportedOperationException.class,
+                () -> mResolver.update(SimRecords.getContentUri(1, EF_SDN), values, null));
+        assertThrows(UnsupportedOperationException.class,
+                () -> mResolver.update(SimRecords.getContentUri(1, EF_SDN), values, null));
+        assertThrows(UnsupportedOperationException.class, () ->
+                mResolver.update(SimRecords.getItemUri(1, ElementaryFiles.EF_FDN, 1), values,
+                        null));
+        assertThrows(UnsupportedOperationException.class, () ->
+                mResolver.update(SimRecords.getItemUri(1, ElementaryFiles.EF_SDN, 1), values,
+                        null));
+    }
+
+    @Test
+    public void delete_nonAdnDirUris_throwsUnsupportedOperationException() {
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+
+        ContentValues values = new ContentValues();
+        values.put(SimRecords.NAME, "Name");
+        values.put(SimRecords.PHONE_NUMBER, "8005550101");
+
+        assertThrows(UnsupportedOperationException.class, () ->
+                mResolver.delete(ElementaryFiles.CONTENT_URI, null));
+        assertThrows(UnsupportedOperationException.class,
+                () -> mResolver.delete(SimRecords.getContentUri(1, EF_FDN), null));
+        assertThrows(UnsupportedOperationException.class,
+                () -> mResolver.delete(SimRecords.getContentUri(1, EF_SDN), null));
+        assertThrows(UnsupportedOperationException.class,
+                () -> mResolver.delete(SimRecords.getContentUri(1, EF_SDN), null));
+        assertThrows(UnsupportedOperationException.class, () ->
+                mResolver.delete(SimRecords.getItemUri(1, ElementaryFiles.EF_FDN, 1), null));
+        assertThrows(UnsupportedOperationException.class, () ->
+                mResolver.delete(SimRecords.getItemUri(1, ElementaryFiles.EF_SDN, 1), null));
+    }
+
+    @Test
+    public void subscriptionsChange_callsNotifyChange() {
+        // Clear invocations that happened in setUp
+        Mockito.reset(mMockSubscriptionManager);
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+        SimPhonebookProvider.ContentNotifier mockNotifier = mock(
+                SimPhonebookProvider.ContentNotifier.class);
+        ArgumentCaptor<SubscriptionManager.OnSubscriptionsChangedListener> listenerCaptor =
+                ArgumentCaptor.forClass(SubscriptionManager.OnSubscriptionsChangedListener.class);
+
+        TestableSimPhonebookProvider.setup(
+                mResolver, mMockSubscriptionManager, mIccPhoneBook, mockNotifier);
+        verify(mMockSubscriptionManager).addOnSubscriptionsChangedListener(
+                any(), listenerCaptor.capture());
+        listenerCaptor.getValue().onSubscriptionsChanged();
+        setupSimsWithSubscriptionIds(1, 2);
+        listenerCaptor.getValue().onSubscriptionsChanged();
+        listenerCaptor.getValue().onSubscriptionsChanged();
+
+        verify(mockNotifier, times(2)).notifyChange(eq(SimPhonebookContract.AUTHORITY_URI));
+    }
+
+    @Test
+    public void insert_callsNotifyChange() {
+        // Clear invocations that happened in setUp
+        Mockito.reset(mMockSubscriptionManager);
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.makeAllEfsSupported(1);
+        SimPhonebookProvider.ContentNotifier mockNotifier = mock(
+                SimPhonebookProvider.ContentNotifier.class);
+
+        TestableSimPhonebookProvider.setup(
+                mResolver, mMockSubscriptionManager, mIccPhoneBook, mockNotifier);
+
+        ContentValues values = new ContentValues();
+        values.put(SimRecords.NAME, "name");
+        values.put(SimRecords.PHONE_NUMBER, "5550101");
+        mResolver.insert(SimRecords.getContentUri(1, EF_ADN), values);
+
+        verify(mockNotifier).notifyChange(eq(SimPhonebookContract.AUTHORITY_URI));
+    }
+
+    @Test
+    public void update_callsNotifyChange() {
+        // Clear invocations that happened in setUp
+        Mockito.reset(mMockSubscriptionManager);
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.addAdnRecord(1, "Initial", "5550101");
+        SimPhonebookProvider.ContentNotifier mockNotifier = mock(
+                SimPhonebookProvider.ContentNotifier.class);
+
+        TestableSimPhonebookProvider.setup(
+                mResolver, mMockSubscriptionManager, mIccPhoneBook, mockNotifier);
+
+        ContentValues values = new ContentValues();
+        values.put(SimRecords.NAME, "Updated");
+        values.put(SimRecords.PHONE_NUMBER, "5550102");
+        mResolver.update(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1), values, null);
+
+        verify(mockNotifier).notifyChange(eq(SimPhonebookContract.AUTHORITY_URI));
+    }
+
+    @Test
+    public void delete_callsNotifyChange() {
+        // Clear invocations that happened in setUp
+        Mockito.reset(mMockSubscriptionManager);
+        setupSimsWithSubscriptionIds(1);
+        mIccPhoneBook.addAdnRecord(1, "Initial", "5550101");
+        SimPhonebookProvider.ContentNotifier mockNotifier = mock(
+                SimPhonebookProvider.ContentNotifier.class);
+
+        TestableSimPhonebookProvider.setup(
+                mResolver, mMockSubscriptionManager, mIccPhoneBook, mockNotifier);
+
+        mResolver.delete(SimRecords.getItemUri(1, ElementaryFiles.EF_ADN, 1), null);
+
+        verify(mockNotifier).notifyChange(eq(SimPhonebookContract.AUTHORITY_URI));
+    }
+
+    @Test
+    public void getEncodedNameLength_returnsValueIsCorrect() {
+        String name = "";
+        int length = SimRecords.getEncodedNameLength(mResolver, name);
+        assertThat(length).isEqualTo(0);
+
+        name = "First Last";
+        length = SimRecords.getEncodedNameLength(mResolver, name);
+        assertThat(length).isEqualTo(name.length());
+
+        name = "日本";
+        length = SimRecords.getEncodedNameLength(mResolver, name);
+        assertThat(length).isEqualTo(name.length() * 2 + 1);
+
+        name = EMOJI;
+        length = SimRecords.getEncodedNameLength(mResolver, name);
+        assertThat(length).isEqualTo(name.length() * 2 + 1);
+
+        name = "abc日本" + EMOJI;
+        length = SimRecords.getEncodedNameLength(mResolver, name);
+        assertThat(length).isEqualTo(name.length() * 2 + 1);
+    }
+
+    private void setupSimsWithSubscriptionIds(int... subscriptionIds) {
+        when(mMockSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(subscriptionIds);
+        when(mMockSubscriptionManager.getActiveSubscriptionInfoCount())
+                .thenReturn(subscriptionIds.length);
+        List<SubscriptionInfo> subscriptions = createSubscriptionsWithIds(subscriptionIds);
+        when(mMockSubscriptionManager.getActiveSubscriptionInfoList()).thenReturn(subscriptions);
+        for (SubscriptionInfo info : subscriptions) {
+            when(mMockSubscriptionManager.getActiveSubscriptionInfo(info.getSubscriptionId()))
+                    .thenReturn(info);
+        }
+    }
+
+    public static class FakeIccPhoneBook extends IIccPhoneBook.Default {
+
+        private static final int DEFAULT_RECORD_SIZE = 30;
+        private static final int DEFAULT_RECORDS_COUNT = 100;
+
+        // The key for both maps is the (subscription ID, efid)
+        private Map<Pair<Integer, Integer>, AdnRecord[]> mRecords = new HashMap<>();
+        // The value is the single record size
+        private Map<Pair<Integer, Integer>, Integer> mRecordSizes = new HashMap<>();
+
+        private int mDefaultSubscriptionId = 101;
+
+        private void addRecord(Pair<Integer, Integer> key, AdnRecord record) {
+            // Assume that if records are being added then the test wants it to be a valid
+            // elementary file so set sizes as well.
+            if (!mRecordSizes.containsKey(key)) {
+                setRecordsSize(key.first, key.second,
+                        Math.max(record.getRecId(), DEFAULT_RECORDS_COUNT), DEFAULT_RECORD_SIZE);
+            }
+            mRecords.get(key)[record.getRecId() - 1] = record;
+        }
+
+        public void addRecord(AdnRecord record) {
+            addRecord(Pair.create(mDefaultSubscriptionId, record.getEfid()), record);
+        }
+
+        public void addRecord(int subscriptionId, AdnRecord record) {
+            addRecord(Pair.create(subscriptionId, record.getEfid()), record);
+        }
+
+        public void addRecord(int subscriptionId, int efId, String name, String phoneNumber) {
+            Pair<Integer, Integer> key = Pair.create(subscriptionId, efId);
+            AdnRecord[] records = mRecords.computeIfAbsent(key, unused ->
+                    createEmptyRecords(efId, 100));
+            int recordIndex = -1;
+            for (int i = 0; i < records.length; i++) {
+                if (records[i].isEmpty()) {
+                    recordIndex = i;
+                    break;
+                }
+            }
+            if (recordIndex == -1) {
+                throw new IllegalStateException("");
+            }
+            addRecord(key, new AdnRecord(efId, recordIndex + 1, name, phoneNumber));
+        }
+
+        public void addAdnRecord(int subscriptionId, String name, String phoneNumber) {
+            addRecord(subscriptionId, IccConstants.EF_ADN, name, phoneNumber);
+        }
+
+        public void addAdnRecord(String name, String phoneNumber) {
+            addRecord(mDefaultSubscriptionId, IccConstants.EF_ADN, name, phoneNumber);
+        }
+
+        public List<AdnRecord> getAllValidRecords() {
+            List<AdnRecord> result = new ArrayList<>();
+            for (AdnRecord[] records : mRecords.values()) {
+                for (AdnRecord record : records) {
+                    if (!record.isEmpty()) {
+                        result.add(record);
+                    }
+                }
+            }
+            return result;
+        }
+
+        public void makeAllEfsSupported() {
+            makeAllEfsSupported(mDefaultSubscriptionId);
+        }
+
+        /**
+         * Sets up the fake to return valid records size for all elementary files for the provided
+         * subscription IDs.
+         */
+        public void makeAllEfsSupported(int... subscriptionIds) {
+            for (int subId : subscriptionIds) {
+                makeAllEfsSupported(subId);
+            }
+        }
+
+        /**
+         * Sets up the fake to return valid records size for all elementary files for the provided
+         * subscription IDs.
+         */
+        public void makeAllEfsSupported(int subscriptionId) {
+            setRecordsSize(subscriptionId, IccConstants.EF_ADN, DEFAULT_RECORDS_COUNT,
+                    DEFAULT_RECORD_SIZE);
+            setRecordsSize(subscriptionId, IccConstants.EF_FDN, DEFAULT_RECORDS_COUNT,
+                    DEFAULT_RECORD_SIZE);
+            setRecordsSize(subscriptionId, IccConstants.EF_SDN, DEFAULT_RECORDS_COUNT,
+                    DEFAULT_RECORD_SIZE);
+        }
+
+        public void setRecordsSize(int subscriptionId, int efid, int maxRecordCount,
+                int maxRecordSize) {
+            Pair<Integer, Integer> key = Pair.create(subscriptionId, efid);
+            mRecordSizes.put(key, maxRecordSize);
+            AdnRecord[] records = mRecords.computeIfAbsent(key, unused ->
+                    createEmptyRecords(efid, maxRecordCount));
+            if (records.length < maxRecordCount) {
+                throw new IllegalStateException("Records already initialized with a smaller size");
+            }
+        }
+
+        private AdnRecord[] createEmptyRecords(int efid, int count) {
+            AdnRecord[] records = new AdnRecord[count];
+            for (int i = 0; i < records.length; i++) {
+                if (records[i] == null) {
+                    records[i] = new AdnRecord(efid, i + 1, "", "");
+                }
+            }
+            return records;
+        }
+
+        public void setDefaultSubscriptionId(int defaultSubscriptionId) {
+            mDefaultSubscriptionId = defaultSubscriptionId;
+        }
+
+        public void reset() {
+            mRecords.clear();
+            mRecordSizes.clear();
+        }
+
+        @Override
+        public List<AdnRecord> getAdnRecordsInEf(int efid) {
+            return getAdnRecordsInEfForSubscriber(mDefaultSubscriptionId, efid);
+        }
+
+        @Override
+        public List<AdnRecord> getAdnRecordsInEfForSubscriber(int subId, int efid) {
+            return Arrays.asList(
+                    mRecords.getOrDefault(Pair.create(subId, efid), new AdnRecord[0]));
+        }
+
+        @Override
+        public boolean updateAdnRecordsInEfBySearchForSubscriber(int subId, int efid,
+                ContentValues values, String pin2) {
+            final String oldTag = values.getAsString(IccProvider.STR_TAG);
+            final String oldPhoneNumber = values.getAsString(IccProvider.STR_NUMBER);
+            final String newTag = values.getAsString(IccProvider.STR_NEW_TAG);
+            final String newPhoneNumber = values.getAsString(IccProvider.STR_NEW_NUMBER);
+            return updateAdnRecordsInEfBySearchForSubscriber(subId, efid, oldTag, oldPhoneNumber,
+                    newTag, newPhoneNumber, pin2);
+
+        }
+
+        private boolean updateAdnRecordsInEfBySearchForSubscriber(int subId, int efid, String oldTag,
+                String oldPhoneNumber, String newTag, String newPhoneNumber, String pin2) {
+            if (!oldTag.isEmpty() || !oldPhoneNumber.isEmpty()) {
+                throw new IllegalArgumentException(
+                        "updateAdnRecordsInEfBySearchForSubscriber only supports insert");
+            }
+            addRecord(subId, efid, newTag, newPhoneNumber);
+            return true;
+        }
+
+        @Override
+        public boolean updateAdnRecordsInEfByIndexForSubscriber(int subId, int efid,
+                ContentValues values, int index, String pin2) {
+            final String newTag = values.getAsString(IccProvider.STR_NEW_TAG);
+            final String newPhoneNumber = values.getAsString(IccProvider.STR_NEW_NUMBER);
+            return updateAdnRecordsInEfByIndexForSubscriber(subId, efid, newTag, newPhoneNumber,
+                    index, pin2);
+
+        }
+
+        private boolean updateAdnRecordsInEfByIndexForSubscriber(int subId, int efid, String newTag,
+                String newPhoneNumber, int index, String pin2) {
+            AdnRecord[] records = mRecords.computeIfAbsent(Pair.create(subId, efid), unused ->
+                    createEmptyRecords(efid, 100));
+            records[index - 1] = new AdnRecord(efid, index, newTag, newPhoneNumber);
+            return true;
+        }
+
+        @Override
+        public int[] getAdnRecordsSize(int efid) {
+            return getAdnRecordsSizeForSubscriber(mDefaultSubscriptionId, efid);
+        }
+
+        @Override
+        public int[] getAdnRecordsSizeForSubscriber(int subId, int efid) {
+            Pair<Integer, Integer> key = Pair.create(subId, efid);
+            Integer recordSize = mRecordSizes.get(key);
+            if (recordSize == null) {
+                return new int[]{0, 0, 0};
+            }
+            int count = mRecords.get(key).length;
+            return new int[]{recordSize, recordSize * count, count};
+        }
+
+        @Override
+        public AdnCapacity getAdnRecordsCapacityForSubscriber(int subId) {
+            return new AdnCapacity(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+        }
+    }
+
+    /**
+     * Implementation of SimPhonebookProvider that allows test-doubles to be injected.
+     *
+     * <p>The ProviderTestRule doesn't seem to allow a better way to do this since it just
+     * invokes the constructor.
+     */
+    public static class TestableSimPhonebookProvider extends SimPhonebookProvider {
+
+        public static void setup(
+                ContentResolver resolver,
+                SubscriptionManager subscriptionManager,
+                IIccPhoneBook iccPhoneBook) {
+            setup(resolver, subscriptionManager, iccPhoneBook, uri -> {
+            });
+        }
+
+        public static void setup(
+                ContentResolver resolver,
+                SubscriptionManager subscriptionManager,
+                IIccPhoneBook iccPhoneBook,
+                ContentNotifier notifier) {
+            TestableSimPhonebookProvider provider =
+                    (TestableSimPhonebookProvider) Objects.requireNonNull(
+                            resolver.acquireContentProviderClient(
+                                    SimPhonebookContract.AUTHORITY))
+                            .getLocalContentProvider();
+            InstrumentationRegistry.getInstrumentation().runOnMainSync(() ->
+                    provider.onCreate(subscriptionManager, () -> iccPhoneBook, notifier));
+        }
+
+        @Override
+        public boolean onCreate() {
+            // We stub super.onCreate because it initializes services which causes an
+            // IllegalArgumentException because of the context used for the test.
+            return true;
+        }
+    }
+}
diff --git a/tests/src/com/android/phone/callcomposer/PictureManagerTest.java b/tests/src/com/android/phone/callcomposer/PictureManagerTest.java
index b52b297..f1ce3b8 100644
--- a/tests/src/com/android/phone/callcomposer/PictureManagerTest.java
+++ b/tests/src/com/android/phone/callcomposer/PictureManagerTest.java
@@ -34,6 +34,7 @@
 import android.provider.CallLog;
 import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyManager;
+import android.telephony.gba.TlsParams;
 import android.telephony.gba.UaSecurityProtocolIdentifier;
 
 import org.junit.After;
@@ -78,6 +79,14 @@
         PersistableBundle b = new PersistableBundle();
         b.putString(CarrierConfigManager.KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING,
                 FAKE_URL_BASE);
+        b.putInt(CarrierConfigManager.KEY_GBA_MODE_INT,
+                CarrierConfigManager.GBA_ME);
+        b.putInt(CarrierConfigManager.KEY_GBA_UA_SECURITY_ORGANIZATION_INT,
+                UaSecurityProtocolIdentifier.ORG_3GPP);
+        b.putInt(CarrierConfigManager.KEY_GBA_UA_SECURITY_PROTOCOL_INT,
+                UaSecurityProtocolIdentifier.UA_SECURITY_PROTOCOL_3GPP_TLS_DEFAULT);
+        b.putInt(CarrierConfigManager.KEY_GBA_UA_TLS_CIPHER_SUITE_INT,
+                TlsParams.TLS_RSA_WITH_AES_128_CBC_SHA);
         when(telephonyManager.getCarrierConfig()).thenReturn(b);
     }
 
@@ -263,7 +272,7 @@
 
     public void testGbaCredLookup(GbaCredentialsSupplier supplier, boolean forceExpected)
             throws Exception {
-        String fakeRealm = "3gpp-bootstraping@naf1.example.com";
+        String fakeNafId = "https://3GPP-bootstrapping@www.example.com";
         byte[] fakeKey = new byte[] {1, 2, 3, 4, 5};
         String fakeTxId = "89sdfjggf";
 
@@ -271,8 +280,9 @@
                 ArgumentCaptor.forClass(TelephonyManager.BootstrapAuthenticationCallback.class);
 
         CompletableFuture<GbaCredentials> credsFuture =
-                supplier.getCredentials(fakeRealm, CallComposerPictureManager.getExecutor());
-        verify(telephonyManager).bootstrapAuthenticationRequest(anyInt(), eq(Uri.parse(fakeRealm)),
+                supplier.getCredentials(fakeNafId, CallComposerPictureManager.getExecutor());
+        verify(telephonyManager).bootstrapAuthenticationRequest(anyInt(),
+                eq(Uri.parse(fakeNafId)),
                 nullable(UaSecurityProtocolIdentifier.class), eq(forceExpected),
                 nullable(Executor.class),
                 authCallbackCaptor.capture());
@@ -285,9 +295,9 @@
         // Do it again and see if we make another request, then make sure that matches up with what
         // we expected.
         CompletableFuture<GbaCredentials> credsFuture1 =
-                supplier.getCredentials(fakeRealm, CallComposerPictureManager.getExecutor());
+                supplier.getCredentials(fakeNafId, CallComposerPictureManager.getExecutor());
         verify(telephonyManager, times(forceExpected ? 2 : 1))
-                .bootstrapAuthenticationRequest(anyInt(), eq(Uri.parse(fakeRealm)),
+                .bootstrapAuthenticationRequest(anyInt(), eq(Uri.parse(fakeNafId)),
                         nullable(UaSecurityProtocolIdentifier.class),
                         eq(forceExpected),
                         nullable(Executor.class),
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 07fe6a8..898b5ad 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -26,10 +26,10 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -42,8 +42,6 @@
 import android.os.AsyncResult;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Message;
 import android.telecom.ConnectionRequest;
 import android.telecom.DisconnectCause;
 import android.telecom.PhoneAccountHandle;
@@ -58,6 +56,8 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.TelephonyTestBase;
+import com.android.internal.telecom.IConnectionService;
+import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallStateException;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.Phone;
@@ -84,6 +84,24 @@
 
 @RunWith(AndroidJUnit4.class)
 public class TelephonyConnectionServiceTest extends TelephonyTestBase {
+    /**
+     * Unlike {@link TestTelephonyConnection}, a bare minimal {@link TelephonyConnection} impl
+     * that does not try to configure anything.
+     */
+    public static class SimpleTelephonyConnection extends TelephonyConnection {
+        public boolean wasDisconnected = false;
+
+        @Override
+        public TelephonyConnection cloneConnection() {
+            return null;
+        }
+
+
+        @Override
+        public void onDisconnect() {
+            wasDisconnected = true;
+        }
+    }
 
     private static final long TIMEOUT_MS = 100;
     private static final int SLOT_0_PHONE_ID = 0;
@@ -107,13 +125,18 @@
     @Mock TelephonyConnectionService.PhoneSwitcherProxy mPhoneSwitcherProxy;
     @Mock TelephonyConnectionService.PhoneNumberUtilsProxy mPhoneNumberUtilsProxy;
     @Mock TelephonyConnectionService.PhoneUtilsProxy mPhoneUtilsProxy;
-    @Mock TelephonyConnectionService.HandlerFactory mHandlerFactory;
     @Mock TelephonyConnectionService.DisconnectCauseFactory mDisconnectCauseFactory;
     @Mock Handler mMockHandler;
     @Mock EmergencyNumberTracker mEmergencyNumberTracker;
     @Mock PhoneSwitcher mPhoneSwitcher;
     @Mock RadioOnHelper mRadioOnHelper;
     @Mock ServiceStateTracker mSST;
+    @Mock Call mCall;
+    @Mock Call mCall2;
+    @Mock com.android.internal.telephony.Connection mInternalConnection;
+    @Mock com.android.internal.telephony.Connection mInternalConnection2;
+    private Phone mPhone0;
+    private Phone mPhone1;
 
     private static class TestTelephonyConnectionService extends TelephonyConnectionService {
 
@@ -132,6 +155,7 @@
     }
 
     private TelephonyConnectionService mTestConnectionService;
+    private IConnectionService.Stub mBinderStub;
 
     @Before
     public void setUp() throws Exception {
@@ -147,11 +171,6 @@
                 .thenAnswer(invocation -> invocation.getArgument(1));
         mTestConnectionService.setPhoneNumberUtilsProxy(mPhoneNumberUtilsProxy);
         mTestConnectionService.setPhoneUtilsProxy(mPhoneUtilsProxy);
-        HandlerThread mockHandlerThread = mock(HandlerThread.class);
-        doReturn(mockHandlerThread).when(mHandlerFactory).createHandlerThread(anyString());
-        doReturn(null).when(mockHandlerThread).getLooper();
-        doReturn(mMockHandler).when(mHandlerFactory).createHandler(any());
-        mTestConnectionService.setHandlerFactory(mHandlerFactory);
         mTestConnectionService.setDeviceState(mDeviceState);
         mTestConnectionService.setRadioOnHelper(mRadioOnHelper);
         doReturn(new DisconnectCause(DisconnectCause.UNKNOWN)).when(mDisconnectCauseFactory)
@@ -161,6 +180,7 @@
         mTestConnectionService.setDisconnectCauseFactory(mDisconnectCauseFactory);
         mTestConnectionService.onCreate();
         mTestConnectionService.setTelephonyManagerProxy(mTelephonyManagerProxy);
+        mBinderStub = (IConnectionService.Stub) mTestConnectionService.onBind(null);
     }
 
     @After
@@ -931,22 +951,20 @@
     @Test
     @SmallTest
     public void testCreateOutgoingEmergencyConnection_delayDial_carrierconfig_dds() {
-        Phone testPhone = setupConnectionServiceForDelayDial();
-        Runnable delayDialRunnable = verifyRunnablePosted();
-
         // Setup test to not support SUPL on the non-DDS subscription
         doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
         getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
                 CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
                 null);
-        testPhone.getServiceState().setRoaming(false);
         getTestContext().getCarrierConfig(0 /*subId*/).putInt(
                 CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
                 CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY);
         getTestContext().getCarrierConfig(0 /*subId*/).putString(
                 CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "150");
-        delayDialRunnable.run();
 
+        Phone testPhone = setupConnectionServiceForDelayDial(
+                false /* isRoaming */, false /* setOperatorName */, null /* operator long name*/,
+                        null /* operator short name */, null /* operator numeric name */);
         verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(0) /*phoneId*/ ,
                 eq(150) /*extensionTime*/, any());
     }
@@ -997,11 +1015,9 @@
         assertTrue(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
 
         callback.getValue().onComplete(null, true);
-        Runnable delayDialRunnable = verifyRunnablePosted();
 
         try {
             doAnswer(invocation -> null).when(mContext).startActivity(any());
-            delayDialRunnable.run();
             verify(testPhone).dial(anyString(), any());
         } catch (CallStateException e) {
             // This shouldn't happen
@@ -1016,22 +1032,20 @@
     @Test
     @SmallTest
     public void testCreateOutgoingEmergencyConnection_delayDial_nocarrierconfig() {
-        Phone testPhone = setupConnectionServiceForDelayDial();
-        Runnable delayDialRunnable = verifyRunnablePosted();
-
         // Setup test to not support SUPL on the non-DDS subscription
         doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
         getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
                 CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
                 null);
-        testPhone.getServiceState().setRoaming(false);
         getTestContext().getCarrierConfig(0 /*subId*/).putInt(
                 CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
                 CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK);
         getTestContext().getCarrierConfig(0 /*subId*/).putString(
                 CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
-        delayDialRunnable.run();
 
+        Phone testPhone = setupConnectionServiceForDelayDial(
+                false /* isRoaming */, false /* setOperatorName */, null /* operator long name*/,
+                        null /* operator short name */, null /* operator numeric name */);
         verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
     }
 
@@ -1042,22 +1056,20 @@
     @Test
     @SmallTest
     public void testCreateOutgoingEmergencyConnection_delayDial_supportsuplondds() {
-        Phone testPhone = setupConnectionServiceForDelayDial();
-        Runnable delayDialRunnable = verifyRunnablePosted();
-
         // If the non-DDS supports SUPL, dont switch data
         doReturn(false).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
         getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
                 CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
                 null);
-        testPhone.getServiceState().setRoaming(false);
         getTestContext().getCarrierConfig(0 /*subId*/).putInt(
                 CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
                 CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY);
         getTestContext().getCarrierConfig(0 /*subId*/).putString(
                 CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
-        delayDialRunnable.run();
 
+        Phone testPhone = setupConnectionServiceForDelayDial(
+                false /* isRoaming */, false /* setOperatorName */, null /* operator long name*/,
+                         null /* operator short name */, null /* operator numeric name */);
         verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
     }
 
@@ -1068,22 +1080,20 @@
     @Test
     @SmallTest
     public void testCreateOutgoingEmergencyConnection_delayDial_roaming_nocarrierconfig() {
-        Phone testPhone = setupConnectionServiceForDelayDial();
-        Runnable delayDialRunnable = verifyRunnablePosted();
-
         // Setup test to not support SUPL on the non-DDS subscription
         doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
         getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
                 CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY,
                 null);
-        testPhone.getServiceState().setRoaming(true);
         getTestContext().getCarrierConfig(0 /*subId*/).putInt(
                 CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
                 CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY);
         getTestContext().getCarrierConfig(0 /*subId*/).putString(
                 CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
-        delayDialRunnable.run();
 
+        Phone testPhone = setupConnectionServiceForDelayDial(
+                true /* isRoaming */, false /* setOperatorName */, null /* operator long name*/,
+                         null /* operator short name */, null /* operator numeric name */);
         verify(mPhoneSwitcher, never()).overrideDefaultDataForEmergency(anyInt(), anyInt(), any());
     }
 
@@ -1095,16 +1105,10 @@
     @Test
     @SmallTest
     public void testCreateOutgoingEmergencyConnection_delayDial_roamingcarrierconfig() {
-        Phone testPhone = setupConnectionServiceForDelayDial();
-        Runnable delayDialRunnable = verifyRunnablePosted();
-
+        doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
         // Setup voice roaming scenario
         String testRoamingOperator = "001001";
-        // In some roaming conditions, we are not technically "roaming"
-        testPhone.getServiceState().setRoaming(false);
-        testPhone.getServiceState().setOperatorName("TestTel", "TestTel", testRoamingOperator);
         // Setup test to not support SUPL on the non-DDS subscription
-        doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
         String[] roamingPlmns = new String[1];
         roamingPlmns[0] = testRoamingOperator;
         getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
@@ -1115,8 +1119,11 @@
                 CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK);
         getTestContext().getCarrierConfig(0 /*subId*/).putString(
                 CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
-        delayDialRunnable.run();
 
+        Phone testPhone = setupConnectionServiceForDelayDial(
+                false /* isRoaming */, true /* setOperatorName */,
+                        "TestTel" /* operator long name*/, "TestTel" /* operator short name */,
+                                testRoamingOperator /* operator numeric name */);
         verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(0) /*phoneId*/ ,
                 eq(0) /*extensionTime*/, any());
     }
@@ -1129,15 +1136,10 @@
     @Test
     @SmallTest
     public void testCreateOutgoingEmergencyConnection_delayDial__roaming_roamingcarrierconfig() {
-        Phone testPhone = setupConnectionServiceForDelayDial();
-        Runnable delayDialRunnable = verifyRunnablePosted();
-
-        // Setup voice roaming scenario
-        String testRoamingOperator = "001001";
-        testPhone.getServiceState().setRoaming(true);
-        testPhone.getServiceState().setOperatorName("TestTel", "TestTel", testRoamingOperator);
         // Setup test to not support SUPL on the non-DDS subscription
         doReturn(true).when(mDeviceState).isSuplDdsSwitchRequiredForEmergencyCall(any());
+        // Setup voice roaming scenario
+        String testRoamingOperator = "001001";
         String[] roamingPlmns = new String[1];
         roamingPlmns[0] = testRoamingOperator;
         getTestContext().getCarrierConfig(0 /*subId*/).putStringArray(
@@ -1148,17 +1150,225 @@
                 CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK);
         getTestContext().getCarrierConfig(0 /*subId*/).putString(
                 CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0");
-        delayDialRunnable.run();
 
+        Phone testPhone = setupConnectionServiceForDelayDial(
+                false /* isRoaming */, true /* setOperatorName */,
+                        "TestTel" /* operator long name*/, "TestTel" /* operator short name */,
+                                testRoamingOperator /* operator numeric name */);
         verify(mPhoneSwitcher).overrideDefaultDataForEmergency(eq(0) /*phoneId*/ ,
                 eq(0) /*extensionTime*/, any());
     }
 
     /**
+     * Verifies for an incoming call on the same SIM that we don't set
+     * {@link android.telecom.Connection#EXTRA_ANSWERING_DROPS_FG_CALL} on the incoming call extras.
+     * @throws Exception
+     */
+    @Test
+    @SmallTest
+    public void testIncomingDoesntRequestDisconnect() throws Exception {
+        setupForCallTest();
+
+        mBinderStub.createConnection(PHONE_ACCOUNT_HANDLE_1, "TC@1",
+                new ConnectionRequest(PHONE_ACCOUNT_HANDLE_1, Uri.parse("tel:16505551212"),
+                        new Bundle()),
+                true, false, null);
+        waitForHandlerAction(mTestConnectionService.getHandler(), TIMEOUT_MS);
+        assertEquals(1, mTestConnectionService.getAllConnections().size());
+
+        // Make sure the extras do not indicate that it answering will disconnect another call.
+        android.telecom.Connection connection = (android.telecom.Connection)
+                mTestConnectionService.getAllConnections().toArray()[0];
+        assertFalse(connection.getExtras() != null && connection.getExtras().containsKey(
+                android.telecom.Connection.EXTRA_ANSWERING_DROPS_FG_CALL));
+    }
+
+    /**
+     * Verifies where there is another call on the same sub, we don't set
+     * {@link android.telecom.Connection#EXTRA_ANSWERING_DROPS_FG_CALL} on the incoming call extras.
+     * @throws Exception
+     */
+    @Test
+    @SmallTest
+    public void testSecondCallSameSubWontDisconnect() throws Exception {
+        // Previous test gets us into a good enough state
+        testIncomingDoesntRequestDisconnect();
+
+        when(mCall.getState()).thenReturn(Call.State.ACTIVE);
+        when(mCall2.getState()).thenReturn(Call.State.WAITING);
+        when(mCall2.getLatestConnection()).thenReturn(mInternalConnection2);
+        when(mPhone0.getRingingCall()).thenReturn(mCall2);
+
+        mBinderStub.createConnection(PHONE_ACCOUNT_HANDLE_1, "TC@2",
+                new ConnectionRequest(PHONE_ACCOUNT_HANDLE_1, Uri.parse("tel:16505551213"),
+                        new Bundle()),
+                true, false, null);
+        waitForHandlerAction(mTestConnectionService.getHandler(), TIMEOUT_MS);
+        assertEquals(2, mTestConnectionService.getAllConnections().size());
+
+        // None of the connections should have the extra set.
+        assertEquals(0, mTestConnectionService.getAllConnections().stream()
+                .filter(c -> c.getExtras() != null && c.getExtras().containsKey(
+                        android.telecom.Connection.EXTRA_ANSWERING_DROPS_FG_CALL))
+                .count());
+    }
+
+    /**
+     * Verifies where there is another call on the same sub, we don't set
+     * {@link android.telecom.Connection#EXTRA_ANSWERING_DROPS_FG_CALL} on the incoming call extras.
+     * @throws Exception
+     */
+    @Test
+    @SmallTest
+    public void testSecondCallDifferentSubWillDisconnect() throws Exception {
+        // Previous test gets us into a good enough state
+        testIncomingDoesntRequestDisconnect();
+
+        when(mCall.getState()).thenReturn(Call.State.ACTIVE);
+        when(mCall2.getState()).thenReturn(Call.State.WAITING);
+        when(mCall2.getLatestConnection()).thenReturn(mInternalConnection2);
+        // At this point the call is ringing on the second phone.
+        when(mPhone0.getRingingCall()).thenReturn(null);
+        when(mPhone1.getRingingCall()).thenReturn(mCall2);
+
+        mBinderStub.createConnection(PHONE_ACCOUNT_HANDLE_2, "TC@2",
+                new ConnectionRequest(PHONE_ACCOUNT_HANDLE_2, Uri.parse("tel:16505551213"),
+                        new Bundle()),
+                true, false, null);
+        waitForHandlerAction(mTestConnectionService.getHandler(), TIMEOUT_MS);
+        assertEquals(2, mTestConnectionService.getAllConnections().size());
+
+        // The incoming connection should have the extra set.
+        assertEquals(1, mTestConnectionService.getAllConnections().stream()
+                .filter(c -> c.getExtras() != null && c.getExtras().containsKey(
+                        android.telecom.Connection.EXTRA_ANSWERING_DROPS_FG_CALL))
+                .count());
+    }
+
+    private static final PhoneAccountHandle SUB1_HANDLE = new PhoneAccountHandle(
+            new ComponentName("test", "class"), "1");
+    private static final PhoneAccountHandle SUB2_HANDLE = new PhoneAccountHandle(
+            new ComponentName("test", "class"), "2");
+
+    @Test
+    @SmallTest
+    public void testDontDisconnectSameSub() {
+        ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
+        SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
+        tcs.add(tc1);
+        TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(tcs, SUB1_HANDLE);
+        // Would've preferred to use mockito, but can't mock out TelephonyConnection/Connection
+        // easily.
+        assertFalse(tc1.wasDisconnected);
+    }
+
+    @Test
+    @SmallTest
+    public void testDontDisconnectEmergency() {
+        ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
+        SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, true);
+        tcs.add(tc1);
+        TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(tcs, SUB2_HANDLE);
+        // Other call is an emergency call, so don't disconnect it.
+        assertFalse(tc1.wasDisconnected);
+    }
+
+    @Test
+    @SmallTest
+    public void testDontDisconnectExternal() {
+        ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
+        SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE,
+                android.telecom.Connection.PROPERTY_IS_EXTERNAL_CALL, false);
+        tcs.add(tc1);
+        TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(tcs, SUB2_HANDLE);
+        // Other call is an external call, so don't disconnect it.
+        assertFalse(tc1.wasDisconnected);
+    }
+
+    @Test
+    @SmallTest
+    public void testDisconnectDifferentSub() {
+        ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
+        SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
+        tcs.add(tc1);
+        TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(tcs, SUB2_HANDLE);
+        assertTrue(tc1.wasDisconnected);
+    }
+
+    @Test
+    @SmallTest
+    public void testDisconnectDifferentSubTwoCalls() {
+        ArrayList<android.telecom.Connection> tcs = new ArrayList<>();
+        SimpleTelephonyConnection tc1 = createTestConnection(SUB1_HANDLE, 0, false);
+        SimpleTelephonyConnection tc2 = createTestConnection(SUB1_HANDLE, 0, false);
+
+        tcs.add(tc1);
+        tcs.add(tc2);
+        TelephonyConnectionService.maybeDisconnectCallsOnOtherSubs(tcs, SUB2_HANDLE);
+        assertTrue(tc1.wasDisconnected);
+        assertTrue(tc2.wasDisconnected);
+    }
+
+    private SimpleTelephonyConnection createTestConnection(PhoneAccountHandle handle,
+            int properties, boolean isEmergency) {
+        SimpleTelephonyConnection connection = new SimpleTelephonyConnection();
+        connection.setShouldTreatAsEmergencyCall(isEmergency);
+        connection.setConnectionProperties(properties);
+        connection.setPhoneAccountHandle(handle);
+        return connection;
+    }
+
+    /**
+     * Setup the mess of mocks for {@link #testSecondCallSameSubWontDisconnect()} and
+     * {@link #testIncomingDoesntRequestDisconnect()}.
+     */
+    private void setupForCallTest() {
+        // Setup a bunch of stuff.  Blech.
+        mTestConnectionService.setReadyForTest();
+        mPhone0 = makeTestPhone(0 /*phoneId*/, ServiceState.STATE_IN_SERVICE,
+                false /*isEmergencyOnly*/);
+        when(mCall.getState()).thenReturn(Call.State.INCOMING);
+        when(mCall.getPhone()).thenReturn(mPhone0);
+        when(mPhone0.getRingingCall()).thenReturn(mCall);
+        mPhone1 = makeTestPhone(1 /*phoneId*/, ServiceState.STATE_IN_SERVICE,
+                false /*isEmergencyOnly*/);
+        when(mCall2.getPhone()).thenReturn(mPhone1);
+        List<Phone> phones = new ArrayList<>(2);
+        doReturn(true).when(mPhone0).isRadioOn();
+        doReturn(true).when(mPhone1).isRadioOn();
+        doReturn(GSM_PHONE).when(mPhone0).getPhoneType();
+        doReturn(GSM_PHONE).when(mPhone1).getPhoneType();
+        phones.add(mPhone0);
+        phones.add(mPhone1);
+        setPhones(phones);
+        when(mPhoneUtilsProxy.getSubIdForPhoneAccountHandle(eq(PHONE_ACCOUNT_HANDLE_1)))
+                .thenReturn(0);
+        when(mSubscriptionManagerProxy.getPhoneId(0)).thenReturn(0);
+        when(mPhoneFactoryProxy.getPhone(eq(0))).thenReturn(mPhone0);
+        when(mPhoneUtilsProxy.getSubIdForPhoneAccountHandle(eq(PHONE_ACCOUNT_HANDLE_2)))
+                .thenReturn(1);
+        when(mSubscriptionManagerProxy.getPhoneId(1)).thenReturn(1);
+        when(mPhoneFactoryProxy.getPhone(eq(1))).thenReturn(mPhone1);
+        setupDeviceConfig(mPhone0, mPhone1, 1);
+
+        when(mInternalConnection.getCall()).thenReturn(mCall);
+        when(mInternalConnection.getState()).thenReturn(Call.State.ACTIVE);
+        when(mInternalConnection2.getCall()).thenReturn(mCall2);
+        when(mInternalConnection2.getState()).thenReturn(Call.State.WAITING);
+    }
+
+    /**
      * Set up a mock MSIM device with TEST_ADDRESS set as an emergency number.
+     * @param isRoaming whether it is roaming
+     * @param setOperatorName whether operator name needs to set
+     * @param operatorNameLongName the operator long name if needs to set
+     * @param operatorNameShortName the operator short name if needs to set
+     * @param operatorNameNumeric the operator numeric name if needs to set
      * @return the Phone associated with slot 0.
      */
-    private Phone setupConnectionServiceForDelayDial() {
+    private Phone setupConnectionServiceForDelayDial(boolean isRoaming, boolean setOperatorName,
+            String operatorNameLongName, String operatorNameShortName,
+                    String operatorNameNumeric) {
         ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
                 .setAccountHandle(PHONE_ACCOUNT_HANDLE_1)
                 .setAddress(TEST_ADDRESS)
@@ -1183,15 +1393,17 @@
         emergencyNumbers.put(0 /*subId*/, numbers);
         doReturn(emergencyNumbers).when(mTelephonyManagerProxy).getCurrentEmergencyNumberList();
         doReturn(2).when(mTelephonyManagerProxy).getPhoneCount();
-
+        testPhone0.getServiceState().setRoaming(isRoaming);
+        if (setOperatorName) {
+            testPhone0.getServiceState().setOperatorName(operatorNameLongName,
+                    operatorNameShortName, operatorNameNumeric);
+        }
         mConnection = mTestConnectionService.onCreateOutgoingConnection(
                 PHONE_ACCOUNT_HANDLE_1, connectionRequest);
         assertNotNull("test connection was not set up correctly.", mConnection);
-
         return testPhone0;
     }
 
-
     /**
      * Set up a mock MSIM device with TEST_ADDRESS set as an emergency number in airplane mode.
      * @return the Phone associated with slot 0.
@@ -1231,15 +1443,6 @@
         return testPhone0;
     }
 
-    private Runnable verifyRunnablePosted() {
-        ArgumentCaptor<Message> runnableCaptor = ArgumentCaptor.forClass(Message.class);
-        verify(mMockHandler).sendMessageDelayed(runnableCaptor.capture(), anyLong());
-        assertNotNull("Invalid Message created", runnableCaptor.getValue());
-        Runnable runnable = runnableCaptor.getValue().getCallback();
-        assertNotNull("sendMessageDelayed never occurred.", runnableCaptor);
-        return runnable;
-    }
-
     private EmergencyNumber setupEmergencyNumber(Uri address) {
         return new EmergencyNumber(address.getSchemeSpecificPart(), "", "",
         EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
@@ -1248,11 +1451,11 @@
         EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY);
     }
 
-    private void setupHandleToPhoneMap(PhoneAccountHandle handle,  Phone phone) {
+    private void setupHandleToPhoneMap(PhoneAccountHandle handle, Phone phone) {
         // use subId 0
-        when(mPhoneUtilsProxy.getSubIdForPhoneAccountHandle(handle)).thenReturn(0);
-        when(mSubscriptionManagerProxy.getPhoneId(0)).thenReturn(0);
-        when(mPhoneFactoryProxy.getPhone(0)).thenReturn(phone);
+        when(mPhoneUtilsProxy.getSubIdForPhoneAccountHandle(eq(handle))).thenReturn(0);
+        when(mSubscriptionManagerProxy.getPhoneId(eq(0))).thenReturn(0);
+        when(mPhoneFactoryProxy.getPhone(eq(0))).thenReturn(phone);
     }
 
     private AsyncResult getSuppServiceNotification(int notificationType, int code) {
@@ -1273,6 +1476,8 @@
         when(phone.getDefaultPhone()).thenReturn(phone);
         when(phone.getEmergencyNumberTracker()).thenReturn(mEmergencyNumberTracker);
         when(phone.getServiceStateTracker()).thenReturn(mSST);
+        doNothing().when(phone).registerForPreciseCallStateChanged(any(Handler.class), anyInt(),
+                any(Object.class));
         when(mEmergencyNumberTracker.getEmergencyNumber(anyString())).thenReturn(null);
         return phone;
     }
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionTest.java
index 94c9063..c55dee7 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionTest.java
@@ -1,20 +1,156 @@
 package com.android.services.telephony;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 import static junit.framework.TestCase.assertFalse;
 
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
 import android.os.Bundle;
 import android.telecom.Connection;
+import android.telephony.CarrierConfigManager;
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.d2d.DtmfTransport;
+import com.android.internal.telephony.d2d.RtpTransport;
+import com.android.internal.telephony.imsphone.ImsPhoneConnection;
+import com.android.phone.R;
+
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidJUnit4.class)
 public class TelephonyConnectionTest {
+    @Mock
+    private ImsPhoneConnection mImsPhoneConnection;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        when(mImsPhoneConnection.getState()).thenReturn(Call.State.ACTIVE);
+        when(mImsPhoneConnection.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_IMS);
+    }
+
+    /**
+     * Ensures an Ims connection uses the D2D communicator when it is enabled.
+     */
+    @Test
+    public void testSetupCommunicator() {
+        TestTelephonyConnection c = new TestTelephonyConnection();
+        c.setIsImsConnection(true);
+        // Enable D2D comms.
+        when(c.mMockResources.getBoolean(eq(
+                R.bool.config_use_device_to_device_communication))).thenReturn(true);
+        c.getCarrierConfigBundle().putBoolean(
+                CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL,
+                true);
+
+        c.setOriginalConnection(mImsPhoneConnection);
+        assertNotNull(c.getCommunicator());
+    }
+
+    /**
+     * Ensures an Ims connection does not use the D2D communicator when it is disabled.
+     */
+    @Test
+    public void testDoNotSetupCommunicatorWhenDisabled() {
+        TestTelephonyConnection c = new TestTelephonyConnection();
+        c.setIsImsConnection(true);
+        // Disable D2D comms.
+        when(c.mMockResources.getBoolean(eq(
+                R.bool.config_use_device_to_device_communication))).thenReturn(false);
+
+        c.setOriginalConnection(mImsPhoneConnection);
+        assertNull(c.getCommunicator());
+    }
+
+    /**
+     * Ensures an Ims connection does not use the D2D communicator for a non-IMS call.
+     */
+    @Test
+    public void testDoNotSetupCommunicatorForNonIms() {
+        TestTelephonyConnection c = new TestTelephonyConnection();
+        c.setIsImsConnection(false);
+        // Disable D2D comms.
+        when(c.mMockResources.getBoolean(eq(
+                R.bool.config_use_device_to_device_communication))).thenReturn(true);
+
+        c.setOriginalConnection(mImsPhoneConnection);
+        assertNull(c.getCommunicator());
+    }
+
+    /**
+     * Ensures an Ims connection does not use the D2D communicator when it is disabled.
+     */
+    @Test
+    public void testDoNotSetupCommunicatorNoTransports() {
+        TestTelephonyConnection c = new TestTelephonyConnection();
+        c.setIsImsConnection(true);
+        // Enable D2D comms.
+        when(c.mMockResources.getBoolean(eq(
+                R.bool.config_use_device_to_device_communication))).thenReturn(true);
+        // But carrier disables transports.  Womp.
+        c.getCarrierConfigBundle().putBoolean(
+                CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL,
+                false);
+        c.getCarrierConfigBundle().putBoolean(
+                CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL,
+                false);
+        c.setOriginalConnection(mImsPhoneConnection);
+        assertNull(c.getCommunicator());
+    }
+
+    @Test
+    public void testSetupRtpOnly() {
+        TestTelephonyConnection c = new TestTelephonyConnection();
+        c.setIsImsConnection(true);
+        // Enable D2D comms.
+        when(c.mMockResources.getBoolean(eq(
+                R.bool.config_use_device_to_device_communication))).thenReturn(true);
+        // But carrier disables transports.  Womp.
+        c.getCarrierConfigBundle().putBoolean(
+                CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL,
+                false);
+        c.getCarrierConfigBundle().putBoolean(
+                CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL,
+                true);
+        c.setOriginalConnection(mImsPhoneConnection);
+        assertNotNull(c.getCommunicator());
+        assertEquals(1, c.getCommunicator().getTransportProtocols().size());
+        assertTrue(c.getCommunicator().getTransportProtocols()
+                .stream().anyMatch(p -> p instanceof RtpTransport));
+    }
+
+    @Test
+    public void testSetupDtmfOnly() {
+        TestTelephonyConnection c = new TestTelephonyConnection();
+        c.setIsImsConnection(true);
+        // Enable D2D comms.
+        when(c.mMockResources.getBoolean(eq(
+                R.bool.config_use_device_to_device_communication))).thenReturn(true);
+        // But carrier disables transports.  Womp.
+        c.getCarrierConfigBundle().putBoolean(
+                CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL,
+                true);
+        c.getCarrierConfigBundle().putBoolean(
+                CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL,
+                false);
+        c.setOriginalConnection(mImsPhoneConnection);
+        assertNotNull(c.getCommunicator());
+        assertEquals(1, c.getCommunicator().getTransportProtocols().size());
+        assertTrue(c.getCommunicator().getTransportProtocols()
+                .stream().anyMatch(p -> p instanceof DtmfTransport));
+    }
 
     @Test
     public void testCodecInIms() {
diff --git a/tests/src/com/android/services/telephony/TestTelephonyConnection.java b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
index 596393c..817e9a7 100644
--- a/tests/src/com/android/services/telephony/TestTelephonyConnection.java
+++ b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
@@ -16,6 +16,10 @@
 
 package com.android.services.telephony;
 
+import android.content.AttributionSource;
+import android.content.ContentResolver;
+import android.os.Process;
+import android.os.UserHandle;
 import android.telephony.TelephonyManager;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -46,6 +50,8 @@
 
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -66,6 +72,9 @@
     Context mMockContext;
 
     @Mock
+    ContentResolver mMockContentResolver;
+
+    @Mock
     Resources mMockResources;
 
     @Mock
@@ -97,6 +106,7 @@
     private List<String> mLastConnectionEvents = new ArrayList<>();
     private List<Bundle> mLastConnectionEventExtras = new ArrayList<>();
     private Object mLock = new Object();
+    private PersistableBundle mCarrierConfig = new PersistableBundle();
 
     @Override
     public com.android.internal.telephony.Connection getOriginalConnection() {
@@ -118,6 +128,9 @@
         super(null, null, android.telecom.Call.Details.DIRECTION_INCOMING);
         MockitoAnnotations.initMocks(this);
 
+        AttributionSource attributionSource = new AttributionSource.Builder(
+                Process.myUid()).build();
+
         mIsImsConnection = false;
         mIsImsExternalConnection = false;
         mMockPhone = mock(Phone.class);
@@ -143,8 +156,12 @@
         when(mMockPhone.getContext()).thenReturn(mMockContext);
         when(mMockPhone.getCurrentSubscriberUris()).thenReturn(null);
         when(mMockContext.getResources()).thenReturn(mMockResources);
+        when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
         when(mMockContext.getSystemService(Context.TELEPHONY_SERVICE))
                 .thenReturn(mMockTelephonyManager);
+        when(mMockContext.getAttributionSource()).thenReturn(attributionSource);
+        when(mMockContentResolver.getUserId()).thenReturn(UserHandle.USER_CURRENT);
+        when(mMockContentResolver.getAttributionSource()).thenReturn(attributionSource);
         when(mMockResources.getBoolean(anyInt())).thenReturn(false);
         when(mMockPhone.getDefaultPhone()).thenReturn(mMockPhone);
         when(mMockPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_IMS);
@@ -201,7 +218,7 @@
     public PersistableBundle getCarrierConfig() {
         // Depends on PhoneGlobals for context in TelephonyConnection, do not implement during
         // testing.
-        return new PersistableBundle();
+        return mCarrierConfig;
     }
 
     @Override
@@ -284,4 +301,8 @@
                 .thenReturn(mCarrierConfigManager);
         when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
     }
+
+    public PersistableBundle getCarrierConfigBundle() {
+        return mCarrierConfig;
+    }
 }
diff --git a/tests/src/com/android/services/telephony/rcs/DelegateStateTrackerTest.java b/tests/src/com/android/services/telephony/rcs/DelegateStateTrackerTest.java
index 4d40702..8236f44 100644
--- a/tests/src/com/android/services/telephony/rcs/DelegateStateTrackerTest.java
+++ b/tests/src/com/android/services/telephony/rcs/DelegateStateTrackerTest.java
@@ -24,9 +24,10 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.net.InetAddresses;
 import android.telephony.ims.DelegateRegistrationState;
 import android.telephony.ims.FeatureTagState;
-import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.SipDelegateConfiguration;
 import android.telephony.ims.SipDelegateManager;
 import android.telephony.ims.aidl.ISipDelegate;
 import android.telephony.ims.aidl.ISipDelegateConnectionStateCallback;
@@ -44,6 +45,7 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
+import java.net.InetSocketAddress;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
@@ -85,13 +87,17 @@
         DelegateRegistrationState regState = new DelegateRegistrationState.Builder()
                 .addRegisteredFeatureTag(ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG)
                 .build();
-        SipDelegateImsConfiguration config = new SipDelegateImsConfiguration.Builder(1/*version*/)
-                .build();
+        InetSocketAddress localAddr = new InetSocketAddress(
+                InetAddresses.parseNumericAddress("1.1.1.1"), 80);
+        InetSocketAddress serverAddr = new InetSocketAddress(
+                InetAddresses.parseNumericAddress("2.2.2.2"), 81);
+        SipDelegateConfiguration c = new SipDelegateConfiguration.Builder(1,
+                SipDelegateConfiguration.SIP_TRANSPORT_TCP, localAddr, serverAddr).build();
         stateTracker.onRegistrationStateChanged(regState);
-        stateTracker.onImsConfigurationChanged(config);
+        stateTracker.onConfigurationChanged(c);
         verify(mAppCallback).onFeatureTagStatusChanged(eq(regState),
                 eq(new ArrayList<>(deniedTags)));
-        verify(mAppCallback).onImsConfigurationChanged(config);
+        verify(mAppCallback).onConfigurationChanged(c);
 
         verify(mAppCallback, never()).onDestroyed(anyInt());
     }
diff --git a/tests/src/com/android/services/telephony/rcs/MessageTransportStateTrackerTest.java b/tests/src/com/android/services/telephony/rcs/MessageTransportStateTrackerTest.java
deleted file mode 100644
index 5e05085..0000000
--- a/tests/src/com/android/services/telephony/rcs/MessageTransportStateTrackerTest.java
+++ /dev/null
@@ -1,243 +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.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.os.RemoteException;
-import android.telephony.ims.SipDelegateManager;
-import android.telephony.ims.SipMessage;
-import android.telephony.ims.aidl.ISipDelegate;
-import android.telephony.ims.aidl.ISipDelegateMessageCallback;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.TelephonyTestBase;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.concurrent.Executor;
-import java.util.function.Consumer;
-
-@RunWith(AndroidJUnit4.class)
-public class MessageTransportStateTrackerTest extends TelephonyTestBase {
-    private static final int TEST_SUB_ID = 1;
-
-    private static final SipMessage TEST_MESSAGE = new SipMessage(
-            "INVITE sip:callee@ex.domain.com SIP/2.0",
-            "Via: SIP/2.0/UDP ex.place.com;branch=z9hG4bK776asdhds",
-            new byte[0]);
-
-    // Use for finer-grained control of when the Executor executes.
-    private static class PendingExecutor implements Executor {
-        private final ArrayList<Runnable> mPendingRunnables = new ArrayList<>();
-
-        @Override
-        public void execute(Runnable command) {
-            mPendingRunnables.add(command);
-        }
-
-        public void executePending() {
-            for (Runnable r : mPendingRunnables) {
-                r.run();
-            }
-            mPendingRunnables.clear();
-        }
-    }
-
-    @Mock private ISipDelegateMessageCallback mDelegateMessageCallback;
-    @Mock private ISipDelegate mISipDelegate;
-    @Mock private Consumer<Boolean> mMockCloseConsumer;
-
-    @Before
-    public void setUp() throws Exception {
-        super.setUp();
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        super.tearDown();
-    }
-
-    @SmallTest
-    @Test
-    public void testDelegateConnectionSendOutgoingMessage() throws Exception {
-        MessageTransportStateTracker tracker = new MessageTransportStateTracker(TEST_SUB_ID,
-                Runnable::run, mDelegateMessageCallback);
-
-        tracker.openTransport(mISipDelegate, Collections.emptySet());
-        tracker.getDelegateConnection().sendMessage(TEST_MESSAGE, 1 /*version*/);
-        verify(mISipDelegate).sendMessage(TEST_MESSAGE, 1 /*version*/);
-
-        doThrow(new RemoteException()).when(mISipDelegate).sendMessage(any(), anyLong());
-        tracker.getDelegateConnection().sendMessage(TEST_MESSAGE, 1 /*version*/);
-        verify(mDelegateMessageCallback).onMessageSendFailure(any(),
-                eq(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD));
-
-        tracker.close(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED);
-        tracker.getDelegateConnection().sendMessage(TEST_MESSAGE, 1 /*version*/);
-        verify(mDelegateMessageCallback).onMessageSendFailure(any(),
-                eq(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED));
-    }
-
-    @SmallTest
-    @Test
-    public void testDelegateConnectionCloseGracefully() throws Exception {
-        PendingExecutor executor = new PendingExecutor();
-        MessageTransportStateTracker tracker = new MessageTransportStateTracker(TEST_SUB_ID,
-                executor, mDelegateMessageCallback);
-
-        tracker.openTransport(mISipDelegate, Collections.emptySet());
-        tracker.getDelegateConnection().sendMessage(TEST_MESSAGE, 1 /*version*/);
-        executor.executePending();
-        verify(mISipDelegate).sendMessage(TEST_MESSAGE, 1 /*version*/);
-        verify(mDelegateMessageCallback, never()).onMessageSendFailure(any(), anyInt());
-
-        // Use PendingExecutor a little weird here, we need to queue sendMessage first, even though
-        // closeGracefully will complete partly synchronously to test that the pending message will
-        // return MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION before the scheduled
-        // graceful close operation completes.
-        tracker.getDelegateConnection().sendMessage(TEST_MESSAGE, 1 /*version*/);
-        tracker.closeGracefully(
-                SipDelegateManager.MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION,
-                SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED,
-                mMockCloseConsumer);
-        verify(mMockCloseConsumer, never()).accept(any());
-        // resolve pending close operation
-        executor.executePending();
-        verify(mDelegateMessageCallback).onMessageSendFailure(any(),
-                eq(SipDelegateManager.MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION));
-        // Still should only report one call of sendMessage from before
-        verify(mISipDelegate).sendMessage(TEST_MESSAGE, 1 /*version*/);
-        verify(mMockCloseConsumer).accept(true);
-
-        // ensure that after close operation completes, we get the correct
-        // MESSAGE_FAILURE_REASON_DELEGATE_CLOSED message.
-        tracker.getDelegateConnection().sendMessage(TEST_MESSAGE, 1 /*version*/);
-        executor.executePending();
-        verify(mDelegateMessageCallback).onMessageSendFailure(any(),
-                eq(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED));
-        // Still should only report one call of sendMessage from before
-        verify(mISipDelegate).sendMessage(TEST_MESSAGE, 1 /*version*/);
-    }
-
-    @SmallTest
-    @Test
-    public void testDelegateConnectionNotifyMessageReceived() throws Exception {
-        MessageTransportStateTracker tracker = new MessageTransportStateTracker(TEST_SUB_ID,
-                Runnable::run, mDelegateMessageCallback);
-        tracker.openTransport(mISipDelegate, Collections.emptySet());
-        tracker.getDelegateConnection().notifyMessageReceived("z9hG4bK776asdhds");
-        verify(mISipDelegate).notifyMessageReceived("z9hG4bK776asdhds");
-    }
-
-    @SmallTest
-    @Test
-    public void testDelegateConnectionNotifyMessageReceiveError() throws Exception {
-        MessageTransportStateTracker tracker = new MessageTransportStateTracker(TEST_SUB_ID,
-                Runnable::run, mDelegateMessageCallback);
-        tracker.openTransport(mISipDelegate, Collections.emptySet());
-        tracker.getDelegateConnection().notifyMessageReceiveError("z9hG4bK776asdhds",
-                SipDelegateManager.MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE);
-        verify(mISipDelegate).notifyMessageReceiveError("z9hG4bK776asdhds",
-                SipDelegateManager.MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE);
-    }
-
-    @SmallTest
-    @Test
-    public void testDelegateConnectionCloseDialog() throws Exception {
-        MessageTransportStateTracker tracker = new MessageTransportStateTracker(TEST_SUB_ID,
-                Runnable::run, mDelegateMessageCallback);
-        tracker.openTransport(mISipDelegate, Collections.emptySet());
-        tracker.getDelegateConnection().closeDialog("testCallId");
-        verify(mISipDelegate).closeDialog("testCallId");
-    }
-
-    @SmallTest
-    @Test
-    public void testDelegateOnMessageReceived() throws Exception {
-        MessageTransportStateTracker tracker = new MessageTransportStateTracker(TEST_SUB_ID,
-                Runnable::run, mDelegateMessageCallback);
-        tracker.openTransport(mISipDelegate, Collections.emptySet());
-
-        tracker.getMessageCallback().onMessageReceived(TEST_MESSAGE);
-        verify(mDelegateMessageCallback).onMessageReceived(TEST_MESSAGE);
-
-        doThrow(new RemoteException()).when(mDelegateMessageCallback).onMessageReceived(any());
-        tracker.getMessageCallback().onMessageReceived(TEST_MESSAGE);
-        verify(mISipDelegate).notifyMessageReceiveError(any(),
-                eq(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD));
-    }
-
-    @SmallTest
-    @Test
-    public void testDelegateOnMessageReceivedClosedGracefully() throws Exception {
-        PendingExecutor executor = new PendingExecutor();
-        MessageTransportStateTracker tracker = new MessageTransportStateTracker(TEST_SUB_ID,
-                executor, mDelegateMessageCallback);
-        tracker.openTransport(mISipDelegate, Collections.emptySet());
-
-        tracker.getMessageCallback().onMessageReceived(TEST_MESSAGE);
-        executor.executePending();
-        verify(mDelegateMessageCallback).onMessageReceived(TEST_MESSAGE);
-
-        tracker.getMessageCallback().onMessageReceived(TEST_MESSAGE);
-        tracker.closeGracefully(
-                SipDelegateManager.MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION,
-                SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED,
-                mMockCloseConsumer);
-        executor.executePending();
-        // Incoming SIP message should not be blocked by closeGracefully
-        verify(mDelegateMessageCallback, times(2)).onMessageReceived(TEST_MESSAGE);
-    }
-
-    @SmallTest
-    @Test
-    public void testDelegateOnMessageSent() throws Exception {
-        MessageTransportStateTracker tracker = new MessageTransportStateTracker(TEST_SUB_ID,
-                Runnable::run, mDelegateMessageCallback);
-        tracker.openTransport(mISipDelegate, Collections.emptySet());
-        tracker.getMessageCallback().onMessageSent("z9hG4bK776asdhds");
-        verify(mDelegateMessageCallback).onMessageSent("z9hG4bK776asdhds");
-    }
-
-    @SmallTest
-    @Test
-    public void testDelegateonMessageSendFailure() throws Exception {
-        MessageTransportStateTracker tracker = new MessageTransportStateTracker(TEST_SUB_ID,
-                Runnable::run, mDelegateMessageCallback);
-        tracker.openTransport(mISipDelegate, Collections.emptySet());
-        tracker.getMessageCallback().onMessageSendFailure("z9hG4bK776asdhds",
-                SipDelegateManager.MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE);
-        verify(mDelegateMessageCallback).onMessageSendFailure("z9hG4bK776asdhds",
-                SipDelegateManager.MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE);
-    }
-}
diff --git a/tests/src/com/android/services/telephony/rcs/MessageTransportWrapperTest.java b/tests/src/com/android/services/telephony/rcs/MessageTransportWrapperTest.java
new file mode 100644
index 0000000..5ced75c
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/MessageTransportWrapperTest.java
@@ -0,0 +1,343 @@
+/*
+ * 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 junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.net.InetAddresses;
+import android.os.RemoteException;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConfiguration;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
+import android.util.ArraySet;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+import com.android.TestExecutorService;
+import com.android.services.telephony.rcs.validator.ValidationResult;
+
+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.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.function.Consumer;
+
+@RunWith(AndroidJUnit4.class)
+public class MessageTransportWrapperTest extends TelephonyTestBase {
+    private static final int TEST_SUB_ID = 1;
+
+    private static final SipMessage TEST_MESSAGE = new SipMessage(
+            "INVITE sip:callee@ex.domain.com SIP/2.0",
+            "Via: SIP/2.0/UDP ex.place.com;branch=z9hG4bK776asdhds",
+            new byte[0]);
+
+    // Derived from TEST_MESSAGE above.
+    private static final String TEST_TRANSACTION_ID = "z9hG4bK776asdhds";
+
+    @Mock private ISipDelegateMessageCallback mDelegateMessageCallback;
+    @Mock private TransportSipMessageValidator mTransportSipSessionValidator;
+    @Mock private ISipDelegate mISipDelegate;
+
+    // Test executor that just calls run on the Runnable provided in execute.
+    private ScheduledExecutorService mExecutor = new TestExecutorService();
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @SmallTest
+    @Test
+    public void testImsConfigurationChanged() {
+        MessageTransportWrapper tracker = createTestMessageTransportWrapper();
+        InetSocketAddress localAddr = new InetSocketAddress(
+                InetAddresses.parseNumericAddress("1.1.1.1"), 80);
+        InetSocketAddress serverAddr = new InetSocketAddress(
+                InetAddresses.parseNumericAddress("2.2.2.2"), 81);
+        SipDelegateConfiguration c = new SipDelegateConfiguration.Builder(1,
+                SipDelegateConfiguration.SIP_TRANSPORT_TCP, localAddr, serverAddr).build();
+        // Ensure IMS config changes are propagated to the message tracker.
+        tracker.onConfigurationChanged(c);
+        verify(mTransportSipSessionValidator).onConfigurationChanged(c);
+    }
+
+    @SmallTest
+    @Test
+    public void testOpenTransport() {
+        HashSet<String> allowedTags = new HashSet<>(1);
+        allowedTags.add("testTag");
+        HashSet<FeatureTagState> deniedTags = new HashSet<>(1);
+        deniedTags.add(new FeatureTagState("testBadTag",
+                SipDelegateManager.DENIED_REASON_INVALID));
+        MessageTransportWrapper tracker = createTestMessageTransportWrapper();
+        // Ensure openTransport passes denied tags to the session tracker
+        tracker.openTransport(mISipDelegate, allowedTags, deniedTags);
+        verify(mTransportSipSessionValidator).onTransportOpened(allowedTags, deniedTags);
+    }
+
+    @SmallTest
+    @Test
+    public void testRegistrationStateChanged() throws Exception {
+        ArraySet<String> callIds = new ArraySet<>(2);
+        callIds.add("callId1");
+        callIds.add("callId2");
+        // empty registration state for testing
+        DelegateRegistrationState state = new DelegateRegistrationState.Builder().build();
+        MessageTransportWrapper tracker = createTestMessageTransportWrapper();
+        tracker.openTransport(mISipDelegate, Collections.emptySet(), Collections.emptySet());
+
+        Consumer<Set<String>> callIdConsumer = trackerRegStateChanged(tracker, state);
+        callIdConsumer.accept(callIds);
+        // Verify that the pending call IDs are closed properly.
+        for (String callId : callIds) {
+            verify(mTransportSipSessionValidator).onSipSessionCleanup(callId);
+            verify(mISipDelegate).cleanupSession(callId);
+        }
+    }
+
+    @SmallTest
+    @Test
+    public void testCloseGracefully() throws Exception {
+        int closingReason = DelegateRegistrationState.DEREGISTERING_REASON_PROVISIONING_CHANGE;
+        int closedReason = DelegateRegistrationState.DEREGISTERED_REASON_NOT_PROVISIONED;
+        MessageTransportWrapper tracker = createTestMessageTransportWrapper();
+
+        Boolean[] result = new Boolean[1];
+        Consumer<Set<String>> callIdConsumer = closeTrackerGracefully(tracker, closingReason,
+                closedReason, (r) -> result[0] = r);
+        callIdConsumer.accept(Collections.emptySet());
+        // Verify that the pending call IDs are closed properly.
+        verify(mTransportSipSessionValidator, never()).onSipSessionCleanup(anyString());
+        verify(mISipDelegate, never()).cleanupSession(anyString());
+        // Result is true in the case that all call IDs were successfully closed.
+        assertTrue(result[0]);
+    }
+
+    @SmallTest
+    @Test
+    public void testCloseGracefullyForceCloseCallIds() throws Exception {
+        ArraySet<String> callIds = new ArraySet<>(2);
+        callIds.add("callId1");
+        callIds.add("callId2");
+        int closingReason = DelegateRegistrationState.DEREGISTERING_REASON_PROVISIONING_CHANGE;
+        int closedReason = DelegateRegistrationState.DEREGISTERED_REASON_NOT_PROVISIONED;
+        MessageTransportWrapper tracker = createTestMessageTransportWrapper();
+        tracker.openTransport(mISipDelegate, Collections.emptySet(), Collections.emptySet());
+
+        Boolean[] result = new Boolean[1];
+        Consumer<Set<String>> callIdConsumer = closeTrackerGracefully(tracker, closingReason,
+                closedReason, (r) -> result[0] = r);
+        callIdConsumer.accept(callIds);
+        // Verify that the pending call IDs are closed properly.
+        for (String callId : callIds) {
+            verify(mTransportSipSessionValidator).onSipSessionCleanup(callId);
+            verify(mISipDelegate).cleanupSession(callId);
+        }
+        // Result is false in this case because there were still callIds left that were not
+        // successfully closed.
+        assertFalse(result[0]);
+    }
+
+    @SmallTest
+    @Test
+    public void testClose() throws Exception {
+        ArraySet<String> callIds = new ArraySet<>(2);
+        callIds.add("callId1");
+        callIds.add("callId2");
+        int closedReason = SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED;
+        doReturn(callIds).when(mTransportSipSessionValidator).closeSessions(closedReason);
+        MessageTransportWrapper tracker = createTestMessageTransportWrapper();
+        tracker.openTransport(mISipDelegate, Collections.emptySet(), Collections.emptySet());
+
+        tracker.close(closedReason);
+        // Verify that the pending call IDs are closed properly.
+        for (String callId : callIds) {
+            verify(mTransportSipSessionValidator).onSipSessionCleanup(callId);
+            verify(mISipDelegate).cleanupSession(callId);
+        }
+    }
+
+    @SmallTest
+    @Test
+    public void testDelegateConnectionSendOutgoingMessage() throws Exception {
+        MessageTransportWrapper tracker = createTestMessageTransportWrapper();
+
+        tracker.openTransport(mISipDelegate, Collections.emptySet(), Collections.emptySet());
+        doReturn(ValidationResult.SUCCESS)
+                .when(mTransportSipSessionValidator)
+                .verifyOutgoingMessage(TEST_MESSAGE, 1 /*version*/);
+        tracker.getDelegateConnection().sendMessage(TEST_MESSAGE, 1 /*version*/);
+        verify(mISipDelegate).sendMessage(TEST_MESSAGE, 1 /*version*/);
+
+        doThrow(new RemoteException()).when(mISipDelegate).sendMessage(any(), anyLong());
+        tracker.getDelegateConnection().sendMessage(TEST_MESSAGE, 1 /*version*/);
+        verify(mDelegateMessageCallback).onMessageSendFailure(any(),
+                eq(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD));
+
+        doReturn(new ValidationResult(
+                SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED, ""))
+                .when(mTransportSipSessionValidator)
+                .verifyOutgoingMessage(TEST_MESSAGE, 1 /*version*/);
+        tracker.getDelegateConnection().sendMessage(TEST_MESSAGE, 1 /*version*/);
+        verify(mDelegateMessageCallback).onMessageSendFailure(TEST_TRANSACTION_ID,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED);
+    }
+
+    @SmallTest
+    @Test
+    public void testDelegateConnectionNotifyMessageReceived() throws Exception {
+        MessageTransportWrapper tracker = createTestMessageTransportWrapper();
+        tracker.openTransport(mISipDelegate, Collections.emptySet(), Collections.emptySet());
+        tracker.getDelegateConnection().notifyMessageReceived(TEST_TRANSACTION_ID);
+        verify(mISipDelegate).notifyMessageReceived(TEST_TRANSACTION_ID);
+        verify(mTransportSipSessionValidator).acknowledgePendingMessage(TEST_TRANSACTION_ID);
+    }
+
+    @SmallTest
+    @Test
+    public void testDelegateConnectionNotifyMessageReceiveError() throws Exception {
+        MessageTransportWrapper tracker = createTestMessageTransportWrapper();
+        tracker.openTransport(mISipDelegate, Collections.emptySet(), Collections.emptySet());
+        tracker.getDelegateConnection().notifyMessageReceiveError(TEST_TRANSACTION_ID,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE);
+        verify(mISipDelegate).notifyMessageReceiveError(TEST_TRANSACTION_ID,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE);
+        verify(mTransportSipSessionValidator).notifyPendingMessageFailed(TEST_TRANSACTION_ID);
+    }
+
+    @SmallTest
+    @Test
+    public void testDelegateConnectionCloseSession() throws Exception {
+        MessageTransportWrapper tracker = createTestMessageTransportWrapper();
+        tracker.openTransport(mISipDelegate, Collections.emptySet(), Collections.emptySet());
+        tracker.getDelegateConnection().cleanupSession("testCallId");
+        verify(mISipDelegate).cleanupSession("testCallId");
+        verify(mTransportSipSessionValidator).onSipSessionCleanup("testCallId");
+    }
+
+    @SmallTest
+    @Test
+    public void testDelegateOnMessageReceived() throws Exception {
+        MessageTransportWrapper tracker = createTestMessageTransportWrapper();
+        tracker.openTransport(mISipDelegate, Collections.emptySet(), Collections.emptySet());
+
+        doReturn(ValidationResult.SUCCESS)
+                .when(mTransportSipSessionValidator).verifyIncomingMessage(TEST_MESSAGE);
+        tracker.getMessageCallback().onMessageReceived(TEST_MESSAGE);
+        verify(mDelegateMessageCallback).onMessageReceived(TEST_MESSAGE);
+
+        doThrow(new RemoteException()).when(mDelegateMessageCallback).onMessageReceived(any());
+        tracker.getMessageCallback().onMessageReceived(TEST_MESSAGE);
+        verify(mISipDelegate).notifyMessageReceiveError(TEST_TRANSACTION_ID,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD);
+
+        doReturn(new ValidationResult(
+                SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD, ""))
+                .when(mTransportSipSessionValidator).verifyIncomingMessage(TEST_MESSAGE);
+        tracker.getMessageCallback().onMessageReceived(TEST_MESSAGE);
+        verify(mISipDelegate, times(2)).notifyMessageReceiveError(TEST_TRANSACTION_ID,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD);
+    }
+
+    @SmallTest
+    @Test
+    public void testDelegateOnMessageSent() throws Exception {
+        MessageTransportWrapper tracker = createTestMessageTransportWrapper();
+        tracker.openTransport(mISipDelegate, Collections.emptySet(), Collections.emptySet());
+        tracker.getMessageCallback().onMessageSent(TEST_TRANSACTION_ID);
+        verify(mTransportSipSessionValidator).acknowledgePendingMessage(TEST_TRANSACTION_ID);
+        verify(mDelegateMessageCallback).onMessageSent(TEST_TRANSACTION_ID);
+    }
+
+    @SmallTest
+    @Test
+    public void testDelegateOnMessageSendFailure() throws Exception {
+        MessageTransportWrapper tracker = createTestMessageTransportWrapper();
+        tracker.openTransport(mISipDelegate, Collections.emptySet(), Collections.emptySet());
+        tracker.getMessageCallback().onMessageSendFailure(TEST_TRANSACTION_ID,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE);
+        verify(mTransportSipSessionValidator).notifyPendingMessageFailed(TEST_TRANSACTION_ID);
+        verify(mDelegateMessageCallback).onMessageSendFailure(TEST_TRANSACTION_ID,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE);
+    }
+
+    private MessageTransportWrapper createTestMessageTransportWrapper() {
+        return new MessageTransportWrapper(TEST_SUB_ID,
+                mExecutor, mDelegateMessageCallback, mTransportSipSessionValidator);
+    }
+
+    private Consumer<Set<String>> trackerRegStateChanged(MessageTransportWrapper tracker,
+            DelegateRegistrationState state) {
+        ArrayList<Consumer<Set<String>>> consumerCaptor = new ArrayList<>(1);
+        Mockito.doAnswer(it -> {
+            // Capture the consumer here.
+            consumerCaptor.add(it.getArgument(0));
+            return null;
+        }).when(mTransportSipSessionValidator).onRegistrationStateChanged(any(), eq(state));
+        tracker.onRegistrationStateChanged(state);
+        verify(mTransportSipSessionValidator).onRegistrationStateChanged(any(), eq(state));
+        assertFalse(consumerCaptor.isEmpty());
+        return consumerCaptor.get(0);
+    }
+
+    private Consumer<Set<String>> closeTrackerGracefully(MessageTransportWrapper tracker,
+            int closingReason, int closedReason, Consumer<Boolean> resultConsumer) {
+        ArrayList<Consumer<Set<String>>> consumerCaptor = new ArrayList<>(1);
+        Mockito.doAnswer(it -> {
+            // Capture the consumer here.
+            consumerCaptor.add(it.getArgument(0));
+            return null;
+        }).when(mTransportSipSessionValidator).closeSessionsGracefully(any(), eq(closingReason),
+                eq(closedReason));
+        tracker.closeGracefully(closingReason, closedReason, resultConsumer);
+        verify(mTransportSipSessionValidator).closeSessionsGracefully(any(), eq(closingReason),
+                eq(closedReason));
+        assertFalse(consumerCaptor.isEmpty());
+        return consumerCaptor.get(0);
+    }
+}
diff --git a/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java b/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
index 7e87dc7..da614fc 100644
--- a/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
+++ b/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
@@ -31,6 +31,7 @@
 import android.telephony.AccessNetworkConstants;
 import android.telephony.ims.ImsException;
 import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
 import android.telephony.ims.RegistrationManager;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsRegistrationCallback;
@@ -57,6 +58,8 @@
 @RunWith(AndroidJUnit4.class)
 public class RcsFeatureControllerTest extends TelephonyTestBase {
 
+    private static final int TEST_SUB_ID = 1;
+
     private static final ImsReasonInfo REASON_DISCONNECTED = new ImsReasonInfo(
             ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN, 0, "test");
 
@@ -95,7 +98,7 @@
         // Connect the RcsFeatureManager
         mConnectorListener.getValue().connectionReady(mFeatureManager);
 
-        verify(mFeatureManager).updateCapabilities();
+        verify(mFeatureManager).updateCapabilities(TEST_SUB_ID);
         verify(mFeatureManager).registerImsRegistrationCallback(any());
         verify(mMockFeature).onRcsConnected(mFeatureManager);
 
@@ -131,29 +134,31 @@
         mConnectorListener.getValue().connectionReady(mFeatureManager);
 
         try {
-            controller.registerImsRegistrationCallback(0 /*subId*/, regCb);
-            controller.registerRcsAvailabilityCallback(0 /*subId*/, capCb);
+            controller.registerImsRegistrationCallback(TEST_SUB_ID, regCb);
+            controller.registerRcsAvailabilityCallback(TEST_SUB_ID, capCb);
             controller.isCapable(RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE,
                     ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
-            controller.isAvailable(RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE);
+            controller.isAvailable(RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE,
+                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
             controller.getRegistrationTech(integer -> {
             });
-            verify(mFeatureManager).registerImsRegistrationCallback(0, regCb);
-            verify(mFeatureManager).registerRcsAvailabilityCallback(0, capCb);
+            verify(mFeatureManager).registerImsRegistrationCallback(TEST_SUB_ID, regCb);
+            verify(mFeatureManager).registerRcsAvailabilityCallback(TEST_SUB_ID, capCb);
             verify(mFeatureManager).isCapable(
                     RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE,
                     ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
             verify(mFeatureManager).isAvailable(
-                    RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE);
+                    RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE,
+                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
             verify(mFeatureManager).getImsRegistrationTech(any());
         } catch (ImsException e) {
             fail("ImsException not expected.");
         }
 
-        controller.unregisterImsRegistrationCallback(0, regCb);
-        controller.unregisterRcsAvailabilityCallback(0, capCb);
-        verify(mFeatureManager).unregisterImsRegistrationCallback(0, regCb);
-        verify(mFeatureManager).unregisterRcsAvailabilityCallback(0, capCb);
+        controller.unregisterImsRegistrationCallback(TEST_SUB_ID, regCb);
+        controller.unregisterRcsAvailabilityCallback(TEST_SUB_ID, capCb);
+        verify(mFeatureManager).unregisterImsRegistrationCallback(TEST_SUB_ID, regCb);
+        verify(mFeatureManager).unregisterRcsAvailabilityCallback(TEST_SUB_ID, capCb);
     }
 
     @Test
@@ -173,7 +178,9 @@
         });
         verify(mRegistrationCallback).handleImsUnregistered(REASON_DISCONNECTED);
 
-        captor.getValue().onRegistering(ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+        ImsRegistrationAttributes attr = new ImsRegistrationAttributes.Builder(
+                ImsRegistrationImplBase.REGISTRATION_TECH_LTE).build();
+        captor.getValue().onRegistering(attr);
         controller.getRegistrationState(result -> {
             assertNotNull(result);
             assertEquals(RegistrationManager.REGISTRATION_STATE_REGISTERING, result.intValue());
@@ -181,7 +188,7 @@
         verify(mRegistrationCallback).handleImsRegistering(
                 AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
 
-        captor.getValue().onRegistered(ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+        captor.getValue().onRegistered(attr);
         controller.getRegistrationState(result -> {
             assertNotNull(result);
             assertEquals(RegistrationManager.REGISTRATION_STATE_REGISTERED, result.intValue());
@@ -211,13 +218,13 @@
                 FeatureConnector.UNAVAILABLE_REASON_DISCONNECTED);
 
         try {
-            controller.registerImsRegistrationCallback(0 /*subId*/, null /*callback*/);
+            controller.registerImsRegistrationCallback(TEST_SUB_ID, null /*callback*/);
             fail("ImsException expected for IMS registration.");
         } catch (ImsException e) {
             //expected
         }
         try {
-            controller.registerRcsAvailabilityCallback(0 /*subId*/, null /*callback*/);
+            controller.registerRcsAvailabilityCallback(TEST_SUB_ID, null /*callback*/);
             fail("ImsException expected for availability");
         } catch (ImsException e) {
             //expected
@@ -230,7 +237,8 @@
             //expected
         }
         try {
-            controller.isAvailable(RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE);
+            controller.isAvailable(RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE,
+                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
             fail("ImsException expected for availability check");
         } catch (ImsException e) {
             //expected
@@ -239,10 +247,25 @@
             assertNotNull(integer);
             assertEquals(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, integer.intValue());
         });
-        controller.unregisterImsRegistrationCallback(0, regCb);
-        controller.unregisterRcsAvailabilityCallback(0, capCb);
-        verify(mFeatureManager, never()).unregisterImsRegistrationCallback(0, regCb);
-        verify(mFeatureManager, never()).unregisterRcsAvailabilityCallback(0, capCb);
+        controller.unregisterImsRegistrationCallback(TEST_SUB_ID, regCb);
+        controller.unregisterRcsAvailabilityCallback(TEST_SUB_ID, capCb);
+        verify(mFeatureManager, never()).unregisterImsRegistrationCallback(TEST_SUB_ID, regCb);
+        verify(mFeatureManager, never()).unregisterRcsAvailabilityCallback(TEST_SUB_ID, capCb);
+    }
+
+    @Test
+    public void testCarrierConfigChanged() throws Exception {
+        RcsFeatureController controller = createFeatureController();
+        // Connect the RcsFeatureManager
+        mConnectorListener.getValue().connectionReady(mFeatureManager);
+        verify(mFeatureManager).updateCapabilities(TEST_SUB_ID);
+        controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
+
+        controller.onCarrierConfigChangedForSubscription();
+
+        verify(mFeatureManager, times(2)).updateCapabilities(TEST_SUB_ID);
+        verify(mMockFeature).onCarrierConfigChanged();
+        verify(mMockFeature, never()).onAssociatedSubscriptionUpdated(anyInt());
     }
 
     @Test
@@ -250,13 +273,13 @@
         RcsFeatureController controller = createFeatureController();
         // Connect the RcsFeatureManager
         mConnectorListener.getValue().connectionReady(mFeatureManager);
-        verify(mFeatureManager).updateCapabilities();
+        verify(mFeatureManager).updateCapabilities(TEST_SUB_ID);
         controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
 
-        controller.updateAssociatedSubscription(1 /*new sub id*/);
+        controller.updateAssociatedSubscription(2 /*new subId*/);
 
-        verify(mFeatureManager, times(2)).updateCapabilities();
-        verify(mMockFeature).onAssociatedSubscriptionUpdated(1 /*new sub id*/);
+        verify(mFeatureManager).updateCapabilities(2 /*new subId*/);
+        verify(mMockFeature).onAssociatedSubscriptionUpdated(2 /*new subId*/);
     }
 
     @Test
@@ -275,7 +298,7 @@
 
     private RcsFeatureController createFeatureController() {
         RcsFeatureController controller = new RcsFeatureController(mContext, 0 /*slotId*/,
-                mRegistrationFactory);
+                TEST_SUB_ID, mRegistrationFactory);
         controller.setFeatureConnectorFactory(mFeatureFactory);
         doReturn(mFeatureConnector).when(mFeatureFactory).create(any(), anyInt(),
                 mConnectorListener.capture(), any(), any());
diff --git a/tests/src/com/android/services/telephony/rcs/SipDelegateBinderConnectionTest.java b/tests/src/com/android/services/telephony/rcs/SipDelegateBinderConnectionTest.java
index 360fa21..3d8e94b 100644
--- a/tests/src/com/android/services/telephony/rcs/SipDelegateBinderConnectionTest.java
+++ b/tests/src/com/android/services/telephony/rcs/SipDelegateBinderConnectionTest.java
@@ -25,12 +25,13 @@
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.verify;
 
+import android.net.InetAddresses;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.telephony.ims.DelegateRegistrationState;
 import android.telephony.ims.DelegateRequest;
 import android.telephony.ims.FeatureTagState;
-import android.telephony.ims.SipDelegateImsConfiguration;
+import android.telephony.ims.SipDelegateConfiguration;
 import android.telephony.ims.SipDelegateManager;
 import android.telephony.ims.aidl.IImsRegistration;
 import android.telephony.ims.aidl.ISipDelegate;
@@ -51,6 +52,7 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
+import java.net.InetSocketAddress;
 import java.util.ArrayList;
 import java.util.Set;
 import java.util.function.BiConsumer;
@@ -194,9 +196,14 @@
         cb.onCreated(mMockDelegate, new ArrayList<>(deniedTags));
         verify(mMockCreatedCallback).accept(mMockDelegate, deniedTags);
 
-        SipDelegateImsConfiguration config = new SipDelegateImsConfiguration.Builder(1).build();
-        cb.onImsConfigurationChanged(config);
-        verify(mMockStateCallback).onImsConfigurationChanged(config);
+        InetSocketAddress localAddr = new InetSocketAddress(
+                InetAddresses.parseNumericAddress("1.1.1.1"), 80);
+        InetSocketAddress serverAddr = new InetSocketAddress(
+                InetAddresses.parseNumericAddress("2.2.2.2"), 81);
+        SipDelegateConfiguration c = new SipDelegateConfiguration.Builder(1,
+                SipDelegateConfiguration.SIP_TRANSPORT_TCP, localAddr, serverAddr).build();
+        cb.onConfigurationChanged(c);
+        verify(mMockStateCallback).onConfigurationChanged(c);
 
         DelegateRegistrationState regState = new DelegateRegistrationState.Builder()
                 .addRegisteredFeatureTags(request.getFeatureTags()).build();
diff --git a/tests/src/com/android/services/telephony/rcs/SipDelegateControllerTest.java b/tests/src/com/android/services/telephony/rcs/SipDelegateControllerTest.java
index 27f896b..5b0e7c5 100644
--- a/tests/src/com/android/services/telephony/rcs/SipDelegateControllerTest.java
+++ b/tests/src/com/android/services/telephony/rcs/SipDelegateControllerTest.java
@@ -58,13 +58,14 @@
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
+import java.util.stream.Collectors;
 
 @RunWith(AndroidJUnit4.class)
 public class SipDelegateControllerTest extends TelephonyTestBase {
     private static final int TEST_SUB_ID = 1;
 
     @Mock private ISipDelegate mMockSipDelegate;
-    @Mock private MessageTransportStateTracker mMockMessageTracker;
+    @Mock private MessageTransportWrapper mMockMessageTracker;
     @Mock private ISipDelegateMessageCallback mMockMessageCallback;
     @Mock private DelegateStateTracker mMockDelegateStateTracker;
     @Mock private DelegateBinderStateManager mMockBinderConnection;
@@ -104,12 +105,44 @@
         assertFalse(future.isDone());
         consumer.accept(mMockSipDelegate, Collections.emptySet());
         assertTrue(future.get());
-        verify(mMockMessageTracker).openTransport(mMockSipDelegate, Collections.emptySet());
+        verify(mMockMessageTracker).openTransport(mMockSipDelegate, request.getFeatureTags(),
+                Collections.emptySet());
         verify(mMockDelegateStateTracker).sipDelegateConnected(Collections.emptySet());
     }
 
     @SmallTest
     @Test
+    public void testCreateDeniedFeatures() throws Exception {
+        DelegateRequest request = getLargeDelegateRequest();
+        ArraySet<FeatureTagState> deniedTags = new ArraySet<>(1);
+        deniedTags.add(new FeatureTagState(ImsSignallingUtils.GROUP_CHAT_TAG,
+                SipDelegateManager.DENIED_REASON_NOT_ALLOWED));
+        SipDelegateController controller = getTestDelegateController(request,
+                deniedTags);
+
+        doReturn(true).when(mMockBinderConnection).create(eq(mMockMessageCallback), any());
+        CompletableFuture<Boolean> future = controller.create(request.getFeatureTags(),
+                deniedTags);
+        BiConsumer<ISipDelegate, Set<FeatureTagState>> consumer =
+                verifyConnectionCreated(1);
+        assertNotNull(consumer);
+
+        assertFalse(future.isDone());
+        // Send in additional tags denied by the service
+        deniedTags.add(new FeatureTagState(ImsSignallingUtils.FILE_TRANSFER_HTTP_TAG,
+                SipDelegateManager.DENIED_REASON_NOT_ALLOWED));
+        consumer.accept(mMockSipDelegate, deniedTags);
+        assertTrue(future.get());
+        // Allowed tags should be initial request set - denied tags
+        ArraySet<String> allowedTags = new ArraySet<>(request.getFeatureTags());
+        allowedTags.removeAll(deniedTags.stream().map(FeatureTagState::getFeatureTag)
+                .collect(Collectors.toSet()));
+        verify(mMockMessageTracker).openTransport(mMockSipDelegate, allowedTags, deniedTags);
+        verify(mMockDelegateStateTracker).sipDelegateConnected(deniedTags);
+    }
+
+    @SmallTest
+    @Test
     public void testCreateDelegateTransportDied() throws Exception {
         DelegateRequest request = getBaseDelegateRequest();
         SipDelegateController controller = getTestDelegateController(request,
@@ -212,7 +245,9 @@
         consumer.accept(mMockSipDelegate, Collections.emptySet());
         assertTrue(pendingChange.get());
 
-        verify(mMockMessageTracker, times(2)).openTransport(mMockSipDelegate,
+        verify(mMockMessageTracker).openTransport(mMockSipDelegate, request.getFeatureTags(),
+                Collections.emptySet());
+        verify(mMockMessageTracker).openTransport(mMockSipDelegate, newFts,
                 Collections.emptySet());
         verify(mMockDelegateStateTracker, times(2)).sipDelegateConnected(Collections.emptySet());
     }
@@ -235,10 +270,22 @@
         return request;
     }
 
+    private ArraySet<String> getLargeFTSet() {
+        ArraySet<String> request = new ArraySet<>();
+        request.add(ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG);
+        request.add(ImsSignallingUtils.GROUP_CHAT_TAG);
+        request.add(ImsSignallingUtils.FILE_TRANSFER_HTTP_TAG);
+        return request;
+    }
+
     private DelegateRequest getBaseDelegateRequest() {
         return new DelegateRequest(getBaseFTSet());
     }
 
+    private DelegateRequest getLargeDelegateRequest() {
+        return new DelegateRequest(getLargeFTSet());
+    }
+
     private SipDelegateController getTestDelegateController(DelegateRequest request,
             Set<FeatureTagState> deniedSet) {
         return new SipDelegateController(TEST_SUB_ID, request, "", mExecutorService,
diff --git a/tests/src/com/android/services/telephony/rcs/SipDialogTest.java b/tests/src/com/android/services/telephony/rcs/SipDialogTest.java
new file mode 100644
index 0000000..17830c5
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/SipDialogTest.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2021 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 junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import android.telephony.ims.SipMessage;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SipDialogTest {
+
+    private static final String SIP_URI_ALICE = "sip:alice@client.example.com";
+    private static final String BASE_CONTACT_URI_ALICE = "Alice <" + SIP_URI_ALICE + ">";
+    private static final String SIP_URI_BOB = "sip:bob@client.example.com";
+    private static final String BASE_CONTACT_URI_BOB = "Bob <" + SIP_URI_BOB + ">";
+
+    @Test
+    public void testCreateEarlyDialog() {
+        String branchId = "testBranchId";
+        String fromTag = "abcd";
+        String callId = "testCallId";
+        SipMessage inviteRequest = SipMessageUtils.generateSipRequest(
+                SipMessageUtils.INVITE_SIP_METHOD,
+                BASE_CONTACT_URI_ALICE, BASE_CONTACT_URI_BOB, SIP_URI_BOB, branchId, callId,
+                fromTag, null /*toTag*/);
+        SipDialog dialog = SipDialog.fromSipMessage(inviteRequest);
+        assertNotNull(dialog);
+        assertEquals(SipDialog.STATE_EARLY, dialog.getState());
+        assertEquals(callId, dialog.getCallId());
+        assertNull(dialog.getToTag());
+        // receive an earlyResponse
+        String toTag = "testToTag";
+        dialog.earlyResponse(toTag);
+        assertEquals(toTag, dialog.getToTag());
+    }
+
+    @Test
+    public void testIsResponseAssociated() {
+        String branchId = "testBranchId";
+        String fromTag = "abcd";
+        String callId = "testCallId";
+        SipMessage inviteRequest = SipMessageUtils.generateSipRequest(
+                SipMessageUtils.INVITE_SIP_METHOD,
+                BASE_CONTACT_URI_ALICE, BASE_CONTACT_URI_BOB, SIP_URI_BOB, branchId, callId,
+                fromTag, null /*toTag*/);
+        SipDialog dialog = SipDialog.fromSipMessage(inviteRequest);
+        assertNotNull(dialog);
+
+        // A response with no to tag should match
+        SipMessage inviteTrying = SipMessageUtils.generateSipResponse("100", "Trying",
+                BASE_CONTACT_URI_ALICE, BASE_CONTACT_URI_BOB, branchId, callId, fromTag,
+                null /*toTag*/);
+        assertTrue(dialog.isResponseAssociatedWithDialog(inviteTrying));
+        // A response with a different to tag should match
+        inviteTrying = SipMessageUtils.generateSipResponse("100", "Trying",
+                BASE_CONTACT_URI_ALICE, BASE_CONTACT_URI_BOB, branchId, callId, fromTag,
+                "testToTag");
+        assertTrue(dialog.isResponseAssociatedWithDialog(inviteTrying));
+        // A response with a different from tag shouldn't match.
+        String fromTag2 = "testFromTag2";
+        inviteTrying = SipMessageUtils.generateSipResponse("100", "Trying",
+                BASE_CONTACT_URI_ALICE, BASE_CONTACT_URI_BOB, branchId, callId, fromTag2,
+                null /*toTag*/);
+        assertFalse(dialog.isResponseAssociatedWithDialog(inviteTrying));
+        // A response with a different branch ID shouldn't match.
+        String branchId2 = "testBranchId2";
+        inviteTrying = SipMessageUtils.generateSipResponse("100", "Trying",
+                BASE_CONTACT_URI_ALICE, BASE_CONTACT_URI_BOB, branchId2, callId, fromTag,
+                null /*toTag*/);
+        assertFalse(dialog.isResponseAssociatedWithDialog(inviteTrying));
+        // A response with a different call id shouldn't match.
+        String callId2 = "testCallId2";
+        inviteTrying = SipMessageUtils.generateSipResponse("100", "Trying",
+                BASE_CONTACT_URI_ALICE, BASE_CONTACT_URI_BOB, branchId, callId2, fromTag,
+                null /*toTag*/);
+        assertFalse(dialog.isResponseAssociatedWithDialog(inviteTrying));
+    }
+
+    @Test
+    public void testFork() {
+        String branchId = "testBranchId";
+        String fromTag = "abcd";
+        String callId = "testCallId";
+        SipMessage inviteRequest = SipMessageUtils.generateSipRequest(
+                SipMessageUtils.INVITE_SIP_METHOD,
+                BASE_CONTACT_URI_ALICE, BASE_CONTACT_URI_BOB, SIP_URI_BOB, branchId, callId,
+                fromTag, null /*toTag*/);
+        SipDialog dialog = SipDialog.fromSipMessage(inviteRequest);
+        assertNotNull(dialog);
+        assertEquals(SipDialog.STATE_EARLY, dialog.getState());
+        assertEquals(callId, dialog.getCallId());
+        // receive an earlyResponse
+        dialog.earlyResponse("testToTag");
+        assertEquals(SipDialog.STATE_EARLY, dialog.getState());
+        // fork dialog
+        SipDialog dialogFork = dialog.forkDialog();
+        assertEquals(SipDialog.STATE_EARLY, dialogFork.getState());
+        assertEquals(callId, dialogFork.getCallId());
+        assertNull(dialogFork.getToTag());
+    }
+
+    @Test
+    public void testConfirmDialog() {
+        String branchId = "testBranchId";
+        String fromTag = "abcd";
+        String callId = "testCallId";
+        SipMessage inviteRequest = SipMessageUtils.generateSipRequest(
+                SipMessageUtils.INVITE_SIP_METHOD,
+                BASE_CONTACT_URI_ALICE, BASE_CONTACT_URI_BOB, SIP_URI_BOB, branchId, callId,
+                fromTag, null /*toTag*/);
+        SipDialog dialog = SipDialog.fromSipMessage(inviteRequest);
+        assertNotNull(dialog);
+        assertEquals(SipDialog.STATE_EARLY, dialog.getState());
+        assertEquals(callId, dialog.getCallId());
+        // receive a confirm response
+        dialog.confirm("testToTag");
+        assertEquals(SipDialog.STATE_CONFIRMED, dialog.getState());
+        assertEquals(callId, dialog.getCallId());
+    }
+
+    @Test
+    public void testIsRequestAssociated() {
+        String branchId = "testBranchId";
+        String fromTag = "testFromTag";
+        String callId = "testCallId";
+        String toTag = "testToTag";
+        SipMessage inviteRequest = SipMessageUtils.generateSipRequest(
+                SipMessageUtils.INVITE_SIP_METHOD,
+                BASE_CONTACT_URI_ALICE, BASE_CONTACT_URI_BOB, SIP_URI_BOB, branchId, callId,
+                fromTag, null /*toTag*/);
+        SipDialog dialog = SipDialog.fromSipMessage(inviteRequest);
+        assertNotNull(dialog);
+        dialog.earlyResponse(toTag);
+
+        SipMessage cancelRequest = SipMessageUtils.generateSipRequest(
+                SipMessageUtils.CANCEL_SIP_METHOD, BASE_CONTACT_URI_ALICE, BASE_CONTACT_URI_BOB,
+                SIP_URI_BOB, branchId, callId, fromTag, toTag);
+        assertTrue(dialog.isRequestAssociatedWithDialog(cancelRequest));
+        // cancel request with no toTag should fail
+        cancelRequest = SipMessageUtils.generateSipRequest(
+                SipMessageUtils.INVITE_SIP_METHOD, BASE_CONTACT_URI_ALICE, BASE_CONTACT_URI_BOB,
+                SIP_URI_BOB, branchId, callId, fromTag, null /*toTag*/);
+        assertFalse(dialog.isRequestAssociatedWithDialog(cancelRequest));
+        // cancel request to a different dialog in the same session should fail
+        String toTag2 = "testToTag2";
+        cancelRequest = SipMessageUtils.generateSipRequest(
+                SipMessageUtils.INVITE_SIP_METHOD, BASE_CONTACT_URI_ALICE, BASE_CONTACT_URI_BOB,
+                SIP_URI_BOB, branchId, callId, fromTag, toTag2);
+        assertFalse(dialog.isRequestAssociatedWithDialog(cancelRequest));
+        // cancel request to a different session should fail (even with the same from/to)
+        String callId2 = "testCallId2";
+        cancelRequest = SipMessageUtils.generateSipRequest(
+                SipMessageUtils.INVITE_SIP_METHOD, BASE_CONTACT_URI_ALICE, BASE_CONTACT_URI_BOB,
+                SIP_URI_BOB, branchId, callId2, fromTag, toTag);
+        assertFalse(dialog.isRequestAssociatedWithDialog(cancelRequest));
+        // Same call id but different from and to (although not really possible) should fail.
+        String fromTag3 = "testFromTag3";
+        String toTag3 = "testToTag3";
+        cancelRequest = SipMessageUtils.generateSipRequest(
+                SipMessageUtils.INVITE_SIP_METHOD, BASE_CONTACT_URI_ALICE, BASE_CONTACT_URI_BOB,
+                SIP_URI_BOB, branchId, callId, fromTag3, toTag3);
+        assertFalse(dialog.isRequestAssociatedWithDialog(cancelRequest));
+    }
+
+    @Test
+    public void testCloseDialog() {
+        String branchId = "testBranchId";
+        String fromTag = "abcd";
+        String callId = "testCallId";
+        SipMessage inviteRequest = SipMessageUtils.generateSipRequest(
+                SipMessageUtils.INVITE_SIP_METHOD,
+                BASE_CONTACT_URI_ALICE, BASE_CONTACT_URI_BOB, SIP_URI_BOB, branchId, callId,
+                fromTag, null /*toTag*/);
+        SipDialog dialog = SipDialog.fromSipMessage(inviteRequest);
+        assertNotNull(dialog);
+        assertEquals(SipDialog.STATE_EARLY, dialog.getState());
+        assertEquals(callId, dialog.getCallId());
+
+        // receive a confirm response
+        dialog.confirm("testToTag");
+        assertEquals(SipDialog.STATE_CONFIRMED, dialog.getState());
+
+        dialog.close();
+        assertEquals(SipDialog.STATE_CLOSED, dialog.getState());
+    }
+}
diff --git a/tests/src/com/android/services/telephony/rcs/SipMessageParsingUtilsTest.java b/tests/src/com/android/services/telephony/rcs/SipMessageParsingUtilsTest.java
new file mode 100644
index 0000000..ff96eb6
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/SipMessageParsingUtilsTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021 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 junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+
+import android.util.ArraySet;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.telephony.SipMessageParsingUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+public class SipMessageParsingUtilsTest {
+
+    @Test
+    public void testNoAcceptContactHeader() {
+        String header = "Via: SIP/2.0/UDP ex.place.com;branch=z9hG4bK776asdhds";
+        assertTrue(SipMessageParsingUtils.getAcceptContactFeatureTags(header).isEmpty());
+    }
+
+    @Test
+    public void testAcceptContactHeaderMultipleValues() {
+        Set<String> testFeatures = new ArraySet<>(2);
+        // "+a="b,c" should be split into +a="b" and +a="c"
+        testFeatures.add("+a=\"b,c\"");
+        testFeatures.add("+d");
+        testFeatures.add("+e=\"f\"");
+        // These non-feature tags should be filtered out.
+        testFeatures.add("video");
+        testFeatures.add("blah=BLAH");
+        String header = "Via: SIP/2.0/UDP ex.place.com;branch=z9hG4bK776asdhds";
+        header = addFeatures(header, testFeatures);
+        Set<String> features = SipMessageParsingUtils.getAcceptContactFeatureTags(header);
+        assertNotNull(features);
+        assertEquals(4, features.size());
+        assertTrue(features.containsAll(Arrays.asList("+a=\"b\"", "+a=\"c\"", "+d", "+e=\"f\"")));
+    }
+
+    private String addFeatures(String headers, Set<String> features) {
+        StringBuilder newHeader = new StringBuilder(headers + "\n");
+        newHeader.append("Accept-Contact:*");
+        for (String feature : features) {
+            newHeader.append(";").append(feature);
+        }
+        return newHeader.toString();
+    }
+}
diff --git a/tests/src/com/android/services/telephony/rcs/SipMessageUtils.java b/tests/src/com/android/services/telephony/rcs/SipMessageUtils.java
new file mode 100644
index 0000000..8ad0f2a
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/SipMessageUtils.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2021 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.telephony.ims.SipMessage;
+
+public class SipMessageUtils {
+
+    public static final String INVITE_SIP_METHOD = "INVITE";
+    public static final String CANCEL_SIP_METHOD = "CANCEL";
+    public static final String BYE_SIP_METHOD = "BYE";
+    public static final String ACK_SIP_METHOD = "ACK";
+    public static final String BASE_ADDRESS = "client.example.com";
+
+    private static final String PARAM_SEPARATOR = ";";
+    private static final String PARAM_KEY_VALUE_SEPARATOR = "=";
+    private static final String BASE_VIA_HEADER_VALUE = "SIP/2.0/TCP " + BASE_ADDRESS + ":5060";
+
+    /**
+     * @return A new SIP request from the given parameters.
+     */
+    public static SipMessage generateSipRequest(String requestMethod, String fromContact,
+            String toContact, String toUri, String branchId, String callId, String fromTag,
+            String toTag) {
+        String header = "Via: " + addParamToHeader(BASE_VIA_HEADER_VALUE, "branch", branchId);
+        header += "\n";
+        header += "From: " + addParamToHeader(fromContact, "tag", fromTag);
+        header += "\n";
+        // To tag is optional
+        header += "To: " + ((toTag != null)
+                ? addParamToHeader(toContact, "tag", toTag) : toContact);
+        header += "\n";
+        header += "Call-ID: " + callId;
+        return new SipMessage(requestMethod + " " + toUri + " SIP/2.0", header, new byte[0]);
+    }
+
+    /**
+     * @return Generates a SIP response.
+     */
+    public static SipMessage generateSipResponse(String statusCode, String statusString,
+            String fromContact, String toContact, String branchId, String callId, String fromTag,
+            String toTag) {
+        String header = "Via: " + addParamToHeader(BASE_VIA_HEADER_VALUE, "branch", branchId);
+        header += "\n";
+        header += "From: " + addParamToHeader(fromContact, "tag", fromTag);
+        header += "\n";
+        // To tag is optional
+        header += "To: " + ((toTag != null)
+                ? addParamToHeader(toContact, "tag", toTag) : toContact);
+        header += "\n";
+        header += "Call-ID: " + callId;
+        return new SipMessage("SIP/2.0 " + statusCode + " " + statusString, header,
+                new byte[0]);
+    }
+
+    private static String addParamToHeader(String headerValue, String paramKey, String paramValue) {
+        headerValue += PARAM_SEPARATOR + paramKey.trim() + PARAM_KEY_VALUE_SEPARATOR
+                + paramValue.trim();
+        return headerValue;
+    }
+}
diff --git a/tests/src/com/android/services/telephony/rcs/SipSessionTrackerTest.java b/tests/src/com/android/services/telephony/rcs/SipSessionTrackerTest.java
new file mode 100644
index 0000000..823a8be
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/SipSessionTrackerTest.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2021 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 junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+
+import android.net.Uri;
+import android.telephony.ims.SipMessage;
+import android.util.Base64;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@RunWith(AndroidJUnit4.class)
+public class SipSessionTrackerTest {
+
+    private class DialogAttributes {
+        public final String branchId;
+        public final String callId;
+        public final String fromHeader;
+        public final String fromTag;
+        public final String toUri;
+        public final String toHeader;
+        private final String mFromUri;
+        // This may be populated later.
+        public String toTag;
+
+        DialogAttributes() {
+            branchId = getNextString();
+            callId = getNextString();
+            mFromUri = generateRandomSipUri();
+            fromHeader = generateContactUri(mFromUri);
+            fromTag = getNextString();
+            toUri = generateRandomSipUri();
+            toHeader = generateContactUri(toUri);
+        }
+
+        private DialogAttributes(String branchId, String callId, String fromUri,
+                String fromTag, String toUri, String toTag) {
+            this.branchId = branchId;
+            this.callId = callId;
+            this.mFromUri = fromUri;
+            this.fromHeader = generateContactUri(fromUri);
+            this.fromTag = fromTag;
+            this.toUri = toUri;
+            this.toHeader = generateContactUri(toUri);
+            this.toTag = toTag;
+        }
+
+        public void setToTag() {
+            if (toTag == null) {
+                toTag = getNextString();
+            }
+        }
+
+        public DialogAttributes fromExisting() {
+            return new DialogAttributes(branchId, callId, mFromUri, fromTag, toUri, null);
+        }
+
+        public DialogAttributes invertFromTo() {
+            return new DialogAttributes(branchId, callId, toUri, fromTag, mFromUri, toTag);
+        }
+    }
+
+    // Keep track of the string entry so we can generate unique strings.
+    private int mStringEntryCounter = 0;
+    private SipSessionTracker mTrackerUT;
+
+    @Before
+    public void setUp() {
+        mStringEntryCounter = 0;
+        mTrackerUT = new SipSessionTracker();
+    }
+
+    @Test
+    public void testEarlyDialogToConfirmed() {
+        DialogAttributes attr = new DialogAttributes();
+        // INVITE A -> B
+        SipMessage inviteRequest = generateSipRequest(SipMessageUtils.INVITE_SIP_METHOD, attr);
+        filterMessage(inviteRequest, attr);
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getEarlyDialogs(), attr);
+        // 100 TRYING A <- proxy
+        SipMessage inviteTrying = generateSipResponse("100", "Trying", attr);
+        filterMessage(inviteTrying, attr);
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getEarlyDialogs(), attr);
+        // INVITE proxy -> B
+        // (BOB generates To tag)
+        attr.setToTag();
+        // 180 RINGING proxy <- B
+        // 180 RINGING A <- proxy
+        SipMessage inviteRinging = generateSipResponse("180", "Ringing", attr);
+        filterMessage(inviteRinging, attr);
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getEarlyDialogs(), attr);
+        // User answers phone
+        // 200 OK proxy <- B
+        // 200 OK A <- proxy
+        SipMessage inviteConfirm = generateSipResponse("200", "OK", attr);
+        filterMessage(inviteConfirm, attr);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getConfirmedDialogs(), attr);
+    }
+
+    @Test
+    public void testForkDialog() {
+        DialogAttributes attrB1 = new DialogAttributes();
+        // INVITE A -> B
+        SipMessage inviteRequest = generateSipRequest(SipMessageUtils.INVITE_SIP_METHOD, attrB1);
+        filterMessage(inviteRequest, attrB1);
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getEarlyDialogs(), attrB1);
+        // INVITE proxy -> B
+        // (BOB generates To tag)
+        attrB1.setToTag();
+        // 180 RINGING proxy <- B1
+        // 180 RINGING A <- proxy
+        SipMessage inviteRingingB1 = generateSipResponse("180", "Ringing", attrB1);
+        filterMessage(inviteRingingB1, attrB1);
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getEarlyDialogs(), attrB1);
+        // Now get another RINGING indication from another device associated with the same user.
+        // 180 RINGING proxy <- B2
+        // 180 RINGING A <- proxy
+        DialogAttributes attrB2 = attrB1.fromExisting();
+        // set different To tag
+        attrB2.setToTag();
+        SipMessage inviteRingingB2 = generateSipResponse("180", "Ringing", attrB2);
+        filterMessage(inviteRingingB2, attrB2);
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getEarlyDialogs(), attrB1, attrB2);
+        // User answers B1
+        // 200 OK proxy <- B1
+        // 200 OK A <- proxy
+        SipMessage inviteConfirm = generateSipResponse("200", "OK", attrB1);
+        filterMessage(inviteConfirm, attrB1);
+        verifyContainsCallIds(mTrackerUT.getEarlyDialogs(), attrB2);
+        verifyContainsCallIds(mTrackerUT.getConfirmedDialogs(), attrB1);
+        // Receive indication that B2 is terminated because user answered on B1
+        // 487 A <- proxy
+        SipMessage terminatedResponse = generateSipResponse("487",
+                "Request Terminated", attrB2);
+        filterMessage(terminatedResponse, attrB2);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getConfirmedDialogs(), attrB1);
+        verifyContainsCallIds(mTrackerUT.getClosedDialogs(), attrB2);
+        SipMessage byeRequest = generateSipRequest(SipMessageUtils.BYE_SIP_METHOD, attrB1);
+        // Send BYE request for the open dialog.
+        filterMessage(byeRequest, attrB1);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getClosedDialogs(), attrB1, attrB2);
+        // Clean up the session and ensure the close dialog is completely removed from the tracker.
+        mTrackerUT.cleanupSession(attrB1.callId);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        assertTrue(mTrackerUT.getClosedDialogs().isEmpty());
+    }
+
+    @Test
+    public void testCloseLocalDialog() {
+        DialogAttributes attr = new DialogAttributes();
+        attr.setToTag();
+        createConfirmedDialog(attr);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getConfirmedDialogs(), attr);
+
+        // Send BYE request for a dialog that was started locally and ensure that we see the call id
+        // move to the closed list.
+        SipMessage byeRequest = generateSipRequest(SipMessageUtils.BYE_SIP_METHOD, attr);
+        filterMessage(byeRequest, attr);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getClosedDialogs(), attr);
+        // Clean up the session and ensure the close dialog is completely removed from the tracker.
+        mTrackerUT.cleanupSession(attr.callId);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        assertTrue(mTrackerUT.getClosedDialogs().isEmpty());
+    }
+
+    @Test
+    public void testAcceptContactFts() {
+        DialogAttributes attr = new DialogAttributes();
+        attr.setToTag();
+        SipMessage inviteRequest = generateSipRequest(
+                SipMessageUtils.INVITE_SIP_METHOD,
+                attr);
+        // add accept contact header
+        inviteRequest = new SipMessage(inviteRequest.getStartLine(),
+                inviteRequest.getHeaderSection() + "\nAccept-Contact:*;+test",
+                new byte[0]);
+        filterMessage(inviteRequest, attr);
+        assertTrue(mTrackerUT.getCallIdsAssociatedWithFeatureTag(Collections.singleton("+test"))
+                .contains(attr.callId));
+    }
+
+    @Test
+    public void testCloseRemoteDialog() {
+        DialogAttributes remoteAttr = new DialogAttributes();
+        remoteAttr.setToTag();
+        createConfirmedDialog(remoteAttr);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getConfirmedDialogs(), remoteAttr);
+
+        // Send BYE request on a dialog that was started from the remote party.
+        DialogAttributes localAttr = remoteAttr.invertFromTo();
+        SipMessage byeRequest = generateSipRequest(SipMessageUtils.BYE_SIP_METHOD, localAttr);
+        filterMessage(byeRequest, localAttr);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getClosedDialogs(), remoteAttr);
+        // Clean up the session and ensure the dialog is completely removed from the tracker.
+        mTrackerUT.cleanupSession(remoteAttr.callId);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        assertTrue(mTrackerUT.getClosedDialogs().isEmpty());
+    }
+
+    @Test
+    public void testCleanupConfirmedDialog() {
+        DialogAttributes attr = new DialogAttributes();
+        attr.setToTag();
+        createConfirmedDialog(attr);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getConfirmedDialogs(), attr);
+        // Clean up the session and ensure the dialog is completely removed from the tracker.
+        mTrackerUT.cleanupSession(attr.callId);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        assertTrue(mTrackerUT.getClosedDialogs().isEmpty());
+    }
+
+    @Test
+    public void testMultipleDialogs() {
+        DialogAttributes attr1 = new DialogAttributes();
+        createConfirmedDialog(attr1);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getConfirmedDialogs(), attr1);
+        // add a second dialog
+        DialogAttributes attr2 = new DialogAttributes();
+        createConfirmedDialog(attr2);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getConfirmedDialogs(), attr1, attr2);
+        // Send BYE request on dialogs
+        SipMessage byeRequest = generateSipRequest(SipMessageUtils.BYE_SIP_METHOD, attr1);
+        filterMessage(byeRequest, attr1);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getConfirmedDialogs(), attr2);
+        verifyContainsCallIds(mTrackerUT.getClosedDialogs(), attr1);
+        mTrackerUT.cleanupSession(attr1.callId);
+        // Send BYE request on dialogs
+        byeRequest = generateSipRequest(SipMessageUtils.BYE_SIP_METHOD, attr2);
+        filterMessage(byeRequest, attr2);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getClosedDialogs(), attr2);
+        mTrackerUT.cleanupSession(attr2.callId);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        assertTrue(mTrackerUT.getClosedDialogs().isEmpty());
+    }
+
+    @Test
+    public void testAcknowledgeMessageFailed() {
+        DialogAttributes attr = new DialogAttributes();
+        SipMessage inviteRequest = generateSipRequest(SipMessageUtils.INVITE_SIP_METHOD, attr);
+        mTrackerUT.filterSipMessage(inviteRequest);
+        // Do not acknowledge the request and ensure that the operation has not been applied yet.
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        // send message ack failed event, the operation shouldn't have been applied
+        mTrackerUT.pendingMessageFailed(attr.branchId);
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+    }
+
+    @Test
+    public void testAcknowledgeBatchEvents() {
+        DialogAttributes attr = new DialogAttributes();
+        SipMessage inviteRequest = generateSipRequest(SipMessageUtils.INVITE_SIP_METHOD, attr);
+        attr.setToTag();
+        SipMessage inviteConfirm = generateSipResponse("200", "OK", attr);
+        // We unexpectedly received two filter requests for the same branchId without
+        // acknowledgePendingMessage being called in between. Ensure that when it is called, it
+        // applies both operations.
+        mTrackerUT.filterSipMessage(inviteRequest);
+        mTrackerUT.filterSipMessage(inviteConfirm);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        assertTrue(mTrackerUT.getConfirmedDialogs().isEmpty());
+        // we should skip right to confirmed as both operations run back-to-back
+        mTrackerUT.acknowledgePendingMessage(attr.branchId);
+        assertTrue(mTrackerUT.getEarlyDialogs().isEmpty());
+        verifyContainsCallIds(mTrackerUT.getConfirmedDialogs(), attr);
+    }
+
+    private void filterMessage(SipMessage m, DialogAttributes attr) {
+        mTrackerUT.filterSipMessage(m);
+        mTrackerUT.acknowledgePendingMessage(attr.branchId);
+    }
+    private void verifyContainsCallIds(Set<SipDialog> callIdSet, DialogAttributes... attrs) {
+        Set<String> callIds = Arrays.stream(attrs).map(a -> a.callId).collect(
+                Collectors.toSet());
+        assertTrue(callIdSet.stream().map(SipDialog::getCallId).collect(Collectors.toSet())
+                .containsAll(callIds));
+    }
+
+    private SipMessage generateSipRequest(String requestMethod,
+            DialogAttributes attr) {
+        return SipMessageUtils.generateSipRequest(requestMethod, attr.fromHeader, attr.toHeader,
+                attr.toUri, attr.branchId, attr.callId, attr.fromTag, attr.toTag);
+    }
+    private SipMessage generateSipResponse(String statusCode, String statusString,
+            DialogAttributes attr) {
+        return SipMessageUtils.generateSipResponse(statusCode, statusString, attr.fromHeader,
+                attr.toHeader, attr.branchId, attr.callId, attr.fromTag, attr.toTag);
+    }
+
+    private String generateContactUri(String sipUri) {
+        Uri uri = Uri.parse(sipUri);
+        assertNotNull(uri);
+        String[] user = uri.getSchemeSpecificPart().split("@", 2);
+        assertNotNull(user);
+        assertEquals(2, user.length);
+        return user[0] + " <" + sipUri + ">";
+    }
+
+    private String generateRandomSipUri() {
+        return "sip:" + getNextString() + "@" + SipMessageUtils.BASE_ADDRESS;
+    }
+
+    private void createConfirmedDialog(DialogAttributes attr) {
+        // INVITE ALICE -> BOB
+        SipMessage inviteRequest = generateSipRequest(
+                SipMessageUtils.INVITE_SIP_METHOD,
+                attr);
+        filterMessage(inviteRequest, attr);
+        attr.setToTag();
+        // skip to confirmed state for test.
+        SipMessage inviteConfirm = generateSipResponse("200", "OK",
+                attr);
+        filterMessage(inviteConfirm, attr);
+    }
+
+    private String getNextString() {
+        // Get a string representation of the entry counter
+        byte[] idByteArray = ByteBuffer.allocate(4).putInt(mStringEntryCounter++).array();
+        return Base64.encodeToString(idByteArray,
+                Base64.NO_WRAP | Base64.NO_PADDING | Base64.URL_SAFE);
+    }
+}
diff --git a/tests/src/com/android/services/telephony/rcs/SipTransportControllerTest.java b/tests/src/com/android/services/telephony/rcs/SipTransportControllerTest.java
index fa27775..0f139f8 100644
--- a/tests/src/com/android/services/telephony/rcs/SipTransportControllerTest.java
+++ b/tests/src/com/android/services/telephony/rcs/SipTransportControllerTest.java
@@ -35,7 +35,9 @@
 
 import android.app.role.RoleManager;
 import android.os.IBinder;
+import android.os.PersistableBundle;
 import android.os.UserHandle;
+import android.telephony.CarrierConfigManager;
 import android.telephony.ims.DelegateRequest;
 import android.telephony.ims.FeatureTagState;
 import android.telephony.ims.ImsException;
@@ -54,11 +56,13 @@
 import com.android.TelephonyTestBase;
 import com.android.TestExecutorService;
 import com.android.ims.RcsFeatureManager;
+import com.android.phone.RcsProvisioningMonitor;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
 import java.util.ArrayList;
@@ -84,17 +88,25 @@
         public final String packageName;
         public final DelegateRequest delegateRequest;
         public final SipDelegateController delegateController;
-        public final ISipDelegate mMockDelegate;
-        public final IBinder mMockDelegateBinder;
+        private final ISipDelegate mMockDelegate;
+        public final IBinder mockDelegateBinder;
+        public final ISipDelegateConnectionStateCallback mockDelegateConnectionCallback;
+        public final ISipDelegateMessageCallback mockMessageCallback;
+        public final IBinder mockMessageCallbackBinder;
 
         SipDelegateControllerContainer(int id, String name, DelegateRequest request) {
             delegateController = mock(SipDelegateController.class);
             mMockDelegate = mock(ISipDelegate.class);
-            mMockDelegateBinder = mock(IBinder.class);
-            doReturn(mMockDelegateBinder).when(mMockDelegate).asBinder();
+            mockDelegateBinder = mock(IBinder.class);
+            doReturn(mockDelegateBinder).when(mMockDelegate).asBinder();
+            mockDelegateConnectionCallback = mock(ISipDelegateConnectionStateCallback.class);
+            mockMessageCallback = mock(ISipDelegateMessageCallback.class);
+            mockMessageCallbackBinder = mock(IBinder.class);
+            doReturn(mockMessageCallbackBinder).when(mockMessageCallback).asBinder();
             doReturn(name).when(delegateController).getPackageName();
             doReturn(request).when(delegateController).getInitialRequest();
             doReturn(mMockDelegate).when(delegateController).getSipDelegateInterface();
+            doReturn(mockMessageCallback).when(delegateController).getAppMessageCallback();
             subId = id;
             packageName = name;
             delegateRequest = request;
@@ -104,8 +116,6 @@
     @Mock private RcsFeatureManager mRcsManager;
     @Mock private ISipTransport mSipTransport;
     @Mock private IImsRegistration mImsRegistration;
-    @Mock private ISipDelegateConnectionStateCallback mMockStateCallback;
-    @Mock private ISipDelegateMessageCallback mMockMessageCallback;
     @Mock private SipTransportController.SipDelegateControllerFactory
             mMockDelegateControllerFactory;
     @Mock private SipTransportController.RoleManagerAdapter mMockRoleManager;
@@ -129,6 +139,9 @@
             return c;
         }).when(mMockDelegateControllerFactory).create(anyInt(), any(), anyString(), any(), any(),
                 any(), any(), any());
+        setFeatureAllowedConfig(TEST_SUB_ID, new String[]{ImsSignallingUtils.MMTEL_TAG,
+                ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG, ImsSignallingUtils.GROUP_CHAT_TAG,
+                ImsSignallingUtils.FILE_TRANSFER_HTTP_TAG});
     }
 
     @After
@@ -138,6 +151,7 @@
         if (!isShutdown) {
             mExecutorService.shutdownNow();
         }
+        RcsProvisioningMonitor.getInstance().overrideImsFeatureValidation(TEST_SUB_ID, null);
     }
 
     @SmallTest
@@ -242,7 +256,8 @@
         try {
             controller.createSipDelegate(TEST_SUB_ID + 1,
                     new DelegateRequest(Collections.emptySet()), TEST_PACKAGE_NAME,
-                    mMockStateCallback, mMockMessageCallback);
+                    mock(ISipDelegateConnectionStateCallback.class),
+                    mock(ISipDelegateMessageCallback.class));
             fail();
         } catch (ImsException e) {
             assertEquals(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION, e.getCode());
@@ -258,7 +273,8 @@
         try {
             controller.createSipDelegate(TEST_SUB_ID,
                     new DelegateRequest(Collections.emptySet()), TEST_PACKAGE_NAME,
-                    mMockStateCallback, mMockMessageCallback);
+                    mock(ISipDelegateConnectionStateCallback.class),
+                    mock(ISipDelegateMessageCallback.class));
             fail();
         } catch (ImsException e) {
             assertEquals(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION, e.getCode());
@@ -275,7 +291,8 @@
         try {
             controller.createSipDelegate(TEST_SUB_ID,
                     new DelegateRequest(Collections.emptySet()), TEST_PACKAGE_NAME,
-                    mMockStateCallback, mMockMessageCallback);
+                    mock(ISipDelegateConnectionStateCallback.class),
+                    mock(ISipDelegateMessageCallback.class));
             fail();
         } catch (ImsException e) {
             assertEquals(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE, e.getCode());
@@ -289,9 +306,8 @@
 
         DelegateRequest r = getBaseDelegateRequest();
 
-        SipDelegateController c = injectMockDelegateController(TEST_PACKAGE_NAME, r);
-        createDelegateAndVerify(controller, c, r, r.getFeatureTags(), Collections.emptySet(),
-                TEST_PACKAGE_NAME);
+        SipDelegateControllerContainer c = injectMockDelegateController(TEST_PACKAGE_NAME, r);
+        createDelegateAndVerify(controller, c, r.getFeatureTags(), Collections.emptySet());
         verifyDelegateRegistrationChangedEvent(1 /*times*/, 0 /*waitMs*/);
         triggerFullNetworkRegistrationAndVerify(controller, c);
     }
@@ -302,19 +318,45 @@
         SipTransportController controller = setupLiveTransportController();
 
         DelegateRequest r = getBaseDelegateRequest();
-        SipDelegateController c = injectMockDelegateController(TEST_PACKAGE_NAME, r);
-        createDelegateAndVerify(controller, c, r, r.getFeatureTags(), Collections.emptySet(),
-                TEST_PACKAGE_NAME);
+        SipDelegateControllerContainer c = injectMockDelegateController(TEST_PACKAGE_NAME, r);
+        createDelegateAndVerify(controller, c, r.getFeatureTags(), Collections.emptySet());
+        verify(c.mockMessageCallbackBinder).linkToDeath(any(), anyInt());
         verifyDelegateRegistrationChangedEvent(1, 0 /*throttle*/);
 
         destroyDelegateAndVerify(controller, c, false,
                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        verify(c.mockMessageCallbackBinder).unlinkToDeath(any(), anyInt());
         verifyDelegateRegistrationChangedEvent(2 /*times*/, 0 /*waitMs*/);
         triggerFullNetworkRegistrationAndVerifyNever(controller, c);
     }
 
     @SmallTest
     @Test
+    public void createDestroyAppDied() throws Exception {
+        SipTransportController controller = setupLiveTransportController();
+        DelegateRequest r = getBaseDelegateRequest();
+        SipDelegateControllerContainer c = injectMockDelegateController(TEST_PACKAGE_NAME, r);
+        createDelegateAndVerify(controller, c, r.getFeatureTags(), Collections.emptySet());
+        ArgumentCaptor<IBinder.DeathRecipient> captor =
+                ArgumentCaptor.forClass(IBinder.DeathRecipient.class);
+        verify(c.mockMessageCallbackBinder).linkToDeath(captor.capture(), anyInt());
+        IBinder.DeathRecipient deathRecipient = captor.getValue();
+        assertNotNull(deathRecipient);
+        verifyDelegateRegistrationChangedEvent(1, 0 /*throttle*/);
+
+        CompletableFuture<Integer> pendingDestroy = setDestroyFuture(c.delegateController, true,
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD);
+        // Simulate app dying
+        deathRecipient.binderDied();
+        waitForExecutorAction(mExecutorService, TIMEOUT_MS);
+        verifyDestroyDelegate(c.delegateController, pendingDestroy, true,
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD);
+        // If binderDied is called, then unregister does not lead to unlinkToDeath
+        triggerFullNetworkRegistrationAndVerifyNever(controller, c);
+    }
+
+    @SmallTest
+    @Test
     public void testCreateButNotInRole() throws Exception {
         SipTransportController controller = setupLiveTransportController();
 
@@ -323,9 +365,8 @@
                 SipDelegateManager.DENIED_REASON_NOT_ALLOWED);
 
         // Try to create a SipDelegate for a package that is not the default sms role.
-        SipDelegateController c = injectMockDelegateController(TEST_PACKAGE_NAME_2, r);
-        createDelegateAndVerify(controller, c, r, Collections.emptySet(), getDeniedTags,
-                TEST_PACKAGE_NAME_2);
+        SipDelegateControllerContainer c = injectMockDelegateController(TEST_PACKAGE_NAME_2, r);
+        createDelegateAndVerify(controller, c, Collections.emptySet(), getDeniedTags);
     }
 
     @SmallTest
@@ -338,10 +379,9 @@
         ArraySet<String> firstDelegate = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
         firstDelegate.remove(ImsSignallingUtils.GROUP_CHAT_TAG);
         DelegateRequest firstDelegateRequest = new DelegateRequest(firstDelegate);
-        SipDelegateController c1 = injectMockDelegateController(TEST_PACKAGE_NAME,
+        SipDelegateControllerContainer c1 = injectMockDelegateController(TEST_PACKAGE_NAME,
                 firstDelegateRequest);
-        createDelegateAndVerify(controller, c1, firstDelegateRequest, firstDelegate,
-                Collections.emptySet(), TEST_PACKAGE_NAME);
+        createDelegateAndVerify(controller, c1, firstDelegate, Collections.emptySet());
         // there is a delay in the indication to update reg, so it should not happen yet.
         verifyNoDelegateRegistrationChangedEvent();
 
@@ -353,10 +393,10 @@
         Pair<Set<String>, Set<FeatureTagState>> grantedAndDenied = getAllowedAndDeniedTagsForConfig(
                 secondDelegateRequest, SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE,
                 firstDelegate);
-        SipDelegateController c2 = injectMockDelegateController(TEST_PACKAGE_NAME,
+        SipDelegateControllerContainer c2 = injectMockDelegateController(TEST_PACKAGE_NAME,
                 secondDelegateRequest);
-        createDelegateAndVerify(controller, c2, secondDelegateRequest, grantedAndDenied.first,
-                grantedAndDenied.second, TEST_PACKAGE_NAME, 1);
+        createDelegateAndVerify(controller, c2, grantedAndDenied.first,
+                grantedAndDenied.second, 1);
         // a reg changed event should happen after wait.
         verifyDelegateRegistrationChangedEvent(1, 2 * THROTTLE_MS);
     }
@@ -370,10 +410,10 @@
         Set<FeatureTagState> firstDeniedTags = getDeniedTagsForReason(
                 firstDelegateRequest.getFeatureTags(),
                 SipDelegateManager.DENIED_REASON_NOT_ALLOWED);
-        SipDelegateController c1 = injectMockDelegateController(TEST_PACKAGE_NAME,
+        SipDelegateControllerContainer c1 = injectMockDelegateController(TEST_PACKAGE_NAME,
                 firstDelegateRequest);
-        createDelegateAndVerify(controller, c1, firstDelegateRequest,
-                firstDelegateRequest.getFeatureTags(), Collections.emptySet(), TEST_PACKAGE_NAME);
+        createDelegateAndVerify(controller, c1, firstDelegateRequest.getFeatureTags(),
+                Collections.emptySet());
         verifyDelegateRegistrationChangedEvent(1 /*times*/, THROTTLE_MS);
 
         DelegateRequest secondDelegateRequest = getBaseDelegateRequest();
@@ -381,16 +421,16 @@
                 secondDelegateRequest.getFeatureTags(),
                 SipDelegateManager.DENIED_REASON_NOT_ALLOWED);
         // Try to create a SipDelegate for a package that is not the default sms role.
-        SipDelegateController c2 = injectMockDelegateController(TEST_PACKAGE_NAME_2,
+        SipDelegateControllerContainer c2 = injectMockDelegateController(TEST_PACKAGE_NAME_2,
                 secondDelegateRequest);
-        createDelegateAndVerify(controller, c2, secondDelegateRequest, Collections.emptySet(),
-                secondDeniedTags, TEST_PACKAGE_NAME_2, 1);
+        createDelegateAndVerify(controller, c2, Collections.emptySet(), secondDeniedTags, 1);
 
         // now swap the SMS role.
-        CompletableFuture<Boolean> pendingC1Change = setChangeSupportedFeatureTagsFuture(c1,
-                Collections.emptySet(), firstDeniedTags);
-        CompletableFuture<Boolean> pendingC2Change = setChangeSupportedFeatureTagsFuture(c2,
-                secondDelegateRequest.getFeatureTags(), Collections.emptySet());
+        CompletableFuture<Boolean> pendingC1Change = setChangeSupportedFeatureTagsFuture(
+                c1.delegateController, Collections.emptySet(), firstDeniedTags);
+        CompletableFuture<Boolean> pendingC2Change = setChangeSupportedFeatureTagsFuture(
+                c2.delegateController, secondDelegateRequest.getFeatureTags(),
+                Collections.emptySet());
         setSmsRoleAndEvaluate(controller, TEST_PACKAGE_NAME_2);
         // swapping roles should trigger a deregistration event on the ImsService side.
         verifyDelegateDeregistrationEvent();
@@ -398,17 +438,18 @@
         verifyDelegateRegistrationChangedEvent(1 /*times*/, THROTTLE_MS);
         // trigger completion stage to run
         waitForExecutorAction(mExecutorService, TIMEOUT_MS);
-        verify(c1).changeSupportedFeatureTags(Collections.emptySet(), firstDeniedTags);
+        verify(c1.delegateController).changeSupportedFeatureTags(Collections.emptySet(),
+                firstDeniedTags);
         // we should not get a change for c2 until pendingC1Change completes.
-        verify(c2, never()).changeSupportedFeatureTags(secondDelegateRequest.getFeatureTags(),
-                Collections.emptySet());
+        verify(c2.delegateController, never()).changeSupportedFeatureTags(
+                secondDelegateRequest.getFeatureTags(), Collections.emptySet());
         // ensure we are not blocking executor here
         waitForExecutorAction(mExecutorService, TIMEOUT_MS);
         completePendingChange(pendingC1Change, true);
         // trigger completion stage to run
         waitForExecutorAction(mExecutorService, TIMEOUT_MS);
-        verify(c2).changeSupportedFeatureTags(secondDelegateRequest.getFeatureTags(),
-                Collections.emptySet());
+        verify(c2.delegateController).changeSupportedFeatureTags(
+                secondDelegateRequest.getFeatureTags(), Collections.emptySet());
         // ensure we are not blocking executor here
         waitForExecutorAction(mExecutorService, TIMEOUT_MS);
         completePendingChange(pendingC2Change, true);
@@ -425,10 +466,9 @@
         ArraySet<String> firstDelegate = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
         firstDelegate.remove(ImsSignallingUtils.GROUP_CHAT_TAG);
         DelegateRequest firstDelegateRequest = new DelegateRequest(firstDelegate);
-        SipDelegateController c1 = injectMockDelegateController(TEST_PACKAGE_NAME,
+        SipDelegateControllerContainer c1 = injectMockDelegateController(TEST_PACKAGE_NAME,
                 firstDelegateRequest);
-        createDelegateAndVerify(controller, c1, firstDelegateRequest, firstDelegate,
-                Collections.emptySet(), TEST_PACKAGE_NAME);
+        createDelegateAndVerify(controller, c1, firstDelegate, Collections.emptySet());
         verifyNoDelegateRegistrationChangedEvent();
 
         // First delegate requests RCS message + Group RCS message. For this delegate, single RCS
@@ -439,21 +479,21 @@
         Pair<Set<String>, Set<FeatureTagState>> grantedAndDenied = getAllowedAndDeniedTagsForConfig(
                 secondDelegateRequest, SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE,
                 firstDelegate);
-        SipDelegateController c2 = injectMockDelegateController(TEST_PACKAGE_NAME,
+        SipDelegateControllerContainer c2 = injectMockDelegateController(TEST_PACKAGE_NAME,
                 secondDelegateRequest);
-        createDelegateAndVerify(controller, c2, secondDelegateRequest, grantedAndDenied.first,
-                grantedAndDenied.second, TEST_PACKAGE_NAME, 1);
+        createDelegateAndVerify(controller, c2, grantedAndDenied.first, grantedAndDenied.second, 1);
         verifyNoDelegateRegistrationChangedEvent();
 
         // Destroy the firstDelegate, which should now cause all previously denied tags to be
         // granted to the new delegate.
-        CompletableFuture<Boolean> pendingC2Change = setChangeSupportedFeatureTagsFuture(c2,
-                secondDelegate, Collections.emptySet());
+        CompletableFuture<Boolean> pendingC2Change = setChangeSupportedFeatureTagsFuture(
+                c2.delegateController, secondDelegate, Collections.emptySet());
         destroyDelegateAndVerify(controller, c1, false /*force*/,
                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
         // wait for create to be processed.
         assertTrue(waitForExecutorAction(mExecutorService, TIMEOUT_MS));
-        verify(c2).changeSupportedFeatureTags(secondDelegate, Collections.emptySet());
+        verify(c2.delegateController).changeSupportedFeatureTags(secondDelegate,
+                Collections.emptySet());
         completePendingChange(pendingC2Change, true);
 
         verifyDelegateRegistrationChangedEvent(1 /*times*/, THROTTLE_MS);
@@ -468,10 +508,10 @@
         ArraySet<String> firstDelegate = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
         firstDelegate.remove(ImsSignallingUtils.GROUP_CHAT_TAG);
         DelegateRequest firstDelegateRequest = new DelegateRequest(firstDelegate);
-        SipDelegateController c1 = injectMockDelegateController(TEST_PACKAGE_NAME,
+        SipDelegateControllerContainer c1 = injectMockDelegateController(TEST_PACKAGE_NAME,
                 firstDelegateRequest);
         CompletableFuture<Boolean> pendingC1Change = createDelegate(controller, c1,
-                firstDelegateRequest, firstDelegate, Collections.emptySet(), TEST_PACKAGE_NAME);
+                firstDelegate, Collections.emptySet());
 
         // Request RCS message + group RCS Message. For this delegate, single RCS message should be
         // denied.
@@ -481,11 +521,10 @@
         Pair<Set<String>, Set<FeatureTagState>> grantedAndDeniedC2 =
                 getAllowedAndDeniedTagsForConfig(secondDelegateRequest,
                         SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE, firstDelegate);
-        SipDelegateController c2 = injectMockDelegateController(TEST_PACKAGE_NAME,
+        SipDelegateControllerContainer c2 = injectMockDelegateController(TEST_PACKAGE_NAME,
                 secondDelegateRequest);
         CompletableFuture<Boolean> pendingC2Change = createDelegate(controller, c2,
-                secondDelegateRequest, grantedAndDeniedC2.first, grantedAndDeniedC2.second,
-                TEST_PACKAGE_NAME);
+                grantedAndDeniedC2.first, grantedAndDeniedC2.second);
 
         // Request group RCS message + file transfer. All should be denied at first
         ArraySet<String> thirdDelegate = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
@@ -495,42 +534,44 @@
                 getAllowedAndDeniedTagsForConfig(thirdDelegateRequest,
                         SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE, firstDelegate,
                         grantedAndDeniedC2.first);
-        SipDelegateController c3 = injectMockDelegateController(TEST_PACKAGE_NAME,
+        SipDelegateControllerContainer c3 = injectMockDelegateController(TEST_PACKAGE_NAME,
                 thirdDelegateRequest);
         CompletableFuture<Boolean> pendingC3Change = createDelegate(controller, c3,
-                thirdDelegateRequest, grantedAndDeniedC3.first, grantedAndDeniedC3.second,
-                TEST_PACKAGE_NAME);
+                grantedAndDeniedC3.first, grantedAndDeniedC3.second);
 
         verifyNoDelegateRegistrationChangedEvent();
         assertTrue(scheduleDelayedWait(2 * THROTTLE_MS));
-        verifyDelegateChanged(c1, pendingC1Change, firstDelegate, Collections.emptySet(), 0);
-        verifyDelegateChanged(c2, pendingC2Change, grantedAndDeniedC2.first,
+        verifyDelegateChanged(c1.delegateController, pendingC1Change, firstDelegate,
+                Collections.emptySet(), 0);
+        verifyDelegateChanged(c2.delegateController, pendingC2Change, grantedAndDeniedC2.first,
                 grantedAndDeniedC2.second, 0);
-        verifyDelegateChanged(c3, pendingC3Change, grantedAndDeniedC3.first,
+        verifyDelegateChanged(c3.delegateController, pendingC3Change, grantedAndDeniedC3.first,
                 grantedAndDeniedC3.second, 0);
         verifyDelegateRegistrationChangedEvent(1, 2 * THROTTLE_MS);
 
         // Destroy the first and second controller in quick succession, this should only generate
         // one reevaluate for the third controller.
         CompletableFuture<Boolean> pendingChangeC3 = setChangeSupportedFeatureTagsFuture(
-                c3, thirdDelegate, Collections.emptySet());
-        CompletableFuture<Integer> pendingDestroyC1 = destroyDelegate(controller, c1,
-                false /*force*/,
+                c3.delegateController, thirdDelegate, Collections.emptySet());
+        CompletableFuture<Integer> pendingDestroyC1 = destroyDelegate(controller,
+                c1.delegateController, false /*force*/,
                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
-        CompletableFuture<Integer> pendingDestroyC2 = destroyDelegate(controller, c2,
-                false /*force*/,
+        CompletableFuture<Integer> pendingDestroyC2 = destroyDelegate(controller,
+                c2.delegateController, false /*force*/,
                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
         assertTrue(scheduleDelayedWait(2 * THROTTLE_MS));
-        verifyDestroyDelegate(controller, c1, pendingDestroyC1, false /*force*/,
+        verifyDestroyDelegate(c1.delegateController, pendingDestroyC1, false /*force*/,
                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
-        verifyDestroyDelegate(controller, c2, pendingDestroyC2, false /*force*/,
+        verifyDestroyDelegate(c2.delegateController, pendingDestroyC2, false /*force*/,
                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
 
         // All requested features should now be granted
         completePendingChange(pendingChangeC3, true);
-        verify(c3).changeSupportedFeatureTags(thirdDelegate, Collections.emptySet());
+        verify(c3.delegateController)
+                .changeSupportedFeatureTags(thirdDelegate, Collections.emptySet());
         // In total reeval should have only been called twice.
-        verify(c3, times(2)).changeSupportedFeatureTags(any(), any());
+        verify(c3.delegateController, times(2))
+                .changeSupportedFeatureTags(any(), any());
         verifyDelegateRegistrationChangedEvent(2 /*times*/, 2 * THROTTLE_MS);
     }
 
@@ -541,17 +582,16 @@
 
         ArraySet<String> firstDelegate = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
         DelegateRequest firstDelegateRequest = new DelegateRequest(firstDelegate);
-        SipDelegateController c1 = injectMockDelegateController(TEST_PACKAGE_NAME,
+        SipDelegateControllerContainer c = injectMockDelegateController(TEST_PACKAGE_NAME,
                 firstDelegateRequest);
-        createDelegateAndVerify(controller, c1, firstDelegateRequest, firstDelegate,
-                Collections.emptySet(), TEST_PACKAGE_NAME);
+        createDelegateAndVerify(controller, c, firstDelegate, Collections.emptySet());
         verifyDelegateRegistrationChangedEvent(1 /*times*/, 0 /*waitMs*/);
 
-        CompletableFuture<Integer> pendingDestroy =  setDestroyFuture(c1, true,
+        CompletableFuture<Integer> pendingDestroy =  setDestroyFuture(c.delegateController, true,
                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN);
         controller.onAssociatedSubscriptionUpdated(TEST_SUB_ID + 1);
         waitForExecutorAction(mExecutorService, TIMEOUT_MS);
-        verifyDestroyDelegate(controller, c1, pendingDestroy, true /*force*/,
+        verifyDestroyDelegate(c.delegateController, pendingDestroy, true /*force*/,
                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN);
         verifyDelegateRegistrationChangedEvent(2 /*times*/, 0 /*waitMs*/);
     }
@@ -563,16 +603,15 @@
 
         ArraySet<String> firstDelegate = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
         DelegateRequest firstDelegateRequest = new DelegateRequest(firstDelegate);
-        SipDelegateController c1 = injectMockDelegateController(TEST_PACKAGE_NAME,
+        SipDelegateControllerContainer c = injectMockDelegateController(TEST_PACKAGE_NAME,
                 firstDelegateRequest);
-        createDelegateAndVerify(controller, c1, firstDelegateRequest, firstDelegate,
-                Collections.emptySet(), TEST_PACKAGE_NAME);
+        createDelegateAndVerify(controller, c, firstDelegate, Collections.emptySet());
 
-        CompletableFuture<Integer> pendingDestroy =  setDestroyFuture(c1, true,
+        CompletableFuture<Integer> pendingDestroy =  setDestroyFuture(c.delegateController, true,
                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD);
         controller.onRcsDisconnected();
         waitForExecutorAction(mExecutorService, TIMEOUT_MS);
-        verifyDestroyDelegate(controller, c1, pendingDestroy, true /*force*/,
+        verifyDestroyDelegate(c.delegateController, pendingDestroy, true /*force*/,
                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD);
         verifyDelegateRegistrationChangedEvent(1, 0 /*waitMs*/);
     }
@@ -584,18 +623,17 @@
 
         ArraySet<String> firstDelegate = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
         DelegateRequest firstDelegateRequest = new DelegateRequest(firstDelegate);
-        SipDelegateController c1 = injectMockDelegateController(TEST_PACKAGE_NAME,
+        SipDelegateControllerContainer c = injectMockDelegateController(TEST_PACKAGE_NAME,
                 firstDelegateRequest);
-        createDelegateAndVerify(controller, c1, firstDelegateRequest, firstDelegate,
-                Collections.emptySet(), TEST_PACKAGE_NAME);
+        createDelegateAndVerify(controller, c, firstDelegate, Collections.emptySet());
 
-        CompletableFuture<Integer> pendingDestroy =  setDestroyFuture(c1, true,
-                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN);
+        CompletableFuture<Integer> pendingDestroy =  setDestroyFuture(c.delegateController,
+                true, SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN);
         controller.onDestroy();
         waitForExecutorAction(mExecutorService, TIMEOUT_MS);
         verifyDelegateDeregistrationEvent();
         // verify change was called.
-        verify(c1).destroy(true /*force*/,
+        verify(c.delegateController).destroy(true /*force*/,
                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN);
         // ensure thread is not blocked while waiting for pending complete.
         waitForExecutorAction(mExecutorService, TIMEOUT_MS);
@@ -607,19 +645,23 @@
     @Test
     public void testTimingSubIdChangedAndCreateNewSubId() throws Exception {
         SipTransportController controller = setupLiveTransportController(THROTTLE_MS, 0);
+        setFeatureAllowedConfig(TEST_SUB_ID + 1, new String[]{ImsSignallingUtils.MMTEL_TAG,
+                ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG, ImsSignallingUtils.GROUP_CHAT_TAG,
+                ImsSignallingUtils.FILE_TRANSFER_HTTP_TAG});
 
         ArraySet<String> firstDelegate = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
         DelegateRequest firstDelegateRequest = new DelegateRequest(firstDelegate);
-        SipDelegateController c1 = injectMockDelegateController(TEST_PACKAGE_NAME,
+        SipDelegateControllerContainer c1 = injectMockDelegateController(TEST_PACKAGE_NAME,
                 firstDelegateRequest);
         CompletableFuture<Boolean> pendingC1Change = createDelegate(controller, c1,
-                firstDelegateRequest, firstDelegate, Collections.emptySet(), TEST_PACKAGE_NAME);
+                firstDelegate, Collections.emptySet());
         assertTrue(scheduleDelayedWait(2 * THROTTLE_MS));
-        verifyDelegateChanged(c1, pendingC1Change, firstDelegate, Collections.emptySet(), 0);
+        verifyDelegateChanged(c1.delegateController, pendingC1Change, firstDelegate,
+                Collections.emptySet(), 0);
 
 
-        CompletableFuture<Integer> pendingDestroy =  setDestroyFuture(c1, true,
-                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN);
+        CompletableFuture<Integer> pendingDestroy =  setDestroyFuture(c1.delegateController,
+                true, SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN);
         // triggers reeval now.
         controller.onAssociatedSubscriptionUpdated(TEST_SUB_ID + 1);
         waitForExecutorAction(mExecutorService, TIMEOUT_MS);
@@ -629,18 +671,82 @@
         secondDelegate.add(ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG);
         secondDelegate.add(ImsSignallingUtils.FILE_TRANSFER_HTTP_TAG);
         DelegateRequest secondDelegateRequest = new DelegateRequest(secondDelegate);
-        SipDelegateController c2 = injectMockDelegateController(TEST_SUB_ID + 1,
+        SipDelegateControllerContainer c2 = injectMockDelegateController(TEST_SUB_ID + 1,
                 TEST_PACKAGE_NAME, secondDelegateRequest);
-        CompletableFuture<Boolean> pendingC2Change = createDelegate(controller, c2,
-                TEST_SUB_ID + 1, secondDelegateRequest, secondDelegate,
-                Collections.emptySet(), TEST_PACKAGE_NAME);
+        CompletableFuture<Boolean> pendingC2Change = createDelegate(controller, c2, secondDelegate,
+                Collections.emptySet());
         assertTrue(scheduleDelayedWait(THROTTLE_MS));
 
         //trigger destroyed event
-        verifyDestroyDelegate(controller, c1, pendingDestroy, true /*force*/,
+        verifyDestroyDelegate(c1.delegateController, pendingDestroy, true /*force*/,
                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN);
         assertTrue(scheduleDelayedWait(2 * THROTTLE_MS));
-        verifyDelegateChanged(c2, pendingC2Change, secondDelegate, Collections.emptySet(), 0);
+        verifyDelegateChanged(c2.delegateController, pendingC2Change, secondDelegate,
+                Collections.emptySet(), 0);
+    }
+
+    @SmallTest
+    @Test
+    public void testFeatureTagsDeniedByConfig() throws Exception {
+        setFeatureAllowedConfig(TEST_SUB_ID, new String[]{ImsSignallingUtils.GROUP_CHAT_TAG,
+                ImsSignallingUtils.FILE_TRANSFER_HTTP_TAG});
+        SipTransportController controller = setupLiveTransportController(THROTTLE_MS, 0);
+
+        ArraySet<String> requestTags = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
+        ArraySet<String> allowedTags = new ArraySet<>(requestTags);
+        ArraySet<String> deniedTags = new ArraySet<>();
+        DelegateRequest delegateRequest = new DelegateRequest(requestTags);
+        SipDelegateControllerContainer c = injectMockDelegateController(TEST_PACKAGE_NAME,
+                delegateRequest);
+        CompletableFuture<Boolean> pendingScChange = createDelegate(controller, c, requestTags,
+                Collections.emptySet());
+        allowedTags.remove(ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG);
+        deniedTags.add(ImsSignallingUtils.ONE_TO_ONE_CHAT_TAG);
+
+        assertTrue(scheduleDelayedWait(2 * THROTTLE_MS));
+        verifyDelegateChanged(c.delegateController, pendingScChange, allowedTags,
+                getDeniedTagsForReason(deniedTags,
+                        SipDelegateManager.DENIED_REASON_NOT_ALLOWED), 0);
+    }
+
+    @SmallTest
+    @Test
+    public void testFeatureTagsDeniedByOverride() throws Exception {
+        RcsProvisioningMonitor.getInstance().overrideImsFeatureValidation(TEST_SUB_ID, false);
+        SipTransportController controller = setupLiveTransportController(THROTTLE_MS, 0);
+
+        ArraySet<String> requestTags = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
+        ArraySet<String> deniedTags = new ArraySet<>(requestTags);
+        DelegateRequest delegateRequest = new DelegateRequest(requestTags);
+        SipDelegateControllerContainer c = injectMockDelegateController(TEST_PACKAGE_NAME,
+                delegateRequest);
+        CompletableFuture<Boolean> pendingScChange = createDelegate(controller, c, requestTags,
+                Collections.emptySet());
+
+        assertTrue(scheduleDelayedWait(2 * THROTTLE_MS));
+        verifyDelegateChanged(c.delegateController, pendingScChange, Collections.emptySet(),
+                getDeniedTagsForReason(deniedTags,
+                        SipDelegateManager.DENIED_REASON_NOT_ALLOWED), 0);
+    }
+
+    @SmallTest
+    @Test
+    public void testFeatureTagsDeniedByConfigAllowedByOverride() throws Exception {
+        setFeatureAllowedConfig(TEST_SUB_ID, new String[]{});
+        RcsProvisioningMonitor.getInstance().overrideImsFeatureValidation(TEST_SUB_ID, true);
+        SipTransportController controller = setupLiveTransportController(THROTTLE_MS, 0);
+
+        ArraySet<String> requestTags = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
+        ArraySet<String> allowedTags = new ArraySet<>(requestTags);
+        DelegateRequest delegateRequest = new DelegateRequest(requestTags);
+        SipDelegateControllerContainer controllerContainer =
+                injectMockDelegateController(TEST_PACKAGE_NAME, delegateRequest);
+        CompletableFuture<Boolean> pendingScChange = createDelegate(controller, controllerContainer,
+                requestTags, Collections.emptySet());
+
+        assertTrue(scheduleDelayedWait(2 * THROTTLE_MS));
+        verifyDelegateChanged(controllerContainer.delegateController, pendingScChange, allowedTags,
+                Collections.emptySet(), 0);
     }
 
     @SafeVarargs
@@ -685,31 +791,32 @@
     }
 
     private void createDelegateAndVerify(SipTransportController controller,
-            SipDelegateController delegateController, DelegateRequest r, Set<String> allowedTags,
-            Set<FeatureTagState> deniedTags, String packageName,
-            int numPreviousChanges) throws ImsException {
+            SipDelegateControllerContainer controllerContainer, Set<String> allowedTags,
+            Set<FeatureTagState> deniedTags, int numPreviousChanges) {
 
-        CompletableFuture<Boolean> pendingChange = createDelegate(controller, delegateController, r,
-                allowedTags, deniedTags, packageName);
-        verifyDelegateChanged(delegateController, pendingChange, allowedTags, deniedTags,
-                numPreviousChanges);
+        CompletableFuture<Boolean> pendingChange = createDelegate(controller, controllerContainer,
+                allowedTags, deniedTags);
+        verifyDelegateChanged(controllerContainer.delegateController, pendingChange, allowedTags,
+                deniedTags, numPreviousChanges);
     }
 
     private void createDelegateAndVerify(SipTransportController controller,
-            SipDelegateController delegateController, DelegateRequest r, Set<String> allowedTags,
-            Set<FeatureTagState> deniedTags, String packageName) throws ImsException {
-        createDelegateAndVerify(controller, delegateController, r, allowedTags, deniedTags,
-                packageName, 0);
+            SipDelegateControllerContainer controllerContainer, Set<String> allowedTags,
+            Set<FeatureTagState> deniedTags) {
+        createDelegateAndVerify(controller, controllerContainer, allowedTags, deniedTags, 0);
     }
 
     private CompletableFuture<Boolean> createDelegate(SipTransportController controller,
-            SipDelegateController delegateController, int subId, DelegateRequest r,
-            Set<String> allowedTags, Set<FeatureTagState> deniedTags, String packageName) {
+            SipDelegateControllerContainer delegateControllerContainer, Set<String> allowedTags,
+            Set<FeatureTagState> deniedTags) {
         CompletableFuture<Boolean> pendingChange = setChangeSupportedFeatureTagsFuture(
-                delegateController, allowedTags, deniedTags);
+                delegateControllerContainer.delegateController, allowedTags, deniedTags);
         try {
-            controller.createSipDelegate(subId, r, packageName, mMockStateCallback,
-                    mMockMessageCallback);
+            controller.createSipDelegate(delegateControllerContainer.subId,
+                    delegateControllerContainer.delegateRequest,
+                    delegateControllerContainer.packageName,
+                    delegateControllerContainer.mockDelegateConnectionCallback,
+                    delegateControllerContainer.mockMessageCallback);
         } catch (ImsException e) {
             fail("ImsException thrown:" + e);
         }
@@ -720,13 +827,6 @@
         return pendingChange;
     }
 
-    private CompletableFuture<Boolean> createDelegate(SipTransportController controller,
-            SipDelegateController delegateController, DelegateRequest r, Set<String> allowedTags,
-            Set<FeatureTagState> deniedTags, String packageName) throws ImsException {
-        return createDelegate(controller, delegateController, TEST_SUB_ID, r, allowedTags,
-                deniedTags, packageName);
-    }
-
     private void verifyDelegateChanged(SipDelegateController delegateController,
             CompletableFuture<Boolean> pendingChange, Set<String> allowedTags,
             Set<FeatureTagState> deniedTags, int numPreviousChangeStages) {
@@ -746,10 +846,11 @@
     }
 
     private void destroyDelegateAndVerify(SipTransportController controller,
-            SipDelegateController delegateController, boolean force, int reason) {
+            SipDelegateControllerContainer controllerContainer, boolean force, int reason) {
+        SipDelegateController delegateController = controllerContainer.delegateController;
         CompletableFuture<Integer> pendingDestroy =  destroyDelegate(controller, delegateController,
                 force, reason);
-        verifyDestroyDelegate(controller, delegateController, pendingDestroy, force, reason);
+        verifyDestroyDelegate(delegateController, pendingDestroy, force, reason);
     }
 
     private CompletableFuture<Integer> destroyDelegate(SipTransportController controller,
@@ -765,9 +866,8 @@
         return pendingDestroy;
     }
 
-    private void verifyDestroyDelegate(SipTransportController controller,
-            SipDelegateController delegateController, CompletableFuture<Integer> pendingDestroy,
-            boolean force, int reason) {
+    private void verifyDestroyDelegate(SipDelegateController delegateController,
+            CompletableFuture<Integer> pendingDestroy, boolean force, int reason) {
         // verify destroy was called.
         verify(delegateController).destroy(force, reason);
         // ensure thread is not blocked while waiting for pending complete.
@@ -776,7 +876,8 @@
     }
 
     private void triggerFullNetworkRegistrationAndVerify(SipTransportController controller,
-            SipDelegateController delegateController) {
+            SipDelegateControllerContainer controllerContainer) {
+        SipDelegateController delegateController = controllerContainer.delegateController;
         controller.triggerFullNetworkRegistration(TEST_SUB_ID,
                 delegateController.getSipDelegateInterface(), 403, "forbidden");
         // move to internal & trigger event
@@ -785,7 +886,8 @@
     }
 
     private void triggerFullNetworkRegistrationAndVerifyNever(SipTransportController controller,
-            SipDelegateController delegateController) {
+            SipDelegateControllerContainer controllerContainer) {
+        SipDelegateController delegateController = controllerContainer.delegateController;
         controller.triggerFullNetworkRegistration(TEST_SUB_ID,
                 delegateController.getSipDelegateInterface(), 403, "forbidden");
         // move to internal & potentially trigger event
@@ -801,29 +903,22 @@
         return new DelegateRequest(featureTags);
     }
 
-    private Set<FeatureTagState> getBaseDeniedSet() {
-        Set<FeatureTagState> deniedTags = new ArraySet<>();
-        deniedTags.add(new FeatureTagState(ImsSignallingUtils.MMTEL_TAG,
-                SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE));
-        return deniedTags;
-    }
-
     private Set<FeatureTagState> getDeniedTagsForReason(Set<String> deniedTags, int reason) {
         return deniedTags.stream().map(t -> new FeatureTagState(t, reason))
                 .collect(Collectors.toSet());
     }
 
-    private SipDelegateController injectMockDelegateController(String packageName,
+    private SipDelegateControllerContainer injectMockDelegateController(String packageName,
             DelegateRequest r) {
         return injectMockDelegateController(TEST_SUB_ID, packageName, r);
     }
 
-    private SipDelegateController injectMockDelegateController(int subId, String packageName,
-            DelegateRequest r) {
+    private SipDelegateControllerContainer injectMockDelegateController(int subId,
+            String packageName, DelegateRequest r) {
         SipDelegateControllerContainer c = new SipDelegateControllerContainer(subId,
                 packageName, r);
         mMockControllers.add(c);
-        return c.delegateController;
+        return c;
     }
 
     private SipDelegateController getMockDelegateController(int subId, String packageName,
@@ -910,4 +1005,10 @@
         }
         return true;
     }
+
+    private void setFeatureAllowedConfig(int subId, String[] tags) {
+        PersistableBundle bundle = mContext.getCarrierConfig(subId);
+        bundle.putStringArray(
+                CarrierConfigManager.Ims.KEY_RCS_FEATURE_TAG_ALLOWED_STRING_ARRAY, tags);
+    }
 }
diff --git a/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java b/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
index a1f97a0..39469b6 100644
--- a/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
+++ b/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
@@ -50,6 +50,7 @@
 
     @Captor ArgumentCaptor<BroadcastReceiver> mReceiverCaptor;
     @Mock TelephonyRcsService.FeatureFactory mFeatureFactory;
+    @Mock TelephonyRcsService.ResourceProxy mResourceProxy;
     @Mock UceControllerManager mMockUceSlot0;
     @Mock UceControllerManager mMockUceSlot1;
     @Mock SipTransportController mMockSipTransportSlot0;
@@ -66,10 +67,12 @@
         super.setUp();
         doReturn(mFeatureConnector).when(mFeatureConnectorFactory).create(any(), anyInt(),
                 any(), any(), any());
-        mFeatureControllerSlot0 = createFeatureController(0 /*slotId*/);
-        mFeatureControllerSlot1 = createFeatureController(1 /*slotId*/);
-        doReturn(mFeatureControllerSlot0).when(mFeatureFactory).createController(any(), eq(0));
-        doReturn(mFeatureControllerSlot1).when(mFeatureFactory).createController(any(), eq(1));
+        mFeatureControllerSlot0 = createFeatureController(0 /*slotId*/, 1 /*subId*/);
+        mFeatureControllerSlot1 = createFeatureController(1 /*slotId*/, 2 /*subId*/);
+        doReturn(mFeatureControllerSlot0).when(mFeatureFactory).createController(any(), eq(0),
+                anyInt());
+        doReturn(mFeatureControllerSlot1).when(mFeatureFactory).createController(any(), eq(1),
+                anyInt());
         doReturn(mMockUceSlot0).when(mFeatureFactory).createUceControllerManager(any(), eq(0),
                 anyInt());
         doReturn(mMockUceSlot1).when(mFeatureFactory).createUceControllerManager(any(), eq(1),
@@ -78,6 +81,7 @@
                 eq(0), anyInt());
         doReturn(mMockSipTransportSlot1).when(mFeatureFactory).createSipTransportController(any(),
                 eq(1), anyInt());
+        doReturn(true).when(mResourceProxy).getDeviceUceEnabled(any());
         //set up default slot-> sub ID mappings.
         setSlotToSubIdMapping(0 /*slotId*/, 1/*subId*/);
         setSlotToSubIdMapping(1 /*slotId*/, 2/*subId*/);
@@ -225,7 +229,7 @@
     }
 
     @Test
-    public void testCarrierConfigUpdate() {
+    public void testCarrierConfigUpdateAssociatedSub() {
         setCarrierConfig(1 /*subId*/,
                 CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL,
                 true /*isEnabled*/);
@@ -249,6 +253,26 @@
     }
 
     @Test
+    public void testCarrierConfigNotifyFeatures() {
+        setCarrierConfig(1 /*subId*/,
+                CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL,
+                true /*isEnabled*/);
+        createRcsService(1 /*numSlots*/);
+        verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UceControllerManager.class);
+        verify(mFeatureControllerSlot0).connect();
+
+
+        // Send carrier config update twice with no update to subId
+        sendCarrierConfigChanged(0 /*slotId*/, 1 /*subId*/);
+        verify(mFeatureControllerSlot0).updateAssociatedSubscription(1);
+        verify(mFeatureControllerSlot0, never()).onCarrierConfigChangedForSubscription();
+        sendCarrierConfigChanged(0 /*slotId*/, 1 /*subId*/);
+        verify(mFeatureControllerSlot0, times(1)).updateAssociatedSubscription(1);
+        // carrier config changed should be sent here
+        verify(mFeatureControllerSlot0).onCarrierConfigChangedForSubscription();
+    }
+
+    @Test
     public void testCarrierConfigUpdateUceToNoUce() {
         setCarrierConfig(1 /*subId*/,
                 CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL,
@@ -325,17 +349,17 @@
     }
 
     private TelephonyRcsService createRcsService(int numSlots) {
-        TelephonyRcsService service = new TelephonyRcsService(mContext, numSlots);
+        TelephonyRcsService service = new TelephonyRcsService(mContext, numSlots, mResourceProxy);
         service.setFeatureFactory(mFeatureFactory);
         service.initialize();
         verify(mContext).registerReceiver(mReceiverCaptor.capture(), any());
         return service;
     }
 
-    private RcsFeatureController createFeatureController(int slotId) {
+    private RcsFeatureController createFeatureController(int slotId, int subId) {
         // Create a spy instead of a mock because TelephonyRcsService relies on state provided by
         // RcsFeatureController.
-        RcsFeatureController controller = spy(new RcsFeatureController(mContext, slotId,
+        RcsFeatureController controller = spy(new RcsFeatureController(mContext, slotId, subId,
                 mRegistrationFactory));
         controller.setFeatureConnectorFactory(mFeatureConnectorFactory);
         return controller;
diff --git a/tests/src/com/android/services/telephony/rcs/TransportSipMessageValidatorTest.java b/tests/src/com/android/services/telephony/rcs/TransportSipMessageValidatorTest.java
new file mode 100644
index 0000000..4d222e3
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/TransportSipMessageValidatorTest.java
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2021 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 junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.net.InetAddresses;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.SipDelegateConfiguration;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+import android.util.ArraySet;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+import com.android.TestExecutorService;
+import com.android.services.telephony.rcs.validator.IncomingTransportStateValidator;
+import com.android.services.telephony.rcs.validator.OutgoingTransportStateValidator;
+import com.android.services.telephony.rcs.validator.ValidationResult;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.net.InetSocketAddress;
+import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ScheduledExecutorService;
+
+@RunWith(AndroidJUnit4.class)
+public class TransportSipMessageValidatorTest extends TelephonyTestBase {
+    private static final int TEST_SUB_ID = 1;
+    private static final int TEST_CONFIG_VERSION = 1;
+    private static final SipMessage TEST_MESSAGE = new SipMessage(
+            "INVITE sip:bob@biloxi.com SIP/2.0",
+            // Typical Via
+            "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\n"
+                    + "Max-Forwards: 70\n"
+                    + "To: Bob <sip:bob@biloxi.com>\n"
+                    + "From: Alice <sip:alice@atlanta.com>;tag=1928301774\n"
+                    + "Call-ID: testid\n"
+                    + "CSeq: 314159 INVITE\n"
+                    + "Contact: <sip:alice@pc33.atlanta.com>\n"
+                    + "Content-Type: application/sdp\n"
+                    + "Content-Length: 142",
+            new byte[0]);
+
+    @Mock
+    private SipSessionTracker mSipSessionTracker;
+    @Mock
+    private IncomingTransportStateValidator mIncomingStateValidator;
+    @Mock
+    private OutgoingTransportStateValidator mOutgoingStateValidator;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Test
+    public void testTransportOpening() {
+        TestExecutorService executor = new TestExecutorService();
+        TransportSipMessageValidator tracker = getTestTracker(executor);
+        tracker.onTransportOpened(Collections.emptySet(), Collections.emptySet());
+        verify(mOutgoingStateValidator).open(Collections.emptySet(), Collections.emptySet());
+        verify(mIncomingStateValidator).open();
+        // Incoming messages are already verified
+        assertTrue(isIncomingTransportOpen(tracker));
+        // IMS config needs to be sent before outgoing messages can be verified.
+        assertFalse(isOutgoingTransportOpen(tracker));
+        tracker.onConfigurationChanged(getConfigBuilder(TEST_CONFIG_VERSION).build());
+        // Incoming messages are already verified
+        // Config set, transport is now open.
+        assertTrue(isIncomingTransportOpen(tracker));
+        assertTrue(isOutgoingTransportOpen(tracker));
+    }
+
+    @Test
+    public void testTransportOpenConfigChange() {
+        TestExecutorService executor = new TestExecutorService();
+        TransportSipMessageValidator tracker = openTransport(executor);
+
+        // Update IMS config version and send a message with an outdated version.
+        tracker.onConfigurationChanged(getConfigBuilder(TEST_CONFIG_VERSION + 1).build());
+        assertEquals(SipDelegateManager.MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION,
+                verifyOutgoingTransportClosed(tracker));
+    }
+
+    @Test
+    public void testSessionTrackerFiltering() {
+        TestExecutorService executor = new TestExecutorService();
+        TransportSipMessageValidator tracker = openTransport(executor);
+        // Since the incoming/outgoing messages were verified, there should have been two calls
+        // to filter the message.
+        verify(mSipSessionTracker, times(2)).filterSipMessage(TEST_MESSAGE);
+        // ensure pass through methods are working
+        tracker.acknowledgePendingMessage("abc");
+        verify(mSipSessionTracker).acknowledgePendingMessage("abc");
+        tracker.notifyPendingMessageFailed("abc");
+        verify(mSipSessionTracker).pendingMessageFailed("abc");
+        tracker.onSipSessionCleanup("abc");
+        verify(mSipSessionTracker).cleanupSession("abc");
+        // Now have validators return a non-successful result for validation and the tracker should
+        // not get the indication to filter the message.
+        doReturn(new ValidationResult(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED,
+                "")).when(mOutgoingStateValidator).validate(any());
+        doReturn(new ValidationResult(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED,
+                "")).when(mIncomingStateValidator).validate(any());
+        assertFalse(tracker.verifyIncomingMessage(TEST_MESSAGE).isValidated);
+        assertFalse(tracker.verifyOutgoingMessage(TEST_MESSAGE, TEST_CONFIG_VERSION).isValidated);
+        // The number of times the filter method was called should still only be two after these
+        // messages were not validated.
+        verify(mSipSessionTracker, times(2)).filterSipMessage(TEST_MESSAGE);
+    }
+
+
+    @Test
+    public void testTransportClosingGracefullyNoPendingSessions() {
+        TestExecutorService executor = new TestExecutorService(true /*wait*/);
+        TransportSipMessageValidator tracker = openTransport(executor);
+
+        doReturn(Collections.emptySet()).when(mSipSessionTracker).getTrackedDialogs();
+        ArraySet<String> pendingCallIds = new ArraySet<>();
+        CountDownLatch l = new CountDownLatch(1);
+        tracker.closeSessionsGracefully((ids) -> {
+            pendingCallIds.addAll(ids);
+            l.countDown();
+        }, SipDelegateManager.MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED);
+        // Since there are no pending call ids, this should be completed with no call ids pending.
+        assertEquals(0, l.getCount());
+        assertTrue(pendingCallIds.isEmpty());
+        verify(mOutgoingStateValidator).close(anyInt());
+        verify(mIncomingStateValidator).close(anyInt());
+    }
+
+    @Test
+    public void testTransportClosingGracefullyCloseCallIds() {
+        TestExecutorService executor = new TestExecutorService(true /*wait*/);
+        TransportSipMessageValidator tracker = openTransport(executor);
+
+        ArraySet<SipDialog> dialogs = new ArraySet<>();
+        dialogs.add(SipDialog.fromSipMessage(TEST_MESSAGE));
+        doReturn(dialogs).when(mSipSessionTracker).getTrackedDialogs();
+        ArraySet<String> pendingCallIds = new ArraySet<>();
+        CountDownLatch l = new CountDownLatch(1);
+        tracker.closeSessionsGracefully((ids) -> {
+            pendingCallIds.addAll(ids);
+            l.countDown();
+        }, SipDelegateManager.MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED);
+        // Before executor executes, outgoing messages will be restricted due to pending call ids.
+        assertTrue(l.getCount() >= 1);
+        assertTrue(pendingCallIds.isEmpty());
+        assertTrue(isIncomingTransportOpen(tracker));
+        verify(mOutgoingStateValidator).restrict(anyInt());
+        // pretend a sip message has been acknowledged, which closed pending call id. Since there
+        // are no more pending call ids, the transport should move to closed.
+        dialogs.clear();
+        tracker.acknowledgePendingMessage("blah");
+        assertEquals(0, l.getCount());
+        assertTrue(pendingCallIds.isEmpty());
+        verify(mOutgoingStateValidator).close(anyInt());
+        verify(mIncomingStateValidator).close(anyInt());
+    }
+
+    @Test
+    public void testTransportClosingGracefullyThenForceClose() {
+        TestExecutorService executor = new TestExecutorService(true /*wait*/);
+        TransportSipMessageValidator tracker = openTransport(executor);
+
+        ArraySet<SipDialog> dialogs = new ArraySet<>();
+        dialogs.add(SipDialog.fromSipMessage(TEST_MESSAGE));
+        doReturn(dialogs).when(mSipSessionTracker).getTrackedDialogs();
+        ArraySet<String> pendingCallIds = new ArraySet<>();
+        CountDownLatch l = new CountDownLatch(1);
+        tracker.closeSessionsGracefully((ids) -> {
+            pendingCallIds.addAll(ids);
+            l.countDown();
+        }, SipDelegateManager.MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED);
+        // Before executor executes, outgoing messages will be restricted due to pending call ids.
+        assertTrue(l.getCount() >= 1);
+        assertTrue(pendingCallIds.isEmpty());
+        assertTrue(isIncomingTransportOpen(tracker));
+        verify(mOutgoingStateValidator).restrict(anyInt());
+        // force close amd ensure pending close is
+        assertTrue(tracker.closeSessions(
+                SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED).isEmpty());
+        assertEquals(0, l.getCount());
+        assertEquals(pendingCallIds, pendingCallIds);
+        verify(mOutgoingStateValidator).close(anyInt());
+        verify(mIncomingStateValidator).close(anyInt());
+    }
+
+    @Test
+    public void testTransportClosingGracefullyTimeout() {
+        TestExecutorService executor = new TestExecutorService(true /*wait*/);
+        TransportSipMessageValidator tracker = openTransport(executor);
+
+        ArraySet<SipDialog> dialogs = new ArraySet<>();
+        dialogs.add(SipDialog.fromSipMessage(TEST_MESSAGE));
+        doReturn(dialogs).when(mSipSessionTracker).getTrackedDialogs();
+        ArraySet<String> pendingCallIds = new ArraySet<>();
+        CountDownLatch l = new CountDownLatch(1);
+        tracker.closeSessionsGracefully((ids) -> {
+            pendingCallIds.addAll(ids);
+            l.countDown();
+        }, SipDelegateManager.MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED);
+        // Before executor executes, outgoing messages will be restricted due to pending call ids.
+        assertTrue(l.getCount() >= 1);
+        assertTrue(pendingCallIds.isEmpty());
+        assertTrue(isIncomingTransportOpen(tracker));
+        verify(mOutgoingStateValidator).restrict(anyInt());
+        // Process timeout event - pending call id should be passed to transport.
+        executor.executePending();
+        assertEquals(0, l.getCount());
+        assertTrue(pendingCallIds.contains("testid"));
+        verify(mOutgoingStateValidator).close(anyInt());
+        verify(mIncomingStateValidator).close(anyInt());
+    }
+
+    @Test
+    public void testTransportClosingGracefullyCleanup() {
+        TestExecutorService executor = new TestExecutorService(true /*wait*/);
+        TransportSipMessageValidator tracker = openTransport(executor);
+
+        ArraySet<SipDialog> dialogs = new ArraySet<>();
+        dialogs.add(SipDialog.fromSipMessage(TEST_MESSAGE));
+        doReturn(dialogs).when(mSipSessionTracker).getTrackedDialogs();
+        ArraySet<String> pendingCallIds = new ArraySet<>();
+        CountDownLatch l = new CountDownLatch(1);
+        tracker.closeSessionsGracefully((ids) -> {
+            pendingCallIds.addAll(ids);
+            l.countDown();
+        }, SipDelegateManager.MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED);
+        // Before executor executes, outgoing messages will be restricted due to pending call ids.
+        assertTrue(l.getCount() >= 1);
+        assertTrue(pendingCallIds.isEmpty());
+        assertTrue(isIncomingTransportOpen(tracker));
+        verify(mOutgoingStateValidator).restrict(anyInt());
+        // Mock cleanupSession event was called for pending callId
+        dialogs.clear();
+        tracker.onSipSessionCleanup("abc");
+        assertEquals(0, l.getCount());
+        assertTrue(pendingCallIds.isEmpty());
+        verify(mOutgoingStateValidator).close(anyInt());
+        verify(mIncomingStateValidator).close(anyInt());
+    }
+
+    @Test
+    public void testTransportClosingForcefully() {
+        TestExecutorService executor = new TestExecutorService();
+        TransportSipMessageValidator tracker = openTransport(executor);
+
+        tracker.closeSessions(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED);
+
+        // All messages will be rejected.
+        verify(mOutgoingStateValidator).close(anyInt());
+        verify(mIncomingStateValidator).close(anyInt());
+    }
+
+    @Test
+    public void setRegStateChangedNoPendingCallIds() {
+        TestExecutorService executor = new TestExecutorService(true /*wait*/);
+        TransportSipMessageValidator tracker = openTransport(executor);
+
+        doReturn(Collections.emptySet()).when(mSipSessionTracker)
+                .getCallIdsAssociatedWithFeatureTag(any());
+        ArraySet<String> pendingCallIds = new ArraySet<>();
+        CountDownLatch l = new CountDownLatch(1);
+        // no feature tags are deregistering/deregistered, should return immediately
+        tracker.onRegistrationStateChanged((ids) -> {
+            pendingCallIds.addAll(ids);
+            l.countDown();
+        }, getTestRegistrationState());
+        verify(mOutgoingStateValidator, times(2)).restrictFeatureTags(Collections.emptySet());
+        assertTrue(pendingCallIds.isEmpty());
+        assertEquals(0, l.getCount());
+    }
+
+    @Test
+    public void setRegStateChangedPendingCallIdsMultipleCalls() {
+        TestExecutorService executor = new TestExecutorService(true /*wait*/);
+        TransportSipMessageValidator tracker = openTransport(executor);
+
+        ArraySet<String> callIds = new ArraySet<>(1);
+        callIds.add("abc");
+        doReturn(callIds).when(mSipSessionTracker)
+                .getCallIdsAssociatedWithFeatureTag(any());
+        ArraySet<String> pendingCallIds = new ArraySet<>();
+        CountDownLatch l = new CountDownLatch(2);
+        tracker.onRegistrationStateChanged((ids) -> {
+            pendingCallIds.addAll(ids);
+            l.countDown();
+        }, getTestRegistrationState());
+        assertEquals(2, l.getCount());
+        // If called again, the previous request will complete with no call ids
+        tracker.onRegistrationStateChanged((ids) -> {
+            pendingCallIds.addAll(ids);
+            l.countDown();
+        }, getTestRegistrationState());
+        assertEquals(1, l.getCount());
+        assertTrue(pendingCallIds.isEmpty());
+        // Simulate timeout - we should get callback with the pending call ids.
+        executor.executePending();
+        assertEquals(0, l.getCount());
+        assertEquals(callIds, pendingCallIds);
+    }
+
+    @Test
+    public void setRegStateChangedPendingCallIdsTimeout() {
+        TestExecutorService executor = new TestExecutorService(true /*wait*/);
+        TransportSipMessageValidator tracker = openTransport(executor);
+
+        ArraySet<String> callIds = new ArraySet<>(1);
+        callIds.add("abc");
+        doReturn(callIds).when(mSipSessionTracker)
+                .getCallIdsAssociatedWithFeatureTag(any());
+        ArraySet<String> pendingCallIds = new ArraySet<>();
+        CountDownLatch l = new CountDownLatch(1);
+        tracker.onRegistrationStateChanged((ids) -> {
+            pendingCallIds.addAll(ids);
+            l.countDown();
+        }, getTestRegistrationState());
+        assertEquals(1, l.getCount());
+        assertTrue(pendingCallIds.isEmpty());
+        // Simulate timeout - we should get callback with the pending call ids.
+        executor.executePending();
+        assertEquals(0, l.getCount());
+        assertEquals(callIds, pendingCallIds);
+    }
+
+    @Test
+    public void setRegStateChangedPendingCallIdsResolved() {
+        TestExecutorService executor = new TestExecutorService(true /*wait*/);
+        TransportSipMessageValidator tracker = openTransport(executor);
+
+        ArraySet<String> callIds = new ArraySet<>(1);
+        callIds.add("abc");
+        doReturn(callIds).when(mSipSessionTracker)
+                .getCallIdsAssociatedWithFeatureTag(any());
+        ArraySet<String> pendingCallIds = new ArraySet<>();
+        CountDownLatch l = new CountDownLatch(1);
+        tracker.onRegistrationStateChanged((ids) -> {
+            pendingCallIds.addAll(ids);
+            l.countDown();
+        }, getTestRegistrationState());
+        assertEquals(1, l.getCount());
+        assertTrue(pendingCallIds.isEmpty());
+        // Simulate ack pending SIP session, which has cleared pending call id
+        doReturn(Collections.emptySet()).when(mSipSessionTracker)
+                .getCallIdsAssociatedWithFeatureTag(any());
+        tracker.acknowledgePendingMessage("blah");
+        assertEquals(0, l.getCount());
+        assertEquals(callIds, pendingCallIds);
+    }
+
+    @Test
+    public void setRegStateChangedPendingCallIdsChanged() {
+        TestExecutorService executor = new TestExecutorService(true /*wait*/);
+        TransportSipMessageValidator tracker = openTransport(executor);
+
+        ArraySet<String> callIds = new ArraySet<>(1);
+        callIds.add("abc");
+        doReturn(callIds).when(mSipSessionTracker)
+                .getCallIdsAssociatedWithFeatureTag(any());
+        ArraySet<String> pendingCallIds = new ArraySet<>();
+        CountDownLatch l = new CountDownLatch(1);
+        tracker.onRegistrationStateChanged((ids) -> {
+            pendingCallIds.addAll(ids);
+            l.countDown();
+        }, getTestRegistrationState());
+        assertEquals(1, l.getCount());
+        assertTrue(pendingCallIds.isEmpty());
+        // Simulate pending callIds changed to add another session.
+        callIds.add("def");
+        executor.executePending();
+        assertEquals(0, l.getCount());
+        assertEquals(callIds, pendingCallIds);
+    }
+
+    @Test
+    public void setRegStateRegThenClose() {
+        TestExecutorService executor = new TestExecutorService(true /*wait*/);
+        TransportSipMessageValidator tracker = openTransport(executor);
+
+        ArraySet<String> callIds = new ArraySet<>(1);
+        callIds.add("abc");
+        doReturn(callIds).when(mSipSessionTracker)
+                .getCallIdsAssociatedWithFeatureTag(any());
+        ArraySet<String> pendingCallIds = new ArraySet<>();
+        CountDownLatch l = new CountDownLatch(1);
+        tracker.onRegistrationStateChanged((ids) -> {
+            pendingCallIds.addAll(ids);
+            l.countDown();
+        }, getTestRegistrationState());
+        assertEquals(1, l.getCount());
+        assertTrue(pendingCallIds.isEmpty());
+        // If close is called during pending reg state change, it should be completed with no
+        // pending call IDs (close will take care of closing everything).
+        ArraySet<SipDialog> dialogs = new ArraySet<>();
+        dialogs.add(SipDialog.fromSipMessage(TEST_MESSAGE));
+        doReturn(dialogs).when(mSipSessionTracker).getTrackedDialogs();
+        tracker.closeSessions(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD);
+        assertEquals(0, l.getCount());
+        assertEquals(Collections.emptySet(), pendingCallIds);
+    }
+
+    private SipDelegateConfiguration.Builder getConfigBuilder(int version) {
+        InetSocketAddress localAddr = new InetSocketAddress(
+                InetAddresses.parseNumericAddress("1.1.1.1"), 80);
+        InetSocketAddress serverAddr = new InetSocketAddress(
+                InetAddresses.parseNumericAddress("2.2.2.2"), 81);
+        return new SipDelegateConfiguration.Builder(version,
+                SipDelegateConfiguration.SIP_TRANSPORT_TCP, localAddr, serverAddr);
+    }
+
+    private boolean isIncomingTransportOpen(TransportSipMessageValidator tracker) {
+        return tracker.verifyIncomingMessage(TEST_MESSAGE).isValidated;
+    }
+
+    private boolean isOutgoingTransportOpen(TransportSipMessageValidator tracker) {
+        return tracker.verifyOutgoingMessage(TEST_MESSAGE, TEST_CONFIG_VERSION).isValidated;
+    }
+
+    private int verifyOutgoingTransportClosed(TransportSipMessageValidator tracker) {
+        ValidationResult result = tracker.verifyOutgoingMessage(TEST_MESSAGE, TEST_CONFIG_VERSION);
+        assertFalse(result.isValidated);
+        return result.restrictedReason;
+    }
+
+    private DelegateRegistrationState getTestRegistrationState() {
+        return new DelegateRegistrationState.Builder().build();
+    }
+
+    private TransportSipMessageValidator openTransport(ScheduledExecutorService executor) {
+        TransportSipMessageValidator tracker = getTestTracker(executor);
+        tracker.onTransportOpened(Collections.emptySet(), Collections.emptySet());
+        tracker.onConfigurationChanged(getConfigBuilder(TEST_CONFIG_VERSION).build());
+        tracker.onRegistrationStateChanged((ignore) -> {}, getTestRegistrationState());
+        // Config set + IMS reg state sent, transport is now open.
+        assertTrue(isIncomingTransportOpen(tracker));
+        assertTrue(isOutgoingTransportOpen(tracker));
+        return tracker;
+    }
+
+    private TransportSipMessageValidator getTestTracker(ScheduledExecutorService executor) {
+        doReturn(ValidationResult.SUCCESS).when(mOutgoingStateValidator).validate(any());
+        doReturn(ValidationResult.SUCCESS).when(mIncomingStateValidator).validate(any());
+        doReturn(mIncomingStateValidator).when(mIncomingStateValidator).andThen(any());
+        return new TransportSipMessageValidator(TEST_SUB_ID, executor, mSipSessionTracker,
+                mOutgoingStateValidator, mIncomingStateValidator);
+    }
+}
diff --git a/tests/src/com/android/services/telephony/rcs/UceControllerManagerTest.java b/tests/src/com/android/services/telephony/rcs/UceControllerManagerTest.java
index 4148d13..8d719fd 100644
--- a/tests/src/com/android/services/telephony/rcs/UceControllerManagerTest.java
+++ b/tests/src/com/android/services/telephony/rcs/UceControllerManagerTest.java
@@ -17,8 +17,10 @@
 package com.android.services.telephony.rcs;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
 import android.net.Uri;
@@ -94,12 +96,31 @@
     }
 
     @Test
-    public void testSubscriptionUpdated() throws Exception {
+    public void testSubIdAndCarrierConfigUpdate() throws Exception {
         UceControllerManager uceCtrlManager = getUceControllerManager();
 
-        uceCtrlManager.onAssociatedSubscriptionUpdated(mSubId);
+        // Updates with the same subId should not destroy the UceController
+        uceCtrlManager.onCarrierConfigChanged();
+        verify(mUceController, never()).onDestroy();
+
+        // Updates with different subIds should trigger the creation of a new controller.
+        uceCtrlManager.onAssociatedSubscriptionUpdated(mSubId + 1);
+        verify(mUceController).onDestroy();
+    }
+
+    @Test
+    public void testSubIdAndCarrierConfigUpdateWithInvalidSubId() throws Exception {
+        UceControllerManager uceCtrlManager = getUceControllerManager();
+
+        // Updates with the same subId should not destroy the UceController
+        uceCtrlManager.onCarrierConfigChanged();
+        verify(mUceController, never()).onDestroy();
+
+        // Updates with invalid subscription ID
+        uceCtrlManager.onAssociatedSubscriptionUpdated(-1);
 
         verify(mUceController).onDestroy();
+        assertNull(uceCtrlManager.getUceController());
     }
 
     @Test
@@ -237,9 +258,8 @@
     }
 
     private UceControllerManager getUceControllerManager() {
-        UceControllerManager manager = new UceControllerManager(mContext, mSlotId, mSubId,
-                mExecutorService);
-        manager.setUceController(mUceController);
+        UceControllerManager manager = new UceControllerManager(mContext, mSlotId,
+                mExecutorService, mUceController);
         return manager;
     }
 }
diff --git a/tests/src/com/android/services/telephony/rcs/validator/IncomingTransportStateValidatorTest.java b/tests/src/com/android/services/telephony/rcs/validator/IncomingTransportStateValidatorTest.java
new file mode 100644
index 0000000..0e7e1be
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/validator/IncomingTransportStateValidatorTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 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.validator;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class IncomingTransportStateValidatorTest {
+    private static final SipMessage TEST_MESSAGE = new SipMessage(
+            "INVITE sip:bob@biloxi.com SIP/2.0",
+            "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\n"
+                    + "Max-Forwards: 70\n"
+                    + "To: Bob <sip:bob@biloxi.com>\n"
+                    + "From: Alice <sip:alice@atlanta.com>;tag=1928301774\n"
+                    + "Call-ID: a84b4c76e66710@pc33.atlanta.com\n"
+                    + "CSeq: 314159 INVITE\n"
+                    + "Contact: <sip:alice@pc33.atlanta.com>\n"
+                    + "Content-Type: application/sdp\n"
+                    + "Content-Length: 142",
+            new byte[0]);
+
+    @Test
+    public void testVerifyMessageAndUpdateState() {
+        IncomingTransportStateValidator validator = new IncomingTransportStateValidator();
+        ValidationResult result = validator.validate(TEST_MESSAGE);
+        assertFalse(result.isValidated);
+        assertEquals(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED,
+                result.restrictedReason);
+
+        validator.open();
+        result = validator.validate(TEST_MESSAGE);
+        assertTrue(result.isValidated);
+
+        validator.close(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED);
+        result = validator.validate(TEST_MESSAGE);
+        assertFalse(result.isValidated);
+        assertEquals(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED,
+                result.restrictedReason);
+    }
+}
diff --git a/tests/src/com/android/services/telephony/rcs/validator/MalformedSipMessageValidatorTest.java b/tests/src/com/android/services/telephony/rcs/validator/MalformedSipMessageValidatorTest.java
new file mode 100644
index 0000000..18b37fc
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/validator/MalformedSipMessageValidatorTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 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.validator;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class MalformedSipMessageValidatorTest {
+
+    @Test
+    public void testValidRequest() {
+        SipMessage msg = new SipMessage(
+                "INVITE sip:bob@biloxi.com SIP/2.0",
+                // Typical Via
+                "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK.TeSt\n"
+                        + "Max-Forwards: 70\n"
+                        + "To: Bob <sip:bob@biloxi.com>\n"
+                        + "From: Alice <sip:alice@atlanta.com>;tag=1928301774\n"
+                        + "Call-ID: a84b4c76e66710@pc33.atlanta.com\n"
+                        + "CSeq: 314159 INVITE\n"
+                        + "Contact: <sip:alice@pc33.atlanta.com>\n"
+                        + "Content-Type: application/sdp\n"
+                        + "Content-Length: 142",
+                new byte[0]);
+        ValidationResult result = new MalformedSipMessageValidator().validate(msg);
+        assertTrue(result.isValidated);
+    }
+
+    @Test
+    public void testInvalidRequest() {
+        SipMessage msg = new SipMessage(
+                "INVITE sip:bob@biloxi.comSIP/2.0",
+                "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\n"
+                        + "Max-Forwards: 70\n"
+                        + "To: Bob <sip:bob@biloxi.com>\n"
+                        + "From: Alice <sip:alice@atlanta.com>;tag=1928301774\n"
+                        + "Call-ID: a84b4c76e66710@pc33.atlanta.com\n"
+                        + "CSeq: 314159 INVITE\n"
+                        + "Contact: <sip:alice@pc33.atlanta.com>\n"
+                        + "Content-Type: application/sdp\n"
+                        + "Content-Length: 142",
+                new byte[0]);
+        ValidationResult result = new MalformedSipMessageValidator().validate(msg);
+        assertFalse(result.isValidated);
+        assertEquals(SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_START_LINE,
+                result.restrictedReason);
+    }
+
+    @Test
+    public void testInvalidResponse() {
+        SipMessage msg = new SipMessage(
+                "SIP/2.0 200OK",
+                "Via: SIP/2.0/TCP terminal.vancouver.example.com;"
+                        + "branch=z9hG4bKwYb6QREiCL\n"
+                        + "To: <sip:adam-buddies@pres.vancouver.example.com>;tag=zpNctbZq\n"
+                        + "From: <sip:adam@vancouver.example.com>;tag=ie4hbb8t\n"
+                        + "Call-ID: cdB34qLToC@terminal.vancouver.example.com\n"
+                        + "CSeq: 322723822 SUBSCRIBE\n"
+                        + "Contact: <sip:pres.vancouver.example.com>\n"
+                        + "Expires: 7200\n"
+                        + "Require: eventlist\n"
+                        + "Content-Length: 0",
+                new byte[0]);
+        ValidationResult result = new MalformedSipMessageValidator().validate(msg);
+        assertFalse(result.isValidated);
+        assertEquals(SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_START_LINE,
+                result.restrictedReason);
+    }
+}
diff --git a/tests/src/com/android/services/telephony/rcs/validator/OutgoingTransportStateValidatorTest.java b/tests/src/com/android/services/telephony/rcs/validator/OutgoingTransportStateValidatorTest.java
new file mode 100644
index 0000000..8dbeb9b
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/validator/OutgoingTransportStateValidatorTest.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2021 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.validator;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+import android.util.ArraySet;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+import com.android.services.telephony.rcs.SipDialog;
+import com.android.services.telephony.rcs.SipSessionTracker;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.Collections;
+import java.util.Random;
+
+@RunWith(AndroidJUnit4.class)
+public class OutgoingTransportStateValidatorTest  extends TelephonyTestBase {
+
+    @Mock
+    private SipSessionTracker mMockSessionTracker;
+    private final Random mRandom = new Random();
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Test
+    public void testVerifyMessageInOpenCloseState() {
+        SipMessage testMessage = generateSipRequestForCallId("callId1");
+        OutgoingTransportStateValidator validator =
+                new OutgoingTransportStateValidator(mMockSessionTracker);
+        ValidationResult result = validator.validate(testMessage);
+        assertFalse(result.isValidated);
+        assertEquals(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED,
+                result.restrictedReason);
+
+        validator.open(Collections.singleton("+tag"), Collections.emptySet());
+        validator.restrictFeatureTags(Collections.emptySet());
+        result = validator.validate(testMessage);
+        assertTrue(result.isValidated);
+
+        validator.close(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED);
+        result = validator.validate(testMessage);
+        assertFalse(result.isValidated);
+        assertEquals(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED,
+                result.restrictedReason);
+    }
+
+    @Test
+    public void testVerifyMessageRestricted() {
+        String callId1 = "callId1";
+        String callId2 = "callId2";
+        String callId3 = "callId3";
+        SipMessage testInDialogEarlyMessage = generateSipRequestForCallId(callId1);
+        SipMessage testInDialogConfirmedMessage = generateSipRequestForCallId(callId2);
+        SipMessage testOutOfDialogInvite = generateSipRequestForCallId(callId3);
+        SipMessage testStatelessRequest = generateMessageRequest();
+        ArraySet<SipDialog> inDialogEarlyCallIds = new ArraySet<>(1);
+        inDialogEarlyCallIds.add(SipDialog.fromSipMessage(testInDialogEarlyMessage));
+        ArraySet<String> inDialogConfirmedCallIds = new ArraySet<>();
+        inDialogEarlyCallIds.add(SipDialog.fromSipMessage(testInDialogConfirmedMessage));
+        // For the sake of testing, add the same call id to early and confirmed dialogs, since we
+        // will accept requests for both right now.
+        doReturn(inDialogEarlyCallIds).when(mMockSessionTracker).getEarlyDialogs();
+        doReturn(inDialogConfirmedCallIds).when(mMockSessionTracker).getConfirmedDialogs();
+        OutgoingTransportStateValidator validator =
+                new OutgoingTransportStateValidator(mMockSessionTracker);
+        validator.restrict(
+                SipDelegateManager.MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION);
+
+        // ensure a response to a pending request is not restricted, even if it is not for a tracked
+        // call ID.
+        ValidationResult result = validator.validate(generate200OkResponse("callId4"));
+        assertTrue(result.isValidated);
+
+        // confirm in dialog messages are not restricted
+        result = validator.validate(testInDialogEarlyMessage);
+        assertTrue(result.isValidated);
+        result = validator.validate(testInDialogConfirmedMessage);
+        assertTrue(result.isValidated);
+
+        // confirm out-of-dialog requests are restricted.
+        result = validator.validate(testOutOfDialogInvite);
+        assertFalse(result.isValidated);
+        assertEquals(SipDelegateManager.MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION,
+                result.restrictedReason);
+        result = validator.validate(testStatelessRequest);
+        assertFalse(result.isValidated);
+        assertEquals(SipDelegateManager.MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION,
+                result.restrictedReason);
+    }
+
+    @Test
+    public void testDeniedFeatureTag() {
+        SipMessage testMessage = generateSipRequestForCallId("callId1");
+        OutgoingTransportStateValidator validator =
+                new OutgoingTransportStateValidator(mMockSessionTracker);
+        ValidationResult result = validator.validate(testMessage);
+        assertFalse(result.isValidated);
+        assertEquals(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED,
+                result.restrictedReason);
+
+        // Assert that invites associated with denied tags are denied.
+        validator.open(Collections.emptySet(), Collections.singleton("+tag"));
+        validator.restrictFeatureTags(Collections.emptySet());
+        result = validator.validate(testMessage);
+        assertFalse(result.isValidated);
+        assertEquals(SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG,
+                result.restrictedReason);
+    }
+
+    @Test
+    public void testRestrictedFeatureTag() {
+        SipMessage testMessage = generateSipRequestForCallId("callId1");
+        OutgoingTransportStateValidator validator =
+                new OutgoingTransportStateValidator(mMockSessionTracker);
+        ValidationResult result = validator.validate(testMessage);
+        assertFalse(result.isValidated);
+        assertEquals(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED,
+                result.restrictedReason);
+
+        validator.open(Collections.singleton("+tag"), Collections.emptySet());
+        // Ensure that when supported tags are restricted, the message is not validated.
+        validator.restrictFeatureTags(Collections.singleton("+tag"));
+        result = validator.validate(testMessage);
+        assertFalse(result.isValidated);
+        assertEquals(SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG,
+                result.restrictedReason);
+    }
+
+    @Test
+    public void testNoSupportedFeatureTag() {
+        SipMessage testMessage = generateSipRequestForCallId("callId1");
+        OutgoingTransportStateValidator validator =
+                new OutgoingTransportStateValidator(mMockSessionTracker);
+        ValidationResult result = validator.validate(testMessage);
+        assertFalse(result.isValidated);
+        assertEquals(SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED,
+                result.restrictedReason);
+
+        // Assert if a message doesn't have any related supported tags, it should be denied
+        validator.open(Collections.emptySet(), Collections.emptySet());
+        validator.restrictFeatureTags(Collections.emptySet());
+        result = validator.validate(testMessage);
+        assertFalse(result.isValidated);
+        assertEquals(SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG,
+                result.restrictedReason);
+
+        // responses and non-dialog starting messages do not matter
+        result = validator.validate(generate200OkResponse("callId2"));
+        assertTrue(result.isValidated);
+        result = validator.validate(generateMessageRequest());
+        assertTrue(result.isValidated);
+    }
+
+    /**
+     * @return A INVITE with the call ID specified. Note: this request is not technically valid, but
+     * only contains the relevant headers for testing.
+     */
+    private SipMessage generateSipRequestForCallId(String callId) {
+        return new SipMessage(
+                "INVITE sip:b@client.example.com SIP/2.0",
+                "Via: SIP/2.0/UDP client.example.com;branch=z9hG4bK776asdhds\n"
+                        + "To: B <sip:b@example.com>\n"
+                        + "From: A <sip:a@example.com>;tag=1928301774\n"
+                        + "Accept-Contact: *;+tag\n"
+                        + "Call-ID: " + callId,
+                new byte[0]);
+    }
+
+    /**
+     * @return A MESSAGE request. Note: this request is not technically valid, but only contains the
+     * relevant headers for testing.
+     */
+    private SipMessage generateMessageRequest() {
+        return new SipMessage(
+                "MESSAGE sip:b@client.example.com SIP/2.0",
+                "Via: SIP/2.0/UDP client.example.com;branch=z9hG4bK776asdhds\n"
+                        + "To: B <sip:b@example.com>\n"
+                        + "From: A <sip:a@example.com>;tag=1928301774\n",
+                new byte[0]);
+    }
+
+    /**
+     * @return A 200 OK associated with the supplied call ID.
+     */
+    private SipMessage generate200OkResponse(String callId) {
+        return new SipMessage(
+                "SIP/2.0 200 OK",
+                "Via: SIP/2.0/UDP client.example.com;branch=z9hG4bK776asdhds\n"
+                        + "To: B <sip:b@example.com>\n"
+                        + "From: A <sip:a@example.com>;tag=1928301774\n"
+                        + "Call-ID: " + callId,
+                new byte[0]);
+    }
+}
diff --git a/tests/src/com/android/services/telephony/rcs/validator/RestrictedOutgoingSipRequestValidatorTest.java b/tests/src/com/android/services/telephony/rcs/validator/RestrictedOutgoingSipRequestValidatorTest.java
new file mode 100644
index 0000000..a90aaeb
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/validator/RestrictedOutgoingSipRequestValidatorTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2021 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.validator;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RestrictedOutgoingSipRequestValidatorTest {
+
+    @Test
+    public void testRegisterNotAllowed() {
+        SipMessage msg = new SipMessage(
+                "REGISTER sip:bob@biloxi.com SIP/2.0",
+                // Not representative of real REGISTER message, but close enough for validation.
+                "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\n"
+                        + "Max-Forwards: 70\n"
+                        + "To: Bob <sip:bob@biloxi.com>\n"
+                        + "From: Alice <sip:alice@atlanta.com>;tag=1928301774\n"
+                        + "CSeq: 314159 REGISTER\n"
+                        + "Contact: <sip:alice@pc33.atlanta.com>\n",
+                new byte[0]);
+        ValidationResult result = new RestrictedOutgoingSipRequestValidator().validate(msg);
+        assertFalse(result.isValidated);
+        assertEquals(SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_START_LINE,
+                result.restrictedReason);
+    }
+
+    @Test
+    public void testPublishNotAllowed() {
+        SipMessage msg = new SipMessage(
+                "PUBLISH sip:bob@biloxi.com SIP/2.0",
+                // Not representative of real REGISTER message, but close enough for validation.
+                "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\n"
+                        + "Max-Forwards: 70\n"
+                        + "To: Bob <sip:bob@biloxi.com>\n"
+                        + "From: Alice <sip:alice@atlanta.com>;tag=1928301774\n"
+                        + "CSeq: 314159 PUBLISH\n"
+                        + "Contact: <sip:alice@pc33.atlanta.com>\n",
+                new byte[0]);
+        ValidationResult result = new RestrictedOutgoingSipRequestValidator().validate(msg);
+        assertFalse(result.isValidated);
+        assertEquals(SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_START_LINE,
+                result.restrictedReason);
+
+    }
+
+    @Test
+    public void testOptionsNotAllowed() {
+        SipMessage msg = new SipMessage(
+                "OPTIONS sip:bob@biloxi.com SIP/2.0",
+                // Not representative of real REGISTER message, but close enough for validation.
+                "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\n"
+                        + "Max-Forwards: 70\n"
+                        + "To: Bob <sip:bob@biloxi.com>\n"
+                        + "From: Alice <sip:alice@atlanta.com>;tag=1928301774\n"
+                        + "CSeq: 314159 OPTIONS\n"
+                        + "Contact: <sip:alice@pc33.atlanta.com>\n",
+                new byte[0]);
+        ValidationResult result = new RestrictedOutgoingSipRequestValidator().validate(msg);
+        assertFalse(result.isValidated);
+        assertEquals(SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_START_LINE,
+                result.restrictedReason);
+
+    }
+
+    @Test
+    public void testInviteAllowed() {
+        SipMessage msg = new SipMessage(
+                "INVITE sip:bob@biloxi.com SIP/2.0",
+                "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\n"
+                        + "Max-Forwards: 70\n"
+                        + "To: Bob <sip:bob@biloxi.com>\n"
+                        + "From: Alice <sip:alice@atlanta.com>;tag=1928301774\n"
+                        + "Call-ID: a84b4c76e66710@pc33.atlanta.com\n"
+                        + "CSeq: 314159 INVITE\n"
+                        + "Contact: <sip:alice@pc33.atlanta.com>\n"
+                        + "Content-Type: application/sdp\n"
+                        + "Content-Length: 142",
+                new byte[0]);
+        ValidationResult result = new RestrictedOutgoingSipRequestValidator().validate(msg);
+        assertTrue(result.isValidated);
+    }
+
+    @Test
+    public void testResponseAllowed() {
+        SipMessage msg = new SipMessage(
+                "SIP/2.0 200 OK",
+                "Via: SIP/2.0/TCP terminal.vancouver.example.com;"
+                        + "branch=z9hG4bKwYb6QREiCL\n"
+                        + "To: <sip:adam-buddies@pres.vancouver.example.com>;tag=zpNctbZq\n"
+                        + "From: <sip:adam@vancouver.example.com>;tag=ie4hbb8t\n"
+                        + "Call-ID: cdB34qLToC@terminal.vancouver.example.com\n"
+                        + "CSeq: 322723822 SUBSCRIBE\n"
+                        + "Contact: <sip:pres.vancouver.example.com>\n"
+                        + "Expires: 7200\n"
+                        + "Require: eventlist\n"
+                        + "Content-Length: 0",
+                new byte[0]);
+        ValidationResult result = new RestrictedOutgoingSipRequestValidator().validate(msg);
+        assertTrue(result.isValidated);
+    }
+}
diff --git a/tests/src/com/android/services/telephony/rcs/validator/RestrictedOutgoingSubscribeValidatorTest.java b/tests/src/com/android/services/telephony/rcs/validator/RestrictedOutgoingSubscribeValidatorTest.java
new file mode 100644
index 0000000..9864872
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/validator/RestrictedOutgoingSubscribeValidatorTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 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.validator;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.SipMessage;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RestrictedOutgoingSubscribeValidatorTest {
+
+    @Test
+    public void testValidUnrelatedRequest() {
+        SipMessage msg = new SipMessage(
+                "INVITE sip:bob@biloxi.com SIP/2.0",
+                // Typical Via
+                "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK.TeSt\n"
+                        + "Max-Forwards: 70\n"
+                        + "To: Bob <sip:bob@biloxi.com>\n"
+                        + "From: Alice <sip:alice@atlanta.com>;tag=1928301774\n"
+                        + "Call-ID: a84b4c76e66710@pc33.atlanta.com\n"
+                        + "CSeq: 314159 INVITE\n"
+                        + "Contact: <sip:alice@pc33.atlanta.com>\n"
+                        + "Content-Type: application/sdp\n"
+                        + "Content-Length: 142",
+                new byte[0]);
+        ValidationResult result = new MalformedSipMessageValidator().validate(msg);
+        assertTrue(result.isValidated);
+    }
+
+    @Test
+    public void testValidUnrelatedResponse() {
+        SipMessage msg = new SipMessage(
+                "SIP/2.0 200 OK",
+                "Via: SIP/2.0/TCP terminal.vancouver.example.com;"
+                        + "branch=z9hG4bKwYb6QREiCL\n"
+                        + "To: <sip:adam-buddies@pres.vancouver.example.com>;tag=zpNctbZq\n"
+                        + "From: <sip:adam@vancouver.example.com>;tag=ie4hbb8t\n"
+                        + "Call-ID: cdB34qLToC@terminal.vancouver.example.com\n"
+                        + "CSeq: 322723822 SUBSCRIBE\n"
+                        + "Contact: <sip:pres.vancouver.example.com>\n"
+                        + "Expires: 7200\n"
+                        + "Require: eventlist\n"
+                        + "Content-Length: 0",
+                new byte[0]);
+        ValidationResult result = new MalformedSipMessageValidator().validate(msg);
+        assertTrue(result.isValidated);
+    }
+
+    @Test
+    public void testValidSubscribeRequest() {
+        SipMessage msg = new SipMessage(
+                "SUBSCRIBE sip:joe@example.com SIP/2.0",
+                "Via: SIP/2.0/UDP app.example.com;branch=z9hG4bKnashds7\n"
+                        + "From: sip:app.example.com;tag=123aa9\n"
+                        + "To: sip:joe@example.com\n"
+                        + "Call-ID: 9987@app.example.com\n"
+                        + "CSeq: 9887 SUBSCRIBE\n"
+                        + "Contact: sip:app.example.com\n"
+                        + "Event:    conference \n"
+                        + "Max-Forwards: 70\n",
+                new byte[0]);
+        ValidationResult result = new RestrictedOutgoingSubscribeValidator().validate(msg);
+        assertTrue(result.isValidated);
+    }
+
+    @Test
+    public void testInvalidSubscribeRequest() {
+        SipMessage msg = new SipMessage(
+                "SUBSCRIBE sip:joe@example.com SIP/2.0",
+                "Via: SIP/2.0/UDP app.example.com;branch=z9hG4bKnashds7\n"
+                        + "From: sip:app.example.com;tag=123aa9\n"
+                        + "To: sip:joe@example.com\n"
+                        + "Call-ID: 9987@app.example.com\n"
+                        + "CSeq: 9887 SUBSCRIBE\n"
+                        + "Contact: sip:app.example.com\n"
+                        + "Event:  presence  \n"
+                        + "Max-Forwards: 70\n",
+                new byte[0]);
+        ValidationResult result = new RestrictedOutgoingSubscribeValidator().validate(msg);
+        assertFalse(result.isValidated);
+        assertEquals(SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS,
+                result.restrictedReason);
+
+    }
+}