Merge "Revert "Do not use unstable Android styable.CheckBoxPreference""
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 34c67a4..ba60d67 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -34,7 +34,6 @@
     <protected-broadcast android:name="android.intent.action.ANY_DATA_STATE" />
     <protected-broadcast android:name="android.intent.action.DATA_STALL_DETECTED" />
     <protected-broadcast android:name="android.intent.action.SIM_STATE_CHANGED" />
-    <protected-broadcast android:name="android.telephony.action.NETWORK_SET_TIME" />
     <protected-broadcast android:name="com.android.internal.intent.action.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS" />
     <protected-broadcast android:name="android.intent.action.ACTION_MDN_STATE_CHANGED" />
     <protected-broadcast android:name="android.provider.Telephony.SPN_STRINGS_UPDATED" />
@@ -49,6 +48,7 @@
     <protected-broadcast android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />
     <protected-broadcast android:name="android.provider.Telephony.SMS_CB_RECEIVED" />
     <protected-broadcast android:name="android.provider.action.SMS_EMERGENCY_CB_RECEIVED" />
+    <protected-broadcast android:name="android.provider.Telephony.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED" />
     <protected-broadcast android:name="android.provider.Telephony.SECRET_CODE" />
     <protected-broadcast android:name= "com.android.internal.stk.command" />
     <protected-broadcast android:name= "com.android.internal.stk.session_end" />
@@ -76,7 +76,7 @@
     <protected-broadcast android:name= "com.android.imsconnection.DISCONNECTED" />
     <protected-broadcast android:name= "com.android.intent.action.IMS_FEATURE_CHANGED" />
     <protected-broadcast android:name= "com.android.intent.action.IMS_CONFIG_CHANGED" />
-    <protected-broadcast android:name= "com.android.ims.REGISTRATION_ERROR" />
+    <protected-broadcast android:name= "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR" />
     <protected-broadcast android:name= "com.android.phone.vvm.omtp.sms.REQUEST_SENT" />
     <protected-broadcast android:name= "com.android.phone.vvm.ACTION_VISUAL_VOICEMAIL_SERVICE_EVENT" />
     <protected-broadcast android:name= "com.android.internal.telephony.CARRIER_VVM_PACKAGE_INSTALLED" />
@@ -90,10 +90,14 @@
     <protected-broadcast android:name= "android.telephony.action.SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED" />
     <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" />
 
     <!-- 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" />
+
     <!-- Allows granting runtime permissions to telephony related components. -->
     <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS" />
 
@@ -130,7 +134,7 @@
     <uses-permission android:name="android.permission.SEND_SMS" />
     <uses-permission android:name="android.permission.SEND_RESPOND_VIA_MESSAGE" />
     <uses-permission android:name="android.permission.SET_TIME_ZONE" />
-    <uses-permission android:name="android.permission.SUGGEST_PHONE_TIME_AND_ZONE" />
+    <uses-permission android:name="android.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
     <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
@@ -209,6 +213,7 @@
          ACTION_SIM_SLOT_STATUS_CHANGED broadcast to start activities
          from the background.  -->
     <uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
+    <uses-permission android:name="android.permission.NETWORK_STATS_PROVIDER" />
 
     <application android:name="PhoneApp"
             android:persistent="true"
@@ -307,10 +312,19 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="CdmaCallForwardOptions"
+                android:label="@string/labelCF"
+                android:configChanges="orientation|screenSize|keyboardHidden"
+                android:theme="@style/DialerSettingsLight">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="GsmUmtsCallBarringOptions"
                 android:label="@string/labelCallBarring"
                 android:configChanges="orientation|screenSize|keyboardHidden"
-                android:theme="@style/CallSettingsWithoutDividerTheme">
+                android:theme="@style/DialerSettingsLight">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
             </intent-filter>
@@ -508,7 +522,7 @@
 
         <receiver android:name="com.android.services.telephony.sip.SipIncomingCallReceiver">
             <intent-filter>
-                <action android:name="com.android.phone.SIP_INCOMING_CALL" />
+                <action android:name="android.net.sip.action.SIP_INCOMING_CALL" />
             </intent-filter>
         </receiver>
 
@@ -539,6 +553,12 @@
                 android:uiOptions="splitActionBarWhenNarrow">
         </activity>
 
+        <service android:name="com.android.services.telephony.sip.components.TelephonySipService">
+            <intent-filter>
+                <action android:name="android.net.sip.action.START_SIP" />
+            </intent-filter>
+        </service>
+
         <!-- End SIP -->
 
         <activity android:name="MMIDialogActivity"
@@ -631,5 +651,33 @@
                 <action android:name="android.telephony.data.DataService" />
             </intent-filter>
         </service>
+
+        <provider
+            android:name="ServiceStateProvider"
+            android:authorities="service-state"
+            android:exported="true"
+            android:multiprocess="false"
+            android:singleUser="true"
+            android:writePermission="android.permission.MODIFY_PHONE_STATE"/>
+
+        <activity
+            android:name=".settings.RadioInfo"
+            android:label="@string/phone_info_label"
+            android:theme="@style/Theme.AppCompat.DayNight">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEVELOPMENT_PREFERENCE" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".settings.BandMode"
+                  android:label="@string/band_mode_title"
+                  android:theme="@style/Theme.AppCompat.DayNight">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.VOICE_LAUNCH" />
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
deleted file mode 100644
index e69de29..0000000
--- a/MODULE_LICENSE_APACHE2
+++ /dev/null
diff --git a/OWNERS b/OWNERS
index 849347f..3059d4d 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,15 +1,15 @@
 amitmahajan@google.com
 breadley@google.com
 fionaxu@google.com
-hallliu@google.com
 jackyu@google.com
+hallliu@google.com
 rgreenwalt@google.com
 tgunn@google.com
-refuhoo@google.com
-mpq@google.com
 jminjie@google.com
 shuoq@google.com
-paulye@google.com
+refuhoo@google.com
 nazaninb@google.com
 sarahchin@google.com
 dbright@google.com
+xiaotonj@google.com
+
diff --git a/apex/Android.bp b/apex/Android.bp
index 16bcb72..a7137d9 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -14,7 +14,7 @@
 apex {
     name: "com.android.telephony",
     manifest: "apex_manifest.json",
-    apps: ["StkLib"],
+    //apps: ["StkLib"],
 
     defaults:["com.android.telephony-defaults"],
 }
@@ -30,4 +30,4 @@
     // This will use com.android.telephony.x509.pem (the cert) and
     // com.android.telephony.pk8 (the private key)
     certificate: "com.android.telephony",
-}
\ No newline at end of file
+}
diff --git a/res/layout/emergency_dialer.xml b/res/layout/emergency_dialer.xml
index d14a679..ab32c62 100644
--- a/res/layout/emergency_dialer.xml
+++ b/res/layout/emergency_dialer.xml
@@ -58,8 +58,6 @@
         android:accessibilityPaneTitle="@string/pane_title_emergency_dialpad"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:paddingLeft="36dp"
-        android:paddingRight="36dp"
         android:paddingBottom="@dimen/dialpad_bottom_padding"
         android:visibility="visible">
         <LinearLayout
@@ -73,6 +71,7 @@
                 android:id="@+id/emergency_action_group"
                 android:layout_height="64dp"
                 android:layout_width="match_parent"
+                android:layout_marginHorizontal="36dp"
                 android:layout_marginTop="16dp"
                 android:layout_marginBottom="24dp">
 
@@ -155,8 +154,14 @@
                 </FrameLayout>
 
             </com.android.phone.EmergencyActionGroup>
-
+            <Space
+                android:id="@+id/emergency_info_dialpad_spacer"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1"/>
             <include layout="@layout/dialpad_view_unthemed"
+                     android:layout_height="wrap_content"
+                     android:layout_width="match_parent"
                      android:theme="?attr/dialpadTheme" />
 
         </LinearLayout>
diff --git a/res/layout/sim_ndp.xml b/res/layout/sim_ndp.xml
index 5e3c472..5f03d7b 100644
--- a/res/layout/sim_ndp.xml
+++ b/res/layout/sim_ndp.xml
@@ -29,6 +29,7 @@
             android:layout_centerInParent="true">
 
         <TextView
+                android:id="@+id/perso_subtype_text"
                 android:textAppearance="?android:attr/textAppearanceMedium"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
diff --git a/res/values-af/config.xml b/res/values-af/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-af/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-am/config.xml b/res/values-am/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-am/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ar/config.xml b/res/values-ar/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ar/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-as/config.xml b/res/values-as/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-as/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-az/config.xml b/res/values-az/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-az/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-b+sr+Latn/config.xml b/res/values-b+sr+Latn/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-b+sr+Latn/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-be/config.xml b/res/values-be/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-be/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-bg/config.xml b/res/values-bg/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-bg/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-bn/config.xml b/res/values-bn/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-bn/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-bs/config.xml b/res/values-bs/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-bs/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ca/config.xml b/res/values-ca/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ca/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-cs/config.xml b/res/values-cs/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-cs/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-da/config.xml b/res/values-da/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-da/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-de/config.xml b/res/values-de/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-de/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-el/config.xml b/res/values-el/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-el/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-en-rAU/config.xml b/res/values-en-rAU/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-en-rAU/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-en-rCA/config.xml b/res/values-en-rCA/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-en-rCA/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-en-rGB/config.xml b/res/values-en-rGB/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-en-rGB/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-en-rIN/config.xml b/res/values-en-rIN/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-en-rIN/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-en-rXC/config.xml b/res/values-en-rXC/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-en-rXC/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-es-rUS/config.xml b/res/values-es-rUS/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-es-rUS/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-es/config.xml b/res/values-es/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-es/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-et/config.xml b/res/values-et/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-et/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-eu/config.xml b/res/values-eu/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-eu/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-fa/config.xml b/res/values-fa/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-fa/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-fi/config.xml b/res/values-fi/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-fi/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-fr-rCA/config.xml b/res/values-fr-rCA/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-fr-rCA/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-fr/config.xml b/res/values-fr/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-fr/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-gl/config.xml b/res/values-gl/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-gl/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-gu/config.xml b/res/values-gu/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-gu/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-h500dp/dimens.xml b/res/values-h500dp/dimens.xml
index 2c7c797..d74f0a1 100644
--- a/res/values-h500dp/dimens.xml
+++ b/res/values-h500dp/dimens.xml
@@ -16,5 +16,5 @@
   -->
 
 <resources>
-    <dimen name="dialpad_bottom_padding">36dp</dimen>
+    <dimen name="dialpad_bottom_padding">16dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/res/values-hi/config.xml b/res/values-hi/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-hi/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-hr/config.xml b/res/values-hr/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-hr/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-hu/config.xml b/res/values-hu/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-hu/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-hy/config.xml b/res/values-hy/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-hy/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-in/config.xml b/res/values-in/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-in/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-is/config.xml b/res/values-is/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-is/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-it/config.xml b/res/values-it/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-it/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-iw/config.xml b/res/values-iw/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-iw/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ja/config.xml b/res/values-ja/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ja/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ka/config.xml b/res/values-ka/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ka/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-kk/config.xml b/res/values-kk/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-kk/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-km/config.xml b/res/values-km/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-km/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-kn/config.xml b/res/values-kn/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-kn/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ko/config.xml b/res/values-ko/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ko/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ky/config.xml b/res/values-ky/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ky/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-lo/config.xml b/res/values-lo/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-lo/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-lt/config.xml b/res/values-lt/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-lt/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-lv/config.xml b/res/values-lv/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-lv/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-mk/config.xml b/res/values-mk/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-mk/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ml/config.xml b/res/values-ml/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ml/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-mn/config.xml b/res/values-mn/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-mn/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-mr/config.xml b/res/values-mr/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-mr/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ms/config.xml b/res/values-ms/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ms/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-my/config.xml b/res/values-my/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-my/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-nb/config.xml b/res/values-nb/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-nb/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ne/config.xml b/res/values-ne/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ne/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-nl/config.xml b/res/values-nl/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-nl/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-or/config.xml b/res/values-or/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-or/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-pa/config.xml b/res/values-pa/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-pa/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-pl/config.xml b/res/values-pl/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-pl/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-pt-rPT/config.xml b/res/values-pt-rPT/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-pt-rPT/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-pt/config.xml b/res/values-pt/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-pt/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ro/config.xml b/res/values-ro/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ro/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ru/config.xml b/res/values-ru/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ru/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-si/config.xml b/res/values-si/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-si/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-sk/config.xml b/res/values-sk/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-sk/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-sl/config.xml b/res/values-sl/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-sl/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-sq/config.xml b/res/values-sq/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-sq/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-sr/config.xml b/res/values-sr/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-sr/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-sv/config.xml b/res/values-sv/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-sv/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-sw/config.xml b/res/values-sw/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-sw/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ta/config.xml b/res/values-ta/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ta/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-te/config.xml b/res/values-te/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-te/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-th/config.xml b/res/values-th/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-th/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-tl/config.xml b/res/values-tl/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-tl/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-tr/config.xml b/res/values-tr/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-tr/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-uk/config.xml b/res/values-uk/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-uk/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-ur/config.xml b/res/values-ur/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-ur/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-uz/config.xml b/res/values-uz/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-uz/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-vi/config.xml b/res/values-vi/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-vi/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-zh-rCN/config.xml b/res/values-zh-rCN/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-zh-rCN/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-zh-rHK/config.xml b/res/values-zh-rHK/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-zh-rHK/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-zh-rTW/config.xml b/res/values-zh-rTW/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-zh-rTW/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values-zu/config.xml b/res/values-zu/config.xml
deleted file mode 100644
index 509a3c8..0000000
--- a/res/values-zu/config.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  Copyright (C) 2009 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.
- -->
-
-<!--  NOTE: Many variables that used to be in this file have been migrated to
-     CarrierConfigManager.java. Please consider whether new variables belong
-     there before adding to this file. Variables here should be more closely
-     related to devices than to networks.  -->
-
-<!--  Phone app resources that may need to be customized
-     for different hardware or product builds.  -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="system_visual_voicemail_client" msgid="1787338073957698459"></string>
-</resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 8d84baf..fca8acf 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -15,21 +15,6 @@
 -->
 
 <resources>
-    <!-- Base attributes available to CheckBoxPreference. Copied from frameworks/base/core/res. -->
-    <declare-styleable name="CheckBoxPreference">
-        <!-- The summary for the Preference in a PreferenceActivity screen when the
-             CheckBoxPreference is checked. If separate on/off summaries are not
-             needed, the summary attribute can be used instead. -->
-        <attr name="android:summaryOn" />
-        <!-- The summary for the Preference in a PreferenceActivity screen when the
-             CheckBoxPreference is unchecked. If separate on/off summaries are not
-             needed, the summary attribute can be used instead. -->
-        <attr name="android:summaryOff" />
-        <!-- The state (true for on, or false for off) that causes dependents to be disabled. By default,
-             dependents will be disabled when this is unchecked, so the value of this preference is false. -->
-        <attr name="android:disableDependentsState" />
-    </declare-styleable>
-
     <declare-styleable name="EditPhoneNumberPreference">
         <!-- The enable button text. -->
         <attr name="enableButtonText" format="string" />
diff --git a/res/values/config.xml b/res/values/config.xml
index 8c36b1a..7e71068 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -209,7 +209,7 @@
 
     <!-- The package to handle visual voicemail if the default dialer or the package
     CarrierConfigManager.KEY_CARRIER_VVM_PACKAGE_NAME_STRING does not handle it -->
-    <string name="system_visual_voicemail_client"></string>
+    <string name="system_visual_voicemail_client" translatable="false"/>
 
     <!-- Flag to enable VVM3 visual voicemail. VVM3 is used by Verizon Wireless. -->
     <bool name="vvm3_enabled">false</bool>
@@ -236,10 +236,10 @@
     <bool name="config_support_rtt">false</bool>
 
     <!-- String indicating the package name of the device ImsService implementation for MMTEL. -->
-    <string name="config_ims_mmtel_package"></string>
+    <string name="config_ims_mmtel_package" translatable="false"/>
 
     <!-- String indicating the package name of the device ImsService implementation for RCS. -->
-    <string name="config_ims_rcs_package"></string>
+    <string name="config_ims_rcs_package" translatable="false"/>
 
     <!-- The package name for the platform number verification supplier app. -->
     <string name="platform_number_verification_package" translatable="false"></string>
@@ -257,6 +257,24 @@
          audio stream which the remote party will be able to hear. -->
     <bool name="config_support_telephony_audio_device">false</bool>
 
+    <!-- Whether the device supports dialing emergency RTT calls when there's no SIM card installed
+    -->
+    <bool name="config_support_simless_emergency_rtt">false</bool>
+
+    <!-- Array of countries that support sim-less emergency RTT calls. Values should be
+         ISO3166 country codes in lowercase. -->
+    <string-array name="config_simless_emergency_rtt_supported_countries" translatable="false">
+        <item>us</item>
+    </string-array>
+
+    <string-array translatable="false" name="config_volte_provision_error_on_publish_response">
+        <item>403 not authorized for presence</item>
+    </string-array>
+
+    <string-array translatable="false" name="config_rcs_provision_error_on_publish_response">
+        <item>404 not found</item>
+    </string-array>
+
     <!-- The country list that shortcut view can be enabled. -->
     <string-array name="config_countries_to_enable_shortcut_view" translatable="false">
     </string-array>
@@ -267,4 +285,18 @@
          slot. If true, telephony will always try to place the emergency call on the subscription
          associated with default data first, instead of using the default voice configuration.-->
     <bool name="config_gnss_supl_requires_default_data_for_emergency">false</bool>
+
+    <!-- Whether a device can have 5G connection in DSDS mode. It should be true by default, but
+         in some devices per modem limitation 5G network can't be connected if two or more SIMs
+         are active simultaneously. In that case, this value should be false. -->
+    <bool name="config_5g_connection_in_dsds_mode">true</bool>
+
+    <!-- Vibrator pattern to be used as the default for notifications
+         that specify DEFAULT_VIBRATE. -->
+    <integer-array name="config_defaultNotificationVibePattern">
+        <item>0</item>
+        <item>350</item>
+        <item>250</item>
+        <item>350</item>
+    </integer-array>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6ac95c3..b74b2ff 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -273,6 +273,15 @@
     <string name="sum_cfnrc_disabled">Off</string>
     <!-- Error message displayed after failing to disable forwarding calls when the phone is unreachable -->
     <string name="disable_cfnrc_forbidden">Your carrier doesn\'t support disabling call forwarding when your phone is unreachable.</string>
+    <string name="registration_cf_forbidden">Your carrier doesn\'t support call forwarding.</string>
+
+    <!-- Cdma Call waiting settings screen, setting option name -->
+    <string name="cdma_call_waiting">Turn on call waiting?</string>
+    <string name="enable_cdma_call_waiting_setting">During a call, you\'ll be notified about incoming calls</string>
+    <string name="enable_cdma_cw">Turn on</string>
+    <string name="disable_cdma_cw">Cancel</string>
+    <string name="cdma_call_waiting_in_ims_on">CDMA Call Waiting under IMS On</string>
+    <string name="cdma_call_waiting_in_ims_off">CDMA Call Waiting under IMS Off</string>
 
     <!-- Title of the progress dialog displayed while updating Call settings -->
     <string name="updating_title">Call settings</string>
@@ -1447,6 +1456,17 @@
     <string name="alert_dialog_no">No</string>
     <!-- ECM: ECM exit dialog choice -->
     <string name="alert_dialog_dismiss">Dismiss</string>
+    <!-- ECM: Notification body wihout data restriction hint -->
+    <string name="phone_in_ecm_call_notification_text_without_data_restriction_hint">The phone is in emergency callback mode</string>
+    <!-- ECM: Displays the time when ECM will end without data restriction hint, Example: "Until 10:45 AM" -->
+    <string name="phone_in_ecm_notification_complete_time_without_data_restriction_hint">Until <xliff:g id="completeTime">%s</xliff:g></string>
+    <!-- ECM: Dialog box message without data restriction hint for exiting from the notifications screen -->
+    <plurals name="alert_dialog_exit_ecm_without_data_restriction_hint">
+        <!-- number of minutes is one -->
+        <item quantity="one">The phone will be in emergency callback mode for <xliff:g id="count">%s</xliff:g> minute.\nDo you want to exit now?</item>
+        <!-- number of minutes is not equal to one -->
+        <item quantity="other">The phone will be in emergency callback mode for <xliff:g id="count">%s</xliff:g> minutes.\nDo you want to exit now?</item>
+    </plurals>
 
     <!-- For incoming calls, this is a string we can get from a CDMA network instead of
          the actual phone number, to indicate there's no number present.  DO NOT TRANSLATE. -->
@@ -1843,8 +1863,6 @@
     <string name="messageCallBarring">Enter password</string>
     <!-- Call barring settings screen, section heading -->
     <string name="call_barring_settings">Call barring settings</string>
-    <!-- Call barring settings screen, deactivate all call barring settings -->
-    <string name="call_barring_deactivate_all_no_password">Deactivate all call barring settings?</string>
     <!-- In-call screen: error message shown when the user attempts to place a call, but the network
          does not have enough resources (e.g. it is busy) and the call cannot be placed. -->
     <string name="callFailed_NetworkBusy">Network is busy.  Please try your call again later.</string>
@@ -2144,5 +2162,4 @@
     <string name="carrier_provisioning">Carrier Provisioning Info</string>
     <!-- Trigger Carrier Provisioning [CHAR LIMIT=NONE] -->
     <string name="trigger_carrier_provisioning">Trigger Carrier Provisioning</string>
-
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index d131bd8..e8a0bed 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -260,7 +260,7 @@
         <item name="android:navigationBarColor">@android:color/transparent</item>
         <item name="android:homeAsUpIndicator">@drawable/ic_back_arrow</item>
         <item name="emergencyButtonBackgroundColor">#3cffffff</item>
-        <item name="dialpadTheme">@style/Dialpad_DarkTransparent</item>
+        <item name="dialpadTheme">@style/Dialpad_DarkTransparent.Emergency</item>
     </style>
 
     <style name="EmergencyDialerThemeDark" parent="@style/EmergencyDialerTheme">
@@ -268,7 +268,15 @@
         <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
         <item name="emergencyButtonBackgroundColor">#19414549</item>
         <item name="android:colorControlHighlight">#40000000</item>
-        <item name="dialpadTheme">@style/Dialpad_LightTransparent</item>
+        <item name="dialpadTheme">@style/Dialpad_LightTransparent.Emergency</item>
+    </style>
+
+    <style name="Dialpad_LightTransparent.Emergency">
+        <item name="dialpad_delete_padding">16dp</item>
+    </style>
+
+    <style name="Dialpad_DarkTransparent.Emergency">
+        <item name="dialpad_delete_padding">16dp</item>
     </style>
 
     <style name="EmergencyDialerAlertDialogTheme"
diff --git a/res/xml/cdma_call_privacy.xml b/res/xml/cdma_call_privacy.xml
index 1aeeefe..a16a504 100644
--- a/res/xml/cdma_call_privacy.xml
+++ b/res/xml/cdma_call_privacy.xml
@@ -7,4 +7,14 @@
         android:title="@string/voice_privacy"
         android:persistent="false"
         android:summary="@string/voice_privacy_summary"/>
+
+    <PreferenceScreen
+        android:key="call_forwarding_key"
+        android:title="@string/labelCF"
+        android:persistent="false" />
+
+    <com.android.phone.CdmaCallWaitingPreference
+        android:key="call_waiting_key"
+        android:title="@string/labelCW"
+        android:persistent="false" />
 </PreferenceScreen>
diff --git a/sip/src/com/android/services/telephony/sip/SipIncomingCallReceiver.java b/sip/src/com/android/services/telephony/sip/SipIncomingCallReceiver.java
index 3212c00..2dbd707 100644
--- a/sip/src/com/android/services/telephony/sip/SipIncomingCallReceiver.java
+++ b/sip/src/com/android/services/telephony/sip/SipIncomingCallReceiver.java
@@ -84,7 +84,7 @@
     }
 
     private boolean isRunningInSystemUser() {
-        return UserHandle.myUserId() == UserHandle.USER_SYSTEM;
+        return UserHandle.myUserId() == UserHandle.SYSTEM.getIdentifier();
     }
 
     private static void log(String msg) {
diff --git a/sip/src/com/android/services/telephony/sip/SipSettings.java b/sip/src/com/android/services/telephony/sip/SipSettings.java
index ded16df..813ba51 100644
--- a/sip/src/com/android/services/telephony/sip/SipSettings.java
+++ b/sip/src/com/android/services/telephony/sip/SipSettings.java
@@ -42,6 +42,7 @@
 import com.android.phone.R;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.LinkedHashMap;
@@ -238,9 +239,9 @@
     }
 
     private void processActiveProfilesFromSipService() {
-        SipProfile[] activeList = {};
+        List<SipProfile> activeList = new ArrayList<>();
         try {
-            activeList = mSipManager.getListOfProfiles();
+            activeList = mSipManager.getProfiles();
         } catch (SipException e) {
             log("SipManager could not retrieve SIP profiles: " + e);
         }
diff --git a/sip/src/com/android/services/telephony/sip/SipUtil.java b/sip/src/com/android/services/telephony/sip/SipUtil.java
index ff38754..828174e 100644
--- a/sip/src/com/android/services/telephony/sip/SipUtil.java
+++ b/sip/src/com/android/services/telephony/sip/SipUtil.java
@@ -29,12 +29,11 @@
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.phone.PhoneGlobals;
-import com.android.phone.R;
-import com.android.server.sip.SipService;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -46,6 +45,7 @@
             "com.android.services.telephony.sip.incoming_call_intent";
     static final String EXTRA_PHONE_ACCOUNT =
             "com.android.services.telephony.sip.phone_account";
+    static final String PHONE_PACKAGE = "com.android.phone";
 
     private SipUtil() {
     }
@@ -53,9 +53,9 @@
     public static boolean isVoipSupported(Context context) {
         return SipManager.isVoipSupported(context) &&
                 context.getResources().getBoolean(
-                        com.android.internal.R.bool.config_built_in_sip_phone) &&
-                context.getResources().getBoolean(
-                        com.android.internal.R.bool.config_voice_capable);
+                        com.android.internal.R.bool.config_built_in_sip_phone)
+                && ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE))
+                        .isVoiceCapable();
     }
 
     static PendingIntent createIncomingCallPendingIntent(
@@ -190,7 +190,10 @@
         // Migrate SIP database from DE->CE storage if the device has just upgraded.
         possiblyMigrateSipDb(phoneGlobalsContext);
         // Wait until boot complete to start SIP so that it has access to CE storage.
-        SipService.start(phoneGlobalsContext);
+        Intent startSipIntent = new Intent();
+        startSipIntent.setAction(SipManager.ACTION_START_SIP);
+        startSipIntent.setPackage(PHONE_PACKAGE);
+        phoneGlobalsContext.startService(startSipIntent);
     }
 
     /**
diff --git a/sip/src/com/android/services/telephony/sip/components/TelephonySipService.java b/sip/src/com/android/services/telephony/sip/components/TelephonySipService.java
new file mode 100644
index 0000000..5b863b1
--- /dev/null
+++ b/sip/src/com/android/services/telephony/sip/components/TelephonySipService.java
@@ -0,0 +1,43 @@
+/*
+ * 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.sip.components;
+
+import android.app.Service;
+import android.content.Intent;
+import android.net.sip.SipManager;
+import android.os.IBinder;
+
+import com.android.server.sip.SipService;
+
+/**
+ * This class represents telephony SIP service which handle start SIP service requests from
+ * Telephony.
+ */
+public class TelephonySipService extends Service {
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if ((intent != null)
+                && SipManager.ACTION_START_SIP.equals(intent.getAction())) {
+            SipService.start(this);
+        }
+        return START_STICKY;
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+}
diff --git a/src/com/android/phone/CallBarringDeselectAllPreference.java b/src/com/android/phone/CallBarringDeselectAllPreference.java
index e9310f8..7191937 100644
--- a/src/com/android/phone/CallBarringDeselectAllPreference.java
+++ b/src/com/android/phone/CallBarringDeselectAllPreference.java
@@ -19,12 +19,9 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.View;
 import android.widget.EditText;
 
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.phone.settings.fdn.EditPinPreference;
 
 /**
@@ -34,9 +31,6 @@
     private static final String LOG_TAG = "CallBarringDeselectAllPreference";
     private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
 
-    private boolean mShowPassword;
-    private Phone mPhone;
-
     /**
      * CallBarringDeselectAllPreference constructor.
      *
@@ -49,28 +43,10 @@
 
     @Override
     protected void showDialog(Bundle state) {
-        // Finds out if the password field should be shown or not.
-        ImsPhone imsPhone = mPhone != null ? (ImsPhone) mPhone.getImsPhone() : null;
-        mShowPassword = !(imsPhone != null && imsPhone.isUtEnabled());
-
-        // Selects dialog message depending on if the password field is shown or not.
-        setDialogMessage(getContext().getString(mShowPassword
-                ? R.string.messageCallBarring : R.string.call_barring_deactivate_all_no_password));
-
-        if (DBG) {
-            Log.d(LOG_TAG, "showDialog: mShowPassword: " + mShowPassword);
-        }
-
+        setDialogMessage(getContext().getString(R.string.messageCallBarring));
         super.showDialog(state);
     }
 
-    void init(Phone phone) {
-        if (DBG) {
-            Log.d(LOG_TAG, "init: phoneId = " + phone.getPhoneId());
-        }
-        mPhone = phone;
-    }
-
     @Override
     protected void onBindDialogView(View view) {
         super.onBindDialogView(view);
@@ -78,20 +54,7 @@
         final EditText editText = (EditText) view.findViewById(android.R.id.edit);
         if (editText != null) {
             // Hide the input-text-line if the password is not shown.
-            editText.setVisibility(mShowPassword ? View.VISIBLE : View.GONE);
+            editText.setVisibility(View.VISIBLE);
         }
     }
-
-    @Override
-    protected boolean needInputMethod() {
-        // Input method should only be displayed if the password-field is shown.
-        return mShowPassword;
-    }
-
-    /**
-     * Returns whether the password field is shown.
-     */
-    boolean isPasswordShown() {
-        return mShowPassword;
-    }
 }
diff --git a/src/com/android/phone/CallBarringEditPreference.java b/src/com/android/phone/CallBarringEditPreference.java
index edff1e3..b8e3250 100644
--- a/src/com/android/phone/CallBarringEditPreference.java
+++ b/src/com/android/phone/CallBarringEditPreference.java
@@ -35,10 +35,10 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
-import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.phone.settings.fdn.EditPinPreference;
 
 import java.lang.ref.WeakReference;
@@ -57,10 +57,7 @@
     private CharSequence mDisableText;
     private CharSequence mSummaryOn;
     private CharSequence mSummaryOff;
-    private CharSequence mDialogMessageEnabled;
-    private CharSequence mDialogMessageDisabled;
     private int mButtonClicked;
-    private boolean mShowPassword;
     private final MyHandler mHandler = new MyHandler(this);
     private Phone mPhone;
     private TimeConsumingPreferenceListener mTcpListener;
@@ -90,10 +87,6 @@
         typedArray = context.obtainStyledAttributes(attrs,
                 R.styleable.CallBarringEditPreference, 0, R.style.EditPhoneNumberPreference);
         mFacility = typedArray.getString(R.styleable.CallBarringEditPreference_facility);
-        mDialogMessageEnabled = typedArray.getString(
-                R.styleable.CallBarringEditPreference_dialogMessageEnabledNoPwd);
-        mDialogMessageDisabled = typedArray.getString(
-                R.styleable.CallBarringEditPreference_dialogMessageDisabledNoPwd);
         typedArray.recycle();
     }
 
@@ -116,7 +109,7 @@
         if (!skipReading) {
             // Query call barring status
             mPhone.getCallBarring(mFacility, "", mHandler.obtainMessage(
-                    MyHandler.MESSAGE_GET_CALL_BARRING), 0);
+                    MyHandler.MESSAGE_GET_CALL_BARRING), CommandsInterface.SERVICE_CLASS_VOICE);
             if (mTcpListener != null) {
                 mTcpListener.onStarted(this, true);
             }
@@ -130,29 +123,8 @@
     }
 
     @Override
-    protected boolean needInputMethod() {
-        // Input method should only be displayed if the password-field is shown.
-        return mShowPassword;
-    }
-
-    void setInputMethodNeeded(boolean needed) {
-        mShowPassword = needed;
-    }
-
-    @Override
     protected void showDialog(Bundle state) {
-        setShowPassword();
-        if (mShowPassword) {
-            setDialogMessage(getContext().getString(R.string.messageCallBarring));
-        } else {
-            setDialogMessage(mIsActivated ? mDialogMessageEnabled : mDialogMessageDisabled);
-        }
-
-        if (DBG) {
-            Log.d(LOG_TAG, "showDialog: mShowPassword: " + mShowPassword
-                    + ", mIsActivated: " + mIsActivated);
-        }
-
+        setDialogMessage(getContext().getString(R.string.messageCallBarring));
         super.showDialog(state);
     }
 
@@ -204,8 +176,7 @@
             editText.setTransformationMethod(PasswordTransformationMethod.getInstance());
             editText.setKeyListener(DigitsKeyListener.getInstance());
 
-            // Hide the input-text-line if the password is not shown.
-            editText.setVisibility(mShowPassword ? View.VISIBLE : View.GONE);
+            editText.setVisibility(View.VISIBLE);
         }
     }
 
@@ -217,17 +188,14 @@
                     + positiveResult);
         }
         if (mButtonClicked != DialogInterface.BUTTON_NEGATIVE) {
-            String password = null;
-            if (mShowPassword) {
-                password = getEditText().getText().toString();
+            String password = getEditText().getText().toString();
 
-                // Check if the password is valid.
-                if (password == null || password.length() != PW_LENGTH) {
-                    Toast.makeText(getContext(),
-                            getContext().getString(R.string.call_barring_right_pwd_number),
-                            Toast.LENGTH_SHORT).show();
-                    return;
-                }
+            // Check if the password is valid.
+            if (password == null || password.length() != PW_LENGTH) {
+                Toast.makeText(getContext(),
+                        getContext().getString(R.string.call_barring_right_pwd_number),
+                        Toast.LENGTH_SHORT).show();
+                return;
             }
 
             if (DBG) {
@@ -235,7 +203,8 @@
             }
             // Send set call barring message to RIL layer.
             mPhone.setCallBarring(mFacility, !mIsActivated, password,
-                    mHandler.obtainMessage(MyHandler.MESSAGE_SET_CALL_BARRING), 0);
+                    mHandler.obtainMessage(MyHandler.MESSAGE_SET_CALL_BARRING),
+                    CommandsInterface.SERVICE_CLASS_VOICE);
             if (mTcpListener != null) {
                 mTcpListener.onStarted(this, false);
             }
@@ -254,11 +223,6 @@
         notifyDependencyChange(shouldDisableDependents());
     }
 
-    private void setShowPassword() {
-        ImsPhone imsPhone = mPhone != null ? (ImsPhone) mPhone.getImsPhone() : null;
-        mShowPassword = !(imsPhone != null && imsPhone.isUtEnabled());
-    }
-
     @Override
     public boolean shouldDisableDependents() {
         return mIsActivated;
@@ -310,16 +274,6 @@
                 pref.mTcpListener.onFinished(pref, false);
             } else {
                 pref.mTcpListener.onFinished(pref, true);
-                ImsPhone imsPhone = pref.mPhone != null
-                        ? (ImsPhone) pref.mPhone.getImsPhone() : null;
-                if (!pref.mShowPassword && (imsPhone == null || !imsPhone.isUtEnabled())) {
-                    // Re-enable password when rejected from NW and modem would perform CSFB
-                    pref.mShowPassword = true;
-                    if (DBG) {
-                        Log.d(LOG_TAG,
-                                "handleGetCallBarringResponse: mShowPassword changed for CSFB");
-                    }
-                }
             }
 
             // Unsuccessful query for call barring.
@@ -374,7 +328,7 @@
                     "",
                     obtainMessage(MESSAGE_GET_CALL_BARRING, 0, MESSAGE_SET_CALL_BARRING,
                             ar.exception),
-                    0);
+                    CommandsInterface.SERVICE_CLASS_VOICE);
         }
     }
 }
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index 3f57cae..ef83ead 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -97,6 +97,7 @@
             "phone_account_settings_preference_screen";
 
     private static final String ENABLE_VIDEO_CALLING_KEY = "button_enable_video_calling";
+    private static final String BUTTON_VP_KEY = "button_voice_privacy_key";
 
     private Phone mPhone;
     private ImsManager mImsMgr;
@@ -396,11 +397,10 @@
             } else {
                 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
                     prefSet.removePreference(fdnButton);
-
-                    if (!carrierConfig.getBoolean(
-                            CarrierConfigManager.KEY_VOICE_PRIVACY_DISABLE_UI_BOOL)) {
-                        addPreferencesFromResource(R.xml.cdma_call_privacy);
-                    }
+                    addPreferencesFromResource(R.xml.cdma_call_privacy);
+                    CdmaVoicePrivacySwitchPreference buttonVoicePrivacy =
+                            (CdmaVoicePrivacySwitchPreference) findPreference(BUTTON_VP_KEY);
+                    buttonVoicePrivacy.setPhone(mPhone);
                 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
                     if (mPhone.getIccCard() == null || !mPhone.getIccCard().getIccFdnAvailable()) {
                         prefSet.removePreference(fdnButton);
diff --git a/src/com/android/phone/CallForwardEditPreference.java b/src/com/android/phone/CallForwardEditPreference.java
index 76f17b1..8e0b685 100644
--- a/src/com/android/phone/CallForwardEditPreference.java
+++ b/src/com/android/phone/CallForwardEditPreference.java
@@ -122,7 +122,27 @@
         Log.d(LOG_TAG, "mButtonClicked=" + mButtonClicked + ", positiveResult=" + positiveResult);
         // Ignore this event if the user clicked the cancel button, or if the dialog is dismissed
         // without any button being pressed (back button press or click event outside the dialog).
-        if (this.mButtonClicked != DialogInterface.BUTTON_NEGATIVE) {
+        if (isUnknownStatus() && this.mButtonClicked != DialogInterface.BUTTON_NEGATIVE) {
+            int action = (mButtonClicked == DialogInterface.BUTTON_POSITIVE) ?
+                CommandsInterface.CF_ACTION_REGISTRATION :
+                CommandsInterface.CF_ACTION_DISABLE;
+            final String number = (action == CommandsInterface.CF_ACTION_DISABLE) ?
+                    "" : getPhoneNumber();
+
+            Log.d(LOG_TAG, "reason=" + reason + ", action=" + action + ", number=" + number);
+
+            // Display no forwarding number while we're waiting for confirmation.
+            setSummaryOff("");
+
+            mPhone.setCallForwardingOption(action,
+                    reason,
+                    number,
+                    mServiceClass,
+                    0,
+                    mHandler.obtainMessage(MyHandler.MESSAGE_SET_CF,
+                        action,
+                        MyHandler.MESSAGE_SET_CF));
+        } else if (this.mButtonClicked != DialogInterface.BUTTON_NEGATIVE) {
             int action = (isToggled() || (mButtonClicked == DialogInterface.BUTTON_POSITIVE)) ?
                     CommandsInterface.CF_ACTION_REGISTRATION :
                     CommandsInterface.CF_ACTION_DISABLE;
@@ -159,6 +179,7 @@
                     mPhone.setCallForwardingOption(action,
                             reason,
                             number,
+                            mServiceClass,
                             time,
                             mHandler.obtainMessage(MyHandler.MESSAGE_SET_CF,
                                     action,
@@ -193,6 +214,7 @@
             Log.i(LOG_TAG, "handleGetCFResponse: Overridding CF number");
         }
 
+        setUnknownStatus(callForwardInfo.status == CommandsInterface.SS_STATUS_UNKNOWN);
         setToggled(callForwardInfo.status == 1);
         boolean displayVoicemailNumber = false;
         if (TextUtils.isEmpty(callForwardInfo.number)) {
@@ -216,7 +238,7 @@
      */
     void startCallForwardOptionsQuery() {
         if (!mCallForwardByUssd) {
-            mPhone.getCallForwardingOption(reason,
+            mPhone.getCallForwardingOption(reason, mServiceClass,
                     mHandler.obtainMessage(MyHandler.MESSAGE_GET_CF,
                             // unused in this case
                             CommandsInterface.CF_ACTION_DISABLE,
@@ -342,6 +364,7 @@
             AsyncResult ar = (AsyncResult) msg.obj;
 
             callForwardInfo = null;
+            boolean summaryOff = false;
             if (ar.exception != null) {
                 Log.d(LOG_TAG, "handleGetCFResponse: ar.exception=" + ar.exception);
                 if (ar.exception instanceof CommandException) {
@@ -359,9 +382,8 @@
                     mTcpListener.onError(CallForwardEditPreference.this, RESPONSE_ERROR);
                 }
                 CallForwardInfo cfInfoArray[] = (CallForwardInfo[]) ar.result;
-                if (cfInfoArray.length == 0) {
+                if (cfInfoArray == null || cfInfoArray.length == 0) {
                     Log.d(LOG_TAG, "handleGetCFResponse: cfInfoArray.length==0");
-                    setEnabled(false);
                     mTcpListener.onError(CallForwardEditPreference.this, RESPONSE_ERROR);
                 } else {
                     for (int i = 0, length = cfInfoArray.length; i < length; i++) {
@@ -372,10 +394,16 @@
                             CallForwardInfo info = cfInfoArray[i];
                             handleCallForwardResult(info);
 
+                            summaryOff = (info.status == CommandsInterface.SS_STATUS_UNKNOWN);
+
+                            if (ar.userObj instanceof Throwable) {
+                                Log.d(LOG_TAG, "Skipped duplicated error dialog");
+                                continue;
+                            }
+
                             // Show an alert if we got a success response but
                             // with unexpected values.
-                            // Currently only handle the fail-to-disable case
-                            // since we haven't observed fail-to-enable.
+                            // Handle the fail-to-disable case.
                             if (msg.arg2 == MESSAGE_SET_CF &&
                                     msg.arg1 == CommandsInterface.CF_ACTION_DISABLE &&
                                     info.status == 1) {
@@ -399,7 +427,21 @@
                                 }
                                 AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
                                 builder.setNeutralButton(R.string.close_dialog, null);
-                                builder.setTitle(getContext().getText(R.string.error_updating_title));
+                                builder.setTitle(getContext()
+                                        .getText(R.string.error_updating_title));
+                                builder.setMessage(s);
+                                builder.setCancelable(true);
+                                builder.create().show();
+                            } else if (msg.arg2 == MESSAGE_SET_CF &&
+                                    msg.arg1 == CommandsInterface.CF_ACTION_REGISTRATION &&
+                                    info.status == 0) {
+                                // Handle the fail-to-enable case.
+                                CharSequence s = getContext()
+                                    .getText(R.string.registration_cf_forbidden);
+                                AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+                                builder.setNeutralButton(R.string.close_dialog, null);
+                                builder.setTitle(getContext()
+                                        .getText(R.string.error_updating_title));
                                 builder.setMessage(s);
                                 builder.setCancelable(true);
                                 builder.create().show();
@@ -412,7 +454,15 @@
             // Now whether or not we got a new number, reset our enabled
             // summary text since it may have been replaced by an empty
             // placeholder.
-            updateSummaryText();
+            // for CDMA, doesn't display summary.
+            if (summaryOff) {
+                setSummaryOff("");
+            } else {
+                // Now whether or not we got a new number, reset our enabled
+                // summary text since it may have been replaced by an empty
+                // placeholder.
+                updateSummaryText();
+            }
         }
 
         private void handleSetCFResponse(Message msg) {
@@ -421,9 +471,19 @@
                 Log.d(LOG_TAG, "handleSetCFResponse: ar.exception=" + ar.exception);
                 // setEnabled(false);
             }
+
+            if (ar.result != null) {
+                int arr = (int)ar.result;
+                if (arr == CommandsInterface.SS_STATUS_UNKNOWN) {
+                    Log.d(LOG_TAG, "handleSetCFResponse: no need to re get in CDMA");
+                    mTcpListener.onFinished(CallForwardEditPreference.this, false);
+                    return;
+                }
+            }
+
             Log.d(LOG_TAG, "handleSetCFResponse: re get");
             if (!mCallForwardByUssd) {
-                mPhone.getCallForwardingOption(reason,
+                mPhone.getCallForwardingOption(reason, mServiceClass,
                         obtainMessage(MESSAGE_GET_CF, msg.arg1, MESSAGE_SET_CF, ar.exception));
             } else {
                 mHandler.sendMessage(mHandler.obtainMessage(mHandler.MESSAGE_GET_CF_USSD,
diff --git a/src/com/android/phone/CallNotifier.java b/src/com/android/phone/CallNotifier.java
index 0af1b23..23a9649 100644
--- a/src/com/android/phone/CallNotifier.java
+++ b/src/com/android/phone/CallNotifier.java
@@ -21,6 +21,7 @@
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.media.AudioManager;
+import android.media.AudioSystem;
 import android.media.ToneGenerator;
 import android.os.AsyncResult;
 import android.os.Handler;
@@ -392,10 +393,10 @@
             try {
                 int stream;
                 if (mBluetoothHeadset != null) {
-                    stream = isBluetoothAudioOn() ? AudioManager.STREAM_BLUETOOTH_SCO :
-                        AudioManager.STREAM_VOICE_CALL;
+                    stream = isBluetoothAudioOn() ? AudioSystem.STREAM_BLUETOOTH_SCO :
+                            AudioSystem.STREAM_VOICE_CALL;
                 } else {
-                    stream = AudioManager.STREAM_VOICE_CALL;
+                    stream = AudioSystem.STREAM_VOICE_CALL;
                 }
                 toneGenerator = new ToneGenerator(stream, toneVolume);
                 // if (DBG) log("- created toneGenerator: " + toneGenerator);
@@ -616,7 +617,7 @@
         for (int i = 0; i < subInfos.size(); i++) {
             int subId = subInfos.get(i).getSubscriptionId();
             if (!mPhoneStateListeners.containsKey(subId)) {
-                CallNotifierPhoneStateListener listener = new CallNotifierPhoneStateListener();
+                CallNotifierPhoneStateListener listener = new CallNotifierPhoneStateListener(subId);
                 mTelephonyManager.createForSubscriptionId(subId).listen(listener,
                         PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
                         | PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR);
@@ -768,8 +769,11 @@
             };
 
     private class CallNotifierPhoneStateListener extends PhoneStateListener {
-        public CallNotifierPhoneStateListener() {
+        private final int mSubId;
+
+        CallNotifierPhoneStateListener(int subId) {
             super();
+            this.mSubId = subId;
         }
 
         @Override
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 3812b2f..f181e9f 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -1,4 +1,4 @@
-/**
+/*
  * Copyright (c) 2015, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,6 +21,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.AppOpsManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -37,9 +38,11 @@
 import android.os.IBinder;
 import android.os.Message;
 import android.os.PersistableBundle;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.preference.PreferenceManager;
 import android.service.carrier.CarrierIdentifier;
 import android.service.carrier.CarrierService;
@@ -47,6 +50,8 @@
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.LocalLog;
 import android.util.Log;
 
@@ -71,6 +76,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 /**
  * CarrierConfigLoader binds to privileged carrier apps to fetch carrier config overlays.
@@ -95,8 +101,12 @@
     private PersistableBundle[] mOverrideConfigs;
     // Service connection for binding to config app.
     private CarrierServiceConnection[] mServiceConnection;
-    // Whether we have sent config change bcast for each phone id.
+    // Whether we are bound to a service for each phone
+    private boolean[] mServiceBound;
+    // Whether we have sent config change broadcast for each phone id.
     private boolean[] mHasSentConfigChange;
+    // Whether the broadcast was sent from EVENT_SYSTEM_UNLOCKED, to track rebroadcasts
+    private boolean[] mFromSystemUnlocked;
     // SubscriptionInfoUpdater
     private final SubscriptionInfoUpdater mSubscriptionInfoUpdater;
 
@@ -151,6 +161,12 @@
     // SharedPreferences key for last known build fingerprint.
     private static final String KEY_FINGERPRINT = "build_fingerprint";
 
+    // Argument for #dump that indicates we should also call the default and specified carrier
+    // service's #dump method. In multi-SIM devices, it's possible that carrier A is on SIM 1 and
+    // carrier B is on SIM 2, in which case we should not dump carrier B's service when carrier A
+    // requested the dump.
+    private static final String DUMP_ARG_REQUESTING_PACKAGE = "--requesting-package";
+
     // Handler to process various events.
     //
     // For each phoneId, the event sequence should be:
@@ -172,42 +188,41 @@
         @Override
         public void handleMessage(Message msg) {
             final int phoneId = msg.arg1;
-            logWithLocalLog("mHandler: " + msg.what + " phoneId: " + phoneId);
+            logdWithLocalLog("mHandler: " + msg.what + " phoneId: " + phoneId);
             if (!SubscriptionManager.isValidPhoneId(phoneId)
                     && msg.what != EVENT_MULTI_SIM_CONFIG_CHANGED) {
                 return;
             }
             switch (msg.what) {
-                case EVENT_CLEAR_CONFIG:
-                {
+                case EVENT_CLEAR_CONFIG: {
                     clearConfigForPhone(phoneId, true);
                     break;
                 }
 
-                case EVENT_SYSTEM_UNLOCKED:
-                {
+                case EVENT_SYSTEM_UNLOCKED: {
                     for (int i = 0; i < TelephonyManager.from(mContext).getActiveModemCount();
                             ++i) {
-                        // When user unlock device, we should only try to send broadcast again if we
-                        // have sent it before unlock. This will avoid we try to load carrier config
-                        // when SIM is still loading when unlock happens.
+                        // When the user unlocks the device, send the broadcast again (with a
+                        // rebroadcast extra) if we have sent it before unlock. This will avoid
+                        // trying to load the carrier config when the SIM is still loading when the
+                        // unlock happens.
                         if (mHasSentConfigChange[i]) {
-                            logWithLocalLog("System unlocked");
+                            logdWithLocalLog("System unlocked");
+                            mFromSystemUnlocked[i] = true;
                             updateConfigForPhoneId(i);
                         }
                     }
                     break;
                 }
 
-                case EVENT_PACKAGE_CHANGED:
-                {
+                case EVENT_PACKAGE_CHANGED: {
                     final String carrierPackageName = (String) msg.obj;
                     // Only update if there are cached config removed to avoid updating config for
                     // unrelated packages.
                     if (clearCachedConfigForPackage(carrierPackageName)) {
                         int numPhones = TelephonyManager.from(mContext).getActiveModemCount();
                         for (int i = 0; i < numPhones; ++i) {
-                            logWithLocalLog("Package changed: " + carrierPackageName
+                            logdWithLocalLog("Package changed: " + carrierPackageName
                                     + ", phone=" + i);
                             updateConfigForPhoneId(i);
                         }
@@ -215,13 +230,12 @@
                     break;
                 }
 
-                case EVENT_DO_FETCH_DEFAULT:
-                {
+                case EVENT_DO_FETCH_DEFAULT: {
                     // Restore persistent override values.
                     PersistableBundle config = restoreConfigFromXml(
                             mPlatformCarrierConfigPackage, OVERRIDE_PACKAGE_ADDITION, phoneId);
                     if (config != null) {
-                        log("Loaded persistent override config from XML. package="
+                        logd("Loaded persistent override config from XML. package="
                                 + mPlatformCarrierConfigPackage
                                 + " phoneId=" + phoneId);
                         mPersistentOverrideConfigs[phoneId] = config;
@@ -229,7 +243,7 @@
 
                     config = restoreConfigFromXml(mPlatformCarrierConfigPackage, "", phoneId);
                     if (config != null) {
-                        log(
+                        logd(
                                 "Loaded config from XML. package="
                                         + mPlatformCarrierConfigPackage
                                         + " phoneId="
@@ -259,13 +273,12 @@
                     break;
                 }
 
-                case EVENT_CONNECTED_TO_DEFAULT:
-                {
+                case EVENT_CONNECTED_TO_DEFAULT: {
                     removeMessages(EVENT_BIND_DEFAULT_TIMEOUT);
                     final CarrierServiceConnection conn = (CarrierServiceConnection) msg.obj;
                     // If new service connection has been created, unbind.
                     if (mServiceConnection[phoneId] != conn || conn.service == null) {
-                        mContext.unbindService(conn);
+                        unbindIfBound(mContext, conn, phoneId);
                         break;
                     }
                     final CarrierIdentifier carrierId = getCarrierIdentifierForPhoneId(phoneId);
@@ -274,7 +287,7 @@
                             new ResultReceiver(this) {
                                 @Override
                                 public void onReceiveResult(int resultCode, Bundle resultData) {
-                                    mContext.unbindService(conn);
+                                    unbindIfBound(mContext, conn, phoneId);
                                     // If new service connection has been created, this is stale.
                                     if (mServiceConnection[phoneId] != conn) {
                                         loge("Received response for stale request.");
@@ -290,7 +303,7 @@
                                     PersistableBundle config =
                                             resultData.getParcelable(KEY_CONFIG_BUNDLE);
                                     saveConfigToXml(mPlatformCarrierConfigPackage, "", phoneId,
-                                        carrierId, config);
+                                            carrierId, config);
                                     mConfigFromDefaultApp[phoneId] = config;
                                     sendMessage(
                                             obtainMessage(
@@ -302,13 +315,13 @@
                         ICarrierService carrierService =
                                 ICarrierService.Stub.asInterface(conn.service);
                         carrierService.getCarrierConfig(carrierId, resultReceiver);
-                        logWithLocalLog("fetch config for default app: "
+                        logdWithLocalLog("Fetch config for default app: "
                                 + mPlatformCarrierConfigPackage
                                 + " carrierid: " + carrierId.toString());
                     } catch (RemoteException e) {
                         loge("Failed to get carrier config from default app: " +
                                 mPlatformCarrierConfigPackage + " err: " + e.toString());
-                        mContext.unbindService(conn);
+                        unbindIfBound(mContext, conn, phoneId);
                         break; // So we don't set a timeout.
                     }
                     sendMessageDelayed(
@@ -318,9 +331,8 @@
                 }
 
                 case EVENT_BIND_DEFAULT_TIMEOUT:
-                case EVENT_FETCH_DEFAULT_TIMEOUT:
-                {
-                    loge("bind/fetch time out from " + mPlatformCarrierConfigPackage);
+                case EVENT_FETCH_DEFAULT_TIMEOUT: {
+                    loge("Bind/fetch time out from " + mPlatformCarrierConfigPackage);
                     removeMessages(EVENT_FETCH_DEFAULT_TIMEOUT);
                     // 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
@@ -328,15 +340,14 @@
                     if (mServiceConnection[phoneId] != null) {
                         // If a ResponseReceiver callback is in the queue when this happens, we will
                         // unbind twice and throw an exception.
-                        mContext.unbindService(mServiceConnection[phoneId]);
+                        unbindIfBound(mContext, mServiceConnection[phoneId], phoneId);
                         broadcastConfigChangedIntent(phoneId);
                     }
                     notifySubscriptionInfoUpdater(phoneId);
                     break;
                 }
 
-                case EVENT_FETCH_DEFAULT_DONE:
-                {
+                case EVENT_FETCH_DEFAULT_DONE: {
                     // If we attempted to bind to the app, but the service connection is null, then
                     // config was cleared while we were waiting and we should not continue.
                     if (!msg.getData().getBoolean("loaded_from_xml", false)
@@ -345,7 +356,7 @@
                     }
                     final String carrierPackageName = getCarrierPackageForPhoneId(phoneId);
                     if (carrierPackageName != null) {
-                        log("Found carrier config app: " + carrierPackageName);
+                        logd("Found carrier config app: " + carrierPackageName);
                         sendMessage(obtainMessage(EVENT_DO_FETCH_CARRIER, phoneId, -1));
                     } else {
                         notifySubscriptionInfoUpdater(phoneId);
@@ -353,13 +364,12 @@
                     break;
                 }
 
-                case EVENT_DO_FETCH_CARRIER:
-                {
+                case EVENT_DO_FETCH_CARRIER: {
                     final String carrierPackageName = getCarrierPackageForPhoneId(phoneId);
                     final PersistableBundle config =
                             restoreConfigFromXml(carrierPackageName, "", phoneId);
                     if (config != null) {
-                        log(
+                        logd(
                                 "Loaded config from XML. package="
                                         + carrierPackageName
                                         + " phoneId="
@@ -370,31 +380,27 @@
                         sendMessage(newMsg);
                     } else {
                         // No cached config, so fetch it from a carrier app.
-                        if (carrierPackageName != null
-                                && bindToConfigPackage(
-                                        carrierPackageName,
-                                        phoneId,
-                                        EVENT_CONNECTED_TO_CARRIER)) {
+                        if (carrierPackageName != null && bindToConfigPackage(carrierPackageName,
+                                phoneId, EVENT_CONNECTED_TO_CARRIER)) {
                             sendMessageDelayed(
                                     obtainMessage(EVENT_BIND_CARRIER_TIMEOUT, phoneId, -1),
                                     BIND_TIMEOUT_MILLIS);
                         } else {
                             // Send broadcast if bind fails.
                             broadcastConfigChangedIntent(phoneId);
-                            loge("bind to carrier app: " + carrierPackageName + " fails");
+                            loge("Bind to carrier app: " + carrierPackageName + " fails");
                             notifySubscriptionInfoUpdater(phoneId);
                         }
                     }
                     break;
                 }
 
-                case EVENT_CONNECTED_TO_CARRIER:
-                {
+                case EVENT_CONNECTED_TO_CARRIER: {
                     removeMessages(EVENT_BIND_CARRIER_TIMEOUT);
                     final CarrierServiceConnection conn = (CarrierServiceConnection) msg.obj;
                     // If new service connection has been created, unbind.
                     if (mServiceConnection[phoneId] != conn || conn.service == null) {
-                        mContext.unbindService(conn);
+                        unbindIfBound(mContext, conn, phoneId);
                         break;
                     }
                     final CarrierIdentifier carrierId = getCarrierIdentifierForPhoneId(phoneId);
@@ -403,7 +409,7 @@
                             new ResultReceiver(this) {
                                 @Override
                                 public void onReceiveResult(int resultCode, Bundle resultData) {
-                                    mContext.unbindService(conn);
+                                    unbindIfBound(mContext, conn, phoneId);
                                     // If new service connection has been created, this is stale.
                                     if (mServiceConnection[phoneId] != conn) {
                                         loge("Received response for stale request.");
@@ -433,12 +439,12 @@
                         ICarrierService carrierService =
                                 ICarrierService.Stub.asInterface(conn.service);
                         carrierService.getCarrierConfig(carrierId, resultReceiver);
-                        logWithLocalLog("fetch config for carrier app: "
+                        logdWithLocalLog("Fetch config for carrier app: "
                                 + getCarrierPackageForPhoneId(phoneId)
                                 + " carrierid: " + carrierId.toString());
                     } catch (RemoteException e) {
                         loge("Failed to get carrier config: " + e.toString());
-                        mContext.unbindService(conn);
+                        unbindIfBound(mContext, conn, phoneId);
                         break; // So we don't set a timeout.
                     }
                     sendMessageDelayed(
@@ -448,9 +454,8 @@
                 }
 
                 case EVENT_BIND_CARRIER_TIMEOUT:
-                case EVENT_FETCH_CARRIER_TIMEOUT:
-                {
-                    loge("bind/fetch from carrier app timeout");
+                case EVENT_FETCH_CARRIER_TIMEOUT: {
+                    loge("Bind/fetch from carrier app timeout");
                     removeMessages(EVENT_FETCH_CARRIER_TIMEOUT);
                     // 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
@@ -458,14 +463,13 @@
                     if (mServiceConnection[phoneId] != null) {
                         // If a ResponseReceiver callback is in the queue when this happens, we will
                         // unbind twice and throw an exception.
-                        mContext.unbindService(mServiceConnection[phoneId]);
+                        unbindIfBound(mContext, mServiceConnection[phoneId], phoneId);
                         broadcastConfigChangedIntent(phoneId);
                     }
                     notifySubscriptionInfoUpdater(phoneId);
                     break;
                 }
-                case EVENT_FETCH_CARRIER_DONE:
-                {
+                case EVENT_FETCH_CARRIER_DONE: {
                     // If we attempted to bind to the app, but the service connection is null, then
                     // config was cleared while we were waiting and we should not continue.
                     if (!msg.getData().getBoolean("loaded_from_xml", false)
@@ -476,13 +480,12 @@
                     break;
                 }
 
-                case EVENT_CHECK_SYSTEM_UPDATE:
-                {
+                case EVENT_CHECK_SYSTEM_UPDATE: {
                     SharedPreferences sharedPrefs =
                             PreferenceManager.getDefaultSharedPreferences(mContext);
                     final String lastFingerprint = sharedPrefs.getString(KEY_FINGERPRINT, null);
                     if (!Build.FINGERPRINT.equals(lastFingerprint)) {
-                        log(
+                        logd(
                                 "Build fingerprint changed. old: "
                                         + lastFingerprint
                                         + " new: "
@@ -537,10 +540,12 @@
         mPersistentOverrideConfigs = new PersistableBundle[numPhones];
         mOverrideConfigs = new PersistableBundle[numPhones];
         mServiceConnection = new CarrierServiceConnection[numPhones];
+        mServiceBound = new boolean[numPhones];
         mHasSentConfigChange = new boolean[numPhones];
+        mFromSystemUnlocked = new boolean[numPhones];
         // Make this service available through ServiceManager.
         ServiceManager.addService(Context.CARRIER_CONFIG_SERVICE, this);
-        log("CarrierConfigLoader has started");
+        logd("CarrierConfigLoader has started");
         mSubscriptionInfoUpdater = PhoneFactory.getSubscriptionInfoUpdater();
         mHandler.sendEmptyMessage(EVENT_CHECK_SYSTEM_UPDATE);
     }
@@ -593,6 +598,10 @@
             configToSend = mConfigFromDefaultApp[phoneId];
         }
 
+        if (configToSend == null) {
+            configToSend = new PersistableBundle();
+        }
+
         // mOverrideConfigs is for testing. And it will override current configs.
         PersistableBundle config = mOverrideConfigs[phoneId];
         if (config != null) {
@@ -612,7 +621,6 @@
     private void broadcastConfigChangedIntent(int phoneId, boolean addSubIdExtra) {
         Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
-                Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND |
                 Intent.FLAG_RECEIVER_FOREGROUND);
         if (addSubIdExtra) {
             int simApplicationState = TelephonyManager.SIM_STATE_UNKNOWN;
@@ -622,6 +630,8 @@
                         .createForSubscriptionId(subIds[0]);
                 simApplicationState = telMgr.getSimApplicationState();
             }
+            logd("Broadcast CARRIER_CONFIG_CHANGED for phone " + phoneId
+                    + " simApplicationState " + simApplicationState);
             // Include subId/carrier id extra only if SIM records are loaded
             if (simApplicationState != TelephonyManager.SIM_STATE_UNKNOWN
                     && simApplicationState != TelephonyManager.SIM_STATE_NOT_READY) {
@@ -632,20 +642,33 @@
             }
         }
         intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, phoneId);
-        log("Broadcast CARRIER_CONFIG_CHANGED for phone " + phoneId);
-        mContext.sendBroadcast(intent);
+        intent.putExtra(CarrierConfigManager.EXTRA_REBROADCAST_ON_UNLOCK,
+                mFromSystemUnlocked[phoneId]);
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        int[] subIds = SubscriptionManager.getSubId(phoneId);
+        if (subIds != null && subIds.length > 0) {
+            logd("Broadcast CARRIER_CONFIG_CHANGED for phone " + phoneId + ", subId=" + subIds[0]);
+        } else {
+            logd("Broadcast CARRIER_CONFIG_CHANGED for phone " + phoneId);
+        }
         mHasSentConfigChange[phoneId] = true;
+        mFromSystemUnlocked[phoneId] = false;
     }
 
     /** Binds to the default or carrier config app. */
     private boolean bindToConfigPackage(String pkgName, int phoneId, int eventId) {
-        logWithLocalLog("Binding to " + pkgName + " for phone " + phoneId);
+        logdWithLocalLog("Binding to " + pkgName + " for phone " + phoneId);
         Intent carrierService = new Intent(CarrierService.CARRIER_SERVICE_INTERFACE);
         carrierService.setPackage(pkgName);
-        mServiceConnection[phoneId] = new CarrierServiceConnection(phoneId, eventId);
+        mServiceConnection[phoneId] = new CarrierServiceConnection(phoneId, pkgName, eventId);
         try {
-            return mContext.bindService(carrierService, mServiceConnection[phoneId],
-                    Context.BIND_AUTO_CREATE);
+            if (mContext.bindService(carrierService, mServiceConnection[phoneId],
+                    Context.BIND_AUTO_CREATE)) {
+                mServiceBound[phoneId] = true;
+                return true;
+            } else {
+                return false;
+            }
         } catch (SecurityException ex) {
             return false;
         }
@@ -745,9 +768,9 @@
      *
      * @param packageName the name of the package from which we fetched this bundle.
      * @param extraString An extra string to be used in the XML file name.
-     * @param phoneId the phone ID.
-     * @param carrierId contains all carrier-identifying information.
-     * @param config the bundle to be written. Null will be treated as an empty bundle.
+     * @param phoneId     the phone ID.
+     * @param carrierId   contains all carrier-identifying information.
+     * @param config      the bundle to be written. Null will be treated as an empty bundle.
      */
     private void saveConfigToXml(String packageName, @NonNull String extraString, int phoneId,
             CarrierIdentifier carrierId, PersistableBundle config) {
@@ -778,7 +801,8 @@
             return;
         }
 
-        logWithLocalLog("save config to xml, packagename: " + packageName + " phoneId: " + phoneId);
+        logdWithLocalLog(
+                "Save config to xml, packagename: " + packageName + " phoneId: " + phoneId);
 
         FileOutputStream outFile = null;
         try {
@@ -789,8 +813,7 @@
             config.writeToStream(outFile);
             outFile.flush();
             outFile.close();
-        }
-        catch (IOException e) {
+        } catch (IOException e) {
             loge(e.toString());
         }
     }
@@ -806,9 +829,9 @@
      *
      * @param packageName the name of the package from which we fetched this bundle.
      * @param extraString An extra string to be used in the XML file name.
-     * @param phoneId the phone ID.
+     * @param phoneId     the phone ID.
      * @return the bundle from the XML file. Returns null if there is no saved config, the saved
-     *         version does not match, or reading config fails.
+     * version does not match, or reading config fails.
      */
     private PersistableBundle restoreConfigFromXml(String packageName, @NonNull String extraString,
             int phoneId) {
@@ -848,13 +871,11 @@
             }
 
             inFile.close();
-        }
-        catch (FileNotFoundException e) {
+        } catch (FileNotFoundException e) {
             // Missing file is normal occurrence that might occur with a new sim or when restoring
             // an override file during boot and should not be treated as an error.
-            if (file != null) log("File not found: " + file.getPath());
-        }
-        catch (IOException e) {
+            if (file != null) logd("File not found: " + file.getPath());
+        } catch (IOException e) {
             loge(e.toString());
         }
 
@@ -883,7 +904,7 @@
         });
         if (packageFiles == null || packageFiles.length < 1) return false;
         for (File f : packageFiles) {
-            log("deleting " + f.getName());
+            logd("Deleting " + f.getName());
             f.delete();
         }
         return true;
@@ -891,7 +912,7 @@
 
     /** Builds a canonical file name for a config file. */
     private String getFilenameForConfig(@NonNull String packageName, @NonNull String extraString,
-                                        @NonNull String iccid, int cid) {
+            @NonNull String iccid, int cid) {
         // the same carrier should have a single copy of XML file named after carrier id.
         // However, it's still possible that platform doesn't recognize the current sim carrier,
         // we will use iccid + carrierid as the canonical file name. carrierid can also handle the
@@ -934,12 +955,14 @@
     }
 
     @Override
-    public @NonNull PersistableBundle getConfigForSubId(int subId, String callingPackage) {
+    @NonNull
+    public PersistableBundle getConfigForSubId(int subId, String callingPackage) {
         return getConfigForSubIdWithFeature(subId, callingPackage, null);
     }
 
     @Override
-    public @NonNull PersistableBundle getConfigForSubIdWithFeature(int subId, String callingPackage,
+    @NonNull
+    public PersistableBundle getConfigForSubIdWithFeature(int subId, String callingPackage,
             String callingFeatureId) {
         if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId, callingPackage,
                 callingFeatureId, "getCarrierConfig")) {
@@ -983,7 +1006,7 @@
         //TODO: Also check for SHELL UID to restrict this method to testing only (b/131326259)
         int phoneId = SubscriptionManager.getPhoneId(subscriptionId);
         if (!SubscriptionManager.isValidPhoneId(phoneId)) {
-            log("Ignore invalid phoneId: " + phoneId + " for subId: " + subscriptionId);
+            logd("Ignore invalid phoneId: " + phoneId + " for subId: " + subscriptionId);
             return;
         }
         overrideConfig(mOverrideConfigs, phoneId, overrides);
@@ -1022,7 +1045,7 @@
     public void notifyConfigChangedForSubId(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
         if (!SubscriptionManager.isValidPhoneId(phoneId)) {
-            log("Ignore invalid phoneId: " + phoneId + " for subId: " + subId);
+            logd("Ignore invalid phoneId: " + phoneId + " for subId: " + subId);
             return;
         }
 
@@ -1044,7 +1067,7 @@
     public void updateConfigForPhoneId(int phoneId, String simState) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.MODIFY_PHONE_STATE, null);
-        logWithLocalLog("update config for phoneId: " + phoneId + " simState: " + simState);
+        logdWithLocalLog("Update config for phoneId: " + phoneId + " simState: " + simState);
         if (!SubscriptionManager.isValidPhoneId(phoneId)) {
             return;
         }
@@ -1072,44 +1095,78 @@
         return mPlatformCarrierConfigPackage;
     }
 
+    private void unbindIfBound(Context context, CarrierServiceConnection conn,
+            int phoneId) {
+        if (mServiceBound[phoneId]) {
+            mServiceBound[phoneId] = false;
+            context.unbindService(conn);
+        }
+    }
+
+    /**
+     * 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
+     * where we don't call {@code dumpsys activity service all-non-platform} because that contains
+     * too much info, but we still want to let carrier apps include their diagnostics.
+     */
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        IndentingPrintWriter indentPW = new IndentingPrintWriter(pw, "    ");
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                 != PackageManager.PERMISSION_GRANTED) {
-            pw.println("Permission Denial: can't dump carrierconfig from from pid="
+            indentPW.println("Permission Denial: can't dump carrierconfig from from pid="
                     + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
             return;
         }
-        pw.println("CarrierConfigLoader: " + this);
-        for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
-            pw.println("Phone Id = " + i);
-            // display default values in CarrierConfigManager
-            printConfig(CarrierConfigManager.getDefaultConfig(), pw,
-                    "Default Values from CarrierConfigManager");
-            pw.println("");
-            // display ConfigFromDefaultApp
-            printConfig(mConfigFromDefaultApp[i], pw, "mConfigFromDefaultApp");
-            pw.println("");
-            // display ConfigFromCarrierApp
-            printConfig(mConfigFromCarrierApp[i], pw, "mConfigFromCarrierApp");
-            pw.println("");
-            printConfig(mPersistentOverrideConfigs[i], pw, "mPersistentOverrideConfigs");
-            pw.println("");
-            printConfig(mOverrideConfigs[i], pw, "mOverrideConfigs");
+        String requestingPackage = null;
+        int requestingPackageIndex = ArrayUtils.indexOf(args, DUMP_ARG_REQUESTING_PACKAGE);
+        if (requestingPackageIndex >= 0 && requestingPackageIndex < args.length - 1
+                && !TextUtils.isEmpty(args[requestingPackageIndex + 1])) {
+            requestingPackage = args[requestingPackageIndex + 1];
+            // Throws a SecurityException if the caller is impersonating another app in an effort to
+            // dump extra info (which may contain PII the caller doesn't have a right to).
+            enforceCallerIsSystemOrRequestingPackage(requestingPackage);
         }
 
-        pw.println("CarrierConfigLoadingLog=");
-        mCarrierConfigLoadingLog.dump(fd, pw, args);
+        indentPW.println("CarrierConfigLoader: " + this);
+        for (int i = 0; i < TelephonyManager.from(mContext).getActiveModemCount(); i++) {
+            indentPW.println("Phone Id = " + i);
+            // display default values in CarrierConfigManager
+            printConfig(CarrierConfigManager.getDefaultConfig(), indentPW,
+                    "Default Values from CarrierConfigManager");
+            // display ConfigFromDefaultApp
+            printConfig(mConfigFromDefaultApp[i], indentPW, "mConfigFromDefaultApp");
+            // display ConfigFromCarrierApp
+            printConfig(mConfigFromCarrierApp[i], indentPW, "mConfigFromCarrierApp");
+            printConfig(mPersistentOverrideConfigs[i], indentPW, "mPersistentOverrideConfigs");
+            printConfig(mOverrideConfigs[i], indentPW, "mOverrideConfigs");
+        }
+
+        indentPW.println("CarrierConfigLoadingLog=");
+        mCarrierConfigLoadingLog.dump(fd, indentPW, args);
+
+        if (requestingPackage != null) {
+            logd("Including default and requesting package " + requestingPackage
+                    + " carrier services in dump");
+            indentPW.println("");
+            indentPW.println("Connected services");
+            dumpCarrierServiceIfBound(fd, indentPW, "Default config package",
+                    mPlatformCarrierConfigPackage, false /* considerCarrierPrivileges */);
+            dumpCarrierServiceIfBound(fd, indentPW, "Requesting package", requestingPackage,
+                    true /* considerCarrierPrivileges */);
+        }
     }
 
-    private void printConfig(PersistableBundle configApp, PrintWriter pw, String name) {
-        IndentingPrintWriter indentPW = new IndentingPrintWriter(pw, "    ");
+    private void printConfig(PersistableBundle configApp, IndentingPrintWriter indentPW,
+            String name) {
+        indentPW.increaseIndent();
         if (configApp == null) {
-            indentPW.increaseIndent();
             indentPW.println(name + " : null ");
+            indentPW.decreaseIndent();
+            indentPW.println("");
             return;
         }
-        indentPW.increaseIndent();
         indentPW.println(name + " : ");
         List<String> sortedKeys = new ArrayList<String>(configApp.keySet());
         Collections.sort(sortedKeys);
@@ -1125,27 +1182,162 @@
                 indentPW.println(key + " = " + configApp.get(key));
             }
         }
+        indentPW.decreaseIndent();
+        indentPW.decreaseIndent();
+        indentPW.decreaseIndent();
+        indentPW.println("");
+    }
+
+    /**
+     * Passes without problem when one of these conditions is true:
+     * - The caller is a privileged UID (e.g. for dumpstate.cpp generating a bug report, where the
+     * system knows the true caller plumbed in through the {@link android.os.BugreportManager} API).
+     * - The caller's UID matches the supplied package.
+     *
+     * @throws SecurityException if none of the above conditions are met.
+     */
+    private void enforceCallerIsSystemOrRequestingPackage(String requestingPackage)
+            throws SecurityException {
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid == Process.ROOT_UID || callingUid == Process.SYSTEM_UID
+                || callingUid == Process.SHELL_UID || callingUid == Process.PHONE_UID) {
+            // Bug reports (dumpstate.cpp) run as SHELL, and let some other privileged UIDs through
+            // as well.
+            return;
+        }
+        // An app is trying to dump extra detail, block it if they aren't who they claim to be.
+        AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+        if (appOps == null) {
+            throw new SecurityException("No AppOps");
+        }
+        // Will throw a SecurityException if the UID and package don't match.
+        appOps.checkPackage(callingUid, requestingPackage);
+    }
+
+    /**
+     * Searches for one or more appropriate {@link CarrierService} instances to dump based on the
+     * current connections.
+     *
+     * @param targetPkgName             the target package name to dump carrier services for
+     * @param considerCarrierPrivileges if true, allow a carrier service to be dumped if it shares
+     *                                  carrier privileges with {@code targetPkgName};
+     *                                  otherwise, only dump a carrier service if it is {@code
+     *                                  targetPkgName}
+     */
+    private void dumpCarrierServiceIfBound(FileDescriptor fd, IndentingPrintWriter indentPW,
+            String prefix, String targetPkgName, boolean considerCarrierPrivileges) {
+        // Null package is possible if it's early in the boot process, there was a recent crash, we
+        // loaded the config from XML most recently, or a SIM slot is empty. Carrier apps with
+        // long-lived bindings should typically get dumped here regardless. Even if an app is being
+        // used for multiple phoneIds, we assume that it's smart enough to handle that on its own,
+        // and that in most cases we'd just be dumping duplicate information and bloating a report.
+        indentPW.increaseIndent();
+        indentPW.println(prefix + " : " + targetPkgName);
+        Set<String> dumpedPkgNames = new ArraySet<>(mServiceConnection.length);
+        for (CarrierServiceConnection connection : mServiceConnection) {
+            if (connection == null || !SubscriptionManager.isValidPhoneId(connection.phoneId)
+                    || TextUtils.isEmpty(connection.pkgName)) {
+                continue;
+            }
+            final String servicePkgName = connection.pkgName;
+            // Note: we intentionally ignore system components here because we should NOT match the
+            // shell caller that's typically used for bug reports via non-BugreportManager triggers.
+            final boolean exactPackageMatch = TextUtils.equals(targetPkgName, servicePkgName);
+            final boolean carrierPrivilegesMatch =
+                    considerCarrierPrivileges && hasCarrierPrivileges(targetPkgName,
+                            connection.phoneId);
+            if (!exactPackageMatch && !carrierPrivilegesMatch) continue;
+            // Make sure this service is actually alive before trying to dump it. We don't pay
+            // attention to mServiceBound[connection.phoneId] because typically carrier apps will
+            // request long-lived bindings, and even if we unbind the app, it may still be alive due
+            // to CarrierServiceBindHelper. Pull it out as a reference so even if it gets set to
+            // null within the ServiceConnection during unbinding we can avoid an NPE.
+            final IBinder service = connection.service;
+            if (service == null || !service.isBinderAlive() || !service.pingBinder()) continue;
+            // We've got a live service. Last check is just to make sure we don't dump a package
+            // multiple times.
+            if (!dumpedPkgNames.add(servicePkgName)) continue;
+            if (!exactPackageMatch) {
+                logd(targetPkgName + " has carrier privileges on phoneId " + connection.phoneId
+                        + ", service provided by " + servicePkgName);
+                indentPW.increaseIndent();
+                indentPW.println("Proxy : " + servicePkgName);
+                indentPW.decreaseIndent();
+            }
+            // Flush before we let the app output anything to ensure correct ordering of output.
+            // Internally, Binder#dump calls flush on its printer after finishing so we don't
+            // need to do anything after.
+            indentPW.flush();
+            try {
+                logd("Dumping " + servicePkgName);
+                // We don't need to give the carrier service any args.
+                connection.service.dump(fd, null /* args */);
+                logd("Done with " + servicePkgName);
+            } catch (RemoteException e) {
+                logd("RemoteException from " + servicePkgName, e);
+                indentPW.increaseIndent();
+                indentPW.println("RemoteException");
+                indentPW.increaseIndent();
+                e.printStackTrace(indentPW);
+                indentPW.decreaseIndent();
+                indentPW.decreaseIndent();
+                // We won't retry this package again because now it's in dumpedPkgNames.
+            }
+            indentPW.println("");
+        }
+        if (dumpedPkgNames.isEmpty()) {
+            indentPW.increaseIndent();
+            indentPW.println("Not bound");
+            indentPW.decreaseIndent();
+            indentPW.println("");
+        }
+        indentPW.decreaseIndent();
+    }
+
+    private boolean hasCarrierPrivileges(String pkgName, int phoneId) {
+        int[] subIds = SubscriptionManager.getSubId(phoneId);
+        if (ArrayUtils.isEmpty(subIds)) {
+            return false;
+        }
+        return TelephonyManager.from(mContext).createForSubscriptionId(
+                subIds[0]).checkCarrierPrivilegesForPackage(pkgName)
+                == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
     }
 
     private class CarrierServiceConnection implements ServiceConnection {
-        int phoneId;
-        int eventId;
+        final int phoneId;
+        final String pkgName;
+        final int eventId;
         IBinder service;
 
-        public CarrierServiceConnection(int phoneId, int eventId) {
+        CarrierServiceConnection(int phoneId, String pkgName, int eventId) {
             this.phoneId = phoneId;
+            this.pkgName = pkgName;
             this.eventId = eventId;
         }
 
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
-            log("Connected to config app: " + name.flattenToString());
+            logd("Connected to config app: " + name.flattenToShortString());
             this.service = service;
             mHandler.sendMessage(mHandler.obtainMessage(eventId, phoneId, -1, this));
         }
 
         @Override
         public void onServiceDisconnected(ComponentName name) {
+            logd("Disconnected from config app: " + name.flattenToShortString());
+            this.service = null;
+        }
+
+        @Override
+        public void onBindingDied(ComponentName name) {
+            logd("Binding died from config app: " + name.flattenToShortString());
+            this.service = null;
+        }
+
+        @Override
+        public void onNullBinding(ComponentName name) {
+            logd("Null binding from config app: " + name.flattenToShortString());
             this.service = null;
         }
     }
@@ -1156,8 +1348,9 @@
             String action = intent.getAction();
             boolean replace = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
             // If replace is true, only care ACTION_PACKAGE_REPLACED.
-            if (replace && !Intent.ACTION_PACKAGE_REPLACED.equals(action))
+            if (replace && !Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
                 return;
+            }
 
             switch (action) {
                 case Intent.ACTION_BOOT_COMPLETED:
@@ -1179,11 +1372,15 @@
         }
     }
 
-    private void log(String msg) {
+    private void logd(String msg) {
         Log.d(LOG_TAG, msg);
     }
 
-    private void logWithLocalLog(String msg) {
+    private void logd(String msg, Throwable tr) {
+        Log.d(LOG_TAG, msg, tr);
+    }
+
+    private void logdWithLocalLog(String msg) {
         Log.d(LOG_TAG, msg);
         mCarrierConfigLoadingLog.log(msg);
     }
diff --git a/src/com/android/phone/CdmaCallForwardOptions.java b/src/com/android/phone/CdmaCallForwardOptions.java
new file mode 100644
index 0000000..a8d2e93
--- /dev/null
+++ b/src/com/android/phone/CdmaCallForwardOptions.java
@@ -0,0 +1,254 @@
+/*
+ * 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 android.app.ActionBar;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.telephony.CarrierConfigManager;
+import android.util.Log;
+import android.view.MenuItem;
+
+import com.android.internal.telephony.CallForwardInfo;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.Phone;
+
+import java.util.ArrayList;
+
+public class CdmaCallForwardOptions extends TimeConsumingPreferenceActivity {
+    private static final String LOG_TAG = "CdmaCallForwardOptions";
+
+    private static final String NUM_PROJECTION[] = {
+        android.provider.ContactsContract.CommonDataKinds.Phone.NUMBER
+    };
+
+    private static final String BUTTON_CFU_KEY   = "button_cfu_key";
+    private static final String BUTTON_CFB_KEY   = "button_cfb_key";
+    private static final String BUTTON_CFNRY_KEY = "button_cfnry_key";
+    private static final String BUTTON_CFNRC_KEY = "button_cfnrc_key";
+
+    private static final String KEY_TOGGLE = "toggle";
+    private static final String KEY_STATUS = "status";
+    private static final String KEY_NUMBER = "number";
+    private static final String KEY_ENABLE = "enable";
+
+    private CallForwardEditPreference mButtonCFU;
+    private CallForwardEditPreference mButtonCFB;
+    private CallForwardEditPreference mButtonCFNRy;
+    private CallForwardEditPreference mButtonCFNRc;
+
+    private final ArrayList<CallForwardEditPreference> mPreferences =
+            new ArrayList<CallForwardEditPreference> ();
+    private int mInitIndex= 0;
+
+    private boolean mFirstResume;
+    private Bundle mIcicle;
+    private Phone mPhone;
+    private SubscriptionInfoHelper mSubscriptionInfoHelper;
+    private boolean mReplaceInvalidCFNumbers;
+    private boolean mCallForwardByUssd;
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        addPreferencesFromResource(R.xml.callforward_options);
+
+        mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, getIntent());
+        mSubscriptionInfoHelper.setActionBarTitle(
+                getActionBar(), getResources(), R.string.call_forwarding_settings_with_label);
+        mPhone = mSubscriptionInfoHelper.getPhone();
+
+        PersistableBundle b = null;
+        boolean supportCFNRc = true;
+        if (mSubscriptionInfoHelper.hasSubId()) {
+            b = PhoneGlobals.getInstance().getCarrierConfigForSubId(
+                    mSubscriptionInfoHelper.getSubId());
+        } else {
+            b = PhoneGlobals.getInstance().getCarrierConfig();
+        }
+        if (b != null) {
+            mReplaceInvalidCFNumbers = b.getBoolean(
+                    CarrierConfigManager.KEY_CALL_FORWARDING_MAP_NON_NUMBER_TO_VOICEMAIL_BOOL);
+            mCallForwardByUssd = b.getBoolean(
+                    CarrierConfigManager.KEY_USE_CALL_FORWARDING_USSD_BOOL);
+            supportCFNRc = b.getBoolean(
+                    CarrierConfigManager.KEY_CALL_FORWARDING_WHEN_UNREACHABLE_SUPPORTED_BOOL);
+        }
+
+        PreferenceScreen prefSet = getPreferenceScreen();
+        mButtonCFU = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFU_KEY);
+        mButtonCFB = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFB_KEY);
+        mButtonCFNRy = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFNRY_KEY);
+        mButtonCFNRc = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFNRC_KEY);
+
+        mButtonCFU.setParentActivity(this, mButtonCFU.reason);
+        mButtonCFB.setParentActivity(this, mButtonCFB.reason);
+        mButtonCFNRy.setParentActivity(this, mButtonCFNRy.reason);
+        mButtonCFNRc.setParentActivity(this, mButtonCFNRc.reason);
+
+        mPreferences.add(mButtonCFU);
+        mPreferences.add(mButtonCFB);
+        mPreferences.add(mButtonCFNRy);
+
+        if (supportCFNRc) {
+            mPreferences.add(mButtonCFNRc);
+        } else {
+            // When CFNRc is not supported, mButtonCFNRc is grayed out from the menu.
+            // Default state for the preferences in this PreferenceScreen is disabled.
+            // Only preferences listed in the ArrayList mPreferences will be enabled.
+            // By not adding mButtonCFNRc to mPreferences it will be kept disabled.
+            Log.d(LOG_TAG, "onCreate: CFNRc is not supported, grey out the item.");
+        }
+
+        if (mCallForwardByUssd) {
+            //the call forwarding ussd command's behavior is similar to the call forwarding when
+            //unanswered,so only display the call forwarding when unanswered item.
+            prefSet.removePreference(mButtonCFU);
+            prefSet.removePreference(mButtonCFB);
+            prefSet.removePreference(mButtonCFNRc);
+            mPreferences.remove(mButtonCFU);
+            mPreferences.remove(mButtonCFB);
+            mPreferences.remove(mButtonCFNRc);
+            mButtonCFNRy.setDependency(null);
+        }
+
+        // we wait to do the initialization until onResume so that the
+        // TimeConsumingPreferenceActivity dialog can display as it
+        // relies on onResume / onPause to maintain its foreground state.
+
+        mFirstResume = true;
+        mIcicle = icicle;
+
+        ActionBar actionBar = getActionBar();
+        if (actionBar != null) {
+            // android.R.id.home will be triggered in onOptionsItemSelected()
+            actionBar.setDisplayHomeAsUpEnabled(true);
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        if (mFirstResume) {
+            if (mIcicle == null) {
+                Log.d(LOG_TAG, "start to init ");
+                CallForwardEditPreference pref = mPreferences.get(mInitIndex);
+                pref.init(this, mPhone, mReplaceInvalidCFNumbers, mCallForwardByUssd);
+                pref.startCallForwardOptionsQuery();
+
+            } else {
+                mInitIndex = mPreferences.size();
+
+                for (CallForwardEditPreference pref : mPreferences) {
+                    Bundle bundle = mIcicle.getParcelable(pref.getKey());
+                    pref.setToggled(bundle.getBoolean(KEY_TOGGLE));
+                    pref.setEnabled(bundle.getBoolean(KEY_ENABLE));
+                    CallForwardInfo cf = new CallForwardInfo();
+                    cf.number = bundle.getString(KEY_NUMBER);
+                    cf.status = bundle.getInt(KEY_STATUS);
+                    pref.init(this, mPhone, mReplaceInvalidCFNumbers, mCallForwardByUssd);
+                    pref.restoreCallForwardInfo(cf);
+                }
+            }
+            mFirstResume = false;
+            mIcicle = null;
+        }
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        for (CallForwardEditPreference pref : mPreferences) {
+            Bundle bundle = new Bundle();
+            bundle.putBoolean(KEY_TOGGLE, pref.isToggled());
+            bundle.putBoolean(KEY_ENABLE, pref.isEnabled());
+            if (pref.callForwardInfo != null) {
+                bundle.putString(KEY_NUMBER, pref.callForwardInfo.number);
+                bundle.putInt(KEY_STATUS, pref.callForwardInfo.status);
+            }
+            outState.putParcelable(pref.getKey(), bundle);
+        }
+    }
+
+    @Override
+    public void onFinished(Preference preference, boolean reading) {
+        if (mInitIndex < mPreferences.size()-1 && !isFinishing()) {
+            mInitIndex++;
+            CallForwardEditPreference pref = mPreferences.get(mInitIndex);
+            pref.init(this, mPhone, mReplaceInvalidCFNumbers, mCallForwardByUssd);
+            pref.startCallForwardOptionsQuery();
+        }
+
+        super.onFinished(preference, reading);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        Log.d(LOG_TAG, "onActivityResult: done");
+        if (resultCode != RESULT_OK) {
+            Log.d(LOG_TAG, "onActivityResult: contact picker result not OK.");
+            return;
+        }
+        Cursor cursor = null;
+        try {
+            cursor = getContentResolver().query(data.getData(),
+                NUM_PROJECTION, null, null, null);
+            if ((cursor == null) || (!cursor.moveToFirst())) {
+                Log.d(LOG_TAG, "onActivityResult: bad contact data, no results found.");
+                return;
+            }
+
+            switch (requestCode) {
+                case CommandsInterface.CF_REASON_UNCONDITIONAL:
+                    mButtonCFU.onPickActivityResult(cursor.getString(0));
+                    break;
+                case CommandsInterface.CF_REASON_BUSY:
+                    mButtonCFB.onPickActivityResult(cursor.getString(0));
+                    break;
+                case CommandsInterface.CF_REASON_NO_REPLY:
+                    mButtonCFNRy.onPickActivityResult(cursor.getString(0));
+                    break;
+                case CommandsInterface.CF_REASON_NOT_REACHABLE:
+                    mButtonCFNRc.onPickActivityResult(cursor.getString(0));
+                    break;
+                default:
+                    // TODO: may need exception here.
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        final int itemId = item.getItemId();
+        if (itemId == android.R.id.home) {  // See ActionBar#setDisplayHomeAsUpEnabled()
+            CallFeaturesSetting.goUpToTopLevelSetting(this, mSubscriptionInfoHelper);
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+}
diff --git a/src/com/android/phone/CdmaCallOptions.java b/src/com/android/phone/CdmaCallOptions.java
index acfa496..2e310aa 100644
--- a/src/com/android/phone/CdmaCallOptions.java
+++ b/src/com/android/phone/CdmaCallOptions.java
@@ -19,20 +19,20 @@
 import android.os.Bundle;
 import android.os.PersistableBundle;
 import android.preference.Preference;
-import android.preference.PreferenceActivity;
 import android.preference.PreferenceScreen;
-import android.preference.SwitchPreference;
 import android.telephony.CarrierConfigManager;
 import android.view.MenuItem;
 
 import com.android.internal.telephony.PhoneConstants;
 
-public class CdmaCallOptions extends PreferenceActivity {
+public class CdmaCallOptions extends TimeConsumingPreferenceActivity {
     private static final String LOG_TAG = "CdmaCallOptions";
     private final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
 
     private static final String BUTTON_VP_KEY = "button_voice_privacy_key";
-    private SwitchPreference mButtonVoicePrivacy;
+    private static final String CALL_FORWARDING_KEY = "call_forwarding_key";
+    private static final String CALL_WAITING_KEY = "call_waiting_key";
+    private CdmaVoicePrivacySwitchPreference mButtonVoicePrivacy;
 
     @Override
     protected void onCreate(Bundle icicle) {
@@ -44,7 +44,8 @@
         subInfoHelper.setActionBarTitle(
                 getActionBar(), getResources(), R.string.labelCdmaMore_with_label);
 
-        mButtonVoicePrivacy = (SwitchPreference) findPreference(BUTTON_VP_KEY);
+        mButtonVoicePrivacy = (CdmaVoicePrivacySwitchPreference) findPreference(BUTTON_VP_KEY);
+        mButtonVoicePrivacy.setPhone(subInfoHelper.getPhone());
         PersistableBundle carrierConfig;
         if (subInfoHelper.hasSubId()) {
             carrierConfig = PhoneGlobals.getInstance().getCarrierConfigForSubId(
@@ -55,8 +56,15 @@
         if (subInfoHelper.getPhone().getPhoneType() != PhoneConstants.PHONE_TYPE_CDMA
                 || carrierConfig.getBoolean(CarrierConfigManager.KEY_VOICE_PRIVACY_DISABLE_UI_BOOL)) {
             // disable the entire screen
-            getPreferenceScreen().setEnabled(false);
+            mButtonVoicePrivacy.setEnabled(false);
         }
+
+        Preference callForwardingPref = getPreferenceScreen().findPreference(CALL_FORWARDING_KEY);
+        callForwardingPref.setIntent(subInfoHelper.getIntent(CdmaCallForwardOptions.class));
+
+        CdmaCallWaitingPreference callWaitingPref = (CdmaCallWaitingPreference)getPreferenceScreen()
+                                                     .findPreference(CALL_WAITING_KEY);
+        callWaitingPref.init(this, subInfoHelper.getPhone());
     }
 
     @Override
@@ -76,5 +84,4 @@
         }
         return false;
     }
-
 }
diff --git a/src/com/android/phone/CdmaCallWaitingPreference.java b/src/com/android/phone/CdmaCallWaitingPreference.java
new file mode 100644
index 0000000..4cda7ba
--- /dev/null
+++ b/src/com/android/phone/CdmaCallWaitingPreference.java
@@ -0,0 +1,194 @@
+/*
+ * 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 com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.Phone;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.TypedArray;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.util.AttributeSet;
+import android.util.Log;
+
+public class CdmaCallWaitingPreference extends Preference {
+    private static final String LOG_TAG = "CdmaCallWaitingPreference";
+    private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
+
+    private int mButtonClicked;
+    private Context mContext;
+    private Phone mPhone;
+    private SubscriptionInfoHelper mSubscriptionInfoHelper;
+    private TimeConsumingPreferenceListener mTcpListener;
+    private MyHandler mHandler = new MyHandler();
+
+    public CdmaCallWaitingPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mContext = context;
+    }
+
+    public CdmaCallWaitingPreference(Context context, AttributeSet attrs) {
+        this(context, attrs, com.android.internal.R.attr.preferenceStyle);
+    }
+
+    public CdmaCallWaitingPreference(Context context) {
+        this(context, null);
+    }
+
+    public void init(TimeConsumingPreferenceListener listener, Phone phone) {
+        mPhone = phone;
+        mTcpListener = listener;
+        Log.d(LOG_TAG, "phone id= " + mPhone.getPhoneId());
+        mPhone.getCallWaiting(mHandler.obtainMessage(MyHandler.MESSAGE_GET_CALL_WAITING,
+                    MyHandler.MESSAGE_GET_CALL_WAITING, MyHandler.MESSAGE_GET_CALL_WAITING));
+        if (mTcpListener != null) {
+            mTcpListener.onStarted(this, true);
+        }
+    }
+
+    @Override
+    public void onClick() {
+        super.onClick();
+
+        AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
+        builder.setTitle(mContext.getText(R.string.cdma_call_waiting));
+        builder.setMessage(mContext.getText(R.string.enable_cdma_call_waiting_setting));
+        builder.setPositiveButton(R.string.enable_cdma_cw, new DialogInterface.OnClickListener() {
+            public void onClick(DialogInterface dialog, int whichButton) {
+                mPhone.setCallWaiting(true,
+                        mHandler.obtainMessage(MyHandler.MESSAGE_SET_CALL_WAITING));
+                if (mTcpListener != null) {
+                    mTcpListener.onStarted(CdmaCallWaitingPreference.this, false);
+                }
+            }
+        });
+        builder.setNegativeButton(R.string.disable_cdma_cw, new DialogInterface.OnClickListener() {
+            public void onClick(DialogInterface dialog, int whichButton) {
+                mPhone.setCallWaiting(false,
+                        mHandler.obtainMessage(MyHandler.MESSAGE_SET_CALL_WAITING));
+                if (mTcpListener != null) {
+                    mTcpListener.onStarted(CdmaCallWaitingPreference.this, false);
+                }
+            }
+        });
+        builder.create().show();
+    }
+
+    private class MyHandler extends Handler {
+        static final int MESSAGE_GET_CALL_WAITING = 0;
+        static final int MESSAGE_SET_CALL_WAITING = 1;
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MESSAGE_GET_CALL_WAITING:
+                    handleGetCallWaitingResponse(msg);
+                    break;
+                case MESSAGE_SET_CALL_WAITING:
+                    handleSetCallWaitingResponse(msg);
+                    break;
+            }
+        }
+
+        private void handleGetCallWaitingResponse(Message msg) {
+            AsyncResult ar = (AsyncResult) msg.obj;
+
+            if (mTcpListener != null) {
+                if (msg.arg2 == MESSAGE_SET_CALL_WAITING) {
+                    mTcpListener.onFinished(CdmaCallWaitingPreference.this, false);
+                } else {
+                    mTcpListener.onFinished(CdmaCallWaitingPreference.this, true);
+                }
+            }
+
+            if (ar.exception instanceof CommandException) {
+                if (DBG) {
+                    Log.d(LOG_TAG, "handleGetCallWaitingResponse: CommandException=" +
+                            ar.exception);
+                }
+                if (mTcpListener != null) {
+                    mTcpListener.onException(CdmaCallWaitingPreference.this,
+                                             (CommandException)ar.exception);
+                }
+            } else if (ar.userObj instanceof Throwable || ar.exception != null) {
+                if (DBG) {
+                    Log.d(LOG_TAG, "handleGetCallWaitingResponse: Exception" + ar.exception);
+                }
+                if (mTcpListener != null) {
+                    mTcpListener.onError(CdmaCallWaitingPreference.this,
+                                         TimeConsumingPreferenceActivity.RESPONSE_ERROR);
+                }
+            } else {
+                if (DBG) {
+                    Log.d(LOG_TAG, "handleGetCallWaitingResponse: CW state successfully queried.");
+                }
+                int[] cwArray = (int[])ar.result;
+                if (cwArray == null) {
+                    if (mTcpListener != null) {
+                        mTcpListener.onError(CdmaCallWaitingPreference.this,
+                                             TimeConsumingPreferenceActivity.RESPONSE_ERROR);
+                    }
+                    return;
+                }
+
+                try {
+                    if (cwArray[0] == CommandsInterface.SS_STATUS_UNKNOWN) {
+                        setSummary("");
+                    } else if(cwArray[0] == 1) {
+                        setSummary(mContext.getString(R.string.cdma_call_waiting_in_ims_on));
+                    } else if(cwArray[0] == 0) {
+                        setSummary(mContext.getString(R.string.cdma_call_waiting_in_ims_off));
+                    }
+                } catch (ArrayIndexOutOfBoundsException e) {
+                    setSummary("");
+                    Log.e(LOG_TAG, "handleGetCallWaitingResponse: improper result: err ="
+                            + e.getMessage());
+                }
+            }
+        }
+
+        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);
+                }
+            }
+
+            if (ar.result != null) {
+                int arr = (int)ar.result;
+                if (arr == CommandsInterface.SS_STATUS_UNKNOWN) {
+                    Log.d(LOG_TAG, "handleSetCallWaitingResponse: no need to re get in CDMA");
+                    mTcpListener.onFinished(CdmaCallWaitingPreference.this, false);
+                    return;
+                }
+            }
+
+            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));
+        }
+    }
+}
diff --git a/src/com/android/phone/CdmaVoicePrivacySwitchPreference.java b/src/com/android/phone/CdmaVoicePrivacySwitchPreference.java
index b79a3f0..0192c2a 100644
--- a/src/com/android/phone/CdmaVoicePrivacySwitchPreference.java
+++ b/src/com/android/phone/CdmaVoicePrivacySwitchPreference.java
@@ -102,4 +102,8 @@
             phone.getEnhancedVoicePrivacy(obtainMessage(MESSAGE_GET_VP));
         }
     }
+
+    public void setPhone(Phone phone) {
+        this.phone = phone;
+    }
 }
diff --git a/src/com/android/phone/CellInfoUtil.java b/src/com/android/phone/CellInfoUtil.java
index 8272029..f6d741f 100644
--- a/src/com/android/phone/CellInfoUtil.java
+++ b/src/com/android/phone/CellInfoUtil.java
@@ -33,6 +33,7 @@
 
 import com.android.internal.telephony.OperatorInfo;
 
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -149,7 +150,8 @@
                 mcc,
                 mnc,
                 operatorInfo.getOperatorAlphaLong(),
-                operatorInfo.getOperatorAlphaShort());
+                operatorInfo.getOperatorAlphaShort(),
+                Collections.emptyList());
 
         CellInfoGsm ci = new CellInfoGsm();
         ci.setCellIdentity(cig);
diff --git a/src/com/android/phone/EditPhoneNumberPreference.java b/src/com/android/phone/EditPhoneNumberPreference.java
index 35af20d..505c284 100644
--- a/src/com/android/phone/EditPhoneNumberPreference.java
+++ b/src/com/android/phone/EditPhoneNumberPreference.java
@@ -16,6 +16,9 @@
 
 package com.android.phone;
 
+import static android.view.View.LAYOUT_DIRECTION_LOCALE;
+import static android.view.View.TEXT_DIRECTION_LOCALE;
+
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.content.Context;
@@ -37,6 +40,8 @@
 import android.widget.ImageButton;
 import android.widget.TextView;
 
+import com.android.internal.telephony.CommandsInterface;
+
 public class EditPhoneNumberPreference extends EditTextPreference {
 
     //allowed modes for this preference.
@@ -90,6 +95,7 @@
     private String mPhoneNumber;
     private boolean mChecked;
 
+    private boolean mIsUnknownStatus;
 
     /**
      * Interface for the dialog closed listener, related to
@@ -136,9 +142,9 @@
         a.recycle();
 
         //get the summary settings, use CheckBoxPreference as the standard.
-        a = context.obtainStyledAttributes(attrs, R.styleable.CheckBoxPreference, 0, 0);
-        mSummaryOn = a.getString(R.styleable.CheckBoxPreference_summaryOn);
-        mSummaryOff = a.getString(R.styleable.CheckBoxPreference_summaryOff);
+        a = context.obtainStyledAttributes(attrs, android.R.styleable.CheckBoxPreference, 0, 0);
+        mSummaryOn = a.getString(android.R.styleable.CheckBoxPreference_summaryOn);
+        mSummaryOff = a.getString(android.R.styleable.CheckBoxPreference_summaryOff);
         a.recycle();
     }
 
@@ -209,7 +215,9 @@
                 }
             }
             editText.setText(BidiFormatter.getInstance().unicodeWrap(
-                    mPhoneNumber, TextDirectionHeuristics.LTR));
+                    mPhoneNumber, TextDirectionHeuristics.LOCALE));
+            editText.setTextDirection(TEXT_DIRECTION_LOCALE);
+            editText.setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
             editText.setMovementMethod(ArrowKeyMovementMethod.getInstance());
             editText.setKeyListener(DialerKeyListener.getInstance());
             editText.setOnFocusChangeListener(mDialogFocusChangeListener);
@@ -254,7 +262,13 @@
         // displayed, since there is no need to hide the edittext
         // field anymore.
         if (mConfirmationMode == CM_ACTIVATION) {
-            if (mChecked) {
+            if (mIsUnknownStatus) {
+                builder.setPositiveButton(mEnableText, this);
+                builder.setNeutralButton(mDisableText, this);
+                if (mPrefId == CommandsInterface.CF_REASON_ALL) {
+                    builder.setPositiveButton(null, null);
+                }
+            } else if (mChecked) {
                 builder.setPositiveButton(mChangeNumberText, this);
                 builder.setNeutralButton(mDisableText, this);
             } else {
@@ -310,7 +324,8 @@
     @Override
     public void onClick(DialogInterface dialog, int which) {
         // The neutral button (button3) is always the toggle.
-        if ((mConfirmationMode == CM_ACTIVATION) && (which == DialogInterface.BUTTON_NEUTRAL)) {
+        if ((mConfirmationMode == CM_ACTIVATION) && (which == DialogInterface.BUTTON_NEUTRAL)
+             && !mIsUnknownStatus) {
             //flip the toggle if we are in the correct mode.
             setToggled(!isToggled());
         }
@@ -499,4 +514,12 @@
     public void showPhoneNumberDialog() {
         showDialog(null);
     }
+
+    public void setUnknownStatus(boolean isUnknown) {
+        mIsUnknownStatus = isUnknown;
+    }
+
+    public boolean isUnknownStatus() {
+        return mIsUnknownStatus;
+    }
 }
diff --git a/src/com/android/phone/EmergencyActionGroup.java b/src/com/android/phone/EmergencyActionGroup.java
index 53ec1eb..4961a69 100644
--- a/src/com/android/phone/EmergencyActionGroup.java
+++ b/src/com/android/phone/EmergencyActionGroup.java
@@ -159,7 +159,7 @@
 
         if (v.getId() == R.id.action1 || v.getId() == R.id.action2 || v.getId() == R.id.action3) {
             AccessibilityManager accessibilityMgr =
-                    (AccessibilityManager) mContext.getSystemService(
+                    (AccessibilityManager) getContext().getSystemService(
                             Context.ACCESSIBILITY_SERVICE);
             if (accessibilityMgr.isTouchExplorationEnabled()) {
                 getContext().startActivity(intent);
diff --git a/src/com/android/phone/EmergencyCallbackModeExitDialog.java b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
index dcfa024..1210627 100644
--- a/src/com/android/phone/EmergencyCallbackModeExitDialog.java
+++ b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
@@ -35,6 +35,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.telephony.TelephonyManager;
 import android.util.Log;
 
 import com.android.internal.telephony.Phone;
@@ -300,8 +301,14 @@
             return String.format(getResources().getQuantityText(
                     R.plurals.alert_dialog_not_avaialble_in_ecm, minutes).toString(), time);
         case EXIT_ECM_DIALOG:
-            return String.format(getResources().getQuantityText(R.plurals.alert_dialog_exit_ecm,
-                    minutes).toString(), time);
+                boolean shouldRestrictData = mPhone.getImsPhone() != null
+                        && mPhone.getImsPhone().isInImsEcm();
+                return String.format(getResources().getQuantityText(
+                        // During IMS ECM, data restriction hint should be removed.
+                        shouldRestrictData
+                        ? R.plurals.alert_dialog_exit_ecm_without_data_restriction_hint
+                        : R.plurals.alert_dialog_exit_ecm,
+                        minutes).toString(), time);
         }
         return null;
     }
@@ -324,7 +331,8 @@
             // Received exit Emergency Callback Mode notification close all dialogs
             if (intent.getAction().equals(
                     TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
-                if (intent.getBooleanExtra("phoneinECMState", false) == false) {
+                // Cancel if the sticky broadcast extra for whether or not we are in ECM is false.
+                if (!intent.getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false)) {
                     if (mAlertDialog != null)
                         mAlertDialog.dismiss();
                     if (mProgressDialog != null)
diff --git a/src/com/android/phone/EmergencyCallbackModeService.java b/src/com/android/phone/EmergencyCallbackModeService.java
index 41d83c4..6b247bd 100644
--- a/src/com/android/phone/EmergencyCallbackModeService.java
+++ b/src/com/android/phone/EmergencyCallbackModeService.java
@@ -30,7 +30,9 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
+import android.os.UserHandle;
 import android.sysprop.TelephonyProperties;
+import android.telephony.TelephonyManager;
 import android.util.Log;
 
 import com.android.internal.telephony.Phone;
@@ -105,7 +107,8 @@
             mPhone.unregisterForEcmTimerReset(mHandler);
 
             // Cancel the notification and timer
-            mNotificationManager.cancel(R.string.phone_in_ecm_notification_title);
+            mNotificationManager.cancelAsUser(null, R.string.phone_in_ecm_notification_title,
+                    UserHandle.ALL);
             mTimer.cancel();
         }
     }
@@ -119,7 +122,7 @@
             // Stop the service when phone exits Emergency Callback Mode
             if (intent.getAction().equals(
                     TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
-                if (intent.getBooleanExtra("phoneinECMState", false) == false) {
+                if (!intent.getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false)) {
                     stopSelf();
                 }
             }
@@ -194,7 +197,11 @@
         // Format notification string
         String text = null;
         if(mInEmergencyCall) {
-            text = getText(R.string.phone_in_ecm_call_notification_text).toString();
+            text = getText(
+                    // During IMS ECM, data restriction hint should be removed.
+                    (imsPhone != null && imsPhone.isInImsEcm())
+                    ? R.string.phone_in_ecm_call_notification_text_without_data_restriction_hint
+                    : R.string.phone_in_ecm_call_notification_text).toString();
         } else {
             // Calculate the time in ms when the notification will be finished.
             long finishedCountMs = millisUntilFinished + System.currentTimeMillis();
@@ -205,14 +212,19 @@
 
             String completeTime = SimpleDateFormat.getTimeInstance(SimpleDateFormat.SHORT).format(
                     finishedCountMs);
-            text = getResources().getString(R.string.phone_in_ecm_notification_complete_time,
+            text = getResources().getString(
+                    // During IMS ECM, data restriction hint should be removed.
+                    (imsPhone != null && imsPhone.isInImsEcm())
+                    ? R.string.phone_in_ecm_notification_complete_time_without_data_restriction_hint
+                    : R.string.phone_in_ecm_notification_complete_time,
                     completeTime);
         }
         builder.setContentText(text);
         builder.setChannelId(NotificationChannelController.CHANNEL_ID_ALERT);
 
         // Show notification
-        mNotificationManager.notify(R.string.phone_in_ecm_notification_title, builder.build());
+        mNotificationManager.notifyAsUser(null, R.string.phone_in_ecm_notification_title,
+                builder.build(), UserHandle.ALL);
     }
 
     /**
diff --git a/src/com/android/phone/EmergencyDialer.java b/src/com/android/phone/EmergencyDialer.java
index 7531aca..183742e 100644
--- a/src/com/android/phone/EmergencyDialer.java
+++ b/src/com/android/phone/EmergencyDialer.java
@@ -40,12 +40,11 @@
 import android.os.Bundle;
 import android.os.PersistableBundle;
 import android.provider.Settings;
-import android.telecom.ParcelableCallAnalytics;
 import android.telecom.PhoneAccount;
 import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -490,7 +489,7 @@
         if (!TextUtils.isEmpty(phoneNumber)) {
             if (DBG) Log.d(LOG_TAG, "dial emergency number: " + Rlog.pii(LOG_TAG, phoneNumber));
 
-            placeCall(phoneNumber, ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_SHORTCUT,
+            placeCall(phoneNumber, TelecomManager.CALL_SOURCE_EMERGENCY_SHORTCUT,
                     mShortcutViewConfig.getPhoneInfo());
         } else {
             Log.d(LOG_TAG, "emergency number is empty");
@@ -720,8 +719,12 @@
             isEmergencyNumber = true;
             phoneToMakeCall = mShortcutViewConfig.getPhoneInfo();
         } else {
-            isEmergencyNumber = getSystemService(TelephonyManager.class)
-                    .isEmergencyNumber(mLastNumber);
+            try {
+                isEmergencyNumber = getSystemService(TelephonyManager.class)
+                        .isEmergencyNumber(mLastNumber);
+            } catch (IllegalStateException ise) {
+                isEmergencyNumber = false;
+            }
         }
 
         if (isEmergencyNumber) {
@@ -734,7 +737,7 @@
                 return;
             }
 
-            placeCall(mLastNumber, ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_DIALPAD,
+            placeCall(mLastNumber, TelecomManager.CALL_SOURCE_EMERGENCY_DIALPAD,
                     phoneToMakeCall);
         } else {
             if (DBG) Log.d(LOG_TAG, "rejecting bad requested number " + mLastNumber);
@@ -1172,9 +1175,9 @@
 
     private String callSourceToString(int callSource) {
         switch (callSource) {
-            case ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_DIALPAD:
+            case TelecomManager.CALL_SOURCE_EMERGENCY_DIALPAD:
                 return "DialPad";
-            case ParcelableCallAnalytics.CALL_SOURCE_EMERGENCY_SHORTCUT:
+            case TelecomManager.CALL_SOURCE_EMERGENCY_SHORTCUT:
                 return "Shortcut";
             default:
                 return "Unknown-" + callSource;
diff --git a/src/com/android/phone/EmergencyInfoGroup.java b/src/com/android/phone/EmergencyInfoGroup.java
index f5aca7f..a62edfd 100644
--- a/src/com/android/phone/EmergencyInfoGroup.java
+++ b/src/com/android/phone/EmergencyInfoGroup.java
@@ -209,7 +209,7 @@
     public void onClick(View view) {
         if (view.getId() == R.id.emergency_info_view) {
             AccessibilityManager accessibilityMgr =
-                    (AccessibilityManager) mContext.getSystemService(
+                    (AccessibilityManager) getContext().getSystemService(
                             Context.ACCESSIBILITY_SERVICE);
             if (accessibilityMgr.isTouchExplorationEnabled()) {
                 if (mOnConfirmClickListener != null) {
diff --git a/src/com/android/phone/EmergencyShortcutButton.java b/src/com/android/phone/EmergencyShortcutButton.java
index d147ce4..bfa956c 100644
--- a/src/com/android/phone/EmergencyShortcutButton.java
+++ b/src/com/android/phone/EmergencyShortcutButton.java
@@ -184,7 +184,7 @@
     public void onClick(View view) {
         if (view.getId() == R.id.emergency_call_number_info_view) {
             AccessibilityManager accessibilityMgr =
-                    (AccessibilityManager) mContext.getSystemService(
+                    (AccessibilityManager) getContext().getSystemService(
                             Context.ACCESSIBILITY_SERVICE);
             if (accessibilityMgr.isTouchExplorationEnabled()) {
                 // TalkBack itself includes a prompt to confirm click action implicitly,
diff --git a/src/com/android/phone/GsmUmtsCallBarringOptions.java b/src/com/android/phone/GsmUmtsCallBarringOptions.java
index 3c9cd84..30e9b5c 100644
--- a/src/com/android/phone/GsmUmtsCallBarringOptions.java
+++ b/src/com/android/phone/GsmUmtsCallBarringOptions.java
@@ -37,7 +37,6 @@
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.GsmCdmaPhone;
 import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.phone.settings.fdn.EditPinPreference;
 
 import java.util.ArrayList;
@@ -75,7 +74,6 @@
     private static final String DIALOG_PW_ENTRY_KEY = "dialog_pw_enter_key";
     private static final String KEY_STATUS = "toggle";
     private static final String PREFERENCE_ENABLED_KEY = "PREFERENCE_ENABLED";
-    private static final String PREFERENCE_SHOW_PASSWORD_KEY = "PREFERENCE_SHOW_PASSWORD";
     private static final String SAVED_BEFORE_LOAD_COMPLETED_KEY = "PROGRESS_SHOWING";
 
     private CallBarringEditPreference mButtonBAOC;
@@ -141,16 +139,13 @@
             return;
         }
 
-        String password = null;
-        if (mButtonDisableAll.isPasswordShown()) {
-            password = mButtonDisableAll.getText();
-            // Validate the length of password first, before submitting it to the
-            // RIL for CB disable.
-            if (!validatePassword(password)) {
-                mButtonDisableAll.setText("");
-                displayMessage(R.string.call_barring_right_pwd_number);
-                return;
-            }
+        String password = mButtonDisableAll.getText();
+        // Validate the length of password first, before submitting it to the
+        // RIL for CB disable.
+        if (!validatePassword(password)) {
+            mButtonDisableAll.setText("");
+            displayMessage(R.string.call_barring_right_pwd_number);
+            return;
         }
 
         // Submit the disable all request
@@ -417,34 +412,18 @@
         mPreferences.add(mButtonBAIC);
         mPreferences.add(mButtonBAICr);
 
-        // Find out if password is currently used.
-        boolean usePassword = true;
-        boolean useDisableaAll = true;
-
-        ImsPhone imsPhone = mPhone != null ? (ImsPhone) mPhone.getImsPhone() : null;
-        if (imsPhone != null && imsPhone.isUtEnabled()) {
-            usePassword = false;
-            useDisableaAll = false;
-        }
-
         // Find out if the sim card is ready.
         boolean isSimReady = TelephonyManager.from(this).getSimState(
                 SubscriptionManager.getSlotIndex(mPhone.getSubId()))
                         == TelephonyManager.SIM_STATE_READY;
 
-        // Deactivate all option is unavailable when sim card is not ready or Ut is enabled.
-        if (isSimReady && useDisableaAll) {
+        // Deactivate all option and Change password option are unavailable
+        // when sim card is not ready.
+        if (isSimReady) {
             mButtonDisableAll.setEnabled(true);
-            mButtonDisableAll.init(mPhone);
-        } else {
-            mButtonDisableAll.setEnabled(false);
-        }
-
-        // Change password option is unavailable when sim card is not ready or when the password is
-        // not used.
-        if (isSimReady && usePassword) {
             mButtonChangePW.setEnabled(true);
         } else {
+            mButtonDisableAll.setEnabled(false);
             mButtonChangePW.setEnabled(false);
             mButtonChangePW.setSummary(R.string.call_barring_change_pwd_description_disabled);
         }
@@ -472,8 +451,6 @@
                     pref.handleCallBarringResult(bundle.getBoolean(KEY_STATUS));
                     pref.init(this, true, mPhone);
                     pref.setEnabled(bundle.getBoolean(PREFERENCE_ENABLED_KEY, pref.isEnabled()));
-                    pref.setInputMethodNeeded(bundle.getBoolean(PREFERENCE_SHOW_PASSWORD_KEY,
-                            pref.needInputMethod()));
                 }
             }
             mPwChangeState = mIcicle.getInt(PW_CHANGE_STATE_KEY);
@@ -513,7 +490,6 @@
             Bundle bundle = new Bundle();
             bundle.putBoolean(KEY_STATUS, pref.mIsActivated);
             bundle.putBoolean(PREFERENCE_ENABLED_KEY, pref.isEnabled());
-            bundle.putBoolean(PREFERENCE_SHOW_PASSWORD_KEY, pref.needInputMethod());
             outState.putParcelable(pref.getKey(), bundle);
         }
         outState.putInt(PW_CHANGE_STATE_KEY, mPwChangeState);
diff --git a/src/com/android/phone/IccNetworkDepersonalizationPanel.java b/src/com/android/phone/IccNetworkDepersonalizationPanel.java
index 8bd10a2..7d854cd 100644
--- a/src/com/android/phone/IccNetworkDepersonalizationPanel.java
+++ b/src/com/android/phone/IccNetworkDepersonalizationPanel.java
@@ -22,7 +22,10 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
 import android.text.Editable;
 import android.text.Spannable;
 import android.text.TextUtils;
@@ -37,6 +40,8 @@
 import android.widget.TextView;
 
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.uicc.IccCardApplicationStatus;
+import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
 
 /**
  * "SIM network unlock" PIN entry screen.
@@ -52,7 +57,8 @@
      * Tracks whether there is an instance of the network depersonalization dialog showing or not.
      * Ensures only a single instance of the dialog is visible.
      */
-    private static boolean sShowingDialog = false;
+    private static boolean [] sShowingDialog =
+            new boolean[TelephonyManager.getDefault().getSimCount()];
 
     //debug constants
     private static final boolean DBG = false;
@@ -61,29 +67,48 @@
     private static final int EVENT_ICC_NTWRK_DEPERSONALIZATION_RESULT = 100;
 
     private Phone mPhone;
+    private int mPersoSubtype;
+    private static IccNetworkDepersonalizationPanel [] sNdpPanel =
+            new IccNetworkDepersonalizationPanel[TelephonyManager.getDefault().getSimCount()];
 
     //UI elements
     private EditText     mPinEntry;
     private LinearLayout mEntryPanel;
     private LinearLayout mStatusPanel;
+    private TextView     mPersoSubtypeText;
+    private PersoSubState mPersoSubState;
     private TextView     mStatusText;
 
     private Button       mUnlockButton;
     private Button       mDismissButton;
 
+    enum statusType {
+        ENTRY,
+        IN_PROGRESS,
+        ERROR,
+        SUCCESS
+    }
+
     /**
      * Shows the network depersonalization dialog, but only if it is not already visible.
      */
-    public static void showDialog(Phone phone) {
-        if (sShowingDialog) {
+    public static void showDialog(Phone phone, int subType) {
+        int phoneId = phone == null ? 0: phone.getPhoneId();
+        if (sShowingDialog[phoneId]) {
             Log.i(TAG, "[IccNetworkDepersonalizationPanel] - showDialog; skipped already shown.");
             return;
         }
         Log.i(TAG, "[IccNetworkDepersonalizationPanel] - showDialog; showing dialog.");
-        sShowingDialog = true;
-        IccNetworkDepersonalizationPanel ndpPanel =
-                new IccNetworkDepersonalizationPanel(PhoneGlobals.getInstance(), phone);
-        ndpPanel.show();
+        sShowingDialog[phoneId] = true;
+        sNdpPanel[phoneId] = new IccNetworkDepersonalizationPanel(PhoneGlobals.getInstance(),
+                phone, subType);
+        sNdpPanel[phoneId].show();
+    }
+
+    public static void dialogDismiss(int phoneId) {
+        if (sNdpPanel[phoneId] != null && sShowingDialog[phoneId]) {
+            sNdpPanel[phoneId].dismiss();
+        }
     }
 
     //private textwatcher to control text entry.
@@ -109,37 +134,41 @@
                 AsyncResult res = (AsyncResult) msg.obj;
                 if (res.exception != null) {
                     if (DBG) log("network depersonalization request failure.");
-                    indicateError();
+                    displayStatus(statusType.ERROR.name());
                     postDelayed(new Runnable() {
-                                    public void run() {
-                                        hideAlert();
-                                        mPinEntry.getText().clear();
-                                        mPinEntry.requestFocus();
-                                    }
-                                }, 3000);
+                        public void run() {
+                            hideAlert();
+                            mPinEntry.getText().clear();
+                            mPinEntry.requestFocus();
+                        }
+                    }, 3000);
                 } else {
                     if (DBG) log("network depersonalization success.");
-                    indicateSuccess();
+                    displayStatus(statusType.SUCCESS.name());
                     postDelayed(new Runnable() {
-                                    public void run() {
-                                        dismiss();
-                                    }
-                                }, 3000);
+                        public void run() {
+                            dismiss();
+                        }
+                    }, 3000);
                 }
             }
         }
     };
 
+
     //constructor
     public IccNetworkDepersonalizationPanel(Context context) {
         super(context);
         mPhone = PhoneGlobals.getPhone();
+        mPersoSubtype = PersoSubState.PERSOSUBSTATE_SIM_NETWORK.ordinal();
     }
 
     //constructor
-    public IccNetworkDepersonalizationPanel(Context context, Phone phone) {
+    public IccNetworkDepersonalizationPanel(Context context, Phone phone,
+            int subtype) {
         super(context);
         mPhone = phone == null ? PhoneGlobals.getPhone() : phone;
+        mPersoSubtype = subtype;
     }
 
     @Override
@@ -158,6 +187,8 @@
         span.setSpan(mPinEntryWatcher, 0, text.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
 
         mEntryPanel = (LinearLayout) findViewById(R.id.entry_panel);
+        mPersoSubtypeText = (TextView) findViewById(R.id.perso_subtype_text);
+        displayStatus(statusType.ENTRY.name());
 
         mUnlockButton = (Button) findViewById(R.id.ndp_unlock);
         mUnlockButton.setOnClickListener(mUnlockListener);
@@ -190,7 +221,8 @@
     public void onStop() {
         super.onStop();
         Log.i(TAG, "[IccNetworkDepersonalizationPanel] - showDialog; hiding dialog.");
-        sShowingDialog = false;
+        int phoneId = mPhone == null ? 0 : mPhone.getPhoneId();
+        sShowingDialog[phoneId] = false;
     }
 
     //Mirrors IccPinUnlockPanel.onKeyDown().
@@ -210,29 +242,45 @@
                 return;
             }
 
-            if (DBG) log("requesting network depersonalization with code " + pin);
-            mPhone.getIccCard().supplyNetworkDepersonalization(pin,
-                    Message.obtain(mHandler, EVENT_ICC_NTWRK_DEPERSONALIZATION_RESULT));
-            indicateBusy();
+            log("Requesting De-Personalization for subtype " + mPersoSubtype);
+
+            try {
+                mPhone.getIccCard().supplySimDepersonalization(mPersoSubState,pin,
+                        Message.obtain(mHandler, EVENT_ICC_NTWRK_DEPERSONALIZATION_RESULT));
+            } catch (NullPointerException ex) {
+                log("NullPointerException @supplySimDepersonalization" + ex);
+            }
+            displayStatus(statusType.IN_PROGRESS.name());
         }
     };
 
-    private void indicateBusy() {
-        mStatusText.setText(R.string.requesting_unlock);
-        mEntryPanel.setVisibility(View.GONE);
-        mStatusPanel.setVisibility(View.VISIBLE);
-    }
+    private void displayStatus(String type) {
+        int label = 0;
 
-    private void indicateError() {
-        mStatusText.setText(R.string.unlock_failed);
-        mEntryPanel.setVisibility(View.GONE);
-        mStatusPanel.setVisibility(View.VISIBLE);
-    }
+        mPersoSubState = PersoSubState.values()[mPersoSubtype];
+        log("displayStatus mPersoSubState: " +mPersoSubState.name() +"type: " +type);
 
-    private void indicateSuccess() {
-        mStatusText.setText(R.string.unlock_success);
-        mEntryPanel.setVisibility(View.GONE);
-        mStatusPanel.setVisibility(View.VISIBLE);
+        label = getContext().getResources().getIdentifier(mPersoSubState.name()
+                + "_" + type, "string", "android");
+
+        if (label == 0) {
+            log ("Unable to get the PersoSubType string");
+            return;
+        }
+
+        if(!PersoSubState.isPersoLocked(mPersoSubState)) {
+            log ("Unsupported Perso Subtype :" + mPersoSubState.name());
+            return;
+        }
+
+        if (type == statusType.ENTRY.name()) {
+            String displayText = getContext().getString(label);
+            mPersoSubtypeText.setText(displayText);
+        } else {
+            mStatusText.setText(label);
+            mEntryPanel.setVisibility(View.GONE);
+            mStatusPanel.setVisibility(View.VISIBLE);
+        }
     }
 
     private void hideAlert() {
@@ -241,13 +289,13 @@
     }
 
     View.OnClickListener mDismissListener = new View.OnClickListener() {
-            public void onClick(View v) {
-                if (DBG) log("mDismissListener: skipping depersonalization...");
-                dismiss();
-            }
-        };
+        public void onClick(View v) {
+            if (DBG) log("mDismissListener: skipping depersonalization...");
+            dismiss();
+        }
+    };
 
     private void log(String msg) {
-        Log.v(TAG, "[IccNetworkDepersonalizationPanel] " + msg);
+        Log.d(TAG, "[IccNetworkDepersonalizationPanel] " + msg);
     }
 }
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index 1a28afd..f390501 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -22,21 +22,26 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
+import android.telephony.SubscriptionManager;
 import android.telephony.ims.ImsException;
 import android.telephony.ims.RegistrationManager;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsRcsController;
 import android.telephony.ims.aidl.IImsRegistrationCallback;
 import android.telephony.ims.aidl.IRcsUceControllerCallback;
+import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
 import android.telephony.ims.feature.RcsFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.util.Log;
 
 import com.android.ims.ImsManager;
-import com.android.ims.RcsFeatureManager;
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.TelephonyPermissions;
 import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.services.telephony.rcs.RcsFeatureController;
+import com.android.services.telephony.rcs.TelephonyRcsService;
+import com.android.services.telephony.rcs.UserCapabilityExchangeImpl;
 
 import java.util.List;
 
@@ -50,6 +55,7 @@
     private static ImsRcsController sInstance;
 
     private PhoneGlobals mApp;
+    private TelephonyRcsService mRcsService;
 
     /**
      * Initialize the singleton ImsRcsController instance.
@@ -74,16 +80,16 @@
     }
 
     /**
-     * Register a IImsRegistrationCallback to receive IMS network registration state.
+     * Register a {@link RegistrationManager.RegistrationCallback} to receive IMS network
+     * registration state.
      */
     @Override
-    public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback callback)
-            throws RemoteException {
+    public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback callback) {
         enforceReadPrivilegedPermission("registerImsRegistrationCallback");
         final long token = Binder.clearCallingIdentity();
         try {
-            getRcsFeatureManager(subId).registerImsRegistrationCallback(callback);
-        } catch (com.android.ims.ImsException e) {
+            getRcsFeatureController(subId).registerImsRegistrationCallback(subId, callback);
+        } catch (ImsException e) {
             Log.e(TAG, "registerImsRegistrationCallback: sudId=" + subId + ", " + e.getMessage());
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -92,14 +98,14 @@
     }
 
     /**
-     * Removes an existing {@link RegistrationCallback}.
+     * Removes an existing {@link RegistrationManager.RegistrationCallback}.
      */
     @Override
     public void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback callback) {
         enforceReadPrivilegedPermission("unregisterImsRegistrationCallback");
         final long token = Binder.clearCallingIdentity();
         try {
-            getRcsFeatureManager(subId).unregisterImsRegistrationCallback(callback);
+            getRcsFeatureController(subId).unregisterImsRegistrationCallback(subId, callback);
         } catch (ServiceSpecificException e) {
             Log.e(TAG, "unregisterImsRegistrationCallback: error=" + e.errorCode);
         } finally {
@@ -115,7 +121,7 @@
         enforceReadPrivilegedPermission("getImsRcsRegistrationState");
         final long token = Binder.clearCallingIdentity();
         try {
-            getImsPhone(subId).getImsRcsRegistrationState(regState -> {
+            getRcsFeatureController(subId).getRegistrationState(regState -> {
                 try {
                     consumer.accept((regState == null)
                             ? RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED : regState);
@@ -136,7 +142,7 @@
         enforceReadPrivilegedPermission("getImsRcsRegistrationTransportType");
         final long token = Binder.clearCallingIdentity();
         try {
-            getImsPhone(subId).getImsRcsRegistrationTech(regTech -> {
+            getRcsFeatureController(subId).getRegistrationTech(regTech -> {
                 // Convert registration tech from ImsRegistrationImplBase -> RegistrationManager
                 int regTechConverted = (regTech == null)
                         ? ImsRegistrationImplBase.REGISTRATION_TECH_NONE : regTech;
@@ -161,13 +167,12 @@
      * @param callback The ImsCapabilityCallback to be registered.
      */
     @Override
-    public void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback)
-            throws RemoteException {
+    public void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) {
         enforceReadPrivilegedPermission("registerRcsAvailabilityCallback");
         final long token = Binder.clearCallingIdentity();
         try {
-            getRcsFeatureManager(subId).registerRcsAvailabilityCallback(callback);
-        } catch (com.android.ims.ImsException e) {
+            getRcsFeatureController(subId).registerRcsAvailabilityCallback(subId, callback);
+        } catch (ImsException e) {
             Log.e(TAG, "registerRcsAvailabilityCallback: sudId=" + subId + ", " + e.getMessage());
             throw new ServiceSpecificException(e.getCode());
         } finally {
@@ -186,9 +191,41 @@
         enforceReadPrivilegedPermission("unregisterRcsAvailabilityCallback");
         final long token = Binder.clearCallingIdentity();
         try {
-            getRcsFeatureManager(subId).unregisterRcsAvailabilityCallback(callback);
-        } catch (com.android.ims.ImsException e) {
-            Log.e(TAG, "unregisterRcsAvailabilityCallback: sudId=" + subId + "," + e.getMessage());
+            getRcsFeatureController(subId).unregisterRcsAvailabilityCallback(subId, callback);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
+        enforceReadPrivilegedPermission("registerUcePublishStateCallback");
+        final long token = Binder.clearCallingIdentity();
+        try {
+            UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
+                    UserCapabilityExchangeImpl.class);
+            if (uce == null) {
+                throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                    "This subscription does not support UCE.");
+            }
+            uce.registerPublishStateCallback(c);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void unregisterUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c) {
+        enforceReadPrivilegedPermission("unregisterUcePublishStateCallback");
+        final long token = Binder.clearCallingIdentity();
+        try {
+            UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
+                    UserCapabilityExchangeImpl.class);
+            if (uce == null) {
+                throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                    "This subscription does not support UCE.");
+            }
+            uce.unregisterUcePublishStateCallback(c);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -209,8 +246,8 @@
         enforceReadPrivilegedPermission("isCapable");
         final long token = Binder.clearCallingIdentity();
         try {
-            return getRcsFeatureManager(subId).isCapable(capability, radioTech);
-        } catch (com.android.ims.ImsException e) {
+            return getRcsFeatureController(subId).isCapable(capability, radioTech);
+        } catch (ImsException e) {
             Log.e(TAG, "isCapable: sudId=" + subId
                     + ", capability=" + capability + ", " + e.getMessage());
             return false;
@@ -233,8 +270,8 @@
         enforceReadPrivilegedPermission("isAvailable");
         final long token = Binder.clearCallingIdentity();
         try {
-            return getRcsFeatureManager(subId).isAvailable(capability);
-        } catch (com.android.ims.ImsException e) {
+            return getRcsFeatureController(subId).isAvailable(capability);
+        } catch (ImsException e) {
             Log.e(TAG, "isAvailable: sudId=" + subId
                     + ", capability=" + capability + ", " + e.getMessage());
             return false;
@@ -244,26 +281,71 @@
     }
 
     @Override
-    public void requestCapabilities(int subId, List<Uri> contactNumbers,
-            IRcsUceControllerCallback c) {
+    public void requestCapabilities(int subId, String callingPackage, String callingFeatureId,
+            List<Uri> contactNumbers, IRcsUceControllerCallback c) {
         enforceReadPrivilegedPermission("requestCapabilities");
+        if (!isUceSettingEnabled(subId, callingPackage, callingFeatureId)) {
+            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                    "The user has not enabled UCE for this subscription.");
+        }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
+                    UserCapabilityExchangeImpl.class);
+            if (uce == null) {
+                throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                        "This subscription does not support UCE.");
+            }
+            uce.requestCapabilities(contactNumbers, c);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     @Override
     public int getUcePublishState(int subId) {
         enforceReadPrivilegedPermission("getUcePublishState");
-        return -1;
+        final long token = Binder.clearCallingIdentity();
+        try {
+            UserCapabilityExchangeImpl uce = getRcsFeatureController(subId).getFeature(
+                    UserCapabilityExchangeImpl.class);
+            if (uce == null) {
+                throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                        "This subscription does not support UCE.");
+            }
+            return uce.getUcePublishState();
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     @Override
-    public boolean isUceSettingEnabled(int subId) {
-        enforceReadPrivilegedPermission("isUceSettingEnabled");
-        return false;
+    public boolean isUceSettingEnabled(int subId, String callingPackage, String callingFeatureId) {
+        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+                mApp, subId, callingPackage, callingFeatureId, "isUceSettingEnabled")) {
+            Log.w(TAG, "isUceSettingEnabled: READ_PHONE_STATE app op disabled when accessing "
+                    + "isUceSettingEnabled");
+            return false;
+        }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return SubscriptionManager.getBooleanSubscriptionProperty(subId,
+                    SubscriptionManager.IMS_RCS_UCE_ENABLED, false /*defaultValue*/, mApp);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     @Override
     public void setUceSettingEnabled(int subId, boolean isEnabled) {
         enforceModifyPermission();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            SubscriptionManager.setSubscriptionProperty(subId,
+                    SubscriptionManager.IMS_RCS_UCE_ENABLED, (isEnabled ? "1" : "0"));
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     /**
@@ -317,26 +399,30 @@
      * @return The RcsFeatureManager instance
      * @throws ServiceSpecificException if getting RcsFeatureManager instance failed.
      */
-    private RcsFeatureManager getRcsFeatureManager(int subId) {
+    private RcsFeatureController getRcsFeatureController(int subId) {
         if (!ImsManager.isImsSupportedOnDevice(mApp)) {
             throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
                     "IMS is not available on device.");
         }
+        if (mRcsService == null) {
+            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                    "IMS is not available on device.");
+        }
         Phone phone = PhoneGlobals.getPhone(subId);
         if (phone == null) {
             throw new ServiceSpecificException(ImsException.CODE_ERROR_INVALID_SUBSCRIPTION,
                     "Invalid subscription Id: " + subId);
         }
-        ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
-        if (imsPhone == null) {
-            throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
-                    "Cannot find ImsPhone instance: " + subId);
+        int slotId = phone.getPhoneId();
+        RcsFeatureController c = mRcsService.getFeatureController(slotId);
+        if (c == null) {
+            throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                    "The requested operation is not supported for subId " + subId);
         }
-        RcsFeatureManager rcsFeatureManager = imsPhone.getRcsManager();
-        if (rcsFeatureManager == null) {
-            throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
-                    "Cannot find RcsFeatureManager instance: " + subId);
-        }
-        return rcsFeatureManager;
+        return c;
+    }
+
+    void setRcsService(TelephonyRcsService rcsService) {
+        mRcsService = rcsService;
     }
 }
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index fe55335..fccceec 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -470,7 +470,7 @@
 
     private Intent getShowVoicemailIntentForDefaultDialer(UserHandle userHandle) {
         String dialerPackage = mContext.getSystemService(TelecomManager.class)
-                .getDefaultDialerPackage(userHandle.getIdentifier());
+                .getDefaultDialerPackage(userHandle);
         return new Intent(TelephonyManager.ACTION_SHOW_VOICEMAIL_NOTIFICATION)
                 .setPackage(dialerPackage);
     }
diff --git a/src/com/android/phone/NumberVerificationManager.java b/src/com/android/phone/NumberVerificationManager.java
index 9ec16f8..2298d40 100644
--- a/src/com/android/phone/NumberVerificationManager.java
+++ b/src/com/android/phone/NumberVerificationManager.java
@@ -135,7 +135,7 @@
                 }
 
                 // make sure at least one phone is registered for voice
-                if (phone.getServiceState().getVoiceRegState() == ServiceState.STATE_IN_SERVICE) {
+                if (phone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE) {
                     isAnyPhoneVoiceRegistered = true;
                 }
                 // make sure at least one phone has room for an incoming call.
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 9ecb2c5..df1f470 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -71,6 +71,7 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.phone.settings.SettingsConstants;
 import com.android.phone.vvm.CarrierVvmPackageInstalledReceiver;
+import com.android.services.telephony.rcs.TelephonyRcsService;
 import com.android.services.telephony.sip.SipAccountRegistry;
 import com.android.services.telephony.sip.SipUtil;
 
@@ -149,6 +150,7 @@
     CallerInfoCache callerInfoCache;
     NotificationMgr notificationMgr;
     ImsResolver mImsResolver;
+    TelephonyRcsService mTelephonyRcsService;
     public PhoneInterfaceManager phoneMgr;
     public ImsRcsController imsRcsController;
     CarrierConfigLoader configLoader;
@@ -203,6 +205,16 @@
 
     private final SettingsObserver mSettingsObserver;
 
+    private static class EventSimStateChangedBag {
+        final int mPhoneId;
+        final String mIccStatus;
+
+        EventSimStateChangedBag(int phoneId, String iccStatus) {
+            mPhoneId = phoneId;
+            mIccStatus = iccStatus;
+        }
+    }
+
     Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
@@ -223,7 +235,8 @@
                         // they enter a valid SIM network PIN.
                         Log.i(LOG_TAG, "show sim depersonal panel");
                         Phone phone = (Phone) ((AsyncResult) msg.obj).userObj;
-                        IccNetworkDepersonalizationPanel.showDialog(phone);
+                        int subType = (Integer)((AsyncResult)msg.obj).result;
+                        IccNetworkDepersonalizationPanel.showDialog(phone, subType);
                     }
                     break;
 
@@ -251,8 +264,9 @@
                     // Marks the event where the SIM goes into ready state.
                     // Right now, this is only used for the PUK-unlocking
                     // process.
-                    if (msg.obj.equals(IccCardConstants.INTENT_VALUE_ICC_READY)
-                            || msg.obj.equals(IccCardConstants.INTENT_VALUE_ICC_LOADED)) {
+                    EventSimStateChangedBag bag = (EventSimStateChangedBag)msg.obj;
+                    if (bag.mIccStatus == IccCardConstants.INTENT_VALUE_ICC_READY
+                            || bag.mIccStatus == IccCardConstants.INTENT_VALUE_ICC_LOADED) {
                         // when the right event is triggered and there
                         // are UI objects in the foreground, we close
                         // them to display the lock panel.
@@ -264,6 +278,8 @@
                             mPUKEntryProgressDialog.dismiss();
                             mPUKEntryProgressDialog = null;
                         }
+                        Log.i(LOG_TAG, "Dismissing depersonal panel");
+                        IccNetworkDepersonalizationPanel.dialogDismiss(bag.mPhoneId);
                     }
                     break;
 
@@ -303,8 +319,8 @@
         // Cache the "voice capable" flag.
         // This flag currently comes from a resource (which is
         // overrideable on a per-product basis):
-        sVoiceCapable =
-                getResources().getBoolean(com.android.internal.R.bool.config_voice_capable);
+        sVoiceCapable = ((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE))
+                .isVoiceCapable();
         // ...but this might eventually become a PackageManager "system
         // feature" instead, in which case we'd do something like:
         // sVoiceCapable =
@@ -372,6 +388,13 @@
 
             imsRcsController = ImsRcsController.init(this);
 
+            if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS)) {
+                mTelephonyRcsService = new TelephonyRcsService(this,
+                        PhoneFactory.getPhones().length);
+                mTelephonyRcsService.initialize();
+                imsRcsController.setRcsService(mTelephonyRcsService);
+            }
+
             configLoader = CarrierConfigLoader.init(this);
 
             // Create the CallNotifier singleton, which handles
@@ -403,7 +426,7 @@
             IntentFilter sipIntentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
             sipIntentFilter.addAction(SipManager.ACTION_SIP_SERVICE_UP);
             sipIntentFilter.addAction(SipManager.ACTION_SIP_CALL_OPTION_CHANGED);
-            sipIntentFilter.addAction(SipManager.ACTION_SIP_REMOVE_PHONE);
+            sipIntentFilter.addAction(SipManager.ACTION_SIP_REMOVE_PROFILE);
             registerReceiver(mSipReceiver, sipIntentFilter);
 
             mCarrierVvmPackageInstalledReceiver.register(this);
@@ -640,14 +663,9 @@
                     PhoneUtils.unregisterIccStatus(mHandler, phoneId);
                     PhoneUtils.registerIccStatus(mHandler, EVENT_SIM_NETWORK_LOCKED, phoneId);
                 }
-                if (mPUKEntryActivity != null) {
-                    // if an attempt to un-PUK-lock the device was made, while we're
-                    // receiving this state change notification, notify the handler.
-                    // NOTE: This is ONLY triggered if an attempt to un-PUK-lock has
-                    // been attempted.
-                    mHandler.sendMessage(mHandler.obtainMessage(EVENT_SIM_STATE_CHANGED,
-                            intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE)));
-                }
+                String iccStatus = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
+                mHandler.sendMessage(mHandler.obtainMessage(EVENT_SIM_STATE_CHANGED,
+                        new EventSimStateChangedBag(phoneId, iccStatus)));
             } else if (action.equals(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED)) {
                 String newPhone = intent.getStringExtra(PhoneConstants.PHONE_NAME_KEY);
                 Log.d(LOG_TAG, "Radio technology switched. Now " + newPhone + " is active.");
@@ -662,7 +680,8 @@
                     if (TelephonyCapabilities.supportsEcm(phoneInEcm)) {
                         Log.d(LOG_TAG, "Emergency Callback Mode arrived in PhoneApp.");
                         // Start Emergency Callback Mode service
-                        if (intent.getBooleanExtra("phoneinECMState", false)) {
+                        if (intent.getBooleanExtra(
+                                TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false)) {
                             context.startService(new Intent(context,
                                     EmergencyCallbackModeService.class));
                         } else {
@@ -708,7 +727,7 @@
             } else if (action.equals(SipManager.ACTION_SIP_SERVICE_UP)
                     || action.equals(SipManager.ACTION_SIP_CALL_OPTION_CHANGED)) {
                 sipAccountRegistry.setup(context);
-            } else if (action.equals(SipManager.ACTION_SIP_REMOVE_PHONE)) {
+            } else if (action.equals(SipManager.ACTION_SIP_REMOVE_PROFILE)) {
                 if (DBG) {
                     Log.d(LOG_TAG, "SIP_REMOVE_PHONE "
                             + intent.getStringExtra(SipManager.EXTRA_LOCAL_URI));
@@ -905,6 +924,12 @@
             e.printStackTrace();
         }
         pw.decreaseIndent();
+        pw.println("RcsService:");
+        try {
+            if (mTelephonyRcsService != null) mTelephonyRcsService.dump(fd, pw, args);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
         pw.decreaseIndent();
         pw.println("------- End PhoneGlobals -------");
     }
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 859131f..3372aae 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -35,7 +35,6 @@
 import android.content.pm.ComponentInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.net.NetworkStats;
 import android.net.Uri;
 import android.os.AsyncResult;
 import android.os.Binder;
@@ -46,8 +45,10 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
+import android.os.ParcelFileDescriptor;
 import android.os.ParcelUuid;
 import android.os.PersistableBundle;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
@@ -65,6 +66,7 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telephony.Annotation.ApnType;
+import android.telephony.CallForwardingInfo;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CarrierRestrictionRules;
 import android.telephony.CellIdentity;
@@ -84,7 +86,6 @@
 import android.telephony.PhoneNumberRange;
 import android.telephony.RadioAccessFamily;
 import android.telephony.RadioAccessSpecifier;
-import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionInfo;
@@ -110,24 +111,27 @@
 import android.telephony.ims.aidl.IImsRegistrationCallback;
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.feature.RcsFeature;
 import android.telephony.ims.stub.ImsConfigImplBase;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
-import android.util.Slog;
 
 import com.android.ims.ImsManager;
 import com.android.ims.internal.IImsServiceFeatureCallback;
+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.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.HalVersion;
+import com.android.internal.telephony.IBooleanConsumer;
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.INumberVerificationCallback;
 import com.android.internal.telephony.ITelephony;
@@ -173,6 +177,7 @@
 import com.android.phone.vvm.RemoteVvmTaskManager;
 import com.android.phone.vvm.VisualVoicemailSettingsUtil;
 import com.android.phone.vvm.VisualVoicemailSmsFilterConfig;
+import com.android.telephony.Rlog;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -185,6 +190,7 @@
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Set;
+import java.util.function.Consumer;
 
 /**
  * Implementation of the ITelephony interface.
@@ -266,6 +272,20 @@
     private static final int EVENT_SET_FORBIDDEN_PLMNS_DONE = 73;
     private static final int CMD_ERASE_MODEM_CONFIG = 74;
     private static final int EVENT_ERASE_MODEM_CONFIG_DONE = 75;
+    private static final int CMD_CHANGE_ICC_LOCK_PASSWORD = 76;
+    private static final int EVENT_CHANGE_ICC_LOCK_PASSWORD_DONE = 77;
+    private static final int CMD_SET_ICC_LOCK_ENABLED = 78;
+    private static final int EVENT_SET_ICC_LOCK_ENABLED_DONE = 79;
+    private static final int CMD_SET_SYSTEM_SELECTION_CHANNELS = 80;
+    private static final int EVENT_SET_SYSTEM_SELECTION_CHANNELS_DONE = 81;
+    private static final int CMD_GET_CALL_FORWARDING = 83;
+    private static final int EVENT_GET_CALL_FORWARDING_DONE = 84;
+    private static final int CMD_SET_CALL_FORWARDING = 85;
+    private static final int EVENT_SET_CALL_FORWARDING_DONE = 86;
+    private static final int CMD_GET_CALL_WAITING = 87;
+    private static final int EVENT_GET_CALL_WAITING_DONE = 88;
+    private static final int CMD_SET_CALL_WAITING = 89;
+    private static final int EVENT_SET_CALL_WAITING_DONE = 90;
 
     // Parameters of select command.
     private static final int SELECT_COMMAND = 0xA4;
@@ -800,6 +820,148 @@
                     getPhoneFromRequest(request).getAvailableNetworks(onCompleted);
                     break;
 
+                case CMD_GET_CALL_FORWARDING:
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_GET_CALL_FORWARDING_DONE, request);
+                    int callForwardingReason = (Integer) request.argument;
+                    getPhoneFromRequest(request).getCallForwardingOption(
+                            callForwardingReason, onCompleted);
+                    break;
+
+                case EVENT_GET_CALL_FORWARDING_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    CallForwardingInfo callForwardingInfo = null;
+                    if (ar.exception == null && ar.result != null) {
+                        CallForwardInfo[] callForwardInfos = (CallForwardInfo[]) ar.result;
+                        for (CallForwardInfo callForwardInfo : callForwardInfos) {
+                            // Service Class is a bit mask per 3gpp 27.007. Search for
+                            // any service for voice call.
+                            if ((callForwardInfo.serviceClass
+                                    & CommandsInterface.SERVICE_CLASS_VOICE) > 0) {
+                                callForwardingInfo = new CallForwardingInfo(
+                                        callForwardInfo.serviceClass, callForwardInfo.reason,
+                                                callForwardInfo.number,
+                                                        callForwardInfo.timeSeconds);
+                                break;
+                            }
+                        }
+                        // Didn't find a call forward info for voice call.
+                        if (callForwardingInfo == null) {
+                            callForwardingInfo = new CallForwardingInfo(
+                                    CallForwardingInfo.STATUS_UNKNOWN_ERROR,
+                                            0 /* reason */, null /* number */, 0 /* timeout */);
+                        }
+                    } else {
+                        if (ar.result == null) {
+                            loge("EVENT_GET_CALL_FORWARDING_DONE: Empty response");
+                        }
+                        if (ar.exception != null) {
+                            loge("EVENT_GET_CALL_FORWARDING_DONE: Exception: " + ar.exception);
+                        }
+                        int errorCode = CallForwardingInfo.STATUS_UNKNOWN_ERROR;
+                        if (ar.exception instanceof CommandException) {
+                            CommandException.Error error =
+                                    ((CommandException) (ar.exception)).getCommandError();
+                            if (error == CommandException.Error.FDN_CHECK_FAILURE) {
+                                errorCode = CallForwardingInfo.STATUS_FDN_CHECK_FAILURE;
+                            } else if (error == CommandException.Error.REQUEST_NOT_SUPPORTED) {
+                                errorCode = CallForwardingInfo.STATUS_NOT_SUPPORTED;
+                            }
+                        }
+                        callForwardingInfo = new CallForwardingInfo(
+                                errorCode, 0 /* reason */, null /* number */, 0 /* timeout */);
+                    }
+                    request.result = callForwardingInfo;
+                    notifyRequester(request);
+                    break;
+
+                case CMD_SET_CALL_FORWARDING:
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_SET_CALL_FORWARDING_DONE, request);
+                    CallForwardingInfo callForwardingInfoToSet =
+                            (CallForwardingInfo) request.argument;
+                    getPhoneFromRequest(request).setCallForwardingOption(
+                            callForwardingInfoToSet.getStatus(),
+                            callForwardingInfoToSet.getReason(),
+                            callForwardingInfoToSet.getNumber(),
+                            callForwardingInfoToSet.getTimeoutSeconds(), onCompleted);
+                    break;
+
+                case EVENT_SET_CALL_FORWARDING_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    if (ar.exception == null) {
+                        request.result = true;
+                    } else {
+                        request.result = false;
+                        loge("setCallForwarding exception: " + ar.exception);
+                    }
+                    notifyRequester(request);
+                    break;
+
+                case CMD_GET_CALL_WAITING:
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_GET_CALL_WAITING_DONE, request);
+                    getPhoneFromRequest(request).getCallWaiting(onCompleted);
+                    break;
+
+                case EVENT_GET_CALL_WAITING_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    int callForwardingStatus = TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR;
+                    if (ar.exception == null && ar.result != null) {
+                        int[] callForwardResults = (int[]) ar.result;
+                        // Service Class is a bit mask per 3gpp 27.007.
+                        // Search for any service for voice call.
+                        if (callForwardResults.length > 1
+                                && ((callForwardResults[1]
+                                        & CommandsInterface.SERVICE_CLASS_VOICE) > 0)) {
+                            callForwardingStatus = callForwardResults[0] == 0
+                                    ? TelephonyManager.CALL_WAITING_STATUS_INACTIVE
+                                            : TelephonyManager.CALL_WAITING_STATUS_ACTIVE;
+                        } else {
+                            callForwardingStatus = TelephonyManager.CALL_WAITING_STATUS_INACTIVE;
+                        }
+                    } else {
+                        if (ar.result == null) {
+                            loge("EVENT_GET_CALL_WAITING_DONE: Empty response");
+                        }
+                        if (ar.exception != null) {
+                            loge("EVENT_GET_CALL_WAITING_DONE: Exception: " + ar.exception);
+                        }
+                        if (ar.exception instanceof CommandException) {
+                            CommandException.Error error =
+                                    ((CommandException) (ar.exception)).getCommandError();
+                            if (error == CommandException.Error.REQUEST_NOT_SUPPORTED) {
+                                callForwardingStatus =
+                                        TelephonyManager.CALL_WAITING_STATUS_NOT_SUPPORTED;
+                            }
+                        }
+                    }
+                    request.result = callForwardingStatus;
+                    notifyRequester(request);
+                    break;
+
+                case CMD_SET_CALL_WAITING:
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_SET_CALL_WAITING_DONE, request);
+                    boolean isEnable = (Boolean) request.argument;
+                    getPhoneFromRequest(request).setCallWaiting(isEnable, onCompleted);
+                    break;
+
+                case EVENT_SET_CALL_WAITING_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    if (ar.exception == null) {
+                        request.result = true;
+                    } else {
+                        request.result = false;
+                        loge("setCallWaiting exception: " + ar.exception);
+                    }
+                    notifyRequester(request);
+                    break;
+
                 case EVENT_PERFORM_NETWORK_SCAN_DONE:
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
@@ -859,14 +1021,47 @@
                     onCompleted = obtainMessage(EVENT_GET_MODEM_ACTIVITY_INFO_DONE, request);
                     if (defaultPhone != null) {
                         defaultPhone.getModemActivityInfo(onCompleted, request.workSource);
+                    } else {
+                        ResultReceiver result = (ResultReceiver) request.argument;
+                        Bundle bundle = new Bundle();
+                        bundle.putParcelable(TelephonyManager.MODEM_ACTIVITY_RESULT_KEY,
+                                new ModemActivityInfo(0, 0, 0, new int[0], 0));
+                        result.send(0, bundle);
                     }
                     break;
 
                 case EVENT_GET_MODEM_ACTIVITY_INFO_DONE:
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
+                    ResultReceiver result = (ResultReceiver) request.argument;
+
+                    ModemActivityInfo ret = new ModemActivityInfo(0, 0, 0, new int[0], 0);
                     if (ar.exception == null && ar.result != null) {
-                        request.result = ar.result;
+                        // Update the last modem activity info and the result of the request.
+                        ModemActivityInfo info = (ModemActivityInfo) ar.result;
+                        if (isModemActivityInfoValid(info)) {
+                            int[] mergedTxTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
+                            int[] txTimeMs = info.getTransmitTimeMillis();
+                            int[] lastModemTxTimeMs = mLastModemActivityInfo
+                                    .getTransmitTimeMillis();
+                            for (int i = 0; i < mergedTxTimeMs.length; i++) {
+                                mergedTxTimeMs[i] = txTimeMs[i] + lastModemTxTimeMs[i];
+                            }
+                            mLastModemActivityInfo.setTimestamp(info.getTimestamp());
+                            mLastModemActivityInfo.setSleepTimeMillis(info.getSleepTimeMillis()
+                                    + mLastModemActivityInfo.getSleepTimeMillis());
+                            mLastModemActivityInfo.setIdleTimeMillis(info.getIdleTimeMillis()
+                                    + mLastModemActivityInfo.getIdleTimeMillis());
+                            mLastModemActivityInfo.setTransmitTimeMillis(mergedTxTimeMs);
+                            mLastModemActivityInfo.setReceiveTimeMillis(
+                                    info.getReceiveTimeMillis()
+                                            + mLastModemActivityInfo.getReceiveTimeMillis());
+                        }
+                        ret = new ModemActivityInfo(mLastModemActivityInfo.getTimestamp(),
+                                mLastModemActivityInfo.getSleepTimeMillis(),
+                                mLastModemActivityInfo.getIdleTimeMillis(),
+                                mLastModemActivityInfo.getTransmitTimeMillis(),
+                                mLastModemActivityInfo.getReceiveTimeMillis());
                     } else {
                         if (ar.result == null) {
                             loge("queryModemActivityInfo: Empty response");
@@ -877,10 +1072,9 @@
                             loge("queryModemActivityInfo: Unknown exception");
                         }
                     }
-                    // Result cannot be null. Return ModemActivityInfo with all fields set to 0.
-                    if (request.result == null) {
-                        request.result = new ModemActivityInfo(0, 0, 0, null, 0);
-                    }
+                    Bundle bundle = new Bundle();
+                    bundle.putParcelable(TelephonyManager.MODEM_ACTIVITY_RESULT_KEY, ret);
+                    result.send(0, bundle);
                     notifyRequester(request);
                     break;
 
@@ -1178,6 +1372,23 @@
                     }
                     notifyRequester(request);
                     break;
+                case CMD_SET_SYSTEM_SELECTION_CHANNELS: {
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_SET_SYSTEM_SELECTION_CHANNELS_DONE, request);
+                    Pair<List<RadioAccessSpecifier>, Consumer<Boolean>> args =
+                            (Pair<List<RadioAccessSpecifier>, Consumer<Boolean>>) request.argument;
+                    request.phone.setSystemSelectionChannels(args.first, onCompleted);
+                    break;
+                }
+                case EVENT_SET_SYSTEM_SELECTION_CHANNELS_DONE: {
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    Pair<List<RadioAccessSpecifier>, Consumer<Boolean>> args =
+                            (Pair<List<RadioAccessSpecifier>, Consumer<Boolean>>) request.argument;
+                    args.second.accept(ar.exception == null);
+                    notifyRequester(request);
+                    break;
+                }
                 case EVENT_SET_FORBIDDEN_PLMNS_DONE:
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
@@ -1230,6 +1441,43 @@
                 case EVENT_ERASE_MODEM_CONFIG_DONE:
                     handleNullReturnEvent(msg, "eraseModemConfig");
                     break;
+
+                case CMD_CHANGE_ICC_LOCK_PASSWORD:
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_CHANGE_ICC_LOCK_PASSWORD_DONE, request);
+                    Pair<String, String> changed = (Pair<String, String>) request.argument;
+                    getPhoneFromRequest(request).getIccCard().changeIccLockPassword(
+                            changed.first, changed.second, onCompleted);
+                    break;
+                case EVENT_CHANGE_ICC_LOCK_PASSWORD_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    if (ar.exception == null) {
+                        request.result = TelephonyManager.CHANGE_ICC_LOCK_SUCCESS;
+                    } else {
+                        request.result = msg.arg1;
+                    }
+                    notifyRequester(request);
+                    break;
+
+                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;
+                    } else {
+                        request.result = msg.arg1;
+                    }
+                    notifyRequester(request);
+                    break;
+
                 default:
                     Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
                     break;
@@ -1453,6 +1701,16 @@
         }
     }
 
+    private boolean isImsAvailableOnDevice() {
+        PackageManager pm = getDefaultPhone().getContext().getPackageManager();
+        if (pm == null) {
+            // For some reason package manger is not available.. This will fail internally anyway,
+            // so do not throw error and allow.
+            return true;
+        }
+        return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS, 0);
+    }
+
     public void dial(String number) {
         dialForSubscriber(getPreferredVoiceSubscription(), number);
     }
@@ -2005,6 +2263,7 @@
                                 .setCallingPid(Binder.getCallingPid())
                                 .setCallingUid(Binder.getCallingUid())
                                 .setMethod("getCellLocation")
+                                .setMinSdkVersionForCoarse(Build.VERSION_CODES.BASE)
                                 .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
                                 .build());
         switch (locationResult) {
@@ -2027,16 +2286,7 @@
     }
 
     @Override
-    public String getNetworkCountryIsoForPhone(int phoneId, String callingPackage,
-            String callingFeatureId) {
-        if (!TextUtils.isEmpty(callingPackage)) {
-            final int subId = mSubscriptionController.getSubIdUsingPhoneId(phoneId);
-            if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, subId, callingPackage,
-                    callingFeatureId, "getNetworkCountryIsoForPhone")) {
-                return "";
-            }
-        }
-
+    public String getNetworkCountryIsoForPhone(int phoneId) {
         // Reporting the correct network country is ambiguous when IWLAN could conflict with
         // registered cell info, so return a NULL country instead.
         final long identity = Binder.clearCallingIdentity();
@@ -2046,29 +2296,15 @@
                 phoneId = SubscriptionManager.DEFAULT_PHONE_INDEX;
             }
             final int subId = mSubscriptionController.getSubIdUsingPhoneId(phoneId);
-            // Todo: fix this when we can get the actual cellular network info when the device
-            // is on IWLAN.
-            if (TelephonyManager.NETWORK_TYPE_IWLAN
-                    == getVoiceNetworkTypeForSubscriber(subId, mApp.getPackageName(),
-                    null)) {
-                return "";
-            }
             Phone phone = PhoneFactory.getPhone(phoneId);
-            if (phone != null) {
-                ServiceStateTracker sst = phone.getServiceStateTracker();
-                EmergencyNumberTracker emergencyNumberTracker = phone.getEmergencyNumberTracker();
-                if (sst != null) {
-                    LocaleTracker lt = sst.getLocaleTracker();
-                    if (lt != null) {
-                        if (!TextUtils.isEmpty(lt.getCurrentCountry())) {
-                            return lt.getCurrentCountry();
-                        } else if (emergencyNumberTracker != null) {
-                            return emergencyNumberTracker.getEmergencyCountryIso();
-                        }
-                    }
-                }
-            }
-            return "";
+            if (phone == null) return "";
+            ServiceStateTracker sst = phone.getServiceStateTracker();
+            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();
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -2440,6 +2676,17 @@
         mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
     }
 
+    /**
+     * Make sure the caller is system.
+     *
+     * @throws SecurityException if the caller is not system.
+     */
+    private void enforceSystemCaller() {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Caller must be system");
+        }
+    }
+
     private void enforceActiveEmergencySessionPermission() {
         mApp.enforceCallingOrSelfPermission(
                 android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null);
@@ -2677,7 +2924,8 @@
      */
     @Override
     public boolean setVoiceMailNumber(int subId, String alphaTag, String number) {
-        TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(subId, "setVoiceMailNumber");
+        TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
+                mApp, subId, "setVoiceMailNumber");
 
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -2783,14 +3031,15 @@
     }
 
     @Override
-    public void sendVisualVoicemailSmsForSubscriber(String callingPackage, int subId,
-            String number, int port, String text, PendingIntent sentIntent) {
+    public void sendVisualVoicemailSmsForSubscriber(String callingPackage,
+            String callingAttributionTag, int subId, String number, int port, String text,
+            PendingIntent sentIntent) {
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
         enforceVisualVoicemailPackage(callingPackage, subId);
         enforceSendSmsPermission();
         SmsController smsController = PhoneFactory.getSmsController();
-        smsController.sendVisualVoicemailSmsForSubscriber(callingPackage, subId, number, port, text,
-                sentIntent);
+        smsController.sendVisualVoicemailSmsForSubscriber(callingPackage, callingAttributionTag,
+                subId, number, port, text, sentIntent);
     }
 
     /**
@@ -2926,7 +3175,7 @@
         TelecomManager tm = defaultPhone.getContext().getSystemService(TelecomManager.class);
         String defaultDialer = tm.getDefaultDialerPackage();
         if (!TextUtils.equals(callingPackage, defaultDialer)) {
-            TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
+            TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mApp,
                     getDefaultSubscription(), "sendDialerSpecialCode");
         }
 
@@ -2940,11 +3189,18 @@
 
     @Override
     public int getNetworkSelectionMode(int subId) {
-        if (!isActiveSubscription(subId)) {
-            return TelephonyManager.NETWORK_SELECTION_MODE_UNKNOWN;
+        TelephonyPermissions
+                    .enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                    mApp, subId, "getNetworkSelectionMode");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            if (!isActiveSubscription(subId)) {
+                return TelephonyManager.NETWORK_SELECTION_MODE_UNKNOWN;
+            }
+            return (int) sendRequest(CMD_GET_NETWORK_SELECTION_MODE, null /* argument */, subId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
-
-        return (int) sendRequest(CMD_GET_NETWORK_SELECTION_MODE, null /* argument */, subId);
     }
 
     @Override
@@ -2963,10 +3219,17 @@
         return false;
     }
 
+    /**
+     * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+     * @param subId The subscription to use to check the configuration.
+     * @param c The callback that will be used to send the result.
+     */
     @Override
     public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback c)
             throws RemoteException {
-        enforceReadPrivilegedPermission("registerImsRegistrationCallback");
+        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "registerImsRegistrationCallback");
+
         if (!ImsManager.isImsSupportedOnDevice(mApp)) {
             throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
                     "IMS not available on device.");
@@ -2983,9 +3246,15 @@
         }
     }
 
+    /**
+     * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+     * @param subId The subscription to use to check the configuration.
+     * @param c The callback that will be used to send the result.
+     */
     @Override
     public void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback c) {
-        enforceReadPrivilegedPermission("unregisterImsRegistrationCallback");
+        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "unregisterImsRegistrationCallback");
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
@@ -3041,7 +3310,8 @@
      */
     @Override
     public void getImsMmTelRegistrationTransportType(int subId, IIntegerConsumer consumer) {
-        enforceReadPrivilegedPermission("getImsMmTelRegistrationTransportType");
+        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "getImsMmTelRegistrationTransportType");
         if (!ImsManager.isImsSupportedOnDevice(mApp)) {
             throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
                     "IMS not available on device.");
@@ -3072,10 +3342,16 @@
         }
     }
 
+    /**
+     * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+     * @param subId The subscription to use to check the configuration.
+     * @param c The callback that will be used to send the result.
+     */
     @Override
     public void registerMmTelCapabilityCallback(int subId, IImsCapabilityCallback c)
             throws RemoteException {
-        enforceReadPrivilegedPermission("registerMmTelCapabilityCallback");
+        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "registerMmTelCapabilityCallback");
         if (!ImsManager.isImsSupportedOnDevice(mApp)) {
             throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
                     "IMS not available on device.");
@@ -3092,9 +3368,15 @@
         }
     }
 
+    /**
+     * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+     * @param subId The subscription to use to check the configuration.
+     * @param c The callback that will be used to send the result.
+     */
     @Override
     public void unregisterMmTelCapabilityCallback(int subId, IImsCapabilityCallback c) {
-        enforceReadPrivilegedPermission("unregisterMmTelCapabilityCallback");
+        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "unregisterMmTelCapabilityCallback");
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
             throw new IllegalArgumentException("Invalid Subscription ID: " + subId);
         }
@@ -3141,6 +3423,9 @@
             Phone phone = getPhone(subId);
             if (phone == null) return false;
             return phone.isImsCapabilityAvailable(capability, regTech);
+        } catch (com.android.ims.ImsException e) {
+            Log.w(LOG_TAG, "IMS isAvailable - service unavailable: " + e.getMessage());
+            return false;
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -3184,9 +3469,15 @@
         }
     }
 
+    /**
+     * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+     * @param subId The subscription to use to check the configuration.
+     */
     @Override
     public boolean isAdvancedCallingSettingEnabled(int subId) {
-        enforceReadPrivilegedPermission("enforceReadPrivilegedPermission");
+        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "isAdvancedCallingSettingEnabled");
+
         // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
         final long token = Binder.clearCallingIdentity();
         try {
@@ -3215,9 +3506,14 @@
         }
     }
 
+    /**
+     * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+     * @param subId The subscription to use to check the configuration.
+     */
     @Override
     public boolean isVtSettingEnabled(int subId) {
-        enforceReadPrivilegedPermission("isVtSettingEnabled");
+        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "isVtSettingEnabled");
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -3244,9 +3540,14 @@
         }
     }
 
+    /**
+     * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+     * @param subId The subscription to use to check the configuration.
+     */
     @Override
     public boolean isVoWiFiSettingEnabled(int subId) {
-        enforceReadPrivilegedPermission("isVoWiFiSettingEnabled");
+        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "isVoWiFiSettingEnabled");
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -3274,9 +3575,14 @@
         }
     }
 
+    /**
+     * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+     * @param subId The subscription to use to check the configuration.
+     */
     @Override
     public boolean isVoWiFiRoamingSettingEnabled(int subId) {
-        enforceReadPrivilegedPermission("isVoWiFiRoamingSettingEnabled");
+        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "isVoWiFiRoamingSettingEnabled");
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -3321,9 +3627,14 @@
         }
     }
 
+    /**
+     * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+     * @param subId The subscription to use to check the configuration.
+     */
     @Override
     public int getVoWiFiModeSetting(int subId) {
-        enforceReadPrivilegedPermission("getVoWiFiModeSetting");
+        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "getVoWiFiModeSetting");
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -3398,9 +3709,14 @@
         }
     }
 
+    /**
+     * Requires carrier privileges or READ_PRECISE_PHONE_STATE permission.
+     * @param subId The subscription to use to check the configuration.
+     */
     @Override
     public boolean isTtyOverVolteEnabled(int subId) {
-        enforceReadPrivilegedPermission("isTtyOverVolteEnabled");
+        TelephonyPermissions.enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                mApp, subId, "isTtyOverVolteEnabled");
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
@@ -3418,6 +3734,10 @@
         enforceReadPrivilegedPermission("registerImsProvisioningChangedCallback");
         final long identity = Binder.clearCallingIdentity();
         try {
+            if (!isImsAvailableOnDevice()) {
+                throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+                        "IMS not available on device.");
+            }
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
             ImsManager.getInstance(mApp, getSlotIndexOrException(subId))
                     .addProvisioningCallbackForSubscription(callback, subId);
@@ -3449,6 +3769,91 @@
         }
     }
 
+
+    private void checkModifyPhoneStatePermission(int subId, String message) {
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
+                message);
+    }
+
+    private boolean isImsProvisioningRequired(int subId, int capability,
+            boolean isMmtelCapability) {
+        Phone phone = getPhone(subId);
+        if (phone == null) {
+            loge("phone instance null for subid " + subId);
+            return false;
+        }
+        if (isMmtelCapability) {
+            if (!doesImsCapabilityRequireProvisioning(phone.getContext(), subId, capability)) {
+                return false;
+            }
+        } else {
+            if (!doesRcsCapabilityRequireProvisioning(phone.getContext(), subId, capability)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public void setRcsProvisioningStatusForCapability(int subId, int capability,
+            boolean isProvisioned) {
+        checkModifyPhoneStatePermission(subId, "setRcsProvisioningStatusForCapability");
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            if (!isImsProvisioningRequired(subId, capability, false)) {
+                return;
+            }
+
+            // this capability requires provisioning, route to the correct API.
+            ImsManager ims = ImsManager.getInstance(mApp, getSlotIndex(subId));
+            switch (capability) {
+                case RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE:
+                case RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE:
+                    ims.setEabProvisioned(isProvisioned);
+                    break;
+                default: {
+                    throw new IllegalArgumentException("Tried to set provisioning for "
+                            + "rcs capability '" + capability + "', which does not require "
+                            + "provisioning.");
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+
+    }
+
+
+    @Override
+    public boolean getRcsProvisioningStatusForCapability(int subId, int capability) {
+        enforceReadPrivilegedPermission("getRcsProvisioningStatusForCapability");
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
+            if (!isImsProvisioningRequired(subId, capability, false)) {
+                return true;
+            }
+
+            ImsManager ims = ImsManager.getInstance(mApp, getSlotIndex(subId));
+            switch (capability) {
+                case RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE:
+                case RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE:
+                    return ims.isEabProvisionedOnDevice();
+
+                default: {
+                    throw new IllegalArgumentException("Tried to get rcs provisioning for "
+                            + "capability '" + capability + "', which does not require "
+                            + "provisioning.");
+                }
+            }
+
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     @Override
     public void setImsProvisioningStatusForCapability(int subId, int capability, int tech,
             boolean isProvisioned) {
@@ -3456,18 +3861,11 @@
                 && tech != ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
             throw new IllegalArgumentException("Registration technology '" + tech + "' is invalid");
         }
-        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp, subId,
-                "setProvisioningStatusForCapability");
+        checkModifyPhoneStatePermission(subId, "setImsProvisioningStatusForCapability");
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            Phone phone = getPhone(subId);
-            if (phone == null) {
-                loge("setImsProvisioningStatusForCapability: phone instance null for subid "
-                        + subId);
-                return;
-            }
-            if (!doesImsCapabilityRequireProvisioning(phone.getContext(), subId, capability)) {
+            if (!isImsProvisioningRequired(subId, capability, true)) {
                 return;
             }
 
@@ -3505,8 +3903,9 @@
                     break;
                 }
                 default: {
-                    throw new IllegalArgumentException("Tried to set provisioning for capability '"
-                            + capability + "', which does not require provisioning.");
+                    throw new IllegalArgumentException("Tried to set provisioning for "
+                            + "MmTel capability '" + capability + "', which does not require "
+                            + "provisioning. ");
                 }
             }
 
@@ -3525,16 +3924,7 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             // TODO: Refactor to remove ImsManager dependence and query through ImsPhone directly.
-            Phone phone = getPhone(subId);
-            if (phone == null) {
-                loge("getImsProvisioningStatusForCapability: phone instance null for subid "
-                        + subId);
-                // We will fail with "true" as the provisioning status because this is the default
-                // if we do not require provisioning.
-                return true;
-            }
-
-            if (!doesImsCapabilityRequireProvisioning(phone.getContext(), subId, capability)) {
+            if (!isImsProvisioningRequired(subId, capability, true)) {
                 return true;
             }
 
@@ -3560,8 +3950,9 @@
                     return isMmTelCapabilityProvisionedInCache(subId, capability, tech);
                 }
                 default: {
-                    throw new IllegalArgumentException("Tried to get provisioning for capability '"
-                            + capability + "', which does not require provisioning.");
+                    throw new IllegalArgumentException(
+                            "Tried to get provisioning for MmTel capability '" + capability
+                                    + "', which does not require provisioning.");
                 }
             }
 
@@ -3670,6 +4061,29 @@
         return false;
     }
 
+    private boolean doesRcsCapabilityRequireProvisioning(Context context, int subId,
+            int capability) {
+        CarrierConfigManager configManager = new CarrierConfigManager(context);
+        PersistableBundle c = configManager.getConfigForSubId(subId);
+
+        boolean requireRcsProvisioning = c.getBoolean(
+                CarrierConfigManager.KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL, false);
+
+        // First check to make sure that the capability requires provisioning.
+        switch (capability) {
+            case RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE:
+                // intentional fallthrough
+            case RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE: {
+                if (requireRcsProvisioning) {
+                    // OPTION or PRESENCE requires provisioning
+                    return true;
+                }
+                break;
+            }
+        }
+        return false;
+    }
+
     @Override
     public int getImsProvisioningInt(int subId, int key) {
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
@@ -3922,9 +4336,9 @@
     @Override
     public int getLteOnCdmaModeForSubscriber(int subId, String callingPackage,
             String callingFeatureId) {
-        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mApp, subId, callingPackage, callingFeatureId,
-                "getLteOnCdmaModeForSubscriber")) {
+        try {
+            enforceReadPrivilegedPermission("getLteOnCdmaModeForSubscriber");
+        } catch (SecurityException e) {
             return PhoneConstants.LTE_ON_CDMA_UNKNOWN;
         }
 
@@ -4482,6 +4896,27 @@
     }
 
     /**
+     * Toggle IMS disable and enable for the framework to reset it. See {@link #enableIms(int)} and
+     * {@link #disableIms(int)}.
+     * @param slotIndex device slot.
+     */
+    public void resetIms(int slotIndex) {
+        enforceModifyPermission();
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            if (mImsResolver == null) {
+                // may happen if the does not support IMS.
+                return;
+            }
+            mImsResolver.disableIms(slotIndex);
+            mImsResolver.enableIms(slotIndex);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
      * Enables IMS for the framework. This will trigger IMS registration and ImsFeature capability
      * status updates, if not already enabled.
      */
@@ -4720,12 +5155,11 @@
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
                 mApp, subId, "setNetworkSelectionModeAutomatic");
 
-        if (!isActiveSubscription(subId)) {
-            return;
-        }
-
         final long identity = Binder.clearCallingIdentity();
         try {
+            if (!isActiveSubscription(subId)) {
+                return;
+            }
             if (DBG) log("setNetworkSelectionModeAutomatic: subId " + subId);
             sendRequest(CMD_SET_NETWORK_SELECTION_MODE_AUTOMATIC, null, subId);
         } finally {
@@ -4804,6 +5238,75 @@
     }
 
     /**
+     * Get the call forwarding info, given the call forwarding reason.
+     */
+    @Override
+    public CallForwardingInfo getCallForwarding(int subId, int callForwardingReason) {
+        enforceReadPrivilegedPermission("getCallForwarding");
+        long identity = Binder.clearCallingIdentity();
+        try {
+            if (DBG) {
+                log("getCallForwarding: subId " + subId
+                        + " callForwardingReason" + callForwardingReason);
+            }
+            return (CallForwardingInfo) sendRequest(
+                    CMD_GET_CALL_FORWARDING, callForwardingReason, subId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Sets the voice call forwarding info including status (enable/disable), call forwarding
+     * reason, the number to forward, and the timeout before the forwarding is attempted.
+     */
+    @Override
+    public boolean setCallForwarding(int subId, CallForwardingInfo callForwardingInfo) {
+        enforceModifyPermission();
+        long identity = Binder.clearCallingIdentity();
+        try {
+            if (DBG) {
+                log("setCallForwarding: subId " + subId
+                        + " callForwardingInfo" + callForwardingInfo);
+            }
+            return (Boolean) sendRequest(CMD_SET_CALL_FORWARDING, callForwardingInfo, subId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Get the call forwarding info, given the call forwarding reason.
+     */
+    @Override
+    public int getCallWaitingStatus(int subId) {
+        enforceReadPrivilegedPermission("getCallForwarding");
+        long identity = Binder.clearCallingIdentity();
+        try {
+            if (DBG) log("getCallWaitingStatus: subId " + subId);
+            return (Integer) sendRequest(CMD_GET_CALL_WAITING, null, subId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Sets the voice call forwarding info including status (enable/disable), call forwarding
+     * reason, the number to forward, and the timeout before the forwarding is attempted.
+     */
+    @Override
+    public boolean setCallWaitingStatus(int subId, boolean isEnable) {
+        enforceModifyPermission();
+        long identity = Binder.clearCallingIdentity();
+        try {
+            if (DBG) log("setCallWaitingStatus: subId " + subId + " isEnable: " + isEnable);
+            return (Boolean) sendRequest(CMD_SET_CALL_WAITING, isEnable, subId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
      * Starts a new network scan and returns the id of this scan.
      *
      * @param subId id of the subscription
@@ -4944,7 +5447,6 @@
 
     /**
      * Set the preferred network type.
-     * Used for device configuration by some CDMA operators.
      *
      * @param networkType the preferred network type, defined in RILConstants.java.
      * @return true on success; false on any failure.
@@ -4956,14 +5458,12 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            if (DBG) log("setPreferredNetworkType: subId " + subId + " type " + networkType);
+            Settings.Global.putInt(mApp.getContentResolver(),
+                    Settings.Global.PREFERRED_NETWORK_MODE + subId, networkType);
+
             Boolean success = (Boolean) sendRequest(
                     CMD_SET_PREFERRED_NETWORK_TYPE, networkType, subId);
             if (DBG) log("setPreferredNetworkType: " + (success ? "ok" : "fail"));
-            if (success) {
-                Settings.Global.putInt(mApp.getContentResolver(),
-                        Settings.Global.PREFERRED_NETWORK_MODE + subId, networkType);
-            }
             return success;
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -4971,6 +5471,49 @@
     }
 
     /**
+     * Get the allowed network types that store in the telephony provider.
+     *
+     * @param subId the id of the subscription.
+     * @return allowedNetworkTypes the allowed network types.
+     */
+    @Override
+    public long getAllowedNetworkTypes(int subId) {
+        TelephonyPermissions
+                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                    mApp, subId, "getAllowedNetworkTypes");
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return SubscriptionManager.getLongSubscriptionProperty(
+                    subId, SubscriptionManager.ALLOWED_NETWORK_TYPES, -1, mApp);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Set the allowed network types.
+     *
+     * @param subId the id of the subscription.
+     * @param allowedNetworkTypes the allowed network types.
+     * @return true on success; false on any failure.
+     */
+    @Override
+    public boolean setAllowedNetworkTypes(int subId, long allowedNetworkTypes) {
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+                mApp, subId, "setAllowedNetworkTypes");
+
+        SubscriptionManager.setSubscriptionProperty(subId,
+                SubscriptionManager.ALLOWED_NETWORK_TYPES,
+                String.valueOf(allowedNetworkTypes));
+
+        int preferredNetworkMode = Settings.Global.getInt(mApp.getContentResolver(),
+                Settings.Global.PREFERRED_NETWORK_MODE + subId,
+                RILConstants.PREFERRED_NETWORK_MODE);
+        return setPreferredNetworkType(subId, preferredNetworkMode);
+    }
+
+    /**
      * Check whether DUN APN is required for tethering with subId.
      *
      * @param subId the id of the subscription to require tethering.
@@ -5021,6 +5564,34 @@
     }
 
     /**
+     * Enable or disable always reporting signal strength changes from radio.
+     *
+     * @param isEnable {@code true} for enabling; {@code false} for disabling.
+     */
+    @Override
+    public void setAlwaysReportSignalStrength(int subId, boolean isEnable) {
+        enforceModifyPermission();
+        enforceSystemCaller();
+
+        final long identity = Binder.clearCallingIdentity();
+        final Phone phone = getPhone(subId);
+        try {
+            if (phone != null) {
+                if (DBG) {
+                    log("setAlwaysReportSignalStrength: subId=" + subId
+                            + " isEnable=" + isEnable);
+                }
+                phone.setAlwaysReportSignalStrength(isEnable);
+            } else {
+                loge("setAlwaysReportSignalStrength: no phone found for subId="
+                        + subId);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
      * Get the user enabled state of Mobile Data.
      *
      * TODO: remove and use isUserDataEnabled.
@@ -5074,24 +5645,15 @@
     }
 
     /**
-     * Get whether mobile data is enabled.
+     * Checks if the device is capable of mobile data by considering whether whether the
+     * user has enabled mobile data, whether the carrier has enabled mobile data, and
+     * whether the network policy allows data connections.
      *
-     * Comparable to {@link #isUserDataEnabled(int)}, this considers all factors deciding
-     * whether mobile data is actually enabled.
-     *
-     * Accepts either ACCESS_NETWORK_STATE, MODIFY_PHONE_STATE or carrier privileges.
-     *
-     * @return {@code true} if data is enabled else {@code false}
+     * @return {@code true} if the overall data connection is capable; {@code false} if not.
      */
     @Override
     public boolean isDataEnabled(int subId) {
-        try {
-            mApp.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE,
-                    null);
-        } catch (Exception e) {
-            TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
-                    mApp, subId, "isDataEnabled");
-        }
+        enforceReadPrivilegedPermission("isDataEnabled");
 
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -5111,14 +5673,13 @@
         }
     }
 
-    private int getCarrierPrivilegeStatusFromCarrierConfigRules(int privilegeFromSim,
+    private int getCarrierPrivilegeStatusFromCarrierConfigRules(int privilegeFromSim, int uid,
             Phone phone) {
         //load access rules from carrier configs, and check those as well: b/139133814
         SubscriptionController subController = SubscriptionController.getInstance();
         if (privilegeFromSim == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
                 || subController == null) return privilegeFromSim;
 
-        int uid = Binder.getCallingUid();
         PackageManager pkgMgr = phone.getContext().getPackageManager();
         String[] packages = pkgMgr.getPackagesForUid(uid);
 
@@ -5172,11 +5733,12 @@
 
         return getCarrierPrivilegeStatusFromCarrierConfigRules(
             card.getCarrierPrivilegeStatusForCurrentTransaction(
-                phone.getContext().getPackageManager()), phone);
+                phone.getContext().getPackageManager()), Binder.getCallingUid(), phone);
     }
 
     @Override
     public int getCarrierPrivilegeStatusForUid(int subId, int uid) {
+        enforceReadPrivilegedPermission("getCarrierPrivilegeStatusForUid");
         final Phone phone = getPhone(subId);
         if (phone == null) {
             loge("getCarrierPrivilegeStatusForUid: Invalid subId");
@@ -5189,8 +5751,8 @@
             return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
         }
         return getCarrierPrivilegeStatusFromCarrierConfigRules(
-            profile.getCarrierPrivilegeStatusForUid(
-                phone.getContext().getPackageManager(), uid), phone);
+                profile.getCarrierPrivilegeStatusForUid(
+                        phone.getContext().getPackageManager(), uid), uid, phone);
     }
 
     @Override
@@ -5262,7 +5824,7 @@
                         PackageManager.MATCH_DISABLED_COMPONENTS
                             | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
                             | PackageManager.GET_SIGNING_CERTIFICATES,
-                            UserHandle.USER_SYSTEM);
+                            UserHandle.SYSTEM.getIdentifier());
                 }
                 for (int p = packages.size() - 1; p >= 0; p--) {
                     PackageInfo pkgInfo = packages.get(p);
@@ -5312,7 +5874,7 @@
     @Override
     public boolean setLine1NumberForDisplayForSubscriber(int subId, String alphaTag,
             String number) {
-        TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mApp,
                 subId, "setLine1NumberForDisplayForSubscriber");
 
         final long identity = Binder.clearCallingIdentity();
@@ -5325,7 +5887,7 @@
             final String subscriberId = phone.getSubscriberId();
 
             if (DBG_MERGE) {
-                Slog.d(LOG_TAG, "Setting line number for ICC=" + iccId + ", subscriberId="
+                Rlog.d(LOG_TAG, "Setting line number for ICC=" + iccId + ", subscriberId="
                         + subscriberId + " to " + number);
             }
 
@@ -5451,7 +6013,7 @@
                         final String numberKey = PREF_CARRIERS_NUMBER_PREFIX + iccId;
                         mergeNumber = (String) prefs.get(numberKey);
                         if (DBG_MERGE) {
-                            Slog.d(LOG_TAG, "Found line number " + mergeNumber
+                            Rlog.d(LOG_TAG, "Found line number " + mergeNumber
                                     + " for active subscriber " + subscriberId);
                         }
                         if (!TextUtils.isEmpty(mergeNumber)) {
@@ -5485,7 +6047,7 @@
             final String[] resultArray = result.toArray(new String[result.size()]);
             Arrays.sort(resultArray);
             if (DBG_MERGE) {
-                Slog.d(LOG_TAG,
+                Rlog.d(LOG_TAG,
                         "Found subscribers " + Arrays.toString(resultArray) + " after merge");
             }
             return resultArray;
@@ -5540,7 +6102,7 @@
 
     @Override
     public boolean setOperatorBrandOverride(int subId, String brand) {
-        TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
+        TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mApp,
                 subId, "setOperatorBrandOverride");
 
         final long identity = Binder.clearCallingIdentity();
@@ -5556,7 +6118,8 @@
     public boolean setRoamingOverride(int subId, List<String> gsmRoamingList,
             List<String> gsmNonRoamingList, List<String> cdmaRoamingList,
             List<String> cdmaNonRoamingList) {
-        TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(subId, "setRoamingOverride");
+        TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
+                mApp, subId, "setRoamingOverride");
 
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -5757,7 +6320,12 @@
     public boolean isRttEnabled(int subscriptionId) {
         final long identity = Binder.clearCallingIdentity();
         try {
-            return isRttSupported(subscriptionId);
+            boolean isRttSupported = isRttSupported(subscriptionId);
+            boolean isUserRttSettingOn = Settings.Secure.getInt(
+                    mApp.getContentResolver(), Settings.Secure.RTT_CALLING_MODE, 0) != 0;
+            boolean shouldIgnoreUserRttSetting = mApp.getCarrierConfigForSubId(subscriptionId)
+                    .getBoolean(CarrierConfigManager.KEY_IGNORE_RTT_MODE_SETTING_BOOL);
+            return isRttSupported && (isUserRttSettingOn || shouldIgnoreUserRttSetting);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -6027,38 +6595,7 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            ModemActivityInfo ret = null;
-            synchronized (mLastModemActivityInfo) {
-                ModemActivityInfo info = (ModemActivityInfo) sendRequest(
-                        CMD_GET_MODEM_ACTIVITY_INFO,
-                        null, workSource);
-                if (isModemActivityInfoValid(info)) {
-                    int[] mergedTxTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
-                    int[] txTimeMs = info.getTransmitTimeMillis();
-                    int[] lastModemTxTimeMs = mLastModemActivityInfo.getTransmitTimeMillis();
-                    for (int i = 0; i < mergedTxTimeMs.length; i++) {
-                        mergedTxTimeMs[i] = txTimeMs[i] + lastModemTxTimeMs[i];
-                    }
-                    mLastModemActivityInfo.setTimestamp(info.getTimestamp());
-                    mLastModemActivityInfo.setSleepTimeMillis(info.getSleepTimeMillis()
-                            + mLastModemActivityInfo.getSleepTimeMillis());
-                    mLastModemActivityInfo.setIdleTimeMillis(
-                            info.getIdleTimeMillis() + mLastModemActivityInfo.getIdleTimeMillis());
-                    mLastModemActivityInfo.setTransmitTimeMillis(mergedTxTimeMs);
-                    mLastModemActivityInfo.setReceiveTimeMillis(
-                            info.getReceiveTimeMillis() + mLastModemActivityInfo
-                                .getReceiveTimeMillis());
-                }
-
-                ret = new ModemActivityInfo(mLastModemActivityInfo.getTimestamp(),
-                        mLastModemActivityInfo.getSleepTimeMillis(),
-                        mLastModemActivityInfo.getIdleTimeMillis(),
-                        mLastModemActivityInfo.getTransmitTimeMillis(),
-                        mLastModemActivityInfo.getReceiveTimeMillis());
-            }
-            Bundle bundle = new Bundle();
-            bundle.putParcelable(TelephonyManager.MODEM_ACTIVITY_RESULT_KEY, ret);
-            result.send(0, bundle);
+            sendRequestAsync(CMD_GET_MODEM_ACTIVITY_INFO, result, null, workSource);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -6589,33 +7126,6 @@
     }
 
     /**
-     * Get aggregated video call data usage since boot.
-     *
-     * @param perUidStats True if requesting data usage per uid, otherwise overall usage.
-     * @return Snapshot of video call data usage
-     * {@hide}
-     */
-    @Override
-    public NetworkStats getVtDataUsage(int subId, boolean perUidStats) {
-        mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_NETWORK_USAGE_HISTORY,
-                null);
-
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            // NetworkStatsService keeps tracking the active network interface and identity. It
-            // records the delta with the corresponding network identity.
-            // We just return the total video call data usage snapshot since boot.
-            Phone phone = getPhone(subId);
-            if (phone != null) {
-                return phone.getVtDataUsage(perUidStats);
-            }
-            return null;
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    /**
      * Policy control of data connection. Usually used when data limit is passed.
      * @param enabled True if enabling the data, otherwise disabling.
      * @param subId Subscription index
@@ -6995,23 +7505,6 @@
         }
     }
 
-    @Override
-    public void setRadioIndicationUpdateMode(int subId, int filters, int mode) {
-        enforceModifyPermission();
-        final Phone phone = getPhone(subId);
-        if (phone == null) {
-            loge("setRadioIndicationUpdateMode fails with invalid subId: " + subId);
-            return;
-        }
-
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            phone.setRadioIndicationUpdateMode(filters, mode);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
     /**
      * A test API to reload the UICC profile.
      *
@@ -7132,7 +7625,8 @@
 
     @Override
     public int getCdmaRoamingMode(int subId) {
-        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+        TelephonyPermissions
+                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                 mApp, subId, "getCdmaRoamingMode");
 
         final long identity = Binder.clearCallingIdentity();
@@ -7300,7 +7794,7 @@
     }
 
     @Override
-    public void updateTestOtaEmergencyNumberDbFilePath(String otaFilePath) {
+    public void updateOtaEmergencyNumberDbFilePath(ParcelFileDescriptor otaParcelFileDescriptor) {
         enforceActiveEmergencySessionPermission();
 
         final long identity = Binder.clearCallingIdentity();
@@ -7308,7 +7802,24 @@
             for (Phone phone: PhoneFactory.getPhones()) {
                 EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker();
                 if (tracker != null) {
-                    tracker.updateTestOtaEmergencyNumberDbFilePath(otaFilePath);
+                    tracker.updateOtaEmergencyNumberDbFilePath(otaParcelFileDescriptor);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void resetOtaEmergencyNumberDbFilePath() {
+        enforceActiveEmergencySessionPermission();
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            for (Phone phone: PhoneFactory.getPhones()) {
+                EmergencyNumberTracker tracker = phone.getEmergencyNumberTracker();
+                if (tracker != null) {
+                    tracker.resetOtaEmergencyNumberDbFilePath();
                 }
             }
         } finally {
@@ -7558,6 +8069,15 @@
     }
 
     /**
+     * Get the current calling package name.
+     * @return the current calling package name
+     */
+    @Override
+    public String getCurrentPackageName() {
+        return mApp.getPackageManager().getPackagesForUid(Binder.getCallingUid())[0];
+    }
+
+    /**
      * Return whether data is enabled for certain APN type. This will tell if framework will accept
      * corresponding network requests on a subId.
      *
@@ -7565,7 +8085,7 @@
      *  1) user data is turned on, or
      *  2) APN is un-metered for this subscription, or
      *  3) APN type is whitelisted. E.g. MMS is whitelisted if
-     *  {@link SubscriptionManager#setAlwaysAllowMmsData} is turned on.
+     *  {@link TelephonyManager#setAlwaysAllowMmsData} is turned on.
      *
      * @return whether data is allowed for a apn type.
      *
@@ -7606,6 +8126,39 @@
     }
 
     @Override
+    public void setSystemSelectionChannels(List<RadioAccessSpecifier> specifiers,
+            int subscriptionId, IBooleanConsumer resultCallback) {
+        enforceModifyPermission();
+        long token = Binder.clearCallingIdentity();
+        try {
+            Phone phone = getPhone(subscriptionId);
+            if (phone == null) {
+                try {
+                    if (resultCallback != null) {
+                        resultCallback.accept(false);
+                    }
+                } catch (RemoteException e) {
+                    // ignore
+                }
+                return;
+            }
+            Pair<List<RadioAccessSpecifier>, Consumer<Boolean>> argument =
+                    Pair.create(specifiers, (x) -> {
+                        try {
+                            if (resultCallback != null) {
+                                resultCallback.accept(x);
+                            }
+                        } catch (RemoteException e) {
+                            // ignore
+                        }
+                    });
+            sendRequestAsync(CMD_SET_SYSTEM_SELECTION_CHANNELS, argument, phone, null);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
     public boolean isMvnoMatched(int subId, int mvnoType, @NonNull String mvnoMatchData) {
         IccRecords iccRecords = UiccController.getInstance().getIccRecords(
                 SubscriptionManager.getPhoneId(subId), UiccController.APP_FAM_3GPP);
@@ -7617,10 +8170,15 @@
     }
 
     @Override
-    public void enqueueSmsPickResult(String callingPackage, IIntegerConsumer pendingSubIdResult) {
+    public void enqueueSmsPickResult(String callingPackage, String callingAttributionTag,
+            IIntegerConsumer pendingSubIdResult) {
+        if (callingPackage == null) {
+            callingPackage = getCurrentPackageName();
+        }
         SmsPermissions permissions = new SmsPermissions(getDefaultPhone(), mApp,
                 (AppOpsManager) mApp.getSystemService(Context.APP_OPS_SERVICE));
-        if (!permissions.checkCallingCanSendSms(callingPackage, "Sending message")) {
+        if (!permissions.checkCallingCanSendSms(callingPackage, callingAttributionTag,
+                "Sending message")) {
             throw new SecurityException("Requires SEND_SMS permission to perform this operation");
         }
         PickSmsSubscriptionActivity.addPendingResult(pendingSubIdResult);
@@ -7689,6 +8247,22 @@
         }
     }
 
+    @Override
+    public boolean setAlwaysAllowMmsData(int subId, boolean alwaysAllow) {
+        enforceModifyPermission();
+
+        // Now that all security checks passes, perform the operation as ourselves.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            Phone phone = getPhone(subId);
+            if (phone == null) return false;
+
+            return phone.getDataEnabledSettings().setAlwaysAllowMmsData(alwaysAllow);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     /**
      * Updates whether conference event pacakge handling is enabled.
      * @param isCepEnabled {@code true} if CEP handling is enabled (default), or {@code false}
@@ -7716,4 +8290,111 @@
             Binder.restoreCallingIdentity(identity);
         }
     }
+
+    /**
+     * Notify that an RCS autoconfiguration XML file has been received for provisioning.
+     *
+     * @param config       The XML file to be read. ASCII/UTF8 encoded text if not compressed.
+     * @param isCompressed The XML file is compressed in gzip format and must be decompressed
+     *                     before being read.
+     */
+    @Override
+    public void notifyRcsAutoConfigurationReceived(int subId, @NonNull byte[] config, boolean
+            isCompressed) {
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+                mApp, subId, "notifyRcsAutoConfigurationReceived");
+        try {
+            IImsConfig configBinder = getImsConfig(getSlotIndex(subId), ImsFeature.FEATURE_RCS);
+            if (configBinder == null) {
+                Rlog.e(LOG_TAG, "null result for getImsConfig");
+            } else {
+                configBinder.notifyRcsAutoConfigurationReceived(config, isCompressed);
+            }
+        } catch (RemoteException e) {
+            Rlog.e(LOG_TAG, "fail to getImsConfig " + e.getMessage());
+        }
+    }
+
+    @Override
+    public boolean isIccLockEnabled(int subId) {
+        enforceReadPrivilegedPermission("isIccLockEnabled");
+
+        // Now that all security checks passes, perform the operation as ourselves.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            Phone phone = getPhone(subId);
+            if (phone != null && phone.getIccCard() != null) {
+                return phone.getIccCard().getIccLockEnabled();
+            } else {
+                return false;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Set the ICC pin lock enabled or disabled..
+     *
+     * @return Integer.MAX_VALUE if enable/disable IccLock successfully. If failed it will return
+     * 0 or a positive integer as the attempts remaining value.
+     *
+     */
+    @Override
+    public int setIccLockEnabled(int subId, boolean enabled, String password) {
+        enforceModifyPermission();
+
+        Phone phone = getPhone(subId);
+        if (phone == null) {
+            return 0;
+        }
+        // Now that all security checks passes, perform the operation as ourselves.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            int attemptsRemaining = (int) sendRequest(CMD_SET_ICC_LOCK_ENABLED,
+                    new Pair<Boolean, String>(enabled, password), phone, null);
+            return attemptsRemaining;
+
+        } catch (Exception e) {
+            Log.e(LOG_TAG, "setIccLockEnabled. Exception e =" + e);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+        return 0;
+    }
+
+    /**
+     * Change the ICC password used in ICC pin lock.
+     *
+     * @return Integer.MAX_VALUE if enable/disable IccLock successfully. If failed it will return
+     * 0 or a positive integer as the attempts remaining value.
+     *
+     */
+    @Override
+    public int changeIccLockPassword(int subId, String oldPassword, String newPassword) {
+        enforceModifyPermission();
+
+        Phone phone = getPhone(subId);
+        if (phone == null) {
+            return 0;
+        }
+        // Now that all security checks passes, perform the operation as ourselves.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            int attemptsRemaining = (int) sendRequest(CMD_CHANGE_ICC_LOCK_PASSWORD,
+                    new Pair<String, String>(oldPassword, newPassword), phone, null);
+            return attemptsRemaining;
+
+        } catch (Exception e) {
+            Log.e(LOG_TAG, "changeIccLockPassword. Exception e =" + e);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+        return 0;
+    }
+
+    @Override
+    public boolean canConnectTo5GInDsdsMode() {
+        return mApp.getResources().getBoolean(R.bool.config_5g_connection_in_dsds_mode);
+    }
 }
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index c865f14..8eaa336 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -22,16 +22,22 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.RingtoneManager;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Message;
 import android.os.PersistableBundle;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.VideoProfile;
 import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
 import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
 import android.util.Log;
@@ -53,7 +59,9 @@
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.TelephonyCapabilities;
 import com.android.phone.settings.SuppServicesUiUtil;
+import com.android.telephony.Rlog;
 
+import java.io.IOException;
 import java.util.List;
 
 /**
@@ -79,6 +87,9 @@
     /** Define for not a special CNAP string */
     private static final int CNAP_SPECIAL_CASE_NO = -1;
 
+    /** Define for default vibrate pattern if res cannot be found */
+    private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
+
     /**
      * Theme to use for dialogs displayed by utility methods in this class. This is needed
      * because these dialogs are displayed using the application context, which does not resolve
@@ -87,7 +98,6 @@
     private static final int THEME = com.android.internal.R.style.Theme_DeviceDefault_Dialog_Alert;
 
     /** USSD information used to aggregate all USSD messages */
-    private static AlertDialog sUssdDialog = null;
     private static StringBuilder sUssdMsg = new StringBuilder();
 
     private static final ComponentName PSTN_CONNECTION_SERVICE_COMPONENT =
@@ -500,9 +510,59 @@
                 newDialog.getButton(DialogInterface.BUTTON_NEGATIVE)
                         .setTextColor(context.getResources().getColor(R.color.dialer_theme_color));
             }
+
+            if (mmiCode.isNetworkInitiatedUssd()) {
+                playSound(context);
+            }
         }
     }
 
+    private static void playSound(Context context) {
+        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        int callsRingerMode = audioManager.getRingerMode();
+
+        if (callsRingerMode == AudioManager.RINGER_MODE_NORMAL) {
+            log("playSound : RINGER_MODE_NORMAL");
+            try {
+                Uri notificationUri = RingtoneManager.getDefaultUri(
+                        RingtoneManager.TYPE_NOTIFICATION);
+                MediaPlayer mediaPlayer = new MediaPlayer();
+                mediaPlayer.setDataSource(context, notificationUri);
+                AudioAttributes aa = new AudioAttributes.Builder()
+                        .setLegacyStreamType(AudioManager.STREAM_NOTIFICATION)
+                        .setUsage(AudioAttributes.USAGE_NOTIFICATION)
+                        .build();
+                mediaPlayer.setAudioAttributes(aa);
+                mediaPlayer.setLooping(false);
+                mediaPlayer.prepare();
+                mediaPlayer.start();
+            } catch (IOException e) {
+                log("playSound exception : " + e);
+            }
+        } else if (callsRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
+            log("playSound : RINGER_MODE_VIBRATE");
+            Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+            // Use NotificationManagerService#DEFAULT_VIBRATE_PATTERN if
+            // R.array.config_defaultNotificationVibePattern is not defined.
+            long[] pattern = getLongArray(context.getResources(),
+                    R.array.config_defaultNotificationVibePattern, DEFAULT_VIBRATE_PATTERN);
+            vibrator.vibrate(VibrationEffect.createWaveform(pattern, -1));
+        }
+    }
+
+    private static long[] getLongArray(Resources r, int resid, long[] def) {
+        int[] ar = r.getIntArray(resid);
+        if (ar == null) {
+            return def;
+        }
+        final int len = ar.length;
+        long[] out = new long[len];
+        for (int i = 0; i < len; i++) {
+            out[i] = ar[i];
+        }
+        return out;
+    }
+
     /**
      * It displays the message dialog for user about the mmi code result message.
      *
@@ -524,39 +584,36 @@
         // displaying system alert dialog on the screen instead of
         // using another activity to display the message.  This
         // places the message at the forefront of the UI.
+        AlertDialog ussdDialog = new AlertDialog.Builder(context, THEME)
+                .setPositiveButton(R.string.ok, null)
+                .setCancelable(true)
+                .setOnDismissListener(new DialogInterface.OnDismissListener() {
+                    @Override
+                    public void onDismiss(DialogInterface dialog) {
+                        sUssdMsg.setLength(0);
+                    }
+                })
+                .create();
 
-        if (sUssdDialog == null) {
-            sUssdDialog = new AlertDialog.Builder(context, THEME)
-                    .setPositiveButton(R.string.ok, null)
-                    .setCancelable(true)
-                    .setOnDismissListener(new DialogInterface.OnDismissListener() {
-                        @Override
-                        public void onDismiss(DialogInterface dialog) {
-                            sUssdMsg.setLength(0);
-                        }
-                    })
-                    .create();
+        ussdDialog.getWindow().setType(windowType);
+        ussdDialog.getWindow().addFlags(
+                WindowManager.LayoutParams.FLAG_DIM_BEHIND);
 
-            sUssdDialog.getWindow().setType(windowType);
-            sUssdDialog.getWindow().addFlags(
-                    WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-        }
         if (sUssdMsg.length() != 0) {
-            sUssdMsg
-                    .insert(0, "\n")
+            sUssdMsg.insert(0, "\n")
                     .insert(0, app.getResources().getString(R.string.ussd_dialog_sep))
                     .insert(0, "\n");
         }
         if (phone != null && phone.getCarrierName() != null) {
-            sUssdDialog.setTitle(app.getResources().getString(R.string.carrier_mmi_msg_title,
+            ussdDialog.setTitle(app.getResources().getString(R.string.carrier_mmi_msg_title,
                     phone.getCarrierName()));
         } else {
-            sUssdDialog
+            ussdDialog
                     .setTitle(app.getResources().getString(R.string.default_carrier_mmi_msg_title));
         }
         sUssdMsg.insert(0, text);
-        sUssdDialog.setMessage(sUssdMsg.toString());
-        sUssdDialog.show();
+        ussdDialog.setMessage(sUssdMsg.toString());
+        ussdDialog.show();
     }
 
     /**
diff --git a/src/com/android/phone/ServiceStateProvider.java b/src/com/android/phone/ServiceStateProvider.java
new file mode 100644
index 0000000..a7d27d5
--- /dev/null
+++ b/src/com/android/phone/ServiceStateProvider.java
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2019 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.Telephony.ServiceStateTable;
+import static android.provider.Telephony.ServiceStateTable.CONTENT_URI;
+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;
+import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionIdAndField;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.MatrixCursor.RowBuilder;
+import android.net.Uri;
+import android.os.Parcel;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The class to provide base facility to access ServiceState related content,
+ * which is stored in a SQLite database.
+ */
+public class ServiceStateProvider extends ContentProvider {
+    private static final String TAG = "ServiceStateProvider";
+
+    public static final String AUTHORITY = ServiceStateTable.AUTHORITY;
+    public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
+
+    /**
+     * The current service state.
+     *
+     * This is the entire {@link ServiceState} object in byte array.
+     *
+     * @hide
+     */
+    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()}.
+     * @hide
+     */
+    public static final String VOICE_ROAMING_TYPE = "voice_roaming_type";
+
+    /**
+     * An integer value indicating the current data roaming type.
+     * <p>
+     * This is the same as {@link ServiceState#getDataRoamingType()}.
+     * @hide
+     */
+    public static final String DATA_ROAMING_TYPE = "data_roaming_type";
+
+    /**
+     * The current registered voice network operator name in long alphanumeric format.
+     * <p>
+     * This is the same as {@link ServiceState#getOperatorAlphaLong()}.
+     * @hide
+     */
+    public static final String VOICE_OPERATOR_ALPHA_LONG = "voice_operator_alpha_long";
+
+    /**
+     * The current registered operator name in short alphanumeric format.
+     * <p>
+     * In GSM/UMTS, short format can be up to 8 characters long. The current registered voice
+     * network operator name in long alphanumeric format.
+     * <p>
+     * This is the same as {@link ServiceState#getOperatorAlphaShort()}.
+     * @hide
+     */
+    public static final String VOICE_OPERATOR_ALPHA_SHORT = "voice_operator_alpha_short";
+
+    /**
+     * The current registered operator numeric id.
+     * <p>
+     * In GSM/UMTS, numeric format is 3 digit country code plus 2 or 3 digit
+     * network code.
+     * <p>
+     * This is the same as {@link ServiceState#getOperatorNumeric()}.
+     */
+    public static final String VOICE_OPERATOR_NUMERIC = "voice_operator_numeric";
+
+    /**
+     * The current registered data network operator name in long alphanumeric format.
+     * <p>
+     * This is the same as {@link ServiceState#getOperatorAlphaLong()}.
+     * @hide
+     */
+    public static final String DATA_OPERATOR_ALPHA_LONG = "data_operator_alpha_long";
+
+    /**
+     * The current registered data network operator name in short alphanumeric format.
+     * <p>
+     * This is the same as {@link ServiceState#getOperatorAlphaShort()}.
+     * @hide
+     */
+    public static final String DATA_OPERATOR_ALPHA_SHORT = "data_operator_alpha_short";
+
+    /**
+     * The current registered data network operator numeric id.
+     * <p>
+     * This is the same as {@link ServiceState#getOperatorNumeric()}.
+     * @hide
+     */
+    public static final String DATA_OPERATOR_NUMERIC = "data_operator_numeric";
+
+    /**
+     * This is the same as {@link ServiceState#getRilVoiceRadioTechnology()}.
+     * @hide
+     */
+    public static final String RIL_VOICE_RADIO_TECHNOLOGY = "ril_voice_radio_technology";
+
+    /**
+     * This is the same as {@link ServiceState#getRilDataRadioTechnology()}.
+     * @hide
+     */
+    public static final String RIL_DATA_RADIO_TECHNOLOGY = "ril_data_radio_technology";
+
+    /**
+     * This is the same as {@link ServiceState#getCssIndicator()}.
+     * @hide
+     */
+    public static final String CSS_INDICATOR = "css_indicator";
+
+    /**
+     * This is the same as {@link ServiceState#getCdmaNetworkId()}.
+     * @hide
+     */
+    public static final String NETWORK_ID = "network_id";
+
+    /**
+     * This is the same as {@link ServiceState#getCdmaSystemId()}.
+     * @hide
+     */
+    public static final String SYSTEM_ID = "system_id";
+
+    /**
+     * This is the same as {@link ServiceState#getCdmaRoamingIndicator()}.
+     * @hide
+     */
+    public static final String CDMA_ROAMING_INDICATOR = "cdma_roaming_indicator";
+
+    /**
+     * This is the same as {@link ServiceState#getCdmaDefaultRoamingIndicator()}.
+     * @hide
+     */
+    public static final String CDMA_DEFAULT_ROAMING_INDICATOR =
+            "cdma_default_roaming_indicator";
+
+    /**
+     * This is the same as {@link ServiceState#getCdmaEriIconIndex()}.
+     * @hide
+     */
+    public static final String CDMA_ERI_ICON_INDEX = "cdma_eri_icon_index";
+
+    /**
+     * This is the same as {@link ServiceState#getCdmaEriIconMode()}.
+     * @hide
+     */
+    public static final String CDMA_ERI_ICON_MODE = "cdma_eri_icon_mode";
+
+    /**
+     * This is the same as {@link ServiceState#isEmergencyOnly()}.
+     * @hide
+     */
+    public static final String IS_EMERGENCY_ONLY = "is_emergency_only";
+
+    /**
+     * This is the same as {@link ServiceState#getDataRoamingFromRegistration()}.
+     * @hide
+     */
+    public static final String IS_DATA_ROAMING_FROM_REGISTRATION =
+            "is_data_roaming_from_registration";
+
+    /**
+     * This is the same as {@link ServiceState#isUsingCarrierAggregation()}.
+     * @hide
+     */
+    public static final String IS_USING_CARRIER_AGGREGATION = "is_using_carrier_aggregation";
+
+    /**
+     * The current registered raw data network operator name in long alphanumeric format.
+     * <p>
+     * This is the same as {@link ServiceState#getOperatorAlphaLongRaw()}.
+     * @hide
+     */
+    public static final String OPERATOR_ALPHA_LONG_RAW = "operator_alpha_long_raw";
+
+    /**
+     * The current registered raw data network operator name in short alphanumeric format.
+     * <p>
+     * This is the same as {@link ServiceState#getOperatorAlphaShortRaw()}.
+     * @hide
+     */
+    public static final String OPERATOR_ALPHA_SHORT_RAW = "operator_alpha_short_raw";
+
+    private final HashMap<Integer, ServiceState> mServiceStates = new HashMap<>();
+    private static final String[] sColumns = {
+        VOICE_REG_STATE,
+        DATA_REG_STATE,
+        VOICE_ROAMING_TYPE,
+        DATA_ROAMING_TYPE,
+        VOICE_OPERATOR_ALPHA_LONG,
+        VOICE_OPERATOR_ALPHA_SHORT,
+        VOICE_OPERATOR_NUMERIC,
+        DATA_OPERATOR_ALPHA_LONG,
+        DATA_OPERATOR_ALPHA_SHORT,
+        DATA_OPERATOR_NUMERIC,
+        IS_MANUAL_NETWORK_SELECTION,
+        RIL_VOICE_RADIO_TECHNOLOGY,
+        RIL_DATA_RADIO_TECHNOLOGY,
+        CSS_INDICATOR,
+        NETWORK_ID,
+        SYSTEM_ID,
+        CDMA_ROAMING_INDICATOR,
+        CDMA_DEFAULT_ROAMING_INDICATOR,
+        CDMA_ERI_ICON_INDEX,
+        CDMA_ERI_ICON_MODE,
+        IS_EMERGENCY_ONLY,
+        IS_USING_CARRIER_AGGREGATION,
+        OPERATOR_ALPHA_LONG_RAW,
+        OPERATOR_ALPHA_SHORT_RAW,
+    };
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    /**
+     * Returns the {@link ServiceState} information on specified subscription.
+     *
+     * @param subId whose subscriber id is returned
+     * @return the {@link ServiceState} information on specified subscription.
+     */
+    @VisibleForTesting
+    public ServiceState getServiceState(int subId) {
+        return mServiceStates.get(subId);
+    }
+
+    /**
+     * Returns the system's default subscription id.
+     *
+     * @return the "system" default subscription id.
+     */
+    @VisibleForTesting
+    public int getDefaultSubId() {
+        return SubscriptionManager.getDefaultSubscriptionId();
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        if (isPathPrefixMatch(uri, CONTENT_URI)) {
+            // Parse the subId
+            int subId = 0;
+            try {
+                subId = Integer.parseInt(uri.getLastPathSegment());
+            } catch (NumberFormatException e) {
+                Log.e(TAG, "insert: no subId provided in uri");
+                throw e;
+            }
+            Log.d(TAG, "subId=" + subId);
+
+            // handle DEFAULT_SUBSCRIPTION_ID
+            if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+                subId = getDefaultSubId();
+            }
+
+            final Parcel p = Parcel.obtain();
+            final byte[] rawBytes = values.getAsByteArray(SERVICE_STATE);
+            p.unmarshall(rawBytes, 0, rawBytes.length);
+            p.setDataPosition(0);
+
+            // create the new service state
+            final ServiceState newSS = ServiceState.CREATOR.createFromParcel(p);
+
+            // notify listeners
+            // if ss is null (e.g. first service state update) we will notify for all fields
+            ServiceState ss = getServiceState(subId);
+            notifyChangeForSubIdAndField(getContext(), ss, newSS, subId);
+            notifyChangeForSubId(getContext(), ss, newSS, subId);
+
+            // store the new service state
+            mServiceStates.put(subId, newSS);
+            return uri;
+        }
+        return null;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        throw new RuntimeException("Not supported");
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        throw new RuntimeException("Not supported");
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        throw new RuntimeException("Not supported");
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        if (!isPathPrefixMatch(uri, CONTENT_URI)) {
+            throw new IllegalArgumentException("Invalid URI: " + uri);
+        } else {
+            // Parse the subId
+            int subId = 0;
+            try {
+                subId = Integer.parseInt(uri.getLastPathSegment());
+            } catch (NumberFormatException e) {
+                Log.d(TAG, "query: no subId provided in uri, using default.");
+                subId = getDefaultSubId();
+            }
+            Log.d(TAG, "subId=" + subId);
+
+            // handle DEFAULT_SUBSCRIPTION_ID
+            if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+                subId = getDefaultSubId();
+            }
+
+            // Get the service state
+            ServiceState ss = getServiceState(subId);
+            if (ss == null) {
+                Log.d(TAG, "returning null");
+                return null;
+            }
+
+            // Build the result
+            final int voice_reg_state = ss.getState();
+            final int data_reg_state = ss.getDataRegistrationState();
+            final int voice_roaming_type = ss.getVoiceRoamingType();
+            final int data_roaming_type = ss.getDataRoamingType();
+            final String voice_operator_alpha_long = ss.getOperatorAlphaLong();
+            final String voice_operator_alpha_short = ss.getOperatorAlphaShort();
+            final String voice_operator_numeric = ss.getOperatorNumeric();
+            final String data_operator_alpha_long = ss.getOperatorAlphaLong();
+            final String data_operator_alpha_short = ss.getOperatorAlphaShort();
+            final String data_operator_numeric = ss.getOperatorNumeric();
+            final int is_manual_network_selection = (ss.getIsManualSelection()) ? 1 : 0;
+            final int ril_voice_radio_technology = ss.getRilVoiceRadioTechnology();
+            final int ril_data_radio_technology = ss.getRilDataRadioTechnology();
+            final int css_indicator = ss.getCssIndicator();
+            final int network_id = ss.getCdmaNetworkId();
+            final int system_id = ss.getCdmaSystemId();
+            final int cdma_roaming_indicator = ss.getCdmaRoamingIndicator();
+            final int cdma_default_roaming_indicator = ss.getCdmaDefaultRoamingIndicator();
+            final int cdma_eri_icon_index = ss.getCdmaEriIconIndex();
+            final int cdma_eri_icon_mode = ss.getCdmaEriIconMode();
+            final int is_emergency_only = (ss.isEmergencyOnly()) ? 1 : 0;
+            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();
+
+            return buildSingleRowResult(projection, sColumns, new Object[] {
+                    voice_reg_state,
+                    data_reg_state,
+                    voice_roaming_type,
+                    data_roaming_type,
+                    voice_operator_alpha_long,
+                    voice_operator_alpha_short,
+                    voice_operator_numeric,
+                    data_operator_alpha_long,
+                    data_operator_alpha_short,
+                    data_operator_numeric,
+                    is_manual_network_selection,
+                    ril_voice_radio_technology,
+                    ril_data_radio_technology,
+                    css_indicator,
+                    network_id,
+                    system_id,
+                    cdma_roaming_indicator,
+                    cdma_default_roaming_indicator,
+                    cdma_eri_icon_index,
+                    cdma_eri_icon_mode,
+                    is_emergency_only,
+                    is_using_carrier_aggregation,
+                    operator_alpha_long_raw,
+                    operator_alpha_short_raw,
+            });
+        }
+    }
+
+    private static Cursor buildSingleRowResult(String[] projection, String[] availableColumns,
+            Object[] data) {
+        if (projection == null) {
+            projection = availableColumns;
+        }
+        final MatrixCursor c = new MatrixCursor(projection, 1);
+        final RowBuilder row = c.newRow();
+        for (int i = 0; i < c.getColumnCount(); i++) {
+            final String columnName = c.getColumnName(i);
+            boolean found = false;
+            for (int j = 0; j < availableColumns.length; j++) {
+                if (availableColumns[j].equals(columnName)) {
+                    row.add(data[j]);
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+                throw new IllegalArgumentException("Invalid column " + projection[i]);
+            }
+        }
+        return c;
+    }
+
+    /**
+     * Notify interested apps that certain fields of the ServiceState have changed.
+     *
+     * Apps which want to wake when specific fields change can use
+     * JobScheduler's TriggerContentUri.  This replaces the waking functionality of the implicit
+     * broadcast of ACTION_SERVICE_STATE_CHANGED for apps targeting version O.
+     *
+     * We will only notify for certain fields. This is an intentional change from the behavior of
+     * the broadcast. Listeners will be notified when the voice or data registration state or
+     * roaming type changes.
+     */
+    @VisibleForTesting
+    public static void notifyChangeForSubIdAndField(Context context, ServiceState oldSS,
+            ServiceState newSS, int subId) {
+        final boolean firstUpdate = (oldSS == null) ? true : false;
+
+        // for every field, if the field has changed values, notify via the provider
+        if (firstUpdate || voiceRegStateChanged(oldSS, newSS)) {
+            context.getContentResolver().notifyChange(
+                    getUriForSubscriptionIdAndField(subId, VOICE_REG_STATE),
+                    /* observer= */ null, /* syncToNetwork= */ false);
+        }
+        if (firstUpdate || dataRegStateChanged(oldSS, newSS)) {
+            context.getContentResolver().notifyChange(
+                    getUriForSubscriptionIdAndField(subId, DATA_REG_STATE), null, false);
+        }
+        if (firstUpdate || voiceRoamingTypeChanged(oldSS, newSS)) {
+            context.getContentResolver().notifyChange(
+                    getUriForSubscriptionIdAndField(subId, VOICE_ROAMING_TYPE), null, false);
+        }
+        if (firstUpdate || dataRoamingTypeChanged(oldSS, newSS)) {
+            context.getContentResolver().notifyChange(
+                    getUriForSubscriptionIdAndField(subId, DATA_ROAMING_TYPE), null, false);
+        }
+    }
+
+    private static boolean voiceRegStateChanged(ServiceState oldSS, ServiceState newSS) {
+        return oldSS.getState() != newSS.getState();
+    }
+
+    private static boolean dataRegStateChanged(ServiceState oldSS, ServiceState newSS) {
+        return oldSS.getDataRegistrationState() != newSS.getDataRegistrationState();
+    }
+
+    private static boolean voiceRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) {
+        return oldSS.getVoiceRoamingType() != newSS.getVoiceRoamingType();
+    }
+
+    private static boolean dataRoamingTypeChanged(ServiceState oldSS, ServiceState newSS) {
+        return oldSS.getDataRoamingType() != newSS.getDataRoamingType();
+    }
+
+    /**
+     * Notify interested apps that the ServiceState has changed.
+     *
+     * Apps which want to wake when any field in the ServiceState has changed can use
+     * JobScheduler's TriggerContentUri.  This replaces the waking functionality of the implicit
+     * broadcast of ACTION_SERVICE_STATE_CHANGED for apps targeting version O.
+     *
+     * We will only notify for certain fields. This is an intentional change from the behavior of
+     * the broadcast. Listeners will only be notified when the voice/data registration state or
+     * roaming type changes.
+     */
+    @VisibleForTesting
+    public static void notifyChangeForSubId(Context context, ServiceState oldSS, ServiceState newSS,
+            int subId) {
+        // if the voice or data registration or roaming state field has changed values, notify via
+        // the provider.
+        // 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)) {
+            context.getContentResolver().notifyChange(getUriForSubscriptionId(subId), null, false);
+        }
+    }
+
+    /**
+     * Test if this is a path prefix match against the given Uri. Verifies that
+     * scheme, authority, and atomic path segments match.
+     *
+     * Copied from frameworks/base/core/java/android/net/Uri.java
+     */
+    private boolean isPathPrefixMatch(Uri uriA, Uri uriB) {
+        if (!Objects.equals(uriA.getScheme(), uriB.getScheme())) return false;
+        if (!Objects.equals(uriA.getAuthority(), uriB.getAuthority())) return false;
+
+        List<String> segA = uriA.getPathSegments();
+        List<String> segB = uriB.getPathSegments();
+
+        final int size = segB.size();
+        if (segA.size() < size) return false;
+
+        for (int i = 0; i < size; i++) {
+            if (!Objects.equals(segA.get(i), segB.get(i))) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Used to insert a ServiceState into the ServiceStateProvider as a ContentValues instance.
+     *
+     * @param state the ServiceState to convert into ContentValues
+     * @return the convertedContentValues instance
+     * @hide
+     */
+    public static ContentValues getContentValuesForServiceState(ServiceState state) {
+        ContentValues values = new ContentValues();
+        final Parcel p = Parcel.obtain();
+        state.writeToParcel(p, 0);
+        // Turn the parcel to byte array. Safe to do this because the content values were never
+        // written into a persistent storage. ServiceStateProvider keeps values in the memory.
+        values.put(SERVICE_STATE, p.marshall());
+        return values;
+    }
+}
diff --git a/src/com/android/phone/TimeConsumingPreferenceActivity.java b/src/com/android/phone/TimeConsumingPreferenceActivity.java
index 8c5ae6d..3b5fe21 100644
--- a/src/com/android/phone/TimeConsumingPreferenceActivity.java
+++ b/src/com/android/phone/TimeConsumingPreferenceActivity.java
@@ -187,6 +187,11 @@
     @Override
     public void onError(Preference preference, int error) {
         if (DBG) dumpState();
+        if (!preference.isEnabled()) {
+            Log.i(LOG_TAG, "onError, skipped duplicated error popup");
+            return;
+        }
+
         Log.i(LOG_TAG, "onError, preference=" + preference.getKey() + ", error=" + error);
 
         if (mIsForeground) {
diff --git a/src/com/android/phone/settings/AccessibilitySettingsFragment.java b/src/com/android/phone/settings/AccessibilitySettingsFragment.java
index c9aa630..37212cf 100644
--- a/src/com/android/phone/settings/AccessibilitySettingsFragment.java
+++ b/src/com/android/phone/settings/AccessibilitySettingsFragment.java
@@ -19,11 +19,13 @@
 import android.content.Context;
 import android.media.AudioManager;
 import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.preference.Preference;
 import android.preference.PreferenceFragment;
 import android.preference.PreferenceScreen;
 import android.preference.SwitchPreference;
 import android.provider.Settings;
+import android.telephony.AccessNetworkConstants;
 import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionManager;
@@ -38,6 +40,11 @@
 import com.android.phone.PhoneGlobals;
 import com.android.phone.R;
 
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
 public class AccessibilitySettingsFragment extends PreferenceFragment {
     private static final String LOG_TAG = AccessibilitySettingsFragment.class.getSimpleName();
     private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
@@ -47,6 +54,8 @@
     private static final String BUTTON_RTT_KEY = "button_rtt_key";
     private static final String RTT_INFO_PREF = "button_rtt_more_information_key";
 
+    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
@@ -64,12 +73,9 @@
                 // support multi sim configuration.
                 TelephonyManager telephonyManager =
                         (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
-                final boolean isVolteTtySupported = getVolteTtySupported();
-                final boolean isVolteCurrentlyEnabled =
-                        ImsManager.isVolteEnabledByPlatform(mContext);
-                pref.setEnabled((isVolteTtySupported && isVolteCurrentlyEnabled &&
-                        !isVideoCallOrConferenceInProgress()) ||
-                        (telephonyManager.getCallState() == TelephonyManager.CALL_STATE_IDLE));
+                final boolean isVolteTtySupported = isVolteTtySupportedInAnySlot();
+                pref.setEnabled((isVolteTtySupported && !isVideoCallOrConferenceInProgress())
+                        || (telephonyManager.getCallState() == TelephonyManager.CALL_STATE_IDLE));
             }
         }
     };
@@ -112,10 +118,13 @@
         }
 
         if (shouldShowRttSetting()) {
-            // TODO: this is going to be a on/off switch for now. Ask UX about how to integrate
-            // this settings with TTY
-            if (TelephonyManager.getDefault().isNetworkRoaming(
-                    SubscriptionManager.getDefaultVoiceSubscriptionId())) {
+            TelephonyManager tm =
+                    (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+            boolean isRoaming = tm.isNetworkRoaming(
+                    SubscriptionManager.getDefaultVoiceSubscriptionId());
+
+            boolean shouldDisableBecauseRoamingOffWfc = isRoaming && !isOnWfc();
+            if (shouldDisableBecauseRoamingOffWfc) {
                 mButtonRtt.setSummary(TextUtils.concat(getText(R.string.rtt_mode_summary), "\n",
                         getText(R.string.no_rtt_when_roaming)));
             }
@@ -166,17 +175,55 @@
             int rttMode = mButtonRtt.isChecked() ? 1 : 0;
             Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.RTT_CALLING_MODE,
                     rttMode);
+            // Update RTT config with IMS Manager if the always-on carrier config isn't set to true.
+            CarrierConfigManager configManager = (CarrierConfigManager) mContext.getSystemService(
+                            Context.CARRIER_CONFIG_SERVICE);
+            for (int subId : SubscriptionController.getInstance().getActiveSubIdList(true)) {
+                if (!configManager.getConfigForSubId(subId).getBoolean(
+                        CarrierConfigManager.KEY_IGNORE_RTT_MODE_SETTING_BOOL, false)) {
+                    int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
+                    ImsManager imsManager = ImsManager.getInstance(getContext(), phoneId);
+                    imsManager.setRttEnabled(mButtonRtt.isChecked());
+                }
+            }
             return true;
         }
 
         return false;
     }
 
-    private boolean getVolteTtySupported() {
+    private boolean isVolteTtySupportedInAnySlot() {
+        final Phone[] phones = PhoneFactory.getPhones();
+        if (phones == null) {
+            if (DBG) Log.d(LOG_TAG, "isVolteTtySupportedInAnySlot: No phones found.");
+            return false;
+        }
+
         CarrierConfigManager configManager =
                 (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        return configManager.getConfig().getBoolean(
-                CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL);
+        for (Phone phone : phones) {
+            // Check if this phone supports VoLTE.
+            ImsManager imsManager = ImsManager.getInstance(mContext, phone.getPhoneId());
+            boolean volteEnabled = false;
+            if (imsManager != null) {
+                volteEnabled = imsManager.isVolteEnabledByPlatform();
+            }
+
+            // Check if this phone suports VoLTE TTY.
+            boolean volteTtySupported = false;
+            PersistableBundle carrierConfig = configManager.getConfigForSubId(phone.getSubId());
+            if (carrierConfig != null) {
+                volteTtySupported = carrierConfig.getBoolean(
+                        CarrierConfigManager.KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL);
+            }
+
+            if (volteEnabled && volteTtySupported) {
+                // VoLTE TTY is supported on this phone that also suports VoLTE.
+                return true;
+            }
+        }
+        // VoLTE TTY was not supported on any phone that also supports VoLTE.
+        return false;
     }
 
     private boolean isVideoCallOrConferenceInProgress() {
@@ -194,6 +241,21 @@
         return false;
     }
 
+    private boolean isOnWfc() {
+        LinkedBlockingQueue<Integer> result = new LinkedBlockingQueue<>(1);
+        Executor executor = Executors.newSingleThreadExecutor();
+        mContext.getSystemService(android.telephony.ims.ImsManager.class)
+                .getImsMmTelManager(SubscriptionManager.getDefaultSubscriptionId())
+                .getRegistrationTransportType(executor, result::offer);
+        try {
+            Integer transportType = result.poll(WFC_QUERY_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+            return transportType != null
+                    && transportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN;
+        } catch (InterruptedException e) {
+            return false;
+        }
+    }
+
     private boolean shouldShowRttSetting() {
         // Go through all the subs -- if we want to display the RTT setting for any of them, do
         // display it.
diff --git a/src/com/android/phone/settings/CallForwardInfoUtil.java b/src/com/android/phone/settings/CallForwardInfoUtil.java
index 1983fab..b963df8 100644
--- a/src/com/android/phone/settings/CallForwardInfoUtil.java
+++ b/src/com/android/phone/settings/CallForwardInfoUtil.java
@@ -82,6 +82,7 @@
         phone.setCallForwardingOption(commandInterfaceCfAction,
                 info.reason,
                 info.number,
+                info.serviceClass,
                 info.timeSeconds,
                 message);
     }
@@ -93,10 +94,12 @@
      */
     public static CallForwardInfo getCallForwardInfo(CallForwardInfo[] infos, int reason) {
         CallForwardInfo info = null;
-        for (int i = 0 ; i < infos.length; i++) {
-            if (isServiceClassVoice(infos[i])) {
-                info = infos[i];
-                break;
+        if (infos != null) {
+            for (int i = 0 ; i < infos.length; i++) {
+                if (isServiceClassVoice(infos[i])) {
+                    info = infos[i];
+                    break;
+                }
             }
         }
 
diff --git a/src/com/android/phone/settings/RadioInfo.java b/src/com/android/phone/settings/RadioInfo.java
index 77f1135..d0951e4 100644
--- a/src/com/android/phone/settings/RadioInfo.java
+++ b/src/com/android/phone/settings/RadioInfo.java
@@ -38,6 +38,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
@@ -465,12 +466,12 @@
 
         mQueuedWork = new ThreadPoolExecutor(1, 1, RUNNABLE_TIMEOUT_MS, TimeUnit.MICROSECONDS,
                 new LinkedBlockingDeque<Runnable>());
-        mTelephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
         mConnectivityManager = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
         mPhone = PhoneFactory.getDefaultPhone();
+        mTelephonyManager = ((TelephonyManager) getSystemService(TELEPHONY_SERVICE))
+                .createForSubscriptionId(mPhone.getSubId());
 
-        mImsManager = ImsManager.getInstance(getApplicationContext(),
-                SubscriptionManager.getDefaultVoicePhoneId());
+        mImsManager = ImsManager.getInstance(getApplicationContext(), mPhone.getPhoneId());
 
         sPhoneIndexLabels = getPhoneIndexLabels(mTelephonyManager);
 
@@ -1449,15 +1450,25 @@
     OnCheckedChangeListener mRadioPowerOnChangeListener = new OnCheckedChangeListener() {
         @Override
         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-            log("toggle radio power: currently " + (isRadioOn() ? "on" : "off"));
-            mPhone.setRadioPower(isChecked);
+            // TODO: b/145681511. Within current design, radio power on all of the phones need
+            // to be controlled at the same time.
+            Phone[] phones = PhoneFactory.getPhones();
+            if (phones == null) {
+                return;
+            }
+            log("toggle radio power: phone*" + phones.length + " " + (isRadioOn() ? "on" : "off"));
+            for (int phoneIndex = 0; phoneIndex < phones.length; phoneIndex++) {
+                if (phones[phoneIndex] != null) {
+                    phones[phoneIndex].setRadioPower(isChecked);
+                }
+            }
         }
     };
 
     private boolean isImsVolteProvisioned() {
-        if (mPhone != null && mImsManager != null) {
-            return mImsManager.isVolteEnabledByPlatform(mPhone.getContext())
-                && mImsManager.isVolteProvisionedOnDevice(mPhone.getContext());
+        if (mImsManager != null) {
+            return mImsManager.isVolteEnabledByPlatform()
+                && mImsManager.isVolteProvisionedOnDevice();
         }
         return false;
     }
@@ -1470,9 +1481,9 @@
     };
 
     private boolean isImsVtProvisioned() {
-        if (mPhone != null && mImsManager != null) {
-            return mImsManager.isVtEnabledByPlatform(mPhone.getContext())
-                && mImsManager.isVtProvisionedOnDevice(mPhone.getContext());
+        if (mImsManager != null) {
+            return mImsManager.isVtEnabledByPlatform()
+                && mImsManager.isVtProvisionedOnDevice();
         }
         return false;
     }
@@ -1485,9 +1496,9 @@
     };
 
     private boolean isImsWfcProvisioned() {
-        if (mPhone != null && mImsManager != null) {
-            return mImsManager.isWfcEnabledByPlatform(mPhone.getContext())
-                && mImsManager.isWfcProvisionedOnDevice(mPhone.getContext());
+        if (mImsManager != null) {
+            return mImsManager.isWfcEnabledByPlatform()
+                && mImsManager.isWfcProvisionedOnDevice();
         }
         return false;
     }
@@ -1529,13 +1540,14 @@
         return provisioned;
     }
 
-    private static boolean isEabEnabledByPlatform(Context context) {
-        if (context != null) {
+    private boolean isEabEnabledByPlatform() {
+        if (mPhone != null) {
             CarrierConfigManager configManager = (CarrierConfigManager)
-                    context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-            if (configManager != null && configManager.getConfig().getBoolean(
-                        CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL)) {
-                return true;
+                    mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+            PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId());
+            if (b != null) {
+                return b.getBoolean(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL,
+                        false);
             }
         }
         return false;
@@ -1552,25 +1564,25 @@
         mImsVolteProvisionedSwitch.setChecked(isImsVolteProvisioned());
         mImsVolteProvisionedSwitch.setOnCheckedChangeListener(mImsVolteCheckedChangeListener);
         mImsVolteProvisionedSwitch.setEnabled(!IS_USER_BUILD
-                && mImsManager.isVolteEnabledByPlatform(mPhone.getContext()));
+                && mImsManager.isVolteEnabledByPlatform());
 
         mImsVtProvisionedSwitch.setOnCheckedChangeListener(null);
         mImsVtProvisionedSwitch.setChecked(isImsVtProvisioned());
         mImsVtProvisionedSwitch.setOnCheckedChangeListener(mImsVtCheckedChangeListener);
         mImsVtProvisionedSwitch.setEnabled(!IS_USER_BUILD
-                && mImsManager.isVtEnabledByPlatform(mPhone.getContext()));
+                && mImsManager.isVtEnabledByPlatform());
 
         mImsWfcProvisionedSwitch.setOnCheckedChangeListener(null);
         mImsWfcProvisionedSwitch.setChecked(isImsWfcProvisioned());
         mImsWfcProvisionedSwitch.setOnCheckedChangeListener(mImsWfcCheckedChangeListener);
         mImsWfcProvisionedSwitch.setEnabled(!IS_USER_BUILD
-                && mImsManager.isWfcEnabledByPlatform(mPhone.getContext()));
+                && mImsManager.isWfcEnabledByPlatform());
 
         mEabProvisionedSwitch.setOnCheckedChangeListener(null);
         mEabProvisionedSwitch.setChecked(isEabProvisioned());
         mEabProvisionedSwitch.setOnCheckedChangeListener(mEabCheckedChangeListener);
         mEabProvisionedSwitch.setEnabled(!IS_USER_BUILD
-                && isEabEnabledByPlatform(mPhone.getContext()));
+                && isEabEnabledByPlatform());
     }
 
     OnClickListener mDnsCheckButtonHandler = new OnClickListener() {
@@ -1718,7 +1730,7 @@
             mQueuedWork.execute(new Runnable() {
                 public void run() {
                     mTelephonyManager.setOpportunisticNetworkState(state);
-                    mCbrsDataSwitch.setChecked(getCbrsDataState());
+                    mHandler.post(() -> mCbrsDataSwitch.setChecked(getCbrsDataState()));
                 }
             });
         }
diff --git a/src/com/android/phone/settings/SuppServicesUiUtil.java b/src/com/android/phone/settings/SuppServicesUiUtil.java
index 4e9841f..4f1a79f 100644
--- a/src/com/android/phone/settings/SuppServicesUiUtil.java
+++ b/src/com/android/phone/settings/SuppServicesUiUtil.java
@@ -84,7 +84,7 @@
                 .create();
     }
 
-    private static String makeMessage(Context context, String preferenceKey, Phone phone) {
+    public static String makeMessage(Context context, String preferenceKey, Phone phone) {
         String message = "";
         int simSlot = (phone.getPhoneId() == 0) ? 1 : 2;
         String suppServiceName = getSuppServiceName(context, preferenceKey);
diff --git a/src/com/android/phone/settings/VoicemailSettingsActivity.java b/src/com/android/phone/settings/VoicemailSettingsActivity.java
index e18dc93..66b1af9 100644
--- a/src/com/android/phone/settings/VoicemailSettingsActivity.java
+++ b/src/com/android/phone/settings/VoicemailSettingsActivity.java
@@ -43,6 +43,7 @@
 import android.widget.Toast;
 
 import com.android.internal.telephony.CallForwardInfo;
+import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.util.NotificationChannelController;
@@ -733,6 +734,7 @@
             for (int i = 0; i < mForwardingReadResults.length; i++) {
                 mPhone.getCallForwardingOption(
                         VoicemailProviderSettings.FORWARDING_SETTINGS_REASONS[i],
+                        CommandsInterface.SERVICE_CLASS_VOICE,
                         mGetOptionComplete.obtainMessage(EVENT_FORWARDING_GET_COMPLETED, i, 0));
             }
             showDialogIfForeground(VoicemailDialogUtil.VM_FWD_READING_DIALOG);
diff --git a/src/com/android/phone/settings/fdn/DeleteFdnContactScreen.java b/src/com/android/phone/settings/fdn/DeleteFdnContactScreen.java
index 92baa97..8b17cfb 100644
--- a/src/com/android/phone/settings/fdn/DeleteFdnContactScreen.java
+++ b/src/com/android/phone/settings/fdn/DeleteFdnContactScreen.java
@@ -64,7 +64,8 @@
 
         resolveIntent();
 
-        authenticatePin2();
+        // 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);
diff --git a/src/com/android/services/telephony/CdmaConference.java b/src/com/android/services/telephony/CdmaConference.java
old mode 100755
new mode 100644
index 693fd16..7458195
--- a/src/com/android/services/telephony/CdmaConference.java
+++ b/src/com/android/services/telephony/CdmaConference.java
@@ -17,6 +17,7 @@
 package com.android.services.telephony;
 
 import android.content.Context;
+import android.net.Uri;
 import android.os.PersistableBundle;
 import android.telecom.Connection;
 import android.telecom.PhoneAccountHandle;
@@ -73,6 +74,22 @@
     }
 
     @Override
+    public void onAddConferenceParticipants(List<Uri> participants) {
+        Log.e(this, new Exception(), "Adding Conference Participants not supported " +
+                " for CDMA conference call.");
+    }
+
+    @Override
+    public void onAnswer(int videoState) {
+        Log.e(this, new Exception(), "Answer not supported for CDMA conference call.");
+    }
+
+    @Override
+    public void onReject() {
+        Log.e(this, new Exception(), "Reject not supported for CDMA conference call.");
+    }
+
+    @Override
     public void onHold() {
         Log.e(this, new Exception(), "Hold not supported for CDMA conference call.");
     }
diff --git a/src/com/android/services/telephony/CdmaConferenceController.java b/src/com/android/services/telephony/CdmaConferenceController.java
index 9afcd0a..8523a5f 100644
--- a/src/com/android/services/telephony/CdmaConferenceController.java
+++ b/src/com/android/services/telephony/CdmaConferenceController.java
@@ -97,7 +97,7 @@
             return;
         }
 
-        if (!mCdmaConnections.isEmpty() && connection.isOutgoing()) {
+        if (!mCdmaConnections.isEmpty() && connection.isOutgoingCall()) {
             // There already exists a connection, so this will probably result in a conference once
             // it is added. For outgoing connections which are added while another connection
             // exists, we mark them as "dialing" for a set amount of time to give the user time to
@@ -183,7 +183,7 @@
                 isNewlyCreated = true;
             }
 
-            if (newConnection.isOutgoing()) {
+            if (newConnection.isOutgoingCall()) {
                 // Only an outgoing call can be merged with an ongoing call.
                 mConference.updateCapabilities(Connection.CAPABILITY_MERGE_CONFERENCE);
             } else {
diff --git a/src/com/android/services/telephony/CdmaConnection.java b/src/com/android/services/telephony/CdmaConnection.java
index bd015e3..90e7663 100644
--- a/src/com/android/services/telephony/CdmaConnection.java
+++ b/src/com/android/services/telephony/CdmaConnection.java
@@ -81,9 +81,9 @@
             Connection connection,
             EmergencyTonePlayer emergencyTonePlayer,
             boolean allowMute,
-            boolean isOutgoing,
+            int callDirection,
             String telecomCallId) {
-        super(connection, telecomCallId, isOutgoing);
+        super(connection, telecomCallId, callDirection);
         mEmergencyTonePlayer = emergencyTonePlayer;
         mAllowMute = allowMute;
         mIsCallWaiting = connection != null && connection.getState() == Call.State.WAITING;
@@ -151,7 +151,7 @@
     @Override
     public TelephonyConnection cloneConnection() {
         CdmaConnection cdmaConnection = new CdmaConnection(getOriginalConnection(),
-                mEmergencyTonePlayer, mAllowMute, mIsOutgoing, getTelecomCallId());
+                mEmergencyTonePlayer, mAllowMute, getCallDirection(), getTelecomCallId());
         return cdmaConnection;
     }
 
@@ -202,10 +202,6 @@
         }
     }
 
-    boolean isOutgoing() {
-        return mIsOutgoing;
-    }
-
     boolean isCallWaiting() {
         return mIsCallWaiting;
     }
@@ -288,9 +284,8 @@
 
     private boolean isEmergency() {
         Phone phone = getPhone();
-        return phone != null &&
-                PhoneNumberUtils.isLocalEmergencyNumber(
-                    phone.getContext(), getAddress().getSchemeSpecificPart());
+        return phone != null && getAddress() != null && PhoneNumberUtils.isLocalEmergencyNumber(
+                phone.getContext(), getAddress().getSchemeSpecificPart());
     }
 
     /**
@@ -306,7 +301,7 @@
 
     private void handleCdmaConnectionTimeReset() {
         boolean isImsCall = getOriginalConnection() instanceof ImsPhoneConnection;
-        if (!isImsCall && !mIsConnectionTimeReset && mIsOutgoing
+        if (!isImsCall && !mIsConnectionTimeReset && isOutgoingCall()
                 && getOriginalConnection() != null
                 && getOriginalConnection().getState() == Call.State.ACTIVE
                 && getOriginalConnection().getDurationMillis() > 0) {
diff --git a/src/com/android/services/telephony/ConferenceParticipantConnection.java b/src/com/android/services/telephony/ConferenceParticipantConnection.java
index ed92e7f..b7ecd48 100644
--- a/src/com/android/services/telephony/ConferenceParticipantConnection.java
+++ b/src/com/android/services/telephony/ConferenceParticipantConnection.java
@@ -19,13 +19,13 @@
 import android.net.Uri;
 import android.telecom.Connection;
 import android.telecom.DisconnectCause;
-import android.telephony.Rlog;
 import android.telephony.SubscriptionInfo;
 import android.text.TextUtils;
 
 import com.android.ims.internal.ConferenceParticipant;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
+import com.android.telephony.Rlog;
 
 /**
  * Represents a participant in a conference call.
@@ -215,7 +215,7 @@
         sb.append(" connectTime:");
         sb.append(getConnectTimeMillis());
         sb.append(" connectElapsedTime:");
-        sb.append(getConnectElapsedTimeMillis());
+        sb.append(getConnectionStartElapsedRealtimeMillis());
         sb.append("]");
 
         return sb.toString();
diff --git a/src/com/android/services/telephony/DisconnectCauseUtil.java b/src/com/android/services/telephony/DisconnectCauseUtil.java
index 9e32f00..fd645a8 100644
--- a/src/com/android/services/telephony/DisconnectCauseUtil.java
+++ b/src/com/android/services/telephony/DisconnectCauseUtil.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.media.ToneGenerator;
 import android.os.PersistableBundle;
+import android.provider.Settings;
 import android.telecom.DisconnectCause;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
@@ -347,6 +348,9 @@
             case android.telephony.DisconnectCause.DATA_LIMIT_REACHED:
                 resourceId = R.string.callFailed_data_limit_reached;
                 break;
+            case android.telephony.DisconnectCause.WIFI_LOST:
+                resourceId = R.string.callFailed_wifi_lost;
+                break;
             case android.telephony.DisconnectCause.ALREADY_DIALING:
                 resourceId = R.string.callFailed_already_dialing;
                 break;
@@ -360,7 +364,12 @@
                 resourceId = R.string.callFailed_too_many_calls;
                 break;
             case android.telephony.DisconnectCause.IMS_SIP_ALTERNATE_EMERGENCY_CALL:
-                resourceId = R.string.incall_error_power_off;
+                int airplaneMode = Settings.Global.getInt(context.getContentResolver(),
+                        Settings.Global.AIRPLANE_MODE_ON, 0);
+                resourceId = R.string.incall_error_call_failed;
+                if (airplaneMode != 0) {
+                    resourceId = R.string.incall_error_power_off;
+                }
                 break;
             case android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS:
                 resourceId = R.string.callFailed_otasp_provisioning_in_process;
@@ -753,7 +762,12 @@
                 resourceId = R.string.callFailed_too_many_calls;
                 break;
             case android.telephony.DisconnectCause.IMS_SIP_ALTERNATE_EMERGENCY_CALL:
-                resourceId = R.string.incall_error_power_off;
+                int airplaneMode = Settings.Global.getInt(context.getContentResolver(),
+                        Settings.Global.AIRPLANE_MODE_ON, 0);
+                resourceId = R.string.incall_error_call_failed;
+                if (airplaneMode != 0) {
+                    resourceId = R.string.incall_error_power_off;
+                }
                 break;
             case android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS:
                 resourceId = R.string.callFailed_otasp_provisioning_in_process;
@@ -847,17 +861,18 @@
             case android.telephony.DisconnectCause.UNOBTAINABLE_NUMBER:
                 return ToneGenerator.TONE_SUP_ERROR;
 
+            case android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY:
+                // Do not play any tones if disconnected because of a successful merge.
+                return ToneGenerator.TONE_UNKNOWN;
+
             case android.telephony.DisconnectCause.ERROR_UNSPECIFIED:
             case android.telephony.DisconnectCause.LOCAL:
             case android.telephony.DisconnectCause.NORMAL:
             case android.telephony.DisconnectCause.NORMAL_UNSPECIFIED:
             case android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED:
-                return ToneGenerator.TONE_PROP_PROMPT;
-
-            case android.telephony.DisconnectCause.IMS_MERGED_SUCCESSFULLY:
-                // Do not play any tones if disconnected because of a successful merge.
+            case android.telephony.DisconnectCause.WIFI_LOST:
             default:
-                return ToneGenerator.TONE_UNKNOWN;
+                return ToneGenerator.TONE_PROP_PROMPT;
         }
     }
 }
diff --git a/src/com/android/services/telephony/GsmConnection.java b/src/com/android/services/telephony/GsmConnection.java
index 47434c0..769ec22 100644
--- a/src/com/android/services/telephony/GsmConnection.java
+++ b/src/com/android/services/telephony/GsmConnection.java
@@ -22,8 +22,8 @@
  * Manages a single phone call handled by GSM.
  */
 final class GsmConnection extends TelephonyConnection {
-    GsmConnection(Connection connection, String telecomCallId, boolean isOutgoing) {
-        super(connection, telecomCallId, isOutgoing);
+    GsmConnection(Connection connection, String telecomCallId, int callDirection) {
+        super(connection, telecomCallId, callDirection);
     }
 
     /**
@@ -35,9 +35,7 @@
      */
     @Override
     public TelephonyConnection cloneConnection() {
-        GsmConnection gsmConnection = new GsmConnection(getOriginalConnection(),
-                getTelecomCallId(), mIsOutgoing);
-        return gsmConnection;
+        return new GsmConnection(getOriginalConnection(), getTelecomCallId(), getCallDirection());
     }
 
     /** {@inheritDoc} */
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index 4c3f9c9..f7fcf64 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -20,7 +20,6 @@
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.PersistableBundle;
 import android.telecom.Connection;
 import android.telecom.Connection.VideoProvider;
 import android.telecom.DisconnectCause;
@@ -28,9 +27,7 @@
 import android.telecom.StatusHints;
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
-import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
 import android.util.Pair;
 
 import com.android.ims.internal.ConferenceParticipant;
@@ -39,9 +36,9 @@
 import com.android.internal.telephony.CallStateException;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
-import com.android.phone.PhoneGlobals;
 import com.android.phone.PhoneUtils;
 import com.android.phone.R;
+import com.android.telephony.Rlog;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -79,6 +76,126 @@
     }
 
     /**
+     * Abstracts out carrier configuration items specific to the conference.
+     */
+    public static class CarrierConfiguration {
+        /**
+         * Builds and instance of {@link CarrierConfiguration}.
+         */
+        public static class Builder {
+            private boolean mIsMaximumConferenceSizeEnforced = false;
+            private int mMaximumConferenceSize = 5;
+            private boolean mShouldLocalDisconnectEmptyConference = false;
+            private boolean mIsHoldAllowed = false;
+
+            /**
+             * Sets whether the maximum size of the conference is enforced.
+             * @param isMaximumConferenceSizeEnforced {@code true} if conference size enforced.
+             * @return builder instance.
+             */
+            public Builder setIsMaximumConferenceSizeEnforced(
+                    boolean isMaximumConferenceSizeEnforced) {
+                mIsMaximumConferenceSizeEnforced = isMaximumConferenceSizeEnforced;
+                return this;
+            }
+
+            /**
+             * Sets the maximum size of an IMS conference.
+             * @param maximumConferenceSize Max conference size.
+             * @return builder instance.
+             */
+            public Builder setMaximumConferenceSize(int maximumConferenceSize) {
+                mMaximumConferenceSize = maximumConferenceSize;
+                return this;
+            }
+
+            /**
+             * Sets whether an empty conference should be locally disconnected.
+             * @param shouldLocalDisconnectEmptyConference {@code true} if conference should be
+             * locally disconnected if empty.
+             * @return builder instance.
+             */
+            public Builder setShouldLocalDisconnectEmptyConference(
+                    boolean shouldLocalDisconnectEmptyConference) {
+                mShouldLocalDisconnectEmptyConference = shouldLocalDisconnectEmptyConference;
+                return this;
+            }
+
+            /**
+             * Sets whether holding the conference is allowed.
+             * @param isHoldAllowed {@code true} if holding is allowed.
+             * @return builder instance.
+             */
+            public Builder setIsHoldAllowed(boolean isHoldAllowed) {
+                mIsHoldAllowed = isHoldAllowed;
+                return this;
+            }
+
+            /**
+             * Build instance of {@link CarrierConfiguration}.
+             * @return carrier config instance.
+             */
+            public ImsConference.CarrierConfiguration build() {
+                return new ImsConference.CarrierConfiguration(mIsMaximumConferenceSizeEnforced,
+                        mMaximumConferenceSize, mShouldLocalDisconnectEmptyConference,
+                        mIsHoldAllowed);
+            }
+        }
+
+        private CarrierConfiguration(boolean isMaximumConferenceSizeEnforced,
+                int maximumConferenceSize, boolean shouldLocalDisconnectEmptyConference,
+                boolean isHoldAllowed) {
+            mIsMaximumConferenceSizeEnforced = isMaximumConferenceSizeEnforced;
+            mMaximumConferenceSize = maximumConferenceSize;
+            mShouldLocalDisconnectEmptyConference = shouldLocalDisconnectEmptyConference;
+            mIsHoldAllowed = isHoldAllowed;
+        }
+
+        private boolean mIsMaximumConferenceSizeEnforced;
+
+        private int mMaximumConferenceSize;
+
+        private boolean mShouldLocalDisconnectEmptyConference;
+
+        private boolean mIsHoldAllowed;
+
+        /**
+         * Determines whether the {@link ImsConference} should enforce a size limit based on
+         * {@link #getMaximumConferenceSize()}.
+         * {@code true} if maximum size limit should be enforced, {@code false} otherwise.
+         */
+        public boolean isMaximumConferenceSizeEnforced() {
+            return mIsMaximumConferenceSizeEnforced;
+        }
+
+        /**
+         * Determines the maximum number of participants (not including the host) in a conference
+         * which is enforced when {@link #isMaximumConferenceSizeEnforced()} is {@code true}.
+         */
+        public int getMaximumConferenceSize() {
+            return mMaximumConferenceSize;
+        }
+
+        /**
+         * Determines whether this {@link ImsConference} should locally disconnect itself when the
+         * number of participants in the conference drops to zero.
+         * {@code true} if empty conference should be locally disconnected, {@code false}
+         * otherwise.
+         */
+        public boolean shouldLocalDisconnectEmptyConference() {
+            return mShouldLocalDisconnectEmptyConference;
+        }
+
+        /**
+         * Determines whether holding the conference is permitted or not.
+         * {@code true} if hold is permitted, {@code false} otherwise.
+         */
+        public boolean isHoldAllowed() {
+            return mIsHoldAllowed;
+        }
+    }
+
+    /**
      * Listener used to respond to changes to the underlying radio connection for the conference
      * host connection.  Used to respond to SRVCC changes.
      */
@@ -199,6 +316,18 @@
                     TelephonyConnection telephonyConnection = (TelephonyConnection) c;
                     handleConferenceParticipantsUpdate(telephonyConnection, participants);
                 }
+
+                /**
+                 * Handles request to play a ringback tone.
+                 *
+                 * @param c The connection.
+                 * @param ringback Whether the ringback tone is to be played.
+                 */
+                @Override
+                public void onRingbackRequested(android.telecom.Connection c, boolean ringback) {
+                    Log.d(this, "onRingbackRequested ringback %s", ringback ? "Y" : "N");
+                    setRingbackRequested(ringback);
+                }
             };
 
     /**
@@ -224,6 +353,11 @@
     private TelecomAccountRegistry mTelecomAccountRegistry;
 
     /**
+     * The participant with which Adhoc Conference call is getting formed.
+     */
+    private List<Uri> mParticipants;
+
+    /**
      * The known conference participant connections.  The HashMap is keyed by a Pair containing
      * the handle and endpoint Uris.
      * Access to the hashmap is protected by the {@link #mUpdateSyncRoot}.
@@ -243,6 +377,7 @@
     private boolean mIsHoldable;
     private boolean mCouldManageConference;
     private FeatureFlagProxy mFeatureFlagProxy;
+    private final CarrierConfiguration mCarrierConfig;
     private boolean mIsEmulatingSinglePartyCall = false;
     private boolean mIsUsingSimCallManager = false;
 
@@ -284,29 +419,30 @@
     public ImsConference(TelecomAccountRegistry telecomAccountRegistry,
             TelephonyConnectionServiceProxy telephonyConnectionService,
             TelephonyConnection conferenceHost, PhoneAccountHandle phoneAccountHandle,
-            FeatureFlagProxy featureFlagProxy) {
+            FeatureFlagProxy featureFlagProxy, CarrierConfiguration carrierConfig) {
 
         super(phoneAccountHandle);
 
         mTelecomAccountRegistry = telecomAccountRegistry;
         mFeatureFlagProxy = featureFlagProxy;
+        mCarrierConfig = carrierConfig;
 
         // Specify the connection time of the conference to be the connection time of the original
         // connection.
         long connectTime = conferenceHost.getOriginalConnection().getConnectTime();
         long connectElapsedTime = conferenceHost.getOriginalConnection().getConnectTimeReal();
         setConnectionTime(connectTime);
-        setConnectionStartElapsedRealTime(connectElapsedTime);
+        setConnectionStartElapsedRealtimeMillis(connectElapsedTime);
         // Set the connectTime in the connection as well.
         conferenceHost.setConnectTimeMillis(connectTime);
-        conferenceHost.setConnectionStartElapsedRealTime(connectElapsedTime);
+        conferenceHost.setConnectionStartElapsedRealtimeMillis(connectElapsedTime);
 
         mTelephonyConnectionService = telephonyConnectionService;
         setConferenceHost(conferenceHost);
 
         int capabilities = Connection.CAPABILITY_MUTE |
                 Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN;
-        if (canHoldImsCalls()) {
+        if (mCarrierConfig.isHoldAllowed()) {
             capabilities |= Connection.CAPABILITY_SUPPORT_HOLD | Connection.CAPABILITY_HOLD;
             mIsHoldable = true;
         }
@@ -357,6 +493,10 @@
                 Connection.CAPABILITY_CAN_PAUSE_VIDEO,
                 mConferenceHost.getVideoPauseSupported() && isVideoCapable());
 
+        conferenceCapabilities = changeBitmask(conferenceCapabilities,
+                Connection.CAPABILITY_ADD_PARTICIPANT,
+                (capabilities & Connection.CAPABILITY_ADD_PARTICIPANT) != 0);
+
         return conferenceCapabilities;
     }
 
@@ -383,6 +523,10 @@
         conferenceProperties = changeBitmask(conferenceProperties,
                 Connection.PROPERTY_REMOTELY_HOSTED, !isConferenceHost());
 
+        conferenceProperties = changeBitmask(conferenceProperties,
+                Connection.PROPERTY_IS_ADHOC_CONFERENCE,
+                (properties & Connection.PROPERTY_IS_ADHOC_CONFERENCE) != 0);
+
         return conferenceProperties;
     }
 
@@ -422,6 +566,26 @@
         return VideoProfile.STATE_AUDIO_ONLY;
     }
 
+    public Connection getConferenceHost() {
+        return mConferenceHost;
+    }
+
+    /**
+     * @return The address's to which this Connection is currently communicating.
+     */
+    public final List<Uri> getParticipants() {
+        return mParticipants;
+    }
+
+    /**
+     * Sets the value of the {@link #getParticipants()}.
+     *
+     * @param address The new address's.
+     */
+    public final void setParticipants(List<Uri> address) {
+        mParticipants = address;
+    }
+
     /**
      * Invoked when the Conference and all its {@link Connection}s should be disconnected.
      * <p>
@@ -446,6 +610,8 @@
             } catch (CallStateException e) {
                 Log.e(this, e, "Exception thrown trying to hangup conference");
             }
+        } else {
+            Log.w(this, "onDisconnect - null call");
         }
     }
 
@@ -481,6 +647,41 @@
     }
 
     /**
+     * Supports adding participants to an existing conference call
+     *
+     * @param participants that are pulled to existing conference call
+     */
+    @Override
+    public void onAddConferenceParticipants(List<Uri> participants) {
+        if (mConferenceHost == null) {
+            return;
+        }
+        mConferenceHost.performAddConferenceParticipants(participants);
+    }
+
+    /**
+     * Invoked when the conference is answered.
+     */
+    @Override
+    public void onAnswer(int videoState) {
+        if (mConferenceHost == null) {
+            return;
+        }
+        mConferenceHost.performAnswer(videoState);
+    }
+
+    /**
+     * Invoked when the conference is rejected.
+     */
+    @Override
+    public void onReject() {
+        if (mConferenceHost == null) {
+            return;
+        }
+        mConferenceHost.performReject(android.telecom.Call.REJECT_REASON_DECLINED);
+    }
+
+    /**
      * Invoked when the conference should be put on hold.
      */
     @Override
@@ -678,7 +879,8 @@
             setAddress(mConferenceHost.getAddress(), mConferenceHost.getAddressPresentation());
             setCallerDisplayName(mConferenceHost.getCallerDisplayName(),
                     mConferenceHost.getCallerDisplayNamePresentation());
-            setConnectionStartElapsedRealTime(mConferenceHost.getConnectElapsedTimeMillis());
+            setConnectionStartElapsedRealtimeMillis(
+                    mConferenceHost.getConnectionStartElapsedRealtimeMillis());
             setConnectionTime(mConferenceHost.getConnectTimeMillis());
         }
 
@@ -838,7 +1040,8 @@
                             + "newParticipantcount=%d", oldParticipantCount, newParticipantCount);
             // If the single party call emulation fature flag is enabled, we can potentially treat
             // the conference as a single party call when there is just one participant.
-            if (mFeatureFlagProxy.isUsingSinglePartyCallEmulation()) {
+            if (mFeatureFlagProxy.isUsingSinglePartyCallEmulation() &&
+                    !mConferenceHost.isAdhocConferenceCall()) {
                 if (oldParticipantCount != 1 && newParticipantCount == 1) {
                     // If number of participants goes to 1, emulate a single party call.
                     startEmulatingSinglePartyCall();
@@ -853,6 +1056,14 @@
             if (newParticipantsAdded || oldParticipantsRemoved) {
                 updateManageConference();
             }
+
+            // If the conference is empty and we're supposed to do a local disconnect, do so now.
+            if (mCarrierConfig.shouldLocalDisconnectEmptyConference()
+                    && oldParticipantCount > 0 && newParticipantCount == 0) {
+                Log.i(this, "handleConferenceParticipantsUpdate: empty conference; "
+                        + "local disconnect.");
+                onDisconnect();
+            }
         }
     }
 
@@ -895,7 +1106,8 @@
             Log.d(this,
                     "stopEmulatingSinglePartyCall: restored lone participant connect time");
             loneParticipant.setConnectTimeMillis(getConnectionTime());
-            loneParticipant.setConnectionStartElapsedRealTime(getConnectionStartElapsedRealTime());
+            loneParticipant.setConnectionStartElapsedRealtimeMillis(
+                    getConnectionStartElapsedRealtimeMillis());
         }
 
         // Tell Telecom its a conference again.
@@ -935,8 +1147,10 @@
             setAddress(entry.getAddress(), entry.getAddressPresentation());
             setCallerDisplayName(entry.getCallerDisplayName(),
                     entry.getCallerDisplayNamePresentation());
-            setConnectionStartElapsedRealTime(entry.getConnectElapsedTimeMillis());
+            setConnectionStartElapsedRealtimeMillis(
+                    entry.getConnectionStartElapsedRealtimeMillis());
             setConnectionTime(entry.getConnectTimeMillis());
+            setCallDirection(entry.getCallDirection());
             mLoneParticipantIdentity = new Pair<>(entry.getUserEntity(), entry.getEndpoint());
 
             // Remove the participant from Telecom.  It'll get picked up in a future CEP update
@@ -979,10 +1193,11 @@
                 !isConferenceHost() /* isRemotelyHosted */);
         if (participant.getConnectTime() == 0) {
             connection.setConnectTimeMillis(parent.getConnectTimeMillis());
-            connection.setConnectionStartElapsedRealTime(parent.getConnectElapsedTimeMillis());
+            connection.setConnectionStartElapsedRealtimeMillis(
+                    parent.getConnectionStartElapsedRealtimeMillis());
         } else {
             connection.setConnectTimeMillis(participant.getConnectTime());
-            connection.setConnectionStartElapsedRealTime(participant.getConnectElapsedTime());
+            connection.setConnectionStartElapsedRealtimeMillis(participant.getConnectElapsedTime());
         }
         // Indicate whether this is an MT or MO call to Telecom; the participant has the cached
         // data from the time of merge.
@@ -1140,7 +1355,7 @@
             if (mConferenceHost.getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
                 Log.i(this,"handleOriginalConnectionChange : SRVCC to GSM");
                 GsmConnection c = new GsmConnection(originalConnection, getTelecomCallId(),
-                        mConferenceHost.isOutgoingCall());
+                        mConferenceHost.getCallDirection());
                 // This is a newly created conference connection as a result of SRVCC
                 c.setConferenceSupported(true);
                 c.setTelephonyConnectionProperties(
@@ -1148,7 +1363,8 @@
                 c.updateState();
                 // Copy the connect time from the conferenceHost
                 c.setConnectTimeMillis(mConferenceHost.getConnectTimeMillis());
-                c.setConnectionStartElapsedRealTime(mConferenceHost.getConnectElapsedTimeMillis());
+                c.setConnectionStartElapsedRealtimeMillis(
+                        mConferenceHost.getConnectionStartElapsedRealtimeMillis());
                 mTelephonyConnectionService.addExistingConnection(phoneAccountHandle, c);
                 mTelephonyConnectionService.addConnectionToConferenceController(c);
             } // CDMA case not applicable for SRVCC
@@ -1173,11 +1389,13 @@
         switch (state) {
             case Connection.STATE_INITIALIZING:
             case Connection.STATE_NEW:
-            case Connection.STATE_RINGING:
                 // No-op -- not applicable.
                 break;
+            case Connection.STATE_RINGING:
+                setConferenceOnRinging();
+                break;
             case Connection.STATE_DIALING:
-                setDialing();
+                setConferenceOnDialing();
                 break;
             case Connection.STATE_DISCONNECTED:
                 DisconnectCause disconnectCause;
@@ -1198,10 +1416,10 @@
                 destroyTelephonyConference();
                 break;
             case Connection.STATE_ACTIVE:
-                setActive();
+                setConferenceOnActive();
                 break;
             case Connection.STATE_HOLDING:
-                setOnHold();
+                setConferenceOnHold();
                 break;
         }
     }
@@ -1258,51 +1476,6 @@
         return sb.toString();
     }
 
-    private boolean canHoldImsCalls() {
-        PersistableBundle b = getCarrierConfig();
-        // Return true if the CarrierConfig is unavailable
-        return b == null || b.getBoolean(CarrierConfigManager.KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL);
-    }
-
-    private PersistableBundle getCarrierConfig() {
-        if (mConferenceHost == null) {
-            return null;
-        }
-
-        Phone phone = mConferenceHost.getPhone();
-        if (phone == null) {
-            return null;
-        }
-        return PhoneGlobals.getInstance().getCarrierConfigForSubId(phone.getSubId());
-    }
-
-    /**
-     * @return {@code true} if the carrier associated with the conference requires that the maximum
-     *      size of the conference is enforced, {@code false} otherwise.
-     */
-    public boolean isMaximumConferenceSizeEnforced() {
-        PersistableBundle b = getCarrierConfig();
-        // Return false if the CarrierConfig is unavailable
-        return b != null && b.getBoolean(
-                CarrierConfigManager.KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL);
-    }
-
-    /**
-     * @return The maximum size of a conference call where
-     * {@link #isMaximumConferenceSizeEnforced()} is true.
-     */
-    public int getMaximumConferenceSize() {
-        PersistableBundle b = getCarrierConfig();
-
-        // If there is no carrier config its really a problem, but we'll still define a sane limit
-        // of 5 so that we can still make a conference.
-        if (b == null) {
-            Log.w(this, "getMaximumConferenceSize - failed to get conference size");
-            return 5;
-        }
-        return b.getInt(CarrierConfigManager.KEY_IMS_CONFERENCE_SIZE_LIMIT_INT);
-    }
-
     /**
      * @return The number of participants in the conference.
      */
@@ -1315,8 +1488,8 @@
      *      participants in the conference has reached the limit, {@code false} otherwise.
      */
     public boolean isFullConference() {
-        return isMaximumConferenceSizeEnforced()
-                && getNumberOfParticipants() >= getMaximumConferenceSize();
+        return mCarrierConfig.isMaximumConferenceSizeEnforced()
+                && getNumberOfParticipants() >= mCarrierConfig.getMaximumConferenceSize();
     }
 
     /**
diff --git a/src/com/android/services/telephony/ImsConferenceController.java b/src/com/android/services/telephony/ImsConferenceController.java
index 1e10055..6d3d4c2 100644
--- a/src/com/android/services/telephony/ImsConferenceController.java
+++ b/src/com/android/services/telephony/ImsConferenceController.java
@@ -16,13 +16,17 @@
 
 package com.android.services.telephony;
 
+import android.content.Context;
+import android.os.PersistableBundle;
 import android.telecom.Conference;
 import android.telecom.Conferenceable;
 import android.telecom.Connection;
 import android.telecom.ConnectionService;
 import android.telecom.DisconnectCause;
 import android.telecom.PhoneAccountHandle;
-import android.telephony.Rlog;
+import android.telephony.CarrierConfigManager;
+
+import com.android.telephony.Rlog;
 
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
@@ -52,8 +56,19 @@
                 Log.v(ImsConferenceController.class, "onDestroyed: %s", conference);
             }
 
+            if (conference instanceof ImsConference) {
+                // Ims Conference call ended, so UE may now have the ability to initiate
+                // an Adhoc Conference call. Hence, try enabling adhoc conference capability
+                mTelecomAccountRegistry.refreshAdhocConference(true);
+            }
             mImsConferences.remove(conference);
         }
+
+        @Override
+        public void onStateChanged(Conference conference, int oldState, int newState) {
+            Log.v(this, "onStateChanged: Conference = " + conference);
+            recalculateConferenceable();
+        }
     };
 
     private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
@@ -101,10 +116,11 @@
     private final ArrayList<TelephonyConnection> mTelephonyConnections = new ArrayList<>();
 
     /**
-     * List of known {@link ImsConference}s.  Realistically there will only ever be a single
-     * concurrent IMS conference.
+     * List of known {@link ImsConference}s. There can be upto maximum two Ims conference calls.
+     * One conference call can be a host conference call and another conference call formed as a
+     * result of accepting incoming conference call.
      */
-    private final ArrayList<ImsConference> mImsConferences = new ArrayList<>(1);
+    private final ArrayList<ImsConference> mImsConferences = new ArrayList<>(2);
 
     private TelecomAccountRegistry mTelecomAccountRegistry;
 
@@ -122,6 +138,17 @@
         mFeatureFlagProxy = featureFlagProxy;
     }
 
+    void addConference(ImsConference conference) {
+        if (mImsConferences.contains(conference)) {
+            // Adding a duplicate realistically shouldn't happen.
+            Log.w(this, "addConference - conference already tracked; conference=%s", conference);
+            return;
+        }
+        mImsConferences.add(conference);
+        conference.addTelephonyConferenceListener(mConferenceListener);
+        recalculateConferenceable();
+    }
+
     /**
      * Adds a new connection to the IMS conference controller.
      *
@@ -250,6 +277,11 @@
                 continue;
             }
 
+            // Since UE cannot host two conference calls, remove the ability to initiate
+            // another conference call as there already exists a conference call, which
+            // is hosted on this device.
+            mTelecomAccountRegistry.refreshAdhocConference(false);
+
             switch (conference.getState()) {
                 case Connection.STATE_ACTIVE:
                     //fall through
@@ -360,6 +392,10 @@
             Log.v(this, "Start new ImsConference - connection: %s", connection);
         }
 
+        if (connection.isAdhocConferenceCall()) {
+            Log.w(this, "start new ImsConference - control should never come here");
+            return;
+        }
         // Make a clone of the connection which will become the Ims conference host connection.
         // This is necessary since the Connection Service does not support removing a connection
         // from Telecom.  Instead we create a new instance and remove the old one from telecom.
@@ -371,6 +407,7 @@
         PhoneAccountHandle phoneAccountHandle = null;
 
         // Attempt to determine the phone account associated with the conference host connection.
+        ImsConference.CarrierConfiguration carrierConfig = null;
         if (connection.getPhone() != null &&
                 connection.getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
             Phone imsPhone = connection.getPhone();
@@ -378,11 +415,13 @@
             // base GSM or CDMA phone, not on the ImsPhone itself).
             phoneAccountHandle =
                     PhoneUtils.makePstnPhoneAccountHandle(imsPhone.getDefaultPhone());
+            carrierConfig = getCarrierConfig(imsPhone);
         }
 
         ImsConference conference = new ImsConference(mTelecomAccountRegistry, mConnectionService,
-                conferenceHostConnection, phoneAccountHandle, mFeatureFlagProxy);
+                conferenceHostConnection, phoneAccountHandle, mFeatureFlagProxy, carrierConfig);
         conference.setState(conferenceHostConnection.getState());
+        conference.setCallDirection(conferenceHostConnection.getCallDirection());
         conference.addTelephonyConferenceListener(mConferenceListener);
         conference.updateConferenceParticipantsAfterCreation();
         mConnectionService.addConference(conference);
@@ -401,4 +440,33 @@
         // conferenceable connections for the conference to show merge calls option.
         recalculateConferenceable();
     }
+
+    public static ImsConference.CarrierConfiguration getCarrierConfig(Phone phone) {
+        ImsConference.CarrierConfiguration.Builder config =
+                new ImsConference.CarrierConfiguration.Builder();
+        if (phone == null) {
+            return config.build();
+        }
+
+        CarrierConfigManager cfgManager = (CarrierConfigManager)
+                phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        if (cfgManager != null) {
+            PersistableBundle bundle = cfgManager.getConfigForSubId(phone.getSubId());
+            boolean isMaximumConferenceSizeEnforced = bundle.getBoolean(
+                    CarrierConfigManager.KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL);
+            int maximumConferenceSize = bundle.getInt(
+                    CarrierConfigManager.KEY_IMS_CONFERENCE_SIZE_LIMIT_INT);
+            boolean isHoldAllowed = bundle.getBoolean(
+                    CarrierConfigManager.KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL);
+            boolean shouldLocalDisconnectOnEmptyConference = bundle.getBoolean(
+                    CarrierConfigManager.KEY_LOCAL_DISCONNECT_EMPTY_IMS_CONFERENCE_BOOL);
+
+            config.setIsMaximumConferenceSizeEnforced(isMaximumConferenceSizeEnforced)
+                    .setMaximumConferenceSize(maximumConferenceSize)
+                    .setIsHoldAllowed(isHoldAllowed)
+                    .setShouldLocalDisconnectEmptyConference(
+                            shouldLocalDisconnectOnEmptyConference);
+        }
+        return config.build();
+    }
 }
diff --git a/src/com/android/services/telephony/MmiCodeUtil.java b/src/com/android/services/telephony/MmiCodeUtil.java
new file mode 100644
index 0000000..d208ec3
--- /dev/null
+++ b/src/com/android/services/telephony/MmiCodeUtil.java
@@ -0,0 +1,147 @@
+/*
+ * 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 java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class MmiCodeUtil {
+    //***** Constants
+
+    // Supp Service codes from TS 22.030 Annex B
+
+    //Called line presentation
+    static final String SC_CLIP    = "30";
+    static final String SC_CLIR    = "31";
+
+    // Call Forwarding
+    static final String SC_CFU     = "21";
+    static final String SC_CFB     = "67";
+    static final String SC_CFNRy   = "61";
+    static final String SC_CFNR    = "62";
+
+    static final String SC_CF_All = "002";
+    static final String SC_CF_All_Conditional = "004";
+
+    // Call Waiting
+    static final String SC_WAIT     = "43";
+
+    // Call Barring
+    static final String SC_BAOC         = "33";
+    static final String SC_BAOIC        = "331";
+    static final String SC_BAOICxH      = "332";
+    static final String SC_BAIC         = "35";
+    static final String SC_BAICr        = "351";
+
+    static final String SC_BA_ALL       = "330";
+    static final String SC_BA_MO        = "333";
+    static final String SC_BA_MT        = "353";
+
+    // Supp Service Password registration
+    static final String SC_PWD          = "03";
+
+    // PIN/PIN2/PUK/PUK2
+    static final String SC_PIN          = "04";
+    static final String SC_PIN2         = "042";
+    static final String SC_PUK          = "05";
+    static final String SC_PUK2         = "052";
+
+    // See TS 22.030 6.5.2 "Structure of the MMI"
+
+    static Pattern sPatternSuppService = Pattern.compile(
+        "((\\*|#|\\*#|\\*\\*|##)(\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?#)(.*)");
+/*       1  2                    3          4  5       6   7         8    9     10  11             12
+
+         1 = Full string up to and including #
+         2 = action (activation/interrogation/registration/erasure)
+         3 = service code
+         5 = SIA
+         7 = SIB
+         9 = SIC
+         10 = dialing number
+*/
+
+    static final int MATCH_GROUP_SERVICE_CODE = 3;
+
+    public static final String BUTTON_CLIR_KEY  = "button_clir_key";
+    public static final String BUTTON_CW_KEY    = "button_cw_key";
+    public static final String CALL_FORWARDING_KEY = "call_forwarding_key";
+    public static final String CALL_BARRING_KEY = "call_barring_key";
+
+    //***** Public Class methods
+    public static String getMmiServiceCode(String dialString) {
+        Matcher m;
+        String ret = null;
+
+        m = sPatternSuppService.matcher(dialString);
+
+        if (m.matches()) {
+            ret = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE));
+        }
+
+        return ret;
+    }
+
+    private static String makeEmptyNull(String s) {
+        if (s != null && s.length() == 0) return null;
+
+        return s;
+    }
+
+    static boolean isServiceCodeCallForwarding(String sc) {
+        return sc != null &&
+                (sc.equals(SC_CFU)
+                || sc.equals(SC_CFB) || sc.equals(SC_CFNRy)
+                || sc.equals(SC_CFNR) || sc.equals(SC_CF_All)
+                || sc.equals(SC_CF_All_Conditional));
+    }
+
+    static boolean isServiceCodeCallBarring(String sc) {
+        return sc != null &&
+            (sc.equals(SC_BAOC)
+             || sc.equals(SC_BAOIC) || sc.equals(SC_BAOICxH)
+             || sc.equals(SC_BAIC) || sc.equals(SC_BAICr)
+             || sc.equals(SC_BA_ALL) || sc.equals(SC_BA_MO)
+             || sc.equals(SC_BA_MT));
+    }
+
+    static boolean isPinPukCommand(String sc) {
+        return sc != null && (sc.equals(SC_PIN) || sc.equals(SC_PIN2)
+                              || sc.equals(SC_PUK) || sc.equals(SC_PUK2));
+     }
+
+    public static String getSuppServiceKey(String dialString) {
+        String sc = getMmiServiceCode(dialString);
+        if (sc != null && sc.equals(SC_CLIP)) {
+            return "";
+        } else if (sc != null && sc.equals(SC_CLIR)) {
+            return BUTTON_CLIR_KEY;
+        } else if (isServiceCodeCallForwarding(sc)) {
+            return CALL_FORWARDING_KEY;
+        } else if (isServiceCodeCallBarring(sc)) {
+            return CALL_BARRING_KEY;
+        } else if (sc != null && sc.equals(SC_PWD)) {
+            return "";
+        } else if (sc != null && sc.equals(SC_WAIT)) {
+            return BUTTON_CW_KEY;
+        } else if (isPinPukCommand(sc)) {
+            return "";
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/src/com/android/services/telephony/PstnIncomingCallNotifier.java b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
index 2dfeaed..d2dfab5 100644
--- a/src/com/android/services/telephony/PstnIncomingCallNotifier.java
+++ b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
@@ -25,7 +25,7 @@
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
-import android.telephony.Rlog;
+import com.android.telephony.Rlog;
 import android.text.TextUtils;
 
 import com.android.internal.telephony.Call;
@@ -288,7 +288,11 @@
             }
         } else {
             TelecomManager tm = mPhone.getContext().getSystemService(TelecomManager.class);
-            tm.addNewIncomingCall(handle, extras);
+            if (connection.isMultiparty()) {
+                tm.addNewIncomingConference(handle, extras);
+            } else {
+                tm.addNewIncomingCall(handle, extras);
+            }
         }
     }
 
diff --git a/src/com/android/services/telephony/RadioOnHelper.java b/src/com/android/services/telephony/RadioOnHelper.java
index 116c61d..25ac220 100644
--- a/src/com/android/services/telephony/RadioOnHelper.java
+++ b/src/com/android/services/telephony/RadioOnHelper.java
@@ -77,7 +77,8 @@
      * RadioOnHelper's handler (thus ensuring that the rest of the sequence is entirely
      * serialized, and runs on the main looper.)
      */
-    public void triggerRadioOnAndListen(RadioOnStateListener.Callback callback) {
+    public void triggerRadioOnAndListen(RadioOnStateListener.Callback callback,
+            boolean forEmergencyCall, Phone phoneForEmergencyCall) {
         setupListeners();
         mCallback = callback;
         mInProgressListeners.clear();
@@ -89,16 +90,17 @@
             }
 
             mInProgressListeners.add(mListeners.get(i));
-            mListeners.get(i).waitForRadioOn(phone, this);
+            mListeners.get(i).waitForRadioOn(phone, this, forEmergencyCall,
+                    forEmergencyCall && phone == phoneForEmergencyCall);
         }
 
-        powerOnRadio();
+        powerOnRadio(forEmergencyCall, phoneForEmergencyCall);
     }
     /**
      * Attempt to power on the radio (i.e. take the device out of airplane mode). We'll eventually
      * get an onServiceStateChanged() callback when the radio successfully comes up.
      */
-    private void powerOnRadio() {
+    private void powerOnRadio(boolean forEmergencyCall, Phone phoneForEmergencyCall) {
 
         // If airplane mode is on, we turn it off the same way that the Settings activity turns it
         // off.
@@ -112,7 +114,7 @@
 
             for (Phone phone : PhoneFactory.getPhones()) {
                 Log.d(this, "powerOnRadio, enabling Radio");
-                phone.setRadioPower(true);
+                phone.setRadioPower(true, forEmergencyCall, phone == phoneForEmergencyCall, false);
             }
 
             // Post the broadcast intend for change in airplane mode
diff --git a/src/com/android/services/telephony/RadioOnStateListener.java b/src/com/android/services/telephony/RadioOnStateListener.java
index 43269b4..93e1e3c 100644
--- a/src/com/android/services/telephony/RadioOnStateListener.java
+++ b/src/com/android/services/telephony/RadioOnStateListener.java
@@ -70,7 +70,10 @@
                         Phone phone = (Phone) args.arg1;
                         RadioOnStateListener.Callback callback =
                                 (RadioOnStateListener.Callback) args.arg2;
-                        startSequenceInternal(phone, callback);
+                        boolean forEmergencyCall = (boolean) args.arg3;
+                        boolean isSelectedPhoneForEmergencyCall = (boolean) args.arg4;
+                        startSequenceInternal(phone, callback, forEmergencyCall,
+                                isSelectedPhoneForEmergencyCall);
                     } finally {
                         args.recycle();
                     }
@@ -97,6 +100,10 @@
 
     private Callback mCallback;  // The callback to notify upon completion.
     private Phone mPhone;  // The phone that will attempt to place the call.
+    private boolean mForEmergencyCall; // Whether radio is being turned on for emergency call.
+    // Whether this phone is selected to place emergency call. Can be true only if
+    // mForEmergencyCall is true.
+    private boolean mSelectedPhoneForEmergencyCall;
     private int mNumRetriesSoFar;
 
     /**
@@ -113,7 +120,8 @@
      * RadioOnStateListener's handler (thus ensuring that the rest of the sequence is entirely
      * serialized, and runs only on the handler thread.)
      */
-    public void waitForRadioOn(Phone phone, Callback callback) {
+    public void waitForRadioOn(Phone phone, Callback callback,
+            boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall) {
         Log.d(this, "waitForRadioOn: Phone " + phone.getPhoneId());
 
         if (mPhone != null) {
@@ -124,6 +132,8 @@
         SomeArgs args = SomeArgs.obtain();
         args.arg1 = phone;
         args.arg2 = callback;
+        args.arg3 = forEmergencyCall;
+        args.arg4 = isSelectedPhoneForEmergencyCall;
         mHandler.obtainMessage(MSG_START_SEQUENCE, args).sendToTarget();
     }
 
@@ -132,7 +142,8 @@
      *
      * @see #waitForRadioOn
      */
-    private void startSequenceInternal(Phone phone, Callback callback) {
+    private void startSequenceInternal(Phone phone, Callback callback,
+            boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall) {
         Log.d(this, "startSequenceInternal: Phone " + phone.getPhoneId());
 
         // First of all, clean up any state left over from a prior RadioOn call sequence. This
@@ -142,6 +153,8 @@
 
         mPhone = phone;
         mCallback = callback;
+        mForEmergencyCall = forEmergencyCall;
+        mSelectedPhoneForEmergencyCall = isSelectedPhoneForEmergencyCall;
 
         registerForServiceStateChanged();
         // Register for RADIO_OFF to handle cases where emergency call is dialed before
@@ -159,6 +172,7 @@
      * call with {@link Callback#isOkToCall}
      */
     private void onServiceStateChanged(ServiceState state) {
+        if (mPhone == null) return;
         Log.d(this, "onServiceStateChanged(), new state = %s, Phone = %s", state,
                 mPhone.getPhoneId());
 
@@ -182,6 +196,7 @@
     }
 
     private void onRadioOn() {
+        if (mPhone == null) return;
         ServiceState state =  mPhone.getServiceState();
         Log.d(this, "onRadioOn, state = %s, Phone = %s", state,
                 mPhone.getPhoneId());
@@ -203,6 +218,7 @@
      * Handles the retry timer expiring.
      */
     private void onRetryTimeout() {
+        if (mPhone == null) return;
         int serviceState = mPhone.getServiceState().getState();
         Log.d(this, "onRetryTimeout():  phone state = %s, service state = %d, retries = %d.",
                 mPhone.getState(), serviceState, mNumRetriesSoFar);
@@ -231,7 +247,8 @@
                 cleanup();
             } else {
                 Log.d(this, "Trying (again) to turn on the radio.");
-                mPhone.setRadioPower(true);
+                mPhone.setRadioPower(true, mForEmergencyCall, mSelectedPhoneForEmergencyCall,
+                        false);
                 startRetryTimer();
             }
         }
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 5a1e6a6..c38923f 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -32,16 +32,17 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.PersistableBundle;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.provider.Telephony;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneStateListener;
-import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
@@ -49,22 +50,29 @@
 import android.telephony.TelephonyManager;
 import android.telephony.ims.ImsException;
 import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.ImsRcsManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.RegistrationManager;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.text.TextUtils;
 
 import com.android.ims.ImsManager;
+import com.android.internal.telephony.ExponentialBackoff;
+import com.android.internal.telephony.LocaleTracker;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.SubscriptionController;
 import com.android.phone.PhoneGlobals;
 import com.android.phone.PhoneUtils;
 import com.android.phone.R;
+import com.android.telephony.Rlog;
 
 import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Optional;
+import java.util.function.Predicate;
 
 /**
  * Owns all data we have registered with Telecom including handling dynamic addition and
@@ -79,6 +87,26 @@
     private final static int DEFAULT_SIM_ICON =  R.drawable.ic_multi_sim;
     private final static String GROUP_PREFIX = "group_";
 
+    private static final int REGISTER_START_DELAY_MS = 1 * 1000; // 1 second
+    private static final int REGISTER_MAXIMUM_DELAY_MS = 60 * 1000; // 1 minute
+
+    /**
+     * Indicates the {@link SubscriptionManager.OnSubscriptionsChangedListener} has not yet been
+     * registered.
+     */
+    private static final int LISTENER_STATE_UNREGISTERED = 0;
+
+    /**
+     * Indicates the first {@link SubscriptionManager.OnSubscriptionsChangedListener} registration
+     * attempt failed and we are performing backoff registration.
+     */
+    private static final int LISTENER_STATE_PERFORMING_BACKOFF = 2;
+
+    /**
+     * Indicates the {@link SubscriptionManager.OnSubscriptionsChangedListener} has been registered.
+     */
+    private static final int LISTENER_STATE_REGISTERED = 3;
+
     final class AccountEntry implements PstnPhoneCapabilitiesNotifier.Listener {
         private final Phone mPhone;
         private PhoneAccount mAccount;
@@ -86,9 +114,11 @@
         private final PstnPhoneCapabilitiesNotifier mPhoneCapabilitiesNotifier;
         private boolean mIsEmergency;
         private boolean mIsRttCapable;
+        private boolean mIsAdhocConfCapable;
         private boolean mIsEmergencyPreferred;
         private MmTelFeature.MmTelCapabilities mMmTelCapabilities;
         private ImsMmTelManager.CapabilityCallback mMmtelCapabilityCallback;
+        private RegistrationManager.RegistrationCallback mImsRegistrationCallback;
         private ImsMmTelManager mMmTelManager;
         private final boolean mIsDummy;
         private boolean mIsVideoCapable;
@@ -106,6 +136,7 @@
             mPhone = phone;
             mIsEmergency = isEmergency;
             mIsDummy = isDummy;
+            mIsAdhocConfCapable = mPhone.isImsRegistered();
             mAccount = registerPstnPhoneAccount(isEmergency, isDummy);
             Log.i(this, "Registered phoneAccount: %s with handle: %s",
                     mAccount, mAccount.getAccountHandle());
@@ -138,15 +169,38 @@
                     updateRttCapability();
                 }
             };
-
             registerMmTelCapabilityCallback();
+
+            mImsRegistrationCallback = new RegistrationManager.RegistrationCallback() {
+                @Override
+                public void onRegistered(int imsRadioTech) {
+                    updateAdhocConfCapability(true);
+                }
+
+                @Override
+                public void onRegistering(int imsRadioTech) {
+                    updateAdhocConfCapability(false);
+                }
+
+                @Override
+                public void onUnregistered(ImsReasonInfo imsReasonInfo) {
+                    updateAdhocConfCapability(false);
+                }
+            };
+            registerImsRegistrationCallback();
         }
 
         void teardown() {
             mIncomingCallNotifier.teardown();
             mPhoneCapabilitiesNotifier.teardown();
-            if (mMmTelManager != null && mMmtelCapabilityCallback != null) {
-                mMmTelManager.unregisterMmTelCapabilityCallback(mMmtelCapabilityCallback);
+            if (mMmTelManager != null) {
+                if (mMmtelCapabilityCallback != null) {
+                    mMmTelManager.unregisterMmTelCapabilityCallback(mMmtelCapabilityCallback);
+                }
+
+                if (mImsRegistrationCallback != null) {
+                    mMmTelManager.unregisterImsRegistrationCallback(mImsRegistrationCallback);
+                }
             }
         }
 
@@ -171,6 +225,25 @@
             }
         }
 
+        private void registerImsRegistrationCallback() {
+            if (mMmTelManager == null || mImsRegistrationCallback == null) {
+                return;
+            }
+
+            try {
+                mMmTelManager.registerImsRegistrationCallback(mContext.getMainExecutor(),
+                        mImsRegistrationCallback);
+            } catch (ImsException e) {
+                Log.w(this, "registerImsRegistrationCallback: registration failed, no ImsService"
+                        + " available. Exception: " + e.getMessage());
+                return;
+            } catch (IllegalArgumentException e) {
+                Log.w(this, "registerImsRegistrationCallback: registration failed, invalid"
+                        + " subscription, Exception" + e.getMessage());
+                return;
+            }
+        }
+
         /**
          * Trigger re-registration of this account.
          */
@@ -226,6 +299,7 @@
             // slotId from the subId or the phoneId in all instances.
             SubscriptionInfo record =
                     mSubscriptionManager.getActiveSubscriptionInfo(subId);
+            TelephonyManager tm = mTelephonyManager.createForSubscriptionId(subId);
 
             if (isEmergency) {
                 label = mContext.getResources().getString(R.string.sim_label_emergency_calls);
@@ -234,7 +308,7 @@
             } else if (mTelephonyManager.getPhoneCount() == 1) {
                 // For single-SIM devices, we show the label and description as whatever the name of
                 // the network is.
-                description = label = mTelephonyManager.getNetworkOperatorName();
+                description = label = tm.getNetworkOperatorName();
             } else {
                 CharSequence subDisplayName = null;
 
@@ -321,6 +395,12 @@
                 extras.putAll(getPhoneAccountExtras());
             }
 
+            if (mIsAdhocConfCapable && isCarrierAdhocConferenceCallSupported()) {
+                capabilities |= PhoneAccount.CAPABILITY_ADHOC_CONFERENCE_CALLING;
+            } else {
+                capabilities &= ~PhoneAccount.CAPABILITY_ADHOC_CONFERENCE_CALLING;
+            }
+
             final boolean isHandoverFromSupported = mContext.getResources().getBoolean(
                     R.bool.config_support_handover_from);
             if (isHandoverFromSupported && !isEmergency) {
@@ -486,16 +566,31 @@
         }
 
         /**
-         * Determines from carrier configuration whether RCS presence indication for video calls is
-         * supported.
+         * Determines from carrier configuration and user setting whether RCS presence indication
+         * for video calls is supported.
          *
          * @return {@code true} if RCS presence indication for video calls is supported.
          */
         private boolean isCarrierVideoPresenceSupported() {
             PersistableBundle b =
                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
-            return b != null &&
-                    b.getBoolean(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL);
+            boolean carrierConfigEnabled = b != null
+                    && b.getBoolean(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL);
+            return carrierConfigEnabled && isUserContactDiscoverySettingEnabled();
+        }
+
+        /**
+         * @return true if the user has enabled contact discovery for the subscription associated
+         * with this account entry, false otherwise.
+         */
+        private boolean isUserContactDiscoverySettingEnabled() {
+            try {
+                ImsRcsManager manager = mImsManager.getImsRcsManager(mPhone.getSubId());
+                return manager.getUceAdapter().isUceSettingEnabled();
+            } catch (Exception e) {
+                Log.w(LOG_TAG, "isUserContactDiscoverySettingEnabled caught exception: " + e);
+                return false;
+            }
         }
 
         /**
@@ -511,6 +606,19 @@
         }
 
         /**
+         * Determines from carrier config whether adhoc conference calling is supported.
+         *
+         * @return {@code true} if adhoc conference calling is supported, {@code false} otherwise.
+         */
+        private boolean isCarrierAdhocConferenceCallSupported() {
+            PersistableBundle b =
+                    PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
+            return b != null &&
+                    b.getBoolean(CarrierConfigManager.KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL);
+        }
+
+
+        /**
          * Determines from carrier config whether merging calls is supported.
          *
          * @return {@code true} if merging calls is supported, {@code false} otherwise.
@@ -664,6 +772,45 @@
             }
         }
 
+        public void updateAdhocConfCapability(boolean isAdhocConfCapable) {
+            synchronized (mAccountsLock) {
+                if (!mAccounts.contains(this)) {
+                    // Account has already been torn down, don't try to register it again.
+                    // This handles the case where teardown has already happened, and we got a Ims
+                    // registartion update that lost the race for the mAccountsLock.  In such a
+                    // scenario by the time we get here, the original phone account could have been
+                    // torn down.
+                    return;
+                }
+
+                if (isAdhocConfCapable !=  mIsAdhocConfCapable) {
+                    Log.i(this, "updateAdhocConfCapability - changed, new value: "
+                            + isAdhocConfCapable);
+                    mIsAdhocConfCapable = isAdhocConfCapable;
+                    mAccount = registerPstnPhoneAccount(mIsEmergency, mIsDummy);
+                }
+            }
+        }
+
+        public void updateVideoPresenceCapability() {
+            synchronized (mAccountsLock) {
+                if (!mAccounts.contains(this)) {
+                    // Account has already been torn down, don't try to register it again.
+                    // This handles the case where teardown has already happened, and we got a Ims
+                    // registration update that lost the race for the mAccountsLock.  In such a
+                    // scenario by the time we get here, the original phone account could have been
+                    // torn down.
+                    return;
+                }
+                boolean isVideoPresenceSupported = isCarrierVideoPresenceSupported();
+                if (mIsVideoPresenceSupported != isVideoPresenceSupported) {
+                    Log.i(this, "updateVideoPresenceCapability for subId=" + mPhone.getSubId()
+                            + ", new value= " + isVideoPresenceSupported);
+                    mAccount = registerPstnPhoneAccount(mIsEmergency, mIsDummy);
+                }
+            }
+        }
+
         public void updateRttCapability() {
             boolean isRttEnabled = isRttCurrentlySupported();
             if (isRttEnabled != mIsRttCapable) {
@@ -686,14 +833,51 @@
          * device.
          */
         private boolean isRttCurrentlySupported() {
+            // First check the emergency case -- if it's supported and turned on,
+            // we want to present RTT as available on the emergency-only phone account
+            if (mIsEmergency) {
+                // First check whether the device supports it
+                boolean devicesSupportsRtt =
+                        mContext.getResources().getBoolean(R.bool.config_support_rtt);
+                boolean deviceSupportsEmergencyRtt = mContext.getResources().getBoolean(
+                        R.bool.config_support_simless_emergency_rtt);
+                if (!(deviceSupportsEmergencyRtt && devicesSupportsRtt)) {
+                    Log.i(this, "isRttCurrentlySupported -- emergency acct and no device support");
+                    return false;
+                }
+                // Next check whether we're in or near a country that supports it
+                String country =
+                        mPhone.getServiceStateTracker().getLocaleTracker()
+                                .getCurrentCountry().toLowerCase();
+                String[] supportedCountries = mContext.getResources().getStringArray(
+                        R.array.config_simless_emergency_rtt_supported_countries);
+                if (supportedCountries == null || Arrays.stream(supportedCountries).noneMatch(
+                        Predicate.isEqual(country))) {
+                    Log.i(this, "isRttCurrentlySupported -- emergency acct and"
+                            + " not supported in this country: " + country);
+                    return false;
+                }
+                
+                return true;
+            }
+
             boolean hasVoiceAvailability = isImsVoiceAvailable();
 
             boolean isRttSupported = PhoneGlobals.getInstance().phoneMgr
                     .isRttEnabled(mPhone.getSubId());
 
             boolean isRoaming = mTelephonyManager.isNetworkRoaming(mPhone.getSubId());
+            boolean isOnWfc = mPhone.getImsRegistrationTech()
+                    == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
 
-            return hasVoiceAvailability && isRttSupported && !isRoaming;
+            boolean shouldDisableBecauseRoamingOffWfc = isRoaming && !isOnWfc;
+            Log.i(this, "isRttCurrentlySupported -- regular acct,"
+                    + " hasVoiceAvailability: " + hasVoiceAvailability + "\n"
+                    + " isRttSupported: " + isRttSupported + "\n"
+                    + " isRoaming: " + isRoaming + "\n"
+                    + " isOnWfc: " + isOnWfc + "\n");
+
+            return hasVoiceAvailability && isRttSupported && !shouldDisableBecauseRoamingOffWfc;
         }
 
         /**
@@ -790,18 +974,46 @@
             new OnSubscriptionsChangedListener() {
         @Override
         public void onSubscriptionsChanged() {
-            // Any time the SubscriptionInfo changes...rerun the setup
-            Log.i(this, "onSubscriptionsChanged - update accounts");
+            if (mSubscriptionListenerState != LISTENER_STATE_REGISTERED) {
+                mRegisterSubscriptionListenerBackoff.stop();
+                mHandlerThread.quitSafely();
+            }
+            mSubscriptionListenerState = LISTENER_STATE_REGISTERED;
+
+            // Any time the SubscriptionInfo changes rerun the setup
+            Log.i(this, "TelecomAccountRegistry: onSubscriptionsChanged - update accounts");
             tearDownAccounts();
             setupAccounts();
         }
+
+        @Override
+        public void onAddListenerFailed() {
+            // Woe!  Failed to add the listener!
+            Log.w(this, "TelecomAccountRegistry: onAddListenerFailed - failed to register "
+                    + "OnSubscriptionsChangedListener");
+
+            // Even though registering the listener failed, we will still try to setup the phone
+            // accounts now; the phone instances should already be present and ready, so even if
+            // telephony registry is poking along we can still try to setup the phone account.
+            tearDownAccounts();
+            setupAccounts();
+
+            if (mSubscriptionListenerState == LISTENER_STATE_UNREGISTERED) {
+                // Initial registration attempt failed; start exponential backoff.
+                mSubscriptionListenerState = LISTENER_STATE_PERFORMING_BACKOFF;
+                mRegisterSubscriptionListenerBackoff.start();
+            } else {
+                // We're already doing exponential backoff and a registration failed.
+                mRegisterSubscriptionListenerBackoff.notifyFailed();
+            }
+        }
     };
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
-                Log.i(this, "User changed, re-registering phone accounts.");
+                Log.i(this, "TelecomAccountRegistry: User changed, re-registering phone accounts.");
 
                 int userHandleId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
                 UserHandle currentUserHandle = UserHandle.of(userHandleId);
@@ -860,23 +1072,47 @@
     private static TelecomAccountRegistry sInstance;
     private final Context mContext;
     private final TelecomManager mTelecomManager;
+    private final android.telephony.ims.ImsManager mImsManager;
     private final TelephonyManager mTelephonyManager;
     private final SubscriptionManager mSubscriptionManager;
     private List<AccountEntry> mAccounts = new LinkedList<AccountEntry>();
     private final Object mAccountsLock = new Object();
+    private int mSubscriptionListenerState = LISTENER_STATE_UNREGISTERED;
     private int mServiceState = ServiceState.STATE_POWER_OFF;
     private int mActiveDataSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private boolean mIsPrimaryUser = true;
+    private ExponentialBackoff mRegisterSubscriptionListenerBackoff;
+    private final HandlerThread mHandlerThread = new HandlerThread("TelecomAccountRegistry");
 
     // TODO: Remove back-pointer from app singleton to Service, since this is not a preferred
     // pattern; redesign. This was added to fix a late release bug.
     private TelephonyConnectionService mTelephonyConnectionService;
 
+    // Used to register subscription changed listener when initial attempts fail.
+    private Runnable mRegisterOnSubscriptionsChangedListenerRunnable = new Runnable() {
+        @Override
+        public void run() {
+            if (mSubscriptionListenerState != LISTENER_STATE_REGISTERED) {
+                Log.i(this, "TelecomAccountRegistry: performing delayed register.");
+                SubscriptionManager.from(mContext).addOnSubscriptionsChangedListener(
+                        mOnSubscriptionsChangedListener);
+            }
+        }
+    };
+
     TelecomAccountRegistry(Context context) {
         mContext = context;
         mTelecomManager = context.getSystemService(TelecomManager.class);
+        mImsManager = context.getSystemService(android.telephony.ims.ImsManager.class);
         mTelephonyManager = TelephonyManager.from(context);
         mSubscriptionManager = SubscriptionManager.from(context);
+        mHandlerThread.start();
+        mRegisterSubscriptionListenerBackoff = new ExponentialBackoff(
+                REGISTER_START_DELAY_MS,
+                REGISTER_MAXIMUM_DELAY_MS,
+                2, /* multiplier */
+                mHandlerThread.getLooper(),
+                mRegisterOnSubscriptionsChangedListenerRunnable);
     }
 
     /**
@@ -1048,6 +1284,21 @@
         return null;
     }
 
+    public void refreshAdhocConference(boolean isEnableAdhocConf) {
+        synchronized (mAccountsLock) {
+            Log.v(this, "refreshAdhocConference isEnable = " + isEnableAdhocConf);
+            for (AccountEntry entry : mAccounts) {
+                boolean hasAdhocConfCapability = entry.mAccount.hasCapabilities(
+                        PhoneAccount.CAPABILITY_ADHOC_CONFERENCE_CALLING);
+                if (!isEnableAdhocConf && hasAdhocConfCapability) {
+                    entry.updateAdhocConfCapability(isEnableAdhocConf);
+                } else if (isEnableAdhocConf && !hasAdhocConfCapability) {
+                    entry.updateAdhocConfCapability(entry.mPhone.isImsRegistered());
+                }
+            }
+        }
+    }
+
     /**
      * Returns whethere a the subscription associated with a {@link PhoneAccountHandle} is using a
      * sim call manager.
@@ -1077,6 +1328,7 @@
 
         // Register for SubscriptionInfo list changes which is guaranteed
         // to invoke onSubscriptionsChanged the first time.
+        Log.i(this, "TelecomAccountRegistry: setupOnBoot - register subscription listener");
         SubscriptionManager.from(mContext).addOnSubscriptionsChangedListener(
                 mOnSubscriptionsChangedListener);
 
@@ -1094,9 +1346,14 @@
 
         //We also need to listen for locale changes
         //(e.g. system language changed -> SIM card name changed)
-        mContext.registerReceiver(mLocaleChangeReceiver,
-                new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
+        IntentFilter localeChangeFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
+        localeChangeFilter.addAction(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED);
+        mContext.registerReceiver(mLocaleChangeReceiver, localeChangeFilter);
 
+        registerContentObservers();
+    }
+
+    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())) {
@@ -1113,6 +1370,23 @@
         Uri rttSettingUri = Settings.Secure.getUriFor(Settings.Secure.RTT_CALLING_MODE);
         mContext.getContentResolver().registerContentObserver(
                 rttSettingUri, false, rttUiSettingObserver);
+
+        // Listen to the changes to the user's Contacts Discovery Setting.
+        ContentObserver contactDiscoveryObserver = new ContentObserver(
+                new Handler(Looper.getMainLooper())) {
+            @Override
+            public void onChange(boolean selfChange) {
+                synchronized (mAccountsLock) {
+                    for (AccountEntry account : mAccounts) {
+                        account.updateVideoPresenceCapability();
+                    }
+                }
+            }
+        };
+        Uri contactDiscUri = Uri.withAppendedPath(Telephony.SimInfo.CONTENT_URI,
+                Telephony.SimInfo.COLUMN_IMS_RCS_UCE_ENABLED);
+        mContext.getContentResolver().registerContentObserver(
+                contactDiscUri, true /*notifyForDescendants*/, contactDiscoveryObserver);
     }
 
     /**
@@ -1214,30 +1488,6 @@
 
         // Clean up any PhoneAccounts that are no longer relevant
         cleanupPhoneAccounts();
-
-        // At some point, the phone account ID was switched from the subId to the iccId.
-        // If there is a default account, check if this is the case, and upgrade the default account
-        // from using the subId to iccId if so.
-        PhoneAccountHandle defaultPhoneAccount =
-                mTelecomManager.getUserSelectedOutgoingPhoneAccount();
-        ComponentName telephonyComponentName =
-                new ComponentName(mContext, TelephonyConnectionService.class);
-
-        if (defaultPhoneAccount != null &&
-                telephonyComponentName.equals(defaultPhoneAccount.getComponentName()) &&
-                !hasAccountEntryForPhoneAccount(defaultPhoneAccount)) {
-
-            String phoneAccountId = defaultPhoneAccount.getId();
-            if (!TextUtils.isEmpty(phoneAccountId) && TextUtils.isDigitsOnly(phoneAccountId)) {
-                PhoneAccountHandle upgradedPhoneAccount =
-                        PhoneUtils.makePstnPhoneAccountHandle(
-                                PhoneGlobals.getPhone(Integer.parseInt(phoneAccountId)));
-
-                if (hasAccountEntryForPhoneAccount(upgradedPhoneAccount)) {
-                    mTelecomManager.setUserSelectedOutgoingPhoneAccount(upgradedPhoneAccount);
-                }
-            }
-        }
     }
 
     private void tearDownAccounts() {
diff --git a/src/com/android/services/telephony/TelephonyConference.java b/src/com/android/services/telephony/TelephonyConference.java
index afbe89d..7e4693f 100644
--- a/src/com/android/services/telephony/TelephonyConference.java
+++ b/src/com/android/services/telephony/TelephonyConference.java
@@ -16,6 +16,7 @@
 
 package com.android.services.telephony;
 
+import android.net.Uri;
 import android.telecom.Connection;
 import android.telecom.PhoneAccountHandle;
 
@@ -93,6 +94,22 @@
     }
 
     @Override
+    public void onAnswer(int videoState) {
+        Log.e(this, new Exception(), "Answer not supported for GSM conference call.");
+    }
+
+    @Override
+    public void onReject() {
+        Log.e(this, new Exception(), "Reject not supported for GSM conference call.");
+    }
+
+    @Override
+    public void onAddConferenceParticipants(List<Uri> participants) {
+        Log.e(this, new Exception(), "Adding Conference Participants not supported " +
+                " for GSM conference call.");
+    }
+
+    @Override
     public void onMerge(Connection connection) {
         try {
             Phone phone = ((TelephonyConnection) connection).getPhone();
diff --git a/src/com/android/services/telephony/TelephonyConferenceBase.java b/src/com/android/services/telephony/TelephonyConferenceBase.java
index 5e7ecf6..1c81fb9 100644
--- a/src/com/android/services/telephony/TelephonyConferenceBase.java
+++ b/src/com/android/services/telephony/TelephonyConferenceBase.java
@@ -48,6 +48,14 @@
         public void onConferenceMembershipChanged(Connection connection) {}
 
         /**
+         * Listener called when there conference call state changes.
+         * @param conference The conference.
+         * @param oldState previous state of conference call.
+         * @param newState new state of conference call.
+         */
+        public void onStateChanged(Conference conference, int oldState, int newState) {}
+
+        /**
          * Listener called when a conference is destroyed.
          * @param conference The conference.
          */
@@ -129,6 +137,54 @@
     }
 
     /**
+     * Sets state to be on hold.
+     */
+     public final void setConferenceOnHold() {
+         int oldState = getState();
+         if (oldState == Connection.STATE_HOLDING) {
+             return;
+         }
+         setOnHold();
+         notifyStateChanged(oldState, getState());
+     }
+
+     /**
+      * Sets state to be dialing.
+      */
+     public final void setConferenceOnDialing() {
+         int oldState = getState();
+         if (oldState == Connection.STATE_DIALING) {
+             return;
+         }
+         setDialing();
+         notifyStateChanged(oldState, getState());
+     }
+
+     /**
+      * Sets state to be ringing.
+      */
+     public final void setConferenceOnRinging() {
+         int oldState = getState();
+         if (oldState == Connection.STATE_RINGING) {
+             return;
+         }
+         setRinging();
+         notifyStateChanged(oldState, getState());
+     }
+
+     /**
+      * Sets state to be active.
+      */
+     public final void setConferenceOnActive() {
+         int oldState = getState();
+         if (oldState == Connection.STATE_ACTIVE) {
+             return;
+         }
+         setActive();
+         notifyStateChanged(oldState, getState());
+     }
+
+    /**
      * Updates RIL voice radio technology used for current conference after its creation.
      */
     public void updateCallRadioTechAfterCreation() {
@@ -188,4 +244,12 @@
             listener.onDestroyed(this);
         }
     }
+
+    private void notifyStateChanged(int oldState, int newState) {
+        if (oldState != newState) {
+            for (TelephonyConferenceListener listener : mListeners) {
+                listener.onStateChanged(this, oldState, newState);
+            }
+        }
+    }
 }
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
old mode 100644
new mode 100755
index 14f0cb8..19afe24
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -40,7 +40,6 @@
 import android.telephony.CarrierConfigManager;
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
-import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.ServiceState.RilRadioTechnology;
 import android.telephony.SubscriptionManager;
@@ -51,6 +50,7 @@
 import android.util.Pair;
 
 import com.android.ims.ImsCall;
+import com.android.ims.ImsException;
 import com.android.ims.internal.ConferenceParticipant;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
@@ -63,12 +63,14 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.internal.telephony.imsphone.ImsPhoneCall;
 import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
 import com.android.phone.ImsUtil;
 import com.android.phone.PhoneGlobals;
 import com.android.phone.PhoneUtils;
 import com.android.phone.R;
+import com.android.telephony.Rlog;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -113,6 +115,13 @@
     private static final int MSG_SET_CALL_RADIO_TECH = 18;
     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 String JAPAN_COUNTRY_CODE_WITH_PLUS_SIGN = "+81";
+    private static final String JAPAN_ISO_COUNTRY_CODE = "JP";
+
+    private List<Uri> mParticipants;
+    private boolean mIsAdhocConferenceCall;
 
     private final Handler mHandler = new Handler(Looper.getMainLooper()) {
         @Override
@@ -163,7 +172,9 @@
                                 "not foreground connection, skipping");
                         return;
                     }
-                    setRingbackRequested((Boolean) ((AsyncResult) msg.obj).result);
+                    boolean ringback = (Boolean) ((AsyncResult) msg.obj).result;
+                    setRingbackRequested(ringback);
+                    notifyRingbackRequested(ringback);
                     break;
                 case MSG_DISCONNECT:
                     updateState();
@@ -268,6 +279,10 @@
                     int cause = (int) msg.obj;
                     hangup(cause);
                     break;
+                case MSG_REJECT:
+                    int rejectReason = (int) msg.obj;
+                    reject(rejectReason);
+                    break;
 
                 case MSG_SET_CALL_RADIO_TECH:
                     int vrat = (int) msg.obj;
@@ -463,6 +478,7 @@
         public void onVideoProviderChanged(android.telecom.Connection c,
                 Connection.VideoProvider videoProvider) {}
         public void onVideoStateChanged(android.telecom.Connection c, int videoState) {}
+        public void onRingbackRequested(Connection c, boolean ringback) {}
     }
 
     private final PostDialListener mPostDialListener = new PostDialListener() {
@@ -756,11 +772,6 @@
     private boolean mIsCdmaVoicePrivacyEnabled;
 
     /**
-     * Indicates whether this call is an outgoing call.
-     */
-    protected final boolean mIsOutgoing;
-
-    /**
      * Indicates whether the connection can be held. This filed combined with the state of the
      * connection can determine whether {@link Connection#CAPABILITY_HOLD} should be added to the
      * connection.
@@ -794,8 +805,8 @@
             new ConcurrentHashMap<TelephonyConnectionListener, Boolean>(8, 0.9f, 1));
 
     protected TelephonyConnection(com.android.internal.telephony.Connection originalConnection,
-            String callId, boolean isOutgoingCall) {
-        mIsOutgoing = isOutgoingCall;
+            String callId, @android.telecom.Call.Details.CallDirection int callDirection) {
+        setCallDirection(callDirection);
         setTelecomCallId(callId);
         if (originalConnection != null) {
             setOriginalConnection(originalConnection);
@@ -859,6 +870,11 @@
     }
 
     @Override
+    public void onAddConferenceParticipants(List<Uri> participants) {
+        performAddConferenceParticipants(participants);
+    }
+
+    @Override
     public void onAbort() {
         Log.v(this, "onAbort");
         mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.LOCAL).sendToTarget();
@@ -876,14 +892,7 @@
 
     @Override
     public void onAnswer(int videoState) {
-        Log.v(this, "onAnswer");
-        if (isValidRingingCall() && getPhone() != null) {
-            try {
-                getPhone().acceptCall(videoState);
-            } catch (CallStateException e) {
-                Log.e(this, e, "Failed to accept call.");
-            }
-        }
+        performAnswer(videoState);
     }
 
     @Override
@@ -927,15 +936,76 @@
 
     @Override
     public void onReject() {
-        Log.v(this, "onReject");
+        performReject(android.telecom.Call.REJECT_REASON_DECLINED);
+    }
+
+    @Override
+    public void onReject(@android.telecom.Call.RejectReason int rejectReason) {
+        performReject(rejectReason);
+    }
+
+    public void performReject(int rejectReason) {
+        Log.v(this, "performReject");
         if (isValidRingingCall()) {
-            mHandler.obtainMessage(MSG_HANGUP, android.telephony.DisconnectCause.INCOMING_REJECTED)
+            mHandler.obtainMessage(MSG_REJECT, rejectReason)
                     .sendToTarget();
         }
         super.onReject();
     }
 
     @Override
+    public void onTransfer(Uri number, boolean isConfirmationRequired) {
+        Log.v(this, "onTransfer");
+        if (mOriginalConnection != null) {
+            if (number == null) {
+                Log.w(this, "call transfer uri is null");
+                return;
+            }
+            String scheme = number.getScheme();
+            String transferNumber = "";
+            String uriString = number.getSchemeSpecificPart();
+            if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {
+                if (!PhoneAccount.SCHEME_TEL.equals(scheme)) {
+                    Log.w(this, "onTransfer, number scheme is not of type tel instead: "
+                            + scheme);
+                    return;
+                }
+                if (PhoneNumberUtils.isUriNumber(uriString)) {
+                    Log.w(this, "Invalid transfer address. Not a legal PSTN number.");
+                    return;
+                }
+                transferNumber = PhoneNumberUtils.convertAndStrip(uriString);
+                if (TextUtils.isEmpty(transferNumber)) {
+                    Log.w(this, "Empty transfer number obtained from uri");
+                    return;
+                }
+            } else {
+                Log.w(this, "Cannot transfer to voicemail uri");
+                return;
+            }
+
+            try {
+                mOriginalConnection.transfer(transferNumber, isConfirmationRequired);
+            } catch (CallStateException e) {
+                Log.e(this, e, "Failed to transfer call.");
+            }
+        }
+    }
+
+    @Override
+    public void onTransfer(Connection otherConnection) {
+        Log.v(this, "onConsultativeTransfer");
+        if (mOriginalConnection != null && (otherConnection instanceof TelephonyConnection)) {
+            try {
+                mOriginalConnection.consultativeTransfer(
+                        ((TelephonyConnection) otherConnection).getOriginalConnection());
+            } catch (CallStateException e) {
+                Log.e(this, e, "Failed to transfer call.");
+            }
+        }
+    }
+
+    @Override
     public void onPostDialContinue(boolean proceed) {
         Log.v(this, "onPostDialContinue, proceed: " + proceed);
         if (mOriginalConnection != null) {
@@ -1001,6 +1071,17 @@
         originalConnection.sendRttModifyResponse(textStream);
     }
 
+    public void performAnswer(int videoState) {
+        Log.v(this, "performAnswer");
+        if (isValidRingingCall() && getPhone() != null) {
+            try {
+                getPhone().acceptCall(videoState);
+            } catch (CallStateException e) {
+                Log.e(this, e, "Failed to accept call.");
+            }
+        }
+    }
+
     public void performHold() {
         Log.v(this, "performHold");
         // TODO: Can dialing calls be put on hold as well since they take up the
@@ -1096,6 +1177,29 @@
         }
     }
 
+    private String[] getAddConferenceParticipants(List<Uri> participants) {
+        String[] addConfParticipants = new String[participants.size()];
+        int i = 0;
+        for (Uri participant : participants) {
+           addConfParticipants[i] = participant.getSchemeSpecificPart();
+           i++;
+        }
+        return addConfParticipants;
+    }
+
+    public void performAddConferenceParticipants(List<Uri> participants) {
+        Log.v(this, "performAddConferenceParticipants");
+        if (mOriginalConnection.getCall() instanceof ImsPhoneCall) {
+            ImsPhoneCall imsPhoneCall = (ImsPhoneCall)mOriginalConnection.getCall();
+            try {
+                imsPhoneCall.getImsCall().inviteParticipants(
+                        getAddConferenceParticipants(participants));
+            } catch(ImsException e) {
+                Log.e(this, e, "failed to add conference participants");
+            }
+        }
+    }
+
     /**
      * Builds connection capabilities common to all TelephonyConnections. Namely, apply IMS-based
      * capabilities.
@@ -1130,6 +1234,12 @@
         newCapabilities = changeBitmask(newCapabilities, CAPABILITY_SUPPORT_DEFLECT,
                 isImsConnection() && canDeflectImsCalls());
 
+        newCapabilities = applyAddParticipantCapabilities(newCapabilities);
+        newCapabilities = changeBitmask(newCapabilities, CAPABILITY_TRANSFER_CONSULTATIVE,
+                isImsConnection() && canConsultativeTransfer());
+        newCapabilities = changeBitmask(newCapabilities, CAPABILITY_TRANSFER,
+                isImsConnection() && canTransferToNumber());
+
         if (getConnectionCapabilities() != newCapabilities) {
             setConnectionCapabilities(newCapabilities);
             notifyConnectionCapabilitiesChanged(newCapabilities);
@@ -1162,11 +1272,13 @@
                 isExternalConnection());
         newProperties = changeBitmask(newProperties, PROPERTY_HAS_CDMA_VOICE_PRIVACY,
                 mIsCdmaVoicePrivacyEnabled);
-        newProperties = changeBitmask(newProperties, PROPERTY_ASSISTED_DIALING_USED,
+        newProperties = changeBitmask(newProperties, PROPERTY_ASSISTED_DIALING,
                 mIsUsingAssistedDialing);
         newProperties = changeBitmask(newProperties, PROPERTY_IS_RTT, isRtt());
         newProperties = changeBitmask(newProperties, PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL,
                 isNetworkIdentifiedEmergencyCall());
+        newProperties = changeBitmask(newProperties, PROPERTY_IS_ADHOC_CONFERENCE,
+                isAdhocConferenceCall());
 
         if (getConnectionProperties() != newProperties) {
             setTelephonyConnectionProperties(newProperties);
@@ -1186,6 +1298,9 @@
             if (isShowingOriginalDialString()
                     && mOriginalConnection.getOrigDialString() != null) {
                 address = getAddressFromNumber(mOriginalConnection.getOrigDialString());
+            } else if (isNeededToFormatIncomingNumberForJp()) {
+                address = getAddressFromNumber(
+                        formatIncomingNumberForJp(mOriginalConnection.getAddress()));
             } else {
                 address = getAddressFromNumber(mOriginalConnection.getAddress());
             }
@@ -1255,6 +1370,7 @@
         setTelephonyVideoState(mOriginalConnection.getVideoState());
         setOriginalConnectionCapabilities(mOriginalConnection.getConnectionCapabilities());
         setIsNetworkIdentifiedEmergencyCall(mOriginalConnection.isNetworkIdentifiedEmergencyCall());
+        setIsAdhocConferenceCall(mOriginalConnection.isAdhocConference());
         setAudioModeIsVoip(mOriginalConnection.getAudioModeIsVoip());
         setTelephonyVideoProvider(mOriginalConnection.getVideoProvider());
         setAudioQuality(mOriginalConnection.getAudioQuality());
@@ -1272,6 +1388,8 @@
         if (PhoneNumberUtils.isEmergencyNumber(mOriginalConnection.getAddress())) {
             mTreatAsEmergencyCall = true;
         }
+        // Propagate VERSTAT for IMS calls.
+        setCallerNumberVerificationStatus(mOriginalConnection.getNumberVerificationStatus());
 
         if (isImsConnection()) {
             mWasImsConnection = true;
@@ -1511,6 +1629,39 @@
         return true;
     }
 
+    /**
+     * @return The address's to which this Connection is currently communicating.
+     */
+    public final @Nullable List<Uri> getParticipants() {
+        return mParticipants;
+    }
+
+    /**
+     * Sets the value of the {@link #getParticipants()} property.
+     *
+     * @param address The participant address's.
+     */
+    public final void setParticipants(@Nullable List<Uri> address) {
+        mParticipants = address;
+    }
+
+    /**
+     * @return true if connection is adhocConference call else false.
+     */
+    public final boolean isAdhocConferenceCall() {
+        return mIsAdhocConferenceCall;
+    }
+
+    /**
+     * Sets the value of the {@link #isAdhocConferenceCall()} property.
+     *
+     * @param isAdhocConferenceCall represents if the call is adhoc conference call or not.
+     */
+    public void setIsAdhocConferenceCall(boolean isAdhocConferenceCall) {
+        mIsAdhocConferenceCall = isAdhocConferenceCall;
+        updateConnectionProperties();
+    }
+
     private boolean canHoldImsCalls() {
         PersistableBundle b = getCarrierConfig();
         // Return true if the CarrierConfig is unavailable
@@ -1520,6 +1671,75 @@
                 || !VideoProfile.isVideo(getVideoState()));
     }
 
+    private boolean isConferenceHosted() {
+        boolean isHosted = false;
+        if (getTelephonyConnectionService() != null) {
+            for (Conference current : getTelephonyConnectionService().getAllConferences()) {
+                if (current instanceof ImsConference) {
+                    ImsConference other = (ImsConference) current;
+                    if (getState() == current.getState()) {
+                        continue;
+                    }
+                    if (other.isConferenceHost()) {
+                        isHosted = true;
+                        break;
+                    }
+                }
+            }
+        }
+        return isHosted;
+    }
+
+    private boolean isAddParticipantCapable() {
+        // not add participant capable for non ims phones
+        if (getPhone() == null || getPhone().getPhoneType() != PhoneConstants.PHONE_TYPE_IMS) {
+            return false;
+        }
+
+        if (!getCarrierConfig()
+                .getBoolean(CarrierConfigManager.KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL)) {
+            return false;
+        }
+
+        boolean isCapable = !mTreatAsEmergencyCall && (mConnectionState == Call.State.ACTIVE ||
+                mConnectionState == Call.State.HOLDING);
+
+        // add participant capable if current connection is a host connection or
+        // if conference is not hosted on the device
+        isCapable = isCapable && ((mOriginalConnection != null &&
+                mOriginalConnection.isConferenceHost()) ||
+                !isConferenceHosted());
+
+        /**
+          * For individual IMS calls, if the extra for remote conference support is
+          *     - indicated, then consider the same for add participant capability
+          *     - not indicated, then the add participant capability is same as before.
+          */
+        if (isCapable && (mOriginalConnection != null) && !mIsMultiParty) {
+            isCapable = mOriginalConnectionExtras.getBoolean(
+                    ImsCallProfile.EXTRA_CONFERENCE_AVAIL, isCapable);
+        }
+        return isCapable;
+    }
+
+    /**
+     * Applies the add participant capabilities to the {@code CallCapabilities} bit-mask.
+     *
+     * @param callCapabilities The {@code CallCapabilities} bit-mask.
+     * @return The capabilities with the add participant capabilities applied.
+     */
+    private int applyAddParticipantCapabilities(int callCapabilities) {
+        int currentCapabilities = callCapabilities;
+        if (isAddParticipantCapable()) {
+            currentCapabilities = changeBitmask(currentCapabilities,
+                    Connection.CAPABILITY_ADD_PARTICIPANT, true);
+        } else {
+            currentCapabilities = changeBitmask(currentCapabilities,
+                    Connection.CAPABILITY_ADD_PARTICIPANT, false);
+        }
+        return currentCapabilities;
+    }
+
     @VisibleForTesting
     public PersistableBundle getCarrierConfig() {
         Phone phone = getPhone();
@@ -1540,6 +1760,50 @@
         return false;
     }
 
+    private boolean isCallTransferSupported() {
+        PersistableBundle b = getCarrierConfig();
+        // Return false if the CarrierConfig is unavailable
+        if (b != null) {
+            return b.getBoolean(CarrierConfigManager.KEY_CARRIER_ALLOW_TRANSFER_IMS_CALL_BOOL);
+        }
+        return false;
+    }
+
+    private boolean canTransfer(TelephonyConnection c) {
+        com.android.internal.telephony.Connection connection = c.getOriginalConnection();
+        return (connection != null && !connection.isMultiparty()
+                && (c.getState() == STATE_ACTIVE || c.getState() == STATE_HOLDING));
+    }
+
+    private boolean canTransferToNumber() {
+        if (!isCallTransferSupported()) {
+            return false;
+        }
+        return canTransfer(this);
+    }
+
+    private boolean canConsultativeTransfer() {
+        if (!isCallTransferSupported()) {
+            return false;
+        }
+        if (!canTransfer(this)) {
+            return false;
+        }
+        boolean canConsultativeTransfer = false;
+        if (getTelephonyConnectionService() != null) {
+            for (Connection current : getTelephonyConnectionService().getAllConnections()) {
+                if (current != this && current instanceof TelephonyConnection) {
+                    TelephonyConnection other = (TelephonyConnection) current;
+                    if (canTransfer(other)) {
+                        canConsultativeTransfer = true;
+                        break;
+                    }
+                }
+            }
+        }
+        return canConsultativeTransfer;
+    }
+
     /**
      * Determines if the device will respect the value of the
      * {@link CarrierConfigManager#KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL} configuration option.
@@ -1628,6 +1892,46 @@
         }
     }
 
+    protected void reject(@android.telecom.Call.RejectReason int rejectReason) {
+        if (mOriginalConnection != null) {
+            mHangupDisconnectCause = android.telephony.DisconnectCause.INCOMING_REJECTED;
+            try {
+                // Hanging up a ringing call requires that we invoke call.hangup() as opposed to
+                // connection.hangup(). Without this change, the party originating the call
+                // will not get sent to voicemail if the user opts to reject the call.
+                if (isValidRingingCall()) {
+                    Call call = getCall();
+                    if (call != null) {
+                        call.hangup(rejectReason);
+                    } else {
+                        Log.w(this, "Attempting to hangup a connection without backing call.");
+                    }
+                } else {
+                    // We still prefer to call connection.hangup() for non-ringing calls
+                    // in order to support hanging-up specific calls within a conference call.
+                    // If we invoked call.hangup() while in a conference, we would end up
+                    // hanging up the entire conference call instead of the specific connection.
+                    mOriginalConnection.hangup();
+                }
+            } catch (CallStateException e) {
+                Log.e(this, e, "Call to Connection.hangup failed with exception");
+            }
+        } else {
+            if (getState() == STATE_DISCONNECTED) {
+                Log.i(this, "hangup called on an already disconnected call!");
+                close();
+            } else {
+                // There are a few cases where mOriginalConnection has not been set yet. For
+                // example, when the radio has to be turned on to make an emergency call,
+                // mOriginalConnection could not be set for many seconds.
+                setTelephonyConnectionDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
+                        android.telephony.DisconnectCause.LOCAL,
+                        "Local Disconnect before connection established."));
+                close();
+            }
+        }
+    }
+
     com.android.internal.telephony.Connection getOriginalConnection() {
         return mOriginalConnection;
     }
@@ -1742,6 +2046,12 @@
 
                     // Ensure extras are propagated to Telecom.
                     putTelephonyExtras(mOriginalConnectionExtras);
+                    // If extras contain Conference support information,
+                    // then ensure capabilities are updated.
+                    if (mOriginalConnectionExtras.containsKey(
+                            ImsCallProfile.EXTRA_CONFERENCE_AVAIL)) {
+                        updateConnectionCapabilities();
+                    }
                 } else {
                     Log.d(this, "Extras update not required");
                 }
@@ -2119,8 +2429,8 @@
     /**
      * @return {@code true} if this is an outgoing call, {@code false} otherwise.
      */
-    boolean isOutgoingCall() {
-        return mIsOutgoing;
+    public boolean isOutgoingCall() {
+        return getCallDirection() == android.telecom.Call.Details.DIRECTION_OUTGOING;
     }
 
     /**
@@ -2530,6 +2840,8 @@
         }
         sb.append(" confSupported:");
         sb.append(mIsConferenceSupported ? "Y" : "N");
+        sb.append(" isAdhocConf:");
+        sb.append(isAdhocConferenceCall() ? "Y" : "N");
         sb.append("]");
         return sb.toString();
     }
@@ -2654,8 +2966,13 @@
      * <p>
      * Note: This should be used instead of {@link #setVideoState(int)} to ensure listeners are
      * notified.
+     * @param videoState The new video state. Valid values:
+     *                   {@link VideoProfile#STATE_AUDIO_ONLY},
+     *                   {@link VideoProfile#STATE_BIDIRECTIONAL},
+     *                   {@link VideoProfile#STATE_TX_ENABLED},
+     *                   {@link VideoProfile#STATE_RX_ENABLED}.
      */
-    public void setTelephonyVideoState(@VideoProfile.VideoState int videoState) {
+    public void setTelephonyVideoState(int videoState) {
         setVideoState(videoState);
         notifyVideoStateChanged(videoState);
     }
@@ -2846,15 +3163,29 @@
 
     /**
      * Notifies {@link TelephonyConnectionListener}s of a change to the video state of a connection.
-     * @param videoState The new video state.
+     * @param videoState The new video state. Valid values:
+     *                   {@link VideoProfile#STATE_AUDIO_ONLY},
+     *                   {@link VideoProfile#STATE_BIDIRECTIONAL},
+     *                   {@link VideoProfile#STATE_TX_ENABLED},
+     *                   {@link VideoProfile#STATE_RX_ENABLED}.
      */
-    private void notifyVideoStateChanged(@VideoProfile.VideoState int videoState) {
+    private void notifyVideoStateChanged(int videoState) {
         for (TelephonyConnectionListener listener : mTelephonyListeners) {
             listener.onVideoStateChanged(this, videoState);
         }
     }
 
     /**
+     * Notifies {@link TelephonyConnectionListener}s of a whether to play Ringback Tone or not.
+     * @param ringback Whether the ringback tone is to be played
+     */
+    private void notifyRingbackRequested(boolean ringback) {
+        for (TelephonyConnectionListener listener : mTelephonyListeners) {
+            listener.onRingbackRequested(this, ringback);
+        }
+    }
+
+    /**
      * Notifies {@link TelephonyConnectionListener}s of changes to the video provider for a
      * connection.
      * @param videoProvider The new video provider.
@@ -2875,4 +3206,29 @@
             listener.onStatusHintsChanged(this, statusHints);
         }
     }
+
+    /**
+     * Whether the incoming call number should be formatted to national number for Japan.
+     * @return {@code true} should be convert to the national format, {@code false} otherwise.
+     */
+    private boolean isNeededToFormatIncomingNumberForJp() {
+        if (mOriginalConnection.isIncoming()
+                && !TextUtils.isEmpty(mOriginalConnection.getAddress())
+                && mOriginalConnection.getAddress().startsWith(JAPAN_COUNTRY_CODE_WITH_PLUS_SIGN)) {
+            PersistableBundle b = getCarrierConfig();
+            return b != null && b.getBoolean(
+                    CarrierConfigManager.KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL);
+        }
+        return false;
+    }
+
+    /**
+     * Format the incoming call number to national number for Japan.
+     * @param number
+     * @return the formatted phone number (e.g, "+819012345678" -> "09012345678")
+     */
+    private String formatIncomingNumberForJp(String number) {
+        return PhoneNumberUtils.stripSeparators(
+                PhoneNumberUtils.formatNumber(number, JAPAN_ISO_COUNTRY_CODE));
+    }
 }
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 2aba682..bd10aea 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -17,10 +17,13 @@
 package com.android.services.telephony;
 
 import android.annotation.NonNull;
+import android.app.AlertDialog;
+import android.app.Dialog;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.Uri;
@@ -46,6 +49,7 @@
 import android.telephony.emergency.EmergencyNumber;
 import android.text.TextUtils;
 import android.util.Pair;
+import android.view.WindowManager;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.Call;
@@ -65,12 +69,14 @@
 import com.android.phone.MMIDialogActivity;
 import com.android.phone.PhoneUtils;
 import com.android.phone.R;
+import com.android.phone.settings.SuppServicesUiUtil;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -246,12 +252,20 @@
 
         @Override
         public boolean isCurrentEmergencyNumber(String number) {
-            return mTelephonyManager.isEmergencyNumber(number);
+            try {
+                return mTelephonyManager.isEmergencyNumber(number);
+            } catch (IllegalStateException ise) {
+                return false;
+            }
         }
 
         @Override
         public Map<Integer, List<EmergencyNumber>> getCurrentEmergencyNumberList() {
-            return mTelephonyManager.getEmergencyNumberList();
+            try {
+                return mTelephonyManager.getEmergencyNumberList();
+            } catch (IllegalStateException ise) {
+                return new HashMap<>();
+            }
         }
     }
 
@@ -421,6 +435,14 @@
     }
 
     /**
+     * Overrides radioOnHelper for testing.
+     */
+    @VisibleForTesting
+    public void setRadioOnHelper(RadioOnHelper radioOnHelper) {
+        mRadioOnHelper = radioOnHelper;
+    }
+
+    /**
      * Overrides PhoneSwitcher dependencies for testing.
      */
     @VisibleForTesting
@@ -516,6 +538,138 @@
         return super.onUnbind(intent);
     }
 
+    private Conference placeOutgoingConference(ConnectionRequest request,
+            Connection resultConnection, Phone phone) {
+        if (resultConnection instanceof TelephonyConnection) {
+            return placeOutgoingConference((TelephonyConnection) resultConnection, phone, request);
+        }
+        return null;
+    }
+
+    private Conference placeOutgoingConference(TelephonyConnection conferenceHostConnection,
+            Phone phone, ConnectionRequest request) {
+        updatePhoneAccount(conferenceHostConnection, phone);
+        com.android.internal.telephony.Connection originalConnection = null;
+        try {
+            originalConnection = phone.startConference(
+                    getParticipantsToDial(request.getParticipants()),
+                    new ImsPhone.ImsDialArgs.Builder()
+                    .setVideoState(request.getVideoState())
+                    .setRttTextStream(conferenceHostConnection.getRttTextStream())
+                    .build());
+        } catch (CallStateException e) {
+            Log.e(this, e, "placeOutgoingConference, phone.startConference exception: " + e);
+            handleCallStateException(e, conferenceHostConnection, phone);
+            return null;
+        }
+
+        if (originalConnection == null) {
+            Log.d(this, "placeOutgoingConference, phone.startConference returned null");
+            conferenceHostConnection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
+                    android.telephony.DisconnectCause.OUTGOING_FAILURE,
+                    "conferenceHostConnection is null",
+                    phone.getPhoneId()));
+            conferenceHostConnection.clearOriginalConnection();
+            conferenceHostConnection.destroy();
+        } else {
+            conferenceHostConnection.setOriginalConnection(originalConnection);
+        }
+
+        return prepareConference(conferenceHostConnection, request.getAccountHandle());
+    }
+
+    Conference prepareConference(Connection conn, PhoneAccountHandle phoneAccountHandle) {
+        if (!(conn instanceof TelephonyConnection)) {
+            Log.w(this, "prepareConference returning NULL conference");
+            return null;
+        }
+
+        TelephonyConnection connection = (TelephonyConnection)conn;
+
+        ImsConference conference = new ImsConference(TelecomAccountRegistry.getInstance(this),
+                mTelephonyConnectionServiceProxy, connection,
+                phoneAccountHandle, () -> true,
+                ImsConferenceController.getCarrierConfig(connection.getPhone()));
+        mImsConferenceController.addConference(conference);
+        conference.setVideoState(connection,
+                connection.getVideoState());
+        conference.setVideoProvider(connection,
+                connection.getVideoProvider());
+        conference.setStatusHints(connection.getStatusHints());
+        conference.setAddress(connection.getAddress(),
+                connection.getAddressPresentation());
+        conference.setCallerDisplayName(connection.getCallerDisplayName(),
+                connection.getCallerDisplayNamePresentation());
+        conference.setParticipants(connection.getParticipants());
+        return conference;
+    }
+
+    @Override
+    public @Nullable Conference onCreateIncomingConference(
+            @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
+            @NonNull final ConnectionRequest request) {
+        Log.i(this, "onCreateIncomingConference, request: " + request);
+        Connection connection = onCreateIncomingConnection(connectionManagerPhoneAccount, request);
+        Log.d(this, "onCreateIncomingConference, connection: %s", connection);
+        if (connection == null) {
+            Log.i(this, "onCreateIncomingConference, implementation returned null connection.");
+            return Conference.createFailedConference(
+                    new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONNECTION"),
+                    request.getAccountHandle());
+        }
+
+        final Phone phone = getPhoneForAccount(request.getAccountHandle(),
+                false /* isEmergencyCall*/, null /* not an emergency call */);
+        if (phone == null) {
+            Log.d(this, "onCreateIncomingConference, phone is null");
+            return Conference.createFailedConference(
+                    DisconnectCauseUtil.toTelecomDisconnectCause(
+                            android.telephony.DisconnectCause.OUT_OF_SERVICE,
+                            "Phone is null"),
+                    request.getAccountHandle());
+        }
+
+        return prepareConference(connection, request.getAccountHandle());
+    }
+
+    @Override
+    public @Nullable Conference onCreateOutgoingConference(
+            @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
+            @NonNull final ConnectionRequest request) {
+        Log.i(this, "onCreateOutgoingConference, request: " + request);
+        Connection connection = onCreateOutgoingConnection(connectionManagerPhoneAccount, request);
+        Log.d(this, "onCreateOutgoingConference, connection: %s", connection);
+        if (connection == null) {
+            Log.i(this, "onCreateOutgoingConference, implementation returned null connection.");
+            return Conference.createFailedConference(
+                    new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONNECTION"),
+                    request.getAccountHandle());
+        }
+
+        final Phone phone = getPhoneForAccount(request.getAccountHandle(),
+                false /* isEmergencyCall*/, null /* not an emergency call */);
+        if (phone == null) {
+            Log.d(this, "onCreateOutgoingConference, phone is null");
+            return Conference.createFailedConference(
+                    DisconnectCauseUtil.toTelecomDisconnectCause(
+                            android.telephony.DisconnectCause.OUT_OF_SERVICE,
+                            "Phone is null"),
+                    request.getAccountHandle());
+        }
+
+        return placeOutgoingConference(request, connection, phone);
+    }
+
+    private String[] getParticipantsToDial(List<Uri> participants) {
+        String[] participantsToDial = new String[participants.size()];
+        int i = 0;
+        for (Uri participant : participants) {
+           participantsToDial[i] = participant.getSchemeSpecificPart();
+           i++;
+        }
+        return participantsToDial;
+    }
+
     @Override
     public Connection onCreateOutgoingConnection(
             PhoneAccountHandle connectionManagerPhoneAccount,
@@ -523,7 +677,9 @@
         Log.i(this, "onCreateOutgoingConnection, request: " + request);
 
         Uri handle = request.getAddress();
-        if (handle == null) {
+        boolean isAdhocConference = request.isAdhocConferenceCall();
+
+        if (!isAdhocConference && handle == null) {
             Log.d(this, "onCreateOutgoingConnection, handle is null");
             return Connection.createFailedConnection(
                     mDisconnectCauseFactory.toTelecomDisconnectCause(
@@ -626,19 +782,21 @@
         }
         final String numberToDial = number;
 
-
         final boolean isAirplaneModeOn = mDeviceState.isAirplaneModeOn(this);
 
         boolean needToTurnOnRadio = (isEmergencyNumber && (!isRadioOn() || isAirplaneModeOn))
                 || isRadioPowerDownOnBluetooth();
 
+        // Get the right phone object from the account data passed in.
+        final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber,
+                /* Note: when not an emergency, handle can be null for unknown callers */
+                handle == null ? null : handle.getSchemeSpecificPart());
+
         if (needToTurnOnRadio) {
             final Uri resultHandle = handle;
-            // By default, Connection based on the default Phone, since we need to return to Telecom
-            // now.
-            final int originalPhoneType = mPhoneFactoryProxy.getDefaultPhone().getPhoneType();
+            final int originalPhoneType = phone.getPhoneType();
             final Connection resultConnection = getTelephonyConnection(request, numberToDial,
-                    isEmergencyNumber, resultHandle, PhoneFactory.getDefaultPhone());
+                    isEmergencyNumber, resultHandle, phone);
             if (mRadioOnHelper == null) {
                 mRadioOnHelper = new RadioOnHelper(this);
             }
@@ -646,7 +804,7 @@
                 @Override
                 public void onComplete(RadioOnStateListener listener, boolean isRadioReady) {
                     handleOnComplete(isRadioReady, isEmergencyNumber, resultConnection, request,
-                            numberToDial, resultHandle, originalPhoneType);
+                            numberToDial, resultHandle, originalPhoneType, phone);
                 }
 
                 @Override
@@ -675,7 +833,7 @@
                                 || serviceState == ServiceState.STATE_IN_SERVICE;
                     }
                 }
-            });
+            }, isEmergencyNumber && !isTestEmergencyNumber, phone);
             // Return the still unconnected GsmConnection and wait for the Radios to boot before
             // connecting it to the underlying Phone.
             return resultConnection;
@@ -691,14 +849,18 @@
                                 "Add call restricted due to ongoing video call"));
             }
 
-            // Get the right phone object from the account data passed in.
-            final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber,
-                    /* Note: when not an emergency, handle can be null for unknown callers */
-                    handle == null ? null : handle.getSchemeSpecificPart());
             if (!isEmergencyNumber) {
                 final Connection resultConnection = getTelephonyConnection(request, numberToDial,
                         false, handle, phone);
-                return placeOutgoingConnection(request, resultConnection, phone);
+                if (isAdhocConference) {
+                    if (resultConnection instanceof TelephonyConnection) {
+                        TelephonyConnection conn = (TelephonyConnection)resultConnection;
+                        conn.setParticipants(request.getParticipants());
+                    }
+                    return resultConnection;
+                } else {
+                    return placeOutgoingConnection(request, resultConnection, phone);
+                }
             } else {
                 final Connection resultConnection = getTelephonyConnection(request, numberToDial,
                         true, handle, phone);
@@ -763,18 +925,21 @@
      */
     private void handleOnComplete(boolean isRadioReady, boolean isEmergencyNumber,
             Connection originalConnection, ConnectionRequest request, String numberToDial,
-            Uri handle, int originalPhoneType) {
+            Uri handle, int originalPhoneType, Phone phone) {
         // Make sure the Call has not already been canceled by the user.
         if (originalConnection.getState() == Connection.STATE_DISCONNECTED) {
             Log.i(this, "Call disconnected before the outgoing call was placed. Skipping call "
                     + "placement.");
+            if (isEmergencyNumber) {
+                // If call is already canceled by the user, notify modem to exit emergency call
+                // mode by sending radio on with forEmergencyCall=false.
+                for (Phone curPhone : mPhoneFactoryProxy.getPhones()) {
+                    curPhone.setRadioPower(true, false, false, true);
+                }
+            }
             return;
         }
-        // Get the right phone object since the radio has been turned on successfully.
         if (isRadioReady) {
-            final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber,
-                    /* Note: when not an emergency, handle can be null for unknown callers */
-                    handle == null ? null : handle.getSchemeSpecificPart());
             if (!isEmergencyNumber) {
                 adjustAndPlaceOutgoingConnection(phone, originalConnection, request, numberToDial,
                         handle, originalPhoneType, false);
@@ -896,7 +1061,7 @@
             int dataNetType = phone.getServiceState().getDataNetworkType();
             if (dataNetType == TelephonyManager.NETWORK_TYPE_LTE ||
                     dataNetType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
-                state = phone.getServiceState().getDataRegState();
+                state = phone.getServiceState().getDataRegistrationState();
             }
         }
 
@@ -982,7 +1147,7 @@
 
         final TelephonyConnection connection =
                 createConnectionFor(phone, null, true /* isOutgoing */, request.getAccountHandle(),
-                        request.getTelecomCallId());
+                        request.getTelecomCallId(), request.isAdhocConferenceCall());
         if (connection == null) {
             return Connection.createFailedConnection(
                     mDisconnectCauseFactory.toTelecomDisconnectCause(
@@ -995,6 +1160,8 @@
         connection.setTelephonyVideoState(request.getVideoState());
         connection.setRttTextStream(request.getRttTextStream());
         connection.setTtyEnabled(isTtyModeEnabled);
+        connection.setIsAdhocConferenceCall(request.isAdhocConferenceCall());
+        connection.setParticipants(request.getParticipants());
         return connection;
     }
 
@@ -1026,11 +1193,26 @@
         Call call = phone.getRingingCall();
         if (!call.getState().isRinging()) {
             Log.i(this, "onCreateIncomingConnection, no ringing call");
-            return Connection.createFailedConnection(
+            Connection connection = Connection.createFailedConnection(
                     mDisconnectCauseFactory.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.INCOMING_MISSED,
                             "Found no ringing call",
                             phone.getPhoneId()));
+            Bundle extras = request.getExtras();
+
+            long time = extras.getLong(TelecomManager.EXTRA_CALL_CREATED_EPOCH_TIME_MILLIS);
+            if (time != 0) {
+                Log.i(this, "onCreateIncomingConnection. Set connect time info.");
+                connection.setConnectTimeMillis(time);
+            }
+
+            Uri address = extras.getParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS);
+            if (address != null) {
+                Log.i(this, "onCreateIncomingConnection. Set caller id info.");
+                connection.setAddress(address, TelecomManager.PRESENTATION_ALLOWED);
+            }
+
+            return connection;
         }
 
         com.android.internal.telephony.Connection originalConnection =
@@ -1043,7 +1225,8 @@
 
         TelephonyConnection connection =
                 createConnectionFor(phone, originalConnection, false /* isOutgoing */,
-                        request.getAccountHandle(), request.getTelecomCallId());
+                        request.getAccountHandle(), request.getTelecomCallId(),
+                        request.isAdhocConferenceCall());
         handleIncomingRtt(request, originalConnection);
         if (connection == null) {
             return Connection.createCanceledConnection();
@@ -1155,6 +1338,29 @@
         connection.close();
     }
 
+    /**
+     * Called by the {@link ConnectionService} when a newly created {@link Conference} has been
+     * added to the {@link ConnectionService} and sent to Telecom.  Here it is safe to send
+     * connection events.
+     *
+     * @param conference the {@link Conference}.
+     */
+    @Override
+    public void onCreateConferenceComplete(Conference conference) {
+        if (conference instanceof ImsConference) {
+            ImsConference imsConference = (ImsConference)conference;
+            TelephonyConnection telephonyConnection =
+                    (TelephonyConnection)(imsConference.getConferenceHost());
+            maybeSendInternationalCallEvent(telephonyConnection);
+        }
+    }
+
+    public void onCreateIncomingConferenceFailed(PhoneAccountHandle connectionManagerPhoneAccount,
+            ConnectionRequest request) {
+        Log.i(this, "onCreateIncomingConferenceFailed, request: " + request);
+        onCreateIncomingConnectionFailed(connectionManagerPhoneAccount, request);
+    }
+
     @Override
     public void triggerConferenceRecalculate() {
         if (mTelephonyConferenceController.shouldRecalculate()) {
@@ -1449,7 +1655,16 @@
 
     private void placeOutgoingConnection(
             TelephonyConnection connection, Phone phone, int videoState, Bundle extras) {
-        String number = connection.getAddress().getSchemeSpecificPart();
+
+        String number = (connection.getAddress() != null)
+                ? connection.getAddress().getSchemeSpecificPart()
+                : "";
+
+        if (showDataDialog(phone, number)) {
+            connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
+                        android.telephony.DisconnectCause.DIALED_MMI, "UT is not available"));
+            return;
+        }
 
         com.android.internal.telephony.Connection originalConnection = null;
         try {
@@ -1471,6 +1686,12 @@
                                                     .OUTGOING_EMERGENCY_CALL_PLACED);
                                 }
                             }
+                            for (Conference c : getAllConferences()) {
+                                if (c.getState() != Connection.STATE_DISCONNECTED
+                                        && c instanceof Conference) {
+                                    ((Conference) c).onDisconnect();
+                                }
+                            }
                         } else if (!isVideoCallHoldAllowed(phone)) {
                             // If we do not support holding ongoing video call for an outgoing
                             // emergency call, disconnect the ongoing video call.
@@ -1496,41 +1717,15 @@
             }
         } catch (CallStateException e) {
             Log.e(this, e, "placeOutgoingConnection, phone.dial exception: " + e);
-            int cause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
-            switch (e.getError()) {
-                case CallStateException.ERROR_OUT_OF_SERVICE:
-                    cause = android.telephony.DisconnectCause.OUT_OF_SERVICE;
-                    break;
-                case CallStateException.ERROR_POWER_OFF:
-                    cause = android.telephony.DisconnectCause.POWER_OFF;
-                    break;
-                case CallStateException.ERROR_ALREADY_DIALING:
-                    cause = android.telephony.DisconnectCause.ALREADY_DIALING;
-                    break;
-                case CallStateException.ERROR_CALL_RINGING:
-                    cause = android.telephony.DisconnectCause.CANT_CALL_WHILE_RINGING;
-                    break;
-                case CallStateException.ERROR_CALLING_DISABLED:
-                    cause = android.telephony.DisconnectCause.CALLING_DISABLED;
-                    break;
-                case CallStateException.ERROR_TOO_MANY_CALLS:
-                    cause = android.telephony.DisconnectCause.TOO_MANY_ONGOING_CALLS;
-                    break;
-                case CallStateException.ERROR_OTASP_PROVISIONING_IN_PROCESS:
-                    cause = android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS;
-                    break;
-            }
-            connection.setTelephonyConnectionDisconnected(
-                    mDisconnectCauseFactory.toTelecomDisconnectCause(cause, e.getMessage(),
-                            phone.getPhoneId()));
-            connection.close();
+            handleCallStateException(e, connection, phone);
             return;
         }
 
         if (originalConnection == null) {
             int telephonyDisconnectCause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
             // On GSM phones, null connection means that we dialed an MMI code
-            if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
+            if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM ||
+                phone.isUtEnabled()) {
                 Log.d(this, "dialed MMI code");
                 int subId = phone.getSubId();
                 Log.d(this, "subId: "+subId);
@@ -1539,7 +1734,7 @@
                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                         Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                 if (SubscriptionManager.isValidSubscriptionId(subId)) {
-                    intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+                    SubscriptionManager.putSubscriptionIdExtra(intent, subId);
                 }
                 startActivity(intent);
             }
@@ -1562,7 +1757,7 @@
             return true;
         }
         return cfgManager.getConfigForSubId(phone.getSubId()).getBoolean(
-                CarrierConfigManager.KEY_ALLOW_HOLDING_VIDEO_CALL_BOOL, true);
+                CarrierConfigManager.KEY_ALLOW_HOLD_VIDEO_CALL_BOOL, true);
     }
 
     private boolean shouldHoldForEmergencyCall(Phone phone) {
@@ -1577,24 +1772,71 @@
                 CarrierConfigManager.KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL, true);
     }
 
+    private void handleCallStateException(CallStateException e, TelephonyConnection connection,
+            Phone phone) {
+        int cause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
+        switch (e.getError()) {
+            case CallStateException.ERROR_OUT_OF_SERVICE:
+                cause = android.telephony.DisconnectCause.OUT_OF_SERVICE;
+                break;
+            case CallStateException.ERROR_POWER_OFF:
+                 cause = android.telephony.DisconnectCause.POWER_OFF;
+                 break;
+            case CallStateException.ERROR_ALREADY_DIALING:
+                 cause = android.telephony.DisconnectCause.ALREADY_DIALING;
+                 break;
+            case CallStateException.ERROR_CALL_RINGING:
+                 cause = android.telephony.DisconnectCause.CANT_CALL_WHILE_RINGING;
+                 break;
+            case CallStateException.ERROR_CALLING_DISABLED:
+                 cause = android.telephony.DisconnectCause.CALLING_DISABLED;
+                 break;
+            case CallStateException.ERROR_TOO_MANY_CALLS:
+                 cause = android.telephony.DisconnectCause.TOO_MANY_ONGOING_CALLS;
+                 break;
+            case CallStateException.ERROR_OTASP_PROVISIONING_IN_PROCESS:
+                 cause = android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS;
+                 break;
+        }
+        connection.setTelephonyConnectionDisconnected(
+                DisconnectCauseUtil.toTelecomDisconnectCause(cause, e.getMessage(),
+                        phone.getPhoneId()));
+        connection.close();
+    }
+
     private TelephonyConnection createConnectionFor(
             Phone phone,
             com.android.internal.telephony.Connection originalConnection,
             boolean isOutgoing,
             PhoneAccountHandle phoneAccountHandle,
             String telecomCallId) {
+            return createConnectionFor(phone, originalConnection, isOutgoing, phoneAccountHandle,
+                    telecomCallId, false);
+    }
+
+    private TelephonyConnection createConnectionFor(
+            Phone phone,
+            com.android.internal.telephony.Connection originalConnection,
+            boolean isOutgoing,
+            PhoneAccountHandle phoneAccountHandle,
+            String telecomCallId,
+            boolean isAdhocConference) {
         TelephonyConnection returnConnection = null;
         int phoneType = phone.getPhoneType();
+        int callDirection = isOutgoing ? android.telecom.Call.Details.DIRECTION_OUTGOING
+                : android.telecom.Call.Details.DIRECTION_INCOMING;
         if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
-            returnConnection = new GsmConnection(originalConnection, telecomCallId, isOutgoing);
+            returnConnection = new GsmConnection(originalConnection, telecomCallId, callDirection);
         } else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) {
             boolean allowsMute = allowsMute(phone);
             returnConnection = new CdmaConnection(originalConnection, mEmergencyTonePlayer,
-                    allowsMute, isOutgoing, telecomCallId);
+                    allowsMute, callDirection, telecomCallId);
         }
         if (returnConnection != null) {
-            // Listen to Telephony specific callbacks from the connection
-            returnConnection.addTelephonyConnectionListener(mTelephonyConnectionListener);
+            if (!isAdhocConference) {
+                // Listen to Telephony specific callbacks from the connection
+                returnConnection.addTelephonyConnectionListener(mTelephonyConnectionListener);
+            }
             returnConnection.setVideoPauseSupported(
                     TelecomAccountRegistry.getInstance(this).isVideoPauseSupported(
                             phoneAccountHandle));
@@ -1942,30 +2184,24 @@
                             return 1;
                         }
                         // sort by number of RadioAccessFamily Capabilities.
-                        int compare = Integer.bitCount(o1.capabilities) -
-                                Integer.bitCount(o2.capabilities);
+                        int compare = RadioAccessFamily.compare(o1.capabilities, o2.capabilities);
                         if (compare == 0) {
-                            // Sort by highest RAF Capability if the number is the same.
-                            compare = RadioAccessFamily.getHighestRafCapability(o1.capabilities) -
-                                    RadioAccessFamily.getHighestRafCapability(o2.capabilities);
-                            if (compare == 0) {
-                                if (firstOccupiedSlot != null) {
-                                    // If the RAF capability is the same, choose based on whether or
-                                    // not any of the slots are occupied with a SIM card (if both
-                                    // are, always choose the first).
-                                    if (o1.slotId == firstOccupiedSlot.getPhoneId()) {
-                                        return 1;
-                                    } else if (o2.slotId == firstOccupiedSlot.getPhoneId()) {
-                                        return -1;
-                                    }
-                                } else {
-                                    // No slots have SIMs detected in them, so weight the default
-                                    // Phone Id greater than the others.
-                                    if (o1.slotId == defaultPhoneId) {
-                                        return 1;
-                                    } else if (o2.slotId == defaultPhoneId) {
-                                        return -1;
-                                    }
+                            if (firstOccupiedSlot != null) {
+                                // If the RAF capability is the same, choose based on whether or
+                                // not any of the slots are occupied with a SIM card (if both
+                                // are, always choose the first).
+                                if (o1.slotId == firstOccupiedSlot.getPhoneId()) {
+                                    return 1;
+                                } else if (o2.slotId == firstOccupiedSlot.getPhoneId()) {
+                                    return -1;
+                                }
+                            } else {
+                                // No slots have SIMs detected in them, so weight the default
+                                // Phone Id greater than the others.
+                                if (o1.slotId == defaultPhoneId) {
+                                    return 1;
+                                } else if (o2.slotId == defaultPhoneId) {
+                                    return -1;
                                 }
                             }
                         }
@@ -2134,6 +2370,78 @@
         }
     }
 
+    private boolean showDataDialog(Phone phone, String number) {
+        boolean ret = false;
+        final Context context = getApplicationContext();
+        String suppKey = MmiCodeUtil.getSuppServiceKey(number);
+        if (suppKey != null) {
+            boolean clirOverUtPrecautions = false;
+            boolean cfOverUtPrecautions = false;
+            boolean cbOverUtPrecautions = false;
+            boolean cwOverUtPrecautions = false;
+
+            CarrierConfigManager cfgManager = (CarrierConfigManager)
+                phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+            if (cfgManager != null) {
+                clirOverUtPrecautions = cfgManager.getConfigForSubId(phone.getSubId())
+                    .getBoolean(CarrierConfigManager.KEY_CALLER_ID_OVER_UT_WARNING_BOOL);
+                cfOverUtPrecautions = cfgManager.getConfigForSubId(phone.getSubId())
+                    .getBoolean(CarrierConfigManager.KEY_CALL_FORWARDING_OVER_UT_WARNING_BOOL);
+                cbOverUtPrecautions = cfgManager.getConfigForSubId(phone.getSubId())
+                    .getBoolean(CarrierConfigManager.KEY_CALL_BARRING_OVER_UT_WARNING_BOOL);
+                cwOverUtPrecautions = cfgManager.getConfigForSubId(phone.getSubId())
+                    .getBoolean(CarrierConfigManager.KEY_CALL_WAITING_OVER_UT_WARNING_BOOL);
+            }
+
+            boolean isSsOverUtPrecautions = SuppServicesUiUtil
+                .isSsOverUtPrecautions(context, phone);
+            if (isSsOverUtPrecautions) {
+                boolean showDialog = false;
+                if (suppKey == MmiCodeUtil.BUTTON_CLIR_KEY && clirOverUtPrecautions) {
+                    showDialog = true;
+                } else if (suppKey == MmiCodeUtil.CALL_FORWARDING_KEY && cfOverUtPrecautions) {
+                    showDialog = true;
+                } else if (suppKey == MmiCodeUtil.CALL_BARRING_KEY && cbOverUtPrecautions) {
+                    showDialog = true;
+                } else if (suppKey == MmiCodeUtil.BUTTON_CW_KEY && cwOverUtPrecautions) {
+                    showDialog = true;
+                }
+
+                if (showDialog) {
+                    Log.d(this, "Creating UT Data enable dialog");
+                    String message = SuppServicesUiUtil.makeMessage(context, suppKey, phone);
+                    AlertDialog.Builder builder = new AlertDialog.Builder(context);
+                    DialogInterface.OnClickListener networkSettingsClickListener =
+                            new Dialog.OnClickListener() {
+                                @Override
+                                public void onClick(DialogInterface dialog, int which) {
+                                    Intent intent = new Intent(Intent.ACTION_MAIN);
+                                    ComponentName mobileNetworkSettingsComponent
+                                        = new ComponentName(
+                                                context.getString(
+                                                    R.string.mobile_network_settings_package),
+                                                context.getString(
+                                                    R.string.mobile_network_settings_class));
+                                    intent.setComponent(mobileNetworkSettingsComponent);
+                                    context.startActivity(intent);
+                                }
+                            };
+                    Dialog dialog = builder.setMessage(message)
+                        .setNeutralButton(context.getResources().getString(
+                                R.string.settings_label),
+                                networkSettingsClickListener)
+                        .setPositiveButton(context.getResources().getString(
+                                R.string.supp_service_over_ut_precautions_dialog_dismiss), null)
+                        .create();
+                    dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+                    dialog.show();
+                    ret = true;
+                }
+            }
+        }
+        return ret;
+    }
+
     /**
      * Adds a {@link Conference} to the telephony ConnectionService and registers a listener for
      * changes to the conference.  Should be used instead of {@link #addConference(Conference)}.
diff --git a/src/com/android/services/telephony/rcs/RcsFeatureController.java b/src/com/android/services/telephony/rcs/RcsFeatureController.java
new file mode 100644
index 0000000..fcfe312
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/RcsFeatureController.java
@@ -0,0 +1,442 @@
+/*
+ * 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.annotation.AnyThread;
+import android.content.Context;
+import android.net.Uri;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IImsRegistrationCallback;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.ims.FeatureConnector;
+import com.android.ims.IFeatureConnector;
+import com.android.ims.RcsFeatureManager;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.imsphone.ImsRegistrationCallbackHelper;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Contains the RCS feature implementations that are associated with this slot's RcsFeature.
+ */
+@AnyThread
+public class RcsFeatureController {
+    private static final String LOG_TAG = "RcsFeatureController";
+
+    /**
+     * Interface used by RCS features that need to listen for when the associated service has been
+     * connected.
+     */
+    public interface Feature {
+        /**
+         * The RcsFeature has been connected to the framework and is ready.
+         */
+        void onRcsConnected(RcsFeatureManager manager);
+
+        /**
+         * The framework has lost the binding to the RcsFeature or it is in the process of changing.
+         */
+        void onRcsDisconnected();
+
+        /**
+         * The subscription associated with the slot this controller is bound to has changed or its
+         * carrier configuration has changed.
+         */
+        void onAssociatedSubscriptionUpdated(int subId);
+
+        /**
+         * Called when the feature should be destroyed.
+         */
+        void onDestroy();
+    }
+
+    /**
+     * Used to inject FeatureConnector instances for testing.
+     */
+    @VisibleForTesting
+    public interface FeatureConnectorFactory<T extends IFeatureConnector> {
+        /**
+         * @return a {@link FeatureConnector} associated for the given {@link IFeatureConnector}
+         * and slot id.
+         */
+        FeatureConnector<T> create(Context context, int slotId,
+                FeatureConnector.Listener<T> listener, Executor executor, String tag);
+    }
+
+    /**
+     * Used to inject ImsRegistrationCallbackHelper instances for testing.
+     */
+    @VisibleForTesting
+    public interface RegistrationHelperFactory {
+        /**
+         * @return an {@link ImsRegistrationCallbackHelper}, which helps manage IMS registration
+         * state.
+         */
+        ImsRegistrationCallbackHelper create(
+                ImsRegistrationCallbackHelper.ImsRegistrationUpdate cb, Executor executor);
+    }
+
+    private FeatureConnectorFactory<RcsFeatureManager> mFeatureFactory = FeatureConnector::new;
+    private RegistrationHelperFactory mRegistrationHelperFactory =
+            ImsRegistrationCallbackHelper::new;
+
+    private final Map<Class<?>, Feature> mFeatures = new ArrayMap<>();
+    private final Context mContext;
+    private final ImsRegistrationCallbackHelper mImsRcsRegistrationHelper;
+    private final int mSlotId;
+    private final Object mLock = new Object();
+    private FeatureConnector<RcsFeatureManager> mFeatureConnector;
+    private RcsFeatureManager mFeatureManager;
+
+    private FeatureConnector.Listener<RcsFeatureManager> mFeatureConnectorListener =
+            new FeatureConnector.Listener<RcsFeatureManager>() {
+                @Override
+                public RcsFeatureManager getFeatureManager() {
+                    return new RcsFeatureManager(mContext, mSlotId);
+                }
+
+                @Override
+                public void connectionReady(RcsFeatureManager manager)
+                        throws com.android.ims.ImsException {
+                    if (manager == null) {
+                        logw("connectionReady returned null RcsFeatureManager");
+                        return;
+                    }
+                    try {
+                        // May throw ImsException if for some reason the connection to the
+                        // ImsService is gone.
+                        updateConnectionStatus(manager);
+                        setupConnectionToService(manager);
+                    } catch (ImsException e) {
+                        updateConnectionStatus(null /*manager*/);
+                        // Use deprecated Exception for compatibility.
+                        throw new com.android.ims.ImsException(e.getMessage(),
+                                ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);
+                    }
+                }
+
+                @Override
+                public void connectionUnavailable() {
+                    // Call before disabling connection to manager.
+                    removeConnectionToService();
+                    updateConnectionStatus(null /*manager*/);
+                }
+            };
+
+    private ImsRegistrationCallbackHelper.ImsRegistrationUpdate mRcsRegistrationUpdate = new
+            ImsRegistrationCallbackHelper.ImsRegistrationUpdate() {
+                @Override
+                public void handleImsRegistered(int imsRadioTech) {
+                }
+
+                @Override
+                public void handleImsRegistering(int imsRadioTech) {
+                }
+
+                @Override
+                public void handleImsUnregistered(ImsReasonInfo imsReasonInfo) {
+                }
+
+                @Override
+                public void handleImsSubscriberAssociatedUriChanged(Uri[] uris) {
+                }
+            };
+
+    public RcsFeatureController(Context context, int slotId) {
+        mContext = context;
+        mSlotId = slotId;
+        mImsRcsRegistrationHelper = mRegistrationHelperFactory.create(mRcsRegistrationUpdate,
+                mContext.getMainExecutor());
+    }
+
+    /**
+     * Should only be used to inject registration helpers for testing.
+     */
+    @VisibleForTesting
+    public RcsFeatureController(Context context, int slotId, RegistrationHelperFactory f) {
+        mContext = context;
+        mSlotId = slotId;
+        mRegistrationHelperFactory = f;
+        mImsRcsRegistrationHelper = mRegistrationHelperFactory.create(mRcsRegistrationUpdate,
+                mContext.getMainExecutor());
+    }
+
+    /**
+     * This method should be called after constructing an instance of this class to start the
+     * connection process to the associated RcsFeature.
+     */
+    public void connect() {
+        synchronized (mLock) {
+            if (mFeatureConnector != null) return;
+            mFeatureConnector = mFeatureFactory.create(mContext, mSlotId, mFeatureConnectorListener,
+                    mContext.getMainExecutor(), LOG_TAG);
+            mFeatureConnector.connect();
+        }
+    }
+
+    /**
+     * Adds a {@link Feature} to be tracked by this FeatureController.
+     */
+    public <T extends Feature> void addFeature(T connector, Class<T> clazz) {
+        synchronized (mLock) {
+            mFeatures.put(clazz, connector);
+        }
+        RcsFeatureManager manager = getFeatureManager();
+        if (manager != null) {
+            connector.onRcsConnected(manager);
+        } else {
+            connector.onRcsDisconnected();
+        }
+    }
+
+    /**
+     * @return The RCS feature implementation tracked by this controller.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T getFeature(Class<T> clazz) {
+        synchronized (mLock) {
+            return (T) mFeatures.get(clazz);
+        }
+    }
+
+    /**
+     * Removes the feature associated with this class.
+     */
+    public <T> void removeFeature(Class<T> clazz) {
+        synchronized (mLock) {
+            RcsFeatureController.Feature feature = mFeatures.remove(clazz);
+            feature.onDestroy();
+        }
+    }
+
+    /**
+     * @return true if this controller has features it is actively tracking.
+     */
+    public boolean hasActiveFeatures() {
+        synchronized (mLock) {
+            return mFeatures.size() > 0;
+        }
+    }
+
+    /**
+     * Update the subscription associated with this controller.
+     */
+    public void updateAssociatedSubscription(int newSubId) {
+        RcsFeatureManager manager = getFeatureManager();
+        if (manager != null) {
+            try {
+                manager.updateCapabilities();
+            } catch (ImsException e) {
+                Log.w(LOG_TAG, "associatedSubscriptionChanged failed:" + e);
+            }
+        }
+        synchronized (mLock) {
+            for (Feature c : mFeatures.values()) {
+                c.onAssociatedSubscriptionUpdated(newSubId);
+            }
+        }
+    }
+
+    /**
+     * Call before this controller is destroyed to tear down associated features.
+     */
+    public void destroy() {
+        synchronized (mLock) {
+            Log.i(LOG_TAG, "destroy: slotId=" + mSlotId);
+            if (mFeatureConnector != null) {
+                mFeatureConnector.disconnect();
+            }
+            for (Feature c : mFeatures.values()) {
+                c.onRcsDisconnected();
+                c.onDestroy();
+            }
+            mFeatures.clear();
+        }
+    }
+
+    @VisibleForTesting
+    public void setFeatureConnectorFactory(FeatureConnectorFactory factory) {
+        mFeatureFactory = factory;
+    }
+
+    /**
+     * Add a {@link RegistrationManager.RegistrationCallback} callback that gets called when IMS
+     * registration has changed for a specific subscription.
+     */
+    public void registerImsRegistrationCallback(int subId, IImsRegistrationCallback callback)
+            throws ImsException {
+        RcsFeatureManager manager = getFeatureManager();
+        if (manager == null) {
+            throw new ImsException("Service is not available",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+        manager.registerImsRegistrationCallback(subId, callback);
+    }
+
+    /**
+     * Removes a previously registered {@link RegistrationManager.RegistrationCallback} callback
+     * that is associated with a specific subscription.
+     */
+    public void unregisterImsRegistrationCallback(int subId, IImsRegistrationCallback callback) {
+        RcsFeatureManager manager = getFeatureManager();
+        if (manager != null) {
+            manager.unregisterImsRegistrationCallback(subId, callback);
+        }
+    }
+
+    /**
+     * Register an {@link ImsRcsManager.AvailabilityCallback} with the associated RcsFeature,
+     * which will provide availability updates.
+     */
+    public void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback)
+            throws ImsException {
+        RcsFeatureManager manager = getFeatureManager();
+        if (manager == null) {
+            throw new ImsException("Service is not available",
+                    ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+        manager.registerRcsAvailabilityCallback(subId, callback);
+    }
+
+    /**
+     * Remove a registered {@link ImsRcsManager.AvailabilityCallback} from the RcsFeature.
+     */
+    public void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback callback) {
+        RcsFeatureManager manager = getFeatureManager();
+        if (manager != null) {
+            manager.unregisterRcsAvailabilityCallback(subId, callback);
+        }
+    }
+
+    /**
+     * Query for the specific capability.
+     */
+    public boolean isCapable(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.isCapable(capability, radioTech);
+    }
+
+    /**
+     * Query the availability of an IMS RCS capability.
+     */
+    public boolean isAvailable(int capability) 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);
+    }
+
+    /**
+     * Get the IMS RCS registration technology for this Phone.
+     */
+    public void getRegistrationTech(Consumer<Integer> callback) {
+        RcsFeatureManager manager = getFeatureManager();
+        if (manager != null) {
+            manager.getImsRegistrationTech(callback);
+        }
+        callback.accept(ImsRegistrationImplBase.REGISTRATION_TECH_NONE);
+    }
+
+    /**
+     * Retrieve the current RCS registration state.
+     */
+    public void getRegistrationState(Consumer<Integer> callback) {
+        callback.accept(mImsRcsRegistrationHelper.getImsRegistrationState());
+    }
+
+    private void setupConnectionToService(RcsFeatureManager manager) throws ImsException {
+        // Open persistent listener connection, sends RcsFeature#onFeatureReady.
+        manager.openConnection();
+        manager.updateCapabilities();
+        manager.registerImsRegistrationCallback(mImsRcsRegistrationHelper.getCallbackBinder());
+    }
+
+    private void removeConnectionToService() {
+        RcsFeatureManager manager = getFeatureManager();
+        if (manager != null) {
+            manager.unregisterImsRegistrationCallback(
+                    mImsRcsRegistrationHelper.getCallbackBinder());
+            // Remove persistent listener connection.
+            manager.releaseConnection();
+        }
+        mImsRcsRegistrationHelper.reset();
+    }
+
+    private void updateConnectionStatus(RcsFeatureManager manager) {
+        synchronized (mLock) {
+            mFeatureManager = manager;
+            if (mFeatureManager != null) {
+                for (Feature c : mFeatures.values()) {
+                    c.onRcsConnected(manager);
+                }
+            } else {
+                for (Feature c : mFeatures.values()) {
+                    c.onRcsDisconnected();
+                }
+            }
+        }
+    }
+
+    private RcsFeatureManager getFeatureManager() {
+        synchronized (mLock) {
+            return mFeatureManager;
+        }
+    }
+
+    /**
+     * Dump this controller's instance information for usage in dumpsys.
+     */
+    public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+        IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
+        pw.print("slotId=");
+        pw.println(mSlotId);
+        pw.print("RegistrationState=");
+        pw.println(mImsRcsRegistrationHelper.getImsRegistrationState());
+        pw.print("connected=");
+        synchronized (mLock) {
+            pw.println(mFeatureManager != null);
+        }
+    }
+
+    private void logw(String log) {
+        Log.w(LOG_TAG, getLogPrefix().append(log).toString());
+    }
+
+    private StringBuilder getLogPrefix() {
+        StringBuilder sb = new StringBuilder("[");
+        sb.append(mSlotId);
+        sb.append("] ");
+        return sb;
+    }
+}
diff --git a/src/com/android/services/telephony/rcs/TelephonyRcsService.java b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
new file mode 100644
index 0000000..69d8f82
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
@@ -0,0 +1,285 @@
+/*
+ * 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.annotation.AnyThread;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.PhoneConfigurationManager;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Singleton service setup to manage RCS related services that the platform provides such as User
+ * Capability Exchange.
+ */
+@AnyThread
+public class TelephonyRcsService {
+
+    private static final String LOG_TAG = "TelephonyRcsService";
+
+    /**
+     * Used to inject RcsFeatureController and UserCapabilityExchangeImpl instances for testing.
+     */
+    @VisibleForTesting
+    public interface FeatureFactory {
+        /**
+         * @return an {@link RcsFeatureController} assoicated with the slot specified.
+         */
+        RcsFeatureController createController(Context context, int slotId);
+
+        /**
+         * @return an instance of {@link UserCapabilityExchangeImpl} associated with the slot
+         * specified.
+         */
+        UserCapabilityExchangeImpl createUserCapabilityExchange(Context context, int slotId,
+                int subId);
+    }
+
+    private FeatureFactory mFeatureFactory = new FeatureFactory() {
+        @Override
+        public RcsFeatureController createController(Context context, int slotId) {
+            return new RcsFeatureController(context, slotId);
+        }
+
+        @Override
+        public UserCapabilityExchangeImpl createUserCapabilityExchange(Context context, int slotId,
+                int subId) {
+            return new UserCapabilityExchangeImpl(context, slotId, subId);
+        }
+    };
+
+    // Notifies this service that there has been a change in available slots.
+    private static final int HANDLER_MSIM_CONFIGURATION_CHANGE = 1;
+
+    private final Context mContext;
+    private final Object mLock = new Object();
+    private int mNumSlots;
+
+    // Maps slot ID -> RcsFeatureController.
+    private SparseArray<RcsFeatureController> mFeatureControllers;
+
+    private BroadcastReceiver mCarrierConfigChangedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent == null) {
+                return;
+            }
+            if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
+                Bundle bundle = intent.getExtras();
+                if (bundle == null) {
+                    return;
+                }
+                int slotId = bundle.getInt(CarrierConfigManager.EXTRA_SLOT_INDEX,
+                        SubscriptionManager.INVALID_PHONE_INDEX);
+                int subId = bundle.getInt(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
+                        SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+                updateFeatureControllerSubscription(slotId, subId);
+            }
+        }
+    };
+
+    private Handler mHandler = new Handler(Looper.getMainLooper(), (msg) -> {
+        switch (msg.what) {
+            case HANDLER_MSIM_CONFIGURATION_CHANGE: {
+                AsyncResult result = (AsyncResult) msg.obj;
+                Integer numSlots = (Integer) result.result;
+                if (numSlots == null) {
+                    Log.w(LOG_TAG, "msim config change with null num slots.");
+                    break;
+                }
+                updateFeatureControllerSize(numSlots);
+                break;
+            }
+            default:
+                return false;
+        }
+        return true;
+    });
+
+    public TelephonyRcsService(Context context, int numSlots) {
+        mContext = context;
+        mNumSlots = numSlots;
+        mFeatureControllers = new SparseArray<>(numSlots);
+    }
+
+    /**
+     * @return the {@link RcsFeatureController} associated with the given slot.
+     */
+    public RcsFeatureController getFeatureController(int slotId) {
+        synchronized (mLock) {
+            return mFeatureControllers.get(slotId);
+        }
+    }
+
+    /**
+     * Called after instance creation to initialize internal structures as well as register for
+     * system callbacks.
+     */
+    public void initialize() {
+        updateFeatureControllerSize(mNumSlots);
+
+        PhoneConfigurationManager.registerForMultiSimConfigChange(mHandler,
+                HANDLER_MSIM_CONFIGURATION_CHANGE, null);
+        mContext.registerReceiver(mCarrierConfigChangedReceiver, new IntentFilter(
+                CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+    }
+
+    @VisibleForTesting
+    public void setFeatureFactory(FeatureFactory f) {
+        mFeatureFactory = f;
+    }
+
+    /**
+     * Update the number of {@link RcsFeatureController}s that are created based on the number of
+     * active slots on the device.
+     */
+    @VisibleForTesting
+    public void updateFeatureControllerSize(int newNumSlots) {
+        synchronized (mLock) {
+            int oldNumSlots = mFeatureControllers.size();
+            if (oldNumSlots == newNumSlots) {
+                return;
+            }
+            Log.i(LOG_TAG, "updateFeatureControllers: oldSlots=" + oldNumSlots + ", newNumSlots="
+                    + newNumSlots);
+            mNumSlots = newNumSlots;
+            if (oldNumSlots < newNumSlots) {
+                for (int i = oldNumSlots; i < newNumSlots; i++) {
+                    RcsFeatureController c = constructFeatureController(i);
+                    // Do not add feature controllers for inactive subscriptions
+                    if (c.hasActiveFeatures()) {
+                        mFeatureControllers.put(i, c);
+                    }
+                }
+            } else {
+                for (int i = (oldNumSlots - 1); i > (newNumSlots - 1); i--) {
+                    RcsFeatureController c = mFeatureControllers.get(i);
+                    if (c != null) {
+                        mFeatureControllers.remove(i);
+                        c.destroy();
+                    }
+                }
+            }
+        }
+    }
+
+    private void updateFeatureControllerSubscription(int slotId, int newSubId) {
+        synchronized (mLock) {
+            RcsFeatureController f = mFeatureControllers.get(slotId);
+            Log.i(LOG_TAG, "updateFeatureControllerSubscription: slotId=" + slotId + " newSubId="
+                    + newSubId + ", existing feature=" + (f != null));
+            if (SubscriptionManager.isValidSubscriptionId(newSubId)) {
+                if (f == null) {
+                    // A controller doesn't exist for this slot yet.
+                    f = mFeatureFactory.createController(mContext, slotId);
+                    updateSupportedFeatures(f, slotId, newSubId);
+                    if (f.hasActiveFeatures()) mFeatureControllers.put(slotId, f);
+                } else {
+                    updateSupportedFeatures(f, slotId, newSubId);
+                    // Do not keep an empty container around.
+                    if (!f.hasActiveFeatures()) {
+                        f.destroy();
+                        mFeatureControllers.remove(slotId);
+                    }
+                }
+            }
+            if (f != null) f.updateAssociatedSubscription(newSubId);
+        }
+    }
+
+    private RcsFeatureController constructFeatureController(int slotId) {
+        RcsFeatureController c = mFeatureFactory.createController(mContext, slotId);
+        int subId = getSubscriptionFromSlot(slotId);
+        updateSupportedFeatures(c, slotId, subId);
+        return c;
+    }
+
+    private void updateSupportedFeatures(RcsFeatureController c, int slotId, int subId) {
+        if (doesSubscriptionSupportPresence(subId)) {
+            if (c.getFeature(UserCapabilityExchangeImpl.class) == null) {
+                c.addFeature(mFeatureFactory.createUserCapabilityExchange(mContext, slotId, subId),
+                        UserCapabilityExchangeImpl.class);
+            }
+        } else {
+            if (c.getFeature(UserCapabilityExchangeImpl.class) != null) {
+                c.removeFeature(UserCapabilityExchangeImpl.class);
+            }
+        }
+        // Only start the connection procedure if we have active features.
+        if (c.hasActiveFeatures()) c.connect();
+    }
+
+    private boolean doesSubscriptionSupportPresence(int subId) {
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) return false;
+        CarrierConfigManager carrierConfigManager =
+                mContext.getSystemService(CarrierConfigManager.class);
+        if (carrierConfigManager == null) return false;
+        boolean supportsUce = carrierConfigManager.getConfigForSubId(subId).getBoolean(
+                CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL);
+        supportsUce |= carrierConfigManager.getConfigForSubId(subId).getBoolean(
+                CarrierConfigManager.KEY_USE_RCS_SIP_OPTIONS_BOOL);
+        return supportsUce;
+    }
+
+
+    private int getSubscriptionFromSlot(int slotId) {
+        SubscriptionManager manager = mContext.getSystemService(SubscriptionManager.class);
+        if (manager == null) {
+            Log.w(LOG_TAG, "Couldn't find SubscriptionManager for slotId=" + slotId);
+            return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        }
+        int[] subIds = manager.getSubscriptionIds(slotId);
+        if (subIds != null && subIds.length > 0) {
+            return subIds[0];
+        }
+        return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    }
+
+    /**
+     * Dump this instance into a readable format for dumpsys usage.
+     */
+    public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+        IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
+        pw.println("RcsFeatureControllers:");
+        pw.increaseIndent();
+        synchronized (mLock) {
+            for (int i = 0; i < mNumSlots; i++) {
+                RcsFeatureController f = mFeatureControllers.get(i);
+                if (f == null) continue;
+                pw.increaseIndent();
+                f.dump(fd, printWriter, args);
+                pw.decreaseIndent();
+            }
+        }
+        pw.decreaseIndent();
+    }
+}
diff --git a/src/com/android/services/telephony/rcs/UserCapabilityExchangeImpl.java b/src/com/android/services/telephony/rcs/UserCapabilityExchangeImpl.java
new file mode 100644
index 0000000..9b0a303
--- /dev/null
+++ b/src/com/android/services/telephony/rcs/UserCapabilityExchangeImpl.java
@@ -0,0 +1,830 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.services.telephony.rcs;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.provider.Telephony;
+import android.telecom.TelecomManager;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsManager;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.RcsUceAdapter;
+import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.aidl.IRcsUceControllerCallback;
+import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.RcsCapabilityExchange;
+import android.telephony.ims.stub.RcsPresenceExchangeImplBase;
+import android.util.Log;
+
+import com.android.ims.RcsFeatureManager;
+import com.android.ims.RcsFeatureManager.RcsFeatureCallbacks;
+import com.android.ims.ResultCode;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.phone.R;
+import com.android.service.ims.presence.ContactCapabilityResponse;
+import com.android.service.ims.presence.PresenceBase;
+import com.android.service.ims.presence.PresencePublication;
+import com.android.service.ims.presence.PresencePublisher;
+import com.android.service.ims.presence.PresenceSubscriber;
+import com.android.service.ims.presence.SubscribePublisher;
+
+import java.lang.ref.WeakReference;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+/**
+ * Implements User Capability Exchange using Presence.
+ */
+public class UserCapabilityExchangeImpl implements RcsFeatureController.Feature, SubscribePublisher,
+        PresencePublisher {
+
+    private static final String LOG_TAG = "RcsUceImpl";
+
+    private final int mSlotId;
+    private volatile int mSubId;
+    private volatile boolean mImsContentChangedCallbackRegistered = false;
+    // The result of requesting publish
+    private volatile int mPublishState = PresenceBase.PUBLISH_STATE_NOT_PUBLISHED;
+    // The network type which IMS registers on
+    private volatile int mNetworkRegistrationType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+    // The MMTel capabilities of this subscription Id
+    private MmTelFeature.MmTelCapabilities mMmTelCapabilities;
+    private final Object mCapabilitiesLock = new Object();
+
+    private final Context mContext;
+    private final UceImplHandler mUceImplHandler;
+    private RcsFeatureManager mRcsFeatureManager;
+
+    private final PresencePublication mPresencePublication;
+    private final PresenceSubscriber mPresenceSubscriber;
+
+    // The task Ids of updating capabilities
+    private final Set<Integer> mRequestingPublishTaskIds = new HashSet<>();
+
+    // The callbacks to notify publish state changed.
+    private final RemoteCallbackList<IRcsUcePublishStateCallback> mPublishStateCallbacks;
+
+    private final ConcurrentHashMap<Integer, IRcsUceControllerCallback> mPendingCapabilityRequests =
+            new ConcurrentHashMap<>();
+
+    UserCapabilityExchangeImpl(Context context, int slotId, int subId) {
+        mSlotId = slotId;
+        mSubId = subId;
+        logi("created");
+
+        mContext = context;
+        mPublishStateCallbacks = new RemoteCallbackList<>();
+
+        HandlerThread handlerThread = new HandlerThread("UceImplHandlerThread");
+        handlerThread.start();
+        mUceImplHandler = new UceImplHandler(this, handlerThread.getLooper());
+
+        String[] volteError = context.getResources().getStringArray(
+                R.array.config_volte_provision_error_on_publish_response);
+        String[] rcsError = context.getResources().getStringArray(
+                R.array.config_rcs_provision_error_on_publish_response);
+
+        // Initialize PresencePublication
+        mPresencePublication = new PresencePublication(null /*PresencePublisher*/, context,
+                volteError, rcsError);
+        // Initialize PresenceSubscriber
+        mPresenceSubscriber = new PresenceSubscriber(null /*SubscribePublisher*/, context,
+                volteError, rcsError);
+
+        onAssociatedSubscriptionUpdated(mSubId);
+        registerReceivers();
+    }
+
+    @VisibleForTesting
+    UserCapabilityExchangeImpl(Context context, int slotId, int subId, Looper looper,
+            PresencePublication presencePublication, PresenceSubscriber presenceSubscriber,
+            RemoteCallbackList<IRcsUcePublishStateCallback> publishStateCallbacks) {
+        mSlotId = slotId;
+        mSubId = subId;
+        mContext = context;
+        mPublishStateCallbacks = publishStateCallbacks;
+        mUceImplHandler = new UceImplHandler(this, looper);
+        mPresencePublication = presencePublication;
+        mPresenceSubscriber = presenceSubscriber;
+        onAssociatedSubscriptionUpdated(mSubId);
+        registerReceivers();
+    }
+
+    // Runs on main thread.
+    @Override
+    public void onRcsConnected(RcsFeatureManager rcsFeatureManager) {
+        logi("onRcsConnected");
+        mRcsFeatureManager = rcsFeatureManager;
+        mRcsFeatureManager.addFeatureListenerCallback(mRcsFeatureCallback);
+
+        mPresencePublication.updatePresencePublisher(this);
+        mPresenceSubscriber.updatePresenceSubscriber(this);
+    }
+
+    // Runs on main thread.
+    @Override
+    public void onRcsDisconnected() {
+        logi("onRcsDisconnected");
+        mPresencePublication.removePresencePublisher();
+        mPresenceSubscriber.removePresenceSubscriber();
+
+        if (mRcsFeatureManager != null) {
+            mRcsFeatureManager.releaseConnection();
+            mRcsFeatureManager = null;
+        }
+    }
+
+    // Runs on main thread.
+    @Override
+    public void onAssociatedSubscriptionUpdated(int subId) {
+        logi("onAssociatedSubscriptionUpdated: new subId=" + subId);
+
+        // Listen to the IMS content changed with new subId.
+        mUceImplHandler.registerImsContentChangedReceiver(subId);
+
+        mSubId = subId;
+        mPresencePublication.handleAssociatedSubscriptionChanged(subId);
+        mPresenceSubscriber.handleAssociatedSubscriptionChanged(subId);
+    }
+
+    /**
+     * Should be called before destroying this instance.
+     * This instance is not usable after this method is called.
+     */
+    // Called on main thread.
+    public void onDestroy() {
+        logi("onDestroy");
+        mUceImplHandler.getLooper().quit();
+        unregisterReceivers();
+        unregisterImsProvisionCallback(mSubId);
+        onRcsDisconnected();
+    }
+
+    /**
+     * @return the UCE Publish state.
+     */
+    // May happen on a Binder thread, PresencePublication locks to get result.
+    public int getUcePublishState() {
+        int publishState = mPresencePublication.getPublishState();
+        return toUcePublishState(publishState);
+    }
+
+    @VisibleForTesting
+    public UceImplHandler getHandler() {
+        return mUceImplHandler;
+    }
+
+    /**
+     * Register receiver to receive UCE publish state changed.
+     */
+    public void registerPublishStateCallback(IRcsUcePublishStateCallback c) {
+        synchronized (mPublishStateCallbacks) {
+            mPublishStateCallbacks.register(c);
+        }
+    }
+
+    /**
+     * Unregister UCE publish state callback.
+     */
+    public void unregisterUcePublishStateCallback(IRcsUcePublishStateCallback c) {
+        synchronized (mPublishStateCallbacks) {
+            mPublishStateCallbacks.unregister(c);
+        }
+    }
+
+    private void clearPublishStateCallbacks() {
+        synchronized (mPublishStateCallbacks) {
+            logi("clearPublishStateCallbacks");
+            final int lastIndex = mPublishStateCallbacks.getRegisteredCallbackCount() - 1;
+            for (int index = lastIndex; index >= 0; index--) {
+                IRcsUcePublishStateCallback callback =
+                        mPublishStateCallbacks.getRegisteredCallbackItem(index);
+                mPublishStateCallbacks.unregister(callback);
+            }
+        }
+    }
+
+    private void notifyPublishStateChanged(@PresenceBase.PresencePublishState int state) {
+        int result = toUcePublishState(state);
+        mPublishStateCallbacks.broadcast(c -> {
+            try {
+                c.onPublishStateChanged(result);
+            } catch (RemoteException e) {
+                logw("notifyPublishStateChanged error: " + e);
+            }
+        });
+    }
+
+    /**
+     * Perform a capabilities request and call {@link IRcsUceControllerCallback} with the result.
+     */
+    // May happen on a Binder thread, PresenceSubscriber locks when requesting Capabilities.
+    public void requestCapabilities(List<Uri> contactNumbers, IRcsUceControllerCallback c) {
+        List<String> numbers = contactNumbers.stream()
+                .map(UserCapabilityExchangeImpl::getNumberFromUri).collect(Collectors.toList());
+        int taskId = mPresenceSubscriber.requestCapability(numbers,
+                new ContactCapabilityResponse() {
+                    @Override
+                    public void onSuccess(int reqId) {
+                        logi("onSuccess called for reqId:" + reqId);
+                    }
+
+                    @Override
+                    public void onError(int reqId, int resultCode) {
+                        IRcsUceControllerCallback c = mPendingCapabilityRequests.remove(reqId);
+                        try {
+                            if (c != null) {
+                                c.onError(toUceError(resultCode));
+                            } else {
+                                logw("onError called for unknown reqId:" + reqId);
+                            }
+                        } catch (RemoteException e) {
+                            logi("Calling back to dead service");
+                        }
+                    }
+
+                    @Override
+                    public void onFinish(int reqId) {
+                        logi("onFinish called for reqId:" + reqId);
+                    }
+
+                    @Override
+                    public void onTimeout(int reqId) {
+                        IRcsUceControllerCallback c = mPendingCapabilityRequests.remove(reqId);
+                        try {
+                            if (c != null) {
+                                c.onError(RcsUceAdapter.ERROR_REQUEST_TIMEOUT);
+                            } else {
+                                logw("onTimeout called for unknown reqId:" + reqId);
+                            }
+                        } catch (RemoteException e) {
+                            logi("Calling back to dead service");
+                        }
+                    }
+
+                    @Override
+                    public void onCapabilitiesUpdated(int reqId,
+                            List<RcsContactUceCapability> contactCapabilities,
+                            boolean updateLastTimestamp) {
+                        IRcsUceControllerCallback c = mPendingCapabilityRequests.remove(reqId);
+                        try {
+                            if (c != null) {
+                                c.onCapabilitiesReceived(contactCapabilities);
+                            } else {
+                                logw("onCapabilitiesUpdated, unknown reqId:" + reqId);
+                            }
+                        } catch (RemoteException e) {
+                            logw("onCapabilitiesUpdated on dead service");
+                        }
+                    }
+                });
+        if (taskId < 0) {
+            try {
+                c.onError(toUceError(taskId));
+                return;
+            } catch (RemoteException e) {
+                logi("Calling back to dead service");
+            }
+        }
+        mPendingCapabilityRequests.put(taskId, c);
+    }
+
+    /**
+     * The feature callback is to receive the request and update from RcsPresExchangeImplBase
+     */
+    @VisibleForTesting
+    public RcsFeatureCallbacks mRcsFeatureCallback = new RcsFeatureCallbacks() {
+        public void onCommandUpdate(int commandCode, int operationToken) {
+            logi("onCommandUpdate: code=" + commandCode + ", token=" + operationToken);
+            onCommandUpdateForPublishRequest(commandCode, operationToken);
+        }
+
+        /** See {@link RcsPresenceExchangeImplBase#onNetworkResponse(int, String, int)} */
+        public void onNetworkResponse(int responseCode, String reason, int operationToken) {
+            logi("onNetworkResponse: code=" + responseCode + ", reason=" + reason
+                    + ", operationToken=" + operationToken);
+            onNetworkResponseForPublishRequest(responseCode, reason, operationToken);
+        }
+
+        /** See {@link RcsPresenceExchangeImplBase#onCapabilityRequestResponse(List, int)} */
+        public void onCapabilityRequestResponsePresence(List<RcsContactUceCapability> infos,
+                int operationToken) {
+
+        }
+
+        /** See {@link RcsPresenceExchangeImplBase#onNotifyUpdateCapabilites(int)} */
+        public void onNotifyUpdateCapabilities(int publishTriggerType) {
+            logi("onNotifyUpdateCapabilities: type=" + publishTriggerType);
+            mUceImplHandler.notifyUpdateCapabilities(publishTriggerType);
+        }
+
+        /** See {@link RcsPresenceExchangeImplBase#onUnpublish()} */
+        public void onUnpublish() {
+            logi("onUnpublish");
+            mUceImplHandler.unpublish();
+        }
+    };
+
+    private static class UceImplHandler extends Handler {
+        private static final int EVENT_REGISTER_IMS_CHANGED_RECEIVER = 1;
+        private static final int EVENT_NOTIFY_UPDATE_CAPABILITIES = 2;
+        private static final int EVENT_UNPUBLISH = 3;
+
+        private static final int REGISTER_IMS_CHANGED_DELAY = 10000;  //10 seconds
+
+        private final WeakReference<UserCapabilityExchangeImpl> mUceImplRef;
+
+        UceImplHandler(UserCapabilityExchangeImpl uceImpl, Looper looper) {
+            super(looper);
+            mUceImplRef = new WeakReference(uceImpl);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            UserCapabilityExchangeImpl uceImpl = mUceImplRef.get();
+            if (uceImpl == null) {
+                return;
+            }
+            switch (msg.what) {
+                case EVENT_REGISTER_IMS_CHANGED_RECEIVER:
+                    int subId = msg.arg1;
+                    uceImpl.registerImsContentChangedReceiverInternal(subId);
+                    break;
+                case EVENT_NOTIFY_UPDATE_CAPABILITIES:
+                    int publishTriggerType = msg.arg1;
+                    uceImpl.onNotifyUpdateCapabilities(publishTriggerType);
+                    break;
+                case EVENT_UNPUBLISH:
+                    uceImpl.updatePublisherState(PresenceBase.PUBLISH_STATE_NOT_PUBLISHED);
+                    break;
+                default:
+                    Log.w(LOG_TAG, "handleMessage: error=" + msg.what);
+                    break;
+            }
+        }
+
+        private void retryRegisteringImsContentChangedReceiver(int subId) {
+            sendRegisteringImsContentChangedMessage(subId, REGISTER_IMS_CHANGED_DELAY);
+        }
+
+        private void registerImsContentChangedReceiver(int subId) {
+            sendRegisteringImsContentChangedMessage(subId, 0);
+        }
+
+        private void sendRegisteringImsContentChangedMessage(int subId, int delay) {
+            if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+                return;
+            }
+            removeRegisteringImsContentChangedReceiver();
+            Message message = obtainMessage(EVENT_REGISTER_IMS_CHANGED_RECEIVER);
+            message.arg1 = subId;
+            sendMessageDelayed(message, delay);
+        }
+
+        private void removeRegisteringImsContentChangedReceiver() {
+            removeMessages(EVENT_REGISTER_IMS_CHANGED_RECEIVER);
+        }
+
+        private void notifyUpdateCapabilities(int publishTriggerType) {
+            Message message = obtainMessage(EVENT_NOTIFY_UPDATE_CAPABILITIES);
+            message.arg1 = publishTriggerType;
+            sendMessage(message);
+        }
+
+        private void unpublish() {
+            sendEmptyMessage(EVENT_UNPUBLISH);
+        }
+    }
+
+    private void onNotifyUpdateCapabilities(int publishTriggerType) {
+        mPresencePublication.onStackPublishRequested(publishTriggerType);
+    }
+
+    @Override
+    public @PresenceBase.PresencePublishState int getPublisherState() {
+        return mPublishState;
+    }
+
+    @Override
+    public int requestPublication(RcsContactUceCapability capabilities, String contactUri,
+            int taskId) {
+        if (mRcsFeatureManager == null) {
+            logw("requestPublication error: RcsFeatureManager is null.");
+            return ResultCode.ERROR_SERVICE_NOT_AVAILABLE;
+        }
+
+        logi("requestPublication: taskId=" + taskId);
+        addPublishRequestTaskId(taskId);
+
+        try {
+            mRcsFeatureManager.requestPublication(capabilities, taskId);
+        } catch (Exception ex) {
+            logw("requestPublication error: " + ex.getMessage());
+            removePublishRequestTaskId(taskId);
+            return ResultCode.PUBLISH_GENERIC_FAILURE;
+        }
+        return ResultCode.SUCCESS;
+    }
+
+    /*
+     * Handle the callback method RcsFeatureCallbacks#onCommandUpdate(int, int)
+     */
+    private void onCommandUpdateForPublishRequest(int commandCode, int operationToken) {
+        if (!isPublishRequestExisted(operationToken)) {
+            return;
+        }
+        int resultCode = ResultCode.SUCCESS;
+        if (commandCode != RcsCapabilityExchange.COMMAND_CODE_SUCCESS) {
+            logw("Command is failed: taskId=" + operationToken + ", code=" + commandCode);
+            removePublishRequestTaskId(operationToken);
+            resultCode = ResultCode.PUBLISH_GENERIC_FAILURE;
+        }
+        mPresencePublication.onCommandStatusUpdated(operationToken, operationToken, resultCode);
+    }
+
+    /*
+     * Handle the callback method RcsFeatureCallbacks#onNetworkResponse(int, String, int)
+     */
+    private void onNetworkResponseForPublishRequest(int responseCode, String reason,
+            int operationToken) {
+        if (!isPublishRequestExisted(operationToken)) {
+            return;
+        }
+        removePublishRequestTaskId(operationToken);
+        mPresencePublication.onSipResponse(operationToken, responseCode, reason);
+    }
+
+    @Override
+    public int requestCapability(String[] formatedContacts, int taskId) {
+        return 0;
+    }
+
+    @Override
+    public int requestAvailability(String formattedContact, int taskId) {
+        return 0;
+    }
+
+    @Override
+    public int getStackStatusForCapabilityRequest() {
+        return 0;
+    }
+
+    @Override
+    public void updatePublisherState(@PresenceBase.PresencePublishState int publishState) {
+        logi("updatePublisherState: from " + mPublishState + " to " + publishState);
+        mPublishState = publishState;
+        notifyPublishStateChanged(publishState);
+    }
+
+    private void addPublishRequestTaskId(int taskId) {
+        synchronized (mRequestingPublishTaskIds) {
+            mRequestingPublishTaskIds.add(taskId);
+        }
+    }
+
+    private void removePublishRequestTaskId(int taskId) {
+        synchronized (mRequestingPublishTaskIds) {
+            mRequestingPublishTaskIds.remove(taskId);
+        }
+    }
+
+    private boolean isPublishRequestExisted(Integer taskId) {
+        synchronized (mRequestingPublishTaskIds) {
+            return mRequestingPublishTaskIds.contains(taskId);
+        }
+    }
+
+    private static String getNumberFromUri(Uri uri) {
+        String number = uri.getSchemeSpecificPart();
+        String[] numberParts = number.split("[@;:]");
+
+        if (numberParts.length == 0) {
+            return null;
+        }
+        return numberParts[0];
+    }
+
+    private static int toUcePublishState(int publishState) {
+        switch (publishState) {
+            case PresenceBase.PUBLISH_STATE_200_OK:
+                return RcsUceAdapter.PUBLISH_STATE_OK;
+            case PresenceBase.PUBLISH_STATE_NOT_PUBLISHED:
+                return RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED;
+            case PresenceBase.PUBLISH_STATE_VOLTE_PROVISION_ERROR:
+                return RcsUceAdapter.PUBLISH_STATE_VOLTE_PROVISION_ERROR;
+            case PresenceBase.PUBLISH_STATE_RCS_PROVISION_ERROR:
+                return RcsUceAdapter.PUBLISH_STATE_RCS_PROVISION_ERROR;
+            case PresenceBase.PUBLISH_STATE_REQUEST_TIMEOUT:
+                return RcsUceAdapter.PUBLISH_STATE_REQUEST_TIMEOUT;
+            case PresenceBase.PUBLISH_STATE_OTHER_ERROR:
+                return RcsUceAdapter.PUBLISH_STATE_OTHER_ERROR;
+            default:
+                return RcsUceAdapter.PUBLISH_STATE_OTHER_ERROR;
+        }
+    }
+
+    private static int toUceError(int resultCode) {
+        switch (resultCode) {
+            case ResultCode.SUBSCRIBE_NOT_REGISTERED:
+                return RcsUceAdapter.ERROR_NOT_REGISTERED;
+            case ResultCode.SUBSCRIBE_REQUEST_TIMEOUT:
+                return RcsUceAdapter.ERROR_REQUEST_TIMEOUT;
+            case ResultCode.SUBSCRIBE_FORBIDDEN:
+                return RcsUceAdapter.ERROR_FORBIDDEN;
+            case ResultCode.SUBSCRIBE_NOT_FOUND:
+                return RcsUceAdapter.ERROR_NOT_FOUND;
+            case ResultCode.SUBSCRIBE_TOO_LARGE:
+                return RcsUceAdapter.ERROR_REQUEST_TOO_LARGE;
+            case ResultCode.SUBSCRIBE_INSUFFICIENT_MEMORY:
+                return RcsUceAdapter.ERROR_INSUFFICIENT_MEMORY;
+            case ResultCode.SUBSCRIBE_LOST_NETWORK:
+                return RcsUceAdapter.ERROR_LOST_NETWORK;
+            case ResultCode.SUBSCRIBE_ALREADY_IN_QUEUE:
+                return RcsUceAdapter.ERROR_ALREADY_IN_QUEUE;
+            default:
+                return RcsUceAdapter.ERROR_GENERIC_FAILURE;
+        }
+    }
+
+    /*
+     * Register receivers for updating capabilities
+     */
+    private void registerReceivers() {
+        IntentFilter filter = new IntentFilter(TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED);
+        filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        mContext.registerReceiver(mReceiver, filter);
+
+        ContentResolver resolver = mContext.getContentResolver();
+        if (resolver != null) {
+            // Register mobile data content changed.
+            resolver.registerContentObserver(
+                    Settings.Global.getUriFor(Settings.Global.MOBILE_DATA), false,
+                    mMobileDataObserver);
+
+            // Register SIM info content changed.
+            resolver.registerContentObserver(Telephony.SimInfo.CONTENT_URI, false,
+                    mSimInfoContentObserver);
+        }
+    }
+
+    private void unregisterReceivers() {
+        mContext.unregisterReceiver(mReceiver);
+        ContentResolver resolver = mContext.getContentResolver();
+        if (resolver != null) {
+            resolver.unregisterContentObserver(mMobileDataObserver);
+            resolver.unregisterContentObserver(mSimInfoContentObserver);
+        }
+    }
+
+    /**
+     * Register IMS and provision content changed.
+     *
+     * Call the UceImplHandler#registerImsContentChangedReceiver instead of
+     * calling this method directly.
+     */
+    private void registerImsContentChangedReceiverInternal(int subId) {
+        mUceImplHandler.removeRegisteringImsContentChangedReceiver();
+        try {
+            final int originalSubId = mSubId;
+            if ((originalSubId == subId) && (mImsContentChangedCallbackRegistered)) {
+                logi("registerImsContentChangedReceiverInternal: already registered. skip");
+                return;
+            }
+            // Unregister original IMS and Provision callback
+            unregisterImsProvisionCallback(originalSubId);
+            // Register new IMS and Provision callback
+            registerImsProvisionCallback(subId);
+        } catch (ImsException e) {
+            logw("registerImsContentChangedReceiverInternal error: " + e);
+            mUceImplHandler.retryRegisteringImsContentChangedReceiver(subId);
+        }
+    }
+
+    private void unregisterImsProvisionCallback(int subId) {
+        if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            return;
+        }
+        // Unregister IMS callback
+        ImsMmTelManager imsMmtelManager = getImsMmTelManager(subId);
+        if (imsMmtelManager != null) {
+            try {
+                imsMmtelManager.unregisterImsRegistrationCallback(mImsRegistrationCallback);
+                imsMmtelManager.unregisterMmTelCapabilityCallback(mCapabilityCallback);
+            } catch (RuntimeException e) {
+                logw("unregister IMS callback error: " + e.getMessage());
+            }
+        }
+
+        // Unregister provision changed callback
+        ProvisioningManager provisioningManager =
+                ProvisioningManager.createForSubscriptionId(subId);
+        try {
+            provisioningManager.unregisterProvisioningChangedCallback(mProvisioningChangedCallback);
+        } catch (RuntimeException e) {
+            logw("unregister provisioning callback error: " + e.getMessage());
+        }
+
+        // Remove all publish state callbacks
+        clearPublishStateCallbacks();
+
+        mImsContentChangedCallbackRegistered = false;
+    }
+
+    private void registerImsProvisionCallback(int subId) throws ImsException {
+        if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            return;
+        }
+        // Register IMS callback
+        ImsMmTelManager imsMmtelManager = getImsMmTelManager(subId);
+        if (imsMmtelManager != null) {
+            imsMmtelManager.registerImsRegistrationCallback(mContext.getMainExecutor(),
+                    mImsRegistrationCallback);
+            imsMmtelManager.registerMmTelCapabilityCallback(mContext.getMainExecutor(),
+                    mCapabilityCallback);
+        }
+        // Register provision changed callback
+        ProvisioningManager provisioningManager =
+                ProvisioningManager.createForSubscriptionId(subId);
+        provisioningManager.registerProvisioningChangedCallback(mContext.getMainExecutor(),
+                mProvisioningChangedCallback);
+
+        mImsContentChangedCallbackRegistered = true;
+        logi("registerImsProvisionCallback");
+    }
+
+    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent == null) return;
+            switch (intent.getAction()) {
+                case TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED:
+                    int preferredMode = intent.getIntExtra(
+                            TelecomManager.EXTRA_TTY_PREFERRED_MODE, TelecomManager.TTY_MODE_OFF);
+                    logi("TTY preferred mode changed: " + preferredMode);
+                    mPresencePublication.onTtyPreferredModeChanged(preferredMode);
+                    break;
+
+                case Intent.ACTION_AIRPLANE_MODE_CHANGED:
+                    boolean airplaneMode = intent.getBooleanExtra("state", false);
+                    logi("Airplane mode changed: " + airplaneMode);
+                    mPresencePublication.onAirplaneModeChanged(airplaneMode);
+                    break;
+            }
+        }
+    };
+
+    private ContentObserver mMobileDataObserver = new ContentObserver(
+            new Handler(Looper.getMainLooper())) {
+        @Override
+        public void onChange(boolean selfChange) {
+            boolean isEnabled = Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.MOBILE_DATA, 1) == 1;
+            logi("Mobile data changed: enabled=" + isEnabled);
+            mPresencePublication.onMobileDataChanged(isEnabled);
+        }
+    };
+
+    private ContentObserver mSimInfoContentObserver = new ContentObserver(
+            new Handler(Looper.getMainLooper())) {
+        @Override
+        public void onChange(boolean selfChange) {
+            if (mSubId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+                return;
+            }
+
+            ImsMmTelManager ims = getImsMmTelManager(mSubId);
+            if (ims == null) return;
+
+            try {
+                boolean isEnabled = ims.isVtSettingEnabled();
+                logi("SimInfo changed: VT setting=" + isEnabled);
+                mPresencePublication.onVtEnabled(isEnabled);
+            } catch (RuntimeException e) {
+                logw("SimInfo changed error: " + e);
+            }
+        }
+    };
+
+    private RegistrationManager.RegistrationCallback mImsRegistrationCallback =
+            new RegistrationManager.RegistrationCallback() {
+        @Override
+        public void onRegistered(int imsTransportType) {
+            logi("onRegistered: type=" + imsTransportType);
+            mNetworkRegistrationType = imsTransportType;
+            mPresencePublication.onImsConnected();
+
+            // Also trigger PresencePublication#onFeatureCapabilityChanged method
+            MmTelFeature.MmTelCapabilities capabilities = null;
+            synchronized (mCapabilitiesLock) {
+                capabilities = mMmTelCapabilities;
+            }
+            mPresencePublication.onFeatureCapabilityChanged(mNetworkRegistrationType, capabilities);
+        }
+
+        @Override
+        public void onUnregistered(ImsReasonInfo info) {
+            logi("onUnregistered");
+            mNetworkRegistrationType = AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
+
+            // Also trigger PresencePublication#onFeatureCapabilityChanged method
+            MmTelFeature.MmTelCapabilities capabilities = null;
+            synchronized (mCapabilitiesLock) {
+                capabilities = mMmTelCapabilities;
+            }
+            mPresencePublication.onFeatureCapabilityChanged(mNetworkRegistrationType, capabilities);
+            mPresencePublication.onImsDisconnected();
+        }
+    };
+
+    private ImsMmTelManager.CapabilityCallback mCapabilityCallback =
+            new ImsMmTelManager.CapabilityCallback() {
+        @Override
+        public void onCapabilitiesStatusChanged(MmTelFeature.MmTelCapabilities capabilities) {
+            synchronized (mCapabilitiesLock) {
+                mMmTelCapabilities = capabilities;
+            }
+            mPresencePublication.onFeatureCapabilityChanged(mNetworkRegistrationType, capabilities);
+        }
+    };
+
+    private ProvisioningManager.Callback mProvisioningChangedCallback =
+            new ProvisioningManager.Callback() {
+        @Override
+        public void onProvisioningIntChanged(int item, int value) {
+            logi("onProvisioningIntChanged: item=" + item);
+            switch (item) {
+                case ProvisioningManager.KEY_EAB_PROVISIONING_STATUS:
+                case ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS:
+                case ProvisioningManager.KEY_VT_PROVISIONING_STATUS:
+                    mPresencePublication.handleProvisioningChanged();
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+
+    private ImsMmTelManager getImsMmTelManager(int subId) {
+        try {
+            ImsManager imsManager = (ImsManager) mContext.getSystemService(
+                    Context.TELEPHONY_IMS_SERVICE);
+            return (imsManager == null) ? null : imsManager.getImsMmTelManager(subId);
+        } catch (IllegalArgumentException e) {
+            logw("getImsMmTelManager error: " + e.getMessage());
+            return null;
+        }
+    }
+
+    private void logi(String log) {
+        Log.i(LOG_TAG, getLogPrefix().append(log).toString());
+    }
+
+    private void logw(String log) {
+        Log.w(LOG_TAG, getLogPrefix().append(log).toString());
+    }
+
+    private StringBuilder getLogPrefix() {
+        StringBuilder builder = new StringBuilder("[");
+        builder.append(mSlotId);
+        builder.append("->");
+        builder.append(mSubId);
+        builder.append("] ");
+        return builder;
+    }
+}
diff --git a/testapps/Android.mk b/testapps/Android.mk
deleted file mode 100644
index 5053e7d..0000000
--- a/testapps/Android.mk
+++ /dev/null
@@ -1 +0,0 @@
-include $(call all-subdir-makefiles)
diff --git a/testapps/EmbmsServiceTestApp/Android.bp b/testapps/EmbmsServiceTestApp/Android.bp
new file mode 100644
index 0000000..e4a54cb
--- /dev/null
+++ b/testapps/EmbmsServiceTestApp/Android.bp
@@ -0,0 +1,11 @@
+// Build the Sample Embms Services
+android_app {
+    name: "EmbmsTestService",
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+    certificate: "platform",
+    privileged: true,
+    // Uncomment the following line to build the EmbmsTestService
+    // into the userdebug build:
+    // LOCAL_MODULE_TAGS := debug
+}
diff --git a/testapps/EmbmsServiceTestApp/Android.mk b/testapps/EmbmsServiceTestApp/Android.mk
deleted file mode 100644
index 29b8112..0000000
--- a/testapps/EmbmsServiceTestApp/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# Build the Sample Embms Services
-include $(CLEAR_VARS)
-
-src_dirs := src
-res_dirs := res
-
-LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
-
-LOCAL_PACKAGE_NAME := EmbmsTestService
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
-# Uncomment the following line to build the EmbmsTestService into the userdebug build.
-# LOCAL_MODULE_TAGS := debug
-
-include $(BUILD_PACKAGE)
diff --git a/testapps/EmbmsTestDownloadApp/Android.bp b/testapps/EmbmsTestDownloadApp/Android.bp
new file mode 100644
index 0000000..63f4e83
--- /dev/null
+++ b/testapps/EmbmsTestDownloadApp/Android.bp
@@ -0,0 +1,12 @@
+src_dirs = ["src"]
+res_dirs = ["res"]
+android_test {
+    name: "EmbmsTestDownloadApp",
+    static_libs: [
+        "androidx.recyclerview_recyclerview",
+        "androidx.legacy_legacy-support-v4",
+    ],
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/testapps/EmbmsTestDownloadApp/Android.mk b/testapps/EmbmsTestDownloadApp/Android.mk
deleted file mode 100644
index bd53d79..0000000
--- a/testapps/EmbmsTestDownloadApp/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# Build the Sample Embms Download frontend
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-        androidx.recyclerview_recyclerview \
-        androidx.legacy_legacy-support-v4
-
-src_dirs := src
-res_dirs := res
-
-LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
-
-LOCAL_PACKAGE_NAME := EmbmsTestDownloadApp
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-LOCAL_MODULE_TAGS := tests
-
-include $(BUILD_PACKAGE)
diff --git a/testapps/EmbmsTestStreamingApp/Android.bp b/testapps/EmbmsTestStreamingApp/Android.bp
new file mode 100644
index 0000000..814c5ca
--- /dev/null
+++ b/testapps/EmbmsTestStreamingApp/Android.bp
@@ -0,0 +1,7 @@
+android_test {
+    name: "EmbmsTestStreamingApp",
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+    certificate: "platform",
+    //LOCAL_MODULE_TAGS := debug
+}
diff --git a/testapps/EmbmsTestStreamingApp/Android.mk b/testapps/EmbmsTestStreamingApp/Android.mk
deleted file mode 100644
index f574990..0000000
--- a/testapps/EmbmsTestStreamingApp/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# Build the Sample Embms Streaming frontend
-include $(CLEAR_VARS)
-
-src_dirs := src
-res_dirs := res
-
-LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
-
-LOCAL_PACKAGE_NAME := EmbmsTestStreamingApp
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-LOCAL_MODULE_TAGS := tests
-#LOCAL_MODULE_TAGS := debug
-
-include $(BUILD_PACKAGE)
diff --git a/testapps/ImsTestService/Android.bp b/testapps/ImsTestService/Android.bp
new file mode 100644
index 0000000..a0b4edb
--- /dev/null
+++ b/testapps/ImsTestService/Android.bp
@@ -0,0 +1,13 @@
+android_app {
+    name: "ImsTestApp",
+    static_libs: [
+        "androidx.legacy_legacy-support-v4",
+        "androidx.appcompat_appcompat",
+        "androidx.recyclerview_recyclerview",
+        "androidx.cardview_cardview",
+    ],
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+    certificate: "platform",
+    privileged: true,
+}
diff --git a/testapps/ImsTestService/Android.mk b/testapps/ImsTestService/Android.mk
deleted file mode 100644
index 2869c86..0000000
--- a/testapps/ImsTestService/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_STATIC_ANDROID_LIBRARIES := \
-    androidx.legacy_legacy-support-v4 \
-    androidx.appcompat_appcompat \
-    androidx.recyclerview_recyclerview \
-    androidx.cardview_cardview
-
-LOCAL_USE_AAPT2 := true
-
-src_dirs := src
-res_dirs := res
-
-LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
-
-LOCAL_PACKAGE_NAME := ImsTestApp
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
-
-include $(BUILD_PACKAGE)
diff --git a/testapps/SmsManagerTestApp/Android.bp b/testapps/SmsManagerTestApp/Android.bp
new file mode 100644
index 0000000..5333eab
--- /dev/null
+++ b/testapps/SmsManagerTestApp/Android.bp
@@ -0,0 +1,5 @@
+android_app {
+    name: "SmsManagerTestApp",
+    srcs: ["src/**/*.java"],
+    sdk_version: "current",
+}
diff --git a/testapps/SmsManagerTestApp/Android.mk b/testapps/SmsManagerTestApp/Android.mk
deleted file mode 100644
index 307366b..0000000
--- a/testapps/SmsManagerTestApp/Android.mk
+++ /dev/null
@@ -1,17 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-src_dirs := src
-res_dirs := res
-
-LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
-
-LOCAL_PACKAGE_NAME := SmsManagerTestApp
-
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_PACKAGE)
diff --git a/testapps/TelephonyManagerTestApp/Android.bp b/testapps/TelephonyManagerTestApp/Android.bp
new file mode 100644
index 0000000..8a37c99
--- /dev/null
+++ b/testapps/TelephonyManagerTestApp/Android.bp
@@ -0,0 +1,7 @@
+android_test {
+    name: "TelephonyManagerTestApp",
+    srcs: ["src/**/*.java"],
+    javacflags: ["-parameters"],
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/testapps/TelephonyManagerTestApp/Android.mk b/testapps/TelephonyManagerTestApp/Android.mk
deleted file mode 100644
index 290b261..0000000
--- a/testapps/TelephonyManagerTestApp/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-src_dirs := src
-res_dirs := res
-
-LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
-
-LOCAL_JAVACFLAGS := -parameters
-
-LOCAL_PACKAGE_NAME := TelephonyManagerTestApp
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-LOCAL_MODULE_TAGS := tests
-
-include $(BUILD_PACKAGE)
diff --git a/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/ParameterParser.java b/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/ParameterParser.java
index 097c90a..ccb5639 100644
--- a/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/ParameterParser.java
+++ b/testapps/TelephonyManagerTestApp/src/com/android/phone/testapps/telephonymanagertestapp/ParameterParser.java
@@ -19,12 +19,19 @@
 import android.content.Context;
 import android.telephony.NumberVerificationCallback;
 import android.telephony.PhoneNumberRange;
+import android.telephony.RadioAccessSpecifier;
+import android.text.TextUtils;
 import android.widget.Toast;
 
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 import java.util.function.Function;
+import java.util.stream.Collectors;
 
 class ParameterParser {
     private static ParameterParser sInstance;
@@ -42,6 +49,9 @@
                 put(PhoneNumberRange.class, ParameterParser::parsePhoneNumberRange);
                 put(Executor.class, s -> parseExecutor(s));
                 put(NumberVerificationCallback.class, s -> parseNumberVerificationCallback(s));
+                put(Consumer.class, s -> parseConsumer(s));
+                put(List.class, s -> parseList(s));
+                put(RadioAccessSpecifier.class, s -> parseRadioAccessSpecifier(s));
             }};
 
     private ParameterParser(Context context) {
@@ -49,6 +59,7 @@
     }
 
     Object executeParser(Class type, String input) {
+        if ("null".equals(input)) return null;
         return mParsers.getOrDefault(type, s -> null).apply(input);
     }
 
@@ -64,6 +75,57 @@
         return mContext.getMainExecutor();
     }
 
+    private Consumer parseConsumer(String input) {
+        return o -> Toast.makeText(mContext, "Consumer: " + String.valueOf(o), Toast.LENGTH_SHORT)
+                .show();
+    }
+
+    /**
+     * input format: (ran)/(band1)+(band2)+.../(chan1)+(chan2)+...
+     * @return
+     */
+    private RadioAccessSpecifier parseRadioAccessSpecifier(String input) {
+        String[] parts = input.split("/");
+        int ran = Integer.parseInt(parts[0]);
+        String[] bandStrings = parts[1].split("\\+");
+        int[] bands = new int[bandStrings.length];
+        String[] chanStrings = parts[2].split("\\+");
+        int[] chans = new int[chanStrings.length];
+
+        for (int i = 0; i < bands.length; i++) {
+            bands[i] = Integer.parseInt(bandStrings[i]);
+        }
+
+        for (int i = 0; i < chans.length; i++) {
+            chans[i] = Integer.parseInt(chanStrings[i]);
+        }
+        return new RadioAccessSpecifier(ran, bands, chans);
+    }
+
+    private List parseList(String input) {
+        if (TextUtils.isEmpty(input)) {
+            return Collections.emptyList();
+        }
+        String[] components = input.split(" ");
+        String className = components[0];
+        Class c;
+        try {
+            c = mContext.getClassLoader().loadClass(className);
+        } catch (ClassNotFoundException e) {
+            Toast.makeText(mContext, "Invalid class " + className,
+                    Toast.LENGTH_SHORT).show();
+            return null;
+        }
+        if (!mParsers.containsKey(c)) {
+            Toast.makeText(mContext, "Cannot parse " + className,
+                    Toast.LENGTH_SHORT).show();
+            return null;
+        }
+        return Arrays.stream(components).skip(1)
+                .map(mParsers.get(c))
+                .collect(Collectors.toList());
+    }
+
     private NumberVerificationCallback parseNumberVerificationCallback(String input) {
         return new NumberVerificationCallback() {
             @Override
diff --git a/testapps/TelephonyRegistryTestApp/Android.bp b/testapps/TelephonyRegistryTestApp/Android.bp
new file mode 100644
index 0000000..fec5286
--- /dev/null
+++ b/testapps/TelephonyRegistryTestApp/Android.bp
@@ -0,0 +1,7 @@
+android_test {
+    name: "TelephonyRegistryTestApp",
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+    certificate: "platform",
+    //LOCAL_MODULE_TAGS := debug
+}
diff --git a/testapps/TelephonyRegistryTestApp/Android.mk b/testapps/TelephonyRegistryTestApp/Android.mk
deleted file mode 100644
index 8c0d286..0000000
--- a/testapps/TelephonyRegistryTestApp/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-src_dirs := src
-res_dirs := res
-
-LOCAL_SRC_FILES := $(call all-java-files-under, $(src_dirs))
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
-
-LOCAL_PACKAGE_NAME := TelephonyRegistryTestApp
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-LOCAL_MODULE_TAGS := tests
-#LOCAL_MODULE_TAGS := debug
-
-include $(BUILD_PACKAGE)
diff --git a/tests/Android.bp b/tests/Android.bp
index 22b40b5..7ed234e 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -25,6 +25,7 @@
         "telephony-common",
         "android.test.base",
         "ims-common",
+        "android.test.mock",
     ],
     platform_apis: true,
     certificate: "platform",
diff --git a/tests/src/com/android/TelephonyTestBase.java b/tests/src/com/android/TelephonyTestBase.java
index 01267d8..86c5402 100644
--- a/tests/src/com/android/TelephonyTestBase.java
+++ b/tests/src/com/android/TelephonyTestBase.java
@@ -16,6 +16,8 @@
 
 package com.android;
 
+import static org.mockito.Mockito.spy;
+
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Log;
@@ -34,7 +36,7 @@
 
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mContext = new TestContext();
+        mContext = spy(new TestContext());
         // Set up the looper if it does not exist on the test thread.
         if (Looper.myLooper() == null) {
             Looper.prepare();
diff --git a/tests/src/com/android/TestContext.java b/tests/src/com/android/TestContext.java
index 776ec6a..b1c6923 100644
--- a/tests/src/com/android/TestContext.java
+++ b/tests/src/com/android/TestContext.java
@@ -20,6 +20,7 @@
 import static org.mockito.Mockito.doReturn;
 
 import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -29,17 +30,21 @@
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsManager;
 import android.test.mock.MockContext;
 
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.concurrent.Executor;
+
 public class TestContext extends MockContext {
 
     @Mock CarrierConfigManager mMockCarrierConfigManager;
     @Mock TelecomManager mMockTelecomManager;
     @Mock TelephonyManager mMockTelephonyManager;
     @Mock SubscriptionManager mMockSubscriptionManager;
+    @Mock ImsManager mMockImsManager;
 
     private PersistableBundle mCarrierConfig = new PersistableBundle();
 
@@ -49,6 +54,12 @@
     }
 
     @Override
+    public Executor getMainExecutor() {
+        // Just run on current thread
+        return Runnable::run;
+    }
+
+    @Override
     public Context getApplicationContext() {
         return this;
     }
@@ -81,6 +92,11 @@
     }
 
     @Override
+    public ContentResolver getContentResolver() {
+        return null;
+    }
+
+    @Override
     public Object getSystemService(String name) {
         switch (name) {
             case (Context.CARRIER_CONFIG_SERVICE) : {
@@ -95,6 +111,9 @@
             case (Context.TELEPHONY_SUBSCRIPTION_SERVICE) : {
                 return mMockSubscriptionManager;
             }
+            case(Context.TELEPHONY_IMS_SERVICE) : {
+                return mMockImsManager;
+            }
         }
         return null;
     }
diff --git a/tests/src/com/android/phone/NumberVerificationManagerTest.java b/tests/src/com/android/phone/NumberVerificationManagerTest.java
index d476ba5..f7914ab 100644
--- a/tests/src/com/android/phone/NumberVerificationManagerTest.java
+++ b/tests/src/com/android/phone/NumberVerificationManagerTest.java
@@ -57,7 +57,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         ServiceState ss = mock(ServiceState.class);
-        when(ss.getVoiceRegState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+        when(ss.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
         when(mPhone1.getServiceState()).thenReturn(ss);
         when(mPhone1.getForegroundCall()).thenReturn(mForegroundCall);
         when(mPhone1.getRingingCall()).thenReturn(mRingingCall);
@@ -107,7 +107,7 @@
     @Test
     public void testNoPhoneInServiceFailure() throws Exception {
         ServiceState ss = mock(ServiceState.class);
-        when(ss.getVoiceRegState()).thenReturn(ServiceState.STATE_POWER_OFF);
+        when(ss.getState()).thenReturn(ServiceState.STATE_POWER_OFF);
         when(mPhone1.getServiceState()).thenReturn(ss);
         when(mPhone2.getServiceState()).thenReturn(ss);
         NumberVerificationManager manager =
@@ -138,7 +138,7 @@
     @Test
     public void testVerificationWorksWithOnePhoneInService() throws Exception {
         ServiceState ss = mock(ServiceState.class);
-        when(ss.getVoiceRegState()).thenReturn(ServiceState.STATE_POWER_OFF);
+        when(ss.getState()).thenReturn(ServiceState.STATE_POWER_OFF);
         when(mPhone1.getServiceState()).thenReturn(ss);
         NumberVerificationManager manager =
                 new NumberVerificationManager(() -> new Phone[]{mPhone1, mPhone2});
diff --git a/tests/src/com/android/phone/PhoneGlobalsTest.java b/tests/src/com/android/phone/PhoneGlobalsTest.java
new file mode 100644
index 0000000..d45a890
--- /dev/null
+++ b/tests/src/com/android/phone/PhoneGlobalsTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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 org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+
+import androidx.test.InstrumentationRegistry;
+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 java.util.Locale;
+
+
+@RunWith(AndroidJUnit4.class)
+public class PhoneGlobalsTest extends TelephonyTestBase {
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Test
+    public void testGetImsResources() throws Exception {
+        // Do not use test context here, we are testing that overlaying for different locales works
+        // correctly
+        Context realContext = InstrumentationRegistry.getTargetContext();
+        String defaultImsMmtelPackage = getResourcesForLocale(realContext, Locale.US).getString(
+                R.string.config_ims_mmtel_package);
+        String defaultImsMmtelPackageUk = getResourcesForLocale(realContext, Locale.UK).getString(
+                R.string.config_ims_mmtel_package);
+        assertEquals("locales changed IMS package configuration!", defaultImsMmtelPackage,
+                defaultImsMmtelPackageUk);
+    }
+
+    private Resources getResourcesForLocale(Context context, Locale locale) {
+        Configuration config = new Configuration();
+        config.setToDefaults();
+        config.setLocale(locale);
+        Context localeContext = context.createConfigurationContext(config);
+        return localeContext.getResources();
+    }
+}
diff --git a/tests/src/com/android/phone/ServiceStateProviderTest.java b/tests/src/com/android/phone/ServiceStateProviderTest.java
new file mode 100644
index 0000000..574c0c9
--- /dev/null
+++ b/tests/src/com/android/phone/ServiceStateProviderTest.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2019 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.Telephony.ServiceStateTable;
+import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionId;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.content.pm.ProviderInfo;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.test.mock.MockContentResolver;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for simple queries of ServiceStateProvider.
+ *
+ * Build, install and run the tests by running the commands below:
+ *     runtest --path <dir or file>
+ *     runtest --path <dir or file> --test-method <testMethodName>
+ *     e.g.)
+ *         runtest --path tests/src/com/android/phone/ServiceStateProviderTest.java \
+ *                 --test-method testGetServiceState
+ */
+public class ServiceStateProviderTest {
+    private static final String TAG = "ServiceStateProviderTest";
+
+    private Context mContext;
+    private MockContentResolver mContentResolver;
+    private ServiceState mTestServiceState;
+    private ServiceState mTestServiceStateForSubId1;
+
+    private final String[] mTestProjection =
+    {
+        ServiceStateTable.VOICE_REG_STATE,
+        ServiceStateTable.DATA_REG_STATE,
+        ServiceStateTable.VOICE_OPERATOR_ALPHA_LONG,
+        ServiceStateTable.VOICE_OPERATOR_ALPHA_SHORT,
+        ServiceStateTable.VOICE_OPERATOR_NUMERIC,
+        ServiceStateTable.DATA_OPERATOR_ALPHA_LONG,
+        ServiceStateTable.DATA_OPERATOR_ALPHA_SHORT,
+        ServiceStateTable.DATA_OPERATOR_NUMERIC,
+        ServiceStateTable.IS_MANUAL_NETWORK_SELECTION,
+        ServiceStateTable.RIL_VOICE_RADIO_TECHNOLOGY,
+        ServiceStateTable.RIL_DATA_RADIO_TECHNOLOGY,
+        ServiceStateTable.CSS_INDICATOR,
+        ServiceStateTable.NETWORK_ID,
+        ServiceStateTable.SYSTEM_ID,
+        ServiceStateTable.CDMA_ROAMING_INDICATOR,
+        ServiceStateTable.CDMA_DEFAULT_ROAMING_INDICATOR,
+        ServiceStateTable.CDMA_ERI_ICON_INDEX,
+        ServiceStateTable.CDMA_ERI_ICON_MODE,
+        ServiceStateTable.IS_EMERGENCY_ONLY,
+        ServiceStateTable.IS_USING_CARRIER_AGGREGATION,
+        ServiceStateTable.OPERATOR_ALPHA_LONG_RAW,
+        ServiceStateTable.OPERATOR_ALPHA_SHORT_RAW,
+    };
+
+    @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!");
+            }
+        };
+        doReturn(mContentResolver).when(mContext).getContentResolver();
+
+        mTestServiceState = new ServiceState();
+        mTestServiceState.setStateOutOfService();
+        mTestServiceStateForSubId1 = new ServiceState();
+        mTestServiceStateForSubId1.setStateOff();
+
+        // Mock out the actual phone state
+        ServiceStateProvider provider = new ServiceStateProvider() {
+            @Override
+            public ServiceState getServiceState(int subId) {
+                if (subId == 1) {
+                    return mTestServiceStateForSubId1;
+                } else {
+                    return mTestServiceState;
+                }
+            }
+
+            @Override
+            public int getDefaultSubId() {
+                return 0;
+            }
+        };
+        ProviderInfo providerInfo = new ProviderInfo();
+        providerInfo.authority = "service-state";
+        provider.attachInfoForTesting(mContext, providerInfo);
+        mContentResolver.addProvider("service-state", provider);
+    }
+
+    @Test
+    @SmallTest
+    public void testQueryServiceStateWithNoSubId() {
+        // Verify that when calling query with no subId in the uri the default ServiceState is
+        // returned.
+        // In this case the subId is set to 0 and the expected service state is
+        // mTestServiceState.
+        verifyServiceStateForSubId(ServiceStateTable.CONTENT_URI, mTestServiceState);
+    }
+
+    @Test
+    @SmallTest
+    public void testGetServiceStateWithDefaultSubId() {
+        // Verify that when calling with the DEFAULT_SUBSCRIPTION_ID the correct ServiceState is
+        // returned
+        // In this case the subId is set to 0 and the expected service state is
+        // mTestServiceState.
+        verifyServiceStateForSubId(
+                getUriForSubscriptionId(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID),
+                mTestServiceState);
+    }
+
+    /**
+     * Test querying the service state for a given subId
+     */
+    @Test
+    @SmallTest
+    public void testGetServiceStateForSubId() {
+        // Verify that when calling with a specific subId the correct ServiceState is returned
+        // In this case the subId is set to 1 and the expected service state is
+        // mTestServiceStateForSubId1
+        verifyServiceStateForSubId(getUriForSubscriptionId(1), mTestServiceStateForSubId1);
+    }
+
+    private void verifyServiceStateForSubId(Uri uri, ServiceState ss) {
+        Cursor cursor = mContentResolver.query(uri, mTestProjection, "",
+                null, null);
+        assertNotNull(cursor);
+        cursor.moveToFirst();
+
+        final int voiceRegState = ss.getState();
+        final int dataRegState = ss.getDataRegistrationState();
+        final String voiceOperatorAlphaLong = ss.getOperatorAlphaLong();
+        final String voiceOperatorAlphaShort = ss.getOperatorAlphaShort();
+        final String voiceOperatorNumeric = ss.getOperatorNumeric();
+        final String dataOperatorAlphaLong = ss.getOperatorAlphaLong();
+        final String dataOperatorAlphaShort = ss.getOperatorAlphaShort();
+        final String dataOperatorNumeric = ss.getOperatorNumeric();
+        final int isManualNetworkSelection = (ss.getIsManualSelection()) ? 1 : 0;
+        final int rilVoiceRadioTechnology = ss.getRilVoiceRadioTechnology();
+        final int rilDataRadioTechnology = ss.getRilDataRadioTechnology();
+        final int cssIndicator = ss.getCssIndicator();
+        final int networkId = ss.getCdmaNetworkId();
+        final int systemId = ss.getCdmaSystemId();
+        final int cdmaRoamingIndicator = ss.getCdmaRoamingIndicator();
+        final int cdmaDefaultRoamingIndicator = ss.getCdmaDefaultRoamingIndicator();
+        final int cdmaEriIconIndex = ss.getCdmaEriIconIndex();
+        final int cdmaEriIconMode = ss.getCdmaEriIconMode();
+        final int isEmergencyOnly = (ss.isEmergencyOnly()) ? 1 : 0;
+        final int isUsingCarrierAggregation = (ss.isUsingCarrierAggregation()) ? 1 : 0;
+        final String operatorAlphaLongRaw = ss.getOperatorAlphaLongRaw();
+        final String operatorAlphaShortRaw = ss.getOperatorAlphaShortRaw();
+
+        assertEquals(voiceRegState, cursor.getInt(0));
+        assertEquals(dataRegState, cursor.getInt(1));
+        assertEquals(voiceOperatorAlphaLong, cursor.getString(2));
+        assertEquals(voiceOperatorAlphaShort, cursor.getString(3));
+        assertEquals(voiceOperatorNumeric, cursor.getString(4));
+        assertEquals(dataOperatorAlphaLong, cursor.getString(5));
+        assertEquals(dataOperatorAlphaShort, cursor.getString(6));
+        assertEquals(dataOperatorNumeric, cursor.getString(7));
+        assertEquals(isManualNetworkSelection, cursor.getInt(8));
+        assertEquals(rilVoiceRadioTechnology, cursor.getInt(9));
+        assertEquals(rilDataRadioTechnology, cursor.getInt(10));
+        assertEquals(cssIndicator, cursor.getInt(11));
+        assertEquals(networkId, cursor.getInt(12));
+        assertEquals(systemId, cursor.getInt(13));
+        assertEquals(cdmaRoamingIndicator, cursor.getInt(14));
+        assertEquals(cdmaDefaultRoamingIndicator, cursor.getInt(15));
+        assertEquals(cdmaEriIconIndex, cursor.getInt(16));
+        assertEquals(cdmaEriIconMode, cursor.getInt(17));
+        assertEquals(isEmergencyOnly, cursor.getInt(18));
+        assertEquals(isUsingCarrierAggregation, cursor.getInt(19));
+        assertEquals(operatorAlphaLongRaw, cursor.getString(20));
+        assertEquals(operatorAlphaShortRaw, cursor.getString(21));
+    }
+
+    /**
+     * Test that we don't notify for certain field changes. (e.g. we don't notify when the NetworkId
+     * or SystemId change) This is an intentional behavior change from the broadcast.
+     */
+    @Test
+    @SmallTest
+    public void testNoNotify() {
+        int subId = 0;
+
+        ServiceState oldSS = new ServiceState();
+        oldSS.setStateOutOfService();
+        oldSS.setCdmaSystemAndNetworkId(1, 1);
+
+        ServiceState newSS = new ServiceState();
+        newSS.setStateOutOfService();
+        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);
+    }
+
+    @Test
+    @SmallTest
+    public void testNotifyChanged() {
+        int subId = 0;
+
+        ServiceState oldSS = new ServiceState();
+        oldSS.setStateOutOfService();
+        oldSS.setVoiceRegState(ServiceState.STATE_OUT_OF_SERVICE);
+
+        ServiceState copyOfOldSS = new ServiceState();
+        copyOfOldSS.setStateOutOfService();
+        copyOfOldSS.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);
+
+        // Test that notifyChange is called by notifyChangeForSubId when the voice_reg_state changes
+        notifyChangeWasCalled = false;
+        try {
+            ServiceStateProvider.notifyChangeForSubId(mContext, oldSS, newSS, subId);
+        } catch (RuntimeException e) {
+            final String message = e.getMessage();
+            if (message != null &&  message.equals("notifyChange!")) {
+                notifyChangeWasCalled = true;
+            }
+        }
+        assertTrue(notifyChangeWasCalled);
+    }
+}
diff --git a/tests/src/com/android/services/telephony/DisconnectCauseUtilTest.java b/tests/src/com/android/services/telephony/DisconnectCauseUtilTest.java
new file mode 100644
index 0000000..7f9efdc
--- /dev/null
+++ b/tests/src/com/android/services/telephony/DisconnectCauseUtilTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.services.telephony;
+
+import static android.media.ToneGenerator.TONE_PROP_PROMPT;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.TestCase.assertEquals;
+
+import android.telephony.DisconnectCause;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class DisconnectCauseUtilTest {
+    /**
+     * Verifies that a call drop due to loss of WIFI results in a disconnect cause of error and that
+     * the label, description and tone are all present.
+     */
+    @Test
+    public void testDropDueToWifiLoss() {
+        android.telecom.DisconnectCause tcCause = DisconnectCauseUtil.toTelecomDisconnectCause(
+                DisconnectCause.WIFI_LOST);
+        assertEquals(android.telecom.DisconnectCause.ERROR, tcCause.getCode());
+        assertEquals(TONE_PROP_PROMPT, tcCause.getTone());
+        assertNotNull(tcCause.getDescription());
+        assertNotNull(tcCause.getReason());
+    }
+}
diff --git a/tests/src/com/android/services/telephony/ImsConferenceTest.java b/tests/src/com/android/services/telephony/ImsConferenceTest.java
index 7251402..b84aca1 100644
--- a/tests/src/com/android/services/telephony/ImsConferenceTest.java
+++ b/tests/src/com/android/services/telephony/ImsConferenceTest.java
@@ -43,6 +43,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.Arrays;
+import java.util.Collections;
 
 public class ImsConferenceTest {
     @Mock
@@ -73,14 +74,15 @@
 
         ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
                 mMockTelephonyConnectionServiceProxy, mConferenceHost,
-                null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+                null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+                new ImsConference.CarrierConfiguration.Builder().build());
 
         ConferenceParticipant participant1 = new ConferenceParticipant(
                 Uri.parse("tel:6505551212"),
                 "A",
                 Uri.parse("sip:6505551212@testims.com"),
                 Connection.STATE_ACTIVE,
-                Call.Details.DIRECTION_INCOMING);
+                Call.Details.DIRECTION_OUTGOING);
         ConferenceParticipant participant2 = new ConferenceParticipant(
                 Uri.parse("tel:6505551213"),
                 "A",
@@ -98,6 +100,8 @@
         imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
                 Arrays.asList(participant1));
         assertEquals(0, imsConference.getNumberOfParticipants());
+        // Ensure the call direction is set correctly during emulation
+        assertEquals(Call.Details.DIRECTION_OUTGOING, imsConference.getCallDirection());
         reset(mMockTelephonyConnectionServiceProxy);
 
         // Back to 2!
@@ -120,7 +124,8 @@
 
         ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
                 mMockTelephonyConnectionServiceProxy, mConferenceHost,
-                null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+                null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+                new ImsConference.CarrierConfiguration.Builder().build());
 
         // Start off with 3 participants.
         ConferenceParticipant participant1 = new ConferenceParticipant(
@@ -183,7 +188,8 @@
 
         ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
                 mMockTelephonyConnectionServiceProxy, mConferenceHost,
-                null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+                null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+                new ImsConference.CarrierConfiguration.Builder().build());
 
         // Start off with 3 participants.
         ConferenceParticipant participant1 = new ConferenceParticipant(
@@ -240,7 +246,8 @@
 
         ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
                 mMockTelephonyConnectionServiceProxy, mConferenceHost,
-                null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+                null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+                new ImsConference.CarrierConfiguration.Builder().build());
 
         // Setup the initial conference state with 2 participants.
         ConferenceParticipant participant1 = new ConferenceParticipant(
@@ -298,7 +305,8 @@
 
         ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
                 mMockTelephonyConnectionServiceProxy, mConferenceHost,
-                null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+                null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+                new ImsConference.CarrierConfiguration.Builder().build());
 
         final boolean[] isConferenceState = new boolean[1];
         TelephonyConferenceBase.TelephonyConferenceListener conferenceListener =
@@ -354,7 +362,8 @@
 
         ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
                 mMockTelephonyConnectionServiceProxy, mConferenceHost,
-                null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+                null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+                new ImsConference.CarrierConfiguration.Builder().build());
 
         ConferenceParticipant participant1 = new ConferenceParticipant(
                 Uri.parse("tel:6505551212"),
@@ -379,7 +388,8 @@
 
         ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
                 mMockTelephonyConnectionServiceProxy, mConferenceHost,
-                null /* phoneAccountHandle */, () -> true /* featureFlagProxy */);
+                null /* phoneAccountHandle */, () -> true /* featureFlagProxy */,
+                new ImsConference.CarrierConfiguration.Builder().build());
 
         ConferenceParticipant participant1 = new ConferenceParticipant(
                 Uri.parse("tel:6505551212"),
@@ -410,13 +420,14 @@
 
     @Test
     @SmallTest
-    public void testNormalConference() {
+    public void testNormalConference() throws Exception {
         when(mMockTelecomAccountRegistry.isUsingSimCallManager(any(PhoneAccountHandle.class)))
                 .thenReturn(false);
 
         ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
                 mMockTelephonyConnectionServiceProxy, mConferenceHost,
-                null /* phoneAccountHandle */, () -> false /* featureFlagProxy */);
+                null /* phoneAccountHandle */, () -> false /* featureFlagProxy */,
+                new ImsConference.CarrierConfiguration.Builder().build());
 
         ConferenceParticipant participant1 = new ConferenceParticipant(
                 Uri.parse("tel:6505551212"),
@@ -438,5 +449,45 @@
         imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
                 Arrays.asList(participant1));
         assertEquals(1, imsConference.getNumberOfParticipants());
+
+        // Drop to 0 participants; should not hangup the conf now
+        imsConference.handleConferenceParticipantsUpdate(mConferenceHost, Collections.emptyList());
+        assertEquals(0, imsConference.getNumberOfParticipants());
+        verify(mConferenceHost.mMockCall, never()).hangup();
+    }
+
+    @Test
+    @SmallTest
+    public void testLocalDisconnectOnEmptyConference() throws Exception {
+        when(mMockTelecomAccountRegistry.isUsingSimCallManager(any(PhoneAccountHandle.class)))
+                .thenReturn(false);
+
+        ImsConference imsConference = new ImsConference(mMockTelecomAccountRegistry,
+                mMockTelephonyConnectionServiceProxy, mConferenceHost,
+                null /* phoneAccountHandle */, () -> false /* featureFlagProxy */,
+                new ImsConference.CarrierConfiguration.Builder()
+                        .setShouldLocalDisconnectEmptyConference(true)
+                        .build());
+
+        ConferenceParticipant participant1 = new ConferenceParticipant(
+                Uri.parse("tel:6505551212"),
+                "A",
+                Uri.parse("sip:6505551212@testims.com"),
+                Connection.STATE_ACTIVE,
+                Call.Details.DIRECTION_INCOMING);
+        ConferenceParticipant participant2 = new ConferenceParticipant(
+                Uri.parse("tel:6505551213"),
+                "A",
+                Uri.parse("sip:6505551213@testims.com"),
+                Connection.STATE_ACTIVE,
+                Call.Details.DIRECTION_INCOMING);
+        imsConference.handleConferenceParticipantsUpdate(mConferenceHost,
+                Arrays.asList(participant1, participant2));
+        assertEquals(2, imsConference.getNumberOfParticipants());
+
+        // Drop to 0 participants; should have a hangup request.
+        imsConference.handleConferenceParticipantsUpdate(mConferenceHost, Collections.emptyList());
+        assertEquals(0, imsConference.getNumberOfParticipants());
+        verify(mConferenceHost.mMockCall).hangup();
     }
 }
diff --git a/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java b/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
index afdfab5..d55a2fa 100644
--- a/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
+++ b/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
@@ -69,9 +69,11 @@
     @Override
     @After
     public void tearDown() throws Exception {
+        mListener.setTimeBetweenRetriesMillis(5000);
+        mListener.setMaxNumRetries(5);
+        mListener.getHandler().removeCallbacksAndMessages(null);
         // Wait for the queue to clear...
         waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS /*ms timeout*/);
-        mListener.getHandler().removeCallbacksAndMessages(null);
         mListener = null;
         super.tearDown();
     }
@@ -83,7 +85,7 @@
     @SmallTest
     public void testRegisterForCallback() {
         mMockPhone.mCi = mMockCi;
-        mListener.waitForRadioOn(mMockPhone, mCallback);
+        mListener.waitForRadioOn(mMockPhone, mCallback, false, false);
 
         waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
 
@@ -105,10 +107,11 @@
     public void testPhoneChangeState_OkToCallTrue() {
         ServiceState state = new ServiceState();
         state.setState(ServiceState.STATE_IN_SERVICE);
+        when(mMockPhone.getServiceState()).thenReturn(state);
         when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
         when(mCallback.isOkToCall(eq(mMockPhone), anyInt())).thenReturn(true);
         mMockPhone.mCi = mMockCi;
-        mListener.waitForRadioOn(mMockPhone, mCallback);
+        mListener.waitForRadioOn(mMockPhone, mCallback, false, false);
         waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
 
         mListener.getHandler().obtainMessage(RadioOnStateListener.MSG_SERVICE_STATE_CHANGED,
@@ -132,7 +135,7 @@
         when(mCallback.isOkToCall(eq(mMockPhone), anyInt())).thenReturn(false);
         when(mMockPhone.getServiceState()).thenReturn(state);
         mMockPhone.mCi = mMockCi;
-        mListener.waitForRadioOn(mMockPhone, mCallback);
+        mListener.waitForRadioOn(mMockPhone, mCallback, false, false);
         waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
 
         mListener.getHandler().obtainMessage(RadioOnStateListener.MSG_SERVICE_STATE_CHANGED,
@@ -161,10 +164,32 @@
 
         // Wait for the timer to expire and check state manually in onRetryTimeout
         mMockPhone.mCi = mMockCi;
-        mListener.waitForRadioOn(mMockPhone, mCallback);
+        mListener.waitForRadioOn(mMockPhone, mCallback, false, false);
         waitForHandlerActionDelayed(mListener.getHandler(), TIMEOUT_MS, TIMEOUT_MS /*delay*/);
 
         verify(mCallback).onComplete(eq(mListener), eq(false));
-        verify(mMockPhone, times(2)).setRadioPower(eq(true));
+        verify(mMockPhone, times(2)).setRadioPower(eq(true),
+                eq(false), eq(false), eq(false));
+    }
+
+    @Test
+    @SmallTest
+    public void testTimeout_RetryFailure_ForEmergency() {
+        ServiceState state = new ServiceState();
+        state.setState(ServiceState.STATE_POWER_OFF);
+        when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
+        when(mMockPhone.getServiceState()).thenReturn(state);
+        when(mCallback.isOkToCall(eq(mMockPhone), anyInt())).thenReturn(false);
+        mListener.setTimeBetweenRetriesMillis(0/*ms*/);
+        mListener.setMaxNumRetries(2);
+
+        // Wait for the timer to expire and check state manually in onRetryTimeout
+        mMockPhone.mCi = mMockCi;
+        mListener.waitForRadioOn(mMockPhone, mCallback, true, true);
+        waitForHandlerActionDelayed(mListener.getHandler(), TIMEOUT_MS, TIMEOUT_MS /*delay*/);
+
+        verify(mCallback).onComplete(eq(mListener), eq(false));
+        verify(mMockPhone, times(2)).setRadioPower(eq(true),
+                eq(true), eq(true), eq(false));
     }
 }
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 6e11e51..2060e6f 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -16,7 +16,10 @@
 
 package com.android.services.telephony;
 
+import static com.android.internal.telephony.RILConstants.GSM_PHONE;
+
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
@@ -26,6 +29,7 @@
 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.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -58,6 +62,7 @@
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneSwitcher;
+import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 
@@ -93,6 +98,7 @@
     private static final PhoneAccountHandle PHONE_ACCOUNT_HANDLE_2 = new PhoneAccountHandle(
             TEST_COMPONENT_NAME, TEST_ACCOUNT_ID2);
     private static final Uri TEST_ADDRESS = Uri.parse("tel:+16505551212");
+    private android.telecom.Connection mConnection;
 
     @Mock TelephonyConnectionService.TelephonyManagerProxy mTelephonyManagerProxy;
     @Mock TelephonyConnectionService.SubscriptionManagerProxy mSubscriptionManagerProxy;
@@ -106,6 +112,8 @@
     @Mock Handler mMockHandler;
     @Mock EmergencyNumberTracker mEmergencyNumberTracker;
     @Mock PhoneSwitcher mPhoneSwitcher;
+    @Mock RadioOnHelper mRadioOnHelper;
+    @Mock ServiceStateTracker mSST;
 
     private static class TestTelephonyConnectionService extends TelephonyConnectionService {
 
@@ -135,6 +143,8 @@
         doReturn(false).when(mDeviceState).shouldCheckSimStateBeforeOutgoingCall(any());
         mTestConnectionService.setPhoneSwitcherProxy(mPhoneSwitcherProxy);
         doReturn(mPhoneSwitcher).when(mPhoneSwitcherProxy).getPhoneSwitcher();
+        when(mPhoneNumberUtilsProxy.convertToEmergencyNumber(any(), anyString()))
+                .thenAnswer(invocation -> invocation.getArgument(1));
         mTestConnectionService.setPhoneNumberUtilsProxy(mPhoneNumberUtilsProxy);
         mTestConnectionService.setPhoneUtilsProxy(mPhoneUtilsProxy);
         HandlerThread mockHandlerThread = mock(HandlerThread.class);
@@ -143,6 +153,7 @@
         doReturn(mMockHandler).when(mHandlerFactory).createHandler(any());
         mTestConnectionService.setHandlerFactory(mHandlerFactory);
         mTestConnectionService.setDeviceState(mDeviceState);
+        mTestConnectionService.setRadioOnHelper(mRadioOnHelper);
         doReturn(new DisconnectCause(DisconnectCause.UNKNOWN)).when(mDisconnectCauseFactory)
                 .toTelecomDisconnectCause(anyInt(), any());
         doReturn(new DisconnectCause(DisconnectCause.UNKNOWN)).when(mDisconnectCauseFactory)
@@ -941,6 +952,64 @@
     }
 
     /**
+     * Test that the TelephonyConnectionService successfully turns radio on before placing the
+     * emergency call.
+     */
+    @Test
+    @SmallTest
+    public void testCreateOutgoingEmerge_exitingApm_disconnected() {
+        when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
+        Phone testPhone = setupConnectionServiceInApm();
+
+        ArgumentCaptor<RadioOnStateListener.Callback> callback =
+                ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
+        verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
+                eq(testPhone));
+
+        assertFalse(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+        when(mSST.isRadioOn()).thenReturn(true);
+        assertTrue(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+
+        mConnection.setDisconnected(null);
+        callback.getValue().onComplete(null, true);
+        for (Phone phone : mPhoneFactoryProxy.getPhones()) {
+            verify(phone).setRadioPower(true, false, false, true);
+        }
+    }
+
+    /**
+     * Test that the TelephonyConnectionService successfully turns radio on before placing the
+     * emergency call.
+     */
+    @Test
+    @SmallTest
+    public void testCreateOutgoingEmergencyConnection_exitingApm_placeCall() {
+        when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
+        Phone testPhone = setupConnectionServiceInApm();
+
+        ArgumentCaptor<RadioOnStateListener.Callback> callback =
+                ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
+        verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
+                eq(testPhone));
+
+        assertFalse(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+        when(mSST.isRadioOn()).thenReturn(true);
+        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
+            fail();
+        }
+    }
+
+    /**
      * Test that the TelephonyConnectionService does not perform a DDS switch when the carrier
      * supports control-plane fallback.
      */
@@ -1115,9 +1184,49 @@
         doReturn(emergencyNumbers).when(mTelephonyManagerProxy).getCurrentEmergencyNumberList();
         doReturn(2).when(mTelephonyManagerProxy).getPhoneCount();
 
-        android.telecom.Connection testConnection = mTestConnectionService
-                .onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1, connectionRequest);
-        assertNotNull("test connection was not set up correctly.", testConnection);
+        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.
+     */
+    private Phone setupConnectionServiceInApm() {
+        ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
+                .setAccountHandle(PHONE_ACCOUNT_HANDLE_1)
+                .setAddress(TEST_ADDRESS)
+                .build();
+        Phone testPhone0 = makeTestPhone(0 /*phoneId*/, ServiceState.STATE_POWER_OFF,
+                false /*isEmergencyOnly*/);
+        Phone testPhone1 = makeTestPhone(1 /*phoneId*/, ServiceState.STATE_POWER_OFF,
+                false /*isEmergencyOnly*/);
+        doReturn(GSM_PHONE).when(testPhone0).getPhoneType();
+        doReturn(GSM_PHONE).when(testPhone1).getPhoneType();
+        List<Phone> phones = new ArrayList<>(2);
+        doReturn(false).when(testPhone0).isRadioOn();
+        doReturn(false).when(testPhone1).isRadioOn();
+        phones.add(testPhone0);
+        phones.add(testPhone1);
+        setPhones(phones);
+        setupHandleToPhoneMap(PHONE_ACCOUNT_HANDLE_1, testPhone0);
+        setupDeviceConfig(testPhone0, testPhone1, 0);
+        doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(
+                TEST_ADDRESS.getSchemeSpecificPart());
+        HashMap<Integer, List<EmergencyNumber>> emergencyNumbers = new HashMap<>(1);
+        List<EmergencyNumber> numbers = new ArrayList<>();
+        numbers.add(setupEmergencyNumber(TEST_ADDRESS));
+        emergencyNumbers.put(0 /*subId*/, numbers);
+        doReturn(emergencyNumbers).when(mTelephonyManagerProxy).getCurrentEmergencyNumberList();
+        doReturn(2).when(mTelephonyManagerProxy).getPhoneCount();
+
+        mConnection = mTestConnectionService.onCreateOutgoingConnection(
+                PHONE_ACCOUNT_HANDLE_1, connectionRequest);
+        assertNotNull("test connection was not set up correctly.", mConnection);
 
         return testPhone0;
     }
@@ -1163,6 +1272,7 @@
         when(phone.getPhoneId()).thenReturn(phoneId);
         when(phone.getDefaultPhone()).thenReturn(phone);
         when(phone.getEmergencyNumberTracker()).thenReturn(mEmergencyNumberTracker);
+        when(phone.getServiceStateTracker()).thenReturn(mSST);
         when(mEmergencyNumberTracker.getEmergencyNumber(anyString())).thenReturn(null);
         return phone;
     }
@@ -1193,6 +1303,7 @@
 
     private void setPhones(List<Phone> phones) {
         when(mPhoneFactoryProxy.getPhones()).thenReturn(phones.toArray(new Phone[phones.size()]));
+        when(mPhoneFactoryProxy.getDefaultPhone()).thenReturn(phones.get(0));
     }
 
     private void setPhonesDialConnection(Phone phone, Connection c) {
diff --git a/tests/src/com/android/services/telephony/TestTelephonyConnection.java b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
index 5b31c0f..c36c405 100644
--- a/tests/src/com/android/services/telephony/TestTelephonyConnection.java
+++ b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
@@ -30,6 +30,7 @@
 import static org.mockito.Mockito.when;
 
 import com.android.internal.telephony.Call;
+import com.android.internal.telephony.CallStateException;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
@@ -72,8 +73,13 @@
         return mMockRadioConnection;
     }
 
+    @Override
+    protected Call getCall() {
+        return mMockCall;
+    }
+
     public TestTelephonyConnection() {
-        super(null, null, false);
+        super(null, null, android.telecom.Call.Details.DIRECTION_INCOMING);
         MockitoAnnotations.initMocks(this);
 
         mMockPhone = mock(Phone.class);
@@ -100,6 +106,11 @@
         when(mMockPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_IMS);
         when(mMockCall.getState()).thenReturn(Call.State.ACTIVE);
         when(mMockCall.getPhone()).thenReturn(mMockPhone);
+        try {
+            doNothing().when(mMockCall).hangup();
+        } catch (CallStateException e) {
+            e.printStackTrace();
+        }
     }
 
     @Override
diff --git a/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java b/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
new file mode 100644
index 0000000..fbb270d
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/RcsFeatureControllerTest.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.services.telephony.rcs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.telephony.AccessNetworkConstants;
+import android.telephony.ims.ImsException;
+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.feature.RcsFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+import com.android.ims.FeatureConnector;
+import com.android.ims.RcsFeatureManager;
+import com.android.internal.telephony.imsphone.ImsRegistrationCallbackHelper;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+
+import java.util.concurrent.Executor;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsFeatureControllerTest extends TelephonyTestBase {
+
+    private static final ImsReasonInfo REASON_DISCONNECTED = new ImsReasonInfo(
+            ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN, 0, "test");
+
+    @Mock RcsFeatureManager mFeatureManager;
+    @Mock RcsFeatureController.FeatureConnectorFactory<RcsFeatureManager> mFeatureFactory;
+    @Mock ImsRegistrationCallbackHelper.ImsRegistrationUpdate mRegistrationCallback;
+    @Mock FeatureConnector<RcsFeatureManager> mFeatureConnector;
+    @Mock RcsFeatureController.Feature mMockFeature;
+    @Captor ArgumentCaptor<FeatureConnector.Listener<RcsFeatureManager>> mConnectorListener;
+
+    private RcsFeatureController.RegistrationHelperFactory mRegistrationFactory =
+            new RcsFeatureController.RegistrationHelperFactory() {
+                @Override
+                public ImsRegistrationCallbackHelper create(
+                        ImsRegistrationCallbackHelper.ImsRegistrationUpdate cb, Executor executor) {
+                    // Run on current thread for testing.
+                    return new ImsRegistrationCallbackHelper(mRegistrationCallback, Runnable::run);
+                }
+            };
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Test
+    public void testRcsFeatureManagerConnectDisconnect() throws Exception {
+        RcsFeatureController controller = createFeatureController();
+        controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
+        verify(mMockFeature).onRcsDisconnected();
+        // Connect the RcsFeatureManager
+        mConnectorListener.getValue().connectionReady(mFeatureManager);
+
+        verify(mFeatureManager).updateCapabilities();
+        verify(mFeatureManager).registerImsRegistrationCallback(any());
+        verify(mMockFeature).onRcsConnected(mFeatureManager);
+
+        // Disconnect
+        mConnectorListener.getValue().connectionUnavailable();
+
+        verify(mFeatureManager).unregisterImsRegistrationCallback(any());
+        verify(mMockFeature, times(2)).onRcsDisconnected();
+    }
+
+    @Test
+    public void testFeatureManagerConnectedAddRemoveFeature() throws Exception {
+        RcsFeatureController controller = createFeatureController();
+        // Connect the RcsFeatureManager
+        mConnectorListener.getValue().connectionReady(mFeatureManager);
+        controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
+
+        verify(mMockFeature).onRcsConnected(mFeatureManager);
+        assertEquals(mMockFeature, controller.getFeature(RcsFeatureController.Feature.class));
+
+        controller.removeFeature(RcsFeatureController.Feature.class);
+        verify(mMockFeature).onDestroy();
+        assertNull(controller.getFeature(RcsFeatureController.Feature.class));
+    }
+
+    @Test
+    public void testFeatureManagerConnectedRegister() throws Exception {
+        RcsFeatureController controller = createFeatureController();
+        IImsRegistrationCallback regCb = mock(IImsRegistrationCallback.class);
+        IImsCapabilityCallback capCb = mock(IImsCapabilityCallback.class);
+        // Connect the RcsFeatureManager
+        mConnectorListener.getValue().connectionReady(mFeatureManager);
+
+        try {
+            controller.registerImsRegistrationCallback(0 /*subId*/, regCb);
+            controller.registerRcsAvailabilityCallback(0 /*subId*/, capCb);
+            controller.isCapable(RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE,
+                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+            controller.isAvailable(RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE);
+            controller.getRegistrationTech(integer -> {
+            });
+            verify(mFeatureManager).registerImsRegistrationCallback(0, regCb);
+            verify(mFeatureManager).registerRcsAvailabilityCallback(0, capCb);
+            verify(mFeatureManager).isCapable(
+                    RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE,
+                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+            verify(mFeatureManager).isAvailable(
+                    RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE);
+            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);
+    }
+
+    @Test
+    public void testFeatureManagerConnectedHelper() throws Exception {
+        RcsFeatureController controller = createFeatureController();
+        // Connect the RcsFeatureManager
+        mConnectorListener.getValue().connectionReady(mFeatureManager);
+        ArgumentCaptor<IImsRegistrationCallback> captor =
+                ArgumentCaptor.forClass(IImsRegistrationCallback.class);
+        verify(mFeatureManager).registerImsRegistrationCallback(captor.capture());
+        assertNotNull(captor.getValue());
+
+        captor.getValue().onDeregistered(REASON_DISCONNECTED);
+        controller.getRegistrationState(result -> {
+            assertNotNull(result);
+            assertEquals(RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED, result.intValue());
+        });
+        verify(mRegistrationCallback).handleImsUnregistered(REASON_DISCONNECTED);
+
+        captor.getValue().onRegistering(ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+        controller.getRegistrationState(result -> {
+            assertNotNull(result);
+            assertEquals(RegistrationManager.REGISTRATION_STATE_REGISTERING, result.intValue());
+        });
+        verify(mRegistrationCallback).handleImsRegistering(
+                AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+        captor.getValue().onRegistered(ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+        controller.getRegistrationState(result -> {
+            assertNotNull(result);
+            assertEquals(RegistrationManager.REGISTRATION_STATE_REGISTERED, result.intValue());
+        });
+        verify(mRegistrationCallback).handleImsRegistered(
+                AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+    }
+
+    @Test
+    public void testFeatureManagerDisconnectedAddFeature() {
+        RcsFeatureController controller = createFeatureController();
+        // Disconnect the RcsFeatureManager
+        mConnectorListener.getValue().connectionUnavailable();
+        controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
+
+        verify(mMockFeature).onRcsDisconnected();
+    }
+
+    @Test
+    public void testFeatureManagerDisconnectedException() {
+        RcsFeatureController controller = createFeatureController();
+        IImsRegistrationCallback regCb = mock(IImsRegistrationCallback.class);
+        IImsCapabilityCallback capCb = mock(IImsCapabilityCallback.class);
+        // Disconnect the RcsFeatureManager
+        mConnectorListener.getValue().connectionUnavailable();
+
+        try {
+            controller.registerImsRegistrationCallback(0 /*subId*/, null /*callback*/);
+            fail("ImsException expected for IMS registration.");
+        } catch (ImsException e) {
+            //expected
+        }
+        try {
+            controller.registerRcsAvailabilityCallback(0 /*subId*/, null /*callback*/);
+            fail("ImsException expected for availability");
+        } catch (ImsException e) {
+            //expected
+        }
+        try {
+            controller.isCapable(RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE,
+                    ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+            fail("ImsException expected for capability check");
+        } catch (ImsException e) {
+            //expected
+        }
+        try {
+            controller.isAvailable(RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE);
+            fail("ImsException expected for availability check");
+        } catch (ImsException e) {
+            //expected
+        }
+        controller.getRegistrationTech(integer -> {
+            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);
+    }
+
+    @Test
+    public void testChangeSubId() throws Exception {
+        RcsFeatureController controller = createFeatureController();
+        // Connect the RcsFeatureManager
+        mConnectorListener.getValue().connectionReady(mFeatureManager);
+        verify(mFeatureManager).updateCapabilities();
+        controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
+
+        controller.updateAssociatedSubscription(1 /*new sub id*/);
+
+        verify(mFeatureManager, times(2)).updateCapabilities();
+        verify(mMockFeature).onAssociatedSubscriptionUpdated(1 /*new sub id*/);
+    }
+
+    @Test
+    public void testDestroy() throws Exception {
+        RcsFeatureController controller = createFeatureController();
+        // Connect the RcsFeatureManager
+        mConnectorListener.getValue().connectionReady(mFeatureManager);
+        controller.addFeature(mMockFeature, RcsFeatureController.Feature.class);
+        controller.destroy();
+
+        verify(mFeatureConnector).disconnect();
+        verify(mMockFeature).onRcsDisconnected();
+        verify(mMockFeature).onDestroy();
+        assertNull(controller.getFeature(RcsFeatureController.Feature.class));
+    }
+
+    private RcsFeatureController createFeatureController() {
+        RcsFeatureController controller = new RcsFeatureController(mContext, 0 /*slotId*/,
+                mRegistrationFactory);
+        controller.setFeatureConnectorFactory(mFeatureFactory);
+        doReturn(mFeatureConnector).when(mFeatureFactory).create(any(), anyInt(),
+                mConnectorListener.capture(), any(), any());
+        controller.connect();
+        assertNotNull(mConnectorListener.getValue());
+        return controller;
+    }
+}
diff --git a/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java b/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
new file mode 100644
index 0000000..cfb68b7
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/TelephonyRcsServiceTest.java
@@ -0,0 +1,249 @@
+/*
+ * 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.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+import com.android.ims.FeatureConnector;
+import com.android.ims.RcsFeatureManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+
+@RunWith(AndroidJUnit4.class)
+public class TelephonyRcsServiceTest extends TelephonyTestBase {
+
+    @Captor ArgumentCaptor<BroadcastReceiver> mReceiverCaptor;
+    @Mock TelephonyRcsService.FeatureFactory mFeatureFactory;
+    @Mock UserCapabilityExchangeImpl mMockUceSlot0;
+    @Mock UserCapabilityExchangeImpl mMockUceSlot1;
+    @Mock RcsFeatureController.RegistrationHelperFactory mRegistrationFactory;
+    @Mock RcsFeatureController.FeatureConnectorFactory<RcsFeatureManager> mFeatureConnectorFactory;
+    @Mock FeatureConnector<RcsFeatureManager> mFeatureConnector;
+
+    private RcsFeatureController mFeatureControllerSlot0;
+    private RcsFeatureController mFeatureControllerSlot1;
+
+    @Before
+    public void setUp() throws Exception {
+        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));
+        doReturn(mMockUceSlot0).when(mFeatureFactory).createUserCapabilityExchange(any(), eq(0),
+                anyInt());
+        doReturn(mMockUceSlot1).when(mFeatureFactory).createUserCapabilityExchange(any(), eq(1),
+                anyInt());
+        //set up default slot-> sub ID mappings.
+        setSlotToSubIdMapping(0 /*slotId*/, 1/*subId*/);
+        setSlotToSubIdMapping(1 /*slotId*/, 2/*subId*/);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Test
+    public void testUserCapabilityExchangePresenceConnected() {
+        setCarrierConfig(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, true /*isEnabled*/);
+        createRcsService(1 /*numSlots*/);
+        verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot0).connect();
+    }
+
+    @Test
+    public void testUserCapabilityExchangeOptionsConnected() {
+        setCarrierConfig(CarrierConfigManager.KEY_USE_RCS_SIP_OPTIONS_BOOL, true /*isEnabled*/);
+        createRcsService(1 /*numSlots*/);
+        verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot0).connect();
+    }
+
+    @Test
+    public void testNoFeaturesEnabled() {
+        createRcsService(1 /*numSlots*/);
+        // No carrier config set for UCE.
+        verify(mFeatureControllerSlot0, never()).addFeature(mMockUceSlot0,
+                UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot0, never()).connect();
+    }
+
+    @Test
+    public void testNoFeaturesEnabledCarrierConfigChanged() {
+        createRcsService(1 /*numSlots*/);
+        // No carrier config set for UCE.
+
+        sendCarrierConfigChanged(0, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        verify(mFeatureControllerSlot0, never()).addFeature(mMockUceSlot0,
+                UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot0, never()).connect();
+        verify(mFeatureControllerSlot0, never()).updateAssociatedSubscription(anyInt());
+    }
+
+
+    @Test
+    public void testSlotUpdates() {
+        setCarrierConfig(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, true /*isEnabled*/);
+        TelephonyRcsService service = createRcsService(1 /*numSlots*/);
+        verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot0).connect();
+
+        // there should be no changes if the new num slots = old num
+        service.updateFeatureControllerSize(1 /*newNumSlots*/);
+        verify(mFeatureControllerSlot0, times(1)).addFeature(mMockUceSlot0,
+                UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot0, times(1)).connect();
+
+        // Add a new slot.
+        verify(mFeatureControllerSlot1, never()).addFeature(mMockUceSlot1,
+                UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot1, never()).connect();
+        service.updateFeatureControllerSize(2 /*newNumSlots*/);
+        // This shouldn't have changed for slot 0.
+        verify(mFeatureControllerSlot0, times(1)).addFeature(mMockUceSlot0,
+                UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot0, times(1)).connect();
+        verify(mFeatureControllerSlot1).addFeature(mMockUceSlot1, UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot1, times(1)).connect();
+
+        // Remove a slot.
+        verify(mFeatureControllerSlot0, never()).destroy();
+        verify(mFeatureControllerSlot1, never()).destroy();
+        service.updateFeatureControllerSize(1 /*newNumSlots*/);
+        // addFeature/connect shouldn't have been called again
+        verify(mFeatureControllerSlot0, times(1)).addFeature(mMockUceSlot0,
+                UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot0, times(1)).connect();
+        verify(mFeatureControllerSlot1, times(1)).addFeature(mMockUceSlot1,
+                UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot1, times(1)).connect();
+        // Verify destroy is only called for slot 1.
+        verify(mFeatureControllerSlot0, never()).destroy();
+        verify(mFeatureControllerSlot1, times(1)).destroy();
+    }
+
+    @Test
+    public void testCarrierConfigUpdate() {
+        setCarrierConfig(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, true /*isEnabled*/);
+        createRcsService(2 /*numSlots*/);
+        verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot1).addFeature(mMockUceSlot1, UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot0).connect();
+        verify(mFeatureControllerSlot1).connect();
+
+
+        // Send carrier config update for each slot.
+        sendCarrierConfigChanged(0 /*slotId*/, 1 /*subId*/);
+        verify(mFeatureControllerSlot0).updateAssociatedSubscription(1);
+        verify(mFeatureControllerSlot1, never()).updateAssociatedSubscription(1);
+        sendCarrierConfigChanged(1 /*slotId*/, 2 /*subId*/);
+        verify(mFeatureControllerSlot0, never()).updateAssociatedSubscription(2);
+        verify(mFeatureControllerSlot1, times(1)).updateAssociatedSubscription(2);
+    }
+
+    @Test
+    public void testCarrierConfigUpdateUceToNoUce() {
+        setCarrierConfig(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, true /*isEnabled*/);
+        createRcsService(1 /*numSlots*/);
+        verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot0).connect();
+
+
+        // Send carrier config update for each slot.
+        setCarrierConfig(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, false /*isEnabled*/);
+        sendCarrierConfigChanged(0 /*slotId*/, 1 /*subId*/);
+        verify(mFeatureControllerSlot0).removeFeature(UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot0).updateAssociatedSubscription(1);
+    }
+
+    @Test
+    public void testCarrierConfigUpdateNoUceToUce() {
+        createRcsService(1 /*numSlots*/);
+        verify(mFeatureControllerSlot0, never()).addFeature(mMockUceSlot0,
+                UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot0, never()).connect();
+
+
+        // Send carrier config update for each slot.
+        setCarrierConfig(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, true /*isEnabled*/);
+        sendCarrierConfigChanged(0 /*slotId*/, 1 /*subId*/);
+        verify(mFeatureControllerSlot0).addFeature(mMockUceSlot0, UserCapabilityExchangeImpl.class);
+        verify(mFeatureControllerSlot0).connect();
+        verify(mFeatureControllerSlot0).updateAssociatedSubscription(1);
+    }
+
+    private void sendCarrierConfigChanged(int slotId, int subId) {
+        Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+        intent.putExtra(CarrierConfigManager.EXTRA_SLOT_INDEX, slotId);
+        intent.putExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, subId);
+        mReceiverCaptor.getValue().onReceive(mContext, intent);
+    }
+
+    private void setCarrierConfig(String key, boolean value) {
+        PersistableBundle bundle = mContext.getCarrierConfig();
+        bundle.putBoolean(key, value);
+    }
+
+    private void setSlotToSubIdMapping(int slotId, int loadedSubId) {
+        SubscriptionManager m = mContext.getSystemService(SubscriptionManager.class);
+        int [] subIds = new int[1];
+        subIds[0] = loadedSubId;
+        doReturn(subIds).when(m).getSubscriptionIds(eq(slotId));
+    }
+
+    private TelephonyRcsService createRcsService(int numSlots) {
+        TelephonyRcsService service = new TelephonyRcsService(mContext, numSlots);
+        service.setFeatureFactory(mFeatureFactory);
+        service.initialize();
+        verify(mContext).registerReceiver(mReceiverCaptor.capture(), any());
+        return service;
+    }
+
+    private RcsFeatureController createFeatureController(int slotId) {
+        // Create a spy instead of a mock because TelephonyRcsService relies on state provided by
+        // RcsFeatureController.
+        RcsFeatureController controller = spy(new RcsFeatureController(mContext, slotId,
+                mRegistrationFactory));
+        controller.setFeatureConnectorFactory(mFeatureConnectorFactory);
+        return controller;
+    }
+}
diff --git a/tests/src/com/android/services/telephony/rcs/UserCapabilityExchangeImplTest.java b/tests/src/com/android/services/telephony/rcs/UserCapabilityExchangeImplTest.java
new file mode 100644
index 0000000..2457d28
--- /dev/null
+++ b/tests/src/com/android/services/telephony/rcs/UserCapabilityExchangeImplTest.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.services.telephony.rcs;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.RemoteCallbackList;
+import android.telephony.ims.ImsManager;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
+import android.telephony.ims.stub.RcsCapabilityExchange;
+import android.telephony.ims.stub.RcsPresenceExchangeImplBase;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.TelephonyTestBase;
+import com.android.ims.RcsFeatureManager;
+import com.android.ims.RcsFeatureManager.RcsFeatureCallbacks;
+import com.android.ims.ResultCode;
+import com.android.service.ims.presence.PresenceBase;
+import com.android.service.ims.presence.PresencePublication;
+import com.android.service.ims.presence.PresencePublisher;
+import com.android.service.ims.presence.PresenceSubscriber;
+import com.android.service.ims.presence.SubscribePublisher;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+
+import java.util.concurrent.Executor;
+
+@RunWith(AndroidJUnit4.class)
+public class UserCapabilityExchangeImplTest extends TelephonyTestBase {
+
+    private int  mSlotId = 0;
+    private int mSubId = 1;
+    private int mUpdatedSubId = 2;
+
+    @Captor ArgumentCaptor<IRcsUcePublishStateCallback> mPublishStateCallbacksCaptor;
+
+    @Mock PresencePublication mPresencePublication;
+    @Mock PresenceSubscriber mPresenceSubscriber;
+    @Mock RcsFeatureManager mRcsFeatureManager;
+    @Mock ImsMmTelManager mImsMmTelManager;
+    @Mock RemoteCallbackList<IRcsUcePublishStateCallback> mPublishStateCallbacks;
+
+    private Looper mLooper;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        ImsManager imsManager =
+                (ImsManager) mContext.getSystemService(Context.TELEPHONY_IMS_SERVICE);
+        when(imsManager.getImsMmTelManager(mSubId)).thenReturn(mImsMmTelManager);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+
+        if (mLooper != null) {
+            mLooper.quit();
+            mLooper = null;
+        }
+    }
+
+    @Test
+    public void testServiceConnected() throws Exception {
+        UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
+        uceImpl.onRcsConnected(mRcsFeatureManager);
+
+        verify(mRcsFeatureManager).addFeatureListenerCallback(any(RcsFeatureCallbacks.class));
+        verify(mPresencePublication).updatePresencePublisher(any(PresencePublisher.class));
+        verify(mPresenceSubscriber).updatePresenceSubscriber(any(SubscribePublisher.class));
+    }
+
+    @Test
+    public void testServiceDisconnected() throws Exception {
+        UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
+        uceImpl.onRcsDisconnected();
+
+        verify(mPresencePublication).removePresencePublisher();
+        verify(mPresenceSubscriber).removePresenceSubscriber();
+    }
+
+    @Test
+    public void testSubscriptionUpdated() throws Exception {
+        UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
+        uceImpl.onAssociatedSubscriptionUpdated(mUpdatedSubId);
+
+        verify(mImsMmTelManager).registerImsRegistrationCallback(any(Executor.class),
+                any(RegistrationManager.RegistrationCallback.class));
+        verify(mImsMmTelManager).registerMmTelCapabilityCallback(any(Executor.class),
+                any(ImsMmTelManager.CapabilityCallback.class));
+        verify(mPresencePublication).handleAssociatedSubscriptionChanged(mUpdatedSubId);
+        verify(mPresenceSubscriber).handleAssociatedSubscriptionChanged(mUpdatedSubId);
+    }
+
+    @Test
+    public void testUcePublishStateRetrieval() throws Exception {
+        UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
+        uceImpl.getUcePublishState();
+
+        verify(mPresencePublication).getPublishState();
+    }
+
+    @Test
+    public void testRegisterPublishStateCallbacks() throws Exception {
+        UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
+        uceImpl.registerPublishStateCallback(any(IRcsUcePublishStateCallback.class));
+        verify(mPublishStateCallbacks).register(mPublishStateCallbacksCaptor.capture());
+    }
+
+    @Test
+    public void testOnNotifyUpdateCapabilities() throws Exception {
+        UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
+        uceImpl.onRcsConnected(mRcsFeatureManager);
+
+        int triggerType = RcsPresenceExchangeImplBase.CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN;
+        uceImpl.mRcsFeatureCallback.onNotifyUpdateCapabilities(triggerType);
+        waitForMs(1000);
+
+        verify(mPresencePublication).onStackPublishRequested(triggerType);
+    }
+
+    @Test
+    public void testRequestPublicationWithSuccessfulResponse() throws Exception {
+        int taskId = 1;
+        int sipResponse = 200;
+        Uri contact = Uri.fromParts("sip", "test", null);
+        RcsContactUceCapability.Builder builder = new RcsContactUceCapability.Builder(contact);
+        RcsContactUceCapability capability = builder.build();
+
+        UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
+        uceImpl.onRcsConnected(mRcsFeatureManager);
+
+        doAnswer(invocation -> {
+            uceImpl.mRcsFeatureCallback.onCommandUpdate(RcsCapabilityExchange.COMMAND_CODE_SUCCESS,
+                    taskId);
+            uceImpl.mRcsFeatureCallback.onNetworkResponse(sipResponse, null, taskId);
+            return null;
+        }).when(mRcsFeatureManager).requestPublication(capability, taskId);
+
+        // Request publication
+        int result = uceImpl.requestPublication(capability, contact.toString(), taskId);
+
+        assertEquals(ResultCode.SUCCESS, result);
+        verify(mPresencePublication).onCommandStatusUpdated(taskId, taskId, ResultCode.SUCCESS);
+        verify(mPresencePublication).onSipResponse(taskId, sipResponse, null);
+    }
+
+    @Test
+    public void testRequestPublicationWithFailedResponse() throws Exception {
+        int taskId = 1;
+        Uri contact = Uri.fromParts("sip", "test", null);
+        RcsContactUceCapability.Builder builder = new RcsContactUceCapability.Builder(contact);
+        RcsContactUceCapability capability = builder.build();
+
+        UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
+        uceImpl.onRcsConnected(mRcsFeatureManager);
+
+        doAnswer(invocation -> {
+            uceImpl.mRcsFeatureCallback.onCommandUpdate(
+                    RcsCapabilityExchange.COMMAND_CODE_GENERIC_FAILURE, taskId);
+            return null;
+        }).when(mRcsFeatureManager).requestPublication(capability, taskId);
+
+        // Request publication
+        int result = uceImpl.requestPublication(capability, contact.toString(), taskId);
+
+        assertEquals(ResultCode.SUCCESS, result);
+        verify(mPresencePublication).onCommandStatusUpdated(taskId, taskId,
+                ResultCode.PUBLISH_GENERIC_FAILURE);
+    }
+
+    @Test
+    public void testUpdatePublisherState() throws Exception {
+        IRcsUcePublishStateCallback callback = Mockito.mock(IRcsUcePublishStateCallback.class);
+        doAnswer(invocation -> {
+            callback.onPublishStateChanged(anyInt());
+            return null;
+        }).when(mPublishStateCallbacks).broadcast(any());
+
+        UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
+        uceImpl.onRcsConnected(mRcsFeatureManager);
+        uceImpl.registerPublishStateCallback(callback);
+        uceImpl.updatePublisherState(PresenceBase.PUBLISH_STATE_200_OK);
+
+        assertEquals(PresenceBase.PUBLISH_STATE_200_OK, uceImpl.getPublisherState());
+        verify(callback).onPublishStateChanged(anyInt());
+    }
+
+    @Test
+    public void testUnpublish() throws Exception {
+        IRcsUcePublishStateCallback callback = Mockito.mock(IRcsUcePublishStateCallback.class);
+        doAnswer(invocation -> {
+            callback.onPublishStateChanged(anyInt());
+            return null;
+        }).when(mPublishStateCallbacks).broadcast(any());
+
+        UserCapabilityExchangeImpl uceImpl = createUserCapabilityExchangeImpl();
+        uceImpl.onRcsConnected(mRcsFeatureManager);
+        uceImpl.mRcsFeatureCallback.onUnpublish();
+        waitForMs(1000);
+
+        assertEquals(PresenceBase.PUBLISH_STATE_NOT_PUBLISHED, uceImpl.getPublisherState());
+        verify(callback).onPublishStateChanged(anyInt());
+    }
+
+    private UserCapabilityExchangeImpl createUserCapabilityExchangeImpl() throws Exception {
+        HandlerThread handlerThread = new HandlerThread("UceImplHandlerThread");
+        handlerThread.start();
+        mLooper = handlerThread.getLooper();
+        UserCapabilityExchangeImpl uceImpl = new UserCapabilityExchangeImpl(mContext, mSlotId,
+                mSubId, mLooper, mPresencePublication, mPresenceSubscriber,
+                mPublishStateCallbacks);
+        verify(mPresencePublication).handleAssociatedSubscriptionChanged(1);
+        verify(mPresenceSubscriber).handleAssociatedSubscriptionChanged(1);
+        waitForHandlerAction(uceImpl.getHandler(), 1000);
+        verify(mImsMmTelManager, atLeast(1)).registerImsRegistrationCallback(
+                any(Executor.class), any(RegistrationManager.RegistrationCallback.class));
+        verify(mContext).registerReceiver(any(), any());
+        return uceImpl;
+    }
+}